评审指出 ADR-014 被标「已实现」后,正文 §3.2/§4/§5 仍是实施前的三动词提案, 与真实 schema(auto-recipe.ts)和 turn-runner.ts 的拍内次序对不上,会误导 下一个直接翻开这份 ADR 的读者(W05 重写实现者)。逐条对代码核过后分两档改: §3.2 动词表与 §5 拍内次序按代码改对(含之前遗漏的「有卡未答就静默重跑配方」 那道前置门);§4 的两个例子不逐行改,改成开头一段按语指出三处已知出入 (workOrder→as、view 挪到 PlanStep、self/joinRows 未实现)。顺带把 §11 待定的前两条标成已定并指回销账位置。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
25 KiB
ADR-014 确定性步骤:确定的活由引擎按声明做,不问模型
状态:已实现(2026-08-29;设计见 docs/superpowers/specs/2026-08-29-act-work-05-rewrite-design.md §5)
背景出处:ACT-WORK-05 差异评估九步流程盘点(§2)、docs/ACT-WORK-05-端到端测试记录.md 发现表 A–AE。
关联:ADR-004(活动包 ABI:系统不执行代码即知包为何物)、docs/活动开发规范.md §4.4(自动收步)、
PlanStep.humanWrites(2026-08-28 立的墙)、ADR-005(工具契约与落地预检)。
1. 问题
用户 2026-08-28 的原话:
「这个不是一个确定的流程吗?根本不需要交给 ai,你直接查库不就知道有没有了吗?」
指的是 W05 的「逐个料号查库 → 不在库就铺骨架、发数据工单」。这一段没有任何判断余地,却写成了模型的 guide。 不是活动作者偷懒 —— 平台里没有别的落点:
| 现有机制 | 能做 | 不能做 |
|---|---|---|
纯工具(pure) |
算、读台账(1.2.0 起) | 写台账、发卡 |
| 判定表(DMN) | 出结论 | 写台账、发卡 |
doneWhen |
判「办完没有」,引擎自己收步 | 做事 |
哨兵(entryCriteria) |
判「能不能进这一步」 | 做事 |
| 视图绑定语言 | 把台账取出来摆到屏上 | 写回去 |
「读台账 + 写草稿 + 发人卡」跨了三样,没有一个机制能整段承接。而引擎这三样能力全有
(data.view / data.draftWrite / humanTool.issueExecution)—— 缺的只是声明面。
packages/activity-sdk/src/define.ts 里 run? 的注释早就写着:
留着它是为**确定性子序列(结晶片段)**将来有个落点;不写就是不写,不是缺件。
这份 ADR 就是给那个落点定形。
代价已经付过。 W05 的事故里,至少八条落在「本来就该是确定的」那几步上(§7 逐条验算)。
2. 盘点:W05 九步里哪些是确定的
| 步 | 确定的部分 | 需判断的部分 |
|---|---|---|
| S0_bind 建上下文 | 铸案号 · 查客户在不在库 · 写需求单(入参搬运+常量)· 逐料号查库写核对表 · 写结论行 | 零 |
| S0d_p08 派主数据单 | 定名单 · 铺三张表骨架 · 发卡 · 人交回后改旗 | 有 datasheet 附件时从原文抽参数值 |
| S0d_p10 派资料单 | 定名单 · 发卡 · 人交回后改旗 | 零 |
| S1 评估输入清单 | 清单九行(四行绑定投影 + 两行固定缺口 + 三行常量默认)· 发执行单 · 把 supplement 抄进需求单 | 人补的自由文本归纳成技术约束 |
| S2 标准化对照表 | 关系骨架 · 逐参数对照行(原 PN 参数集 × 候选同名行的 join)· 补封装/生命周期两行 | 单位换算;constraint_type 未标时归类(应进 DMN,不进配方) |
| S3 逐项差异评估 | 工具串接 · 四态照抄 · 计数 · difference_summary · 置信取最小 |
客户原话 → 约束式 · 影响陈述 · UNKNOWN 去向与验证条件 · 推荐理由扩写 |
| S4 结论审核 | 发决策卡(内容是计数投影)· approve 收步 | 读人的自由文本意见 → 定位到具体行 → 改实 |
| S5 拟落账 | 分析状态推 FAE 确认 · 回写需求单五项(风险取最高档、VAL 号拼接、编号、常量三项) | 只有 summary 一句人话(可模板化) |
| T_release 放行 | submit · 无分支去向 | 两条裁量判据 · 打回后修订 |
agent 真正不可替代的只有四类:①自然语言 → 结构化;②读人的意见并落到具体行;③写给人看的话; ④非结构化来源抽取(datasheet 抽值、单位换算)。第四类在实跑中一次都没发生过(没传附件)。
结论:九步里三步整个确定(S0_bind / S0d_p10 / S5),四步八成确定,只有 S3 是 agent 的主场。 按拍数算,一单十来拍里有七八拍在做确定性的活 —— 而每一拍都是一次采样,都有编错号、抄错行、漏一列的机会。
3. 决策
3.1 声明式配方,不执行活动包代码
sys.activity_package.artifact(包的 index.js)存了库,但至今一次都没被执行过。这份设计不开这条路:
- ADR-004 的立场是「系统不执行代码即知包为何物」;配方是数据,装进来仍然只是数据, 可静态审、可回放、可在治理台上摆出「这一步系统会做什么」。执行代码则相反。
- 执行第三方包要配沙箱、超时、内存墙,而
scanArtifact只是文本扫描,拦不住混淆。 - 配方要表达的形状很窄(§3.2 五个动词),不值得为它开一个能跑任意代码的面。
代价:表达力有上限。上限之外的不要硬塞 —— 退回模型,或者把那一段切成独立的 agent 步(§3.4)。
3.2 五个动词,复用已有的两套求值
改口(2026-08-29,落地时定):设计之初只列了三个,实现时补了 analyze 与 submit
(§11 待定①②随之销账,见该节按语)。配方有五个动词:
| 动词 | 做什么 |
|---|---|
project |
从「某个来源的每一行」投影出台账行,draftWrite 落草稿层 |
analyze |
一次结构化 AI 调用:系统组装入参、声明出参 JSON Schema,出参落拍内作用域供后续 project 消费。不给工具、不给记忆、不多轮 |
card |
发一张人类工具的卡(执行工单 / 数据工单 / 决策卡),至多一张 |
submit |
提交放行,发验收卡挂起(走与模型 submit 同一条 raiseAcceptance) |
say |
一句给人看的交代(模板串,不是模型生成) |
条件与聚合不发明第二套求值:
- 条件用 S-FEEL(
when,对任务视图求值)—— 与哨兵、doneWhen同一个求值器; - 计数/聚合用基座读数(
_rows/_missing/_distinct_x/ tally 计数)—— 本来就在作用域里。
3.3 auto 是「问模型之前先把确定的事做了」,不是「整步替代」
PlanStep.auto 是一个属性。引擎在这一步的每一拍拍首先跑配方,然后:
- 配方发了卡 → 这一拍到此为止(零 token,不问模型);
- 没发卡且这一步没有
guide→ 求doneWhen,真了收步、进下一步(下一步若也是 auto 就接着跑); - 没发卡且这一步有
guide→ 照常问模型(半 auto:确定的已经做完,模型只面对剩下的判断)。
这样老步骤一个字不改,可以一步一步搬。
3.4 作用域:一个计划项内,到交出控制权为止
配方跑到「发卡」就结束 —— 与现有 handsOff 语义(ask/human/decide/listen/submit 是交出控制权)对齐。
人答回来之后,那一步若还有活,归 guide。
推论(重要):一步要么确定,要么不是。不做「一半 auto」的复杂机制 —— 判断点要先从那一步里切出去。 例:S0d_p08 的「datasheet 抽值」应当移出这一步(归到起单读来件),这一步才变成纯 auto。 这条逼着活动作者把判断点想清楚,是特性不是限制。
3.5 幂等
配方会被反复进入(人答卡后再 tick),必须幂等:
- 写:
draftWrite本来就是同 PK 覆盖。值没变 → 静默跳过,不留痕、不算空转被拒 (「写了等于没写」那条判据是拦模型空转的,配方不是模型); - 发卡:先看
worldTouched—— 发过就不再发。人工激活的 auto 步,配方发卡就是启动。
3.6 出错 = 交人,不重试、不退回模型
配方抛错(地址不存在、S-FEEL 算不出、卡骨架不合契约)是活动包的 bug:如实留痕 + 交人。 不重试五遍,也不退回模型 —— 退回模型就是让它去凑,那正是这份 ADR 要消灭的东西。
3.7 静态审在装的时候把住
staticAudit 扩到配方(与 refersTo 目标检查同款,写错地址 = 每一单都错):
into/each/lookup/joinRows的台账地址必须在declarations.ledger里(与写的授权面同一份名单);fields的列名必须在该对象的列声明里;when的 S-FEEL 必须能解析,引用的名字要绑得上(boundNames,已有);- 卡骨架的
view必须在viewTemplates里; - 同一张表既在
auto.project里又在humanWrites里 → 警告(铺骨架合法,但要作者确认列不重叠)。
3.8 观测与度量
- 配方的每一笔照常进
sys.task_memory,detail.auto = '<步名>#<第几笔>'; 工作日志三色里归系统灰(已有档,不必新增); - 治理台的活动详情能摆出「这一步系统会做什么」(配方是数据,渲染得出来)—— 这是相对提示词的一大好处;
- 自主率的分法要认第三档:现在两档(AI / 人),配方做的事既不是 AI 判的也不是人办的,是系统做的。
改口(2026-08-29,实现时踩出来的):docs/superpowers/specs/2026-08-29-act-work-05-rewrite-design.md
§5.9 写的是「analyze 那一笔归 AI 蓝」—— 那句要改口,它也归系统灰。理由:那一行记的是
「系统把 N 条参数交给模型、拿回 M 条结论」,发起与落表都是系统做的,模型只提供内容;
内容本身的归属在它落下的台账行上看得见,不必让日志行冒充成「AI 判的」。
连带修了一处会让整条留痕静默消失的坑:配方留痕最初用的是 kind: 'result' —— 那不在工作日志
的骨架集合里(result 是进模型叙事轨迹那一类,不是给人看的那一类);project / analyze /
「自动收步」那几笔当时又都标了 internal: true,会被 machinery() 再滤掉一道 ——
三段各自「正确」,合起来一笔都进不了工作日志,人打开日志想知道「系统刚做了什么」,
看到的是空的。现在统一改成 kind: 'observation' 且不标 internal(「已发过卡,不再发第二张」
这一笔仍保留 internal —— 它每拍都打一遍,是排障痕,不是给人看的交代)。
4. 声明形状
这一节是 2026-08-28 的原始提案,落地时有出入。精确语法以
packages/contracts/src/auto-recipe.ts与《活动开发规范》§4.6 为准。 已知的三处差异(下面 §4.1/§4.2 的例子按原始提案写,未逐行改过,读的时候按这三条心里默认翻译):workOrder→as(且没有entry,只有execution/decide);卡上不写view—— 画面归PlanStep.view(已有机制,一处声明,§3 没提是因为原提案漏写了这条,不是后来删的);{ self: true }与{ joinRows: … }未实现 ——each: 'in:candidateInternalPns'这种 扁平数组(元素不是对象、是原始值)的本项,实现里是rowsAt把每个元素包成{ value: 原值 }再取{ col: 'value' },不是self;joinRows(多行一列拼成串)没有对应写法,真要拼多行, 目前得退回模型或切成独立步骤。
// PlanStep 新增(可选,老活动一个字不改)
auto?: AutoRecipe;
type AutoRecipe = {
/** 配方级局部值:先算一遍,后面所有表达式都能引用({in:'evalRef'})—— 整单只有一个案号,靠它 */
readonly let?: Record<string, AutoExpr>;
/** 按序执行;每一笔可带 when(S-FEEL),假就跳过这一笔 */
readonly do: ReadonlyArray<AutoAction>;
};
type AutoAction =
| { kind: 'project'; when?: string; into: string; from: AutoSource; fields: Record<string, AutoExpr> }
| { kind: 'card'; when?: string; workOrder: 'execution' | 'entry'; variant?: 'data';
title: AutoExpr; gateLedgers?: string[]; view?: string; /* 指导书等静态文本直接写 */ }
| { kind: 'say'; when?: string; text: AutoExpr };
type AutoSource =
| { once: true } // 一行
| { rows: ReadonlyArray<Record<string, AutoExpr>> } // 固定几行(S1 的默认三项)
| { each: string; where?: Record<string, AutoExpr>; // 'in:candidateInternalPns' | 'mine:域.对象' | 'ledger:域.对象'
then?: { each: string; where?: Record<string, AutoExpr> } }; // 第二层,**最多两层**
type AutoExpr =
| string | number | boolean // 常量
| { in: string } // 入参 / case:* / 基座读数 / let
| { self: true } // each 是数组时的本项
| { col: string } | { outer: string } // 本行的列 / 外层那一行的列
| { cat: AutoExpr[] } // 拼接
| { fmt: AutoExpr; as: 'digits' } // 只留数字(铸号)
| { lookup: string; by: Record<string, AutoExpr>; take: string; else?: AutoExpr } // 查另一张表的一列
| { joinRows: string; where?: Record<string, AutoExpr>; take: string; sep?: string } // 多行一列拼成串
| { when: string; then: AutoExpr; else: AutoExpr }; // S-FEEL 条件(可嵌套)
字段按声明顺序求值,已算出的字段进同一行的 S-FEEL 作用域 —— 「先 lookup 出依据,再按依据判 found/absent」 这种写法靠它成立。
4.1 例:S0d_p10_document(整步确定,最小的那个)
auto: {
do: [
{ kind: 'card',
when: 'sub_eval_pn_check_exists_status_absent > 0',
workOrder: 'execution', variant: 'data', view: 'p10-document-create',
gateLedgers: ['parts.document'],
title: { cat: ['数据工单 · 原厂技术资料创建(P-10):',
{ joinRows: 'mine:sub_eval.pn_check', where: { exists_status: 'absent' },
take: 'internal_pn', sep: '、' }] } },
{ kind: 'say', text: '这几颗料的原厂技术资料库里查无记录,已派数据工单请你登记。' },
],
}
// guide 不写。doneWhen 照旧:parts_document_rows >= parts_profile_rows and parts_document_missing = 0
// 人在件上填完确认 → 数据即入库 → 下一拍 doneWhen 成立 → 引擎收步。**模型在这一步一次都不出现。**
4.2 例:S0_bind 的铸号与存在性核对
auto: {
let: {
digits: { fmt: { in: 'case:openedAt' }, as: 'digits' },
evalRef: { cat: ['W05-', { in: 'digits' }] },
requestId:{ cat: ['REQ-', { in: 'digits' }] },
},
do: [
// 客户不在库 → 发数据录入卡,这一拍到此为止
{ kind: 'card', when: 'customer_profile_rows = 0', workOrder: 'entry', /* … */ },
{ kind: 'project', into: 'demand.request', from: { once: true },
fields: { request_id: { in: 'requestId' }, customer_code: { in: 'customerCode' },
requirement_version: 'V1', request_type: '技术需求',
request_description: { in: 'requestDescription' },
input_snapshot_ref: { cat: ['SNAP-', { in: 'evalRef' }] },
original_internal_pn: { in: 'originalInternalPn' },
request_status: 'pending', current_activity: 'ACT-WORK-05', current_stage: 'assessing' } },
{ kind: 'project', into: 'sub_eval.pn_check', from: { each: 'in:candidateInternalPns' },
fields: { eval_ref: { in: 'evalRef' }, internal_pn: { self: true }, role: 'candidate',
found_pn_ref: { lookup: 'ledger:parts.profile', by: { internal_pn: { self: true } },
take: 'internal_pn', else: '' },
exists_status: { when: 'found_pn_ref != ""', then: 'found', else: 'absent' } } },
// 原 PN 那一行同理(from: { once: true },role: 'original')
{ kind: 'project', into: 'sub_eval.verdict', from: { once: true }, fields: { /* … */ } },
],
}
「在不在库」从此是一次 lookup,不是模型的一句断言 —— 08-27 那条「原 PN 不在库却判在库」结构性消失。
5. 引擎行为(拍内次序)
改口(2026-08-29,与 turn-runner.ts 实际次序对齐 —— 落地时多了 analyze/submit
两个动词,分支也比原提案细):
tick 开始
├─ ⓪ 这一单还有任何卡没交回(不论哪一步发的)→ 这一拍不问模型;
│ 若当下这一步声明了 auto → 静默跑一遍它的配方(quiet:只跑 project / say,
│ 「保存即铺格」——人在件上按了保存,系统按最新草稿把该铺的行铺出来)→
│ 记痕「等人交回」→ status awaiting,这一拍到此为止,不进入下面任何一步
├─ ① 拍首自动收步(现有:doneWhen 为真的项,人工激活项需 worldTouched)
├─ ② 算 current(当下在办的那一步)
├─ ③ current.auto 存在 → 跑配方(走到这里已经过了 ⓪ 那道门,`quiet` 恒为 false;
│ `do` 数组按声明顺序逐笔执行,五个动词可任意穿插)
│ ├─ 求 let
│ ├─ 按序执行每一笔(when 假则跳过;project 写/撤过东西后,下一笔重新取一次读数)
│ │ · project → 逐行 draftWrite(同 PK 覆盖;值没变静默跳过)
│ │ · analyze → 一次结构化 AI 调用,出参落拍内作用域 `result:<into>`,供后续 project 消费
│ │ · card → 先看 worldTouched,发过就当这一笔已完成、跳过(不当被拒);
│ │ 发了标 worldTouched、烧 humanCalls,这一笔之后停(交出控制权)
│ │ · submit → 记下 rationale,这一笔之后停(交出控制权)
│ │ · say → 进对话区
│ ├─ 每一笔留痕(by=系统,detail.auto)
│ └─ 抛错 → 留痕 + 交人(不重试、不退回模型),这一拍到此为止
├─ ④ 配方发了卡 → 标 worldTouched、按声明推画面,status awaiting,收拍(零 token)
├─ ⑤ 配方 submit → 发验收卡挂起(走与模型 `submit` 同一条 `raiseAcceptance`),status awaiting,收拍
├─ ⑥ 没发卡也没 submit,但配方写过东西(project 落了行/撤了行)→ 重求 doneWhen;
│ 真了收步,下一拍重新导航(封顶 8 轮,与现有自动收步同款)
├─ ⑦ 卡答过了就收步(近道):doneWhen 未声明 · 卡已发过(worldTouched)· 配方会发卡
│ · 这一步**没有** `guide`(全 auto)→ 引擎直接把这一项收掉,不问模型
│ (**半 auto 的步 —— 声明了非空 `guide` —— 不走这条**,R9,见 §3.3)
├─ ⑧ 这一步有 guide → 照常问模型(半 auto)
└─ ⑨ 无 guide、doneWhen 不成立、也没有卡在等 → **交人**(配方没做到位,是包的 bug)
预算:配方不烧 token(analyze 是例外 —— 它是一次真实的模型调用,烧 token、计入预算,
但不计入 stalledTurns 熔断计数,见 debt.md);发卡烧 humanCalls;配方段在同一 tick 内,不额外计 step。
6. 与现有机制的衔接
| 机制 | 处置 |
|---|---|
doneWhen |
不变。auto 步几乎都该有 —— 它做的事是可验的 |
哨兵 entryCriteria |
不变。auto 步照样要满足入场前提才在场 |
worldTouched |
配方发的卡同样标 —— 它是真发了 |
humanWrites(2026-08-28) |
配方豁免:那道墙防的是模型即兴,配方是审过的声明。铺骨架合法,人填内容不冲突(静态审提示列不要重叠) |
| 写三分 | 不变:配方只写草稿层,落正式表仍靠放行 / 数据工单确认入库 |
| 「一单一卡」 | 不变:配方至多一张卡 |
| 熔断 | 配方不会「被拒」,不进 stalledTurns |
| 试跑 / 回放 | 配方是纯数据求值,同输入同结果 —— 可回放。配方里没有时钟与随机(时刻取 case:openedAt) |
| 记账旗 | 该删的删,不是该 auto —— input_confirmed_total 这类基座本就数得出来 |
7. 回溯验算:历史事故有几条会结构性消失
| # | 事故 | 落在哪 | auto 后 |
|---|---|---|---|
| 1 | 编号从正式表里别单的案号里挑 | S0_bind 铸号 | 消失(模板铸号) |
| 2 | 原 PN 不在库却判「在库」(发现 I) | S0_bind 查库 | 消失(lookup 现查) |
| 3 | 数据工单派给了在库的那颗(发现 J) | S0d_p08 名单 | 消失(where absent) |
| 4 | 压拍没写骨架就发卡,人打开是空表(发现 Q) | S0d_p08/p10 | 消失(配方按序) |
| 5 | S1 清单 11 行全漏 input_item,人无处可填 |
S1 前半 | 消失(字段声明齐全) |
| 6 | 库里有值的参数被 S1 当缺口再问一遍(发现 AB,三次复现) | S1 前半 | 消失(清单是投影) |
| 7 | P-10 抄原 PN 的规格书安在候选头上(发现 AE) | S0d_p10 | 消失(模型不出现) |
| 8 | finish 撞 worldTouched 墙五拍熔断(发现 AE 同单) |
S0d_p10 | 消失(那一步不问模型) |
八条落在会被 auto 掉的三步半上。 剩下的事故(工具没调、比对入参抄错、验收卡面孔、重做循环) 不在这个范围内 —— 它们该由别的机制治,这份 ADR 不承诺。
8. 不做什么
- 不调工具:工具有副作用面与预算面,先不给(S3 的
compare → recommend串接留作待定; 实现时换了个更窄的口子解掉同一个需求,见下方「两处改口」与 §11 待定②的销账) 不(finish、不submit、不发决策卡:收步归doneWhen,终局归 submit,岔路口本就是判断submit与发决策卡两条已撤,见下方「两处改口」;finish这条不变 —— 配方从不 显式收步,收步仍旧只有「引擎按doneWhen自动收」与「半 auto 步问模型、模型 finish」两条路)- 不写正式表:仍走草稿层
- 不做三层以上的嵌套:两层(关系 × 参数)是 W05 的上限;超出就别 auto
- 不在治理台上编辑配方:那就成了低代码平台;配方随包走,改配方 = 升版本重装
- 不承诺消灭 agent:§2 的四类判断照旧归它
两处改口(2026-08-29,ACT-WORK-05 重写时用户定):
submit与发决策卡这两条禁令撤掉。 它们原本是假设还有 agent 在场时的保守话;而全程无 agent 的活动没有别人来干这两件事, 不开口最后两步就没人做。护栏没松:submit只许出现在声明了人工验收清单的那一步, 提交门(判据全绿)照旧把关 —— 系统提交不比模型提交宽松;决策卡只在variant:'review'时 放宽到单选项(它不是岔路口,是一道签字关)。
9. 代价与风险
- 活动作者要写结构化声明,不能再靠散文。 但那些散文本来就在(而且更长、还不能测); 配方能单测(纯数据求值,包内自测钉死)。
- 代码错是必然的错,模型错是概率的错。 配方写错 = 每一单都错。 对策:静态审(§3.7)+ 包内自测 + 安装两审的回放审拿样例入参跑一遍配方。 但必然的错比概率的错好查 —— 这正是把它从模型手里拿走的理由。
- 立场要改口。 「组织的工作定义为可编排的工作流……活动是完成一项任务的 Agent 机制」—— 加一句:agent 只在真需要判断的地方出现,与「人只在责任点与放行点出现」是同一条原则。 确定的事交系统,判断交 AI,责任交人。
- 把有判断的地方硬写成规则的风险。
constraint_type的 hard/soft 归类按品类会变 —— 这类进 DMN 判定表,不进配方;配方里只许出现「取值、拼装、投影、发卡」。 - 现在的「确定」有一半是靠强制命名一致换来的。 S2 能纯 join,前提是「候选与原 PN 参数名一模一样」 (P-08 建骨架时用中文名硬压 + 门上的行数对账)。一旦料号来自外部导入,同义归一就回来了 —— 那是判断,是 agent 的活。auto 化不等于永远不需要 agent。
10. 迁移次序(每搬一步都要五案例 e2e:released + 被拒 0 才算数)
S0d_p10_document—— 最小、纯粹、刚出过事;拿它验证机制(配方 + 静态审 + 留痕 + 观测)S5_prepare_landing—— 整步确定、不发卡;验证「不发卡时接着走 doneWhen 收步」那条路S0_bind—— 整步确定、含一张录入卡的分岔;验证let铸号与lookup存在性S1_input_confirm前半 —— 清单投影 + 发卡;半 auto(收口仍归 guide)S0d_p08_master—— 先把 datasheet 抽值切出去,再 autoS2_normalize—— 两层嵌套,最难;搬之前先确认它与比对工具的 join 是否重复(§11)
11. 待定(要用户定的)
决策卡要不要给配方:S4 第一张卡的 ask 内容是计数投影,确定;但决策卡是岔路口,给了会不会滑坡? 已定:给了——card.as: 'decide'(variant:'review'时放宽到单选项),见 §8「两处改口」。工具串接要不要做第四个动词:S3 的compare → recommend之间模型只是转手 counts,却占掉整整一拍。 已定:没照原样给——没有做「按序调用多个工具」那个第四动词,换成了更窄的analyze(一次结构化 AI 调用,不给工具不给记忆不多轮),见 §3.2 与docs/debt.md「平台缺『确定性步骤』」条。humanWrites该不该细到列:现在是表级。auto 铺骨架 + 人填内容之后,列级更准。- S2 与比对工具的 join 是否重复:
compare-parameters1.2.0 起自己从台账取参数对, 而 S2 让模型抄的difference_item对照表是同一份 join。一份事实做了两遍,一遍还是手抄的 —— 要么 S2 auto 化,要么让工具的出参直接落difference_item(那是另一个机制:工具出参绑台账)。 T_release第三条裁量判据「推荐等级与差异明细一致」就是工具的定级规则本身, 让模型再裁一次是给自己盖章 —— 该删(与 08-01 在 A1 上拆掉 L4 同款)。