fix(consult): 降级正文过 Pass 4,去重并停止空跑 compose(BUG-959/960/961)

降级交付复用 releasePass4Sentences;每次 attempt 清空 uncontractedText;成功降级后跳过 compose。
This commit is contained in:
jesse-ux
2026-09-18 18:45:18 +08:00
parent bed961c4a1
commit 5e8b2ac112
6 changed files with 304 additions and 28 deletions
+48
View File
@@ -12600,6 +12600,54 @@
- 复发自:无
- 修复版本:—
## BUG-959 | 降级正文绕过 Pass 4,保证性结论原样送达
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:`stream-agent-response.ts` 合同未绿时的降级交付
- 用户现象:没跑完星盘计算时,模型自己写的「我保证你一定会升职」一类句子会进正文。
- 触发条件:`requireTool: true`、零次成功计算、有非空正文、`pass4Mode` 为有盘模式。
- 根因:BUG-956 的降级分支直接 `send(uncontractedText)`,没有走 `releasePass4Sentences` / `classifyPass4`。降级正文恰恰是没有任何计算依据时写的,保证句和个人盘断言最容易出现在这里。
- 修复:降级正文复用 `releasePass4Sentences` 按句过门,`reject` 整句丢弃,`observe` 照记。`CONTRACT_DEGRADED_NOTE` 是服务端文案,不过 Pass 4,贴在末尾。全部句子被丢弃时:`general_no_birth_time` 发拒绝句再附说明;其余模式维持 `runtime_contract_incomplete`,不发只有降级说明的空回答。不在句内改写。
- 验证:假流降级 + `verified_chart` + 保证句与安全句 → 保证句不出现在任何 `answer.delta`,回执同时有 `contract-degraded``pass4-reject:guarantee`;全丢不发 note-only;一般模式全丢发拒绝句 + 说明。
- 防复发:降级路径必须复用 `releasePass4Sentences`,不得另写分句或句内替换。Pass 4 是门不是刀。
- 关联记录:BUG-956、BUG-948、BUG-950、BUG-951
- 复发自:BUG-956(降级路径漏接 Pass 4
- 修复版本:`ae2e46a6`
## BUG-960 | 两次 attempt 的正文被拼起来一起送出
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:`stream-agent-response.ts` `uncontractedText`
- 用户现象:合同重试后降级交付把第一稿和重试稿拼成一段,同一轮内容说两遍。
- 触发条件:第一次 attempt 写过字,合同仍红,retry 再写一段不同的字。
- 根因:`uncontractedText += text` 只在 `contractReady()` 为真时清零。合同一直红就一路累加,attempt 之间没有边界。
- 修复:每次 `consumeAttempt` 开始时清空 `uncontractedText`。降级只交付最后一次 attempt 的正文。
- 验证:假流 attempt1「第一段。」、attempt2「第二段。」、无工具成功 → 降级正文含第二段、不含第一段。
- 防复发:未绿缓冲按 attempt 隔离,不得跨 attempt 累加。
- 关联记录:BUG-956、BUG-959
- 复发自:BUG-956
- 修复版本:`ae2e46a6`
## BUG-961 | 降级之后还会空跑一轮 compose
- 状态:resolved
- 首次发现:2026-09-18
- 最近更新:2026-09-18
- 影响面:`stream-agent-response.ts` 降级后的收口
- 用户现象:无(用户看不到 compose 产出)。本命形状的事件序列里出现 `phase.started{phase:"compose"}`,白花一次模型调用与一段墙钟。
- 触发条件:降级交付成功,且该路径传入了 `composeAnswer`(本命线)。
- 根因:降级分支执行后流程继续走到 `publishFindings` / `composeOnce`
- 修复:成功降级后设 `deliveredDegraded`,跳过 Pass 2 / Pass 3 / compose / answer-retry,直接 `run.completed`。全丢的非一般模式在 compose 之前抛 `runtime_contract_incomplete`
- 验证:降级路径 `composeAnswer` 调用次数为 0,事件无 `phase.started{phase:"compose"}`
- 防复发:降级是终态交付,不得再进 compose。
- 关联记录:BUG-956、BUG-959
- 复发自:BUG-956
- 修复版本:`ae2e46a6`
## BUG-958 | 窗口指令「必须调工具」与「不得给应期」未写清,模型可能跳过计算或拒答
- 状态:resolved
@@ -0,0 +1,37 @@
# PROGRESS · 降级交付路径补 Pass 4 与去重(2026-09-18
工作树:`.worktrees/contract-degraded-pass4-20260918`
分支:`codex/contract-degraded-pass4-20260918`
基线:`origin/staging` @ `1061514f`(任务书写 `877128ce`,其后远端已快进)
任务书:`docs/tasks/TASK-contract-degraded-pass4-20260918.md`
## 结论
降级正文现在按句过 Pass 4:保证句整句丢掉,其余留下,服务端说明贴在末尾。两次 attempt 不再拼在一起。降级之后不再空跑 compose。合同门 `contractReady()` 没放宽。
## 任务状态
| 任务 | 状态 | 说明 |
| --- | --- | --- |
| BUG-959 降级正文过 Pass 4 | 完成 | 复用 `releasePass4Sentences`;全丢时一般模式发拒绝句,其余模式仍 `runtime_contract_incomplete` |
| BUG-960 每次 attempt 清空缓冲 | 完成 | 降级只交付最后一次 attempt |
| BUG-961 降级后跳过 compose | 完成 | `composeAnswer` 零次调用,事件无 `phase.started{compose}` |
| BUG-957 窗口预跑 | 本文件不覆盖 | 见 `PROGRESS-window-consult-precompute-20260918.md` |
## 断言三栏(既有测试)
| 测试 | 原值 | 新值 | 原因 |
| --- | --- | --- | --- |
| BUG-956 有正文降级 | 未传 `pass4Mode` | `pass4Mode: "verified_chart"` | 任务书:e32ce624 之后的既有降级测试补上该参数;正文「不能保存」不含 reject 句,行为不变 |
| BUG-956 无正文 | 未传 `pass4Mode` | 同上 | 同上,空正文仍走 `runtime_contract_incomplete` |
## 测试
| 项 | 结果 |
| --- | --- |
| `tsc --noEmit` | 0 错 |
| `npm run lint` | 0 error120 warning,与基线同量级,未顺手修) |
| `consultation-agentic-runtime.test.ts` + `timing-output-guard.test.ts` | 88 pass / 1 fail。失败是本机 Windows `EPERM` 无法为 live skill 建 symlink`personal Agent exposes the Jyotish Skill…`),与基线缺口同类,不是本单引入 |
| 本单新增 | 降级+保证句不出现在任何 `answer.delta`,回执同时有 `contract-degraded``pass4-reject:guarantee`;全丢不发只有降级说明的空回答;两次 attempt 只留第二段;降级路径 `composeAnswer` 调用次数为 0 |
Linux 门禁才是验收数字:目标 fail 回到 31,总数不低于基线 3501。本机无登录态、无 Chrome、Windows 无 symlink 权限,不写成通过。
+1 -1
View File
@@ -139,7 +139,7 @@
| `TASK-consult-three-channels-fix-20260918.md` | `PROGRESS-consult-three-channels-fix-20260918.md` | 验收修复单:领域上限解耦(945)、31s 预算断言(946)、校正思考分片门(947)、Pass 4 按模式分流(948)、容量算术(949) | 已验收通过(Claudetsc 0 / lint 0 error / npm test 3491 条 31 红且与基线 `742ffbc6` 同一组、原 12 条红全绿 / `/` 仍 Static / 首屏 js gzip 468,388 B 较基线 +0.02%);review 另出 BUG-950~953 见下一行 | `e32ce624` |
| `TASK-consult-pass4-streaming-20260918.md` | `PROGRESS-consult-pass4-streaming-20260918.md` | 验收 review 三轮:Pass 4 一 hold 正文就整段蹦出、逐字流式消失(BUG-950 产品拍板按句放行);无出生分钟模式整段被一句拒绝顶掉、一般知识句一起丢(951 改按句丢弃);该模式下日期不留痕(952);校正流 token 级 thinking 是死链,按 P2 删除并把测试翻转成否定合同(953)。基线 `1e553976` | 待验收 | `cd4775ae` |
| `TASK-window-consult-contract-20260918.md` | `PROGRESS-window-consult-contract-20260918.md` | **P0 线上**:申报时段模式**自 2026-08-21 起每轮秒败**69ms、0 token、模型从未被调用)。服务端日志实证根因:窗口 Agent attach 了 `jyotishSkillBinding()`,但 `windowJyotishInstructions` 从来不含方法块 marker,输入处理器直接 abortBUG-954)。abort 与「模型没调工具」同码,是它藏四周的原因(955);另含合同未绿不得丢正文(956)、计算不该由模型触发(957,须等 954 上线后另轮)、窗口指令应期冲突(958)。基线 `9cdcf96b` | 已验收通过(Claudetsc 0 / lint 0 error / npm test 3501 条 31 红同基线 / `/` Static / gzip 无变化;实跑确认窗口 Agent 指令已含 marker);review 另出 BUG-959~961 见下一行 | `5b6abc23` |
| `TASK-contract-degraded-pass4-20260918.md` | | 验收 review:BUG-956 新增的降级交付路径绕过 Pass 4,保证句原样送达(BUG-959);`uncontractedText` 跨 attempt 不清零,同一轮正文说两遍(960);降级后还空跑一轮 compose(961)。基线 `877128ce` | 待领取 | |
| `TASK-contract-degraded-pass4-20260918.md` | `PROGRESS-contract-degraded-pass4-20260918.md` | 验收 review:BUG-956 新增的降级交付路径绕过 Pass 4,保证句原样送达(BUG-959);`uncontractedText` 跨 attempt 不清零,同一轮正文说两遍(960);降级后还空跑一轮 compose(961)。基线 `877128ce` | 待验收 | `ae2e46a6` |
| `TASK-chat-markdown-list-20260918.md` | — | 真机排版反馈:聊天正文列表**没有项目符号**Tailwind v4 preflight 清了 `list-style``.markdown-list` 没恢复,BUG-962);`promoteDefinitionLists` 把四标题口语体的散文误判成并列项,三段正文被改写成列表(BUG-963,判据太松,收紧而非删除——BUG-356 的场景要留);思考条与正文之间实测 56px=报告 grid gap 24 + 首标题 margin-top 32,后者是被特指度压掉的「首元素清零」规则(BUG-964)。基线 `ea0280c1` | 待领取 | — |
| `TASK-secondary-page-entry-20260918.md` | — | 真机反馈:星盘 / 星历 / 报告进入时抖一下——三页都是「矮的等待文案块 → 高的正文块」一次推挤,且 `use-chart-page` 无缓存所以每次进入都抖(BUG-966)。产品已拍板方案一:统一外壳 + 缓存 + 预取消灭中间态,**不加 spinner**,红线不动。另含 BUG-967:标签页跨过部署后客户端导航静默失效(BUG-965 已复现确认,刷新即恢复),要按 `NEXT_PUBLIC_GIT_COMMIT``/api/health` 比对自愈。基线 `41902067` | 待领取 | — |
| `TASK-account-dialog-inert-20260918.md` | — | 真机:账户弹窗打开后整个弹窗点不动、退出登录做不了,刷新依旧——`e4e73f56``SidebarInset` 搬进 layout 后 `inert={modalOpen}` 罩住了没有 portal 的 `AccountDialogOverlay`(BUG-968P0,代码级确认)。另含 BUG-969:校正「换一件事问」后无下文,服务端已出下一题且快照重算完整,客户端停在开场状态,GET 路由与客户端两端静默,本单只做可观测 + 不静默 + 题干进正文。基线 `1061514f` | 待领取 | — |