- 变更规模:新增 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/ 知识文件,按口径入库)
3271 lines
337 KiB
Markdown
3271 lines
337 KiB
Markdown
# 2026-10-01 工作日志(DSH 工作区 · 协作机制线)
|
||
|
||
---
|
||
|
||
## 00:2x–00:3x · 协作机制线接续棒:用户两条订正 —— **「目标由对话说明」+「看板=实时动态看板」**
|
||
|
||
**用户两条原话**
|
||
1. 「**目标是 通过对话在调用 会话协作skill时说明的,不是固定的**」
|
||
2. 「**我说的看板是 实时动态看板 现在被关闭了**」
|
||
|
||
### 一、订正 ①:目标不是固定配置
|
||
|
||
**我上一轮做错了什么**:把 `goal.json.topics` 按**工作区里现有的接续入口/接续包文件名**填了 4 类
|
||
(唤醒机制/文档库治理/规则载体/IM),还当成"待用户拍板定死"的事项去问。
|
||
⇒ **那是猜,不是说明**;危害不是"填错会报错",而是它**静默决定**「哪些会话算本项目、投递往哪条主会话去」,
|
||
且**没有任何一处会说"这批类别是猜的"**(同族红线:`architecture.md §2.3.2` 那种"不崩溃,只少说一句话")。
|
||
|
||
**落地三件**
|
||
| # | 落点 | 内容 |
|
||
|---|---|---|
|
||
| 1 | 技能 `SKILL.md §0.05` + `references/architecture.md §0.1` | 目标/为什么/任务类别/验收判据**四样都由"调用技能时的对话"产生**;⛔ 技能侧与脚本**不许预设**、⛔ **不许从目录名/文件名/接续入口名推** |
|
||
| 2 | 使用方 `.workbuddy/collab/goalctl.py` **新命令 `declare`** | "说明"的**唯一落点**:`declare --title "…" [--why …] [--topics "A,B"] [--kpi "V1=pass"] --yes`。⛔ 默认干跑;`--title` **必填**(脚本**不替你编目标**);省略某项 ⇒ **不动**该项;`--topics ""` ⇒ 显式清空;重写 `--kpi` 会**点明丢掉了哪些说明行** |
|
||
| 3 | 看板 `project.topics_source` | `kind ∈ {declared, fallback, none}` ⇒ `fallback` 时**明写「⚠ 未声明 ⇒ 暂回落目标简称」**(⛔ 不许让人以为"这就是定下来的类别");`declared` 时显示说明时间/说明人 |
|
||
|
||
**数据订正**:`goal.json` 里**我猜的 3 类已移出 `topics`**(进 `_topics候选`,注明"未经对话确认、依据是文件名"),
|
||
`topics` 只留**有依据的那条** `唤醒机制`(来自本线会话标题 `主控 · 唤醒机制线 · …`);`lines` 同步。
|
||
新增 `_目标来源` 键写明这条规矩。备份 `tmp/bak-目标来源订正-20261001/`。
|
||
|
||
### 二、订正 ②:看板是**实时动态看板**
|
||
|
||
**事实**(我确认过):`goalctl stop --yes`(09-30 20:27)把 `board.py --serve` 一起停了
|
||
⇒ 21:00 之后我给你的只是**离线快照页**(复核手段)—— 那不是你指的那个东西。
|
||
|
||
**已恢复**:`http://127.0.0.1:8788/` 重新起着。
|
||
- **起法(本机唯一可行)**:**会话后台任务 + stdout 重定向到文件**
|
||
```
|
||
COLLABD_CONFIG=<ws>/.workbuddy/collab/collabd.config.json DSH_COLLAB_WS=<ws> \
|
||
"<py>" "<技能>/scripts/board.py" --serve 8788 --takeover > <ws>/tmp/board-serve.out.log 2>&1
|
||
```
|
||
⛔ 必须带 `COLLABD_CONFIG` + `DSH_COLLAB_WS`(`board.py` 只从 env 取工作区)。
|
||
- **验收读数**:`GET /` → **200 / 59,076 B**;`GET /board.json` → **200 / 9,551 B**;
|
||
连拉两次 `epoch` **1790785810.9 → 1790785814.1** ⇒ **真·实时**;`netstat` 确认**只绑 `127.0.0.1:8788`**(PID 34184)。
|
||
- **踩到的小坑**:刚起 3 秒内 `/board.json` 返回 `{"warming": true}`(53 B)—— 后台线程还没产第一份快照。
|
||
⚠️ 别把这 53 B 当成"起失败了"。
|
||
|
||
**落点**:`SKILL.md §0.5.0` 新增「**看板默认指那个实时动态看板**」(地址/起法/`--takeover`/停机出口)
|
||
+ **两条如实登记的边界**:
|
||
1. 它是**会话后台任务** ⇒ **关会话/关宿主就停** —— 本机**没有**真正常驻手段(detached 活不过工具调用边界;
|
||
`schtasks`/`reg` 等在内置黑名单里)⇒ **这不是"忘了常驻",是做不到**。
|
||
2. ⚠️ **该会话挂着 `pending`/`running` 后台任务时,宿主 `idle` 钩子被静默压制**(09-30 实测压 6h20m)
|
||
⇒ 正解=**让"起看板"落在不承担派活职责的会话里**,⛔ 别让主会话干。
|
||
3. `goalctl start` 的 ③ 服务层**原本只说"不自启常驻"、没给起看板命令**("开"这条路漏了看板)⇒ 已补上可复制的整行命令。
|
||
|
||
### 三、验证读数
|
||
|
||
- 技能自测 **PASS 32 / FAIL 0**(新增用例 `看板:任务类别**来源**必须说得出口`,6 项)
|
||
- 看板渲染断言 **18 / 18**(⚠️ 顺带修掉一处**写死数字**的断言:原来假定"3 个类别无主会话",
|
||
数据一变就假红 ⇒ 改成**从快照现算**,⛔ 断言里的数字不许写死)
|
||
- 架构图几何自检 **3 / 3**(现在 9 个 rect:1 个类别 + 1 个「未归类」折叠格)
|
||
|
||
### 四、仍然如实挂着的状态
|
||
|
||
- 台账 4 条(N9/N10/V6/前置-20090)**全是手机线遗产**、且都不属于任何当前类别
|
||
⇒ 看板按「未归类」折叠格显示,⛔ 不隐藏也⛔ 不冒充类别。
|
||
- 验收仍**未声明**(只有两条说明行)⇒ 控制台/看板显式判「判不出来,⛔ 不因此判完成」。
|
||
- 服务器侧 `/opt/dsh/state/.op-lock` 仍被 `SF-验收复跑-01`(09-26 起)占着 —— **不是我持有的,按 R9 不动它**。
|
||
|
||
---
|
||
|
||
## 01:0x · 唤醒定时任务:**换形状 + 显真状态**(用户四条原话连着来)
|
||
|
||
**用户原话(按序)**
|
||
1. 「**之前 主会话 左边的唤醒通道是不是没有了**,给新建的唤醒定时任务 换个样式 和 协作会话区分开」
|
||
2. 「**唤醒机制 改为 唤醒定时任务**,要**实时展示运行状态**,包括**图框样式要能体现**」
|
||
3. 「唤醒定时任务 和 主会话下方的 唤醒机制 **是不是 重复了**」
|
||
4. 「你说的唤醒机制 是不是 **主会话根据目标创建的 协作会话**」
|
||
|
||
### 一、先答「通道是不是没有了」——**没有**
|
||
|
||
`front.triggers` 一直有 1 条(`up=false`)。2026-09-30 已修过**同一个回归**(当时用户原话「看板 唤醒节点都没了」,
|
||
根因=只遍历**现役** `tasks`、现役一被清空那格就整格消失 ⇒ 已改为回落最近一条历史并把"通道已停用"写进 `detail`)。
|
||
⇒ 这次**不是没了,是"已停用"占位**。
|
||
|
||
### 二、改了什么
|
||
|
||
| # | 落点 | 改动 |
|
||
|---|---|---|
|
||
| 1 | `assets/board.html`(技能) | 该格由 `<rect rx=14>`(**与协作会话同形**)换成 **六边形 `<path>` + 外圈虚线 `.trig-halo`** ⇒ 全图**唯一**一处非矩形,一眼区分「脉冲式触发器」vs「常驻角色」 |
|
||
| 2 | 同上 | 新增 **状态徽章**(六边形上方):`running` 绿 ●运行中 / `paused` 黄 ‖已暂停 / `none` 灰 ○未登记。🔴 **用 SVG 图形不用 `●‖○` 字符**(字符依赖字体) |
|
||
| 3 | 同上 | `front.triggers[].state` 新增(缺省回落 `up` ⇒ 老使用方不受影响);图外 `extra` + `aria-label` 同步说明 |
|
||
| 4 | `SKILL.md` **§0.5.5**(新) | 「**形状即语义**」:六边形=时间驱动触发器;圆角矩形=会话/程序。名字仍由**使用方**给(⛔ 技能里不写死项目名) |
|
||
| 5 | `.workbuddy/collab/board_ext.py`(使用方) | `_triggers()` **读数改走 `automations` 表**(原只读已废弃的网关册子 `gateway-schedules.json` ⇒ 永远只得"通道已停用"这一句死结论,看不见"已换成自动化排期"这个事实)。旧函数保留改名 `_triggers_gw` 作来历留档 |
|
||
| 6 | 同上 | `nm` 由「唤醒」→「**唤醒定时任务**」(用户 10-01 定的名)。⚠️ **属项目侧**,⛔ 未写进技能 |
|
||
|
||
### 三🔴 过程中抓到一个**假绿**(本线同族红线再现)
|
||
|
||
第一版判据只按 `name` 含「唤醒」捞 ⇒ 捞出 **11 条**,其中 **8 条是一次性派棒**
|
||
(如「接续 · 机制线复核(**唤醒**回路实战)」「主控 · 机制线(查**唤醒**为什么断)」)⇒ 它们 `status=ACTIVE`
|
||
⇒ 判成 `running` =**假绿**。
|
||
🔴 **收紧:必须限定 `schedule_type='recurring'`** ——「定时任务」的本质是"到点自己响"。
|
||
⇒ 捞到 **3 条真·周期排期**,**全部 PAUSED**:
|
||
`1eaf45c3` 协同监管·心跳(已过期)/`16bec5ce` `[协作]-唤醒机制-唤醒轮B`/`caca9a89` `[协作]-唤醒机制-唤醒轮A`。
|
||
⇒ 真状态= **`paused`(登记着但没启用,且 `last_run_at` 全为空=从未跑过)**。
|
||
⚠️ 判据与坑一并写进 `SKILL.md §0.5.5`。
|
||
|
||
### 四、回答 ③④(**不是重复;「唤醒机制」也不是协作会话**)
|
||
|
||
- **③** 左边六边形「唤醒定时任务」=**一个零件**(时间驱动叫醒器);下方「唤醒机制」=**任务类别**(分工板一格)⇒ **层级不同,不是重复**。
|
||
**看着像重复**的两个原因:① 名字都含「唤醒」;② **本工作区只有 1 个类别** ⇒ 分工板只剩一格,与主会话那格信息重合。
|
||
- **④** **不是**。「唤醒机制」是**任务类别名**(第 2 层,`goal.json.topics`);协作会话是**主会话派活时创建的干活会话**(第 4 层,`[协作]-<类别>-<具体>`)。
|
||
🔴 **实证**:本工作区**一条协作会话都没有** —— 现有 2 条是 `a202550c`(主控 · 唤醒机制线)+ `fe146dd9`(接续 · 机制线)。
|
||
|
||
### 五、验证读数
|
||
|
||
| 项 | 结果 |
|
||
|---|---|
|
||
| `board_ext.py` 语法 | `py_compile` 通过 |
|
||
| 技能自测 `selftest.py` | **PASS 32 / FAIL 0** |
|
||
| 渲染断言(新增 5 条:六边形/光晕/徽章/徽章↔state/图外说明) | **23 / 23** |
|
||
| 几何自检(新增 2 条:六边形与徽章**在层间空白带**、⛔ 不压上下两层) | **5 / 5** |
|
||
| 实时看板 | `/` **200 / 63,306 B**;`/board.json` **200 / 9,322 B**;`trigger.state = paused` |
|
||
| 六边形实测坐标 | body `x150–386 / y176–254`;halo `x145–391 / y171–259`(⛔ 未越出第二层底 260);徽章 `y156–166`(在 R1 底 116 与六边形顶之间) |
|
||
|
||
### 六、顺带:`MEMORY.md` 超限清理(这一轮被系统要求)
|
||
|
||
- 原件已存档 ⇒ **`MEMORY-全文-20261001.md`**
|
||
- **10,005 → 7,787 字符**(净减 2,218)。手法=**去重**:承载两分/形态元假设/插件数据落点/其他线品牌/手机接入/配置外置线/覆盖网络线 等**设计口径**原文
|
||
已逐字在 **`MEMORY-设计口径-20261001.md`** ⇒ 主文件压成**一句判据 + 一行指针**(⛔ 不是删信息,是不重复存两份)。
|
||
|
||
---
|
||
|
||
## 01:0x · 第八条:分工格「跟主会话一个 ID」—— 查出并修掉一处**自相矛盾**
|
||
|
||
用户原话:「**那你的唤醒机制 是啥意思嘛 跟主会话都一个 ID,难道是主会话?**」
|
||
|
||
### 一、答:不是主会话,是**任务类别名**;同 ID = **同一条会话出现在两个栏位**
|
||
|
||
分工板一格 = **一个任务类别**(`goal.topics`)。那格里的行各有角色:
|
||
|
||
| 行 | 是什么 | 来源 |
|
||
|---|---|---|
|
||
| ① 类别名 | 这一类活叫什么 | `labor[].name` ← `goal.topics` |
|
||
| ② `主会话 <id8>` | 这一类的件**投给谁** | `project.main_by_topic[类别]` = `{唤醒机制: a202550c}`(`source=prefix:主控`) |
|
||
| ③ 最近的事 | 台账最近那件(无件 ⇒ 回落该类最近会话) | `labor[].latest` |
|
||
| ④ `承接 <id8>` | 现在**谁在跑棒** | `labor[].running[0]` |
|
||
| ⑤ 件 N · 完成 M | 台账汇总 | — |
|
||
|
||
⇒ ②④ 都是 `a202550c` = **同一会话的两个身份**(它既是该类主会话,又正在跑棒)。
|
||
本工作区**只有 1 个类别**,那条会话标题正好是 `主控 · 唤醒机制线 · …` ⇒ 天然同形。
|
||
|
||
### 二、🔴 顺带查出一处真缺陷(=用户困惑的直接来源)
|
||
|
||
`board.py::_labor()` 的"台账无件 ⇒ 回落该类最近会话"分支带着 `and str(s.get("role")) != "主会话"`。
|
||
本类唯一会话 `a202550c` 的 `role` 正是「主会话」⇒ 被排除 ⇒ `latest=null`;
|
||
而同一会话在 ④ 又被 `_tp == ln` 收下 ⇒ **同一格 ③ 说"无可归到它的会话"、④ 说"承接 a202550c"** = 自相矛盾。
|
||
⇒ 修:候选**不排除任何角色**,排序 `working` 优先("正在跑的"就是"最近在做的")。
|
||
|
||
### 三、文案修改(消歧义)
|
||
|
||
| 行 | 改前 | 改后 |
|
||
|---|---|---|
|
||
| ② | `主会话 a202550c` | `收件主会话 a202550c` |
|
||
| ③ | `(该类还没有记录,也无可归到它的会话)` | `本类就这一条会话(兼收件与承接)`(回落会话=④承接者时) |
|
||
|
||
实测渲染(第三层):
|
||
```
|
||
唤醒机制
|
||
收件主会话 a202550c
|
||
本类就这一条会话(兼收件与承接)
|
||
承接 a202550c · 主控 · 唤醒机制线 · 棒:worker 退出后…
|
||
件 0 · 完成 0
|
||
```
|
||
|
||
### 四、验证读数
|
||
|
||
| 项 | 结果 |
|
||
|---|---|
|
||
| 技能自测 `selftest.py` | **PASS 32 / FAIL 0** |
|
||
| 渲染断言(新增 2 条) | **25 / 25** |
|
||
| 几何自检 | **5 / 5** |
|
||
| 看板重启 | `✓ 接管:已停旧看板 PID 83680`;`/` **200 / 64,255 B**;`/board.json` **200 / 9,485 B**(`z5gxyX` 报 failed 即被接管的旧实例,属预期) |
|
||
| 新文案在线校验 | 首页含「收件主会话」「本类就这一条会话」✅ |
|
||
|
||
⚠️ 断言第一次写错:把 `kind='legacy'` 的行也数成"类别格"(应 3 实 1)⇒ 按渲染侧同款判据
|
||
`kind !== 'legacy'` 修正。**教训:断言的分母必须和渲染用的是同一条判据。**
|
||
|
||
### 五、⚠️ 仍存疑(未擅自改):类别名的**来源**
|
||
|
||
`project.topics_source` 现写 `kind: declared`,但 `by` =「**本线会话标题里的类别名**
|
||
(`主控 · 唤醒机制线 · …`)」、`goal_declared_at` 为空 ⇒ 这个「唤醒机制」是**我据会话标题反推的**,
|
||
⛔ 不是用户**在对话里说明**的(用户原话:「目标是 **通过对话**在调用 会话协作skill时说明的,**不是固定的**」)。
|
||
⇒ **建议请用户定名**(`candidates` 里还躺着 `文档库治理/规则载体/IM`)。**未动,等用户点头。**
|
||
|
||
---
|
||
|
||
## 01:1x · 第九条:第三层改成「只放协作会话」+ 类别搬进主会话框
|
||
|
||
用户原话(分三轮给的):
|
||
1. 「**主会话 下面 那一排只放协作会话**,横着排 有几个放几个,当前没有对应协作会话 就空着。
|
||
**唤醒机制如果是主会话的 事就放到主会话框里去**」
|
||
2. (空态文案)「**放一个空的框 说明 暂无协作会话**」
|
||
3. (文案精简)原「这一排只放协作会话(主会话派活时才创建)· 有则逐个排在框里」⇒ 改为「**主会话派活时创建**」
|
||
|
||
### 改了什么
|
||
|
||
| # | 位置 | 改动 |
|
||
|---|---|---|
|
||
| ① | `board.html` 第三层 | `cells` 从"任务类别"(`labor` 里 `kind!=='legacy'`)改成 **协作会话**(`sessions` 里 `role !== '主会话'`);宽度 `clamp(150,340,可用/n)` ⇒ **有几个排几个** |
|
||
| ② | 同上 · 空态 | **画虚线空框**:`暂无协作会话` + `主会话派活时创建`(⛔ 不许因为"没内容"就省略整层) |
|
||
| ③ | `board.html` R2 主会话框 | 高 84→112,多一行「**负责类别:…**」(按 `main_by_topic` **反查**;查不到写「未在对话里说明」) |
|
||
| ④ | 图外说明 | 台账「未归类」不再占第三层 ⇒ 改由文字摊开说(几条线 · 共几件 · 完成几件) |
|
||
| ⑤ | `aria-label`/`archHint`/形状图例 | 「分工」⇒「协作会话」 |
|
||
| ⑥ | `SKILL.md` | §0.5.6 整体重写(含**三次换语义**对照表)+ §0.5.2 加作废状态块;版本 1.1.6 → **1.1.7** |
|
||
|
||
🔴 **这一排已换过三次语义**:按线(≤09-30 上午)→ 按任务类别(09-30 晚)→ **按协作会话(现行)**。
|
||
⛔ 旧结论没删,只加状态块指过来(本线文档纪律)。
|
||
|
||
### 🔴 本轮新增的方法:**正例快照必须造**
|
||
|
||
真实工作区当前 **0 条协作会话** ⇒ 只跑默认快照的话,「有几个放几个」那条分支**从未被执行过**(=未验证)。
|
||
⇒ 造一份把 `sessions` 追加 3 条 `role='协作会话'` 的快照(含一条 `topic` 为空),
|
||
`node tmp/render-check.mjs tmp/board-verify-ws.json` 再跑一遍。
|
||
**当场抓出一个拼接 bug**:`fmtAge()` 返回值自带"前"字 ⇒ 拼成过「4 分钟前**前**有活动」。
|
||
|
||
### 验证读数
|
||
|
||
| 项 | 结果 |
|
||
|---|---|
|
||
| 技能自测 | **PASS 32 / 0** |
|
||
| 渲染断言(空态) | **26 / 26** |
|
||
| 渲染断言(正例 3 条协作会话) | **26 / 26** |
|
||
| 几何自检 | **5 / 5**(层高表 `rows` 的 R2 已同步 84→112,⛔ 不同步=假绿) |
|
||
| 线上看板 | `/` **200 / 63,900 B**;`html_sig` 已变 ⇒ 浏览器自动重载;「收件主会话」旧渲染**零残留** |
|
||
|
||
正例实际渲染(x=120/484/848 三格横排):
|
||
|
||
```
|
||
[协作]-[唤醒机制]-钩子锚点取证 · 第一轮 执行中 · ws00000 类别:唤醒机制 4 分钟前有活动
|
||
[文档库治理]-[索引重建]-跑一轮 执行中 · ws00001 类别:文档库治理 37 分钟前有活动
|
||
[IM]-[连接层]-Centrifugo 试点 已完成 · ws00002 (标题里没读类别前缀) 2 小时前有活动
|
||
```
|
||
|
||
⚠️ **仍然未动的一件事**(延续上一条):类别名「唤醒机制」至今是**我从会话标题反推的**,⛔ 不是用户在
|
||
对话里说明的 —— 现在它已搬进主会话框显示为「负责类别:唤醒机制」。等用户定名。
|
||
|
||
---
|
||
|
||
## 01:0x · 第十条:按架构图讲一遍流程 + 逐条校验(查出 2 处图与实际不符)
|
||
|
||
用户:「根据协作架构图 把协作流程详细说一遍,看看是否正确,包括协作机制在中间起到什么作用」
|
||
|
||
**交付**:两张图(① 协作流程六步 + 中间层谁在驱动 ② 六条校验结果)。校验全部用**实测读数**
|
||
(线上 `/board.json` 的 `runtime.*`/`front.*` + `tmp/supervise-inbox/` 三个信号文件)。
|
||
|
||
### 🔴 查出 2 处**图与实际不符**(待用户定,⛔ 未擅自改)
|
||
|
||
| # | 问题 | 实测 |
|
||
|---|---|---|
|
||
| 1 | 架构图**缺「自动化排期」这一环** —— ① 那条线从主会话**直连**协作会话 | 实际派活**必须经宿主排期**(⛔ 钩子开不了新会话,`SKILL.md §1` 硬边界)。图上只在标签里写了"自动化排期"四个字,**没有节点** |
|
||
| 2 | **空态时 ①/② 两条通道在图上看不见** | 没协作会话时 `cx` 为空 ⇒ ①② 两条走线带都不画,**② 的标签甚至没画**(只在 else 分支里)。空态读不出主线 |
|
||
|
||
⇒ 两条都要**改图结构**(加节点/空态也画虚线带)。**已列给用户定,⛔ 没擅自改**
|
||
(他刚连发三条指令精调这一排 ⇒ 这张图的形态他自己很在意)。
|
||
|
||
### ✅ 已修 2 处(文案类,低风险)
|
||
|
||
| # | 问题 | 修法 |
|
||
|---|---|---|
|
||
| 3 | **假绿式文案**:`board_ext.py` 写「补这个缺口的正是…那格「**心跳**」」—— ① 名字早已定名「唤醒定时任务」;② 写这话时 **3 条周期排期全 PAUSED、`last_run_at` 全空** ⇒ 宣称一个**不成立的能力**(同日 00:59 `STALL.md`:「5.3 小时无成果 + 未来 1 小时零排期 ⇒ **确定性静默**」) | 改成**先查排期真实状态再说**(三态:有 ACTIVE / 全 PAUSED / 读不到,⛔ 不合并),全停时 tone 转 `warn` 并明说「**缺口眼下是敞开的**」 |
|
||
| 4 | 「协作程序」格只写「**在线**」⇒ 会被读成常驻服务 | 实测 `runtime.prog.by = "钩子 --tick"` ⇒ 它是**按需唤起**的。格内小字补成「需求台账 / 队列 · 只维护不上报 · **按需唤起**」 |
|
||
|
||
### ℹ️ 2 处遗留 / 存疑(未动)
|
||
|
||
- `runtime.deliver.stopped = true`(旧守护留下的 `guard.stop` 文件)与 `label="就绪"` 并存 ——
|
||
界面只用 `up`,**无碍**;但这个字段将来会误导读数的人(判据在 `board.py:625`)。
|
||
- `progress.main_busy = false`,而图上主会话显示「执行中」(来自会话表 `status='working'`)
|
||
—— **两套读数打架**,未定位谁对(`main_busy` 来自使用方 probe,不是技能侧算的)。
|
||
|
||
### 🔴 机制在中间起什么作用(本轮结论 · 用实物佐证)
|
||
|
||
> **中间那三个框(协作程序/上报/Hook进程)不替你干活,它们干三件事**:
|
||
> ① **维护唯一权威**(协作程序 = 需求台账四态队列)
|
||
> ② **把状态送到主会话眼前**(上报 = 投递唯一出口)
|
||
> ③ **在没人动的时候把"该谁做"喊出来**(`STALL.md`/`NEXT.md`/`TO-MAIN.md` 三个信号文件就是实物)
|
||
>
|
||
> 🔴 **边界(重要)**:`STALL.md` 自己就写着「该谁做(**本程序⛔ 做不到**):主会话抢域锁 → 读摘要 →
|
||
> 按缺口派下一棒」—— 机制**开不了新会话**,所以它只能"喊";喊完还得**主会话**来派。
|
||
|
||
### 验证读数
|
||
|
||
自测 **32/32** · 渲染断言 **26/26** · 几何 **5/5** · 看板重启 `✓ 接管:已停旧看板 PID 85388`
|
||
(`cDsf3N failed` 又是被接管的旧实例,属预期)· 新文案已在线。
|
||
|
||
---
|
||
|
||
## 01:0x · 第十一条:**我上面第 1 条判断被用户当场纠正**
|
||
|
||
用户原话:「**3 自动化排期开新会话:这个不就是主会话创建和派活吗 也是自动任务的方式**」
|
||
|
||
⇒ **他对,我错**。上一轮我把「自动化排期」当成"架构图上缺的一环"报上去,是把**动作**当成了**角色**:
|
||
|
||
| | 架构图上的节点 | 「自动化排期」 |
|
||
|---|---|---|
|
||
| 是什么 | **角色**(用户/主会话/协作会话/程序/宿主) | **主会话干的一个动作**(建一条排期) |
|
||
| 该画在哪 | 节点框 | **连线标签**(`① 派活 · 自动化排期`)就够 ✅ |
|
||
| 单开节点? | — | ⛔ **不该** |
|
||
|
||
🔴 **判据(已写进 `SKILL.md §0.5.6` 防回潮)**:**角色才配节点,动作只配标签/说明。**
|
||
|
||
⚠️ 但有一条**保留**:「它是**开新会话的唯一通道**(钩子开不了会话)」这条硬约束**光看线看不出来**
|
||
⇒ 加进**图外说明**一句,⛔ 不动图结构(用户对图形态很在意,这轮他连发三条精调过)。
|
||
|
||
### 顺带补的空态遗漏
|
||
|
||
`② 上报 · --report` 的标签原来只在 `else` 分支里画 ⇒ **一条协作会话都没有时,图上只剩 ①**,
|
||
看不出还有 ② 这条回程。⇒ 空态也画上(⛔ 空态 ≠ 这两条通道不存在)。
|
||
|
||
### 验证
|
||
|
||
渲染断言 **26/26**(空态 + 正例)· 几何 **5/5** · 实测空态下 ①② 两个标签都在 ✅ ·
|
||
`SKILL.md` 1.1.7 → **1.1.8**。
|
||
|
||
---
|
||
|
||
## 01:0x · 第十二条:唤醒在流程里的位置(用户问"流程图缺唤醒")
|
||
|
||
用户:「**流程图 缺少 唤醒任务的事呢,流程怎样处理的**」
|
||
|
||
### 答(取证来源:那 3 条排期的 `prompt` 原文 + `.workbuddy/collab/wake-session.py`)
|
||
|
||
🔴 **唤醒不在主链上,它是旁路** —— 主链(用户发话 → 主会话派活 → 排期开棒 → 协作会话干 →
|
||
台账 → 上报回主会话)**每一步都靠"有东西在动"触发**(Hook 只在有会话活动时才被喊);
|
||
唤醒是**时间驱动**,专补"谁都没动"的时刻。
|
||
|
||
**四步路径**:
|
||
|
||
| 步 | 做什么 | 取证 |
|
||
|---|---|---|
|
||
| ① | 排期到点(每小时)⇒ 宿主开一个「**唤醒轮**」会话 | 排期 prompt 自称:「**本排期只是「闹钟」,不是干活的人**」 |
|
||
| ② | 唤醒轮让**协作程序**判条件是否**同时**成立 | `TO-MAIN.md`:「主会话未在处理 · 队列无待反馈 · 需求仍未完成」 |
|
||
| ③ | 满足 ⇒ 经**本机网关**把话投给主会话 | `wake-session.py`:单次投递、不重试、fail-closed、不夺会话、不监听端口 |
|
||
| ④ | 主会话被叫起 ⇒ **回到主链第 2 步**(判断与派活) | 排期 prompt:「自动任务是通过**协作程序**去唤醒**主会话**,这样流程统一」(用户 09-30 定) |
|
||
|
||
⚠️ **一个易混点**:「唤醒轮」**本身也是一个会话**,由排期开出来 ⇒ 它走的就是架构图上
|
||
「自动化排期 ⇒ 开新会话」那条路;区别只在**派谁**(派棒干活 vs 派个去叫醒的)。
|
||
⇒ 这也是"**自动化排期是动作不是角色**"那条判据的又一个例证。
|
||
|
||
⚠️ **当前整条旁路是断的**:3 条周期排期全 PAUSED 且 `last_run_at` 全空(从未触发)
|
||
⇒ 这正是 `STALL.md` 说的「5.3 小时无成果 + 未来 1 小时零排期 ⇒ **确定性静默**」。
|
||
|
||
**本轮⛔ 未改代码**(纯解释 + 一张旁路图)。
|
||
|
||
---
|
||
|
||
## 01:0x · 第十三条:定时任务能否"不开新会话、复用一个唤醒会话"(选型)
|
||
|
||
用户:「**定时任务 能否 不开新会话 复用一个唤醒会话**」(按 `dsh-decision`:**技术实现自己定,只把红线单独上抛**)
|
||
|
||
### 取证(三条硬事实)
|
||
|
||
| # | 事实 | 来源 |
|
||
|---|---|---|
|
||
| 1 | 🔴 **排期不可能投给已有会话** | `automations` 表 31 个列里**没有任何"目标会话"字段**(只有 `cwds` 工作目录 + `prompt`)⇒ 它的能力边界就是"**到点开一个新会话**" |
|
||
| 2 | **"投给已有会话"是本机的另一条路** | `wake-session.py`:程序 → 本机网关 → `POST /api/v1/sessions/{id}/reply`;⛔ 排期走不了这条路(所以才另写了个程序) |
|
||
| 3 | ✅ **"自己叫自己"已实测通过** | `tmp/_pulse_state.txt`:`pulse 60s fired 13:09:12` / `13:10:31`(`wake-pulse.sh`,09-30) |
|
||
|
||
### 结论
|
||
|
||
- **排期那一层:❌ 做不到** —— 它没有"投给哪个已有会话"这个能力。
|
||
- **"复用一个唤醒会话"本身:✅ 能做** —— **换掉节拍器**:不用排期,改用**会话自建的一次性静默脉冲**
|
||
(`wake-pulse.sh N`):静默 sleep → 进程退出 → 宿主往**本会话**投完成通知 → **本会话开新一轮**
|
||
⇒ **零新增会话**,被唤醒的始终是同一条。
|
||
|
||
### 代价三条(⛔ 如实说,不粉饰)
|
||
|
||
1. 🔴 **会话被关掉就断** —— 节拍从"**宿主持有**"变成"**会话自己持有**"。这是最关键的一条:
|
||
排期是**唯一**"会话被单独关掉、照样能响"的路径。
|
||
2. 间隔越短越频繁 ⇒ **token 比每小时一轮更贵**(每轮都是真 AI 轮次)。
|
||
3. 🔴 **该会话的 idle 钩子会被持续压制**(只要脉冲任务在 `pending`/`running`)—— 会影响一切依赖 idle 钩子的机制(MEMORY 有实测记录)。
|
||
|
||
### 建议(技术实现,AI 自定)
|
||
|
||
**主节拍换成脉冲**(满足"不堆会话")+ **保留一条低频排期做兜底**(防会话被关导致全断)。
|
||
|
||
🔴 **只有一条要用户点头("扩大可见面",属红线,按规矩单独问)**:
|
||
要不要**新建一个常驻唤醒会话**、并把现在每小时那两条唤醒轮排期**撤掉或降频**。
|
||
|
||
### 用户处置(同轮)
|
||
|
||
**跳过了这个问题,未作答** ⇒ 按"**没点头就不动**"处理:⛔ **不新建常驻会话、⛔ 不动那两条排期**,现状保持。
|
||
(新建常驻会话=占资源 + 改生产形态,⛔ 不是可以自己拍的技术实现 ⇒ 缺点头就维持原状。)
|
||
|
||
### 顺带:`MEMORY.md` 又被系统判超限 ⇒ 二次精简(同轮)
|
||
|
||
- **7,797 → 7,608 字符**(上限标注同步改 ≈7,650);行数与结构不变。
|
||
- 🔴 **顺手修正了一条已过期的结论**:「协作·唤醒线」那行原文写"主会话下方=**任务类别**" —— 那是 09-30 的旧语义,
|
||
**10-01 已第三次换语义为「协作会话」** ⇒ 已改正,并补上「**排期只能开新会话**(表里无目标会话字段)」这条边界。
|
||
- ⚠️ 教训:MEMORY 里的**状态行会随语义变更而过期** ⇒ 压缩时⛔ 不能只做字面删减,**必须顺带核对是否已过时**。
|
||
|
||
## 01:2x · 第十四条:用户两条批注 —— **唤醒三条件收窄 + 投递路径纠正**
|
||
|
||
用户两条都是「**描述现状 → `'''` 里给规范**」的形式:
|
||
|
||
> ①「是主会话 和 协作会话都空闲,协作程序没有待处理队列,需要唤醒的情况:需求未完成(唤醒任务继续)/
|
||
> 需求有阻碍(唤醒任务暂停)不需要唤醒的情况:需求已完成(唤醒任务暂停)」+
|
||
> 「**唤醒轮让协作程序判条件是否同时成立** 主会话空闲 · 队列没有待反馈 · 需求仍未完成(三者都满足才叫)」
|
||
> ②「满足 ⇒ 把话投给主会话(走本机网关,单次投递、不重试)…」+
|
||
> 「**这个应该是 提交到 协作程序 队列中,上报给主会话处理**」
|
||
|
||
### 核对结果(与 `collabd.py` 现状逐条比)
|
||
|
||
| 用户口径 | 实现现状 | 判 |
|
||
|---|---|---|
|
||
| (a) 主会话**和协作会话**都空闲 | `main_busy = (_main_state(st)=="working")` ⇒ **只看主会话** | ❌ **缺一半** |
|
||
| (b) 队列没有待反馈 | `no_fb = (not aw) and (not pend)`(第 2054 行) | ✅ |
|
||
| (c) 需求仍未完成 | `goal_open = goals_open()`(第 2055 行) | ✅ |
|
||
| 需求**有阻碍 ⇒ 暂停** | 无 blocked 分支(`goals_open` 只判"有没有非 done 节点") | ❌ **相反** |
|
||
| 需求已完成 ⇒ 暂停 | 隐含(`goal_open` 为假即不叫) | ✅ |
|
||
| 投递**进队列**、走上报流程 | 心跳**绕开队列**,直接 `_deliver_str(txt,"上报·唤醒",st)`(第 2103 行) | ❌ **相反** |
|
||
|
||
🔴 **另一处独立发现(同族 · 同一事实两处打架)**:`board_ext.py` 里同一条判据两种写法 ——
|
||
`_last_wakeup` 写「主会话空闲 + 无待反馈 + 需求未完成」;`_triggers_gw` 的 tip 写
|
||
「主会话闲着 + **没有待处理的事** + 没有待反馈的」(**漏了"需求未完成"**)。
|
||
|
||
### 本轮做了什么
|
||
|
||
- ✅ **`board_ext.py` 三处文案统一**到用户口径(`_last_wakeup` docstring / `_triggers_gw` tip /
|
||
并在 `_triggers()` 的 tips 里**新增**一条「它什么时候才出声」:三条件 + 有阻碍/已完成⇒暂停)。
|
||
- ✅ **`SKILL.md` 新增 §1.4a**:两条口径钉成表(含"现状 ❌"对照),⛔ 不改正文结论。
|
||
- ⏸ **`collabd.py` 未改**(机制层,两处待改):**锁脚本已找不到**(`preflight-lock.sh` 只剩
|
||
`归档/…/*.bak`,工作区 `scripts/` 已于 09-24 撤除)⇒ 按"⛔ 无锁不动机制层"挂待办。
|
||
|
||
### 待用户定(③ 的岔路 —— ⛔ 不是技术实现层面能自己拍的)
|
||
|
||
「心跳进队列」有三种落地,语义不同:
|
||
① 写进**需求台账**(固定 id,随需求翻转)⇒ 借用上报流程;
|
||
② 只把内容写进**队列投影文件**,投递时仍发一句"队列有变化";
|
||
③ 只把投递 `kind` 统一成「上报」,流程不变(改动最小)。
|
||
⚠️ **风险**:① 若主会话忘了标 `done` ⇒ 队列永远非空 ⇒ `no_fb` 永假 ⇒ **心跳永久停摆**(回到"僵死无人叫")。
|
||
|
||
## 01:15 · 第十五条:脉冲复用方案(20 分钟 + 单条会话)满不满足
|
||
|
||
用户:「脉冲 · 复用同一条会话:用这个方案是否可以设置唤醒间隔 20M,只需要复用唤醒会话单独执行就满足要求了」
|
||
|
||
**答:可以**(`wake-pulse.sh` 默认 `300` 秒,改 `1200` 即可;`sleep` + 退出 + 留痕,机制上没有拦路的东西),
|
||
**但有一个自指死结必须先解**:
|
||
|
||
🔴 **死结**:三条件第一条要求「主会话和协作会话**都空闲**」,而脉冲唤醒的正是**这条会话自己**
|
||
⇒ 它醒来那一刻自己就在 `working` ⇒ **自己把自己顶掉** ⇒ 条件①永不成立。
|
||
⇒ 两个解法:① 判条件时**排除自己**;② 这条会话醒来**直接干活、不判条件**(=它自己就是那个"主会话")。
|
||
|
||
✅ **顺带解决了第十四条的 ③**:若这条会话本身就是干活的人 ⇒ **不需要"投递给主会话"这一步**
|
||
⇒ 也就不存在"投递要不要进队列"的问题 —— 这是本方案最大的好处。
|
||
|
||
**代价三条**(同前,⛔ 不粉饰):会话被关 ⇒ 整链断|任务 `pending` 期间**该会话 idle 钩子被压制**|
|
||
⚠️ **20 分钟没实测**(只验过 60 s;脚本默认 300 s)⇒ 待验。
|
||
**成本**:20 分钟 ⇒ **一天 72 轮**真 AI 轮次(每小时是 24 轮)。
|
||
**建议**:间隔⛔ 别写死,**每次醒来自己决定睡多久**(需求在推进 ⇒ 睡短;刚派完活 ⇒ 睡长)。
|
||
|
||
## 01:19 · 第十六条:用户给解法 —— **把自己排除 + 判据交给代码**
|
||
|
||
用户:「**把自己排除不就行了,通过会话名称前缀区分**,还有**也不用他自己判断,可以通过代码判断**」
|
||
|
||
⇒ 采纳(=我上一轮提的「破法①」,但**必须由代码判**;⛔ 不是「干脆别判」那条)。
|
||
|
||
### 落地(`collabd.py` 五处)
|
||
|
||
| # | 改动 |
|
||
|---|---|
|
||
| 1 | `parse_session_name()` 加第三类角色 `"唤醒": "waker"` |
|
||
| 2 | 新增 `WAKER_PREFIX = "[唤醒]"` + `_busy_sessions()` + `_anyone_busy()` |
|
||
| 3 | 条件 (a) 收窄:`main_busy = (_main_state(st)=="working") or _anyone_busy(_busy_sessions())` |
|
||
| 4 | 条件 (c) 加 `goal_stuck`(只剩 `blocked` 项、无 `pending`/`running` ⇒ 不叫)+ 进 `info["probe"]` |
|
||
| 5 | 心跳正文三条件表述更新(⛔ 不含唤醒会话自己) |
|
||
|
||
### 排除自己 = **两道互补,都要**
|
||
|
||
1. `SELF_SID`(`CODEBUDDY_SESSION_ID`)—— **精确**:程序跑在会话里时=自己;
|
||
2. 名字前缀 `[唤醒]` —— 程序在会话外常驻时**拿不到** `SELF_SID`,只能认名字。
|
||
|
||
### 🔴 实测(当场复现死结)
|
||
|
||
`py_compile` OK;`_anyone_busy` 4 例全过;跑真库查出**此刻唯一在跑的会话就是本会话**
|
||
(id = `SELF_SID` = `a202550c…`;标题 `主控 · 唤醒机制线 · 棒:worker 退出后能否被外部叫起`)
|
||
⇒ **印证**:不排除自己 ⇒ 它自己就把条件①否掉。
|
||
|
||
⚠️ **顺带暴露一个命名问题**:该会话名是 `主控 · 唤醒机制线 · 棒:…`(**中点分隔、无方括号**)
|
||
⇒ `parse_session_name()` **解析不出来**(`role=""`, `ok=False`)⇒ 现役会话**大面积不符**
|
||
`[角色]-[类别]-<具体>` 铁律 ⇒ **光靠名字判会漏**,`SELF_SID` 那道**不能省**。**已提给用户**。
|
||
|
||
## 01:22 · 第十七条:用户定「**现在不是唤醒定时任务了**」⇒ 排查需要更新的地方
|
||
|
||
用户:「所以看板的也要更新 ,现在不是唤醒定时任务了,看看还有其他需要更新的地方吗」
|
||
|
||
⚠️ **前提变了**:唤醒的实现从「**排期(`automations` 定时任务,每小时开新会话)**」
|
||
换成「**脉冲(会话自建一次性静默任务,复用同一条唤醒会话)**」⇒ 看板那格的**名字/数据源/文案全要跟**。
|
||
|
||
### 排查清单(20 处 · 按"能不能单独改"分组)
|
||
|
||
| 层 | 处 | 位置 |
|
||
|---|---|---|
|
||
| **① 只是改字** | 看板大标题 `"name"` | `board_ext.py:398` |
|
||
| | 图外说明(用户可见) | `board.html:776`、`board_ext.py:553/555` |
|
||
| | 无障碍标签 | `board.html:295` |
|
||
| | 各处注释 | `board.html:228/514/579`、`board_ext.py:220/322/373`、`SKILL.md:329/336` |
|
||
| | MEMORY 状态行 | `MEMORY.md` 协作·唤醒线 |
|
||
| **② 必须和名字同改** | 🔴 数据源(现读 `automations` 排期) | `board_ext.py:278 _wakeup_autos()` |
|
||
| | 🔴 投递台账读数(`wakeups.jsonl`) | `board_ext.py:91 _last_wakeup()` |
|
||
| | tips 里"周期唤醒排期 N 条" | `board_ext.py:373` 起 |
|
||
| **③ 要用户先拍板** | 新名字叫什么(建议「**唤醒脉冲**」) | — |
|
||
| | 那两条 `[协作]-唤醒机制-唤醒轮A/B` 排期**撤不撤**(现 PAUSED) | `automations` |
|
||
| | 投递程序 `wake-session.py` 还留不留(唤醒会话自己干活就不需要) | `.workbuddy/collab/` |
|
||
| | 外围件:`goalctl.py`(整篇围绕"唤醒轮")/`advance-watch.py` | `.workbuddy/collab/`、`tools/` |
|
||
|
||
### 🔴 为什么本轮**没直接改**
|
||
|
||
**名字和数据源必须一起改** —— 只改名字、读数还看 `automations` ⇒ **名不副实**(本线最痛的假绿)。
|
||
新名字要用户定 ⇒ 先交清单,等一句口径,一次改完(⛔ 不先改一半再返工)。
|
||
|
||
### 补 · 用户定性:「**唤醒脉冲会话,本质还是会话**」(01:24)
|
||
|
||
⇒ 那格**⛔ 不是"调度配置",是"一条会话"** ⇒ 三件事跟着变:
|
||
**形状**(六边形 → 会话同族的圆角矩形)/**读数**(不读排期表,读**这条会话自己的状态**)/
|
||
**归属**(它得有个位置)。
|
||
|
||
⚠️ **由此产生一个待用户解的矛盾**:用户 2026-10-01 早先特意要求「给唤醒定时任务换个样式,
|
||
和协作会话**区分开**」(所以才画六边形);现在它本质是会话 ⇒ 用回圆角矩形就**没有区分度**了。
|
||
**我的建议**:靠**位置**区分(仍单独贴主会话左边,⛔ 不进"协作会话那一排"——那排是"派活时创建的干活会话"),
|
||
形状同族(圆角矩形),可选**虚边**表示"它靠脉冲被叫醒"。
|
||
|
||
⚠️ **读数的诚实边界**:脉冲是**会话自建的后台任务** ⇒ 「**下次几点到点**」程序**读不到**(无公开接口),
|
||
只能给「**上次真的响过是什么时候**」(脚本留痕 `tmp/_pulse_state.txt`)⇒ ⛔ 不许编一个"下次到点"。
|
||
|
||
---
|
||
|
||
## 八、(本轮)唤醒脉冲会话:断言同步 + 技能侧收口(2026-10-01 02:3x)
|
||
|
||
**用户本轮两条口径**:「**唤醒脉冲会话,本质还是会话**」+「**唤醒脉冲会话,位置不变**」。
|
||
|
||
> ⚠️ **锁的事先说明**:`preflight-lock.sh` 在本机**只剩 `归档/…/*.bak`**(工作区 `scripts/` 09-24 已撤)
|
||
> ⇒ 开工第 0 步**没法跑**。本轮**未动机制层**(`collabd.py` 一行没改),
|
||
> 只改了**渲染层 + 验证层 + 技能文档** ⇒ 按「⛔ 无锁不动机制层」未被触发。
|
||
|
||
### 8.1 本轮做完的
|
||
|
||
- **两条过时断言同步到新形状**(形状改了、断言没改 ⇒ **假红**):
|
||
`tmp/render-check.mjs` 里「触发源画成**六边形**」⇒ 改成「**会话同族的圆角矩形**(`trig-node`)」;
|
||
图外说明那条改判「它是**一条会话**、⛔ 不进下方那一排」。
|
||
- **消掉一条假绿**:`tmp/arch-geom-check.mjs` 原来查 `<path class="trig-hex">` ——
|
||
形状改回 `<rect>` 后**恒不命中 ⇒ 断言恒真**(改了形状却"看起来还是绿的")⇒ 换成**真几何判定**:
|
||
① 本体与光晕**成对**;② 本体顶边 **≡ 主会话那行的顶**(=用户「**位置不变**」的**回归闸**);③ 不压上下两层。
|
||
- **新增 2 条静态断言**(`?` 帮助文本,它不在任何渲染片段里 ⇒ 只能查源文件):
|
||
⛔ 不再说「第三层按**任务类别**分工」(09-30 旧语义);✅ 说清「唤醒脉冲会话**本身就是一条会话**」。
|
||
- **修 `?` 帮助文本**:原写「第三层按任务类别分工,一格一个类别…」⇒ 改为现行三层语义
|
||
(左边=唤醒脉冲会话/主会话框写负责类别/下方那排=协作会话)。
|
||
- **技能侧收口**:重写 `SKILL.md §0.5.5`(形状改过两次对照表 + 读数源变更 + 诚实边界 +
|
||
"改形状必须同步断言/恒真断言=假绿"这条教训);`last_change` 换新、版本 **1.1.8 → 1.1.9**
|
||
(并裁掉最旧一条 changelog 控体积);`references/architecture.md` 补**第三类角色**
|
||
`[唤醒]-<类别>-<具体>`(`role='waker'`)+ 自指死结与**两道排除**;§7.3 加状态块说明
|
||
**看板读数已从 `automations` 切到 `sessions`**。
|
||
- **`board.html` 两处过时注释**:CSS 注释仍写「① 形状=**六边形**」;渲染注释写「状态徽章(**六边形**上方)」。
|
||
|
||
### 8.2 验证读数(**从改动后的源现取**跑的,⚠️ 不是读副本)
|
||
|
||
| 项 | 读数 |
|
||
|---|---|
|
||
| `render-check.mjs`(默认快照) | **28/28** |
|
||
| `render-check.mjs`(正例快照 `board-verify-ws.json`,3 条协作会话) | **28/28** |
|
||
| `arch-geom-check.mjs`(两个快照各一遍) | **6/6** |
|
||
| **反向对照**:故意把本体挪到 `y=205` | **如期报红 rc=1**,明细「顶 205 ≠ R2 顶 176」 |
|
||
| **反向对照**:新静态断言两条 regex | 旧文案判红 / 新文案判绿,**四组方向全对** |
|
||
| 实时看板 `http://127.0.0.1:8788/` | **200** / 65,801 B;页内 `trig-hex` **0** 次、`trig-node` **2** 次 |
|
||
|
||
⚠️ **本轮最值钱的教训**:**改形状必须同步改断言** —— 否则过时断言 = **假红**;
|
||
更阴的是**恒真断言 = 假绿**(本轮真踩到:正则查的东西已经不存在了,它就一直"绿"着)。
|
||
⇒ **新增/改动的断言一律做一次反向对照**(故意造错),确认它**真会红**。
|
||
|
||
### 8.3 仍待用户拍板(⛔ 一处未动)
|
||
|
||
- 两条 `[协作]-唤醒机制-唤醒轮A/B` 排期(现**全 PAUSED**)**撤不撤**。
|
||
- 投递程序 `wake-session.py` **还留不留**。
|
||
- (第十四条 ③)心跳**进队列**的三种落地选哪种(我倾向「只写队列文件」)——
|
||
⚠️ 这条**要改机制层** ⇒ **得先有锁**(见本节开头)。
|
||
|
||
### 8.4 用户**第三次定性**:唤醒会话的三条硬定性(本轮追加)
|
||
|
||
> **用户原话**:「**唤醒会话 是独立会话,不要在主会话上处理,是随着需求确定时创建的**」
|
||
|
||
| # | 定性 | 落地 |
|
||
|---|---|---|
|
||
| (a) | **独立会话** | 看板上它是**独立一格**(⛔ 不是主会话的"职能");判「都空闲」时**必须排除它** |
|
||
| (b) | ⛔ **不在主会话上处理** | 叫醒的**执行体是它自己** ⇒ ⛔ 不把该它干的活挂到主会话身上;脉冲任务起在**它**里面 |
|
||
| (c) | **随需求确定时创建** | 创建时机 = **需求确定那一刻**(⛔ 不提前建)⇒ 看板上「**还没建**」是**正常态**、⛔ 不是故障 |
|
||
|
||
**落地**:
|
||
- `board_ext.py`:`_waker_session()` docstring、`_triggers()` docstring + tips、
|
||
链路前置文案("还没建"那支补「按定性它**随需求确定时才创建**」)。
|
||
⚠️ 顺带修掉 docstring 里一块**已过期的旧语义** —— 它还写着"`running` = 至少一条排期 `ACTIVE`"、
|
||
"只看排期器自己的 `status`",那是**②之前**的读法。
|
||
- `board.html`:图例(`archNote`)+ `aria-label` 说清三点;「还没建」不再写含糊的"待创建"。
|
||
- `SKILL.md`:新增 **§1.4a ⑤**(三条定性对照表);§0.5.5 加三条定性一行;`last_change` 补 ⑥。
|
||
- `references/architecture.md`:`[唤醒]` 那条命名规则补齐三条定性。
|
||
- **新增 2 条断言**(「独立会话/⛔ 不在主会话上处理」+「随需求确定时才创建」)。
|
||
|
||
**验证**:渲染断言 **30/30**(默认 + 正例各一遍)· 几何 **6/6** · `board_ext.py` `py_compile` OK ·
|
||
实时看板 **200** / 66,351 B(页内「独立会话」×4、「不在主会话上处理」×3、「随需求确定时才创建」×1)。
|
||
|
||
---
|
||
|
||
## 九、协作机制全面体检(2026-10-01 01:37–01:45 · 用户点「跑起来试试排查问题」)
|
||
|
||
**报告落点**:`tmp/协作机制体检-2026-10-01.md`(⛔ 没取域锁就不往共享的 `交付物/` 写)。
|
||
|
||
### 9.1 结论
|
||
|
||
**机械部件全在岗且正常;坏的是"状态语义"** —— 一个 **09-30 20:27 被人为停掉**的需求,
|
||
仍在**每轮向每个会话广播「🔴 中断/脱节」**,而实际是"人把它停了、活也早干完了"。
|
||
|
||
### 9.2 实测读数(14 项,详见报告)
|
||
|
||
✅ 在岗:全局钩子(`PreToolUse ^Bash$` + `UserPromptSubmit` → `wb-result-hook.py`)|投影轮真跑
|
||
(stamp 跟着本会话的 Bash 动)|看板 200 / 3 秒一刷 / 单实例|「唤醒脉冲会话」那格按新定性显示「还没建」|
|
||
`preflight-lock.sh` 在文档库 `07-scripts/`(**更正上轮"锁缺失"的说法**)|任务图 **18/18 done**、台账 4/4 done。
|
||
|
||
🔴 问题:`run=paused` 但投影轮不认 ⇒ 照产全套告警|`wake_enable=false` ⇒ **投递零 14h59m**
|
||
(`wakeups.jsonl` 停在 09-30 10:44:43)|投递目标是条 **completed** 会话|`blocked.json` 两项陈旧
|
||
⇒ `NEXT.md` 假称"有活"|`acceptance_state` 无判据 ⇒ `goals_open` 恒真 ⇒ 心跳永不暂停。
|
||
|
||
### 9.3 本轮踩到的一个坑(值得记)
|
||
|
||
⚠️ **管道吞掉退出码**:`cmd | tail -25; echo "rc=$?"` 拿到的是 **`tail`** 的退出码。
|
||
我第一次就这么把 `selftest` 的**真 rc=1 报成了 rc=0**(差点误报"全绿")。
|
||
⇒ 要真 rc:重定向到文件,或读 `PIPESTATUS`。
|
||
|
||
### 9.4 待办(⛔ 本轮一处没动)
|
||
|
||
- **P0**:`--once` 加总闸(`run != active` ⇒ 只写一行"已停")—— ⚠️ 改机制层 ⇒ **先取域锁**。
|
||
- **P0**:验收判据要么补一条,要么把判据改成「全 done 且未声明 ⇒ 喊一次就静默」。
|
||
- **P1**:重启投递前**必须实测投递目标**(`roles` 现在指着一条 completed 会话);摘掉陈旧受阻项。
|
||
- **P2**:清技能目录污染(`scripts/__pycache__` + `_collabd.log`);看板 `deliver.stopped` 的 `reason` 补上;
|
||
清掉每轮重复报的那条哑火(`8074a69b`,属已退役的手机接入线)。
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
## 九、(本轮 07:14–08:0x)协作机制**全面体检与修复**(主控 · 唤醒机制线)
|
||
|
||
用户指令:「全面检查多会话协作机制是否正常且稳定运行,并修复问题」。
|
||
|
||
### 体检读数(修复前)
|
||
- 钩子接线 ✅(`wb-result-hook.py` 内 fork `--once`/`--tick`;stamp 07:14 在动)
|
||
- 自测 31/32(1 红=技能目录产物污染)
|
||
- 🔴 **实时看板没起**(8788 拒连 —— 用户明确要的是"活着的那一个")
|
||
- 🔴 `goal.json.run=paused`(09-30 20:27 `goalctl stop`)+ `wake_enable=false` ⇒ **投递停摆**
|
||
- 🔴 最后一笔真投递=**09-30 10:44:43**(`ok:true`),之后到今天一路空白
|
||
- 🔴 队列/任务图 18 条**全是已退役的手机接入线**(N9/N10/V6/前置-20090)⇒ 队首长期指向退役需求,
|
||
`NEXT.md`/`NEED-USER.md` 还在喊用户去修「垫片 20090」那种已退役的事
|
||
- 🔴 `acceptance_state` 无有效判据 ⇒ 每轮刷同一行日志 40+ 遍(真事件被淹没)
|
||
|
||
### 修了五处(技能内迭代,⛔ 未在工作区另开平行文档)
|
||
1. **已停总闸**:新增 `goal_paused()`/`paused_round()` ⇒ `run != active` 时 `--once` 只写一行"已停"、
|
||
清掉 `NEXT/STALL/NEED-USER/queue` 投影;`--tick` ⛔ 不投递 ⇒ 自测新增 6 项全绿
|
||
2. **主会话解析只认活会话**(🔴 投递断点的真身):新增 `_pick_live()` ⇒ 解析曾选中
|
||
**已 `completed` 的 `a202550c`(标题「主控 · 唤醒机制线」)**,而唯一活着的会话被它压在后面
|
||
⇒ 投递一路 `main-not-live`。修后判读转为 **`target-busy`**(找到人、对方在忙)= 路径已通
|
||
3. **退役线清队列**:旧台账/`blocked.json`/任务图**归档**(`.workbuddy/collab/归档/`)⇒ 按当前类别重建 M1–M7
|
||
4. **日志降噪**:`log_throttled(key, 600s)`(同一结论每轮连刷 ⇒ 淹没真事件)
|
||
5. **技能目录污染**:`_collabd.log` 归档并删除;`__pycache__` 从自测"硬项"移出(CPython 产物,列进去=恒红假红)
|
||
|
||
### 结果
|
||
- `selftest.py` **33/33 全绿**
|
||
- 实时看板 **已起**:`http://127.0.0.1:8788/`,`/healthz` → `ok:true`、快照延迟 2.8 s
|
||
- 验收判据(**由主会话提,⛔ 可推翻**):V2 看板/V3 自测/V4 回路/V5 队列 = `pass`;**V1 投递 = `fail`**
|
||
(断点已修但**真投递还没发生**:我这条会话一直在跑 ⇒ `target-busy` ⇒ 待我空闲后由钩子自动发出)
|
||
|
||
### 🔴 待用户拍板(未擅自做)
|
||
**唤醒脉冲会话从未被建**:会话表里**零条** `[唤醒]` 前缀会话(带"唤醒"字样的 6 条全是 `completed` 旧棒)。
|
||
⇒ 技能 §1.4a 定的"随需求确定时创建"从未执行;叠加"网关排期=死能力"(`CODEBUDDY_DISABLE_CRON=1`,09-30 定案)
|
||
⇒ **全员静止时零唤醒**(今天"投递一路静默"的结构性原因)。⚠️ 建它属派活/接续白名单外 ⇒ 等你点头。
|
||
⚠️ 另:看板是**会话后台任务** ⇒ 关掉本会话就停(本机没有真常驻手段);且它挂着 ⇒ 本会话的 idle 钩子被压制。
|
||
|
||
---
|
||
|
||
## 九、(本轮)看板改版:多目标 tab + 目标卡以「目标/完成情况」为主角(2026-10-01 08:0x)
|
||
|
||
### 用户两条原话
|
||
- 「为什么看板里不显示**项目目标**了,项目的**属性标签 规则 用一列描述**就好,重点是看**目标需求 完成情况**」
|
||
- 「把协作实时看板改为 **tab 支持多个目标**执行协作状态展示」
|
||
- (追问)「再确认下**目标名称**显示在哪里的,没看到呢」
|
||
|
||
### 改了什么
|
||
**`board.py`(技能侧)**
|
||
- 加 `GOALS_DIR` / `goal_files()`:`goal.json`(**活跃·排第一**)+ `goals/*.json`,按 `id` 去重(同名以 `goal.json` 为准)
|
||
- 抽 `_goal_block(g, active, tasks_all, srows, st, warn, multi, all_topics)` ⇒ 一个目标一块;
|
||
`build()` 出 **`goals[]`**,而**顶层键=活跃那一份**(`goal`/`project`/`tasks`/`sessions`/`labor`/`orphan`/`queue`/`progress`/`others_running`)
|
||
⇒ ⛔ 老渲染器与老断言**逐字照旧可用**(实测"缺旧键:无")
|
||
- 台账切分:`multi=False` ⇒ 原样全给(单目标逐字不变);`multi=True` ⇒ `line ∈ 目标 topics`;
|
||
`line` 不属**任何**目标的件归**活跃目标**且照常显示(⛔ 不静默丢)
|
||
- 参数化:`_scan_ws_mains(st, topics)` / `_resolve_main(st, topics)` / `project_scope(g)` / `_sessions(sc, rows)`
|
||
+ 新 `_session_rows()` ⇒ **宿主库只读一次**,N 个目标共用(⛔ 不 N 次读库)
|
||
- 🔴 `_labor()` 内部两处 `_goal_topics()` 改 `_goal_topics(goal)` —— **不传 goal 会让每个目标都按活跃目标的类别算分工板**(静默串线)
|
||
|
||
**`board.html`(技能侧)**
|
||
- 新 `renderTabs()` / `paintScope()` / `scopeView()`;当前格记进 **URL hash `#g=<目标id>`**(刷新/分享不丢);
|
||
切格**只重画不取数**;⛔ 老快照(无 `goals`)⇒ 回落"一格",行为与旧版一致
|
||
- 作用域:切 tab 只换 **本项目/协作架构/需求台账/会话明细**;**前置/队列通知/最近上报=工作区级**,⛔ 不随 tab 变
|
||
- 目标卡改版:**① 目标**(`目标` 标签 + pill 简称 + **粗体全名**)→ **② 完成情况**(进度条 + `N / M 通过` + **未完项逐条点名**)→ ③ 任务类别 chips(带该类主会话)→ ④ **属性+规则压成一列** `.projAttr`
|
||
- 🔴 tab 第一行改**完整目标名**(原版只给 `short` 简称,全名藏在 `title=` 悬停里 ⇒ 用户"没看到目标名称")
|
||
- ⛔ 一个目标时**也画** tab 条(含"怎么加第二格"的说明)—— 让"这是多目标看板"自己说得出口
|
||
|
||
### 验证(四套,全绿)
|
||
- `selftest.py` **34/34**(新增 `看板:多目标 tab` 11 项)
|
||
- `tmp/render-check.mjs` 单目标 **36/36**;双目标样本 `tmp/board-verify-2goals.json` **39/39**
|
||
—— 含"**改 hash 再重画**"⇒ 真验**切格换数据**(⛔ 不是只数 tab 格数)
|
||
- `tmp/arch-geom-check.mjs` **6/6**
|
||
- 线上:`127.0.0.1:8788/healthz` → `ok:true`;`board.json` 顶层含 `goals`(格数 1);`meta.goal_count=1`
|
||
|
||
### 🔴 本轮踩到的两处教训
|
||
1. **断言必须与代码同批改(第三次)**:目标卡改成"不再逐条列判据"后,`render-check` 里那条
|
||
"每条判据都出现"当场变**假红** ⇒ 断言同步改成"**N/M 通过 + 未完项点名**"这个新契约。
|
||
2. **"有却不说"又犯一次**:tab 第一行只写 `short`(简称)⇒ 用户认不出那是哪个目标。
|
||
同类红线(`architecture.md §2.3.2`)⇒ 已收进该表第 ③ 行。
|
||
|
||
### 现状
|
||
- **当前执行中的目标**:全名=「本机(桌面客户端)内的多会话协作」|简称=「本机协作」|id=`local-collab`|
|
||
任务类别=`['唤醒机制']`|`run=active`|完成=**4/5 通过,未完 `V1-投递`**
|
||
- ⚠️ 线上只有 **1 格 tab**(`goals/` 目录尚未启用);放第二个 `<目标id>.json` 即多一格
|
||
- ⚠️ 看板仍是**会话后台任务**(关掉本会话就停);探针文件已撤,`goals/` 未在生产留任何夹具
|
||
|
||
### 九·补(用户两条追问,2026-10-01 08:1x)
|
||
|
||
**追问 ①「下面是干啥的,为什么也在 tab 区域」**(指 tab 条上那句"这个工作区现在只登记了 1 个目标 ⇒ 只有一格/
|
||
要并行看第二个目标:在 `goals/` 里放一份…")
|
||
- 🔴 根因:那是我写的**使用说明**,不是**数据** ⇒ 摊在版面上就是噪声(老毛病「把小字说明写到版面」)。
|
||
- 修:**tab 条上⛔ 不放任何说明文字**;「怎么多一格」搬进「本项目」右上角 `?`;删 `.tabHint` CSS;
|
||
⛔ 也**不**退回「共 N 个目标」那种话(有几格看格子就知道,写出来信息量为零 —— `architecture.md §2.3.2` 判据①)。
|
||
- 防回潮:`tmp/render-check.mjs` 加断言「tab 条上不放说明文字」;`selftest.py::看板:版面纪律` 的
|
||
`DELETED` 清单加进这两句。
|
||
|
||
**追问 ②「这一段话看起来 就不是目标」**(「本机(桌面客户端)内的多会话协作」只是**范围**,不是**有目的的目标**)
|
||
- 🔴 根因在**数据**,不在 UI:`goal.json.title` 当初写成了**主题/范围**;`why`(目的)那一栏一直没被渲染。
|
||
- 修:按用户原话用 `goalctl declare` 重登记 ⇒ **title =「测试并修复本机(桌面客户端)内的多会话协作功能」**,
|
||
`why` =「让本机桌面客户端里的多个会话能可靠地协同推进项目工作」+用户原话注在 `declared_by`
|
||
(备份 `.workbuddy/collab/bak-goalctl-20261001/goal.json`)。
|
||
- ✅ 看板**自动跟随**(每轮重读 `goal.json`)⇒ **不必重启** board.py(只有改 `board.py` 才要重启)。
|
||
- 🔴 教训(同族第三次):「目标」字段一旦写成主题,**看板再怎么写都救不回来** —— 看板只能如实显示。
|
||
目标由对话说明 ⇒ **措辞是主会话的责任**,登记时就要写成"动词+对象"。
|
||
|
||
---
|
||
|
||
## 十、(本轮)看板"去说明书化"收口 + 断言假红修复 + 接上 M5(2026-10-01 08:2x)
|
||
|
||
### 用户原话(第 8 轮)
|
||
> 「还有这一段也是很杂乱,看板要精简的展示重点有效的信息,**不是写说明书**:
|
||
> 任务类别(同一个工作区 · 按名称前缀区分):唤醒机制 主会话a202550c 来源:调用时在对话里说明 · …
|
||
> 目标 id local-collab · 默认主会话 a202550c · 工作区 ai1net-dsh-server · 归属判据(任一命中即算):…」
|
||
|
||
### 改了什么(`assets/board.html` · `renderProject()`)
|
||
- 🔴 **项目卡版面只剩三行**:`目标`(全名加粗)/`完成情况`(进度条 + N/M 通过 + 未完项点名)/`任务类别`(chips,每枚带该类主会话)。
|
||
- 其余**全是背景与规则**(目标 id / 默认主会话 / 工作区 / 归属判据 / 类别来源的时间与人)
|
||
⇒ 一条没丢,**全部追加进这块右上角 `?`(`#projTip`)**;`?` 本来就是"小字说明"的正式落点。
|
||
- 删 CSS:`.projTitle` `.projMeta` `.projAttr`;`#projTip` 加 `id` 并给 `dataset.base` 复位(⛔ 不逐轮叠加)。
|
||
- `任务类别` 标签去掉括注("同一个工作区 · 按名称前缀区分");chip 的 `title=` 里留着来源细节。
|
||
|
||
### 🔴 教训(同族第 4 次:断言必须与代码同批改)
|
||
搬进 `?` 后那句话被 `<b>任务类别的来源</b>:` 包了标签 ⇒ 旧断言逐字匹配 `来源:调用时在对话里说明`
|
||
**永远不中**(跨 `</b>`),两条样本各报 1 FAIL = **假红**。
|
||
⇒ 修法**不是**放宽成永真、也不是硬搬回 `proj`,而是把断言拆成两条**语义**条件:
|
||
① 标签 `任务类别的来源` 在 `?` 里说得出口 ② 且给了具体来源(`调用时在对话里说明` / `还没在对话里说明过`)。
|
||
|
||
### 验证(四套,全绿 · 收口读数)
|
||
| 套件 | 读数 |
|
||
|---|---|
|
||
| `selftest.py` | **PASS 34 / FAIL 0** |
|
||
| `tmp/render-check.mjs`(单目标) | **FAIL 0 / 44** |
|
||
| `tmp/render-check.mjs`(双目标样本) | **FAIL 0 / 47** |
|
||
| `tmp/arch-geom-check.mjs` | **FAIL 0 / 6** |
|
||
| 线上 | `127.0.0.1:8788/healthz` → `ok:true`(快照 age 1.8s) |
|
||
|
||
线上 `board.json` 实测:`goal_count=1` · `goals_dir=…\tmp\supervise-inbox\goals` ·
|
||
`goal.title=测试并修复本机(桌面客户端)内的多会话协作功能` · `topics=['唤醒机制']` ·
|
||
`acc_summary=非 pass:['V1-投递']` · `labor=[('唤醒机制','gap',2,3)]` · **缺旧键:无**(向后兼容保持)。
|
||
|
||
### 收尾自判 → 派下一棒(白名单「派活」· 免确认)
|
||
- 信号文件(`tmp/supervise-inbox/`):`STALL.md`「未来 1 小时内零排期」+ `READY.md`「**可派未派:M5**」。
|
||
- 台账 `tasks.json`:`M5 state=running` 但**无活持有者**(M6/M7 已 done)⇒ 关键路径断在 M5。
|
||
- 缺口判据(可核对):`goal.json.acceptance_state` 唯一 `fail` = **`V1-投递`**;`wakeups.jsonl` 现有 81 行
|
||
**全部 `ok:true`**,但**最新一条停在 `2026-09-30T10:44:43`** ⇒ **2026-10-01 当天 0 条**,即本轮目标期内的投递**一次都没成**。
|
||
- 动作:建**一次性**自动化 `b1085bdd-8183-4f8f-9fb4-f8fee5099e49`(`08:31`)
|
||
「`[协作]-本机协作-M5 验 --tick 真投递一次(wakeups.jsonl 新增 ok:true)`」;
|
||
prompt 里写死:**只有 `wakeups.jsonl` 新增 2026-10-01 的 `ok:true` 行才算 done**,
|
||
`target-busy` / `main-not-live` **不算通过**、必须把判读原文取出来留证。
|
||
- ⚠️ 本轮我**先释放了锁才继续改文件**(属实 · 已如实报告)⇒ 本轮开工时已用
|
||
`--claim-exec "主控·协作机制线·项目卡改版" --domains "ai1net-dsh-server/"` 重新抢回。
|
||
|
||
### 顺带发现(未动,只报告)
|
||
- `automation_update list` 里有**大量一次性排期(~50 条)停在 2026-09-27~09-30 且状态仍 ACTIVE**
|
||
⇒ 全是已过期未清理的"僵尸排期"(当前目标期内**未来 1 小时零排期**,与 STALL 吻合)。
|
||
清理属"删冗余",按 §F 可直接做 —— 但本轮不做(⛔ 不顺手扩大范围),留给用户/后续棒决定。
|
||
- `NEED-USER.md`(08:23 新鲜写入):「手机接入链路前置掉了(`127.0.0.1:20090` 无 LISTENING)⇒ 需用户介入」。
|
||
⚠️ **手机接入线已于 2026-09-30 随 V1–V7 全过退役** ⇒ 这条**可能已过时**,但它自己写着"需用户介入",如实上报。
|
||
|
||
---
|
||
|
||
## 九、(本轮)M5 接续棒:**抢锁被主会话挡下 ⇒ 未开工**;判读原文取到 = `target-busy`(2026-10-01 08:3x)
|
||
|
||
> 🔴 **本节结论已被文末「M5 收口」一节取代(08:36)** —— 本节记的"未通过"是 **08:31–08:35 那一刻**的事实;
|
||
> 08:36:37 主会话让出(并释放了锁)后,`--tick` **当即真投出** ⇒ **M5 已 done**。下文原样保留作过程记录。
|
||
|
||
**本棒**:会话 `[协作]-本机协作-M5 验 --tick 真投递一次`(sid `885f4083-26f1-46ff-bd45-6395c869a3f1`,
|
||
自动化 `b1085bdd-8183-4f8f-9fb4-f8fee5099e49`)|线 `唤醒机制`。
|
||
|
||
### 1. 第 0 步读数
|
||
- `state.py`:锁「空闲」;⛔ 但入口段提示本机有 **2 条工作线** ⇒ 只走自己那条。
|
||
- `tmp/supervise-inbox/NEXT.md`:队首 = **M5**(线 `唤醒机制`,`state=running`)✔ 与任务书一致;
|
||
`claims/M5` **不存在** ⇒ 确认「无人持有」✔。
|
||
- **抢锁 ⇒ ✗ 失败**:
|
||
```
|
||
✗ 抢域锁失败:冲突域被占用
|
||
· 域「ai1net-dsh-server/」已被「主控·协作机制线·项目卡改版」占用
|
||
```
|
||
`.locks/<hash>/OWNER` 实测:`主控·协作机制线·项目卡改版 | 开始:10-01 08:28 | 会话:f8a792ab-b156-448d-9143-fa524dd5ddc1`。
|
||
- ⇒ 按任务书「**抢不到 ⇒ 停手报告**」执行:**本轮未开工**、⛔ 未动任何项目文件、
|
||
⛔ 未删锁 / 未接管(R9)、⛔ 未跑 `--tick`(任务书把开工资格挂在锁上)。
|
||
- 另:`preflight-lock.sh` 对本件两文件判【D】**未归类**(`tmp/supervise-inbox/wakeups.jsonl`、`collabd-state.json`)。
|
||
|
||
### 2. 「判读」原文(取自**协作程序生产日志**,⛔ 非本轮亲跑)
|
||
`.workbuddy/collab/logs/_collabd.log`(07:31 → 08:34,每 ~2 min 一轮,**30+ 轮判读逐字相同**):
|
||
```
|
||
[2026-10-01 07:31:12] 主会话变更:a202550c -> f8a792ab(依据 live-fallback)⇒ 已跟随
|
||
[2026-10-01 08:34:31] 延后投递:目标会话 f8a792ab 正在执行 ⇒ 等它空闲
|
||
[2026-10-01 08:34:31] 投递未成(target-busy)⇒ 保留 M5=running 在队首,下一轮重试
|
||
```
|
||
- 解析到的目标会话 id8 = **`f8a792ab`**;判读 = **`target-busy`**
|
||
⇒ **⛔ 不是 `main-not-live`**(那一型 07:31 已被 `live-fallback` 修掉),也**不是**投出去了。
|
||
- 代码依据 `collabd.py:1087‑1090`:目标会话 `status=='working'` ⇒ `skipped="target-busy"`、**明令不降级**。
|
||
- 机读侧同款:`tmp/supervise-inbox/collabd-state.json` → `queue_info.deliver = {"skipped": "target-busy"}`。
|
||
|
||
### 3. 产物判定
|
||
- `wakeups.jsonl`:**前 81 行 / 后 81 行(本轮零新增)**;`grep -c 2026-10-01` ⇒ **0**;
|
||
最新一条仍停在 `2026-09-30T10:44:43`(`ok:true`,`监督程序·心跳`)。
|
||
- ⇒ **M5 = 未通过**。⛔ 不写成"已通过";⛔ **不把"找到了人但对方在忙"说成投递成功**。
|
||
|
||
### 4. 卡点结论(本轮最有价值的发现)——**同一条线被「自己」双向堵死**
|
||
堵点都是同一条会话 `f8a792ab`:
|
||
|
||
| 侧 | 机制 | 结果 |
|
||
|---|---|---|
|
||
| **投递侧** | `f8a792ab` 既是**投递目标**、又自 07:31 起**持续 `working`**(>1h) | 投递闸恒 `target-busy` ⇒ **V1-投递结构上过不去** |
|
||
| **派活侧** | `f8a792ab` 持 `ai1net-dsh-server/` **全域(粗域)锁** | 它**派出的棒第 0 步就被挡回** ⇒ **整轮白开**(本轮即实例) |
|
||
|
||
- 判据(技能 §6.5 原话):**「域锁要切细…⛔ 禁止整工作区域粗域 —— 那是自造串行瓶颈(实测造成多次
|
||
『抢锁失败 ⇒ 整轮白开』的纯浪费)」**。
|
||
- 🔴 更准的一刀:`handoff-guard.sh:197` 的冲突判定是**域字符串精确相等**(`awk '$1==want'`)
|
||
⇒ **粗域只挡得住"同一字符串"**,而唯一写这个字符串的就是它自己的派活模板
|
||
⇒ **它挡掉的恰好只有自己派出去的棒**(其余会话若声明细粒度域,反而进得来)。
|
||
|
||
### 5. 留证 / 上报 / 未动项
|
||
- ✅ `tmp/supervise-inbox/blocked.json` ⇒ M5 记入(**解除条件写进值里**)。
|
||
- ✅ 台账走正规通道:`collabd.py --report M5 --state running --artifact …`(⛔ 未手改 `tasks.json`)。
|
||
🔴 **故意不标 `--state blocked`**:M6/M7 已 `done` ⇒ 一标 blocked 就使 `goal_stuck` 成立
|
||
⇒ 技能 §1.4a ②「有阻碍 ⇒ 唤醒暂停」⇒ **投递不再重试 ⇒ V1 反而永远过不去**。这是坑,勿踩。
|
||
- ⛔ **`NEED-USER.md` 未动**:它是程序**每轮覆写**的全局告警件,当前载的是**另一条线**(垫片 `20090`)的告警;
|
||
且本棒条件**不需要用户动作**(主会话让出即时自动解除)⇒ 写入既会抹掉别人的告警、也会被下一轮覆写。
|
||
- ⛔ 未跑 `--tick`、⛔ 未 `pkill`、⛔ 未接管辖,锁**仍由 `f8a792ab` 持有**。
|
||
|
||
### 6. 收尾自判 ⇒ 派下一棒(白名单「接续会话」)
|
||
- 缺口(可核对):`goal.json.acceptance_state` 唯一 `fail` = **`V1-投递`**;`wakeups.jsonl` 2026-10-01 仍 0 条。
|
||
- 动作:建**一次性**自动化「`[协作]-本机协作-M5 复验 --tick 真投递(看主会话是否让出)`」,
|
||
排期 = 现在 + 6 min。**prompt 自带三件**:① 先判 `blocked.json` 的解除条件是否已消失
|
||
② 判据只认 `wakeups.jsonl` 新增 2026-10-01 的 `ok:true` ③ 抢锁改用**细粒度域**
|
||
`ai1net-dsh-server/tmp/supervise-inbox/`(本件不碰任何其他文件),抢不到 ⇒ **纯只读取证后如实记**。
|
||
|
||
---
|
||
|
||
## 十一、(本轮)架构图 Hook进程 框加宽 + `--tick` 线改**纯竖直**(2026-10-01 08:3x)
|
||
|
||
### 用户原话
|
||
> 「Hook进程 框图宽度往右边延长一些 让 tick那条线 能竖直摆放」
|
||
|
||
### 改了什么(`assets/board.html` · 架构图 ⑤ 段)
|
||
| 项 | 前 | 后 |
|
||
|---|---|---|
|
||
| Hook进程框宽 `hkW` | 480(右边界 880) | **490**(右边界 890) |
|
||
| `--tick` 走线 | `M840 702 →L840 685 →L850 685 →L850 668`(**10px 横折**) | `M850 702 →L850 668`(**纯竖直**,折点 0) |
|
||
| 出口 x 来源 | `hkX+hkW-40`(凑数,≠ 入口) | **`TICKX = UX+PW/2`**(≡ 上报框中心,⛔ 不再凑) |
|
||
| 标签 `--tick` | `(hkX+hkW-28, B3-6)` | `(TICKX+12, B3+4)`(竖线右侧、垂直居中) |
|
||
|
||
🔴 关键判据:**出口点必须仍落在 Hook 框顶边之内**(`hkX..hkX+hkW`)—— 这就是加宽的理由,
|
||
两者**必须一起改**,否则线头会悬在框外。已写进 board.html 注释与几何自检 ⑥ 段。
|
||
|
||
### 新增断言(`tmp/arch-geom-check.mjs` ⑥ 段 · 共 7 条)
|
||
判据**全部从渲染产物几何现算**(⛔ 不写死 850、⛔ 不查源码字面量):灰线 + 两点且 x 相同(=竖直)
|
||
+ 跨度恰为 `R4底..R5顶` + 落点在**上报框横向跨度内** ⇒ 取最右那条 = `--tick`;
|
||
再验 ① 对齐上报框中心 ② 不悬在框外。
|
||
|
||
### 🔴🔴 本轮最值钱的教训:**负向测试本身也会假绿**
|
||
第一次写的变异脚本只按"两点竖线"取,**改中了 `用户→主会话` 那条彩色线**(`M640 116 L640 176`)
|
||
⇒ 自检照旧全绿 ⇒ **那次"负向测试"什么也没验证到**(还差点让我以为判据没问题)。
|
||
修法:变异脚本的**选取口径必须与检查器逐条一致**(灰线 + 竖直 + 跨度 + 落点在框内),
|
||
并加打印 `MUTATED(... x=850 ...)` **把改中的那条线念出来** —— 一眼就能看出打歪没有。
|
||
✅ 最终两个分支都验过:`jog`(改回横折)⇒ 报「找不到 --tick 竖线」;
|
||
`shift`(左移 10px)⇒ 报「没对齐上报框中心(840 ≠ 850)」。两条都 rc=1。
|
||
|
||
⚠️ 另一处小坑:两次试验之间我用了个空 heredoc(`"$PY" - <<'EOF'`)⇒ **整条命令被 SIGTERM**,
|
||
`rendered-arch.html` 停在**被变异**的状态没还原 ⇒ 下一轮开工时才发现。
|
||
⇒ 规矩:**变异-验证-还原必须写在同一串命令里、且还原放在最后无条件执行**;⛔ 不插无关命令。
|
||
(已在下一轮命令开头加"若发现 `_arch-bak.html` 残留就以它还原"的兜底。)
|
||
|
||
### 验证(四套,全绿)
|
||
| 套件 | 读数 |
|
||
|---|---|
|
||
| `selftest.py` | **PASS 34 / FAIL 0** |
|
||
| `render-check` 单目标 | **FAIL 0 / 44** |
|
||
| `render-check` 双目标 | **FAIL 0 / 47** |
|
||
| `arch-geom-check` | **FAIL 0 / 7**(含新增 ⑥) |
|
||
| 线上 | `8788/healthz` → `ok:true`;`GET /` 实测**已供上新版**(`hkW=490` 命中 1) |
|
||
|
||
🔴 **不必重启 board.py** —— `do_GET` 里是 `html_p.read_bytes()` **每请求现读盘**(`board.py:1003`);
|
||
只有改 `board.py` 才要重启。页面刷新(F5)即可看到新图。
|
||
|
||
---
|
||
|
||
## 十二、(本轮)M5 收口 + 「创建了协作会话但看板没展示」根因修复(2026-10-01 08:3x–08:5x)
|
||
|
||
### A. M5 收口(主会话按产物核对,⛔ 不认自述)
|
||
用户转来队列表:`M5 -> running … 产物:未取得验收产物(08:3x 抢锁失败,未开工);判读=target-busy`。
|
||
**核对产物 ⇒ M5 其实已完成**(那条报告**旧了 1.5 分钟**):
|
||
- `tmp/supervise-inbox/wakeups.jsonl` **第 82 行**:`{"ts":"2026-10-01T08:36:37","http":200,"ok":true,"kind":"上报·单条","sessionId":"f8a792ab…"}`(**今天第 1 条 ok:true**;此前 81 条最新停在 09-30T10:44:43)
|
||
- `_collabd.log`:`08:36:37 notify(上报·单条) -> f8a792ab@50094 http=200` ⇒ **真投出**
|
||
- `08:37:05 task report M5: running -> done`
|
||
|
||
**为什么那时才成**:`f8a792ab`(=**本会话**)在 07:31–08:35 一直 `working` ⇒ 投递闸恒 `target-busy`(`collabd.py:1087-1090` **明令不降级**)⇒ **我一停手它就投出去了**。
|
||
⇒ 🔴 **教训:投递目标 = 投递方自己时,"我一直在跑"就是那个"结构性过不去"的根因** —— 不是代码坏,是**判据本来就要求目标空闲**。
|
||
⇒ **推论(写进判据)**:⛔ **别把看板之类的常驻后台任务挂在"投递目标会话"上** —— 那会让该会话永不空闲 ⇒ 投递闸恒 target-busy ⇒ 把 V1 弄回 fail。
|
||
|
||
**收口动作**(均走正规落点):
|
||
| 动作 | 落点 | 结果 |
|
||
|---|---|---|
|
||
| 验收 `V1-投递` fail→pass(5 条全 pass) | `goalctl declare --kpi … --yes` | ✅ 看板 `acc_summary = 全部 pass(5 条)` |
|
||
| 保住 `_` 历史说明行(declare 会整表重写并丢掉) | `tmp/_restore-kpi-notes.py` 按**备份原顺序**合并回 | ✅ `_说明/_更新/_判据口径/_V1待验` 四条原样 |
|
||
| ⛔ 不重派已完成的件 | `automation_update delete 07b51661`(M5 复验,**08:41 就要触发**) | ✅ 已删(晚 3 分钟就又白开一个会话) |
|
||
| `blocked.json` 的 M5 条目 | 已被 M5 棒自己清空(现为 `{}`) | ✅ 无需动 |
|
||
|
||
### B. 🔴 「为什么创建了协作会话 但是 看板中没有展示」—— 根因是**规范写错了**
|
||
**证据链**(全部实测):
|
||
1. 棒会话行:`885f4083` · `status=completed` · `title='[协作]-本机协作-M5 验 --tick 真投递一次(…)'` · `cwd=E:/ProgramData/AIProject/ai1net-dsh-server`
|
||
2. `board.py::in_project()`:① sid ∈ `main_sids`?`885f4083` ∉ `[a202550c, f8a792ab]` ✗ ② `"[唤醒机制]" in title`?标题里只有 `[协作]` ✗ ⇒ **False**
|
||
3. `collabd.py::parse_session_name()`:第 2 级要求**以 `[` 开头**;`本机协作` 不以 `[` 开头 ⇒ `topic=""`、`ok=False`(=「未按约定命名」)
|
||
4. ⇒ 它**不进 `sessions`/`labor`**,第三层渲染**空态**「暂无协作会话」—— 一条真跑过的棒**静默消失**。
|
||
5. 🔴 **根因不是我的笔误,是规范**:`collabd.py:582` 生成的 `NEXT.md` 原文写着
|
||
「两级前缀 `[协作]-[主题]-<具体>`(**主题取自 `goal.json` 的 `short`**)」——
|
||
**按这句话命名出来的棒,判据必然认不回**(`_in_project` 收的是 `topics`,且要方括号)。
|
||
|
||
**修法(三处,缺一不可)**:
|
||
| # | 改哪 | 改什么 |
|
||
|---|---|---|
|
||
| ① | `collabd.py::_next_md` | 规范改成 `[协作]-[<类别>]-<具体>`,**第 2 级带方括号、值取 `topics`(⛔ 不是 `short`)**,并把"写错 ⇒ 静默漏管"写在同一句里 |
|
||
| ② | `board.py::_sessions()` + `_goal_block()` | 多收一路 `unrecognized`=**同工作区 cwd 尾名相同、但没过三级归属判据**的会话(带 `age_min`)⇒ 快照出 `sessions_unrecognized` |
|
||
| ③ | `assets/board.html` | 图外说明**点名**:在跑/近 3h 的**列 id8+标题**(最多 3 条),更旧的**只计一个数**(⛔ 兼顾"精简"与"不许少说一句") |
|
||
|
||
🔴🔴 **⛔ 绝不用 cwd 改归属**:`cwd` **只用来决定"要不要提醒"**(`_ct == _wstail`),
|
||
**它们不进 `mine`、不进分工板、不算「本项目会话共 N 个」** —— 守 §2.3「绝不回落到 cwd 推断」。
|
||
`selftest` 新用例专门守这一条(含"判据未松"那项)。
|
||
|
||
### C. 顺带发现(**未动**,属设计分叉 ⇒ 待拍板)
|
||
`unrecognized` 一上线就点出 **9 条**同工作区未归入的会话,其中**不只是我那条**:
|
||
- `6ecf6d98` `[主控]-[机制线]-接续:查「唤醒」为什么断`(第 2 级=`机制线`,是**旧类别名**)
|
||
- `5df58d22` `主控 · 唤醒机制线 · 复现验证棒(…)` 等若干 —— 走的是 `goal.json` 里**白纸黑字认可**的
|
||
主会话写法 `主控 · <类别> · <具体>`(**无方括号**),但 `_in_project()` **只认方括号形式**。
|
||
- ⚠️ 而 `_topic_in_title()` 的 docstring 明写「两种写法都要认」⇒ **两处判据口径不一致**。
|
||
⇒ 取舍:**(甲) 放宽 `_in_project` 到"子串命中类别"**(与 `_topic_in_title` 一致,能认回旧棒;代价:归属变成弱约束)
|
||
/ **(乙) 保持严格方括号**(只需规范写对,新棒必被认出;代价:历史棒会一直挂在披露里)。**未拍板。**
|
||
|
||
### D. 验证(四套全绿)
|
||
| 套件 | 读数 |
|
||
|---|---|
|
||
| `selftest.py` | **PASS 35 / FAIL 0**(新增「同工作区但没归入本项目的会话不许静默丢」4 项) |
|
||
| `render-check` 单/双目标 | **FAIL 0 / 45** · **FAIL 0 / 48** |
|
||
| `arch-geom-check` | **FAIL 0 / 7** |
|
||
| 负向测试 | 掐掉 `UNREC` 来源 ⇒ **FAIL 1/45**;还原 ⇒ 0/45(判据**真能红**)✅ |
|
||
|
||
### E. ⚠️ 未落地的一件事(**需要用户/别的会话动手**)
|
||
`board.py` 的改动**尚未生效在线上** —— `board.py` 是进程启动时载入的(只有 `board.html` 是每请求现读盘)。
|
||
⇒ 线上要看 ②③ 那两句披露,**必须重启看板进程**(现 PID 21084)。
|
||
🔴 **但⛔ 不要从这个会话(`f8a792ab`,=投递目标)里重启** —— 那会把它变回"有后台任务的会话" ⇒
|
||
永不空闲 ⇒ 投递闸又恒 `target-busy` ⇒ **刚过的 V1 立刻回到 fail**。这是本节 A 那条推论的反面应用。
|
||
|
||
---
|
||
|
||
## M5 收口:**M5 已通过**(2026-10-01 08:36:37)——「判读=target-busy」被实测坐实
|
||
|
||
> 本棒 = `[协作]-本机协作-M5 验 --tick 真投递一次`(sid `885f4083`,自动化 `b1085bdd`)。
|
||
> 上文那节("抢锁被主会话挡下 ⇒ 未开工")记的是 **08:31–08:35** 的事实,结论已被本节取代。
|
||
|
||
### 转机(08:35 → 08:36)
|
||
复核锁状态时发现:**主会话的域锁目录已消失**。
|
||
- 08:31 实测还在:`.locks/<hash>/OWNER` = `主控·协作机制线·项目卡改版|开始 10-01 08:28|会话 f8a792ab`;
|
||
- 08:36 再列 `.locks/`:**只剩一个 09-29 的陈锁**(N9)⇒ **主会话已 `--release-exec`**。
|
||
⇒ 我随即**重抢同一粗域并成功**:`✓ 已持域锁(主控·唤醒机制线·验tick投递)· 域:ai1net-dsh-server/`。
|
||
|
||
### 真投递(判据产物 —— 只认这一条)
|
||
`.workbuddy/collab/logs/_collabd.log`:
|
||
```
|
||
[2026-10-01 08:35:42] task report M5: running -> running (主控·唤醒机制线·验tick投递)
|
||
[2026-10-01 08:36:37] notify(上报·单条) -> f8a792ab@50094 http=200 hash=246a038e1418e45c
|
||
```
|
||
`tmp/supervise-inbox/wakeups.jsonl`:**行数 81 → 82**;`grep -c 2026-10-01`:**0 → 1**。第 82 行:
|
||
```json
|
||
{"ts": "2026-10-01T08:36:37", "epoch": 1790814997.5, "port": 50094,
|
||
"sessionId": "f8a792ab-b156-448d-9143-fa524dd5ddc1", "hash": "246a038e1418e45c",
|
||
"http": 200, "ok": true, "kind": "上报·单条"}
|
||
```
|
||
⇒ **带 2026-10-01 日期的 `ok:true` 行成立 ⇒ M5 = done**(可核对产物,非"命令跑通"式自证)。
|
||
|
||
### 结论:卡点判断被 60 秒验证
|
||
主会话一让出,**同一条命令、同一份代码、同一个目标**,投递**立刻**由 `target-busy` 变 `http=200`
|
||
⇒ **回路本身是通的**;此前"一直投不出去"**不是机制坏**,而是**目标会话长期 `working`**(07:31→08:36 共 30+ 轮)。
|
||
⇒ 另一条自查也坐实:**粗域锁只挡得住"同一个域字符串"**(`handoff-guard.sh:197` `awk '$1==want'`),
|
||
而写这个字符串的恰好是**它自己的派活模板** —— 所以本轮"同字符串能抢到 / 上一棒同字符串被挡",
|
||
差别**只在持有人当时是不是它自己**。
|
||
|
||
### 台账 / 留证(全走正规通道)
|
||
- `--report M5 --state done --by "主控·唤醒机制线·验tick投递" --line 唤醒机制 --artifact "第 82 行 …"` ⇒ `OK 已上报:M5 running -> done`
|
||
- `tmp/supervise-inbox/blocked.json` ⇒ **已摘除 M5**(回到 `{}`)—— 解除条件① 已满足。
|
||
- ⚠️ 本轮我**先写了 blocked.json 才察觉条件已解除** ⇒ 已按"解除后记得摘掉"清掉,⛔ 不留过期阻碍。
|
||
- ✅ **删掉本轮刚建的复验棒** `07b51661-…`(M5 已 done ⇒ 成冗余排期;属 §F「删冗余」⇒ 可直接做、须报告)。
|
||
- ⛔ `NEED-USER.md` 未动(理由见上文那节:程序每轮覆写的全局告警件,当前载的是**另一条线**的告警)。
|
||
- 收尾清本棒 `tmp/`:已删 `tmp/_q-sessions.py`。
|
||
|
||
### 本线剩余缺口(收尾自判)
|
||
- 台账 **M1–M7 全 done**;`V1-投递` 的**实测已满足**,但 `goal.json.acceptance_state["V1-投递"]` 仍写 **`fail`**
|
||
⇒ 登记值与实测不一致 ⇒ 程序继续判"验收未过" ⇒ 唤醒回路继续空转。
|
||
- ⚠️ `acceptance_state` 属**验收声明**(技能:由用户说/主会话提),⛔ 不在本棒任务书范围内 ⇒
|
||
**未自行改写 goal.json**,改为**派一棒按实测刷新**(并留「若判定不属协作棒职权 ⇒ 上抛主会话」的出口)。
|
||
|
||
---
|
||
|
||
## 十一、(本轮)会话哑掉(诊断日志撞 10 MiB)—— 真因坐实 + 机制已修(2026-10-01 09:00)
|
||
|
||
**用户报障**:「这个会话 又不输出内容了」(指主会话 `f8a792ab`)。
|
||
|
||
**真因(实测 · ⛔ 不是协作逻辑写错)**:宿主**单会话诊断日志有 10 MiB 硬上限**,撞上限后**拒写**
|
||
(`logs/daemon.log` → `[conversations] diagnostic log write failed`,code=**EPERM**)⇒ 该会话**界面不再刷新**。
|
||
- `f8a792ab` 日志停在 **10,485,715 B**(mtime **08:08**),此后零增长;累计 **45,422 行 / 8.9 MB 被丢**。
|
||
- 日志构成:40,257 行里 **36,747 行是同一条重复的 `tool_call_update`**(同一 requestId 刷数百遍)⇒ 半小时推爆。
|
||
- 对照实测:纯聊天会话 `tool_call_update = 0`、整会话仅 15 KB;本会话 2.3 MB;哑会话 10.5 MB
|
||
⇒ **10 MiB 是"工具调用密度"的墙,不是聊天的墙**(150 倍差距)。
|
||
- 轮转机制**存在**(`a202550c` 转出 3 个 `.log.N`,每个 ~10 MiB)⇒ 本次是**轮转没成**才直接哑。
|
||
|
||
**机制缺陷(本轮已修)**:投递只认 `http=200` 判成功 ⇒ **向已哑会话投递仍记 `ok:true`(假绿)**,通知全落空而机制无感。
|
||
- 🔴 **关键路径(第一版修法没拦住的原因,实测踩到)**:`f8a792ab` 虽已 `completed`,**却仍在网关口 live**
|
||
⇒ 登记 `roles.main` 直接命中 ⇒ **绕过 `_pick_live`** ⇒ 闸必须加在"出手前"。
|
||
|
||
**改动**(技能 `multi-session-collab/scripts/collabd.py`;全平台共用 ⇒ 持**独占**锁操作,**已释放**):
|
||
1. 新增 `_deaf_sids()`:`logs/<今|昨>/sdk/conversations/<sid>.log` ≥ 9.5 MiB ⇒ 判**哑**(只读 · 扫不到即空集)。
|
||
2. `_pick_live()`:排除哑会话(⛔ 不当主会话)。
|
||
3. `_deliver_str()` **两道闸**:① 粗判后(按登记目标)② **出手前**(按网关实际目标,`skipped=target-deaf`)
|
||
⇒ ⛔ 不投 + `need_user()` 喊用户换会话。
|
||
4. 验证:`ast` 过 + `selftest.py` **PASS 35 / FAIL 0** + 实测 `--tick` **未新增投递**(wakeups 尾行仍 08:55:03)、
|
||
日志 `停投:目标…已哑` 命中 1 次。
|
||
|
||
**遗留(⛔ 未做,明确交接)**:
|
||
- 未归档 `f8a792ab`、未换主会话(属**会话归属** ⇒ ⛔ 不在本棒职权,须用户/主会话定)。
|
||
- `NEED-USER.md` 现载的仍是**退役"手机接入 / 垫片 20090"文案**(`board_ext.py` 两句残留)⇒ 待清。
|
||
- `selftest.py` **尚未补本坑的回归用例**(本轮预算耗尽 ⇒ 踩坑先记此处,下棒补)。
|
||
- ⚠️ 本会话日志 20 分钟已 2.3 MB ⇒ 按此速度约 40 分钟到上限 ⇒ **须换会话**。
|
||
|
||
## 十二、阈值交接规则**已固化**(2026-10-01 09:05 · 用户定则)
|
||
|
||
用户原话:「**到达阈值后 继续对话自动创建接续会话处理**,固化到会话规则中」。
|
||
|
||
- ✅ 已写入 `CODEBUDDY.md` **§1.5 G**(实体章 ⇒ 每会话自动加载,下一请求即生效)。
|
||
- 规则要点:**两条阈值**(上下文告警 / 本会话日志 ≥ 9.5 MiB)命中任一 ⇒
|
||
**① 不硬撑 ② 先落盘交接材料 ③ 开接续会话(走 `automation_update`,白名单免确认)④ 告知用户 + 释放锁**。
|
||
- ⛔ 明确禁止"为此建定时轮询式自动化"(属 §F 白名单外)。
|
||
- ⚠️ 遗留:钩子侧**尚未**自动注入"本会话已到阈值"(现靠宿主自身的预算告警 + 会话自判)⇒ 待实现。
|
||
|
||
### 十二-b 阈值**改线**(2026-10-01 09:07 · 用户定)
|
||
|
||
- **上下文线:120 K(宿主告警)⇒ 36,000 token**(用户原话:「把上下文阈值改为36K」)。
|
||
理由=历史追加式全量重发 ⇒ 早换会话直接省钱;宿主的 120 K 告警**太晚,⛔ 不要等它**。
|
||
- **日志线:9.5 MiB ⇒ 5 MiB**(我定的,用户授权"你定个合适的")。
|
||
判据:=宿主硬上限 10 MiB 的一半;实测正常会话 2~3 MB/20 分钟 ⇒ 5 MiB ≈ 35~40 分钟的量,留足交接缓冲。
|
||
⚠️ 它是**安全兜底**(防"上下文没涨多少、日志被事件洪水写爆"),**不是成本线** ⇒ 正常会话碰不到它。
|
||
- 🔴 **两条 5 MiB / 9.5 MiB 分工必须记牢**:5 MiB =「该交接了」(会话主动换);
|
||
9.5 MiB = `collabd.py _deaf_sids()` 的「已哑、不许再投」拦截线。⛔ 别混。
|
||
- ✅ 已改 `CODEBUDDY.md §1.5 G` 两处(锁:`阈值改 36K`,已释放)。
|
||
|
||
## 十三、(本轮)会话哑掉线收尾:补回归用例 + 清看板退役文案 + 归属报到用户(2026-10-01 09:1x–09:3x)
|
||
|
||
**来源**:阈值交接棒(自动化会话 `345e6839`;上棒因到阈值交接,背景见 §十一 §十二)。锁:机制层 ⇒ 持**全局独占**,**已释放**。
|
||
|
||
**做掉 2 件**:
|
||
1. `multi-session-collab/scripts/selftest.py` **补 2 条回归用例**(本坑):① `_deaf_sids()` ≥9.5 MiB 判哑(含 **9,499,999 B 边界不判哑** + 结果恰好一条)② 投递命中哑目标 ⇒ `skipped=target-deaf` + **零投递** + 落 `NEED-USER.md`。
|
||
- 🔑 不碰生产的做法:`_deaf_sids()` **每次调用现读** `CODEBUDDY_CONFIG_DIR` ⇒ 用例临时改指 `tmp/selftest/fake-cfg/`(`truncate` 造**稀疏文件**拿 10 MiB 体积读数),**finally 还原**(`_db()` 也读同一变量 ⇒ 不还原会把后面用例全带偏)。
|
||
- 验证:`python selftest.py` ⇒ **PASS 37 / FAIL 0**(基线 35 + 新增 2)。
|
||
2. `.workbuddy/collab/board_ext.py` **清 2 处退役文案**:① 文件头「手机接入」线 → 「本机协作」(退役线名)② chip① `dsh-client · 设备接入 · 本地中转 :20090` → `dsh-plugin-ai1net · …`(`dsh-client` 是**退役包名**:原 `@dsh-client/device-shim` 09-29 已归并进 `dsh-plugin-ai1net/lib/device-access.js`=模块⑥「设备接入」)。
|
||
- 实测直调 `build()`:退役词 **0 命中**。**有意保留**:「本地中转」(09-30「说人话」批量替换有意定的)、chip② 的 `dsh-client`(属**另一条组件**、未取证 ⇒ ⛔ 不改)、裸端口 `20090`(**真读数**)。
|
||
|
||
**⛔ 未动 1 件(判不准 ⇒ 报用户)**:`f8a792ab` 仍登记 `roles.main` 且**已哑**。取证:completed(08:55) + 今天日志 **10.0 MB**;`a202550c` completed(07:13);**唯一带「主控」前缀的活会话 `345e6839` = 本自动化会话自身(`is_background_automation=1` ⇒ 用户看不见、不可接续)** ⇒ **无可接任者**,改认谁都是错的。⇒ 未改 roles、未归档(宿主库 `sessions` 属活库,⛔ 更不碰)。
|
||
|
||
**🔴 本轮新发现(⛔ 未动手,超清单,登记待派)**:`_deaf_sids()` 扫 **今天+昨天**,而宿主日志**按天分目录** ⇒ **昨天撞过上限、今天已正常轮转**的会话被**永久判哑**。实证 `a202550c`:昨天 10.0 MB、**今天 4.2 MB(宿主仍在写它)** ⇒ 它没哑却仍判哑 ⇒ **方向相反的新假绿**(该投的被拦,09:12 那条 NEED-USER 即由此而来)。判据收窄(只认最新一天)有取舍,属机制层 ⇒ ⛔ 本轮不改。
|
||
- 报告:`交付物/会话哑掉线-收尾报告-20261001.md`。
|
||
|
||
### §十三 主会话「动态解析」vs「旧登记」—— 两个 id 的来历(10-01 09:2x · 纯取证,⛔ 未改任何文件)
|
||
> 用户问「看板主会话框显示 `345e6839`,那 `f8a792ab` 是在哪看到的」⇒ 澄清如下(**长期判据,值得记**):
|
||
|
||
🔴 **看板主会话框 ≠ 登记值**。`board.py::_resolve_main()` 是**动态解析**(🔴 与 `collabd.py::resolve_main()` / `_scan_mains()` **必须同款**,⛔ 改一处必须改两处):
|
||
① 本工作区(`cwd == 本工作区`)里标题**以「主控」开头**的 ⇒ 认(用户自己标的,优先)
|
||
② 否则本工作区、标题**不带 `[协作]`**、**最近活动**的那条(⚠️ **不按 `status='working'` 筛** —— 主会话两轮之间本就空闲,筛了永远漏)
|
||
③ 都取不到 ⇒ 退回**登记** `_main_sid()`;再没有 ⇒ `""`(⛔ 不猜)
|
||
|
||
⇒ 当前 `345e6839`(标题「主控 · 协作机制 · 阈值交接收尾」)走①命中 ⇒ 看板/`交付物/本机协作-实时状态.md` 都显示它是主会话。
|
||
⇒ `f8a792ab` 只是 ①`tmp/supervise-inbox/collabd-state.json` 的 **`roles` 旧登记**(`"f8a792ab-…": "main"`,仅在①②落空时才被退到)②我上棒 **08:47 的离线快照** `tmp/rendered-arch.html` ③会话清单里一条已完成会话(标题「检查多会话协作机制并修复问题」)—— **⛔ 都不是"当前主会话"**。
|
||
⚠️ **教训**:离线快照里的主会话 id8 **是渲染那一刻的值**,过后主会话一换就**过期** ⇒ ⛔ 别拿快照当现状(`agent-operating-rules §1.7b`)。上棒报告写「主会话 = `f8a792ab`」已被此条修正。
|
||
|
||
🔴 **「负责类别:—(未在对话里说明)」= 反查落空的如实说明** —— ⛔ 不是报错、⛔ 不代表这条会话没干活。
|
||
判据:`TOPS.filter(t => MBT[t] === main.id8)` 反查 `main_by_topic`(`{类别: sid8}`);类别取自**标题里出现的 `goal.topics` 项**(最长优先)。
|
||
当前 `topics = ["唤醒机制"]`,而「唤醒机制」的主会话是 `a202550c` ⇒ `345e6839` 标题里**没有任何类别名** ⇒ 反查 0 条 ⇒ 按 §0.5.6 设计**明说**「未在对话里说明」(同族红线:**查不到也不许闭嘴**)。
|
||
⇒ 该行文案来历:2026-10-01 用户「**唤醒机制如果是主会话的事就放到主会话框里去**」⇒ 「任务类别」从第三层那排搬进主会话框,R2 高 84→112。
|
||
|
||
### §十四 结案:会话归属 —— **接续会话就是主会话的继任者**(10-01 09:2x · 用户纠正)
|
||
|
||
用户原话:「**主会话是会创建 接续会话 顶替的**」⇒ 纠正我上棒「唯一活会话是自动化会话 ⇒ 无可接任者 ⇒ 归属判不准」的结论。
|
||
|
||
🔴 **修正后的判据**:主会话靠「**创建接续会话顶替自己**」延续(§1.5 G 的阈值规则就是这个机制);`345e6839` **本身就是**这样一条接续会话(它顶替了 `f8a792ab`)。
|
||
⇒ 看板/实时状态认它为主会话 = **正确**,⛔ **不是缺陷、不用修**。
|
||
⇒ **遗留 #3 结案:⛔ 不需要动任何登记** —— 动态解析(判据①)已优先认它;`f8a792ab` 的 `roles` 旧登记**只在解析落空时才被退到** ⇒ 无害,自然过期即可,⛔ 不必手动摘。
|
||
|
||
⚠️🔴 **我上棒错在哪**:把「**自动化开的会话**」误判成「**用户看不见、不可接续**」。**接续会话本来就走自动化通道创建**(自动化是唯一能开新会话的通道)⇒ 它接的**就是**主会话的班。⛔ 别再把「是自动化会话」当成「当不了主会话」的理由。
|
||
|
||
⇒ 🔴 **本会话 `345e6839` 已达阈值**(上下文 **163 K ≫ 36 K** 线)⇒ 按 §1.5 G 已**开接续会话顶替自己**:automation **`ca054445-bb69-44f4-9d5d-3736da219dea`**(排期 2026-10-01 09:28,唯一任务=修 `collabd.py::_deaf_sids()` 的「永久判哑」缺陷 + 补回归用例)。
|
||
|
||
## 十五、(本轮)**接续会话棒**:`_deaf_sids()`「永久判哑」已修 + 回归用例(2026-10-01 09:28–09:4x)
|
||
|
||
**身份**:本会话 `5f607d3e` = §十四 那条接续会话(automation `ca054445`),**继承主会话职能**(§十四 结案)。
|
||
锁:机制层 ⇒ **全局独占**(`--claim-exec "5f607d3e"`),收尾已 `--release-exec`。
|
||
|
||
**改了什么**(`~/.workbuddy/skills/multi-session-collab/scripts/collabd.py::_deaf_sids()`):
|
||
- 判据由「今天+昨天**任一** ≥9.5 MiB ⇒ 判哑」收窄为「**每个会话取它最新一天**那份日志 ≥9.5 MiB ⇒ 判哑」
|
||
(实现:按 `昨天 → 今天` 顺序建 `sid -> size` 字典 ⇒ 今天的值**覆盖**昨天的)。
|
||
- ⚠️ **兜底保留**:某会话今天**没有**日志 ⇒ 仍退到昨天那份判(比"直接放行"保守)。
|
||
- 🔴 病根复述:宿主会话日志**按天分目录**、昨天那份**永不再增长** ⇒ 旧写法把「昨天撞过上限、今天已正常轮转」的会话**永久判哑**
|
||
⇒ **方向相反的新假绿**(该投的被静默拦下)。
|
||
|
||
**生产数据只读实测**(⛔ 未跑 `--tick`,避免真投递):旧判据 **7** 条哑 → 新判据 **6** 条哑;
|
||
- 唯一被收窄掉的正是 **`a202550c`**(昨 10.49 MB / 今 4.35 MB,宿主仍在写)✅
|
||
- **`f8a792ab`(今日 10,485,715 B)仍判哑** ✅ —— 第一轮那道闸没被削掉。
|
||
- 🔴 **副作用面=零**:主会话解析(旧 vs 新,同一次只读对比)**结果逐字相同**=`5f607d3e` · `prefix:主控`。
|
||
|
||
**回归用例**(`scripts/selftest.py`):
|
||
1. +1 用例「只认最新一天」(昨 10 MB/今 4.2 MB ⇒ ⛔ 不判哑;今天才撞上限 ⇒ 判哑;只有昨天 ⇒ 仍判哑;含**精确集合**断言)。
|
||
2. `_fake_cfg()` 增 `day_offset`(0=今天 / 1=昨天);`_prepare()` **每轮清空** `tmp/selftest/fake-cfg`
|
||
—— 🔑 防的是**"第二轮才红"的跨轮污染**(假配置树跨轮留文件,而哑用例断言的是精确集合)。
|
||
3. ✅ `python selftest.py` ⇒ **PASS 38 / FAIL 0**(基线 37 + 新增 1);**连跑两次均绿**。
|
||
|
||
🔴 **顺带修掉一条环境假红(⛔ 与本次改动无关 —— 既有用例前提覆盖不全)**:
|
||
「僵尸对账」用例只靠 `adopted` 反推前提的**一半**,而僵尸分支的完整前提 =「除观察者外无人在跑」**∧「无 IN_PROGRESS 运行」**。
|
||
⇒ **自动化会话跑本自测时**,它**自己**的 automation run 必然 `IN_PROGRESS`(且它是唯一在跑的会话,`CODEBUDDY_SESSION_ID` 已让 `me` 命中)
|
||
⇒ 前提不成立却不 SKIP ⇒ **必然假红**(实测:收窄前整套 **36/1**,只有本条红;`reconcile` 返回 `adopted=[] zombie=[]`)。
|
||
⇒ 照该用例既有的「前提不成立就 SKIP」姿势**补上后一半**(**纯测试侧**,⛔ 未动生产逻辑)。
|
||
⚠️ **教训**:`selftest` 里凡"靠副作用反推前提"的用例,都要把**全部**前提显式判一遍 —— 半覆盖 = 换个环境就假红。
|
||
|
||
**⛔ 未做**:未 `commit`/`push`(未获指令);未把技能「本机 → 服务器」单向推 + md5 双端对账(留待按 §1.5 B 阶段 5 处理)。
|
||
报告:`交付物/会话哑掉线-收尾报告-20261001.md` §七。
|
||
|
||
## 十六、主会话**分组提案**(用户 09:38 提出)—— 现状归因核对 + 评估(⛔ 未动代码)
|
||
|
||
**用户两问**:① 「所有会话都会按底层规则到阈值后创建接续会话 ⇒ 没有固定的主会话 ⇒ 投递不准,是这样吗?」
|
||
② 提议:主会话下建**三类子会话**(任务协作/上报处理/唤醒),各组到阈值**组内顶替**;用户只操作主会话(定目标、建初始协作会话、按需建后续)。
|
||
|
||
**核对结论(L2 代码 + L3 本机实测)**:
|
||
- ① **归因半对**:阈值接续确实**全会话通用**(`CODEBUDDY.md §1.5 G`),但**"主会话"从来不是固定 id** ——
|
||
它是 `collabd.py::resolve_main()` **动态解析出的角色**:`roles登记且活` → `本工作区标题带「主控」且活` →
|
||
`本工作区最近活动(标题不含 [协作])` → `退回登记`。⇒ 「不固定」是**设计使然**,⛔ 不是缺陷本身。
|
||
- 🔴 **投递不准的真因**=**判据是"人写的标题字符串 + 最近活动"**,且**候选池没有分组**:
|
||
实测本工作区 **候选 15 条**(标题不含 `[协作]`),其中只有 7 条真带「主控」
|
||
⇒ `23d447d3`/`f8a792ab`(标题「检查多会话协作机制并修复问题」)、`fe146dd9`(「接续 · 机制线…」)等
|
||
**全在池子里**,谁最近活动谁就可能被认成主会话 ⇒ **投错窗口**。
|
||
- 🔴 **必须纠正用户一处前提**:主会话**自己也会撞 10 MiB / 36K 阈值**,也得被顶替
|
||
⇒ 「固定主会话」**不可达**;可达的目标是「**角色稳定、实例可换**」。
|
||
|
||
**对提案的评估(技术实现由我定)**:方向**对**(用**结构性分组**替掉"标题+最近活动"的猜),但三组要分开看:
|
||
| 组 | 判断 | 理由 |
|
||
|---|---|---|
|
||
| 1 任务协作(多类别前缀) | ✅ **已有** | `[协作]-<类别>-<具体>` 已在用,只需纳入解析判据 |
|
||
| 3 唤醒 | ✅ **已有** | `[唤醒]` 前缀 + 10-01 三条定性 |
|
||
| 2 上报处理 | ⛔ **建议先不建** | 现在上报(`--report`/`TO-MAIN`)直投主会话=主会话只做判断;再插一层=**多一类常驻身份**(成本先付、收益未证),且与"主会话只做判断与派活"的边界要重划。**重开判据**:实测到"主会话被上报淹"再拆 |
|
||
|
||
**落地三件(真正要改的地方,⛔ 本轮只登记不动手)**:
|
||
1. **角色=字段**:接续/派活时把角色写进 `roles` 登记(现登记是**第④位兜底**,应提到**第①位**)。
|
||
2. **顶替时角色自动传承**:建接续会话时把父会话的角色登记**转交**(现靠人给标题起名带「主控」,是约定不是保证)。
|
||
3. **候选池按分组收窄**:主会话候选池**只认"主控组"**,`[协作]`/`[唤醒]`/其它**一律排除**(现在只排 `[协作]`)。
|
||
|
||
**阈值处置**(`§1.5 G`):本会话上下文 **144 K ≫ 36 K** ⇒ ① 本轮不复核代码 ② 已落盘本节(交接材料)
|
||
③ **开接续会话**(automation,唯一任务=把本提案落成正式方案单,⛔ 不实施)④ 告知用户并释放锁(⛔ 本会话未持锁,无需释放)。
|
||
|
||
---
|
||
|
||
## 十七、(本轮)**接续会话到底有没有用?「同一会话+上下文压缩」能否替代?** —— 实测取证(2026-10-01 09:43–09:5x)
|
||
|
||
**用户原话**:「还有接续会话是否真的有用,真解决了问题吗,同一个会话持续聊天 通过上下文压缩的方式是否也可以正常执行 协作任务」
|
||
|
||
**结论**:**接续会话真有用,但它只做「复位」、不做「止血」**;上下文压缩**只能替代「成本」那一半**,
|
||
对「哑掉」那一半**零作用** —— 有实测反证(见下第 4 条)。
|
||
|
||
**一、两条失效必须分开看(分开之后答案自明)**
|
||
|
||
| 失效 | 驱动量 | 上下文压缩能解? | 接续会话能解? |
|
||
|---|---|---|---|
|
||
| 单轮越来越贵 | **送模型的历史长度** | 🟡 部分(压缩本身要付一次全量清算,且有损) | ✅ 历史归零 |
|
||
| 界面**静默哑掉** | **本会话磁盘日志体积** | ⛔ **完全不能** | ✅ 新会话新文件从 0 起 |
|
||
|
||
**二、实测读数(2026-10-01 · 日志**只读**,⛔ 未跑 `--tick`、未动生产日志目录一个字节)**
|
||
|
||
1. 🔴 **日志 97.3% 的字节=同一条流式事件**:`5f607d3e` 共 23,528 行 / 5,766,920+ B,其中 `event-machine:dispatch` = **5,768,726 B = 97.3%**(`state-machine:transition` 1.5%,其余全部 <1%)。重复度最高的整行(抹掉时间戳/requestId 后)= `{"input":"tool_call_update",…}` **×15,695 + ×6,747 = 22,442 行(95%)**。
|
||
⇒ 该事件按**工具调用进度**投递,**与「历史有多长」无关**。
|
||
2. **涨速 188~341 KB/分** ⇒ 干活中的会话 **30~55 分钟**写满 10 MiB:`f8a792ab` 54.6 分/188 KB·min⁻¹|`5f607d3e` 16.5 分/341|`345e6839` 18.7 分/232。
|
||
3. **撞顶后的形态**(`f8a792ab` 尾部实证):`diagnostic-log:dropped {"droppedLines":153,"droppedBytes":37446}` ⇒ 宿主开始**丢行**;该文件停在 10,485,715 B,时间戳断在 `10-01T00:08:36Z`(本地 **08:08**)**之后再无一行** ⇒ **静默**(界面不刷新正是这个)。
|
||
4. 🔴🔴 **压缩与撞顶零相关(决定性反证)**:全部出现过 `compact-summary` 的会话 —— `6ecf6d98`/`f8a792ab`/`13f04696`/`3e52604e`/`9999f351`,**5 个全部撞满 10 MiB**,且**都是「压缩×2」之后照撞**;反例:`42fcfd49` 压缩 **4 次**仅 7.5 MB、`ac8de40d` 压缩 **4 次**仅 1.8 MB。
|
||
⇒ **决定日志体积的是「干活量」,不是上下文**。压缩次数与体积**无单调关系**。
|
||
|
||
**三、机理(为什么压缩救不了)**:压缩动的是「**送到模型的历史**」(payload);日志写的是「**本机发生的事件**」(side effect)—— 两个不同的量。压缩本身还是一次模型调用 ⇒ 反而**多**一轮事件。
|
||
|
||
**四、接续会话的真实缺陷(诚实登记)**:它是**周期性复位**,不是**治本**;治本=**压单会话输出体量**(大输出先落盘、只读关键行 —— 项目已有成本律)。它的缺陷**不是「有没有用」**,而是**复位时角色没跟着走**(=§十六 的落地三件)。⇒ 用户上一轮感到的"投递不准",根因在这里,⛔ 不在接续本身。
|
||
|
||
**五、本会话自己就是一例**:`5f607d3e` 09:28 起跑,17.8 分钟已 **6,256,665 B**(≈351 KB/分)⇒ 照此 **09:58 前后撞 10 MiB**;**已越过 `§1.5 G` 的 5 MiB 线**(同规则第 2 条阈值)。
|
||
|
||
**六、处置**:① **保留**接续会话 + 补 §十六 三件 ② **治本**=压单轮输出体量 ③ 压缩只可当**过渡**,⛔ 不能当替代 ④ 本会话已越 5 MiB ⇒ 按 `§1.5 G` **把本轮结论并入既有接续会话 `3277fc39`**(09:49 那条,原文只要求写方案单)⇒ 让方案单含「接续机制有效性」一节;⛔ **未新建任何自动化**;⛔ 本会话未持锁,无需释放。
|
||
|
||
---
|
||
|
||
## 十八、「**是哪里一直在给会话加 10 MiB 日志**」—— 归因取证(2026-10-01 09:47–09:5x · 用户追问)
|
||
|
||
**用户原话**:「为什么哑的这么快,以前正常用的时候也没遇到这个问题,是哪里一直在给会话增加 10MiB 的日志」
|
||
|
||
**结论(一句话)**:🔴 **没有任何进程在"写文件"** —— 是宿主**事件机**在工具调用期间**把同一条空状态按秒重投**(单次调用可达 3 万条);而 10 MiB 余量是按**"人机对话"**设计的,**跑一次十几分钟的命令就能吃掉 7.6 MB**。
|
||
|
||
**一、归因读数(只读 · ⛔ 未动生产日志一字节)**
|
||
|
||
| 项 | 读数 |
|
||
|---|---|
|
||
| 单次调用事件数(`f8a792ab`) | requestId `01a0f4bf6e` = **30,044 条**,跨度 **12.0 分钟** ⇒ **41.7 条/秒** |
|
||
| 🔴 **载荷重复度** | 30,044 条里**互不相同的载荷只有 91 种**;其中 **1 种逐字重复 29,268 次** |
|
||
| 载荷内容 | `{"completeAssistantStream":false,"hasUsage":false,"hasTitle":false,"resourceEffectCount":0}` ⇒ **全 false 的空壳,零新信息** |
|
||
| 单秒峰值 | **415~497 条/秒** |
|
||
| 占比 | 40,257 行里 29,268 行是那同一条 ⇒ **≈72.7% 的行 ≈ 7.6 MB**(**一条调用就写掉 10 MiB 的 73%**) |
|
||
|
||
**二、四个会话全部命中同一形态**(⇒ 不是偶发)
|
||
|
||
| 会话 | 最大单次调用 | 该调用跨度 | 会话总长 |
|
||
|---|---|---|---|
|
||
| `f8a792ab`(10-01) | 30,044 条 | 12.0 分 | 54.6 分 |
|
||
| `5f607d3e`(10-01) | 16,582 条 | 19.5 分 | 19.3 分 |
|
||
| `706d2476`(09-30) | 25,143 条 | 22.5 分 | 22.5 分 |
|
||
| `f0fb0dbc`(09-29) | 8,243 条 | 49.8 分 | 49.1 分 |
|
||
|
||
⇒ **每个会话都被 1~3 个"超长调用"撑起来**,速率 2.8~42 条/秒。
|
||
|
||
**三、🔴 为什么"以前正常用没遇到"(有直接反例)**
|
||
|
||
| 会话 | 存活 | dispatch 条数 | 日志 |
|
||
|---|---|---|---|
|
||
| `11a263a2` | **102 分钟** | **9 条** | **2,846 B** |
|
||
| `12732a10` | 19.9 分钟 | **0 条** | 2,271 B |
|
||
| `f8a792ab` | 54.6 分钟 | 38,612 条 | 10,485,715 B ⇒ **哑** |
|
||
|
||
⇒ 🔴 **会话活得久 ≠ 日志大**(102 分钟只有 2.8 KB)。体积**只由"有没有在跑工具调用、跑多久"决定**。
|
||
⇒ **普通用法=聊 + 短问答 ⇒ 近乎 0 条 dispatch**;本工作区=**连续跑分钟级命令**(实测单次调用 12~50 分钟)⇒ 直接拉满。
|
||
⇒ ⛔ **不是宿主版本回归**:09-29 的日志已是这个形态(8,243 条/单调用),**早于** 10-01 的 update 日志。
|
||
|
||
**四、推论**:① 10 MiB 是按「对话」设计的余量,对「agent 连续跑长命令」**不够用**;② 唯一有效手法=**压单次工具调用的时长与输出量**(大输出先落盘、只读关键行、⛔ 不跑长时间流式命令);③ 接续只是**复位**(§十七)。
|
||
|
||
**五、处置**:本节读数**已并入接续会话 `3277fc39` 的方案单要求**(与 §十七 同源);⛔ 未改一行代码、⛔ 未新建自动化、⛔ 未持锁。
|
||
|
||
---
|
||
|
||
## 十九、🔴 **纠正 §十八 的归因**:⛔「agent 连续跑长命令」是**错的** —— 真因=**宿主按 ~100 倍冗余逐帧落盘**(2026-10-01 09:51 · 用户质疑后复核)
|
||
|
||
**用户质疑原话**:「什么时候agent连续跑长命令了,还一直投递空状态,谁要求的」
|
||
|
||
**结论**:🔴 **§十八 里"agent 连续跑分钟级命令"是未经证实的推断,现予撤回**(用户质疑成立)。复核证据如下。
|
||
|
||
**一、动作速率:根本不存在"长命令"**
|
||
|
||
| 会话 | 存活 | 真实动作 `tool_call` | 速率 | 日志 `tool_call_update` | 🔴 **放大倍数** |
|
||
|---|---|---|---|---|---|
|
||
| `f8a792ab` | 54.6 分 | 449 | 8.2 次/分(每 7.3 s) | 36,747 | **82×** |
|
||
| `5f607d3e` | 25.0 分 | 284 | 11.4 次/分(每 5.3 s) | 37,282 | **131×** |
|
||
| `706d2476` | 22.5 分 | 126 | 5.6 次/分(每 10.7 s) | 24,590 | **195×** |
|
||
| `f0fb0dbc` | 49.1 分 | 54 | 1.1 次/分(每 54.5 s) | 8,012 | **148×** |
|
||
|
||
⇒ 🔴 最快也只有 **11.4 次/分**,调用**又短又碎**,⛔ **不是"长命令"**。
|
||
⇒ 真因是 **"每跑一次被写多少条"**:**82~195 倍冗余**。
|
||
|
||
**二、turn 结束原因:正常结束也照样灌爆**
|
||
`f8a792ab` 有 2 次 `TURN_ERROR/refusal`;但 `5f607d3e` 是 4 次 `TURN_COMPLETED/end_turn`、`706d2476` 与 `f0fb0dbc` 各 1 次 `end_turn`。
|
||
⇒ ⛔ **不是"错误/重试循环"导致**(我中途的第二个假设同样不成立)。**正常完成的 turn 一样撞顶**。
|
||
|
||
**三、量纲(可据此推算)**
|
||
`日志字节 ≈ 事件条数 × 265 B`;`事件条数 ≈ 工具调用次数 × ~150`
|
||
`f8a792ab`:39,632 事件 × 265 B ≈ **10.3 MB** ✅ 与实测 10,485,715 B **吻合**。
|
||
|
||
**四、"谁要求的" —— 没有人要求**
|
||
- 本项目**没有任何规则/脚本/自动化**要求记录工具调用进度。这是**宿主内置的诊断日志**(`logs/<日期>/sdk/conversations/<sid>.log`)在 SDK 事件流上**逐帧、无去重、无节流**落盘 —— 连"状态没变"的帧也记(§十八实测:30,044 条里载荷只有 91 种,平均每种被投 **330 次**)。
|
||
- 本机配置目录 `E:/ProgramData/.workbuddy/` **未找到** logLevel/verbosity/diagnostic 之类开关 ⇒ ⛔ 我们这侧**关不掉**。
|
||
|
||
**五、🔴 治本(纠正 §十八 的表述)**
|
||
⛔ **不是"压单次工具调用的时长"**(本来就没有长命令),而是 **压工具调用次数** —— 这与项目既有成本律**是同一条杠杆**(压一轮工具调用次数:大输出先落盘只读关键行/合并多步为一步/限流)。
|
||
不变结论:接续会话=**复位**(非止血)。
|
||
|
||
**六、处置**:已将本纠正**同步进接续会话 `3277fc39` 的方案单要求**(覆盖原第⑥节"压时长"的表述);⛔ 未改一行代码、⛔ 未新建自动化、⛔ 未持锁。
|
||
|
||
---
|
||
|
||
## 二十、(本轮)**主会话分组方案单已入库**(`04-调整方案/151`)+ **现值取证抓到一条红线级实况**(2026-10-01 09:49–10:0x)
|
||
|
||
**身份**:本会话 `7fbefc96` = §十六/§十七 那条接续棒(automation `3277fc39`,上一棒 `5f607d3e`)。
|
||
锁:域锁 `dsh-server-docs/04-调整方案` + 单级锁 `151` ⇒ **已反序释放**(另 1 把属他人,按 R9 未动)。
|
||
|
||
**交付**:`04-调整方案/151-多会话协作-主会话分组方案-20261001.md`(20,176 B · 七节 · LF)。
|
||
内容=① 现值取证 ② 用户提案原文(①原话/②③转述,已标口径)③ 方案对比表(A/B/C + 倾向 B)④ 落地步骤(3 件 + **本轮新增第 4 件**)⑤ 风险与影响面 ⑥ 接续机制有效性论证 ⑦ 待拍板(一轮一问)。
|
||
**已并入**:写入期间到达的 §十九 ⇒ 改写本单 §6.2-6 与 §6.4-2("治本=压时长"→ **压工具调用次数**)。
|
||
|
||
**🔴 本轮新发现(实测 · 未改代码)—— 归属解析的 ①级不过滤「哑」**:
|
||
- `resolve_main()` **①级(`roles` 登记)不经过 `_pick_live()`** ⇒ 不过滤哑会话;②③级才过滤。
|
||
- 实况:`roles` 登记 `f8a792ab` = `main`,而它**正是 §十七 里撞满 10 MiB 的哑会话**
|
||
⇒ `resolve_main()` 返回 `sid=f8a792ab / source=roles:main`,而「活 ∧ 不哑」= **False** ⇒ **投得进去 = 否**。
|
||
- 🔴 **自我强化**:`wake.sessionId` **也是** `f8a792ab` ⇒ `_main_sid()` 兜底同指它;`resolve_mains()['唤醒机制']`
|
||
= `f8a792ab / live-fallback` ⇒ **兜底兜回同一条哑会话**。无自愈。
|
||
- ⇒ 用户感到的「投递不准」**比 §十六 记录时更严重**:不只是"投错窗口",而是**投进一条不会醒的会话**。
|
||
已作为**落地第 4 件**写进方案单(①级复用 `_pick_live()`)+ 验收判据(造"登记为 main 的那条哑掉"的场景,⛔ 不许静默投)。
|
||
|
||
**候选池实测(只读 · 同轮)**:`cand` = **16 条**(标题不含 `[协作]`);显式「主控」`named` = **6 条**、**活着 0 条**;
|
||
`cand` 里「活 ∧ 不哑」= **1 条**=**本接续棒自己** ⇒ **除本会话外可用主会话候选 = 0 条**。
|
||
(`_deaf_sids()` = 6 条,与 §十五 修完后的读数一致。)
|
||
⚠️ **口径**:别用 `cand` 条数(16)代表可用性 —— 必须联判「活 ∧ 不哑」。
|
||
|
||
**⛔ 未做**:未动 `scripts/` 一字节(`collabd.py` mtime 停在 09:30/`selftest.py` 09:32,皆上一棒);未新建自动化;未改宿主库;未 commit/push;
|
||
未跑文档库四件套(派生件 `INDEX.md §二`/`docs-manifest.json` 在声明域之外 —— 留待该库统一整理)。
|
||
取证脚本落 `tmp/` 后已清(收口)。
|
||
|
||
---
|
||
|
||
## 二十、🔴 **源头定位**:这份日志记的**不是会话内容,是宿主自己的调度流水**(2026-10-01 09:5x)
|
||
|
||
**用户质疑原话**:「要说明源头 为什么会有这些问题,如何产生的 会话好端端的 不会没事自己写空状态日志把」
|
||
|
||
**结论**:**你说得对 —— 它不是在写"内容"**。实测 `logs/<日期>/sdk/conversations/<sid>.log` 里 **>99% 是宿主内部调度流水**,⛔ 不含模型输出、⛔ 不含工具结果。
|
||
|
||
**一、日志记的到底是什么(按行类型 · `f8a792ab` 实测)**
|
||
|
||
| 行类型 | 占比 | 记什么 | 实测细节 |
|
||
|---|---|---|---|
|
||
| `event-machine:dispatch` | **97.3%** | **每一次事件派发**写 1 行,**定长 244 B** | 载荷恒为 `{completeAssistantStream:false, hasUsage:false, hasTitle:false, resourceEffectCount:0}` ⇒ 字面即**"什么都没完成、什么都没带来"** |
|
||
| `state-machine:transition` | 2.5% | 宿主状态机每次相位切换 | 🔴 **596 次是 `working --PHASE_WORKING--> working` 自环(状态没变也记一条)**;`working↔planning` 来回 203/199 次 |
|
||
| `method:requests` / `:result` | 0.4% | 宿主**反复查询消息历史** | 每次 `byteLength` 恒 = **5 MiB**(固定缓冲);一次查询记 2 行 |
|
||
|
||
🔴 **决定性**:日志行**只有** `instanceId / input(事件类型) / requestId / 一个全 false 的 output 摘要`
|
||
⇒ **⛔ 不记事件的实际载荷**。所以"3 万条长得一模一样"**不能**直接读成"重复投递" ——
|
||
**而是这份日志本来只记"派发发生过"这一件事**(§十八"91 种载荷"一条据此**降级为不可用推论**)。
|
||
|
||
**二、"好端端的却在写" —— 因为写的是宿主自己的心跳**
|
||
`working→working` 自环 **596 次**=**状态没变也记一条**。这就是"空状态日志"的字面来源。
|
||
另:60% 的相邻行间隔 = **0 ms**(同毫秒成批落盘),累计 71 次"同毫秒多次迁移"⇒ 落盘是**批量**的。
|
||
|
||
**三、触发条件(为什么我们这儿特别猛)**
|
||
高频工具调用 ⇒ 宿主在大工作量下状态机高频摆动 + 每次派发记一行。
|
||
对照:**纯对话不触发这些调度** —— `11a263a2` 活 **102 分钟只有 9 条**(0 次工具调用)。
|
||
|
||
**四、没有人要求**
|
||
⛔ 本项目**无任何规则/脚本/自动化**要求记录这些;本机 `E:/ProgramData/.workbuddy/` **未找到** logLevel/verbosity/diagnostic 开关 ⇒ **我们这侧关不掉**。
|
||
|
||
**五、量纲与治本(合并 §十八/十九 的修正)**
|
||
`10 MiB ÷ 244 B ≈ 43,000 行` ⇒ **一次"干活 30 分钟"的会话正好写满**,与实测(30~55 分钟)吻合。
|
||
⇒ 治本=**降事件密度**(= 压工具调用次数,与项目成本律**同一杠杆**);接续会话=**复位**。
|
||
⇒ ⛔ 已撤回的表述:「agent 连续跑长命令」「压缩能替代」。
|
||
|
||
**六、处置**:本节读数已**并入接续会话 `3277fc39`**;⛔ 未改一行代码、⛔ 未新建自动化、⛔ 未持锁。
|
||
|
||
---
|
||
|
||
## 二十一、🔴🔴 **源码级根因**:宿主的日志过滤名单**漏了 `tool_call_update`**(2026-10-01 09:5x · 读 app.asar 取证)
|
||
|
||
**用户质疑原话**:「说了半天 还是不知道如何产生的 还在表面」
|
||
|
||
**取证方法**:解析 `C:/Users/Administrator/AppData/Local/Programs/WorkBuddy/resources/app.asar`
|
||
(302 MB / 17,800 个文件)⇒ 按**字节偏移反查命中文件** ⇒ 抽出 `main/conversations.js`(1,477,503 B)与
|
||
`main/handlers.js`(707,739 B)读源码。⛔ **只读,未改宿主一个字节**(抽取物已清)。
|
||
|
||
**一、产生链(逐环都落在源码上)**
|
||
|
||
| 环 | 位置 | 源码事实 |
|
||
|---|---|---|
|
||
| ① 事件源 | SDK → `handleSessionUpdate` | 工具调用的**每一次进度更新**=一个 `sessionUpdate` 帧 |
|
||
| ② 🔴 **采样闸** | `main/conversations.js::shouldLogEventMachineDispatch()` | `var STREAM_CHUNK_UPDATES = new Set(["agent_message_chunk","agent_thought_chunk"])`,`return !STREAM_CHUNK_UPDATES.has(sessionUpdate ?? "")` ⇒ **只滤「模型打字」与「模型思考」;`tool_call`/`tool_call_update`/`session_info_update`/`usage_update` 全部放行** |
|
||
| ③ 落日志 | 同文件 | `this.init.log("event-machine:dispatch", {input, requestId, seq, messageId, textLen, textHash, output:{...}})` ⇒ 每行 ~244 B |
|
||
| ④ 内存驻留队列 | `main/handlers.js::enqueue()` | 超限即**静默丢行**:`GLOBAL_MAX_LINES=20,000`|`GLOBAL_MAX_BYTES=8 MiB`|`PER_FILE_MAX_LINES=4,000`|`PER_FILE_MAX_BYTES=2 MiB` ⇒ `recordDropped()` |
|
||
| ⑤ 落盘/轮转 | `rotateIfNeeded()` / `rotateFile()` | 🔴 `PER_FILE_DISK_MAX_BYTES = 10485760`(**就是那个 10 MiB**)|`PER_FILE_ROTATE_COUNT = 3` ⇒ `.log.1/.2/.3`;**轮转失败 ⇒ 抛 `ROTATE_UNAVAILABLE` ⇒ 退避重试(1 s 起、上限 30 s)⇒ 期间队列积压 ⇒ 丢行** |
|
||
| ⑥ 丢行留痕 | `takeDroppedSummary()` | 汇总成一条 `diagnostic-log:dropped {droppedLines, droppedBytes}` = `f8a792ab` 尾部那条 |
|
||
|
||
**二、🔴 这就是"如何产生的":名单只覆盖「打字」,没覆盖「工具进度」**
|
||
- **人机对话**:事件几乎全是 `agent_message_chunk` ⇒ **被滤掉** ⇒ 日志近乎不涨(`11a263a2` 102 分钟=9 条 ✅)。
|
||
- **agent 干活**:事件主要是 `tool_call_update` ⇒ **不在名单** ⇒ **每一次都写一行**(449 次调用 ⇒ 36,747 行)。
|
||
⇒ ⛔ **不是"会话没事自己写",而是这套 verbosity 名单是为「对话」设计的,对「工具密集型」没有闸门。**
|
||
|
||
**三、旁证**:`WB_CONVERSATION_LOG_CONTENT=1` 是**反向开关**(设 1 ⇒ 连被滤掉的正文 chunk 也写、并加 `textPreview`);
|
||
实测我们日志里**没有** `textPreview` ⇒ 该开关**未开**,当前**已是默认精简态**。
|
||
|
||
**四、撤回清单(累计三处)**
|
||
⛔「agent 连续跑长命令」(§十九)|⛔「压缩导致的」(§十八)|⛔「91 种载荷 ⇒ 重复 330 次」(§二十)—— 均已撤回。
|
||
|
||
**五、治不了 vs 治得了**
|
||
- 宿主的过滤名单**我们这侧改不了**(⛔ 不改官方主程序;未找到能关掉 `tool_call_update` 的环境变量)。
|
||
- ⇒ 我们能做的只有**降工具调用次数**(与项目成本律**同一杠杆**);接续会话=**复位**。
|
||
|
||
**六、处置**:本节已并入接续会话 `3277fc39`;⛔ 未改宿主、⛔ 未改代码、⛔ 未新建自动化、⛔ 未持锁。
|
||
|
||
---
|
||
|
||
## 二十二、🔴🔴🔴 **最底层:turn 不是人发的 —— 宿主自己在连续注入**(2026-10-01 10:0x)
|
||
|
||
**用户质疑原话**:「关键是他在调什么工具,谁让他一直在调工具的?」
|
||
|
||
**取证**:读会话转录 `E:/ProgramData/.workbuddy/projects/e-ProgramData-AIProject-ai1net-dsh-server/f8a792ab-….jsonl`
|
||
(5,608,953 B / 1,354 行;只读)。结构:`function_call` **422**|`function_call_result` 422|`message` 84|`reasoning` 255|`file-history-snapshot` 168。
|
||
|
||
**一、调了什么工具(f8a792ab 全会话 422 次)**
|
||
|
||
| 工具 | 次数 |
|
||
|---|---|
|
||
| 🔴 **Bash** | **204** |
|
||
| 🔴 **Edit** | **119** |
|
||
| Read | 66 |
|
||
| Grep 10 | Write 9 | present_files 5 | automation_update 4 | PowerShell 2 | Skill 1 | widget_guidelines 1 | show_widget 1 | |
|
||
|
||
⇒ 全是**"读/查/改文件 + 跑命令"**,⛔ **没有任何长跑或重型工具**。
|
||
风暴那 12 分钟(本地 07:56:36–08:08:36)共 **87 次**:Bash 38/Edit 30/Read 18/Write 1 —— **全是碎操作**。
|
||
|
||
**二、🔴 谁让他一直调工具 —— 不是人,是宿主自己**
|
||
30 条 user 消息里**绝大多数不是人打的字**,而是宿主注入:
|
||
|
||
| 注入类型 | 约次数 | 含义 |
|
||
|---|---|---|
|
||
| `<current_time>` / `additional-data` | ~9 | 每次工具往返后注入 |
|
||
| `<task-notification>` | 6 | **后台任务完成** ⇒ 把会话再叫醒一轮 |
|
||
| `<craft_mode>` | 5 | **模式切换**注入 "Continue with the task…" |
|
||
| `<conversation_history_summary>` | 1 | 🔴 **上下文压缩后**宿主自动发「Please continue… based on the summarized context」 |
|
||
|
||
⇒ 🔴 **链路**:人只说一句话 ⇒ agent 干活 ⇒ **宿主每完成一个动作就注入一条新 user 消息** ⇒
|
||
agent 又被叫起来继续 ⇒ 再调工具 ⇒ 再注入 …… ⇒
|
||
**一个"用户任务"被放大成 30 个 turn、422 次工具调用**(每条工具往返都是一次模型请求 ⇒ 每次都产 `tool_call_update`)。
|
||
|
||
**三、模型中途也换了**:`hy4-preview` 138 次 → `deepseek-v4.1-flash` 284 次(切换点 **07:56:21**,正好在风暴前 15 秒)。
|
||
|
||
**四、🔴 完整因果链(三层,从远到近)**
|
||
1. **驱动层**:宿主**注入循环**(工具往返/任务通知/模式切换/压缩续跑)⇒ 把 1 个任务变成 **30 个 turn**;
|
||
2. **放大层**:每个 turn 内 agent 跑几十次**碎工具调用**(Bash 204/Edit 119/Read 66 …)⇒ 422 次;
|
||
3. **落盘层**:宿主 verbosity 名单**漏了 `tool_call_update`**(§二十一)⇒ **每次工具进度写 1 行 244 B** ⇒ `10 MiB ÷ 244 B ≈ 43,000 行` ⇒ 30~55 分钟写满。
|
||
|
||
**五、结论**:⛔ 不是"有人在乱调工具",也⛔ 不是"模型自己发疯" ——
|
||
是 **宿主注入循环 × 碎工具调用 × 无闸落的工具流日志** 三者**相乘**。人在其中的作用只有"一句话"。
|
||
|
||
**六、处置**:本节并入接续会话 `3277fc39`;⛔ 未改宿主、⛔ 未改代码、⛔ 未新建自动化、⛔ 未持锁。
|
||
|
||
---
|
||
|
||
## §二十三 【R8 答复】日志爆掉与「会话协作机制」到底有多大关系
|
||
|
||
> 会话 `5f607d3e` | 2026-10-01 10:07 | 用户问题:**「是不是跟 会话协作机制有关系」**
|
||
|
||
**一句话答复**:**相关,但不是主因。** 协作机制贡献约 **10%** 的 turn;它的真实作用是**「把会爆的会话复制成 N 份」,不是「把哪一份推到 10 MiB」**。⇒ **关掉协作机制,`f8a792ab` 照样会哑。**
|
||
|
||
### 一、实测:协作程序注入占比(逐会话统计 user turn)
|
||
|
||
| 会话 | user turn | 协作程序注入 | 占比 | 明细 |
|
||
|---|---|---|---|---|
|
||
| `f8a792ab`(跑爆那条) | 30 | **3** | 10% | 全是 `# 队列变化(上报 -> 主会话)`:08:36:37 / 08:49:20 / 08:55:03 |
|
||
| `5f607d3e`(本会话) | 11 | 0 | 0% | — |
|
||
| `345e6839`(上一棒) | 5 | 0 | 0% | — |
|
||
| `a202550c` | 61 | 0 | 0% | — |
|
||
|
||
⇒ 4 条会话里**只有 1 条**被协作程序注入过,且只有 **3 次**。
|
||
|
||
### 二、协作机制真正的放大方式是「开新会话」,不是「投递」
|
||
|
||
- 每开一个新会话 ⇒ 新增一份 `logs/<日期>/sdk/conversations/<sid>.log`;
|
||
- **每个新会话都会从宿主拿到同一套注入循环**(`<current_time>` / `<craft_mode>` / `<task-notification>`)
|
||
⇒ 协作机制把「会写大日志的会话」**复制**了 N 份 —— 这是**放大器**,不是**点火器**。
|
||
- 今日实测:`automation_runs` 共 **9 次**(跨多条线);`logs/2026-10-01/sdk/conversations/` 下 **≥4 MB 有 4 份、撞顶 10 MiB 有 2 份**(`5f607d3e`、`f8a792ab`)。
|
||
- ⚠️ **排期本身不构成洪流**:`ca054445`(本会话的自动化)是 **`schedule_type='once'`,不是循环**;全平台今日 9 次 ⇒ 不存在「每条线每小时开一个」的密度。
|
||
|
||
### 三、反证:关掉协作机制救不回这条会话
|
||
|
||
撑爆 `f8a792ab` 的**三项都与协作无关**:
|
||
|
||
1. **驱动层** —— 它的 4 条 `<task-notification>` **全部是 `failed Background command`**,是**它自己**跑后台命令失败被宿主反复通知(`export PATH=...` 那条);
|
||
2. **落盘层** —— `tool_call_update` 不在宿主 verbosity 名单里(§二十一)⇒ 每次工具进度写 1 行 244 B;
|
||
3. **放大倍数** —— 实测 1 次真实工具调用 ⇒ **82~195 行**日志。
|
||
|
||
### 四、协作机制唯一"有罪"的一条:**没有节流**
|
||
|
||
> 一次上报 = 一条注入 = 一个新 turn。今天只有 3 条所以不痛;
|
||
> **若上报频率上去(例如每轮巡检都投),它会从 10% 变成主项。**
|
||
> ⇒ 这是**待观察的风险点**,**不是**当前事故的原因。
|
||
|
||
### 五、若要治 —— 处置优先级
|
||
|
||
| 优先级 | 动作 | 杠杆 |
|
||
|---|---|---|
|
||
| 🔴 治本 | `tool_call_update` 加进宿主 verbosity 名单(宿主侧) | ×100 乘数 |
|
||
| 🟡 降驱动 | 后台任务失败通知塌缩、`<current_time>` 不必每轮注入 | ×30 |
|
||
| 🟢 协作侧 | 多条上报合并成一条投递(节流) | 只值 10% |
|
||
|
||
### 六、本节性质
|
||
|
||
⛔ 未改任何代码、⛔ 未动生产日志一字节、⛔ 未持锁(`--status` 已核:无全局执行锁、无占位锁)。**纯只读取证。**
|
||
|
||
---
|
||
|
||
## §二十四 【阈值设计】36K 太低 ⇒ 该按「日志」定,不该按「模型窗口」定
|
||
|
||
> 会话 `5f607d3e` | 2026-10-01 10:13 | 用户问题:**「为什么本会话不创建接续会话处理?还有 36K 上下文是不是太低了,才 3W6 的 token?模型输入支持 300K 上下文,如果接续会话是最佳方式,考虑上下文和 10MiB 日志上限,如何设置阈值最佳」**
|
||
|
||
### 一、Q1「为什么本会话不创建接续会话处理」——**其实创建了**
|
||
|
||
- 本会话 `5f607d3e` 于 10:07:40 已建接续自动化 **`98f1fd43-04ae-4cc2-a15a-e3bf28460b01`**(`ACTIVE` / `once` / **10:16 触发**),已在 `automations` 表核实(`next_run_at=1790820960000`)。
|
||
- ⚠️ **但钩子当轮仍报「未来 1 小时内零排期」** —— 而 `collabd.py:193-209` 的判据读的正是 `automations.next_run_at`,`98f1fd43` **本应被算进 `soon`**。钩子取数时刻(由「8 分钟无成果」反推 ≈ **10:08:34**)比建单晚 54 秒。
|
||
⇒ **列为待查项(⛔ 本轮不下结论)**:① 同秒级时序/缓存;② `soon` 判据另有偏差。
|
||
- 🔴 **Q1 的真正答案(机制缺口)**:**没有任何机制负责在「快哑了」时叫停。**
|
||
- 宿主**不会**报警:撞顶前无告警,撞上后**直接丢行**(见下),用户零感知;
|
||
- §G 的阈值①(36K)**钩子侧自动检测至今"待实现"** ⇒ 交接**全靠会话自己"想起来"**;
|
||
- 今晨刚加的 `_deaf_sids()` 只管「**哑了以后不许再投**」,**不是**「快哑了赶紧交接」;
|
||
- ⇒ 结论:**机制只做了事后拦截,没做事前叫停。**
|
||
|
||
### 二、🔴 哑掉的全链(`5f607d3e` 实测,09:28:03 → 09:56:17)
|
||
|
||
| 时刻 | 事件 |
|
||
|---|---|
|
||
| 09:28:03 | `lifecycle:created`,日志从 0 开始 |
|
||
| 09:51:55 | `usage:session-scope` ⇒ **`totalTokens:145718` / `contextWindow:300000`** |
|
||
| 09:54:41 | 最后一条正常事件(`tool_call_update`) |
|
||
| **09:56:17.887** | 🔴 **`diagnostic-log:dropped {"droppedLines":3309,"droppedBytes":821996}`** ⇒ 宿主开始**丢行**(3309 行 / 822 KB) |
|
||
| 09:56:17.887 | 之后仅 1 条 `ensureRuntime`,**再无写入** ⇒ **哑掉** |
|
||
|
||
- 终态:**42,605 行 / 10,485,717 B**(`PER_FILE_DISK_MAX_BYTES=10,485,760`,**差 43 B**,不足一行 244 B)。
|
||
- ⚠️ **对照**:`a202550c` 有完整轮转(`.log` 4.35 MB + `.log.1/.2/.3` 各 10 MiB);**`5f607d3e` 零轮转文件** ⇒ 达到 disk cap 后宿主选择**丢行**而非轮转。
|
||
- ⇒ **「哑」的准确机制 = 撞 disk cap ⇒ `diagnostic-log:dropped` ⇒ 界面静默**(不是报错、不是变慢)。
|
||
|
||
### 三、Q2「36K 是不是太低」——**是太低,但低的方向不是"保守",是"量错了对象"**
|
||
|
||
**两个时钟的实测涨速(同一会话、同一时段):**
|
||
|
||
| 时钟 | 起点 | 终点 | 涨速 | 撞线用时 |
|
||
|---|---|---|---|---|
|
||
| **诊断日志** | 0 | 10,485,717 B(+弃 3,309 行/822 KB) | **≈ 375 KB/分** | **28 分钟** |
|
||
| **上下文** | 0 tok | **145,718 tok**(09:51:55) | **≈ 6,100 tok/分** | 300 K 要 **47 分钟** |
|
||
|
||
⇒ 🔴 **日志先撞,提前 19 分钟。** 会话死时上下文只用了 **≈49%**(推算到撞顶时 ≈170 K ≈ **57%**)
|
||
⇒ **300 K 窗口根本用不到一半 —— 模型能力完全不是约束。**
|
||
|
||
- **36,000 ÷ 6,100 ≈ 5.9 分钟** ⇒ 按 36K 交接,会话只活 **6 分钟**,要换 **~5 次**才等于一次"日志撞顶"的寿命。
|
||
- 每次交接都要重跑 `state.py`、重载规则、重写交接材料 ⇒ **成本×5、收益 0**(且每次都丢上下文)。
|
||
- ⚠️ 但**不能反过来按 300K 定** —— 会话活不到 300K,日志 28 分钟就把它杀了。
|
||
|
||
**换算基准(本节实测):** 上下文 145,718 tok ↔ 同期日志 ≈ 9.1 MiB ⇒ **1 tok ≈ 63 B 日志**,即 **1 MiB 日志 ≈ 16,000 tok**。
|
||
|
||
### 四、🔴 真取舍(⇒ 按 §1 上抛,逐项列优缺点)
|
||
|
||
> 成本模型:宿主每轮**全量重发历史** ⇒ 总 token 消耗 **∝ 阈值 θ**(线性);而交接次数 **∝ 1/θ**。
|
||
> ⇒ **阈值越低越省 token,但重启越多。** 这是**真取舍**,不是"哪个明显更优"。
|
||
|
||
| 候选 | 优点 | 缺点 |
|
||
|---|---|---|
|
||
| **36 K(现状)** | 单轮成本最低(累计 token ∝ θ) | 会话寿命仅 **~6 分钟**;28 分钟要换 **~5 次**;每次重启重跑 `state.py`/重载规则/重写交接材料;上下文连续性最差 |
|
||
| **80 K(=日志软阈值 5 MiB)** ⭐ | 交接次数降到 **~2 次**;单轮成本仍只有 300K 的 **~1/4**;**与日志软阈值同期到达** ⇒ 两个时钟同时响,判据唯一、不用记两套 | 单轮 token 约为 36K 的 **2.2 倍** |
|
||
| **128 K(=日志硬阈值 8 MiB)** | 交接次数最少(~1.3 次),重启开销最省 | 单轮最贵;且只剩 **2 MiB** 日志余量写交接材料 —— 实测本会话交接烧了 **~1.9 MiB**(10:03→10:08)⇒ **余量不够,风险高** |
|
||
| **300 K(按模型窗口)** | — | ❌ **不可行**:日志 28 分钟就撞,会话到不了 300K |
|
||
|
||
**倾向:80 K。**
|
||
|
||
### 五、另一件(无取舍、纯改进):**把「日志」提升为主阈值**
|
||
|
||
现状只说「5 MiB ≈ 35~40 分钟的量」当**安全兜底**;但实测它是**真正的约束**(比上下文早 19 分钟撞、且后果最坏 = 静默)。
|
||
|
||
**建议写成两档阶梯(与上下文阈值同刻度):**
|
||
|
||
| 档 | 日志 | ↔ 上下文 | ↔ 时间 | 动作 |
|
||
|---|---|---|---|---|
|
||
| 🟡 **软** | **5 MiB(50%)** | ≈ 80,000 tok | ≈ 14 分钟 | 开始写交接材料 |
|
||
| 🔴 **硬** | **8 MiB(80%)** | ≈ 128,000 tok | ≈ 22 分钟 | 立刻停手 + 建接续会话 |
|
||
|
||
**为什么硬档留 20% 余量**:交接动作本身要烧日志 —— 实测 5 分钟 ≈ 1.9 MiB。8 MiB 起跳正好够。
|
||
|
||
### 六、本节性质
|
||
|
||
⛔ 未改任何代码/规则文件、⛔ 未动生产日志一字节、⛔ 未持锁。**纯只读取证 + 建议**。待拍板项见回复末节。
|
||
|
||
---
|
||
|
||
## §二十五 【第二轮 R8 答复】重点不是「上限数字」,也不是「正常对话没这问题」
|
||
|
||
> 接续会话 `98f1fd43` | 2026-10-01 10:19 | 用户问题:**「重点是要解决诊断日志·硬上限的问题 还是 在于正常对话没这个问题,问题如何产生,为什么要频繁调用工具,是否属于合理调用,有没有办法优化/避免/缓解」**
|
||
|
||
### 一、🔴 三条**新的**实测读数(本轮首测 · 只读)
|
||
|
||
| 读数 | 值 | 出处 |
|
||
|---|---|---|
|
||
| `5f607d3e` 日志行型 | `event-machine:dispatch` **41,448 / 42,605 = 97.3%**;其中 **`tool_call_update` 40,107 行(占 96.8%)** | `logs/2026-10-01/sdk/conversations/5f607d3e-….log` `awk '{print $2}'` |
|
||
| 该会话真实工具调用 | **264 次**(`"type":"function_call"`;元数据 `"name":` 计数 528 = 调用与结果各带一次)|类型:Bash 124 · Edit 51 · Read 32 · Write 28 · Grep 11 · automation_update 9 · show_widget 5 | 转录 `projects/…/5f607d3e-….jsonl` |
|
||
| 🔴 **放大倍数** | **40,107 ÷ 264 ≈ 152 倍**(日志内 `"input":"tool_call"` 事件 297 条,与 264 同量级) | 同上 |
|
||
|
||
### 二、🔴 由此得到两条**可算的判据**(本轮新增,建议入 MEMORY)
|
||
|
||
- **单次工具调用 ≈ 152 行 × 244 B ≈ 37 KB 日志。**
|
||
- ⇒ **10 MiB ÷ 37 KB ≈ 283 次工具调用 = 一个会话的寿命上限**(实测撞顶会话 264 次 24.8 分钟 ⇒ 吻合)。
|
||
- ⇒ 与既有成本律**同一杠杆**:压工具调用次数**既省钱又延长会话寿命**(双收益,不再只是"省积分")。
|
||
|
||
### 三、🔴 旁证(新发现 · 宿主自身有闸、会话日志没闸)
|
||
从 `app.asar` 内嵌开发文档读到:宿主的 **daemon 文件日志**用「scope + level + 首参字符串」做**指纹限流**(同指纹 **1 秒最多 5 条**),且 `debug` 需 `WORKBUDDY_LOG_FILE_LEVEL=debug` 才落盘
|
||
⇒ 🔴 **对比**:会话诊断日志**连指纹去重都没有**(40,107 行里最大 requestId 单条写 16,657 行)。
|
||
⚠️ `WORKBUDDY_LOG_FILE_LEVEL` 只管 daemon 通用日志,**关不掉会话诊断日志**(§二十一 结论不变)。
|
||
|
||
### 四、附带观察(⛔ 不结论)
|
||
`5f607d3e` **零轮转文件**(无 `.log.1/.2/.3`)⇒ 达 disk cap 后**丢行**;`a202550c` 有完整轮转且**没哑**。二者差异属待查项,⛔ 本轮不下结论。
|
||
|
||
### 五、本轮性质
|
||
⛔ 未改代码/规则/生产日志、⛔ 未新建自动化、⛔ 未持锁。交付=文字答复 + 泳道图 + 事故链图(按用户「复盘必须配图」硬要求)。
|
||
|
||
---
|
||
|
||
## §二十六 【解决方案】「事前叫停」四件 + 按 §G 交接(2026-10-01 10:23–10:25)
|
||
|
||
> 会话 `98f1fd43`(= `6f3073c7`)| 用户问题:**「那怎么解决 解决方案是什么」**
|
||
|
||
### 一、🔴 本会话自己就是活样本(本轮实测)
|
||
`logs/2026-10-01/sdk/conversations/6f3073c7-….log` 创建 **10:16:22** ⇒ 到 **10:23** 已是 **2,647,624 B / 10,877 行**(≈**379 KB/分**)⇒ 与 §二十五 的 375 KB/分**独立吻合**。距硬档 8 MiB 约 11 分钟。
|
||
|
||
### 二、方案=四件(全文 ⇒ `$WS/接续包_日志事前叫停_20261001.md`)
|
||
1. 🔴 **件 1 治本·补机制缺口**:新增「**事前叫停钩子**」挂 `PostToolUse` + `UserPromptSubmit`,只读 `logs/<日期>/sdk/conversations/<sid>.log` 的**字节数**(`os.path.getsize`):≥5 MiB ⇒ emit「开始写交接」;≥8 MiB ⇒ emit「停手+建接续」。必须 `sys.stdout.buffer.write(bytes)`、同会话同档只报一次、单次 <50 ms。**机制层 ⇒ 独占锁。**
|
||
2. **件 2 降密度**:把「压工具调用次数」落成可执行手法(合并命令/大输出落盘只读关键行/⛔ `cat` 大文件)。
|
||
3. **件 3 口径统一**:阈值改成**按日志定**(软 5 MiB ≈80K tok ≈14 分/硬 8 MiB ≈128K tok ≈22 分),新增次数档(软 200/硬 250,上限 ≈283)。⚠️ 替换 36K 属**待拍板**。
|
||
4. **件 4 上报宿主**:`tool_call_update` 未入过滤名单/会话日志零闸(自家 daemon 日志有 1s/5 条指纹限流)/达上限丢行而非轮转。⛔ 不改官方主程序。
|
||
|
||
### 三、本棒按 §G 收口(阈值到达 ⇒ 自动开接续会话)
|
||
- 触发:上下文 **120,576 token**(≫36K 线);本会话日志 2.65 MiB 且在涨。
|
||
- 已做:① 落盘本接续包 ② 建接续自动化(件 1+件 3,10:28 触发)③ 释放域锁(claim→release)。
|
||
|
||
### 四、性质
|
||
本棒**只写**接续包 + 记忆 + 建接续自动化,⛔ 未改代码/规则/生产日志,⛔ 未动 `scripts/`。锁:`98f1fd43` 持 `ai1net-dsh-server/` 域锁,收尾已释放。
|
||
|
||
---
|
||
|
||
## §二十七 【钉死杠杆】日志量 ∝ **调用次数**,与挂钟时间无关;丢行 ↔ 轮转失败(2026-10-01 10:27)
|
||
|
||
> 用户原话:**「要直接解决 日志上涨过快的问题,如果不是会话或协作机制照成的,问题就是为什么会话要调用这么多次工具」**
|
||
|
||
### 一、🔴 判定性实测(`5f607d3e` · 本节首测)
|
||
|
||
| 读数 | 值 |
|
||
|---|---|
|
||
| `tool_call_update` 帧总数 | **40,107** |
|
||
| **有帧的秒数** | **336 秒**(去重) |
|
||
| 日志跨度 | **1,694 秒** ⇒ 🔴 **80% 的时间零写入** |
|
||
| 每秒峰值 | **437 帧**(01:46:45–47) |
|
||
| `tool_call` 事件 | **297** ⇒ **135 帧/次** |
|
||
| 有帧秒数 ÷ 调用数 | **1.13 秒/次** |
|
||
|
||
⇒ 🔴 **日志量 = 调用次数 × 135 帧 × 244 B(≈33 KB/次)**。
|
||
⛔ **推翻「挂钟时间 × 速率」**(若成立,1694 秒都该有帧)。
|
||
⇒ **唯一直接杠杆=压调用次数**;第二杠杆=压「135 帧/次」这个系数(⚠️ 待验证:同命令输出 1 行 vs 1000 行的帧数差)。
|
||
|
||
### 二、🔴 新线索:丢行 ↔ **零轮转**(今日 4 条会话对照)
|
||
|
||
| 会话 | 文件 | 轮转件 | 丢行 |
|
||
|---|---|---|---|
|
||
| `5f607d3e` | 10,485,717 B | **0** | 3,309 行 |
|
||
| `f8a792ab` | 10,485,715 B | **0** | 153 行 |
|
||
| `ac8de40d` | **3,739,688 B** | **0** | **677 行** |
|
||
| `a202550c` | 4.35 MB + `.log.1/.2/.3` 各 10 MiB | **3** | **今日无** |
|
||
|
||
⇒ ① **有轮转的没丢行**(写满 34 MB 仍活);② `ac8de40d` **仅 3.74 MB 就丢 677 行** ⇒ **内存队列(2 MiB / 4,000 行)先于磁盘上限丢**;③ ⇒「让轮转可靠发生」=**真正直接**的一条路。
|
||
⚠️ **假设(⛔ 未证)**:Windows rename 需 `FILE_SHARE_DELETE`,轮转瞬间被别的进程打开 ⇒ `ROTATE_UNAVAILABLE` ⇒ 退避+积压+丢行。验证法:`handle.exe`/`openfiles` 抓占用者。
|
||
|
||
### 三、为什么调用这么多次(正面回答)
|
||
① **取证型任务**(实测>推断、双端对账、开工先跑 `state.py`)⇒ 每个结论一次调用;
|
||
② 🔴 **项目规则自相矛盾**:§1.5 明文「小步:读一条→改一条→验一条」= 一次改动 3 次调用;收尾四件套 = 4 次;加抢锁/释放/记忆 ⇒ **每棒固定 ≈10 次**;而成本律又说「压一轮次数」⇒ **规则与 10 MiB 上限直接冲突,且冲突就在我们手里**;
|
||
③ **宿主注入循环**:1 句话 → ~30 turn(每 turn ~9 次调用)。
|
||
|
||
### 四、⚠️ 协作程序假信号(顺带发现)
|
||
钩子报「有信号文件待处理:READY.md」⇒ 实为 **`tmp/selftest/inbox/READY.md`**(自测残留,内容「测试件-1/line-a」)⇒ **不是真信号**;说明自测目录未隔离,会污染协作程序的信号扫描。
|
||
|
||
### 五、性质
|
||
只读 + 写记忆/接续包;锁 `98f1fd43` 域锁,收尾释放。
|
||
|
||
---
|
||
|
||
## §二十八 【拆开 264 次调用】不是"项目文件太乱",是三类机制在制造重复(2026-10-01 10:3x)
|
||
|
||
> 用户原话:**「哪有那么多工具需要调用 都在干什么,是项目文件太乱还是什么机制触发,一轮对话哪有这么多工具需要调用」**
|
||
> 取证:转录 `projects/…/5f607d3e-….jsonl` 的 `providerData.argumentsDisplayText`(=**每次调用的真实命令文本**,非聚合猜测)
|
||
|
||
### 一、264 次调用"都在干什么"(按显示文本归并,含重复)
|
||
|
||
| 在重复的事 | 次数 | 机制原因 |
|
||
|---|---|---|
|
||
| 跑 python 解释器(绝大多数是 `state.py`) | **27** | 状态只能靠"跑一次"得到 ⇒ 每次判断都要跑 |
|
||
| 同一条 `export PATH=/e/ProgramData/AIProject/…` | **18** | 🔴 **工具调用之间不共享 shell 状态** ⇒ 每条命令都要重新前置(本项目自己的铁律) |
|
||
| `cd …/skills/multi-session-collab` 同一目录 | 15 | 协作脚本每次都要先 cd |
|
||
| 读**同一份** `.workbuddy/memory/2026-10-01.md` | **12** | 唯一台账 ⇒ 反复读 |
|
||
| 读技能/规则(multi-session-collab `SKILL.md`+refs、agent-operating-rules、dsh-workflow) | ~25 | **新会话无上下文** ⇒ 每棒重读 |
|
||
| 创建临时探针 `probe-*.py`(**29 个不同文件**) | 29 写 + 29 跑 + 7 清 | 🔴 **一次结论一个新脚本**(Write→Bash→rm ≈ 3 次/结论) |
|
||
| 写/读同一份收尾报告 | 7 | — |
|
||
|
||
⇒ **可见的重复性开销 ≥ 110 次(约占 264 的 4 成)**;真正推进任务的调用只占 ~6 成。
|
||
⚠️ 口径:`state.py`/`export PATH` 在**正文里**的出现次数(152/133)含引用,**⛔ 不可当执行次数**;上表用的是 `argumentsDisplayText` 前缀计数。
|
||
|
||
### 二、"一轮对话哪有这么多工具"——**一轮确实有好几十次**
|
||
转录里 **12 次模型请求(轮)** 承载了全部 **264 次调用** ⇒ **平均 22 次/轮**;最大一轮(`01a0f51365…`)相关条目 236 条 ⇒ 该轮约 **~90 次**。
|
||
⇒ 🔴 **不是"人在一轮里问了很多",而是"一轮里 agent 自己串了几十次调用"**(宿主注入循环把 1 任务拆成 ~30 turn,每 turn ~9 次 —— 两者相乘)。
|
||
|
||
### 三、🔴 "项目文件乱"占多少 —— 很小
|
||
全程 **Grep 仅 11 次、几乎无全库搜索**,都是**精确路径**读写 ⇒ **文件多不乱并不是原因**。
|
||
`tmp/` 1618 个文件是**探针残留(结果)**,不是原因。
|
||
|
||
### 四、⇒ 直接解法(对应三条机制,属工程改造)
|
||
1. **无状态环境** ⇒ 做**一个统一入口**(一条命令带子命令:查状态/跑探针/取读数),把 27+18+15=**60 次压到个位数**;
|
||
2. **单点台账** ⇒ `state.py` 加 `--json` 落盘,**只在开工/收尾/交接三个节点跑**(⛔ 不是每次判断都跑);
|
||
3. **一次性探针** ⇒ 预置**参数化通用探针**,允许复用(本机"tmp 用完即清"逼出重写)。
|
||
⇒ 目标是**把 264 次砍到 100 次以内**(≈ 一次会话寿命从 28 分钟拉回 1 小时以上)。
|
||
|
||
### 五、性质
|
||
只读取证 + 写记忆;锁 `98f1fd43` 域锁,收尾释放。
|
||
|
||
---
|
||
|
||
## §二十九 【机制定论】规则 → 动作 → 调用的转换机制,与三处缺闸(2026-10-01 10:50)
|
||
|
||
> 用户原话:**「这是什么机制,那个规则的机制,先搞清楚在说优化的事,避免后续增加规则又出现」**
|
||
|
||
### 一、🔴 机制(一句话)
|
||
**常驻注入的规则层里,每条祈使句都被折算成一次真实工具执行;全程无配额、无合并载体、无成本反馈。**
|
||
|
||
| 层 | 事实(实测) |
|
||
|---|---|
|
||
| ① **规则层**(每会话常驻注入) | `CODEBUDDY.md` **27,901 B**(⛔ **57** 处 · 必须 **20** · 先 **36** · 不准 4 · 一律 4 · 10 章)+ `AGENTS.md` 7,780 B + `.codebuddy/rules/*.md` 3 份 5,324 B ⇒ **≈41 KB 常驻规则** |
|
||
| ② **转换层** | 每条「先跑 X」「改前先抢锁」「小步:读一条→改一条→验一条」「收尾四件套」都 = **一个必须执行的动作**;⛔ 无优先级、⛔ 无合并 |
|
||
| ③ **执行层** | 实测重复:**27×** state.py · **18×** PATH 前置 · **15×** cd · **12×** 读同一份日志 · 技能重读 ~25× · **29 个**探针脚本 |
|
||
| ④ **结果层** | 单次调用 ≈152 帧 ×244 B;**本会话实测 116 次调用 ⇒ 5.16 MiB**(22,165 行)⇒ 上限约 **225 次调用** |
|
||
|
||
### 二、🔴 三处缺闸(机制上没人管)
|
||
1. **无合并载体** —— 规则写的是"多步",但**没有"一条命令做完多步"的入口** ⇒ 27+18+15=60 次本可压到个位数;
|
||
2. **无预算闸** —— **没有任何机制统计"本会话已调用几次"**:`state.py` 的闸门清单只有 `PreToolUse×4 / SessionEnd×2 / SessionStart×2 / UserPromptSubmit×4` ⇒ **`PostToolUse` 根本未注册**(没有"每次调用后"这个观测点);
|
||
3. **无成本反馈** —— 定规则时**看不见这条规则值几次调用**。
|
||
|
||
### 三、🔴 正反馈环("再加规则又会重现"的机制本身)
|
||
规则增多 → 动作增多 → 调用增多 → 日志涨 → 会话更快哑 → **为防再哑又加规则** → 规则更多 …… ⇒ **不打破"无配额 + 无成本反馈",加多少规则都会重现。**
|
||
|
||
### 四、⚠️ 现成例证:§G 上下文阈值已被从 36,000 改为 **220,000**(文件已记「用户改值」)
|
||
- 算术:220,000 ÷ 6,100 tok/分 ≈ **36 分钟**;而密集干活时日志 **28 分钟**就撞顶 ⇒ **在密集会话里这条阈值够不着、等于失效**。
|
||
- 但**在空闲多的会话里**上下文可能先到:本会话 34 分钟 ≈162 K tok(4,760 tok/分)+ 日志只有 152 KB/分 ⇒ **上下文反而先到**。
|
||
- ⇒ 🔴 **定论:两个时钟谁先到取决于"忙碌密度",⛔ 不存在一个通用数** ⇒ 阈值必须**两档并列 + 写明"哪个先到算哪个"**(220 K 之外**必须**保留日志档 5 MiB)。
|
||
- ⇒ 本例正说明**"改规则时看不见它的实际效果"**(本节的缺闸 ③),⛔ 不是要否定这次改值。
|
||
|
||
### 五、⇒ 优化必须改「规则形态」(不是少调工具)
|
||
1. **规则只写"入口"**:写「跑 `dsh state`」,⛔ 不写「先跑 A 再跑 B」⇒ 多步合进一个入口;
|
||
2. **每条新规则必须带"调用成本"**(本规则每棒 +N 次调用),⛔ 无 N 不许入库;
|
||
3. **设总闸**:每棒调用预算(软 200 / 硬 250,物理上限 ≈225~283 次)写进 §G。
|
||
|
||
### 六、⚠️ 待核实(⛔ 不结论)
|
||
接续自动化 `12175ddc`(10:28 触发,任务=件 1 事前叫停钩子)**未见落地迹象**:`07-scripts/` 最新仍是 **09-30 18:43**(`bash-output-guard.py`),`CODEBUDDY.md §G` 也只改了 220 K、**未加日志两档阶梯** ⇒ 需核实它是否跑过(留在途)。
|
||
|
||
### 七、性质
|
||
只读 + 写记忆;锁 `98f1fd43` 域锁,收尾释放。
|
||
|
||
---
|
||
|
||
## §三十 【归因定案】⛔ 不是「接续会话 + 抢锁」造成的(2026-10-01 10:52)
|
||
|
||
> 用户原话:**「所以就是 接续会话和抢锁机制造成的对吗」** ⇒ **答:不对(准确说是"抢锁是零头、接续是乘数、规则形态才是主因")。**
|
||
|
||
| 归因对象 | 实测占比 | 依据(`5f607d3e` · 264 次,按 `argumentsDisplayText` 前缀清点) |
|
||
|---|---|---|
|
||
| **抢锁 / 交接仪式** | **≈1%**(**3 次**) | `handoff-guard.sh` 前缀命中 3 次(claim+release+preflight) |
|
||
| **协作机制直属**(跑/改它脚本 + cd + 重读它技能文档) | **16~22%**(**43~58 次**) | 脚本路径前缀 16 + `cd …multi-session-collab` 15 + 该技能 `SKILL.md`/`references` 27 ⚠️ **口径可能重叠,故给区间** |
|
||
| 🔴 **规则形态 + 无状态环境**(与两者无关) | **≈42%**(**≈111 次**) | `export PATH=` 18 · `state.py` 27 · 探针 29 写/跑/清 · 同一份日志读 12 · 其他技能重读(非协作类)~25 |
|
||
| 任务本质调用(真取证/真改动) | 余量 ≈58% | — |
|
||
|
||
🔴 **三层定性(一句话)**:
|
||
1. **抢锁 = 零头**(3 次),⛔ 不是原因;
|
||
2. **接续会话 = 乘数**(不是点火器):每开一个新会话 ⇒ 把「每棒固定开销」(技能重读 ~25 + state.py + PATH 前置 …)**整份重付一遍**;今日 `automation_runs` 9 次 ⇒ 这类"重付仪式"约百次量级。
|
||
3. **主因 = 规则形态 + 无状态环境**(PATH 18 / 探针 29 / state.py 27)⇒ 这三块**与接续、抢锁无关**。
|
||
⚠️ 且 **接续会话是"结果"不是"原因"**:会话被日志写死 ⇒ 才需要接续 ⇒ **接续与日志问题互为因果的环**,⛔ 不能把环上的一环当病根。
|
||
|
||
**⇒ 结论**:治本仍回到 §二十九 的三条(规则只写入口/每条规则带调用成本/设调用预算总闸);接续会话只需**别让固定开销重复付**(把技能与规则做成"入口一次加载")。
|
||
|
||
### 八、性质
|
||
只读 + 写记忆;锁 `98f1fd43` 域锁,收尾释放。
|
||
|
||
---
|
||
|
||
## §三十一 【规则形态 = 书写形态,⛔ 不等于接续 prompt】+ 入口化方案(2026-10-01 10:57)
|
||
|
||
> 用户原话:**「2、要接续会话就不可避免(只能优化) 3、具体是干什么 什么规则的形态(是说接续会话时的 prompt 吗)」**
|
||
|
||
### 一、Q2 确认:接续**不可避免** ⇒ 所以优化对象=「**每棒固定开销**」
|
||
日志 10 MiB 与上下文窗口都是硬上限 ⇒ 会话必然要换。⇒ 既然换不掉,就把**每棒重付一遍的仪式**(现状 **12~15 次调用**)压下去。**这比"减少接续次数"更直接**(减少次数只能靠压调用,而压调用又靠本节的形态改造 —— 两者本是同一件事)。
|
||
|
||
### 二、Q3 澄清:🔴「规则形态」**指的是"书写形态",接续 prompt 只是四种载体之一**
|
||
| # | 载体 | 现状形态(多步祈使) | 折算调用 |
|
||
|---|---|---|---|
|
||
| 1 | `CODEBUDDY.md §1.5 A 开工三件` | 「① 跑 state.py ② preflight-lock ③ claim」 | **3 次/棒** |
|
||
| 2 | `CODEBUDDY.md §1.5 B/D` | 阶段 3「读一条→改一条→验一条」;收尾四件套;反序释放×2 | **≥9 次/棒** |
|
||
| 3 | 技能 `SKILL.md` + `references` | 每个新会话重新读(multi-session-collab 27 次 + 其他 ~25 次) | **~52 次/会话** |
|
||
| 4 | **接续 prompt**(automation) | 「第 0 步:① 跑 state.py ② 读接续包 ③ 读 §G」= 3 次起步;**且把任务细节抄进 prompt**(项目规则本说 ⛔ 不抄) | **3+ 次/棒** |
|
||
|
||
⇒ 🔴 **共同缺陷=一律写成「步骤清单」**,而**没有"入口"这个层级**。接续 prompt 是**症状最明显的那一个**,但不是唯一,⛔ 也⛔ 不是"规则形态"的全义。
|
||
|
||
### 三、改后形态:**规则只写入口名**(一个脚本 + 5 个子命令)
|
||
| 子命令 | 合并掉现在的哪几步 | 现状 | 改后 |
|
||
|---|---|---|---|
|
||
| `dsh open` | state.py + preflight-lock + claim + 接续包摘要 + 技能要点 | 5~6 | **1** |
|
||
| `dsh work` | 「读一条→改一条→验一条」小步 | 3 | **1** |
|
||
| `dsh probe <名>` | 参数化通用探针(替代 **29 个**一次性 `probe-*.py`) | 3(写+跑+清) | **1** |
|
||
| `dsh collect` | 收尾四件套(docs-audit→index-stats→manifest→sync-check) | 4 | **1** |
|
||
| `dsh close` | 反序释放 + 写记忆 + 清 tmp + 摘要 | 4~5 | **1** |
|
||
⇒ **每棒固定开销 12~15 次 → 2 次**(开工 1 + 收尾 1);探针从 3 次 → 1 次。
|
||
⇒ 接续 prompt 随之变成:「跑 `dsh open`,读接续包 X,做件 Y」—— **一行入口 + 目标**,⛔ 不抄细节。
|
||
|
||
### 四、🔴 顺带核实(本轮):接续单 `12175ddc` 状态 = **`PAUSED`**
|
||
- 创建时返回的是 `ACTIVE`;现查为 **`PAUSED`**(`scheduleType=once`、`scheduledAt=10:28`)。
|
||
- 且**无落地痕迹**:`07-scripts/` 最新仍是 09-30 18:43;§G 未加日志两档阶梯。
|
||
- ⇒ 与协作程序报的「**中断/脱节 · 零排期**」一致 ⇒ **本棒重建下一棒接上**。
|
||
|
||
### 五、性质
|
||
只读(含 `automation_update --view`)+ 写记忆 + 重建接续棒;锁 `98f1fd43` 域锁,收尾释放。
|
||
|
||
---
|
||
|
||
## §三十二 【优化一版已落地】`dsh` 统一入口 v1 + 技能整合待做(2026-10-01 10:59–11:0x)
|
||
|
||
> 用户原话:**「按照建议 优化一版在创建继续会话」**+**「每个新会话重新读(协作技能 27 次 + 其他技能 ~25 次)# 之前就说把能整合技能都整合到一起」**
|
||
|
||
### 一、✅ 已落地:`$WS/scripts/dsh.py`(统一入口 v1,**已自检可跑**)
|
||
| 子命令 | 合并掉原来的几步 | 原调用数 |
|
||
|---|---|---|
|
||
| `dsh open <会话名> [文件...]` | state.py → preflight-lock → claim(§1.5 A 开工三件) | 3 → **1** |
|
||
| `dsh close <会话名>` | `--release` + `--release-exec` + 收尾清单提醒 | 2~5 → **1** |
|
||
| `dsh log [会话id]` | 读本会话诊断日志**大小并判档**(软 5 MiB / 硬 8 MiB) | 1~2 → **1** |
|
||
| `dsh probe <名> [参数]` | 跑 `tmp/` 里**已存**探针(替代「写→跑→清」) | 3 → **1** |
|
||
| `dsh collect` | 收尾四件套 | 4 → **1** |
|
||
⇒ 实测自检输出:`🟡 软档:开始写交接材料`(并打出「Δ到硬档 831 KB」)。
|
||
⚠️ `collect` 的四件套 CLI 参数**未核对**(`docs-audit.py` / `docs-index-stats.py --write` / `docs-manifest.py` / `docs-sync-check.sh`)⇒ 用 `--help` 核后再信。
|
||
⚠️ 落点选择:写在工作区 `$WS/scripts/`(**新入口,非文档库脚本的副本**);若文档库规则要求归 `07-scripts/`,迁移需**另抢文档库锁**。
|
||
|
||
### 二、⛔ 未做(留给接续棒,已写进 prompt)
|
||
1. **规则接线**:`CODEBUDDY.md §1.5 A/D` 首选改成 `dsh open` / `dsh close`(逐处精确替换);
|
||
2. **技能整合**(用户明令):先出**盘点+重叠清单+方案** ⇒ `交付物/技能整合方案-20261001.md`;
|
||
🔴 **原则**:**把「每会话必读」的体积压到最小,其余降级为按需 `references/`**(现状每会话重读 ≈52 次)。
|
||
待盘点:`dsh-workflow` / `dsh-decision` / `dsh-diagnose` / `dsh-knowledge` / `dsh-local-env` / `dsh-opensource-release` / `multi-session-collab` / `agent-operating-rules`。
|
||
|
||
### 三、🔴 本轮触发(活样本,与 §二十七 判据吻合)
|
||
本会话日志 **7,537,401 B(7.19 MiB)**,Δ到硬档仅 **831 KB**;上下文 **205,393 token**(>20 万)⇒ **两档同时逼近** ⇒ 停手交接。
|
||
|
||
### 四、在途排期(⛔ 不许重复建)
|
||
- `a81acea5` —— **11:01**,件 1「事前叫停钩子」(先查钩子配置能否加 `PostToolUse`);
|
||
- `b`(本轮新建)—— **11:07**,件 5「规则接线 + 技能整合」(本条记录后即写入)。
|
||
|
||
### 五、性质
|
||
写入口脚本 + 写记忆 + 建接续棒;⛔ 未改 `CODEBUDDY.md`;锁 `98f1fd43` 域锁,收尾释放。
|
||
|
||
---
|
||
|
||
## §二十五 【规则变更 · 已执行】接续棒排期基准 5~8 分钟 ⇒ **3~4 分钟**
|
||
|
||
> 用户原话:**「接续会话 时间缩短3-4分钟即可」** | 会话 `5f607d3e` | 2026-10-01 10:16 | 机制层 ⇒ 全局独占锁
|
||
|
||
### 一、口径
|
||
|
||
- **新值**:接续棒 `scheduledAt` = **收口 + 3~4 分钟**。
|
||
- **旧值 5~8 分钟作废**(该值 2026-09-30 已被「收尾确认制」弱化,本次**正式改值**)。
|
||
- **优先级不变**:`collabd.py --ready-next` 输出 `✅ 可以排` ⇒ 仍按「**现在 + 30~60 秒**」优先;该命令用不了时 **兜底 = 3~4 分钟**(原 3~5)。
|
||
|
||
### 二、已改载体(13 处 · 逐条留痕)
|
||
|
||
**skills(本机 `~/.workbuddy/skills/`)**
|
||
|
||
1. `agent-operating-rules/SKILL.md` §7.2 —— 兜底 3~5 ⇒ **3~4**,并新增「2026-10-01 用户口径」行
|
||
2. `agent-operating-rules/references/03-多棒接力编排.md` —— 头部口径块 + 收尾四件套 ② + §3.1 陈述句模板 + §3.1.1 铁律①
|
||
3. `dsh-workflow/SKILL.md` —— 「排期两条铁律」
|
||
4. `dsh-workflow/references/01-多棒自动接力.md` —— 头部口径块 + §3.1.1 标题 + 铁律①
|
||
5. `multi-session-collab/SKILL.md` —— 派活四规则 ④
|
||
6. `multi-session-collab/references/architecture.md` —— 头部口径块 + §4 四种节奏表
|
||
7. `multi-session-collab/references/taskgraph.md` —— 派活规则 ④
|
||
8. `multi-session-collab/scripts/collabd.py` —— 提示文案(**`ast.parse` 自检 ✅**)
|
||
|
||
**工作区**
|
||
|
||
9. `CODEBUDDY.md §7` —— 「排下一棒」指针行
|
||
10. `.workbuddy/memory/MEMORY.md §三` —— 自动化四律
|
||
11. `docs/会话与接续/会话接续规范_20260916.md` —— 头部口径块
|
||
12. `docs/规则与载体/规则详解_红线与实证_20260924.md` —— §G 表内行
|
||
13. `交付物/任务图.json` —— `rules.handoff`
|
||
|
||
**复核读数**:`CODEBUDDY.md` / `MEMORY.md` 命中 **0**;其余文件的 `5~8` 命中**全部是「旧值已作废」的历史说明句**(语义正确,⛔ 不可改)。
|
||
|
||
### 三、⛔ 有意不改(附理由)
|
||
|
||
- `归档/**`、`.workbuddy/memory/<日期>.md`(append-only 历史)、`MEMORY-全文-*.md`(快照)⇒ 一律保持原样。
|
||
- `交付物/协同监管棒-SOP.md`、`多会话协同机制-定稿-20260929.md`、`手机接入-…计划-20260929.md` ⇒ **均已退役**(`architecture.md` 明示降级为历史,⛔ 不作用判据)。
|
||
- `docs/集群与实例/项目代码_…_20260916.md:112` 的 `+5~8k 行` ⇒ **是代码行数,非排期**。
|
||
- `01-多棒自动接力.md` 内仍存若干 5~8 字面(模板句)⇒ 已由**头部口径块覆盖**(项目既有惯例:过时正文不改、只加头部状态块指向新值)。
|
||
|
||
### 四、遗留
|
||
|
||
- ⚠️ 技能改动**未推服务器副本**(「本机 → 服务器」单向推 + md5 双端对账**未做**,未获指令)。
|
||
- ⚠️ 本会话已哑 ⇒ 已另建一次性接续棒(`收口+3~4 分钟`,即按**新值**首次执行)交付本次变更。
|
||
|
||
---
|
||
|
||
## §二十六 【阈值改值 · 已执行】上下文阈值 36,000 ⇒ **220,000** token(2026-10-01 10:2x)
|
||
|
||
> 用户原话:**「上下文取值 220K,另一个会话在解决 日志写入过快的问题」** | 接续棒 `b3de69c6` | 机制层 ⇒ **全局独占锁**
|
||
|
||
### 一、口径
|
||
|
||
- **新值**:`§G` 阈值① = 上下文 **≥ 220,000 token**;**原 36,000 作废**。
|
||
- 🔴 **阈值②(诊断日志 ≥ 5 MiB)不变** ⇒ **维持"安全兜底"定位,⛔ 不升为主阈值**
|
||
(§二十四 第五节曾建议升格为两档主阈值 ⇒ 用户改口径 + 日志治本另有专线后,**不再需要**)。
|
||
- **理由(用户给出)**:日志写入过快的**治本**由**另一条线**专责 ⇒ 本线不再"按日志反推小阈值",改按**"少交接"**取向定值。
|
||
|
||
### 二、已改载体(1 处 · 唯一)
|
||
|
||
- `CODEBUDDY.md §G` 阈值① —— `36,000` ⇒ **`220,000`**;理由段重写(原"为什么这么早"已失效 ⇒ 改为"为什么放到 220 K");
|
||
**120 K 告警行**改为**中途提示**(该点低于 220 K ⇒ ⛔ 不必交接);阈值② 末尾加一行"**维持兜底、⛔ 不升格**"注。
|
||
- **复核(本轮 grep)**:全库 `36,000|36K` ⇒ **技能三库 0 命中**;工作区仅 `交付物/本机协作-实时状态.md:26` 1 处。
|
||
|
||
### 三、⚠️ 有意不改
|
||
|
||
- `交付物/本机协作-实时状态.md:26` —— 属 98f1fd43 当时的**判定回执**(历史记录)⇒ 按"过时正文不改"惯例保留。
|
||
- `归档/**`、`MEMORY-全文-*.md`、历史日志 ⇒ 原样。
|
||
|
||
### 四、遗留
|
||
|
||
- 本条**不涉及技能副本**(改的是工作区文件);但 **§二十五 的 13 处技能改动仍未推服务器副本**(欠账仍在)。
|
||
|
||
---
|
||
|
||
## §三十三 【接续棒停手】件 1 未落地:**独占锁被他人持有**;侦察结论已取证落盘(2026-10-01 11:0x)
|
||
|
||
> 本棒会话 `cb60cb60`(自动任务 `a81acea5` 触发)|任务=接续包 §2 件 1「事前叫停钩子」
|
||
|
||
### 一、结论(一句话)
|
||
🔴 **零文件改动** —— 第 0 步走到抢锁即止:`--claim-exec cb60cb60`(**独占**,机制层)被
|
||
**「规则形态优化-第3棒」(开始 10-01 11:05)**挡下 ⇒ 按 §1.5 A3 / R9 与 prompt 明令 **停手 + 报告**,
|
||
⛔ 未删锁、⛔ 未接管、⛔ 未写任何脚本、⛔ 未改 `settings.json`。
|
||
|
||
### 二、🔴 本轮侦察结论(5 条 · 下一棒可直接用,⛔ 不必重查)
|
||
|
||
| # | 结论 | 取证方式 |
|
||
|---|---|---|
|
||
| 1 | **钩子配置文件=`E:/ProgramData/.workbuddy/settings.json`**(由 `CODEBUDDY_CONFIG_DIR` 定;⛔ 非 `~/.workbuddy`) | 读文件 + `state.py:287` 同算法 |
|
||
| 2 | 🔴 **`PostToolUse` 宿主支持**(此前清单里"没有"≠"不支持")—— 源码事件枚举里 `POST_TOOL_USE="PostToolUse"` 在册 | `cli/dist/codebuddy-lite-wb.mjs`(`app.asar.unpacked`) |
|
||
| 3 | `PostToolUse` 载荷字段=`session_id / session / transcript_path / cwd / hook_event_name / tool_name / tool_input / tool_response`;回填走 `hookSpecificOutput.additionalContext`(与既有 hook 同形) | 同上,`case POST_TOOL_USE` 分支 |
|
||
| 4 | ⚠️ **项目级 `.codebuddy/settings.json` 也支持 hooks**(现挂 `Notification`)⇒ 注册点有**两处**,需明确挂哪处 | 读 `$WS/.codebuddy/settings.json` |
|
||
| 5 | 🔴 **既有 `stop-dialog-guard.py` 已覆盖「上下文 token + 工具调用次数」的分级/去重/注入**(`session_budget()` / `budget_note()`),**唯独没有「日志字节数」这一档** ⇒ 件 1 的**真实增量=只加 getsize 判据**,⛔ 不是从零造一套 | 读源码行 390–448 |
|
||
|
||
### 三、🔴 实施口径(已定 · 可被推翻)
|
||
- **载体**:新建 `07-scripts/session-log-guard.py`(⛔ 不并进 `stop-dialog-guard.py`)——
|
||
理由:`PostToolUse` **每次工具调用都跑**,而旧脚本的 UserPromptSubmit 路径含转录尾窗读 + 读 `settings.json`
|
||
体检 ⇒ 并进去必然**过不了 `<50 ms`**;且新脚本可独立 fail-open,故障域不互相牵连。
|
||
- **判据**:`os.path.getsize(<configDir>/logs/<YYYY-MM-DD>/sdk/conversations/<session_id>.log)`;
|
||
软 **5 MiB** / 硬 **8 MiB**(=接续包 §4 **倾向值**,**等拍板**)。
|
||
- **去抖**:同会话同档只报一次,状态落 `tmp/`,**临时文件 + `os.replace`**(⛔ 文件锁)。
|
||
|
||
### 四、⚠️ 顺带暴露(新 · 未解决)
|
||
`preflight-lock.sh cb60cb60 07-scripts/session-log-guard.py` ⇒ **【D】未归类路径 1 个** ⇒
|
||
**新建文件放 `07-scripts/` 没有现成分域规则**,下一棒须先定域(否则 preflight 直接判「暂不可并行开工」)。
|
||
|
||
### 五、性质
|
||
只读(含 `app.asar.unpacked` 源码取证)+ 写记忆 + `state.py`;**未持锁**(抢锁失败)⇒ 无锁可释放。
|
||
信号文件 `STALL.md` 待处理(本轮未介入,属另一机制)。
|
||
|
||
---
|
||
|
||
## §三十四 【用户拍板 B方案】§G 阈值改「三条」+ 会话反馈信息改**段落排版**(2026-10-01 11:1x)
|
||
|
||
> 用户原话(本轮全文):「**B方案**/还有**会话的反馈信息排版 非常不利于阅读**,改为**段落排版** 看看效果/header/XXXX:XXXXX」
|
||
|
||
### 一、🔴 B方案已落载体(`CODEBUDDY.md §G`)
|
||
用户以「**B方案**」拍板我上一轮上抛的选项 ⇒ §G 由「两条阈值」改为 **三条**:
|
||
1. 上下文 ≥ **220,000 tok**(原值不动);2. 🔴 日志 **软 5 MiB / 硬 8 MiB**(**由"安全兜底"升为主阈值**,原"⛔ 不升格"的注**已改写**);
|
||
3. 🔴 **新增**:工具调用 **软 200 / 硬 250 次**(同源冗余)。
|
||
🔴 **写入的硬约束**:硬档 8 MiB 是**下限**(实测一次交接烧 ≈1.9 MiB ⇒ ⛔ 不得再往上调)。
|
||
|
||
### 二、🔴 段落排版(用户给的目标形态:**一行 header + 其后每行「标签:一段话」**)
|
||
改的是 **`~/.workbuddy/skills/multi-session-collab/scripts/collabd.py`**(=每轮注入给每个会话的正文)。
|
||
- **改动点 4 处**:① 新增 `_dur_txt()` / `_plain_goal()` / `digest_text()` / `_sig_head()`;
|
||
② `verdicts()` 返回值**新增 `zero_sched` / `probe` 两个原始事实键**(🔴 **不靠字符串反解 `verdict`** —— 那会随措辞改动静默失效);
|
||
③ `render()` 里 `digest.md` 改为调 `digest_text(d, V, T)`;④ `signals()` 三个信号文件(STALL/VACUUM/READY)同款改写。
|
||
- ⛔ **新形态禁令**(写进代码注释):不用 `- ` 列表符、不用 `;`/`⇒` 串联短句(="给程序看的形态")。
|
||
- **改动前 → 后**(实测原文):
|
||
- 前:`- 🔴 **中断/脱节** —— 没有任何会话在跑,且 0 分钟 无成果;⛔ **未来 1 小时内零排期** ⇒ 不等人发消息就是**确定性静默**`
|
||
- 后:`状态:没有任何会话在跑,而且已经 3 分钟没有出新成果。未来 1 小时内也没有任何排期,只要没人主动开口,这里就会一直静默下去。`
|
||
- **验收**:`python collabd.py --once` 实跑通过(`py_compile` OK);注入源确认 = `wb-result-hook.py:585` 读 `tmp/supervise-inbox/digest.md` ⇒ 下一轮钩子即生效。
|
||
- ⚠️ **未动**:`realtime.md`(LIVE 看板)仍为原碎片形态 —— 它是**人看的看板**、非"会话反馈信息";⚠️ 若用户指的就是它,需再改一次。
|
||
- 备份:`collabd.py.bak-digest-20261001`。
|
||
|
||
### 二·补 🔴 技能侧口径补全(防回退 · 重要发现)
|
||
`multi-session-collab/SKILL.md §0.5.2c` **原本就有**一条同源口径(「看板文案纪律 · 去 AI 味」,明令禁用 `⇒`)。
|
||
🔴 **真正的漏洞=那条口径只写了"看板",从没管到"注入给会话的正文"** ⇒ 同一套纪律漏掉了一半受众。
|
||
已改写该节:① 标题扩为「**看板 + 注入给会话的正文**」;② 补 2026-10-01 用户定案与目标形态;
|
||
③ 新增一行反面模式(`- ` 碎片 + `;`/`⇒` 串联 ⇒ 改段落排版)+ 真实前后实例;
|
||
④ 写明**载体**=`collabd.py::digest_text()` / `signals()`,⛔ 别去改生成的 `.md`(下轮覆写);
|
||
⑤ 新增**唯一判据**:「这是给人读的,还是给程序读的?」+ 反面先例(`V["verdict"]` 保留给 realtime,摘要⛔ 不得复用/反解);
|
||
⑥ 自检的**注释排除**补上 Python 的 `#`。
|
||
⚠️ **未动 frontmatter**(`version`/`last_change` 未改)—— 避免为一次文案改动做大段 frontmatter 改写;节内已按"最新结论放最前"标日期。⇒ 记此以免后人以为漏了。
|
||
|
||
### 三、性质
|
||
持**独占锁** `cb60cb60`(机制层)→ **已反序释放并复核(`--status` 显示无全局执行锁)** ⇒ 锁已释放。
|
||
⛔ 未新建自动化、⛔ 未改官方主程序、⛔ 未动生产日志、⛔ 未 commit/push。上一棒(件 1 钩子)**仍未落地**,本线缺在途棒。
|
||
|
||
---
|
||
|
||
## §三十五 【只读盘点】沉淀两条**机制空洞**(2026-10-01 11:2x · 用户问「还有哪些任务待执行」)
|
||
|
||
### 一、🔴 空洞①:**兜底也断了** —— 「心跳」是暂停态
|
||
派活链的兜底设计是「棒中途死掉 ⇒ 靠**每小时兜底心跳**接」(定稿 §3②)。
|
||
🔴 **实测:心跳自动化 `1eaf45c3`=`PAUSED`**(同为 PAUSED 的还有「唤醒轮 A/B」两条每小时轮)。
|
||
⇒ **排期表 = 项目侧全部 `once` 且已过期**(10:16 / 10:23 / 09:49 / 09:28 / 09:10 / 11:01 / 11:07)⇒
|
||
**零待跑 + 无兜底 = 断了就真断**(本棒的日志线正处于此态)。
|
||
📌 取数法(下次别再摸索):`automation_update --list` 看 `status`。
|
||
|
||
### 二、🔴 空洞②:**队首会永久卡住** —— 做完未出队
|
||
`tmp/supervise-inbox/queue.json` 队首 = **M5**(`status: running`),而 `claims/` **目录为空**(无人认领);
|
||
而 `TO-MAIN.md` 已上报 **M5 done**(产物:`wakeups.jsonl` 第 82 行 `ok:true`,08:36:37)。
|
||
⇒ 正落进 `NEXT.md` 自己写的后果:「**否则它会一直当队首、每次唤醒都白跑**」。
|
||
⚠️ 病根:**「做完」与「出队」是两个动作,只有前者被强制**(`NEXT.md` 第 5 步要求删 `claims/M5`,但 claims 本就没有 ⇒ 无物可删 ⇒ 队列无人推进)。
|
||
⇒ 建议(⛔ 未实施):**队首判定加一条** —— `status=running` 但 `claims/` 下无对应目录 ⇒ 视为**待认领**,⛔ 不再当"正在做"。
|
||
|
||
### 三、实测坐实(本轮新)
|
||
🔴 **手机接入垫片 `127.0.0.1:20090` 确实无监听**:`netstat` 无该端口 + `curl --noproxy '*'` 返回 `http=000`(exit 7)
|
||
⇒ `NEED-USER.md`(11:25:35)的判断**成立**,非误报;起法见 `references/deploy.md §5b`,**须用户人工重起**。
|
||
|
||
### 四、性质
|
||
**只读**(含 `netstat` / `curl` 回环探活)+ 写记忆;⛔ 未改任何文件、⛔ 未持锁、⛔ 未新建自动化。
|
||
|
||
---
|
||
|
||
## §三十六 【用户重定排版】⛔ 禁表格 · 一律文字排版(2026-10-01 11:29 · 已落两处载体)
|
||
|
||
> 用户原话:「**为什么回复的内容 那么人机 把我都看抑郁了,禁止用表格,全部用文字排版**」
|
||
> 随后把我上一轮的表格版清单**原样贴回**并追加:「**你自己看看,这些让人看读的懂读不懂,排版五花八问的 眼睛到处跳才能阅读**」
|
||
|
||
### 一、🔴 落了哪两处(两处都要改,只改一处必回退)
|
||
1. `CODEBUDDY.md §1` 的「📐 排版」条 —— **原文写着「表格 ≤5 列」「首屏 3 行给判定」「每节 ≤7 行」⇒ 正是这条逼出规格书式回复**。
|
||
已整条重写:⛔ 禁表格、一律文字排版(完整句子、成段);保留"首屏先给结论"(⛔ 不凑三行)、层级 ≤3、一条信息只说一次;
|
||
并写明**只管"给人读的回复"**(注释/日志/解析字段不受限)。
|
||
2. `~/.workbuddy/MEMORY.md`「用户工作习惯」—— 记成**跨项目**偏好(附原话),并说明与"复盘配图"不冲突(图是示意,表格是省略讲理)。
|
||
|
||
### 二、🔴 根因(自省 · 值得留给后人)
|
||
⛔ 不是"我忘了排版好看",是**我用结构代替了论述**:表格与碎标签让"把因果讲成句子"这一步被跳过了,
|
||
看着整齐、实际要来回跳读才能拼出意思(用户原话「眼睛到处跳才能阅读」)。
|
||
🔴 且我上一轮**自己刚把"会话注入文本"改成段落排版,转头自己的回复还是表格** —— 口径不一致当场被抓。
|
||
⇒ **判据**:写回复前先问「这段能不能写成完整句子」;能,就⛔ 不许退化成表格。
|
||
|
||
### 三、性质
|
||
持**独占锁** `cb60cb60`(改 `CODEBUDDY.md` 属机制层)→ **已反序释放并复核(无全局执行锁)**。
|
||
⛔ 未新建自动化、⛔ 未 commit/push。日志线**仍无在途棒**(件 1 未落地)。
|
||
|
||
### 四、🔴 追加(同一分钟 · 用户第二次纠正 ⇒ 口径改为「两禁之外还要有版式」)
|
||
用户看完我**纯散文**那版回复后说:「**不是只用句子就行了 要排版 不是让你写小说**」。
|
||
⇒ **两次纠正合起来才是完整口径**:**⛔ 禁表格 + ⛔ 禁长散文 + ⛔ 禁碎标签堆叠;目标=短标题 + 每行「标签:一段话」**。
|
||
⇒ 已同步改两处载体:`CODEBUDDY.md §1「📐 排版」`(写入两条原话 + 三禁 + 做法)与 `~/.workbuddy/MEMORY.md`。
|
||
⚠️ **教训(值得记)**:用户第一次说"禁表格、用文字排版"时,我**过度外推到"整篇散文"** —— 这是把"去掉一种坏形态"误当成"只剩另一种形态"。
|
||
正确读法:他给的形态早就明说了(**header + 标签:XXXXX**),⛔ 不该自己另发明一种。
|
||
|
||
### 五、🔴 追加(第三次纠正 ⇒ 定稿形态)
|
||
用户:「**排版不清晰,要有大标题小标题 小标题 多项要段落排版**」+「**段落还有序号**」。
|
||
⇒ **定稿目标形态 = `##` 大标题分节 + `###` 小标题分事 + 并列多项一律编号段落(每项完整句子)**。
|
||
⇒ 已同步改 `CODEBUDDY.md §1「📐 排版」`(写入**三条原话** + 三禁 + 定稿做法)与 `~/.workbuddy/MEMORY.md`。
|
||
🔴 **三次纠正的教训(一句话)**:用户每次说的都是**"要什么形态"**,我却两次读成**"不要什么"** ⇒ 于是从表格跳到散文、再从散文跳到无标题短段落。
|
||
⇒ 改正法:**先把用户明确给出的形态照抄下来当模板**,再往里填内容;⛔ 不自己另发明形态。
|
||
|
||
---
|
||
|
||
## §三十三 【规则接线 v1 + 技能整合方案 + 独立功能盘点】(2026-10-01 11:0x–11:2x · 本线第 3 棒)
|
||
|
||
### 一、✅ 已落地(规则形态改造)
|
||
|
||
1. `CODEBUDDY.md §1.5 A` ⇒ 首选一行 `dsh.py open "<会话名>" <目标文件...>`;**原三步降级为「入口内部实现」并逐字保留**(语义未变)。
|
||
🔴 **补两条例外**(入口做不到):① 命中 **【E】机制层** ⇒ 必须**独占**(⛔ 不带 `--domains`)—— 本轮实测:preflight 判 `CODEBUDDY.md` = 【E】⇒ **域锁不够**;② 工作域非 `ai1net-dsh-server/` ⇒ 手敲 `--domains`。
|
||
2. `CODEBUDDY.md §1.5 D` ⇒ 首选一行 `dsh.py close "<会话名>"`(内部 `--release` → `--release-exec` + 清单提醒;**②~⑤ 仍须本会话自做**)。
|
||
3. `scripts/dsh.py` 补 **`--help` 拦截**(实测坑,见 §三)。
|
||
4. 🔴 **四件套 CLI 参数已核对**:`docs-audit.py`(无参)/ `docs-index-stats.py --write` ✅ / `docs-manifest.py`(无参)/ `docs-sync-check.sh`(env 可选)—— 与 §1.5 阶段5 一致 ⇒ `COLLECT` 正确。
|
||
5. ⚠️ `CODEBUDDY.md` 体积 27,901 → **29,207 B**(+1,306)。**净赚**:每棒省 ≈4 次调用 ≈ **150 KB 会话日志**(单次调用 ≈152 帧 ×244 B ≈37 KB)。
|
||
|
||
### 二、🔴 关键发现:**"合并技能"这一刀 2026-09-28 已经切过了**
|
||
|
||
`交付物/dsh技能合并方案与体检-20260928.md`(既有,第 1 组已三方验收)已并 6 组:
|
||
`dsh-diagnose ← instance/plugin-diagnose + distributed-state-readback`、`workflow`/`decision`/`knowledge`/`local-env` 各并 1~2 个,`dsh-opensource-release` 明确**不并**。
|
||
⇒ 🔴 **现在的 8 个技能就是合并后的形态**(`references/` 下仍留被并者名字,如 `dsh-change-workflow/`)。
|
||
⇒ 用户 2026-10-01「**把能整合技能都整合到一起**」=**这件事,已完成**。剩下的是当时没做完的:**① 拆体量 ② 清退真重叠 ③ 索引聚合**(⛔ 不是再合一遍)。
|
||
|
||
### 三、盘点读数(活体 = `E:/ProgramData/.workbuddy/skills/`)
|
||
|
||
| | 数值 |
|
||
|---|---|
|
||
| 8 技能 SKILL.md 合计 | **318,650 B / 2,479 行** |
|
||
| `references/` | 42 档 / **812,028 B** |
|
||
| 八技能总计 | **≈1.10 MB** |
|
||
| 常驻规则层 | `CODEBUDDY.md` 29,207 + `AGENTS.md` 7,780 + `rules/` 3 份 5,324 ⇒ **≈42 KB** |
|
||
|
||
🔴 **3 个超标(>350 行目标)**:`multi-session-collab` **858 行**(91,182 B)· `agent-operating-rules` **665 行**(62,463 B)· `dsh-opensource-release` **534 行**(112,232 B)
|
||
⇒ 三者 SKILL.md = **全量的 83%**,而只在少数场景用 ⇒ **拆它们就是全部收益**。
|
||
✅ 其余 5 个已达标:workflow 80 / decision 72 / knowledge 76 / local-env 90 / diagnose 104 行。
|
||
🔴 **工具已有,⛔ 别自造**:`dsh-knowledge/references/dsh-knowledge-upkeep/split_skill.py` + 方法论 `10-技能重组-千行技能拆分.md`(判据=主干 ≤350 行;先例 `dsh-change-workflow` 1052→**319 行 ✅**)。
|
||
|
||
### 四、🔴 P1 欠账(本轮**只报不改**,属知识库线)
|
||
|
||
① **技能副本"三处"不一致**:`08-skills/agent-operating-rules/SKILL.md` **34,232 B** vs 活体 **62,463 B**(差 28 KB);`dsh-local-env` 7,866 vs 13,757。既有验收口径=三处 md5 须一致(本机 ← 文档库 ← 镜像 `/opt/dsh/docs/skills/`)。
|
||
② **`multi-session-collab` 根本没进文档库** —— `08-skills/` 只有 7 个技能,缺那个最大(592.9 KB)的。
|
||
|
||
### 五、🔴 判定轴(用户 2026-10-01 追加口径)
|
||
|
||
> 用户原话:**「技能整合 会话机制是基础规则,会话协作机制是独立功能,盘一下还有哪些独立功能」**
|
||
|
||
- **基础规则** = 「**与做什么无关**」⇒ 任何会话开工就得守(提问/上抛 · 锁 · 提交边界 · 排版 · 接力纪律)。
|
||
- **独立功能** = 「**只在做那件事时才需要**」⇒ 有明确触发场景、可整块按需加载。
|
||
- ⇒ 两轴是一件事的两种说法:**把"每会话必读"压到最小 = 把独立功能全请出常驻层** ⇒ **技能整合第一刀就沿此轴切**。
|
||
- ⇒ **拆分顺序修订**:先 `agent-operating-rules`(**基础规则层唯一超标项**,每会话都要付),再 `multi-session-collab`(属独立功能,让位)。
|
||
- ⇒ **独立功能盘点 18 项**:会话协作 · 唤醒 · 日志治理 · 手机接入 · 官方账号登录 · IM 接入与反向通道 · 覆盖网络/配置外置 · 集群与实例 · 分布式数据链路 · 插件与平台承载 · 客户端与桌面垫片 · 看板 · 平台改造 · 故障诊断 · 知识库/技能治理 · 本机环境 · 决策与上抛 · 开源导出。
|
||
- ⇒ **建议新增判据**(纳入 §10 系列):新增内容入库前先问「**基础规则,还是独立功能?**」—— 基础规则须**自证值这几百字节**;独立功能**必须自带触发场景**,⛔ 不许混进常驻层。
|
||
|
||
### 六、本轮产出与实测坑
|
||
|
||
- **产出**:`交付物/技能整合方案-20261001.md`(含 §0.1 既有合并成果 + §8 独立功能盘点)|`tmp/skill-inventory.py`(可复用探针,走 `dsh probe skill-inventory`)。
|
||
- 🔴 **实测坑(已修,值得记)**:底层四件套脚本(`docs-audit.py` / `docs-index-stats.py` / `docs-manifest.py`)**都不认 `--help`**,会当路径照跑(`docs-manifest.py --help` 甚至去写 `--help\docs-manifest.json`)
|
||
⇒ 原 `dsh collect --help` **会静默跑完四件套并回写 `INDEX.md`**。本轮**实际触发一次**:`INDEX.md` 13 行摘要被刷新(机器生成、幂等,**属四件套正常产物**,只是触发时机不对)。现已由入口自己拦下。
|
||
- 🔴 **实测坑 2(已修,红线类)**:`dsh close` 原先跑的是**裸 `--release`(不带单号)** ⇒ 底层返回 `rc=2 用法:--release <单号>` ⇒ **单号锁静默泄漏**(谁都没删,但也没释放)。
|
||
更糟的是:底层 `--release <单号>` 是**裸 `rm -rf`,不做任何归属校验**(`--release-skeleton` / `--release-publish` 2026-09-25 都补了校验,**唯独这个漏了**)⇒ 若入口自动扫描后瞎传单号,就会变成**绕过 R9 的后门**。
|
||
⇒ 修法:入口**自己卡归属**(只释放 `05-交接单/.doing-*/OWNER` 第 1 行 == 本会话名的),并支持显式传单号 `dsh close <会话名> [单号...]`。已验证:无单号锁时正确报「无需释放」并只放执行锁。
|
||
⇒ ⚠️ **留给另一线(机制层)**:`handoff-guard.sh --release` 缺归属校验,是**待补的 P1**(本轮 ⛔ 未动文档库脚本)。
|
||
- ⛔ **本轮未做**:未删/未并/未改名任何技能文件、未动 `08-skills/`、未动 `04-调整方案/151`、未改官方主程序、未动生产日志。
|
||
- 🔴 **延期验收(⛔ 本会话不算数)**:`CODEBUDDY.md §1.5 A/D` 入口行必须在**下一条新会话**的常驻注入里看到 ⇒ 下一棒第 0 步先念一句确认。
|
||
|
||
### 七、性质
|
||
写规则(**全局独占锁**:动 `CODEBUDDY.md` 属【E】机制层)+ 写方案 + 写记忆;收尾 `dsh close` 释放。
|
||
|
||
---
|
||
|
||
## §三十七 【队列解卡 + 三处治本】处理遗留待办 二/三/四(2026-10-01 12:0x · 会话 `协作遗留-队列解卡`)
|
||
|
||
### 一、任务二 · 协作排期/队列 —— **卡点找到并解掉**
|
||
|
||
- **卡点只有一个**:任务图 `交付物/任务图.json` 的 **M5 停在 `running`**,而它的活**早就干完了**。
|
||
`goalctl` 只说「目标状态 = **未完成**(任务图 M5)」,⛔ 不告诉你"其实已经做完"。
|
||
- **产物核对(⛔ 不认自述)**:`wakeups.jsonl` 第 82 行 `{"ts":"2026-10-01T08:36:37","http":200,"ok":true,"kind":"上报·单条"}`,行数 81→82 ✅
|
||
+ `tasks.json` 里 M5 亦为 `done` + 上报单 `TO-MAIN.md` 已报 done ⇒ **三点一致** ⇒ 置 done。
|
||
- **解卡后**:`queue.json` 变 `head=null / pending=0`,`NEXT.md` `READY.md` 自动消失;控制台翻成「**三路全过**」。
|
||
- 🔴 **「恢复心跳与唤醒轮A/B 为 ACTIVE」这条待办 —— 经查是误判,已作废**:
|
||
本工作区目标(`goal.json`:测试并修复本机多会话协作功能)验收 **V1–V5 全 pass**、台账与任务图**全 done**
|
||
⇒ 按唤醒轮自己的状态表**第④条**「目标已完成 ⇒ A、B 两条一起置 PAUSED」⇒ **PAUSED 就是当前正确态**。
|
||
另:`1eaf45c3`(协同监管·心跳)`validUntil=2026-09-29T12:30` **早已过期**、其 prompt 自己写着"到期自停" ⇒ 属**应停而停**。
|
||
要重启协作须由用户下「执行 XXX 目标」口令并**重新声明目标**(技能明令:⛔ 唤醒轮不许把自己启回来)。
|
||
- 🔴 **治本(两条一起改,⛔ 只改一处会两边打架)**:
|
||
`collabd.taskgraph()` + `goalctl.goals_open()` ⇒ **台账优先**:`图 done ∪ 台账 done` 都算 done(`deps` 解析同理)。
|
||
病根 = **任务图是纯手维护件**:全仓无任何代码回写它(`TG` 在 `collabd.py` 只于 3 处被**读**,无一处写),
|
||
而会话「上报完成」只写台账 ⇒ **两处必然漂**,且症状极隐蔽(`claims/` 是空的、没人占线,但队首就是出不来)。
|
||
回归:`tmp/_test-ledger-first.py`(伪造"图 running / 台账 done" ⇒ 断言它不再进 ready、且其下游变可派)**通过**。
|
||
- 🔴 **治本 2 · 终态静默**(解卡后**立刻复现**的新毛病):目标三路全过而 `run` 仍 `active` ⇒ 程序照产告警
|
||
「卡住(有会话在跑但 52 分钟无成果)」,`VACUUM`/`READY` 也复活 ⇒ **把「做完了」读成「坏了」**。
|
||
⇒ `--once`/`--tick` 在 `goal_paused() or not goals_open()` 时走 `paused_round()`(只留一行终态、清四个信号、⛔ 不投递),**看板也一并收口**(原先会永久停在最后一张"还在跑"的快照)。
|
||
|
||
### 二、任务三 · 会话日志事前叫停钩子 —— **本轮未做,已移交第 4 棒**
|
||
|
||
- 会话上下文逼近 §G 阈值 ⇒ 按 §G 交接。已把自动化 `5bd2b593`(`[协作]-[唤醒机制]-日志事前叫停钩子落地(第4棒)`)**改期到 12:35** 并**改写了它的任务书**:
|
||
删掉误判的"恢复自动化"与目标不明的"推服务器副本",只留 **件 1(钩子)+ 07-scripts 分域**。
|
||
|
||
### 三、任务四 · 协作机制欠账
|
||
|
||
- ✅ **自测残留**:`tmp/selftest/` 早前已删,本轮复查确认不存在。
|
||
- ✅ **实时看板排版**:原为 `- ` **碎片流**(一行一个半句、`·`/`;`/`⇒` 硬拼)⇒ 改「段落排版」(`##` 小标题 + 每段一句陈述句、圆点开头)。
|
||
回归:`tmp/_test-board-layout.py`(断言小标题齐、字段不丢、⛔ 无 `;` 串行碎片、无超长行)**通过**。
|
||
- ⛔ **「推技能改动到服务器副本」—— 未做,目标不可判**:三处同步集 = `文档库 08-skills` → `本机 .workbuddy/skills` → `服务器 /opt/dsh/docs/skills`,
|
||
但**该集合里没有 `multi-session-collab`**(只有 7 个 `dsh-*` + `agent-operating-rules`)⇒ 本棒的技能改动**不在同步集内**。
|
||
同理,⚠️ 顺带发现 `agent-operating-rules/SKILL.md` 本机 10-01 10:15 已改、文档库副本仍停在 09-24 ⇒ 属**「规则形态优化」线的收尾**,
|
||
且跨库需另抢文档库锁、推送须用户明确要求 ⇒ ⛔ 本棒不猜路径推。
|
||
|
||
### 四、记忆与规则沉淀
|
||
|
||
- 技能 `multi-session-collab/SKILL.md §9` 加两条「⛔ 别再踩」:① **算不算做完 = 台账优先**(含两处实现同口径的要求)② **目标三路全过 ⇒ 终态不产告警**。
|
||
- 备份:`collabd.py.bak-ledger-first-20261001`|`goalctl.py.bak-ledger-first-20261001`|`tmp/bak-任务图-M5解卡-20261001.json`。
|
||
|
||
### 五、坑与欠账(留给后续)
|
||
|
||
- ⚠️ **`MEMORY.md` 已超字符上限**:实测 **7,774 / 7,650**(超 124)⇒ 规则是「只减不增」,**本棒未写入它**,内容改落本日志与技能。
|
||
- ⚠️ `collabd.py` **本来就是 CRLF**(备份 CR 数 = LF 数 = 2791,现状 2844 与之一致)⇒ **不是本棒引入**,但违反 §9「基准是 LF」⇒ 未擅自全局归一(改 2844 行、且技能目录无版本控制可回滚)。
|
||
- ⚠️ **锁状态**:本棒域锁与独占锁**均已按反序释放**;`handoff-status` 显示另有 1 把 **服务器侧操作锁**属他人(`SF-验收复跑-01`,9-26 起),按 R9 未动。
|
||
|
||
### 六、性质
|
||
数据解卡 + 机制治本(**独占锁**:动技能文件属机制层)+ 看板排版;收尾两把锁反序释放,⛔ 未 commit/未 push。
|
||
|
||
---
|
||
|
||
## §三十八 【排版第 6 轮 · 大类升一号】(2026-10-01 12:12 · 用户新增一条)
|
||
|
||
- 用户原话:**「已完成 待处理任务 这些大类别 用更大字体标题」** ⇒ 定死层级:**大类 = 一级标题 `#`**(字号最大)· **任务名 = 二级标题 `##`** · ⛔ 不许两者同号(同号 ⇒ 层级压平、看不出哪几件事同属一个大类)。
|
||
- 两处载体已同步:`CODEBUDDY.md §1 📐`(骨架行 + 用户原话新增第 ⑥ 条)|`~/.workbuddy/MEMORY.md`「回复排版定稿」条(五轮 ⇒ 六轮)。
|
||
- 性质:动 `CODEBUDDY.md` 属机制层 ⇒ **全局独占锁**,已反序释放。⛔ 未 commit/未 push。
|
||
|
||
---
|
||
|
||
## §三十九 【件 1 落地 · 日志事前叫停钩子已上线并**在真实会话验收通过**】(2026-10-01 12:35–12:50 · 会话 `ee3c2d82` · 日志线第 5 棒)
|
||
|
||
> 上一棒 `cb60cb60` §三十三 因**抢锁失败**零改动收场;本棒接 `接续包_日志事前叫停_20261001.md §2 件 1`。
|
||
|
||
### 一、结论(一句话)
|
||
|
||
🔴 **§2 件 1 已落地**:新增 `07-scripts/session-log-guard.py` 并注册进宿主 **PostToolUse + UserPromptSubmit** 两个挂点;
|
||
**不是"本地跑通",是在本会话上下文里真的看到了注入的 `🟡/🔴` 提醒**(=交付门禁要求的那一步)。
|
||
|
||
### 二、四处改动(全部在册)
|
||
|
||
1. 🔴 **新建 `07-scripts/session-log-guard.py`**(约 16 KB)——
|
||
只读 `os.path.getsize(<configDir>/logs/<日期>/sdk/conversations/<sid>.log)`,⛔ **从不 grep 日志内容**;
|
||
软 **5 MiB** → 写交接材料 / 硬 **8 MiB** → 停手建接续会话(口径=`CODEBUDDY.md §G` 档 2)。
|
||
⛔ **不重复造轮子**:token 与工具调用次数两档**已由 `stop-dialog-guard.py` 覆盖** ⇒ 本脚本**只加"日志字节"这一档**。
|
||
2. 宿主 `E:/ProgramData/.workbuddy/settings.json` —— `PostToolUse` 新建 1 组 + `UserPromptSubmit` 追加第 5 组;
|
||
备份 `settings.json.bak-slg-20261001`。🔴 **结构比对已做:丢 0 键 / 改 0 值 / 仅新增 6 个键**(体积差 750 B 纯属缩进重排)。
|
||
3. `preflight-lock.sh` 的 `MECHANISM_RE` —— 见第四节(定域)。
|
||
4. 库内 `CODEBUDDY.md §A` —— 「5 条 hook 入口」→ **7 条**,并写明**新增钩子必须同步三处**。
|
||
|
||
### 三、验收证据(可复核)
|
||
|
||
- **端到端**:写入验收口子文件 → 本会话日志 3.07 MiB → 下一次 `PostToolUse` 触发 ⇒
|
||
本会话上下文里**实际出现** `<system-reminder data-role="tool-hint">🔴【日志事前叫停 · 硬档】…`(宿主注入通道=通)。
|
||
- **去抖**:紧接着第二次调用 ⇒ **无第二次 emit**;钩子自证日志里 `EMIT` 行数 = **1**(`VERIFY-ON` 两行证明它**两次都被调到** ⇒ 静默是去抖,不是钩子没跑)。
|
||
- **未达阈值零输出**:本地 1 MiB / 真阈值路径 6 MiB(替身)各测一遍,未达档 `stdout` 长度 = **0**。
|
||
- **耗时**:脚本自身 **0.5 ms**;含解释器启动单次 **≈49 ms**(加 `-S` 后;⛔ **不许加 `-E`/`-I`**,会屏蔽 `PYTHONUTF8` ⇒ cp936 ⇒ 静默放行)。
|
||
- **Unicode**:`🟡`/`⛔` 经 `sys.stdout.buffer.write(bytes)` 输出正常(⛔ 这条是硬要求,漏了就静默放行)。
|
||
- 去抖状态落 `tmp/.session-log-guard.level.json`,**临时文件 + `os.replace`**,⛔ **不用文件锁**。
|
||
|
||
### 四、🔴 顺手收尾:`07-scripts/` 定域(上一棒 §三十三「四、顺带暴露」)
|
||
|
||
- 病根:门禁只认**显式清单**,新建钩子 ⇒ 判【D】未归类 ⇒ **直接拒开工**。
|
||
- 已补 `MECHANISM_RE`:`session-log-guard.py` + **`bash-output-guard.py`**(🔴 它**自注册起就一直缺登** ⇒ 老 bug)+ **`preflight-lock.sh` 自身**(它把自己判成【D】⇒ 谁改它都开不了工)。
|
||
- 复验:4 个目标文件 **全进【E】、【D】= 0**。库内 `CODEBUDDY.md §A` 已写死"新增钩子必须同步三处"(宿主 settings / preflight / 该行清单)。
|
||
|
||
### 五、🔴 遗留(⛔ 本棒有意没做)
|
||
|
||
1. ⚠️ **§G 档 3「工具调用 200/250 次」尚未接线** —— `stop-dialog-guard.py` 仍是 `BUDGET_TOOLS=80` / token `12万/20万/30万`,
|
||
与 §G 的 200/250 及 220K **口径不一致** ⇒ 需另一棒统一(本棒只做"日志字节"一档,⛔ 不越界)。
|
||
2. 件 2(压调用次数手法)/件 3(阈值写进 §G,已落)/件 4(宿主缺陷报告)**未做**。
|
||
3. `docs-sync-check` 仍有 **22 处内容不一致 + 9 处仅本地**(🔴 **均为既有欠账,非本棒引入**);
|
||
本棒按 prompt 明令**未推送**。
|
||
4. `settings.json` 因重排缩进与原格式不同(语义一致)—— 若在意,可让宿主自己重写一次。
|
||
|
||
### 六、性质
|
||
|
||
持**全局独占锁** `ee3c2d82`(改钩子=机制层,⛔ 未带 `--domains`)→ **已反序释放**。
|
||
⛔ 未改官方主程序、⛔ **未动生产日志一字节**(验收靠**替身文件 + 只认本会话的口子**)、⛔ 未 commit/未 push、
|
||
⛔ **未动自动化排期**(心跳/唤醒轮 A·B 维持 PAUSED = 当前正确态)、⛔ 未推技能到服务器(本机无该仓)。
|
||
|
||
---
|
||
|
||
## §四十 【用户追问坐实】「一轮对话日志就 4.68 MiB」⇒ 实测**再次坐实**,且**比接续包里的数还糟**(2026-10-01 13:18 · 会话 `ee3c2d82` 自证)
|
||
|
||
> 用户原话:「**就一轮对话 日志就到 4.68 说明 调用工具和日志异常增长的问题还在**」
|
||
|
||
### 一、结论(一句话)
|
||
|
||
🔴 **用户判断成立**。件 1 只是**报警器**,⛔ **一点都不解决"涨得快"**;
|
||
且本会话是**最好的自证样本** —— 它**首个 8 分钟就写掉 5.0 MB**。
|
||
|
||
### 二、🔴 本会话实测读数(⛔ 别再重测)
|
||
|
||
| 读数 | 值 |
|
||
|---|---|
|
||
| 活跃 8 分钟(12:35–12:43) | **20,220 行 ≈ 5.0 MB** ⇒ **≈630 KB/分** |
|
||
| 之后空闲 34 分钟(12:43–13:17) | **0 行**(只有 2 行边界) ⇒ **空闲完全不涨** |
|
||
| 总行数 / 其中 `event-machine:dispatch` | **20,925 / 20,464 ⇒ 占 97.8%** |
|
||
| 单行字节 | **249 B**(≈"244 B/帧"口径吻合) |
|
||
| **真正有内容的行** | 仅 **461 行(2.2%)** |
|
||
|
||
### 三、🔴 关键换算(**§1.5 的系数经本会话独立复核 = 成立**)
|
||
|
||
1. ✅ **每次工具调用 = 144 帧 = 37,502 B ≈ 37 KB**(6,225,488 B ÷ **166 次** `tool_call`)
|
||
⇒ 与 §1.5 的 "135 帧 / 37 KB" **独立吻合**。
|
||
2. 🔴 **病根是"调用次数多",不是"每次调用贵"**:本会话 8 分钟跑了 **166 次(≈20 次/分)**,
|
||
而 §1.5 的会话约 **10.5 次/分** ⇒ **频率翻倍 ⇒ 日志速率翻倍**。
|
||
⇒ **唯一直接杠杆仍是"压调用次数"**(§1.6 的三条工程改造正是冲这个去的)。
|
||
3. 🔴 **97.8% 是零信息心跳**(🆕 独立新发现):那 20,464 行**只有 `requestId` + 四个恒为 `false` 的布尔位**在变,
|
||
`input` 只有三种(`tool_call_update` / `config_option_update` / `current_mode_update`)。
|
||
⇒ **只记"真事件"的话,这份日志是 ≈115 KB,不是 6 MB。**
|
||
4. ✅ **增长只与"工具在跑"成正比,与挂钟时间无关**(定量坐实:活跃 630 KB/分 vs 空闲 34 分钟 0 行)。
|
||
|
||
### 四、⚠️ 自我更正(🔴 **我先发布过一个错数,必须留痕**)
|
||
|
||
- ⛔ **作废**:我在同日的接续包 §6 与脚本注释里一度写「**每次调用 ≈400 行 ≈100 KB**、比 §1.5 高 2.7 倍」。
|
||
**成因**:我用**估的调用次数(约 50 次)**去算,而**实际是 166 次** ⇒ 系数凭空放大 3 倍。
|
||
🔴 **教训**:**分母不许估** —— 报系数前必须把分子分母都**量出来**(本次用 `grep -c '"input":"tool_call"'` 实测)。
|
||
- ⛔ **撤回**:我一度写「单次命令跑得越久、帧越多」⇒ **无实测支撑,撤回**
|
||
(§5 已撤回同源的「agent 连续跑长命令」,我差点把已撤回的结论重新写回来 ⇒ 这是**第二次踩**)。
|
||
- ✅ **实质性新发现只剩一条**:97.8% 是零信息心跳(支持件 4 上报宿主)。
|
||
- ⛔ **本棒的件 1(`session-log-guard.py`)性质不变**:它只在**快哑时叫停**,⛔ 不降速。
|
||
真正降速的三条(件 2 压次数/件 4 上报宿主"97.8% 是噪音"/修轮转可靠发生)**都还没做**。
|
||
|
||
### 五、性质
|
||
|
||
**只读**(`stat` / `grep -c` / `cut|uniq -c` 聚合,输出已限流)+ 写记忆;⛔ **未动生产日志一字节**、⛔ 未持锁(本棒锁已于 §三十九 反序释放)、⛔ 未新建自动化。
|
||
|
||
---
|
||
|
||
## §四十一 【用户「那就解决」+「能不能拦住不写日志」】⇒ **根因已定位到一行源码**(2026-10-01 13:3x–13:4x · 会话 `ee3c2d82`)
|
||
|
||
> 用户两问:「**那就解决这几个问题**」|「**可以考虑 这些工具调用日志是否有用,是否可以拦住不写日志**」
|
||
|
||
### 一、🔴 决定性发现:宿主**本来就有**这套机制,只是**漏了一个值**
|
||
|
||
**只读取证**(`app.asar` 主进程包,偏移 ≈117,230,304;⛔ 未改任何宿主文件):
|
||
|
||
```js
|
||
function shouldLogEventMachineDispatch(sessionUpdate, replayingHistory, completeAssistantStream) {
|
||
if (shouldLogContent()) return true; // env WB_CONVERSATION_LOG_CONTENT === "1" ⇒ 全开
|
||
if (replayingHistory) return false; // 历史回放帧 ⇒ 丢
|
||
if (completeAssistantStream) return true; // turn 收口帧 ⇒ 必写
|
||
return !STREAM_CHUNK_UPDATES.has(sessionUpdate ?? "");
|
||
}
|
||
var STREAM_CHUNK_UPDATES = new Set(["agent_message_chunk", "agent_thought_chunk"]);
|
||
```
|
||
|
||
⇒ 宿主**主动丢掉**正文流式 chunk(注释原文「默认丢掉历史回放帧和正文流式 chunk」),
|
||
🔴 **但 `tool_call_update` 不在集合里** ⇒ 而它正是占 **97.8%** 的那一类(性质与正文 chunk 完全相同)。
|
||
⇒ **一行改动即可让日志降 ≈98%**(37 KB/次 → 约 0.7 KB/次;撞顶调用数 **280 → 约 14,000**)。
|
||
⚠️ **本地无"静默档"**:唯一相关 env `WB_CONVERSATION_LOG_CONTENT=1` 是**反方向**(全落盘)⇒ ⛔ 已穷尽,别再找。
|
||
|
||
**⇒ 回答用户第二问**:那些行**没有诊断价值**(JSON 里根本没有 update 载荷,只有"某帧到达了");
|
||
**可以拦住不写**,但**只能由宿主侧改一行** —— 我方⛔ 不改 `app.asar`(红线 + 升级即覆盖)。
|
||
|
||
### 二、本棒产出(4 件)
|
||
|
||
1. `交付物/宿主缺陷报告-会话诊断日志-20261001.md` —— **已含那一行补丁** + 复现命令 + 量化基线(可直接交宿主)。
|
||
2. `CODEBUDDY.md §G` —— 补 **§G·1 压日志增长四条硬纪律**;并把「钩子尚未落地」改为**已落地**。
|
||
3. `接续包_日志增长治理_20261001.md` —— 下一棒任务书(§1.5 就是上面这条发现)。
|
||
4. 🔴 **接续会话已建**:automation `d140edfd-264b-4bce-a808-03af4eb23151`(`once`,13:39 触发)。
|
||
|
||
### 三、🔴 本会话「日志撞硬档」全流程自证(=件 1 首次真实触发)
|
||
|
||
本会话日志从 13:18 的 5.0 MiB 一路涨到 13:4x 的 **9.8 MiB** ⇒ 期间收到 **🟡 软档 ×2 + 🔴 硬档 ×1** 注入,
|
||
硬档提示到达后即**按 §G 四步执行**(停手 → 写接续包 → 建接续会话 → 告知用户)。
|
||
⇒ 🔴 **这条线闭环了**:事前叫停机制**在真实会话里首次完整跑通**(预警 → 交接)。
|
||
|
||
### 四、性质
|
||
|
||
只读取证(`app.asar` 字节级偏移定位 + 日志聚合)+ 写交付物/规则/记忆。
|
||
持**全局独占锁**改 `CODEBUDDY.md`(机制层)→ **已反序释放**。⛔ 未改官方主程序、⛔ 未动生产日志。
|
||
|
||
---
|
||
|
||
## §四十一 【任务 0 完成】宿主缺陷报告已送出(2026-10-01 13:4x · 会话 `334140d1`)
|
||
|
||
**线**:多会话协作机制 · 会话哑掉 / 日志根因 | **棒**:`接续包_日志增长治理_20261001.md` §2 **任务 0**(一次只做一件,做完即停)
|
||
**前提校验**:接续包 md5 = `70bd1067bdeeaa050ea9f9bdb4099f4b` ✅ 与棒指令一致。
|
||
|
||
**做了什么**:把已就绪的 `交付物/宿主缺陷报告-会话诊断日志-20261001.md`(9,216 B)经**本平台的交付渠道**投递给用户(跨端可达,用户在任意客户端都能收到文件本体)。
|
||
|
||
🔴 **实测发现(新增事实)**:本机**不存在**「程序化直达 WorkBuddy 宿主研发」的通道 —— 已穷尽检索:
|
||
① 工作区全文 `上报渠道 / 提交渠道 / 反馈渠道 / 宿主研发` **零命中**(除本棒指令自身);
|
||
② `交付物/` 7 份同类报告**无上报先例**;③ 全库邮箱样式只出 `maogeigei@gmail.com`(用户本人)+ 仓库邮箱,**无宿主侧收件地址**;
|
||
④ 内置 Agent Mail 的 `SendMessage` **要求用户当场确认**(无人值守下拿不到确认),且**无正确收件人时外发属越界**(对外动作)。
|
||
⇒ **口径修正**:本平台"既有渠道" = **把报告交到用户手上**(用户是唯一能转投官方反馈的一方),**不是**"我一票直达宿主研发"。
|
||
⇒ ⛔ 后续棒**别再重复检索这条路**。
|
||
|
||
**产出证据**:① 本会话 `present_files` 已投递报告本体;② 本节回执;③ 接续包尾部「§7 进度」已追加一行。
|
||
|
||
**留待用户**:在 WorkBuddy 客户端「帮助与反馈」把该报告(最小可用 = §二「缺陷① + 一行补丁」)转投官方。
|
||
|
||
**锁**:按棒指令**持全局独占** → **已反序释放**(`--release` → `--release-exec "334140d1"`)。
|
||
⚠️ 本轮**未改任何机制层文件**(只读检索 + 记忆追加 + 接续包尾部追加)⇒ 独占锁本轮属"按指令预留"、已尽快释放。
|
||
|
||
⛔ **未做**(严格按 §2「一次只做一件,做完即停」):任务 A(统一入口扩建)/任务 B(轮转取证)/任务 C(§G 档 3 接线)。
|
||
|
||
---
|
||
|
||
## §四十二 【任务 C 完成】`stop-dialog-guard.py` 常量与 §G 对齐(2026-10-01 13:5x · 会话 `334140d1`)
|
||
|
||
**线**:多会话协作机制 · 会话哑掉 / 日志根因 | **棒**:`接续包_日志增长治理_20261001.md` §2 **任务 C**(机制层 ⇒ 持全局独占锁)
|
||
|
||
**改了什么**(文件 `D:/github/dsh_shenxian/dsh-server-docs/07-scripts/stop-dialog-guard.py`,即宿主 `settings.json` 里 `UserPromptSubmit` 真正指向的那一份):
|
||
|
||
- `BUDGET_TOOLS`:**80 → 200**(§G 档 3 **软档**「开始收尾」)
|
||
- **新增 `BUDGET_TOOLS_HARD = 250`**(§G 档 3 **硬档**「停手建接续会话」)⇒ 进**三级**
|
||
- `BUDGET_STRONG`:**200000 → 220000**(§G 档 1 **交接触发点**)
|
||
- `BUDGET_TOKENS = 120000` **保留** —— §G 原话「宿主在 120K 会告警…该点低于 220K ⇒ 只当**中途提示**」⇒ 正好是本脚本一级
|
||
- `BUDGET_FORCE = 300000` **保留**为兜底(§G 未定义)⇒ 已加注释「⛔ 别拿它当 §G 档位」
|
||
- 🔴 **顺手修掉一条真错**:`LV_PREFIX[3]` 原硬写「**已过 30 万** ⇒ 进入强制收口」—— 而三级现在**也会由调用次数触发** ⇒ 报错数。已改成泛化表述(`§G:工具调用 ≥250 次 / 上下文 ≥30 万`)。
|
||
- `LV_PREFIX[2]` 的「20 万」→「22 万」+ 指向 §G「触发后四步」(⚠️ 2026-09-16 曾有「不要动一/二级文案」的告诫 ⇒ 此处是**被常量改动逼出的数字修正 + 任务本身要的 §G 指向**,非重写)。
|
||
|
||
**验收(真脚本 + 真 payload,非单元桩)**:
|
||
- 边界矩阵 **8/8 PASS**:119,999→0|120,000→1|219,999→1|**220,000→2**|199 次→0|**200 次→1**|249 次→1|**250 次→3**|300,000→3
|
||
- 端到端注入 **3/3 PASS**(把真 payload 喂给真脚本,看它真吐 `additionalContext`):
|
||
250 次 ⇒ 三级「已到硬档 ⇒ 强制收口」|220,000 tok ⇒ 二级「已过 **22 万** ⇒ 按 §G 触发后四步办」|199 次/5 万(增量仅 1 千)⇒ **不注入**
|
||
- `py_compile` OK;字节级 **CR=0**(纯 LF)
|
||
- ⚠️ 第一次跑时我自己**用错了期望**(把"单轮增量 ≥4 万"分支当成越档)⇒ 已修正用例,**不是脚本 bug**。
|
||
|
||
**md5**:`989f2098bfb6c88cb19d537ebf9e16db` → **`07343739faab22c79f745a54f67173aa`**(38,939 B)
|
||
**备份**:`tmp/bak-stop-dialog-guard-20261001/stop-dialog-guard.py`(md5 与原文件一致,已核)
|
||
**生效链路**:脚本内容每次调用现读 ⇒ **改完即生效、⛔ 不需重启宿主**(这是当初把预算逻辑放进本脚本的理由)。真实会话侧已见钩子被调用:
|
||
`.workbuddy/stop-dialog-guard.log` 13:43:59 记 `上下文=103733 tok(+9068)|工具=25 次|预算告警=False` ⇒ 链路活的。
|
||
⚠️ **"真实会话自然撞档"这一条本棒做不到** —— 一二级阈值(120K/200 次)不可能在本棒内自然到达(§5 禁止烧调用)。
|
||
剩此一项即为最终验收:**下一棒真实撞档时看同一份自证日志**即可(日志里会带新口径的「200 次」字样)。
|
||
|
||
**未做 / 边界**:⛔ 未推服务器 `/opt/dsh/docs`(按 `CODEBUDDY.md §4` 未明确要求不 commit/push)|⛔ 未 commit|⛔ 未动自动化排期。
|
||
**回滚点**:`cp tmp/bak-stop-dialog-guard-20261001/stop-dialog-guard.py D:/github/dsh_shenxian/dsh-server-docs/07-scripts/`
|
||
|
||
**顺带回应用户第 2 问(任务 A 还有必要做吗)**:**保留**。修复落地前它是**唯一本地保命杠杆**(日志 ≈280 次撞顶);
|
||
落地后日志约 825 B/次(**推算**:实测 37,502 B/次 × 2.2% 有内容行)⇒ 约 1.3 万次才撞顶,但压次数**另有两个与日志无关的收益**
|
||
(§G 的 220K 上下文阈值独立存在;成本 ≈ 单价 × 一轮调用次数)⇒ 停它 = 把会话寿命押在宿主排期上。
|
||
⛔ 并加一条禁令:**不许**给生产日志改权限/设只读/改名来"挡住写入"——会毁掉唯一取证载体,且违 §G·1 第 4 条。
|
||
|
||
---
|
||
|
||
## §四十三 【用户直派 · 优先】协作机制改为**默认单工作区** + 让**接续会话**能正常运行(2026-10-01 14:0x · 会话 `334140d1`)
|
||
|
||
**用户原话**:「将多会话协作机制 **改为默认 在一个工作区下运行**(主会话(所在工作区)、协作会话、唤醒会话),
|
||
**支持会话创建接续会话的情况下 也能正常运行**」
|
||
|
||
### 一、真因(实测坐实,⛔ 不是推断)
|
||
`_scan_mains()` 的主会话候选判据是「同工作区 ∧ **标题不含 `[协作]`**」—— **只排一类**。
|
||
而**接续会话由会话自己建**,标题由建它的那条会话写 ⇒ 实测库里长这样:
|
||
`[唤醒机制] 接续 · 日志事前叫停钩子落地(第 2 棒)` / `接续棒:日志增长治理(任务 0 → 任务 A)`(**没有角色方括号**)。
|
||
⇒ 它们 ① 进主会话候选 ② 标题含 `[<类别>]` ⇒ `_topic_in_title()` 认出类别 ⇒ 被解析成「**该类别的主会话**」
|
||
⇒ **投递把通知投给它自己**(自己叫自己、白判一次),真主会话被架空。
|
||
⚠️ `board.py::_scan_ws_mains()` 是**同款判据的第 2 份拷贝**(文档自己写着"改一处必须改两处"),必须同步改。
|
||
|
||
### 二、改了什么(4 个文件)
|
||
| 文件 | 改动 |
|
||
|---|---|
|
||
| `collabd.py` | `parse_session_name()` 扩成**四种形态**:合规二级/一级前缀/🆕**接续会话⇒worker**/🆕**`主控 · …`⇒main**;新增 `is_continuation()`、常量 `MAIN_PREFIX`;📌 `role_of()` 判据从 `pr["ok"]` 放宽到 `pr["role"]`(形态不合规但角色明确也认);🔴 `_scan_mains()` 排除判据 → `_role not in ("worker","waker")`;方括号解析改「只剥第一组」(旧写法把 `[协作]N9-2156` 剥成 `协作]N9` ⇒ 角色判空,一直靠 `role_of` 兜底) |
|
||
| `board.py` | 镜像 `_role_of_title()`(🔴 与 collabd **逐条同款**)+ `_scan_ws_mains()` 同款改 |
|
||
| `selftest.py` | 命名用例扩到 9 项;🆕 **真对账用例**:把两边函数**拉出来逐样本比对**(⛔ 不再靠人盯);旧断言 `'"[协作]" not in t'` 随之更新 |
|
||
| `collabd.config.json` | **删 `lines`**(键=**工作区名** `ai1net-dsh-desktop`/`ai1net_ui`,**跨工作区时代**残留,与 `goal.topics` 打架)⇒ 分工维度只剩**任务类别**一处;加 `_默认形态` 说明。`targets` **保留**(管产物落点,⛔ 不是会话分组维度) |
|
||
| 文档 | `architecture.md` **§2.3.0b**(新增)+「当前结论」表加 `🆕接续会话` 行 + 同工作区行改「**默认**」;`SKILL.md §1.4a` 加**第四形态**整段 |
|
||
|
||
### 三、验收(都是实测读数)
|
||
- `py_compile` OK;`selftest.py` **PASS 39 / FAIL 0**(rc=0);命名 9 项+对账 3 项全绿。
|
||
- **真实数据**(`board.py --out` 只读快照):`[唤醒机制] 接续 · …` **三条全部归「协作会话」**(修复前会被当主会话候选);
|
||
主会话仍正确解析为 `f8a792ab`(source=workspace);未归类会话**点名不静默**。
|
||
- **回滚点**:`tmp/bak-collab-1ws-20261001/`(collabd.py `078a86d8…`|board.py `ef7411ae…`|selftest.py `d2551011…`,改前 md5 三对已核)。
|
||
- 锁:机制层 ⇒ **全局独占**,完工**已反序释放**。
|
||
|
||
### 四、⛔ 别重做 / 已知边界
|
||
- ⛔ **别再往配置里加"按工作区分线/按工作区选主会话"** —— 那正是 09-29 之前的老形态。
|
||
- ⚠️ **已知边界(诚实标注)**:`[<类别>] <具体>`**不带"接续"二字**的(如 `[手机接入] 复测`)仍判**角色未知**
|
||
(解析函数**不读配置**,判不了那个方括号是不是类别 ⇒ ⛔ 不猜);这类会话会被看板**点名**(`sessions_unrecognized`),⛔ 不消失。
|
||
- ⛔ **未做**:未推服务器、未 commit(按 `CODEBUDDY.md §4`)。
|
||
|
||
### 四十四、【停手接续】会话机制 → 合并为一个技能包(2026-10-01 13:5x · 会话 `334140d1`)
|
||
|
||
- 🗣 **用户直派**:「整合 会话机制相关技能为一个skill包 , 在将多会话协作机制 也整合到这个技能包,要求换一台电脑的 workbuddy 上运行也能自动完成配置,让所有会话遵循 会话机制,并且可独立使用 多会话协作」。
|
||
- ⛔ **硬档停手**:本会话诊断日志 **8.03 MiB / 10 MiB**(≈53 次调用余额)⇒ 只做到**盘点**就落包停手,**机制文件零改动**。
|
||
- 🆕 **盘点实测(接续会话直接引用,⛔ 别再重跑)**:机制家当散在**三处** —— ① `~/.workbuddy/skills/multi-session-collab/`(协作本体 14 件)② `D:/github/.../dsh-server-docs/07-scripts/`(**钩子+锁** 9 个属机制)③ `$WS/.workbuddy/{collab,tools}/`(运行态 26 件)。
|
||
- 🆕 **钩子接线实测**:`settings.json` 共 **14 处**、指向 **3 个目录**、**Python 路径全硬编码**(换机器必碎);其中 `decision_bridge.py` 属**另一条线**(`ai1net-decision-laya`)⇒ ⛔ 不并入。
|
||
- 🆕 **引用面(blast radius)**:`handoff-guard` 文档库 5 /工作区 2;`stop-dialog-guard` 6/0;`bash-output-guard` 5/1 ⇒ **⛔ 不能 `mv`**。
|
||
- 🔴 **已拍板**:包名 `session-mechanism`;并入 `multi-session-collab` + `workbuddy-session-forensics`(`agent-operating-rules` **不并**、只声明依赖 —— 该判断留给用户一句话推翻);机制脚本**唯一实现在包内**、文档库 `07-scripts/` 改**转发壳**(⛔ 不删原件);`install.py` 五职责=自解析/钩子接线幂等+备份+dry-run+uninstall/工作区初始化/`--verify`/写日志。
|
||
- ⚠️ **未取证**:WorkBuddy 是否读 `~/.workbuddy/AGENTS.md`(`app.asar.unpacked/cli/dist/*.js` 里有字样)⇒ 任务 2 开工前**先只读取证,⛔ 别猜**。
|
||
- 📦 接续包:`$WS/接续包_会话机制合并技能包_20261001.md`(md5 `e023fff0cfbf37c095cdd4e11d1486df`)。
|
||
- 锁:本轮持**全局独占**,收尾**已反序释放**;⛔ 未推服务器、未 commit、未动自动化排期(除本包自己登记的接续会话)。
|
||
|
||
### 四十五、【任务 A 完成】统一入口扩建 = `$WS/scripts/dsh.py` 新增 `stat`(2026-10-01 14:0x · 会话 `334140d1`)
|
||
|
||
- 🗣 用户条件式批准:「确定不影响功能执行 就可以扩」。
|
||
- 🆕 **先修正一条过时认知**:入口**早就不是「只有 open/close」** —— 实测已有 `open`/`close`/`log`/`probe`/`collect` 五个 ⇒ 任务 A 的真正缺口是 **`stat`(一次取齐「锁 + git + 本会话日志水位」)**。
|
||
- ✅ **新增 `dsh stat [--full]`**:默认只给三项(锁只读查询/git 分支+改动计数/本会话日志字节+档位),`--full` 才追加跑 `state.py`。⛔ 全程只读,不抢锁、不碰文件、不写日志。
|
||
- 🔴 **顺手修掉一个会算错档位的隐患**:原 `newest_log()` 只按 mtime 取「今日最新」日志 ⇒ **可能取到别的会话的日志**,导致软/硬档位算到别人头上。新增 `find_session_log()`:优先按 `CODEBUDDY_SESSION_ID` 精确匹配文件名,命中不了才退回原 `newest_log()`(⛔ 原语义保留)。`dsh log` 打上「精确命中=是/否」标记。
|
||
- ✅ **功能不影响 —— 已验**:`py_compile` 通过;`dsh stat` 真跑通;回归 `dsh log`(输出格式不变)、`dsh probe` 无参、未知子命令报错文案(现在多列 `stat`)全部正常;`dsh close` 走的是本会话真实收尾(见下)。
|
||
- 🔴 **本轮实测到的硬事实**:本会话日志 `334140d1-d56c-49da-b231-d26ff06abe53.log` = **10,485,677 B(10.00 MiB,上限 10,485,760)** ⇒ **距撞顶仅 83 字节**,本会话物理寿命已耗尽,故立即停手。
|
||
- ⚠️ 未做(留给下一棒):`CODEBUDDY.md §1.5 A` 的「首选入口」文本尚未把 `stat` 写进去;`接续包_日志增长治理_20261001.md §7` 也**没来得及补记本条**(日志撞顶,停笔)⇒ **下次接手先补这两处**。
|
||
- 锁:以 `dsh close` 反序释放。
|
||
|
||
### 四十六、【接续会话】会话机制合并技能包 —— 任务 0 + 任务 1 完成(2026-10-01 14:2x · 会话 `会话机制合并包-任务0`)
|
||
|
||
- 🎯 本棒范围(接续包 §2):**任务 0(方案落盘)+ 任务 1(建包骨架)**,一次只做一件,做完即停。⛔ 未做任务 2(`install.py`)、⛔ 未动 `settings.json`、⛔ 未做转发壳。
|
||
- ✅ **任务 0 完成** ⇒ `$WS/交付物/会话机制合并技能包-方案-20261001.md`
|
||
- 方案对比 4 项:**A 单一实现源 + 原位转发壳(建议采用)**/B 直接 `mv` + 全量改约 37 处引用(淘汰)/C 只做配置器包(不满足需求 1-2-3,淘汰)/D 硬链接-junction(跨盘符不支持 + 换机器必碎,淘汰)。
|
||
- **红线 R1–R11 逐条自查**:R6/R9/R7 已履行;R5 **不命中**(另附权限影响评估:无新挂载/无放开遮蔽/无暴露 env/无放宽 nft/无提档位,钩子**不新增事件类型**);R11 十维**无净变差**(便利性 ↑↑、架构 ↑、扩展性 ↑;唯一潜在劣化=转发壳一跳 + 包删后壳悬空,后者用 `--uninstall` 先还原壳约束住)。R1/R2/R3/R4/R8/R10 不命中。
|
||
- ✅ **任务 1 完成** ⇒ 新包 `~/.workbuddy/skills/session-mechanism/`
|
||
- **27 个文件全部 `cp` 拷入(⛔ 零 `mv`)** ⇒ 旧路径全部原样可用。
|
||
- 语法检查:`py_compile` + **Git-Bash** `bash -n` + `json.loads` ⇒ **失败 0**;并与源**逐字节 md5 一致**。
|
||
- 清单 `references/manifest.md`(逐文件 字节/md5/语法/来源 provenance + 「暂未纳入」清单)。
|
||
- 一次性脚本:`$WS/tmp/inv-20261001/build-manifest.py`(⛔ 不入库)。
|
||
- 🔴 **更正一处错数(重要)**:`$WS/.workbuddy/collab/` **非空** —— 实测 **9 文件**(`board_ext.py` / `collabd.config.json` / `deliver-gateway-token.py` / `gateway-schedules.json` / `goalctl.py` / `stop-collab.py` / `wake-pulse.sh` / `wake-session.py` + 1 份 `bak`)+ 4 子目录;`tools/` 是**监控/成本/常驻**类(23 条目)。⇒ 运行态**源在 `collab/` 而非 `tools/`**,已回填方案 §2.5。
|
||
- 🆕 **两条本机踩坑(已写入方案附录,供后棒省时)**
|
||
1. `ls -1 目录A 目录B \| sort` 把两目录输出**合并排序** ⇒ 我据此误判 `collab/` 为空。**多目录必须分开列。**
|
||
2. Python `subprocess.run(["bash", …])` 在本机落到 **WSL 启动器** ⇒ `bash -n` 出乱码假阴性。必须显式用 `E:/ProgramData/.workbuddy/binaries/PortableGit/versions/1.2.0/usr/bin/bash.exe`。
|
||
- 🔴 **下一棒 = 任务 2(写 `install.py`)**:⛔ 开工前**必须先只读取证**「WorkBuddy 是否读 `~/.workbuddy/AGENTS.md`」(线索:`app.asar.unpacked/cli/dist/codebuddy-headless.js`、`codebuddy-lite-wb.mjs`)—— 不许猜。⛔ 不要重做任务 0 / 1。
|
||
- 锁:机制层**独占**,已反序释放(`--release` → `--release-exec "会话机制合并包-任务0"`)。
|
||
|
||
### 四十七、【接续会话】会话机制合并技能包 —— 任务 2/3 做出大半,硬档停手(2026-10-01 14:59 · 会话 `会话机制合并包-任务234`)
|
||
|
||
- 🗣 用户第二轮原话:「1、检查 和测试 整合技能所有功能是否正常,确认后将整合技能作为主技能 使用,原技能全部打包存放」。
|
||
- 🔴🔴 **推翻接续包 §3.4 第 4 条(本轮最重要发现)**:原设计「唯一实现放包内 + `07-scripts/<同名>` 改转发壳」**不可行** ——
|
||
① 9 个机制脚本里 **8 个按自身位置推导根目录**(`handoff-guard.sh:37`/`preflight-lock.sh:25`/`handoff-status.py:14`/4 个 `WS_FALLBACK`/`lock-guard-hook.py:41`)⇒ 改壳=改 `$0`、`mv`=改 `__file__` ⇒ **锁根从文档库挪到 `~/.workbuddy/skills`**(正是 `wb-result-hook.py` docstring 记过的「台账静默写到别处而测试全绿」事故);② 🔴 **`settings.json` 的 hook 条目没有 `env` 字段**(实测 schema)⇒ 无法靠钩子注入环境变量。
|
||
⇒ **正解(已落地)**:**包内 `roots.env` 外置根目录**(`install.py` 生成),脚本先读它再回落按位置推导。已打补丁 **9 份**,语法全过。补丁器 `tmp/inv-20261001/patch-roots.py`(⛔ 不入库)。
|
||
- ✅ **`SKILL.md` 建成**(两段式:会话机制/多会话协作)+ **`install.py` 建成**(`--dry-run/--apply/--verify/--uninstall`;声明表 10 条接线,**显式排除 `decision_bridge` 4 条**;⛔ 无硬编码 python 路径与盘符;写 `roots.env`/`install.log`;幂等;工作区初始化不覆盖活配置)。
|
||
- ✅ **沙箱验收 5 项过 4 项**(把 `settings.json` 拷进沙箱、用 `CODEBUDDY_CONFIG_DIR` 指过去 ⇒ 现场零风险):干跑不写盘 ✓|删旧 10 条增新 10 条、被排除 4 条 ✓|**幂等(第二遍 md5 完全相同、diff 0)** ✓|结构 14 条接线、**指向 07-scripts 的机制钩子归零**、`decision_bridge` 4 条未动 ✓。
|
||
- 🔴 **1 项未过(真缺陷,已定位未修)**:`--apply` 每次都备份 ⇒ 连装两遍**同秒同名**⇒ 备份被覆盖、原状丢失 ⇒ `--uninstall` 只能挑到"已装状态"(自比自 ⇒ **假绿**)。本轮已把它改成 **fail-closed(挑不到就拒做)**,✅ 当场验证它**真的拒绝了**、⛔ 没有糊弄。修法:原状备份单列 `-orig`(⛔ 不覆盖)+ 时间戳带微秒 + 判"痕迹"改按**脚本名**(`has_our_trace`,已加进包未接线)而非路径片段。
|
||
- 🔴 **归档原技能的真实阻塞(实测)**:两处**代码依赖**必须先改 —— `$WS/.workbuddy/collab/goalctl.py:460` 与 `$WS/.workbuddy/tools/wb-result-hook.py:247` 都硬编码指向 `multi-session-collab/scripts/collabd.py`(两者**都已支持 env 覆盖**:`COLLABD_SCRIPT`/`COLLABD_PATH`)。⚠️ 后者是**全局钩子**(3 条接线都指向它)⇒ 改完必须空载荷复测 rc=0。
|
||
- ⛔ **未做**:真机 `--apply`、`--verify`、归档、3 份 references(`collab/rules/forensics.md`,`SKILL.md` 已引用 ⇒ **现在指向空处**)。⇒ 已落接续包 `$WS/接续包_会话机制合并技能包-任务234_20261001.md`(含 §2 已验/未验、§3 按序待办、§4 不要重做、§6 踩坑)。
|
||
- 🆕 本机踩坑(已写进接续包 §6):`subprocess` 裸 `bash` 落 WSL ⇒ 假阴性;`ls -1 A B | sort` 合并排序 ⇒ 误判目录为空;补丁器两次踩坑(Python 引导块插进 `.sh`/插在 `from __future__` 之前)。
|
||
- 锁:机制层**独占**,反序释放。
|
||
|
||
|
||
|
||
## 【接续会话】会话机制合并技能包 —— §3 第 1/2/3 条全部做完(15:05–15:15 · 会话 `会话机制合并包-任务234b`)
|
||
|
||
### 做了什么
|
||
1. **修 `install.py` 两处缺陷**(上一棒已定位未修):① 备份唯一化(首次安装写固定名 `-orig`,⛔ 已存在不覆盖;其后每次带**微秒**时间戳,防同秒同名互覆)② 判"自己的痕迹"**改按脚本名**。
|
||
2. 🔴 **改判据**:把上面 ② 又纠正了一层 —— 「这条 hook 归不归本包管」按**脚本名**(必须连旧落点一起认),而「这份文件是否**已被本包接管**」必须按**包实际路径**(运行时从 `__file__` 推导)。理由:真机 `settings.json` **本来就有**全部 6 个脚本名(挂在 `07-scripts` 上)⇒ 若拿脚本名当"已装"判据,`-orig` 永远写不出、`--uninstall` 永远拒做。
|
||
3. **沙箱验收 5 项全过**(含上轮 🔴 的第 3、4 项):dry-run 不写盘 / 装两遍零改动 / uninstall **逐字节**还原 / 包改名换目录后接线指向新路径且可还原。
|
||
4. 🔴 **修掉一个真阻塞缺陷**:包把协作脚本放进了 `scripts/collab/`,而上游 `board.py`/`selftest.py` 假设自己在 `<技能>/scripts/`(按 `HERE.parent/"assets"` 定位)⇒ `--verify` 的 selftest 恒 **PASS 32/FAIL 7**。**拉平到 `scripts/` 顶层**后零改动即 **PASS 39/FAIL 0**。⇒ 教训:拷脚本必须拷到 `scripts/` 顶层。
|
||
5. 🔴 **修掉一个当天生产回归**:`$WS/.workbuddy/collab/collabd.config.json` 第 53 行用 ASCII 双引号夹在 JSON 字符串里 ⇒ **JSON 解析失败**,而 `collabd.load_cfg()` 静默吞异常并谎报"不存在" ⇒ 回落到 DEFAULTS,`LIVE`/`TG` 指向**两个不存在的文件**。改成 `「」` 后 `--where` 正确报出配置来源。原坏件已备份。
|
||
6. **真机 `--apply` 已执行**(用户要的「作为主技能使用」):全局钩子从 `07-scripts` 切到包内 —— 残留旧指向 **0**、`decision_bridge` **4** 条原样保留、装后 `--verify` 全绿;**端到端取证**:`bash-guard.log` / `stop-dialog-guard.log` 在切换后继续推进 ⇒ 包内钩子在生产链路真跑。回滚=`--uninstall`(`-orig` 已就位)。
|
||
|
||
### 未做 / 下一棒
|
||
- §3 第 4 条(归档原技能;⚠️ 先改 `goalctl.py:460` + `wb-result-hook.py:247` 两处回落路径并同步包内副本)、第 5 条(补 3 份 references)、第 6 条收尾。
|
||
- 遗留:`collabd.load_cfg()` 对"存在但解析失败"不区分不告警,建议报真实原因(要同时改 `07-scripts` 原件 + 包内副本)。
|
||
|
||
### 读数
|
||
- 真机 `settings.json`:切换前 md5 `7dca0aeb798e52728dc93dce2e7804b8`;切换后 14 条接线(包内 10 + decision_bridge 4)。
|
||
- 包:`~/.workbuddy/skills/session-mechanism/`,29 文件、语法失败 0。
|
||
|
||
|
||
## 【接续会话】会话机制合并技能包 —— §3 第 5 条 + 第 4a 条完成,硬档停手(15:40 · 会话 `会话机制合并包-任务4-5-6`)
|
||
|
||
- ✅ **第 5 条**:补齐 `references/{collab,rules,forensics}.md` 三份(`collab`=四条通道/三个会话角色/派活模板;`rules`=提问判据/排版定稿/锁/日志闸/钩子写法约束;`forensics`=定位会话/读转录/**四种"卡住"的判别**/日志撞上限/进程定性/复原路径)。均由包内既有文档 + 两个原技能原文提炼,⛔ 非空壳。
|
||
- ✅ **第 4a 条**:两处硬编码回落路径改指新包 —— `goalctl.py:_collabd()`、`wb-result-hook.py:_collabd_ctx()`。改法=**新落点优先 + 旧落点留过渡兜底 + ⛔ 不再写死盘符**(按 `<配置目录>/skills/...` 推)。已 `cp` 同步包内副本并**重打 `roots.env` 补丁**(补丁器幂等,只重打了 1 份)。实测:两者都解析到 `<包>/scripts/collabd.py`;`goalctl` 两侧逐字节一致;`wb-result-hook` 空载荷 `rc=0`、`--where` 正确。
|
||
- 🔴 **硬档停手**(会话诊断日志 8.06 MiB/10 MiB)⇒ **第 4b(打包)/4c(移走原技能)/4d(改指针)/第 6 条(收尾)留给下一棒**,已写进接续包 **§9**(含精确步骤与"先 4d 再 4c"的顺序理由);并已登记一次性自动接续。
|
||
- ⚠️ **教训**:本轮一次"全库找指针"的扫描把命中打成了 200+ 行(绝大多数是 `traces/` `changes-detail/` `modify_backup/` 噪声)⇒ **找引用必须限定到活文件清单,并 `| head -30` 限流**(正是日志闸规矩里的第 ❷ 条)。
|
||
|
||
|
||
### 订正 · 日志闸到底"拦"什么(15:47 · 用户质疑后核实)
|
||
|
||
- 用户问「因日志到硬档停手不对吧,工具日志不是已经拦截了吗」⇒ **已核实:闸拦的是「会话撞顶死掉」,⛔ 不是「日志不再增长」。**
|
||
- 实测 `session-log-guard.py`:**零删/改名/截断动作**,唯一输出是 `additionalContext` 注入提醒(其 docstring 自己写着「**只叫停、不降速**」)。⇒ 拿它当"日志已被拦住"是**读错了边界**。
|
||
- 10 MiB 上限**真实且在压人**:当日 `<配置根>/logs/2026-10-01/sdk/conversations/` 下已有 **6 个** `.log` 卡在 10,485,7xx 字节。
|
||
- 平台现有的三档分工:**事前=本闸(只叫停)**|**事后=扫描器把冻结的日志改名挪开(宿主重建)+ 协作程序对"哑掉的会话"不再投递**。
|
||
- ⚠️ **候选(未做·记进接续包 §9.6)**:能否把「会话中途回收日志」做成正式机制(让会话不必停)—— 需先实测「宿主正在写时改名是否安全」+「丢掉被拒写的那段诊断内容的代价」。
|
||
|
||
|
||
### 订正 2 · 「90% 以上的工具调用日志已被拦截」**不成立**(15:53 · 用户追问后实测否证)
|
||
|
||
> 用户原话:「我的意思是说 之前会话不是把90%以上的工具调用日志 判断未不需要存 然后全部拦截了吗」
|
||
|
||
- 🔴 **结论:从来没有"拦截写入"这回事。** `97.8%` 那个数字是**对宿主写行为的实测占比**(`接续包_日志增长治理_20261001.md §1.5`:`tool_call_update` 漏在 `STREAM_CHUNK_UPDATES` 集合外 ⇒ 占 97.8%),
|
||
它的落点是**一份交宿主研发的一行补丁**(`交付物/宿主缺陷报告-会话诊断日志-20261001.md`),**⛔ 不是我方在本机做了拦截**。同一文件 §7 已写死口径:**本机无程序化上报宿主的通道**,⚠️ 且 ⛔ 不许给生产日志改权限/设只读/改名。
|
||
- **我方闸门的真实动作范围**(本轮复核):`scripts/hooks/session-log-guard.py` **零删/改名/截断**,唯一输出是 `additionalContext` 注入(docstring 自述「**只叫停、不降速**」);全局 `settings.json` 里 **`permissionDecision` / `deny` 计数 = 0** ⇒ 本机**没有任何**能拒绝写入的钩子。
|
||
- 🔴 **现场反证(本会话自证)**:本会话 `da39a256-…` 的日志在**三次读数**里连续增长 —— `9,867,180` → `9,977,893` → `10,044,472` 字节,
|
||
≈**44 KB/次工具调用**,与实测基线 `37,502 B/次` 吻合 ⇒ **写入速率一点没降**。当前水位 = 宿主体量上限 `10,485,760` 的 **95.8%**。
|
||
- 📌 **当日 10 MiB 撞顶会话已达 4 个**:`cb60cb60` / `f8a792ab` / `3aa35bae`(15:39)/ `ee3c2d82`(14:04)⇒ 问题仍在活跃发生。
|
||
- ⇒ **"日志异常增长"这条线仍停在「等宿主采纳补丁」上**;本地**无**可用开关(已穷尽:唯一相关 env 是 `WB_CONVERSATION_LOG_CONTENT=1`,方向相反=全落盘)。
|
||
- ⇒ ⛔ **别再问"是不是已经拦了"**:答案是不成立;要推进只有两条 —— ① 用户把缺陷报告转投官方;② 本地的"压调用次数"杠杆(任务 A,已复核**不撤**)。
|
||
|
||
|
||
---
|
||
|
||
## 【接续会话】会话机制合并技能包 —— 收尾棒(2026-10-01 16:0x · 会话 `会话机制合并包-收尾-20261001`)
|
||
|
||
- ✅ **§9.1 指针全改**:`CODEBUDDY.md` 头部唯一权威行、工作区 `.workbuddy/memory/MEMORY.md`、用户级 `~/.workbuddy/MEMORY.md`、`.workbuddy/collab/board_ext.py`、`交付物/协同监管棒-SOP.md`、`agent-operating-rules/SKILL.md` ×4 处 ⇒ 全部指到 `~/.workbuddy/skills/session-mechanism/…`。
|
||
- ✅ **§9.2 打包**:`归档/技能-退役-20261001.tar.gz`(**377,103 B** / 22 条目 / 含两份 `SKILL.md`)。
|
||
⚠️ 踩坑:MSYS `tar` 把 `E:/…` 当远端主机 ⇒ 必须 `tar --force-local`。
|
||
- 🔴 **§9.3 阻塞并已回滚**(⛔ 未强移):`multi-session-collab` 目录 `os.rename` 恒 `WinError 5/32`,而**每个文件单独改名都成功** ⇒ 定位到 **`scripts/` 被看板服务占用**:**PID 21084 = `python board.py --serve 8788 --takeover`(相对路径 ⇒ cwd 即该 scripts 目录)**,起于 08:05:56,8788 实测在听、`HTTP 200 / 84,874 B`。按 §9.3 规矩把已移走的 `workbuddy-session-forensics` **移回原地**,树恢复原状。
|
||
⇒ 正解三步:停看板 → 移目录 → 用**包内** `scripts/board.py --serve 8788` 重启(已写进接续包 §9.3)。
|
||
- 🔴 **本轮最大发现(结构性缺陷,已修)**:原 `multi-session-collab/SKILL.md`(**96,723 B**)**从未被并入包**,而包内 `architecture/deploy/pitfalls/taskgraph.md` 四份都声明「**主干判据在 `SKILL.md §x`**」⇒ 包内 **8 处悬空引用**(`§0.05/§0.5.2/§0.5.4/§0.5.5/§1.3/§1.4a/§3/§6`,实测这些章节只存在于原文件)。
|
||
修法:**逐字带进** `references/collab-detail.md`(头部写明「**冲突以 `architecture.md` 为准**」,避免"多份文档打架")+ 8 处全改指 + 清掉另 9 处旧名/旧路径活文本(含 `collabd.py` 用户可见的启动提示)。
|
||
- ✅ **§9.4**:`references/manifest.md` 重算(**33 文件 / 0 语法失败**);`board_ext.py` 与使用方原件重新同步。
|
||
- ⚠️ **未了(已写进包内 manifest「包内待补」)**:① §9.3 的三步(须先停看板);② `collabd.load_cfg()` 对「配置存在但解析失败」不区分、还谎报"不存在"(要同时改 `07-scripts` 原件与包内副本)。
|
||
|
||
## 【同一棒 · 用户当场追问】工具日志该不该存 / 怎么自动清 —— 实测结论
|
||
|
||
- 🔴 **10 MiB 问题的真身**:`<配置根>/logs/<YYYY-MM-DD>/sdk/conversations/<session_id>.log`。今日实测 **42 个**,其中 **4 个卡在 10,485,7xx B**(10.0 MiB)—— 用户口中"一直在耗"的就是它。
|
||
- ✅ **现成工具已存在且判据正确**:`.workbuddy/tools/wb-logcap-sweep.py`(`--dry-run` 实测准确点出那 4 个;只处理**当天**目录、只碰 `sdk/conversations/*.log`、**只改名不删**、mtime 冻结 >10 s 才动)。2026-09-29 实测:改名后 30 s 内宿主重建、丢写告警归零。
|
||
- 🔴 **真正的缺口=没人自动跑它**:`logcap-sweep.cmd` 是**交互式**(`choice /Y/N` = 要人点)⇒ 现在全靠人手。⇒ 结论:**不用"拦截"、也不用"删"**,把它接成定时/由日志闸顺手触发即可(属机制层 + 排期,须用户点头)。
|
||
|
||
|
||
## 【同棒 · 用户报障】接续会话频繁弹「抱歉,发生未知错误暂无响应」—— 根因已坐实
|
||
|
||
- 🔴 **根因**:接续/协作类自动化被设成 **`model_id=deepseek-v4.1-flash` + `model_is_thinking=0`(关思考)**,而**该模型不支持关闭思考**
|
||
⇒ 网关回 `-32603 Internal error / "Current model does not support disabling thinking (modelId=deepseek-v4.1-flash)"`
|
||
⇒ 宿主把首轮判成 `to=error` ⇒ **界面就弹那句「抱歉,发生未知错误暂无响应。您可以稍后再试,或尝试新建任务、切换模型。」**
|
||
- **实证(daemon.log,本会话自己的诞生时序)**:`07:55:17.079Z execute start 2e0bcb75` → `07:55:17.392Z session attached 3f43ce71-f763-…` → **`07:55:18.731Z stateChange from=working,to=error, terminalError={…refusal,-32603,…not support disabling thinking…}`**(=本会话)。
|
||
- **不致命的机制**:宿主随后用 `thought-level-fallback:attempt:1` 重试(daemon.log 共 236 条:b103b9ec 153 + 2e0bcb75 86 条)⇒ 最终能跑起来,所以表现为「**经常弹错但活还干得成**」。
|
||
- **影响面(实测)**:`automations` 里 **12 条 = deepseek-v4.1-flash + thinking=0**(错的一侧);另 **3 条 = hy4-preview + thinking=1**(正常)。`automation_runs` 里 **0 条失败记录** ⇒ **这个错不进库,只能靠 daemon.log 查**。
|
||
- ⚠️ **修法**:把这 12 条的「思考」开关打开(或改用支持关思考的模型)。**但**:① 用户明令⛔不许动自动化排期 ⇒ 本棒**未改**;② 产品工具 `automation_update` **无模型/思考字段** ⇒ 没有合规通路,只能 UI 改或用户明确授权直改库。
|
||
- 📌 **诊断法(可复用)**:`grep -o 'terminalError={[^}]*}' ~/.workbuddy/logs/daemon.log` ⇒ 终端错误的原文只在这里;
|
||
再对 `automations` 表比 `model_id` × `model_is_thinking`,即可定位「模型能力 ↔ 开关」不匹配。
|
||
|
||
|
||
### 追加:谁让「关闭思考」的 —— 追查结论(用户追问「谁让关闭思考的」)
|
||
|
||
- 🔴 **没有人主动关 —— 那是建自动化时的默认值**。实测 `automations` **全表 110 条**:`think=0` 占 **107 条**
|
||
(95 条 `deepseek-v4.1-flash` + 10 条 `custom-local:deepseek-flash`),只有 **3 条 `think=1`** 且全是 `hy4-preview`。
|
||
⇒ 「关思考」与**模型族绑定**、与人无关。
|
||
- 🔴 **AI 侧根本没有选择权**:我给会话用的建自动化工具 schema 里**只有 name / prompt / 排期 / 状态 / cwds**,
|
||
**没有 model 也没有 thinking 字段** ⇒ 凡由 AI 登记的自动化(本线 12 条的名字形如「接续 · …」「[协作]-…」「主控 · …」)**只能吃默认值 0**。
|
||
- ⇒ **根因升级为产品缺陷**:`deepseek-v4.1-flash` 的**默认「不思考」本身就该模型拒绝**(网关原话 `does not support disabling thinking`)
|
||
⇒ 「默认值 ↔ 模型能力」自相矛盾 ⇒ 凡用它建的自动化**必然首轮报错**(靠降级重试兜住)。
|
||
- ⚠️ 因此修法有两层:**表层**=把那 12 条改成「思考=开」;**治本**=要么让产品不给该模型落 `think=0` 默认,要么新建自动化时校验「模型是否支持所选思考开关」。
|
||
|
||
|
||
### 追加(用户:「到底怎么解决问题」)—— 会话日志长效治理:已落地 + 两条实测
|
||
|
||
**关键实测(本机,零风险 scratch 复现)**
|
||
- 🔴 **正在被写入的文件【不可以】改名**:起一个持续 append 的子进程,父进程在写入进行中 `os.rename` ⇒ 连试 3 次全 `WinError 32`。
|
||
⇒ **「在日志闸钩子里就地回收、让会话永不撞顶」这条路被实测否掉**(`wb-logcap-sweep.py` 的「mtime 冻结 >10s」不是保守,是**唯一能改名的窗口**)。
|
||
- 🔴 **哑掉的会话不会产生任何事件 ⇒ 钩子根本不会跑** ⇒ **只有"会话外的时钟"能救它**(=项目原话「沉睡者不会自己醒」)。
|
||
⇒ 长效机制**只能**是「会话外分钟级清扫 + 撞顶后尽快恢复写入」。
|
||
|
||
**已落地(会话外时钟)**
|
||
- ✅ Windows 计划任务 `WorkBuddy-LogCapSweep`:**每 1 分钟**跑一次 `wb-logcap-sweep.py --quiet`(只改名、⛔ 不删)。
|
||
实测:State=Ready、LastTaskResult=0、已按点触发。旧 4 个卡死日志(各 10.0 MiB、mtime 冻结 1.3–4.7 小时)**已当场挪开**,复验「无需处理」。
|
||
- ⚠️ **为什么不用那条现成的自动化**:`07976988 WorkBuddy 日志定时清理` 是 `FREQ=DAILY;BYHOUR=2` ⇒ **一天只跑一次**(撞顶是 20 分钟内的事,频率完全不匹配);
|
||
且它靠**起 AI 会话**去删(烧 token),还被日期目录上的 `Deny Delete` ACL 挡住 —— 它的跑次记录里明写「**日志段一个都没删掉,权限被硬锁了**」。
|
||
⇒ 结论:**机械动作必须下沉到常驻程序,不能挂 AI 自动化**(这也是项目既定原理)。
|
||
|
||
**剩余未验证点(交给接续棒做有界实验)**
|
||
- ❓ **工具调用的间隙里,日志文件是否被释放**?我的复现是「句柄常开」⇒ 失败;但宿主大概率是**每次调用开-写-关**
|
||
⇒ 若"间隙可改名",则钩子就地回收**仍然可行**(可把会话寿命从 ~280 次调用拉到无限)。
|
||
- ❓ **恢复写入之后,那一轮会话会不会自己继续**?(改名只保证"记录恢复",不保证"已失败的那轮重跑")
|
||
⇒ 若不自动继续 ⇒ 必须靠「哑掉检测 → 自动重排接续」兜住链条(本机已有 `_deaf_sids` 判哑机制,需确认覆盖"日志哑掉"这一型)。
|
||
|
||
|
||
## 【接续会话】会话机制合并技能包 —— §9.3 解阻收尾 + ② 日志自愈落地 + ③ 缺口定位(2026-10-01 16:5x · 会话 `会话机制合并包-收尾2-20261001`)
|
||
|
||
### ① §9.3 已解开(先前判为"阻塞"的那步)
|
||
|
||
- 🔴 **真因确认无需新工具**:包内 `board.py --serve 8788 --takeover` **自带"先停旧实例"**(`_stop_pid` → `taskkill /F`)
|
||
⇒ 一条命令同时完成「停旧看板 + 用包内代码起新看板」。**顺序改成「接管 → 移目录 → 复测」比原计划「先停 → 移 → 再启」更安全**
|
||
(新代码先验证可用;万一它起不来,目录还没动,旧实例可直接回退)。
|
||
- 实测:`✓ 接管:已停旧看板 PID 21084(成功)`;新 **PID 35068**;`healthz {"ok":true}`、`/` **HTTP 200 / 84,874 B**、`/board.json` 是**真实快照**(goal+V1–V5 全在);8788 上**只有 1 个**实例。
|
||
- 两个原技能目录已 `os.rename` 移入 `归档/技能-退役-20261001/`(⛔ 未用 `mv`);`agent-operating-rules` **原样保留**。
|
||
- **三项复测全过**:`wb-result-hook.py` 空载荷 `rc=0` 零输出 | `goalctl._collabd()` 与 `wb-result-hook._collabd_ctx()` **运行时**都解析到 `<包>/scripts/collabd.py`(写了个只读探针实测,不是读代码猜)| `install.py --verify` **PASS 39 / FAIL 0**、`✅ 全绿`。
|
||
- ⚠️ 本机看板的**唯一可行起法**=会话后台任务 + stdout 全重定向(旧实例 08:05 起、存活 9 小时 ⇒ 该模式实测 durable);新实例日志 `tmp/board-serve.out.log`。
|
||
|
||
### ② 有界实验:会话日志"自愈" —— 🔴 **可行(推翻了当天早些时候的相反结论)**
|
||
|
||
- **实测**:在真实会话里对**本会话自己的** `logs/2026-10-01/sdk/conversations/63f5e790-….log` 做**一次** `os.rename` ⇒ **成功**(无 `WinError 32`);
|
||
紧邻的下一次读数:**原文件名已回来**(宿主重建、50,073 B)且继续增长,旧件**冻结**在 1,728,992 B。
|
||
- ⇒ 判语:**宿主不是"句柄常开",而是每次日志批写开-写-关** ⇒ **工具调用间隙里句柄是关着的**。
|
||
当天更早那条"不可改名"只对**"正在被写的那一瞬"**成立(scratch 复现里子进程一直在 append)——两条**不矛盾**,别再把前者当全局结论。
|
||
- ✅ **已接进机制**(`<包>/scripts/hooks/session-log-guard.py` + 文档库原件 `07-scripts/session-log-guard.py` 同语义同步):
|
||
硬档(8 MiB)**先试一次就地回收**(改名 `.recycled-<时间戳>`)⇒ 成功则**复位档位**+注入 ♻️ 提示("可继续、不必停手");
|
||
失败则**逐字退回**原「停手+建接续」口径。⛔ 只动本会话自己那份、⛔ 只试一次、不 sleep 不重试、⛔ 后缀不以 `.log` 结尾(与清扫器天然不重叠)。开关 `DSH_SLG_NO_RECYCLE=1`。
|
||
- **验收**:空载荷 `rc=0` 零输出 ✅ | 未达档真实载荷 `rc=0` 零输出 ✅ | `--verify` 全绿 ✅ | 验收通道端到端跑出真记录 `RECYCLE|size=279862 B|… -> …recycled-20261001-165257` + ♻️ 注入 1,077 B ✅(通道文件**跑完即删**,已复核不存在)。
|
||
- ⚠️ **途中修掉 1 个真缺陷**:提示串里字面量 `97.8%` 未转义 ⇒ `TypeError: not enough arguments for format string`(回收已生效、只是提示发不出)⇒ 两副本均改 `%%`。
|
||
- ⚠️ 另一处:回收阈值**必须传实际生效的 `hard`**(⛔ 不写死模块常量),否则验收通道只能验"叫停"、**验不到"回收"** —— 这个坑实际踩到过。
|
||
- ⛔ **仍未验证**:回收后**那一轮已失败的会话会不会自己继续**(本轮只证明"写入不中断")。
|
||
|
||
### ③ 缺口:链路上的"哑掉"只拦不排 —— 🔴 **部分覆盖,已写清最小修法,⛔ 未改代码**
|
||
|
||
- **已覆盖**:`_deaf_sids()` 检测(最新一天 ≥9.5 MiB)|两条投递路径命中即 `skipped=target-deaf`+`need_user`(**不记假绿**)|`_pick_live()` 把哑会话剔出主会话候选。
|
||
- 🔴 **未覆盖**:**没有"重排"** —— 处置只有"喊用户换会话";且 `reconcile()` 的僵尸判据是「台账 running **且除观察者外无人在跑**」,
|
||
哑会话在库里**仍记 `working`** 时会被当成"有人还在跑" ⇒ **僵尸判据永不触发** ⇒ 死角:**投不进去、又不判死、也没人重派**。
|
||
(若哑会话在库里已转 `completed`,僵尸判据**可以**触发 ⇒ 属"条件性覆盖"。)
|
||
- ⛔ `collabd.py` **设计上就不派活/不开新会话**(其 docstring 第 12 行明写)⇒ 不能指望它自动补接续。
|
||
- **最小修法(未实施)**:`reconcile()` 里把 `_deaf_sids()` 命中者**从"在跑"集合剔除**(1–2 行,复用现成检测)⇒ 僵尸判据得以触发 ⇒ 标 `blocked`+`待主会话重派`。
|
||
⚠️ 比较口径:`_deaf_sids()` 返回**完整 sid**,`reconcile` 里 `me` 是**前 8 位** ⇒ 剔哑要按完整 sid 比。
|
||
⚠️ 前置:先让 ② 跑一段时间(它已把撞顶概率压低)再决定这条兜底要不要上。
|
||
|
||
### 其他
|
||
|
||
- ⛔ 未动:自动化排期、`decision_bridge` 线、`wb-logcap-sweep.py` 与计划任务 `WorkBuddy-LogCapSweep`、`tmp/inv-20261001/`、`collabd.load_cfg()`(§9.5 遗留)。
|
||
- ✅ `references/manifest.md` 定点更新(只有 `session-log-guard.py` 一份被动过:18,527→24,770 B,md5 `3e09…`→`3c4180a71ebee7a9639e4e10be3681dd`;并注明 provenance 列里的 `skills/multi-session-collab/…` 已是历史路径)。
|
||
- ✅ 指针残留复核(9 个活文件):`CODEBUDDY.md`/两份 `MEMORY.md`/`board_ext.py`/`协同监管棒-SOP.md`/`agent-operating-rules/SKILL.md` **命中 0**;
|
||
`goalctl.py` 与 `wb-result-hook.py` 各 3 处是**刻意保留的过渡兜底**(⛔ 不删);`任务图.json` 剩 1 处是 13:05 的**历史记述**(⛔ 不改历史)。
|
||
- 锁:机制层**独占**,已反序释放(`--release` → `--release-exec "会话机制合并包-收尾2-20261001"`)。
|
||
|
||
### 2026-10-01 17:0x · 🔴 会话日志洪水根因查清(用户问的"根本问题哪里来的")
|
||
|
||
- 实测构成(样本 `logs/2026-10-01/sdk/conversations/5f607d3e-*.log` 尾段 **16,159 行 / 4 MB**):**97.72% 的行是 `event-machine:dispatch`**;其中 **95.83% 是同一条 `input:"tool_call_update"`** —— 244 B/行、逐字相同,**只有 `requestId` 在变**(`output:{completeAssistantStream:false,hasUsage:false,hasTitle:false,resourceEffectCount:0}`)。有信息量的行合计不到 2.5%。
|
||
- 发出点:`app.asar` @117210567 —— `if (shouldLogEventMachineDispatch(update?.sessionUpdate, frameContext.replayingHistory, result.completeAssistantStream === true)) this.init.log("event-machine:dispatch", {...})`;门控定义 @117230304:`var STREAM_CHUNK_UPDATES = new Set(["agent_message_chunk","agent_thought_chunk"])`,函数注释写明「**默认丢掉历史回放帧和正文流式 chunk**」。
|
||
- 🔴 **缺陷**:排除集合只列了两种**正文** chunk,**漏掉 `tool_call` / `tool_call_update`** —— 而工具入参同样是流式增量(一次工具调用几十上百帧)⇒ **真正的洪水正是这两个被漏掉的 key**,抑制机制等于没生效。
|
||
- ✅ **一行修复**:`STREAM_CHUNK_UPDATES` 补上 `"tool_call"`、`"tool_call_update"` ⇒ 体积降约 **96%**;按 ≈37 KB/次调用折算,10 MiB 上限对应寿命由 **≈280 次 → ≈7,000 次**工具调用,正常会话再也撞不到顶。
|
||
- ⛔ **外部关不掉**:`WB_CONVERSATION_LOG_CONTENT` 只能**放大**(=`1` 变 verbose + 恢复 replay 逐帧),不存在"设 0 少记";写入器目录 `join(homeDir,"logs")` 与上限 `PER_FILE_DISK_MAX_BYTES=10485760` 均硬编码(`perFileDiskMaxBytes` 全包仅类内一处、无调用方传值);且"让它写不进去"=直接落入丢写分支=**故障态本身**。
|
||
- 相邻问题:轮转 `rotateFile()` 四步串行、**全成才重置计数器** ⇒ 任一步 `EPERM`(文件被占用)⇒ 计数器恒满 ⇒ 此后每次 flush 重试必败、退避封顶 30 s、每轮只 warn 一次 ⇒ **该会话日志永久写不进**(`daemon.log` 47 次 + `daemon.old.log` 62 次 EPERM,droppedLines 涨到 2445;`f8a792ab-*.log` 卡死 10 MiB 且无 `.log.1` ↔ `a202550c-*` 四件齐)。
|
||
- 完整报障件 ⇒ `交付物/宿主会话日志洪水-根因与一行修复-20261001.md`。
|
||
- ⛔ 本条**只读取证**(解包 app.asar + 读日志),未改任何宿主/共享文件;临时件(asar-scan6/7、emit-src、gate-src、line-shapes)已清理。锁:域锁 `ai1net-dsh-server/`,已 `--release-exec` 释放。
|
||
|
||
#### 2026-10-01 17:0x · 补:待改代码的**完整副本清单**(用户要"在哪里改")
|
||
|
||
- 全量扫描 `C:\Users\Administrator\AppData\Local\Programs\WorkBuddy\resources`:同一处 `new Set(["agent_message_chunk", "agent_thought_chunk"])` 共 **5 个副本**。
|
||
- ✅ 磁盘明文(可直接编辑):`app.asar.unpacked\cli\dist\codebuddy-headless.js` 文件内偏移 2,157,283;`app.asar.unpacked\cli\dist\codebuddy-lite-wb.mjs` 文件内偏移 1,768,678。
|
||
- ❌ 归档内(须解包/重打包):`app.asar` 内偏移 117,230,150 与 287,051,214。
|
||
- ⚠️ 另有一处写法不同:`app.asar` 内偏移 311,175,531,形如 `new Set([...]).has(updateType)`,可能是另一路过滤逻辑,⛔ 先别动。
|
||
- 🔴 **asar 内部不能直接文本替换**:头部记录每个文件的 `size`/`offset` 且带 SHA256 完整性块,改长度⇒其后文件偏移全错位⇒必须 `@electron/asar extract` → 改 → `pack`,或改名 `app.asar` 让它直接加载 `resources/app/` 目录。
|
||
- ⚠️ 风险:升级即覆盖;完整性块可能拒绝加载(未实测);厂商签名主程序 ⛔ 项目红线不许我代改 ⇒ 已把"位置+查换串+做法+风险"写进报障件 §九,交由用户决定。
|
||
- 完整内容 ⇒ `交付物/宿主会话日志洪水-根因与一行修复-20261001.md` §九。
|
||
|
||
#### 2026-10-01 17:2x · 补:补丁脚本交付(已实测)+一处**自我更正**
|
||
|
||
- 🔴 **更正**:先前把两个 CLI 明文包(`app.asar.unpacked\cli\dist\codebuddy-headless.js` / `codebuddy-lite-wb.mjs`)列为补丁目标是**误判**——它们里的同名集合是 `createAcpHistoryMarker` 用的历史标记集合,**本来就含 `tool_call`/`tool_call_update`**,且**不含** `event-machine:dispatch`,与本问题无关。真正补丁点只在 `app.asar` 内:绝对偏移 **117,230,150** 与 **287,051,214**(均后接 `;`=定义);另有 311,175,531 是 `.has(updateType)` 的相似用法,⛔ 不动。
|
||
- **方案=就地等长替换**:`new Set(["agent_message_chunk", "agent_thought_chunk"])`(55 B)→ `{has:()=>true}` + 补空格(55 B)。因头部记录 `size`/`offset`,**改长度即错位**;等长则全不变,只需把该文件的 integrity 记录(1 总哈希 + 6 分块哈希)**原地等长重算**回去。
|
||
- 交付物(桌面):`WorkBuddy-日志补丁-20261001\` = `logpatch.py` + `说明.txt` + `ui-docs-viewer-Bo02yf1Q.js.patched`;工作区留副本 `交付物/logpatch.py`。
|
||
- 实测(**在 app.asar 副本上跑,⛔ 未动真身**):替换 2 处|总长不变 317,114,821 B|头部可正常解析|integrity 7 项全部对齐|幂等(二次运行识别"已打过补丁")|`--revert` 可还原。
|
||
- 📌 **频率实测**(回答用户"为什么这么高频"):样本尾段 24,291 行中 `tool_call_update` **23,179 帧** ↔ `tool_call` **116 次** ⇒ **约 200 帧/次工具调用**、约 **48 KB/次**。⇒ **帧数**由"工具入参按增量流式推送"决定(上游协议),**是否落盘**由那段门控代码决定(本次缺陷)。
|
||
- ⚠️ 派生纪律:这类"厂商程序自身的坑"只做**一次定性取证 + 落档**,⛔ 不往下钻第二层。
|
||
|
||
#### 2026-10-01 17:2x–17:3x · 🔴 补丁**实装真身** + 停掉治标清扫器(用户两次指令)
|
||
|
||
- 🔴 **已改真身**(用户原话「改 不想在看到这个问题了,备份官方的文件」):
|
||
- 备份:`C:\Users\Administrator\AppData\Local\Programs\WorkBuddy\resources\app.asar.bak-20261001`(317,114,821 B,原文件日期 Sep 21 20:37)。
|
||
- 改后:同目录 `app.asar` 就地重写(317,114,821 B,Oct 1 17:25)。命中「集合定义」**2 处** —— `@117230150`(属 `editor_sdk.exe`,`unpacked=True`)与 `@287051214`(属 `renderer/assets/ui-docs-viewer-Bo02yf1Q.js`,`unpacked=False`)。
|
||
- 自检:**大小未变 True(317114821)**|旧串残留 **1 处**(=故意保留的 `.has(updateType)` 变体 @311,175,531)|新串出现 **2 处**|**头部仍可解析 True**|integrity 重算 "改 7 处:hash 已更新 + 6/6 个块"。
|
||
- 换法(等长):`new Set(["agent_message_chunk", "agent_thought_chunk"])`(55 B)→ `{has:()=>true}` + 7 空格(55 B)。语义=让 `.has()` 恒真 ⇒ `shouldLogEventMissionDispatch` 只对**收尾帧**放行 ⇒ 体积降约 96%。
|
||
- 回滚:`python logpatch.py --revert`(从 `app.asar.bak-20261001` 还原);或手工把 `app.asar.bak-20261001` 覆盖回 `app.asar`(须先退出 WorkBuddy)。
|
||
- 🔴 **已停治标程序**(用户原话「然后把掉治标的补丁程序停了」):Windows 计划任务 `WorkBuddy-LogCapSweep` → `Disable-ScheduledTask` ⇒ **`State=Disabled`**(回读确认:`LastRunTime=10/01/2026 17:25:12 LastTaskResult=0`;Trigger 仍 `PT1M Enabled=True` 但任务已禁用 ⇒ 不再触发)。同时**删掉** 18:00 那根一次性接续棒自动化 `dae0d42d-b4a0-4a1d-a732-29c265b19eba`("接续 · 日志清扫器 6 小时盲区(极小棒)")。
|
||
- ⇒ **从此唯一防线=asar 补丁本身**(⛔ 不再有分钟级清扫兜底)。⚠️ **且补丁须重启 WorkBuddy 才生效**。
|
||
- ⚠️ 重新启用清扫器(如需):`Enable-ScheduledTask -TaskName 'WorkBuddy-LogCapSweep'`(`schtasks.exe` 在本机被黑名单拦截,只能走 PowerShell `ScheduledTasks` 模块)。
|
||
- 锁:全程域锁 `ai1net-dsh-server/`;本棒收尾已 `--release-exec "日志补丁实装-20261001"` 释放。
|
||
|
||
#### 2026-10-01 17:3x · 🔴 重启前核对 → 查出**补丁自身缺陷**并修掉 + 桌面双击脚本
|
||
|
||
- 用户指令「核对一遍我就重启」⇒ 做了一次**独立核对**(独立脚本,不复用补丁脚本的判定逻辑,才能查出补丁的 bug)。
|
||
- 🔴 **查出一个真缺陷**:`owner_of()` 取"第一个命中区间者",而 `unpacked` 文件头部是 `offset=0 + 真实 size`(`editor_sdk.exe`:0 + 209,494,568)⇒ 把 @117230150 的归属**抢走**;完整性重算那步又写着"遇 unpacked 就 continue" ⇒ **该处真正的宿主 `main/conversations.js` 字节被改、完整性却没重算**(`hash=MISMATCH`)。
|
||
⇒ 教训:首轮"自检全绿"是**假绿** —— 只验了自己重算过的那个文件,**没有反向核对"该重算的是否都重算了"**。
|
||
- ✅ 已修:按文件条目(用其**唯一 `offset` 做锚点**,避开 basename 歧义)就地重算 `main/conversations.js` 的 `hash` + `blocks`,**先在内存验证通过才落盘**;落盘后差异只落在头部 1 个 1 MiB 块内。
|
||
- ✅ 核对结论:归档**无整文件级顶层哈希**(改字节不会触发整文件校验失败);与官方备份逐 1 MiB 比对,差异**只在头部(完整性记录)+ 2 处补丁点**;全量完整性 **打包文件 17,800 个全过 / 0 不过**;两处补丁点均 `hash=OK`(`main/conversations.js` blocks 1/1;`ui-docs-viewer-*.js` blocks 6/6)。
|
||
- ✅ 交付**桌面双击脚本**(`桌面\WorkBuddy-日志补丁-20261001\`):`应用补丁.bat` / `恢复原始文件.bat` / `检查状态.bat` + `logpatch.py` + `说明.txt`。中文路径下双击链路**实测跑通**;`--revert` 分支在临时目录用假文件验证通过。
|
||
- 🔧 `logpatch.py` 已修:`owner_packed()` 排除 `unpacked` + 多候选取**最小者**;找不到归属 ⇒ **中止不写盘**;新增 `--verify` 全量核对;输出符号降级为 ASCII 标签(避免 cmd 控制台显示方框)。
|
||
- 报障件 §九 已更正(补上归属文件、缺陷与处置、最终核对结果)。锁:域锁 `ai1net-dsh-server/`,收尾释放。
|
||
|
||
#### 2026-10-01 17:4x · 端到端测试(用户「在测试一遍」· 全程不碰真身)
|
||
|
||
- **测试方法**:从**官方备份**复制一份未打补丁的副本(`tmp/asar-e2e/app.asar`,哈希 `c4304eec…`)当靶子,全程只动副本。
|
||
- **脚本层(T3–T8)**:T3 `--check` 报 2 处命中且**归属正确**(`main/conversations.js` / `ui-docs-viewer-*.js`,不再被 `editor_sdk.exe` 抢)⇒ **归属修复生效**|T4 打补丁:2 处命中、**两个文件都重算了完整性**(1/1 + 6/6)、落盘前自检全过|T5 `--verify` 17800 全过 / 0 不过|T6 幂等(再打一次报 0 处命中、什么都不做)|T7 `--revert` 还原|T8 还原后哈希 = `c4304eec…` **与官方原文件逐字节一致**。
|
||
- 🔴 **强结论**:副本打补丁后的哈希 = `e24ac2b7…` **与真身当前哈希完全相同** ⇒ 真身状态正是"从官方备份重跑本脚本"的产物,**全流程可复现**。
|
||
- **批处理层**:给三个 `.bat` 加了可选目标参数(`%~1` → `--asar`),正常双击行为不变;随后在副本上跑通 `应用补丁.bat` / `检查状态.bat` / `恢复原始文件.bat` 三条完整链路(还原后哈希仍 = `c4304eec…`)。
|
||
- **真身默认路径**:`检查状态.bat`(只读)与 `应用补丁.bat`(已补丁态 ⇒ 自动跳过)在真身上跑通。
|
||
- ✅ **真身未被触碰**:测试前后 sha256 均为 `e24ac2b7…`,mtime 仍 17:32。测试副本已清除。
|
||
- 锁:域锁 `ai1net-dsh-server/`,收尾释放。
|
||
|
||
#### 2026-10-01 17:5x · 🔴 打补丁 ⇒ **WorkBuddy 无法启动**(用户报障)⇒ asar 补丁此路不通
|
||
|
||
- 用户报障「修改文件后无法启动」。**取证结论**:
|
||
- `app.asar` **创建时间仍是 9/23**(原文件对象)、**修改时间已回落到 9/21 20:37**(=官方备份的时间戳)⇒ 该文件是被**就地覆盖还原**过的,**补丁已不在**。
|
||
- 新实例 **17:52:04 启动**,`logs/startup/startup-2026-10-01.log` 一路走到 `STARTUP COMPLETE`,末行 `daemon-ready -> renderer-mounted -> ready` ⇒ **当前能正常启动**。
|
||
- 旧实例 PID 32744 在 **17:51:01 `before-quit (userConfirmed=true)`** 正常退出,17:51:06 `finishQuit`。
|
||
- 🔴 **结论:改了 `app.asar` ⇒ 起不来;还原回官方原文件 ⇒ 起得来。** 且失败发生在 **App JS 引导之前**——旁证:`startup/*.log` 的首行就是 `bootstrapMainProcess entered`,而失败那次**没有留下任何 startup 日志**。⇒ 与我此前查到的"per-file integrity 记录"**不是**同一层校验,说明存在**归档之外**(我无法从 asar 内部看到)的启动校验。
|
||
- ⚠️ **教训(推翻我上一轮的判断)**:我上一轮核对"无整文件级顶层哈希、17800 个打包文件完整性全过"就下了"重启不会加载失败"的结论 —— **这个结论是错的**。**"我能看到的校验都过了" ≠ "没有校验"**。Electron 应用可能存在归档之外的完整性/签名校验,**只有真机启动才是唯一验收**。
|
||
- ✅ **已处置**:`说明.txt` 顶部加「本补丁已停用」警示块;`应用补丁.bat` 加**防呆门**(必须手输 `YES` 才执行,输入空即中止、不改任何文件——已实测)。`检查状态.bat` / `恢复原始文件.bat` 保留可用。
|
||
- 📌 **待选替代方案**(不碰程序文件):① 查清"谁占用日志文件导致轮转失败"(唯一可能根治)② 恢复分钟级清扫器(已验证、治标兜底)③ 改名 `app.asar` 走解包目录 ④ 减少单会话工具调用次数 ⑤ 报厂商。
|
||
- 🔴 **口径固化**:**⛔ 不再对 WorkBuddy 的 `app.asar` 做任何改动**;日志问题只走"不动程序文件"的路。
|
||
- 锁:域锁 `ai1net-dsh-server/`,收尾释放。
|
||
|
||
#### 2026-10-01 17:5x · 补测归因:**「200 帧 / 48 KB 是谁的问题」**(用户提问)
|
||
|
||
- 用户问:「一次工具调用写约 200 帧、约 48 KB,也是那个文件的问题吗 还是工具调用方式不对」⇒ 做全量统计归因(4 个会话日志)。
|
||
- 🔴 **帧是零信息的**:完整字段只有 `instanceId` / `input` / `requestId` / `output`;**没有** `seq`/`messageId`/`textLen`/`textHash`(39,999 帧**全部**"无此字段");`output` 四个位恒为 `false`/`0`;**同一轮内逐字节相同**。
|
||
- 🔴 **帧数=活动量的函数,不是定时心跳**:同一轮内帧间隔**中位 0 ms**(20,697 个间隔里 **12,259 个 < 1 ms**)、平均 41.7 帧/秒 ⇒ **成串爆发**。
|
||
- 实测四会话「帧/次」= 169.5 / 183.3 / 138.9 / 209.9;**按轮次拆开在 5.2~422.5 之间波动、中位约 150~220**。🔴 **`requestId` 的真实粒度是「一轮对话」**(一轮内做几十次工具调用),不是单次调用。
|
||
- ✅ **归因结论(两件事分开)**:① 帧的**产生** = 宿主 agent 事件机逐条上报(协议/宿主行为,**外部改不了**;与"调用方式"只在**次数**上相关 ⇒ **不是"调用方式不对"**)② 帧的**落盘** = 门控缺陷(**本应丢弃**)⇒ **唯一责任在第二环**。
|
||
- 已把精确测量补进报障件 **§2.1 / §2.2 / §2.3**(含逐轮表、间隔分布表)。
|
||
- 锁:域锁 `ai1net-dsh-server/`,收尾释放。
|
||
|
||
#### 2026-10-01 18:0x · 补测二:帧 ← 哪个工具 / 改 json / 加 hook(用户三问)
|
||
|
||
- 用户问:「是调用什么工具造成的 还是所有工具,跟修改json和加hook有关系吗」。
|
||
- 🔴 **帧无法定位到具体工具**:逐字段查会话日志,`toolName` / `tool_name` / `kind` / `rawInput` / `toolCallId` **全为 0 次**;帧只有 `instanceId`/`input`/`requestId`/`output`,且 `requestId` 粒度是**一整轮对话** ⇒ **2 万行日志查不出"凶手是谁"**(这本身就是缺陷的一部分)。
|
||
- ✅ **不是某个特定工具,是所有工具**:帧类型 `tool_call_update` 是 ACP 里**所有工具通用**的状态更新通知(不含工具种类)⇒ 每次工具调用都必然产生若干帧;差异只来自"该调用期间产生了多少次更新"。
|
||
- ✅ **与改 json 无关**:源码发出点只读三个**运行时参数**(`sessionUpdate`/`replayingHistory`/`completeAssistantStream`),**不读任何配置**(MCP / settings 都不在这条链路上)。
|
||
- ✅ **与加 hook 无关**:hook 是宿主另起的**子进程回调**,不产生 ACP 的 `tool_call` 事件,因此不贡献 `tool_call_update` 帧。⚠️ 唯一间接路径:hook 若导致工具调用被拒并**重试**,会多出"调用次数"⇒ 按比例多出帧(间接、有界)。
|
||
- 🔴 **实测反证(最强)**:四会话跨整天(08:48 / 10:16 / 12:35 / 15:55 +08),期间 `settings.json` 与 hook 配置**改过多次**,但「帧/次」始终稳定 **169.5 / 183.3 / 138.9 / 209.9**、**无漂移** ⇒ 帧数**不随配置与 hook 变**,只随"该轮工具调用次数"变。
|
||
- 🔴 **活样本(写报告那一刻)**:本会话日志 `3f43ce71-*.log` **就是卡死的** —— 10,485,710 B(上限 10,485,760,卡在距上限 50 B)、mtime 停在 **16:55** 而观测时 **18:04**、同目录**无 `.log.1`**(轮转从未成功)⇒ **最近一个多小时诊断日志全丢**,但**功能不受影响**。与"轮转失败后永不恢复"完全吻合。
|
||
- 已补进报障件 **§2.4 / §2.5 / §2.6**。锁:域锁 `ai1net-dsh-server/`,收尾释放。
|
||
- 🔴 **报障件收口(用户:"把故障报给官方")**:报告新增 **§十「可直接提交给厂商的缺陷说明」** —— 自包含、可整段复制,不必带其余章节。含:基本信息(**WorkBuddy 5.6.2** / Electron 37.10.3-24 / Windows,注册表 `DisplayName=WorkBuddy, DisplayVersion=5.6.2` 实测)、问题一(97.7% 零信息帧 + 门控漏 key 的源码 + 一行修复)、问题二(轮转失败后永久停写 + 活样本)、复现步骤、影响、我方环境、附件建议、提交入口与必填项。
|
||
- 🔴 **提交入口(已查证)**:① 应用内 **头像 → 设置 → 帮助与反馈 → 意见反馈**(勾"上传日志");② 邮件 **`[email protected]`**(1~2 个工作日;紧急在标题前加 `【紧急】`)。官方要求随附:标题 / 版本 / 平台 / 描述 / 截图或日志 / 复现步骤 —— 均已写进 §十。
|
||
- ⚠️ **应用内"提交"这一步必须用户本人点**:`ToolSearch` 只找到 `mcp__weixinpay__weixinpay_feedback`,那是**微信支付专用**(打包描述 + 当日支付日志),**不是**通用产品缺陷通道 ⇒ 只能由使用者走 UI 提交。
|
||
- 🔴 **另存一份可直接复制的提交稿到桌面**:`C:\Users\Administrator\Desktop\WorkBuddy-故障反馈-提交稿-20261001.md`(用"复制线"包住正文,避免 markdown 渲染时丢格式)。
|
||
- 锁:域锁 `ai1net-dsh-server/`(会话 `缺陷说明收口-20261001`)**已释放**(`--release-exec` 回显"✓ 已释放域锁");回显另提示**有 1 把锁属他人**,按 R9 未动。
|
||
|
||
## 18:2x · 会话机制技能包 —— 整合复核 + 瘦身(会话 `技能整合检查与瘦身-20261001`,机制层 ⇒ 全局独占锁)
|
||
|
||
- ✅ **整合复核(独立复跑,不采信旧结论)**:`install.py --verify` **全绿** —— 10 条钩子空载荷 `rc=0`|`collabd --where` → `<包>/scripts/collabd.py`|selftest **PASS 39 / FAIL 0**。两个原技能已在 `<工作区>/归档/技能-退役-20261001/{multi-session-collab,workbuddy-session-forensics}/`(tar 377,103 B 在旁)。活文件零旧技能名残留(命中项都是"原 X 技能"的正当溯源或过渡回落路径)。
|
||
- ✅ **包瘦身 1,098,918 → 1,000,604 B**:① `collab-detail.md` **删原件 YAML 变更流水**(14 行 / 9,858 B —— 那是旧技能的发布流水,参考件里零价值且会腐坏)+ **加「读法索引」表**(标出哪些章节是活判据、哪些已被 `architecture.md` 取代 ⇒ 让 AI 不必从头顺读 88 KB)+ 压缩 §12。② `manifest.md` 去「本轮/上一轮变更」流水账、修**已过期的「未了①(阻塞)」**(目录早已移走)、§12 与头部统一。③ `taskgraph/pitfalls` 旧抬头改指。④ 工作区 `goalctl.py` 的旧技能名文案与包内对齐(两边曾漂移)。⑤ 清包内 `__pycache__`×2 + `hooks/bak-20261001/`(移到 `归档/技能包-旧件-20261001/`)+ 包内 `tmp/`。
|
||
- 🔴 **修掉 1 个真缺陷(包内总冒出 `tmp/` 的根因)**:`install.py --verify` 跑 `collabd --where` **没传 `cwd`** ⇒ collabd 按调用者 cwd 推导工作区 ⇒ **把技能包自己当成工作区**、在 `<包>/tmp/supervise-inbox/` 写出运行态日志(`deploy.md §0` 明写"运行态产物⛔ 不进 skill")。已改为 **②③ 都在 `tempfile.mkdtemp()` 里跑 + `finally` 清理**;复跑已确认不再重生。
|
||
- 🔴 **上抛(⛔ 未擅自改,等用户拍板)**:`architecture.md` **同一文件内自相矛盾且与项目状态层相反** —— 「当前结论」表第 1/2 行 + **§5-1** 说「协作/投递**一直运行(常驻)**、09-30 已回退为 09-29 版、⛔ 不得用钩子取代常驻」;而**同文件 §4 抬头**写「⛔ 仍不靠常驻」、`collab.md §3` 写「⛔ 不靠常驻进程」、`deploy.md §7` 写「投递守护已于 09-29 23:59 止损停掉…旧说法『常驻投递进程=唯一投递方』**已不适用**」、项目层 `MEMORY.md` 状态层写「监管+投递都=钩子事件驱动…**⛔ 不常驻**」。⇒ 按 `architecture.md §9` 自己写的教训条款「**⛔ 不得擅自改"用户定案"**,要改须先说清"哪里坏了+证据"、等用户拍板」,**本轮只取证、未动一字**。
|
||
- 🔑 **教训(写码层)**:用脚本往文件里插入文本时**我必须自己带 `\n`** —— 本轮就漏了,4 行说明被拼成 1 行(已修)。⇒ 传**文本**一律走 `Edit`/`Write`;脚本只用来做**删除/替换/重算**这类机械动作。
|
||
|
||
## 18:5x · 外置根贯通到全部脚本 + 修掉两个真缺陷(会话 `常驻定案统一-20261001`,机制层 ⇒ 全局独占锁)
|
||
|
||
- 🔴 **用户规则(本轮原话)**:「**技能中要用相对路径**」⇒ 按此审计整包,查出**包自述与实现不符**:`SKILL.md §1` 写着「**各脚本**的根目录一律先读包内 `roots.env`」,但只有 **9 份**(钩子/锁)装了引导块,**7 个非钩子脚本仍写死盘符兜底**(`board` / `board_ext` / `collabd` / `deliver-gateway-token` / `goalctl` / `selftest` / `stop-collab`)。
|
||
- ✅ **补齐到 16 份** + **盘符字面量清零**:配置目录兜底改 `os.path.expanduser("~/.workbuddy")`,工作区兜底改 `DSH_WS_ROOT`/cwd,覆盖网日志 glob 改 `DSH_OVERLAY_LOG_GLOB`(⚠️ 原写死的 `E:/dsh-worker-dev` **本机根本不存在** ⇒ 手机接入的前置检查恒报"worker 掉了"=假警报);`install.py::write_roots` 增写 `CODEBUDDY_CONFIG_DIR`(可选键只在宿主 env 有值时落盘,⛔ 不猜)。
|
||
- ✅ **7 份加「输出编码兜底」**(`stdout/stderr.reconfigure(encoding="utf-8", errors="replace")`)。
|
||
- 🔴 **修真缺陷 ①:钩子把技能包当工作区**。`wb-result-hook.py:261` 的 `_WS_ROOT = 3×dirname(__file__)` —— 包内这份(`<包根>/scripts/hooks/`)推出来**正好=技能包自己** ⇒ 交给 collabd 的 `COLLABD_WORKSPACE` 是包目录 ⇒ 包内长出 `tmp/supervise-inbox/`。**同文件里本来就有正确的 `resolve_ws()`**(env 优先 + 内容标志上溯)⇒ 这不是"推错",是**同一事实两套实现**。改为复用 `resolve_ws()` + 未知工作区即停手。
|
||
- 🔴 **修真缺陷 ②:重定向 + GBK ⇒ `print("⛔…")` 抛异常 ⇒ 顶层记 `fatal`、整轮失败**。实测(工作区与包内日志同时出现)连续 4 次 `fatal 'gbk' codec can't encode '\u26d4'`。🔴 **这条正是常驻的拦路石** —— 常驻必须把 stdout 全重定向到文件,不修它**必死在第一句带 ⛔ 的输出上**。另修 `collabd.log()`:配置缺失时**拒写任何文件**(对齐它自己"不落任何文件"的承诺)。
|
||
- ✅ **验收**:`install.py --apply` 已真装(roots.env + settings.json,均带备份)|`--verify` **全绿**(10 条接线 `rc=0`、`collabd --where`、selftest **PASS 39 / FAIL 0**)|清单重算 **34 文件 / 0 失败**|复现原故障路径 **0 次 `fatal`**、包内保持干净|工作区 4 份副本(`board_ext` / `deliver-gateway-token` / `goalctl` / `stop-collab`)已与包内**逐字节一致**(差异全是本轮补丁,无工作区特有内容)。
|
||
- ✅ **沉淀**:`pitfalls.md` 新增 **P0-10**(含"⛔ 反面判据:外层 `rc=0` ⇒ 没崩"是错的 —— 钩子丢弃子进程输出,外面只看得到 0);`SKILL.md` 两条铁律改写(外置根 16 份 + 出口一律声明编码);`manifest.md` 补丁口径 9→16 + 纠「工作区 `tools/wb-result-hook.py` 是现行版本」的**过时说法**(宿主实际接线的是**包内**那份)。
|
||
- ⚠️ **行为变更(如实登记)**:修好 ① 后,钩子驱动的投递从"静默崩"变成**真会跑**;当前 `NEXT.md` 不存在 ⇒ 四道闸门不全 ⇒ **不会真的投出消息**。投递总闸 `wake_enable=true`。
|
||
- 📦 旧件备份 → `归档/技能包-旧件-20261001/bak-roots-20261001/`。
|
||
|
||
## 19:0x · (续 18:5x 同一会话)收尾:库内同源脚本对齐 + `install.py --manifest` 固化 + 释放锁
|
||
|
||
- ✅ **补完最后一处盘符清零(两处同源脚本)**:`scripts/lock/preflight-lock.sh` 与 `scripts/hooks/lock-guard-hook.py` —— **技能包**与**文档库 `07-scripts/`** 各有一份。
|
||
- 🔴 **结论:两份"有意不一致",⛔ 别当 bug 去修平**。判据是**位置**:包内那份住在技能包里,**推不出文档库根** ⇒ 只能走 `roots.env` +(`lock-guard-hook.py` 的)末位字面量兜底;库内那份住在 `<文档库>/07-scripts/` ⇒ **按 `__file__` 往上两级即文档库根**(已改成这个,库内自此零盘符)。⇒ 包内有引导块、库内没有,是位置决定的。已写进 `manifest.md` 的「最近改动」做永久说明。
|
||
- ✅ **对齐核对(规范化行尾后 `difflib` 比对)**:两对文件的**唯一差异就是上面那条 + 引导块**,语义等价;`lock-guard-hook.py` 库内 `py_compile` 通过、两份 `preflight-lock.sh` `bash -n` 均 rc=0(⚠️ 用 `PortableGit/usr/bin/bash.exe`,裸 `bash` 会落到 WSL)。
|
||
- ✅ **固化 `install.py --manifest`**:本轮为「重算 `manifest.md` 逐文件表」**又**手搓了一次一次性脚本(上轮也是)⇒ 固化成子命令。只重写**表 + 计数行 + 重算时间**,⛔ 不碰上方「最近改动」散文(那是人写的结论,脚本代笔会冲成流水账);**行尾随原文件**(本表是 CRLF,写成 LF 会造成一次无声的全文件 diff —— 已实测保持 81 CRLF / 0 裸 LF)。用法:`python install.py --manifest --note "<本轮:…>"`。
|
||
- ✅ **重算 + 复验**:`--manifest` **34 份文件 / 语法失败 0**|`install.py --verify` **全绿**(10 条接线 `rc=0`、`collabd --where`、selftest **PASS 39 / FAIL 0**)|包内**无 `tmp/`、无 `__pycache__`**(证实缺陷 ① 的修法真生效 —— verify 不再往包里写东西)。⚠️ 一点如实澄清:verify 仍会在**工作区**留下 `<WS>/tmp/selftest/`(19:03 重建)—— 因为 selftest 按 `DSH_WS_ROOT` 定位工作区,而 `tmp/` 本就是工作区指定的草稿区、不入库;**受保护的靶子是技能包,这一条已达成**。
|
||
- 🔴 **发现一处陈旧副本(P2,未动)**:`<工作区>/.workbuddy/tools/wb-result-hook.py`(35,975 B,10-01 15:42)比包内那份(37,837 B)旧。宿主 `settings.json` 实测**三条接线全指包内**,故该副本**已无接线**、属死件;`install.py::OWN_BASENAMES` 正是为"旧落点"设计的清理对象。按"未经确认不删他人文件"留在原地,登记为清理候选。
|
||
- 🔴 **顺手揪出并修掉一个「静默假绿」**(清盘符字面量时的连带发现):`scripts/selftest.py:617` 的 `t_tick_wired` 用例**写死了项目绝对路径** `<某工作区>/.workbuddy/tools/wb-result-hook.py`。两处都不对:① 违反用户本轮规则(包去依赖"使用方的"项目目录);② 宿主接线自 10-01 起已改指**包内**那份 ⇒ 在别的机器上恒走"文件不在,跳过"分支 ⇒ **这条用例看着绿、其实什么都没验**。已改为按包内相对路径取 `hooks/wb-result-hook.py` ⇒ 该用例从"跳过"变**真检**(4 项实跑通过,`maybe_run_supervisor_tick` + `--tick` 都在)。⇒ **复验:`--verify` 全绿 / selftest PASS 39 / FAIL 0 / 清单 34 文件 / 0 失败 / 包内零残留。**
|
||
- 🔑 **可复用的判据**:自测/自检里凡是"**找不到就跳过**"的分支,遇到**路径搬迁**就会从"有效"退化成"永真" —— 这是**假绿**的一等来源,比直接报红更危险。⇒ 自检的靶子**必须用相对路径指向自己包内**,⛔ 不许指"使用方的"目录。
|
||
- 🔴 **发现一把陈旧域锁(R9,未动)**:`05-交接单/.locks/<非ASCII映射>-2248-224852526`,属 **`[协作]N9复测-2248`**(09-29 22:48 起,域 `ai1net-dsh-anywhere`)——已陈旧两天。`--release-exec` 回显「另有 1 把锁属他人,按 R9 未动」;**只能由持有者本人释放**。
|
||
- ✅ **锁状态**:本轮持**全局执行锁**(会话 `常驻定案统一-20261001`,18:35 抢;因是**机制层**故按规矩**不带 `--domains` 独占**,故无域锁目录)⇒ 已 `--release-exec "常驻定案统一-20261001"` 释放,回显「✓ 已释放全局执行锁」,`.exec-lock` 目录已消失。
|
||
- ⚠️ **仍未做(如实登记)**:**没起常驻投递**(`collabd.py --supervise` 未拉起);`guard` 自 09-29 23:59 停着;8788 看板服务未运行。⇒ 阻滞项就是"四道闸门不全(`NEXT.md` 不存在)",不是本轮改动引入的。
|
||
- 📦 库内旧件备份 → `归档/技能包-旧件-20261001/bak-roots-20261001/docs07/`。⚠️ **文档库仓库(`D:/github/dsh_shenxian`)有大量未提交改动**(含本轮两处 `07-scripts/`),按规矩**未 commit / 未 push**。
|
||
|
||
## 19:1x · 用户核对「最新方案」三点 ⇒ 逐条比对权威文件 + 揪出一处文档自相矛盾(会话 `方案口径核对-20261001`,机制层 ⇒ 全局独占锁)
|
||
|
||
- 🔴 **用户三问原话**:「1、支持在一个工作区进行会话协作(主会话、协作会话、唤醒会话)都有二级前缀区分类别和作用|2、主会话根据需求的执行自动分派任务给对应协作会话(通过定时任务的方式)|3、我在工作区会话中输入 协作完成 XXXX,协作会话在本工作区自动执行直到目标完成(只有遇到阻碍时停下)」。
|
||
- ✅ **逐条比对结论(权威=技能包 `references/architecture.md`)**:
|
||
· **第 1 条:方向对,但漏了第 4 类**。现役是**四类**会话(主会话/协作会话/唤醒会话/**接续会话**),默认**都在一个工作区**,**只看会话标题**区分(⛔ 不按 cwd);前缀**不统一用方括号** —— 主会话是 `主控 · <类别> · <具体>`(中点分隔),协作/唤醒/接续才是方括号形态。
|
||
· **第 2 条:前半对,后半要拆开读**。「主会话按需求执行自动分派」✅;「派活走 `automations` 周期排期」✅(开会话的**唯一**通道,宿主硬边界);**但「用自动任务当闹钟去驱动投递/心跳」已于 2026-10-01 用户明确废弃**(原话「定时任务的方案已经废弃了」)⇒ 时钟改由**常驻投递**提供。⚠️ 且派活的主路径是「**收尾即接**」(≈3~4 分钟),周期排期只是兜底。
|
||
· **第 3 条:意思对,但没有这个字面命令**。落点是**对话里说明目标**(`goalctl.py declare --title …`),技能侧⛔ 不许预设、⛔ 不许从文件名/目录名推。「只有遇到阻碍才停」✅(四态含「有阻碍」,必须带原因 + 主会话须向用户喊话)。🔴 **但「直到目标完成」有已知判据缺口**:心跳判完成读「台账 ∪ 任务图」,**未读验收判据** ⇒ 目标没达成也会误判完成、心跳停(已登记待修)。
|
||
- 🔴🔴 **揪出并就地修掉一处文档自相矛盾(本轮实质发现)**:`architecture.md` 的「**需求内闭环**」节里那段「✅ 不借力之后的真实状态」写着「**本需求内没有会话活动 ⇒ 就没有心跳**…需求内只有两条路:① 自建周期自动化 ② 不建…**建议 ②**」——它的推理前提是「**宿主树内唯一能定时的是自动化**」,而这条前提**已被 2026-10-01 常驻定案推翻**(常驻投递就是**本需求内**的时钟)。⇒ 已按本文件自己「冲突以最新条为准」的规则,给那段加**作废框**(**只作废由它推出的结论,⛔ 不动「不借力」这条规则** —— 常驻投递属本需求内的件,不构成借力)+ §9 历史记录那条补 `⑦`。全库仅此一处复述该结论(已 grep 确认)。
|
||
- ✅ **顺手更正**:结论表抬头 `最后更新 14:0x` → `19:1x`(常驻定案是 18:5x 改进去的,抬头没跟上)。
|
||
- ✅ **验收**:`install.py --verify` **全绿**(10 条接线 `rc=0` / `collabd --where` / selftest **PASS 39 / FAIL 0**)|清单重算 **34 文件 / 0 失败**|锁:机制层 ⇒ **不带 `--domains` 独占**,已 `--release-exec` 释放(回显「✓ 已释放全局执行锁」)。
|
||
- 🔑 **本轮没有动用户定案**:改的只有「由旧前提推出的那个结论」,且原话与日期都保留在作废框里(可追溯)。
|
||
- 🧩 **顺带补了一处技能缺口**:`agent-operating-rules §1.8`(改「用户已定案」条目一律上抛)原来只有「⛔ 不得改写定案内容」三条 —— 它会把**上面那类修正也一并挡住**(节标题写着"用户定案"⇒ 整节不敢动 ⇒ 矛盾长期留在权威文件里;反过来没边界又会连**定案原话**一起改)。⇒ 已补 **第 4 条「分清『定案原话』与『由它推出的结论』—— 只有前者不可动」**,附「可动的三条硬条件」(① 必须引用**更新的用户定案原话+日期**推翻其前提 ② **只标注作废、原文一字不删** ③ 写清"作废的是结论、哪条规则仍有效")+ 本轮实测起因。`last_change` / `updated_at` 同步。⚠️ 该文件 `agent_created: true`,属可自维护技能。
|
||
|
||
## 19:5x · 🔴 用户订正:会话类别口径(**接续不是类别** + **第 4 类=队列上报的跟进会话**)⇒ 全包改齐(会话 `会话类别口径修正-20261001`,机制层 ⇒ 全局独占锁)
|
||
|
||
- 🔴 **用户订正原话(本轮唯一依据)**:「**接续会话 不是单独的一类会话,是这几类会话到达阈值时 创建的接续会话**,这里的第4类应该是 **队列上报的跟进会话**(之前考虑**只让主会话管目标和方向**,**队列上报的会话 让专门的 跟进会话处理**)」。
|
||
- 🔴 **订正了我上一轮(19:1x)的说法**:上一轮我答用户"现役是四类(主/协作/唤醒/**接续**)"——**把「接续」当成第 4 类,错了**。正确口径:**四类=① 主会话 ② 协作会话 ③ 唤醒会话 ④「队列上报的跟进会话」**;**「接续会话」是「形态」、⛔ 不是第 5 类**(任一类撞阈值时由它自己建出下一棒,**角色继承被接续的那条** ⇒ 主会话的接续**仍是主会话候选**)。
|
||
- ✅ **改齐的载体(一次改完,⛔ 不许只改一半)**:
|
||
· `references/architecture.md`:当前结论表(「同工作区」行改为「同工作区 + **会话类别(4 类)**」+ 新增「**接续会话 = 形态,⛔ 不是第 5 类**」行)|§2.3 **第 1 级=角色**改为四类(含 `[跟进]`,标**未落地**)|§2.3.0b ② 换标题+新增定性块|§2.3.0b ③ 补实测发现|🆕 **新增 `#### 2.3.0c 第④类 =「队列上报的跟进会话」(口径已定 · ⛔ 尚未落地)**|「需求内闭环」节的作废框|§9 历史补 ⑦。
|
||
· `references/pitfalls.md`:🆕 **新增 `P0-11`「接续」被当成角色 ⇒ 主会话的接续被判 worker ⇒ 主会话候选 = 0**(含 8 条实测会话名、`cand=0`、`resolve_main → no-register` 读数)。
|
||
· `references/collab.md §4`:**「三个"会话角色"」→「四个"会话类别"」**(+接续=形态一节)。
|
||
· `references/collab-detail.md`:命名前缀处补「口径已扩到四类、④未落地」+「第四形态」处补**「它是形态⛔ 不是第 5 类」**+ **§12 消歧**(把「**主体**(§12 · 含用户与两个程序)」与「**会话类别**(四类 · 会话那根轴)」拆成两根轴,并如实写明 ③④ 类**尚未列进 §12 主体表**)。
|
||
· `SKILL.md`:`description`(加四类触发词)+ §2 铁律(新增两条:四类口径 / 接续=形态)+ `last_change`。
|
||
· 项目层 `.workbuddy/memory/MEMORY.md`:`协作·唤醒线` 行重写为 **`协作·会话类别`**(四类 + 接续=形态;并把已作废的旧名「唤醒脉冲会话」改掉)—— ⚠️ 该文件原已 **7,956 字符(超 ≈7,650 上限)**,故**同步压缩三处**(插件数据落点行的括注、本机执行环境行的冗余、单机自用行的推论句)⇒ 改后 **7,952**,**净 -4**、守住「只减不增」。
|
||
- 🔴 **两处判据缺口 —— 只登记,⛔ 未擅自改**(都触碰"用户定案"域,须先拍板):① **解析器把「接续」一律判 worker**(`parse_session_name()` / `_scan_mains()` / `board.py::_role_of_title()` 同款)⇒ **主会话的接续被吞掉**;⚠️ 当天早些时候**特意**写成"一律 worker"是为了堵**自指死结**(`[<类别>] 接续 · …` 首括号是**类别不是角色** ⇒ 不收就会冒充该类别的主会话、**把通知投给自己**)⇒ 正解**不是**"改成未知"(会**重新打开**那个死结),而是"**得先有足够信息判它接的是谁**"。② **第④类 `[跟进]` 全库零命中**(`{"主":main,"协作":worker,"唤醒":waker}` 无 `跟进`)⇒ 落地清单(角色表+解析/候选与路由/看板/自测)已写进 §2.3.0c。
|
||
- 🔴 **实测读数(本轮取证,⛔ 别用无配置的假读数)**:**不带 `COLLABD_CONFIG` 直接 import `collabd` ⇒ `cand=0`(那是"没配置",⛔ 不是"逻辑坏了")**;带 `COLLABD_CONFIG=<工作区>/.workbuddy/collab/collabd.config.json` 复跑 ⇒ **工作区内 8 条会话、全部 `role=worker`、`cand=0`**,`resolve_main` 返回 `{"sid":"","source":"no-register"}` ⇒ 主会话候选 **0**。⇒ **「自测全绿 ⇒ 判据没问题」是错的**:那条用例只喂**合成样本**,⛔ 盖不住真实命名。⇒ 登记为 **P0-11**。
|
||
- ✅ **验收**:`install.py --manifest` **34 文件 / 语法失败 0**|`install.py --verify` **全绿**(10 条接线 `rc=0` / `collabd --where` 指向包内 / selftest **PASS 39 / FAIL 0**)|`manifest.md` 行尾保持 **CRLF、0 裸 LF**;表内 **34 行、磁盘缺失 0**(⚠️ 自查脚本一度报 33 行+"缺 `roots.env`",是**我脚本的过滤条件**漏了 `.env` 后缀,非表错 —— 已 grep 直接确认 `roots.env` 在表内)。
|
||
- ✅ **锁**:机制层 ⇒ **不带 `--domains` 独占**;`--release-exec "会话类别口径修正-20261001"` 已释放。
|
||
- 🧩 **可复用判据**:把「**类别**」和「**形态**」分开记 —— 类别是**并列的、穷举的**(靠第 1 级前缀区分);形态是**正交的**(如"接续"可叠加在任一类上)。⇒ 一旦把"形态"错当"类别",第 1 级前缀就被污染(本例:`[<类别>] 接续 · …` 的首括号是**类别**、于是整条被判成"那个类别")⇒ **自指死结/候选清零**都是这么来的。
|
||
|
||
## 20:0x–20:1x · ①看板按最新架构调整 ②用会话协作跑「检查会话协作是否运行正常」自检(会话 `会话协作看板调整与自检-20261001`,机制层 ⇒ 全局独占锁)
|
||
|
||
### ① 看板按最新架构调整(常驻口径回归 + 四类会话)
|
||
- 🔴 **动因**:看板里还留着一整片 09-30 的**旧口径** —— 「钩子按需唤起/跑完即退/**不是常驻进程**/没有"启动-停止"这回事」。用户 2026-10-01 已定案「**协作与投递一直运行(常驻)**」+「**定时任务的方案已经废弃了**」⇒ 这些表述**全部作废**。
|
||
- ✅ **改了三处**:`assets/board.html`(协作程序格副标题→「需求台账/队列 · 只维护不上报 · **常驻**」;上报格两行→「**常驻投递 · 一直运行**」+「宿主钩子只作补充(事件驱动)」;布局注释与帮助文本补**四类会话 + 接续=形态**+常驻口径)|`scripts/board.py`(`prog_by` 不再写「钩子 --tick」——**戳上分不出调用方** ⇒ 只报轮次名;`how` 改成常驻口径;`deliver.label` 「钩子没被唤起」→「**投递没在跑**」)|`scripts/board_ext.py`(`GUARD_STOP_REASON` 重写:把"常驻按设计不再需要、待拍板"改成**已定案常驻**;组件状态那段「非常驻,跑完即退」→「常驻为主、钩子补充」)。
|
||
- ✅ **必须同步的一处(易漏)**:看板扩展件**不是**从包里读的 —— `board.py` 按配置 `board_ext` 读 **`<工作区>/.workbuddy/collab/board_ext.py`** ⇒ 只改包内**不生效**。已 `cp` 同步,两份 **md5 一致**(38,360 B)。⚠️ `board.html` 相反:由 `board.py` 从**包内** `assets/` 提供 ⇒ 改包内即生效。
|
||
- ✅ **三步验证(照 `collab-detail.md §0.5.4`)全绿**:① 语法 ✅ ② 渲染桩 **FAIL 0/45**(双目标样本 **0/48**)③ 几何 **FAIL 0/7**。
|
||
- 🔴 **途中揪出并修掉 1 个真缺陷**:`tmp/render-check.mjs` 的 `BOARD` 还指向 **`skills/multi-session-collab/`(已退役、已归档)** ⇒ 这个唯一的看板回归工具**一跑就崩**。⇒ 已改指 `skills/session-mechanism/`。⚠️ **结构性问题(未动,登记)**:看板回归工具住在 `tmp/`(**按规矩要清的草稿区**)⇒ 一旦清掉,"改完看板怎么验"这条路就断了(skill 侧只有 selftest,盖不住渲染与几何)。
|
||
|
||
### ② 用会话协作跑「检查会话协作是否运行正常」⇒ **结论:机械部分可用,投递链不通**
|
||
- ✅ **按机制流程做的**:`goalctl declare --title "检查会话协作是否运行正常" --why … --topics "会话协作自检" --kpi …` ⇒ 跑 `collabd.py --once`(投影轮)⇒ `--tick`(投递轮)。
|
||
- ✅ **正常的部分**:协作程序能跑(两轮 `rc=0`)|目标/类别接线正确(digest 显示 `[会话协作自检]`)|机械摘要 `digest.md`/`queue.md`/`TO-MAIN.md`/`advance.md` 都能生成|整包自测 **PASS 39 / FAIL 0**。
|
||
- ❌ **投递送不到(最要紧)**:`--tick` 判 **`item=M5=done … deliver=target-deaf`** —— 登记的主会话 `f8a792ab-…` **不响应**;投递台账 `wakeups.jsonl` 最后一条是 **08:55**,20:12 这轮**没有新记录** ⇒ 链路已停 ~11 小时。
|
||
- ❌ **常驻投递未拉起**:`--supervise` 没跑、`guard.stop` 自 09-29 23:59 挂着 ⇒ 与 2026-10-01「一直运行(常驻)」定案不符;钩子虽在跑(20:03 有戳)但**没有独立时钟**。
|
||
- ❌ **声明目标 ≠ 产生工作**:目标切了、5 条判据写了(4 未过),但**队列 0 件**、digest 说「没有可派的任务」⇒ 机制**不会**把验收判据变成活;只有主会话手动写排期/台账才会动(`collabd` 明令**不派活**,属**设计如此**,但用户视角="声明完没反应")。
|
||
- ❌ **台账不随目标切换**:新目标下仍反馈旧类别(`唤醒机制`)的 M5/M6/M7,digest 写「关键路径:M5 → M6」⇒ 新旧目标**混在一条摘要里**。
|
||
- ❌ **验收判据**(🔴 本轮新发现):`declare` 换目标名时**旧判据原样留着** ⇒ **新目标一声明就被判「全过」**(实测 20:09:零件零判据的新目标,协作程序报「目标三路全过」、digest 写「没有可推进的活」)⇒ 机制**完全静默**。⚠️ 我**没有擅自清判据**(清了会让"只想改措辞"的人丢整表验收 —— 破坏性),改为**加醒目告警**把陷阱说出口。
|
||
- ✅ **本轮顺手修掉的 4 个真缺陷**(都在机制层,原位修):① `preflight-lock.sh` **未登记技能包** ⇒ 改技能包一律被误判【D】未归类、报「先定域(R2)」(**正解是"独占"**,提示词本身就是错的)⇒ **任何人改技能包都开不了工**(本轮开工就撞上)。已补 `'\.workbuddy/skills/'` 一条 ⇒ 复跑门禁正确判【E】机制层;**包内 + 库内两处同源都改**、`bash -n` 均过、行尾各自保持(包内 CRLF/库内 LF)。② `goalctl.py` 工作区解析 `os.environ.get("DSH_COLLAB_WS") or HERE.parent.parent` ⇒ 不带环境变量时**静默指向 `…/.workbuddy/skills`**(读数全"无")⇒ 改为 `DSH_COLLAB_WS` → `DSH_WS_ROOT` → cwd(含 `.workbuddy/collab/`),三路落空才回落并**打醒目告警**(⛔ 不许静默)。③ 同文件加**静默陷阱告警**(换目标名不给 `--kpi` ⇒ 提示旧判据会留下)。④ `state.py` 抢锁路径提示写错(`scripts/handoff-guard.sh` **不存在** ⇒ 实际在 `07-scripts/`)—— 照抄提示必失败。
|
||
- ✅ **验收**:`--manifest` **34 文件 / 0 失败**|`--verify` **全绿**(10 条接线 `rc=0`/`collabd --where`/selftest **PASS 39 / FAIL 0**)|看板渲染 0/45、几何 0/7|`board_ext.py`/`goalctl.py` 包内与工作区副本 **md5 一致**|项目配置 `collabd.config.json` 的 `_默认形态` 已同步四类口径。
|
||
- ⚠️ **一处自伤并已修正(如实登记)**:`board_ext.py` 我写的文案里嵌了 **ASCII 双引号**(`那句"不再需要"`)⇒ 截断 Python 字符串 ⇒ `--manifest` 报 **语法失败 1**(⚠️ 而 `--verify` 仍报全绿 —— **验证覆盖面不同**,`--manifest` 才是语法闸)。已改成「」并 `py_compile` 复检通过。
|
||
- 🔴 **仍未做**:**没起常驻投递**(`--supervise` 未拉);登记主会话 `f8a792ab-…` 仍是 `target-deaf`(要用户在桌面把它打开/重登记);8788 看板服务未起。
|
||
- ✅ **锁**:机制层 ⇒ **不带 `--domains` 独占**;收尾走 `dsh.py close` 反序释放。
|
||
|
||
## 20:2x · 起实时看板服务(用户:「打开看板看看」)
|
||
- ✅ **看板已起来**:`board.py --serve 8788 --takeover`(**按机制规定的唯一可行起法**:会话后台任务 + **stdout 全重定向**到 `tmp/board-serve.out.log`,⛔ 不裸跑——裸跑的输出量会把会话日志顶爆)。实测 `http://127.0.0.1:8788/healthz` = `{"ok": true, "snapshots": 3, "age": 0.1}`;`/board.json` 正常出快照(目标「检查会话协作是否运行正常」+ 5 条判据)。
|
||
- ⚠️ **两个坑(起之前要知道)**:① 探本机回环**一律要加 `--noproxy '*'`**(本机 curl 走 `HTTP_PROXY`,不加会 502);② 起服务**⛔ 不抢执行锁** —— 服务是长跑件、锁的生命周期是"任务的生命周期",抢了锁就等于把所有人挡在门外。
|
||
- ✅ **判据据实更新**:V4「看板可服务」→ **pass**(服务确实起来了)。⚠️ **同时把 V2 的说法改准确**:「主会话**可解析**」其实**是解析得到的**(`roles` 里有一条 `main`),真正不成立的是「**主会话可响应**」(`--tick` 判 `target-deaf`)⇒ 判据名改成 **V2-主会话可响应**,值仍 `未过`。
|
||
- ⚠️ **遗留(如实登记)**:看板服务是**会话后台任务** ⇒ 按机制已知硬耦合,**本会话的 idle 钩子会被它压制**;关会话/关宿主即停。
|
||
- 📌 **卡在别人手上**:那一把陈旧域锁(`[协作]N9复测-2248`)仍然在,按 R9 未动。
|
||
|
||
## 20:3x · 删掉看板主会话框里那行「负责类别:—(未在对话里说明)」
|
||
- 🔎 **用户的问题**:「**这个是什么意思,如果没用就删掉**」。**答复**:那是主会话框(R2)里的一行 —— 用 `project.topics` 按 `main_by_topic` **反查**"这条主会话负责哪几个任务类别",查不到就渲染成「负责类别:—(未在对话里说明)」并给警示色。
|
||
- ✅ **判该删(三条依据,都是实测,⛔ 不是口味)**:① **它会说出假话** —— 线上快照 `topics=['会话协作自检']`、`topics_source.kind='declared'`(20:25 在对话里说明过),但 `main_by_topic={}` ⇒ 这行说「未在对话里说明」。**真因是"这条主会话没对应到任何类别"**,与"有没有说明过"是两件事,原判据把两者混成一个 ⇒ 假。② 同一事实**版面上已说过两遍**且方向更对:顶部「本项目」卡的**任务类别 chips**(带「主会话 `<id8>`」/「⚠️ 无主会话」,`?` 里还有来源与时间)+ 图外说明「当前」块第 1 条 ⇒ 那行是**第三遍**、还是**反方向**。③ 用户反复要求「看板要精简的展示重点有效的信息,不是写说明书」。
|
||
- ↩️ **这次是推翻用户自己先前的摆放**(2026-10-01「**唤醒机制如果是主会话的 事就放到主会话框里去**」把它搬进去的)⇒ 在源码注释 + `collab-detail.md` §⓷ 写明**谁删的/为什么删/怎么还原**(还原点 `tmp/bak-负责类别行-20261001/board.html.bak`)。**没有静默丢信息** —— 类别仍在 chips 与「当前」块里。
|
||
- 🔧 **配套改动**:`R2.h` 112 → **84**(回落到加那行之前的高度;`tmp/arch-geom-check.mjs` 的 `rows.R2` 镜像同步改 —— 那文件自己写着"不同步=几何自检假绿")。🔴 **顺带治同源假话**:图外说明「当前」块第 1 条原按 `TOPS.length` 判,而 `TOPS` 走 `_goal_topics()`、**会把目标简称回落成一个类别** ⇒ 从没说明过时也恒 ≥1 ⇒ ❶ 把**回落值当成已声明的类别**报出去(同族红线:没有却说有);❷「⚠ 未在对话里说明过」成了**死分支**。改按 `topics_source.kind`(与顶部 chips **同源**)。另纠正 3 处陈述同一事实的注释。
|
||
- ✅ **回归(三步验证 + 红绿对照)**:渲染桩 **FAIL 0/47**(双目标样本 0/50)、几何 **FAIL 0/7**、`install.py --manifest` **34 文件 / 语法失败 0**、`--verify` **全绿**(10 钩子 rc=0 + 自测 PASS 39 / FAIL 0)。🔴 `tmp/render-check.mjs` 里那条**正向断言**(「主会话框写出『负责类别』」)**反向**成 3 条 —— ⛔ **不是改成恒真的空断言**(那是假绿);并用**改前备份**跑对照:线上真实快照 **改前 6 红 → 改后 4 红**、合成 `kind='fallback'` 样本 **改前 7 红 → 改后 4 红** ⇒ 三条新断言都是**真判定**。
|
||
- ⚠️ **残留(如实说)**:改前/改后都剩 **4 条红**,全是**写死给样本快照 `tmp/board-verify.json` 的内容型断言**(类别名「唤醒机制」、台账件数、未归类的线名等)⇒ 拿**线上快照**跑就会假红。**回归桩本来就约定跑样本**(§0.5.4),所以不算回归;但这是**桩的弱点**(断言该数据驱动,⛔ 不该写死内容)—— 登记为 **P2 待改**。
|
||
- 🔴 **顺带修掉第 5 个真缺陷(我自己触发出来的)**:`install.py --manifest --note` 的括注**逐轮套娃累积**。
|
||
根因:`re.sub(r"最近一次全量重算:.*?)", …)` 里 `.*?` **非贪婪**,只吃到**第一个 `)`** —— 而 `--note` 里
|
||
一旦出现**全角括号**(我这次写的「…行(**会说出假话**)+ …」),第一个 `)` 就是**括注内部**那个
|
||
⇒ 只替换前半截、**旧括注尾巴原样留在原地**,下轮再套一层。实测把 `references/manifest.md` 第 3 行
|
||
写成 **292 字符 / 4 层套娃**。⇒ 改成**用 `**` 收尾界定整段**(`--note` 含什么括号都不受影响),
|
||
缺省 `--note` 仍**保留原括注**(⛔ 不抹人写的结论)但顺手把累积的多余 `)` 收敛。修完重跑:**292 → 155 字符、单层**;
|
||
两个分支都实测过(带 note/不带 note)。**教训**:`--note` 这类"人写自由文本"塞进正则替换时,
|
||
**⛔ 别用非贪婪 `.*?` 去界定它** —— 它的内容里出现定界符的概率不低。
|
||
- 📌 **遗留不变**:常驻投递(`collabd.py --supervise`)**未起**、`guard.stop` 仍在(自 09-29 23:59);注册主会话 `f8a792ab-…` 仍 `target-deaf`;`NEXT.md` 缺失 ⇒ 四道闸门不全。
|
||
|
||
## 20:4x–20:5x · 会话协作机制排查与修复(用户:「继续会话协作完成之前的需求,并把协作机制的问题排查和修复也当作目标一起完成」)
|
||
- 🎯 **目标已扩写**(`goalctl declare`):标题 →「检查会话协作是否运行正常 + 协作机制问题排查与修复」;类别 → **两个**(`会话协作自检` / `机制排查与修复`);判据 → **V1–V7**。理由:用户原话要求把机制修复**也当作目标**。
|
||
- ✅ **第④类「队列上报的跟进会话」落地**(此前只是口径)。改齐**四处同款**:`collabd.py::parse_session_name()` 角色表 + `board.py::_role_of_title()` 镜像 + **两边的排除元组** `("worker","waker","follow")` + `selftest.py` 逐样本对账(**3 项 → 4 项**)。实测 `[跟进]-[会话协作自检]-x` ⇒ `{'role':'follow','topic':'会话协作自检','ok':True}`。
|
||
🔴 **为什么非改不可**:`[跟进]-…` 不进角色表 ⇒ `role` 解析为空 ⇒ 会被 `_scan_mains()` 收进**主会话候选**(标题带 `[<类别>]` 时还会被解析成"该类别的主会话")⇒ **通知投给它自己**=与接续棒同款的**自指死结**。
|
||
- 🔴🔴 **同批修掉一个「一直存在但看不见」的缺陷**:`board.py` 原来把会话角色**二分**(登记/解析出的主会话 ⇒「主会话」,**其余一律**「协作会话」)⇒ **唤醒会话与第④类跟进会话都被标成「协作会话」** ⇒ 看板第三层(按 `role` 挑格子)会把它们**误画进协作会话那排**(唤醒会话**画两遍**、跟进会话**顶错名字**)。**它一直看不见,因为这两类会话从来还没被真的建出来过**。修法:新增 `board.py::_role_label()` 输出**四类**,`board.html` 第三层判据从 `role !== '主会话'` 改成 **`role === '协作会话'`**(两边必须同款)。回归:渲染桩 **0/48**(双目标 **0/51**)、几何 **0/7**。
|
||
- 📌 **开工清单写进五载体**(用户 10-01 明令「开始会话完成需求的时候,主会话需要创建 唤醒会话 以及根据分工类别 创建 协作会话 和 跟进会话呢 不然整个机制跑不起来」):`SKILL.md §2` + `architecture.md`(**§2.3.0c 改为已落地** + **新增 §2.3.0d 开工清单**)+ `collab.md §4.1` + 项目 `CODEBUDDY.md §F`(**列为第 3 类免确认白名单**)+ `MEMORY.md` 对应行。⚠️ MEMORY.md 已 **8013 字符(本就超上限 ≈7650)** ⇒ 本轮**净减 33**(8072→7980 再压),**仍超 330** = **既有超限**,登记 P2。
|
||
- 🔴 **V3「闸门齐全」的真因找到并修好**:`collabd --where` 的 `TG` **来自 `.workbuddy/collab/collabd.config.json`,⛔ 不是 `goal.json.taskgraph`**(两处都写了,以配置为准)。而配置里指的 `交付物/任务图.json` 是**手机接入线**的(M1–M7 **全 done**,那条线 09-30 已退役)⇒ **可派集合恒空** ⇒ 队列 0 件 ⇒ `NEXT.md` **永不产生** ⇒ 四道闸门第一道就断。修法:新建 `交付物/任务图-会话协作自检.json`(S1–S7,`line` 取当前两个类别,`critical_path=[S4,S5,S6,S7]`)+ 配置与 `goal.json` 双改指。**实测**:`NEXT.md` 已产生,队首 **S4「修:起常驻投递」**,`pending=3`,`gate=free` ⇒ **V3 转 pass**。
|
||
- 🔴🔴 **我又犯了同一个错(今天第 3 次),如实登记**:新写的 `交付物/任务图-会话协作自检.json` 第 70 行 `notes` 字段里嵌了 **ASCII 双引号**(`本图管"该做什么"`)⇒ JSON 语法错 ⇒ 协作程序日志报 `goal_state 读任务图失败 Expecting ',' delimiter: line 70` **且不产生 NEXT.md**(**静默**:命令 rc 仍是 0、界面上看不出)。前两次分别是 `board_ext.py`(截断 Python 字符串)与 `selftest.py`(同款)。**结论:这不是手滑,是缺一道工序** ⇒ 凡写 `.py`/`.json`/`.jsonc`,**落盘后立刻 `py_compile` / `json.loads` 验一遍**,⛔ 不靠"我看着对"。已写进技能 `references/pitfalls.md`。
|
||
- 🆕 **建齐三类会话 + 常驻投递容器**(4 条一次性自动化,**只有这条通道能开新会话**):`[协作]-机制排查与修复-常驻投递容器`(20:51)|`[唤醒]-会话协作自检-脉冲`(20:52)|`[协作]-会话协作自检-验收`(20:53)|`[跟进]-会话协作自检-队列上报`(20:54)。⚠️ **本轮未能见证它们跑起来** ⇒ V1/V7 如实记 **未过**。
|
||
- 📊 **验收现状(如实)**:V3 ✅ · V4 ✅ · V5 ✅ · V6 ✅ | V1 ❌(容器会话已排期,未见证)· V2 ❌(**登记主会话 `f8a792ab` 已哑** ⇒ `--tick` 判 `target-deaf`;要过得**登记一条活的主会话**,⛔ 不是重启老的那条)· V7 ❌(自动化已建,会话未验)。
|
||
- 📌 **下一步(一条)**:V2 是**关键路径上的唯一硬阻塞** —— 常驻投递起来了、`NEXT.md` 也在了,但**投给谁**这一环断在主会话哑掉。修法=让**当前活着的会话**成为主会话(标题 `主控 · <类别> · <具体>`)并按现行判据登记,然后 `--tick` 复验。
|
||
|
||
### 20:52 · 常驻投递容器已起(**V1 转 pass**)
|
||
|
||
- ✅ **起法**:会话后台任务(task_id `zYQDJu`)+ stdout/stderr **全重定向** `tmp/supervise-inbox/_supervise-bg.out.log`。进程 **PID 8616**(父 12384),创建 20:52:08,解析为 `python.exe … collabd.py --supervise`。
|
||
- ✅ **首轮见证**:20:52:47 起 `_collabd.log` 连续出轮,节拍 **≈10 s/轮**(20:53:29 / 39 / 49);`_front.stamp`(collabd 写)与 `_token-deliver.stamp`(deliver-gateway-token 写)**随轮刷新**。
|
||
- 🔴 **口径澄清(纠正文档里的模糊说法)**:`_tick.stamp` **由宿主钩子 `wb-result-hook.py` 写**(一次性投递轮),**⛔ 不是常驻写的** ⇒ **不能拿它当常驻存活判据**。正确判据=**进程 PID** + `_front.stamp`/`_token-deliver.stamp` mtime + `_collabd.log` 轮次。
|
||
- ⚠️ **时钟通了、靶子哑着**:每一轮都报 `停投:主会话 f8a792ab 已哑(日志撞上限)⇒ 改喊用户` + `投递未成(target-deaf)⇒ 保留 M5=done 在队首,下一轮重试` ⇒ 印证上一条「V2 是关键路径唯一硬阻塞」,与 V1 是两件事、⛔ 别混为一谈。
|
||
- 📌 本会话=**专用容器**,⛔ 不接别的活、⛔ 不关会话;后台任务活多久=本会话活多久。
|
||
|
||
【唤醒会话·20:55】三条判据**全部成立** ⇒ 已起一次性脉冲 `wake-pulse.sh 300`(后台,无 stdout)。读数:① 本工作区真·在跑 **1** 个(`3f43ce71` 接续·会话机制合并包·任务4b-4d-6;排除自己 `dc1860b3`=`[唤醒]-会话协作自检-脉冲`)② `queue.json` `head=S4`(todo)/`pending=3` ③ `acceptance_state` **3 项未过**(V1-常驻投递/V2-主会话可响应/V7-开工建齐三类)。⚠️ 钩子提示的 STALL.md **全工作区 find 无命中** ⇒ 陈旧误报。
|
||
|
||
【协作会话·会话协作自检·20:56】V1–V7 验收取证跑完(逐条真读数)。**结论:7 条里 3 条过、4 条未过** ⇒ 协作机制**没在正常工作**(时钟类都在跑、真正要「把活投到人手上」那一环断着)。
|
||
- V1 常驻投递=**未过**。判据=**有没有活的 `--supervise` 进程**(⛔ 不是看 tick 新不新鲜):`Get-CimInstance` 全机只剩 1 个 python 进程=`board.py --serve 8788`(那是看板=V4)⇒ **无 supervise**;`tmp/supervise-inbox/_supervise-bg.out.log` 只有一行 `===== supervise-bg start 20:52:08 pid=3116 =====`,**pid 3116 已 DEAD、零后续输出**。⚠️ 陷阱:`_tick.stamp`/`_front.stamp` 看着很新(20:59:01/20:56:57)⇒ **误判成"在跑"** —— 那新鲜度来自**前台/钩子**,⛔ 不能当常驻的判据(我第一遍就写成了"过",查进程后自己改回来)。
|
||
- V2 主会话可响应=**未过**:`collabd.py --tick` ⇒ `deliver=target-deaf`;主会话 `f8a792ab` 诊断日志撞 ~10 MiB 上限(宿主拒写 EPERM)⇒ 界面刷不出 ⇒ 投给它必落空。`NEED-USER.md` 已自动写明「请把主会话换到一条新会话」。
|
||
- V3 闸门齐全=**过**:`NEXT.md` 在(20:54:54,2076 B);`queue.json` `head=S4`(todo)/`pending=3`/`gate=free`。
|
||
- V4 看板可服务=**过**:`curl --noproxy '*' :8788/healthz` ⇒ 200 `{"ok": true, "snapshots": 600, "age": 1.9}`。
|
||
- V5 自测全绿=**未过**:`install.py --verify` ⇒ **PASS 38 / FAIL 1**;唯一红=`🆕 归属判据能吃类别集合`,真因是**断言过时**(断言按字面串 `for _t in _as_list(short):` 匹配,而实现 2026-10-01 已重构成 `_tps = _as_list(short)` + `for _t in _tps:`,`collabd.py:1615/1632`)⇒ **假阴性、非功能缺陷**。
|
||
- V6 跟进会话角色落地=**过**:`parse_session_name('[跟进]-[会话协作自检]-x')` ⇒ `{"role": "follow", "topic": "会话协作自检", "ok": true, "form": "prefix"}`。
|
||
- V7 开工建齐三类=**未过**:读宿主库 `sessions`(`E:\ProgramData\.workbuddy\workbuddy.db`,本工作区命中 51 行)⇒ **唤醒 1 / 协作 4 / 跟进 0** ⇒ 第三类 `[跟进]-` 一条都没建。
|
||
- 已如实回写 `goalctl.py declare`(**V1 那条先在 20:57 误写为"过"、20:59 查进程后改成"未过"**,以第二版为准)。
|
||
- 📌 未做(超出本棒"只取证"范围):V5 的红字**一行就能修**(把断言改成匹配 `_as_list(short)` 的两种写法),但它落在**机制层技能文件** ⇒ 须**全局独占锁**、且会撞正在跑的 `机制排查与修复` 线 ⇒ 留给队列里 S4/S5/S6 收。
|
||
|
||
|
||
|
||
|
||
【跟进会话·队列上报处置·21:0x】会话 `[跟进]-会话协作自检-队列上报`(sid fe212445)。处置队列上报三类共 4 条,全部真读数;⛔ 一轮只做这一件。
|
||
- 已完成·M5「验 --tick 真投递」=**维持 done**。核:`wakeups.jsonl` 第 82 行**逐字命中**(ts 2026-10-01T08:36:37/epoch 1790814997.5/http 200/ok true/kind 上报·单条/sessionId f8a792ab…),现存 85 行 ⇒ 产物可核对。
|
||
- 已完成·M6「验实时看板在线」=**维持 done**(原**无 artifact 字段**,已补上核查读数)。核:`curl --noproxy '*' 127.0.0.1:8788/` ⇒ **200 / 92,282 B**;PID 40224 在 127.0.0.1:8788 LISTEN;看板日志记「已起 8788」。
|
||
- 已完成·M7「验 selftest 全绿」=🔴 **由 done 降级 running**。核:实跑 `selftest.py` = **PASS 38 / FAIL 1**,唯一红=「归属判据能吃类别集合(`_in_project` 的 main_sid/short 都走 `_as_list()`)」⇒ **不是「全绿」**。与 `goal.json` acceptance_state V5「未过(PASS38/FAIL1,断言过时假阴性)」一致 ⇒ 原 done 是**无产物支撑的假 done**。该红字一行可修,但落**机制层** ⇒ 须独占锁。
|
||
- 受阻·S5「修:主会话可响应(验收 V2)」=**写 `blocked.json`(键 S5)**。核:主会话 `f8a792ab` 会话日志 = **10,485,715 B**(≈10 MiB 硬上限)、mtime **冻结在 08:08**(≈13 h 零增长)⇒ 界面停更、投递给它的通知全落空;同目录**另有多条**会话日志同处 ~10 MiB 上限 ⇒ 是「**上限本身**」在批量报废会话。解除条件:① 用户新建主会话(跟进会话建不了主会话);② 治本=修「宿主上限致会话永久变哑且无告警」。
|
||
- 执行中/待执行·**S4**「修:起常驻投递」=写回任务图(status 仍 `todo` + evidence 记「队首·可派未派·1 h 内无排期」)。队列实读:`head=S4`/`doing={}`/`pending 3→2`/`gate=free`。
|
||
- 收尾验证:跑 `collabd.py --once` ⇒ `blocked.json` **真被吸收**(`pending` 3→2、`queue.md` 出现「受阻(⛔ 不当队首):S5」);任务图 JSON 校验通过。
|
||
🔴 本轮附带发现 **3 条机制缺陷**(⛔ 均未修,登记待定夺):
|
||
① `NEED-USER.md` 是**机器文件** —— `collabd.py::need_user()` **每轮无条件整体重写**(固定模板=时间+原因+喊话,**无「解除条件」字段**),而 `NEXT.md` 模板却写「受阻原因与**解除条件**都在里面」⇒ **两者契约对不上**,会话手写的解除条件会被下一轮**静默抹掉**(实测 21:03:14 就被重写一次)。⇒ 解除条件的权威落点改为 `blocked.json`。
|
||
② **「同线互斥」实际不生效**:`collabd.py:597-605` 的 `busy_lines` 取「working 会话 `cwd` **末段**」当线名,本工作区所有会话末段都是 `ai1net-dsh-server`(**工作区名**),与任何节点 `line`(机制排查与修复/会话协作自检)都不相等 ⇒ `if p["line"] not in busy_lines` **恒真**。实测(只读 SQL):`working` 2 条 —— `fe212445`(本会话)、`3f43ce71`(`接续 · 会话机制合并包 · 任务4b-4d-6`,其日志同样已顶到 10,485,710 B);而 `queue.json` 只报出 `blocked_lines=["ai1net-dsh-server"]`。后果:同一线可被同时派多件;`waste`(可派未派)判据把「本会话+那条接续会话在跑」误当成「有棒在跑」。
|
||
③ **全局执行锁被占**:`05-交接单/.exec-lock/OWNER` = `会话协作-机制排查与修复-20261001`(开始 10-01 20:41,**未声明单号**);同线最近活跃会话 `04334c8a`(`[协作]-机制排查与修复-常驻投递容器`)已空闲 ~11 min **未释放**。⇒ S4 改机制层须**独占**,接棒前 `--claim-exec` 会抢不到;⛔ 按 R9 不得删锁/接管 ⇒ 需持有人自行 `--release-exec`。
|
||
- ⚠️ 本棒**未抢文件锁**(动的只是队列自身状态文件 + 任务图;锁现状见上③,待下棒核对)。
|
||
|
||
## 21:15–22:0x · 看板「分组大框」第四改收尾 + **P0-6 治本(常驻被杀的真因)**
|
||
|
||
### 一、第四改(用户逐字:「用一个**虚线大框**把 主会话 唤醒会话 和 跟进会话都框起来,这个虚线大框 **连接 协作会话 虚线大框**就好」)—— 代码+文档全部落齐
|
||
|
||
- ✅ 代码:`assets/board.html`(CSS `.grp` + 布局常量 `UA`/`UB` + 尺寸**从内容现推**、⛔ 不写死坐标;① 派活改成**两个大框之间一条线**,取代原来"主会话往每一格射线"的扇形)、`board.py`(`_sessions()` 截断改「**四类各保底一条**」)、`selftest.py`。
|
||
- ✅ 文档:`architecture.md`(§2.3.0c 删「降级投主会话仍未落地」+新增**投递路由表**与 **§2.3.0c-2 分组大框**)、`collab-detail.md`(§0.5.5 形状/位置/命名沿革)、`collab.md §4`(唤醒位置改「主会话下面那一行左格」、协作=「下面那个虚线大框」、投递路由改**已落地**、补「两个虚线大框=分组」)、`SKILL.md §2` 铁律。
|
||
- ✅ **位置描述订正 4 处**(`board.html` 3 处注释 + `board_ext.py` 1 处)—— 原文还写着「唤醒会话贴在主会话左边」,**已被用户 10-01「单独放一行」推翻**;`board_ext.py` 那句**保留为尺寸依据**并就地标注作废。⚠️ **项目侧同源副本** `.workbuddy/collab/board_ext.py` 已改到 **`diff -q` 逐字节相同**(`board.py` 读的就是它)。
|
||
- ✅ **看板 HTML 无需重装**:`board.py` 按 `HERE.parent/assets/board.html` **实时读盘** ⇒ 刷新页面即见新图(服务在跑:pid 40224 @ `127.0.0.1:8788`)。
|
||
|
||
### 二、🔴🔴 本轮实质修复 1:**P0-6 治本** —— 常驻被杀的真因=「释放锁=删文件」
|
||
|
||
- **现场证据(⛔ 不是推断)**:`tmp/supervise-inbox/_supervise-bg.out.log` 原文
|
||
`[safe-delete][SAFE_DELETE_BULK_CONFIRM_REQUIRED] {"count":50,"threshold":50,"targetCount":1,"targets":[…\wake.lock]}`;
|
||
常驻 **20:52 起、20:56 查已无此进程**(`Get-CimInstance` 只剩看板那一个 python)⇒ **≈10 s/轮 × 50 次删除 ≈ 8 分钟**。
|
||
- 🔴 **2026-09-30 那版修法不充分**:它只把 hash/最小间隔的预判提到取锁之前 ⇒ **只在「锁不需要取」时才省掉删除**;
|
||
而真投的判据是「**内容变了**」、**内容会一直变** ⇒ 快速否决**一次都命中不了** ⇒ 每轮照删。
|
||
- **✅ 治本(已落)**:**锁文件永久存在,释放 = 原地改写内容**(`pid` ↔ `free`)⇒ **零删除**。判据从「文件在不在」改成「**内容 + 陈旧度**」(空/`free`/mtime > 120 s ⇒ 可抢);原地写非原子 ⇒ 半截串**不算** `free` ⇒ **fail-closed**,抢锁后**回读核对**。
|
||
- 🔴 **回归(`selftest` 新增 1 例+改 1 例,并做变异法对照)**:①「锁已释放(不存在 **或** 内容=`free`)」+ ②**反向**「`_deliver_str` 体内 ⛔ 不再出现 `_lk.unlink`,**且要求源码真读到**才算过」+ ③ 真互斥例「**别的进程持有时 ⇒ `locked`**」。⇒ **PASS 43 / FAIL 0**(连跑两轮)。
|
||
**变异法**(把释放改回 `unlink`):① **仍绿**、**② 报红** ⇒ 证明"只看已释放"就是**空判据**(P0-13 同族)+ 变异体 **PASS 42 / FAIL 1**。
|
||
- 🔴 **教训写成判据**:**「降低频率」不是修法** —— 判据是「**稳态下每轮的删除次数 = 0**」。已写进 `pitfalls.md` **P0-6**(含"两个候选真因"与复发信号)。
|
||
- ⚠️ **新版锁文件会一直躺着、内容 `free`** ⇒ 那**不是**残留,⛔ 别当故障去删它。
|
||
|
||
### 三、🔴 本轮实质修复 2:判读层漏登记(`goalctl._WHY`)
|
||
|
||
- 本轮把投递目标从「主会话」改指「跟进会话」后,`_WHY` 表**没登记**三个新跳过原因(`no-follow-session`/`follow-not-live`/`target-deaf`),
|
||
而兜底是**问号占位** ⇒ 落进最后一个分支 ⇒ **打印一句「未登记的跳过原因」然后抢锁去干活**(把「没人收上报」误当成「没人干活」)。
|
||
- 修法:三个新值登记为 **需人看**(⛔ 不降级)+ `main-not-live`/`no-main-session` 改标「**旧路径的值**」(现只由 `resolve_main()` 产出、**服务看板读数**;活路径里只剩在**已停用**的 `wake_round()` 作死代码)+ **兜底由「降级」改成「需人看」**。
|
||
- 顺带订正「文档说一套、代码做一套」三处:`collabd.py` 用法头 `--tick`(投给主会话 → **投给跟进会话**)、`goalctl.py` 的 `wake` 段(唤醒主会话 → **手动补拨一次投递轮**+标注「自动任务当闹钟**已废弃**」)。
|
||
|
||
### 四、回归读数(全部现算,⛔ 不写死期望值)
|
||
|
||
| 项 | 读数 |
|
||
|---|---|
|
||
| `selftest.py` | **PASS 43 / FAIL 0**(连跑两轮同结果) |
|
||
| 看板渲染桩(live) | **FAIL 0 / 56** |
|
||
| 架构图几何自检 | **FAIL 0 / 8** |
|
||
| `install.py --manifest` | 34 份文件 / 语法失败 0 |
|
||
| 全包 `py_compile`(15 份 `.py`) | **全过**(=P0-12 那条判据当场又跑一遍) |
|
||
| 两份任务图 JSON | `json.loads` 全过 |
|
||
| `goal.json`(declare 后) | `json.loads` 过,验收项 7 |
|
||
|
||
### 五、验收如实回写(`goalctl declare` 已写,⛔ 不美化成"全过")
|
||
|
||
- ✅ **过 4 条**:V3-闸门齐全(head=S4/pending=2/gate=free/NEXT.md 在)、V4-看板可服务(pid40224 @127.0.0.1:8788)、**V5-自测全绿(由「未过」转「过」:PASS43/FAIL0)**、**V7-开工建齐三类(由「未过」转「过」:唤醒1/协作3/跟进1)** + V6-跟进会话角色(role=follow)。
|
||
- ❌ **V1-常驻投递=仍未过**:判据=有没有活的 `--supervise` 进程(⛔ 不看 `_tick.stamp` 新不新鲜 —— 那是**钩子**写的)。
|
||
⚠️ **新发现的依赖**:20:51–20:54 建的那批容器/协作会话**现在全是 `completed`** ⇒ 「后台任务随宿主会话一起被回收」是**第二个候选真因**。
|
||
📌 已把 `[协作]-机制排查与修复-常驻投递容器` 自动化**重排到 22:05**(同一条自动化只改排期,⛔ 不新建重复件),并把提示词里的**判据订正**成「PID 存活 + `_front.stamp` 前进 + `_collabd.log` 连续轮次」,加了一条"**若又被杀就把 `_supervise-bg.out.log` 原文逐字抄回来**"(决定下一步查哪:仍有 `SAFE_DELETE` =还有别的删除路径;空文件 =结构性回收)。
|
||
- 🔴 **V2-主会话可响应=待重验**:**判据本身随口径变更作废**(投递⛔ 不投主会话、改投跟进会话);跟进会话 `fe212445` 现为 `completed` ⇒ 现行 `--tick` 会落 `no-follow-session` 并**喊用户**。
|
||
- ⚠️ **另有一条不是本线的阻塞**:`NEED-USER.md`(21:53,由钩子自动写出)报**手机接入链路的垫片 `127.0.0.1:20090` 掉了** ⇒ 起长活进程只有「宿主后台任务」一条路,须用户侧重起(见 `references/deploy.md §5b`)。
|
||
|
||
---
|
||
|
||
## 22:1x–22:2x · 用户三件指令的处置与「唤醒时钟」治本
|
||
|
||
用户逐字(本轮承接):
|
||
> 1、删除这个事情"不是本线的阻塞:钩子 21:53 自动写出 NEED-USER.md —— 手机接入链路的垫片 127.0.0.1:20090 掉了…"
|
||
> 2、看板中协作会话区域 还是看不到协作会话
|
||
> 3、唤醒会话都停了1个多小时了,连自己都唤不醒
|
||
|
||
### 一、第 1 件(删掉那段「不是本线阻塞」的文案)— 已闭环
|
||
|
||
- 出处=**技能包的「前置探针」**(`collabd.py::check_frontline()`/`alert_frontline()`):它把**已退役的手机接入线**的事写进本线 `NEED-USER.md`,
|
||
且带 `20090`/「垫片」等**使用方项目串**(违反「技能就是技能,谁用产生的文件放在他自己那里」)。
|
||
⚠️ 它一直没被发现,是因为那条「零项目串」自检**只扫 `board.py`** —— **判据漏掉一个文件 = 这条判据不存在**。
|
||
- 处置:**删掉两个函数**(原位留「谁删的/为什么删/该放哪」说明块)+ 删掉 `--supervise`/`--tick` 两处调用点;再跑 `--once` ⇒ **`NEED-USER.md` 没有重新生成**(实测)。
|
||
- 加固:把「零项目串」自检**扩到 `collabd.py`**(`board.py` + `collabd.py` 两份都扫)。
|
||
|
||
### 二、第 2 件(看板看不到协作会话)— 已闭环
|
||
|
||
- 真因=**看板服务跑的是旧代码**:服务 20:24:38 起、而改代码在 21:xx ⇒ 它返回的 `board.json` 是旧结果(`[跟进]-…` 被标成「主会话」、**只 2 条会话**)。
|
||
- 处置:按文档姿势 `board.py --serve 8788 --takeover` 重启 ⇒ 日志 `✓ 接管:已停旧看板 PID 40224(成功)` ⇒ 服务返回 **6 条会话、三类齐全**(协作会话 3 条可见)。
|
||
- 教训写进 `pitfalls.md` **P0-17**(判据=直读 `/board.json` 的 `ts`/条数/类别对磁盘,⛔ 不看页面像不像)。
|
||
|
||
### 三、第 3 件(唤醒静默 1h20m)— 治本已落地,**实证待下一跳**
|
||
|
||
- **真因**:唤醒脉冲原本靠**会话内自维持**;而**会话跑完变 `completed` 后,宿主把后台任务回收了** ⇒ 时钟消失。
|
||
实测证据:`board.json` 快照里 `[唤醒]` 与 `[跟进]` **都是 `completed`**;`_collabd.log` 每轮 `no-follow-session`。
|
||
- **治本**:`[唤醒]-会话协作自检-脉冲` 由 `once` → **`recurring`(`FREQ=HOURLY;INTERVAL=1`)** ⇒ **宿主就是时钟**,会话死不死都照样触发;
|
||
prompt 里**明确废止** `wake-pulse.sh` 自维持那套(附实测证据)+ 加**幂等第 0 步**(已有活的 `[唤醒]` ⇒ 本轮让位)。
|
||
- ⚠️ **同时发现**:上一轮那次「重排到 22:15」**根本没落地**(`view` 复核 `scheduledAt` 仍是 `20:52`)
|
||
⇒ **教训:`automation_update` 之后必须 `view` 复核 `nextRunAt`,⛔ 别凭「我发过指令了」当已生效。**
|
||
|
||
### 四、顺带修掉的两个真缺陷
|
||
|
||
1. 🔴 `goalctl._automation_todo` **判据漂移**:只认名字含「唤醒轮」的排期,而命名口径早已改成 `[唤醒]-<类别>-<具体>`
|
||
⇒ `stop`/`start` **永远打印「没找到名为唤醒轮的排期」**(判据跟着旧名字漂了)⇒ 改成**两道一起认**。
|
||
2. `goalctl stop` 补提示:**停机再起必须 `--takeover`**(看板)/**先停旧 PID 再起**(常驻),⛔ 二者都不是热加载。
|
||
|
||
### 五、回归读数(本轮)
|
||
|
||
| 项 | 读数 |
|
||
|---|---|
|
||
| `selftest.py` | **PASS 44 / FAIL 0**(43 → 44,新增 `t_need_user_throttle_keep`) |
|
||
| 变异法(need_user) | 打掉节流 ⇒ ③ **报红**;打掉保留 ⇒ ④ **报红** ⇒ 判据非空(P0-13) |
|
||
| `py_compile`(collabd/selftest/goalctl) | **全过** |
|
||
| `SKILL.md` frontmatter | 6 行 / 11802 字符 / **非键位置「冒号+空格」= 0** / 六个键全在 |
|
||
|
||
### 六、当前在途
|
||
|
||
- 🔴 `[协作]-机制排查与修复-常驻投递容器` 重排 **22:20**:22:06:15 那份 `--supervise`(pid 29944)跑的是**旧代码**
|
||
(日志里仍在打 22:10 已删的「前置探针」、且每 ~10 s 刷一次 `NEED-USER.md`)⇒ **已 `Stop-Process -Id 29944` 停掉**。
|
||
- 🔴 `[唤醒]-会话协作自检-脉冲` 现为 **recurring**,下一跳 **23:14**(待实证:宿主确实会按排期触发)。
|
||
- ⚠️ 常驻/看板**都不是热加载**:改完代码必须**停旧再起**(看板 `--takeover`)。
|
||
|
||
### 七、22:2x–22:3x 收尾:V1 由「未过」转「过」+ 一个 `declare` 分隔符坑
|
||
|
||
**V1-常驻投递:过**(判据四条全中,⛔ 不看 `_tick.stamp` —— 那是**宿主钩子**写的):
|
||
|
||
| 判据 | 读数 |
|
||
|---|---|
|
||
| ① PID 存活 | **`3552`**(`collabd.py --supervise`,22:18:57 起)**已活 >10 分钟**(P0-6 原症状 ~8 分钟被杀) |
|
||
| ② 日志连续 | `_collabd.log` 22:22:10 → 22:28:59 **每 ~22 s 一轮、无中断** |
|
||
| ③ `wake.lock` 稳态 | **文件存在 · 内容 `free` · mtime 在走** ⇒ **零删除**(P0-6 治本证据) |
|
||
| ④ 无新 SD | `SAFE_DELETE` 仅 1 条历史(20:52),**22:18:57 之后零新增** |
|
||
| ⑤ 跑的是新代码 | 「前置探针」最后一条 **22:12:07**,之后 **0 条**;`NEED-USER.md` **停在 22:12:07 后 10 分钟没再刷**(节流生效) |
|
||
|
||
**验收如实回写**(`goalctl declare --yes`,`json.loads` 复核 7 项):**过 6 条** —— V1(由「未过」转「过」)、V3、V4、V5(PASS44)、V6、V7;
|
||
**V2-主会话可响应 = 待重验**(判据随口径变更作废,等 23:21 那一跳实证)。
|
||
|
||
🔴 **踩到并当场记进源码的坑(`goalctl declare --kpi` 的分隔符)**:
|
||
实现是 `str(kpi).replace(";", ";").split(";")` ⇒ **中文 `;` 与 ASCII `;` 都是分隔符**。
|
||
⚠️ 我原以为「中文 `;` 会被当内容拼进去」—— **正好是反的**。第一次写就中招:`V1=过(… 被杀);_collabd.log …`
|
||
把后半截切成**独立的一条没有 `=` 的判据**并**静默跳过**(只在回显印一行提示)⇒ **落库的是被截断的值**,
|
||
而 `rc=0` **看起来"已登记"**(属「看着成功、其实半丢」那一族)。
|
||
✅ **修法**:条目内断句一律用 `,`/`、`/`。`/`——`(这些不是分隔符);且**先 dry-run(不加 `--yes`)读回显的「验收判据:…」那一行**复核条目数,再真写。
|
||
✅ 已把这套写进 `goalctl.py::declare()` 的 docstring;⚠️ 顺带记牢:`--topics` 走 `_items()` 是**按逗号切**,**与 `--kpi` 规则不同**。
|
||
|
||
### 八、给下一棒的交接(本轮收尾时仍未闭合的)
|
||
|
||
1. 🔴 **唤醒链的实证**:`[唤醒]-会话协作自检-脉冲` 现为 **recurring**,**下一跳 23:14** ——
|
||
必须亲眼看到它**被宿主触发并建出一条新唤醒会话**(这才是"能自己唤醒自己"的最终证据 = 用户第 3 件)。
|
||
2. 🔴 **投递链的实证**:`[跟进]-…-队列上报` **recurring 下一跳 23:21** —— 届时 `_collabd.log` 应从 `no-follow-session` 转为**投递成功**。
|
||
3. ⚠️ **全局执行锁**:`dsh-server-docs/05-交接单/.exec-lock/OWNER` 仍写着 `会话协作-机制排查与修复-20261001`(20:41 起、未声明单号),
|
||
其持有会话已 `completed` ⇒ 按 R9 **未接管、未删**,如实上报。
|
||
|
||
---
|
||
|
||
## 22:34–22:45 · 用户追问「队列一直没有上报」的归因:投递被"状态窗口"夹死
|
||
|
||
### 一、现象(用户原话)
|
||
> 「协作程序里有个队列一直没有上报」
|
||
|
||
### 二、真因(已取证,两层)
|
||
|
||
**第一层:队首卡着旧线的件。**
|
||
- `tmp/supervise-inbox/collabd-state.json` 的 **`notify_pending = ["M5=done", "M7=running"]`**
|
||
- 这两条的 `tasks.json.line = "唤醒机制"` —— **已作废的旧线**;而当前 `goal.topics = ['会话协作自检','机制排查与修复']`
|
||
- 从 **21:58 到 22:36** 每轮都是 `no-follow-session`;**22:39 点火起一条跟进会话(`e2ccdea3`)后,立刻变成 `target-busy`**
|
||
|
||
**第二层(根):`target-busy` 判据与"活跃会话恒 working"互相夹死。**
|
||
- 投递前置判据在 `collabd.py` **1231/1364 行**:`_session_status(目标) == "working"` ⇒ **不投**
|
||
(设计原意,见 1222 行注释:正在 working 的会话,「投进去会插进它当前的轮次里」)
|
||
- 而 `_session_status()` 读的是 `sessions.status`,**其取值域实测只有**
|
||
`working` / `completed` / `error` / `archived`(源码 512 行 `SID_DEAD` 写明)—— **没有"空闲"这一档**
|
||
- ⇒ **跟进会话活着(在跑 prompt)= `working` ⇒ 判忙、不投;跑完 = `completed` ⇒ 不 live、不投**
|
||
- ⇒ 🔴 **没有任何一个时刻能收下这条上报** ⇒ 投递**恒失败**
|
||
- **旁证**:`wakeups.jsonl` 里唯一投成的那次(08:55,`http:200, ok:true`)目标是我主会话 `f8a792ab`
|
||
—— 一条**日志早已冻结、不再 working 的"死"会话**。⇒ **只有"死"会话才投得进去**,这本身就是该判据的反证。
|
||
|
||
### 三、顺带确认的两件事
|
||
|
||
1. 🔴 **常驻又断了**:`collabd --supervise` pid **3552**(22:18:57 起)**约 22:31 消失**
|
||
(22:30:50 还在写日志、22:34:19 已无进程);`_supervise-bg.out.log` **无新的 `SAFE_DELETE`**
|
||
⇒ 死因**不是** P0-6 护栏。结合 20:52 那份只活 ~8 分钟、这份 ~12–20 分钟
|
||
⇒ **更像"容器会话结束 ⇒ 宿主回收后台任务"**(结构性限制,非代码缺陷)。
|
||
2. 🔴 **`gate-done.stamp` 从不存在**(`SessionEnd` 钩子从未被宿主投递)⇒ `gate` 判据里 `done_mt` 恒 0
|
||
⇒ **只要 `doing` 非空就是 busy**。⚠️ 但这**不是永久卡死**(claim 会被"持有人失活"或 20 分钟陈旧清掉)⇒ 属**纯延迟**。
|
||
✅ 已把 `NEXT.md` 第 6 条那句**假话**(「你的 SessionEnd 钩子会自动放行下一条」)改成
|
||
「**收尾=自己删 `claims/<id>`**」;`pitfalls.md` 新增 **P0-18**(回路挂在"宿主从不投递的事件"上);
|
||
`gate` 判据处补订正注释(**只改注释、不改逻辑**,改逻辑会放松"一次一件"的保护)。
|
||
|
||
### 四、本轮已做
|
||
|
||
- `[唤醒]-会话协作自检-脉冲` → **recurring(每小时)**,下一跳 23:14
|
||
- `[跟进]-会话协作自检-队列上报` → **recurring(每小时)**,下一跳 23:21
|
||
- **点火**:新建 `[跟进]-[会话协作自检]-点火验证投递`(22:39 已触发,`e2ccdea3` 已起)⇒
|
||
**当场证明"投递通道是好的"**:报错从 `no-follow-session` 变成 `target-busy`(=它找到了收件人)
|
||
- 重排 `[协作]-机制排查与修复-常驻投递容器` 到 **22:46**(重起常驻;prompt 里让它**记录存活时长**)
|
||
|
||
### 五、未解(**需用户拍板**,两条都是结构级的)
|
||
|
||
1. **投递要能成,必须给"目标忙"一个可超时的判据**(如"最近 N 秒内有实际活动"),
|
||
⛔ 而不是看 `status` 字符串 —— 否则"活跃会话恒 working"下**永远投不出**。
|
||
2. **"常驻"在本机没有可靠载体**(会话结束 ⇒ 后台任务被回收,已两次实测)
|
||
⇒ 若要真常驻,需要 **OS 级**方案(计划任务 `pythonw.exe` + `collabd.py --tick`)。
|
||
|
||
## 22:30 · 常驻投递容器(V1-常驻投递 · P0-6 治后首次重起)
|
||
|
||
- **结论**:**通过**。第二棒 22:18:57 起、pid=3552,22:30:25 仍活(**11m28s**),`_collabd.log` 连续出轮(约 20–23 s/轮)。
|
||
- **🔴 但第一棒死过**(6m13s):真凶**不是宿主 SafeDelete**,而是**同伴会话**在 22:12:20 改完 `collabd.py` 后于 **22:12:28 跑 `Stop-Process -Id 29944 -Force`** 强杀本常驻。
|
||
⇒ 新教训:**「重启前先杀旧常驻」必须区分「自己起的」与「别人起的」常驻**;否则每轮 `collabd.py` 热改都会掐掉公共时钟。
|
||
- **🔴 判据②作废**:`_front.stamp` 不前进**不是回归** —— 同伴会话 22:16 把「前置探针(设备侧 worker+垫片)」整块删了(脚本内 `🗑️ 2026-10-01 删除:前置探针`)。
|
||
⇒ 验收判据改为:① 进程 PID 存活 ② `_collabd.log` 连续轮次;`_tick.stamp` 恒不可作判据(宿主钩子写)。
|
||
- **⚠️ `--supervise` 不走单例端口**(`netstat` 查不到 20099)⇒ 重开前**只能查进程**,别用端口判空。
|
||
|
||
## 22:39–22:50 · [跟进]-[会话协作自检]-点火验证投递(第④类跟进会话 · 本轮=处置队列 + 就地待命)
|
||
|
||
- **本轮任务**:核对队列产物并处置 + 当场验证「有活跟进会话时投递能否到达本会话」。**结果:通道能定位、但收不下**(见下第 2 条)。
|
||
- **三处判读(用户要的判据)**:
|
||
1. `_collabd.log` 末尾已从连续 20+ 轮 `no-follow-session` 转为 **`target-busy`**(22:39:27 起每轮「延后投递:跟进会话 e2ccdea3 正在执行」)⇒ 通道**能定位**收件人。
|
||
2. `tmp/supervise-inbox/claims/` **为空** ⇒ S4 从未被取件(队列一直停在队首,无人做)。
|
||
3. 本会话 `SELF_SID=e2ccdea3-cb08-47bc-bbfe-e009d2b90e22`,标题 `[跟进]-[会话协作自检]-点火验证投递`(`working`)。
|
||
- **处置(4 个台账,改动前均已备份到 `tmp/supervise-inbox/_bak-*-22h45.*`)**:
|
||
- `交付物/任务图-会话协作自检.json`:**S4 → done**(逐项核对产物成立:节拍连续 22:18:57→22:30:50 共 42 行、`wake.lock` 内容 `free` 且 mtime 在走、`_collabd.log` 内 `SAFE_DELETE` 命中 0);队首 S4 → **S6**,pending 3。
|
||
- `tmp/supervise-inbox/tasks.json`:**M7 → done**(实跑 `skills/session-mechanism/scripts/selftest.py` = **PASS 44 / FAIL 0**,原 PASS38/FAIL1 的断言已修);M5/M6 同轮复验维持 done(`wakeups.jsonl` 85 行第 82 行逐字命中;看板 HTTP 200 / 110,196 B)。
|
||
- `blocked.json`:S5 刷新为「**当前无活主会话**」(最近一条主会话 `f8a792ab` 已 `completed`)。
|
||
- `NEED-USER.md`:重写为当前真实受阻项(含「## 解除条件」两选一),明确喊「需用户介入」。
|
||
- **🔴 新增两个任务图节点(把原本只躺在日志里的结构级发现**落进队列**)**:
|
||
- **S8** 修:常驻投递的载体 —— `collabd --supervise` pid3552 **22:30:50 后停摆**,进程表(`Get-CimInstance Win32_Process` 全量扫 `*collabd*`)已**无任何常驻进程**,只剩 `board.py --serve 8788`(pid16428);根因读数=宿主会话 **`3309bb55 [协作]-机制排查与修复-常驻投递容器` completed @22:30:55**。⇒ 与本文档 §三·1、§五·2 同源,本条=补登记。
|
||
- **S9** 修:投递「目标忙」判据 —— **源码级取证**:`_session_status()`(:1403) 只回 `sessions.status`;`SID_DEAD={completed,error,archived}`(:512) ⇒ 「活的」=`status ∉ SID_DEAD`(:548);延后判据=`_session_status(_tgt)=="working"`(:1235) ⇒ **「活」与「忙」是同一根字符串** ⇒ 活 ⇒ 忙 ⇒ 恒 `target-busy`,**没有可到达的放行状态**。取值域实测(只读宿主库):completed 123 / working 2 / archived 2 / error 1 ⇒ **无「空闲」档**。⇒ 用户报障的根因之一;也意味着「多起一条跟进会话」**不可能**把投递验成。
|
||
- **⛔ 本轮没做**(守"一轮只做这一件"):S8/S9 的修复、skill 侧 pitfall 归档(机制层 ⇒ 要独占锁)。
|
||
- **待命**:会话保持不关、就地等投递;⚠️ 预判**收不到**(S9 未修)——下一棒应先修 S9 或改判据,别重复点火。
|
||
|
||
## 23:0x–23:1x 用户两条指令落地:跟进会话职责收敛 + 投递「状态窗口死结」治本
|
||
|
||
**① 口径(用户逐字)**「跟进会话 只负责 ,创建协作会话(1、跟进上报后判断是否创建 2、被唤醒后 跟进目标情况 判断是否创建)」
|
||
⇒ 第④类**职责收敛**为「**判断要不要建协作会话,要就建一条**」;两条触发(收到上报/被唤醒)同一个动作;⛔ 它自己不做具体活(不改台账 state、不写 blocked.json、不派活、不抢锁)——那些归**它建出来的那条协作会话**。
|
||
落点:生产排期 `[跟进]-会话协作自检-队列上报` 提示词已改(改后 `nextRunAt` **复核未变**=23:21:35)+ `architecture.md §2.3.0c` + `SKILL.md §2` 四类行。
|
||
|
||
**② 投递死结治本(用户报障「队列一直没有上报」)**
|
||
- **真因=判据恒真**:`sessions.status` 取值域只有 `working/completed/error/archived`(**无"空闲"档**);**活着的**会话跑完一轮、停在等下一轮时**仍是 `working`** ⇒ 「活着⇒判忙不投 / 跑完⇒不 live 不投」⇒ **不存在能投进去的时刻**。实测 `e2ccdea3` 连打 6 分钟 `延后投递…等它空闲`+`target-busy`。旁证:唯一投成那次(08:55)目标是**日志已冻结的"死"会话**。
|
||
- **✅ 判据换人**:宿主工作区日志 `[SessionRunStateMachine]` 的 **`busy=`**(⛔ **不进数据库**,查库永远查不到)。`AGENT_STARTED`/`RUN_ACCEPTED` ⇒ `busy=true`;`AGENT_ENDED` ⇒ `busy=false`。**语义不变、只换判据形态**(用户「执行中就等待上报」,⛔ 不是"超时即投")。
|
||
- **两道判**:① 库里 `status` ∈ `SID_DEAD` ⇒ **直接判"没在跑"**(实测**自动化拉起的会话跑完不写 `busy=false`**,日志末条仍是 `busy=true`;`e2ccdea3` 末条 22:42:10 busy=true、库里 22:45:53 已 completed)② `working` ⇒ 再读日志**末条** `busy=` + **新鲜度**(`SRSM_FRESH=900 s`:长工具调用期间状态机全程静默,实测一次 8 MB 扫描静默 ~6 min ⇒ 窗口太短会把**正在跑**的误判成**空闲**)。
|
||
- **修的三处"判据看着在、其实恒假"(我同轮自己踩的)**:**(a) 正则位置组错位** —— `(?:\.(\d+))?` 内层 `(\d+)` 仍是捕获组 ⇒ `m.group(8)` 整体偏移 ⇒ `busy` **恒 False** ⇒ **恒判空闲**(改**命名组** + 加**源码级反回归断言**)|**(b) 同一秒内"后出现的没胜出"** ⇒ 取到旧状态(改比 `(时间,行序)`)|**(c) 我的变异法对照脚本是空的**:过滤词 `-k 忙判据` 没覆盖 `t_target_busy` ⇒ 变异体根本没被试、报"全绿"(换过滤词后 M4/M5 双双报红)⇒ 判据=**变异跑完必须回读"变异体被哪几条用例跑了"**。
|
||
- **回归**:`selftest` **PASS 45 / FAIL 0**(新增 `t_srsm_busy` 6 项;旧 `t_target_busy` **同批改**+补反向「空闲 ⇒ ⛔ 不延后」)|**五组变异逐一按预期报红、还原复绿**|`install.py --verify` **全绿**。
|
||
- 🔴 **残留(如实报)**:判据换对后拦截原因**从 `target-busy` 变成 `no-follow-session`** —— **真状态**:此刻**一条活的 `[跟进]` 会话都没有**(`e2ccdea3` 跑完变 completed 即退出网关活会话集)⇒ **"推"只在目标活着时可用**,目标不在线靠**排期到点拉起**。
|
||
- 🔴 **未处理**:`[跟进]-[会话协作自检]-点火验证投递`(一次性,已跑完,`nextRunAt` 为空 ⇒ 不会再触发)留在表里;常驻投递载体(S8)仍未重起。
|
||
|
||
## 22:47–23:1x 常驻投递**第三棒**:重起 + 首次越过 20 分钟存活(V1-常驻投递)
|
||
|
||
- **先查后起(幂等)**:`Get-CimInstance Win32_Process` 全量扫 python ⇒ 只剩 `board.py --serve 8788`(pid16428),**无任何 `collabd.py --supervise`** ⇒ pid3552 确已消失,必须重起。
|
||
- 🔴 **新发现(旁证修正)**:22:34–22:45 那段 `_collabd.log` **不是"常驻还在"**,而是**宿主钩子触发的 `collabd.py --tick`** —— 23:10:41 同轮直接抓到 `wb-result-hook.py` 与 `collabd.py --tick` **同秒成对出现**。⇒ **"日志在走"≠"常驻在跑"**,判常驻**只能查进程**。
|
||
- **起法**(唯一可行形态):**会话后台任务**(task_id `Vm8lck`)+ stdout/stderr **全重定向** ⇒ `tmp/supervise-inbox/_supervise-bg.out.log`;命令 `python.exe -u collabd.py --supervise` ⇒ **pid=12348,起于 22:47:05**。⛔ 未用 detached spawn。
|
||
- **三条判据(全过)**:① **PID 12348 存活**(22:56 与 23:10:41 两次探活均在)② **`_collabd.log` 轮次连续、节拍 21–23 s/轮**(22:47:18 首轮 → 23:10:54;`supervise loop start` 累计 4 次)③ **`wake.lock` 存在**、内容 `free`、mtime 随心跳刷新(22:36:25 → 22:55:46 → 23:05:11)⇒ **P0-6「稳态零删除」在本轮成立**。
|
||
- 🔴 **存活读数(本棒最关键)**:**22:47:05 起 → 23:10:41 仍活 = 23 min 36 s,观察窗内未再现消失**。对比:22:06 那份 6m13s、22:18 那份 ~12–20 min ⇒ **本次首次越过 20 分钟**。
|
||
⚠️ **但"结构性回收"假设未被证伪**:观察只能在本轮 turn 内做,而本容器会话一结束后台任务即被回收 ⇒ 仍差**一次「跨 turn 存活」取证**。取证法:下一棒**起完不做观察**、等本会话变 `completed`,**由别的会话**探活 pid。
|
||
- 🔴 **`SAFE_DELETE_BULK_CONFIRM_REQUIRED` 原文逐字抄回**(⚠️ 该行写在 20:52 头部**之上** = **上一轮遗留**;本轮**未再出现**,全文命中计数 1):
|
||
`[safe-delete][SAFE_DELETE_BULK_CONFIRM_REQUIRED] {"count":50,"threshold":50,"scope":"turn","targets":["E:\ProgramData\AIProject\ai1net-dsh-server\tmp\supervise-inbox\wake.lock"],"targetCount":1}`
|
||
⇒ 读法:**仍有别的删除路径**把 `wake.lock` 当"本轮删除目标"累计到 **50 次**阈值(`scope:"turn"`)被拦下 ⇒ **P0-6 未全治、该路径未定位**(本轮 `wake.lock` 全程 `free` 未消失 ⇒ 当前无实际删除发生)。
|
||
- **投递仍全轮未成**:每一轮都是 `投递未成(no-follow-session)`(= S9 修后的**真状态**:此刻**没有活的 `[跟进]` 会话可投**)⇒ 与 22:4x 的 `target-busy` **已是两个不同原因**。
|
||
- **⛔ 本轮没做**(守"一轮只做这一件"):`wake.lock` 其他删除路径定位、跨 turn 存活取证。
|
||
- **待命**:会话**不关**、就地待命;后台任务 `Vm8lck`(pid 12348)继续跑。
|
||
|
||
## 23:1x 看板标签改简称 + 校验桩两条假红修复 + 常驻存活复查(**推翻上一节末句**)
|
||
|
||
**① 用户第六条口径(逐字)**「**唤醒会话 改为 唤醒,跟进会话 改为 跟进 字体和 唤醒一样大小\n协作程序 改为 协作**」
|
||
- `assets/board.html` 两处标签:`'跟进会话','n-title'` → **`'跟进','n-title-sm'`**(**与"唤醒"同字号** 13px,用户明令)|`'协作程序','n-title'` → **`'协作','n-title'`**(16px 沿原样)。同批:布局注释 2 处 + 图外 `?` **新增简称对照** + `aria-label`。
|
||
- 🔴 **简称 ≠ 改角色** ⇒ 正文/文档/代码里的**全称一律不动**(唤醒会话/队列上报的跟进会话/协作程序)。⛔「协作」别读成「协作会话」。
|
||
- `scripts/board_ext.py` 的 `"name"`:`唤醒会话` → **`唤醒`**(图格标题取的就是它);**看板按文件签名热重载**(`board.py::spec_from_file_location`)⇒ **未重起服务**即生效,判据=直读 `/board.json` 的 `triggers[0].name=="唤醒"`(23:07:58 快照已验)。项目侧同源副本 `.workbuddy/collab/board_ext.py` 同步(md5 两边一致)。
|
||
- 落笔:`SKILL.md`(`last_change` 新段 + §2 四类行补简称对照)· `references/architecture.md §2.3.0c-2`(新增简称表)· `references/collab-detail.md`(形状/名称段补一条)。
|
||
|
||
**② 修掉校验桩两条「断言写死内容」的假红(`tmp/render-check.mjs`)** —— 都是同一族病:**把某一时刻的内容①写死成期望值 或 ②拿一个短词当探针**
|
||
- **(a)** 退场用例的还原判据原写 `get('archNote').includes('已收起')`;而 23:07 的线上快照**真出现了退场件** ⇒ 还原后本来就**该有**「已收起」⇒ **必红** ⇒ 改成 **`get('archNote') !== an0`**(`an0` = 注射前原文,**逐字相等**才叫还原)。
|
||
- **(b)** 防回潮判据原拿**用户刚给的名字当子串探针**(`!includes('唤醒')`);简称更短更常见(`[唤醒]-…`、未归类线「唤醒机制」)⇒ **必红** ⇒ 改成**查被删那段的内容**(`/就是一条会话|外圈虚线|不进下方那一排|随需求确定时才创建/`)+ 新增**防恒真对照**(同几个词在 `?` 里**应当有** ⇒ 证明探针不是恒真)。
|
||
- 🔴 **判据沉淀**:断言里**别写死期望值、别拿短词当探针** ⇒ 一律 **与「改前原文」逐字比** 或 **查「那段被删内容」本身**;夹具 `tmp/board-verify.json` 由 22:22 旧快照刷新为 **23:07:58 线上真快照**。
|
||
- **回归(全部现算)**:`selftest` **PASS 45 / FAIL 0**|渲染桩 **FAIL 0 / 61**|几何 **FAIL 0 / 8**|`install.py --verify` 全绿|`py_compile` 全过。
|
||
|
||
**③ 常驻存活复查 —— 上一节末句「pid 12348 继续跑」已被推翻**(⛔ 别照它办)
|
||
- 全量进程扫描(`Win32_Process` 全表,827 条)**只剩 `board.py --serve 8788`(pid 16428)**,**没有任何 `collabd.py --supervise`**。⇒ pid 12348(22:47:05 起)**在 23:10:41 探活仍在**(跨 **23m36s**,首次越过 20 分钟),**到 23:12:2x 已消失**。
|
||
- ⇒ **「容器会话结束 ⇒ 宿主回收后台任务」这条结构性假设仍未证伪**:它恰恰死在发起它的那条会话收工前后。
|
||
- 🔴🔴 **同一坑当天第二次复现**:12348 死后 `_collabd.log` **仍在写**「投递未成(no-follow-session)」(最后一行 **23:12:55**)——且**采样窗 23:13:38–23:14:05 内 0 行**(我停止发工具调用即停)⇒ **那些轮次是宿主钩子触发的 `collabd.py --tick`**(每次工具调用 ≈ 一次 tick)。⇒ **判据(第二次坐实):判常驻只能查进程,⛔「日志在走」≠「常驻在跑」。**
|
||
- 投递仍全轮 `no-follow-session`(=判据换对后的**真状态**:此刻没有活的 `[跟进]` 会话)。
|
||
|
||
**④ 订正 `NEED-USER.md` 的过期情报(⛔ 不许留假情报)**:原文「**请把「唤醒机制」那条跟进会话拉起来**」+ 解除条件①②都在讲**主会话** —— 两处都过期:
|
||
- 真因是**此刻一条活的 `[跟进]` 都没有**(不是"没拉起来")⇒ 靠**宿主排期每小时自动拉起**(`FREQ=HOURLY`),**不需用户动手**;
|
||
- 口径已改为**投递目标=跟进会话、⛔ 不降级投主会话** ⇒ 解除条件①「用户新建主会话」**已作废**。
|
||
- 做法:**只追加订正段,⛔ 不改原文**(`collabd.py::need_user()` 的重写会把「## 解除条件」及其后**原样带走** ⇒ 追加段能活下来;这也是 P0-16 第②条纪律的用法)。
|
||
- ⚠️ **仍在办**:队首压着的是 `M5=done`,其 `line` = **`唤醒机制`(09-30 已退役的旧线)** ⇒ 即使有活的 `[跟进]`,**类别也可能对不上**。旧线残留件(`notify_pending=["M5=done","M7=running"]`)的**归档口径未定**,⛔ 不擅自丢。
|
||
|
||
**⑤ 未做 / 待办(如实报)**:常驻载体**仍未重起**(⛔ 不在本会话起 —— 本会话有 `pending`/`running` 后台任务会**静默压制宿主 idle 钩子**);上一节起它的那条会话**已收工** ⇒ S8 实际**尚未完成**,需由**专用容器会话**再起一次,并**换一条会话**做跨 turn 存活取证。
|
||
|
||
---
|
||
|
||
## 【唤醒会话】23:15–23:19 · 三条判据全成立 ⇒ 推一次 ⇒ 未落点(2026-10-01)
|
||
|
||
会话 `[唤醒]-会话协作自检-脉冲`(sid `f7d7102b`)· 自动化 7fe0fe82 本小时那一跳。
|
||
|
||
- **第 0 步(让位检查)**:本工作区 `[唤醒]` 前缀会话共 2 条,`working` 的只有**本会话自己**(另一条 `dc1860b3`=`completed`)⇒ ⛔ 无"另一条活的唤醒会话"⇒ 本轮不被让位、照做。
|
||
- **三条真读数(全成立)**:① 有 `working` 会话=`3f43ce71`(接续·会话机制合并包·任务4b-4d-6;读时 `working`,**23:18:11 转 `completed`**=正常收工)② `queue.json`:`head=S6`(会话协作自检·todo)、`pending=3`、`doing={}`、`gate=free`、`blocked` 1 条(S5)③ `acceptance_state` 7 项中唯一非 pass = **V2-主会话可响应=待重验**。
|
||
- **推的结果=未投出**:跑 `collabd.py --tick`(唯一投递方)⇒ `deliver=no-follow-session`;`wakeups.jsonl` 仍 85 行、无新记录。根因实测:2 条 `[跟进]`(`e2ccdea3`、`fe212445`)都在库里,但**都不在活会话集**(活会话集只有 `3f43ce71`、`f7d7102b`)⇒ `follow_for_topic()` 返 `why=follow-not-live` ⇒ 按 §4 **喊用户**(`NEED-USER.md` 在册)+ ⛔ 不降级投主会话。落点=`[跟进]-会话协作自检-队列上报` 排期 **23:21:35**。
|
||
- 🔴 **新查出(本轮只报不改)①:`deliver=no-follow-session` 一码两态**(`_deliver_str` 里 `_want==""` 的两个分支同码)——(a) 一条 `[跟进]` 都没有 (b) 有但都不活;**单看 tick 那行 stdout 分不出来**(只有 `NEED-USER.md` 文案能分:会写"找到 N 条…都不活")。建议拆成两码。
|
||
- 🔴 **新查出 ②:常驻投递已死** —— PID 12348(`collabd.py --supervise`,22:47:05 起)经 `Get-Process` **实测 DEAD**(⚠️ 23:10 那份 `_ps-supervise-pid2.txt` 已过期,⛔ 别拿它当现状)⇒ **V1-常驻投递回退**;看板 PID 16428 仍 ALIVE。⇒ 投递只剩"钩子补充",23:21:35 那一下**不保证**自动落点。
|
||
- 🔴 **日志路径订正(避坑)**:真日志=`$WS/.workbuddy/collab/logs/_collabd.log`(尾两行 23:17:12/23:19:19 均 `投递未成(no-follow-session)⇒ 保留 M5=done 在队首,下一轮重试`);`tmp/supervise-inbox/_collabd.log` 是**陈旧件**(停在 15:07,全是已修的 GBK fatal)⇒ ⛔ 别拿它判现状。
|
||
- 探针脚本(临时件,⛔ 不入库):`tmp/wake-probe-20261001.py`、`tmp/ws-sessions-probe-20261001.py`、`tmp/follow-probe-20261001.py`、`tmp/live-probe-20261001.py`、`tmp/autom-probe-20261001.py`、`tmp/sched-probe-20261001.py`。
|
||
- 🔴 **订正(同轮 23:24 补查):上面「落点=23:21:35」未兑现** —— 到 23:24:33(过 ≈3 min)`sessions` 表里**仍无新 `[跟进]` 会话**(最后一条跟进 `fe212445` 停在 21:06:46)⇒ 那一跳没拉起。
|
||
- 🔴 **判据订正(重要·避坑)**:`automations.next_run_at`/`last_run_at` 在本机**不被维护** —— 本会话自己那条 `[唤醒]` 排期**明明已触发**(拉起了 `f7d7102b`),却仍写 `next=23:14:20 / last=None` ⇒ ⛔ **别拿它判"有没有触发"**;**唯一可信判据=`sessions` 表里有没有新会话**。
|
||
- ⛔ 本轮**未**起脉冲/未 spawn 下一棒/未后台 sleep 等落点(遵用户 10-01 22:1x 明令);`NEED-USER.md` 已追加第二次订正。
|
||
|
||
---
|
||
|
||
## 【跟进会话】23:25–23:30 · 职责收敛后**首轮**:只判断「要不要建协作会话」⇒ **建 1 条**(2026-10-01)
|
||
|
||
会话 `[跟进]-会话协作自检-队列上报`(sid `630466ea`,宿主排期 `FREQ=HOURLY` 那一跳)。
|
||
|
||
- 🔴 **第 0 步(让位检查)**:本工作区 `[跟进]` 前缀会话共 3 条,`status='working'` 的**只有本会话自己**(`SELF_SID=630466ea`)⇒ ⛔ 无第二条活跟进 ⇒ 本轮不被让位、照做。
|
||
- 🔴 **订正 23:24 那条结论(宿主排期其实兑现了)**:本会话 `created_at=23:25:37`,而 `[跟进]` 排期目标是 **23:21:35** ⇒ 迟约 **4 min** 但**确实拉起了一条新的 `[跟进]` 会话**。⇒ 23:24 写的「那一跳没拉起(23:24:33 仍无新会话)」**只是查得太早**(3 min 窗口不够);判据仍成立:**只看 `sessions` 表有没有新会话**,⛔ 不看 `next_run_at`。
|
||
- **三条真读数**:
|
||
1、任务图 `交付物/任务图-会话协作自检.json`(updated 22:50)非 `done` 节点 = **S8/S9/S6 todo**(S5 todo 但受阻,S7 依赖未满);宿主库 `sessions` 里**没有任何 `status='working'` 的协作会话**(`[协作]-…` 最近 4 条全 `completed`)⇒ **有件在等、无人推**。
|
||
2、`advance.md`/`digest.md`:`head=S6`、`pending=3`、`doing={}`、`gate=free`、`blocked_lines=[]`;机械摘要点名**可派 S8/S9/S6**。
|
||
3、**常驻投递仍死**:全量扫 python 进程只剩 `board.py --serve 8788`(pid **16428**,22:10:31 起),**无任何 `collabd.py --supervise`** ⇒ `acceptance_state.V1-常驻投递` 的「一直运行」仍未达成(判常驻只能查进程)。
|
||
- ✅ **本轮动作=建 1 条协作会话**(一棒一件 · 关键路径队首优先):登记**一次性自动化** `1e7feb1f-0dc5-4e0f-979b-74c3a15dccd6`,名 `[协作]-[机制排查与修复]-S8 常驻投递载体(跨 turn 存活)`,`scheduledAt=2026-10-01T23:31`,`cwds=E:/ProgramData/AIProject/ai1net-dsh-server`。选题依据:**关键路径 S4→S8→S9→S5→S6→S7 的队首就是 S8**,且本文档 §23:1x ⑤ 明记「**S8 实际尚未完成,需由专用容器会话再起一次**」;prompt 内已写入本件残留要求「**跨 turn 存活取证(起完不做观察、由别的会话探活)**」与「会话内后台任务形态已被三次实测证否」。
|
||
- ⛔ **本轮不做**(守职责收敛,用户 23:0x 逐字):未改 `tasks.json` 的 `state`、未写 `blocked.json`、未改任务图、未抢锁、未碰 `NEED-USER.md`、未起常驻 —— 那些都归**它建出来的那条协作会话**。
|
||
- ⚠️ **未选 S9 的理由(如实说)**:S9「投递目标忙判据」在 23:0x–23:1x 已由 `6c6da234` 落地(判据换 `SRSM busy=`、`selftest` PASS 45/FAIL 0、`install.py --verify` 全绿)⇒ 实质已完成,只是任务图(updated 22:50)**书签滞后**;其更新应归那条协作会话,⛔ 本会话不改图。
|
||
- ⚠️ **仍悬着的真缺口(供下一棒/主会话知悉,⛔ 未处理)**:① **S5/V2** —— 当前无活主会话,`blocked.json` 登记在册(`NEED-USER.md` 的解除条件①「用户新建主会话」已因口径改为「投递目标=跟进会话」而**作废**);② `NEED-USER.md` 里还有一条**旧线残留**:队首曾压 `M5=done`,其 `line=唤醒机制`(09-30 已退役线)⇒ 类别对不上,**归档口径未定**。
|
||
|
||
|
||
|
||
## 23:3x · S8(常驻投递载体)—— 停手:机制层独占锁被孤儿会话持有
|
||
|
||
- 本棒 `[协作]-[机制排查与修复]-S8 常驻投递载体(跨 turn 存活)`,按 `CODEBUDDY.md §1.5 A` 跑完 `state.py`,
|
||
抢机制层独占锁 `handoff-guard.sh --claim-exec`(⛔ 无 `--domains`)**失败** ⇒ 按硬要求与 R9 **停手**。
|
||
- 占用者 `会话协作-机制排查与修复-20261001`,**20:41 起**(≈2h56m)。核证为**孤儿锁**:`sessions` 表无此标题、
|
||
本工作区唯一 `working` 会话是本棒自己、`automations` 无同名排期;guard **无心跳/陈旧判定** ⇒ 不会自愈。
|
||
- 影响:全局执行锁在 ⇒ **域锁路径同样被拒** ⇒ 一切动机制层的棒都开不了工;关键路径 `S4→S8→S9→S5→S6→S7` 卡在 S8。
|
||
- 只读取证:**常驻投递仍死** —— 只剩 `board.py --serve 8788`(pid 16428, 22:10:31),无 `collabd.py --supervise`。
|
||
- 已登记:`collabd.py --report S8 --state blocked`(含原因)+ `tmp/supervise-inbox/blocked.json` 的 `S8` 项
|
||
+ `NEED-USER.md` 第三条(含给用户的释放命令)。
|
||
- ⛔ 未做(受 R9 与硬要求约束):删锁/接管锁/写任务图 JSON。解除须**用户本人**释放该锁。
|
||
- 附带观察(锁卫生,⛔ 本棒未动):`05-交接单/` 下另有 3 个**空**残留锁目录 `.lock-02`(09-25)、`.lock-04`(09-25)、
|
||
`.lock-基础插件打包-01`(09-26);服务器侧锁 `/opt/dsh/state/.op-lock` 仍被 `storyforge-e2e` 持有(远端,未复核)。
|