test(contract): 让前端源码合同跟上搬家,快速门 Python 段恢复全绿(BUG-736)
Independent Staging Quality Gate / validate (push) Failing after 10m4s
Independent Staging Quality Gate / publish (push) Skipped

三条断言过期,不是保证丢了:

- 149e1ec4 把创建路由改走 chatSessionCreateInsertRow(...),`user_id: user.id`
  字面量消失。按三栏改成路由断 `userId: user.id`、write-contract 断
  `user_id: input.userId`,两处都断,不放宽。
- 状态下沉第二批把合盘搬进新建的 use-synastry.ts,而 _home_surface() 只拼
  OPTIONAL_HOME_HOOKS 白名单,于是 fetch("/api/synastry" 与「未能存入历史」
  在并集里找不到。白名单补上该 hook 并注明新增 Home 级 hook 必须同步登记。

为什么没被拦住:这几条是 Python 测试,断言对象却是前端源码;前端轮次的验收
口径不含快速门,AGENTS §10 又只要求「任何 Python 改动」跑快速门——纯前端改动
落在两张网中间。验收方两轮都只跑前端套件,带红合入。防复发写进 BUG-736。

验证:test_supabase_user_data_contract.py 8 条全绿;快速门 pytest 段
792 passed / 1 skipped / 0 failed(此前 3 failed)。整体仍退出 1,剩余唯一
原因是本机无 rsync、无 Docker 的既有环境缺口。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
This commit is contained in:
Jesse_Chen
2026-09-16 02:12:36 +00:00
co-authored by Claude Opus 5
parent 7ae6df3ebb
commit dc8cae316e
2 changed files with 29 additions and 2 deletions
+17
View File
@@ -11460,3 +11460,20 @@
- 相关记录:BUG-721(同一形状的第三个实例)、BUG-734(同一轮的另一处)、BUG-733(同进程差分怎么写)
- 复发自:无
- 修复版本:待发布
## BUG-736 | 纯前端改动打红 Python 源码合同,快速门连续两轮带红合入
- 状态:resolved
- 首次发现:2026-09-16
- 最近更新:2026-09-16
- 影响面:`tests/test_supabase_user_data_contract.py``scripts/run_quality_gate.py --profile quick``frontend/src/app/api/sessions/route.ts``frontend/src/hooks/use-synastry.ts`
- 用户现象:无(门禁问题,用户不可感知)。
- 触发条件:纯前端轮次移动了 `page.tsx` / 路由里的字面量或把逻辑搬进新 hook。
- 根因:`test_supabase_user_data_contract.py` 用正则扫前端源码,`_home_surface()` 只拼 `OPTIONAL_HOME_HOOKS` 白名单里的文件。两轮改动各打红一批断言:`149e1ec4` 把创建路由改走 `chatSessionCreateInsertRow(...)``user_id: user.id` 字面量消失;状态下沉第二批把合盘搬进新建的 `use-synastry.ts`,它不在白名单里,于是 `fetch("/api/synastry"``未能存入历史` 在并集里找不到。**三条都是断言过期,归属校验与云端持久化保证本身没丢。**
- 为什么没被拦住:这几条是 **Python 测试**,但断言对象是**前端源码**。前端轮次的验收口径(tsc / lint / `npm test` / `next build`)不含 Python 快速门,而 AGENTS §10 只要求「任何 Python 改动」跑快速门——纯前端改动落在两张网中间。验收方(Claude)两轮都只跑了前端套件,未跑快速门,因此带红合入。
- 修复:`OPTIONAL_HOME_HOOKS``use-synastry.ts` 并加注释说明新增 Home 级 hook 必须同步登记;`user_id: user.id` 断言按「原值 / 新值 / 原因」三栏改成 `userId: user.id`(路由)+ `user_id: input.userId``chat-session-write-contract.ts`),两处都断,不放宽。
- 验证:`.venv/bin/python -m pytest tests/test_supabase_user_data_contract.py` 8 条全绿;`run_quality_gate.py --profile quick` 的 pytest 段 **792 passed / 1 skipped / 0 failed**(此前 3 failed)。快速门整体仍退出 1,剩余唯一原因是本机无 `rsync` 与无 Docker 的既有环境缺口。
- 防复发:**改动 `page.tsx`、Home 级 hook 或 `frontend/src/app/api/**` 的轮次,验收必须另跑 `run_quality_gate.py --profile quick`**,不能只跑前端套件——有 Python 测试把前端源码当断言对象。新增 Home 级 hook 必须同步登记进 `OPTIONAL_HOME_HOOKS`。取门禁退出码不得隔着管道(`| tail` 会把退出码换成 `tail` 的)。
- 相关记录:BUG-729731(创建路由改造)、BUG-464(会话持久化归属)
- 复发自:无
- 修复版本:待发布
+12 -2
View File
@@ -57,6 +57,10 @@ OPTIONAL_HOME_HOOKS = (
_FRONTEND_SRC / "hooks" / "use-consultation-run.ts",
_FRONTEND_SRC / "hooks" / "use-profile-onboarding.ts",
_FRONTEND_SRC / "hooks" / "use-rectification-surface.ts",
# 状态下沉把合盘从 page.tsx 搬进了自己的 hook2026-09-16)。首页表面是这
# 些文件的并集,新 hook 不进这张白名单,下面的合盘断言就会在代码完全正确
# 的情况下变红。新增 Home 级 hook 时必须同步加进来。
_FRONTEND_SRC / "hooks" / "use-synastry.ts",
)
@@ -133,14 +137,20 @@ def test_chat_page_uses_authenticated_cloud_persistence() -> None:
assert '.from("profiles")' not in source
assert '.from("chat_sessions")' not in source
assert 'await writeChatSession(session.id, values, mode)' in source
assert 'mode === "create" ? "/api/sessions"' in (
write_contract = (
Path(__file__).resolve().parents[1]
/ "frontend"
/ "src"
/ "lib"
/ "chat-session-write-contract.ts"
).read_text(encoding="utf-8")
assert 'user_id: user.id' in create_route
assert 'mode === "create" ? "/api/sessions"' in write_contract
# 原值: assert 'user_id: user.id' in create_route
# 新值: 路由传 userId,由 chat-session-write-contract 落成 user_id
# 原因: 创建路由改走 chatSessionCreateInsertRow(...)BUG-729731 那一轮),
# 字面量搬了家;归属校验本身没变,断言要跟着搬,不是放宽。
assert 'userId: user.id' in create_route
assert 'user_id: input.userId' in write_contract
assert '.eq("user_id", user.id)' in item_route
assert '.upsert(' not in source
assert 'await persistSession(userSession)' not in source