diff --git a/docs/tasks/README.md b/docs/tasks/README.md index 3f1e557b..63a9562a 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -499,3 +499,5 @@ | `TASK-api-server-service-extract-20261013.md` | — | API 单体第一阶段:拆出不带 HTTP 的计算类,生产伪造 handler 归零 | 待领取(排队:dasha-unify) | `codex/api-server-service-extract-20261013` | | `TASK-data-layer-types-20261013.md` | `PROGRESS-data-layer-types-20261013.md` | 数据访问层改用自己的 DataClient 类型第一阶段(BUG-1351;任务书预留的 1343 已被更大编号越过) | 待验收 | `codex/data-layer-types-20261013` | | `TASK-contracts-helpers-dedupe-20261013.md` | — | 前后端契约与小工具去重第一阶段 | 待领取(排队:frontend-dead-code、consult-route-split) | `codex/contracts-helpers-dedupe-20261013` | +| `TASK-people-archive-limit-20-20261017.md` | — | 星盘档案上限 5→20 人(触发器新迁移 + 常量 + 文案;婚恋合盘前置单) | 待领取 | `codex/people-archive-limit-20-20261017`;动表,需 test:db | +| `TASK-marriage-match-p1-20261017.md` | — | 婚恋合盘第一期:独立页、任意两人、Raman 口径火星冲克 / Papasamya / 八项例外并列、按报告扣点(BUG-1370 起) | 排队(等上限单合入) | `codex/marriage-match-p1-20261017`;动表 | diff --git a/docs/tasks/TASK-marriage-match-p1-20261017.md b/docs/tasks/TASK-marriage-match-p1-20261017.md new file mode 100644 index 00000000..9745fd4a --- /dev/null +++ b/docs/tasks/TASK-marriage-match-p1-20261017.md @@ -0,0 +1,130 @@ +# 任务书:婚恋合盘第一期(2026-10-17) + +## 基线与顺序 + +- `origin/staging` = `f95eb670b`(开工前 `git fetch origin --prune`,以最新 `origin/staging` 为准) +- 分支:`codex/marriage-match-p1-20261017`;工作树:`.worktrees/marriage-match-p1-20261017` +- **前置**:`TASK-people-archive-limit-20-20261017.md` 必须先合入 staging。两单都改 `components/people/people-page.tsx`,本单在它之后从最新 `origin/staging` 开分支。 +- 原型(产品已确认 v3):https://claude.ai/artifact/Tq6vhXFMnGitDZhJdamaHe 。原型里的人名、分数、宫位全是虚构示例。 + +## 现状实证(按符号定位) + +| # | 事实 | 位置 | +| --- | --- | --- | +| 1 | 唯一入口是档案页他人行的「和我合盘」,跳 `/?synastry=`;只能「本人 × 他人」 | `components/people/people-page.tsx`(`和我合盘`)、`components/people-home-links.tsx` 的 `SynastryDeepLink`(挂在 `app/(app)/page.tsx`) | +| 2 | 结果没有界面:结果卡随 `chart-library-panel.tsx` 在 d08ffdc0a 删除;现在只往聊天输入框塞一句草稿问题。`use-synastry.ts` 返回的 `synastryReportCard` / `synastryHistory` / `setSynastryRelationshipType` 无人渲染;`globals.css` 里 `synastry-report-card` 样式成孤儿;`page.tsx` 开机仍拉合盘历史 | `hooks/use-synastry.ts`、`lib/home-profile.ts` 的 `buildSynastryQuestion`、`lib/home-cloud-sync.ts` | +| 3 | 不看性别:永远把本人当男方 | `app/api/synastry/route.ts`:`male_moon: moonLongitude(selfChart)`、`female_moon: moonLongitude(partnerChart)` | +| 4 | 网页路径从不算火星冲克:Python `/api/synastry` 只收两个月亮度数,`calculate_ashtakoot` 拿不到整盘,`kuja_*`、`LagnaHouse7` 恒缺 | `scripts/offline_compute_mixins.py` 的 `SynastryMixin._compute_synastry` | +| 5 | 火星冲克有三套互相矛盾的规则:`scripts/ashtakoot.py` 的 `calc_kuja_dosha`(只从上升数 2/4/7/8/12,五颗星按旺弱打 100/80/50 分,`match_kuja_dosha` 女方重判不合、男方重可容忍);`scripts/yogas_doshas.py` 的 `calc_mangal_dosha`(1/2/4/7/8/12);`references/relationship-astrology-guide.md` §5.3 又一套 | 同左 | +| 6 | 36 分的三处简化:性情只给 4/2/0;月宫距离没有主星例外;脉的化解只有「月宫距离有分 + 同绳无碍」一条,没有 pada 重算 | `scripts/ashtakoot.py` 的 `calc_yoni`、`calc_bhakoot`、`calculate_ashtakoot` 里的 exceptions 段 | +| 7 | `scripts/synastry.py` 是旧副本:`calc_ashtakoot` 把互控写死 2.0;星宿下标不取模(360° 越界);只被 CLI `synastry`、`tests/run_all.py`、`tests/test_open_source_integrations.py` 用 | `scripts/synastry.py`;退役建议见 `docs/research/antigravity_round23_synastry_module_retirement_decision_2026_06_25.md` | +| 8 | 文档虚报:`references/quick-reference-guide.md`、`references/technique-capability-matrix.md`、`scripts/jyotish_engine.py` 头注释称已覆盖 Papasamya / Mangal 匹配,代码没有 | 同左 | +| 9 | 合盘不扣点,只有每小时 8 次限流(`request-rate-limit.ts` 的 `synastry` 桶);无缓存 | `app/api/synastry/route.ts` | + +相关历史(开工时按 §5 检索并读完):BUG-466(合盘历史只认云端)、BUG-1030(「设为默认」让合盘把别人叫成「我」)、BUG-1333(两张盘并行算 + 跳过外部概览)、BUG-736、BUG-189。 + +## 根因 + +合盘一直是挂在别的面板上的附属功能:计算只拿月亮度数,界面跟着旧面板一起删了,规则从几个来源拼来、互相打架,没有一处定过口径。 + +## 决策记录(产品负责人 2026-10-17) + +| # | 决定 | +| --- | --- | +| D1 | 合盘做成独立功能:侧栏一项「**婚恋合盘**」,独立页面,页面标题「婚恋合盘」。档案里**任意两个人**都能合(包括两个都不是本人) | +| D2 | 第一版**只做恋爱 / 婚姻**,不给选关系类型;旧的 `relationshipType`(business / family / general)分支删除 | +| D3 | **一次合盘按一次报告扣点**(`report.full` 的定价)。同一对人、两人出生资料都没改、规则版本没变时,再次打开直接读已保存的结果,不扣点 | +| D4 | 八项**逐项展示**,不做大号总分;历史列表不挂分数 | +| D5 | **不考虑同性**:男方女方按档案性别定;两人性别相同或有一人没填时,按选择顺序第一个人当男方,不提示、不互换平均(推翻同日早先「互换取平均」的说法) | +| D6 | 副标题定稿:「看这段感情往后的走向,和两人之间的助力与考验。」页面任何地方不写「合不合」「会不会在一起」 | +| D7 | 新增星盘入口放在两个选人下拉的**最底一行**「+ 新增星盘」,本页弹出与星盘档案同一张新建表,保存后自动选进这一格;档案满时这一行变灰 | +| D8 | 「开始合盘」按钮居中,点数说明在按钮正下方 | +| D9 | **规则口径定为 `raman_muhurtha_v1`**(占星师 2026-10-17 答复,产品采纳),细则见下节。**推翻**现有 `calc_kuja_dosha` 的五星合分、旺弱打分表与 `match_kuja_dosha` 的性别不对称阈值——这些不得出现在新功能里,也不作为并列口径展示(产品 10-17 同意) | +| D10 | **规则有分歧时并列展示**:默认口径在前,另一派写明「按那一派结果会变成什么」,不悄悄选一个 | +| D11 | 第二期(本单不做、不留占位):两人大运同步「什么时候顺」、就这对关系开对话、导出报告、非婚恋关系 | + +### 规则口径 `raman_muhurtha_v1` + +出处:B. V. Raman《Muhurtha or Electional Astrology》(1969)Kuja Dosha 节(原书 pp. 91–92)、Rasi Kuta 例外(p. 81)、Nadi Kuta 与 pada 重算(pp. 86–87)、Special Considerations(pp. 87–88);Hart de Fouw & Svoboda《Light on Life》Kuja Dosha 节(本垣 / 入旺的修饰与 30 岁之说)。书在上游仓库 `yinduzhanxing/references/book_corpus/`,本仓不收原书。下面摘的是实现所需要点。 + +**火星冲克(Kuja Dosha)**——只看火星 + +| 项 | 默认(Raman) | 并列的另一派 | +| --- | --- | --- | +| 参照点 | 从上升、月亮、金星三处分别数;分量 上升 < 月亮 < 金星(只作排序,**不换算成数值权重**) | — | +| 犯宫 | 2、4、7、8、12 | 北 / 西印度:另加 1 宫(profile `north_west_6_house`,整列并列) | +| 完全取消该参照点 | 火星在狮子或水瓶;宫 × 星座例外表:2 宫双子/处女、4 宫白羊/天蝎、7 宫摩羯/巨蟹、8 宫射手/双鱼、12 宫金牛/天秤;火星与木星同宫;火星与月亮同宫 | — | +| 减轻(不取消) | 火星本垣(白羊、天蝎)或入旺(摩羯);木星相位火星(标「需复核」);30 岁后(一派说法) | 本垣 / 入旺**完全取消**(分歧说明里写出按这派结果变成什么) | +| 单人归档 | 无:三处都未触发或已取消;轻:只有「减轻」的触发,或只有上升一处完全触发;明显:月亮或金星有一处完全触发;重:两处及以上完全触发 | 同一归档规则用于另一列 | +| 两人比较 | 相当:等级相同,或相差一级且两人都不是「无」(Raman:两人都有则相抵);一方偏重:其余;定不了:缺资料。**不分男女** | 同左 | +| 与整体结论 | 不改 36 分。一方「重」且另一方「无」或「轻」时,结果页顶部显示「整体:有条件,需复核」 | 任一口径满足即显示,并写明是哪一派 | + +上表「单人归档」「两人比较」的分级是产品为了不出伪精度分数而定的归档规则,不是原书原文;结果页脚注写明。 + +**婚姻宫压力(Papasamya)**:火星、土星、太阳、罗睺、计都落在从上升、月亮、金星数的 1、2、4、7、8、12 宫,逐处列出、数处数,两人比较「相当 / 一方明显更重」(差 ≥ 3 处或一方是另一方两倍以上算明显更重)。**单独一节,不叫火星冲克,不打分。** 宫位集合采用 Papasamya 常见口径,若开工检索到 Raman 原书另有规定,以原书为准并写进进度记录。 + +**八项配对(36 分)** + +| 项 | 改法 | +| --- | --- | +| 性情 Yoni | 补齐五档:同类 4、友 3、中 2、敌 1、仇 0。用版本化的 14 × 14 完整矩阵(以 PyJHora `jhora/horoscope/match/compatibility.py` 的 `YoniArray` 为对照写入,注明出处),不得按动物名临时推。北 / 南印度对蛇—鼠是否相仇不同:命中这一对时两种结果并列 | +| 月宫距离 Bhakoot | 原始 0 / 7 分照算照显示。两人月宫同一主星、或两颗主星互为朋友时,另给 `bhakoot_exception = cancelled_by_same_or_friendly_rashi_lords`;**不得把原始分偷偷改成 7** | +| 脉 Nadi | 顺序:① 按星宿算原始脉;② 同脉时按星宿的脚(pada)重算——108 个 pada 从 Aswini 1 起每三个一行、正反交替(行号 `r = p // 3`,偶数行列号 `p % 3`、奇数行 `2 - p % 3`,列依次为 Vata / Pitta / Sleshma;原书表头几行:Aswini 1/2/3;Aswini 4 在 Sleshma 列、Bharani 1 在 Pitta 列、Bharani 2 在 Vata 列);pada 重算不同脉则取消;③ 仍同脉时查 Raman 例外:(a) 月宫距离与同绳(Rajju)都成立 (b) 两人月宫同一主星 (c) 两颗月宫主星互为朋友,任一成立则取消。逐步写进结果 | +| 合计 | 表尾并列两个数:原始合计;计入例外后的合计 | + +`ashtakoot.py` 里的现有附加项(Mahendra、Stree Deergha、Vedha、Rajju、BadConstellations)保留,Stree Deergha / Rajju 的例外改用 Raman Special Considerations 的写法。 + +## 硬红线 + +1. `scripts/jyotish_api_server.py` 不增长(类方法数、`__new__` 伪造点、行数护栏):新计算放新模块(建议 `scripts/marriage_match.py`),主文件只做薄注册。 +2. `app/(app)/page.tsx` 的 `Home()` 状态数不得增长;本单删掉 `SynastryDeepLink` 与开机拉合盘历史,只会减少。 +3. 新功能里不得出现:五星合分、100/80/50 之类的火星冲克分数、按性别判轻重、「合 / 不合」「会不会在一起」「适合结婚」。 +4. 生产运行时不得 import PyJHora(只作测试对照,许可证边界);验收会屏蔽 PyJHora 模拟生产。 +5. 合同测试的 fixture 用真实引擎输出(golden)。所有示例与测试只用虚构人物或公开名人。 +6. 扣点:先预留、算失败或超时**释放不扣**;读已保存结果不预留。用现有 `authorizeUsage` / `completeUsage` 及其释放路径,记账 metadata 标明 `kind: "marriage_match"`。 +7. 一次等待一次揭幕:等待只有一屏,结果整页一起出现;揭幕后无 spinner / 骨架。 +8. 加表 / 加列可以同轮,不得删旧列(AGENTS §7.6);动表必须真跑 `npm run test:db`。 +9. 删人:删档案里的人时,这个人参与的合盘一并删除;删前确认句在「N 个对话和 M 份报告」后加上「K 份合盘」。 + +## 任务分解 + +| # | 内容 | 验收标准 | +| --- | --- | --- | +| T1 | **引擎**:新模块实现 `raman_muhurtha_v1`——Kuja(三参照点 × 两列口径 × 取消 / 减轻原因)、Papasamya、八项(Yoni 五档、Bhakoot 例外、Nadi pada 重算 + 例外)、单人婚姻征象(复用 `relationship_analysis.analyze_relationship`:7 宫主、金星、D9、UL、DK)、D9 对照(两人 D9 上升、D9 金星、D9 上升相隔几宫)。输入两份出生资料 + 性别,一次请求内算完(占重计算并发位) | 新 pytest:Raman 宫 × 星座例外表 10 格逐格、狮子 / 水瓶、木星同宫、月亮同宫、本垣 / 入旺两列分歧、1 宫只在另一列触发、归档四级与两人比较各至少一例;Yoni 14 × 14 与 PyJHora 对照全等(测试里可 import PyJHora,缺时 skip 并记录);pada 表头 6 个 pada 的列号;Bhakoot 例外不改原始分;至少 20 对虚构出生资料的八项原始分与 PyJHora North 方法对照,差异逐条解释 | +| T2 | **Python 接口**:新端点(如 `POST /api/marriage_match`)薄注册;旧 `/api/synastry` 保留给 MCP / CLI 兼容但不再被网页调用 | `tests/test_api_server_growth_contract.py` 绿;端点 golden 一份进 `frontend/tests/fixtures/` | +| T3 | **退役与文档纠错**:删 `scripts/synastry.py`,CLI `synastry` 改走新模块或删除(同步 `SKILL.md` 命令表、`tests/run_all.py`、`tests/test_open_source_integrations.py`);`ashtakoot.py` 的 `calc_kuja_dosha` / `match_kuja_dosha` 标为旧版、不被新功能调用;删掉三处 Papasamya / Mangal 匹配虚报 | 全量 pytest 与基线逐条比对无新增失败;`git grep "synastry.calc_ashtakoot"` 0 命中 | +| T4 | **数据**:`synastry_reports` 加列——两人的人物引用(本人用约定值,他人用 `chart_profiles.id`)、两人出生资料指纹、规则版本 `rules_version`、结构化结果;旧行不迁移、不显示 | `npm run test:db`:同一对人同指纹第二次读到旧行;任一人改出生资料后指纹不同;删人后其合盘行消失 | +| T5 | **Next 接口**:重写 `app/api/synastry/route.ts`(或新路由,旧的删):收两个人物 id → `loadSubjectBirth` 两次 → 查已保存(命中即返回,不扣点)→ 预留 `report.full` → 调 T2 → 存 → 结算;性别按 D5;保留产品开关 `compatibility` 与限流桶;删掉 `relationshipType` 与 business / family / general 分支 | 路由测试覆盖:命中缓存不扣点;计算失败释放不扣;两个 id 相同 400;不属于本人的 id 拒绝;同性 / 缺性别按顺序定男方 | +| T6 | **页面**:侧栏「婚恋合盘」;`/marriage-match`(路由名可议,写进 DESIGN)选人页(两个自定义下拉,底行「+ 新增星盘」复用星盘档案新建表;居中按钮;点数来自定价不写死;之前的合盘列表只列「人名 × 人名 · 日期」,点开不扣点);结果页按原型 v3:三句话总述 → 各自的婚姻征象 → 八项配对(含例外绿字 / 另一派灰字、两个合计)→ 火星冲克(每人一张两列表 + 两人对比 + 分歧说明)→ 婚姻宫压力 → 脚注口径来源 | 渲染测试:两个状态(口径不一 / 失衡封顶)的 golden 渲染;20 人时下拉可滚动;手机 390 px 无横向滚动;`next build` 后 `/` 仍 Static,首屏 gzip ±2% | +| T7 | **三句话总述**:由服务端按结构化结果确定性拼句(不调模型),讲各自征象、相处的助力与考验、八项合计只作一层参考;有「有条件,需复核」时第三句讲清原因 | 单测覆盖两种状态;文案进 VOICE | +| T8 | **清旧入口**:档案页「和我合盘」改跳新页并预选两人;删 `SynastryDeepLink`、`use-synastry.ts` 中无人用的部分、`buildSynastryQuestion` 草稿路径、孤儿 CSS、`page.tsx` 开机拉合盘历史 | 按 AGENTS §7.8 grep 源码文本合同测试同轮改,三栏说明 | +| T9 | **记录**:DESIGN.md、VOICE.md(新文案、删人确认句、侧栏名)、CHANGELOG、BUG_HISTORY(下节编号)、`docs/testing/marriage-match-p1-20261017.md` 真人清单(选人、新增星盘、扣点与再次打开不扣、删人连带删除、手机宽) | 文档齐 | + +## 让步顺序 + +先保 T1–T5(算对、存对、扣对);T6 不可省;T7 若来不及可先用固定模板句但不得出现红线 3 的词;T3 的 CLI 改写可降为「删 `synastry.py` + CLI 命令删除」。任何一项过不了红线就停下写进 PROGRESS,不得放宽口径。 + +## 开工前置命令 + +```bash +git fetch origin --prune +git worktree add -b codex/marriage-match-p1-20261017 .worktrees/marriage-match-p1-20261017 origin/staging +cd .worktrees/marriage-match-p1-20261017 +git log --oneline -5 -- frontend/src/lib/subject-birth.ts # 确认上限 20 人那单已在 +grep -n "BUG-1[0-9][0-9][0-9]" docs/BUG_HISTORY.md | tail -3 +python3 scripts/pre_work_check.py --remote-timeout 8 --command-timeout 45 +``` + +验证:全量 `pytest tests -n 4` 与开工基线逐条比对;`run_quality_gate.py --profile quick`;前端 `tsc --noEmit`、`npm run lint`、`npm test`(含 grep 源码文本合同测试)、`npm run test:db`、`npm run build`。 + +## BUG 编号 + +从 **BUG-1370** 起(在途的 #77 已用 BUG-1367,它的修复单可能再占几个号;开工时核对最大号,冲突则整体顺延): + +| 建议编号 | 事实 | +| --- | --- | +| BUG-1370 | 合盘不看性别,本人恒为男方(实证 3) | +| BUG-1371 | 网页合盘从不计算火星冲克(实证 4) | +| BUG-1372 | 旧 `synastry.py` 互控写死 2.0 / 星宿下标越界(实证 7) | +| BUG-1373 | 结果卡随旧面板删除,合盘只剩聊天草稿(实证 2) | +| BUG-1374 | 文档虚报 Papasamya / Mangal 匹配已实现(实证 8) | diff --git a/docs/tasks/TASK-people-archive-limit-20-20261017.md b/docs/tasks/TASK-people-archive-limit-20-20261017.md new file mode 100644 index 00000000..dc370403 --- /dev/null +++ b/docs/tasks/TASK-people-archive-limit-20-20261017.md @@ -0,0 +1,62 @@ +# 任务书:星盘档案上限 5 人 → 20 人(2026-10-17) + +## 基线 + +- `origin/staging` = `f95eb670b`(开工前 `git fetch origin --prune`,以最新 `origin/staging` 为准) +- 分支:`codex/people-archive-limit-20-20261017`;工作树:`.worktrees/people-archive-limit-20-20261017` +- 本单是**婚恋合盘第一期**(`TASK-marriage-match-p1-20261017.md`)的前置单,两单都改 `components/people/people-page.tsx`,必须串行:本单先合入 staging,合盘单再开工。 + +## 现状实证(按符号定位) + +上限「本人 + 4 个他人 = 5」写在下面这些地方,缺一处就会出现「界面让加、服务端拒绝」或反过来: + +| 层 | 位置 | 现值 | +| --- | --- | --- | +| 数据库触发器 | `public.chart_profiles_guard_limit()`,最后一次定义在 `frontend/supabase/migrations/20260925030000_chart_subject_trust_and_limit.sql`(开工时 `grep -l chart_profiles_guard_limit frontend/supabase/migrations` 确认没有更晚的重定义) | `if others >= 4 then raise exception 'subject_limit_reached'` | +| 常量 | `frontend/src/lib/subject-birth.ts`:`MAX_OTHER_SUBJECTS`、`MAX_SAVED_SUBJECTS` | 4、5 | +| 接口文案 | `frontend/src/app/api/chart-profiles/route.ts`:`SUBJECT_LIMIT_MESSAGE` | 「最多保存 5 个人的星盘」写死 | +| 页面文案 | `frontend/src/components/people/people-page.tsx`:`LIMIT_COPY` | 同上写死 | +| 文案规范 | `frontend/docs/VOICE.md` 星盘档案两段(「还可以保存 N 个人」「最多保存 5 个人的星盘」) | 5 | +| 自动测试 | `frontend/tests/people-archive-view.test.tsx`(限额句断言)、`frontend/tests/database-chart-subject.test.ts`(第五个人被拒) | 5 | +| 真人清单 | `docs/testing/people-archive-p1-20260924.md`、`docs/testing/people-ephemeris-ui-20260926.md` | 5 | + +## 决策记录 + +- 产品负责人 2026-10-17:档案上限从 5 人改为 **20 人(含本人,即他人最多 19)**。 +- 扣点不变:建档不扣点,各功能照常扣(与 2026-09-24 定稿一致);`profile.extra` 计费键**不启用**。 +- 数字只在一处定义:文案用常量拼出,不再写死「5」或「20」。 + +## 硬红线 + +1. 新增一个迁移文件(`frontend/supabase/migrations/20261017xxxxxx_chart_profiles_limit_20.sql`)重定义 `chart_profiles_guard_limit()`,阈值改为 `others >= 19`;**不改**旧迁移文件,不动其它表、不动 advisory lock 与 `role` 检查逻辑。 +2. 迁移与代码同一提交;生产部署前先跑迁移(写进 PROGRESS 与 CHANGELOG)。 +3. 不改人物表结构、不加列。 +4. 测试数不得减少;改断言写「原值 / 新值 / 原因」三栏(AGENTS §7.3)。 + +## 任务分解 + +| # | 内容 | 验收标准 | +| --- | --- | --- | +| T1 | 新迁移:触发器阈值 19 | `npm run test:db` 中 `database-chart-subject.test.ts` 改为「第 20 个他人(共 21 人)被拒、第 19 个他人可存」,两条都绿 | +| T2 | 常量 `MAX_OTHER_SUBJECTS = 19`、`MAX_SAVED_SUBJECTS = 20`;接口与页面限额句改为由常量拼出「最多保存 {MAX_SAVED_SUBJECTS} 个人的星盘」 | `git grep -n "5 个人"` 在 `frontend/src` 0 命中;`people-archive-view.test.tsx` 断言新文案 | +| T3 | 人多时的列表:档案页列表、标题旁人名切换(`subject-switcher.tsx` / `current-subject-switcher.tsx`)、聊天的 `chat-profile-picker.tsx` 在 20 人时可滚动、不撑出屏幕 | 新增渲染测试:20 人时列表容器有限高 + 纵向滚动;手机宽 390 px 不出现横向滚动(写进真人清单) | +| T4 | VOICE、DESIGN(若 T3 动了样式)、两份真人清单里的 5 改为 20;CHANGELOG 一条 | 文档 grep 无残留「5 个人」 | + +## 让步顺序 + +T1+T2 必须一起交;T3 若列表已能滚动,只补测试即可;T4 不可省。 + +## 开工前置命令 + +```bash +git fetch origin --prune +git worktree add -b codex/people-archive-limit-20-20261017 .worktrees/people-archive-limit-20-20261017 origin/staging +cd .worktrees/people-archive-limit-20-20261017 +grep -l chart_profiles_guard_limit frontend/supabase/migrations/*.sql +``` + +验证:`./node_modules/.bin/tsc --noEmit`、`npm run lint`(0 error)、`npm test`(失败名单与基线逐条一致)、`npm run test:db`(Windows 已知失败名单之外无新增)、`npm run build` 后 `/` 仍 Static。 + +## BUG 编号 + +本单是产品需求,不新增 BUG 编号。