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

131 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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. 决策记录(产品已授权)
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_SQL``case`、活跃时间回填的 `coalesce(turns.last_turn_at, case_row.last_activity_at)``updated_at <` 单调守卫,一个都不许改写或"顺手优化"。改了就不是同一套修补了。
2. **不得动 `deploy/run-staging-migration.sh`、`deploy/docker-compose.postgres.yml` 或任何 `.gitea/workflows/**`**`AGENTS.md` §2.7workflow 只由产品负责人触发和修改)。这条迁移必须能被**现有**的 migrator 原样应用。
3. **用户手动改过的标题不得触碰**——不匹配 `TITLE_MATCH_SQL` 正则的行一律不动。
4. 迁移必须能重复应用而不产生第二次改动(幂等)。
5. `tsc --noEmit` 0 错;`npm run lint` 0 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. 让步顺序
1. 若活跃时间回填(`updated_at`)在迁移里因为跨表 join 写不干净,**先只交标题修补**,活跃时间那段留到下一轮并写进 PROGRESS。标题是用户直接看得见的,优先。
2. **绝不让步**:SQL 逻辑不得改写;不得动 workflow 或 compose;用户手写标题不得触碰;必须幂等。
---
## 7. 开工前置命令
```bash
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,不需要命令行,不需要数据库口令。**