Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
9.3 KiB
9.3 KiB
TASK · 星盘档案 P1 修复单:报告用错人(P0)、本人历史 500(P0)、切人时序(P1)(2026-09-25)
基线与现状
origin/staging = fabe0114,staging 已部署该版本(deploy run 2897)。两个 P0 在 staging 上是实况。修好之前不得提升 main。- 分支
codex/people-archive-p1-fix-20260925。本单最优先,其它修复单可并行但不得碰下列文件。 - Claude 09-25 验收:全量前端测试(Linux / Node 20)3851 条,失败 61 条;与基线
514951d7的 56 条相比多 5 条,全是需要 Node 22mock.module的新测试(本机 Node 20 报ERR_MODULE_NOT_FOUND @/…),门禁 run 2895(Node 22)为绿,判为环境缺口,不算回归。next build://chart/ephemeris/people均○,首屏 gzip 130,933 B 与基线相同。数据库迁移审查:跨用户读 / 删、伪造"已校正"都不可行。
事故实证
P0-1 · 给别人生成报告,实际按户主资料计算且照常扣点
frontend/src/app/api/reports/route.tsPOST:loadSubjectBirth(supabase, userId, requestedSubject)解析到了正确的人,报告行也存了该人的chart_profile_id;但路由总是传jobs: createSupabasePersonalReportJobService(admin)→personal-report-route-core.ts走排队分支返回 202。- 真正生成在后台:
personal-report-worker-core.ts(约 316 行)const profile = await deps.loadProfile(job.userId)→personal-report-worker.tsloadProfile读profiles(户主);还带上户主的校正候选范围。worker 两个文件本轮没改。 subject-route-birth.test.ts只测失败路径,没测经 worker 的成功路径。违反 P1 单红线 1、3 与 D5。
P0-2 · GET /api/sessions(默认本人)在自托管 Postgres 上 500
app/api/sessions/route.ts:29SELF_SESSION_FILTER = "chart_profile_id.is.null,chart_profile_id.eq.self"传给.or();客户端session-list-context.tsx:96每次都带subject=,默认self。- 自托管适配器
frontend/src/lib/db/local-postgres-client-core.tsparsePostgrestOr只支持eq|neq|lt|gt|lte|gte。Claude 实测:parsePostgrestOr("chart_profile_id.is.null,chart_profile_id.eq.self")→THROWS: unsupported or filter: chart_profile_id.is.null。结果:侧栏「聊天记录暂时无法读取」,本人历史消失。 - 与 BUG-990 同类(兼容层
not(col,'is',null)未实现);BUG-990 的防复发写明"兼容层新增算子必须有真实 Postgres 覆盖,不得只加源码正则",本次 route 测试用的是 mock query builder,没拦住。
P1-1 · 整页加载后,在人物列表返回前"当前人物"一律是本人
current-subject.ts只在bindCurrentSubjectAccount(人物列表 fetch 完成后)才读 localStorage。于是:/people「和 TA 对话」整页跳/?newChat=1,NewChatDeepLink在账户和模型就绪时就触发,很可能早于 bind → 新对话绑成本人;「看星盘」「生成报告」整页跳转后先拉本人的盘 / 报告列表再切 → 闪现上一个人(违反红线 4)。
P1-2 · 已打开对话的消息可能被清空
use-session-management.ts新增的订阅在每次 emit 时用第一页列表行(messagesHydrated:false, messages:[])替换 Home 的sessions;bindCurrentSubjectAccount在 id 就绪后总会 emit 一次。已加载的当前对话被换成空行,而ensureSessionMessages以activeSessionId为依赖、不重拉 → 对话内容变空;第一页之外或 URL 打开的会话被丢。SessionListProvider每次 emit 还setSessions([])→ 侧栏闪空。
P2
- 迁移回填把旧 jsonb 里客户端可随意写的
accepted/confirmed与active_*抄进类型化列 → 未经校正的值变成"已校正"。 - 标题切换器的人物列表按页面加载缓存(
loadSubjectCatalog),/people增删改后不刷新;删掉的人仍可选。 /people删除确认在用量请求失败时显示「0 个对话和 0 份报告」,不可逆操作前给出错误数字。- 今日星语是否加载用的是户主的
profileComplete/natalMinuteAvailable/profile.timezoneId;户主缺分钟时别人的卡片被压掉;客户端指纹用户主资料 + 人物 id,改别人资料后当天卡片不更新。 - 「和我合盘」把当前人物切成对方,后续对话可能绑成对方,而问题文本里的「我」是本人。
ab6c55f8在星历挂载时cache.current.clear(); invalidateEphemerisPage()→ 暖进也多拉一次?date=(两轮引擎);首屏刷新失败(429 / 坏包)后永远停在「这一天的星历还没拿到。」,无重试。fabe0114约 11 处改动断言没有「原值 / 新值 / 原因」(session-list-provider、personal-report-api、settings-mvp-contract、chart-view-route、server-owned-birth-profile、secondary-page-entry等),PROGRESS 写「见各测试文件」但文件里没有。
P3:接口 5 人预检在自托管上无效(适配器忽略 count/head,靠触发器兜底,触发器计数无锁);非 uuid 人物 id → 数据库错误 / 500 而不是 404;报告 POST 同时收 subjectId 与 chartProfileId 可能不一致;chart-library-panel.tsx 已无入口仍保留且 Python 合同测试还在断言它;/people 编辑时表单标题仍是「添加一个人」。
决策记录
- D1 worker 必须按报告行的
chart_profile_id经loadSubjectBirth/resolveSubjectBirth取出生资料(红线 3:唯一解析入口);校正候选范围只在本人时使用(沿用 consult 口径)。人物已删 → 任务失败并退点(沿用既有失败退点路径),不回落本人。 - D2 兼容层
parsePostgrestOr增加is算子(is.null/is.true/is.false,编译为is null/is true/is false,不得写成= null),与 BUG-990 的not … is同口径;必须有真实 Postgres 测试覆盖。不改路由语义。 - D3 当前人物在模块加载时同步从 localStorage 读出(按上次账户 id 键;账户未知时先读"最后账户"键),
bindCurrentSubjectAccount只做校验与纠正;新建对话深链、星盘 / 星历 / 报告首屏请求等 bind 完成再发。 - D4 订阅 emit 改为合并进
sessions:保留已 hydrated 的会话与消息;人物未变不 emit;Provider 不再setSessions([])清空,换人时用新人物的列表整体替换一次即可。 - D5 回填纠偏:新迁移把由 jsonb 回填而来的
birth_time_status ∈ {accepted, confirmed}降为reported、active_*置空(这些值从未经过校正流程),写明理由;不动户主profiles。 - D6 其余 P2 / P3 按上表逐条修;「和我合盘」不改当前人物(合盘对话绑定本人,问题里写对方名字);删除确认在用量未知时禁用删除并提示「暂时查不到这个人的对话数量,稍后再试」。
硬红线
- 失败名单与开工基线(
fabe0114)逐条一致、新增 0,必须在 Node 22 + Linux 跑(门禁环境);进度记录附对比命令与结果。推 staging 前必须跑全量(上一轮只跑定向,门禁红了 3 次)。 - P0-1、P0-2 各自至少一条真实路径测试:P0-1 用 worker 成功路径(注入 fake engine,断言引擎收到的是他人的出生资料、户主数据未出现);P0-2 用
npm run test:db真实 Postgres 断言subject=self返回存量null/self行、不返回他人行。 - 每条改动的既有断言写三栏,包括补齐
fabe0114缺的约 11 处。 - 不改已验收的产品口径(5 人、无关系、标题切换、历史按人);不改 workflow。
任务分解
- T1 worker 按人取资料 + 失败退点(P0-1)。
- T2 兼容层
is算子 + 真实 Postgres 测试(P0-2)。 - T3 当前人物同步读取与请求闸门(P1-1)。
- T4 会话列表合并而非替换、Provider 不清空(P1-2)。
- T5 回填纠偏迁移(P2-1);切换器在
/people变更后刷新(P2-2);删除计数失败禁用(P2-3);今日星语按当前人物判定与指纹(P2-4);合盘不切人(P2-5);星历暖进不重复拉取 + 首屏失败可重试(P2-6);补三栏(P2-7)。 - T6 P3 逐条;删除
chart-library-panel.tsx与只测它的断言。 - T7 记录:BUG 新号(P0-1 报告用错人、P0-2 本人历史 500 复发自 BUG-990),BUG-1030 状态改回 investigating 直至真机清单;PROGRESS;更新
docs/testing/people-archive-p1-20260924.md(加:给朋友生成报告后打开报告核对出生资料;本人侧栏历史可见;/people 点「和 TA 对话」新对话标题旁是 TA 的名字;在别人的对话里刷新页面后仍是 TA)。
开工前置命令
git fetch origin --prune
node -v # 必须 22.x;不是就先切到 22 再跑测试
git worktree add -b codex/people-archive-p1-fix-20260925 .worktrees/people-archive-p1-fix-20260925 origin/staging
git worktree add --detach .worktrees/p1fix-baseline origin/staging
cd .worktrees/p1fix-baseline/frontend && npm test > /tmp/p1fix-base.log 2>&1
cd ../../people-archive-p1-fix-20260925/frontend && ./node_modules/.bin/tsc --noEmit && npm run lint
npm run test:db # 需 Docker
BUG 编号
写单时最大 BUG-1030;本单从 BUG-1031 起,开工时核对。