Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016P5RoqzmUQEbeC2qjAkeGr
6.7 KiB
任务书 · BUG-526 修复:报告链路不得直读已收权的校正表(2026-09-04)
基线:origin/staging @ cce919fe(开工时 git fetch 后以 origin/staging HEAD 为准)。
事故实证(沿用执行方 BUG-526 取证,已核对代码与迁移)
- BUG-524(float 崩溃)已修复并部署(
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:25revoke all on table public.agentic_rectification_cases from service_role,此后无任何再授权——该表运行时只许走 security definer RPC,v9 校正链路全部如此,因此校正功能正常。 - 上游同步
a7041529新增了两处 service_role 直读(BUG-526 只点名了第一处):frontend/src/lib/personal-report-worker.ts:278(worker 的generate包装);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 修复已在引擎侧落实)。
硬红线
- 不得恢复
service_role对agentic_rectification_cases的表级权限。20260814 的收权是安全设计(skill 身份不可变性防御),谁也不许 re-grant。 - 读失败必须降级:两处候选窗读取在任何错误(permission denied、超时、行不存在)下都返回
nullrange 并继续——报告以无窗口的敏感度输入正常生成,不得再映射成calculation_unavailable。 - 错误语义区分(BUG-526 防复发):
calculation_unavailable从此只代表引擎调用失败;候选窗读失败只记一条不含内容的 warn 级日志(错误码级别)。 - 迁移(方案 B)照 v9 系列写法:
security definer、revoke all from public、显式grant execute to service_role、事务内、幂等、时间戳晚于现有迁移;必须npm run test:db真跑(需要 Docker;没有就走 BLOCKED 流程停下)。 - 其余既有红线延续(不改
.gitea/workflows/**、不提升 main、./node_modules/.bin/tsc而非npx tsc、隐私日志边界)。
开工前置
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,降级则相应状态)。
任务 4(P1)· 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。
交付物清单
- 共享候选窗读取 helper:降级三态测试 + 错误语义修正
- 只读 RPC 迁移 +
test:db实跑输出(权限矩阵四断言) - 两处调用切换 diff;
completed枚举修正说明 - 部署后真实报告全套实测(章节正文 / telemetry / 回溯 / tokens 与 2 倍线对照)
- BUG-526 收口 + PROGRESS + 全套质量门实际输出