docs(tasks): 第 0 步 required 让全部咨询整轮失败(BUG-282 复发)+ 供应商拒收被静默吞掉的修复单
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
dc1a24ca48
commit
c9d7030f4f
@@ -130,7 +130,8 @@
|
||||
| `TASK-rectification-tiebreak-card-loss-20260915.md` | `PROGRESS-rectification-tiebreak-card-loss-20260915.md` | **P0**:点卡上「再答两道参考题」交付卡消失(BUG-706);按钮亮但选项建不出变成裸题(BUG-708);旁白写「相对支持度」并与卡上入口打架(BUG-709)。卡上入口删除,出卡前收集,有活题时卡留下、采用置灰 | 待验收 | `codex/rectification-p0-20260915` |
|
||||
| `TASK-rectification-p0-fix-20260915.md` | `PROGRESS-rectification-p0-fix-20260915.md` | **验收修复单**:`f51e494c` 六条缺陷全部实现且方式正确,但 `page.tsx` 从 1951 涨到 1964 行,撞了 `chart-view-route.test.ts` 的 `<= 1951` 上限(AGENTS.md §6 增长冻结)。全量 fail 32→33,就这一条。门禁红很可能是 staging 停在 `2d7698ea`、6 个提交未部署的原因。修法是把 BUG-705 的十来行接线搬出 page.tsx,不放宽上限 | 待验收 | `codex/rectification-p0-fix-20260915` |
|
||||
| `TASK-settings-dialog-size-and-nav-20260915.md` | — | **复发单**:设置弹窗四个分区尺寸仍随内容跳变(BUG-698,复发自 BUG-554——旧防复发只查「有没有写 height」,查不到「写了没生效」);首要嫌疑是 `.settings-modal` 的 `dvh` 没有 `vh` 回退,不支持时整条 `height` 作废退化成内容高度,需先复现确认。另按产品要求去掉分区菜单左侧强调条,并拆开与悬停共用的选中态 | 待领取 | `codex/settings-dialog-size-and-nav-20260915` |
|
||||
| `TASK-consult-followup-tool-contract-20260917.md` | `PROGRESS-consult-followup-tool-contract-20260917.md` | 真机:申报时段会话连发「?」「你在说什么鬼」都 `run.failed runtime_contract_incomplete`,回执无任何 `tool` 步骤。根因是 Agent 系统指令写明「简单追问可复用已有 packet / context、不调工具」,而 `contractReady()` 要求每次请求恰好一次成功排盘调用;「已有 packet」跨请求并不存在(缓存只在单次请求内)。本命与窗口两个 Agent 同构。**产品拍板方案 1**:每轮必调工具(BUG-922 删例外句 + BUG-923 第 0 步 `toolChoice: required`);否决「没调工具就走不扣点纯对话」。第一轮正经问题为何失败留 T4 取证(回执只在 web 容器日志) | 待验收 | `codex/consult-followup-tool-contract-20260917` |
|
||||
| `TASK-consult-followup-tool-contract-20260917.md` | `PROGRESS-consult-followup-tool-contract-20260917.md` | 真机:申报时段会话连发「?」「你在说什么鬼」都 `run.failed runtime_contract_incomplete`,回执无任何 `tool` 步骤。根因是 Agent 系统指令写明「简单追问可复用已有 packet / context、不调工具」,而 `contractReady()` 要求每次请求恰好一次成功排盘调用;「已有 packet」跨请求并不存在(缓存只在单次请求内)。本命与窗口两个 Agent 同构。**产品拍板方案 1**:每轮必调工具(BUG-922 删例外句 + BUG-923 第 0 步 `toolChoice: required`);否决「没调工具就走不扣点纯对话」。第一轮正经问题为何失败留 T4 取证(回执只在 web 容器日志) | 已部署 `dc2f2a16`,但 BUG-923 的第 0 步 `required` 让全部咨询整轮失败(BUG-282 复发),由修复单撤回 | `codex/consult-followup-tool-contract-20260917` |
|
||||
| `TASK-consult-followup-tool-contract-fix-20260917.md` | `PROGRESS-consult-followup-tool-contract-fix-20260917.md` | **P0**。部署 `dc2f2a16` 后再聊天仍 `runtime_contract_incomplete`,回执只有 skill + retry。主根因(推断):BUG-923 把本命/窗口第 0 步改成 `toolChoice: required`,而首轮固定开 thinking,BUG-282 实证 thinking 模式供应商拒收非 auto 的 tool_choice → 每一轮咨询在第 0 步就被拒(BUG-936,复发自 BUG-282)。次根因(确定):咨询流不处理 Mastra `error` 块,拒收在回执/事件/日志三处都不可见,合同 retry 照跑同一参数(BUG-937)。T1 撤回 required 只留 activeTools + auto;T2 error 块 → 可诊断错误码、不进合同 retry;T4 部署后取证 | 待领取 | `codex/consult-followup-tool-contract-fix-20260917` |
|
||||
| `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` |
|
||||
| `TASK-first-paint-dead-screen-fallback-20260917.md` | — | 真机:首页永远停在「正在载入账户」,兜底全在没跑起来的 bundle 里(BUG-936 investigating)。根 layout 加与 bundle 无关的内联兜底 + 去掉本仓正则后行断言 | 待领取 | — |
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
# 修复单 · 第 0 步 `toolChoice: required` 让全部咨询整轮失败(BUG-282 复发)+ 供应商拒收被咨询流静默吞掉(2026-09-17)
|
||||
|
||||
## 0. 基线
|
||||
|
||||
- 基线 commit:`db5b0a21`(`origin/staging` head)。staging 已部署 `dc2f2a16`(`/api/health` 的 `deployment.gitCommit`),即 BUG-922/923 的实现;其后到 head 只有会话列表 / 钉顶等前端改动,与本单无交集。
|
||||
- 分支:`codex/consult-followup-tool-contract-fix-20260917`,`git worktree add -b codex/consult-followup-tool-contract-fix-20260917 .worktrees/consult-followup-tool-contract-fix-20260917 origin/staging`。
|
||||
- 范围:只有前端 `frontend/src/mastra/consultation-tools.ts`、`frontend/src/lib/stream-agent-response.ts`、`frontend/src/lib/agent-observability.ts`(如需扩错误码枚举)与对应测试。不动 Python、不动迁移、不动 Skill、不动 `page.tsx`、不动生时校正 Agent、不动 `contractReady()`。
|
||||
- BUG 段:**BUG-936 起**(基线 `docs/BUG_HISTORY.md` 最大号 BUG-935,开工时再核对一次)。
|
||||
- 优先级:**P0**。按 §2 的推断,本命与申报时段两条路线的每一轮咨询在 staging 上都会失败,不只是追问轮。
|
||||
|
||||
## 1. 事故实证
|
||||
|
||||
产品负责人 2026-09-17 在 staging 部署 `dc2f2a16` 之后再次聊天,得到与 BUG-922/923 完全相同形状的失败。事件流原样如下(已去掉会话与用户标识):
|
||||
|
||||
| 事件 | 内容 |
|
||||
| --- | --- |
|
||||
| `run.started` | `runtime: mastra-agentic` |
|
||||
| `skill.started` / `skill.completed` | `jyotish-vedic-astrology` |
|
||||
| `activity` | `phase: loading-method`,「正在补齐方法与计算步骤」 |
|
||||
| `run.failed` | `code: runtime_contract_incomplete`,「Agent 未完成必要的方法与计算步骤,本次不会扣点。」 |
|
||||
| 回执 `skill` | `loaded: true, referenceReads: 0, methodologySections: 0` |
|
||||
| 回执 `steps` | 只有两步:`1 skill: completed`、`2 validation: runtime-contract-retry: completed`,**没有任何 `tool` 步骤** |
|
||||
| 回执 `stepBudget` | `planned: 11, used: 2` |
|
||||
| 回执 `workflow` | `route: declared-birth-window`,`status: blocked`,`missingLayers: ["birth-minute"]`,`techniqueTruth: declared-window` |
|
||||
|
||||
代码定位(按符号,基线 `db5b0a21`):
|
||||
|
||||
- `frontend/src/mastra/consultation-tools.ts` 的 `consultationNatalPrepareStep` 与 `consultationWindowPrepareStep`:第 0 步返回 `{ activeTools: [<排盘工具>], toolChoice: "required" }`,第 1 步起 `"auto"`。这是 BUG-923 在 `dc2f2a16` 加的。
|
||||
- `frontend/src/app/api/consult/route.ts`:`natalStreamOptions` / `windowStreamOptions` 都是 `{ ...streamOptions, prepareStep }`,而 `streamOptions` 展开了 `consultationGenerationSettings(selectedModel.model)`;该函数固定 `thinking: "enabled"`(`agentGenerationSettings(model, { thinking: "enabled" })`)。**本命首轮(`streamWithOverflowRetry(agent, natalStreamOptions)`)、窗口首轮(`streamWithOverflowRetry(agent, windowStreamOptions)`)以及两条路线的合同 `retry` 都走这套选项。**
|
||||
- `node_modules/@mastra/core`(lock 固定 `1.50.1`)`dist/chunk-TOBPSKTN.js` 的循环:每一步先调 `prepareStep`,把返回的 `toolChoice` 交给 `prepareToolsAndToolChoice`,字符串 `"required"` 原样变成 `{ type: "required" }` 送到供应商。**类型上接受,运行时是否被供应商接受是另一回事。**
|
||||
- 同文件循环末尾:供应商调用抛错时,Mastra **不会让 `fullStream` 迭代抛异常**,而是 `controller.enqueue({ type: "error", error })` 然后 `closeStream()`,流正常结束、没有 `finish` 块。
|
||||
- `frontend/src/lib/stream-agent-response.ts` 的 `mapChunk` 只认 `data-jyotish-activity` / `tool-call` / `tool-result` / `tool-error` 四种块,`consumeAttempt` 只对 `step-finish` / `finish` / `reasoning-delta` / `text-delta` 做事。全文没有任何一处判断 `chunk.type === "error"`。于是一个失败的供应商调用在咨询流里表现为:**没有工具步骤、没有文本、没有 finish、没有报错**,`contractReady()` 自然为假,进 `runtime-contract-retry`,第二轮用同一套 `windowStreamOptions` / `natalStreamOptions` 再失败一次,最终 `runtime_contract_incomplete`。
|
||||
- `frontend/src/lib/rectification-agentic/v9/agent-run.ts`:校正 Agent 早就处理这条路——`if (chunk.type === "error" || chunk.type === "abort") streamFailed = true`,并把原文含 `Thinking mode does not support this tool_choice` 的错误归类为 `thinking_tool_choice_unsupported`(BUG-282)。咨询流从未同步这层。
|
||||
|
||||
## 2. 根因
|
||||
|
||||
**主根因(推断,置信度高,见 §2a)——BUG-282 复发。** BUG-282(2026-08)实证:本仓使用的 thinking 模式供应商拒绝任何非 `auto` 的 `tool_choice`,返回原文 `Thinking mode does not support this tool_choice`、`isRetryable: false`;该条的防复发写明「**thinking 模式不能发送 named 或 required `tool_choice`**。需要限制第一步工具时,用 `activeTools` 收窄集合,把选择权留给 auto」。BUG-630 的修复也是据此只用 `activeTools` + `"auto"`。BUG-923 把两条咨询路线的第 0 步改成 `"required"`,而这两条路线的首轮固定开 thinking,于是第 0 步的供应商调用直接被拒。因为每一轮的第 0 步都必经这里,**不只是追问轮,本命与申报时段的每一次咨询在 `dc2f2a16` 之后都会以同一形状失败**。
|
||||
|
||||
**次根因(确定)——咨询流把 Mastra 的 `error` 块当空气。** 供应商拒收在回执、事件流、`agent-observability` 日志三处都不可见:回执只剩 skill + retry 两步,`modelFinishReason` 为空(没有 `finish` 块),错误码被 `contractReady()` 翻译成「合同未完成」。校正 Agent 在 BUG-282 时已经把这条路做成可诊断的 `thinking_tool_choice_unsupported`,咨询流没有对齐。这也是为什么 BUG-922/923 的任务书 T4 要求去 VPS 拉日志取证——即使拉了,日志里也没有这个错。
|
||||
|
||||
**排除的候选:**
|
||||
|
||||
- 「模型带了非法参数、Mastra 在 `execute` 前拒掉」:窗口工具 `run-jyotish-window-consultation` 的 `inputSchema` 只有 `question: z.string().trim().min(1).max(500)`,`z.object` 默认剥离未知键,几乎不可能被拒;本命路线遇到输入拒绝会由 `recordUnrecordedToolFailure` 补一条 `tool: failed` 步骤,回执里也没有。
|
||||
- 「模型没调工具」:`required` 下供应商要么拒收、要么必返回工具调用;二者都不会产生「零工具步骤 + 无报错」以外的形状,只有拒收能同时解释回执与事件流。
|
||||
|
||||
### 2a. 为什么写「推断」而不是「确认」
|
||||
|
||||
本机没有模型凭据,无法向 staging 使用的供应商发一次带 `tool_choice: required` + thinking 的请求;VPS 日志因为次根因也不含供应商原文。证据链是:BUG-282 的供应商原文 + `dc2f2a16` 引入的 `required` + Mastra 1.50.1 的 `error` 块路径 + 咨询流零处理 + 回执形状完全吻合。**T2 落地之后的第一次真实运行会把供应商原文写进日志,那才是最终确认**;若那时错误码不是 `thinking_tool_choice_unsupported`,按 §6 处理,不得硬套。
|
||||
|
||||
## 3. 决策记录
|
||||
|
||||
- **本单撤销 BUG-923 的 `toolChoice: "required"`,回到 BUG-282 / BUG-630 的口径:第 0 步只用 `activeTools` 收窄到排盘工具,`toolChoice` 为 `"auto"`。** BUG-923 记录里「若线上某模型拒收,另立单,不得静默改回 auto 而不改提示词」——本单就是那张单;提示词侧的 BUG-922(每轮必调、packet 不跨请求)**保留不动**,它才是 BUG-922/923 事故里模型跳过工具的真正原因。
|
||||
- 责任说明:`TASK-consult-followup-tool-contract-20260917.md` 的 T2 是本会话(Claude)写的,写时没有检索到 BUG-282 的防复发条款,执行方按单做了。这不是执行方的错。本单在 BUG-936 记录里如实写「复发自 BUG-282」,并说明旧防复发为何没拦住:BUG-282 的约束只落在 `rectification-v9-agent.test.ts` 里锁校正 Agent 的第一步,咨询侧没有任何测试禁止 `required`,任务书作者也没检索到。
|
||||
- **不在本单里尝试「第 0 步关 thinking 再发 `required`」。** Mastra 的 `prepareStep` 返回值支持 `providerOptions` 覆盖,理论上可以在第 0 步关 thinking 换取确定性的工具调用;但本仓没有任何证据证明该供应商在非 thinking 模式接受 `required`,且第 0 步是模型选领域的一步,关掉推理会改变回答质量。这条路要另立研究单、带真实供应商实测,不得在本单顺手试。
|
||||
- 产品负责人 2026-09-17 授权:**先止血(T1)并把错误做成可见(T2)**,不等 T4 取证;T4 的日志证据在部署后补。
|
||||
- 不推翻任何既有红线:`contractReady()` 「恰好一次成功调用」不放宽;BUG-214(失败尝试后重试成功仍过门禁)语义保持;BUG-286(契约未绿即丢弃文本)语义保持;BUG-922 的提示词保持。
|
||||
|
||||
## 4. 硬红线
|
||||
|
||||
1. 不改 `contractReady()`;不改扣点语义(失败轮仍不扣点)。
|
||||
2. 咨询两条路线的第 0 步 `toolChoice` 只能是 `"auto"`;不得为了让工具「一定被调」再发 named / required(BUG-282)。
|
||||
3. `mapChunk` / `consumeAttempt` 对 `error` 块的处理不得把供应商原文、请求体、用户资料写进公开事件;公开事件只带闭合错误码,原文只进服务端日志且脱敏(对齐 BUG-282「attempt 失败日志不含正文」)。
|
||||
4. `page.tsx`、`jyotish_api_server.py`、Skill、`.gitea/workflows/**` 不动。
|
||||
5. 既有测试断言不得静默弱化;改 BUG-923 那两条 `required` 断言时写「原值 / 新值 / 原因」三栏。
|
||||
6. Bug 历史与进度记录不写会话 ID、用户 ID、出生资料。
|
||||
|
||||
## 5. 任务分解
|
||||
|
||||
### T1 第 0 步撤回 `required`,只留 `activeTools`(BUG-936)
|
||||
|
||||
- `consultationNatalPrepareStep` / `consultationWindowPrepareStep`:第 0 步改回 `{ activeTools: [<排盘工具>], toolChoice: "auto" }`;第 1 步起保持 `{ toolChoice: "auto" }`(保留 `activeTools` 不设,即全部工具可用,与现状一致)。
|
||||
- 在两个函数上方加一条注释指向 BUG-282 / BUG-936:thinking 模式不得发 named / required。
|
||||
- 验收:
|
||||
- `frontend/tests/consultation-agentic-runtime.test.ts` 里 BUG-923 加的两条断言改为 `toolChoice: "auto"`,三栏写「原值 `required` / 新值 `auto` / 原因 BUG-282 供应商拒收,BUG-936」。
|
||||
- 新增一条源码契约(放 `consultation-workflow-contract.test.ts`):`consultation-tools.ts` 全文 `doesNotMatch(/toolChoice:\s*"required"/)`,并 `doesNotMatch(/type:\s*"tool",\s*toolName/)`,把 BUG-282 的约束落到咨询侧。
|
||||
- 既有「natal first step exposes only the chart calculation tool」、`route.ts` 申报时段分支使用 `windowStreamOptions` 的契约(`dc2f2a16` 加的)原样绿——`prepareStep` 本身保留,只是不再 required。
|
||||
|
||||
### T2 咨询流识别 Mastra `error` 块并给出可诊断错误码(BUG-937)
|
||||
|
||||
- `frontend/src/lib/stream-agent-response.ts` `consumeAttempt`:遇到 `chunk.type === "error"` 时记录失败并**立即结束本次 attempt**,抛出携带闭合错误码的 `Error`:
|
||||
- 错误原文含 `Thinking mode does not support this tool_choice` → `thinking_tool_choice_unsupported`;
|
||||
- 其它 → `provider_error`。
|
||||
- 原文的提取方式对齐 `rectification-agentic/v9/agent-run.ts` 的做法(取 `error.message` / `String(error)`,不取 `cause` 里的请求体)。
|
||||
- 在 `state.steps` 里追加一步 `{ kind: "validation", name: "model-stream-error", status: "failed" }`,让回执能看见「模型循环本身失败了」,与「模型没调工具」区分开。
|
||||
- **供应商错误不触发合同 `retry`。** 目前 `contractReady()` 为假就调 `options.retry()`,对拒收错误来说第二次必然同样失败,只是多等一轮。实现方式:`consumeAttempt` 抛出的错误直接冒到 `start()` 的 `catch`,不进 `if (!contractReady(options) && options.retry)` 分支(它在 `consumeAttempt` 之后,所以抛出即跳过,无需改门禁)。BUG-214 的「工具失败后重试成功」路径不受影响:工具失败走的是 `tool-error` / `tool-result` 块,不是 `error` 块。
|
||||
- 公开事件 `run.failed` 的 `code` 枚举(`consultation-agent-events.ts` 第 116 行)**不扩**:这两个码对用户没有区别,公开层继续映射到 `calculation_failed`(文案「咨询暂时无法完成,本次不会扣点」)。可诊断码只进:
|
||||
- `runFailedCode()` 的内部返回(新增内部码,映射到公开 `calculation_failed`);
|
||||
- `agent-observability.ts` 的 `knownErrorCodes`:加 `thinking_tool_choice_unsupported`、`provider_error`,让 `errorCode` 字段原样落日志;
|
||||
- 一条 `console.error("[consult-provider-error]", { requestId, code, messageHead })`,`messageHead` 截 200 字符且不含请求体。
|
||||
- 验收:
|
||||
- `consultation-agentic-runtime.test.ts` 新增:喂一个只含 `{ type: "error", error: new Error("Thinking mode does not support this tool_choice") }` 的假流,断言公开事件序列是 `run.started` → skill 两条 → `run.failed code=calculation_failed`,**没有** `activity loading-method`、**没有**第二次 `stream` 调用(`retry` 传一个会 `assert.fail` 的函数),回执 `steps` 含 `validation model-stream-error failed`,`onError` 收到的错误 `message === "thinking_tool_choice_unsupported"`。
|
||||
- 同文件再一条:`{ type: "error", error: new Error("upstream 502") }` → `provider_error`,公开码仍 `calculation_failed`。
|
||||
- `agent-observability.test.ts`(若无则加):`toAgentObservabilityErrorCode(new Error("provider_error")) === "provider_error"`。
|
||||
- 既有「a calculation that succeeds only after failed attempts still satisfies the contract」「text written before the contract completes is dropped」「contract retry runs when the first attempt made no tool call」类测试原样绿。
|
||||
|
||||
### T3 记录
|
||||
|
||||
- `docs/BUG_HISTORY.md`:
|
||||
- 新增 **BUG-936**(`toolChoice: required` 复发 BUG-282,两条咨询路线整轮失败)。状态 `resolved`,「复发自:BUG-282」,写明旧防复发为何未拦住(约束只锁在校正 Agent 测试与记录文字里,咨询侧无测试、任务书作者未检索到)。关联 BUG-282、BUG-630、BUG-922、BUG-923。
|
||||
- 新增 **BUG-937**(咨询流不处理 Mastra `error` 块,供应商拒收不可见、合同重试空转)。关联 BUG-282、BUG-214、BUG-268(同为「回执缺字段导致不可观测」)。
|
||||
- **BUG-923** 记录追加一行「最近更新 2026-09-17:`required` 被 BUG-936 撤回,防复发改为 `activeTools` + `auto`」,状态保持 `resolved` 但修复描述要改成实际留下的做法,不得留着「第 0 步 required」当防复发。
|
||||
- `CHANGELOG.md` 一条:修复部署 `dc2f2a16` 后所有咨询整轮失败;Skill 版本不变。
|
||||
- `docs/tasks/PROGRESS-consult-followup-tool-contract-fix-20260917.md`:测试数字、失败清单与基线逐条比对、gzip 前后、两条断言的三栏说明。
|
||||
- `docs/tasks/README.md`:本单一行,并把 `TASK-consult-followup-tool-contract-20260917.md` 那行状态改为「已部署但被 BUG-936 撤回 required,见修复单」。
|
||||
|
||||
### T4 部署后取证(不占 BUG 号,留给产品负责人 / 部署侧)
|
||||
|
||||
本单合入并部署后,产品负责人在 staging 分别发一轮本命咨询与一轮申报时段咨询。两种结果:
|
||||
|
||||
- 都能回答:主根因确认,本单关闭。
|
||||
- 仍失败:web 容器日志会出现 `[consult-provider-error]` 与 `agent-observability` 里的 `errorCode`。取证命令:
|
||||
|
||||
```bash
|
||||
# 在 VPS 上,以 deploy 用户
|
||||
cd /opt/jyotisha-production
|
||||
docker compose -f deploy/docker-compose.server.yml logs web --since 2026-09-17T00:00:00 \
|
||||
| grep -E 'consult-provider-error|agent-observability' | tail -20
|
||||
```
|
||||
|
||||
若 `errorCode` 是 `thinking_tool_choice_unsupported` 却仍失败,说明 T1 没生效(查部署 SHA);若是 `provider_error`,把 `messageHead` 原文(脱敏)交给 Claude 另立单。
|
||||
|
||||
## 6. 让步顺序
|
||||
|
||||
1. T2 若 `state.steps` 的 `kind` 枚举扩展会牵动 `agentExecutionReceiptSchema` 与客户端解析(`kind: z.enum(["skill", "tool", "validation"])`),就用 `kind: "validation"` + `status: "failed"`,不扩枚举。
|
||||
2. T2 若 `runFailedCode()` 的内部码与公开码分离改动过大,最低要求是:`error` 块必须让 attempt 结束、不进合同 retry、`agent-observability` 的 `errorCode` 能区分 `provider_error` 与 `runtime_contract_incomplete`。
|
||||
3. T1、T3 不可让步。
|
||||
|
||||
## 7. 开工前置命令
|
||||
|
||||
```bash
|
||||
git fetch origin --prune
|
||||
git worktree add -b codex/consult-followup-tool-contract-fix-20260917 .worktrees/consult-followup-tool-contract-fix-20260917 origin/staging
|
||||
cd .worktrees/consult-followup-tool-contract-fix-20260917/frontend
|
||||
./node_modules/.bin/tsc --noEmit && npm run lint && npm test 2>&1 | tail -20 # 记下基线总数与失败清单
|
||||
grep -o "^## BUG-[0-9]*" ../docs/BUG_HISTORY.md | sed 's/## BUG-//' | sort -n | tail -1 # 应为 935
|
||||
grep -n 'toolChoice: "required"' src/mastra/consultation-tools.ts # 应恰好 2 处,做完为 0
|
||||
grep -n '"error"' src/lib/stream-agent-response.ts # 开工应为 0 处
|
||||
```
|
||||
|
||||
验收口径:`tsc` 0 错;lint 0 error;`npm test` 失败清单与基线逐条一致(无 Docker 的那组不算新增)且总数不减;`next build` 后 `/` 仍 Static;首屏 gzip ±2%(本单不动客户端 bundle,应为 0 变化)。部署后 `/api/health` 的 `deployment.gitCommit` 等于本单合入 staging 的 SHA。
|
||||
|
||||
## 8. 环境缺口
|
||||
|
||||
- 本机无模型凭据,无法向供应商实测 `required` 的拒收原文;由 T4 部署后补。
|
||||
- 无登录态,无法在 staging 复现;真机走查条目:本命会话问一句正经问题、再问「?」;申报时段会话同样两句;四次都应得到回答,且回执 `steps` 含 `tool` 步骤。
|
||||
Reference in New Issue
Block a user