feat(consult): career list reads field and public/behind-the-scenes from chart evidence (BUG-1176); opener keeps the affliction rule (BUG-1177)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c74bfcc06b
commit
dd817c8eb8
@@ -59,7 +59,10 @@ test("T1: the shared reading is the first section, once per turn, whatever the p
|
||||
assert.match(text, /逆行不等于「反复、打回来」/);
|
||||
// The line added on top of the brief's six: only hits are yogas this chart has.
|
||||
assert.match(text, /瑜伽只认 yogas\.hits/);
|
||||
assert.equal(CONDENSED_SHARED_READING_LINES.length, 7);
|
||||
// 原值: 7 行
|
||||
// 新值: 8 行(第 8 行:traditional_yogas 只说明力量,不单独下结论,不算受冲也不抵消受冲)
|
||||
// 原因: TASK-consult-career-yoga-functional-20261002 T3 / D3 定稿,通用读法加一行(BUG-1175)
|
||||
assert.equal(CONDENSED_SHARED_READING_LINES.length, 8);
|
||||
});
|
||||
|
||||
// 原值: test "T1/T2 budget blocker is pinned: a single annual or timing turn keeps its pre-brief method"
|
||||
@@ -126,9 +129,13 @@ test("T3: career, marriage and wealth each carry their affliction line", () => {
|
||||
assert.ok(CONSULTATION_CONDENSED_CHECKLISTS.marriage!.some((line) => line.includes("感情这条线上有波折的迹象") && line.includes("不断定有几段") && line.includes("不预设用户现在有伴侣或单身")));
|
||||
assert.ok(!CONSULTATION_CONDENSED_CHECKLISTS.marriage!.join("\n").includes("可能不止一段"));
|
||||
assert.ok(CONSULTATION_CONDENSED_CHECKLISTS.wealth!.some((line) => line.startsWith("8 宫也管版税、继承、意外之财")));
|
||||
// 原值: career / marriage / wealth 都是 5–8 行
|
||||
// 新值: marriage / wealth 仍 5–8 行;career 5–9 行
|
||||
// 原因: TASK-consult-career-yoga-functional-20261002 T4 定稿给事业清单加两行(行业、公众 / 幕后的依据,BUG-1176),定稿文本不得改意思
|
||||
for (const domain of ["career", "marriage", "wealth"] as const) {
|
||||
const lines = CONSULTATION_CONDENSED_CHECKLISTS[domain]!;
|
||||
assert.ok(lines.length >= 5 && lines.length <= 8, domain);
|
||||
const max = domain === "career" ? 9 : 8;
|
||||
assert.ok(lines.length >= 5 && lines.length <= max, domain);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
// TASK-consult-career-yoga-functional-20261002 T3–T5 (BUG-1175~1177): the
|
||||
// closed-list traditional yogas reach the general / career / wealth cards with
|
||||
// a fixed label, the shared reading says how to use them, the career list
|
||||
// reads field and public/behind-the-scenes from chart evidence, and the opener
|
||||
// keeps the affliction rule. Expected yoga names are the real engine's own
|
||||
// layer in the golden (scripts/research/capture_consult_evidence_card_golden.py).
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
buildEvidenceCard,
|
||||
EVIDENCE_CARD_LABELS,
|
||||
EVIDENCE_CARD_SPECS,
|
||||
} from "../src/lib/consultation-evidence-card.ts";
|
||||
import type { ConsultationDomain } from "../src/lib/consultation-domain-registry.ts";
|
||||
import { createConsultationPlan } from "../src/lib/consultation-plan.ts";
|
||||
import {
|
||||
CONDENSED_SHARED_READING_LINES,
|
||||
CONSULTATION_CONDENSED_CHECKLISTS,
|
||||
} from "../src/lib/consultation-condensed-checklist.ts";
|
||||
import { natalAnswerShapeBody } from "../src/lib/consultation-thinking-plan.ts";
|
||||
import {
|
||||
consultationWorkflowResponseSchema,
|
||||
toAgentConsultationContext,
|
||||
toModelOutput,
|
||||
} from "../src/mastra/consultation-workflow.ts";
|
||||
|
||||
type Json = Record<string, unknown>;
|
||||
type GoldenChart = { id: string; workflow: Json; annual_workflow: Json; timing_workflow: Json };
|
||||
const golden = JSON.parse(readFileSync(
|
||||
new URL("./fixtures/consult-evidence-card-golden.json", import.meta.url),
|
||||
"utf8",
|
||||
)) as { charts: GoldenChart[] };
|
||||
const chartById = (id: string) => golden.charts.find((chart) => chart.id === id)!;
|
||||
|
||||
function card(workflow: Json, domain: ConsultationDomain) {
|
||||
const context = toAgentConsultationContext(consultationWorkflowResponseSchema.parse(structuredClone(workflow)));
|
||||
const packet = toModelOutput(context, createConsultationPlan({ userIntent: "q", theme: domain }), domain);
|
||||
return buildEvidenceCard([{ domain, packet, context }]);
|
||||
}
|
||||
|
||||
function engineNames(workflow: Json): string[] {
|
||||
const layers = (workflow.chart as Json).consultation_native_layers as Json;
|
||||
return ((layers.traditional_yogas as Json).names as string[]);
|
||||
}
|
||||
|
||||
test("traditional yogas: fixed label, engine names, general / career / wealth only", () => {
|
||||
assert.equal(EVIDENCE_CARD_LABELS.traditionalYogas, "传统格局,本站未验证,只作参考");
|
||||
const withYogas = (Object.keys(EVIDENCE_CARD_SPECS) as ConsultationDomain[])
|
||||
.filter((domain) => EVIDENCE_CARD_SPECS[domain].traditionalYogas);
|
||||
assert.deepEqual(withYogas.sort(), ["career", "general", "wealth"]);
|
||||
for (const chart of golden.charts) {
|
||||
const names = engineNames(chart.workflow);
|
||||
for (const domain of ["general", "career", "wealth"] as const) {
|
||||
const section = card(chart.workflow, domain).domain_sections[domain]!;
|
||||
if (names.length) assert.deepEqual(section.traditional_yogas, { label: EVIDENCE_CARD_LABELS.traditionalYogas, names }, `${chart.id}/${domain}`);
|
||||
else assert.equal("traditional_yogas" in section, false, `${chart.id}/${domain}`);
|
||||
}
|
||||
assert.equal("traditional_yogas" in card(chart.workflow, "parents").domain_sections.parents!, false);
|
||||
}
|
||||
});
|
||||
|
||||
test("Obama carries Sasa and Neecha Bhanga Raja; Jobs and Taylor carry Gajakesari (engine names)", () => {
|
||||
const general = (id: string) => (card(chartById(id).workflow, "general").domain_sections.general!.traditional_yogas as Json).names as string[];
|
||||
assert.ok(general("barack_obama").includes("Sasa Yoga"));
|
||||
assert.ok(general("barack_obama").includes("Neecha Bhanga Raja Yoga"));
|
||||
assert.ok(general("steve_jobs").includes("Gajakesari Yoga"));
|
||||
assert.ok(general("elizabeth_taylor").includes("Gajakesari Yoga"));
|
||||
});
|
||||
|
||||
test("no formed closed-list yoga: the field is absent", () => {
|
||||
const workflow = structuredClone(chartById("barack_obama").workflow);
|
||||
((workflow.chart as Json).consultation_native_layers as Json).traditional_yogas = { status: "executed", names: [] };
|
||||
assert.equal("traditional_yogas" in card(workflow, "general").domain_sections.general!, false);
|
||||
});
|
||||
|
||||
test("shared reading: traditional yogas only show strength", () => {
|
||||
assert.ok(CONDENSED_SHARED_READING_LINES.includes("`traditional_yogas` 只用来说明力量,不单独下结论,不算受冲,也不抵消受冲。"));
|
||||
});
|
||||
|
||||
test("career list: field by planet nature, public only with chart evidence (T4 text)", () => {
|
||||
const career = CONSULTATION_CONDENSED_CHECKLISTS.career!;
|
||||
assert.ok(career.includes("行业看星的本性:10 宫里的星、10 宫主、AmK,以及 10 宫主在 D9 的定位星。太阳 = 权力、公职、管理;月亮 = 公众、照护、流动;火星 = 体力、竞技、工程、军警;水星 = 沟通、写作、商业、分析;木星 = 教育、法律、顾问、宗教;金星 = 艺术、表演、娱乐、美;土星 = 劳动、组织、长期工程;罗睺 = 新技术、跨界、海外、大众传播。先说最强的一两颗,不平均罗列。"));
|
||||
assert.ok(career.includes("公众型还是幕后型要有依据:太阳或 10 宫主强且在角宫、AL 或 A10 落角宫、10 宫或命宫有入旺入庙星、`traditional_yogas` 有大格局,偏公众;10 宫主落 12 宫且无上述信号,才偏幕后。没有依据就不写这一句。不写「出头慢、越往后越稳、靠专业交付」这类人人适用的话,除非能指出具体盘面。"));
|
||||
});
|
||||
|
||||
test("opener keeps the affliction rule (T5 text)", () => {
|
||||
const body = natalAnswerShapeBody();
|
||||
assert.ok(body.startsWith("开场不要标题,先用一到三句人话回答问题;开场同样守受冲规则:不替用户断定在不在身边、几段感情、有没有病,不写程度结论,只说哪条线顺、哪条线有压力迹象。不给这件事起名字,开场不写行动清单。"));
|
||||
assert.equal(body.includes("第一句就是结论"), false);
|
||||
});
|
||||
@@ -79,12 +79,16 @@ test("each condensed list is 5–8 lines", () => {
|
||||
// 原值:全部清单 5–8 行,且只有 career / marriage / wealth 三份;
|
||||
// 新值:这三份仍 5–8 行;另有 TASK-consult-affliction-reading-20261001 定稿的 8 份(2–6 行,按任务书原文,不凑行数),
|
||||
// 只有 timing 没有本地清单;原因:该任务书 T2 / 决策 D5 补齐清单,定稿文本不得改意思。
|
||||
// 原值:career / marriage / wealth 5–8 行;新值:marriage / wealth 5–8 行、career 5–9 行;
|
||||
// 原因:TASK-consult-career-yoga-functional-20261002 T4 定稿给事业清单加两行(BUG-1176),定稿文本不凑行数。
|
||||
for (const domain of ["career", "marriage", "wealth"] as const) {
|
||||
const lines = CONSULTATION_CONDENSED_CHECKLISTS[domain]!;
|
||||
assert.ok(lines.length >= 5 && lines.length <= 8, `${domain}: ${lines.length} lines`);
|
||||
const max = domain === "career" ? 9 : 8;
|
||||
assert.ok(lines.length >= 5 && lines.length <= max, `${domain}: ${lines.length} lines`);
|
||||
}
|
||||
for (const [domain, lines] of Object.entries(CONSULTATION_CONDENSED_CHECKLISTS)) {
|
||||
assert.ok(lines!.length >= 2 && lines!.length <= 8, `${domain}: ${lines!.length} lines`);
|
||||
const max = domain === "career" ? 9 : 8;
|
||||
assert.ok(lines!.length >= 2 && lines!.length <= max, `${domain}: ${lines!.length} lines`);
|
||||
}
|
||||
assert.deepEqual(Object.keys(CONSULTATION_CONDENSED_CHECKLISTS).sort(), [
|
||||
"annual", "career", "children", "education", "family", "general", "health", "marriage", "migration", "parents", "wealth",
|
||||
|
||||
Reference in New Issue
Block a user