fix(consult): card carries the engine's short Narayana label; AD/PD drop derivable years (BUG-1215)

The full note pushed steve_jobs/marriage to 12,028 characters (budget
12,000), and cutting it in the card broke the 'nothing rewritten' contract.
The engine now also writes algorithm_label 旧算法(从上升起、一律顺行), the card
copies it verbatim, and Narayana AD/PD rows drop years (end_age - start_age).
Largest card 11,979 (baseline 11,983). Goldens re-captured; the biography
golden now keeps the Narayana label keys too.

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:
Jesse_Chen
2026-10-03 16:48:19 +08:00
co-authored by Claude Opus 5.5
parent 921b336ba5
commit 693467bf0c
8 changed files with 20 additions and 13 deletions
@@ -426,6 +426,9 @@ const FUNCTIONAL_KEYS = [
] as const;
const PERIOD_KEYS = ["lord", "start", "end"] as const;
const NARAYANA_PERIOD_KEYS = ["sign", "lord", "years", "start_age", "end_age"] as const;
// BUG-1215: AD / PD drop `years` (it is end_age − start_age) so the legacy
// algorithm label fits the 12,000-character card budget.
const NARAYANA_SUB_PERIOD_KEYS = ["sign", "lord", "start_age", "end_age"] as const;
const KARAKA_KEYS = ["planet", "degree_in_sign", "rank"] as const;
const ZODIAC_SIGNS = [
@@ -610,14 +613,15 @@ function baseTiming(timing: Record<string, unknown>, gaps: string[]) {
const currentNarayana = record(record(timing.narayana_dasha).current_dasha);
const narayana: JsonRecord = {};
for (const level of ["md", "ad", "pd"] as const) {
const picked = pickKeys(currentNarayana[level], NARAYANA_PERIOD_KEYS);
const picked = pickKeys(currentNarayana[level], level === "md" ? NARAYANA_PERIOD_KEYS : NARAYANA_SUB_PERIOD_KEYS);
if (nonEmpty(picked)) narayana[level] = picked;
}
const remaining = copy(currentNarayana.remaining_years);
if (remaining !== undefined) narayana.remaining_years = remaining;
// BUG-1215: say which Narayana this is (legacy: from the Lagna, always zodiacal).
const algorithmNote = record(timing.narayana_dasha).algorithm_note;
if (narayana.md && typeof algorithmNote === "string" && algorithmNote) narayana.algorithm = algorithmNote;
// BUG-1215: say which Narayana this is (the engine's short label,
// 旧算法(从上升起、一律顺行); the full note stays in the data).
const algorithmLabel = copy(record(timing.narayana_dasha).algorithm_label);
if (narayana.md && typeof algorithmLabel === "string" && algorithmLabel) narayana.algorithm = algorithmLabel;
if (!narayana.md) gaps.push("base.narayana.md");
return { vimshottari, narayana };
}
+1 -1
View File
@@ -347,7 +347,7 @@ const timingKeys = new Set([
// and dropped the pratyantar dates, although the engine had both.
"md", "ad", "pd", "years", "startage", "endage", "remainingyears", "pratyantardashatimeline",
// BUG-1215: the running Narayana is the legacy algorithm; its label travels with it.
"algorithmnote",
"algorithmlabel",
]);
const validationKeys = new Set([