From ec06d422d33875f9fc72c88aeb2c92b3a863f9c6 Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Fri, 2 Oct 2026 00:46:48 +0800 Subject: [PATCH] feat(consult): annual and timing turns get the shared reading and annual list (product budget decision 2026-10-02) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Annual / timing single-domain turns take the career / marriage / wealth budget: contract + card <= 12,000, total with the checklist <= 18,000. CONSULT_READING_BUDGET_BLOCKED removed; size assertions and the pinned blocker test changed (原值/新值/原因 in the tests and brief 2's progress record); BUG-1160/1161 resolved, BLOCKED.md entry struck through. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE --- BLOCKED.md | 5 +++ docs/BUG_HISTORY.md | 6 ++-- ...ESS-consult-affliction-reading-20261001.md | 17 +++++++++ frontend/src/lib/consultation-methodology.ts | 26 ++++---------- ...onsult-affliction-reading-20261001.test.ts | 36 +++++++++++-------- ...nsult-condensed-checklist-20260927.test.ts | 13 +++++-- .../consult-evidence-card-20260927.test.ts | 5 ++- 7 files changed, 67 insertions(+), 41 deletions(-) diff --git a/BLOCKED.md b/BLOCKED.md index 75c793d2..14d40eab 100644 --- a/BLOCKED.md +++ b/BLOCKED.md @@ -1,5 +1,10 @@ # BLOCKED +## TASK-consult-affliction-reading:年运 / 时运单领域轮卡在 12,000 字符口径(BUG-1160/1161,2026-10-01,2026-10-02 解除) + +- ~~状态:blocked。年运、时运单领域轮加通用读法(733 字符)与年运清单(334)后最高 12,979 / 12,257,超过 `consult-condensed-checklist-20260927`「size」与 `consult-evidence-card-20260927`「model-visible size」的 12,000;`CONSULT_READING_BUDGET_BLOCKED = {annual, timing}` 让这两类轮次保持旧方法段。~~ +- 已解除(2026-10-02,产品决定):年运、时运与事业 / 婚姻 / 财富同口径——契约 + 卡 ≤ 12,000,含清单 ≤ 18,000。`CONSULT_READING_BUDGET_BLOCKED` 已删,两条 size 断言改口径(三栏见 `docs/tasks/PROGRESS-consult-affliction-reading-20261001.md`「产品决定」段)。实测三张 golden 盘:年运 契约 + 卡 9,501~9,882、总量 12,598~12,979;时运 8,555~8,604、12,208~12,257。分支 `codex/consult-no-presupposition-backtest-20261001`。 + ## TASK-consult-card-affliction-data:大格局进不了数据卡、功能吉凶出处待定(2026-10-01) - 状态:blocked(交产品决定;本单只诊断,见 `docs/tasks/PROGRESS-consult-card-affliction-data-20261001.md` T4 / T5 段) diff --git a/docs/BUG_HISTORY.md b/docs/BUG_HISTORY.md index 77e3de41..4a860dfd 100644 --- a/docs/BUG_HISTORY.md +++ b/docs/BUG_HISTORY.md @@ -15607,14 +15607,14 @@ ## BUG-1160 | 普通对话没有「先看受冲、再看旺弱」的读法,模型默认「旺 = 好」 -- 状态:blocked(部分已修:除年运、时运单领域轮外全部生效;这两类轮次卡在 12,000 字符口径,待产品决定) +- 状态:resolved(文本层;2026-10-02 产品定年运 / 时运字数口径后全部领域生效) - 首次发现 / 最近更新:2026-10-01 / 2026-10-02 - 影响面:`frontend/src/lib/consultation-condensed-checklist.ts`、`frontend/src/lib/consultation-methodology.ts` `consultationMethodologyForDomains`、`frontend/src/mastra/index.ts` 方法说明句。 - 现象:父母题把 9 宫里的火星读成「有分量」,健康题写「两颗星都处在正常状态」,婚姻题「罗睺更多是噪音」;13 份公开名人样稿 11 份漏读关键受冲信号(排查记录 `docs/research/consult_affliction_audit_2026_10_01.md` 第 2 节)。 - 触发条件:任何普通对话;卡上有相位、同宫、燃烧、功能吉凶等事实,但方法段从没说哪些算受冲、受冲与旺弱冲突时听谁的。 - 根因(已核实):数据卡只给事实;方法段只有路由的英文 baseline 与事业 / 婚姻 / 财富三份只讲时间分层的清单,模型只能用常识「入旺 = 好、Shadbala 高 = 没事」。 - 修复:新增 `CONDENSED_SHARED_READING_LINES`(任务书 T1 定稿六条 + 一条「瑜伽只认 yogas.hits」),由 `consultationMethodologyForDomains` 每轮在 `sections` 最前推入一次(标题「通用读法 · 先看受冲」),多领域同轮也只一次;括号里的字段名按卡上真实位置改写(见进度记录)。提示词方法说明句同步讲这一段。 -- 未修部分(blocked):单领域年运轮「契约 + 卡 + 清单」已到 11,918(泰勒),时运 11,523;加通用读法(733 字符)后年运最高 12,979、时运 12,257,超过 `consult-condensed-checklist-20260927`「size」与 `consult-evidence-card-20260927`「model-visible size」两条 12,000 断言。断言未改;`CONSULT_READING_BUDGET_BLOCKED = {annual, timing}` 让只含这两个领域的轮次保持本单之前的方法段,等产品重定口径后删掉对应项即可。 +- 未修部分(2026-10-02 已解除):产品决定年运、时运与事业 / 婚姻 / 财富同口径(契约 + 卡 ≤ 12,000,含清单 ≤ 18,000);`CONSULT_READING_BUDGET_BLOCKED` 已删,两条 size 断言改口径,修复随 `codex/consult-no-presupposition-backtest-20261001`。原记录:单领域年运轮「契约 + 卡 + 清单」已到 11,918(泰勒),时运 11,523;加通用读法(733 字符)后年运最高 12,979、时运 12,257,超过 `consult-condensed-checklist-20260927`「size」与 `consult-evidence-card-20260927`「model-visible size」两条 12,000 断言。断言未改;`CONSULT_READING_BUDGET_BLOCKED = {annual, timing}` 让只含这两个领域的轮次保持本单之前的方法段,等产品重定口径后删掉对应项即可。 - 验证:`frontend/tests/consult-affliction-reading-20261001.test.ts`(每个非阻塞领域 `sections[0]` 是通用读法、多领域同轮只一次、阻塞项被钉住);三个 12,000 断言原样通过。 - 防复发:通用读法的存在与次数、清单字段名都由合同测试钉住;第三单名人回测验收读法效果。 - 相关记录:BUG-1070、BUG-1132、BUG-1134、BUG-1154~1159 @@ -15623,7 +15623,7 @@ ## BUG-1161 | 父母、子女、学业、迁居、家庭、年运、综合 7 个领域不发任何清单,健康只发英文路由 -- 状态:blocked(部分已修:父母、子女、学业、迁居、家庭、综合、健康 7 份已生效;年运清单已写入但因字符口径未下发,同 BUG-1160) +- 状态:resolved(文本层;年运清单 2026-10-02 随产品字数口径决定下发,见 BUG-1160) - 首次发现 / 最近更新:2026-10-01 / 2026-10-02 - 影响面:`consultation-methodology.ts` `domainMethodology`、`consultation-condensed-checklist.ts` `CONSULTATION_CONDENSED_CHECKLISTS`。 - 现象:有清单的领域(婚姻、财富)明显好于没有清单的领域;`domains_without_strict_checklist` 对上述 7 个领域非空。 diff --git a/docs/tasks/PROGRESS-consult-affliction-reading-20261001.md b/docs/tasks/PROGRESS-consult-affliction-reading-20261001.md index 789e40cd..017fb5e2 100644 --- a/docs/tasks/PROGRESS-consult-affliction-reading-20261001.md +++ b/docs/tasks/PROGRESS-consult-affliction-reading-20261001.md @@ -113,6 +113,23 @@ 健康卡比第一单少 2 字符(标签「身心压力」→「健康」);健康方法段从英文路由换成中文清单,含清单总量反而下降(泰勒 11,120 → 10,485)。 +## 产品决定(2026-10-02):年运 / 时运字数口径,阻塞解除 + +产品选上节方案 ①:年运、时运单领域轮与事业 / 婚姻 / 财富同口径,**契约 + 卡 ≤ 12,000,含清单 ≤ 18,000**。在第三单分支 `codex/consult-no-presupposition-backtest-20261001` 单独一个提交落地: + +- 删 `CONSULT_READING_BUDGET_BLOCKED`:每轮第一段都是通用读法;年运单领域轮下发本产品年运清单(`domains_without_strict_checklist` 为空),时运仍是英文路由。 +- 断言改动(原值 / 新值 / 原因): + +| 文件 · 测试 | 原值 | 新值 | 原因 | +| --- | --- | --- | --- | +| `consult-evidence-card-20260927`「model-visible size…」 | `methodHeavy` = career / marriage / wealth | 加 annual、timing | 产品 2026-10-02 字数口径决定 | +| `consult-condensed-checklist-20260927`「size…」 | 全部 12 领域 total ≤ 12,000 | 年运、时运:契约 + 卡 ≤ 12,000、total ≤ 18,000;其余领域不变 | 同上 | +| `consult-affliction-reading-20261001`「T1/T2 budget blocker is pinned」 | 年运只有 baseline 段、报缺;时运无通用读法 | 改为「annual and timing single-domain turns get the shared reading, and annual its list」 | 同上 | +| 同文件 T1、T2 | 跳过年运、时运;annual 期望标题 "" | 全部 12 领域;annual 期望「年运 · consult-annual · 普通对话精简清单」 | 同上 | + +- 实测(三张 golden 盘):年运 契约 + 卡 9,501~9,882、总量 12,598~12,979;时运 8,555~8,604、12,208~12,257,都在新口径内。 +- BUG-1160、BUG-1161 状态改 resolved(文本层),`BLOCKED.md` 对应条目划掉。 + ## 未做 / 交给第三单 - 写作形状去「对象在场」预设、`product-voice.ts` 逆行示例(排查 D 类源头)——第三单。 diff --git a/frontend/src/lib/consultation-methodology.ts b/frontend/src/lib/consultation-methodology.ts index 0561df3b..d3bb760f 100644 --- a/frontend/src/lib/consultation-methodology.ts +++ b/frontend/src/lib/consultation-methodology.ts @@ -65,20 +65,10 @@ const domainMethodology: Readonly = new Set(["annual", "timing"]); +// Annual and timing single-domain turns used to skip the shared reading and the +// annual list on a 12,000-character ceiling (BUG-1160/1161). Product decision +// 2026-10-02: they take the career / marriage / wealth budget instead (contract +// + card <= 12,000, total with the checklist <= 18,000), so every turn gets both. /** Domains whose list was written by the affliction-reading brief rather than condensed from the router. */ const BRIEF_WRITTEN_DOMAINS: ReadonlySet = new Set([ @@ -209,17 +199,13 @@ export function consultationMethodologyForDomains( }; // Once per turn, before anything else (T1), however many domains it plans. - if (unique.some((domain) => !CONSULT_READING_BUDGET_BLOCKED.has(domain))) { - push(CONDENSED_SHARED_READING_TITLE, CONDENSED_SHARED_READING_SOURCE, sharedConsultationReading()); - } + push(CONDENSED_SHARED_READING_TITLE, CONDENSED_SHARED_READING_SOURCE, sharedConsultationReading()); push("Shared mandatory baseline", ROUTER_FILE, markdownSection(router, SHARED_BASELINE_HEADING)); const withoutChecklist: ConsultationDomain[] = []; for (const domain of unique) { const { strictRoute, eventJudgment } = domainMethodology[domain]; - const condensed = BRIEF_WRITTEN_DOMAINS.has(domain) && CONSULT_READING_BUDGET_BLOCKED.has(domain) - ? null - : condensedConsultationChecklist(domain, { gender }); + const condensed = condensedConsultationChecklist(domain, { gender }); if (condensed) { push( `${consultationDomainDefinition(domain).label} · ${strictRoute} · 普通对话精简清单`, diff --git a/frontend/tests/consult-affliction-reading-20261001.test.ts b/frontend/tests/consult-affliction-reading-20261001.test.ts index a49609ef..4464ebb6 100644 --- a/frontend/tests/consult-affliction-reading-20261001.test.ts +++ b/frontend/tests/consult-affliction-reading-20261001.test.ts @@ -16,10 +16,7 @@ import { CONSULT_BRIEF_CHECKLIST_SOURCE, CONSULTATION_CONDENSED_CHECKLISTS, } from "../src/lib/consultation-condensed-checklist.ts"; -import { - CONSULT_READING_BUDGET_BLOCKED, - consultationMethodologyForDomains, -} from "../src/lib/consultation-methodology.ts"; +import { consultationMethodologyForDomains } from "../src/lib/consultation-methodology.ts"; import { consultationDomainDefinition, consultationDomainIds, @@ -43,7 +40,10 @@ const golden = JSON.parse(readFileSync( const titles = (domains: ConsultationDomain[]) => consultationMethodologyForDomains(domains)!.sections.map((section) => section.title); test("T1: the shared reading is the first section, once per turn, whatever the plan", () => { - for (const domain of consultationDomainIds.filter((id) => !CONSULT_READING_BUDGET_BLOCKED.has(id))) { + // 原值: consultationDomainIds.filter((id) => !CONSULT_READING_BUDGET_BLOCKED.has(id))(年运、时运除外) + // 新值: 全部 12 个领域 + // 原因: 产品 2026-10-02 决定年运 / 时运按事业 / 婚姻 / 财富同口径计字数(BUG-1160/1161 解除) + for (const domain of consultationDomainIds) { const list = titles([domain]); assert.equal(list[0], CONDENSED_SHARED_READING_TITLE, domain); assert.equal(list.filter((title) => title === CONDENSED_SHARED_READING_TITLE).length, 1, domain); @@ -62,16 +62,23 @@ test("T1: the shared reading is the first section, once per turn, whatever the p assert.equal(CONDENSED_SHARED_READING_LINES.length, 7); }); -test("T1/T2 budget blocker is pinned: a single annual or timing turn keeps its pre-brief method", () => { - // BLOCKED on the 12,000-character ceiling (see CONSULT_READING_BUDGET_BLOCKED); this - // test documents the gap so lifting it is a deliberate change. - assert.deepEqual([...CONSULT_READING_BUDGET_BLOCKED].sort(), ["annual", "timing"]); - assert.deepEqual(titles(["annual"]), ["Shared mandatory baseline"]); - assert.deepEqual(consultationMethodologyForDomains(["annual"])!.domains_without_strict_checklist, ["annual"]); - assert.deepEqual(titles(["timing"]), ["Shared mandatory baseline", "时运 · event-timing-strict"]); +// 原值: test "T1/T2 budget blocker is pinned: a single annual or timing turn keeps its pre-brief method" +// (年运单领域只有 Shared mandatory baseline、年运列在 domains_without_strict_checklist;时运无通用读法) +// 新值: 年运、时运单领域轮第一段是通用读法;年运带本产品中文清单,时运带英文路由 +// 原因: 产品 2026-10-02 决定年运 / 时运字数口径同事业 / 婚姻 / 财富(契约 + 卡 ≤ 12,000,含清单 ≤ 18,000),阻塞解除 +test("annual and timing single-domain turns get the shared reading, and annual its list (budget decided 2026-10-02)", () => { + assert.deepEqual(titles(["annual"]), [CONDENSED_SHARED_READING_TITLE, "Shared mandatory baseline", "年运 · consult-annual · 普通对话精简清单"]); + const annual = consultationMethodologyForDomains(["annual"])!; + assert.deepEqual(annual.domains_without_strict_checklist, []); + assert.equal(annual.sections[2]!.source, CONSULT_BRIEF_CHECKLIST_SOURCE); + for (const line of CONSULTATION_CONDENSED_CHECKLISTS.annual!) assert.ok(annual.sections[2]!.text.includes(line), line.slice(0, 20)); + assert.deepEqual(titles(["timing"]), [CONDENSED_SHARED_READING_TITLE, "Shared mandatory baseline", "时运 · event-timing-strict"]); }); -test("T2: every domain section, in order, and no domain without a list (annual pending the budget)", () => { +// 原值: 标题「…(annual pending the budget)」,annual 期望 "",年运 / 时运跳过 +// 新值: annual / timing 一并核对;annual 期望本产品清单标题 +// 原因: 产品 2026-10-02 字数口径决定(BUG-1160/1161 解除) +test("T2: every domain section, in order, and no domain without a list", () => { const expected: Record = { career: "事业 · career-timing-strict · 普通对话精简清单", marriage: "关系 · relationship-timing-strict · 普通对话精简清单", @@ -82,13 +89,12 @@ test("T2: every domain section, in order, and no domain without a list (annual p family: "家庭 · consult-family · 普通对话精简清单", parents: "父母 · consult-parents · 普通对话精简清单", children: "子女 · consult-children · 普通对话精简清单", - annual: "", + annual: "年运 · consult-annual · 普通对话精简清单", timing: "时运 · event-timing-strict", general: "综合 · consult-general · 普通对话精简清单", }; for (const domain of consultationDomainIds) { const methodology = consultationMethodologyForDomains([domain])!; - if (CONSULT_READING_BUDGET_BLOCKED.has(domain)) continue; assert.deepEqual(methodology.sections.map((section) => section.title), [ CONDENSED_SHARED_READING_TITLE, "Shared mandatory baseline", expected[domain], ], domain); diff --git a/frontend/tests/consult-condensed-checklist-20260927.test.ts b/frontend/tests/consult-condensed-checklist-20260927.test.ts index d6c02709..4ad50d24 100644 --- a/frontend/tests/consult-condensed-checklist-20260927.test.ts +++ b/frontend/tests/consult-condensed-checklist-20260927.test.ts @@ -167,7 +167,10 @@ test("only the consultation tool builds the methodology: reports and other surfa assert.deepEqual(condensedUsers, ["lib/consultation-methodology.ts"]); }); -test("size: every single-domain turn stays within 12,000 characters, contract and checklist included", async () => { +// 原值: 标题「every single-domain turn stays within 12,000 characters, contract and checklist included」;全部 12 个领域 total ≤ 12,000 +// 新值: 年运、时运按事业 / 婚姻 / 财富同口径:契约 + 卡 ≤ 12,000、含清单 total ≤ 18,000;其余领域 total 仍 ≤ 12,000 +// 原因: 产品 2026-10-02 决定(BUG-1160/1161):年运 / 时运单领域轮加通用读法与年运清单后约 12,980,原口径无余量 +test("size: single-domain turns stay within 12,000 characters with the checklist; annual and timing within 12,000 without it and 18,000 with it", async () => { const serverChart = { name: "public", toolInput: { year: 2000, month: 1, day: 1, hour: 0, minute: 0, city: "fixture", lat: 0, lon: 0, tz: 0, ayanamsa: "raman" }, @@ -188,7 +191,13 @@ test("size: every single-domain turn stays within 12,000 characters, contract an const view = await tools["run-jyotish-consultation"].execute!({ question: "q", domains: [domain] } as never, { writer: { custom: async () => {} } } as never); const total = JSON.stringify(view).length; assert.equal(state.modelVisibleChars, total); - assert.ok(total <= 12_000, `${chart.id}/${domain}: ${total}`); + if (domain === "annual" || domain === "timing") { + const contractAndCard = JSON.stringify({ ...(view as Json), methodology: undefined }).length; + assert.ok(contractAndCard <= 12_000, `${chart.id}/${domain}: contract + card ${contractAndCard}`); + assert.ok(total <= 18_000, `${chart.id}/${domain}: ${total}`); + } else { + assert.ok(total <= 12_000, `${chart.id}/${domain}: ${total}`); + } } } }); diff --git a/frontend/tests/consult-evidence-card-20260927.test.ts b/frontend/tests/consult-evidence-card-20260927.test.ts index 0d90863d..0cf89e43 100644 --- a/frontend/tests/consult-evidence-card-20260927.test.ts +++ b/frontend/tests/consult-evidence-card-20260927.test.ts @@ -274,7 +274,10 @@ test("the 「本轮技法」 rows are exactly the rows the full result produced }); test("model-visible size falls to card scale; only the method checklist can push it past 12,000", async () => { - const methodHeavy = new Set(["career", "marriage", "wealth"]); + // 原值: new Set(["career", "marriage", "wealth"]) + // 新值: 加 annual、timing(契约 + 卡 ≤ 12,000,含清单 ≤ 18,000) + // 原因: 产品 2026-10-02 决定年运 / 时运与事业 / 婚姻 / 财富同口径,通用读法与年运清单下发(BUG-1160/1161) + const methodHeavy = new Set(["career", "marriage", "wealth", "annual", "timing"]); for (const chart of golden.charts) { for (const domain of consultationDomainIds) { const { view, state } = await runTool(chart.workflow, [domain]);