151 lines
7.3 KiB
TypeScript
151 lines
7.3 KiB
TypeScript
/**
|
||
* Skill Path-C eight-method verification report for offer/adopt turns.
|
||
*
|
||
* 80%/60% language is event-fit rate for the current window, never a unique
|
||
* birth-minute confirmation. D9/D10 type tables are rectification method, not
|
||
* fate promises.
|
||
*/
|
||
|
||
import type { EventDashaLedgerRow } from "./refinement-packet";
|
||
import type { WindowScan } from "./varga-observations";
|
||
import type { CandidateSeparation } from "../core/candidate-separation";
|
||
import { D9_TYPE_TABLE, D10_TYPE_TABLE, signKey } from "./varga-type-tables";
|
||
|
||
function typeRow(sign: string, table: "d9" | "d10"): string {
|
||
const key = signKey(sign);
|
||
if (table === "d9") {
|
||
const row = D9_TYPE_TABLE[key];
|
||
if (!row) return `| ${key} | (当前窗上升,类型表无这一行) |`;
|
||
return `| ${key} | ${row.trait} | ${row.spouse} | ${row.marriage} |`;
|
||
}
|
||
const row = D10_TYPE_TABLE[key];
|
||
if (!row) return `| ${key} | (当前窗上升,类型表无这一行) |`;
|
||
return `| ${key} | ${row.trait} | ${row.occupation} | ${row.style} |`;
|
||
}
|
||
|
||
export type SkillVerificationReportInput = Readonly<{
|
||
representativeTime: string | null;
|
||
widthMinutes: number | null;
|
||
candidates: readonly Readonly<{ time: string; rank: number; relativeSupport: number }>[];
|
||
houseLagna?: string | null;
|
||
windowScan?: WindowScan | null;
|
||
eventDashaLedger?: readonly EventDashaLedgerRow[];
|
||
eventFitRate?: Readonly<{
|
||
matched: number;
|
||
total: number;
|
||
percent: number | null;
|
||
label: string;
|
||
user_meaning: string;
|
||
unique_minute_claim: false;
|
||
}> | null;
|
||
dashaAgreement?: Readonly<{ status: string; user_meaning: string }> | null;
|
||
techniqueAuditTable?: readonly Readonly<{
|
||
technique?: string;
|
||
status?: string;
|
||
note?: string;
|
||
calculation_result_id?: string;
|
||
}>[];
|
||
separation?: CandidateSeparation;
|
||
engineResultId?: string | null;
|
||
}>;
|
||
|
||
export function buildSkillVerificationReport(input: SkillVerificationReportInput): string {
|
||
const time = input.representativeTime ?? "尚未选定";
|
||
const width = input.widthMinutes == null ? "未知" : `${input.widthMinutes} 分钟`;
|
||
const lagna = input.houseLagna?.trim() || "未交付";
|
||
const fit = input.eventFitRate;
|
||
const ledger = input.eventDashaLedger ?? [];
|
||
const d9Signs = input.windowScan?.d9_sign_names ?? [];
|
||
const d10Signs = input.windowScan?.d10_sign_names ?? [];
|
||
const tied = input.separation?.status === "not_separated";
|
||
const candidateLines = input.candidates.slice(0, 5).map((row) => (
|
||
`| ${row.rank} | ${row.time} | ${row.relativeSupport} |`
|
||
));
|
||
const dashaRows = ledger.length > 0
|
||
? ledger.map((row) => `| ${row.summary} | ${row.match_label} | ${row.user_meaning} |`)
|
||
: ["| (本轮还没有已确认事件行) | — | 先收带日期经历再填这张表 |"];
|
||
const d9Table = d9Signs.length > 0
|
||
? ["| D9上升 | 感情特质 | 配偶类型 | 婚姻特点 |", "|---|---|---|---|", ...d9Signs.map((sign) => typeRow(sign, "d9"))]
|
||
: ["当前窗还没有可对照的 D9 上升名,类型表待候选换升后填写。"];
|
||
const d10Table = d10Signs.length > 0
|
||
? ["| D10上升 | 事业特质 | 职业类型 | 工作风格 |", "|---|---|---|---|", ...d10Signs.map((sign) => typeRow(sign, "d10"))]
|
||
: ["当前窗还没有可对照的 D10 上升名,类型表待候选换升后填写。"];
|
||
const audit = (input.techniqueAuditTable ?? []).slice(0, 16);
|
||
const auditRows = audit.length > 0
|
||
? audit.map((row) => {
|
||
const executed = row.status === "executed" && Boolean(row.calculation_result_id);
|
||
const status = executed ? "executed" : (row.status === "executed" ? "input_covered" : (row.status ?? "blocked"));
|
||
const note = executed
|
||
? (row.note ?? "")
|
||
: (row.note || "该方法所需资料已覆盖,没有可追溯的 calculationResultId,不能写成已执行。");
|
||
return `| ${row.technique ?? "技法"} | ${status} | ${note} |`;
|
||
})
|
||
: [
|
||
"| Vimshottari + 受控行运 | input_covered | 没有 calculationResultId 时不能写成已执行 |",
|
||
"| D9/D10 分盘 | input_covered | 类型表是校时方法;差异要进入区分探针 |",
|
||
"| 宫位 / 六亲六步 | input_covered | 后三步方法论层 |",
|
||
"| Nakshatra Pada | observation_only | 不确认唯一分钟 |",
|
||
"| KP / D60 / Gulika | observation_only | 不进提出门或确认门计分 |",
|
||
"| 占问 | observation_only | AI 暂不支持独立占问;有问起时间则观察 |",
|
||
];
|
||
|
||
return [
|
||
"## 生时纠正验证报告(skill 八法)",
|
||
"这是当前窗的相对拟合,标签是 `candidate_range_not_birth_time_truth`。采用只会把代表性时间写入当前排盘,不等于确认唯一出生分钟。",
|
||
"",
|
||
"### 筛选",
|
||
tied
|
||
? `- 当前候选基本并列:${(input.separation?.credibleRange ?? []).join("、") || "尚未拉开"}。${time} 只是后续计算的代表点,不是赢家。`
|
||
: `- 当前代表分钟:${time}`,
|
||
`- 不可分宽度:${width}`,
|
||
`- 本命上升(该分钟):${lagna}`,
|
||
input.windowScan?.d9_candidates_differ
|
||
? "- D9 仍会换升。这是候选结构差异,应生成区分探针,不能直接宣布不可分。"
|
||
: "- D9 上升在当前窗较稳。",
|
||
input.windowScan?.d10_candidates_differ
|
||
? "- D10 仍会换升。这是候选结构差异,应生成区分探针,不能直接宣布不可分。"
|
||
: "- D10 上升在当前窗较稳。",
|
||
"",
|
||
"### 候选窗(正文复述;卡片仍作采用控件)",
|
||
"| 排名 | 时间 | 相对支持 |",
|
||
"|---|---|---|",
|
||
...(candidateLines.length > 0 ? candidateLines : ["| — | — | 还没有可出示的候选 |"]),
|
||
"",
|
||
"### 方法1:Dasha + Gochara",
|
||
fit
|
||
? `${fit.user_meaning} ${fit.label}。这是事件拟合程度,不是候选区分程度。`
|
||
: "还没有事件吻合率。有已确认事件后,80%/60% 只描述事件吻合率,不得写成已确认唯一出生分钟,也不得写成候选已经分开。",
|
||
"",
|
||
"| 事件 | 吻合 | Dasha / Gochara |",
|
||
"|---|---|---|",
|
||
...dashaRows,
|
||
input.dashaAgreement ? `双轨:${input.dashaAgreement.user_meaning}` : "Narayana 对照未交付时只保留 Vimshottari。",
|
||
"",
|
||
"### 方法2–3:D9 / D10 类型表",
|
||
"写的是「该分钟下分盘升 X,与用户所述特质的对应/冲突」。这是校时方法,不是咨询命运承诺。",
|
||
...d9Table,
|
||
"",
|
||
...d10Table,
|
||
"",
|
||
"### 方法4:六亲宫位(Raman 六步)",
|
||
"前三步(宫、宫主、自然卡拉卡)可执行;后三步(日月重复、D9、正负多数)标方法论层。家人事件对照 D12 / D7 / D3。",
|
||
"",
|
||
"### 方法5–6:外貌 / 疤痕",
|
||
"只作体质验证、缩小范围,不升主评分,不定分钟。",
|
||
"",
|
||
"### 方法7:职业",
|
||
"对照 D1 第 10 宫 + D10,允许事业类型表。",
|
||
"",
|
||
"### 方法8:占问",
|
||
"状态:`not_used` / `observation_only`。产品可观察一次问起时间,不挡出牌。",
|
||
"",
|
||
"### Technique Audit Table",
|
||
"权重口径:Dasha 40 / 分盘 35 / 宫位 15 / Pada 10。KP、D60、Gulika 观察或 blocked。只有带 calculationResultId 的行才能写 executed。",
|
||
"| 技法 | 状态 | 说明 |",
|
||
"|---|---|---|",
|
||
...auditRows,
|
||
"",
|
||
"不得把 VedAstro/holdout 或高吻合写成已校到唯一分钟。",
|
||
].join("\n");
|
||
}
|