feat(consult): project the native technique layers with per-layer allowlists
TASK-consult-evidence-card-v2-20260927 T2. toAgentConsultationContext carries chart.consultation_native_layers as local_layers.native_layers; toModelOutput projects d9_summary / punarphoo / vivah_saham / day_night into the natal card and slow_transits / double_transit / annual_tajika into the timing card, each through its own nested-key allowlist (as BUG-1054 did for Narayana), so the existing natal and timing key sets are not widened and the depth / list caps are unchanged. Pada A1 (the engine's Arudha Lagna) is allowlisted for the career card's AL. Golden test: every projected layer equals the engine layer value for value on 3 public AA charts (family, timing and annual routes); the Double Transit per-house summary line is the only field left out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d5616c347e
commit
33739e3789
@@ -0,0 +1,103 @@
|
||||
// TASK-consult-evidence-card-v2-20260927 T2: the native technique layers the
|
||||
// engine now attaches (chart.consultation_native_layers) reach the model
|
||||
// projection through their own nested-key allowlists, like BUG-1054.
|
||||
//
|
||||
// Fixture: real engine consultation responses for three public AA charts, the
|
||||
// family, annual and timing routes (scripts/research/capture_consult_evidence_card_golden.py,
|
||||
// AGENTS §7.4). The projected layer must equal the engine's layer value for
|
||||
// value, not merely carry the key.
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import test from "node:test";
|
||||
|
||||
import { createConsultationPlan } from "../src/lib/consultation-plan.ts";
|
||||
import type { ConsultationDomain } from "../src/lib/consultation-domain-registry.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[] };
|
||||
|
||||
function nativeLayers(workflow: Json): Json {
|
||||
return ((workflow.chart as Json).consultation_native_layers ?? {}) as Json;
|
||||
}
|
||||
|
||||
function evidence(workflow: Json, domain: ConsultationDomain, category: "natal_foundation" | "timing"): Json {
|
||||
const context = toAgentConsultationContext(consultationWorkflowResponseSchema.parse(structuredClone(workflow)));
|
||||
const packet = toModelOutput(context, createConsultationPlan({ userIntent: "q", theme: domain }), domain);
|
||||
const card = packet.claim_cards.find((item) => item.category === category);
|
||||
return (card?.evidence ?? {}) as Json;
|
||||
}
|
||||
|
||||
test("the natal layers are projected value for value: D9 summary, Punarphoo, Vivah Saham, day / night", () => {
|
||||
for (const chart of golden.charts) {
|
||||
const engine = nativeLayers(chart.workflow);
|
||||
const natal = evidence(chart.workflow, "marriage", "natal_foundation");
|
||||
for (const name of ["d9_summary", "punarphoo", "vivah_saham", "day_night"]) {
|
||||
assert.equal((engine[name] as Json).status === "blocked", false, `${chart.id} ${name} executed`);
|
||||
assert.deepEqual(natal[name], engine[name], `${chart.id} ${name}`);
|
||||
}
|
||||
const d9 = natal.d9_summary as Json;
|
||||
assert.equal(Object.keys(d9.planets as Json).length, 9, `${chart.id} nine D9 rows`);
|
||||
}
|
||||
});
|
||||
|
||||
test("the timing layers are projected value for value: slow transits, Double Transit conclusions", () => {
|
||||
for (const chart of golden.charts) {
|
||||
const engine = nativeLayers(chart.timing_workflow);
|
||||
const timing = evidence(chart.timing_workflow, "timing", "timing");
|
||||
assert.deepEqual(timing.slow_transits, engine.slow_transits, `${chart.id} slow_transits`);
|
||||
// The per-house summary line of the producer is left out; the conclusions are the result.
|
||||
const { house_7_summary: summary, ...double } = engine.double_transit as Json;
|
||||
assert.equal(typeof summary, "string");
|
||||
assert.deepEqual(timing.double_transit, double, `${chart.id} double_transit`);
|
||||
const current = (timing.slow_transits as Json).current as Json;
|
||||
assert.deepEqual(Object.keys(current).sort(), ["Jupiter", "Ketu", "Rahu", "Saturn"]);
|
||||
for (const body of ["Jupiter", "Saturn"]) {
|
||||
assert.equal(typeof (current[body] as Json).sav, "number", `${chart.id} ${body} SAV`);
|
||||
assert.equal(typeof (current[body] as Json).bav, "number", `${chart.id} ${body} BAV`);
|
||||
}
|
||||
assert.equal((timing.annual_tajika as Json).status, "not_applicable", `${chart.id} no annual pack on the timing route`);
|
||||
}
|
||||
});
|
||||
|
||||
test("the annual pack is projected value for value on the annual route", () => {
|
||||
for (const chart of golden.charts) {
|
||||
const engine = nativeLayers(chart.annual_workflow).annual_tajika as Json;
|
||||
const timing = evidence(chart.annual_workflow, "annual", "timing");
|
||||
assert.equal(engine.status, "parameter_sensitive");
|
||||
assert.deepEqual(timing.annual_tajika, engine, `${chart.id} annual_tajika`);
|
||||
const mudda = (timing.annual_tajika as Json).mudda_dasha as Json[];
|
||||
assert.ok(mudda.length >= 8, `${chart.id} Mudda segments`);
|
||||
for (const row of mudda) {
|
||||
assert.match(String(row.start), /^\d{4}-\d{2}-\d{2}$/);
|
||||
assert.match(String(row.end), /^\d{4}-\d{2}-\d{2}$/);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test("the Arudha Lagna (pada A1) now reaches the projection", () => {
|
||||
for (const chart of golden.charts) {
|
||||
const padas = ((((chart.workflow.chart as Json).modules as Json).arudha_padas as Json).padas as Json);
|
||||
const natal = evidence(chart.workflow, "career", "natal_foundation");
|
||||
const projected = ((natal.arudha_padas as Json).padas as Json).A1 as Json;
|
||||
assert.equal(projected.sign, (padas.A1 as Json).sign, `${chart.id} AL sign`);
|
||||
assert.equal(projected.lord, (padas.A1 as Json).lord, `${chart.id} AL lord`);
|
||||
}
|
||||
});
|
||||
|
||||
test("a response without the native layers projects exactly as before", () => {
|
||||
const workflow = structuredClone(golden.charts[0]!.workflow);
|
||||
delete (workflow.chart as Json).consultation_native_layers;
|
||||
const natal = evidence(workflow, "marriage", "natal_foundation");
|
||||
const timing = evidence(workflow, "timing", "timing");
|
||||
for (const name of ["d9_summary", "punarphoo", "vivah_saham", "day_night"]) assert.equal(name in natal, false, name);
|
||||
for (const name of ["slow_transits", "double_transit", "annual_tajika"]) assert.equal(name in timing, false, name);
|
||||
});
|
||||
Reference in New Issue
Block a user