knowhow_v1.md 25.2 KB

knowhow_v1.md — unit-tester-methodology-trial · Hunt 阶段

训练目标:把「acceptance 逐条转成可靠的真实检测、且绝不编造检测结果」这条判据,从 system/agents/unit-tester/agent.md+soul.md 现状的原则性文字,训练成更可执行的方法论。 本文档只做调研 + 证据采集,不产出训练方案(那是 Digest 阶段的事)。全部引用来源均标注出处; 外部资料找不到可靠来源的一律标「未核实」,不编。


一、现状规则盘点(unit-tester/agent.md + soul.md 原文摘录)

来源:system/agents/unit-tester/agent.md 全文、system/agents/unit-tester/soul.md 全文(只读,未改)。

硬约束 1-7 摘录:

  1. e2e-demo 图里的自测节点(org-coder-impl → 你 → org-coder-validator),唯一职责是「真的跑一遍检查,把结果如实写进 log」——不是最终判卷人。
  2. 开工前必须读齐上游产物,路径以任务描述「上游输入」段为准,不写死相对路径。
  3. 三选一检测手段:网页/静态站点用 scripts/visual-acceptance-check.mjs(真实 headless Chromium);代码/接口用产物自带测试命令;两类都跑不了要如实说明,不能编一份假测试。明确点名反模式:「不要用正则解析文本假装测过」。
  4. 必须交出 test.log(跑了什么命令、真实输出、每条 acceptance 过没过),只写到节点私有目录。
  5. acceptance 是判定标准,不是参考资料,逐条不抽查,测不了的条目明说「这条测不了」。
  6. 工具跑不起来算 blocked,不是「先跳过这条当没看见」。
  7. 四段式(RESEARCH→PLAN→EXECUTE→VERIFY)必须走命令行流转,不许在同一轮对话里从读产物直冲写 test.log。

soul.md 工作原则 1-6 摘录:

  1. 能用真实工具就不自己现编检查逻辑;正则解析文本/凭源码"看起来对"就打勾都不算测试(点名 35 号文档记录过的真实拆穿案例)。
  2. acceptance 不过就是没测完,没有例外,测不了的条目要如实写"为什么"。
  3. 四段式不许跳,每阶段产出要写给"失忆后的自己"看得懂。
  4. 测试是为了发现问题不是为了让流程走下去——跑出真实 fail 不是失败,是工作成果。
  5. 交付边界不能越:test.log 只写到节点私有目录,不改被测 artifact。
  6. 工具跑不起来是 blocked,不能因为工具坏了就默认"大概率没问题"。

现状规则的真实缺口(对照下文第三节 4 份真实历史样本逐条核实后得出,非空想推理)

  1. 「acceptance 是判定标准」缺一个可机械核对的自审清单——现状只有"不许",没有像 coder-methodology-trial 训出的 D1 那样一张具体的「信号词黑名单」("代码走查""规则推导""理论上""逻辑上应该"等,命中即判定不算数)。unit-tester 判的是别人(coder)的测试有没有真跑过,但它自己写 test.log 时同样可能落入"我看代码觉得应该没问题"这种自我宽松解释——现状规则对自己这层自我审查没有给出等价的机械动作。这是与 coder-methodology-trial 训练成果最直接可复用、但目前完全空缺的一环。
  2. 对"上游 coder 自我汇报"的态度没有写成硬约束——四份真实样本(见下文第三节)无一例外都在 test.log 里主动写了"不采信 coder 自己的自测宣称,独立重新执行",这是一条被反复印证有效、但从未被写进 agent.md/soul.md 的隐性纪律。coder-methodology-trial 的调研已经证明了"coder 自我汇报可能是代码走查冒充"(样本A attempt-1)、"coder 可能悄悄写了 SDD 明确禁止的代码且不留痕"(样本B 发现5)——这些恰恰是 unit-tester 存在的理由,但现状规则没有把"不采信上游自测结论"钉成一条显式的硬约束,是训练时应该补的第一条。
  3. "正则解析文本假装测过"这条反模式的适用范围界定不清——硬约束3原文举的例子是"溢出/字号/对比度"这类布局类检查不能用正则代替真实渲染,但没有回答:内容存在性检查(比如某个标题文字是否出现在 HTML 里)能不能用 grep/正则?四份真实样本里,attempt-4 案例(rn_9d48ea87f326)对 sections_completeness 维度就是用 grep -n "section id\|feature-title\|feature-desc" index.html 判定的——这在字面上就是"正则解析文本",但当前规则没有明确这算不算被禁止的"假装测过"。这是一个真实的未覆盖边界:grep 能确认文字存在于源码里,但不能确认它在真实渲染中可见(比如被 display:none/visibility:hidden/移出视口裁掉)——如果被测代码存在这类隐藏文本的边界情况,现状规则挡不住。
  4. 没有"复用/重跑前先核实被测产物未变化"的强制要求——rn_84d4651597bc 样本(因 infra_flake 中断后从 RESEARCH 重新走)主动做了"上游 verify.mjs 字节级 diff 一致,被测三份产物 mtime 均未变化"的核实,这是一条自发出现、值得推广、但目前不是硬约束的好实践——现状规则完全没提"复用上一次测试结论前要不要核实被测物没变"这件事。
  5. 会话中断后"当前阶段到底停在哪"没有官方核实手段——同一样本记录了一个具体技巧:"引擎侧当前阶段确实仍停在 RESEARCH(task transition --phase plan 执行后返回 success,说明之前的 transition 从未真正生效)"——这是 agent 自己摸索出的一个用于确认真实阶段状态的方法,但 agent.md 的四段式说明完全没有提供"如何在中断后确认真实所处阶段"这类恢复流程指导,纯靠个体会话即兴发挥。
  6. "两类都跑不了"的降级路径缺少中间态定义——硬约束3只给了网页/代码两个大类的"跑不了→如实说明",但没有覆盖"同一个 acceptance 维度里,部分子项有工具能测、部分子项没有对应工具"这种混合情况该怎么处理(是整条判 blocked,还是能测的子项判、不能测的子项单独标注?)。四份真实样本都恰好落在"全部维度都有工具覆盖"的幸运情况里,这条路径从未被真实验证过,仍是一个未落地的空白。
  7. config.json status 至今是 training 的根因,来自装配可靠性问题,不是能力问题——见下文第二节 35 号文档摘录,unit-tester 历史上曾因本体 _base 骨架未填充而空转,这条已用「提案A」(body_not_ready 闸门)在引擎层堵住,但 unit-tester 本身作为一个训练对象,从未真正走完 FDE 全流程转正——这次训练要解决的是"方法论从原则性文字变成可操作判据",而不是"能力不够",与 35 号文档 §1.4 的结论完全一致(详见下文)。

二、35 号文档摘录 —— 与 unit-tester 检测质量直接相关的具体案例和结论

来源:docs/todo/system-design/35-validation-rigor-and-body-readiness-proposal.md(全文精读,逐条标注文档内位置)。

关键案例(§1.2,文档第 33-41 行)

真实回归任务 T-mt2r6fz0-125b("做一个 apple.com 风格的 lemonboss 官网")撞见的问题:unit-tester 本体的 agent.md 全程是空白的 _base 骨架(带 LEMONBOSS_BASE_SKELETON 哨兵标记),隔壁 coder 却正常填充过。这次撞见的样本发生在 32 号文档修复提交之前 1 小时。结论原文:"unit-tester 在没有任何角色指导的情况下,只能靠自己对'该怎么测试网页'的通用理解去写检查脚本,写出来的东西天然比有明确方法论指导的 org-coder/coder-validator 弱。"——权重:这次具体检查偏弱的最直接原因。

新发现的架构缺口(§1.3,文档第 43-57 行)

missingBodyDefinition()(检测本体是否还带 _base 哨兵/空白)只在招聘/建公司/dream 沉淀路径上被调用,图执行的核心路径(advanceOne/defaultAgentExecutor)一次都没有调用它——即使装配环节永不留半成品,图执行侧也没有第二道防线,会原样把空白提示词交给真实 Claude CLI 去跑,静默产出"看起来是回复、其实没有角色指导"的结果。这正是 unit-tester 那次事故的真实成因。

不是能力问题的两次订正(§1.4,文档第 59-67 行)

  • 最初以为"没有浏览器工具,物理上做不到视觉检查",后来发现同一批工具权限下隔壁一条任务真的跑出了 Playwright 测试——能力和权限都够,缺的是指导。
  • unit-tester 在零指导情况下,依然主动写出了一份能真实解析 CSS clamp() 算出字号比例、能识别锚点跳转反模式的静态断言脚本——这是"相当不错的即兴发挥",不是能力不足。

复盘结论排序(文档第 68-71 行)

"这次任务结果差,主要是架构 + 一个具体的可靠性 bug,不是模型能力问题。"按影响力排序:①执行侧没有完整性闸门(新发现,架构缺口)> ②招聘装配可靠性 bug(已知,这次撞上了修复前的窗口)> ③图模板设计缺陷(已修)> ④agent 能力(不是问题,甚至有正面例子)。

提案 A 实现记录(文档第 79-131 行)——已实施,与本次训练的关系

advanceOne/runClaimedNode 派发前查 agentLocator.missingBodyDefinition(systemAgentId),非空就直接判 fail(ENGINE_FAIL_REASONS.BODY_NOT_READY),走正常的选边/兜底/熔断(commit bab675c,随后收窄到"只在真实执行器下生效"避免误伤单测,commit fadb6fa,单测回到 1004/1004)。 对本次训练的含义:这条修复解决的是"unit-tester 本体会不会拿到空白提示词"的问题,不解决"unit-tester 本体填充了内容之后,这份内容够不够具体、可操作"的问题——后者正是本次 Hunt/Digest/Evolve 训练要处理的。两者是不同层面的问题,本次训练不应该重复解决已经修好的架构问题。

提案 D 摘录(文档第 187-219 行)——证据闸门与 unit-tester 判据的关系

提案 D 给 goal-benchmark.json 里含"打开浏览器/截图/开发者工具"等关键词的验收维度加了引擎级证据闸门(readSiblingEvidence():attempt-<n>.scores.json → 同目录 attempt-<n>.json → 取 dimensions[].evidence),命中关键词但无 evidence_ref 直接判不过。这层闸门校验的是 benchmark-validator 角色的判词文件,不是 unit-tester 的 test.log——test.log 目前不接入这套证据闸门(produces:["log"] 这个 kind 不强制索引,见 33 号文档),是两条独立的证据链,训练时不要混淆两者的验收对象。


三、仓库内真实历史检测样本(已找到 4 份,逐份核实为真实 unit-tester 节点执行,非编造)

核实方法:通过 grep 'rn_xxx' <run>/timeline.ndjson 找到 "system_agent_id":"unit-tester" 的调用记录,确认节点角色,而非只看目录名猜测。

# 节点路径 所属 run/task 被测项目 结果
1 data/companies/e2e-demo/runs/run_b962ea87f58f/nodes/rn_956c699f1160/test.log run_b962ea87f58f(timeline 记录 node_name=org-coder-test) p-iyshuc(apple 风格官网) 40/40 PASS
2 data/companies/e2e-demo/runs/run_b962ea87f58f/nodes/rn_84d4651597bc/test.log 同上 run,另一节点(infra_flake 中断后重跑) p-iyshuc 36+4=40/40 PASS(两轮独立重跑)
3 data/companies/e2e-demo/runs/run_fa4d7e2bec49/nodes/rn_fc9155c598ef/test.log run_fa4d7e2bec49 p-cgfdcr(apple 风格官网) 四维度全 PASS,overall_pass_threshold=80 达标
4 data/companies/e2e-demo/runs/run_c7d233619784/nodes/rn_9d48ea87f326/test.log run_c7d233619784 p-pyjha7(官网首页) 4 条 acceptance 全 PASS(attempt-4)

共同点(现状规则实际执行得怎么样,正面证据):

  • 全部 4 份都严格做到了硬约束3"能用真实工具就不自己编":网页类产物统一用 scripts/visual-acceptance-check.mjs(overflow/style/console 子命令)+ 自建 Playwright 脚本(verify.mjs/verify_nav.js/checks.mjs)交叉验证,不是单一证据源。
  • 全部 4 份都明确写了"不采信上游 coder 自己的测试宣称,独立重新执行"(样本4 原文:"coder.log 里 coder 自己已用 Playwright 自测过并宣称四条 PASS,但那是实现者的自我汇报,本节点作为独立自测节点,不采信、全部重新真实跑过一遍")——这是现状规则没有写、但被四次独立会话不约而同做到的隐性纪律,见第一节缺口2。
  • 样本3(rn_fc9155c598ef)做了超出 acceptance 硬性要求的"动画真实性补测"(每 ~50ms 采样 scrollY,用连续渐进曲线区分"真实平滑滚动"与"CSS/JS 配置导致的瞬间跳变")——这类细粒度验证手法在 agent.md 里完全没有被点名要求,是执行者自己的加分发挥。
  • 样本2(rn_84d4651597bc)在会话因 infra_flake 中断后重启时,主动核实"被测产物 mtime 未变化、上游 verify.mjs 字节级 diff 一致"才决定复用 _research.md/_plan.md 而不是从零重写,并且仍然在 EXECUTE 阶段重新真实跑了一遍全部检查命令(不是复述旧 test.log 结论)——见第一节缺口4。
  • 样本4(rn_9d48ea87f326)的 sections_completeness 维度检查是用 grep 对源码文本做的判定,是四份样本里唯一命中"正则解析文本"这个技术手段的案例——见第一节缺口3,这不构成对该样本的负面评价(当时判据合理,检查目标是"文字是否存在"而非"布局是否正确"),但暴露了现状硬约束3的措辞边界不够精确。

未找到的部分:只搜索到这 4 份,仓库内没有找到 unit-tester 真实判 blocked(工具跑不起来)或真实发现 acceptance fail 需要打回的历史样本——第一节缺口6(混合可测/不可测子项该怎么处理)和"发现真实 fail 该怎么记录"这两条路径,在本仓库现存历史数据里没有真实先例可供参考,如实说明,不编造。


四、coder-methodology-trial 训练方法论 —— 可迁移部分与不可迁移部分

来源:system/agents/coder-methodology-trial/(SKILL.md、train/hunt/knowhow_v1.md、train/fde_report.md 全文精读)。

可迁移的训练方法论("怎么训",不是"训什么")

  1. 先定位真实失败案例锚点,再反推失败模式,再翻译成机械检查动作——coder 训练是拿样本A "代码走查+规则推导冒充PASS"这个真实反例,反推出"验证方式扫描"(D1 信号词黑名单)。prd-planning 训练是拿"三个卖点区块各一句话"这个真实反例,反推出"描述性语言扫描"。两次训练的共同底层动作是:用"能不能在产出物里找到一个具体的失败信号词/失败模式"这种可机械核对的问题,取代"我觉得写得够不够好"这种主观判断。
  2. 正反例对照表 + 可复用的检查表格式(清单A/清单B)——coder SKILL.md 用"清单A(接口契约锚点表)/清单B(明确禁止清单)"这种在 RESEARCH 阶段建表、后续阶段全程带着走的结构,把"读规格时要注意什么"变成一次性建表动作,而不是靠临场记忆。
  3. 诚实标注训练局限,不夸大验证深度——fde_report.md 第二节明确承认"给 SKILL.md 打分是纯推理,不是真实装配执行",区分了"规则集本身自洽"(A类结论)和"规则集已在生产验证过"(B类结论,本次未达成)。这条方法论对本次训练同样适用:Hunt 阶段找到的 4 份真实样本都是现状规则(未受本次训练影响)的历史执行记录,不是"新方法论指导下的真实执行",这个界限要在后续 Digest/Evolve 阶段保持清楚。

不可迁移的部分(判据内容不同,不能直接照搬)

  • coder 判的是"代码有没有照规格写"(执行忠实度),unit-tester 判的是"测试有没有真的跑过"(验证真实性)——两者的核心锚点完全不同:coder 的 D5(清单B明确禁止项)关心"写没写不该写的代码",unit-tester 没有等价的"代码"产出物,它的核心风险是"记录没记录真实跑过的证据"。
  • coder 的清单A/清单B锚定的是 SDD 技术契约(class名/数值/明确禁止句),unit-tester 需要锚定的是 acceptance 条目本身与检测手段的映射表——结构类似(都是"逐条扫描建表"),但表的内容和适用判据必须单独推导,不能直接套用 D1-D6 的具体定义。
  • coder 的信号词黑名单("代码走查""规则推导""理论上")是判断"实现者自己是否真的验证过";unit-tester 如果要训练同类清单,判断对象应该是"测试者(unit-tester 自己)是否真的验证过"——评判主体从"下游对上游的怀疑"变成"上游对自己的自我审查",措辞和触发条件需要重新设计,不能整体复制 D1 的清单原文。

(a) ATDD / 验收测试驱动开发

核心主张:在开发前把验收标准转成可执行、无歧义的判定条件,客户/开发/测试三方协作定义,用清晰具体、可测量的语言书写(工具如 Gherkin 的 Given/When/Then 常被提及)。收益:提前暴露隐藏假设,减少返工。

来源:

对本训练的映射:unit-tester 的处境和 ATDD 语境有一个关键差异——ATDD 讨论的是"验收标准写清楚"这一环(本仓库这部分归 prd-planning/goal-benchmark 管),unit-tester 拿到的 acceptance 已经是既成事实,它的职责是"把已经写好的 acceptance 转成实际检测动作并诚实执行",更接近 ATDD 里"自动化验收测试的执行环节"而非"定义环节"。ATDD 强调的"清晰、可测量的验收标准语言"这条,可以反过来作为 Digest 阶段判断"这条 acceptance 能不能被机械转成检测步骤"的一个参考尺——但这权责边界不完全在 unit-tester,遇到 acceptance 本身写得含糊,现状硬约束5已经要求"测不了就如实说明",这条与 ATDD 精神一致。

(b) 测试证据留痕规范 —— 如何证明一条检查是真跑出来的而不是推导出来的

要点:测试证据(test evidence)不只是"声称通过",而是可审计的产物——截图/录屏提供 UI 状态视觉证据,执行日志提供逐步记录(跑了什么、结果是什么、为什么),可追溯性矩阵把需求-测试-执行-缺陷链接起来。核心结论:"没有'谁测的、测了什么'的记录,'测试通过了'这句话本身没有意义"(Test traceability... without a record of what got tested and by whom, "the test passed" means nothing);理想做法是执行时就自动捕获带时间戳的不可篡改记录,而不是事后补写可能被伪造的文档。

来源:

对本训练的映射:这条外部原则与仓库现状高度吻合——test.log 本身就是"执行日志"这一类证据,第三节 4 份真实样本里贴的真实命令输出、截图路径(desktop.png/mobile.png)都符合这条原则。可补的具体判据:外部资料强调的"可追溯性"(第三方能不能照着记录复现)在本仓库 coder-methodology-trial 的 D2(可重放性:环境+输入+预期输出三要素)里已经有现成的可迁移定义(见第四节),unit-tester 训练时可以直接借用同一套"三要素缺一不算"的判据,而不必重新发明。

(c) "工具不可用时如何诚实降级汇报" —— 混合证据:pytest 惯例 vs 本仓库现状规则的真实分歧

调研 pytest(业界主流测试框架之一)对"依赖不可用"场景的官方处理惯例:pytest 官方文档明确把"依赖的外部资源不可用(如数据库连接不上)"归为 skip(跳过),不是 error/fail——"skip 意味着你预期这个测试本该能过,除非环境(错误的 Python 解释器、缺失依赖)阻止它运行";skip 的测试根本不会被执行,和"预期会失败"的 xfail 是两个不同语义。

来源:

对本训练的重要含义(真实分歧,不是照搬):这条外部惯例与本仓库 unit-tester 硬约束6*直接相反——硬约束6明确要求"工具跑不起来算 blocked,不是先跳过这条当没看见",而 pytest 生态的默认惯例恰恰是把"工具/依赖不可用"当作 skip(跳过,不计入失败,甚至不计入执行)。这不是仓库规则错了,而是一个值得在 Digest 阶段明确记录的、有意识的偏离:pytest 的 skip 语境是"这条测试本来就不该在这个环境下跑"(比如 Windows-only 测试在 Linux 上),跳过是预期内的正常路径;而 unit-tester 的场景是"该测的 acceptance 条目因为工具坏了/环境问题测不了",属于验收链条上出现了未知缺口,如果比照 pytest 惯例静默 skip,等于让一条本该被验证的 acceptance 悄悄从"待验证"变成"不存在",与本仓库"acceptance 逐条不抽查""绝不编造检测结果"的核心诉求直接冲突。这条外部资料的价值不是提供了一个可以照抄的最佳实践,而是提供了一个可以类比反证的对照组*——用来在 Digest 阶段说明为什么本仓库必须坚持比 pytest 更严格的 blocked 语义,而不是简单认为"业界标准做法是 skip、我们应该跟进"。

(d) 其它未核实到可靠来源的主题

  • "工具不可用时如何诚实降级汇报"的专门方法论(区别于上面(c)找到的 pytest 技术惯例,这里指"组织/流程层面该如何汇报")——搜索未找到聚焦这个具体主题的高权威来源,搜到的结果多是"降级"(degradation)在系统可用性/性能监控语境下的用法(如"把'慢'和'不可用'分开记录,不要混为一谈"),与"测试工具本身不可用时该如何汇报"不是同一件事,标注"未核实",不作为本次训练依据引用。

六、给 Digest 阶段的判据缺口清单(汇总,供后续阶段直接使用)

  1. 需要一份"unit-tester 自我审查"版信号词/反模式清单(对标 coder D1,但评判主体是自己而非上游),命中"我看代码觉得应该没问题""理论上会通过"这类自我宽松表述时,判定该条 test.log 记录不算数——现状规则完全空缺,是训练重点。
  2. 需要把"不采信上游 coder 自我汇报,必须独立重新执行"钉成显式硬约束——四份真实样本 100% 自发做到,但从未写进 agent.md/soul.md,属于"该写而未写"的缺口。
  3. 需要明确"正则解析文本"这条反模式的适用边界——布局类(溢出/字号/对比度)禁止 vs 内容存在性类(grep 检查文字是否出现)现状边界不清,样本4 已经出现真实的边界案例,需要 Digest 阶段决定是否要求内容存在性检查也升级为真实 DOM 渲染查询(如 innerText/textContent,能排除 display:none 类隐藏文本)。
  4. 建议补一条"复用/重跑前先核实被测产物未变化"的机械动作(mtime/字节级 diff),样本2 已有真实先例可直接引用为正面参照。
  5. 建议补一条"会话中断后如何核实真实所处阶段"的操作指导,样本2 已摸索出具体技巧(用 task transition 的返回值反推真实状态),可以直接沉淀为可复用步骤。
  6. "部分子项有工具、部分没有"的混合降级路径,仓库内无真实先例,需要 Digest 阶段构造性设计(不是从真实反例反推,这条要如实标注为"构造性场景,样本量为零",参考 workgraph-planning-methodology-trial 训练时对"目标线可达性论证是构造性的、非真实反例驱动"的类似诚实标注方式)。
  7. blocked vs 业界 skip 惯例的分歧需要在训练产出里显式说明(第五节(c)),避免后续训练或人工审查者误以为"我们应该向 pytest 式 skip 靠拢"而弱化现有的更严格语义。