From e760b1d610bdb7ed28e0171d10cdc3be58b8278e Mon Sep 17 00:00:00 2001 From: jesse-ux Date: Fri, 18 Sep 2026 18:44:52 +0800 Subject: [PATCH] =?UTF-8?q?fix(consult):=20=E7=AA=97=E5=8F=A3=E6=BA=A2?= =?UTF-8?q?=E5=87=BA=E9=87=8D=E8=AF=95=E4=BF=9D=E7=95=99=E9=A2=84=E8=B7=91?= =?UTF-8?q?=E8=AF=81=E6=8D=AE=E5=8C=85=EF=BC=8C=E5=9B=9E=E5=A1=AB=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E7=89=88=E6=9C=AC=20SHA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/BUG_HISTORY.md | 20 +++++++++---------- ...RESS-window-consult-precompute-20260918.md | 2 +- docs/tasks/README.md | 8 ++++---- frontend/src/app/api/consult/route.ts | 7 ++++++- .../consultation-workflow-contract.test.ts | 3 +++ 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index afc1c85f..63f57175 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -12551,7 +12551,7 @@ - 防复发:见 BUG-967。不得再把「刷新就好」当成侧栏无响应的结案。 - 关联记录:BUG-967、BUG-204、BUG-744~746、BUG-926~929、BUG-936 - 复发自:BUG-204 同类(跨发布客户端导航),那次修的是 `deploymentId` / `?dpl=`,在 Vercel 上靠部署路由 404 触发 MPA;本仓是自托管单镜像,那条路径不成立。 -- 修复版本:本轮提交 +- 修复版本:`9ccb65b7` ## BUG-966 | 星盘 / 星历 / 我的报告进入时矮等待块推挤成高正文 @@ -12567,7 +12567,7 @@ - 防复发:次级页进入不得再各写一套等待布局。揭幕后不得加 spinner / 骨架 / 「正在加载」。缓存必须模块级,组件 unmount 不能丢掉。 - 关联记录:BUG-716、BUG-717、BUG-745 - 复发自:无 -- 修复版本:本轮提交 +- 修复版本:`9ccb65b7` ## BUG-967 | 跨部署旧标签页的客户端导航静默失效 @@ -12586,7 +12586,7 @@ - 防复发:站内跨布局跳转走 `AppLink` / `navigateAppPath`,不得假定 Next 的 mismatch fallback 在自托管上会救场。不得用「每次导航都整页重载」或定时刷新糊过去。 - 关联记录:BUG-965、BUG-204 - 复发自:BUG-204 -- 修复版本:本轮提交 +- 修复版本:`9ccb65b7` ## BUG-955 | 输入处理器 abort 与「合同未完成」同码,窗口装配失败藏了四周 @@ -12634,7 +12634,7 @@ - 防复发:窗口计算不得再依赖模型选不选工具;缓存不得回到工厂局部 `let`;咨询侧不得再写 `toolChoice: "required"`。 - 关联记录:BUG-954、BUG-205、BUG-214、BUG-922、BUG-923、BUG-937、BUG-956、BUG-959 - 复发自:无(954 验证通过后按任务书单独做) -- 修复版本:`59788ac6` +- 修复版本:`53d78137` ## BUG-959 | 降级正文绕过 Pass 4,保证性结论原样送达 @@ -12650,7 +12650,7 @@ - 防复发:降级路径必须复用 `releasePass4Sentences`,不得另写分句或句内替换。Pass 4 是门不是刀。 - 关联记录:BUG-956、BUG-948、BUG-950、BUG-951 - 复发自:BUG-956(降级路径漏接 Pass 4) -- 修复版本:`e07d5190` +- 修复版本:`5e8b2ac1` ## BUG-960 | 两次 attempt 的正文被拼起来一起送出 @@ -12666,7 +12666,7 @@ - 防复发:未绿缓冲按 attempt 隔离,不得跨 attempt 累加。 - 关联记录:BUG-956、BUG-959 - 复发自:BUG-956 -- 修复版本:`e07d5190` +- 修复版本:`5e8b2ac1` ## BUG-961 | 降级之后还会空跑一轮 compose @@ -12682,7 +12682,7 @@ - 防复发:降级是终态交付,不得再进 compose。 - 关联记录:BUG-956、BUG-959 - 复发自:BUG-956 -- 修复版本:`e07d5190` +- 修复版本:`5e8b2ac1` ## BUG-958 | 窗口指令「必须调工具」与「不得给应期」未写清,模型可能跳过计算或拒答 @@ -12714,7 +12714,7 @@ - 防复发:`.markdown-list` 规则块必须含 `list-style`;不得再只设缩进不设符号。 - 相关记录:BUG-356、BUG-963、BUG-964 - 复发自:无 -- 修复版本:待发布 +- 修复版本:`dccedf37` ## BUG-963 | 四标题口语散文被自动提升成列表 @@ -12730,7 +12730,7 @@ - 防复发:不得放宽 term 句末标点或 body 长度上限来「多提升一些」;散文冒号不是列表。 - 相关记录:BUG-356、BUG-943、BUG-962 - 复发自:BUG-356(并列短项提升判据过宽,口语体冒号段被误伤) -- 修复版本:待发布 +- 修复版本:`dccedf37` ## BUG-964 | 思考条与正文之间空 56px @@ -12746,7 +12746,7 @@ - 防复发:思考条与正文间距不得再各写各的;会话作用域不得让首元素 `h2` 再带 `--space-8` 的 margin-top。 - 相关记录:BUG-962、BUG-356 - 复发自:无 -- 修复版本:待发布 +- 修复版本:`dccedf37` diff --git a/docs/tasks/PROGRESS-window-consult-precompute-20260918.md b/docs/tasks/PROGRESS-window-consult-precompute-20260918.md index af29451d..bccc5eb0 100644 --- a/docs/tasks/PROGRESS-window-consult-precompute-20260918.md +++ b/docs/tasks/PROGRESS-window-consult-precompute-20260918.md @@ -17,7 +17,7 @@ | 缓存位置 | `WindowConsultationAgentContext.calculationCache`,不是工厂局部 `let`。重建 tools 仍共享 | | 预跑 | `precomputeWindowConsultation(ctx, { question, writer })` 走同一 `execute` 体,记账 `consultationToolCompleted` / `successCount === 1` / 回执 `run-jyotish-window-consultation` | | 时机 | `streamAgentResponse` 的 `warmup`:`run.started` 与 skill 事件之后、`consumeAttempt` 之前 | -| 注入 | 预跑成功后往 `baseMessages` 追加 `windowPrecomputedPacketMessage` | +| 注入 | 预跑成功后往 `baseMessages` 追加 `windowPrecomputedPacketMessage`。上下文溢出缩历史时把同一条消息再贴回去,避免丢掉已算好的包 | | 失败 | warmup 吞掉错误,合同保持红;模型仍可调工具;既有 retry + 降级仍在 | | 本命 | 不预跑 | diff --git a/docs/tasks/README.md b/docs/tasks/README.md index fe86127e..a4b8b0d2 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -139,10 +139,10 @@ | `TASK-consult-three-channels-fix-20260918.md` | `PROGRESS-consult-three-channels-fix-20260918.md` | 验收修复单:领域上限解耦(945)、31s 预算断言(946)、校正思考分片门(947)、Pass 4 按模式分流(948)、容量算术(949) | 已验收通过(Claude:tsc 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,输入处理器直接 abort(BUG-954)。abort 与「模型没调工具」同码,是它藏四周的原因(955);另含合同未绿不得丢正文(956)、计算不该由模型触发(957,须等 954 上线后另轮)、窗口指令应期冲突(958)。基线 `9cdcf96b` | 已验收通过(Claude:tsc 0 / lint 0 error / npm test 3501 条 31 红同基线 / `/` Static / gzip 无变化;实跑确认窗口 Agent 指令已含 marker);review 另出 BUG-959~961 见下一行 | `5b6abc23` | -| `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` | 待验收 | `e07d5190` | -| `TASK-window-consult-contract-20260918.md` §5 | `PROGRESS-window-consult-precompute-20260918.md` | BUG-957:申报时段计算改为服务端预跑并走同请求缓存。产品确认 BUG-954 已在 staging 验证,本轮单独做。不得 `toolChoice: "required"`,本命不预跑 | 待验收 | `efd40d43` | -| `TASK-chat-markdown-list-20260918.md` | `PROGRESS-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` | 待验收 | `8b102d52` | -| `TASK-secondary-page-entry-20260918.md` | `PROGRESS-secondary-page-entry-20260918.md` | 真机反馈:星盘 / 星历 / 报告进入时抖一下——三页都是「矮的等待文案块 → 高的正文块」一次推挤,且 `use-chart-page` 无缓存所以每次进入都抖(BUG-966)。产品已拍板方案一:统一外壳 + 缓存 + 预取消灭中间态,**不加 spinner**,红线不动。另含 BUG-967:标签页跨过部署后客户端导航静默失效(BUG-965 已复现确认,刷新即恢复),要按 `NEXT_PUBLIC_GIT_COMMIT` 与 `/api/health` 比对自愈。基线 `41902067` | 待验收 | `4c69a3dc` | +| `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` | 待验收 | `5e8b2ac1` | +| `TASK-window-consult-contract-20260918.md` §5 | `PROGRESS-window-consult-precompute-20260918.md` | BUG-957:申报时段计算改为服务端预跑并走同请求缓存。产品确认 BUG-954 已在 staging 验证,本轮单独做。不得 `toolChoice: "required"`,本命不预跑 | 待验收 | `53d78137` | +| `TASK-chat-markdown-list-20260918.md` | `PROGRESS-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` | 待验收 | `dccedf37` | +| `TASK-secondary-page-entry-20260918.md` | `PROGRESS-secondary-page-entry-20260918.md` | 真机反馈:星盘 / 星历 / 报告进入时抖一下——三页都是「矮的等待文案块 → 高的正文块」一次推挤,且 `use-chart-page` 无缓存所以每次进入都抖(BUG-966)。产品已拍板方案一:统一外壳 + 缓存 + 预取消灭中间态,**不加 spinner**,红线不动。另含 BUG-967:标签页跨过部署后客户端导航静默失效(BUG-965 已复现确认,刷新即恢复),要按 `NEXT_PUBLIC_GIT_COMMIT` 与 `/api/health` 比对自愈。基线 `41902067` | 待验收 | `9ccb65b7` | | `TASK-account-dialog-inert-20260918.md` | — | 真机:账户弹窗打开后整个弹窗点不动、退出登录做不了,刷新依旧——`e4e73f56` 把 `SidebarInset` 搬进 layout 后 `inert={modalOpen}` 罩住了没有 portal 的 `AccountDialogOverlay`(BUG-968,P0,代码级确认)。另含 BUG-969:校正「换一件事问」后无下文,服务端已出下一题且快照重算完整,客户端停在开场状态,GET 路由与客户端两端静默,本单只做可观测 + 不静默 + 题干进正文。基线 `1061514f` | 待领取 | — | | `TASK-first-paint-dead-screen-fallback-20260917.md` | — | 真机:首页永远停在「正在载入账户」,兜底全在没跑起来的 bundle 里(BUG-936 investigating)。根 layout 加与 bundle 无关的内联兜底 + 去掉本仓正则后行断言 | 待领取 | — | | `TASK-consultation-answer-start-anchor-20260917.md` | `PROGRESS-consultation-answer-start-anchor-20260917.md` | 主会话回答落在结尾:`useConversationScrollAnchor` 是贴底跟随,流式期间视口钉在最后一个字,回答开头滚出视口;改为发送后问题钉顶、回答向下长、长出视口显示「跳到最新」、末尾动态留白;产品追加拍板:校正面同一语义(推翻 BUG-041/048 贴底),本轮开头 = 用户行或新助手行。BUG 段 930 起 | 已验收(经修复单) | `worktree/green-harbor-5be3` | diff --git a/frontend/src/app/api/consult/route.ts b/frontend/src/app/api/consult/route.ts index fb1b468b..0484ee56 100644 --- a/frontend/src/app/api/consult/route.ts +++ b/frontend/src/app/api/consult/route.ts @@ -895,6 +895,7 @@ export async function POST(request: Request) { ]; }; let baseMessages = consultationBaseMessages(false); + let windowPacketMessage: string | null = null; const agentAbortSignal = AbortSignal.timeout(AGENT_TIMEOUT_MS); const streamOptions = { runId: requestId, @@ -930,6 +931,9 @@ export async function POST(request: Request) { // clients cannot parse rectification `attempt.reset`, so the retry stays // server-side and never opens a second user-visible wait. baseMessages = consultationBaseMessages(true); + if (windowPacketMessage) { + baseMessages = [...baseMessages, { role: "user" as const, content: windowPacketMessage }]; + } const overflow = await agent.stream(baseMessages, options); usages.push(overflow.totalUsage); return overflow; @@ -1111,9 +1115,10 @@ export async function POST(request: Request) { }, }, }); + windowPacketMessage = windowPrecomputedPacketMessage(packet); baseMessages = [ ...baseMessages, - { role: "user" as const, content: windowPrecomputedPacketMessage(packet) }, + { role: "user" as const, content: windowPacketMessage }, ]; } catch { // Leave the contract red; the model may still call the tool. diff --git a/frontend/tests/consultation-workflow-contract.test.ts b/frontend/tests/consultation-workflow-contract.test.ts index 8a4a585a..bd4c5359 100644 --- a/frontend/tests/consultation-workflow-contract.test.ts +++ b/frontend/tests/consultation-workflow-contract.test.ts @@ -368,6 +368,9 @@ test("window calculation is precomputed on the agent context cache (BUG-957)", ( assert.match(windowBranch, /precomputeWindowConsultation/); assert.match(windowBranch, /warmup:/); assert.match(windowBranch, /windowPrecomputedPacketMessage/); + assert.match(route, /let windowPacketMessage: string \| null = null/); + assert.match(route, /if \(windowPacketMessage\) \{/); + assert.match(route, /content: windowPacketMessage/); assert.doesNotMatch(windowBranch, /toolChoice:\s*"required"/); assert.doesNotMatch(natalBranch, /precomputeWindowConsultation/); assert.doesNotMatch(natalBranch, /warmup:/);