fix(report): load candidate ranges through a read-only RPC

Accepted profiles were selecting a revoked rectification table and failing the report before the engine ran. Degrade to a null window when that optional read fails.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-04 19:16:45 +08:00
parent 73a04e9970
commit 7baf230066
13 changed files with 602 additions and 84 deletions
+8 -8
View File
@@ -8117,19 +8117,19 @@
## BUG-526 | accepted 生时的个人报告 worker 直接读已收回权限的校正表,16 秒 calculation_unavailable
- 状态:investigating
- 状态:resolved
- 首次发现:2026-09-04
- 最近更新:2026-09-04
- 影响面:`frontend/src/lib/personal-report-worker.ts` `createProductionWorker``generate``public.agentic_rectification_cases`
- 影响面:`frontend/src/lib/personal-report-worker.ts` `createProductionWorker``generate``frontend/src/app/api/reports/route.ts``loadCandidateRange``public.agentic_rectification_cases`
- 用户现象:staging 上 standard personal_full 创建成功后约 16 秒失败,`failureCode = calculation_unavailable`,无章节行。表现与 BUG-524 事故码相同,但引擎访问日志里没有 `POST /api/consultation_workflow`
- 触发条件:资料 `birth_time_status``accepted`(不是 `confirmed`),worker 因此去查 `agentic_rectification_cases``candidate_accepted` 行。
- 根因:`20260814010000_immutable_skill_registry.sql` 已从该表收回 `service_role` 的表级权限,只许走 security definer RPC。报告 worker `from("agentic_rectification_cases").select(...)`。Postgres 三次 `permission denied for table agentic_rectification_cases`(与 job 三次 attempt、默认 5s/10s 重试对齐)。查询失败被映射成可重试 `calculation_unavailable`,报告从未调用引擎。
- 修复:未做。本轮任务书禁止为 BUG-524 改前端 worker。候选方向:`accepted` 已有可用 `active_birth_time` 时不要读这张锁死表;或经允许的 RPC 取候选窗;查询失败时降级为无 range 继续生成,而不是打死整份报告
- 验证:staging Postgres 日志三次 permission denied;同期 API 无 consultation_workflow;同机 web→api 虚构 smoke 为 200BUG-524 已修好)
- 防复发:报告 worker 不得再直接 SELECT 已收回 `service_role` 权限的校正表;`calculation_unavailable` 必须能区分「引擎 500」与「可选候选窗读失败」
- 触发条件:资料 `birth_time_status``accepted`(不是 `confirmed`),worker 与 create route 因此去查 `agentic_rectification_cases``candidate_accepted` 行。
- 根因:`20260814010000_immutable_skill_registry.sql` 已从该表收回 `service_role` 的表级权限,只许走 security definer RPC。报告链路`from("agentic_rectification_cases").select(...)`。Postgres 三次 `permission denied for table agentic_rectification_cases`(与 job 三次 attempt、默认 5s/10s 重试对齐)。查询失败被映射成可重试 `calculation_unavailable`,报告从未调用引擎。第二读者:create route 同样 `if (error) throw error`,会把创建请求打成 `calculation_unavailable`
- 修复:新增只读 RPC `read_report_candidate_range`(不 re-grant 表权限)。worker 与 create route 经共享 helper 调用;任何读失败降级为无窗口并继续生成。`calculation_unavailable` 只保留给引擎调用失败。legacy 表终态以库约束为准:`confirmed` `completed``20260720` 已纳入枚举;前端不再 `.in("status", ["confirmed", "completed"])` 直查)
- 验证:`tests/report-candidate-range.test.ts` 三态降级;`tests/personal-report-api.test.ts` null 窗口仍 201`tests/database-report-candidate-range.test.ts` 权限矩阵与列白名单。产品侧真实 personal_full 待本修复部署后补
- 防复发:报告 worker 与 create route 不得再直接 SELECT 已收回 `service_role` 权限的校正表;候选窗读失败不得再变成 `calculation_unavailable`
- 相关记录:BUG-524
- 复发自:无
- 修复版本:待修复
- 修复版本:待发布
- 编号说明:rebase 到 `origin/staging` 时 BUG-525 已被采集拒答占用,本条落在 BUG-526。
## BUG-527 | 可评分事件不足 3 条时盘外核对抢跑到已拒答领域
@@ -0,0 +1,43 @@
# PROGRESS · BUG-526 报告链路不得直读已收权的校正表(2026-09-04)
工作树:`/Users/jesse/Downloads/Copse/astrology/.worktrees/report-candidate-range-20260904`
分支:`codex/report-candidate-range-20260904`
基线:任务书写 `cce919fe`;开工 `origin/staging` HEAD 为 `846b6064`(含本任务书)。
任务书:仓库根 `TASK-report-candidate-range-read-20260904.md`
未恢复 `service_role` 表级权限,未改表/列/RLS/既有 grant,未改 worker 其余生成管线,未改 `.gitea/workflows/**`,不提升 main。
| 任务 | 状态 | BUG |
| --- | --- | --- |
| 1 两处读取降级 + 语义修正 | 完成 | BUG-526 |
| 2 只读 RPC + `test:db` | 完成(本文件定向套件已绿;全量 `test:db` 见下方门禁输出) | BUG-526 |
| 3 部署后真实 personal_full | 待部署 | BUG-526 |
| 4 BUG-526 收口 | 完成(修复版本待 staging SHA | BUG-526 |
## 实现要点
- 共享 helper `frontend/src/lib/report-candidate-range.ts`:只调 `read_report_candidate_range`。permission denied / 抛错 → warn(仅 `event` + `reason` 错误码)→ `null`;空行 → `null` 且不 warn。非法时钟或 `start > end` 也当无窗口。
- worker `generate``POST /api/reports``loadCandidateRange` 都走 helper,不再 `from("agentic_rectification_cases")` / `from("birth_time_rectification_cases")`
- 迁移 `20260904010000_read_report_candidate_range.sql``security definer``search_path=''``revoke` public/anon/authenticated、`grant execute` 只给 `service_role`。返回值只有 `start_time` / `end_time`
- **`completed` 枚举:** 任务书以为它不在 `birth_time_rectification_cases` 状态枚举里。`20260720010000_conversational_rectification_schema.sql` 已纳入 `confirmed``completed` 两个终态。RPC 使用这两值;前端不再维护一份 `.in("status", …)` 列表。`abandoned` 不取窗。
## 测试
- `tests/report-candidate-range.test.ts`permission denied / throw / 空行三态;合法窗;敏感度输入无窗口;源码锁两处调用。
- `tests/personal-report-api.test.ts``loadCandidateRange` 返回 `null` 时 create 仍 201,不是 `calculation_unavailable`。原「只信 confirmed/completed 直读」改为 RPC helper 源码锁。
- `tests/database-report-candidate-range.test.ts``service_role` 可 execute`anon`/`authenticated` 不可;表 SELECT 仍 denied;无行返回空;窗口 JSON 只有两列;legacy confirmed 行优先。
## 让步
无。未 re-grant 表权限。
## 门禁解堵
`7ee7f825` 的 CSS 合同脱节已由 `0be51e65` / BUG-532 合入 staging,本单不再改那条测试。
## 门禁输出
- `./node_modules/.bin/tsc --noEmit`0
- `npm run lint`0 error / 74 warning(既有,未动)
- 定向:`report-candidate-range` + `personal-report-api` + `personal-report-worker` + migration 源码锁 **75 pass**
- `npm run test:db``tsx --test --test-concurrency=1 tests/database-*.test.ts`):**35 pass, 0 fail**(含新 RPC 套件)。第一次全量 34/35,`database-admin-identity` 迁移在套件开头失败,单跑即过,属 Docker 争用。
- 非数据库 `tsx --test tests/*.test.ts`2626 pass;当时 2 fail 为 CSS 合同(已由 `0be51e65` 修)与 `identity-auth-integration`(与并行 `test:db` 抢 fixture,单跑已过)。
+1
View File
@@ -75,6 +75,7 @@
| `TASK-report-blocked-repairs-20260902.md` | `PROGRESS-report-blocked-repairs-20260902.md` | 全主题 blocked 修复 | 已验收 | `7faf8555` |
| `TASK-report-section-writer-failure-20260902.md` | `PROGRESS-report-writer-failure-20260902.md` | 写作阶段 report_schema_invalid | 已验收 | `eda37c15`(后续 `43294265``5c0bec0c``fbd6e480``cf6405ed` |
| `TASK-report-sensitivity-crash-20260904.md`(仓库根) | `PROGRESS-report-sensitivity-crash-20260904.md` | consultation_workflow float 时辰崩溃,全量 500 | 执行中 | `codex/report-sensitivity-crash-20260904`BUG-524 |
| `TASK-report-candidate-range-read-20260904.md`(仓库根) | `PROGRESS-report-candidate-range-20260904.md` | accepted 生时报告直读已收权校正表 | 待验收 | `codex/report-candidate-range-20260904`BUG-526 |
### 前端基础与工程