feat(consult): evidence card v2 per the astrologer's review

TASK-consult-evidence-card-v2-20260927 T3. Card version evidence-card-v2.

- Base section (every card): the engine's D9 summary (D9 lagna, each
  planet's D9 sign and dignity, Vargottama, D1/D9 reversals); D9 houses and
  aspects stay out.
- Career: + AL (engine pada A1), 10H SAV, SAV of the Jupiter / Saturn
  transit signs.
- Marriage: + 5H / 5L, day / night, Punarphoo (observation_only), Double
  Transit on 7H / 7L (house-7 run) and DK / UL, Vivah Saham, gender unknown.
- Wealth: 8H / 12H named.
- Annual: the annual Tajika chart verbatim (parameter_sensitive), or
  年盘未接入 when the pack is blocked / not attached (never natal data);
  houses 1 + running / next AD lords' houses + the year's Jupiter / Saturn
  transit houses, with their basis; ingress / station dates only.
- Timing: Rahu / Ketu with ingress dates, Jupiter / Saturn SAV and BAV,
  Double Transit conclusions (no degrees); vargas follow the turn's other
  domain, D9 when alone.
- Chara Dasha leaves every card; KP is never on a card. The lookup enum adds
  karakamsha, dispositor_chains, inter_chart_linkage, argala, moon_transit;
  a KP lookup travels with its blocked note.
- System prompt and tool descriptions name the D9 summary, the
  parameter_sensitive / observation_only tags, 年盘未接入 and the lookups.
- Telemetry card version / agentVersion bumped to v2 (three-column notes in
  the touched tests).

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-09-27 16:44:19 +08:00
co-authored by Claude Opus 5.5
parent 33739e3789
commit db17074430
11 changed files with 696 additions and 41 deletions
@@ -13,6 +13,7 @@ import {
buildEvidenceCard,
citedEvidenceCardFields,
EVIDENCE_CARD_BACKSTAGE_LINE,
EVIDENCE_CARD_LABELS,
EVIDENCE_CARD_SPECS,
EVIDENCE_CARD_VERSION,
evidenceCardFieldValues,
@@ -145,10 +146,16 @@ test("every card value is a value the projection or the engine already holds (no
}
return into;
}
// 原值: every leaf must be an engine / projection value.
// 新值: the same, except the two labels the v2 card writes itself
// (EVIDENCE_CARD_LABELS: 性别未知, 年盘未接入).
// 原因: TASK-consult-evidence-card-v2-20260927 decision table mandates both labels.
const cardLabels = new Set(Object.values(EVIDENCE_CARD_LABELS).map((label) => JSON.stringify(label)));
for (const chart of golden.charts) {
for (const domain of consultationDomainIds) {
const input = sources(chart.workflow, domain);
const known = scalars({ packet: input.packet, context: input.context });
for (const label of cardLabels) known.add(label);
const card = buildEvidenceCard([input]);
for (const [fieldId, values] of evidenceCardFieldValues(card)) {
for (const value of values) {
@@ -44,7 +44,8 @@ test("the record has exactly the six approved fields", async () => {
assert.ok(record);
assert.deepEqual(Object.keys(record).sort(), [...EVIDENCE_CARD_TELEMETRY_FIELDS].sort());
assert.deepEqual(record.domains, ["parents"]);
assert.equal(record.cardVersion, "evidence-card-v1");
// 原值: "evidence-card-v1" / 新值: "evidence-card-v2" / 原因: TASK-consult-evidence-card-v2-20260927 T3 卡版本升级
assert.equal(record.cardVersion, "evidence-card-v2");
assert.equal(record.cardChars, state.evidenceCardChars);
assert.equal(record.cardTokenEstimate, Math.round(record.cardChars / 3.5));
assert.deepEqual(record.citedFieldIds, []);
@@ -52,7 +53,7 @@ test("the record has exactly the six approved fields", async () => {
});
test("the schema is strict: no extra field, no free text in field ids", () => {
const valid = { domains: ["parents"], cardVersion: "evidence-card-v1", cardChars: 10, cardTokenEstimate: 3, citedFieldIds: ["base.natal.ascendant.sign"], feedback: "up" };
const valid = { domains: ["parents"], cardVersion: "evidence-card-v2", cardChars: 10, cardTokenEstimate: 3, citedFieldIds: ["base.natal.ascendant.sign"], feedback: "up" };
assert.equal(evidenceCardTelemetrySchema.safeParse(valid).success, true);
for (const extra of ["question", "answer", "sessionId", "userId", "birthDate", "matched"]) {
assert.equal(evidenceCardTelemetrySchema.safeParse({ ...valid, [extra]: "x" }).success, false, extra);
@@ -70,7 +71,7 @@ test("the logged line keeps field ids and drops every word of the user's text",
const answer = `${secrets[0]}(${secrets[1]},生日 ${secrets[2]})问:${secrets[3]}。子运从 ${pdStart} 开始。`;
const lines: AgentObservabilityEvent[] = [];
const log = createAgentObservabilityLogger((event) => lines.push(event));
log({ runId: "req-telemetry", agentVersion: "consultation-evidence-card-v1", evidenceCard: consultationEvidenceCardTelemetry(state, answer) });
log({ runId: "req-telemetry", agentVersion: "consultation-evidence-card-v2", evidenceCard: consultationEvidenceCardTelemetry(state, answer) });
const text = JSON.stringify(lines);
for (const secret of secrets) assert.equal(text.includes(secret), false, secret);
assert.equal(text.includes(pdStart), false, "the matched date is not kept");
@@ -82,7 +83,8 @@ test("the logged line keeps field ids and drops every word of the user's text",
test("the consult route logs the record once per natal turn, without session or user id", () => {
assert.match(route, /const evidenceCard = consultationEvidenceCardTelemetry\(state, answerForTelemetry\);/);
const block = route.slice(route.indexOf("const evidenceCard = consultationEvidenceCardTelemetry"), route.indexOf("const settleRun = async"));
assert.match(block, /agentVersion: "consultation-evidence-card-v1"/);
// 原值: consultation-evidence-card-v1 / 新值: v2 / 原因: 卡 v2(TASK-consult-evidence-card-v2-20260927 T3)
assert.match(block, /agentVersion: "consultation-evidence-card-v2"/);
assert.doesNotMatch(block, /sessionId|userId/);
const natal = route.slice(route.indexOf("const agent = getJyotishAgent(selectedModel, agentContext);"));
assert.match(natal, /answerForTelemetry = output;[\s\S]*return settleRun\(\(\) => completeResponse\(/);
@@ -0,0 +1,346 @@
// TASK-consult-evidence-card-v2-20260927 T3: evidence card v2, card by card,
// item by item, against real engine responses (three public AA charts; family,
// annual and timing routes; scripts/research/capture_consult_evidence_card_golden.py,
// AGENTS §7.4). Expected values are read from the engine's own layers
// (chart.consultation_native_layers, chart.modules), so a card that rewrote,
// converted or re-derived a value fails here.
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import {
buildEvidenceCard,
EVIDENCE_CARD_LABELS,
EVIDENCE_CARD_VERSION,
EVIDENCE_LOOKUP_KP_NOTE,
EVIDENCE_LOOKUP_SECTIONS,
type EvidenceCard,
} from "../src/lib/consultation-evidence-card.ts";
import { consultationDomainIds, type ConsultationDomain } from "../src/lib/consultation-domain-registry.ts";
import { createConsultationPlan } from "../src/lib/consultation-plan.ts";
import { evidenceLookupActivityLabel } from "../src/lib/consultation-activity-labels.ts";
import {
consultationWorkflowResponseSchema,
toAgentConsultationContext,
toModelOutput,
} from "../src/mastra/consultation-workflow.ts";
import { createConsultationRuntimeState, createConsultationTools, readEvidenceLookupSection } from "../src/mastra/consultation-tools.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[] };
function dig(value: unknown, ...keys: string[]): unknown {
let current = value;
for (const key of keys) {
if (!current || typeof current !== "object" || Array.isArray(current)) return undefined;
current = (current as Json)[key];
}
return current;
}
function sources(workflow: Json, domain: ConsultationDomain) {
const context = toAgentConsultationContext(consultationWorkflowResponseSchema.parse(structuredClone(workflow)));
const packet = toModelOutput(context, createConsultationPlan({ userIntent: "q", theme: domain }), domain);
return { domain, packet, context };
}
function card(workflow: Json, ...domains: ConsultationDomain[]): EvidenceCard {
return buildEvidenceCard(domains.map((domain) => sources(workflow, domain)));
}
function native(workflow: Json): Json {
return dig(workflow, "chart", "consultation_native_layers") as Json;
}
function modules(workflow: Json): Json {
return dig(workflow, "chart", "modules") as Json;
}
function pick(source: unknown, keys: readonly string[]) {
const out: Json = {};
for (const key of keys) {
const value = (source as Json)[key];
if (value !== undefined) out[key] = value;
}
return out;
}
test("the card version is v2", () => {
assert.equal(EVIDENCE_CARD_VERSION, "evidence-card-v2");
assert.equal(card(golden.charts[0]!.workflow, "career").card_version, "evidence-card-v2");
});
test("every base section carries the engine's D9 summary: lagna, D9 sign and dignity, Vargottama, reversals", () => {
let reversalsSeen = 0;
for (const chart of golden.charts) {
const summary = native(chart.workflow).d9_summary as Json;
for (const domain of consultationDomainIds) {
const d9 = card(chart.workflow, domain).base.natal.d9;
const where = `${chart.id}/${domain}`;
assert.equal(d9.lagna, summary.lagna, `${where} D9 lagna`);
assert.equal(d9.lagna_vargottama, summary.lagna_vargottama, `${where} lagna vargottama`);
assert.deepEqual(d9.reversals, summary.reversals, `${where} reversals`);
for (const [planet, row] of Object.entries(summary.planets as Json)) {
assert.deepEqual((d9.planets as Json)[planet], pick(row, ["d9_sign", "d9_dignity", "vargottama"]), `${where} ${planet}`);
}
// The D9 houses and aspects stay out of the base section.
assert.equal(JSON.stringify(d9).includes("house"), false, `${where} no D9 houses`);
}
reversalsSeen += (summary.reversals as unknown[]).length;
}
assert.ok(reversalsSeen > 0, "a golden chart has a D1/D9 dignity reversal on the card");
});
test("no card carries Chara Dasha or KP; both are lookups", () => {
for (const chart of golden.charts) {
for (const [workflow, domains] of [
[chart.workflow, consultationDomainIds],
[chart.annual_workflow, ["annual"]],
[chart.timing_workflow, ["timing"]],
] as const) {
for (const domain of domains) {
const built = card(workflow, domain);
const text = JSON.stringify({ base: built.base, sections: built.domain_sections });
for (const absent of ["chara_dasha", "kp_cusps", "sublord", "sub_lord", "cusps"]) {
assert.equal(text.includes(`"${absent}"`), false, `${chart.id}/${domain} ${absent}`);
}
assert.ok(built.meta.supplementable_sections.includes("chara_dasha"), `${chart.id}/${domain} chara lookup`);
assert.ok(built.meta.supplementable_sections.includes("kp_cusps"), `${chart.id}/${domain} KP lookup`);
}
}
}
});
test("career: AL, 10H SAV and the SAV of the Jupiter / Saturn transit signs join v1's career section", () => {
for (const chart of golden.charts) {
const section = card(chart.workflow, "career").domain_sections.career!;
const padas = dig(modules(chart.workflow), "arudha_padas", "padas") as Json;
assert.deepEqual(section.arudha?.AL, pick(padas.A1, ["sign", "lord"]), `${chart.id} AL is pada A1`);
assert.deepEqual(section.arudha?.A10, pick(padas.A10, ["sign", "lord"]), `${chart.id} A10`);
const house10 = dig(modules(chart.workflow), "ashtakavarga", "house_scores", "house_10");
assert.deepEqual(section.sav_by_house?.["10"], pick(house10, ["sign", "sav_score", "level"]), `${chart.id} 10H SAV`);
const current = dig(native(chart.workflow), "slow_transits", "current") as Json;
for (const body of ["Jupiter", "Saturn"]) {
assert.deepEqual((section.slow_transits as Json)[body], pick(current[body], ["sign", "natal_house", "sav"]), `${chart.id} ${body}`);
}
assert.ok(section.vargas.D10 && section.karakas?.AmK && section.yogas && section.shadbala !== undefined, `${chart.id} v1 items kept`);
const supplementable = card(chart.workflow, "career").meta.supplementable_sections;
for (const lookup of ["karakamsha", "dispositor_chains", "inter_chart_linkage", "argala", "kp_cusps", "chara_dasha"]) {
assert.ok(supplementable.includes(lookup as never), `${chart.id} ${lookup} is a lookup`);
}
}
});
test("marriage: 5H / 5L, day / night, Punarphoo, Double Transit on 7H / 7L / DK / UL, Vivah Saham, gender unknown", () => {
for (const chart of golden.charts) {
const built = card(chart.workflow, "marriage");
const section = built.domain_sections.marriage!;
const layers = native(chart.workflow);
const where = chart.id;
assert.deepEqual(Object.keys(section.houses).sort(), ["5", "7"], `${where} houses`);
const owned = built.base.natal.functional_benefic_malefic.owned_houses as Json;
const fifthLords = Object.entries(owned).filter(([, houses]) => (houses as number[]).includes(5)).map(([name]) => name);
assert.deepEqual((section.houses["5"] as Json).lords, fifthLords, `${where} 5L`);
assert.deepEqual(section.day_night, { is_day: dig(modules(chart.workflow), "gulika", "daynight_evidence", "is_daytime") }, `${where} is_day`);
const punarphoo = layers.punarphoo as Json;
assert.deepEqual(section.punarphoo, {
status: "observation_only",
natal_present: punarphoo.natal_present,
orb_deg: punarphoo.orb_deg,
saturn_return_to_natal_moon: pick(punarphoo.saturn_return_to_natal_moon, ["active", "orb_deg"]),
claim_boundary: punarphoo.claim_boundary,
}, `${where} Punarphoo`);
const double = layers.double_transit as Json;
const seventh = (double.conclusions as Json[]).filter((row) => row.house === 7)
.map((row) => pick(row, ["layer", "target", "strength", "jupiter_pac", "saturn_pac"]));
assert.deepEqual(dig(section.double_transit, "house_7"), seventh, `${where} 7H / 7L double transit`);
for (const key of ["DK", "UL"]) {
assert.deepEqual(
dig(section.double_transit, key),
pick((double.karaka_targets as Json)[key], ["target", "jupiter_pac", "saturn_pac", "double_transit"]),
`${where} ${key} double transit`,
);
}
const vivah = layers.vivah_saham as Json;
assert.deepEqual(section.vivah_saham, pick(vivah, ["longitude", "sign", "degree_in_sign", "house", "natal_conjuncts"]), `${where} Vivah`);
assert.equal(typeof (section.vivah_saham as Json).house, "number");
assert.equal(section.gender, EVIDENCE_CARD_LABELS.genderUnknown);
assert.ok(section.vargas.D9 && section.arudha?.UL && section.karakas?.DK, `${where} v1 items kept`);
}
});
test("wealth names 8H and 12H beside 2 / 11 / 9 / 5", () => {
for (const chart of golden.charts) {
const section = card(chart.workflow, "wealth").domain_sections.wealth!;
assert.deepEqual(Object.keys(section.houses).map(Number).sort((a, b) => a - b), [2, 5, 8, 9, 11, 12]);
for (const house of ["8", "12"]) {
assert.equal((section.houses[house] as Json).sign, card(chart.workflow, "wealth").base.natal.houses[house], `${chart.id} ${house}H sign`);
}
}
});
test("annual: the annual chart verbatim, AD-lord and transit houses, key dates only", () => {
for (const chart of golden.charts) {
const built = card(chart.annual_workflow, "annual");
const section = built.domain_sections.annual!;
const layers = native(chart.annual_workflow);
const annual = layers.annual_tajika as Json;
const where = chart.id;
assert.deepEqual(section.annual_chart, pick(annual, [
"status", "varsha_year", "annual_lagna", "varshesha", "muntha", "mudda_dasha", "tajika_aspects", "boundary",
]), `${where} annual chart`);
assert.equal(dig(section.annual_chart, "status"), "parameter_sensitive");
// Houses: 1, the running and next AD lords' houses, the year's Jupiter / Saturn transit houses.
const vim = built.base.timing.vimshottari as Json;
const owned = built.base.natal.functional_benefic_malefic.owned_houses as Json;
const lordHouses = (lord: unknown) => ((owned[String(lord)] as number[] | undefined) ?? []);
const inWindow = dig(layers, "slow_transits", "houses_in_window") as Json;
const expected = [...new Set([
1,
...lordHouses(dig(vim, "antardasha", "lord")),
...lordHouses(dig(vim, "next_antardasha", "lord")),
...(inWindow.Jupiter as number[]),
...(inWindow.Saturn as number[]),
])];
assert.deepEqual(Object.keys(section.houses).map(Number).sort((a, b) => a - b), [...expected].sort((a, b) => a - b), `${where} annual houses`);
assert.deepEqual(section.house_basis, {
antardasha_lord: dig(vim, "antardasha", "lord"),
antardasha_houses: lordHouses(dig(vim, "antardasha", "lord")),
next_antardasha_lord: dig(vim, "next_antardasha", "lord"),
next_antardasha_houses: lordHouses(dig(vim, "next_antardasha", "lord")),
jupiter_transit_houses: inWindow.Jupiter,
saturn_transit_houses: inWindow.Saturn,
}, `${where} house basis`);
// Key dates: Jupiter / Saturn ingress and stations, Rahu / Ketu ingress; no month-by-month rows.
assert.deepEqual(dig(section.slow_transits, "key_dates"), dig(layers, "slow_transits", "key_dates"), `${where} key dates`);
assert.ok(((dig(section.slow_transits, "key_dates") as Json[]).length) <= 24);
for (const row of dig(section.slow_transits, "key_dates") as Json[]) {
assert.ok(["ingress", "station"].includes(String(row.kind)));
assert.ok(["jupiter", "saturn", "rahu", "ketu"].includes(String(row.body)));
}
assert.equal(section.transits, undefined, `${where} no generic transit layer on the annual card`);
}
});
test("annual: a missing or blocked annual pack says 年盘未接入 and never shows natal data in its place", () => {
// Family route: the engine attaches the pack on the annual route only.
const notAttached = card(golden.charts[0]!.workflow, "annual").domain_sections.annual!;
assert.deepEqual(notAttached.annual_chart, {
status: EVIDENCE_CARD_LABELS.annualNotAttached,
reason: "annual pack is attached for the annual route only",
});
const blocked = structuredClone(golden.charts[1]!.annual_workflow);
((blocked.chart as Json).consultation_native_layers as Json).annual_tajika = {
status: "blocked",
reason: "annual_pack_not_reliable:mudda_dasha_periods",
};
const section = card(blocked, "annual").domain_sections.annual!;
assert.deepEqual(section.annual_chart, { status: "年盘未接入", reason: "annual_pack_not_reliable:mudda_dasha_periods" });
assert.deepEqual(Object.keys(section.annual_chart as Json), ["status", "reason"], "no annual field is filled from natal data");
assert.deepEqual(Object.keys(notAttached.annual_chart as Json), ["status", "reason"]);
});
test("timing: Rahu / Ketu, Jupiter / Saturn SAV and BAV, Double Transit conclusions without degrees", () => {
for (const chart of golden.charts) {
const section = card(chart.timing_workflow, "timing").domain_sections.timing!;
const layers = native(chart.timing_workflow);
const current = dig(layers, "slow_transits", "current") as Json;
const where = chart.id;
for (const body of ["Jupiter", "Saturn"]) {
assert.deepEqual(dig(section.slow_transits, "current", body), pick(current[body], ["sign", "degree_in_sign", "retrograde", "natal_house", "sav", "bav"]), `${where} ${body}`);
}
for (const body of ["Rahu", "Ketu"]) {
assert.deepEqual(dig(section.slow_transits, "current", body), pick(current[body], ["sign", "degree_in_sign", "retrograde", "natal_house"]), `${where} ${body}`);
}
const nodeDates = (dig(layers, "slow_transits", "key_dates") as Json[]).filter((row) => row.body === "rahu" || row.body === "ketu");
assert.deepEqual(dig(section.slow_transits, "node_ingresses"), nodeDates, `${where} node ingresses`);
const double = layers.double_transit as Json;
assert.deepEqual(dig(section.double_transit, "conclusions"), (double.conclusions as Json[]).map((row) => pick(row, ["house", "layer", "target", "strength"])), `${where} conclusions`);
assert.deepEqual(dig(section.double_transit, "active_houses"), double.active_houses, `${where} active houses`);
assert.equal(/\d+\.\d+°/.test(JSON.stringify(section.double_transit)), false, `${where} no raw degrees`);
// Moon transit is a lookup, not on the card.
assert.equal(JSON.stringify(section).includes("moon_transit"), false);
}
});
test("timing vargas follow the turn's other domain; D9 when timing is alone", () => {
const workflow = golden.charts[1]!.timing_workflow;
assert.deepEqual(Object.keys(card(workflow, "timing").domain_sections.timing!.vargas), ["D1", "D9"]);
for (const [other, code] of [["career", "D10"], ["marriage", "D9"], ["wealth", "D2"], ["health", "D30"]] as const) {
for (const order of [["timing", other], [other, "timing"]] as ConsultationDomain[][]) {
const built = card(workflow, ...order);
const timing = built.domain_sections.timing!;
assert.deepEqual(Object.keys(timing.vargas), ["D1"], `${order.join("+")} timing keeps D1 only`);
assert.equal(timing.vargas_follow, other, `${order.join("+")} follows ${other}`);
assert.ok(built.domain_sections[other]!.vargas[code], `${order.join("+")} ${code} is in the ${other} section`);
}
}
// Annual carries only D1, so timing falls back to D9.
const withAnnual = card(workflow, "timing", "annual").domain_sections.timing!;
assert.deepEqual(Object.keys(withAnnual.vargas), ["D1", "D9"]);
assert.equal(withAnnual.vargas_follow, undefined);
});
test("the lookup enum carries the optional layers and returns the engine's own values", async () => {
for (const section of ["karakamsha", "dispositor_chains", "inter_chart_linkage", "argala", "moon_transit", "chara_dasha", "kp_cusps", "varga:D10"] as const) {
assert.ok(EVIDENCE_LOOKUP_SECTIONS.includes(section), section);
assert.equal(evidenceLookupActivityLabel(section).includes("一段盘面数据"), false, `${section} has a plain label`);
}
const workflow = golden.charts[0]!.workflow;
const career = sources(workflow, "career");
const execution = { ...career, modelOutput: career.packet };
const cache = { executions: [execution] } as never;
const layers = native(workflow);
assert.deepEqual(readEvidenceLookupSection(cache, "karakamsha"), layers.karakamsha);
assert.deepEqual(readEvidenceLookupSection(cache, "moon_transit"), layers.moon_transit);
assert.deepEqual(readEvidenceLookupSection(cache, "dispositor_chains"), pick(layers.dispositor_chains, ["chains", "boundary"]));
assert.deepEqual(readEvidenceLookupSection(cache, "inter_chart_linkage"), pick(layers.inter_chart_linkage, ["planets", "boundary"]));
assert.deepEqual(readEvidenceLookupSection(cache, "argala"), pick(layers.argala, ["method", "summary", "houses"]));
const kp = readEvidenceLookupSection(cache, "kp_cusps") as Json;
assert.equal(kp.note, EVIDENCE_LOOKUP_KP_NOTE);
assert.ok(kp.kp_cusps, "KP data travels with its blocked note");
assert.ok(readEvidenceLookupSection(cache, "varga:D10"), "D10 for income from work");
// Through the tool: a KP lookup answers with the note.
const state = createConsultationRuntimeState();
const tools = createConsultationTools({
userId: "u", sessionId: "s", requestId: "kp-lookup", consultationMode: "verified_chart",
serverChart: { name: "public", toolInput: { year: 2000, month: 1, day: 1, hour: 0, minute: 0, city: "fixture", lat: 0, lon: 0, tz: 0, ayanamsa: "raman" }, truth: { birthTimeSource: "reported" } } as never,
state,
runWorkflow: async () => structuredClone(workflow) as never,
});
await tools["run-jyotish-consultation"].execute!({ question: "q", domains: ["wealth"] } as never, { writer: { custom: async () => {} } } as never);
const result = await tools["read-consultation-evidence"].execute!({ section: "kp_cusps" } as never, { writer: { custom: async () => {} } } as never) as Json;
assert.equal(dig(result, "data", "note"), EVIDENCE_LOOKUP_KP_NOTE);
});
test("size: contract + card stay under 12,000 characters without the checklist, annual and timing included", 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" },
truth: { birthTimeSource: "reported" },
};
for (const chart of golden.charts) {
for (const [workflow, domain] of [
...consultationDomainIds.map((domain) => [chart.workflow, domain] as const),
[chart.annual_workflow, "annual"] as const,
[chart.timing_workflow, "timing"] as const,
]) {
const state = createConsultationRuntimeState();
const tools = createConsultationTools({
userId: "u", sessionId: "s", requestId: `size-${domain}`, consultationMode: "verified_chart",
serverChart: serverChart as never, state,
runWorkflow: async () => structuredClone(workflow) as never,
});
const view = await tools["run-jyotish-consultation"].execute!({ question: "q", domains: [domain] } as never, { writer: { custom: async () => {} } } as never) as Json;
const { methodology: _methodology, ...rest } = view;
void _methodology;
assert.ok(JSON.stringify(rest).length <= 12_000, `${chart.id}/${domain} contract + card ${JSON.stringify(rest).length}`);
assert.deepEqual(dig(view, "evidence_card", "gaps") ?? [], [], `${chart.id}/${domain} gaps`);
}
}
});
@@ -107,7 +107,8 @@ test("the answer-writing call sees the evidence card, not the full result (red l
const writer = firstPromptWithToolResult(result.prompts, "run-jyotish-consultation");
assert.equal(writer, 1);
const card = toolResultText(result.prompts[writer], "run-jyotish-consultation");
assert.ok(card.includes("evidence-card-v1"), "the card reaches the writer");
// 原值: "evidence-card-v1" / 新值: "evidence-card-v2" / 原因: 卡 v2(TASK-consult-evidence-card-v2-20260927 T3)
assert.ok(card.includes("evidence-card-v2"), "the card reaches the writer");
assert.ok(card.includes(PD_START), "with the engine's pratyantar start");
assert.ok(card.includes(NARAYANA_SIGN), "and the running Narayana sign");
assert.ok(card.includes("can_answer_direction"), "and the answer contract");
@@ -135,7 +136,8 @@ test("a length continuation carries the card (red line 4)", async () => {
assert.deepEqual(result.terminal.map((event) => event.type), ["run.completed"]);
assert.equal(result.continuations.length, 1);
const continuation = JSON.stringify(result.prompts[2]);
assert.ok(continuation.includes("evidence-card-v1"));
// 原值: "evidence-card-v1" / 新值: "evidence-card-v2" / 原因: 同上
assert.ok(continuation.includes("evidence-card-v2"));
assert.ok(continuation.includes(PD_START));
assert.equal(result.completed, ANSWER);
});