8688a60ded
The single-file ledger had grown to 2968 lines / ~42k tokens, mixing current design law with accreted history: 59 cycle-stamped realization blocks, 18 [HISTORY] passages, 22 supersession markers, and the C10 / C22 / C24 reframe sagas layered several supersessions deep. A code-grounding audit (31 agents, adversarially verified) confirmed 11 defects stated as current truth: stale crate homes from the C28 #288 roster split (cost nodes, PositionManagement, PositionEvent, Session), the renamed InputSpec->PortSpec, the pre-#241 project model in C16 and the open-threads section, a stale HarnessKind retirement deferral in C24, and three C28-internal inconsistencies. New shape, per the ailang precedent: - INDEX.md stays the sole addressable entry point: foundation, external components, a C-id-keyed contract map (one line per contract), and only the genuinely open architectural threads. - contracts/cNN-<slug>.md carries each contract's current truth only: Guarantee / Forbids / Why with ratified refinements integrated, plus a code-anchored Current state. All confirmed defects are fixed here; crate anchors were re-verified against the tree. - contracts/cNN-<slug>.history.md (18 sidecars) and INDEX.history.md preserve every superseded block verbatim, stamps and issue refs intact, under a frozen-record banner. Nothing was deleted: superseded design intent remains an addressable working-tree artifact, off the per-cycle audit walk. - Ledger discipline is now stated in INDEX.md: live files are edited in place at cycle close, superseded text moves verbatim to the sidecar, and a supersession marker in a live file is itself an audit finding. Every contract file was verified against its old text by an independent zero-loss pass (statement-by-statement) plus a code-accuracy spot check; C-ids and contract titles are unchanged, so existing C-id citations in code, tests, and issues resolve as before.
145 lines
9.8 KiB
Markdown
145 lines
9.8 KiB
Markdown
# C22 — The playground is a trace explorer; sinks are the recording mechanism
|
|
|
|
**Guarantee.** The World is a *program* (C21): nothing is displayable until it
|
|
runs, and its harnesses are transient machinery (built, run, discarded — C19). The
|
|
only durable, displayable substance is the **recorded trace**, captured by
|
|
**sinks** — pure consumer nodes (C8) that persist a stream (equity, a bias /
|
|
exposure level, a node's output) into the run registry (C18). **Displayable =
|
|
exactly what a sink recorded**; with no sink, only the input params + summary
|
|
metrics remain. The **playground** is therefore an **execution viewer / trace
|
|
explorer**, and it plays *any* harness (the harness *player*, not a harness, never
|
|
bound to a default one): before a run it shows the program *structure*
|
|
(graph-as-data, C9) + param knobs; during a run, live sink streams; after a run,
|
|
recorded traces + metrics from the registry — including meta-views (stitched
|
|
walk-forward, sweep surfaces, multi-strategy / instrument comparison).
|
|
Observability is **explicit and selective** — you instrument what you want to see;
|
|
the choice of sinks is part of the experiment. The engine ships sample blueprints
|
|
and a scaffolded starter so a newcomer has something to run at once; a fresh
|
|
project is empty until built, run, and instrumented. The shipped visual face is
|
|
**web-from-disk** — disk-persisted traces rendered as self-contained HTML chart
|
|
pages (not egui).
|
|
|
|
**Forbids.** A scene-editor model that assumes a persistent, populated world;
|
|
constructing or wiring topology in the UI (topology is Rust + hot-reload, C9/C17 —
|
|
the UI reflects the live graph-as-data and tunes runtime params via sliders, C12);
|
|
retaining un-sinked data; binding the playground to a single / default harness. The
|
|
"never a scene editor" forbid is precisely **no persistent scene-at-rest** (a World
|
|
is a program) — it does *not* forbid a future blueprint *editor* over the
|
|
World-owned topology data (C24); that data format would be the substrate such an
|
|
editor round-trips, not a new mechanism.
|
|
|
|
**Why.** A program has no scene-at-rest to inspect; what persists is what it
|
|
records. Making sinks the one recording-and-observability mechanism keeps "what can
|
|
I see?" answerable by "what did I instrument?", and keeps the engine UI-agnostic
|
|
(C14). Live param tuning (runtime values, no topology change — C12 / C19) gives the
|
|
interactive feel without a wiring DSL. The visual face is **orthogonal and
|
|
optional**: the blueprint data format (C24) is required by C21 (generation /
|
|
structural search) and C18 (reproduction) *at the CLI level, autonomously*,
|
|
independent of any visual surface — so the playground's form (web or egui, viewer
|
|
or editor, or not built at all) is downstream, and topology authoring does not live
|
|
in it. Generalized by the role model (C25): every artifact's canonical, complete
|
|
form is **text**, every operation is executable headless, and any visual surface is
|
|
a **stateless projection** that reads/writes that canonical form and adds no
|
|
semantics; read-only projections (this trace explorer) rank far above write
|
|
editors, because the Blockly-litmus discipline (C25) already keeps each vocabulary
|
|
palette-generatable without one.
|
|
|
|
## Current state
|
|
|
|
**Sinks at the substrate.** A recorded trace is exactly what a recording (tap) node
|
|
pushed out of the graph — there is no engine recording registry; the constructing
|
|
World holds each recording node's destination. `Harness::run` returns `()` and
|
|
recording is a node-side concern, so one run records *many* streams (one per tap)
|
|
rather than exactly one row. Recorded streams are sparse and timestamped (a record
|
|
per fired cycle, tagged `ctx.now()`), matching a trace of timestamped events (C18).
|
|
|
|
**Encoding / storage / rendering split (C14).** A drained tap is encoded as a
|
|
columnar (SoA, C7) `ColumnarTrace` — struct→JSON only — in `aura-engine`
|
|
(`report.rs`); file I/O is `aura-registry::TraceStore`, persisting each run under
|
|
`runs/traces/<name>/` beside the run registry's `runs.jsonl`; rendering is
|
|
`aura-cli` (`render_chart_html` + the vendored `chart-viewer.js`). The engine stays
|
|
headless.
|
|
|
|
**Single run.** `aura run <blueprint.json>` persists every tap the blueprint
|
|
declares to the trace store under the run's own name, on both shapes: a
|
|
`bias`-output strategy (`aura-runner::member::run_signal_r`, R-wrapped) and a bare
|
|
measurement blueprint with ≥1 declared tap but no `bias`
|
|
(`aura-runner::measure::run_measurement`). A tap-free run writes nothing to `runs/`
|
|
and its stdout stays byte-identical. The CLI `--trace <NAME>` flag is **retired** on
|
|
`run` (and on `mc`): it parses but is refused at dispatch (exit 2) — naming a trace
|
|
is the family / campaign path's job, not a per-run flag.
|
|
|
|
**Families.** `aura sweep|walkforward --real <SYM> … --trace <NAME>` persists *each
|
|
member* via the campaign path: the dissolved verb is translated into a
|
|
content-addressed process + campaign document whose `presentation.persist_taps`
|
|
requests the tap vocabulary, run through the one campaign executor;
|
|
`aura-runner::runner::persist_campaign_traces` writes each member under a depth-2
|
|
fan-out `runs/traces/<name>/<cell>/<member>/`. Every written member is
|
|
independently re-run once in non-reduce trace mode over its own recorded window,
|
|
and its re-derived metrics are asserted equal to the recorded member's — a **C1
|
|
drift alarm** that refuses (exit 1) rather than persist a silently-wrong trace. The
|
|
**synthetic** sweep / walk-forward path refuses `--trace` (exit 2);
|
|
**Monte-Carlo is excluded from trace persistence** (`mc --real` refuses
|
|
`--name`/`--trace`, exit 2 — the real-data R-bootstrap campaign itself runs, but
|
|
records no per-member family traces; the synthetic `--seeds` family's realization
|
|
argument, C12, does not carry over to one real series).
|
|
`TraceStore::ensure_name_free` makes name resolution a total function, refusing
|
|
cross-kind reuse of one name by both a run and a family.
|
|
|
|
**Viewer.** `aura chart <name> [--tap <t>] [--panels]` classifies the name on disk
|
|
(`TraceStore::name_kind`: top-level `index.json` → a single Run; member subdirs → a
|
|
Family; else NotFound, which then tries campaign-name resolution), reads the traces
|
|
back, and aligns all taps on a synthetic union-timestamp spine via the post-run
|
|
`join_on_ts` (C3 — a post-run join, not an in-graph merge; C1 — pure, no live
|
|
external call), emitting a self-contained uPlot page (vendored, like the graph
|
|
render). A **single run** overlays all its taps, each on its own y-scale (or the
|
|
one `--tap` selects); a **family** overlays **one tap** (default `equity`, `--tap`
|
|
to pick) of **every member** on a **single shared** y-scale — members measure one
|
|
identical quantity, so a shared scale is what makes them comparable. One mechanism
|
|
yields three correct readings: sweep / MC members share the data window (a true
|
|
overlay), walk-forward members are disjoint OOS windows (null-complementary → the
|
|
stitched curve). A **run-context header** (`ChartMeta` → `window.AURA_TRACES.meta` →
|
|
the pure `buildHeader`) renders chips for kind / name / members / tap / broker /
|
|
window / seed / commit / bound-params; a family's window is the **span**
|
|
`(min from, max to)` across members. Serve-time **min-max decimation** (`decimate`,
|
|
`CHART_DECIMATE_BUCKETS = 2000`) thins the page to a few-thousand spine slots (full
|
|
recorded data stays on disk, view-only), and the reduction is **tap-aware**
|
|
(`reduce_for_tap` / `ReduceKind`): an unbounded cumulative curve (`equity`) keeps
|
|
the per-bucket min/max **envelope** so drawdowns survive, while a bounded level
|
|
series (`exposure` / the C10 bias level, ∈ [-1,+1]) reduces by per-bucket **mean**
|
|
so its net / duty-cycle level shows instead of collapsing to a solid ±1 band.
|
|
`--panels` renders stacked panels instead of the overlay.
|
|
|
|
**Newcomer.** `aura new <name>` scaffolds a **data-only** project — a paths-only
|
|
`Aura.toml`, a runnable starter blueprint `blueprints/signal.json` over the std
|
|
vocabulary (an SMA-cross → `Bias` strategy), `.gitignore`, and a project `CLAUDE.md`
|
|
— no crate, no build step (`aura nodes new` attaches a native node crate, C16/C17).
|
|
The engine also ships example blueprints under
|
|
`crates/aura-cli/examples/r_{sma,breakout,channel,meanrev}.json`. Honest gap against
|
|
the Guarantee's "populated trace immediately": these shipped blueprints are
|
|
*strategies* (a `bias` output, no declared taps), so `aura run` prints summary
|
|
R-metrics to stdout but writes no on-disk trace — a **chartable** trace today comes
|
|
from a blueprint that declares taps (single run) or from a `--real … --trace` family
|
|
campaign.
|
|
|
|
**Deferred.** Live sink streams *during* a run are not built — taps are
|
|
buffer-then-drain (collected, then written after the run), and there is no local
|
|
replay server or replay clock; the shipped face is post-run static HTML. Reworking
|
|
tap draining into live subscribers (stream-to-disk / fold / live consumer) is #283.
|
|
Embeddable chart / graph fragments for hosting in external pages are unbuilt (#150).
|
|
The programmable analysis meta-level — families as composable orchestration
|
|
operators — is C21's open direction, not this contract's.
|
|
|
|
> History: [c22-playground-traces.history.md](c22-playground-traces.history.md)
|
|
|
|
## See also
|
|
- [C27](c27-declared-taps.md) — declared taps: the named sinks a run persists
|
|
- [C8](c08-node-contract.md) — sinks are pure consumer nodes
|
|
- [C21](c21-world.md) — the World is the program the playground plays
|
|
- [C24](c24-blueprint-data.md) — the topology data a future editor would round-trip
|
|
- [C25](c25-role-model.md) — visual surfaces as stateless projections
|
|
- [C14](c14-headless-two-faces.md) — headless core, two faces (the encode / store / render split)
|
|
- [C18](c18-registry.md) — the run registry the trace store sits beside
|
|
- [C9](c09-fractal-composition.md) — topology grown in Rust; the structure the viewer reflects
|
|
- [C10](c10-bias-r-cost.md) — the bounded bias / exposure level the viewer reduces by mean
|