Files
Jyotisha/docs/tasks/TASK-rectification-title-repair-migration-20260915.md
T
Jesse_ChenandClaude Fable 5 ebd6175b40 docs(tasks): give the title repair a button — move it into a migration
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
2026-09-15 15:01:30 +00:00

7.9 KiB
Raw Blame History

TASK · 把会话标题/活跃时间修补做成迁移,让产品能点按钮修

  • 日期:2026-09-15
  • 基线 commitorigin/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.sqlcompose --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_URLrepair-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_SQLwhere 在生产上自然匹配 0 行。

所以这条迁移在生产上是天然的 no-op,不需要为它单独设防,也不必阻止它随 main 提升进入生产。


3. 决策记录(产品已授权)

  1. 改成迁移,不做新 workflow,不走 SSH。 复用 Migrate Staging Database 这个已经存在、产品已经会用的按钮。
  2. 保留 repair-rectification-session-titles.mjs 作为只读核对工具,不删——将来排查时还能用它数行数。但它不再是交付路径。
  3. 迁移必须幂等、只动匹配行、不猜日期(取不到 created_at 就退回不带日期的「生时校正」,与脚本现有规则一致)。

4. 硬红线

  1. SQL 逻辑必须与脚本逐字一致TITLE_MATCH_SQL 的正则与时区(Asia/Shanghai)、TITLE_APPLY_SQLcase、活跃时间回填的 coalesce(turns.last_turn_at, case_row.last_activity_at)updated_at < 单调守卫,一个都不许改写或"顺手优化"。改了就不是同一套修补了。
  2. 不得动 deploy/run-staging-migration.shdeploy/docker-compose.postgres.yml 或任何 .gitea/workflows/**AGENTS.md §2.7:workflow 只由产品负责人触发和修改)。这条迁移必须能被现有的 migrator 原样应用。
  3. 用户手动改过的标题不得触碰——不匹配 TITLE_MATCH_SQL 正则的行一律不动。
  4. 迁移必须能重复应用而不产生第二次改动(幂等)。
  5. tsc --noEmit 0 错;npm run lint 0 error;测试总数不降。

5. 任务分解

任务 1 · 新增迁移文件

1.1frontend/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-699BUG-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. 让步顺序

  1. 若活跃时间回填(updated_at)在迁移里因为跨表 join 写不干净,先只交标题修补,活跃时间那段留到下一轮并写进 PROGRESS。标题是用户直接看得见的,优先。
  2. 绝不让步: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. 产品侧操作(修完之后)

  1. 等门禁转绿、Deploy Staging 把新 SHA 发上去。
  2. Gitea → Migrate Staging Database → 填那个 SHA → 运行。
  3. 在运行日志里找 NOTICE,看修了多少行。
  4. 刷新页面,检查历史会话的名字是否恢复成真实日期。

不需要 SSH,不需要命令行,不需要数据库口令。