Skip to content

feat(admin): the graph HTML export, and review hardening across the pipeline (issue #54) - #171

Merged
edgehero merged 1 commit into
mainfrom
feat/graph-html-export
Aug 11, 2026
Merged

feat(admin): the graph HTML export, and review hardening across the pipeline (issue #54)#171
edgehero merged 1 commit into
mainfrom
feat/graph-html-export

Conversation

@edgehero

Copy link
Copy Markdown
Owner

Final slice of issue #54, plus the fixes from a three-agent adversarial/code review of the whole delivery.

The export

/dispatch graph html [--no-open] renders the same assembled model as every other graph surface into one self-contained HTML file: inline SVG/CSS/JS, zero external requests, works over file://, nothing listens anywhere. Node-RED shape language (pale category chips on the 20px grid, ports, status dots, folder group rects) on the repo's dark chrome; hand-rolled Sugiyama-lite layout; evidence-labelled wires with a legend, the caps line, and the honesty counters; pan/zoom, hover tooltips (textContent-only), click-to-highlight.

The refresh loop: a Reload button, off/5s/30s auto-reload, a live staleness stamp, and hash-persisted view state — made useful by the stable artifact path: the command overwrites <graphDir>/graph.html atomically (tmp+rename), so an open tab picks up a re-run without losing its pan/zoom. URL printed before the spawn; skip-and-say over SSH/headless; a write failure names the path and never opens.

The review hardening (all CONFIRMED by executing reviewers, all regression-pinned)

  • joinRunsToTriggers guarded only the index range, so an in-range triggers.json edit slid one trigger's run history onto whatever entry took its row — the exact lie REQ-TOPOLOGY-GRAPH forbade. The persisted triggerType (sitting unused) now must agree; the same-type-reorder residual is named in the requirement and pinned by test.
  • chainRefused counters folder-scoped; observed edges into unreachable folders dropped-and-counted instead of minting phantom "[missing at HEAD]" endpoints; folderless cron entries keep their config edge; unreadable injected dirs surface in the counters; the folder-cap flag actually reaches meta.truncated.folders; the mention heuristic tests every occurrence.
  • LIST trigger rows now carry the raw file index (the display position was a live-fire wrong-delete past a dropped row); Esc from a GRAPH-entered drill returns to GRAPH.
  • In the page: under-row routes no longer collide with the next row of chips; the pan/zoom mapping respects preserveAspectRatio letterboxing; a background pan no longer wipes the selection; #sel=constructor no longer throws; parallel wires bow apart and their labels never stack. All mutation-verified.
  • Eight literal NUL bytes (the recurring trap) found by the pre-push scan and repaired; the whole tree scans clean.

Specs (same PR)

DES-ADMIN-VIA-PI-EXTENSION AMENDED (a file with no server is not a surface; the socket→file substitution is DES-JOB-OUTBOX-CHAINING's own; two new Rejected entries: a served graph page, and .log bytes in the artifact). DES-QUEUE-BULLMQ-OVER-CUSTOM's "drops the web surface entirely" reworded to "the served web surface" rather than argued around. NEW REQ-GRAPH-HTML-EXPORT; REQ-TOPOLOGY-GRAPH (b) amended to a promise the persisted fields can keep; NEW OQ-024 (opener-spawn WATCH); SECURITY.md export paragraph; docs + READMEs.

Verification

Suite in the CI posture: 2166 pass, 0 skipped; node admin/build.mjs builds. End-to-end: the artifact generated from a real fixture deployment (git repo with three skills, three triggers, chain + forge records) renders in headless Chrome with all controls working and no console errors — screenshots taken before and after each fix round.

…ipeline (issue #54)

Two things in one PR, both issue #54: the browser artifact, and the
fixes from the three-agent review that ran against the whole delivery.

The export. /dispatch graph html renders the same assembled model as
every other graph surface into ONE self-contained HTML file: inline
SVG/CSS/JS, zero external requests, works over file://, nothing
listens anywhere. Node-RED shape language (pale category chips, 20px
grid, ports, status dots, group rects per folder) on the repo's dark
chrome; hand-rolled Sugiyama-lite layout; evidence-labelled wires with
a legend, the caps line, and the honesty counters. The page carries
its own refresh: a Reload button, off/5s/30s auto-reload, a live
staleness stamp, and hash-persisted view state, which the STABLE
artifact path makes useful: the command overwrites <graphDir>/graph.html
atomically (tmp+rename), so an open tab picks up a re-run without
losing its pan/zoom. The URL prints before the spawn; over SSH or
without a display the spawn is skipped and said; --no-open always
skips it; a write failure names the path and never opens.

The hardening, every item CONFIRMED by an executing reviewer and now
pinned by a regression test:
- joinRunsToTriggers guarded only the index RANGE, so an in-range
  triggers.json edit slid one trigger's run history onto whatever
  entry took its row. The persisted triggerType (sitting unused) now
  must agree; the same-type-reorder residual is named in
  REQ-TOPOLOGY-GRAPH and pinned.
- chainRefused counters folder-scoped (same-folder-only chaining makes
  same-named flows different flows).
- Observed edges into unreachable folders minted phantom
  missing-at-HEAD endpoints off a read that never happened; now
  dropped into the counter.
- LIST trigger rows carried the display position, not the raw file
  index: with a garbage entry above, x+y deleted the wrong entry and
  reported the real trigger gone while it kept firing.
- Esc from a GRAPH-entered trigger drill returned to LIST, discarding
  the operator's graph position.
- collectGraphInputs' folder-cap flag never reached the model, so the
  cap banner could never fire; folderless cron entries lost their
  config edge; unreadable injected dirs vanished silently; the mention
  heuristic tested only the first occurrence; the file:// URL was
  concatenated rather than pathToFileURL'd.
- In the page: under-row routes collided with the next row of chips,
  the pan/zoom mapping ignored preserveAspectRatio letterboxing,
  every background pan wiped the selection, #sel=constructor threw at
  load, and parallel wires stacked their labels into garble. All fixed
  and mutation-verified.

Specs: DES-ADMIN-VIA-PI-EXTENSION AMENDED (the artifact; a file with
no server is not a surface, and the socket-to-file substitution is
DES-JOB-OUTBOX-CHAINING's own); DES-QUEUE-BULLMQ-OVER-CUSTOM's 'drops
the web surface entirely' REWORDED to 'the served web surface' rather
than argued around; NEW REQ-GRAPH-HTML-EXPORT; REQ-TOPOLOGY-GRAPH (b)
AMENDED to a promise the persisted fields can keep; NEW OQ-024 (the
opener-spawn WATCH residual); SECURITY.md export paragraph;
docs/graph.md and README updated.

Also repaired in passing, because this commit's own pre-push scan
caught it: six literal NUL bytes in graph-model.mjs and two in
read-model.mjs, written as template-literal separators by an agent
where spaces belonged (the recurring trap CLAUDE-adjacent history
records twice). They were functionally invisible (both sides of every
Map key used the same byte, and the NULs sat past git's 8000-byte
binary-detection window, which is why no Bin marker ever showed), but
a control byte in source is a control byte in source. Replaced with
the spaces they were meant to be; the whole tree now scans clean.

Verified end to end: the artifact generated from a real fixture
deployment renders in headless Chrome with pan/zoom, tooltips,
highlight, refresh controls and no console errors.

Suite in the CI posture: 2166 pass, 0 skipped; admin bundle builds.

Signed-off-by: Rob Boerman <robboerman@live.nl>
@edgehero
edgehero force-pushed the feat/graph-html-export branch from d743b84 to 25cc9ce Compare August 11, 2026 18:59
@edgehero
edgehero merged commit bd810a4 into main Aug 11, 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