pi-multi-viewers 0.2.1 → 0.3.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.
- package/AGENTS.md +3 -2
- package/README.md +13 -6
- package/docs/design.md +42 -2
- package/docs/reviews/2026-09-11-e2e16-mechanization-review.md +177 -0
- package/docs/reviews/README.md +1 -0
- package/docs/test-methodology.md +86 -0
- package/extensions/multi-viewers-say/index.ts +39 -54
- package/fake_agent.py +11 -3
- package/human_viewer.py +3 -8
- package/meeting_fs.py +15 -0
- package/mv_cli.py +363 -0
- package/observability.py +81 -2
- package/package.json +1 -1
- package/prompts/multi-viewers.md +10 -8
- package/scripts/mv.sh +10 -318
- package/start_discussion.py +7 -1
package/AGENTS.md
CHANGED
|
@@ -23,7 +23,8 @@ spec_gen.py spec 生成层(question/骨架/viewers 校验与快照/ag
|
|
|
23
23
|
observability.py 观测层(check_status / --report / --wait / loop 存活检测)
|
|
24
24
|
human_viewer.py 【human 通道】只读展示(增量/--follow/游标)
|
|
25
25
|
human_sayer.py 【human 通道】插话命令(单次/stdin/交互 -i)
|
|
26
|
-
scripts/mv.sh
|
|
26
|
+
scripts/mv.sh 稳定入口 shim(exec mv_cli.py;路径被 prompt/README 引用)
|
|
27
|
+
mv_cli.py 命令行实现(prepare/start/status/report/wait/cleanup/view/say)
|
|
27
28
|
prompts/multi-viewers.md /multi-viewers 入口(视角设计三原则 + 审核闸门)
|
|
28
29
|
extensions/multi-viewers-say/ /multi-viewers-say 插话(registerCommand,零 LLM)
|
|
29
30
|
docs/design.md 设计文档(fork 源模式与规模口径 + 决策记录)
|
|
@@ -176,7 +177,7 @@ loop、状态从 git 共享事实推导、单一事实源 = protocol.json、无
|
|
|
176
177
|
extension × 1(multi-viewers-say 插话:零 LLM,直接 spawn human_sayer.py;
|
|
177
178
|
目录发现 = `<cwd>/mv-<sessionId>-*` 最新,兜底 `mv-*`(排除
|
|
178
179
|
`mv-spec-*`)并警告)
|
|
179
|
-
+ wrapper。**npm 已发布 0.
|
|
180
|
+
+ wrapper。**npm 已发布 0.3.0(2026-09-12)**。
|
|
180
181
|
- **开发机安装(两步,缺一不可;2026-09-10 实测)**:
|
|
181
182
|
① `pi install /root/pi-multi-viewers`——**注册包**(写
|
|
182
183
|
`~/.pi/agent/settings.json` 的 `packages` 数组);pi 不是"扫 node_modules
|
package/README.md
CHANGED
|
@@ -74,6 +74,12 @@ ls ~/.pi/agent/npm/node_modules/pi-multi-viewers/scripts/mv.sh
|
|
|
74
74
|
两个 pi 命令入口(视角/主题走 prompt,插话走 extension——插话是"本地命令
|
|
75
75
|
执行",不需要经过 LLM)。
|
|
76
76
|
|
|
77
|
+
**目录可以省略**:`--view`/`--say`/`--status`/`--report`/`--wait`/`--cleanup`
|
|
78
|
+
不带目录时自动定位"本 session 当前分析"(`mv-<sessionId>-*` 最新;找不到
|
|
79
|
+
则取最新 `mv-*` 并警告;判据 = 含 `repo.git`)。传目录仍支持(显式优先)。
|
|
80
|
+
这样路径不需要经过任何 LLM 记忆——此前命令都要求绝对路径,等于让主 pi
|
|
81
|
+
把长路径记在上下文里复用。
|
|
82
|
+
|
|
77
83
|
或命令行:
|
|
78
84
|
|
|
79
85
|
```bash
|
|
@@ -88,15 +94,16 @@ scripts/mv.sh --start <spec目录> # 启动(自动挂载主 sessi
|
|
|
88
94
|
# 高级:--agents "a,b" 起一次性视角(不建 viewers/ 时用;prompt 入口不传它)
|
|
89
95
|
|
|
90
96
|
# 观看:--start 会输出可直接执行的 !! 流式观看命令(复制执行)
|
|
91
|
-
scripts/mv.sh --view
|
|
97
|
+
scripts/mv.sh --view # 一次性增量查看(主 pi 记录 HEAD 作下轮 --since)
|
|
92
98
|
# --follow 会打印【状态】(meeting/all-freezing/round-robin/concluded)
|
|
93
99
|
# 与【进度】(meeting 消耗/上限 | freezing 集合 | rr → 下一位)
|
|
100
|
+
# 结束时自动附【分析报告】(消息/墙钟/配额/进程跨度/LLM 用量)
|
|
94
101
|
|
|
95
|
-
# 插话 / 状态 /
|
|
96
|
-
scripts/mv.sh --say
|
|
97
|
-
scripts/mv.sh --status
|
|
98
|
-
scripts/mv.sh --report
|
|
99
|
-
scripts/mv.sh --cleanup
|
|
102
|
+
# 插话 / 状态 / 收尾(目录可省略——自动定位本 session 当前分析)
|
|
103
|
+
scripts/mv.sh --say "<文本>" # 插话(命令行形态;pi 内用 /multi-viewers-say)
|
|
104
|
+
scripts/mv.sh --status # running / done / stalled / stopped(done 时附 [result] 路径)
|
|
105
|
+
scripts/mv.sh --report # 只读报告(流程/配额/进程/LLM;冷路径,不持久化)
|
|
106
|
+
scripts/mv.sh --cleanup # 收尾(result.md 自动留存到 <dir>-result.md)
|
|
100
107
|
```
|
|
101
108
|
|
|
102
109
|
## 视角文件写什么(`viewers/<视角名>.md`)
|
package/docs/design.md
CHANGED
|
@@ -100,7 +100,7 @@ compaction 的 `firstKeptEntryId` 起 + 其后的条目"——窗口内含 compa
|
|
|
100
100
|
| `status-<agent>.json` | loop | `{"sessionID": ...}` | 流程(崩溃恢复) | 是(恢复用) | O(1) |
|
|
101
101
|
| `pi-sessions/fork-src-*.jsonl` | pi | 文档化 session schema | fork 构建 + `--report` | 否(报告用) | O(MB) 全量 → **禁轮询** |
|
|
102
102
|
| `result.md`(固定位) | resultWriter loop | 结论文档 | 人 | 是(收尾判据) | — |
|
|
103
|
-
| `--report`(视图) | observability | 文本行 | 人(**三个出口**,见下) | **否**(不得升级为验收 gate) | 冷路径一次性 |
|
|
103
|
+
| `--report`(视图) | observability | 文本行 | 人(**三个出口**,见下) | **否**(不得升级为验收 gate) | 冷路径一次性 —— **O(session 大小)**:每 agent 读整个 fork-src jsonl(实测 3 × 789KB ≈ 2.4MB/次、50–150ms/次,×3 出口 <0.3s/次分析),**不得进入任何轮询路径**(e2e16 评审量化) |
|
|
104
104
|
|
|
105
105
|
**报告的三个出口**(同一 `build_report`,同一份内容):
|
|
106
106
|
1. **`--follow` 结束**——viewer 在 done 分支自动附报告(`human_viewer._print_report`)。
|
|
@@ -282,7 +282,47 @@ commit 是溯源记录、本节是长期引用点——不并存两份权威值
|
|
|
282
282
|
流程依赖 LLM(会漏、不可验收),正是本项目一贯要消除的形态;报告既然
|
|
283
283
|
是给用户的,就该长在用户直接看的通道上。`--view --since` 不附(主 pi
|
|
284
284
|
通道,进 context 且对模型无用)。
|
|
285
|
-
15.
|
|
285
|
+
15. **消费命令的目录可省略(自动发现,仅精确 sid 匹配)**:`--view/--say/
|
|
286
|
+
--status/--report/--wait/--cleanup` 不带目录 → 按 cwd + `PI_SESSION_ID`
|
|
287
|
+
发现当前分析(`mv-<sid>-*` 最新;判据 = 含 `repo.git`,同 engine
|
|
288
|
+
"bare 是分析存在的唯一标志";`mv-spec-*` 不在前缀内)。
|
|
289
|
+
**无降级兜底**(e2e16 评审 2:0:1 裁定):曾有"无 sid 匹配 → 取项目下
|
|
290
|
+
最新 `mv-*`"的兜底,三宗罪——①破坏性操作(`--cleanup`/`--say`)会作用
|
|
291
|
+
于**猜测目录**;②降级只能靠 stderr 中文文案识别(extension 曾用
|
|
292
|
+
`includes("警告")` 还原布尔,文案一改静默失效,与"判据用退出码"自相
|
|
293
|
+
矛盾);③"最新"按整名排序,跨 sid 时**系统性取旧**。核查确认**不存在
|
|
294
|
+
"必须无目录且必然无 sid"的设计内场景**(pi 两条通道都有 sid,终端主路径
|
|
295
|
+
本就显式带目录)→ 未匹配 = rc 1 报错请显式传目录。
|
|
296
|
+
**动机**:唯一知道路径的是 `--start` 的输出,此前每个消费命令都要求
|
|
297
|
+
传它 → 主 pi 必须把长绝对路径记在 LLM 上下文里复用(改错/截断/相对
|
|
298
|
+
路径都出过)。发现逻辑下沉后**路径不经过 LLM**。
|
|
299
|
+
实现单点 = `observability.find_current_dir`(wrapper 的 `resolve_dir`
|
|
300
|
+
与 extension 同走 `observability.py --find-dir`——此前只有 extension
|
|
301
|
+
里一份 TS 实现,wrapper 侧完全没有,两份口径会漂移)。
|
|
302
|
+
`--say` 两形态**按参数个数区分**(1 个 = 文本+自动发现;2 个 = 目录+
|
|
303
|
+
文本)——不是按值猜语义。
|
|
304
|
+
配套:`--status` 在 done 时打印 `[result] <路径>`(目录可省略后调用方
|
|
305
|
+
无法自己拼 `<目录>-result.md`,路径必须由机制给出)。
|
|
306
|
+
16. **prompt 里的失败判据用退出码,不用报错文本匹配**:原 prompt 要求
|
|
307
|
+
"若报错含'未找到 viewers/ 目录'…" —— 匹配 stderr 文案,wrapper 文案
|
|
308
|
+
一改就静默失配(LLM 会以为没报错而继续)。改为"命令非零退出 → 停下来
|
|
309
|
+
读报错原文问用户":判据降为退出码(wrapper 的 `fail()` 保证 `exit 1`),
|
|
310
|
+
原因解释交还给输出原文。
|
|
311
|
+
17. **CLI 层 = Python(bash 只留 shim)**:`scripts/mv.sh` 是 5 行 shim
|
|
312
|
+
(找到仓库根 → `exec python3 mv_cli.py "$@"`),解析/决议/调用/展示全在
|
|
313
|
+
`mv_cli.py`。**路径 `scripts/mv.sh` 不变**(prompt/README/用户习惯引用
|
|
314
|
+
它)。收敛依据不是"更整洁",而是这层产出的真实 bug **全部出自 bash
|
|
315
|
+
陷阱**(`shift` 吃掉显式目录 = F5 回归、`local` 重复声明清空变量、命令
|
|
316
|
+
替换里的 `exit` 不进父 shell 致双重错误消息、参数静默丢弃 = P0),且
|
|
317
|
+
bash 在本项目工具链里**零行覆盖**(Python 覆盖率看不到它)——缺口只能靠
|
|
318
|
+
真实 e2e 或评审暴露。收敛后逻辑进入覆盖率与进程内单测
|
|
319
|
+
(tests/test_mv_cli.py),解析统一出口。子进程边界不变:start_discussion
|
|
320
|
+
/ human_viewer / human_sayer 仍以子进程调用(各自是 CLI 入口)。
|
|
321
|
+
顺带三处"无静默"加固(bash 版是静默的):status/report/wait/cleanup 的
|
|
322
|
+
多余参数响亮失败、`--say` 多于 2 个参数响亮失败(未加引号文本会被当成
|
|
323
|
+
目录)、`--view` 的 viewer 失败透传 rc 且**不打印 HEAD 游标**(给失败的
|
|
324
|
+
一轮发游标会让下一次 `--since` 静默跳过消息)。
|
|
325
|
+
18. **git 守卫范围 = 从讨论 workdir 发起的操作**(`GIT_CEILING_DIRECTORIES`
|
|
286
326
|
注入于 spawn);主项目仓库不在守卫范围(agent 的 cwd 就是主项目,其
|
|
287
327
|
约束归指令层 + 主项目 `.gitignore`)。要拦主仓库需换机制类(沙箱/钩子),
|
|
288
328
|
经评估收益不支撑扩面。
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
> **存档说明**:2026-09-11 真实多视角分析(性能 / 简单 / 铁律)的 result.md
|
|
2
|
+
> 原文,主题为「审阅本项目最近两项机制化改动(消费命令目录可省略 + 报告
|
|
3
|
+
> 机制化)的实现质量与遗漏」——即对 `be958ec` 与 `d20fc46` 的自审。
|
|
4
|
+
> **抓到 1 个真回归(F5:`--view` 显式目录被静默丢弃)与 1 个高危设计缺陷
|
|
5
|
+
> (降级兜底:破坏性操作可作用于猜测目录)**,另有 5 条中低问题与 7 项
|
|
6
|
+
> "确认符合设计"。修复见后续 commit。
|
|
7
|
+
> 报告编号(F1–F8 等)为**当次讨论内部编号**,不可跨文档核验;引用的行号与
|
|
8
|
+
> 实测数字均对应当时工作区状态。
|
|
9
|
+
|
|
10
|
+
# 多视角分析结论:两项机制化改动的审阅
|
|
11
|
+
|
|
12
|
+
**主题**:审阅 `be958ec`(消费命令目录可省略)与 `d20fc46`(报告机制化)的实现质量与遗漏
|
|
13
|
+
**分析目录**:`mv-mv-main-20260911-225616`
|
|
14
|
+
**参与者**:性能 / 简单 / 铁律(+ human 插话 1 条)
|
|
15
|
+
**收敛**:全员 pass,本文件为收敛结论的唯一权威版本
|
|
16
|
+
**方法**:只审代码与 git 历史,不修改、不运行测试;产出意见清单并对清单达成一致
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 一、结论摘要
|
|
21
|
+
|
|
22
|
+
**总体判断**:两项改动的主体符合设计意图,方向正确;但 **目录可省略在 wrapper 层
|
|
23
|
+
引入了一个真回归(高危)**,且其"降级兜底"分支是四类症状的共同根因——**三方推荐
|
|
24
|
+
整个删除**。报告机制化通过(唯一要求:约束成文)。
|
|
25
|
+
|
|
26
|
+
**问题清单 8 条**:高 2 / 中 1 / 低 3 / 观察 2。
|
|
27
|
+
|
|
28
|
+
| # | 严重度 | 问题 | 处置 |
|
|
29
|
+
|---|---|---|---|
|
|
30
|
+
| 1 | **高** | `--view` 显式目录被静默丢弃(回归) | **修**(参数规则统一 + 补测试) |
|
|
31
|
+
| 2 | **高** | 降级发现可作用于破坏性操作(`--cleanup` 删猜测目录) | **删降级兜底**(推荐,2:0:1) |
|
|
32
|
+
| 3 | 中 | 降级信号经 stderr 中文文案解析(`err.includes("警告")`) | 随 #2 一并消失 |
|
|
33
|
+
| 4 | 中 | 降级"最新"按全名排序(跨 sid 取旧目录,与 docstring 不符) | 随 #2 一并消失 |
|
|
34
|
+
| 5 | 中 | 报告 = O(session) 冷路径 × 3 出口,护栏仅存于隐含前提 | **约束成文**(禁入轮询) |
|
|
35
|
+
| 6 | 低 | `result_path` 越界在 viewer(拼路径+组合层为拼串 import) | 移 `meeting_fs` |
|
|
36
|
+
| 7 | 低 | 未找到目录两条错误文案(python + wrapper 各一条) | 留 python 一处 |
|
|
37
|
+
| 8 | 低 | 流程段口径未说明(消息数含流程信号 vs 配额只计 meeting) | 口径行补半句 |
|
|
38
|
+
| — | 观察 | viewer ↔ observability 环(本次新增反向边) | 不 callback 化,不再加边 |
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 二、逐条结论与依据
|
|
43
|
+
|
|
44
|
+
### F5【高】`--view` 显式目录被静默丢弃
|
|
45
|
+
|
|
46
|
+
- **位置**:`scripts/mv.sh:113-119`。shift 吃掉首参后**总是**走
|
|
47
|
+
`resolve_dir ""`(自动发现),显式目录被丢弃。
|
|
48
|
+
- **性质**:相对改动前的**行为回退**(改前 `normalize_dir "$1"` 显式生效);
|
|
49
|
+
且是**静默**的——多 `mv-*` 并存或跨目录调用时会看错对象且无提示。
|
|
50
|
+
- **测试缺口(根因)**:`tests/test_wrapper.py:195` 仅有无参形态 `["--view"]`,
|
|
51
|
+
显式目录零覆盖——测试全绿(395)没挡住的直接原因。
|
|
52
|
+
- **修复**(三方一致):
|
|
53
|
+
```bash
|
|
54
|
+
# 首参非空且非 --since → 显式目录;否则自动发现
|
|
55
|
+
if [ -n "${1:-}" ] && [ "${1:-}" != "--since" ]; then
|
|
56
|
+
dir="$(resolve_dir "$1")"; shift
|
|
57
|
+
else
|
|
58
|
+
dir="$(resolve_dir "")"
|
|
59
|
+
fi
|
|
60
|
+
```
|
|
61
|
+
与 `status/report/wait/cleanup` 的 `resolve_dir "${1:-}"` 同一判据;
|
|
62
|
+
`--say` 保持"按参数个数"(文本可含空格/以 `--` 开头,**不能**按值判形)。
|
|
63
|
+
- **性能补证**(性能视角):显式目录**反而省一次 find-dir spawn**——修 F5
|
|
64
|
+
是恢复更快的路径,不是"加回功能付性能代价"。
|
|
65
|
+
- **验收**:补显式目录形态测试;这是"全绿没挡住"的教训,不只是修一行。
|
|
66
|
+
|
|
67
|
+
### F1/F2/F7 + 简单#2【高】降级兜底 = 一个概念,推荐整体删除
|
|
68
|
+
|
|
69
|
+
四类症状同一根因("无 sid 匹配 → 取项目下最新 `mv-*`"的降级兜底):
|
|
70
|
+
|
|
71
|
+
| 症状 | 后果 |
|
|
72
|
+
|---|---|
|
|
73
|
+
| **F1** 降级无机器判据(退出码仍 0,仅 stderr 文案) | `mv.sh --cleanup`(无参)**可删除不是本次分析的目录**;`--say` 可写错分析 |
|
|
74
|
+
| **F2** extension 靠 `err.includes("警告")` 还原布尔 | 文案一改,降级判断**静默**失效(同仓库内与"判据改退出码"的最新修正相矛盾) |
|
|
75
|
+
| **F7** `sorted(os.listdir)[-1]` 在降级路径比较整名(含 sid) | `mv-zzz-2025…` 排在 `mv-aaa-2026…` 后 → **系统性选旧目录**(与 docstring"取最新"不符) |
|
|
76
|
+
| 简单#2 | 同一概念的第四面 |
|
|
77
|
+
|
|
78
|
+
- **收敛(2:0:1)**:简单 + 铁律支持**删除**;性能中性("不构成支持或反对理由")。
|
|
79
|
+
- **删除依据(关键事实,经三方核查)**:**不存在"必须无目录且必然无 sid"的设计内
|
|
80
|
+
场景**——pi 内两条通道都有 sid(bash 注入 / extension `ctx.sessionManager`);
|
|
81
|
+
终端主路径是 `--start` 输出的观看命令(本就显式带目录);代码里兜底的原始动机是
|
|
82
|
+
"PI 环境变量未注入时 wrapper 拿不到 sid",属 pi 流程自身降级,不是给人用的便利。
|
|
83
|
+
→ 兜底不对任何设计流程构成负载。且**破坏性操作不应由工具猜目标**:
|
|
84
|
+
最坏失败从"静默删错目录"变为"响亮报错要求显式目录"。
|
|
85
|
+
- **删除方案**:仅精确匹配 `mv-<sid>-*`(同 sid 时间戳可比 → F7 消失);
|
|
86
|
+
未匹配 → rc 1 报错,请调用方显式传目录 → F1/F2/简单#2 同时消失。
|
|
87
|
+
- **执行收尾三条**(简单视角提出,三方确认):
|
|
88
|
+
1. **`degraded` 概念删净**:`find_current_dir` 返回值收为一元(`path | None`);
|
|
89
|
+
`--find-dir` 退出码只留 0/1(**不留 rc 2 语义**);extension 的
|
|
90
|
+
`includes("警告")` 与 notify 警告分支一并删——不留无人消费的死代码。
|
|
91
|
+
2. **错误文案留一处**:留 python 的(工具层贴近原因);wrapper `resolve_dir`
|
|
92
|
+
在 spawn 失败/rc=1 时透传 python stderr 后 `exit`,不另打一条。
|
|
93
|
+
3. **测试变更**:删降级用例;加"无 sid 匹配 → rc 1";**保留并新增显式目录用例**
|
|
94
|
+
(F5 验收)。
|
|
95
|
+
- **备选(若最终选择保留)**:**必须三件套齐做**——rc 契约(0 精确/2 降级/1 未找到)
|
|
96
|
+
+ 写操作(`--cleanup`/`--say`)降级时 fail 且**列出候选目录** + F7 时间戳排序。
|
|
97
|
+
**半保留不可接受**(只做 rc 不修 F7 = 警告之后仍系统性读错对象)。
|
|
98
|
+
删/留方案应记入本文件为**分歧项与备选**,非一致意见。
|
|
99
|
+
|
|
100
|
+
### F-报告【中】报告 = O(session) 冷路径 × 3 出口 → 约束成文
|
|
101
|
+
|
|
102
|
+
- **实测(性能视角)**:每 agent 读整个 fork-src jsonl,本次 3 × 789KB = **2.4MB/次**;
|
|
103
|
+
`iter_after_boundary` 逐行扫到边界(本次边界在 777 行中第 772 行 → **99.4% 读取
|
|
104
|
+
用于跳过前缀**);量化 **50–150ms/次**,×2 出口 ≈ **<0.3s/次分析**。冷路径可接受。
|
|
105
|
+
- **处置**:把"报告是 O(session 大小) 的冷路径、**不得进入任何轮询路径**"
|
|
106
|
+
写入既有观测面契约(design.md 契约表"冷路径一次性"行后补半句,**不新开节**)。
|
|
107
|
+
- **不做**尾部 seek 优化(为省 <50ms 引入行/字节边界复杂性——复杂度不匹配)。
|
|
108
|
+
- 出口应为 3 个(`--report` / `--cleanup` / `--follow` 结束);`--view --since`
|
|
109
|
+
不附报告(进 LLM context,无用且占 token)——符合职责归属。
|
|
110
|
+
|
|
111
|
+
### F3【低】`result_path` 不在常量家
|
|
112
|
+
|
|
113
|
+
- `human_viewer.py:49-56` 定义 `f"{base}-result.md"`;S1 已把 "文件叫什么/多大算
|
|
114
|
+
有效" 收进 `meeting_fs`(`RESULT_MD` / `RESULT_MD_MIN_BYTES`),但拼路径留在
|
|
115
|
+
viewer → 组合层(`start_discussion.py:610`)为拼字符串 import 展示模块。
|
|
116
|
+
- 处置:移 `meeting_fs`(与 `RESULT_MD` 同家),viewer re-export 兼容。
|
|
117
|
+
|
|
118
|
+
### F4【观察】viewer ↔ observability 环
|
|
119
|
+
|
|
120
|
+
- `observability → human_viewer` 是既有边(`--wait` 用 incremental/is_finished);
|
|
121
|
+
`human_viewer → observability`(`_print_report` 延迟 import)是**本次新增的反向边**。
|
|
122
|
+
- 处置:**不 callback 化**(2 行延迟 import 换接口变化 + 两处调用方改动 = 净增);
|
|
123
|
+
保持现状 + **不再往环上加第三条边**。`observability.py:156` 函数内重复
|
|
124
|
+
`import human_viewer`(顶层已有)为纯清理项,可延后。
|
|
125
|
+
|
|
126
|
+
### F8【低】流程段口径行
|
|
127
|
+
|
|
128
|
+
- "消息 N(含流程信号)" = 消息文件数(含 freezing/pass/concluded + human),
|
|
129
|
+
与配额段(仅 `mode=meeting` 且 `type=message`)并列时同一 agent 两个数字。
|
|
130
|
+
- 处置:末尾口径行补半句"消息数含流程信号;配额只计 meeting 发言"。
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 三、经检查确认符合设计(不构成问题)
|
|
135
|
+
|
|
136
|
+
1. **流程段弃用 commit subject 正则、改消息文件计数**(`observability.py:241`):
|
|
137
|
+
去掉自由文本耦合(格式一改就静默归零),并与配额/冻结/RR 共用同一次
|
|
138
|
+
`each_agent_messages` 读取——一次读取派生四段,本轮最大的简化净收益。
|
|
139
|
+
2. **extension 删除 TS 版重复发现实现**,改调 python 同一入口——单一实现方向正确
|
|
140
|
+
(问题只在信号通道,不在归并本身)。
|
|
141
|
+
3. **`_print_report` 延迟 import + fail-open 宽捕获**:两行成本消化模块环,
|
|
142
|
+
保住"报告异常不阻断观看退出"契约;延迟 import 的用法正确(冷路径一次)。
|
|
143
|
+
4. **`--status` 在 done 时打印 `[result]` 路径**:目录可省略后调用方无法自己拼路径,
|
|
144
|
+
由机制给出的机器通道——符合单一来源收口方向。
|
|
145
|
+
5. **`--say` 按参数个数区分双形态**(不按值猜语义)——与既有约定一致,保持。
|
|
146
|
+
6. **`find_current_dir` 同 sid 匹配**:目录名尾缀 `YYYYMMDD-HHMMSS` 可比;
|
|
147
|
+
`repo.git` 判据与 engine 一致("bare 是讨论存在的唯一标志");发现逻辑下沉
|
|
148
|
+
python、wrapper/extension 不各写一份——符合单一事实源。
|
|
149
|
+
7. **报告附 `--follow`(用户通道)而非 `--view --since`(主 pi 通道)**:职责归属正确。
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 四、分歧与备选项(如实记录)
|
|
154
|
+
|
|
155
|
+
| 议题 | 立场 | 结论 |
|
|
156
|
+
|---|---|---|
|
|
157
|
+
| 降级兜底:删除 vs 保留 | 简单:删除;铁律:**更正后支持删除**(0005);性能:中性 | **推荐删除**(净删四症状);保留 + 三件套齐做为备选 |
|
|
158
|
+
| 铁律 0002–0004 曾主张保留 | 理由:"PI_SESSION_ID 缺失在 pi 外是常态" | 经简单视角反问"给不出必须无 sid 的场景"后,铁律核查确认场景不存在,**0005 更正**——留档说明立场演化 |
|
|
159
|
+
| 删兜底后无 sid 形态 | 简单建议的"选项 2" | 不是分歧:degraded 概念消失后,无 sid = 报错要求显式目录 |
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 五、过程与统计
|
|
164
|
+
|
|
165
|
+
- **讨论消息**:23 条(性能 5 / 简单 9 / 铁律 8 / human 1)——其中含协议信号
|
|
166
|
+
all-freezing ×3、pass ×3,其余为实质发言
|
|
167
|
+
- **human 插话 1 条**:要求简要叙述(后续发言均从简)
|
|
168
|
+
- **消息统计口径注**:本文件的"消息 N"均指消息文件数(含流程信号);
|
|
169
|
+
配额口径只计 `mode=meeting` 且 `type=message` 的发言
|
|
170
|
+
|
|
171
|
+
## 六、建议执行顺序(供实施参考)
|
|
172
|
+
|
|
173
|
+
1. **F5**(回归,含显式目录测试)——纯实现 bug,任何多分析场景都会静默看错对象
|
|
174
|
+
2. **降级兜底删除**(F1/F2/F7 一次消除)——含执行收尾三条与文档同步
|
|
175
|
+
(design.md 的 `find_current_dir` 决策记录改为"仅精确 sid 匹配、无兜底")
|
|
176
|
+
3. **报告约束成文**(零代码,仅契约补半句)
|
|
177
|
+
4. F3 / F8 / F4 清理项(低风险,可顺手)
|
package/docs/reviews/README.md
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
|---|---|---|---|
|
|
22
22
|
| `2026-09-10-e2e10-fork-source-modes-review.md` | fork 源模式描述与实现一致性(`docs/design.md` / README / AGENTS.md vs 代码) | 非法 `forkMode` 无 fail-fast(静默走"边界后全量"混合分支 → 930k tokens 超窗);wrapper 静默丢弃 `--fork-mode`;文档数字混源无锚;三处"完整上下文"与有损默认矛盾 | `732fdff`(P0/P1/P2)、`fe1955d`(方法论 11–14) |
|
|
23
23
|
| `2026-09-10-e2e11-forkmode-guards-review.md` | forkMode 四层守卫与默认值单一源(含文档口径与方法论条目) | compaction 模式产物含**重复** compaction 条目;budget 窗口含 compaction 时 pi replay **静默丢弃前缀(含 preface)**;台账 `dropped` **双重计数**;`FORK_MODES` 与分派**非结构耦合** | `9412e32`(P1–P3 + I1–I5 不变量 + 台账) |
|
|
24
|
+
| `2026-09-11-e2e16-mechanization-review.md` | 机制化改动自审(消费命令目录可省略 + 报告机制化) | **`--view` 显式目录被静默丢弃(回归,测试只覆盖无目录形态)**;降级兜底可作用于破坏性操作(`--cleanup` 删猜测目录)+ extension 靠中文文案还原布尔 + 排序取最新在跨 sid 时系统性取旧;报告 O(session) 冷路径需约束成文 | 「机制化 1+2 修复」提交 |
|
|
24
25
|
| `2026-09-11-e2e14-observability-review.md` | 本项目可观测性自审(日志系统是否合理 + 如何监控讨论有效性) | 失败/重试不可见(provider error 零记录,实测占墙钟 11%);耗时不可度量;token 无出口;**`--wait` 用 `glob(loop-*.log)` 兼任判定输入**;viewer 与 check_status 的 done 判据分叉;两份逐字相同的 `log()`;观测面契约在文档零命中 | 见「e2e14 修复」提交(第一批 §3.1–§3.5) |
|
|
25
26
|
| `2026-09-11-e2e13-code-review.md` | 全项目代码合理性评审(第二轮,含时间流实测) | `--agents` 非法名在 spec-gen 路径炸出 traceback + 半成品;viewers/agents 校验与列举多实现(隐藏文件静默入场);wrapper 越界检查第三方扩展配置;`--start` 静默删 spec;session 文件两套查找规则;切换叙事拼 agent 定义正文 | `fe3aa09`(R1–R8) |
|
|
26
27
|
| `2026-09-10-e2e12-code-review.md` | 全项目代码合理性评审(性能 / 简单 / 铁律) | `_lock_git` 守卫 **fail-open**(锁态下 git 上溯到主项目仓库);`pgrep -f` 自匹配;`protocol.json` 读取散落 6 处 / 3 种失败语义(含 `result_writer` 默认值求值缺陷);`check_status` 用 `git grep` 全文误报 done;engine 直做 fs I/O;两处 git 入口加固不一致 | 见「e2e12 修复」提交(批 A/B/C) |
|
package/docs/test-methodology.md
CHANGED
|
@@ -279,3 +279,89 @@ python 时被"顺手改写"为含斜杠则不拼——**移植不是重写**:
|
|
|
279
279
|
|
|
280
280
|
**成本视角**:冗余测试不只是耗时——它稀释了"全绿"的信号价值(每次发版都全绿
|
|
281
281
|
时,人不再区分"这次真的验了代码"与"这次只 bump 了版本号")。
|
|
282
|
+
|
|
283
|
+
### 20. LLM 测试的判据 = **时长**,不是"是否用 LLM"(2026-09-11 用户澄清)
|
|
284
|
+
|
|
285
|
+
**规则**(用户原话):
|
|
286
|
+
|
|
287
|
+
> 我要求 llm 测试必须经过我同意的原因是 llm 测试会消耗大量时间,会影响我
|
|
288
|
+
> 后继的操作。因此如果能确保短时间结束的测试是没有关系的,比如仅仅测试
|
|
289
|
+
> 一下启动是没有问题的。
|
|
290
|
+
|
|
291
|
+
**判据**:
|
|
292
|
+
|
|
293
|
+
| 情形 | 是否需要事先同意 |
|
|
294
|
+
|---|---|
|
|
295
|
+
| 完整 e2e 讨论、多轮真实唤醒、分钟级以上占用 | **需要**(且用户在场) |
|
|
296
|
+
| 几秒内结束的链路验证(只验证启动、单次冒烟、构造即停) | 不需要 |
|
|
297
|
+
| spawn 了 pi/loop 但立即终止的测试 | 不需要,但要**主动报告**(说明起过、已停、有无真实请求发出) |
|
|
298
|
+
|
|
299
|
+
**配套纪律**:`mv.sh --start` 会 spawn loop → spawn pi(真实请求)——短测试
|
|
300
|
+
也要**想好怎么停**(构造完立即 cleanup + `check-residue` 复核)。
|
|
301
|
+
|
|
302
|
+
### 21. `mv.sh --start` 会真实启动分析(spawn loop + pi)——冒烟别用它
|
|
303
|
+
|
|
304
|
+
**背景**:`cmd_start` 做两件事:创建环境 + **启动 loop**。后者会立刻 spawn
|
|
305
|
+
`meeting_loop.py`,而 loop 首轮就 spawn `pi --mode json --print`——**真实 LLM
|
|
306
|
+
请求**。在 pi session 内跑(`PI_SESSION_ID`/`PI_SESSION_FILE` 都在)时没有任何
|
|
307
|
+
保护会阻止它。
|
|
308
|
+
|
|
309
|
+
**实测代价**(2026-09-11 两次,同一坑):
|
|
310
|
+
- ① 验证"目录名构造"时跑 `mv.sh --start` → 真起了 2 个 loop + 2 个 pi 进程;
|
|
311
|
+
- ② 验证"目录可省略"时同样跑 `--start` → 同样起来(20 秒后 cleanup 删目录,
|
|
312
|
+
loop 因 repo.git 消失自退出,但 pi 进程已 spawn → 请求可能已发出)。
|
|
313
|
+
|
|
314
|
+
**方法**:
|
|
315
|
+
1. **只创建不启动** → 用 python 侧 `start_discussion.py --dir <d> --spec <s>`
|
|
316
|
+
(不带 `--start`);wrapper 无此形态(`--start` 永远启动)。
|
|
317
|
+
2. 需要走 wrapper 链路时,验证完**立即**:停 loop(`pkill` 按目录精确匹配)
|
|
318
|
+
→ `--cleanup` → `check-residue.sh` 复核。
|
|
319
|
+
3. 判据:"这次验证会不会 spawn pi/loop?"——会,就必须先想好怎么停。
|
|
320
|
+
|
|
321
|
+
### 22. 契约写成矩阵表:命令 × 形态 × 判别力(2026-09-11 e2e16 F5)
|
|
322
|
+
|
|
323
|
+
**背景(F5 回归)**:改 `cmd_view` 时只测"新形态"(目录可省略),
|
|
324
|
+
`--view <dir>` 的显式形态**改动前后都没有测试**(旧测试测的是"无参 → 报错"
|
|
325
|
+
的错误分支);395 个测试全绿没挡住——真实多视角评审(e2e16)才抓出。
|
|
326
|
+
|
|
327
|
+
**诊断(不是"漏写一条用例",是结构性缺口)**:
|
|
328
|
+
1. wrapper 测试按功能增量堆叠,没有"命令 × 形态"的系统性盘点;
|
|
329
|
+
2. **bash 层无行覆盖工具**——缺口不出现在覆盖率里(实测 Python 层 96%,
|
|
330
|
+
wrapper 测不到一行);`--wait` 零测试、`--cleanup` 省略形态零测试;
|
|
331
|
+
3. 旧测试用**弱断言**(仅 `assertNotEqual(rc, 0)`)——契约废除后静默变
|
|
332
|
+
空洞:`test_view_requires_dir` 的"目录必传"契约已不存在,它只因测试
|
|
333
|
+
cwd 里没有分析环境才继续绿,看起来在覆盖 `--view` 实则什么都没测。
|
|
334
|
+
|
|
335
|
+
**方法**:
|
|
336
|
+
1. **契约 = 一张表**(`tests/test_wrapper.py::TestCommandFormMatrix`):
|
|
337
|
+
每行一个形态单元格 = `(args, 前置条件, 期望 rc, 输出须含, 副作用)`。
|
|
338
|
+
新增命令/形态必须补行——**结构性强制,不靠记忆**。
|
|
339
|
+
2. **每格三件事**:退出码 + **输出内容** + **副作用/文件效果**;
|
|
340
|
+
禁止仅断言退出码(弱断言是僵尸测试的温床)。
|
|
341
|
+
3. **判别力优先于覆盖率**:显式参数格子必须能区分"用了显式值"与"恰好
|
|
342
|
+
自动发现同一个"。做法:加**诱饵**(更晚时间戳 + 状态不同 `done` vs
|
|
343
|
+
`stopped` + 独有标记)——两个同构环境的 HEAD/输出完全相同,无诱饵时
|
|
344
|
+
F5 类 bug 照样绿(本次实测:加诱饵后重引入 F5,矩阵立即失败并报
|
|
345
|
+
"走了自动发现(选到诱饵环境)")。
|
|
346
|
+
4. **改写旧测试 ≠ 补充**:契约变更时先问"它测的是哪条既有契约、那条契约
|
|
347
|
+
还有效吗"——有效则必须补等价用例;无效则删除并在表里补新形态格。
|
|
348
|
+
|
|
349
|
+
### 23. 概率性断言 = 测试设计缺陷(2026-09-12 test_n_agents 随机红)
|
|
350
|
+
|
|
351
|
+
**背景**:`test_four_agent_cascade` 断言"N=4 应有 message 发言"。FakeAgent 在
|
|
352
|
+
meeting 阶段有 35% 概率写 freezing,而"全员首轮 freezing"概率 = 0.35⁴ =
|
|
353
|
+
**1.50%**——此时测试必失败(N=5 时 0.53%)。实测约每 10 次全量跑红一次。
|
|
354
|
+
|
|
355
|
+
**定性**:**测试装置问题,不是产品问题**——全员首轮 freezing 是合法协议路径
|
|
356
|
+
(引擎照常走完 af → RR → pass → concluded)。但断言把"典型路径"当成不变式了。
|
|
357
|
+
|
|
358
|
+
**方法**:
|
|
359
|
+
1. **测试可以断言不变式,不可以断言分布尾部**:写断言前先问"这个结论在
|
|
360
|
+
装置的随机性下是否恒成立"。不成立 → 断言错,或装置要改。
|
|
361
|
+
2. **修装置优于挑种子**:本次修法是让 FakeAgent **首轮必写 message**——
|
|
362
|
+
理由是**装置对齐生产**(真实 wake prompt 对首位发言者的指令就是"直接产出
|
|
363
|
+
你的第一条视角分析"),而不是"把概率调到能过"。挑 seed 只治一次,改对齐
|
|
364
|
+
治一类。
|
|
365
|
+
3. **确定性优先于概率**:修复后该断言成为**不变式**(P = 0 不可能失败),
|
|
366
|
+
不再依赖运气——这比"多跑几次都绿"才是证据(10 次全绿对 1.5% 概率的
|
|
367
|
+
事件的置信度很低)。
|
|
@@ -6,14 +6,13 @@
|
|
|
6
6
|
* 不经过 LLM——命令 handler 直接 spawn human_sayer.py(一次调用一次返回),
|
|
7
7
|
* 结果用 ctx.ui.notify 反馈。
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* 最新的 mv-*,并警告降级(宁可提示也不要静默插错分析)。
|
|
9
|
+
* 讨论目录发现(零状态文件):目录名 = mv-<sid>-<时间戳>;
|
|
10
|
+
* handler 取 ctx.sessionManager.getSessionId() + ctx.cwd,**调用
|
|
11
|
+
* observability.py --find-dir**(python 单一实现——wrapper 的消费命令
|
|
12
|
+
* 走同一入口;本扩展不再自持一份发现逻辑,两边口径不会漂移)。
|
|
13
|
+
* **只精确匹配同 sid 的目录,无降级兜底**(e2e16 F1/F2:降级时插话可能
|
|
14
|
+
* 写错分析,且判据曾是"stderr 是否含中文'警告'"——文案一改静默失效);
|
|
15
|
+
* 未找到 → 报错提示。
|
|
17
16
|
*
|
|
18
17
|
* 前缀 mv- 与 pi-agents-helper 的 discuss-* 命名空间隔离(两个系统的
|
|
19
18
|
* 插话命令都按"同 sid 最新目录"发现目标,共用前缀会互相插错)。
|
|
@@ -55,43 +54,36 @@ const PACKAGE_ROOT = findPackageRoot(__dirname, PKG_NAME);
|
|
|
55
54
|
const SAYER = PACKAGE_ROOT
|
|
56
55
|
? path.join(PACKAGE_ROOT, "human_sayer.py")
|
|
57
56
|
: "/root/pi-multi-viewers/human_sayer.py"; // 复制安装退化(开发机)
|
|
57
|
+
const OBSERVABILITY = PACKAGE_ROOT
|
|
58
|
+
? path.join(PACKAGE_ROOT, "observability.py")
|
|
59
|
+
: "/root/pi-multi-viewers/observability.py"; // 复制安装退化(开发机)
|
|
58
60
|
|
|
59
|
-
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
.map((e) => e.name)
|
|
71
|
-
.sort();
|
|
72
|
-
if (bySid.length > 0) {
|
|
73
|
-
const dir = path.join(cwd, bySid[bySid.length - 1]);
|
|
74
|
-
return { dir: fs.existsSync(dir) ? dir : null, degraded: false };
|
|
75
|
-
}
|
|
76
|
-
const any = names
|
|
77
|
-
.filter(
|
|
78
|
-
(e) =>
|
|
79
|
-
e.isDirectory() &&
|
|
80
|
-
e.name.startsWith("mv-") &&
|
|
81
|
-
// 排除 mv-spec-*:那是尚未被 --start 消费的 spec 目录,不是分析
|
|
82
|
-
// 目录(否则会选中它并报出误导性的"分析不存在")
|
|
83
|
-
!e.name.startsWith("mv-spec-"),
|
|
84
|
-
)
|
|
85
|
-
.map((e) => e.name)
|
|
86
|
-
.sort();
|
|
87
|
-
if (any.length > 0) {
|
|
88
|
-
const dir = path.join(cwd, any[any.length - 1]);
|
|
89
|
-
return { dir: fs.existsSync(dir) ? dir : null, degraded: true };
|
|
61
|
+
/** 定位当前分析目录:调用 observability.py --find-dir(**python 单一实现**
|
|
62
|
+
* ——wrapper 消费命令同一入口)。
|
|
63
|
+
*
|
|
64
|
+
* 判据 = **退出码**(不是 stderr 文案——中文提示一改就静默失配,
|
|
65
|
+
* e2e16 F2):rc 0 → stdout 是绝对路径;rc 1 → 未找到(无同 sid 分析)。
|
|
66
|
+
* 无降级通道(不会回退到"项目下最新"——那会插错分析,e2e16 F1)。 */
|
|
67
|
+
function findCurrentDir(cwd: string, sid: string): Promise<string | null> {
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
if (!OBSERVABILITY) {
|
|
70
|
+
resolve(null);
|
|
71
|
+
return;
|
|
90
72
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
73
|
+
const proc = spawn("python3", [OBSERVABILITY, "--find-dir"], {
|
|
74
|
+
cwd,
|
|
75
|
+
env: { ...process.env, PI_SESSION_ID: sid },
|
|
76
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
77
|
+
});
|
|
78
|
+
let out = "";
|
|
79
|
+
proc.stdout.on("data", (d) => (out += d.toString()));
|
|
80
|
+
proc.stderr.on("data", () => {}); // 原因只在 rc=1 时通知用户(见 handler)
|
|
81
|
+
proc.on("close", (code) => {
|
|
82
|
+
const dir = out.trim();
|
|
83
|
+
resolve(code === 0 && dir ? dir : null);
|
|
84
|
+
});
|
|
85
|
+
proc.on("error", () => resolve(null));
|
|
86
|
+
});
|
|
95
87
|
}
|
|
96
88
|
|
|
97
89
|
/** 执行 human_sayer.py 一次插话。返回 { ok, output }。 */
|
|
@@ -126,23 +118,16 @@ export default function register(pi: any) {
|
|
|
126
118
|
return;
|
|
127
119
|
}
|
|
128
120
|
const sid = ctx.sessionManager.getSessionId();
|
|
129
|
-
const
|
|
130
|
-
const dir = found.dir;
|
|
121
|
+
const dir = await findCurrentDir(ctx.cwd, sid);
|
|
131
122
|
if (!dir) {
|
|
132
123
|
ctx.ui.notify(
|
|
133
|
-
"没有正在进行的多视角分析(cwd 下无
|
|
134
|
-
|
|
124
|
+
"本 session 没有正在进行的多视角分析(cwd 下无 " +
|
|
125
|
+
`mv-${sid}-* 分析环境)。先用 /multi-viewers 启动,` +
|
|
126
|
+
"或改用 mv.sh --say <目录> \"<文本>\" 显式指定。",
|
|
135
127
|
"error",
|
|
136
128
|
);
|
|
137
129
|
return;
|
|
138
130
|
}
|
|
139
|
-
if (found.degraded) {
|
|
140
|
-
ctx.ui.notify(
|
|
141
|
-
`未找到本 session 的分析目录(目录名不含 session id——` +
|
|
142
|
-
`PI 环境变量可能未注入)——插话指向项目下最新分析: ${dir}`,
|
|
143
|
-
"warning",
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
131
|
const { ok, output } = await runSayer(dir, text);
|
|
147
132
|
if (ok && output) {
|
|
148
133
|
ctx.ui.notify(output, "success");
|
package/fake_agent.py
CHANGED
|
@@ -88,9 +88,17 @@ def make_responder(min_sleep, max_sleep, crash_rate):
|
|
|
88
88
|
# RR 阶段:单向流,只写 pass(无异议回退,留待以后)
|
|
89
89
|
decision = meeting_core.T_PASS
|
|
90
90
|
else:
|
|
91
|
-
# meeting 阶段:有内容 → message;无话可说 → freezing
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
# meeting 阶段:有内容 → message;无话可说 → freezing。
|
|
92
|
+
# **首轮例外 = 必写 message**(装置对齐生产,2026-09-12):真实
|
|
93
|
+
# wake prompt 对首位发言者的指令是"你是第一位发言者——直接产出
|
|
94
|
+
# 你的第一条视角分析",fake 首轮却可能 freezing——语义不符,且
|
|
95
|
+
# 使"全员首轮 freezing"有 0.35^N 的概率(N=4 时 1.5%)撞上
|
|
96
|
+
# cascade 测试的 "应有 message" 断言 → 随机红(实测 ~2%/全量跑)。
|
|
97
|
+
if is_first:
|
|
98
|
+
decision = meeting_core.T_MESSAGE
|
|
99
|
+
else:
|
|
100
|
+
decision = meeting_core.T_MESSAGE if random.random() < 0.65 \
|
|
101
|
+
else meeting_core.T_FREEZING
|
|
94
102
|
|
|
95
103
|
time.sleep(random.uniform(min_sleep, max_sleep))
|
|
96
104
|
if random.random() < crash_rate:
|
package/human_viewer.py
CHANGED
|
@@ -46,14 +46,9 @@ def participants_from_bare(bare):
|
|
|
46
46
|
return meeting_fs.read_protocol(bare).get("participants") or None
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
resultWriter 的 loop 退出(concluded)时保存到该位置,cleanup 兜底再存
|
|
53
|
-
一次;权威单一事实源是 bare 的 `HEAD:result.md`。调用方**无需**推
|
|
54
|
-
resultWriter 是谁、也不必进 work 子目录——分析目录删除后该文件仍在。
|
|
55
|
-
"""
|
|
56
|
-
return f"{base}-result.md"
|
|
49
|
+
# result_path 的实现已迁 meeting_fs(与 RESULT_MD 同家,e2e16 F3);
|
|
50
|
+
# 本模块保留同名引用,既有的 `human_viewer.result_path` 调用方零改动。
|
|
51
|
+
result_path = meeting_fs.result_path
|
|
57
52
|
|
|
58
53
|
|
|
59
54
|
def new_messages(bare, since):
|
package/meeting_fs.py
CHANGED
|
@@ -31,6 +31,21 @@ RESULT_MD = "result.md"
|
|
|
31
31
|
# 写空文件/仅 frontmatter——只查存在性会退化为空提交,审核 A2)。
|
|
32
32
|
RESULT_MD_MIN_BYTES = 50
|
|
33
33
|
|
|
34
|
+
|
|
35
|
+
def result_path(base):
|
|
36
|
+
"""result.md 的固定位(`<分析目录>-result.md`)。
|
|
37
|
+
|
|
38
|
+
**与 RESULT_MD 同家**(e2e16 F3):S1 把"文件叫什么/多大算有效"收进本
|
|
39
|
+
模块,但拼路径留在 viewer —— 组合层(start_discussion 的 `--status`
|
|
40
|
+
打印 `[result]`)为拼字符串 import 展示模块,职责颠倒。路径规则是
|
|
41
|
+
产物契约的一部分,归 fs。
|
|
42
|
+
|
|
43
|
+
resultWriter 的 loop 退出(concluded)时保存到该位置,cleanup 兜底再存
|
|
44
|
+
一次;权威单一事实源是 bare 的 `HEAD:result.md`。调用方**无需**推
|
|
45
|
+
resultWriter 是谁、也不必进 work 子目录——分析目录删除后该文件仍在。
|
|
46
|
+
"""
|
|
47
|
+
return f"{base}-{RESULT_MD}"
|
|
48
|
+
|
|
34
49
|
# 无进展超时兜底(秒)——协议参数的默认值(gen_protocol 固化进
|
|
35
50
|
# protocol.json;engine/fake_agent 的签名默认与 CLI default 同源于此)。
|
|
36
51
|
DEFAULT_STALL_TIMEOUT = 600
|