Files
Jyotisha/TASK-report-candidate-range-read-20260904.md
T

78 lines
6.7 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.
# 任务书 · BUG-526 修复:报告链路不得直读已收权的校正表(2026-09-04)
基线:`origin/staging` @ `cce919fe`(开工时 `git fetch` 后以 `origin/staging` HEAD 为准)。
## 事故实证(沿用执行方 BUG-526 取证,已核对代码与迁移)
- BUG-524float 崩溃)已修复并部署(`7b1354a7`),staging 引擎 smoke 200——但产品侧 personal_full 仍失败:创建后约 16 秒 `calculation_unavailable`,无章节行,**API 访问日志里没有 `POST /api/consultation_workflow`**(引擎从未被调用)。
- staging Postgres 日志三次 `permission denied for table agentic_rectification_cases`,与 job 三次 attempt 对齐。
- 权限事实链(委托方已核对):`20260812010000_agentic_rectification_v9_runtime.sql:234``grant all ... to service_role``20260814010000_immutable_skill_registry.sql:25` **`revoke all on table public.agentic_rectification_cases from service_role`**,此后无任何再授权——该表运行时只许走 security definer RPCv9 校正链路全部如此,因此校正功能正常。
- 上游同步 `a7041529` 新增了**两处** service_role 直读(BUG-526 只点名了第一处):
1. `frontend/src/lib/personal-report-worker.ts:278`worker 的 `generate` 包装);
2. `frontend/src/app/api/reports/route.ts:205`(报告创建 route 的 `loadCandidateRange`,同样 `admin.from("agentic_rectification_cases")``if (error) throw error`)。
- 触发条件:资料 `birth_time_status = "accepted"`(非 confirmed)。两处读取都会 `permission denied`;错误被映射成可重试 `calculation_unavailable`,报告在调用引擎之前就死了。
## 决策记录
- **解除** `TASK-report-sensitivity-crash-20260904.md` 红线 4"前端不需要改动"的限制——仅限报告链路的候选窗读取(上述两处及其共享工具),其余 worker 逻辑仍不在授权内。
- **授权新增一个只读 security definer RPC 的数据库迁移**(方案 B),作为"不改表结构"红线的明确例外:只新增 function,不动表、列、RLS、既有 grant。
- 沿用既有原则:候选窗是敏感度的**可选增强层**,其读取失败绝不允许打死整份报告(与引擎侧"敏感度层失败降级"同一原则,BUG-524 修复已在引擎侧落实)。
## 硬红线
1. **不得恢复 `service_role` 对 `agentic_rectification_cases` 的表级权限**。20260814 的收权是安全设计(skill 身份不可变性防御),谁也不许 re-grant。
2. **读失败必须降级**:两处候选窗读取在任何错误(permission denied、超时、行不存在)下都返回 `null` range 并继续——报告以无窗口的敏感度输入正常生成,不得再映射成 `calculation_unavailable`
3. 错误语义区分(BUG-526 防复发):`calculation_unavailable` 从此只代表引擎调用失败;候选窗读失败只记一条不含内容的 warn 级日志(错误码级别)。
4. 迁移(方案 B)照 v9 系列写法:`security definer``revoke all from public`、显式 `grant execute to service_role`、事务内、幂等、时间戳晚于现有迁移;**必须 `npm run test:db` 真跑**(需要 Docker;没有就走 BLOCKED 流程停下)。
5. 其余既有红线延续(不改 `.gitea/workflows/**`、不提升 main、`./node_modules/.bin/tsc` 而非 `npx tsc`、隐私日志边界)。
## 开工前置
```bash
git fetch origin --prune
git worktree add -b codex/report-candidate-range-20260904 \
../.worktrees/report-candidate-range-20260904 origin/staging
```
先读:`frontend/src/lib/personal-report-worker.ts`generate 包装)、`frontend/src/app/api/reports/route.ts:195-220``frontend/src/lib/personal-report-route-core.ts``loadCandidateRange` 消费处(创建路径的 catch 现在会把报告直接 markFailed——确认修复后该 catch 只兜真正的异常)、`frontend/supabase/migrations/20260814010000_immutable_skill_registry.sql`(RPC 的既有写法与 grant 模式)、`docs/BUG_HISTORY.md` BUG-526。
## 任务
### 任务 1(P0)· 两处读取降级 + 语义修正
- worker 与 create route 的候选窗读取统一为共享 helper:任何失败 → warn 日志(错误码)→ 返回 `null`,继续生成;`birth_time_rectification_cases` 那条查询(`.in("status", ["confirmed","completed"])`,其中 `completed` 不在该表状态枚举里,顺带修正为真实枚举值)同样降级。
- 验收:单元测试模拟 permission denied / 抛错 / 空行三态,报告链路继续且敏感度输入无窗口;`calculation_unavailable` 不再由候选窗路径产生。
### 任务 2(P0)· 合法读取通道(方案 B)
- 新增只读 RPC(如 `read_report_candidate_range(p_user_id uuid)`):返回该用户最新 `candidate_accepted` 行的 `candidate_range`(以及 `birth_time_rectification_cases` 的 confirmed 候选窗,若同函数覆盖更简洁可合并),security definer + 只授 `service_role`
- 两处读取切到 RPC;RPC 失败仍走任务 1 的降级。
- 验收:`npm run test:db` 覆盖——`service_role` 可 execute、`authenticated`/`anon` 不可 execute、无行时返回空、RPC 不暴露除候选窗外的任何列。
### 任务 3(P0)· 部署后真实验证(历轮顺延的最终实测)
health SHA 对齐后真实生成 standard personal_full(默认 5 主题):报告 ready、各章有正文;telemetry 无 `length`;≥3 处 writer 输出回溯 bundle;每章实测 `inputTokens`/墙钟对照 `docs/tasks/PROGRESS-report-skill-parity-20260901.md` 的 2 倍线裁决。**取证要快**(容器 recreate 丢日志的教训)。accepted 生时下确认敏感度层状态合法(有 RPC 窗口则 `candidate_window_only`,降级则相应状态)。
### 任务 4P1)· BUG-526 收口
`docs/BUG_HISTORY.md` BUG-526 补修复、验证、修复版本;注明第二读者(reports/route.ts:205)与 `completed` 枚举值错误的顺带修正。
## 不在本轮范围
- worker 其余逻辑、报告生成管线、writer 合同。
- `agentic_rectification_cases` 的表结构、RLS、既有 grant。
- 上游同步修复单(TASK-upstream-sync-fix)的其余项。
## 收尾
- `docs/tasks/PROGRESS-report-candidate-range-20260904.md`;推送后核对 health `.deployment.gitCommit`(注意 memory 记录显示 7ee7f825 曾有合同测试挡门禁——若门禁再挡,先修挡门项或走 BLOCKED,不得绕门禁)。
- 不提升 main。
## 交付物清单
1. 共享候选窗读取 helper:降级三态测试 + 错误语义修正
2. 只读 RPC 迁移 + `test:db` 实跑输出(权限矩阵四断言)
3. 两处调用切换 diff`completed` 枚举修正说明
4. 部署后真实报告全套实测(章节正文 / telemetry / 回溯 / tokens 与 2 倍线对照)
5. BUG-526 收口 + PROGRESS + 全套质量门实际输出