Files
dsh_ai1net_server/交付物/唤醒-无人值守可行性-20260930-图.html
T
admin c1b5e4d966 chore(工作区): 全量入库 + 补齐 .gitignore(以工作区为准)
- 变更规模:新增 514 / 修改 62 / 重命名 155 / 删除 4(归档重组与文档轮次)
- .gitignore 修:`归档/**/db-cwd归一-备份-*/` —— 原规则写绝对层级(归档/db-cwd归一-…),
  目录搬进 归档/配置与备份/ 后**静默失效**,43 MB 的 DB 备份又变成未跟踪
- .gitignore 补:嵌套 git 内部数据(归档/内嵌git-20261008/、归档/skills-git-旧线-20261007/dotgit-原样移出/)
- .gitignore 补:运行态与部署副本(.workbuddy/collab/、.workbuddy/tools/、.workbuddy/.load-pending、.workbuddy/tmp-*)
- .gitignore 补:备份件(*.bak-*)
- 未跟踪文件从 2190 降到 890(其余为 归档/ 归档件与 .workbuddy/memory/ 知识文件,按口径入库)
2026-10-10 23:13:22 +08:00

237 lines
15 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>唤醒 · 无人值守可行性 —— 泳道图 + 事故链图(2026-09-30)</title>
<style>
:root{
--bg:#0f1115; --panel:#171a21; --panel2:#1d2129; --line:#2b313c;
--tx:#e6e8ee; --tx2:#a6adbb; --tx3:#727a8a;
--red:#ff5c5c; --redbg:#331a1c; --amber:#ffb454; --amberbg:#332616;
--green:#3ddc97; --greenbg:#122c22; --blue:#5aa9ff; --bluebg:#152232;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--tx);
font-family:ui-sans-serif,-apple-system,"Segoe UI","Microsoft YaHei","PingFang SC",sans-serif;
font-size:14px;line-height:1.6;padding:28px 32px 60px}
h1{font-size:20px;margin:0 0 6px}
.sub{color:var(--tx3);font-size:12.5px;margin-bottom:18px}
.verdict{background:var(--redbg);border:1px solid var(--red);border-left:4px solid var(--red);
border-radius:8px;padding:12px 16px;margin-bottom:8px}
.verdict b{color:var(--red)}
.verdict .ok{color:var(--green)}
.card{background:var(--panel);border:1px solid var(--line);border-radius:10px;
padding:18px 20px 22px;margin-top:18px}
.card h2{font-size:15px;margin:0 0 4px;color:var(--tx)}
.card .hint{color:var(--tx3);font-size:12.5px;margin-bottom:12px}
svg{display:block;max-width:100%;height:auto}
.lg{display:flex;gap:18px;flex-wrap:wrap;color:var(--tx2);font-size:12.5px;margin-top:10px}
.lg span i{display:inline-block;width:11px;height:11px;border-radius:3px;margin-right:6px;vertical-align:-1px}
.t{font-family:ui-monospace,Consolas,"Courier New",monospace}
</style>
</head>
<body>
<h1>唤醒 · 无人值守可行性 —— 两张图</h1>
<div class="sub">2026-09-30 19:4x | 机制线·唤醒机制探索 | 会话 <span class="t">a202550c</span> | 配文:<span class="t">交付物/唤醒-无人值守可行性-20260930.md</span></div>
<div class="verdict">
<b>判定:不能</b> —— 「把<u>已经退出的那个会话</u>从外部叫起来续跑」结构上无路:网关口就长在 worker 进程里,worker 一走端口就走;唯一外部入口 <span class="t">POST /api/v1/runs</span> 又不带"选哪个会话"的能力。<br>
<span class="ok">但"无人值守"本身仍做得到</span> —— 形态是「无人值守地<b>新起一个会话</b>」(=自动化派棒,今天已在跑),代价:粒度最细 1 小时。
</div>
<!-- ══════════════ 图 1 · 泳道图 ══════════════ -->
<div class="card">
<h2>图 1 · 泳道图(角色 × 阶段)</h2>
<div class="hint">看"外部想唤它"这个动作落在哪一格 —— 第 ④ 列把网关口带走,第 ⑤ 列因此没有可投的地址。</div>
<svg viewBox="0 0 1220 560" role="img" aria-label="唤醒链路泳道图">
<defs>
<marker id="ar" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto">
<path d="M0,0 L9,4.5 L0,9 z" fill="#5f6a7c"/>
</marker>
</defs>
<!-- 列头 -->
<g font-size="12.5" fill="#a6adbb">
<text x="150" y="26" font-size="11.5" fill="#727a8a">角色 \ 阶段</text>
<text x="266" y="26">① 会话被激活</text>
<text x="476" y="26">② 回合进行中</text>
<text x="686" y="26">③ 回合结束</text>
<text x="896" y="26">④ worker 自行退出</text>
<text x="1106" y="26">⑤ 外部想叫醒它</text>
</g>
<line x1="150" y1="36" x2="1216" y2="36" stroke="#2b313c"/>
<!-- 行标签列 -->
<g font-size="12.5" fill="#e6e8ee">
<text x="8" y="88">宿主主进程</text><text x="8" y="104" font-size="11" fill="#727a8a">CliPrewarmPool</text>
<text x="8" y="178">CLI worker</text><text x="8" y="194" font-size="11" fill="#727a8a">(=会话宿主)</text>
<text x="8" y="268">网关口 (HTTP)</text>
<text x="8" y="358">外部程序</text><text x="8" y="374" font-size="11" fill="#727a8a">(想唤醒的一方)</text>
<text x="8" y="448">目标旧会话</text>
</g>
<line x1="150" y1="36" x2="150" y2="500" stroke="#2b313c"/>
<!-- 网格底 -->
<g stroke="#232833">
<line x1="150" y1="130" x2="1216" y2="130"/><line x1="150" y1="216" x2="1216" y2="216"/>
<line x1="150" y1="302" x2="1216" y2="302"/><line x1="150" y1="402" x2="1216" y2="402"/>
<line x1="150" y1="500" x2="1216" y2="500"/>
<line x1="360" y1="36" x2="360" y2="500"/><line x1="570" y1="36" x2="570" y2="500"/>
<line x1="780" y1="36" x2="780" y2="500"/><line x1="990" y1="36" x2="990" y2="500"/>
</g>
<!-- ④ 红列底纹 -->
<rect x="990" y="36" width="226" height="464" fill="#ff5c5c" opacity="0.055"/>
<!-- ⑤ 红列底纹 -->
<rect x="990" y="36" width="226" height="464" fill="none"/>
<rect x="804" y="36" width="186" height="464" fill="#ffb454" opacity="0.05"/>
<line x1="780" y1="36" x2="780" y2="500" stroke="#ffb454" opacity="0.35"/>
<line x1="990" y1="36" x2="990" y2="500" stroke="#ff5c5c" opacity="0.5"/>
<g font-size="12.5" fill="#c9cede">
<!-- 宿主主进程 -->
<text x="166" y="76">tryAcquire</text><text x="166" y="93">→ doActivate</text><text x="166" y="110">+ spawn 补池</text>
<text x="376" y="93" fill="#727a8a">(不干预)</text>
<text x="586" y="93" fill="#727a8a">(不干预)</text>
<text x="806" y="76" fill="#ff5c5c">child.on("exit")</text><text x="806" y="93" fill="#ff5c5c">⇒ 只打一条 warn</text>
<text x="806" y="110" fill="#ff5c5c">⛔ 不补池/不重拉</text>
<text x="1000" y="76" fill="#ff5c5c">无入口</text><text x="1000" y="93" fill="#ff5c5c">⛔ 不补池</text><text x="1000" y="110" fill="#ff5c5c">⛔ 不重拉</text>
<!-- CLI worker -->
<text x="166" y="160">起进程</text><text x="166" y="177">+挂网关口</text><text x="166" y="194" fill="#727a8a">:PORT 随进程诞生</text>
<text x="376" y="160">处理消息·跑工具</text><text x="376" y="177" fill="#727a8a">addHistory 不停</text>
<text x="586" y="160">生成收尾</text><text x="586" y="177" fill="#3ddc97">addHistory ✔</text>
<text x="806" y="160" fill="#ff5c5c">code=1 静默退出</text><text x="806" y="177" fill="#ff5c5c">stderr 空·非人为 kill</text>
<text x="806" y="194" fill="#ff5c5c">⇒ 端口也没了</text>
<text x="1000" y="177" fill="#ff5c5c">已不存在</text>
<!-- 网关口 -->
<text x="166" y="250">/live = 本会话</text>
<text x="376" y="250">writerOccupied=true</text>
<text x="586" y="250" fill="#727a8a">(同左)</text>
<text x="806" y="250" fill="#ff5c5c">✗ 随进程消失</text>
<text x="1000" y="250" fill="#ff5c5c">✗ 连不上</text>
<!-- 外部程序 -->
<text x="166" y="345" fill="#727a8a">—</text><text x="376" y="345" fill="#727a8a">—</text><text x="586" y="345" fill="#727a8a">—</text>
<text x="806" y="345" fill="#727a8a">—</text>
<text x="1000" y="330" fill="#ffb454">POST /runs → 202 接受</text>
<text x="1000" y="347" fill="#ffb454">但落到「它自己</text>
<text x="1000" y="364" fill="#ffb454">那个会话」</text>
<!-- 目标旧会话 -->
<text x="166" y="435">← 唯一一次被激活</text>
<text x="376" y="435">跑完它的回合</text>
<text x="586" y="435" fill="#ff5c5c">transcript 冻结</text>
<text x="806" y="435" fill="#ff5c5c">就此冻结</text>
<text x="1000" y="435" fill="#ff5c5c">零反应</text>
<text x="1000" y="452" fill="#ff5c5c" font-size="11">(md5 一字未变)</text>
</g>
<!-- 关键箭头 -->
<path d="M885 130 L885 140" stroke="#5f6a7c" marker-end="url(#ar)"/>
<path d="M1095 216 L1095 300" stroke="#ffb454" stroke-dasharray="4 3" marker-end="url(#ar)"/>
<text x="1002" y="492" font-size="11.5" fill="#727a8a">▲ 红格:④ 把网关口带走 ⇒ ⑤ 无处可投(对别的活网关投=落它自己的会话)</text>
</svg>
<div class="lg">
<span><i style="background:#ff5c5c"></i>断点/失效</span>
<span><i style="background:#ffb454"></i>看似成功、实则落错会话</span>
<span><i style="background:#3ddc97"></i>正常</span>
</div>
</div>
<!-- ══════════════ 图 2 · 事故链图 ══════════════ -->
<div class="card">
<h2>图 2 · 事故链图("想唤醒旧会话"实际怎么断的 + 哪几道防线缺席)</h2>
<div class="hint">★="有人在给它派活"这一瞬间;红格=静默丢单;橙格=202 假绿。</div>
<svg viewBox="0 0 1220 470" role="img" aria-label="唤醒失败事故链图">
<defs>
<marker id="ar2" markerWidth="9" markerHeight="9" refX="7" refY="4.5" orient="auto">
<path d="M0,0 L9,4.5 L0,9 z" fill="#5f6a7c"/>
</marker>
</defs>
<g font-size="12.5">
<!-- 链节点 -->
<rect x="24" y="14" width="600" height="46" rx="8" fill="#171a21" stroke="#3ddc97" stroke-width="1.2"/>
<text x="40" y="34" fill="#3ddc97" font-size="11.5" class="t">19:00:19.720</text>
<text x="150" y="34" fill="#e6e8ee">宿主 tryAcquire → doActivate ⇒ 706d2476 拿到 worker(pid=47188)</text>
<text x="150" y="51" fill="#a6adbb" font-size="11.5">网关口 :PORT 随之诞生</text>
<path d="M324 60 L324 74" stroke="#5f6a7c" marker-end="url(#ar2)"/>
<rect x="24" y="74" width="600" height="34" rx="8" fill="#171a21" stroke="#2b313c"/>
<text x="40" y="96" fill="#a6adbb" font-size="11.5" class="t">19:00–19:09</text>
<text x="150" y="96" fill="#c9cede">该会话跑完它的整轮工作(会话日志连续增长)</text>
<path d="M324 108 L324 122" stroke="#5f6a7c" marker-end="url(#ar2)"/>
<rect x="24" y="122" width="600" height="46" rx="8" fill="#152232" stroke="#5aa9ff"/>
<text x="40" y="142" fill="#5aa9ff" font-size="11.5" class="t">19:09:16.304</text>
<text x="150" y="142" fill="#e6e8ee">★ resolveConfig input {"sessionId":"706d2476…"}</text>
<text x="150" y="159" fill="#5aa9ff" font-size="11.5">=宿主这边**确实**在给它准备一次运行 ⇒ 有人在派活</text>
<path d="M324 168 L324 182" stroke="#5f6a7c" marker-end="url(#ar2)"/>
<rect x="24" y="182" width="600" height="60" rx="8" fill="#331a1c" stroke="#ff5c5c" stroke-width="1.4"/>
<text x="40" y="203" fill="#ff5c5c" font-size="11.5" class="t">19:09:16.570</text>
<text x="150" y="203" fill="#ff5c5c">⛔ activated prewarm entry exited unexpectedly</text>
<text x="150" y="220" fill="#ff5c5c" font-size="11.5" class="t">code=1 signal=none stderr tail: &lt;empty&gt;</text>
<text x="150" y="236" fill="#ffb454" font-size="11.5">⇒ 0.27 s 之内,那次"派活"就地蒸发(静默丢单)</text>
<path d="M324 242 L324 256" stroke="#5f6a7c" marker-end="url(#ar2)"/>
<rect x="24" y="256" width="600" height="46" rx="8" fill="#331a1c" stroke="#ff5c5c" stroke-width="1.4"/>
<text x="40" y="276" fill="#ff5c5c" font-size="11.5">此后</text>
<text x="150" y="276" fill="#ff5c5c">再没有任何 706d2476 的 doActivate / resolveConfig</text>
<text x="150" y="293" fill="#ff5c5c" font-size="11.5">⇒ 宿主不自愈(activated 退出=只 warn,不补不拉)</text>
<path d="M324 302 L324 316" stroke="#5f6a7c" marker-end="url(#ar2)"/>
<rect x="24" y="316" width="600" height="46" rx="8" fill="#332616" stroke="#ffb454" stroke-width="1.4"/>
<text x="40" y="336" fill="#ffb454" font-size="11.5" class="t">19:40:56</text>
<text x="150" y="336" fill="#e6e8ee">本棒把 id=706d2476 的 POST /api/v1/runs 打到另一个**活着的**网关</text>
<text x="150" y="353" fill="#ffb454" font-size="11.5">回 202 accepted、run 表 active=true ⇢ 看似成功</text>
<path d="M324 362 L324 376" stroke="#5f6a7c" marker-end="url(#ar2)"/>
<rect x="24" y="376" width="600" height="60" rx="8" fill="#331a1c" stroke="#ff5c5c" stroke-width="1.4"/>
<text x="40" y="397" fill="#ff5c5c" font-size="11.5" class="t">19:45:00</text>
<text x="150" y="397" fill="#ff5c5c">706d2476 transcript:230 行 / md5 7c404bfd… / mtime 19:09:15.929</text>
<text x="150" y="414" fill="#ff5c5c" font-size="12.5">▲ **一字未变** ⇒ 这一棒想做的事,结构上就做不成</text>
<text x="150" y="430" fill="#727a8a" font-size="11">(activeSessionId 全程仍是投递方自己的会话 a202550c)</text>
</g>
<!-- 右侧:缺席的防线 -->
<rect x="652" y="14" width="546" height="422" rx="10" fill="#1d2129" stroke="#2b313c"/>
<text x="672" y="40" font-size="13.5" fill="#e6e8ee">哪几道防线缺席</text>
<line x1="672" y1="52" x2="1178" y2="52" stroke="#2b313c"/>
<g font-size="12.5">
<circle cx="682" cy="78" r="11" fill="#331a1c" stroke="#ff5c5c"/><text x="682" y="82" fill="#ff5c5c" font-size="11.5" text-anchor="middle">1</text>
<text x="702" y="76" fill="#e6e8ee">投递没有"落到谁"的回执</text>
<text x="702" y="93" fill="#a6adbb" font-size="11.5">202 只说明"受理了";/status.activeSessionId 也不会因这次投递而变</text>
<text x="702" y="110" fill="#ffb454" font-size="11.5">⇒ 调用方无法判断"它到底有没有投到我要的那个会话"</text>
<circle cx="682" cy="146" r="11" fill="#331a1c" stroke="#ff5c5c"/><text x="682" y="150" fill="#ff5c5c" font-size="11.5" text-anchor="middle">2</text>
<text x="702" y="144" fill="#e6e8ee">activated 条目意外退出=只写一条 warn</text>
<text x="702" y="161" fill="#a6adbb" font-size="11.5">不补池、不重拉、不通知任何环节</text>
<text x="702" y="178" fill="#ff5c5c" font-size="11.5">⇒ 会话宿主没了,系统里没有一环会察觉,更不会自愈</text>
<circle cx="682" cy="214" r="11" fill="#331a1c" stroke="#ff5c5c"/><text x="682" y="218" fill="#ff5c5c" font-size="11.5" text-anchor="middle">3</text>
<text x="702" y="212" fill="#e6e8ee">网关没有"打开/激活某个会话"的端点</text>
<text x="702" y="229" fill="#a6adbb" font-size="11.5">会话控制器只有 list / live / workspaces / pins / rename / remove /</text>
<text x="702" y="246" fill="#a6adbb" font-size="11.5">history / replay / reply —— 唯独没有"打开"</text>
<text x="702" y="263" fill="#ff5c5c" font-size="11.5">⇒ 唯一有效的复活动因在桌面端,而它走 pipe,不是 TCP ⇒ 外部接不上</text>
<circle cx="682" cy="299" r="11" fill="#331a1c" stroke="#ff5c5c"/><text x="682" y="303" fill="#ff5c5c" font-size="11.5" text-anchor="middle">4</text>
<text x="702" y="297" fill="#e6e8ee">worker 存活时长没有下限保障</text>
<text x="702" y="314" fill="#a6adbb" font-size="11.5">今天实测:最短 28 s、多数 4–5 min(三天 40 / 55 / 28 次退出)</text>
<text x="702" y="331" fill="#a6adbb" font-size="11.5">idle 钩子要的"连续空闲 ≥60 s"正好落在 worker 生命的尾巴上</text>
<text x="702" y="348" fill="#a6adbb" font-size="11.5">⇒ 18:55:00.210 那次成功,是小概率命中,不是稳定能力</text>
<text x="672" y="386" fill="#727a8a" font-size="11.5">结论:这四条都不是"没做好",是"结构上不存在"——</text>
<text x="672" y="404" fill="#727a8a" font-size="11.5">故"唤醒同一个旧会话"无法实现;能实现的是"无人值守地新起一个会话"(自动化,1 小时粒度)。</text>
<text x="672" y="424" fill="#5aa9ff" font-size="11.5">唯一能改变此判定的未知量:code=1 到底是"回合结束被主动回收"还是"崩溃"。</text>
</g>
</svg>
</div>
</body>
</html>