diff --git a/activities/part-substitution-assessment/__tests__/steps.test.ts b/activities/part-substitution-assessment/__tests__/steps.test.ts index 41549fc..a6a68f2 100644 --- a/activities/part-substitution-assessment/__tests__/steps.test.ts +++ b/activities/part-substitution-assessment/__tests__/steps.test.ts @@ -154,14 +154,30 @@ describe('说明书与剧本的骨架', () => { expect(s3).toContain('工具调用后同拍其余动作作废'); }); - it('S0 剧本写明库里都有就 disable S0d 两项;S0d_p08_master 剧本写明候选 ≥2 才问、=1 必建、原 PN 硬阻塞', () => { + it('S0 剧本写明都在库时数据工单两项本就不会进「当下可为」;S0d_p08_master 剧本写明候选 ≥2 才问、=1 必建、原 PN 硬阻塞', () => { const s0 = ASSESSMENT_STEPS.find((s) => s.item === 'S0_bind')?.guide ?? ''; - expect(s0).toContain('disable S0d_p08_master'); + expect(s0).toContain('当下可为'); + expect(s0).toContain('不要对计划项用'); const s0d = ASSESSMENT_STEPS.find((s) => s.item === 'S0d_p08_master')?.guide ?? ''; expect(s0d).toContain('硬阻塞'); expect(s0d).toContain('候选只有 1 颗'); }); + it('s_bound 哨兵真的检查了「有料号不在库」这件事(都在库时门不该开;退回旧写法会红)', () => { + expect(CMMN_XML).toContain('sub_eval_pn_check_exists_status_absent > 0'); + const sBoundBlock = /[\s\S]*?<\/sentry>/.exec(CMMN_XML)?.[0] ?? ''; + expect(sBoundBlock).toContain('caseFileItemRef="sub_eval_pn_check_exists_status_absent"'); + expect(sBoundBlock).toContain('sub_eval_pn_check_exists_status_absent > 0'); + }); + + it('说明书与全部剧本里不会教模型对计划项调用 disable(允许出现在「不要 disable」这类禁止句里)', () => { + // 只逮「disable + 计划项 id」这种祈使形(教模型真的去调用它); + // 「不要…用 `disable`」「Disabled 只能从 Enabled 来」这类禁止句/状态名不含「disable + id」,不会被误伤。 + const disableCallPattern = /disable\s+(S\d[a-zA-Z_]*|T_release)/; + expect(NARRATIVE).not.toMatch(disableCallPattern); + for (const step of ASSESSMENT_STEPS) expect(step.guide, step.item).not.toMatch(disableCallPattern); + }); + it('S0d 拆成两项各自绑自己的 view(P-10 那份件不会永远推不出来),两项都没有 doneWhen', () => { const p08 = ASSESSMENT_STEPS.find((s) => s.item === 'S0d_p08_master'); const p10 = ASSESSMENT_STEPS.find((s) => s.item === 'S0d_p10_document'); diff --git a/activities/part-substitution-assessment/src/case-model.ts b/activities/part-substitution-assessment/src/case-model.ts index 701ec80..a70c194 100644 --- a/activities/part-substitution-assessment/src/case-model.ts +++ b/activities/part-substitution-assessment/src/case-model.ts @@ -9,8 +9,8 @@ * 工作区件,不能共用同一个 view): * 启动期 → S0_bind(AI:铸案号、建 P-01 V1、锁规则版本、逐个 PN 查库、写存在性核对表) * 0.5a 等待数据(建主数据)→ S0d_p08_master(人:数据工单 P-08;候选 ≥2 缺库先决策卡; - * 库里都有就 disable 这一项) - * 0.5b 等待数据(建资料)→ S0d_p10_document(人:数据工单 P-10;库里都有就 disable 这一项) + * 都在库时哨兵不开,这一项与下一项都不会进「当下可为」) + * 0.5b 等待数据(建资料)→ S0d_p10_document(人:数据工单 P-10;都在库时同样不会进「当下可为」) * 1 建立评估上下文 → S1_input_confirm(人:执行工单 01,评估输入清单,件上补缺口) * 2 读取并标准化 → S2_normalize(AI:P-11 主记录骨架 + 差异明细对照行) * 3 逐项差异评估 → S3_assess(AI + 两枚工具:四态 / 风险 / 证据 / 影响 / 推荐等级 / 验证任务) @@ -61,10 +61,10 @@ export const CMMN_XML = ` - + - update - demand_request_rows > 0 and sub_eval_pn_check_rows > 0 + update + demand_request_rows > 0 and sub_eval_pn_check_rows > 0 and sub_eval_pn_check_exists_status_absent > 0 @@ -108,13 +108,13 @@ export const CMMN_XML = ` - + - + @@ -181,7 +181,7 @@ export const NARRATIVE = `§0 开案:**判断候选料号在技术上能不能 **存在性检查与数据工单**:启动时逐个料号在 \`parts.profile\` 里查(原 PN + 每个候选)。查无记录 = 「完全没有」, 要人从零建整条主数据 —— 发**数据工单**(一张表一张单:P-08 一张、P-10 一张);PN 在库但个别字段缺 = 「有而不全」, 归执行工单 01 补。原 PN 缺 = 硬阻塞必须建;候选缺:候选 ≥2 时先问人放弃还是创建,候选 = 1 时必须建。 -**库里都有就把 S0d_p08_master 与 S0d_p10_document 两项都 disable 掉**(此单不走这条),别发空单。 +**库里都有的话这两项的入场前提本就不成立**(此单不走这条),不会出现在你的「当下可为」里,别发空单。 **六步走**:绑定与存在性核对 → (等待数据)→ 人补评估输入 → 标准化对照表 → 逐项评估 → 人审结论(可循环)→ 拟落账 → 人放行。 @@ -298,8 +298,10 @@ current_stage = "assessing"。application_scenario / bom_position **这一步不 【写 \`${D}.verdict\` 一行】eval_ref、request_id、rule_version = "${RULE_VERSION}"、original_internal_pn、candidate_total = 候选数组长度。 -写完引擎自己收这一项。收完先看 \`${D}.pn_check\`:有 absent 的 → 下一项是 S0d_p08_master(数据工单);一个 absent 都没有 → -**disable S0d_p08_master 与 S0d_p10_document 两项**(reason 写「原 PN 与全部候选都在库,无需建主数据」),然后等 S1 那一项 Enabled。`, +写完这一项就结束。有 absent 的 → 下一项是 S0d_p08_master(数据工单),照常往下办。 +**都在库的话你什么都不用做** —— 数据工单那两项的入场前提本就不成立,它们不会出现在你的「当下可为」里, +下一拍你会直接看到「评估输入与资料补齐确认」那一步。**任何时候都不要对计划项用 \`disable\`**: +本引擎里 \`Disabled\` 只能从 \`Enabled\` 来,而普通计划项一直停在 \`Available\`,disable 一定被拒。`, }, { item: 'S0d_p08_master', @@ -309,7 +311,7 @@ current_stage = "assessing"。application_scenario / bom_position **这一步不 view: 'p08-master-create', tools: [], /* 触达人,不给 doneWhen;人工激活:发卡就算启动 */ - guide: `这一项只在 \`${D}.pn_check\` 里有 absent 行时走;都在库就由 S0_bind 把这一项与下一项 S0d_p10_document 一起 disable 掉。**先分岔,再动作**: + guide: `这一项只在 \`${D}.pn_check\` 里有 absent 行时走 —— 都在库时入场前提本就不成立,这一项与下一项 S0d_p10_document 都不会进你的「当下可为」。**先分岔,再动作**: 【先看轨迹】这一项里已经发过哪些卡、人答了什么(【当前这一步】下面那行引擎事实说得清楚): · 原 PN absent → **硬阻塞**,不问人,直接进入「建」的名单; @@ -341,8 +343,8 @@ current_stage = "assessing"。application_scenario / bom_position **这一步不 view: 'p10-document-create', tools: [], /* 触达人,不给 doneWhen;人工激活:发卡就算启动 */ - guide: `这一项只在 \`${D}.pn_check\` 里有 absent 行时走(要走到这一步,S0d_p08_master 必然已经收完);都在库就由 S0_bind 把这一项与 -S0d_p08_master 一起 disable 掉。 + guide: `这一项只在 \`${D}.pn_check\` 里有 absent 行时走(要走到这一步,S0d_p08_master 必然已经收完);都在库时入场前提本就不成立, +这一项与 S0d_p08_master 都不会进你的「当下可为」。 【发 P-10 数据工单】「建」的名单就是 \`${D}.pn_check\` 里仍是 exists_status = "absent" 的那几行(候选 ≥2 时被放弃的那些, S0d_p08_master 那一步已经把它们改成 "abandoned",不在这份名单里)。同一拍照说明书④发那张 P-10 数据工单(title 里列全料号,只发一次), diff --git a/activities/part-substitution-assessment/src/manifest.ts b/activities/part-substitution-assessment/src/manifest.ts index 1dad0f8..4d32c57 100644 --- a/activities/part-substitution-assessment/src/manifest.ts +++ b/activities/part-substitution-assessment/src/manifest.ts @@ -84,7 +84,7 @@ export const manifest: ActivityManifest = { identity: { activityId: ACTIVITY_ID, name: 'ACT-WORK-05 · 差异评估', - packageVersion: '1.0.0', + packageVersion: '1.0.1', category: '需求分析与方案匹配', sdkVersion: '0.1.0-m5', checksum: 'dev-checksum-part-substitution-assessment',