Archived
docs(w05): 重写收口 —— 头注核正、字段说明按现状重写、规范补四条 auto 纪律、CLAUDE.md 记战果
Task 12 的非走查部分(浏览器走查卡在登录、归人工,不在本次范围)。 ① index.ts 头注仍写着旧的「三条技能 + 五张模版」,重写后是 0 技能 3 张件,核正 (Ruling H25);包内其余「三条技能/五张/两枚工具/九步」等提法逐处核过,均为带日期的 「从…改成…」历史叙述,不是失实断言,未动。 ② docs/数据域字段说明-W05.md 整篇重写(不是打补丁)。旧文档写的是重写前的十张表 (customer.profile/parts.document/demand.request 与 sub_eval 三张旧单据,本活动这次 重写已不再声明),现按 manifest.ts 的 declarations.ledger 逐表核对重写:9 张表、66 列、 20 人填列。写入者词表另定一套(人(件上)/AI(S2 唯一一次 analyze)/配方(project 算出来 的)/基座)——E04/I01 那套多轮 agent 编排的词表在这里说不清楚。逐个 data-edit 核对过 report.ts 才确认写入者归属:S3 的四态等列改成人在件上直接编辑(不再是「人写意见→AI 改」), sub_eval.part/case、substitution.relation、validation.record 四张表没有任何人工编辑 入口、全部配方算。顺手核了 seed-mvp-data.ts 的基线物理列,注了每张表里这次重写不再 声明的旧列(旧九步版本留下的痕迹),并记下 type_id 这条身份链路是本仓唯一一处 integer→text 加宽声明。 ③ docs/活动开发规范.md §4.6 补四条纪律(六条→十条):jsonb 会重排 fields 键序、字段 求值改按依赖拓扑排序不能拿书写顺序当依赖顺序;跨笔顺序仍要自己排(一笔读的是另一笔 写出来的数就要排后面);两笔方向相反的依赖拆两笔写中间夹子表;gateLedgers 同时管 「闸要求非空」与「确认时并进正式表」两件事,漏想一半会悬空引用或永久卡死。§6.5 补第 9 条「测试要能咬住」(镜像测试与读真文件测试证明的是两件事;变异测试本身可能是空的; 能 import 真东西就别手抄)。§7 补两行排障(reset-runs 前先捞证据;dev 进程可能在跑 几小时前的代码,连 tsup --watch 监视进程本身都可能已死)。八条内容逐条去任务报告核过 原始事故与提交号,没有凭印象转述。 ④ CLAUDE.md 现状快照追加一条:四步全 auto、两张类型字典、三张件、退役清单;e2e 三例 released 被拒 0(候选缺库/都在库/原PN缺库,模型调用恰 1 次);类型不一致那例撞上已记 debt.md 的 prune 死锁,两颗都缺库那例卡在「答完卡不推进」、真因未定位如实记录; judgement_class 7/7 逐行核对通过;jsonb 键序地基缺陷的发现与修法(拓扑排序,6636abf)。 顺手核正文档地图里 W05 那一行的旧字段数(十张/110列→九张/66列,与②同步)。 pnpm gates 退出码 0(2447 passed | 34 skipped),与 Task 11 最后一轮记录一致,无回归。 未碰 manifest.ts 里那处与本任务无关的未提交版本号改动(2.1.5→2.1.6,疑似上一个卡在 登录的浏览器走查会话留下)。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
+68
-1
@@ -348,7 +348,7 @@ W05 的两张数据工单(人填的值已入库、必填不缺、逐颗对账)
|
||||
(读人写的自由文本、落到具体的行上)恰恰是模型该干的活,收在它前面等于把半 auto 的
|
||||
后半截整个删掉。写半 auto 的步时记住这条:`guide` 不空,这一步答完卡不会被引擎抢先收掉。
|
||||
|
||||
**六条纪律**(每一条都是踩出来的):
|
||||
**十条纪律**(每一条都是踩出来的):
|
||||
|
||||
1. **行由确定的那一侧决定,不由 AI 决定。** 明细表要按类型字典 `each` 出来,AI 的出参只是拿主键
|
||||
`lookup` 的一张查表 —— 它漏一条,那一格就是空,`required` 立刻变成 `_missing > 0`,闸与放行门当场拦住。
|
||||
@@ -366,6 +366,56 @@ W05 的两张数据工单(人填的值已入库、必填不缺、逐颗对账)
|
||||
6. **`card` 之后的那几笔,要等人交回之后的那一拍才跑。** 卡真发出去那一拍到此为止;
|
||||
人交回之后那一拍,`card` 这一笔被跳过、后面的照跑。所以「铺格 → 发卡 → 收口整理」读起来像一拍连做,
|
||||
其实是两拍 —— 收口那一笔看得见人填的值,这正是它该在的位置。装包时会为此报一条 warn 提醒。
|
||||
7. **`fields` 里的字段书写顺序不是依赖顺序,不能拿文本次序当保证**(2026-08-30,真跑 e2e
|
||||
逼出的地基缺陷)。活动清单存进 `sys.activity_package.manifest` 这个 **jsonb 列**之后,
|
||||
PostgreSQL 按「先短后长、同长字典序」**重排**了每一个嵌套对象的键 —— 而 `project`/`analyze`
|
||||
的 `fields`(以及 `analyze.input.fields`)原来是靠 `Object.entries` 逐字段求值,后一个
|
||||
字段能通过 `ctx.computed` 看见前一个已经算出的值,「声明序 = 依赖序」是一条从没写下来、
|
||||
一进数据库就被打破的隐藏假设,而且**不报错**,只是答案算错了(W05 实证:合成列
|
||||
`judgement_class` 依赖 `difference_status`,被 jsonb 重排到它前面,读到 `undefined`,
|
||||
三级 `when` 全部落空、摔进兜底档;`difference_status` 本身走 `lookup:'result:items'`
|
||||
不依赖字段序,所以是对的 —— 「一个对一个全错」的怪现象由此而来。**隔离测试复现不出来**,
|
||||
因为它直接读 `dist/manifest.json`,从不经数据库往返 —— 只有真跑 e2e 才会现形)。
|
||||
**现在字段求值改按依赖关系做拓扑排序**(Kahn 算法,`packages/utils` 的 `topoOrder`),
|
||||
不是「迭代到不动点」—— 后者是数值收敛判据,会把「结构上真有环、只是两个字段的
|
||||
`else` 分支恰好在错误状态下互相稳定」这种配方误判成排好了,拓扑排序不会有这个假阴性。
|
||||
真正的环在**装包静态审**那一关当场拒装,不兜底、不静默放行。**写配方时不要假设
|
||||
「后一个字段能看见前一个」是因为你把它写在了后面**—— 它能看见,是因为拓扑排序算出来
|
||||
该这么排;把 `when`/`lookup`/`cat` 引用了哪些字段名想清楚,排序自然是对的,想不清楚就
|
||||
会在装包时被拒并点名牵涉的字段。
|
||||
8. **跨笔的依赖仍然是你自己排的顺序**(拓扑排序只管同一笔 `fields` 对象内部的字段序,
|
||||
`do` 数组里 `project`/`analyze`/`card`/`say` 各笔之间的次序不受它管)—— **一笔读的是
|
||||
另一笔写出来的数,就必须排在那一笔之后**。W05 的 S1 曾把摆给人看的 `gate_reason`
|
||||
(案卷那一笔)排在铺主数据/参数骨架那两笔之前:`gate_reason` 的 `when` 链要读
|
||||
`parts_profile_missing`/`parts_parameter_missing`,而这两个基座读数是骨架那两笔刚
|
||||
建出来的行撑出来的 —— 排在前面,它读到的是**这一拍刷新之前**的旧数(参数骨架那一刻
|
||||
还不存在,`_missing` 读到 0),片刻之后骨架一建好又变了。收步与发卡的把关本身没坏
|
||||
(`card.when` 用的是当下真实读数),坏的是**摆给人看那句话**—— 人最需要解释「还差
|
||||
什么」的时候,案卷里存的、卡面上摆的,是一句空话。
|
||||
9. **两笔之间可能存在方向相反的依赖,一趟写不出这个顺序**—— S2 曾把关系表
|
||||
`substitution.relation` 整笔排在差异明细 `substitution.difference_item` 前面:
|
||||
差异明细的主键列声明了 `refersTo`(要求指向的关系行已经真实存在),而关系表的
|
||||
推荐等级/风险/摘要/理由这几个必填列又要读明细表的合成列分布**才算得出来**—— 关系表
|
||||
排前面,`refersTo` 拦住明细表(「要指的那一行还不存在」);排后面,关系表自己那几个
|
||||
必填计算列无从算起。**治法是把关系表拆成同主键的两笔投影,中间夹着差异明细**:
|
||||
第一笔只把主键与不依赖统计的上下文列先摆出来(满足 `refersTo`,这一刻明细表还没有
|
||||
行,所有分布读数都是基座铺的 0,`when` 链落到默认分支 —— 字段值不对但写得出、不报错);
|
||||
第二笔排在明细表已经真实写入之后,用**同一份**投影重算一遍,把第一笔的占位值覆盖成
|
||||
真值。这不是新发明的招式 —— 与 S3 每次人改了四态就要重算关系表,是同一套幂等重算
|
||||
机制,写配方时复用同一个常量,不要抄两份(抄两份早晚分叉)。
|
||||
10. **`gateLedgers` 同时管两件事,漏想一半就会出真人会踩到的坑**:①闸要求哪几张表非空
|
||||
(`_rows > 0`)—— 但「零行 = 没干完」这句话只对**这张卡一定会写**的表成立,列进一张
|
||||
**只是有时候才写**的表会在合法路径上误拦(W05 吃过这个亏:`parts.chip_type` 若不给
|
||||
编辑手段就列进闸,挑一个已有类型是主路,这条路上它天然零行,闸永久判「没干完」,
|
||||
人把别的表填得再全,提交也是 400);②数据工单确认那一刻把闸上点名的表**并进正式表**
|
||||
(`landsOnConfirm`,§3.1)—— 从闸的名单里删掉一张表只顾了①,却会让「引用它的表已经
|
||||
落地、它自己没有落地」:别的表用 `refersTo` 指着它,落地那一刻起正式表里就有一行悬空
|
||||
引用。W05 的解法是让配方**自己把选中的行原样自投影一遍**(不论新建还是选既有的都投,
|
||||
幂等、不产生多余写入)—— 这样 `mine:` 恒非空,该表**同时**满足「闸不会误拦」与
|
||||
「确认时有行可落地」两个要求,可以放心留在 `gateLedgers` 里。**卡上闸住的每一张表,
|
||||
件上必须真有一种写它的手段**(人在件上直接编辑,或配方自己投影进去)—— 否则那颗
|
||||
「确认」钮永远按不下去,而且错误提示只会说「这张卡就是来建它的」,指向一个界面上
|
||||
压根摸不到的操作。
|
||||
|
||||
---
|
||||
|
||||
@@ -560,6 +610,21 @@ A1 是 `customerEmail / subject / body / attachments`)。
|
||||
「稳定的活动出错」比新功能不 work 伤得多。
|
||||
8. **用户重复提的要求,说明上一轮没打中病根**:往下再挖一层,别在同一层再修一遍
|
||||
(「工作成果 1:1」挖了三层才到「办结那一刻存快照」这个真解)。
|
||||
9. **测试要能咬住,不能只是好看**(2026-08-30,ACT-WORK-05 重写全程验证方法论沉淀):
|
||||
- **镜像测试证明的是机制,只有读真文件的测试才证明「那个文件此刻是对的」**—— 两码事,
|
||||
要写两条测试。手写数据喂给一个函数、验证它的行为符合预期,证明的是这段逻辑本身没错;
|
||||
但生产代码会漂移,fixture 常常把漂移的差别悄悄抹平(实测账:真身是 `f.label ?? f.name`,
|
||||
手抄件是 `f.name`,fixture 从没暴露过这个差别)。少了一条「import 真身」或「读真文件
|
||||
逐字比对」的测试,这个洞永远合不上。
|
||||
- **变异测试本身也可能是空的**—— 撞见过单行行组下 `tr:last-of-type` 与
|
||||
`tr:first-of-type` 天然等价(表只有一行时,「最后一个」与「第一个」是同一个元素);
|
||||
往真代码里改一个明摆着错的选择器,6 条「变异」测试全绿零反应,证明测试从没真正执行到
|
||||
那段逻辑。写变异测试要**先让它红过一次**,不红就是测试没咬住东西,不是代码没问题。
|
||||
- **能 import 真东西的,一律 import,不要手抄**—— 手抄件从诞生那一刻就开始漂(同一轮
|
||||
连吃三次这个亏:配方读数函数的手抄漂移、`let` 绑定的手抄漂移、DOM 选择器复刻的手抄
|
||||
漂移)。跨模块分层边界(如 `activities/` 与 `apps/api/` 互不静态 import)是少数几个
|
||||
不得不手抄的理由之一;分层边界**内部**(如 `apps/api` 各模块之间)没有理由手抄 ——
|
||||
通常只是少加了一个 `export`。
|
||||
|
||||
## 7. 排障速查表(症状 → 根因 → 处置;全部实测账)
|
||||
|
||||
@@ -606,6 +671,8 @@ A1 是 `customerEmail / subject / body / attachments`)。
|
||||
| 收口前一两拍预算耗尽 | budget 给太紧 | §5.2-⑦ 留一倍余量 |
|
||||
| 代答脚本交执行卡被拒「必填字段未回填:结果」 | 卡没声明 `fields` 时基座兜底加必填「结果」回执字段 | 交卷 `values.result` 带一句 |
|
||||
| 验收 pass 后落地判「选择组未选出口」 | routedPort 只在前端回传里顶上(已修:服务端发事件前算有效口) | 升级基座;临时:回传自带 `selectedPorts` |
|
||||
| 排障时按协议等了、`advancing_until` 也查过是空,还是查不到那条旧任务的证据 | 诊断前先跑了 `reset-runs`,任务行与 `task_memory` 一并清空了 | 排障留痕要在 `reset-runs` 之前捞;要腾新案例的跑道,先把要查的证据记下来再清(2026-08-30) |
|
||||
| 修完平台代码,行为却和源码对不上,`touch` 源文件也没用 | dev 进程跑的是几小时前的旧代码,连自动重建的 `tsup --watch` 监视进程本身都可能已经退出 | 核 `ps -p <pid> -o lstart=` 与最近一次改动的时间对不对得上;不对就整套重启(kill + 重新 `pnpm dev:api`),别指望 `touch`(2026-08-30) |
|
||||
|
||||
## 8. 待实现与已知不一致
|
||||
|
||||
|
||||
Reference in New Issue
Block a user