pi-multi-viewers 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/AGENTS.md +192 -0
  2. package/README.md +153 -0
  3. package/docs/design.md +288 -0
  4. package/docs/examples/first-experiment/README.md +42 -0
  5. package/docs/examples/first-experiment/work-a/AGENTS.md +10 -0
  6. package/docs/examples/first-experiment/work-a/a/0001.md +65 -0
  7. package/docs/examples/first-experiment/work-a/a/0002.md +70 -0
  8. package/docs/examples/first-experiment/work-a/perspective.md +5 -0
  9. package/docs/examples/first-experiment/work-b/AGENTS.md +10 -0
  10. package/docs/examples/first-experiment/work-b/b/0001.md +100 -0
  11. package/docs/examples/first-experiment/work-b/perspective.md +6 -0
  12. package/docs/reviews/2026-09-10-e2e10-fork-source-modes-review.md +366 -0
  13. package/docs/reviews/2026-09-10-e2e11-forkmode-guards-review.md +229 -0
  14. package/docs/reviews/2026-09-10-e2e12-code-review.md +346 -0
  15. package/docs/reviews/2026-09-11-e2e13-code-review.md +284 -0
  16. package/docs/reviews/2026-09-11-e2e14-observability-review.md +216 -0
  17. package/docs/reviews/README.md +36 -0
  18. package/docs/test-methodology.md +258 -0
  19. package/extensions/multi-viewers-say/index.ts +156 -0
  20. package/fake_agent.py +120 -0
  21. package/human_sayer.py +144 -0
  22. package/human_viewer.py +215 -0
  23. package/meeting_core.py +255 -0
  24. package/meeting_engine.py +733 -0
  25. package/meeting_fs.py +1066 -0
  26. package/meeting_loop.py +606 -0
  27. package/package.json +41 -0
  28. package/prompts/multi-viewers.md +94 -0
  29. package/scripts/check-residue.sh +190 -0
  30. package/scripts/mv.sh +325 -0
  31. package/start_discussion.py +1485 -0
  32. package/templates/AGENTS.md.tpl +100 -0
  33. package/templates/agent.md.tpl +9 -0
  34. package/templates/gitignore.tpl +7 -0
  35. package/templates/spec-readme.md.tpl +87 -0
package/AGENTS.md ADDED
@@ -0,0 +1,192 @@
1
+ # pi-multi-viewers 开发指南
2
+
3
+ > 本文件指导**本项目本身的开发**。与 `templates/AGENTS.md.tpl`(运行时参与
4
+ > 分析的 LLM 行为指令)是两回事。
5
+
6
+ ## 项目目的
7
+
8
+ pi-agents-helper(多方讨论达成共识)的平行第四代:**多视角协同分析**。
9
+ 把主 pi session **fork** 成 N 个视角 agent(人物/开发准则等稳定视角),
10
+ 各带视角任务书,在 meeting 协议下交锋、修正、收敛。与 pi-agents-helper
11
+ 共享 meeting 协议核心(core/fs/engine),独立演化、互不依赖。
12
+
13
+ ## 架构(fork-only,唯一模式)
14
+
15
+ ```
16
+ meeting_core.py 纯逻辑判定——无 I/O(判定只看参与者,human 视而不见)
17
+ meeting_fs.py git/文件层
18
+ meeting_engine.py 【唯一状态机】+ 协议信号 + responder 注入
19
+ meeting_loop.py Pi 薄壳:首唤生成 fork 源 + `--session` 打开 → 存 sid → `--session-id` 续接
20
+ fake_agent.py 测试薄壳:responder = 随机决策
21
+ start_discussion.py 环境生成/启动/清理(viewers 发现 / spec 解析 / fork 源)
22
+ human_viewer.py 【human 通道】只读展示(增量/--follow/游标)
23
+ human_sayer.py 【human 通道】插话命令(单次/stdin/交互 -i)
24
+ scripts/mv.sh wrapper(prepare/start/status/wait/cleanup/view/say)
25
+ prompts/multi-viewers.md /multi-viewers 入口(视角设计三原则 + 审核闸门)
26
+ extensions/multi-viewers-say/ /multi-viewers-say 插话(registerCommand,零 LLM)
27
+ docs/design.md 设计文档(fork 源模式与规模口径 + 决策记录)
28
+ package.json npm 包 pi-multi-viewers(pi.prompts 注册;发版待办)
29
+ templates/ AGENTS.md.tpl / agent.md.tpl / gitignore.tpl / spec-readme.md.tpl
30
+ viewers/ 示例视角(性能/简单/铁律——仅是形态示例,视角内容由用户按需自定)
31
+ docs/examples/first-experiment/ 首次实验存档(机制验证 + 模板原型 + 真实消息)
32
+ docs/reviews/ 自我审阅存档(多视角自审 result.md 原文 + 索引/口径说明)
33
+ tests/ 测试(unittest discover tests)
34
+ ```
35
+
36
+ **fork 三件套**(初始化层与 pi-agents-helper 的全部差异所在):
37
+
38
+ 1. **session fork**:首唤由本地循环生成 **fork 源文件**(`meeting_fs.build_fork_source`:从主 session 按模式裁剪——默认 `budget` 预算+折叠;`compaction` 按 compaction 边界;`full` 全量),再用 `pi --session <fork 源> --name <分析名>-<视角名>` 打开;后续唤醒 `--session-id <sid>` 续接(sid 存 `status-<agent>.json`,预生成 UUID)。
39
+ - **不用 `pi --fork`**:那是全量拷贝(长会话必超窗,实测 731k/930k tokens + 384k completion 预留 > 1M),且无法在尾部注入切换叙事
40
+ - 切换叙事(2 对"停止旧任务 → 新任务说明"对话)注入在 fork 源尾部——切断历史叙事惯性;主题取自 `protocol.json.topic`(**不**二次解析 question.md)
41
+ - `--name` 是显示名 label(session_info entry),不劫持 id(id 归机制=UUID,名字归人);规模/口径见 `docs/design.md`
42
+ 2. **cwd = 主项目**(forkCwd):agent 直接读项目文件;work_dir 仅消息交换
43
+ 区,prompt 中所有路径**绝对化**(msg_path/meta/result.md)
44
+ 3. **协议注入**:work-X/AGENTS.md(讨论协议)不在主项目祖先链上,pi 不会
45
+ 自动发现——wake_llm 无条件 `--append-system-prompt` 注入(视角 prompt_file
46
+ 同理)。e2e 曾暴露缺口:无注入时靠模型能力偶尔跑通,非设计保证
47
+
48
+ **上下文三层**:fork 对话历史(自动)+ 主项目文件(自动,含 cwd/AGENTS.md
49
+ 祖先发现)+ spec background.md(人工可选边界约定——prepare 蒸馏机制已
50
+ 移除,fork 使其冗余;background 只写显式边界,不复述对话)。
51
+
52
+ **关键约定**:pi sessions 目录编码 = `--` + 去首尾斜杠内斜杠换 `-` + `--`
53
+ (`/tmp` → `--tmp--`;wrapper 解析 fork 源依赖它,编码错一根横线 = 静默
54
+ 解析不到——已加显式报错)。**fork-only fail-fast**:缺 fork 源 = 明确报错
55
+ (loop/start/wrapper 三层),无静默退化(无上下文的视角分析违背产品本质)。
56
+
57
+ **fork 容量约束(2026-09-10 实测)**:fork 携带的是 session **原始条目**
58
+ (主 pi 实际发送的上下文由压缩层在渲染时生成,不在条目里)——长会话的
59
+ 原始条目远超模型窗口(实测 930k tokens + 384k completion 预留 > 1M,provider
60
+ 直接 400;`pi --fork` 原生命令同样超窗)。因此 **budget 是长会话唯一可行
61
+ 模式**;compaction/full 只适合中小会话(数字口径见 `docs/design.md`)。
62
+
63
+ **viewers/ 分支约定**:spec 的 `agents/` 目录存在 = 显式模式(优先);
64
+ 不存在 → 项目 cwd 的 `viewers/*.md` 发现(文件名即 agent 名:中文合法,
65
+ 禁路径分隔符/空白/human/≤32;**文件名是 agent 名唯一来源**——视角文件只写
66
+ 视角内容,身份/参与者/消息格式由脚本注入;文件名排序定 starter/RR/
67
+ resultWriter);两者皆无 → 明确报错。**meeting 至少 2 个 LLM agents**
68
+ (0/1 个视角拒绝启动)。**空视角任务书拒绝启动**(纯空白 = 无 lenses 的
69
+ agent,会让多视角退化成同名随机视角——静默退化)。
70
+
71
+ **核心不变式**:状态机只在 `meeting_engine.agent_loop` 一份;fake_agent 与
72
+ meeting_loop 通过注入 responder 复用。human 插话不改变状态机——只在
73
+ 判定函数的**输入过滤**与**配额增量**两处扩展(沿用 pi-agents-helper)。
74
+
75
+
76
+ ## 开发铁律(每次修改代码后必核验三条,用户要求 2026-08-09)
77
+
78
+ 1. **职责边界**:各部件是否只做自己该做的任务,而没有在做别的部件
79
+ 本应负责的任务(LLM 内容 / 引擎流程 / fs I/O / core 纯逻辑 / human writer 写消息)
80
+ 2. **复杂度匹配**:各部分代码复杂度是否符合设计本应的复杂度,
81
+ 不应让大量补丁堆出不必要的复杂度(设计简洁,实现复杂=方法有问题)
82
+ 3. **设计符合度**:代码实现是否真的符合设计——逐项对照设计文档,
83
+ 发现偏差先推演确认,不急着测试(测试验证设计,不替设计纠错)
84
+
85
+ ## 设计原则
86
+
87
+ 沿用 pi-agents-meeting-discuss / pi-agents-helper 的全部原则(确定性归
88
+ loop、状态从 git 共享事实推导、单一事实源 = protocol.json、无静默铁律、
89
+ 测试对象 = 生产对象、新概念禁令、信息层/流程层分离、human 保留名),
90
+ 并新增(fork 模式专属):
91
+
92
+ 1. **fork-only**:唯一模式。缺 fork 源 = 明确报错——不提供无上下文退化
93
+ (违背产品本质);脚本场景先 `pi --print` 造引导 session。
94
+ 2. **初始化三件套集中在 loop**:fork 源 + 视角注入 + 命名,其余层
95
+ (协议/引擎/human 通道/结果回流)与 pi-agents-helper 零差异。
96
+ 3. **稳定资产与每次分析分离**:视角 = viewers/ 项目资产(写好长期用),
97
+ 主题 = question.md(每次变);不把视角内容散落进每次的 spec。
98
+ 4. **单一事实源(骨架)**:spec 骨架生成只在 `gen_spec_skeleton` 一份
99
+ (python);wrapper `--prepare` 只做参数解析后转调,禁止 bash 复刻。
100
+
101
+ ## 注释引用约定(#17,e2e7 评审)
102
+
103
+ 代码注释中的历史编号引用("设计 16.x"、"审核#N"、"review4/5 Lxx"、
104
+ "用户 NNNN")出自**开发过程记录**(会话/上游文档),**在本仓库内不可
105
+ 检索**。约定:这类引用只是溯源线索,**代码行为以自描述注释为准**——
106
+ 读代码时不需要也无法追查编号原文;新增注释不再引入不可核验编号
107
+ (描述清楚"为什么",编号可省略)。
108
+
109
+ ## 设计文档
110
+
111
+ - `docs/design.md`:本项目设计文档——fork 源模式与**规模口径**(产物侧
112
+ 指纹 / 消费侧规模 / 校准比)、决策记录(含被否决方案与重估触发条件)
113
+ - `docs/examples/first-experiment/`:首次实验存档(2026-09-09,历史)——机制验证结论、
114
+ 协议/视角模板原型(措辞经实验验证)、三条真实消息(可作 loop 测试 fixture)
115
+ - `docs/reviews/`:**自我审阅存档**——本项目用多视角机制审阅自身实现的 result.md
116
+ 原文(每份带来源说明与修复 commit;索引与口径见该目录 README)
117
+ - 上游设计文档:`../pi-agents-helper/docs/pi-helper-design.md`(共享协议
118
+ 核心的行为定义——信息层/流程层分离、配额语义、状态机推演对本项目
119
+ 同样有效)
120
+
121
+ ## 单元测试重设计工程(沿用 pi-agents-helper 方法论 2026-09-01)
122
+
123
+ **背景**:只做"观察 agent loop 流程"的集成式测试不够——loop 跑通不代表
124
+ 每个 API 正确;API 细节(边界、异常)未被逐一定义验证。
125
+
126
+ **流程(不可跳步)**:
127
+ 1. **测试计划**:基于 agent loop + 流程设计,分解每个脚本的详细功能
128
+ 2. **脚本 + API 列表**:每个 API 精确定义功能表现(正常/边界/不应出现的
129
+ 情况),API 组合应符合设计流程——**列表即测试基准,用户审阅后生效**
130
+ 3. **逐 API 单元测试**:各种可能出现的情况 + 各种不应该出现的情况
131
+ 4. **测试报告**:列出问题清单
132
+ 5. **报告审阅(不动代码)**:每个问题的影响/修改方案/对逻辑流程的影响
133
+ 6. **逐 API 修改 + 复测**(基于审阅结果)
134
+ 7. **全流程回归**:完整测试 + 针对修改影响的补充测试
135
+
136
+ **进度单一事实源**:API 清单文档状态列(待测/已测/审阅/已修/复测)——
137
+ 任何时候打开清单即知进度;会话中断/重启由 AGENTS.md + 清单恢复。
138
+
139
+ **拼接点盲区教训**(pi-agents-helper 实测教训,同样适用):测试覆盖不能
140
+ 只到函数级——main()/__main__/CLI 分发等**调用链拼接点**是独立盲区(mock
141
+ 打不到 runpy 的 __main__ 新模块)。API 清单必须显式包含拼接点,测试用
142
+ 真实 subprocess 构造环境跑生产调用链。
143
+
144
+ ## 测试方法论(制度核心;方法细节见 docs/test-methodology.md)
145
+
146
+ **总原则(错误→制度化,用户 2026-09-09 定)**:出现错误后要记的不是
147
+ "这个 bug 怎么修",而是**什么制度缺失让它漏过、让定位变慢**——补制度
148
+ 保证同类错误结构性不再犯。复盘四问(详版见方法文档):
149
+ 1. 特性落地时有没有盘点它打破的既有假设(如"agent 名是 ASCII")并补
150
+ 边界测试?
151
+ 2. 定位方法对吗——纯机制层 bug(git/文件/编码/状态机)用确定性复现
152
+ (单测/小实验),禁止真实 LLM e2e 碰运气(只做最终确认)?
153
+ 3. 失败现场保留了吗(测试失败删 log = 测试白跑)?
154
+ 4. 改装置/用新 API 先做秒级最小实验验证语义了吗(不跑长测试试错)?
155
+
156
+ **方法条目仓库**:全部具体方法(git 写前 pull / 进程检测 / 失败三分类 /
157
+ 装置对齐 / 运行观察分离 / 参数形态矩阵二维 / 不留 session / 隔离 +
158
+ 用例级 teardown / 失败保留现场)及历史实例,统一在
159
+ `docs/test-methodology.md`——新方法在那里追加,AGENTS.md 不逐条同步
160
+ (避免 100 个方法全堆进来)。
161
+
162
+ ## Git 准则(用户约定,沿用)
163
+
164
+ 1. **每次改动先更新本地 git**:对本项目代码/文档的每次修改,先 `git add` + `git commit` 记录。
165
+ 2. **阶段性完成即推送**:完成一个阶段性修改后,必须同时 `git push origin main` 推送到 GitHub。
166
+ 3. **本地与远程保持同步**:提交后确认工作区干净、远程与本地 HEAD 一致。
167
+ 4. **提交信息**:使用清晰、描述性的 message,说明本次改动内容。
168
+ 5. **行为/语义修改同步文档**:对协议行为、产品形态的任何修改,必须同步
169
+ README 与相关模板。
170
+
171
+ ## 安装/发版状态(2026-09-11)
172
+
173
+ - **当前形态**:prompt × 1(multi-viewers,开发机已注册可用)+
174
+ extension × 1(multi-viewers-say 插话:零 LLM,直接 spawn human_sayer.py;
175
+ 目录发现 = `<cwd>/mv-<sessionId>-*` 最新,兜底 `mv-*`(排除
176
+ `mv-spec-*`)并警告)
177
+ + wrapper。**npm 已发布 0.1.0(2026-09-11)**。
178
+ - **开发机安装(两步,缺一不可;2026-09-10 实测)**:
179
+ ① `pi install /root/pi-multi-viewers`——**注册包**(写
180
+ `~/.pi/agent/settings.json` 的 `packages` 数组);pi 不是"扫 node_modules
181
+ 就加载",漏这步则命令完全不出现(实测踩过);
182
+ ② `cd ~/.pi/agent/npm && npm install file:/root/pi-multi-viewers
183
+ --legacy-peer-deps`——建 `node_modules/pi-multi-viewers` symlink(prompt
184
+ 里引用的固定路径要靠它可达)+ 把 `file:` 依赖写进 package.json(防后续
185
+ `npm install` prune——手建 symlink 是 extraneous 条目,上游两次实测被清)。
186
+ `pi list` 可查看已注册包。
187
+ - **发版时**参照 pi-agents-helper 成熟路径:`pi install npm:pi-multi-viewers`
188
+ 用户级安装(package.json `pi.prompts` 声明);prompt 路径用固定安装路径
189
+ (只支持用户级,项目级 `.pi/npm/` 下不可达);改动 prompt 后 reload 生效。
190
+ - **核验法**(照上游约定,不用命令行长度判断):
191
+ `readlink -f ~/.pi/agent/npm/node_modules/pi-multi-viewers` 指向仓库根,
192
+ 且该路径下 `scripts/mv.sh` 存在。
package/README.md ADDED
@@ -0,0 +1,153 @@
1
+ # pi-multi-viewers
2
+
3
+ 多视角协同分析(Pi 插件):把主 pi session **fork** 成 N 个视角 agent,
4
+ 各带一份视角任务书(性能/简单化/安全/……),在 meeting 协议下交锋、
5
+ 修正、收敛,产出一份共识结果。
6
+
7
+ 与 [pi-agents-helper](https://github.com/maxdai/pi-agents-helper)(多方
8
+ 讨论达成共识,agent 无主上下文)平行演化;共享 meeting 协议核心
9
+ (core/fs/engine),差异在初始化层。
10
+
11
+ ## 核心机制(2026-09-09/10 实测验证,见 docs/examples/first-experiment)
12
+
13
+ | 机制 | 说明 |
14
+ |---|---|
15
+ | session fork | 首唤由本地循环**生成 fork 源文件**(从主 session 裁剪/折叠,见下表),再用 `pi --session <fork 源> --name <分析名>-<视角名>` 打开——agent 携带发起分析的对话上下文(不是 `pi --fork`:那是全量拷贝且无法在尾部注入切换叙事) |
16
+ | fork 源模式 | `--fork-mode budget`(默认)/ `compaction` / `full`,见下表 |
17
+ | cwd = 主项目 | agent 进程直接读项目文件;work_dir 仅作消息交换区(绝对路径显式指定) |
18
+ | 视角注入 | `--append-system-prompt` ×2(协议 + 视角任务书) |
19
+ | 切换叙事 | fork 源尾部注入 2 对"停止旧任务 → 新任务说明"对话——显式切断历史叙事惯性 |
20
+ | 上下文三层 | fork 历史(自动)+ 主项目文件(自动)+ spec background.md(人工,可选边界约定) |
21
+
22
+ ### fork 源模式(`--fork-mode`)
23
+
24
+ | 模式 | 做法 | 适用 |
25
+ |---|---|---|
26
+ | **budget**(默认) | 按预算(约 80k est,示意值——权威口径见 docs/design.md §二)+ 折叠:丢 thinking、长参数截断、旧工具输出换省略标记 → 从尾部保留 | 长会话**唯一可行**形态 |
27
+ | compaction | 从主 session 最后一个 compaction 边界起:内容原样(不折叠) | 中小会话,零信息损失 |
28
+ | full | 全部条目 | 小会话 / 验证 |
29
+
30
+ **为什么需要 budget(容量事实,2026-09-10 实测)**:fork 携带的是 session
31
+ **原始条目**,而主 pi 实际发送的上下文是被压缩过的(压缩层不在条目里)
32
+ ——本仓库主 session 的原始条目约 930k tokens(口径:消息预算侧实测,
33
+ 2026-09-10;随主会话增长漂移),加模型 384k completion 预留即超 1M 窗口,
34
+ provider 直接 400 拒绝(`pi --fork` 原生命令同样超窗)。budget 把基线压到
35
+ ~80k est,首唤(唤醒 1 首请求)≈132k tokens,可正常进行(e2e 实测:
36
+ 三视角 15–19 分钟完整收敛)。权威口径与测点见 docs/design.md §二。
37
+
38
+ ## 安装
39
+
40
+ **官方方式(npm)**:
41
+
42
+ ```bash
43
+ pi install npm:pi-multi-viewers
44
+ ```
45
+
46
+ **开发机(当前可用方式)**——两步都要做,缺一不可:
47
+
48
+ ```bash
49
+ pi install /root/pi-multi-viewers # ① 注册包(写 ~/.pi/agent/settings.json 的 packages)
50
+ cd ~/.pi/agent/npm && npm install file:/root/pi-multi-viewers --legacy-peer-deps # ② 建 node_modules 符号链接
51
+ ```
52
+
53
+ - ① 让 pi 发现包内资源(prompt 由 `package.json` 的 `pi.prompts` 声明加载)
54
+ - ② 让 prompt 里引用的固定路径 `~/.pi/agent/npm/node_modules/pi-multi-viewers/scripts/mv.sh` 可达;
55
+ 用 `npm install file:` 而非手建 `ln -s`——手建是 extraneous 条目,后续任何
56
+ `npm install` 都会清掉它(上游两次实测教训)
57
+ - **只支持用户级安装**(项目级 `.pi/npm/` 下 prompt 引用的固定路径不可达)
58
+ - 改动 prompt 后 **reload** 生效(pi 从包实时读取;开发机 symlink 下改仓库即生效)
59
+
60
+ 核验(不要用命令行长度判断):
61
+
62
+ ```bash
63
+ readlink -f ~/.pi/agent/npm/node_modules/pi-multi-viewers # 应指向 /root/pi-multi-viewers
64
+ ls ~/.pi/agent/npm/node_modules/pi-multi-viewers/scripts/mv.sh
65
+ ```
66
+
67
+ ## 用法
68
+
69
+ ```
70
+ /multi-viewers "<主题>" # prompt 入口(推荐;视角来自 viewers/)
71
+ /multi-viewers-say "<文本>" # 插话(extension:零 LLM 直接写入 human 消息)
72
+ ```
73
+
74
+ 两个 pi 命令入口(视角/主题走 prompt,插话走 extension——插话是"本地命令
75
+ 执行",不需要经过 LLM)。
76
+
77
+ 或命令行:
78
+
79
+ ```bash
80
+ # 一次性准备:项目 cwd 下建 viewers/<视角名>.md(稳定视角资产,文件名即 agent 名)
81
+ # 写什么见下节「视角文件写什么」;本仓库 viewers/ 下是三个示例,形态可照抄
82
+ ls viewers/
83
+
84
+ # 每次:生成主题骨架(视角自动来自 viewers/*.md)
85
+ scripts/mv.sh --prepare "<主题>" # spec = question.md(+background.md)
86
+ scripts/mv.sh --start <spec目录> # 启动(自动挂载主 session;默认 budget 模式)
87
+ # 可选:--fork-mode compaction|budget|full(见上表;一般不调)
88
+ # 高级:--agents "a,b" 起一次性视角(不建 viewers/ 时用;prompt 入口不传它)
89
+
90
+ # 观看:--start 会输出可直接执行的 !! 流式观看命令(复制执行)
91
+ scripts/mv.sh --view <dir> # 或一次性增量查看(主 pi 记录 HEAD 作下轮 --since)
92
+
93
+ # 插话 / 状态 / 收尾
94
+ scripts/mv.sh --say <dir> "<文本>" # 插话(命令行形态;pi 内用 /multi-viewers-say)
95
+ scripts/mv.sh --status <dir> # running / done / stalled / stopped
96
+ scripts/mv.sh --report <dir> # 只读报告(流程/配额/进程/LLM;冷路径,不持久化)
97
+ scripts/mv.sh --cleanup <dir> # 收尾(result.md 自动留存到 <dir>-result.md)
98
+ ```
99
+
100
+ ## 视角文件写什么(`viewers/<视角名>.md`)
101
+
102
+ 一个视角文件 = **一份视角说明**,纯内容、无格式要求(无 frontmatter、
103
+ 无需标题,**文件名就是全部元数据**)。三个要点(措辞经实验验证):
104
+
105
+ 1. **单一 lenses**——写清这个 agent 用什么角度看(性能 / 简单化 / 安全 /
106
+ 成本 / 用户体验 / ……),并要求"所有观点必须从该视角出发"
107
+ 2. **不越界**——写明"其它视角由别的参与者负责,你不要越界展开"
108
+ (**不要**列举具体是哪几个视角——参与者会变,列举就会过期)
109
+ 3. **交锋义务**——写明"对其它视角的观点可以认同或反驳,但要用本视角的论据"
110
+
111
+ **只写视角本身**。以下由脚本从机制生成,**不要写进视角文件**(写进去必然
112
+ 重复,且会与实际漂移):身份("你是 X")与参与者名单、消息格式与
113
+ frontmatter 字段、写文件路径、独立参与者纪律。
114
+
115
+ ```
116
+ 你是多视角分析中的"性能视角"参与者(agent 性能)。 ← ❌ 不要(脚本按文件名注入)
117
+ 你的所有观点必须从性能角度出发:复杂度、热点…… ← ✅ 视角内容
118
+ ```
119
+
120
+ **规范**:≥2 个视角、内容非空、名字不含空白与路径分隔符、非 `human`、
121
+ ≤32 字符——不合规在生成 spec 前就报错(零产物)。
122
+ 视角之间**互补或对立都可以**,对立产生的分歧正是多视角分析的价值。
123
+
124
+ ### 复用的三种方式(都不需要把视角写进命令行)
125
+
126
+ | 想做的事 | 做法 |
127
+ |---|---|
128
+ | 长期复用 | 写好 `viewers/X.md`——每次分析自动带上 |
129
+ | 这一次想调 | `--prepare` 之后、`--start` 之前改 **spec 的 `agents/X.md` 快照**(改内容 / 删掉某个视角 / 加一个临时视角——删文件即剔除该参与者),不动资产 |
130
+ | 完全一次性(项目还没建 viewers/) | `mv.sh --prepare "<主题>" --agents "a,b"`(wrapper 高级用法) |
131
+
132
+ ## 架构
133
+
134
+ ```
135
+ meeting_core.py 纯判定(冻结级联/RR/聚合)
136
+ meeting_fs.py git 层
137
+ meeting_engine.py 唯一状态机(六分支)
138
+ meeting_loop.py Pi 薄壳(fork 首唤 + --session-id 续接 + 视角注入)
139
+ start_discussion.py 环境生成/启动/状态/清理
140
+ human_viewer/sayer human 插话通道
141
+ ```
142
+
143
+ ## 开发
144
+
145
+ ```bash
146
+ ./tests/run_tests.sh # 全量(~280s)
147
+ ./tests/run_tests.sh --reuse # 指纹未变跳过
148
+ ```
149
+
150
+ 设计文档:`docs/design.md`(fork 源模式与规模口径、决策记录)。
151
+ 开发铁律与测试方法论:`AGENTS.md` + `docs/test-methodology.md`。
152
+ 首次实验存档:`docs/examples/first-experiment/`。
153
+ 自我审阅存档:`docs/reviews/`(本机制审阅自身实现的报告原文)。
package/docs/design.md ADDED
@@ -0,0 +1,288 @@
1
+ # pi-multi-viewers 设计文档
2
+
3
+ 多视角协同分析:把主 pi session **fork** 成 N 个视角 agent,各带一份视角
4
+ 任务书,在 meeting 协议下交锋、收敛,产出共识结果。
5
+
6
+ 与 [pi-agents-helper](https://github.com/maxdai/pi-agents-helper) 平行演化,
7
+ 共享 meeting 协议核心(`meeting_core` / `meeting_fs` / `meeting_engine`),
8
+ 差异集中在**初始化层**(fork 源生成 + 视角注入 + cwd)。协议行为定义
9
+ (信息层/流程层分离、配额语义、状态机推演)见上游设计文档
10
+ `../pi-agents-helper/docs/pi-helper-design.md`,对本项目同样有效。
11
+
12
+ ---
13
+
14
+ ## 一、fork 源:三种模式
15
+
16
+ 首唤不用 `pi --fork`(全量拷贝、且无法在尾部注入切换叙事),而是由本地
17
+ 循环**生成 fork 源文件**(`meeting_fs.build_fork_source`),再用
18
+ `pi --session <fork 源> --name <分析名>-<视角名>` 打开。
19
+
20
+ > **下列产物数字的锚**(口径要求见 §二):产物侧,2026-09-10,本仓库
21
+ > 主 session(≈6.8k 条 / 15MB)。**条数/MB 随主会话增长漂移**(每次跑值
22
+ > 不同),故本节数字只作量级示意;消费侧数字(tokens)一律带唤醒序号。
23
+
24
+ | 模式 | 做法 | 产物量级(锚见上) | 适用 |
25
+ |---|---|---|---|
26
+ | `budget`(默认) | 从 compaction 边界起 + 折叠(丢 thinking、旧工具输出换省略标记、长参数截断)+ 按预算从尾部保留,更早的丢弃并写「上下文说明」preface | ≈0.8k 条 / ≈0.9 MB;`est` 以**预算为上界**(preface 计入、边界回扩可
27
+ 略上浮——精确式见 §二),丢弃数记在 `forkSourceDropped` | 长会话**唯一可行** |
28
+ | `compaction` | 从最后 compaction 的 `firstKeptEntryId` 起,内容原样(不折叠) | ≈2.9k 条 / ≈5.7 MB | 中小会话(零信息损失) |
29
+ | `full` | 全部条目(源会话的**忠实拷贝**,含其 compaction 条目与可见性
30
+ 边界——我们不做窗口构造、不改写历史) | ≈6.8k 条 / ≈15 MB | 小会话 / 验证 |
31
+
32
+ **replay 可见性(实测,2026-09-10)**:pi 的 replay 取"路径上最后一个
33
+ compaction 的 `firstKeptEntryId` 起 + 其后的条目"——窗口内含 compaction
34
+ 时,锚点之前的条目(含我们的 preface)会被静默丢弃。因此 budget 模式
35
+ **移除窗口内全部 compaction 并桥接 parentId**(不变量 I4);compaction
36
+ 模式的锚点由构造保证在产物内;full 模式是忠实拷贝,可见性同源会话。
37
+
38
+ 无 compaction 的源(如引导 session):`compaction` 全量兜底(标记 `full`),
39
+ `budget` 仍跑折叠与统计(干净源下几乎无操作)。
40
+
41
+ ### 为什么需要 budget(容量事实,2026-09-10 实测)
42
+
43
+ - fork 携带的是 session **原始条目**;主 pi 实际发送的上下文由压缩层在
44
+ **渲染时**生成(不在条目里)——同一 session:原始条目文本 ≈930k tokens
45
+ (消息预算侧,2026-09-10),而主 pi 每次请求 ≈185k–237k tokens
46
+ (唤醒序号不适用;随主会话增长)。
47
+ - 模型窗口 1M,每次请求含 384k completion 预留 → 消息预算 ≈664k tokens。
48
+ - 实测:`full` 首请求(唤醒 1)1,303,280 tokens、`compaction` 934,630
49
+ tokens → 均被 provider 400 拒绝;`pi --fork` 原生命令同样超窗(731,331)。
50
+ - `budget` 首唤(唤醒 1 首请求)≈132k tokens → 讨论完整收敛
51
+ (3 视角 / 15–19 分钟;两次 e2e 实测)。
52
+
53
+ ---
54
+
55
+ ## 二、规模口径(单一事实源;README、AGENTS.md、本文件 §一 三处均引本节)
56
+
57
+ 1. **产物侧指纹**(header 自描述,`meeting_fs.read_fork_stats` 单点解析):
58
+ - `forkSourceTokensEst`:同一基准构建的确定性结果(三 agent 恒同值)
59
+ ——用于校验"产物是否同源 / 裁剪是否符合预期",**不预测请求规模**;
60
+ 字符数 / 3 估算,字段名带 `Est` 即为提醒。**以预算为上界**(非"钉住
61
+ 值"):`est ≤ max(预算, est(末条)) + Σ est(回扩条目) + est(preface)`;
62
+ 工程余量 ~53 万 tokens(消息预算 664k 量级),无需为此加保护。
63
+ - `forkSourceDropped`:**双口径合计** = 预算裁剪丢弃数 + 结构规范化移除数
64
+ (移除窗口内 compaction 条目——见 §一 与不变量 I5);**仅 `budget`
65
+ 模式写该字段**(compaction/full 不做预算裁剪,header 无此键——读者
66
+ 勿以为三模式皆有)。日志与验收用(丢弃数为 0 而规模远超预算 = 异常
67
+ 信号)。产物须闭合:源保留区条目数 = 产物非 preface 条目数 +
68
+ `forkSourceDropped`。
69
+ 2. **消费侧规模(验收/成本基准)**:**唤醒 1 的第一次请求** `input +
70
+ cacheRead`(含系统提示与工具定义)。引用必须带唤醒序号,否则数字不可比。
71
+ 3. **校准比(est → 真实)**:唤醒 1 ≈ **1.66×**(e2e10 三点独立样本:
72
+ 1.656 / 1.657 / 1.659,±0.2%);唤醒中后期至 ~2.7×——**不得固化为
73
+ 2.0×**(不同测点值不同)。
74
+ 4. **预算只约束"基线"**:`budget` 管的是 fork 源;**运行规模随该 agent 会话
75
+ 累积增长**(实测 w1 132k → w5 192–207k;满程外推 290–350k,标注为外推)。
76
+ 容量估算不得用 "est × agent 数 × 轮数"。
77
+ 5. **预计值不得用作验收口径**:日志中的派生数字只是可读性便利。
78
+
79
+ ### 观测面契约(可观测性)
80
+
81
+ > 原则(e2e14 自审裁定):**日志是纯信息层**——零判定输入;观测面的
82
+ > 格式/消费者/成本必须显式成文(此前 `loop-*` / `wake-logs` 在
83
+ > `design.md` / `README.md` grep **零命中**,"有没有时间戳"要读源码才能
84
+ > 回答——这本身就是症状)。
85
+
86
+ ### 三域 owner(事实源边界)
87
+
88
+ | 域 | 事实 | 性质 | 取数 |
89
+ |---|---|---|---|
90
+ | bare(git) | 消息 / frontmatter / 协议 / 推进节奏 | **判定域** | 现场派生(无状态) |
91
+ | loop log | 进程事实(唤醒 / 完成 / 超时 / rc) | **无家** | **就地捕获**(零解析) |
92
+ | pi session | LLM 运行事实(usage / stopReason / 时间戳) | 有家(文档化 schema) | **冷路径读**(单一适配器) |
93
+
94
+ ### 观测面登记
95
+
96
+ | 面 | owner | 格式 | 消费者 | 参与判定 | 成本档 |
97
+ |---|---|---|---|---|---|
98
+ | `loop-<agent>.log` | loop + engine(stdout 重定向) | `[YYYY-MM-DDTHH:MM:SS.mmm] <agent>: <msg>` | 人(grep/肉眼)+ `--report`(**仅登记字段**) | **否** | O(1) 捕获 |
99
+ | `wake-logs/<agent>-<epoch>.txt` | loop | `CMD: <shlex.quote 单行>` | 人(排错第一手段) | 否 | O(prompt) |
100
+ | `status-<agent>.json` | loop | `{"sessionID": ...}` | 流程(崩溃恢复) | 是(恢复用) | O(1) |
101
+ | `pi-sessions/fork-src-*.jsonl` | pi | 文档化 session schema | fork 构建 + `--report` | 否(报告用) | O(MB) 全量 → **禁轮询** |
102
+ | `result.md`(固定位) | resultWriter loop | 结论文档 | 人 | 是(收尾判据) | — |
103
+ | `--report`(视图) | start_discussion | 文本行 | 人/主 pi | **否**(不得升级为验收 gate) | 冷路径一次性 |
104
+
105
+ ### 本轮边界(`mv.analysis-start`)
106
+
107
+ fork 源尾部在切换叙事之后追加一条 `custom_message` 边界条目(`meeting_fs.
108
+ BOUNDARY_TYPE`)——**显式登记"历史(fork 携带)/ 本轮"的分界**。
109
+
110
+ 为什么必须显式:`--report` 的 LLM 段要统计**本次分析**的 usage,而 session
111
+ 文件里同时含 fork 携带的历史条目(也有 assistant + usage)。按条数/时间戳
112
+ 推断都会漂移(切换叙事改措辞、时钟精度)。2026-09-11 实测:不带边界时报告
113
+ 把 717 条 fork 历史算成"本轮 367 次响应 / input 1.2M / cacheRead 136.6M"。
114
+
115
+ **报告的打印位置**:`--report`(手动,任意时刻)+ `--cleanup` 前(自动,
116
+ 删目录前最后一次可读——目录删后 `--report` 不可用)。cleanup 层对报告
117
+ fail-open(报告失败不阻断清理,且**打印**失败原因不静默)。
118
+
119
+ 消费规则:`meeting_fs.iter_after_boundary` 只产出边界之后的条目;**未找到
120
+ 边界(老产物/手工 session)→ 返回空、按 n/a 处理,不得退回全文扫描**
121
+ (那正是修掉的口径错误)。pi 对 `custom_message` 条目的容忍已冒烟验证。
122
+
123
+ ### 登记字段(无家就地捕获)
124
+
125
+ 唤醒完成行追加两个字段——**只有这里**产出,`--report` 是唯一读者:
126
+
127
+ - `elapsed_ms=<int>`:**跨度 = pi 进程生命周期**(spawn → exit,monotonic 差值);
128
+ - `rc=<int>`:进程返回值,**总是写**(超时/被 kill 路径不写 = 缺席)。
129
+
130
+ ### 不变量与纪律
131
+
132
+ 1. **日志零判定输入(绝对)**:全仓无一处解析日志内容参与流程判定
133
+ (唯一例外曾为 `--wait` 的 `glob(loop-*.log)` 存在性分叉,已删除)。
134
+ 2. **缺席 ≠ 0**:观测拿不到的显示 `n/a`,绝不允许把"没测到"写成 0。
135
+ 3. **一个数字一个口径**:进程跨度(`elapsed_ms`)≠ per-response 跨度
136
+ (session 时间戳差)≠ 墙钟跨度(commit 时间差),必须标名、不得混算。
137
+ 4. **取数判据"家有无"**:① 无家 → 登记(给它造家);② 有家且读它不需
138
+ 越界假设(文档化字段)→ 复用;③ 有家但只能靠未文档化的私有细节读出
139
+ → 按无家处理。配套:非判定性 / 可降级性(删产物行为不变)/ 成本档。
140
+ 5. **成本档准入**:O(1) 捕获鼓励;O(小) 冷路径允许;O(MB) 全量解析
141
+ **禁轮询/常驻**,仅冷路径按需。
142
+
143
+ ### 明确不做(及理由,e2e14 §4)
144
+
145
+ 不做第二持久化面(信息不减就不新增数据面)、不做心跳文件/常驻监控进程
146
+ (可从 `/proc` + HEAD 派生;第二事实源 → 双写/残留/一致性成本)、不在
147
+ 轮询路径消费 MB 面、不做运行期 LLM 评分(有效性判断留给人 + result.md)、
148
+ 不做目录内 retention(cleanup 是唯一清理点)、message frontmatter 不加
149
+ 时间戳(第二事实源 + 该字段由 LLM 写,不可信;权威时间 = commit 时间)、
150
+ 日志不 JSON 化(主消费者是人)、报告不自动落固定位(视图不占"家")。
151
+
152
+ ## 数字的归宿(一个数字只留一个"家")
153
+
154
+ | 类型 | 例 | 去处 |
155
+ |---|---|---|
156
+ | **结构性质**(不随数据漂移) | "批量读一次进程 / 逐条读 O(n) 子进程" | **docstring**(随函数迁移,不得丢失) |
157
+ | **修复依据的实测值** | 733ms/43.6ms、16.8×、1054.6ms→1.0ms | **commit message**(带口径 + 来源) |
158
+ | **长期可复用的口径数字** | 构建 181ms/55MB、pi 会话 RSS ~1GB、校准比 1.66× | **本节**(design.md 口径) |
159
+
160
+ 判据:**会在下一次评审中被引用吗?** 会 → 本节;只解释本次为何这样改 → commit。
161
+ commit 是溯源记录、本节是长期引用点——不并存两份权威值(长期引用点漂移时
162
+ 就地更新带新口径)。
163
+ **术语注意**:上表第一类**不称"不变量"**——本仓"不变量"已专指 fork 源产物
164
+ 结构的 I1–I5(docstring / 设计文档 / 测试名三处对齐),一词两义会造成歧义。
165
+
166
+ ### 数字的四个类别(每个数字必须能回答"它是什么")
167
+
168
+ | 类别 | 例 | 要求 |
169
+ |---|---|---|
170
+ | 配置派生 | 消息预算 664k = 1M − 384k | 附推导式与重估触发 |
171
+ | 实测 | 132.2–132.4k、1.66× | 附口径与测点 |
172
+ | 外推 | 满程 290–350k | 附依据与触发 |
173
+ | 事后拟合关系 | "基线 ≤ 20%×(窗口−预留)" | 标注为事后归纳(见下) |
174
+
175
+ ### 预算取值的依据来源(honest attribution)
176
+
177
+ - **初版取值来源**:按"对齐 MC 在主会话保留的未丢弃量(88k)"设定——该
178
+ 判断后证为**刻度混淆**(88k 是真实 token,est 应对应 ≈53k)。
179
+ - **现行重估公式**:`基线 ≤ 约 20% ×(模型窗口 − 输出预留)`——当前实例
180
+ 80k est ≈ 132k 真实 ≈ 664k 的 20%。**该公式是事后归纳,不是原始设计
181
+ 目标**,用于将来窗口/预留变化时的重估,不用于追溯解释当初取值。
182
+ - 参考点:pi 自身默认 compaction 为 `keepRecentTokens=20000` /
183
+ `reserveTokens=16384`(`DEFAULT_COMPACTION_SETTINGS`)——讨论 agent 需要
184
+ 更宽的近期窗口,故取更高的量级。
185
+
186
+ ---
187
+
188
+ ## 三、决策记录
189
+
190
+ ### 已定(关键项)
191
+
192
+ 1. **fork-only**:唯一模式;缺 fork 源 = 三层 fail-fast,无静默退化。
193
+ 2. **fork 源由本地生成**(不用 `pi --fork`):可在尾部注入切换叙事,
194
+ 且形态可控(模式/统计自描述)。
195
+ 3. **切换叙事**:fork 源尾部注入 2 对对话("停止旧任务" → assistant 询问
196
+ → 新任务说明 → assistant 确认)——用最后一段对话切断历史叙事惯性。
197
+ 主题取自 `protocol.json.topic`(**不**二次解析 question.md)。
198
+ 4. **budget 为默认模式**(长会话唯一可行;compaction/full 留作中小会话与验证)。
199
+ 5. **模型引用按契约拼接**(provider + id 两字段,无条件拼接)——不按值的
200
+ 形状猜(形状启发式会把 provider 丢掉,静默解析到同名模型)。
201
+ 6. **viewers/ 稳定视角资产**:`--prepare` 快照进 `spec/agents/`(可按场改),
202
+ 文件名即 agent 名(中文合法),排序定 starter/RR/resultWriter,≥2 视角。
203
+ **视角文件只写视角内容**——身份("你是 X")、参与者名单、消息格式、
204
+ 独立纪律都由脚本从 agent 名生成(agent 名 = 文件名,单一来源;手写
205
+ 身份必然与文件名漂移);**空视角任务书拒绝启动**(无 lenses 的 agent
206
+ 会让多视角退化成同名随机视角)。
207
+ 7. **协议单一来源 = `bare HEAD:protocol.json`**(`meeting_fs.read_protocol`
208
+ 唯一实现):engine/loop/viewer/status 全部经它读取,**不读 workdir 本地
209
+ 副本**——本地副本是 LLM 可写的工作副本,判定读本地等于把流程判定暴露给
210
+ 被审查者。附带修掉 `result_writer` 的默认值求值缺陷(原实现
211
+ `proto.get("resultWriter", participants(workdir)[-1])` 的第二参数无条件
212
+ 求值:每次读两遍协议,且 participants 为空时抛 IndexError——即使
213
+ resultWriter 已配置)。
214
+ 8. **状态判定复用状态机定义**:`check_status` 的 concluded 判定调
215
+ `meeting_engine.aggregate_mode`(core 单一判定),**不用 `git grep` 全文
216
+ 匹配**——行文本匹配会被 result.md / 消息正文里的 `type: concluded`
217
+ 误触发(实测误报 done → `--wait` 落无上界轮询)。
218
+ 9. **插话走 extension(零 LLM)**:`/multi-viewers-say <文本>` =
219
+ `registerCommand` handler 直接 spawn `human_sayer.py`(一次调用一次返回),
220
+ 结果经 `ctx.ui.notify` 反馈——**不经过 LLM**(插话本质是本地命令执行;
221
+ 经 LLM 会引入不确定性与额外延迟)。目录发现零状态文件:
222
+ `ctx.cwd` + `sessionManager.getSessionId()` → `mv-<sid>-*` 最新
223
+ (session 隔离);无 sid 目录时兜底项目下最新 `mv-*`(排除
224
+ `mv-spec-*`)并**警告降级**
225
+ (宁可提示也不静默插错分析)。观看仍用 `!!` 流式(命令 API 无原生流式
226
+ 通道,bash 流式是平台原生能力)。
227
+ 10. **git 守卫范围 = 从讨论 workdir 发起的操作**(`GIT_CEILING_DIRECTORIES`
228
+ 注入于 spawn);主项目仓库不在守卫范围(agent 的 cwd 就是主项目,其
229
+ 约束归指令层 + 主项目 `.gitignore`)。要拦主仓库需换机制类(沙箱/钩子),
230
+ 经评估收益不支撑扩面。
231
+
232
+ ### 被否决方案(含重估触发条件)
233
+
234
+ | 方案 | 否因 | 重估触发 | 所在位置 |
235
+ |---|---|---|---|
236
+ | **省一次重读**(首唤时把 tail id 从 `build_fork_source` 传给 `append_handoff_turns`) | ①收益仅 0.011s(budget 产物;full 产物 134ms + 37MB 峰值);②方案自败(保留重读兜底则被指瑕疵的推导代码一行未减);③新增静默失败面(tail id 可能过期 → 接错节点);④把 session 格式知识泄漏到 loop 层。同层替代已评估未采纳:`append_handoff_turns` 只保留尾行(不跨层传状态、不新增失效面)——量级 0.4s vs ~130s/唤醒(0.3%) | 源 ≥100MB 或 N≫3(内存 ≈2.5×文件大小×N) | `meeting_fs.append_handoff_turns` / `meeting_loop` 首唤路径 |
237
+ | **共享 budget 基座**(三 agent 共用缓存) | 引入持久状态 + 失效规则 + 跨进程原子写/清理义务,与"单一事实源/确定性归 loop/无静默"冲突;收益 ≈181ms×3(本机、15.3MB 源实测;旧记录写 ≈0.28s×3——口径不可考,两者差 55%,按修订记录并列不静默替换),相对 ~130s/唤醒可忽略 | N≫3 且会话至 100MB 量级 | `meeting_loop` 首唤路径 |
238
+ | **裁剪改流式 / 环形缓冲** | 收益 = 内存峰值 +37MB **与解析时间**(实测 `json.loads` 占构建耗时 **49%**、约 90% 解析条目最终被预算弃用)——两者都随源规模线性增长;会扩大"先折叠再裁"不变量的证明面 | 源规模使峰值内存或解析耗时成为实际瓶颈时(观测点:首唤日志的构建耗时/峰值 RSS) | `meeting_fs._budget_entries` |
239
+ | **两阶段裁剪**(先廉价估算定窗,再只折叠保留区) | 收益 ≈7ms,为可忽略收益引入复杂度 | 折叠成本成为可测瓶颈(当前 0.01s/2788 条) | `meeting_fs._budget_entries` |
240
+ | **预算提前配置化**(进 protocol/spec) | 灵敏度低:每 10k est ≈ 2.5% 消息预算;80k→53k 仅省 6.6%,代价是保留窗口缩短;配置面成本(每个读者须知其存在/语义/边界) | 出现明确的"按讨论调预算"需求 | `meeting_fs` 常量块 |
241
+ | **改写锚点**(把被裁掉/被移除的 compaction 的 `firstKeptEntryId` 批量改写为窗口内条目) | 语义上伪造历史字段(锚点是 pi 写的记录,不是我们的);且中间锚仍会悬空——**已撤回**(其测量 0.07ms/0.27ms、est 恒等**不并入成稿**) | 出现必须让所有历史锚都可解析的消费者时 | `meeting_fs` compaction/budget 边界 |
242
+ | **保留 `_join_model_ref` 幂等特判** | 与"不得按形状猜"自相矛盾;对"`<provider>/` 开头"的命名空间 id 会少拼 provider(同类误判仍在) | 出现按契约必须传完整 ref 的来源时 | `start_discussion._join_model_ref` |
243
+
244
+ ### 结构拆分的触发条件(当前不拆)
245
+
246
+ `meeting_fs` 的 fork 源区块现为一个章节,实际包含 11 个函数:
247
+ `read_fork_stats` / `_est_tokens` / `_entry_text` / `_shrink_value` /
248
+ `_fold_entry` / `_budget_entries` / `build_fork_source` /
249
+ `append_handoff_turns`,以及同章的引导/回流三函数 `_registry_log` /
250
+ `build_bootstrap` / `preserve_result_md`。
251
+ (列举集合须同批对照——文档"列举集合"与实际集合失配是 e2e10 评审发现的
252
+ 一类问题;完整制度见 docs/test-methodology.md。)
253
+ 满足任一条件时再拆为独立模块(或 `meeting_fork.py`):
254
+
255
+ - 引入**摘要生成**(对丢弃区做 LLM 摘要,而非只带 compaction summary)
256
+ - 引入 **MC 增强**(读 Magic Context 的 compartments 提升旧史摘要质量)
257
+ - **预算可配置**(模式参数化到 spec/protocol)
258
+
259
+ ---
260
+
261
+ ## 四、记录项(不修;每项必带**现在就能用的观测点**)
262
+
263
+ | 项 | 实测/性质(口径) | 触发条件(观测点) |
264
+ |---|---|---|
265
+ | 三处冗余 pass(`_entry_text` 双算 / dropped 全量重算 / `_shrink_value` 先拷贝再比较) | 合计 ~11ms = 构建的 **6%**(本机、15.3MB 源) | 首唤日志已含**构建耗时与峰值 RSS**(`fork 源(… 构建 181ms/55MB)`)→ 构建 >1s 时复测占比(占比是插桩型判据,非监控) |
266
+ | O(源) 内存(全量 `entries` + `folded` 驻留) | 15.3MB 源 → RSS 峰值 **55MB**(≈3.6×);3 loop 并发 ≈165MB | 同一日志的 RSS 字段 >~500MB,或源 >~100MB |
267
+ | **pi 会话常驻内存(fork 场景主导项)** | 实测 **~1GB/个**(935 / 1157 / 954MB,`ps -o rss`;本机 16GB、当时会话长度含扩展)——比 loop 侧高一个量级,容量规划须以它为准 | N≥8 或内存紧张时复核(`ps -o rss` 逐 pi 进程) |
268
+ | 每 agent 各自构建一次 fork 源(不共享) | ≈181ms×3;三 loop 是独立进程,共享需跨进程协调/失效/原子写义务(与「共享 budget 基座」否决同因) | N≫3 且会话至 100MB 量级 |
269
+ | `_est_tokens` 对空文本返 1 | 量级 <0.01%;由 I4(集合身份)消解——est 是集合近似指纹而非数值承诺 | 若将来把 est 用作容量硬判据 |
270
+
271
+ **口径纪律**(方法 14 扩展):改数字先判“漂移 vs 复测”;**无锚旧值按
272
+ 修订记录处理**(并列旧值与其口径不可考,不静默替换)。首个实例:
273
+ 「共享 budget 基座」行的收益由 ≈0.28s×3 修订为 181ms×3。
274
+
275
+ ---
276
+
277
+ ## 五、已知边界与未覆盖
278
+
279
+ - `compaction` / `full` 两模式在长会话下的实跑行为未验证(分析中仅用
280
+ `budget`)——`full` 已知超窗,`compaction` 在中小会话可用。
281
+ - MC 增强路线未实现:fork 源**构建**不依赖任何扩展(纯 pi 语义 + 预算 +
282
+ 折叠);**运行期**上下文受环境扩展(如已安装 MC)的渲染期裁剪影响
283
+ (实测:三份 fork 会话各有 67–70 条被 MC 丢弃的旧内容);未装扩展时的
284
+ 轨迹取决于 pi 核心 compaction(本环境未观测)。
285
+ - 预算取值 80k vs 53k 的**产出质量对比未做**:质量无单点判据、N=3 欠功率。
286
+ 若重启该实验,须**先登记判据**(可机械核查:覆盖 question.md 评审项数、
287
+ 给出 `file:line` 次数、是否达配额)与比较单位(per-agent / per-wake)。
288
+ - 图片块与非字符串叶子在预算估算中的计入未覆盖(当前余量充足)。