feat: keep up to five chart subjects on typed columns

Chart, ephemeris, reports, daily language, consult, and synastry read birth data only through resolveSubjectBirth. The current person is a request parameter outside Home(). /people replaces the settings chart pane, and deleting someone removes that person's chats and reports.
This commit is contained in:
jesse-ux
2026-09-25 04:19:05 +08:00
parent c285646638
commit ab6c55f822
70 changed files with 3195 additions and 619 deletions
+16
View File
@@ -13762,3 +13762,19 @@
- 复发自:无
- 修复版本:本分支,待推送
- 断言三栏:原值是扫描面不含引导屏组件、只要求其中出现 `<BirthTimeRectification`;新值是扫描面包含 `home-onboarding-shell.tsx`,并额外要求 `page.tsx` 挂载 `<HomeOnboardingShell`;原因是 JSX 搬家,行为没变,合同文件清单没跟上。
## BUG-1030 | 「设为默认」把页面资料换成别人,合盘问题和校正标签因此错人
- 状态:resolved(定向回归已锁;浏览器与部署未验收,不把本分支写成已上线)
- 首次发现:2026-09-24
- 最近更新:2026-09-25
- 影响面:首页 `profile`、合盘问题里的「我」、生时校正会话标签、设置里的「当前默认」
- 用户现象:把别人设为当前星盘后,合盘问题把别人写成「我」;校正会话标签显示别人的名字,计算仍用户主。
- 触发条件:`activeChartId` 指向另一份星盘,或点「设为默认」。服务端并不读这个本地键。
- 根因:BUG-466 留下每日星语缓存和 `activeChartId` 不动。BUG-997 / BUG-1000 只把普通对话接到服务端人物,并明确报告、每日星语、生时校正另立任务。页面级 `profile` 仍会被别人的资料覆盖,而星盘、报告和校正继续读 `profiles`。
- 修复:删除 `activeChartId`、`makeDefaultChart` 和替换页面 `profile` 的 effect。当前人物只作为请求参数。星盘、星历、报告、今日星语和普通对话都经 `resolveSubjectBirth` 取服务端资料;失败不回落本人、不扣点。生时校正在当前人物不是本人时禁用。户主仍在 `profiles`。
- 验证:`frontend/src` 不再出现这三个符号。`tests/subject-birth.test.ts` 锁住他人资料与 fail-closed。`tests/database-chart-subject.test.ts` 锁住回填、上限、不可改的填报时间和连带删除。没有登录浏览器,未声称在浏览器里加过人物。
- 防复发:任何客户端代码不得再用他人资料覆盖页面级 `profile`。出生资料只从 `resolveSubjectBirth` 读取。不得恢复「设为默认」或把 `activeChartId` 写回 localStorage。
- 相关记录:BUG-466、BUG-997、BUG-1000
- 复发自:无。BUG-1000 当时规定新对话固定本人,本单按产品决定推翻那一条,不把页面资料再换成人。
- 修复版本:本分支,未推 staging,未部署。
@@ -0,0 +1,34 @@
# PROGRESS · 星盘档案 P1(2026-09-25)
基线 `origin/staging` `4b579af6`。分支 `codex/people-archive-p1-20260924`。没有推 staging。
## 完成
- T1:`chart_profiles` 增加类型化列,从 jsonb 回填,不猜缺字段。删除 `role='self'` 镜像。他人最多 4 个。`reported_birth_time` 写入后不可改。客户端不能写 `active_*`,也不能把状态写成 accepted/confirmed。`delete_chart_subject` 连带删除该人的对话和报告。
- T2:星盘、星历、报告、今日星语、合盘都经 `resolveSubjectBirth`。普通对话的解析函数改为调用它,他人资料读类型化列。列表按 `subject` 过滤;本人存量会话在 `subject=self` 下仍可见(会话列为空或 `self`;报告列是 uuid,本人是 `chart_profile_id is null`)。
- T3:当前人物在 `current-subject.ts`,不进 `Home()` 状态。`SubjectSwitcher` 放在对话和各次级页标题旁。已删除 `activeChartId` / `makeDefaultChart`。新对话读当前人物。切人时星盘、星历、报告在绘制前清空上一份结果。
- T4:`/people` 为侧栏页。设置弹窗不再有星盘资料。`?settings=chart` 去 `/people`。「和 TA 对话」打开空对话,「和我合盘」用本人资料起草问题。
- T5:BUG-1030、CONTEXT、DESIGN、VOICE、CHANGELOG、本文件和真人清单。
## 让步
- 迁移先跑、代码后上的窗口里,旧客户端写本人镜像会失败,账户保存仍成功(旧代码吞掉镜像错误)。旧客户端若只改他人 jsonb,已回填的类型化列不跟着改,避免把正在跑的旧写入打成约束错误。窗口结束后以类型化列为准。
- 生时校正和任意两人合盘按 D12 留 P2。
- 今日星语请求放在 `DailyStarlanguageBinder`,随当前人物重取,没有把 store 放进 `Home()` 的 useState。卡片在 effect 跑起来之前可能仍显示上一张,不会写进错误的缓存键。
## 验证
- `tsc --noEmit` 通过(exit 0)。
- `npm run lint`:0 error,127 warning。warning 是既有未使用符号和 effect 依赖,本单没有为消 warning 改业务代码。
- `frontend/src` 中 `activeChartId`、`activeChartStorageKey`、`makeDefaultChart` 为 0。
- `Home()` useState 33(开工实测 34,上限 36),useRef 37(未增加,上限 37)。`page.tsx` 1348 行(基线 1417,上限 1567)。
- 路由:`tests/subject-route-birth.test.ts` 与 `tests/ephemeris-route.test.ts`。他人 subject 时星盘和星历引擎收到的是该人的年、纬度;不存在的人不回落本人,星盘 `billed: false`,报告 POST 409 且 `authorizeUsage` 未调用。会话列表 `subject=self` 含 `chart_profile_id` 为空和 `self` 的存量行,他人列表只有该人。报告列表同样按人分开。
- `tests/database-chart-subject.test.ts` 与同批 `subject-birth`、`chat-profile-picker`:17 pass,0 fail。覆盖回填、第 5 个他人被拒、`reported_birth_time` 不可改、`active_*` 与 accepted/confirmed 被拒、删除连带会话和报告、跨用户拒绝。
- 全量 `npm run test:db` 在 Windows 上会先撞上 `frontend/db/migrations` 与 `supabase/migrations` 同名文件(symlink 在本机是普通副本)的 duplicate migration filename。这不是本单引入的。本单用例走与校正标题测试相同的去重目录,定向结果以上面的 17/0 为准。
- 没有登录浏览器。没有跑 `next build`,不宣称 `/` 与 `/people` 已在本机确认为 Static。
- 预览会话 `home-bootstrap-run.ts` 的 `updatedAt: timestamp()` 断言在本基线上已失败,本单未改那个对象。
- 开工后 `origin/staging` 又前进了 `81db05c6`、`3dd48c0d`、`c2856466`(首页拆分的测试合同,含 BUG-1029)。功能分支在推送前快进到这些提交之上,BUG-1030 仍用原编号。
## 断言三栏
见各测试文件里的「原值 / 新值 / 原因」注释。主要是:新对话不再固定本人;设置不再有星盘资料;星盘页改为 `loadSubjectBirth`;今日星语缓存键加入 subjectId。
+1 -1
View File
@@ -119,7 +119,7 @@
| --- | --- | --- | --- | --- |
| `TASK-home-page-split-20260925.md` | `PROGRESS-home-page-split-20260925.md` | **首页 page.tsx 拆分(只搬不改)**:启动流程约 285 行抽成 lib 纯函数 `runHomeBootstrap`、恢复轮询约 100 行抽出、错误 / 引导屏抽组件,目标 ≤1300 行并下调增长合同基线;两个按源码切片的测试改为直接调用函数;还原 824647f2 的 import 挤行。排在 chart-ephemeris 之后、people-archive P1 之前 | 待验收 | `da35b3e4` |
| `TASK-staging-gate-fix-20260924.md` | — | **修复单(阻塞 staging 部署)**:慢网首页 `0e4dd0f4` 起 5 条源码合同测试未跟上(硬跳转计数、重试 reload 正则、两处按已删语句切片)→ 门禁红,`0e4dd0f4..f125fae0` 全部未部署;另 page.tsx +9 行、订阅支付不刷新余额、原始附录三文件残留。只修测试与收尾,不改已验收行为;失败名单须与 `edc9c22c` 逐条一致 | 已验收(Claude 09-25:5 条转绿、失败名单与 edc9c22c 逐条一致 56/56、门禁 run 2877 绿;import 挤行凑数为 P3,由 home-page-split D5 还原;原始附录三文件仍未删) | `651c75e7`/`7a001456`/`824647f2`;deploy run 2879 因 ACR 网络失败,待同 SHA 重跑 |
| `TASK-people-archive-p1-20260924.md` | — | **星盘档案(多人物)P1**:每个档案 = 一个人(含本人),最多 5 人、无关系字段;标题旁人名 = 全局切换器(对话 / 星盘 / 星历 / 报告 / 档案页同一组件),星盘、星历、报告、今日星语、新建对话都跟当前人物走(推翻 BUG-1000 新对话固定本人);历史只显示当前人物;档案管理改侧栏 `/people` 页、设置删星盘资料;删人连带删对话与报告;取消「设为默认」/`activeChartId`(BUG-1030 错人)。他人资料改类型化列 + 与户主同级守卫,统一 `resolveSubjectBirth`;户主暂留 `profiles`。生时校正跟人与任意两人合盘留 P2。**同日四单全部合入后开工**,需 Docker 跑 test:db | 待领取 | — |
| `TASK-people-archive-p1-20260924.md` | `PROGRESS-people-archive-p1-20260924.md` | **星盘档案(多人物)P1**:每个档案 = 一个人(含本人),最多 5 人、无关系字段;标题旁人名 = 全局切换器(对话 / 星盘 / 星历 / 报告 / 档案页同一组件),星盘、星历、报告、今日星语、新建对话都跟当前人物走(推翻 BUG-1000 新对话固定本人);历史只显示当前人物;档案管理改侧栏 `/people` 页、设置删星盘资料;删人连带删对话与报告;取消「设为默认」/`activeChartId`(BUG-1030 错人)。他人资料改类型化列 + 与户主同级守卫,统一 `resolveSubjectBirth`;户主暂留 `profiles`。生时校正跟人与任意两人合盘留 P2。**同日四单全部合入后开工**,需 Docker 跑 test:db | 待验收 | 实现提交见分支 `codex/people-archive-p1-20260924` |
| `TASK-home-slow-network-20260924.md` | — | **慢网首页半揭幕 + 账户与点数打开慢(BUG-1021/1022)**:8 秒硬超时包住多跳串行请求、超时不补拉,独立的会话列表 provider 晚到 → 正常首页+报错条、模型「暂不可用」、新建对话灰、重问称呼。产品 09-24 拍板:推翻 8 秒硬超时,等齐再揭幕,20 秒才出错误屏且重试为局部重请求(不整页刷新);账单面板删无用 `/api/account`、预取 chunk、套餐缓存。先于 chart-ephemeris T3 合入 | 已验收(经修复单,Claude 09-25);待部署核对 | `0e4dd0f4`…`824647f2` |
| `TASK-secondary-new-chat-intent-20260923.md` | `PROGRESS-secondary-new-chat-intent-20260923.md` | **次级页「新建对话」落到最近一次对话**:`/chart` `/ephemeris` `/reports` 的侧栏没有 `controls`,「新建对话」只是 `href="/"` 的回首页链接;首页无参启动的落点是 `nextSessions[0]`(最近更新那条),只有最近一条是校正会话才改落空咨询。BUG-745 修过同一入口的「慢」,没修「回到哪」。产品 09-23 拍板:链接改 `/?new=1`,首页看到 `new` 就本地建空咨询并 `replaceState` 抹掉参数;`new` 胜过 `c` 与登录返回存根;沿用 BUG-989 不落库不写 `?c=`;页脚与品牌行的 `/` 不改;刷新无参 `/` 仍落最近对话属既有设计。红线:`/` 保持 Static、不得用 `useSearchParams`;解析与生成只在 `chat-session-url.ts` 一处。BUG-1015;执行中追加 D7:保留旧任务恢复但不抢新建落点。 | 已验收(Claude 09-24:tsc/lint/95 定向/全量失败名单同基线/Static/gzip 同基线;BUG-1015 resolved) | `8902e484`,含于已部署 `1420471a`;真机清单欠 |
| `TASK-chat-message-authority-20260901.md` | — | 消息服务端权威化 | 已验收 | `b6989c3e`(BUG-464) |
@@ -0,0 +1,11 @@
# 星盘档案 P1 真人核对(2026-09-25)
没有登录浏览器。下面每一步都还没在浏览器里做过。用虚构人物,不要填真实姓名、生日或坐标。
1. 登录后打开「星盘档案」,添加四个虚构人物。第五个添加按钮应禁用,并看到「最多保存 5 个人的星盘」。本人不能删除。
2. 在对话、星盘、星历、我的报告之间切换当前人物。四个页面和侧栏历史应一起换成这个人,不要先闪出上一个人的盘。
3. 在已经发过消息的对话里换成另一个人。应切换当前人物,并打开一段空对话。空对话里切换应直接改绑,不新开对话。
4. 删除一个有对话和报告的虚构人物。确认句应是「将同时删除 N 个对话和 M 份报告,无法恢复。」确认后对话和报告一起消失。若删的是当前人物,回到本人。
5. 把当前人物换成别人。生时校正入口应禁用,文案是「生时校正暂时只支持本人」。
6. 用一个升级前就有他人星盘的账号打开档案。原有称呼和出生资料还在;缺字段的行应提示补全,不要被猜成一个时间。
7. 档案详情「和 TA 对话」应打开一段空对话。「和我合盘」生成的问题里,「我」是本人,对方是选中的那个人。