#!/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())