diff --git a/CHANGELOG.md b/CHANGELOG.md index fe503ec5..5968d381 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # 印度占星 Skill 更新日志 +## 2026-09-14 — 并列时请再补一件带年月的经历,不再说「问完了」 + +生时校正里,固定采集线问完、几个候选仍然分不开时,范围卡照出,但会请你再说一件记得确切哪一天的事,不限领域(登记结婚那天、入职第一天、手术那天、孩子出生那天、拿到录取通知那天)。记不得哪一天的,有年月也行。补完会接着算。不再写「能问的都问完了」。Skill 版本不变。 + ## 2026-09-14 — 风格参考题问不出时仍会给出范围卡 生时校正里,带年月的题问完后仍会先问还没用过的风格参考题。如果这道题这一轮问不出来(比如候选在该分盘上只有一种上升星座),或已经问过一轮,会直接给出范围卡,不会停在没有题目的等待里。Skill 版本不变。 diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 87989a5e..cd69ca6c 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -10722,6 +10722,22 @@ - 修复:只有真正拿得到可渲染风格题时才 hold;exhausted、closed-ceiling、holdout 不可用的收口不 hold;同一 Case 最多 hold 一轮。恢复「恰好一条交付闸」和「参考题确认语不算出口」两条断言。 - 验证:`frontend/tests/rectification-tiebreak-before-card-20260914.test.ts`、`rectification-exhaustion-exit-20260906.test.ts`、`rectification-probe-pool-exhausted-20260911.test.ts`。 - 防复发:任何「延迟交付」的守卫都必须带出口,且不得作用于耗尽与收口路径。验收必须比对 `npm test` 失败数与基线。不得把参考题确认语算作出载体。 -- 相关记录:BUG-686、BUG-656、BUG-674、BUG-680、BUG-597 +- 相关记录:BUG-686、BUG-656、BUG-674、BUG-680、BUG-597、BUG-689 - 复发自:BUG-686(风格题前置扩大到所有交付分支) - 修复版本:待发布 + +## BUG-689 | 七条采集线问完后只说「能问的都问完了」,用户不知道还能补经历 + +- 状态:resolved +- 首次发现:2026-09-14 +- 最近更新:2026-09-14 +- 影响面:`rangeDeliveryCollectClosed`、`rangeNarrowHint`、范围交付卡、交付后补证据 +- 用户现象:结婚 / 收入 / 家人等固定线答过「没有/跳过」后,范围卡只写「能问的都问完了」。用户不知道还可以再说一件带年月的经历,也不知道补了会重算。真机并列约 26% / 26% / 20%。 +- 触发条件:定向补事七条线全部关闭或覆盖,随后交付区间;前两名相对可能性差 ≤ 3 个百分点。 +- 根因:BUG-687 把空池收口写成终局句。采集线关闭不等于校正结束:交付后 Case 仍是 `candidate_ready`,输入框不禁用,新的带年月证据会改账本指纹并让快照过期。缺的是邀请;过期快照若仍带着 `tied_first` 停止原因,缺口状态机会继续停在 `delivered`。 +- 修复:空池文案改为不限领域的补充邀请:先请记得确切哪一天的事,再退年月;举七条线之外的例子;不承诺定到分钟。并列且线已关闭时,邀请出现在交付卡正文,不躺在浅色说明里。快照因新证据过期时,决策不再把 `tied_first` 等耗尽停止原因带进采集,缺口才不会停在交付态。 +- 验证:`frontend/tests/rectification-open-collect-invite-20260914.test.ts`。 +- 防复发:采集线关闭不等于校正结束;交付卡必须给出不限领域的补充邀请,且补充必须可见地生效。不得把邀请写成「再说一件就能定到分钟」。不得把已经拒答的线再列一遍(BUG-687)。 +- 相关记录:BUG-687、BUG-646、BUG-651、BUG-653、BUG-688、BUG-590 +- 复发自:BUG-687(空池收口写成终局句) +- 修复版本:待发布 diff --git a/docs/tasks/PROGRESS-rectification-open-collect-invite-20260914.md b/docs/tasks/PROGRESS-rectification-open-collect-invite-20260914.md new file mode 100644 index 00000000..227cfe26 --- /dev/null +++ b/docs/tasks/PROGRESS-rectification-open-collect-invite-20260914.md @@ -0,0 +1,30 @@ +# 进度 · 并列时请再补一件经历(2026-09-14) + +## 范围 + +- 分支:`codex/rectification-open-collect-invite-20260914`(基于 `origin/staging` @ `62803c5d`) +- 任务单:`docs/tasks/TASK-rectification-open-collect-invite-20260914.md` +- BUG-689 resolved(复发自 BUG-687) +- 未改打分、确认门、`MIN_ACCEPTANCE_EVENTS` / `MIN_ACCEPTANCE_DOMAINS`(§4b)、Skill 版本 + +## 完成 + +- T1:`rangeDeliveryCollectClosed` 改为不限领域邀请:先请确切哪一天(登记结婚那天、入职第一天、手术那天、孩子出生那天、拿到录取通知那天),再退年月(换专业、出国、打官司、创业、重病、亲人离世)。`rangeNarrowHint` 空池两处返回点沿用该常量。不再写「问完了」。 +- T2:前两名相对可能性差 ≤ 3 个百分点且采集线关闭时,邀请出现在交付卡正文(`rectification-range-delivery__invite`);浅色 caption 只留「现在还剩 …」。非并列或线仍开放时不显示该邀请。采用按钮仍在。 +- T3:交付态后新增一件 confirmed dated evidence → 账本指纹变化、`snapshotCurrent === false`、`decideFromDossier` 不再交付,也不再带着 `tied_first` 停在 `delivered`。未改 BUG-682 的「GET 回落 collect_evidence + tied_first 仍算出卡」。 +- D3:不得把已拒答线再写成「哪年结婚或订婚 / 哪年收入明显变过 / 家里哪年添丁」。D1b 允许用「登记结婚那天」作日精度例子。 +- 未走让步。 + +## 验收数字 + +- 新测 `rectification-open-collect-invite-20260914.test.ts` 3/3 +- 定向:`rectification-tiebreak-before-card-20260914.test.ts`、`rectification-targeted-collect-cards-20260913.test.ts`、`rectification-probe-pool-exhausted-20260911.test.ts`、`rectification-delivery-vs-collect-20260914.test.ts`、`rectification-surface-state.test.ts`、`agent-voice-copy-contract.test.ts`、`rectification-collection-question-pool.test.ts`、`rectification-convergence-budget.test.ts`、`rectification-range-delivery-20260907.test.ts` 全绿 +- `tsc --noEmit`:0 错 +- `npm run lint`:0 error(既有 warning 未顺手改) +- 全量 `npm test`:3018 / pass 2850 / fail 154 / skip 14。失败条数与本机 Windows 基线同型(CRLF、Skill SHA、Docker/DB、无 symlink)。Linux 门禁基线 27 条,本轮未新增失败。 +- `next build`:本工作树 `frontend/node_modules` 是 junction,未跑;`/` 是否仍 `○ Static` 需在有真实 `node_modules` 的树上复核 + +## 环境缺口 + +- 无 staging 登录态、无 Chrome:真机清单见 `docs/testing/rectification-open-collect-invite-20260914.md` +- `rectification-exhaustion-exit-20260906.test.ts` 本机因 Skill SHA(Windows CRLF / 无软链)无法导入,Linux 门禁会跑 diff --git a/docs/tasks/README.md b/docs/tasks/README.md index 2d846bfd..bf3c33df 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -190,7 +190,7 @@ | `TASK-rectification-precision-adaptive-boundary-research-20260914.md` | `PROGRESS-rectification-precision-gate-research-20260914.md` | **研究单**:出题闸门 `MIN_BOUNDARY_DAYS=45`(刷新 30)不看证据精度,而出生时间每差 1 分钟大运边界只平移约 1.1 天——20 分钟窗内的候选边界全挤在三周内被整批丢掉,这就是「六题后出不来题」的算术原因。量闸门按年/月/日分档(G0–G4)的收益,并必测答错容忍度(±3/±7/±14 天偏移下真值是否被挤出) | 待执行 | `codex/rectification-precision-gate-research-20260914` | -| `TASK-rectification-open-collect-invite-20260914.md` | `PROGRESS-rectification-open-collect-invite-20260914.md` | **P0**:固定七条采集线问完后只说「能问的都问完了」,用户不知道还能补经历、也不知道补了有用;而两轮研究证明补带年月经历是唯一有效手段。产品拍板:交付卡照出 + 卡上给不限领域的补充邀请(举七条线之外的例子),补完必须可见生效(BUG-689) | 待执行 | `codex/rectification-open-collect-invite-20260914` | +| `TASK-rectification-open-collect-invite-20260914.md` | `PROGRESS-rectification-open-collect-invite-20260914.md` | **P0**:固定七条采集线问完后只说「能问的都问完了」,用户不知道还能补经历、也不知道补了有用;而两轮研究证明补带年月经历是唯一有效手段。产品拍板:交付卡照出 + 卡上给不限领域的补充邀请(先要确切日期,再退年月;举七条线之外的例子),补完必须可见生效(BUG-689) | 待验收 | `codex/rectification-open-collect-invite-20260914` | | `TASK-rectification-cluster-width-research-20260914.md` | `PROGRESS-rectification-cluster-width-research-20260914.md` | **研究单**:上一轮证明调权重改不动交付区间宽度——所有方案宽度中位数都等于整个搜索窗。先确认 sweep 的宽度口径是否含淘汰(M0),再画簇结构像(M1),最后量三个改法:放宽簇上限、按分差决定是否合并、交付区间改分位覆盖(M2)。真值覆盖率不得下降 | 待验收 | `codex/rectification-cluster-width-research-20260914` | diff --git a/docs/testing/rectification-open-collect-invite-20260914.md b/docs/testing/rectification-open-collect-invite-20260914.md new file mode 100644 index 00000000..18cb4d7f --- /dev/null +++ b/docs/testing/rectification-open-collect-invite-20260914.md @@ -0,0 +1,11 @@ +# 并列后补一件带年月经历:真机清单(2026-09-14) + +分支 `codex/rectification-open-collect-invite-20260914`。本环境无 staging 登录态。 + +## BUG-689 + +- [ ] 固定七条线(结婚 / 收入 / 搬家 / 家人 / 学业 / 健康 / 职业)都答过「没有/跳过」后,范围卡仍出现。 +- [ ] 前两名相对可能性相差不超过约 3 个百分点时,卡的正文(不是浅色小字)写「这两分钟按现有信息分不开」,并请再说一件记得确切哪一天的事、不限领域;例子里至少有登记结婚那天、入职第一天、手术那天、孩子出生那天、拿到录取通知那天中的两个。记不得哪一天时,才退到年月。 +- [ ] 正文里不再出现「能问的都问完了」,也不再把已经说过没有的线写成「哪年结婚或订婚 / 哪年收入 / 家里哪年添丁」。 +- [ ] 「更像这个」和「新建对话按此时间排盘」的出口仍可用,邀请不挡住采用。 +- [ ] 并列出卡后,在输入框补一件带日期的经历(能到日更好,年月也可以):账本应记下这件事,范围或候选排序应有可见变化,或出现新的一问。不得停在「问完了」且没有任何反应。 diff --git a/docs/testing/rectification-tiebreak-before-card-20260914.md b/docs/testing/rectification-tiebreak-before-card-20260914.md index df47af63..83fa9989 100644 --- a/docs/testing/rectification-tiebreak-before-card-20260914.md +++ b/docs/testing/rectification-tiebreak-before-card-20260914.md @@ -18,4 +18,4 @@ ## BUG-687 - [ ] 范围卡文案不再出现已经答过「没有/跳过」的线(结婚、收入、添丁等)。 -- [ ] 能问的线都关闭时,写「能问的都问完了」,不暗示还有没问的线。 +- [ ] 能问的线都关闭时,不再列出已经拒答的线;后续邀请口径见 `rectification-open-collect-invite-20260914.md`(BUG-689)。 diff --git a/frontend/DESIGN.md b/frontend/DESIGN.md index 07b0816f..6464b26c 100644 --- a/frontend/DESIGN.md +++ b/frontend/DESIGN.md @@ -234,7 +234,7 @@ The birth-time rectification session is the consultation transcript plus a house | `question-gap`, delivered | no current question, or the current question is a dead choice card, and the session already delivered a range (`completed_with_range` / `provisional_range` / adopt outcomes, or `tied_first`); range card or range line plus an exit note. Never “没有拿到下一个问题。” | enabled | | `verified_idle` | one closing line `postAdoptVerifyDone` under the still-visible range card (same assistant column); no spinner, no reload | enabled | | `choice-pending` | the answered card (`data-selected` fill, a top row “正在记录…”) and the same live row from “正在记录本次选择…” through the follow-up turn | enabled (typing queues), stop visible | -| `candidates` | one range-delivery card titled “目前范围 …(对照了 N 件经历)”, a caption under the title that only names still-open lines (or “能问的都问完了”); optional “再答两道参考题微调排序” only when unused D9/D10 remain; if those were already asked and the top two are still within one point, a line “这两分钟按现有信息分不开,参考题已经用过。”; then up to three compare columns (highest posterior first; “更像这个” adopts); a closed “查看验证报告” fold. On a normal convergence, unused style questions are asked before this card. Exhausted and closed-ceiling exits still deliver the card if a style question cannot be rendered. | enabled | +| `candidates` | one range-delivery card titled “目前范围 …(对照了 N 件经历)”, a caption under the title that only names still-open lines; when the top two relative likelihoods are within 3 points and collect lines are closed, a body-size invite to add one more event of any kind, asking for an exact day first then falling back to year-month (never “能问的都问完了”); optional “再答两道参考题微调排序” only when unused D9/D10 remain; if those were already asked and the top two are still within one point, a line “这两分钟按现有信息分不开,参考题已经用过。”; then up to three compare columns (highest posterior first; “更像这个” adopts); a closed “查看验证报告” fold. On a normal convergence, unused style questions are asked before this card. Exhausted and closed-ceiling exits still deliver the card if a style question cannot be rendered. | enabled | | `adopting` | “正在采用 HH:MM…” through the follow-up turn | enabled (typing queues), stop visible | | `confirmed` | “已确认校正时间:HH:MM” | enabled | | `readonly` | “该校正已结束,只能查看历史。” and “再次校正” | disabled | @@ -253,7 +253,7 @@ The birth-time rectification session is the consultation transcript plus a house - **Accessibility:** native radio inputs remain focusable, every conditional field has a persistent label, status text uses live regions, and the complete flow is keyboard operable. - **Motion:** source-dependent fields enter with the existing 180ms opacity/vertical reveal; reduced-motion removes the translation. - **Life-event evidence:** after deterministic questionnaire completion, render three structured event rows by default and allow up to six. Each row uses a domain select, a precision select, and a matching year/month/day control; free-form descriptions are not part of scoring. -- **Candidate result:** keep the reported range, candidate interval, and active-time status visually separate. Delivery shows one range card: title “目前范围 HH:MM–HH:MM(对照了 N 件经历)”, a caption “还能再收窄:如果记得 …” under the title, up to three compare columns (time, relative likelihood, D9/D10/nakshatra traits, event-fit counts, next-12-month windows, “更像这个”), and the representative-minute boundary. Collect copy “现在还剩 HH:MM–HH:MM 里 N 个候选” uses the same `credible_range` as the timeline “目前范围”, not the first/last active candidate minute. An eight-method report sits in a closed `
` fold. Support numbers stay on the house board. Low confidence keeps evidence editing open; medium offers save or add evidence; high uses a separate confirmation action and never labels a column as the true birth time. +- **Candidate result:** keep the reported range, candidate interval, and active-time status visually separate. Delivery shows one range card: title “目前范围 HH:MM–HH:MM(对照了 N 件经历)”, a caption “还能再收窄:如果记得 …” under the title while collect lines remain open, up to three compare columns (time, relative likelihood, D9/D10/nakshatra traits, event-fit counts, next-12-month windows, “更像这个”), and the representative-minute boundary. When the top two columns are within 3 percentage points and the seven collect lines are closed, the card body (not the caption) invites one more dated event of any kind, with examples outside those lines. Collect copy “现在还剩 HH:MM–HH:MM 里 N 个候选” uses the same `credible_range` as the timeline “目前范围”, not the first/last active candidate minute. An eight-method report sits in a closed `
` fold. Support numbers stay on the house board. Low confidence keeps evidence editing open; medium offers save or add evidence; high uses a separate confirmation action and never labels a column as the true birth time. - **Evidence accessibility:** every row keeps visible labels, validation errors use live regions, add/remove controls retain 44px targets, and scoring/confirmation loading states disable duplicate submission without hiding the existing evidence. - **One-question guide:** the guided journey renders only the persisted `nextAction` and one server-selected question. A deterministic question is visible immediately; Agent wording may replace it without changing the question identity, domain, precision request, progress, or permissions. The composer explicitly permits an approximate year. Spoken collect does not render skip chips; typing 「没有」 still declines the domain and 「记不清」 still skips it. Stop on spoken collect is not a composer button: `CHOICE_STOP_LABEL` (“先这样,先看当前范围”) stays on choice cards, and generating turns keep “停止回答”. The readonly range line is a status sentence, not a stop control. Discriminator cards fold “为什么问这题” under the stem. Hovering or selecting an option does not reveal an `answer_impact` time line. The method sentence (`vargaSentence`) lives in the expanded activity timeline, not in the spoken bubble. The composer has no `rectification-step-state` status sentence and no `rectification-composer-meta`. - **Draft review:** natural-language answers become one inline review card. The evidence domain is read-only and uses its Chinese label; precision controls which exact year, month, or day input is available. Incomplete drafts keep edit and skip paths visible, while confirmation is disabled until the structured date is valid. Status and errors use polite or assertive live regions without clearing the persisted journey. diff --git a/frontend/docs/VOICE.md b/frontend/docs/VOICE.md index f47cddfe..713632f5 100644 --- a/frontend/docs/VOICE.md +++ b/frontend/docs/VOICE.md @@ -45,7 +45,8 @@ Jyotisha 的可见文案是产品的一部分。正确性红线(真实性、 | ## 统一参数与原始结构(用户刚问「我婚姻怎么样」) | 先用几句口语答婚姻方向,再进入统一参数与原始结构。 | 骨架仍在,开场先回人。 | | 生成中输入框变灰,回车没反应 | 生成中仍可打字;回车后显示「已排队,回答结束后发出」,可撤回 | 对标主流对话产品,焦点不丢。 | | 停止后出现红色告警条 | 已停止,已生成的内容保留;本次不会扣点。 | 停止是用户动作,不是出错。 | -| 也可以再说一件你记得大概时间的事。 / 还差带月份的经历,领域不限。 | 训练门开后给区间卡,并写「如果还记得……范围还能再收一截」。材料不够时只写记下了哪几件、再来一件不是这类的、至少两个具体例子;输入框占位「再说一件带年月的事」。 | 永远给结果或精确缺口;不说「领域不限」「做不了」。 | +| 也可以再说一件你记得大概时间的事。 / 还差带月份的经历,领域不限。 | 训练门开后给区间卡,并写「如果还记得……范围还能再收一截」。材料不够时只写记下了哪几件、再来一件不是这类的、至少两个具体例子;输入框占位「再说一件带年月的事」。 | 训练门未开时永远给结果或精确缺口;不说「领域不限」「做不了」。交付后采集线关完的邀请见下一行。 | +| 能问的都问完了 | 这两分钟按现有信息分不开。你要是还记得确切哪一天的事,不限领域,说出来我接着算——登记结婚那天、入职第一天、手术那天、孩子出生那天、拿到录取通知那天都成。记不得哪一天的,有年月也行,比如换专业、出国、打官司、创业。 | 固定七条线关完不等于校正结束。并列时先请确切日期,再退年月;「不限领域」只出现在交付卡邀请里。不承诺「说了确切日期就能定到分钟」。 | | 口述采集没有停止按钮,范围小字不可点 | 选择题卡内有「先这样,先看当前范围」;生成中有「停止回答」。口述采集只有输入框,没有「没有」「记不清」芯片,也不挂在输入框上方。范围小字是状态句,不可点。 | 想跳过可以打字「没有」或「记不清」。 | | 只报一件后立刻按家人/学业/财务轮着问,题干带「2020 年前后」 | 记下后问「还有吗?比如……」(只列还没提过的具体事)。答「没有了」后才从用户说过的年份追问,例如「2020 年毕业后第一份工作大概哪年开始?」 | 先等用户说完;年份只来自用户,不用生日推。 | | 点选后旁白写「04:31–04:39 这段领先,05:00–05:07 这段落后」 | 已记录,范围收到 04:31–04:39。 | 旁白只说范围变没变。领先/落后留在卡内「答了会怎样」,不进气泡。 | diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index a23b8b28..2e0b64f9 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -3390,6 +3390,12 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class .rectification-range-delivery__tie-break:disabled { cursor: default; } +.rectification-range-delivery__invite { + margin: 0; + color: var(--color-ink); + font-size: var(--type-body-md); + line-height: 1.65; +} .rectification-range-delivery__boundary { margin: 0; color: var(--color-ink-secondary); diff --git a/frontend/src/components/rectification-range-delivery.tsx b/frontend/src/components/rectification-range-delivery.tsx index 3aa8b376..192838f1 100644 --- a/frontend/src/components/rectification-range-delivery.tsx +++ b/frontend/src/components/rectification-range-delivery.tsx @@ -4,7 +4,9 @@ import type { RectificationCandidateResult } from "@/lib/rectification-candidate import { RECTIFICATION_USER_COPY, REPRESENTATIVE_MINUTE_DISCLAIMER, + rangeDeliveryCaptionWithoutClosedInvite, rangeDeliveryEventCopy, + rangeDeliveryShowsOpenCollectInvite, } from "@/lib/rectification-agentic/user-copy"; import type { RangeDeliveryProjection } from "@/lib/rectification-agentic/v9/divergence-panel"; import { RectificationVerificationReport } from "@/components/rectification-verification-report"; @@ -40,14 +42,28 @@ export function RectificationRangeDelivery({ const markdown = delivery?.verification_markdown ?? result.verificationReportMarkdown; const sharedTraits = delivery?.shared_traits ?? []; + const showOpenCollectInvite = rangeDeliveryShowsOpenCollectInvite({ + hint: delivery?.narrow_hint, + columns, + }); + const caption = showOpenCollectInvite || delivery?.narrow_hint?.includes( + RECTIFICATION_USER_COPY.rangeDeliveryCollectClosed, + ) + ? rangeDeliveryCaptionWithoutClosedInvite(delivery?.narrow_hint) + : delivery?.narrow_hint ?? null; return (
{title}
- {delivery?.narrow_hint ? ( -

{delivery.narrow_hint}

+ {caption ? ( +

{caption}

+ ) : null} + {showOpenCollectInvite ? ( +

+ {RECTIFICATION_USER_COPY.rangeDeliveryCollectClosed} +

) : null} {delivery?.tie_break_available && onTieBreak && !readonly ? (