feat(rectification): segment-gain order on by default up to 61 minutes after the health-focus fix; records and replay evidence (BUG-1143, BUG-1144)
Independent Staging Quality Gate / validate (push) Successful in 13m28s
Independent Staging Quality Gate / publish (push) Successful in 3m31s

The 77-case persisted replay after the BUG-1143 fix keeps every truth segment
at both 21 and 61 minutes with segment order on, so the default envelope moves
from 21 to 61 minutes. Bug numbers follow staging (BUG-1142 was taken).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
This commit is contained in:
Jesse_Chen
2026-10-01 13:15:36 +08:00
co-authored by Claude Opus 5.5
parent 564eede340
commit f1a169cb93
10 changed files with 242241 additions and 14 deletions
+6
View File
@@ -1,5 +1,11 @@
# 印度占星 Skill 更新日志
## 2026-10-01 — 生时校正不再答两三题就提前结束;默认按「哪道题最能分开盘型」出题
- 修复:轮到「健康」方面的选择题时,这道题一直写不进去,系统就直接给出结果,后面还能问的题都不问了。修好后平均每次多问约 2 题(前后 10 分钟:2.8 → 4.6 题;前后 30 分钟:3.6 → 5.4 题),D9 / D10 判对的例数明显上升,没有一例把真实时间排除在外(BUG-1143)。
- 「按盘型挑题」默认开启(范围在前后 30 分钟以内时):优先问最能区分 D9 / D10 上升的题。77 例回放:前后 30 分钟 D9 判对 46 → 51 例、D10 52 → 55 例(BUG-1144)。
- Skill 版本不变;不改数据库结构。
## 2026-10-01 — 普通对话写回答最多可以等 5 分钟(原来 70 秒)
- 推理较重的模型(例如 deepseek-v4-pro)写长回答不再在最后一节被截断;5 分钟只用来防模型卡死,不限制回答长度(BUG-1142)。
+29
View File
@@ -15347,3 +15347,32 @@
- 相关记录:BUG-1051、BUG-1053、BUG-944。
- 复发自:无
- 修复版本:`codex/consult-answer-clock-20261001`
## BUG-1143 | 健康类区分选择题写不进焦点表,流程静默提前交付
- 状态:resolved(2026-10-01 Claude 直接执行;77 例真库持久回放修前修后对比;未部署时以部署后 health 为准)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:`frontend/src/lib/rectification-agentic/v9/server-focus.ts`(区分类焦点写 `targetDomain`)、`answer-choice.ts::persistFocusAfterChoice`(写入失败被吞)、所有生时校正用户。
- 用户现象:答完两三道选择题就直接出交付卡,后面还有家人、感情、搬迁等可答的题却不再问;与「按盘型挑题」开关无关。
- 触发条件:任一轮的区分题落在健康领域(引擎领域名 `health_pressure`)。
- 根因:区分类焦点把计划层领域名原样写入 `agentic_rectification_conversation_focuses.target_domain`;该列检查约束只收 `education/career/relationship/relocation/finance/health/family/other`。写入抛 `target_domain_check` 违约,被 `persistFocusAfterChoice` 的 catch 吞掉(日志仅 `retry next focus failed reason=tool_failed`),流程落到 exhaustion 出口 `terminalNote` 交付。采集类焦点早经 `persistableFocusDomain` 映射,09-13 `530f260f`(BUG-661~663)的区分类写入分支漏掉。假数据库测试不校验约束,所以一直没测出来。
- 修复:新增 `focusTargetDomain`,所有意图的焦点写入都经 `persistableFocusDomain`;`method-followup.ts` 三处焦点与探针的领域比较改走 `sameCollectDomain`(BUG-672 规则);不改数据库约束。
- 验证:`frontend/tests/rectification-focus-target-domain-20261001.test.ts`(引擎全部领域名 × 四种意图映射后都在迁移的约束清单内;健康区分焦点存 `health` 且与 `health_pressure` 探针同域;源码合同禁止原样写领域名与 `probe.domain === focus.targetDomain`)。真库(PostgreSQL 17 替身)持久回放 77 例 × ±10/±30(`docs/research/varga_resolution_persisted_onoff_{before,after}_fix_2026_10_01.json`):平均问题数 ±10 2.82→4.56、±30 3.58→5.43;问满 6 题 21→50、30→69;默认顺序下 D9 / D10 头段命中 ±10 65→70 / 64→65、±30 38→46 / 39→52;真值段保留全部 76/76;修后日志 `retry next focus failed` 0 条。
- 防复发:焦点写入只能经 `focusTargetDomain`;引擎领域与表约束的对照由测试逐值锁定。
- 相关记录:BUG-672(同族:健康 / 职业别名)、BUG-586、BUG-661~663、BUG-1144。
- 复发自:BUG-672(别名未经归并,写入侧新分支漏掉)。
- 修复版本:`codex/rectification-segment-order-20261001`。
## BUG-1144 | 「按盘型挑题」默认关闭
- 状态:resolved(2026-10-01;默认开启窗口 ≤ 61 分钟)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-01
- 影响面:`core/segment-probe-order.ts`、`v9/score-persist.ts`。
- 用户现象:盘型口径实现(BUG-1116)把按段选题做成环境变量 `RECTIFICATION_SEGMENT_ORDER=on` 才启用,生产默认按引擎信息增益出题,分盘头段命中低于研究「按段」列。
- 根因:实现单允许 T5 延后;执行方因真库持久回放未跑通而默认关闭。
- 修复:`segmentOrderEnabledFor(windowMinutes, flag)`:默认窗口 ≤ 61 分钟开启,`off` 关闭,`on` 为研究包络。
- 验证:修复 BUG-1143 后的 77 例真库持久回放,开 vs 关:±10 D9 70→71、D10 65→67;±30 D9 46→51、D10 52→55;真值段保留四格全部 76/76。修 BUG-1143 前 ±30 开启曾丢 3 例真值段(`…before_fix…json`),根因即 BUG-1143(开启后健康题被排到第一位,更早撞上写入失败)。`frontend/tests/rectification-segment-order-default-20261001.test.ts`。
- 防复发:放宽到 > 61 分钟前须用同一回放脚本在对应窗口重跑并逐格核对真值段保留。
- 相关记录:BUG-1116、BUG-1143、BUG-1105。
- 复发自:无
- 修复版本:`codex/rectification-segment-order-20261001`。
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,49 @@
# PROGRESS · 生时校正:健康区分题写入失败 + 按盘型挑题默认开启(2026-10-01)
执行:Claude 直接执行(产品负责人 2026-10-01「同意」:前后 10 分钟先开、修复后重测前后 30 分钟,过红线再放开)。分支 `codex/rectification-segment-order-20261001`。无单独任务书;背景见 `TASK-rectification-varga-resolution-20260930.md` T5 与 `docs/research/rectification_varga_resolution_2026_09_30.md` §4。BUG-1143、BUG-1144。
## 经过
1. 77 例 × ±10 / ±30 真库持久回放(PostgreSQL 17 替身;`frontend/tests/research/rectification-segment-persisted-replay.ts` 经 `scripts/research/varga_resolution_persisted_replay.py`)比较按段选题关 / 开:±10 开启头段多命中 6 例、真值段 0 丢失;±30 开启丢 3 例真值段(`docs/research/varga_resolution_persisted_onoff_before_fix_2026_10_01.json`)。
2. 逐例插桩(临时,未提交):丢失不是排序删题。区分类健康题写焦点时 `target_domain=health_pressure` 违反表约束,写入失败被吞,流程从 exhaustion 出口提前交付;开启排序只是把健康题排到第一位、更早撞上。默认顺序下同样发生(BUG-1143,BUG-672 同族复发)。
3. 修 BUG-1143 后重跑(`…after_fix_2026_10_01.json`)。
## 77 例真库持久回放(修前 → 修后)
| 窗口 / 顺序 | 平均问题数 | 问满 6 题 | D9 头段命中 | D10 头段命中 | 真值段保留 D9 / D10 |
| --- | --- | --- | --- | --- | --- |
| ±10 默认 | 2.82 → 4.56 | 21 → 50 | 65 → 70 | 64 → 65 | 76/76 → 76/76 |
| ±10 按段 | 2.87 → 4.61 | 22 → 52 | 68 → 71 | 67 → 67 | 76/76 → 76/76 |
| ±30 默认 | 3.58 → 5.43 | 30 → 69 | 38 → 46 | 39 → 52 | 76/76 → 76/76 |
| ±30 按段 | 3.53 → 5.43 | 29 → 69 | 41 → 51 | 40 → 55 | 74/75 → 76/76 |
修后日志 `retry next focus failed`:0 条。±60 未回放(排序包络上限 61 分钟,>61 不启用)。
## 改动
| 文件 | 改动 |
| --- | --- |
| `v9/server-focus.ts` | 新增 `focusTargetDomain`,所有意图焦点写入经 `persistableFocusDomain` |
| `v9/method-followup.ts` | 三处焦点 ↔ 探针领域比较改 `sameCollectDomain` |
| `core/segment-probe-order.ts` | `segmentOrderEnabledFor(window, flag)`:默认 ≤ 61 分钟开、`off` 关、`on` 研究包络 |
| `v9/score-persist.ts` | 用上面函数代替 `RECTIFICATION_SEGMENT_ORDER === "on"` |
| 测试 | `rectification-focus-target-domain-20261001.test.ts`(3)、`rectification-segment-order-default-20261001.test.ts`(2) |
既有断言改动:无。
## 门禁(对 `origin/staging`,替身真库除外均为本机)
| 检查 | 基线 | 本分支 |
| --- | --- | --- |
| tsc / lint | 0 / 0 error、126 warning | 0 / 0 error、126 warning |
| npm test | 4846 / fail 24 | 4851 / fail 24;失败清单逐名相同,基线名 0 丢失,新增 5 |
| 按镜像 COPY 清单构建 | — | exit 0 |
| `/` / 首屏 gzip | Static / JS 555,622、CSS 68,813 | Static / 相同 |
| 快速门 | — | Python 1036 passed / 1 skipped / 0 failed |
默认窗口由 21 放宽到 61 后:受影响 6 个测试文件 322 / 322、tsc 0。
## 缺口
- 真库证据为 PostgreSQL 17 替身;本次不改数据库结构。
- 真机:部署后在 staging 走一次前后 30 分钟窗口的校正,确认能问到健康题、能问满几题。
+1
View File
@@ -264,6 +264,7 @@
| `TASK-rectification-message-cleanup-20261001.md` | `PROGRESS-rectification-message-cleanup-20261001.md` | **校正消息清理**:题干两遍(作废旧焦点仍打印题干 + 独立问题块再打印一次)→ 一屏一题干;证据轮正文改为「记下了 N 件事」+ 服务端清单可展开;校正结算后不显示「已完成 N 步」与「1.」泄漏。推翻 BUG-917 作废题留题干、VOICE 逐条复述 | 已验收(Claude 10-01:全套门禁对 staging 209352eb 一致;「1.」来源为推断——收起的有序列表复制补号,结算后整块不渲染已消除;真机待产品) | BUG-1135~1137 |
| `TASK-rectification-chart-tier-caveats-20261001.md` | — | **报告与聊天标注判断不了的分盘**:采用后读 `active_birth_provenance`(segment-v1)→ 结果 `segment_summary` 档位,blocked/indistinct 的 D9/D10 在聊天 answer_policy 与一句说明、报告章节顶部提示 + 主题降级;旧资料逐字节不变;不改库、不改 consult/route.ts | 待领取 | BUG-1138~1139 |
| `TASK-rectification-angle-timing-research-20261001.md` | `PROGRESS-rectification-angle-timing-research-20261001.md` | **角度类时间技法研究(离线)**:行运土木罗压本命上升 / 天顶度数、次限推运角、太阳弧角(1° ≈ 4 分钟出生时间),77 例 v5;乱序日期对照 ≥ 50 次必做、留一法、不得挤出真值;显著才叠加到六题后验比头段命中与区间宽度 | 已实现待验收(0/9 预登记检验显著,BUG-1141 closed_by_design;分支 codex/rectification-angle-timing-research-20261001,未推送) | BUG-1141 |
| (直接执行,无任务书) | `PROGRESS-rectification-segment-order-20261001.md` | **健康区分题写入失败(BUG-1143)+ 按盘型挑题默认开启(BUG-1144)**:区分类焦点把 `health_pressure` 原样写入、违反表约束、写入被吞后提前交付(BUG-672 同族);修后 77 例真库回放平均问题数 ±10 2.8→4.6、±30 3.6→5.4,D10 头段 ±30 39→52;按段选题 ≤61 分钟默认开,±30 D9 46→51、D10 52→55,真值段 76/76 | 已验收(Claude 10-01 直接执行) | 分支 codex/rectification-segment-order-20261001 |
| `TASK-rectification-typed-event-scoring-research-20260929.md` | `PROGRESS-rectification-typed-event-research-20260929.md` | **打字经历按选择题规则计分(离线研究,不上线)**:计分通道不对称 + 已入账年份挡题;R0 学业质量题措辞 / 年精度显示成 1 月(冻结文件,需重新冻结) | 已验收关单(Claude 10-01:三类技法 9 项检验 0 显著,真值平均排位 0.49~0.59;Claude 补正对照——埋入信号排位 0.077、挪年 0.502——流程有效;BUG-1141 closed_by_design) | BUG-1088、1089 |
| `TASK-report-reader-polish-20260929.md` | `PROGRESS-report-reader-polish-20260929.md` | **报告页打磨**:生成入口挪进页面主体(删标题栏按钮)、详情页到底部按钮(懒渲染一次到底)、导出按钮带文字、目录一级/二级分层、去掉「字段」与 RL/NL 缩写表头、状态列同义重复去重、报告表格淡底色、分块导出显示真实文件大小 | Claude 直接执行并自验(tsc 0、lint 0 error、全量 fail 与基线同 24 条、`/` Static、gzip +7 B、快速门 Python 1000 passed),已部署 staging `d7772011`,真机欠 | BUG-1092~1094 |
| `TASK-report-english-edition-20260929.md` | `PROGRESS-report-english-edition-20260929.md` | **报告中英两版**:同一次引擎计算渲染 zh/en 两遍(不用模型翻译),瑜伽库 477 条补英文、模板与前端表头英文化;中文逐字节不变、英文零汉字、两版数字序列一致;阅读页 `?lang=en` 切换,导出当前语言 + 「问 AI 建议导出英文版」提示;旧报告不补英文 | Claude 直接执行(含两个 fork 子代理)并自验(tsc 0、lint 0 error、全量 fail 与基线同 24 条、`/` Static、gzip +0.06%、Python 定向全绿),已部署 staging `d7772011`,真机欠 | — |
@@ -64,13 +64,13 @@ export function orderProbesBySegment(input: {
}
/**
* Production default for segment-gain probe order (BUG-1143). The 77-case persisted replay
* (2026-10-01) gained head hits with no lost truth segment at windows <= 21 minutes; at 61
* minutes it lost truth segments, traced to the health-focus write failure (BUG-1142), so the
* default stays at 21 until that replay is rerun. "off" disables; "on" keeps the research
* envelope used by the persisted replay harness.
* Production default for segment-gain probe order (BUG-1144). 77-case persisted replay
* (2026-10-01, after the health-focus write fix BUG-1143): at 21 and 61 minutes it gained
* D9/D10 head hits with every truth segment kept (76/76). Before that fix the 61-minute cell lost
* truth segments, which is why the first cut stopped at 21. Wider windows were not replayed.
* "off" disables; "on" is the research envelope used by the persisted replay harness.
*/
export const SEGMENT_ORDER_DEFAULT_MAX_WINDOW = 21;
export const SEGMENT_ORDER_DEFAULT_MAX_WINDOW = 61;
export const SEGMENT_ORDER_RESEARCH_MAX_WINDOW = 61;
export function segmentOrderEnabledFor(windowMinutes: number | null | undefined, flag: string | undefined): boolean {
@@ -277,7 +277,7 @@ export function persistableFocusDomain(domain: string | null | undefined): strin
* the table only accepts education/career/relationship/relocation/finance/health/family/other,
* while planner domains include health_pressure and occupation (BUG-672). A raw
* health_pressure on a distinguish focus failed the check constraint, the write was
* swallowed and the session ended early (BUG-1142).
* swallowed and the session ended early (BUG-1143).
*/
export function focusTargetDomain(followup: Pick<MethodFollowup, "intent" | "domain">): string | null {
return persistableFocusDomain(followup.domain)
@@ -4,7 +4,7 @@ import test from "node:test";
import { sameCollectDomain } from "../src/lib/rectification-agentic/v9/domain-alias.ts";
import { focusTargetDomain } from "../src/lib/rectification-agentic/v9/server-focus.ts";
// BUG-1142: a distinguish focus wrote the planner domain verbatim. health_pressure is not in the
// BUG-1143: a distinguish focus wrote the planner domain verbatim. health_pressure is not in the
// focus table's target_domain check, the write failed inside a swallowed retry, and the session
// went straight to delivery although tap cards remained (BUG-672 alias family).
const migration = readFileSync(new URL("../supabase/migrations/20260814020000_rectification_v10_runtime.sql", import.meta.url), "utf8");
@@ -2,14 +2,14 @@ import assert from "node:assert/strict";
import test from "node:test";
import { segmentOrderEnabledFor, SEGMENT_ORDER_DEFAULT_MAX_WINDOW } from "../src/lib/rectification-agentic/core/segment-probe-order.ts";
// BUG-1143: segment-gain order is on by default only where the 77-case persisted replay lost no truth segment.
test("segment order is on by default for windows up to 21 minutes and off beyond", () => {
assert.equal(SEGMENT_ORDER_DEFAULT_MAX_WINDOW, 21);
// BUG-1144: segment-gain order is on by default only where the 77-case persisted replay lost no truth segment.
test("segment order is on by default for windows up to 61 minutes and off beyond", () => {
assert.equal(SEGMENT_ORDER_DEFAULT_MAX_WINDOW, 61);
assert.equal(segmentOrderEnabledFor(21, undefined), true);
assert.equal(segmentOrderEnabledFor(11, ""), true);
assert.equal(segmentOrderEnabledFor(22, undefined), false);
assert.equal(segmentOrderEnabledFor(31, undefined), false);
assert.equal(segmentOrderEnabledFor(61, undefined), false);
assert.equal(segmentOrderEnabledFor(61, undefined), true);
assert.equal(segmentOrderEnabledFor(62, undefined), false);
assert.equal(segmentOrderEnabledFor(121, undefined), false);
});
test("off disables everywhere; on keeps the research envelope; no scan means off", () => {