docs(tasks): lagna precheck + rectification opener limits brief (agent loop, issue #112)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LutgzyJqNhPpZy1nFsJtN3
This commit is contained in:
Jesse_Chen
2026-10-27 00:59:36 +08:00
co-authored by Claude Opus 5.5
parent 6df8d1578a
commit cef7b317ae
2 changed files with 141 additions and 0 deletions
+1
View File
@@ -536,3 +536,4 @@
| `TASK-full-data-parity-1-fix-20261022.md` | `PROGRESS-full-data-parity-1-fix-20261022.md` | **#91 A 段第 1 次打回**:新六章 69 节里 16 节在三张虚构盘上整节「未判定」,其中 12 节数据在资料包里、是读错了字段;测试抓不到整节未判定;新六章绕过了报告口径替换;中文附录悄悄丢列和瑜伽条目要量出来 | 已合入 staging | `codex/full-data-parity-1-20261021` |
| `TASK-full-data-parity-1-b-fix-20261023.md` | `PROGRESS-full-data-parity-1-b-20261023.md` | **#91 B 段修复**:K1–K3 已交;K4 年运键序与 Yogini 子运字段已补(BUG-1425、BUG-1426) | 已合入 staging | `codex/full-data-parity-1-20261021` |
| — | `PROGRESS-full-data-parity-1-fix2-20261022.md` | **#91 A 段第 2 次打回**(Claude 口头修复单 F1~F4):11 节只印「未判定」和键名改读真实记录;测试只认非第一列真实值、禁止键名格;中文瑜伽组合句由中文源给出(BUG-1418);六章降为 `##`、节 `###`(BUG-1419) | 本地完成,待验收 | `codex/full-data-parity-1-fix2-20261022` |
| `TASK-lagna-precheck-20261027.md` | — | **#112 选 A**:星盘页页头告诉用户上升在其时间精度内会不会换(复用现成的 D1 窗口扫描,纯计算);校正开场说清能做 / 不能做(BUG-1444、BUG-1445) | 待执行 | `codex/lagna-precheck-20261027` |
+140
View File
@@ -0,0 +1,140 @@
# 任务书:上升交界预检 + 校正开场说清限度(issue #112,2026-10-27)
- 执行方:Grok。分支 `codex/lagna-precheck-20261027`,worktree `.worktrees/lagna-precheck-20261027`。
- 来源:`docs/research/competitor_past_event_check_2026_10_27.md`(竞品「推断前事」调研);产品负责人 10-27 在 #112 选 **A**。
## 基线
- `origin/staging` = `6df8d1578`(开工时以实测为准)。
- 同期在途:#111(组件库与新手引导修复单,分支 `codex/ui-kit-onboarding-fix-20261026`)改 `ShellFrame`、`globals.css` 等。本单只碰星盘页页头一行与校正开场文案,**不改 `ShellFrame` / `globals.css` 的既有规则**;交回前 `git merge origin/staging`,若 #111 已合入,以合入后的为准解冲突。
## 1. 现状实证(按符号定位)
### 1.1 用户已经告诉我们「时间有多准」
- 资料表与人物档案都存了精度:`birth_time_source`(`hospital_record` / `family_exact` / `approximate` / `period_only` / `unknown` / `legacy_import`)、`uncertainty_before_minutes` / `uncertainty_after_minutes`、`declared_window_start` / `declared_window_end`、`birth_time_status`(迁移 `20260717020000_birth_time_journey.sql`、`20260822010000_declared_birth_window_clocks.sql`、`20260925020000_chart_subject_typed_columns.sql`)。
- 表单:`frontend/src/components/birth-time-intake.tsx` 的 `BirthTimeIntakeFields`;选项在 `frontend/src/lib/birth-time-intake-model.ts`:`approximateUncertaintyOptions`(15 / 30 / 60 / 120 分钟),`birthTimeSourceDefaults.hospital_record` 前后各 2 分钟;时段窗口由 `declaredWindowRangeForDraft` 给出。
- 表单里对医院记录写着「系统会无感检查前后 2 分钟的上升与 D9 / D10 稳定性」——**但星盘页从来没把检查结果告诉用户**。
### 1.2 「整窗只有一个上升」已经能算,而且很快
- `scripts/rectification_varga_segments_api.py::scan_varga_segments`,接口 `POST /api/rectification/v5/varga-segments`,传 `target_charts: ["D1"]` 返回 `segments.D1`(每段起止与星座号)和 `full_window_unique.D1`,自带磁盘缓存;岁差走 `_segment_ayanamsa`(BUG-1309)。
- Claude 本机实测(Linux,虚构地点,冷缓存,只算 D1):前后 15 分钟 31 分钟 **0.02 秒**;前后 2 小时 241 分钟 **0.09 秒**;整天 1441 分钟 **0.54 秒**。所以可以和星盘一起算、一起揭幕,不需要单独等待。
- 目前只有生时校正调用它:`frontend/src/lib/rectification-agentic/opening-boundary.ts::fetchOpeningBoundary`、`v9/segment-scan.ts::scanCaseSegments`。
### 1.3 星盘页
- `frontend/src/components/chart-page/chart-page-view.tsx::ChartPageView`:页头 `birthline`(日期 / 时间 / 地点 + 铅笔)作为 `note` 传给 `SecondaryPageShell`;上升只出现在 Vedic 表格那一行(`chart-vedic-tab.tsx`,`label="上升"`)。
- 取数:`useChartPage` → `fetchChartView` → `GET /api/chart-view` → `frontend/src/lib/chart-view-service.ts::loadChartView`(`loadSubjectBirth` 拿到出生资料后 `postEngine` 调 Python `POST /api/chart`)。
### 1.4 校正开场
- 固定句:`frontend/src/lib/rectification-agentic/user-copy.ts::openingSpokenBody`(「我们来把你的出生时间缩小到更准的范围……做法很简单:你说几件人生里的大事和大概年月,我拿去和星盘对照。」)与 `openingBoundarySentence`;模型写的开场由 `isAcceptableOpeningBody` 把关,不合格就换成固定句(`v9/agent-run-attempt.ts`)。
- 模型开场提示:`v9/agent-run-messages.ts` 的开场简报段;口吻在 `frontend/src/mastra/agentic-rectification.ts`(BUG-1257)。
- 开场**没有说**这一步做不到什么、答「不准」会怎样、可以随时停。
## 2. 根因
1. 「上升在你给的精度内会不会换」这件事我们早就会算,但只在用户走进生时校正之后才用;星盘页对 64/77(±10 分钟,v5 评价集)根本不用问的人什么也没说,对上升会换的人也没提醒。
2. 校正开场只讲「怎么做」,不讲「能做到什么程度」;竞品把限度写在最前面,读起来更可信。
## 3. 决策记录(产品负责人 10-27,#112 选 A)
1. **不做**竞品那种「6 条断语 + 吻合分」,也不新增任何提问。
2. 上升交界预检:纯计算,不调模型,不扣点。
3. 校正开场按竞品的诚实口径改写:能做什么、不能做什么、答「不准」就记不准、可以随时停下照原时间用。
4. 显示位置:**星盘页页头**(本人和人物档案里每个人的星盘页)。首页今天没有任何出生资料展示位,本单不在首页新增;要加另立单。
5. 「上升确定」**只说上升**,不说「不用校正」——09-30 研究(`rectification_varga_resolution_2026_09_30.md`)证明「不用校正」只对上升成立,九分盘 / 十分盘 0/77。
## 4. 硬红线
1. 不改数据库结构,不加迁移。
2. `scripts/jyotish_api_server.py` 不加方法、不加路由(复用现有 `varga-segments` 接口);`frontend/src/app/(app)/page.tsx` 不动。
3. 不调模型,不加新问题,不改校正打分、候选、冻结文件(`references/rectification_sealed_holdout.v1.json` 等)。
4. 一次揭幕:这一行和星盘一起出现;算不出来(超时、接口错、资料不全)就**整行不显示**,不出 spinner、骨架或「正在检查」。预检失败不得让星盘页失败。
5. 文案对照 `frontend/docs/VOICE.md`:不用「分盘」「候选」「区间」「Lagna」「D1」等词;星座用中文名。
6. 改 UI 同提交更新 `frontend/DESIGN.md`;首屏 gzip ±2%(本单不碰首页,预期不变)。
7. 改既有断言写「原值 / 新值 / 原因」三栏(AGENTS §7.3、§7.8)。下列测试按文本断言开场文案,必须同轮改:`rectification-opening-plain-20260926.test.ts(x)`、`rectification-nearest-boundary-opening-20261012.test.ts`、`rectification-v9-agent.test.ts`、`agent-voice-copy-contract.test.ts`;另 grep `openingSpokenBody` / `isAcceptableOpeningBody` 的其余引用。
8. 隐私:测试只用虚构盘。
## 5. 任务分解(一条一个提交)
### L1 · 精度窗口(前端纯函数)
新增 `frontend/src/lib/lagna-precheck.ts`(名字可调):
- 输入:出生资料(同 `loadSubjectBirth` 返回的形状)。输出:`{ start_at, end_at, minutes } | null`。
- 规则:
- `hospital_record`、`family_exact`、`approximate`:报告时间 −`uncertainty_before_minutes` 到 +`uncertainty_after_minutes`(缺值按 `birthTimeSourceDefaults`)。
- `period_only`:`declared_window_start`–`declared_window_end`(跨午夜照 `declaredWindowRangeForDraft` 的口径)。
- `unknown`:当天 00:00–23:59。
- `birth_time_status` 为已确认(采用了校正结果)或 `legacy_import`:返回 `null`(不显示这一行)。
- 窗口超过 1441 分钟时截到 1441(接口上限)。
- 验收:单测覆盖以上每种来源,含跨午夜时段、缺失不确定度时用默认值。
### L2 · 星盘页拿到结果(与星盘同一次请求)
- `loadChartView` 在拿到出生资料后,与 `/api/chart` **并行**调用 `/api/rectification/v5/varga-segments`(`target_charts: ["D1"]`,岁差跟这张星盘当前的岁差设置一致,经纬度与时区同星盘)。单独超时 3 秒;失败就不带这段。
- `ChartViewResponse` 加一个可选字段(例如 `lagnaPrecheck: { state: "fixed" | "may_change" | "unknown_time"; signs: string[]; changeAt?: string[]; windowMinutes }`),映射在 `chart-view-mapper.ts`;没有就不显示。
- 只有本人或人物档案里有权看这张盘的人能拿到(沿用 `loadChartView` 现有鉴权,不新开路由)。
- 验收:单测——接口返回单段 → `fixed`;两段 → `may_change` 且带换座时刻;接口抛错 / 超时 → 字段缺省、星盘照常 200。
### L3 · 页头一行文案
在 `ChartPageView` 页头 `birthline` 下面加一行小字(样式同页头说明文字,不加卡片、不加图标),文案:
| 情况 | 文案 |
| --- | --- |
| 窗口内上升不换 | 按你给的时间精度(前后约 N 分钟),上升星座都是{X}座,这一点不会因为时间误差而变。 |
| 窗口内会换(两个星座) | 按你给的时间精度,上升可能是{X}座,也可能是{Y}座(大约 {HH:MM} 换座)。想定下来可以做一次出生时间校正。 |
| 会换(三个及以上) | 按你给的时间段,上升可能落在{X}座、{Y}座、{Z}座中的一个。想定下来可以做一次出生时间校正。 |
| 不知道出生时间 | 不知道出生时间时,上升星座定不下来;做一次出生时间校正可以缩小范围。 |
- 「出生时间校正」做成链接,进入现有的校正入口(复用现有入口的路由与参数,人物档案里的人进入他自己的校正);**确定**那一行没有链接。
- 时段窗口(`period_only`)把「前后约 N 分钟」换成「按你给的时间段({开始}–{结束})」。
- 英文界面若星盘页有英文版,照同样四句给英文;没有就只做中文。
- 验收:组件测试四种情况各一条;`fixed` 没有校正链接;字段缺省时 DOM 里没有这一行。`frontend/DESIGN.md` 记一句这行的位置与样式。
### L4 · 校正开场说清限度
改 `openingSpokenBody`(固定句)并同步模型开场简报(`agent-run-messages.ts`),开场正文改成三层意思,口吻按 BUG-1257 的姐姐口吻:
1. 做什么:「我们来把你的出生时间缩小到更准的范围,现在先在 {窗口} 之间找。」(保留现有 `productDelivery` 分支的说法)
2. 怎么做:「你说几件人生里的大事和大概年月,我拿去和星盘对照。」
3. **限度(新增)**:「这一步能帮你把用来解读的星盘定下来,但不保证精确到某一分钟;对不上的地方你直接说不准,我会照实记下,不会换个说法圆回来。中途想停也可以,照你原来填的时间用。」
- `openingBoundarySentence` 保留,位置在 1 和 2 之间不变。
- `isAcceptableOpeningBody`:模型写的开场必须含限度这层意思(以「不保证」或「精确到某一分钟」与「不准」两处为判据),否则回落固定句;`OPENING_BODY_JARGON` 不放宽。
- 验收:上面第 4 节第 7 条列的测试按三栏改完并通过;新增一条:模型开场缺限度句 → 回落固定句。
## 6. 验收分工
| 项 | 谁 |
| --- | --- |
| `tsc --noEmit` 0 错、`npm run lint` 0 error、`npm test` 全量与 staging 基线逐条一致 | Grok 跑,Claude 复验 |
| `next build` 后 `/` 仍 Static,首屏 gzip ±2% | Grok 跑,Claude 复验 |
| Python:本单不改 Python;若碰了 `scripts/**` 或 `tests/**`,跑快速门全量 | Grok |
| 浏览器真机:本人星盘页(医院记录 / 大概时间 / 时段三种资料各一次)、人物档案里一个人、点链接进校正、校正开场读一遍 | 产品负责人(写进 `docs/testing/lagna-precheck-20261027.md`) |
## 7. 让步顺序
时间不够时先保 L4(开场文案),再 L1→L2→L3;L3 的三个及以上星座那一句可以并进「会换」一句(列出所有星座)。不得为赶工跳过「失败就不显示」。
## 8. 开工前置命令
```bash
git fetch origin --prune
git worktree add -b codex/lagna-precheck-20261027 .worktrees/lagna-precheck-20261027 origin/staging
cd .worktrees/lagna-precheck-20261027/frontend && npm ci
./node_modules/.bin/tsc --noEmit && npm run lint && npm test # 记下基线失败清单
grep -rn "openingSpokenBody\|isAcceptableOpeningBody\|openingBoundarySentence" tests/ src/ | grep -v node_modules
```
## 9. BUG 编号
开工时 `grep -o 'BUG-[0-9]*' docs/BUG_HISTORY.md | sort -u -V | tail -1`;当前最大 BUG-1443,本单从 **BUG-1444** 起:
- BUG-1444:星盘页不告诉用户上升在其时间精度内是否会换(预检能力早已存在只给校正用)。
- BUG-1445:校正开场不讲限度。
交付:只推分支,在 #112 回报 SHA,标签改「待验收」。