From 3519cf253ea0240654479e37598e74bad8ceabd0 Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Tue, 25 Aug 2026 10:52:33 +0800 Subject: [PATCH] fix(web): restore server supabase import for rectification agent route BUG-368 dropped createServerSupabaseClient while keeping the call, so staging publish failed at next build after validate skipped the production build. Co-authored-by: Cursor --- docs/BUG_HISTORY.md | 16 ++++++++++++++++ .../src/app/api/rectification/agent/route.ts | 1 + .../tests/application-billing-contract.test.ts | 2 ++ .../tests/rectification-agentic-entry.test.ts | 1 + 4 files changed, 20 insertions(+) diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 58c5a978..747564c8 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -5531,6 +5531,22 @@ - 复发自:无 - 修复版本:未修复 +## BUG-371 | staging publish 的 next build 找不到 createServerSupabaseClient + +- 状态:resolved(本地修复,待提交与发布) +- 首次发现:2026-08-25 +- 最近更新:2026-08-25 +- 影响面:Gitea `backend-quality-gate.yml` `publish`、`deploy/railway-web.Dockerfile` 的 `RUN npm run build`、`POST /api/rectification/agent`、staging 镜像发布 +- 用户现象:向 `staging` 推送 `c41c0e17` 后 run `2058` validate 通过,publish 在 web 镜像 `next build` 失败。没有 dispatch `Deploy staging`。 +- 触发条件:staging push 跳过 validate 里的 `npm run build`;publish 才在 Docker 里跑 TypeScript。 +- 根因:BUG-368 改 agent 路由时丢掉 `createServerSupabaseClient` 的 import,调用仍在。源码扫描测试不跑 `tsc`,所以 2006 项测试和 lint 都绿。 +- 修复:补回 `@/lib/supabase/server` 导入。计费合同与入口合同断言该 import 与 `await createServerSupabaseClient()` 同时存在。 +- 验证:`npx tsc --noEmit`;`application-billing-contract` 与 `rectification-agentic-entry` 相关断言。 +- 防复发:改 `frontend/src/app/api/**/route.ts` 后必须跑 `npx tsc --noEmit` 或等价的 `next build`,不能只跑字符串合同。staging push 的类型回归仍只在 publish Docker 暴露。 +- 相关记录:BUG-309、BUG-355、BUG-365、BUG-368、BUG-370 +- 复发自:BUG-365 / BUG-309(validate 跳过 `next build`,publish 才暴露类型错误);BUG-368 丢掉 import +- 修复版本:待发布 + ## BUG-370 | staging 质量门测试全绿后被 prefer-const ESLint 挡住 - 状态:resolved diff --git a/frontend/src/app/api/rectification/agent/route.ts b/frontend/src/app/api/rectification/agent/route.ts index 8625ec0a..66a7861a 100644 --- a/frontend/src/app/api/rectification/agent/route.ts +++ b/frontend/src/app/api/rectification/agent/route.ts @@ -14,6 +14,7 @@ import { loadRuntimeFeatureFlags } from "@/lib/feature-flags"; import { isProductEnabled } from "@/lib/product-access"; import { resolveSessionLanguageModel } from "@/lib/model-catalog"; import { createAdminSupabaseClient } from "@/lib/supabase/admin"; +import { createServerSupabaseClient } from "@/lib/supabase/server"; import { defaultMessageOrigin, isRectificationMessageOrigin } from "@/lib/rectification-agentic/v9/message-origin"; export const runtime = "nodejs"; diff --git a/frontend/tests/application-billing-contract.test.ts b/frontend/tests/application-billing-contract.test.ts index 4a7225fb..60ab4a61 100644 --- a/frontend/tests/application-billing-contract.test.ts +++ b/frontend/tests/application-billing-contract.test.ts @@ -17,6 +17,8 @@ function sourceBetween(source: string, start: string, end: string): string { } test("Agentic rectification reuses one case-level usage authorization and the session-pinned model version", () => { + assert.match(rectificationRoute, /import \{ createServerSupabaseClient \} from "@\/lib\/supabase\/server"/); + assert.match(rectificationRoute, /supabase = await createServerSupabaseClient\(\)/); assert.match(rectificationRoute, /select\("id,messages,session_type,model_id,model_config_version,agentic_rectification_case_id"\)/); assert.match(rectificationRoute, /resolveSessionLanguageModel\(\s*chatSession\.model_id,\s*chatSession\.model_config_version,?\s*\)/); assert.match(rectificationRoute, /modelConfigVersion: selectedModel\.configVersion/); diff --git a/frontend/tests/rectification-agentic-entry.test.ts b/frontend/tests/rectification-agentic-entry.test.ts index ad16f911..c2709848 100644 --- a/frontend/tests/rectification-agentic-entry.test.ts +++ b/frontend/tests/rectification-agentic-entry.test.ts @@ -101,6 +101,7 @@ test("opening is server-owned: shouldStartOpening drives the first turn, never c assert.match(chat, /if \(readonly \|\| openingStarted\.current \|\| !shouldStartOpening\) return/); assert.match(chat, /onOpeningConsumed\?\.\(\)/); assert.match(chat, /void send\("opening", ""\)/); + assert.match(route, /import \{ createServerSupabaseClient \} from "@\/lib\/supabase\/server"/); assert.match(route, /action: z\.enum\(\["opening", "message", "read_only", "answer_choice", "stop_and_review"\]\)/); assert.match(page, /shouldStartOpening=\{rectificationShouldStartOpening\}/); assert.match(page, /setRectificationShouldStartOpening\(opened\.shouldStartOpening\)/);