来源:#920 / PR #939 实施过程的顺带发现(改 :8 / :86-94 / :106 / :110-112 / :133-134 时逐条回源核对同页其余的行为性主张)。基线 origin/main = 4855d50b(平台 17.0.0-rc.3),行号为 fresh main 上的行号,三语同址。
与 #920 / #921 的边界:#920 的裁定面是「状态机会拦下非法转换」那一族,逐行枚举为 :8 / :82-88(现 :86-94)/ :106 / :110 / :111 / :134,PR #939 已按该枚举清空;#921 是状态词表。下面两处都不在这两份枚举里,也不由 PR #939 的改动导致。
两处证据强度不同,刻意分开写,请分别裁。
一、:79-84「The system also enforces」四条 —— 强证据,其中一条是数字错
The system also enforces:
- **Close Date is required** before moving to *Proposal*.
- **Amount is required** before moving to *Negotiation*.
- **Won/Lost reason is required** when entering *Closed Lost* or *Closed Won*.
- **Approval may be required** to enter *Closed Won* if amount > $500K and discount approval hasn't been granted.
| 页面主张 |
src/ 实况 |
| Close Date 在进入 Proposal 前必填 |
opportunity.object.ts:122-124 的 close_date 是无条件 required: true —— 从建单第一秒就必填,不存在「进入 Proposal 前」这个时点。页面把一条更强的约束描述成了阶段性的 |
| Amount 在进入 Negotiation 前必填 |
同上,opportunity.object.ts:82-84 的 amount 也是无条件 required: true |
| 赢单/丢单原因在进入 Closed Won / Closed Lost 时必填 |
✅ 属实,且机制真实:win_reason 的 requiredWhen 是 record.stage == "closed_won"(:303),loss_reason 是 closed_lost(:327) |
| 金额 > $500K 且未获折扣审批时「可能需要审批」 |
数字错。opportunity-approval.flow.ts:88 的 START 条件是 record.amount > 100000 —— $100K 就进审批,走 Sales Manager 一档;$500K 是 :235 那条更高的 Sales Director 加签档(vars.oppRecord.amount > 500000)。页面把二档阈值写成了入口阈值,于是一个 $200K 的单子按页面读是「不需要审批」,实际会进审批 |
前两条是「描述得比实际更宽松」,第四条是具体数字与源码不符,代表读者会据此错判一笔真实交易要不要走审批。第三条正确,不要顺手改掉。
另注:这一节的小标题「The system also enforces」在这里是成立的 —— required 与 requiredWhen 确实会拒绝保存(与状态机那族不同),所以本条不是 #920 的延续,不要套用 PR #939 的「advice, not a gate」口径去改它。
二、:114-122「State machines and the AI Copilot」—— 证据只到「本仓零支撑」,判不了平台侧
The Copilot understands the state machine. When suggesting an action:
- *"Qualify this lead"* → only suggested if status is *Working*.
- *"Convert this lead"* → only suggested if status is *Qualified*.
- *"Mark this opportunity Closed Won"* → only suggested if win conditions are met (...).
This prevents the Copilot from suggesting illegal transitions.
本仓侧实测:grep -rniE "state.?machine|transition|legalNext" src/skills/ src/actions/ 零命中 —— 六个 *.skill.ts 与全部 *.actions.ts 没有任何一处提到转换表、状态机或 legalNextStates。技能的 instructions 里也没有任何一条把状态/阶段的合法后继写进提示词(grep -rniE "qualified|status|stage" src/skills/ 只命中 Customer 360° 与 Revenue Forecasting 里读取字段的说明)。
能证的到此为止。 剩下的部分证不了:平台侧的 agent 在装载对象元数据时是否会把 validations[] 里的 state_machine 一并读进上下文并据以收窄建议,是 @objectstack/runtime / agent 侧的行为,不是本仓能从源码断言的。所以这一条既没被证实也没被证伪,PR #939 因此刻意没动它 —— 同一 PR 里我自己写的初稿有一句「Copilot 只建议转换表允许的那些转换」,正因为证不了而被删掉重写。
要闭合这一条,需要的是一次平台侧探针(起一个 agent 会话,在一条 qualified 的线索上问「Qualify this lead」,看建议是否出现),而不是再读一遍 src/。若探针证明平台不读转换表,那么与 :110(下拉零实现)同型;若证明它读,那这一节是本页唯一真正改变用户所见的地方,应当在 PR #939 改写过的「What a state machine buys you」一节里被点名。
影响
第一处::83 的 $500K 会让读者对一笔 $100K–$500K 的交易做出相反的判断(以为不用审批,实际会被拦进审批流并锁字段)。前两条程度较轻,属于「约束比描述的更严」,读者不会因此漏做什么,但会疑惑为什么建单时就被要求填。
第二处:如果平台确实不读转换表,那么它与 :110 / :111 / :134 是同一批「文档说有、实际没有」的说法,只是躲过了 #920 的枚举。
边界
Refs #920 #921 #575
来源:#920 / PR #939 实施过程的顺带发现(改
:8/:86-94/:106/:110-112/:133-134时逐条回源核对同页其余的行为性主张)。基线origin/main=4855d50b(平台 17.0.0-rc.3),行号为 fresh main 上的行号,三语同址。与 #920 / #921 的边界:#920 的裁定面是「状态机会拦下非法转换」那一族,逐行枚举为
:8/:82-88(现:86-94)/:106/:110/:111/:134,PR #939 已按该枚举清空;#921 是状态词表。下面两处都不在这两份枚举里,也不由 PR #939 的改动导致。两处证据强度不同,刻意分开写,请分别裁。
一、
:79-84「The system also enforces」四条 —— 强证据,其中一条是数字错src/实况opportunity.object.ts:122-124的close_date是无条件required: true—— 从建单第一秒就必填,不存在「进入 Proposal 前」这个时点。页面把一条更强的约束描述成了阶段性的opportunity.object.ts:82-84的amount也是无条件required: truewin_reason的requiredWhen是record.stage == "closed_won"(:303),loss_reason是closed_lost(:327)opportunity-approval.flow.ts:88的 START 条件是record.amount > 100000—— $100K 就进审批,走 Sales Manager 一档;$500K是:235那条更高的 Sales Director 加签档(vars.oppRecord.amount > 500000)。页面把二档阈值写成了入口阈值,于是一个 $200K 的单子按页面读是「不需要审批」,实际会进审批前两条是「描述得比实际更宽松」,第四条是具体数字与源码不符,代表读者会据此错判一笔真实交易要不要走审批。第三条正确,不要顺手改掉。
另注:这一节的小标题「The system also enforces」在这里是成立的 ——
required与requiredWhen确实会拒绝保存(与状态机那族不同),所以本条不是 #920 的延续,不要套用 PR #939 的「advice, not a gate」口径去改它。二、
:114-122「State machines and the AI Copilot」—— 证据只到「本仓零支撑」,判不了平台侧本仓侧实测:
grep -rniE "state.?machine|transition|legalNext" src/skills/ src/actions/零命中 —— 六个*.skill.ts与全部*.actions.ts没有任何一处提到转换表、状态机或legalNextStates。技能的 instructions 里也没有任何一条把状态/阶段的合法后继写进提示词(grep -rniE "qualified|status|stage" src/skills/只命中 Customer 360° 与 Revenue Forecasting 里读取字段的说明)。能证的到此为止。 剩下的部分证不了:平台侧的 agent 在装载对象元数据时是否会把
validations[]里的state_machine一并读进上下文并据以收窄建议,是@objectstack/runtime/ agent 侧的行为,不是本仓能从源码断言的。所以这一条既没被证实也没被证伪,PR #939 因此刻意没动它 —— 同一 PR 里我自己写的初稿有一句「Copilot 只建议转换表允许的那些转换」,正因为证不了而被删掉重写。要闭合这一条,需要的是一次平台侧探针(起一个 agent 会话,在一条
qualified的线索上问「Qualify this lead」,看建议是否出现),而不是再读一遍src/。若探针证明平台不读转换表,那么与:110(下拉零实现)同型;若证明它读,那这一节是本页唯一真正改变用户所见的地方,应当在 PR #939 改写过的「What a state machine buys you」一节里被点名。影响
第一处:
:83的$500K会让读者对一笔 $100K–$500K 的交易做出相反的判断(以为不用审批,实际会被拦进审批流并锁字段)。前两条程度较轻,属于「约束比描述的更严」,读者不会因此漏做什么,但会疑惑为什么建单时就被要求填。第二处:如果平台确实不读转换表,那么它与
:110/:111/:134是同一批「文档说有、实际没有」的说法,只是躲过了 #920 的枚举。边界
administration/state-machines通篇声称状态机会「拦下」非法转换,实测五条规则全是warning:保存照样通过;连「只显示合法下一状态」「bypass state machine 权限」都无实现 #920 不重叠:其枚举面已由 PR docs(service): write the cases and state-machine pages' remaining claims to source (#912, #920, #925, #926) #939 清空,本条两处均不在枚举内。administration/state-machines用的一整套状态词在src/里查无此值:lead 的 Working / Disqualified 是导入别名不是状态,opportunity 的 Closed Without Decision 根本不存在,Needs Analysis 被漏掉;:60的「Disallowed」举例反了 #921 不重叠:那是状态词表(Working/Disqualified/Closed Without Decision);本条不涉及词表。:118的 Copilot 举例句同时被administration/state-machines用的一整套状态词在src/里查无此值:lead 的 Working / Disqualified 是导入别名不是状态,opportunity 的 Closed Without Decision 根本不存在,Needs Analysis 被漏掉;:60的「Disallowed」举例反了 #921 点名(它用了Working),若两单先后落地,注意:118的改法:administration/state-machines用的一整套状态词在src/里查无此值:lead 的 Working / Disqualified 是导入别名不是状态,opportunity 的 Closed Without Decision 根本不存在,Needs Analysis 被漏掉;:60的「Disallowed」举例反了 #921 改词、本条判整节去留。$500K与源码对齐。Refs #920 #921 #575