diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 4e516782..7aeee02b 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -6407,7 +6407,7 @@ - 触发条件:varga 风格标签含禁词或不足两项;已持久化 Focus 的 choice schema 缺四选项;`proposeAllowed` 为 false 时模型仍看到 `offer-candidates`。 - 根因:选项补全和可渲染检查只返回成功数组或 `null`,没有 `reason`。读路径把坏 schema 当成「没有题」。Agent 工具表不看 `decideFromDossier`。TS/Python 合同没有 byte-equal golden。`uniquify` 曾用 `answer_class` 当可见后缀。 - 修复:补全/可渲染返回 `{ ok, options|reason }`。`inspectDiscriminatorProbes` 把丢题写入 `decision.droppedProbes` 和 receipt `dropped_probes`。GET/`turn_decision` 对坏选择题返回 `{ unrenderable: true, reason }`,采集类 Focus 仍为 `null`。`createRectificationV9AgentTools(ctx, decision)` 按 phase / `proposeAllowed` / `selectionAllowed` / `canConfirmExactMinute` 过滤工具。合同 golden 在 `contracts/probe-question-v1.json`,引擎 client 拒不匹配的 `question_contract_version`。重复标签用 `·2` 而不是 `weak_yes`。Mastra 提示词删掉已由代码执行的外貌/财务/工具禁令,Skill 保持 10.0.13。`lib/birth-time-*` 仍被 `app/` 与 `components/` 的 guided/journey/intake 与 `/api/birth-time-journey`、`/api/birth-time-guide` 引用,本批不删。 -- 验证:`rectification-probe-question-contract` 锁定 result type、index uniquify、golden bytes、forbidden_copy 丢题。Python `test_probe_question_contract` 同样对齐 golden。`rectification-answer-choice` 锁定坏 schema 为 unrenderable、采集 Focus 仍 null。`rectification-v10-tool-contract` 锁定 `proposeAllowed` 为 false 时没有 `offer-candidates`。`rectification-v9-engine-contract` 锁定错误合同版本 fail-closed。`rectification-v9-agent` / `rectification-agentic-entry` 锁定瘦身后的提示词。 +- 验证:`rectification-probe-question-contract` 锁定 result type、index uniquify、golden bytes、forbidden_copy 丢题。Python `test_probe_question_contract` 同样对齐 golden。`rectification-answer-choice` 锁定坏 schema 为 unrenderable、采集 Focus 仍 null。`rectification-server-focus` 锁定不完整 choice schema 为 unrenderable 而不是可点选 prompt。`rectification-v10-tool-contract` 锁定 `proposeAllowed` 为 false 时没有 `offer-candidates`。`rectification-v9-engine-contract` 锁定错误合同版本 fail-closed。`rectification-v9-agent` / `rectification-agentic-entry` / `rectification-ingest-p0` / `rectification-confirmation-gate` 锁定瘦身后的提示词(`confirmation_allowed`、`不得宣称唯一出生分钟`、`新事件走 rectification-record-evidence-batch`),不再要求 Mastra 提示词复述 `confirmation_gate`。Independent Staging Quality Gate 必须通过后才算部署。 - 防复发:不得把 `completeStyleOptions` / `isRenderableProbe` 改回只返回数组或 boolean。不得把坏选择题投影成 `current_question: null`。不得在无决策时按 phase 过滤工具后,再把 `offer-candidates` 写进提示词禁令。不得用 `answer_class` 当可见标签后缀。不得在本路径删除仍被 journey/guide UI 引用的 `birth-time-*`。不得把 Skill 升出版本。 - 相关记录:BUG-403、BUG-421、#42 - 复发自:BUG-403(动态四选项合同未贯穿丢题原因) diff --git a/frontend/tests/rectification-confirmation-gate.test.ts b/frontend/tests/rectification-confirmation-gate.test.ts index f9ef0e0c..a118d80d 100644 --- a/frontend/tests/rectification-confirmation-gate.test.ts +++ b/frontend/tests/rectification-confirmation-gate.test.ts @@ -211,8 +211,8 @@ test("holdout not_ready forbids unique-minute copy and still blocks confirm", as assert.match(skill, /unique_minute_path=closed_at_representative/); assert.doesNotMatch(skill, /还不能确认唯一分钟/); assert.match(candidateComparison, /confirmation_gate/); - assert.match(agentSource, /confirmation_gate/); - assert.match(agentSource, /session_outcome=adopt_representative/); + assert.match(agentSource, /confirmation_allowed/); + assert.match(agentSource, /不得宣称唯一出生分钟/); assert.doesNotMatch(agentSource, /±2 分钟/); assert.equal(PUBLIC_RECTIFICATION_TOOLS.length, 14); assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.13"); diff --git a/frontend/tests/rectification-ingest-p0.test.ts b/frontend/tests/rectification-ingest-p0.test.ts index fb0c969e..8af20343 100644 --- a/frontend/tests/rectification-ingest-p0.test.ts +++ b/frontend/tests/rectification-ingest-p0.test.ts @@ -140,7 +140,7 @@ test("a 25-minute tied plateau projects width and forbids unique-minute confirma ); assert.match(candidateComparison, /一段不可分区间/); assert.match(candidateComparison, /代表性候选/); - assert.match(agentSource, /不得说已定位到唯一分钟/); + assert.match(agentSource, /不得宣称唯一出生分钟/); }); test("read-case evidence context keeps day labels and confirm does not rewrite dates", async () => { @@ -215,7 +215,7 @@ test("new-case skill identity is 10.0.13 and the prompt prefers batch ingest", ( assert.equal(RECTIFICATION_SKILL_VERSION, "10.0.13"); assert.match(skill, /^version: 10\.0\.13$/m); assert.match(skill, /不要对同一句用户消息里的多件事件逐条 propose\+confirm/); - assert.match(agentSource, /当前轮新事件一律走 rectification-record-evidence-batch/); + assert.match(agentSource, /新事件走 rectification-record-evidence-batch/); assert.doesNotMatch(agentSource, /分别调用 rectification-propose-evidence 和 rectification-confirm-evidence/); }); diff --git a/frontend/tests/rectification-server-focus.test.ts b/frontend/tests/rectification-server-focus.test.ts index 3926bac0..e3053c3e 100644 --- a/frontend/tests/rectification-server-focus.test.ts +++ b/frontend/tests/rectification-server-focus.test.ts @@ -172,12 +172,15 @@ test("an unpersisted, mismatched, or incomplete focus cannot expose a discrimina questionId: focus.questionId, prompt: "不能展示", }), null); - assert.equal(openQuestionFromPersistedFocus({ + const incomplete = openQuestionFromPersistedFocus({ status: "created", focus: { ...focus, expectedAnswerSchema: { choice: { prompt: "不完整" } } }, questionId: focus.questionId, prompt: "不能展示", - }), null); + }); + assert.equal(incomplete?.unrenderable, true); + assert.equal(incomplete?.prompt, null); + assert.equal(incomplete?.reason, "invalid_choice_schema"); }); test("complete dynamic event options persist a server-owned focus", async () => {