476342d7b1
Hardens the families-comparison chart page (#107) so it is openable and legible on real multi-year data. Two halves, both confined to the CLI render path (engine + registry untouched, C14): #108 — serve-time min-max decimation. A pure `decimate(ChartData, buckets)` transform partitions the shared union-ts spine into <=~2000 buckets and emits each series' per-bucket min+max (nulls preserved), bounding the served page to a few thousand points regardless of the underlying M1 point count. Applied in emit_chart on both the single-run and family paths; a no-op under budget (every existing fixture). Full recorded data stays on disk; only the page is thinned. For a walk-forward family the null-fill blow-up collapses for free (an all-null bucket stays null). Deterministic (C1). #102 — a run-context header. A `ChartMeta` (name/commit/window/broker/seed/taps, + member count for a family, + bound params for a single run) is built from the RunManifest in build_chart_data / build_comparison_chart_data (which gain a `name` arg), injected into window.AURA_TRACES.meta, and rendered by a new pure buildHeader in chart-viewer.js (headless .mjs-guarded). The family window is the SPAN across members (min from, max to), not the first member's window — the only reading that labels a disjoint walk-forward family's true OOS coverage (it collapses to the shared window for sweep/MC). Reuses the existing render_value for param display (no new Scalar Display, keeping C7's tag-only param plane). The earlier "manifest is NOT carried into the page" render pin is flipped to a positive one. Verified: cargo test --workspace = 446 passed / 0 failed; cargo clippy --workspace --all-targets -D warnings clean. New coverage: 5 decimate unit tests, single-run + family meta wiring (incl. the span-window invariant), a buildHeader headless guard, and two end-to-end cli_run tests pinning the served-page meta at the binary boundary. closes #108 closes #102
59 lines
2.9 KiB
JavaScript
59 lines
2.9 KiB
JavaScript
// Headless guard for the run-context header builder (chart-viewer.js buildHeader).
|
|
// Property protected: buildHeader maps an AURA_TRACES.meta object to header chips
|
|
// WITHOUT a DOM — a run shows name/tap/broker/window/commit chips; a family adds a
|
|
// members chip; absent meta yields []. Loads the real viewer module and drives the
|
|
// exported pure buildHeader; mount() (browser-only) is never called.
|
|
import { createRequire } from "node:module";
|
|
import { fileURLToPath } from "node:url";
|
|
import { dirname, join } from "node:path";
|
|
|
|
const require = createRequire(import.meta.url);
|
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
|
|
const fail = (msg) => { console.error("FAIL: " + msg); process.exit(1); };
|
|
const has = (chips, label, needle, msg) => {
|
|
const c = chips.find((c) => c.label === label);
|
|
if (!c) fail(msg + " (no '" + label + "' chip)");
|
|
if (needle != null && String(c.value).indexOf(needle) < 0) fail(msg + " (got " + c.value + ")");
|
|
};
|
|
|
|
// No `document` global -> chart-viewer.js's browser mount is skipped; require gives
|
|
// only the pure export (mirrors chart_viewer_build.mjs stubbing window before require).
|
|
global.window = {};
|
|
const { buildHeader } = require(join(here, "..", "assets", "chart-viewer.js"));
|
|
|
|
// run meta -> name / tap / broker / window / commit chips; no members chip
|
|
const run = {
|
|
kind: "run", name: "eur-demo", commit: "abc1234def567",
|
|
window: [1704067200000000000, 1735689600000000000], broker: "sim-optimal(pip_size=0.0001)",
|
|
seed: 0, taps: ["equity", "exposure"], members: null, params: [["len", "20"]],
|
|
};
|
|
const rc = buildHeader(run);
|
|
has(rc, "run", "eur-demo", "run name chip");
|
|
has(rc, "tap", "equity", "tap chip");
|
|
has(rc, "broker", "sim-optimal", "broker chip");
|
|
has(rc, "window", "2024", "window chip (formatted day)");
|
|
has(rc, "commit", "abc1234", "commit chip (shortened)");
|
|
if (rc.find((c) => c.label === "members")) fail("a single run must not carry a members chip");
|
|
|
|
// family meta -> adds a members chip, name label is 'family', and renders its
|
|
// SPANNING window. The window models a walk-forward family's full OOS coverage
|
|
// (built Rust-side as (min member.from, max member.to)); the rendered chip must
|
|
// show that full span's endpoints, not a single member's window.
|
|
const fam = {
|
|
kind: "family", name: "ger40-5y", commit: "deadbeef",
|
|
window: [1577836800000000000, 1735689600000000000], // 2020-01-01 -> 2025-01-01
|
|
broker: "sim-optimal(pip_size=1)", seed: 0, taps: ["equity"], members: 4, params: [],
|
|
};
|
|
const fc = buildHeader(fam);
|
|
has(fc, "family", "ger40-5y", "family name chip");
|
|
has(fc, "members", "4", "family members chip");
|
|
has(fc, "window", "2020", "family window chip (span start)");
|
|
has(fc, "window", "2025", "family window chip (span end)");
|
|
|
|
// absent meta -> []
|
|
if (buildHeader(undefined).length !== 0) fail("absent meta must yield no chips");
|
|
|
|
console.log("OK — buildHeader run/family/empty chips.");
|
|
process.exit(0);
|