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

6.7 KiB
Raw Blame History

任务书 · 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:234grant all ... to service_role20260814010000_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:278worker 的 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_roleagentic_rectification_cases 的表级权限。20260814 的收权是安全设计(skill 身份不可变性防御),谁也不许 re-grant。
  2. 读失败必须降级:两处候选窗读取在任何错误(permission denied、超时、行不存在)下都返回 null range 并继续——报告以无窗口的敏感度输入正常生成,不得再映射成 calculation_unavailable
  3. 错误语义区分(BUG-526 防复发):calculation_unavailable 从此只代表引擎调用失败;候选窗读失败只记一条不含内容的 warn 级日志(错误码级别)。
  4. 迁移(方案 B)照 v9 系列写法:security definerrevoke all from public、显式 grant execute to service_role、事务内、幂等、时间戳晚于现有迁移;必须 npm run test:db 真跑(需要 Docker;没有就走 BLOCKED 流程停下)。
  5. 其余既有红线延续(不改 .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.tsgenerate 包装)、frontend/src/app/api/reports/route.ts:195-220frontend/src/lib/personal-report-route-core.tsloadCandidateRange 消费处(创建路径的 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. 两处调用切换 diffcompleted 枚举修正说明
  4. 部署后真实报告全套实测(章节正文 / telemetry / 回溯 / tokens 与 2 倍线对照)
  5. BUG-526 收口 + PROGRESS + 全套质量门实际输出