feat(admin): the graph HTML export, and review hardening across the pipeline (issue #54) - #171
Merged
Merged
Conversation
…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
force-pushed
the
feat/graph-html-export
branch
from
August 11, 2026 18:59
d743b84 to
25cc9ce
Compare
This was referenced Aug 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 overfile://, 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.htmlatomically (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)
joinRunsToTriggersguarded only the index range, so an in-rangetriggers.jsonedit slid one trigger's run history onto whatever entry took its row — the exact lieREQ-TOPOLOGY-GRAPHforbade. The persistedtriggerType(sitting unused) now must agree; the same-type-reorder residual is named in the requirement and pinned by test.chainRefusedcounters 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 reachesmeta.truncated.folders; the mention heuristic tests every occurrence.preserveAspectRatioletterboxing; a background pan no longer wipes the selection;#sel=constructorno longer throws; parallel wires bow apart and their labels never stack. All mutation-verified.Specs (same PR)
DES-ADMIN-VIA-PI-EXTENSIONAMENDED (a file with no server is not a surface; the socket→file substitution isDES-JOB-OUTBOX-CHAINING's own; two new Rejected entries: a served graph page, and.logbytes in the artifact).DES-QUEUE-BULLMQ-OVER-CUSTOM's "drops the web surface entirely" reworded to "the served web surface" rather than argued around. NEWREQ-GRAPH-HTML-EXPORT;REQ-TOPOLOGY-GRAPH(b) amended to a promise the persisted fields can keep; NEWOQ-024(opener-spawn WATCH); SECURITY.md export paragraph; docs + READMEs.Verification
Suite in the CI posture: 2166 pass, 0 skipped;
node admin/build.mjsbuilds. 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.