feat(contracts): PlanStep.humanWrites —— 人填的表模型不许写;还欠一张卡的那一步不摆 doneWhen

柴浩实测单 6ddcecf8(连撞五拍熔断):P-10 资料记录的标题 / 版本 / 链接与台账里 `LM2596S-5.0` 那一行
**逐字相同** —— 不是幻觉,是「手边有一行看着对的就拿来用」(那一步 reads 摊开了 `parts.document`,
视野里唯一一份真资料就是原 PN 的)。行找对了、料找错了,而这份假证据会直接喂进差异比对当「硬约束有证据支持」。
三层合谋:①把 P-08 的回卡认成 P-10 的(P-10 的卡从没发过);②08-27 给这一步加的 doneWhen 把
「资料必填不缺」摆成目标、后面还跟「成立了就 finish」,**与同一份提示词里「唯一合法的动作是先发那一张卡」
直接打架**;③它顺手把 `exists_status` 改成 created,按剧本发卡名单自己空了 —— 一个合法动作都不剩。

止血两刀:
①**`PlanStep.humanWrites`**(契约,可选字段、老活动一个字不用改):声明的台账 `write` 当场拒并指路
  「去发那张卡,值由人填」;`drop` 不拦(写错了得撤得掉)。判法认点名的项,点不出退回当下在办的那一步
  (压拍时骨架写在上一步名下正好放行)。**只声明「模型永远没有来源」的表** —— 有附件时该预填的
  (P-08 的参数值从 datasheet 抽)别声明,那是它的活。
②**还欠一张卡的那一步不摆 doneWhen** —— 判据同时是摆在模型眼前的目标,而它此刻只许发卡。

W05@1.6.5 声明 `humanWrites: ['parts.document']` 并从 writes 去掉。4 测(墙 / 放行 / drop / 声明)。
**一个闲着的模型 + 一个它够得着的判据 = 它一定会去凑那个判据。**
契约变更四步:depcruise 反查下游绿、loop-engine CONTRACT.md 同步、api-report 随本提交入库、
可选字段非破坏性。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0167qWjeyBwvfdGovd7FWbCu
This commit is contained in:
xcs
2026-08-28 12:38:26 +08:00
co-authored by Claude Opus 5
parent e1d8d86b16
commit 2cef6f7489
11 changed files with 157 additions and 4 deletions
@@ -239,6 +239,7 @@ P-11 一条(`LM2596S-3.3 → LM2596S-ADJ | alternate | fae_confirmed`);P-01 回
| AA | **数据工单确认了,数据没入库**:卡上写着「确认后这批主数据即入库」,人建好的 P-08/P-10 记录要等整条评估放行才进正式表;试跑单(不落主数据)永远不进(2026-08-27 用户指出) | 草稿层通往正式表只有整单放行一道口子(`releaseWrite(taskId)`),回传路由只记办结 + 抛人事件;而引擎的 `mine:` / `_rows` / 哨兵全靠草稿层认「本单写下的行」,清掉就断 | `releaseWrite(taskId, {only, cause})` 范围放行:只并这张卡 `gateLedgers` 那几张表,并过的行**留在草稿层打 `landed` 记号**(读面不变,整单放行不再写);回传路由在保存与回传落定后调它,痕点名几张表各几行;`createOnly` / `drop` 对已落地行改口(别 drop,撤了读面少一行);活动 1.4.8 剧本改口「已入正式表」 | **平台**(结构)+ 活动 |
| AC | **两颗输入电压范围一模一样却判 MISMATCH、整颗不推荐**(2026-08-27 用户送来 0bddd862):客户写「输入电压在 45V 以内」,模型译成 requirement `>= 45`,原 PN 与候选上限都是 40V | `judgePair` 客户约束一票否决:候选过不了客户线就 mismatch,不看原 PN 自己过不过得了 —— 需求边界与在用料的差距被记在候选头上 | 工具 1.1.0:候选与原 PN 相等 / 盖住且原 PN 也不满足客户线 → 仍按比对结果判(MATCH,风险中),备注点名「需求边界,请回头核对需求」;候选更窄且过不了线仍 MISMATCH | **平台**(工具) |
| AD | **比对没有「认真比每一条」的机制**(2026-08-27 用户:「这个做差异评估比对的 harness 肯定有问题」):模型交代里念出「输入上限 32V 低于客户 40V」—— 这两个数不是这单的,是 S3 剧本示例的原话;工具入参只存哈希,喂了哪 9 对值事后查不到;写回台账时还把工具的 `equal` 改成了 `less` | 比对入参由模型从台账抄给工具(抄错 / 抄示例都拦不住),出参再由模型抄回台账,两头都没有基座核;示例引了具体值 | 工具 1.2.0:只报原 PN + 候选料号 + 客户约束,参数 / 封装 / 生命周期 / 证据引用由工具从 parts.* 现读(taskId 经运行时交给处理器,读任务视图);出参带原值,写盘照抄;≤800 字的工具入参整份留痕;剧本示例只留占位形 | **平台**(工具运行时 + 工具)+ 活动 |
| AE | **AI 把原 PN 的规格书填到了候选料号的资料记录上**(2026-08-28 柴浩实测,单 6ddcecf8):P-10 那一行的标题 / 版本 / 链接与台账里 LM2596S-5.0 那一行逐字相同;随后 `finish S0d_p10_document` 连撞五拍墙熔断 | ①它把 P-08 的回卡认成了 P-10 的(卡从没发过);②我 08-27 给这一步加的 doneWhen 把「资料必填不缺」摆成目标、还配一句「成立了就 finish」,与「唯一合法的动作是先发那一张卡」在同一份提示词里打架;③它顺手把 `exists_status` 改成 created,发卡名单自己清空 —— 一个合法动作都不剩 | 剧本 `PlanStep.humanWrites` 声明「这几张表归人填」,模型 write 当场拒、drop 不拦;还欠一张卡的那一步提示词不摆 doneWhen;W05 的 S0d_p10_document 声明 `humanWrites: ['parts.document']` 并从 writes 里去掉 | **平台**(引擎 + 契约)+ 活动 |
**可推广的三条**(已记 `docs/debt.md`):
+8
View File
@@ -295,6 +295,14 @@ start 已收项无事发生同族)。剧本里也别再教 start —— 教它
(与模型同拍撞车时:引擎收过了,模型这拍的 finish 当已完成,不抛非法迁移);
**拍尾再求一次**(人答卡那一拍里模型写完东西 doneWhen 才真,不等下一拍;这一拍刚发卡的项不算)。
**人填的表要声明 `humanWrites`**(2026-08-28 立):数据工单要人在件上补、而模型**永远没有来源**的那几张表
(P-10 的资料标题 / 版本 / 文件链接),写进剧本的 `humanWrites` —— 模型 write 当场拒、drop 仍可撤。
不声明的后果实测过:模型被 doneWhen 逼着凑「必填不缺」,把台账里**原 PN 那一行**的规格书逐字抄到候选料号头上,
行找对了料找错了,而这份假证据会喂进差异比对当「硬约束有证据支持」。
**只声明模型没有来源的表** —— 有附件时该预填的(P-08 的参数值从 datasheet 抽)是它的活,别拦。
配套:**还欠一张卡的那一步,提示词不摆 doneWhen** —— 判据同时是摆在模型眼前的目标,而它此刻只许发卡。
**一个闲着的模型 + 一个它够得着的判据 = 它一定会去凑那个判据。**
**人工激活的项也可以给 doneWhen,引擎只在人答过卡之后才求它**(2026-08-27 晚立):
门槛是 `worldTouched`(卡真发过)且没有未答的卡 —— 卡没发过时 doneWhen 再真也不收,
那才是替人签字。答过之后,「办完了没有」若是基座数得出来的事实,就写成 doneWhen 交给引擎: