| State | Accepted |
| Architectural Significance | MEDIUM |
| Domain | Developer Tooling |
| Document version | 1.0 |
Puts a served, interactive web UI on the read-only graph API introduced by
ADR-0008 and the cross-repo views of
ADR-0009. The UI is mounted by the
llama-free roteiro explorer server (crates/roteiro/src/main.rs#run_explorer)
beside the JSON data API (crates/roteiro/src/graph_api.rs#router), same-origin.
It consumes only that API's existing routes (/v1/graph/workspaces, .../topology,
.../matrix). It is deliberately distinct from the script-free static export
crates/roteiro/src/overview.rs#render_html (links --matrix --html), which
stays a single self-contained HTML file with no JavaScript.
Introduce client-side JavaScript to Roteiro for the first time — a hand-written
ES app plus one vendored third-party library, cytoscape.js — to power the
interactive workspace explorer's topology graph. The library is committed to the
repo as a single prebuilt UMD file and embedded in the binary with
include_str!, served from GET /vendor/cytoscape.min.js. There is no npm, no
bundler, and no build step: cargo build remains the whole toolchain, and the
asset is served verbatim, same-origin, so there is no CORS surface and no external
network fetch at runtime.
The roteiro explorer server already serves the workspace graph as JSON
(ADR-0008/0009). The workspace view the maintainer signed off on is genuinely
interactive: a radial hub-and-spoke topology (a hub app with deployment
satellites, drift badges, gold/slate edges) plus a scrollable config override
matrix. Two facts make hand-rolled SVG the wrong tool:
links --matrix --html is a file
you can email or commit as an artifact; adding JS to it would break that promise.
So the interactive app is a separate surface, served only by the live server.The open question this ADR settles is how to bring in a client-side library
without importing the npm/bundler ecosystem — which would contradict the project's
pure-cargo, no-C/C++-toolchain posture for the explorer feature (axum + tokio
only, ADR-0008).
roteiro explorer who wants to see the cross-repo topology and overrides
interactively, not just read JSON or a static table.explorer feature is pure Rust (axum + tokio, no
C/C++). A JS bundler would add a whole parallel toolchain for one library; a
prebuilt UMD file needs none.npm audit). Mitigated by pinning the version,
recording it here, and the library's small, stable surface.Vendor a single prebuilt cytoscape.js UMD file, embed it with include_str!, and
serve it same-origin from the explorer server — no npm, no build step.
crates/roteiro/src/assets/ holds index.html (the
shell), app.js (our hand-written app), and cytoscape.min.js (the vendored
library). All three are include_str!-embedded and served by
crates/roteiro/src/explorer_app.rs#router: GET / (and /explorer) → the
shell; GET /app.js; GET /vendor/cytoscape.min.js. Correct content-types,
&'static str bodies.explorer. The UI router is merged onto the data API in
crates/roteiro/src/main.rs#run_explorer only. A full serve build keeps
exposing just the JSON API — no bundled UI, no change to that surface./v1/graph/* from
its own origin. Nothing is loaded from a CDN, so the explorer works offline and
in air-gapped installs, and the served bytes are the reviewed bytes.cargo build is the entire toolchain. The vendored file is a
prebuilt UMD bundle served verbatim; there is no npm, package.json, lockfile, or
bundler anywhere in the tree.crates/roteiro/src/overview.rs#render_html
(links --matrix --html) remains a single self-contained, JavaScript-free file.
The interactive app is a distinct surface with a distinct promise.<script> tag. Zero bytes in git, but adds a third-party runtime
dependency, breaks offline/air-gapped use, introduces a cross-origin fetch, and
makes the served UI non-reproducible. Rejected.cargo, no-extra-toolchain posture of the explorer feature. Rejected.include_str!, same-origin. Pays one
reviewed ~365 KB blob in git and a manual update cadence to get an interactive
graph with no build step, no CORS, and reproducible served bytes.crates/roteiro/src/assets/ directory (HTML/JS/vendored lib),
an crates/roteiro/src/explorer_app.rs#router mounted by run_explorer, and
Roteiro's first client-side JavaScript.npm audit, so the version and rationale live here.
Its MIT license header travels in the file.explorer and served only by
the llama-free server; serve/llama builds are untouched, and the explorer
feature still pulls only axum + tokio (no C/C++ toolchain, no rto-serve).| Version | Date | Notes |
|---|---|---|
| 1.0 | 2026-08-13 | Accepted and implemented (PR 4, the workspace-view UI). Introduces Roteiro's first vendored client-side JavaScript: a hand-written ES app plus cytoscape.js v3.30.4 (MIT) committed as a single prebuilt UMD file and include_str!-embedded, served same-origin by the explorer server (crates/roteiro/src/explorer_app.rs#router, mounted in crates/roteiro/src/main.rs#run_explorer) at GET /, /app.js, /vendor/cytoscape.min.js. No npm, no bundler, no build step; no CDN, no CORS, no runtime fetch. Consumes only the existing read-only API of ADR-0008 / ADR-0009 (/v1/graph/workspaces, .../topology, .../matrix) via crates/roteiro/src/graph_api.rs#router. The script-free static export crates/roteiro/src/overview.rs#render_html is explicitly kept JavaScript-free. Rejects hand-rolled SVG (re-implements a graph library badly), a CDN tag (third-party runtime dep, breaks offline, non-reproducible), and npm+bundler (a parallel toolchain for one library). |