BUG-699 / 704 的数据修补写成了 repair-rectification-session-titles.mjs,要 SCHEMA_DATABASE_URL。但 deploy/run-staging-migration.sh 只做三件事:跑 002-ensure-business-compatibility-roles.sql、compose --profile migration run --rm migrator 应用 frontend/supabase/migrations/ 下的 SQL、打印已应用清单, 不执行任意 Node 脚本。其余 workflow 也都不跑它。于是一个已经验收的修复卡在 "没有按钮"上,用户的历史会话名字还全是错日期。 脚本里 TITLE_MATCH_SQL / TITLE_APPLY_SQL 和活跃时间回填本来就是纯 SQL, Node 只负责连库和打印。搬进一次性迁移就能复用产品已经会用的 Migrate Staging Database 按钮,不用 SSH、不用口令。 生产安全性已核实:7b620c7a 没有 use-rectification-surface.ts,标题是固定的 「生时校正」也不写库,where 在生产上自然匹配 0 行,天然 no-op。 红线:SQL 逐字照搬不许优化;不得动 workflow 或 compose;用户手写标题不碰; 必须幂等;两段 update 各要 RAISE NOTICE 打行数——那是产品唯一看得到数字的 地方。脚本降级为只读核对工具,不删。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0155nFCgCHtoA7jhSDGmZmMu
7.9 KiB
TASK · 把会话标题/活跃时间修补做成迁移,让产品能点按钮修
- 日期:2026-09-15
- 基线 commit:
origin/staging@e7016551 - 执行分支:
codex/rectification-title-repair-migration-20260915 - 规模:把一段已经写好、已经是纯 SQL 的修补逻辑搬进迁移文件。不改任何前端行为。
1. 问题:修补脚本没有可执行的通道
BUG-699 / BUG-704 的数据修补写在 frontend/scripts/repair-rectification-session-titles.mjs。它到今天一次都没跑过,因为产品负责人没有任何地方可以运行它:
| 现有通道 | 能不能跑这个脚本 |
|---|---|
Gitea Migrate Staging Database(手动) |
不能。deploy/run-staging-migration.sh 只做三件事:跑 002-ensure-business-compatibility-roles.sql、compose --profile migration run --rm migrator(应用 frontend/supabase/migrations/ 下的 SQL)、然后打印已应用清单。不执行任意 Node 脚本。 |
Gitea Deploy Staging |
不能,只发布镜像。 |
| 其余 workflow | deploy-production / migrate-production-database / release-quality-gate / reset-staging-account / create-production-recovery,都不跑这个。 |
| 本地 | 脚本要 SCHEMA_DATABASE_URL(repair-rectification-session-titles.mjs:62),那是服务器上的库口令。产品负责人不该拿,验收机也没有。 |
| SSH 上服务器手跑 | 技术上可行,但产品负责人是非程序员;AGENTS.md 也没有"手工在生产机上跑一次性脚本"的既定流程,且口令不得进聊天。 |
结果:一个已经写好、已经验收的修复,卡在"没有按钮"上。 用户的历史会话现在名字全是错的日期,等着这一步。
2. 修法:改成一次性迁移,复用现成的按钮
脚本里的逻辑本来就是纯 SQL——TITLE_MATCH_SQL / TITLE_APPLY_SQL / ACTIVITY_COUNT_SQL 及其 update 全是 SQL 字符串,Node 只负责连库、传 --apply、打印计数。搬进迁移文件是机械操作,不需要重写逻辑。
搬完之后产品只需要:Gitea → Migrate Staging Database → 填 SHA → 跑。和平时迁移数据库一模一样,不用 SSH、不用口令、不用命令行。
2.1 生产安全性(已核实,写进决策)
PROGRESS-rectification-open-retitles-session-20260915.md 已经核对过:生产停在 7b620c7a,那个版本没有 use-rectification-surface.ts,标题用的是固定的「生时校正」,也不写库。生产上不存在被改坏的标题,TITLE_MATCH_SQL 的 where 在生产上自然匹配 0 行。
所以这条迁移在生产上是天然的 no-op,不需要为它单独设防,也不必阻止它随 main 提升进入生产。
3. 决策记录(产品已授权)
- 改成迁移,不做新 workflow,不走 SSH。 复用
Migrate Staging Database这个已经存在、产品已经会用的按钮。 - 保留
repair-rectification-session-titles.mjs作为只读核对工具,不删——将来排查时还能用它数行数。但它不再是交付路径。 - 迁移必须幂等、只动匹配行、不猜日期(取不到
created_at就退回不带日期的「生时校正」,与脚本现有规则一致)。
4. 硬红线
- SQL 逻辑必须与脚本逐字一致:
TITLE_MATCH_SQL的正则与时区(Asia/Shanghai)、TITLE_APPLY_SQL的case、活跃时间回填的coalesce(turns.last_turn_at, case_row.last_activity_at)与updated_at <单调守卫,一个都不许改写或"顺手优化"。改了就不是同一套修补了。 - 不得动
deploy/run-staging-migration.sh、deploy/docker-compose.postgres.yml或任何.gitea/workflows/**(AGENTS.md§2.7:workflow 只由产品负责人触发和修改)。这条迁移必须能被现有的 migrator 原样应用。 - 用户手动改过的标题不得触碰——不匹配
TITLE_MATCH_SQL正则的行一律不动。 - 迁移必须能重复应用而不产生第二次改动(幂等)。
tsc --noEmit0 错;npm run lint0 error;测试总数不降。
5. 任务分解
任务 1 · 新增迁移文件
1.1 在 frontend/supabase/migrations/ 下新增一个一次性数据修补迁移(文件名按既有时间戳规范,排在 20260915010000_rectification_touch_chat_session.sql 之后)。
1.2 内容 = 脚本里那两段 update,原样搬入:
- 标题:
TITLE_APPLY_SQL(含TITLE_MATCH_SQL的 where) - 活跃时间:脚本里的活跃时间回填 update(含
updated_at <单调守卫)
1.3 权限守卫按 20260915010000_rectification_touch_chat_session.sql 的既有写法(current_user <> 'schema_owner' 就 raise exception),保持一致。
1.4 两段 update 各用 RAISE NOTICE 打出影响行数,这样 Migrate Staging Database 的日志里能直接看到修了多少行——这是产品唯一能看到数字的地方,不能省。
验收标准
- 迁移在
npm run test:db里能应用(见任务 3 的环境缺口说明)。 - 重复应用第二次,
RAISE NOTICE的行数为 0。 - 构造一条「标题日期与
created_at不符」的假数据 → 被修正;构造一条用户手写标题(如「妈妈的盘」)→ 不动;构造一条created_at is null的 → 变成不带日期的「生时校正」。
任务 2 · 脚本降级为只读核对
2.1 repair-rectification-session-titles.mjs 去掉 --apply 分支(或让它直接报错并提示改用迁移),只保留计数输出。文件头加注释:修补已由迁移承担,本脚本只用于核对。
2.2 不删文件。
任务 3 · 测试与文档
3.1 npm run test:db --prefix frontend(需 Docker)。验收机没有 Docker——执行方若也没有,如实写进 PROGRESS 与 BLOCKED.md,不得写成通过。这条迁移的真实证据就是产品在 staging 点一次按钮后的 RAISE NOTICE 行数。
3.2 文档:
docs/BUG_HISTORY.md:回到 BUG-699 与 BUG-704 两条记录,在「修复」末尾补一句:数据修补已改为迁移<文件名>,随Migrate Staging Database应用;并把「验证」里的欠账更新为"待产品在 staging 应用后回填行数"。不新增编号(当前最大 BUG-717,这是交付通道补齐,不是新缺陷)。deploy/README.md:在 staging 迁移那一节补一句——一次性数据修补也走同一个按钮,行数看RAISE NOTICE。docs/tasks/PROGRESS-rectification-title-repair-migration-20260915.md。- 不动
CHANGELOG.md(用户可见行为不变;标题恢复正常已经记在 BUG-699 那一轮)。
6. 让步顺序
- 若活跃时间回填(
updated_at)在迁移里因为跨表 join 写不干净,先只交标题修补,活跃时间那段留到下一轮并写进 PROGRESS。标题是用户直接看得见的,优先。 - 绝不让步:SQL 逻辑不得改写;不得动 workflow 或 compose;用户手写标题不得触碰;必须幂等。
7. 开工前置命令
cd /workspace/Jyotisha
git status -sb | head -1
git fetch origin --prune
git worktree add -b codex/rectification-title-repair-migration-20260915 \
.worktrees/rectification-title-repair-migration-20260915 origin/staging
cd .worktrees/rectification-title-repair-migration-20260915/frontend
npm ci
cat scripts/repair-rectification-session-titles.mjs # 逐字照搬的来源
ls supabase/migrations | tail -3 # 时间戳接在最后一条之后
交付:git push origin HEAD:staging,推完核对远端 SHA。
8. 产品侧操作(修完之后)
- 等门禁转绿、
Deploy Staging把新 SHA 发上去。 - Gitea →
Migrate Staging Database→ 填那个 SHA → 运行。 - 在运行日志里找
NOTICE,看修了多少行。 - 刷新页面,检查历史会话的名字是否恢复成真实日期。
不需要 SSH,不需要命令行,不需要数据库口令。