Add a documentation field to composites, surfaced in the graph tooltip #125
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The graph viewer conveys what a node is structurally (type, ports, params) but
not why a composite is composed as it is. A composite carries an authored
design decision — which sub-nodes, wired which way, and to what end — that
exists nowhere in the runnable artifact (the boundary dissolves at compile time,
C23) and nowhere in the rendered graph. An optional
docstring onComposite,authored beside the composite's name and surfaced on hover, captures that
rationale at the tier where it actually lives.
Why composites, not primitives
A primitive's documentation ("what an SMA is") is reference material that
belongs in rustdoc / the generated API docs, and is largely redundant with the
module rustdoc that already exists (e.g.
crates/aura-std/src/sma.rs:1-19). Thedocumentation that exists nowhere else is the compositional "why": "this is a
fast/slow SMA spread that detects trend changes", "this leg is clamped on
purpose". That rationale scales with composition, so the field belongs on
Composite. Primitive-node docs are explicitly out of scope here.One field covers both "sub-graph" and "experiment"
There is no separate
Blueprinttype. The module doc states it verbatim(
crates/aura-engine/src/blueprint.rs:7-9):So a single field,
doc: Option<String>onComposite, reads on two nestinglevels with no second mechanism:
sma_cross,signals,trend) → "why this sub-graphis wired this way"
up this way"
Home
struct Compositeatcrates/aura-engine/src/blueprint.rs:141-147hasname: Stringas its first field. Thedocfield sits beside it.nameis documented (blueprint.rs:152) as "a non-load-bearing rendersymbol (the cluster title for #13); it does not reach the flat graph (the
boundary dissolves at inline, C23)." A
docstring is the same category — theprose twin of the name.
.doc(impl Into<String>)onComposite::new(
blueprint.rs:154-162) and/or onGraphBuilder::new(
crates/aura-engine/src/builder.rs:80).Invariant fit (no ledger amendment; realization note under C9/C23)
evalpayload;it is metadata-beside-the-hot-path, the explicitly-blessed category.
dropped at lowering):
Compositeis authoring-only and inlines away entirely —it never reaches
FlatGraph/Harness. This is the most conservative possiblehome for the field (more so than
NodeSchema, which rides inFlatGraph.signatures).structure-view tooltip; no write-back, no scene-editing.
Render path
"doc"field incomposite_def_json(
crates/aura-engine/src/graph_model.rs:177) for nested composites, and in the"root"record (graph_model.rs:264, the{"root":…,"composites":…}shape)for the blueprint-level doc.
so the doc must be threaded into both:
addInfo(...)→INFO.B[cid](
crates/aura-cli/assets/graph-viewer.js:139calling:109)INFO.C["clust_"+cid](
graph-viewer.js:134)md()renderer (graph-viewer.js:183) already converts\n,**bold**,and
`code`for the#tipdiv — no new tooltip machinery.Hard gate: JSON escaping (verified)
json_str(crates/aura-engine/src/graph_model.rs:47-58) escapes only"and\;\n,\t, and control characters pass through raw:This is safe today because every value it serializes is an identifier. A free-text
doc is the first multi-line value to pass through it — and
md()actively wants\n(it converts\nto<br>), so multi-line docs are the expected case, notan edge case. Without hardening, the first multi-line doc produces invalid JSON.
This must land in the same change.
Acceptance
doc: Option<String>onComposite+ a.doc()builder onComposite::new/GraphBuilder"doc"emitted incomposite_def_json(nested) and the"root"record(blueprint-level)
INFO.B(collapsed) andINFO.C(expanded)
json_strhardened to escape\n,\t, andU+0000–U+001F(\u00XX)graph_model.rs:497),tests/fixtures/sample-model.json,and the
viewer_*.mjsassertions updated for the new optional fieldOut of scope
INFO.Pslot; edges have nomodel identity — separate, deferrable decisions).
Open questions
(
INFO.B/INFO.C), but the root doc emits into the"root"record and needs adisplay surface (a root frame or a graph header). Small, not a blocker.
fork — the same field works at every nesting level. A reusable composite shipped
in a project's
nodes/(C9) wants its own doc, so it is not root-restricted.(no instance renaming, unlike a primitive's
.named()), so the doc istype-level. If composite instancing is added later, an instance-level overlay is
a separate concern.
Related
#28(expose rendering for a consumer's OWN graph, not just the built-insample) gates whether project-authored composite docs are viewable at all.
Today
aura graphrenders only the built-in sample — which does containcomposites (the golden model at
graph_model.rs:497showssma_crossunder"composites"), so engine-side composite docs are viewable immediately; aproject's own composite docs depend on
#28.Triage 2026-07-09 (tree at
68317ec) — premise intact, acceptance list needs two additions from the moved tree:aura graphpath, and (b) get a one-sentence decision on its treatment in the identity-canonical hash (presumably stripped, like other non-load-bearing debug symbols). Optionally the construction Op vocabulary grows a doc-carrying surface.Design reconciliation (specify)
Spec: composite-doc-tooltip. The forks below are listed open (or newly arose from the 2026-07-09 triage) on this issue; this records their resolution (2026-07-11, autonomous run).
doc→ stripped in the identity projection, exactly like the compositenameit sits beside.Basis: derived —
strip_debug_symbols(crates/aura-engine/src/blueprint_serde.rs:143) already blanks every non-load-bearing debug symbol (composite name, instance names, bound-param names, role names, output names, gang names); a doc string is the prose twin of the name and inherits its category.Basis: derived — the Op vocabulary change is additive and separable; an Op-built composite simply carries no doc until that vocabulary grows a slot, while the serde roundtrip (the display path) already tolerates the absent field.
<header>/.subchrome, populated only when the root doc is present).Basis: derived — the page already owns a header with breadcrumb + hint line; a doc line there is the smallest surface that shows the root doc without inventing a new chrome region or overloading a hover map that has no root target.
docis a Tier-1 additive-optional field; NOBLUEPRINT_FORMAT_VERSIONbump.Basis: derived — the loader's documented two-tier discipline (blueprint_serde.rs
LoadError, #156) states an additive-optional field does not bump the version; an older reader tolerates it (pinned byunknown_optional_field_is_tolerated_byte_identically) and the field defaults to prior behaviour.The json_str escaping hardening (escape
\n,\t, U+0000–U+001F) is not a fork — the issue body's verified gate stands (re-verified at2532e8f: crates/aura-engine/src/graph_model.rs:47-59 still escapes only quote and backslash) and lands in the same change.Autonomous spec sign-off (2026-07-11): the spec for this issue's capability — an optional authored
doconComposite, surfaced in the graph tooltip (both view states) and as a root header line, with serde round-trip, identity-strip, andjson_strhardening — was signed by agrounding-checkPASS (all seven load-bearing current-behaviour assumptions ratified by named, currently-green tests), not by a human. The previously unpinned viewer-tooltip layer was pinned first (viewer_tooltip.mjs, commit at the pin'srefson this issue) after an initial BLOCK named it. Design-fork resolutions: [the reconciliation comment above in this thread] — restated: identity-strip likename; Tier-1 no-version-bump; root display = muted header line; construction-Op surface out of scope.Implementation complete (2026-07-11, autonomous run; commits pending push):
Compositecarries the optional authoreddoc—with_doc/doc()plus aGraphBuilder::docknob; persisted as a Tier-1 additive-optional serde field (no format-version bump, absent-field documents byte-unchanged); blanked in the identity projection like the name while staying canonical-byte-bearing; emitted as an optional trailing"doc"fragment in both model scopes withjson_strhardened for multi-line free text; shown in both composite view states (collapsed tooltip via INFO.B, expanded cluster frame via INFO.C) and as a muted root header line (#rootdoc). e2e drivesaura graphover root-doc'd and nested-doc'd blueprint files; the doc moves the content id but never the identity id.Accepted boundaries, per the reconciliation in this thread: the construction op-script vocabulary has no doc-carrying surface (marked at
GraphSession::finish); the root header's DOM population line sits in the browser-only block, the same untested boundary as the breadcrumb (the headless pins covernormalizeModel(...).root.docand the empty header slot).