test_step6_stop_state.py
6.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#!/usr/bin/env python3
"""VERIFY: Step6 报告「三态停止标记」契约 + 超出项分型 gap
(agent-traffic-light-stop-protocol, learn_queue #1403, 2026-10-13 Step6 审计)
学到的知识(原文 L9/L12):
1. 开工第一轮用一行固定目标清单钉住原始需求;此后每轮最后一行输出三态进度条
🟢 还差/🟡 原始需求已达成且超出项能串成有执行标准与验收标准的 Plan/
🔴 原始需求已达成且超出项零散不值得单开一轮(仅压三行存档)。
2. 🟡 落盘成 plan/handoff 等启动指令;转黄/转红后一律停手等指令。
3. 分界判据=超出项能否构成「目标 + 验收标准」的可执行 Plan,缺一即 🔴。
判据句:「Agent 永远不会主动说做完了——每轮结尾的『推荐下一步』把半小时需求拖成一整天」。
原文反例:红灯下罗列的「已完成」(PR 合并 / 远端分支删除)重开对话查证后一件都没做
⇒ 完成声明必须可查证,未查证的不计入达成。
病理 WHERE=step6_apply apply-done 的报告收尾契约
WHY=契约末项只要求自由文本「建议: [下一步行动]」,无收尾态声明、无 🟡/🔴 分型
⇒ 超出项既不落盘成可执行 Plan,也不被压档丢弃,散装建议无限累积。
实测:circle.db 中 46 份 apply_audit,含 🟢/🟡/🔴 三态标记者 = 0。
修复:
1) `_has_stop_state(text)`(全文检测,与存储解耦,CLM #1323 同口径)
2) Step6 报告模板:`- 建议:` → 「超出项 🟡 / 超出项 🔴 / 收尾态」三行 + 收尾纪律段
3) `_on_apply_done` 观测登记:缺失 → gate=NO_STOP_STATE / action=CONTINUE
4) VerifyReason 登记 NO_STOP_STATE(定义 + VERIFY_REASON_CODE 描述)
TDD:
TC1 (RED/复现旧病理) 旧契约下「以散装建议收场」的报告被静默接受 → 收不住
TC2 (GREEN/真实数据) circle.db 实测:46 份 apply_audit 含三态标记 = 0
TC3 (GREEN/判别力) 三态行→True;裸 emoji 无标签→False;只有「已完成」→False
TC4 (源码守卫/防回退) app.py 含 helper + reason code 定义/注册 + 模板三行 + 登记块
TC5 (冻结重跑 spot check) 同一输入 3 次采样,结果一致(确定性纯函数)
"""
import pathlib
import sqlite3
APP = pathlib.Path("/data/data/com.termux/files/home/wechat-viewer/app.py")
DB = pathlib.Path("/data/data/com.termux/files/home/wechat-viewer/circle.db")
FAIL = 0
def old_accepted(report_text, has_human_flag):
"""复现旧契约:只要有报告 + human gate 关键词就算收尾合格(无收尾态要求)。"""
return bool(report_text) and bool(has_human_flag is not None)
def has_stop_state(text):
"""复现新逻辑(与 app.py `_has_stop_state` 同判据)。"""
t = text or ""
has_label = ("收尾态" in t) or ("停止态" in t)
has_light = any(lamp in t for lamp in ("🟢", "🟡", "🔴"))
return has_label and has_light
def check(name, ok, detail=""):
global FAIL
print(("PASS " if ok else "FAIL ") + name + (f" — {detail}" if detail else ""))
if not ok:
FAIL += 1
def main():
scattered_report = ("# Step 6 知识应用报告\n- 发现但未修复: 无\n"
"- 建议: 1) 优化索引 2) 添加回滚测试 3) 更新文档\n"
"- Human gate: 无\n")
print("TC1 复现旧病理:散装建议收场的报告被旧契约静默接受")
check("旧契约接受散装建议报告", old_accepted(scattered_report, "无") is True,
"旧契约 = 报告存在 + human gate 关键词,不校验收尾态")
check("新契约拒绝同一报告", has_stop_state(scattered_report) is False,
"无收尾态标记 → NO_STOP_STATE")
print("\nTC2 真实数据:46 份 apply_audit 含三态标记 = 0(收尾态契约从未生效)")
try:
conn = sqlite3.connect(str(DB))
total = conn.execute(
"SELECT COUNT(*) FROM learning_results WHERE result_type='apply_audit'"
).fetchone()[0]
marked = conn.execute(
"SELECT COUNT(*) FROM learning_results WHERE result_type='apply_audit' "
"AND (result_summary LIKE '%🟡%' OR result_summary LIKE '%🔴%' "
"OR result_summary LIKE '%🟢%')"
).fetchone()[0]
conn.close()
check("存在 apply_audit 样本", total > 0, f"total={total}")
check("含三态标记者 = 0", marked == 0, f"marked={marked}/{total}")
except Exception as e: # 库不可达不阻断判别力测试
check("真实数据可读", False, f"{e}")
print("\nTC3 判别力:标签 + 状态灯双命中才算,单命中不算")
check("完整三态行 → True",
has_stop_state("- 收尾态: 🟡 超出项已成 Plan 并落盘 ~/pixelphone/plans/x.md") is True)
check("🔴 零散收场 → True", has_stop_state("- 收尾态: 🔴 超出项零散已压档") is True)
check("裸 emoji 无标签 → False", has_stop_state("- 🟡\n- Human gate: 无") is False)
check("只有「已完成」→ False", has_stop_state("- 已完成: 全部修复完毕") is False)
check("空文本 → False", has_stop_state("") is False)
print("\nTC4 源码守卫(防回退)")
src = APP.read_text()
check("保存 helper `_has_stop_state`", "def _has_stop_state(" in src)
check("保存 reason code 定义",
'NO_STOP_STATE = "NO_STOP_STATE"' in src)
check("保存 reason code 描述注册",
"VerifyReason.NO_STOP_STATE: " in src)
check("模板含「超出项 🟡 (可执行 Plan)」", "- 超出项 🟡 (可执行 Plan):" in src)
check("模板含「超出项 🔴 (零散观察)」", "- 超出项 🔴 (零散观察):" in src)
check("模板含「收尾态:」末行", "- 收尾态: [" in src)
check("模板不再含裸「- 建议: [下一步行动]」", "- 建议: [下一步行动]" not in src)
check("保存观测登记块", "node=step6_apply.stop_state" in src)
print("\nTC5 冻结重跑 spot check:同一输入 3 次采样(确定性)")
samples = [has_stop_state("- 收尾态: 🟡 已落盘 ~/pixelphone/plans/a.md")
for _ in range(3)]
check("3/3 一致", samples == [True, True, True], f"samples={samples}")
print(f"\n{'ALL PASS' if FAIL == 0 else f'{FAIL} FAILED'}")
return 1 if FAIL else 0
if __name__ == "__main__":
raise SystemExit(main())