Compare commits

...
Author SHA1 Message Date
jesse-uxandGrok (agent loop) 09a67aad5a fix(consult): 解读依据的格局行按卡上状态说话,不再写成成立 (#181, BUG-1824)
- 新增 yoga-status-words.ts:conditions_not_met → 不成立;candidate → 候选;
  computed → 未确认;其他 → 未标状态;结构观察不进这一行
- yogaLine 逐个名字到 card.base.natal.yogas_detail.rows 取状态词并按状态分组
  (候选:A、B;未确认:C),没有可列的写「本题没有检测到相关格局」
- 「格局」兜底文案改为「盘上检测到的格局已列出各自状态」(三栏注释)
- 数据卡、回执 schema、行状态判定、页面组件都不动

Tests: 新增 consult-basis-candidate-status-1824.test.ts(8 条);
consult-technique-evidence-1793 / consult-answer-basis-receipt-20261108 /
consult-answer-mode-ui-20261108 继续通过;tsc 0 错,lint 0 error。

Co-Authored-By: Grok (agent loop)
2026-11-09 14:33:47 +08:00
ClaudeandClaude Opus 5.5 21b1fe1aae docs(task): brief for basis-candidate-status (2026-11-09)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DbBXoyn7Hj3LCW3BUs5bZ8
2026-11-09 14:03:27 +08:00
7 changed files with 340 additions and 3 deletions
+14
View File
@@ -24518,3 +24518,17 @@
- 防复发:上述测试;合同 docstring 写明后续 #174–#178 的注册步骤。
- 关联记录:占星师 2026-11-07 Q9、2026-11-08 第五轮第 9 条;FIX-ondemand-transit-date-1(产品拍板 C 方案)
- 修复版本:分支 codex/ondemand-transit-date-20261109
## BUG-1824 | 「解读依据」把候选格局显示成「成立」
- 状态:fixed-pending-verify(分支 codex/basis-candidate-status-20261109)
- 首次发现 / 最近更新:2026-11-09(第七轮验收包,页面阶段)/ 2026-11-09(修复)
- 影响面:frontend/src/lib/consultation-technique-evidence.ts 的 yogaLine;frontend/src/lib/consult-answer-basis.ts 的「格局」兜底文案;新增 frontend/src/lib/yoga-status-words.ts。
- 现象:简明解读的「解读依据」里,「格局」一行写「成立:Sunaphaa Yoga、Vosi Yoga」,而卡上这些格局的状态是 candidate / computed,卡上的规则写明 none is confirmed。
- 触发条件:任何带数据卡的简明解读回答。
- 根因:yogaLine 只读领域段的 yogas.hits(检测器匹配到的规则名,BUG-1157),一律写成「成立:」;真正的状态在 card.base.natal.yogas_detail.rows[] 的 status / formation.status 里,从来没被这一行读过。
- 修复:新增 yoga-status-words.ts(conditions_not_met → 不成立;candidate → 候选;computed → 未确认;其他 → 未标状态;结构观察不进这一行);yogaLine 改为按名字到 yogas_detail.rows 取状态词并按状态分组(候选:A、B;未确认:C),没有可列的写「本题没有检测到相关格局」;兜底文案由「盘上成立和落空的格局已核对」改为「盘上检测到的格局已列出各自状态」。行状态(已算 / 有条件)与数据卡事实记录不动。
- 验证:frontend/tests/consult-basis-candidate-status-1824.test.ts(8 条:三种状态词、formed_under_source 不写成成立、结构观察不出现、未标状态、灰字与兜底都不含「成立:」和「已确认」、计算状态与解读状态分开、空 hits 文案、词表本身不提供「成立」);consult-technique-evidence-1793(2)、consult-answer-basis-receipt-20261108(4)、consult-answer-mode-ui-20261108(14)继续通过。
- 防复发:上述测试;词表单独成文件,后续 #182 复用同一套词。
- 关联记录:BUG-1793、BUG-1157、#182
- 修复版本:分支 codex/basis-candidate-status-20261109
@@ -0,0 +1,45 @@
# PROGRESS · 「解读依据」候选格局不再写成「成立」(#181,BUG-1824,2026-11-09)
- 分支:`codex/basis-candidate-status-20261109`(基线 `afac4ea9`)
- 任务书:`docs/tasks/TASK-basis-candidate-status-20261109.md`
- Bug:`docs/BUG_HISTORY.md` 末尾追加 BUG-1824
## 改了什么
| 文件 | 内容 |
| --- | --- |
| `frontend/src/lib/yoga-status-words.ts`(新) | `YOGA_STATUS_WORDS` 与 `yogaStatusWord(row)`:`formation.status = conditions_not_met` → 不成立;`status = candidate` → 候选;`status = computed` → 未确认;其他 → 未标状态。另导出 `isStructuralObservation(row)` |
| `frontend/src/lib/consultation-technique-evidence.ts` | 只改 `yogaLine` 与一行 import:仍取领域段 `yogas.hits` 的前 3 个名字,逐个到 `card.base.natal.yogas_detail.rows` 按 `name` 找状态词;结构观察跳过;按状态词分组(组按首次出现排、组内保持原顺序);找不到行 → 未标状态;没有可列的 → 「本题没有检测到相关格局」 |
| `frontend/src/lib/consult-answer-basis.ts` | 「格局」的兜底文案改为「盘上检测到的格局已列出各自状态」,改动处写三栏注释(原因:BUG-1824:候选不得显示为成立) |
没动:数据卡(`yogas.hits` / `yogas_detail` 事实记录一字未改)、回执 schema、`display_status` 取值、页面组件、答题提示词、`references/**`、`scripts/**`。
## 状态词表
| 卡上的值 | 显示 |
| --- | --- |
| `formation.status = "conditions_not_met"` | 不成立 |
| `status = "candidate"` | 候选 |
| `status = "computed"` | 未确认 |
| 其他 / 缺失 | 未标状态 |
`formed_under_source` 不单独显示为「成立」——卡上规则是 `none is confirmed`,按 `status` 显示。
词表里没有「成立」「已确认」这两个词(「不成立」是否定式,是任务书要求的)。
## 自测
| 命令 | 结果 |
| --- | --- |
| `node ... --test tests/consult-basis-candidate-status-1824.test.ts` | 8 过 / 0 败 |
| `tests/consult-technique-evidence-1793.test.ts` | 2 过 / 0 败 |
| `tests/consult-answer-basis-receipt-20261108.test.tsx` | 4 过 / 0 败 |
| `tests/consult-answer-mode-ui-20261108.test.tsx` | 14 过 / 0 败 |
| `tsc --noEmit -p frontend` | 0 错 |
| `npm run lint` | 0 error |
新测试覆盖任务书的 7 条:三种状态词各对各;`formed_under_source` 显示候选、不写成立;结构观察不出现;卡上没有对应行的名字 → 未标状态;灰字与兜底都不含「成立:」「已确认」;`consultAnswerBasisRows` 走一遍时「格局」行 `status === "done"` 且 `result` 是分组灰字(计算状态与解读状态分开);空 `hits` → 「本题没有检测到相关格局」。既有断言没有改动,所以没有三栏注释。
## 说明
- 「格局」行要显示成 `已算`,测试里的审计行用 `interpretation_status: "supporting"`(引擎把 yoga 行标为 observation/supporting 时 `display_status` 才是 firm)。这是既有判定,本单没有改。
- 页面组件 `consult-answer-basis.tsx` 不用改:它只渲染 `result` 与状态标签。
@@ -0,0 +1,106 @@
# TASK:「解读依据」把候选格局显示成“成立”(#181,BUG-1824,2026-11-09)
- Gitea:#181
- 分支:`codex/basis-candidate-status-20261109`(基于 staging `afac4ea92f293a4d2bc81f62563487d26f92e4d2`)
- Bug 编号:BUG-1824(开工时核对 `docs/BUG_HISTORY.md` 的最大编号;1809–1830 已为这一批预留)
- 负责验收:Claude(jyotisha-2a)
- 范围:只改「解读依据」里「格局」这一行的灰字和兜底文案。
不得自行写“产品决定/裁定”;任务书没写的取舍一律选需拍板。
## 现象(第七轮验收包,页面阶段)
简明解读下的「解读依据」里,「格局」一行显示「成立:Sunaphaa Yoga、Vosi Yoga」「成立:Dharidhra Yoga…」。数据卡上这些格局的状态都是 `candidate`(或 `computed`),卡上的规则写明 `none is confirmed`。列表把“计算已执行”说成了“解读成立”。
## 根因(afac4ea9 实测,行号以符号为准)
- `frontend/src/lib/consultation-technique-evidence.ts` 的 `yogaLine(card)` 只读领域段的 `yogas.hits`(名字列表),一律写成 `成立:<名字>`;没有名字时写 `本题没有成立的格局`。
- `hits` 是“检测器在本盘匹配到的规则名”(`consultation-evidence-card.ts` 约 :2401,BUG-1157),不是“成立”。
- 每个格局真正的状态在同一张卡的 `card.base.natal.yogas_detail.rows[]` 里(`consultation-evidence-card.ts` 约 :2007–2028 从引擎层 `yoga_conditions` 原样拷贝):
- `status`:`candidate` | `computed` | `structural_observation`(引擎 `scripts/consultation_native_layers.py` 的 `status_rule`);
- `formation.status`:`formed_under_source` | `candidate` | `conditions_not_met`(`formation_rule`);
- `claim_kind`:`structural_observation` 时不是命名格局。
- `frontend/src/lib/consult-answer-basis.ts` 的 `BASIS_ENTRIES` 里「格局」的兜底文案是 `盘上成立和落空的格局已核对`,同样说过头。
- 行状态(`已算` / `有条件`)来自 `display_status` / 两轴状态,是**计算**状态;这部分是对的,不改。
## 要做的
### 1. 新建状态词表 `frontend/src/lib/yoga-status-words.ts`
一个导出的常量和一个函数,供本单使用(#182 以后也会复用,所以单独成文件):
| 卡上的值 | 列表显示 |
| --- | --- |
| `formation.status = "conditions_not_met"` | 不成立 |
| `status = "candidate"` | 候选 |
| `status = "computed"` | 未确认 |
| 其他 / 缺失 | 未标状态 |
- 判定顺序:先看 `formation.status === "conditions_not_met"`,否则看 `status`。
- `formed_under_source` **不**单独显示为“成立”:卡上规则是 `none is confirmed`,照 `status` 显示(候选 / 未确认)。
- 函数签名建议:`yogaStatusWord(row: Record<string, unknown>): "不成立" | "候选" | "未确认" | "未标状态"`。
- 词表里**不得**出现“成立”“已确认”这类词。
### 2. 改 `yogaLine(card)`(`consultation-technique-evidence.ts`)
- 仍取领域段 `yogas.hits` 的名字(顺序不变,最多 3 个)。
- 每个名字到 `card.base.natal.yogas_detail.rows` 里按 `name` 找对应行,用 `yogaStatusWord` 得到状态词。
- `claim_kind === "structural_observation"` 或 `status === "structural_observation"` 的名字不进这一行(与 `annual-report-sections.ts` 把结构观察单列、不进「格局」的做法一致)。
- 找不到对应行的名字显示“未标状态”。
- 按状态词分组,组内保持原顺序,组按第一次出现的顺序排:`候选:A、B;未确认:C`。
- 没有可列的名字时写 `本题没有检测到相关格局`。
- `LINES` 表、其他技法的灰字、`techniqueDisplayStatus`、`withTechniqueEvidence` 的结构都不改。
### 3. 改兜底文案(`consult-answer-basis.ts`)
`BASIS_ENTRIES` 里「格局」的 `result` 由 `盘上成立和落空的格局已核对` 改为 `盘上检测到的格局已列出各自状态`。改动处写“原值 / 新值 / 原因”三栏注释,原因写 `BUG-1824:候选不得显示为成立`。文件头注释里对应描述同步改一句。其余条目不改。
### 4. 测试 `frontend/tests/consult-basis-candidate-status-1824.test.ts`
用合成卡(不写真人资料),至少覆盖:
1. 卡上状态分别为 `candidate`、`computed`、`formation.status = conditions_not_met` 的三个格局,`yogaLine` 产出 `候选:…;未确认:…;不成立:…`,三个词各对各;
2. `formed_under_source` + `status = candidate` 显示“候选”;
3. 结构观察不出现在这一行;
4. 卡上没有对应行的名字显示“未标状态”;
5. 生成的灰字和兜底文案里都不含“成立:”这个子串,也不含“已确认”;
6. 经 `consultAnswerBasisRows` 走一遍:「格局」行的 `status` 仍是 `done`(已算),`result` 是第 1 条的灰字——计算状态和解读状态分开;
7. 空 `hits` 时灰字为 `本题没有检测到相关格局`。
既有测试 `frontend/tests/consult-technique-evidence-1793.test.ts`、`consult-answer-basis-receipt-20261108.test.tsx`、`consult-answer-mode-ui-20261108.test.tsx` 必须照样通过;若其中有断言写了旧文案,改时写三栏注释,不得删断言。
### 5. 记录
- `docs/BUG_HISTORY.md` 末尾追加 BUG-1824(状态、现象、触发条件、根因、修复、验证、防复发、关联 BUG-1793 / BUG-1157 / #182)。只在末尾追加,不用 sed 改整个文件。
- `docs/tasks/PROGRESS-basis-candidate-status-20261109.md`。
## 允许改的文件(只有这些)
- 新建:`frontend/src/lib/yoga-status-words.ts`、`frontend/tests/consult-basis-candidate-status-1824.test.ts`、`docs/tasks/PROGRESS-basis-candidate-status-20261109.md`
- 修改:`frontend/src/lib/consultation-technique-evidence.ts`(只改 `yogaLine` 和必要的 import)、`frontend/src/lib/consult-answer-basis.ts`(只改「格局」兜底文案与头注释一句)、`docs/BUG_HISTORY.md`(末尾追加)
- 页面组件 `frontend/src/components/consult-answer-basis.tsx` 预计不用改;若确实要改,停下选“需拍板”。
## 不要碰
- `references/**`(占星师方法文件),一字不改;包括 `references/product_runtime_contract.md`。
- `frontend/src/lib/consult-preset-prompts.ts`、`frontend/src/lib/consultation-methodology.ts`、`frontend/src/mastra/product-voice.ts`:不改答题规则。
- `frontend/src/lib/consultation-evidence-card.ts`:数据卡的事实记录一字不改(`yogas.hits`、`yogas_detail` 的字段和内容都不动;“把状态词写进卡和产品规则”归 #182)。该文件也是 #170 / #172 的范围。
- `frontend/src/lib/consultation-answer-fact-guard.ts`:拦截检查归 #182 / #183。
- `frontend/src/lib/consultation-agent-events.ts`(回执 schema,`display_status` 的取值不改)、`frontend/src/app/api/consult/route.ts`。
- `frontend/src/mastra/consultation-tools.ts`(#171)、`frontend/src/mastra/consultation-workflow.ts` 与 `frontend/src/lib/consultation-route-service.ts`(#185)。
- `scripts/**` 全部,特别是冻结文件:`scripts/jyotish_api_server.py`、`scripts/jyotish_engine.py`、`scripts/domain_calculation_service.py`、`scripts/dasha_analyzer.py`、`scripts/narayana_dasha.py`、`scripts/shadbala.py`、`scripts/ashtakavarga.py`、`scripts/jaimini.py`、`scripts/varga.py`、`scripts/functional_benefics.py`、`scripts/divisional_charts_extended.py`;以及 `scripts/consultation_native_layers.py`(引擎的状态值不改)。
- 既有断言不得弱化。
## 自测命令(Windows / PowerShell)
- `frontend\node_modules\.bin\tsc --noEmit -p frontend`
- 在 `frontend` 下运行 `npm run lint`
- 在 `frontend` 下运行 `node node_modules/tsx/dist/cli.mjs --test tests/consult-basis-candidate-status-1824.test.ts tests/consult-technique-evidence-1793.test.ts tests/consult-answer-basis-receipt-20261108.test.tsx tests/consult-answer-mode-ui-20261108.test.tsx`
前端全量测试、Python 快速门和 eslint 由 Claude 在验收时跑。
## 何时停下来选“需拍板”
- 卡上出现上表以外的状态值,且不能归入“未标状态”时。
- 要改数据卡、回执 schema、答题规则或页面组件才能做到时。
- 任务书没写到的任何文案或取舍。
+4 -1
View File
@@ -55,7 +55,10 @@ const BASIS_ENTRIES: readonly BasisEntry[] = [
name: "功能吉凶",
result: (profile) => `按 ${FUNCTIONAL_PROFILE_LABELS[profile]} 判断每颗星在这张盘里起的作用`,
},
{ match: /^Yogas?\b|^格局/i, name: "格局", result: () => "盘上成立和落空的格局已核对" },
// 原值:`盘上成立和落空的格局已核对`
// 新值:`盘上检测到的格局已列出各自状态`
// 原因:BUG-1824:候选不得显示为成立(卡上规则是 none is confirmed)
{ match: /^Yogas?\b|^格局/i, name: "格局", result: () => "盘上检测到的格局已列出各自状态" },
{ match: /^Shadbala\b/i, name: "行星力量", result: () => "每颗星的强弱已算" },
{ match: /^Ashtakavarga$/i, name: "宫位点数", result: () => "每个宫得到的点数已算" },
{ match: /^Arudha\b/i, name: "Arudha 点位", result: () => "事业、婚姻相关的点位落在哪里" },
@@ -11,6 +11,7 @@
*/
import type { TechniqueAuditRow } from "./consultation-agent-events.ts";
import type { EvidenceCard } from "./consultation-evidence-card.ts";
import { isStructuralObservation, yogaStatusWord, YOGA_STATUS_WORDS } from "./yoga-status-words.ts";
type Rec = Record<string, unknown>;
const rec = (value: unknown): Rec => (value && typeof value === "object" && !Array.isArray(value) ? value as Rec : {});
@@ -136,11 +137,31 @@ function savLine(card: EvidenceCard) {
});
}
// 原值:`成立:${hits}` / `本题没有成立的格局`
// 新值:按卡上 yogas_detail 的状态分组写「候选 / 未确认 / 不成立」,没有可列的写「本题没有检测到相关格局」
// 原因:BUG-1824:候选不得显示为成立(卡上规则是 none is confirmed)
function yogaLine(card: EvidenceCard) {
const detail = rec(rec(rec(card.base).natal).yogas_detail);
const rows = Array.isArray(detail.rows) ? detail.rows.map(rec) : [];
const byName = new Map<string, Rec>();
for (const row of rows) {
const name = str(row.name);
if (name) byName.set(name, row);
}
return firstOf(card, (section) => {
if (!("yogas" in section)) return undefined;
const hits = list(rec(section.yogas).hits);
return hits.length ? `成立:${hits.slice(0, 3).join("、")}` : "本题没有成立的格局";
const hits = list(rec(section.yogas).hits).slice(0, 3);
const groups = new Map<string, string[]>();
for (const name of hits) {
const row = byName.get(name);
if (row && isStructuralObservation(row)) continue;
const word = row ? yogaStatusWord(row) : YOGA_STATUS_WORDS.unknown;
const bucket = groups.get(word) ?? [];
bucket.push(name);
groups.set(word, bucket);
}
if (!groups.size) return "本题没有检测到相关格局";
return [...groups.entries()].map(([word, names]) => `${word}:${names.join("、")}`).join(";");
});
}
+39
View File
@@ -0,0 +1,39 @@
/**
* BUG-1824:「解读依据」的「格局」一行以前把「检测器匹配到的规则名」写成「成立」。
* 卡上的规则是 `none is confirmed`,所以这一行只能照卡上的状态说话:
*
* | 卡上的值 | 显示 |
* | --- | --- |
* | `formation.status = "conditions_not_met"` | 不成立 |
* | `status = "candidate"` | 候选 |
* | `status = "computed"` | 未确认 |
* | 其他 / 缺失 | 未标状态 |
*
* `formed_under_source` 不单独显示为“成立”(卡上规则是 none is confirmed)。
* 词表里刻意不含“成立”“已确认”这两个词。
*/
export const YOGA_STATUS_WORDS = {
notFormed: "不成立",
candidate: "候选",
computed: "未确认",
unknown: "未标状态",
} as const;
export type YogaStatusWord = typeof YOGA_STATUS_WORDS[keyof typeof YOGA_STATUS_WORDS];
type Rec = Record<string, unknown>;
const rec = (value: unknown): Rec => (value && typeof value === "object" && !Array.isArray(value) ? value as Rec : {});
/** 一个格局行(`card.base.natal.yogas_detail.rows[]`)的解读状态词。 */
export function yogaStatusWord(row: Rec): YogaStatusWord {
const formation = rec(row.formation);
if (formation.status === "conditions_not_met") return YOGA_STATUS_WORDS.notFormed;
if (row.status === "candidate") return YOGA_STATUS_WORDS.candidate;
if (row.status === "computed") return YOGA_STATUS_WORDS.computed;
return YOGA_STATUS_WORDS.unknown;
}
/** 卡上这一行是不是“结构观察”(不是命名格局),不进「格局」这一行。 */
export function isStructuralObservation(row: Rec): boolean {
return row.claim_kind === "structural_observation" || row.status === "structural_observation";
}
@@ -0,0 +1,109 @@
/**
* BUG-1824:「解读依据」的「格局」一行不得把候选格局写成「成立」。
* 合成卡,不含任何真人资料。
*/
import assert from "node:assert/strict";
import test from "node:test";
import type { TechniqueAuditRow } from "../src/lib/consultation-agent-events.ts";
import { consultAnswerBasisRows } from "../src/lib/consult-answer-basis.ts";
import type { EvidenceCard } from "../src/lib/consultation-evidence-card.ts";
import { withTechniqueEvidence } from "../src/lib/consultation-technique-evidence.ts";
import { yogaStatusWord, YOGA_STATUS_WORDS } from "../src/lib/yoga-status-words.ts";
type Row = Record<string, unknown>;
function cardWith(rows: Row[], hits: string[]): EvidenceCard {
return {
domains: ["career"],
domain_sections: { career: { yogas: { hits } } },
base: { natal: { yogas_detail: { rows } } },
} as unknown as EvidenceCard;
}
function yogaResult(card: EvidenceCard): string | undefined {
const audit: TechniqueAuditRow[] = [{
technique: "Yogas",
calculation_status: "executed",
interpretation_status: "observation_only",
} as unknown as TechniqueAuditRow];
const enriched = withTechniqueEvidence(audit, card) ?? [];
const rows = consultAnswerBasisRows(enriched);
return rows.find((row) => row.name === "格局")?.result;
}
test("candidate, computed and not-formed rows each show their own status word", () => {
const card = cardWith([
{ name: "Sunaphaa Yoga", status: "candidate" },
{ name: "Vosi Yoga", status: "computed" },
{ name: "Dharidhra Yoga", status: "candidate", formation: { status: "conditions_not_met" } },
], ["Sunaphaa Yoga", "Vosi Yoga", "Dharidhra Yoga"]);
assert.equal(yogaResult(card), "候选:Sunaphaa Yoga;未确认:Vosi Yoga;不成立:Dharidhra Yoga");
});
test("formed_under_source still shows the status word, never 成立", () => {
const card = cardWith([
{ name: "Amala Yoga", status: "candidate", formation: { status: "formed_under_source" } },
], ["Amala Yoga"]);
const line = yogaResult(card) ?? "";
assert.equal(line, "候选:Amala Yoga");
assert.doesNotMatch(line, /成立/u);
});
test("structural observations stay out of the 格局 row", () => {
const card = cardWith([
{ name: "Neecha Bhanga witness", status: "structural_observation" },
{ name: "Sunaphaa Yoga", status: "candidate" },
], ["Neecha Bhanga witness", "Sunaphaa Yoga"]);
assert.equal(yogaResult(card), "候选:Sunaphaa Yoga");
});
test("a hit without a card row is 未标状态", () => {
const card = cardWith([{ name: "Sunaphaa Yoga", status: "candidate" }], ["Sunaphaa Yoga", "Unknown Yoga"]);
assert.equal(yogaResult(card), "候选:Sunaphaa Yoga;未标状态:Unknown Yoga");
});
test("neither the grey line nor the fallback copy claims 成立 or 已确认", () => {
const card = cardWith([
{ name: "Sunaphaa Yoga", status: "candidate" },
{ name: "Vosi Yoga", status: "computed" },
], ["Sunaphaa Yoga", "Vosi Yoga"]);
const line = yogaResult(card) ?? "";
const fallback = consultAnswerBasisRows([{
technique: "Yogas",
calculation_status: "executed",
} as unknown as TechniqueAuditRow]).find((row) => row.name === "格局")?.result ?? "";
for (const text of [line, fallback]) {
assert.doesNotMatch(text, /成立:/u);
assert.doesNotMatch(text, /已确认/u);
}
assert.equal(fallback, "盘上检测到的格局已列出各自状态");
});
test("the 格局 row keeps its calculation status while the grey line carries the reading status", () => {
const card = cardWith([{ name: "Sunaphaa Yoga", status: "candidate" }], ["Sunaphaa Yoga"]);
const enriched = withTechniqueEvidence([{
technique: "Yogas",
calculation_status: "executed",
interpretation_status: "supporting",
} as unknown as TechniqueAuditRow], card) ?? [];
const row = consultAnswerBasisRows(enriched).find((item) => item.name === "格局");
assert.ok(row);
assert.equal(row.status, "done");
assert.equal(row.result, "候选:Sunaphaa Yoga");
});
test("no hits at all says nothing was detected, not that nothing formed", () => {
const card = cardWith([{ name: "Sunaphaa Yoga", status: "candidate" }], []);
assert.equal(yogaResult(card), "本题没有检测到相关格局");
});
test("the status vocabulary never offers a word that claims confirmation", () => {
for (const word of Object.values(YOGA_STATUS_WORDS)) {
// 「不成立」是允许的否定式;不允许的是把候选说成「成立」或「已确认」。
assert.notEqual(word, "成立");
assert.notEqual(word, "已确认");
assert.equal(word.startsWith("成立"), false, word);
}
assert.equal(yogaStatusWord({}), YOGA_STATUS_WORDS.unknown);
});