Archived
fix(w05): S3 复用 S2 投影必须自带 let —— 主键靠它绑,少了就写丢或拼出垃圾行
评审 Critical:S3_review.auto 复用 RELATION_PROJECTION/VALIDATION_PROJECTION 时没有自己的
let,而两笔投影的主键/外键(relationId/evalRef/originalPn/candidatePn/typeId)全靠 let 绑的
名字 —— createAutoRunner.run() 每次都从它收到的那份 recipe.let 现算 lets,不跨步传递
(auto-runner.ts:93-97)。后果:关系表主键算成空串,写被"主键算不出来先不写"悄悄丢掉,人在
审核件上改的四态永远不会反映到推荐等级上;验证记录的主键 cat:['VAL-',{in:'evalRef'}] 反而
拼出非空的 'VAL-',写出一行垃圾,破了"P-06 一单固定一条"的不变量。
brief 让两步共享同一个对象引用(toBe 断言),推翻了 spec"S3 的配方里复制一份 ③④ 两笔……
一步一份配方,不做跨步回跳"的原话。采纳评审方案 (i):把 let 也提成模块级常量 SHARED_LET,
S2 与 S3 的 auto 各自持有 let: SHARED_LET —— 绑定的文本仍只有一处真源,运行时各自完整可求值。
核对过 SHARED_LET 五条绑定只读 sub_eval.case/sub_eval.part(S1 写、S2/S3 都不改),S3 那一拍
与 S2 那一拍算出的值逐字相同,方案 (i) 直接适用。
新增执行级回归测试(w05-s3-review-recompute.test.ts,真 createAutoRunner + 手写假端口,
参照既有 w05-gate-reason-order.test.ts 的搭法):模拟人在审核件上把一条四态改成 hard_mismatch
后重跑 S3,断言关系表真的被重算更新、验证记录仍只有一行。变异验证:去掉 let 复现两种失败
(主键算不出来的行丢失 / 拼出 'VAL-' 垃圾行),加回来两个断言都过。
顺带补 docs/debt.md 一条平台缺口:static-audit.ts 的 walkExpr 对 when/lookup 有校验分支,
唯独没有裸 {in:X} 这一支 —— 未绑定的名字装包时不出声,只在运行期现形。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
@@ -1431,3 +1431,30 @@ nudge:[{after:'PT2H',action:'nudge'}], retry:{times:2,backoff:'PT30S'}`)慢慢
|
||||
本身表达不了的跨字段约束)就地重问一次」的档,与 `agent-runner` 对模型输出解析不成形时
|
||||
「就地重问一次,原文经 `note` 存工作日志内部痕,第二次仍不成形才交人」同一族做法
|
||||
(2026-08-03,见 CLAUDE.md「兜底卡不再摆模型原话」条)——`analyze` 目前没有对应机制。
|
||||
|
||||
## 静态审核认不出未绑定的 `{in:X}`(2026-08-29,Task 6 评审 Critical 逼出来的平台缺口)
|
||||
|
||||
`activity-registry` 的 `static-audit.ts`(`walkExpr`,约 `:458-471`)会递归校验一条 `AutoExpr`
|
||||
里的 `when`(→ `checkWhen`,核名字绑得上绑不上)与 `lookup`(→ `checkAddress`,核地址声明过没有),
|
||||
也会递归进 `cat`/`fmt`/`then`/`else` 继续往下找 —— **唯独没有 `'in' in node` 这一支**。裸的
|
||||
`{in:X}` 引用(配方级 `let` 算出来的名字、或起单入参、或 `case:` 读数)在装包那一刻**一声不响**,
|
||||
名字到底绑不绑得上、活着不活着,静态审一个字都不查。
|
||||
|
||||
这个洞在 W05 上真的咬过人(见本文件件头 Critical 记录 / `case-model.ts` 的 `SHARED_LET` 头注 /
|
||||
`apps/api/src/modules/loop-engine/__tests__/w05-s3-review-recompute.test.ts`):`S3_review.auto`
|
||||
一度没有自己的 `let`,而它的两笔投影(`RELATION_PROJECTION`/`VALIDATION_PROJECTION`)里五个
|
||||
`{in:X}` 引用的名字**只在 S2 的 `let` 里绑过**——装包、`pnpm gates`(2395 条全绿)、六条纯结构
|
||||
断言(`steps.test.ts`)全部放行,只有把真引擎(`createAutoRunner`)跑起来求值才现形:一条主键算成
|
||||
空串、整笔写被「主键算不出来先不写」悄悄丢掉;另一条主键因为 `cat` 拼接把空串垫成非空前缀
|
||||
(`'VAL-'`),反而写出一行垃圾,破了「一单固定一条」的表不变量。
|
||||
|
||||
**这是平台缺口,不是 W05 一个活动的病**——凡是拿 `{in:X}` 却没在**这一步自己的** `let` 里定义
|
||||
过(哪怕在别的步的 `let` 里定义过,`let` 不跨步传递,`auto-runner.ts:93-97`),都会在装包时蒙混
|
||||
过关、只在运行期以「写丢了」或「拼出垃圾行」两种面目现形,且不出声、不留痕说清原因(只在
|
||||
`memory.append` 的机械留痕里能看到「主键算不出来没写」,人不会去读那一行)。
|
||||
|
||||
**要治**:`walkExpr` 补 `'in' in node` 这一支,核 `node['in']` 这个名字是否落在
|
||||
`letNames ∪ declaredNames`(与现有 `checkWhen` 同一套判断)里,绑不上就 `schema-invalid` 拒装 ——
|
||||
与 `checkWhen` 现有的「S-FEEL 对未绑定的名字求值为假,这一笔将永远被跳过」同一条纪律,只是
|
||||
`{in:X}` 的失败面目更隐蔽(空串会被拼接、被当合法值继续往下算,不像 S-FEEL 谓词那样直接短路)。
|
||||
`auto` 是 ADR-014 新落地的机制,这个洞会在下一个用到跨步共享投影的活动上重复咬人。
|
||||
|
||||
Reference in New Issue
Block a user