diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 9b1b1deb..aa97b608 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -5531,6 +5531,22 @@ - 复发自:无 - 修复版本:未修复 +## BUG-369 | staging 质量门 2003/2006:计费合同、思考轨迹与业务表清单未跟上 367/368 + +- 状态:resolved(本地修复,待提交与发布) +- 首次发现:2026-08-25 +- 最近更新:2026-08-25 +- 影响面:Gitea `backend-quality-gate.yml` validate `npm test --prefix frontend`、`application-billing-contract.test.ts`、`chat-stream-layout.test.ts`、`database-local-business.test.ts`、staging 发布 +- 用户现象:向 `staging` 推送 `1082338c` 后 quality gate run `2056` 失败。`publish` 因 `needs: validate` 未构建镜像。公网仍为上一成功 SHA。 +- 触发条件:完整 `xiaoxin` runner 跑前端全量测试;Docker PostgreSQL fixture 应用全部 `frontend/supabase/migrations`。 +- 根因:三处源码/schema 合同未与 BUG-367/368 同步。(1) 计费合同仍要求 `action` 只有 `opening|message|read_only`,忽略已加入且不计费的 `answer_choice`/`stop_and_review`。(2) 布局合同仍要求生时纠正聊天把 `thinking.delta` 写入 `appendActivityTraceThinking`,与禁止公开思考流冲突。(3) self-hosted 精确 `public` 表清单仍缺 `agentic_rectification_choice_actions` 与 `agentic_rectification_inference_transitions`。本机先前只跑了聚焦套件,未跑这三项。 +- 修复:计费合同改为完整 action enum,仍要求只有 `message` 走 reserve/complete/release。布局合同改为禁止 `appendActivityTraceThinking` 和 `thinking.delta`,保留共享 Agent action bar 与工具步骤轨迹。全迁移测试补 applied ledger(含 turn origin)和两张新表,并断言 origin 列与 `record_agentic_rectification_turn_origin` 仅 service_role 可执行。不把业务 SQL 拷进 `frontend/db/migrations`。 +- 验证:`frontend/tests/application-billing-contract.test.ts`、`frontend/tests/chat-stream-layout.test.ts`;`frontend/tests/database-local-business.test.ts` 在有 Docker 的 runner 上执行。 +- 防复发:新增业务表必须同步 `database-local-business` 的 applied ledger 和精确表集合。生时纠正路由 enum 变更必须更新计费合同。禁止再把公开 `thinking.delta` 写回纠正聊天合同。 +- 相关记录:BUG-127、BUG-144、BUG-367、BUG-368 +- 复发自:BUG-127(新业务表未更新精确表清单);BUG-367/368 的合同测试未覆盖计费 enum 与思考轨迹扫描 +- 修复版本:待发布 + ## BUG-368 | 工具步骤英文规划被当成回答,set-focus 失败又整轮重放证据 - 状态:resolved(本地修复,待提交与发布) diff --git a/frontend/tests/application-billing-contract.test.ts b/frontend/tests/application-billing-contract.test.ts index 70b3c4f7..4a7225fb 100644 --- a/frontend/tests/application-billing-contract.test.ts +++ b/frontend/tests/application-billing-contract.test.ts @@ -34,7 +34,10 @@ test("free Agentic rectification turns bypass reservation, completion, and cance const completeBilling = sourceBetween(rectificationRoute, "async complete(usage) {", "async release() {"); const releaseBilling = sourceBetween(rectificationRoute, "async release() {", " };\n\n try {"); - assert.match(rectificationRoute, /action: z\.enum\(\["opening", "message", "read_only"\]\)/); + assert.match( + rectificationRoute, + /action: z\.enum\(\["opening", "message", "read_only", "answer_choice", "stop_and_review"\]\)/, + ); assert.equal(rectificationRoute.match(/if \(action !== "message"\)/g)?.length, 3); assert.match(reserveBilling, /if \(action !== "message"\) \{[\s\S]*return \{ success: true, status: 200 \};/); assert.match(reserveBilling, /authorizeUsage\(/); diff --git a/frontend/tests/chat-stream-layout.test.ts b/frontend/tests/chat-stream-layout.test.ts index 385494a2..547d5b3c 100644 --- a/frontend/tests/chat-stream-layout.test.ts +++ b/frontend/tests/chat-stream-layout.test.ts @@ -183,10 +183,10 @@ test("ordinary consultation replies reuse the shared Agent action bar", () => { assert.match(pageSource, /function regenerateLatestAnswer\(renderKey: string\)/); assert.match(pageSource, /messages: session\.messages\.slice\(0, -1\)/); assert.match(pageSource, /restoreOnFailure: session/); - assert.match(rectificationChat, /appendActivityTraceThinking/); + assert.doesNotMatch(rectificationChat, /appendActivityTraceThinking/); + assert.doesNotMatch(rectificationChat, /event\.type === "thinking\.delta"/); assert.match(rectificationChat, /startActivityTraceStep/); assert.match(rectificationChat, /completeActivityTraceStep/); - assert.match(rectificationChat, /activityTrace = appendActivityTraceThinking\(activityTrace, event\.text\)/); assert.match(rectificationChat, /