diff --git a/CHANGELOG.md b/CHANGELOG.md index cc75f171..98af1e8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - 事业清单加一条:判断表里的自然代表星、Jaimini 代表星、大运主星只看力量和受冲,不按星的本性推断职业类型、做事方式或台前 / 幕后。职业类型仍问用户。系统提示没有改。 - 保存后的预热仍走原来的咨询入口,但不再占用重计算名额,避免刚保存就提问时收到 429。预热失败不影响保存。 - 生平回测严重冲突从 3 降到 1,对照组误报仍是 0。齐达内的事业题还有一次按星性写方向,产品按严重冲突 ≤ 1 放行,另开单处理。Skill 版本不变。 +- 保存人物或本人资料时,后台预热安排不上也不会再让保存报错(BUG-1305)。 ## 2026-10-09 — 对话卡按提问当天取数,模型只看见数值(已合入 staging) diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 1a8b6f4a..aa25d2af 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -17519,3 +17519,19 @@ - 相关记录:BUG-1300、BUG-1302。 - 复发自:无。 - 修复版本:无。 + +## BUG-1305 | 保存星盘后安排预热抛错,已写库的保存返回 500(staging 门禁 run 1784 红) + +- 状态:resolved(Claude 直接修复,产品 2026-10-09 授权;推 staging 后由门禁真实 PostgreSQL 套件复核) +- 首次发现 / 最近更新:2026-10-09 / 2026-10-09 +- 来源:staging `backend-quality-gate` run 1784(`cd625e8a`)唯一失败:`database-profile-gender.test.ts`「people and account routes read and write gender on real PostgreSQL without cross-person leaks」,`500 !== 200`。 +- 影响面:新建人物、修改人物、修改本人资料三个保存接口(`/api/chart-profiles`、`/api/chart-profiles/[id]`、`/api/account`)。在 Next 正常请求里 `after` 可用,线上不必然出现;但只要安排预热失败,用户会看到保存失败而数据其实已经写进去。 +- 现象:数据库写入成功后接口仍返回 500。 +- 触发条件:`scheduleConsultPacketWarm` 用默认的 Next `after`,在没有请求作用域时调用(数据库测试直接调路由函数)。 +- 根因:BUG-1300 加的预热只对 fetch 失败兜底,没有对 `after` 本身抛错兜底;抛错落进路由的 catch,变成 500。违反修复单红线「预热失败不影响保存」。验收时本机无 Docker,数据库套件全部 skip,所以本机和执行方都没看到。 +- 修复:`frontend/src/lib/consult-packet-warm.ts` 安排预热包 try/catch,失败返回 false,保存照常返回。 +- 验证:`frontend/tests/consult-packet-warm-20261009.test.ts` 新增「a warm that cannot be scheduled does not break the save」(修复前默认依赖直接抛错);本机 `npm test` 4,999 / 失败 24 与基线逐名相同,tsc 0、lint 0;数据库套件由门禁真实 PostgreSQL 复核。 +- 防复发:上述测试;验收凡改到 `database-*.test.ts` 会调用的路由,推 staging 前用本机 PostgreSQL 替身跑一遍数据库套件。 +- 相关记录:BUG-1300、BUG-1302、BUG-1118(同类:数据库套件只在门禁里跑)。 +- 复发自:无。 +- 修复版本:本提交。 diff --git a/docs/tasks/TASK-consult-career-type-residual-20261009.md b/docs/tasks/TASK-consult-career-type-residual-20261009.md index debbd052..a2b1ee33 100644 --- a/docs/tasks/TASK-consult-career-type-residual-20261009.md +++ b/docs/tasks/TASK-consult-career-type-residual-20261009.md @@ -2,7 +2,7 @@ - 基线:`origin/staging`,含 `fc3a2e68`(对话卡同源已合入)。分支 `codex/consult-career-type-residual-20261009`。 - 前序:`TASK-consult-card-full-source-fix2-20261009.md`(已验收合入)。 -- BUG 编号:本单已登记 BUG-1303、BUG-1304(investigating),新问题从 **1305** 起,开工时核对。 +- BUG 编号:本单已登记 BUG-1303、BUG-1304(investigating),新问题从 **1306** 起,开工时核对。 ## 1. 实证 diff --git a/frontend/src/lib/consult-packet-warm.ts b/frontend/src/lib/consult-packet-warm.ts index 1bee347c..d8c76c72 100644 --- a/frontend/src/lib/consult-packet-warm.ts +++ b/frontend/src/lib/consult-packet-warm.ts @@ -54,12 +54,19 @@ export function scheduleConsultPacketWarm( const body = consultWarmBodyFromColumns(columns); if (!body) return false; const apiBase = deps.apiBase ?? "http://127.0.0.1:5200"; - deps.after(() => { - return deps.fetch(`${apiBase}/api/consultation_workflow`, { - method: "POST", - headers: { "content-type": "application/json" }, - body: JSON.stringify(body), - }).then(() => undefined).catch(() => undefined); - }); + // Warm is best effort. `after` throws outside a request scope; the save has + // already been written by then, so a scheduling failure must not turn it + // into a 500 (BUG-1305). + try { + deps.after(() => { + return deps.fetch(`${apiBase}/api/consultation_workflow`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify(body), + }).then(() => undefined).catch(() => undefined); + }); + } catch { + return false; + } return true; } diff --git a/frontend/tests/consult-packet-warm-20261009.test.ts b/frontend/tests/consult-packet-warm-20261009.test.ts index 393affc3..0b03b710 100644 --- a/frontend/tests/consult-packet-warm-20261009.test.ts +++ b/frontend/tests/consult-packet-warm-20261009.test.ts @@ -33,3 +33,19 @@ test("a saved chart becomes a warm request and the caller does not wait", () => assert.equal(started, true); assert.equal(finished, false); }); + +test("a warm that cannot be scheduled does not break the save (BUG-1305)", () => { + // Default deps use Next `after`, which throws outside a request scope. + assert.equal(scheduleConsultPacketWarm(columns), false); + let fetched = false; + const scheduled = scheduleConsultPacketWarm(columns, { + after: () => { throw new Error("outside a request scope"); }, + fetch: () => { + fetched = true; + return Promise.resolve(new Response(null)); + }, + apiBase: "http://127.0.0.1:9", + }); + assert.equal(scheduled, false); + assert.equal(fetched, false); +});