Skip to content

feat(admin): the insights artifact, one page for topology and spend (issue #175) - #179

Merged
edgehero merged 1 commit into
mainfrom
feat/insights-html
Aug 12, 2026
Merged

feat(admin): the insights artifact, one page for topology and spend (issue #175)#179
edgehero merged 1 commit into
mainfrom
feat/insights-html

Conversation

@edgehero

Copy link
Copy Markdown
Owner

Fourth slice of #175, the one the series exists for: /dispatch insights html [7d|30d|mtd] [--no-open] [--full-paths] writes <graphDir>/insights.html, one self-contained file:// document that puts the trigger/flow topology (spend-badged) beside the cost fold drawn as real charts: KPI tiles, plan verdict cards, a daily spend column chart, and the four breakdown bar lists.

Factoring

graph-html.mjs gains exports, not loads — its purity pin is substring-level and directional on purpose, so it may be a source of truth for a sibling emitter while never loading one itself. buildGraphScene is the normalize+layout+SVG-emission half of buildGraphHtml, extracted whole and behavior-preserving: every existing graph-html pin is green untouched, zero test edits. insights-html.mjs (814 lines) loads exactly two modules — ./graph-html.mjs for the scene/escaping/theme and ./panel.mjs for the money — so every dollar string comes from the REAL fmtCost, not a parity-pinned twin.

The page keeps the class discipline in geometry

A plan-covered bucket draws a plan:<id> chip and no dollar bar (a zero-length bar is the $0.00 lie in geometry); estimates are dashed and translucent beside their ~ est. text, hue never the sole encoding; floors carry on the chart; gap days render as baseline ticks, never compressed away; a null byTrigger renders "not computed"; limit: null renders "limit undisclosed by vendor" with no burn-down anywhere; NO_BASELINE renders its sentence and suppresses even a smuggled number; the page states both windows (spend window and the fixed 30d topology window) plus the retention sentence. Byte-deterministic under permutation of every input array; total on junk (an unreachable cost scan still writes the page with a banner); one script element; no fetching, no innerHTML, tips via textContent only; row→node cross-highlight replays a guarded synthetic click so PAGE_JS keeps sole ownership of selection (GRAPH.nodes[ still appears exactly once).

A headless-Chrome screenshot pass caught one visual defect no unit test saw (the #54 lesson holding): a fixed 75vh topology pane letterboxed a flat topology into a mostly-empty band — the svg now takes the scene's own aspect ratio, clamped.

Command surface

insights joins USAGE/KNOWN_SUBCOMMANDS/description in step (the pin holds member-for-member; the description string also gains the graph mention it had silently dropped). Bare insights answers usage — the artifact IS the feature. insightsHtmlCommand is graphHtmlCommand's structural twin: atomic tmp+rename to the stable path, URL notified FIRST, --no-open, headless skip-and-say, write failure never opens. Default window 30d, deliberately not costs' mtd: the topology half is pinned at a 30d record window and one page's two halves should agree unless the operator asks otherwise. Completion covers insightshtml → the windows. No new LLM tool (the topology assembly spawns git per folder; dispatch_costs unchanged). /dispatch graph html stays as-is — the lighter topology-only export, not an alias.

Specs and docs

NEW REQ-INSIGHTS-HTML-EXPORT (labeling rules inherited verbatim, the visual clauses, both windows, atomic/URL-first/headless/no-port/no-tool). REQ-COST-ANALYTICS AMENDED one sentence (the artifact joins the named surfaces; rules (a)-(g) unchanged). DES-ADMIN-VIA-PI-EXTENSION AMENDED with the factoring facts and the rejections on the record (a shared third emitter module, duplication, folding costs into graph.html in place, a charting library). REQ-GRAPH-HTML-EXPORT UNCHANGED, checked. REQ-TOPOLOGY-GRAPH UNCHANGED, checked. DES-GRAPH-EDGE-DERIVATION UNCHANGED, checked. DES-COST-FOLD-BY-SCAN UNCHANGED, checked. New docs/insights.md; cross-links in docs/graph.md and docs/costs.md. READMEs are deliberately untouched here: the holistic pitch/screenshot pass is the next slice, so it describes the finished surface.

Tests

Suite green in the CI posture (2224 tests, 0 skipped, live Valkey). New: insights-html.test.mjs (17 tests mirroring the graph-html invariants: purity/import allowlist, class parity, XSS, byte-determinism under permutation, well-formedness, the seven file:// needles, the fmtCost honesty pins, chart geometry via the exported layout helpers, canary/path-leak with the fullPaths twin, honesty counters, script hardening incl. new Function parse and the single guarded GRAPH.nodes[ read, degrade pages) and insights-command.test.mjs (9 tests cloning the graph-command harness plus the window argument and the both-halves smoke). Wiring gains the completion/routing pins.

… (issue #175)

The fourth insights slice, the one the series exists for: /dispatch
insights html [7d|30d|mtd] writes <graphDir>/insights.html, one
self-contained file:// document -- the trigger/flow topology with spend
badged onto its triggers, beside the cost fold drawn as hand-rolled
inline SVG charts (KPI tiles, plan verdict cards, a daily spend column
chart, the four breakdown bar lists).

- graph-html.mjs gains exports, not loads: buildGraphScene is the
  normalize+layout+SVG-emission half of buildGraphHtml extracted whole
  (behavior-preserving; every existing pin green untouched), plus
  escapeHtml/embedJson/fmt/PAGE_JS/legendHtml/bannersHtml/PAGE_THEME.
  The purity pin is substring-level and directional on purpose: the
  module may be a source of truth for a sibling emitter, it may never
  load one itself.
- insights-html.mjs: two loads only (graph-html, panel), so the money
  strings come from the REAL fmtCost -- a plan-covered bucket draws a
  plan:<id> chip and NO dollar bar, estimates are dashed and
  translucent beside their ~ est. text (hue never the sole encoding),
  floors carry >= on the chart, gap days render as baseline ticks, a
  null byTrigger renders 'not computed'. Byte-deterministic under
  permutation, total on junk, allowlist-normalized, one script element,
  no fetching, no innerHTML, textContent-only tips, row->node
  cross-highlight by replaying a guarded synthetic click so PAGE_JS
  keeps sole ownership of selection. The topology pane takes the
  scene's own aspect ratio (a fixed pane letterboxed a flat topology
  into a mostly-empty band; caught by a headless-Chrome screenshot).
- index.ts: the insights subcommand (bare insights answers usage; the
  artifact IS the feature), USAGE/KNOWN_SUBCOMMANDS/description in
  step, completion for insights html <window>, assembleInsights
  composing the two existing assemblers, insightsHtmlCommand as
  graphHtmlCommand's structural twin (atomic stable path, URL first,
  headless skip-and-say, write-failure never opens). Default window
  30d, deliberately not costs' mtd: the topology half is pinned at a
  30d record window and one page's halves should agree.

Specs in the same PR: NEW REQ-INSIGHTS-HTML-EXPORT;
REQ-COST-ANALYTICS amended one sentence (the artifact joins the named
surfaces; rules (a)-(g) unchanged); DES-ADMIN-VIA-PI-EXTENSION amended
with the factoring facts and rejections (shared third emitter,
duplication, in-place folding, a charting dependency);
REQ-GRAPH-HTML-EXPORT UNCHANGED, checked (graph html stays the lighter
topology-only export); REQ-TOPOLOGY-GRAPH UNCHANGED, checked;
DES-GRAPH-EDGE-DERIVATION UNCHANGED, checked; DES-COST-FOLD-BY-SCAN
UNCHANGED, checked. New docs/insights.md; cross-links in docs/graph.md
and docs/costs.md.

Suite green in the CI posture: 2224 tests, 0 skipped, live Valkey.

Signed-off-by: Rob Boerman <robboerman@live.nl>
@edgehero
edgehero merged commit e3fdaf2 into main Aug 12, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant