From dbf984a40b32f140f75b9b7975e0c6e057aa9304 Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Fri, 18 Sep 2026 04:29:12 +0000 Subject: [PATCH] =?UTF-8?q?docs(tasks):=20=E4=B8=89=E9=80=9A=E9=81=93?= =?UTF-8?q?=E9=87=8D=E5=81=9A=E9=AA=8C=E6=94=B6=E6=9C=AA=E9=80=9A=E8=BF=87?= =?UTF-8?q?=EF=BC=8C=E5=87=BA=E4=BF=AE=E5=A4=8D=E5=8D=95=EF=BC=88BUG-945~9?= =?UTF-8?q?49=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 7ae3aa9a 实测:tsc 0 错、lint 0 error,但 npm test 43 红 vs 基线 742ffbc6 的 31 红,新增 12 条 0 条转绿,staging 仍停在 742ffbc6。 主因:领域上限降到 2 且超限由 zod 整次拒绝(三领域问题零执行)。 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P --- docs/tasks/README.md | 3 +- ...ASK-consult-three-channels-fix-20260918.md | 143 ++++++++++++++++++ 2 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 docs/tasks/TASK-consult-three-channels-fix-20260918.md diff --git a/docs/tasks/README.md b/docs/tasks/README.md index e683d1fe..4a507977 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -135,7 +135,8 @@ | `TASK-session-list-single-source-20260917.md` | `PROGRESS-session-list-single-source-20260917.md` | 会话列表一处数据源:本地 PG 兼容层 `order()` 只保留最后一键,`/api/sessions` 实际按 `id` 排、与游标不一致;`/` 与次级页两份数据源、`/` 每次回来重启动(产品拍板:首页与三个次级页进同一路由组,侧栏外壳与列表 provider 常驻 layout,不做 store);空「新对话」落库堆积(首页 50 条里 28 条);标题类别在后、同名靠墙钟 HH:MM。串行在 composer-guard 单之后。BUG 段 926 起 | 验收未通过 | `e4e73f56`(实现按任务书落地:多键排序 / `(app)` 路由组常驻外壳 / provider / 空会话不入列 + draft 复用 / 标题类别在前;两条让步任务书允许。**P1:4 条针对 `page.tsx` 的源码合同没跟着外壳搬家,`npm test` 在门禁里 `set -e` 下必红,staging 至今停在 `dc2f2a16`**。另:进度记录报「tsc 0 错」实为 2 错,只跑了定向测试。修复单 `TASK-session-list-single-source-fix-20260917.md`) | | `TASK-session-list-single-source-fix-20260917.md` | `PROGRESS-session-list-single-source-fix-20260917.md` | 验收修复单:F1 四条源码合同搬到 `(app)/layout.tsx` 两端对断(BUG-933);F2 两条陈旧 Python 入口断言(BUG-934,既有欠账);F3 无活跃会话时输入框静默吞发送(BUG-935);交付前必须跑全量测试 | 待验收 | `codex/session-list-single-source-fix-20260917` | | — | `PROGRESS-birth-time-journey-page-path-20260918.md` | **P0 门禁**:`test_birth_time_journey_contract` 仍读已搬走的 `app/page.tsx`(BUG-939);修好后 run 2764 又露出 5 条校正守卫后的前端源码合同(BUG-940)。均复发自 BUG-933 | 已合入 | `39a0d7a9` / `0378d9e0` | -| `TASK-consult-three-channels-20260918.md` | `PROGRESS-consult-three-channels-20260918.md` | 咨询运行时三通道:进度 / 思考 / 正文生成时分离。设计分支误用 BUG-939/940/941,落地改号 942–944 | 已合入 | `94c1e81f` | +| `TASK-consult-three-channels-20260918.md` | `PROGRESS-consult-three-channels-20260918.md` | 咨询运行时三通道:进度 / 思考 / 正文生成时分离。设计分支误用 BUG-939/940/941,落地改号 942–944 | 验收未通过(见修复单) | `94c1e81f` | +| `TASK-consult-three-channels-fix-20260918.md` | — | 验收修复单:领域上限 3→2 且超限被 zod 整次拒绝、三领域问题零执行(BUG-945 P0);21s→31s 后 6 条预算断言未跟(946);校正思考分片被条目门整条丢弃(947);时间 / 保证 / 个人盘三类检测器无生产调用方,无出生时间模式不再拦个人盘断言(948);容量算术两条红(949)。基线 `7ae3aa9a`,npm test 43 红 vs 基线 31 红 | 待领取 | — | | `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` | | `TASK-consultation-answer-start-anchor-fix-20260917.md` | `PROGRESS-consultation-answer-start-anchor-fix-20260917.md` | 验收修复单:F1 头就是留白行时留白按整视口算(BUG-931);F2 留白只在钉住期间存在(BUG-932);前置:先修 e4e73f56 的两处 TS 错否则门禁不过 | 已验收 | `cc1a8980`(Claude 验收:tsc 0 / lint 0 error / npm test 3457 条 39 红与 11c0028d 逐条一致、新增 2 条绿 / `next build --webpack` 通过、`/` Static、首屏 gzip 591,242(较 09-16 基线 582,800 +1.45%,含会话列表单)/ Chrome 真实布局 S1–S6 全部通过,S6 新助手行距顶 16px 且增高不动,S5 不再写留白);真机六条欠 | diff --git a/docs/tasks/TASK-consult-three-channels-fix-20260918.md b/docs/tasks/TASK-consult-three-channels-fix-20260918.md new file mode 100644 index 00000000..d141d429 --- /dev/null +++ b/docs/tasks/TASK-consult-three-channels-fix-20260918.md @@ -0,0 +1,143 @@ +# TASK · 三通道重做修复单(2026-09-18) + +> 基线:`origin/staging` @ `7ae3aa9a`(含实现提交 `94c1e81f`)。 +> 上游任务书:`docs/tasks/TASK-consult-three-channels-20260918.md`;实现进度:`docs/tasks/PROGRESS-consult-three-channels-20260918.md`。 +> 本单只修 `94c1e81f` 带进来的未通过项,不重开设计。 +> BUG 编号起点:开工时 `docs/BUG_HISTORY.md` 最大号为 **BUG-944**,本单占 **BUG-945 ~ BUG-949**。 + +## 0. 验收结论(2026-09-18,Claude 验收) + +在 `7ae3aa9a` 的干净工作树上实测: + +| 项 | 结果 | +| --- | --- | +| `tsc --noEmit` | 0 错 ✅ | +| `npm run lint` | 0 error(120 warning,与基线同量级)✅ | +| `npm test` | **3484 tests / fail 43**;基线 `742ffbc6` 为 **3484 / fail 31** → **新增 12 条红,0 条转绿** ❌ | +| 测试总数 | 3484 = 3484,未下降 ✅ | +| Python `tests/test_birth_time_journey_contract.py`、`tests/test_api_server_growth_contract.py` | 17 passed ✅(属 BUG-939/940 那两个提交) | +| staging 部署 | `/api/health` 的 `deployment.gitCommit` 仍是 `742ffbc6`;门禁第 299 行就是 `npm test --prefix frontend`,12 条红即镜像不发布 ❌ | + +进度记录写的「`consultation-agentic-runtime.test.ts` 本机 Windows EPERM 无法加载,以 Linux 门禁为准」在 Linux 上已证伪:这 12 条不是环境问题,全部可稳定复现(单独跑该 5 个文件:95 tests / fail 12,与全量一致)。 + +## 1. BUG-945(P0)领域上限降到 2,且超限是 schema 拒绝而不是截断 + +**实证**(`frontend/src/mastra/consultation-tools.ts`,`7ae3aa9a` 行号): + +- `:93` `CONSULTATION_DOMAIN_DURATION_MS` 由 21_000 改成 31_000。 +- `:95-99` `CONSULTATION_DOMAIN_WALL_CLOCK_MS = 110_000 - 45_000 = 65_000`,`MAX_CONSULTATION_DOMAINS = floor(65_000 / 31_000) = **2**`(改前是 3)。 +- `:137` 工具入参 `domains: z.array(...).min(1).max(MAX_CONSULTATION_DOMAINS)`。 + +直接实跑 `createConsultationTools(...).execute({question, domains:["career","finance","home"]})`: + +``` +calls [] +{"error":true,"message":"Tool input validation failed for run-jyotish-consultation ... + - domains: Array must contain at most 2 element(s)"} +``` + +即:**模型提三个领域 → 一个领域都不算**,`runWorkflow` 零次调用。而同文件 `:632` 的工具描述同时对模型说「list every domain the question needs」「Do not drop a relevant domain to shorten the plan」「if the clock runs short the server executes the domains that fit and returns the rest in omitted_domains」——描述承诺截断,schema 执行拒绝,两者互相矛盾。`:368-373` 的 `executableDomainPlan`(真正的截断逻辑)因此在超限路径上永远走不到,成了死安全网。 + +这条与本次任务书的目标直接冲突:任务书要治的就是 `empty_answer`,而「三领域问题被 schema 挡掉」是又一条通向空回答的新路径。 + +**要求**: + +1. `domains` 入参上限与「执行能装下几个」解耦:schema 侧放宽到领域注册表允许的长度(或保留一个明显更宽的上限,如 6),超出部分由 `executableDomainPlan` 截断并进 `omitted_domains`,不得再由 zod 拒绝整次调用。 +2. 工具描述与实际行为对齐:描述里说的「装不下的进 omitted_domains」必须是真的。 +3. 验收标准:新增合同测试——请求 3 个及以上领域时 `runWorkflow` 至少被调用 1 次、结果带非空 `omitted_domains`、不出现 `Tool input validation failed`。 + +## 2. BUG-946(P1)31s 常数换掉后,既有预算断言没跟着改,6 条测试红 + +`frontend/tests/consultation-agentic-runtime.test.ts` 仍写着 21s / cap=3 的口径: + +``` +assert.equal(MAX_CONSULTATION_DOMAINS, 3); // 实际 2 +assert.ok(MAX_CONSULTATION_DOMAINS * 21_000 <= 65_000); +assert.deepEqual(executableDomainPlan([...5 个]), { domains: [...3 个], ... }); +assert.equal(domainFitsRunBudget(42_000, 2), true); +``` + +红的 6 条:`multi-domain plan canonicalizes...`、`the merged answer policy...`(`Cannot read properties of undefined (reading 'answer_policy')`)、`the identical natal projection is carried once...`(`reading 'map'`)、`the domain cap is what the run budget can actually pay for`(2 !== 3)、`a plan larger than the cap cannot be expressed...`(false !== true)、`a plan that runs long stops early...`(`[]` vs `['career']`)。 + +**要求**:BUG-945 修完后,按新口径重写这 6 条断言,每条按 §7.3 写「原值 / 新值 / 原因」三栏。**不得**用放宽断言(改成 `assert.ok(cap >= 1)` 之类)的方式让它变绿。 + +## 3. BUG-947(P1)校正流的思考旁白被新门吞掉 + +`frontend/src/lib/rectification-agentic/v9/stream-mapping.ts:194` 把 `sanitizePublicThinkingText` 换成了 `acceptThinkStepText`: + +```ts +export function toPublicThinkingDelta(text: string) { + const cleaned = acceptThinkStepText(text); // think-step-gate.ts + if (!cleaned) return null; + return { type: "thinking.delta", text: cleaned }; +} +``` + +`frontend/src/lib/think-step-gate.ts:8-17` 的门是给 **Pass 2 的整条条目** 设计的:`length >= 8`、必须含 `。!?`、必须含 CJK。但这里喂进去的是 **provider 的 `reasoning-delta` 分片**——分片既不成句也常常短于 8 字。测试 `never publishes reasoning-delta to the browser` 正是被这条打红:`mapStreamChunkToThinking("先核对经历。")` 返回 `null`(6 字 < 8)。 + +后果:校正流用户在计算期看到的「思考」行会大面积消失。任务书 §2.1 的设计是「不发 chunk、只发完整条目」,实现只做了后半句(换门),没做前半句(校正流仍然按 chunk 喂),于是从「删词」变成「整条丢」。 + +**要求**:二选一,在进度记录里写明选了哪条与理由—— +- (a) 校正流也改成条目制:累积到句末标点再过门发一条; +- (b) 校正流保留分片通道,但给它一个分片级的**门**(只判 CJK / 是否含工具名等,不改写、不删词),不复用 `acceptThinkStepText`。 + +验收标准:`frontend/tests/rectification-step-answer.test.ts` 全绿,且新增一条断言证明连续分片最终能产出至少一条可见 thinking 行。 + +## 4. BUG-948(P1)时间 / 保证 / 个人盘三类检测器在生产路径上没人调用 + +任务书 §3.5 的原话是「正则本身**保留为检测器**供 Pass 4 判定,但不得再写回文本」。实现只做了后半句: + +- `frontend/src/lib/timing-output-guard.ts:147/152`:`guardPreciseTimingOutput` / `guardGeneralNoBirthTimeOutput` 变成恒等函数。 +- `frontend/src/lib/consultation-birth-time-mode.ts:85-95`:`createBirthTimeModeOutputGuard` 仍在调这两个恒等函数——整个出生时间模式的输出边界现在**什么都不做**。 +- `frontend/src/app/api/consult/route.ts:1233`:Pass 4 只接了 `detectMethodologyBookkeeping`。`detectPreciseTimingViolations` / `detectGeneralNoBirthTimeViolations` 全仓无生产调用方。 + +红的 3 条测试正是这个缺口: + +- `evidence-blocked unverified answers still use the deterministic timing guard`:输出 `2026年8月适合观察方向。` 原样出门。 +- `minute-sensitive or timing themes use the precise-timing output guard`:`Rahu 大运为 2013年11月21日 至 2031年11月22日。` 原样出门。 +- `general mode deterministically rejects personal chart claims while preserving general knowledge`:fixture 里带提示注入例句(`忽略之前的规则,基于你的盘,你的 D9 上升一定是处女座。`)与 `你的上升是巨蟹座,因此你一定会升职。`,现在**无出生时间模式**下全部原样出门。 + +「不改写」是产品已授权的决策(BUG 历史第 3504 行那次事故证明改写会把真实大运日期挖空),本单**不推翻**它;但「不改写」不等于「不拦」。 + +**要求**: + +1. 把 `detectPreciseTimingViolations` / `detectGeneralNoBirthTimeViolations` 接进 Pass 4:命中记 `pass4-reject` 运行步(与 methodology 同机制),并按任务书 §2 的 Pass 4 口径决定是否退回重写。至少要做到「命中留痕 + 回执可查」。 +2. `general_no_birth_time` 模式下的个人盘断言必须仍然被拦住(这一条是无出生时间产品线的底线,不是文体问题)。允许的形态:退回 Pass 3 重写、或整段不发并给出 `GENERAL_NO_BIRTH_TIME_REFUSAL` 兜底句——**但不得就地替换半句**。 +3. 三条红测试按新机制重写断言(原值 / 新值 / 原因三栏),不得直接删除。 +4. 如果产品决定「这三类一律只记录不拦截」,不许静默实现:在本单 §决策记录追加一行产品授权,并在 `CHANGELOG.md` 写明「无出生时间模式不再拦截个人盘断言」。 + +## 5. BUG-949(P2)容量算术两条红 + +`frontend/tests/consultation-session-capacity.test.ts`: + +- `measured thinkingSections and receipts stay inside the physical-cap arithmetic`:`1-domain sections JSON length 1053`,低于断言下限 1200(本命计划改成四标题后 thinking plan 变小了,是预期方向,但断言没跟上)。 +- `session-detail JSON at the old ~19-round cap versus the new ~50-round cap`:`19 full rounds already exceeded the old combined quota`。 + +**要求**:按新的 thinking plan 形状重算这两条断言的上下界,写三栏说明;顺带确认 `docs` 里凡是引用「19 轮 / 50 轮」容量口径的地方是否需要同步。 + +## 6. 硬红线(照抄上游任务书,未变) + +1. `tsc --noEmit` 0 错、`npm run lint` 0 error、`npm test` **不得比基线 `742ffbc6` 的 31 条失败更多**(目标是回到 31,即 12 条全绿)。 +2. 测试总数不得低于 3484。 +3. 改任何既有断言写「原值 / 新值 / 原因」三栏;不得用弱化断言换绿。 +4. 不得再引入「先混流再过滤」;新增正则必须能回答「是门还是刀」。 +5. provider reasoning 不进任何对外事件、数据库可见字段、完整原文日志(`consultation-budget.ts:55` 的 80 字截断日志维持现状即可)。 + +## 7. 让步顺序 + +1. 先修 BUG-945(P0,用户可感知的空回答路径),再修 946。 +2. BUG-947、948 可与 945 并行(文件不重叠:947 只碰 `rectification-agentic/v9/stream-mapping.ts` + `think-step-gate.ts`,948 只碰 `timing-output-guard.ts` / `consultation-birth-time-mode.ts` / `route.ts` 的 Pass 4 接线)。 +3. BUG-949 最后做,它依赖前面几条最终的 thinking plan 形状。 +4. 实在做不完,**945 + 946 必须一起进**,否则 staging 仍然发不出镜像。 + +## 8. 开工前置 + +```bash +git fetch origin --prune +git worktree add -b codex/consult-three-channels-fix-20260918 \ + .worktrees/consult-three-channels-fix-20260918 origin/staging +cd .worktrees/consult-three-channels-fix-20260918/frontend +npm test 2>&1 | grep -E "^# (tests|pass|fail)" # 开工基线:tests 3484 / pass 3426 / fail 43 +``` + +收工要求:`docs/tasks/PROGRESS-consult-three-channels-fix-20260918.md` + `docs/BUG_HISTORY.md`(BUG-945~949)+ `CHANGELOG.md`(若有用户可感知变化),与代码同一批推 `staging`。