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.
11 KiB
C22 — The playground is a trace explorer; sinks are the recording mechanism: history
FROZEN HISTORICAL RECORD. Each block below was true as of its cycle/date stamp and may be superseded; this file is NOT current truth and NOT a grounding surface. Current contract: c22-playground-traces.md.
Realization (cycle 0006). Sinks-as-recording-mechanism is realized at the
substrate level: a recorded trace is exactly what a recording node pushed out of
the graph (no engine recording registry; the constructing World holds each
recording node's destination). The engine's single observe: usize affordance is
removed — Harness::run returns () and recording is a node-side concern, so one
run records many streams (one per recording node) instead of exactly one row.
Recorded streams are sparse and timestamped (a record per fired cycle, tagged
ctx.now()), matching a trace of timestamped events (C18). No new contract; the
Harness API change (observe removed, run -> ()) is recorded here.
Realization (the web-from-disk visual face, #101). The C14/C22 web seam shipped
(amendment 3b56efb): a recording run persists each drained tap as a columnar (SoA,
C7) ColumnarTrace to runs/traces/<name>/ (aura-registry::TraceStore, beside the
run registry's runs.jsonl), reachable via aura run [--real <SYM> …] --trace <name>; aura chart <name> [--panels] reads them back, aligns all taps on a
synthetic-union timestamp spine via the post-run join_on_ts (C3 — not in-graph;
C1 — pure, no live external call), and emits a static self-contained uPlot page
(vendored like render_html's Graphviz-WASM). The engine stays headless (C14):
encoding lives in aura-engine (ColumnarTrace, struct→JSON only), file I/O in
aura-registry, rendering in aura-cli (render_chart_html + chart-viewer.js).
First cut only — overlay (per-series y-scale) / timestamp-aligned panels; the served
page injected the chart series but no run-context header (the run manifest is
persisted on disk in index.json, deliberately not surfaced in that first cut — a
ratified scope call). The header (#102) and serve-time decimation (#108) landed in
cut 2 (see the served-page-hardening amendment below); the families-comparison view
landed too (#107). A local server and replay-clock controls remain open (see "Open
architectural threads").
Amendment (family-member traces, #104, d3cb5f8). The disk-trace layout extends
to family runs: aura sweep|mc|walkforward --trace <name> persists each member as
a nested standalone run-dir runs/traces/<name>/<member_key>/, reusing
persist_traces/TraceStore verbatim (engine untouched — persistence is a
side-effect inside each per-member closure). The member_key is content-derived and
deterministic — sweep: the varying axes rendered as a filesystem-portable
directory component (<axis-name>-<value> tokens joined by _, charset
[A-Za-z0-9._-], case-less values, length-capped with an FNV fallback), MC
seed{N}, walk-forward oos{ns} — never
a runtime ordinal, because members run in parallel under the engine's Fn + Sync
HOFs, where a counter would be schedule-dependent (C1); concurrent TraceStore::write
targets disjoint member dirs, so it is lock-free. Opt-in: without --trace,
stdout/registry are byte-unchanged. Because TraceStore resolves <name>/<member_key>
as a subpath, aura chart <name>/<member_key> charts any single member with no
view-side change — the write-side precondition for the family-comparison view
(overlay / small-multiples across members) — now realized (#107; see the
families-comparison amendment below).
Amendment (CLI --trace retired, #168 / #224). The CLI-verb --trace story above (single-run run --trace, amendment 3b56efb; per-member sweep|mc|walkforward --trace, #104) describes capabilities that no longer reach the code. run and mc refuse --trace (structurally parseable, refused at dispatch); the per-member family --trace was never wired to the blueprint sweep (run_blueprint_sweep's let _ = persist) and was silently dropped when the welded --strategy triple retired (#159/#220). #168 makes the surface honest — sweep/walkforward --trace now refuse up front (exit 2, forward-pointer to #224). The single live TraceStore::write site is the campaign presentation.persist_taps → runs/traces/<name>/ (persist_campaign_traces); aura chart <name> reads that back. Restoring CLI-side per-member trace-writing is deferred to #224. [Delivered 2026-07-11 (#224 + fieldtest B1 fix): sweep/walkforward --trace now WRITE per-member traces on the real-data campaign path (the depth-2 fan-out runs/traces/<name>/<cell>/<member>/), refusing only on the synthetic path; aura chart <family handle> resolves both the depth-1 campaign layout and the depth-2 fan-out (members keyed <cell>/<member>, C1-sorted). The refusal story in this amendment is historical.]
Amendment (real-data family source, #106, 8e5d14b). The family runs gain an
opt-in real-data source axis: aura sweep|walkforward --real <SYMBOL> [--from <ms>] [--to <ms>] streams real M1 close bars from the data-server archive (the #71
M1FieldSource seam — C6 record-then-replay: a pre-recorded archive, never a live
call mid-sim) instead of the built-in synthetic stream, per family member. A
CLI-side DataSource provider (synthetic | real) supplies each member's source,
pip (resolved from the recorded geometry sidecar (instrument_geometry) — C10
refuse-don't-guess; a symbol with no recorded geometry exits 2 before any data
access), window, and — for walk-forward — its WindowRoller sizes
(synthetic: bar-index 24/12/12; real: fixed calendar-time 90d/30d/30d in ns; CLI
flags for these are deferred). [Amended 2026-07-11 (#239): on the dissolved
walkforward --real/mc --real sugar path the fixed 90/30/30 sizes are a
ceiling, not a constant — fit_wf_ms_sizes (aura-cli) passes them through
byte-identically whenever they fit the resolved campaign window, and scales them
down preserving the 3:1 IS:OOS ratio (step = OOS, one roll at the minimum) when
the window is shorter; authored process documents still validate their declared
sizes as-is.] The engine, ingest, and registry are untouched (C9 —
the whole change is in aura-cli); the synthetic path is byte-unchanged.
MC is excluded (#106 Fork A): its seed varies a synthetic price-walk
realization (C12 axis 4), which is undefined over real data's single realization —
aura mc --real refuses with exit 2; a real MC needs a bootstrap-resampling axis
(its own cycle). The real walk-forward member key oos{from.0} widens from a small
synthetic bar index to an epoch-ns integer (still portable, collision-free). The
built-in demo strategy's length grid is likewise data-kind-dependent
(DataSource::strategy_lengths): synthetic keeps the short lengths that fit the
18/60-bar demo streams (trend SMA {2,3}×{4,5}, MACD 2/4/3), real uses realistic
M1 lengths (trend {50,100}×{200,400}, MACD 12/26/9) so the cross is a genuine
trend signal over tens of thousands of bars, not noise. This — like the roller
sizes — is a demo-strategy calibration patch; the real answer is project-authored
strategies (C9: a project crate owns its own grid), deferred to the project-env
work.
Amendment (families-comparison view, #107, 4c64feb). The family-comparison
view the #104 amendment left open is now realized. aura chart <name>
classifies the name on disk (TraceStore::name_kind: a top-level index.json is a
single run; else member subdirs with index.json are a family; else not-found) and,
for a family, overlays one tap (default equity, --tap to pick) of every
member in one self-contained uPlot page: build_comparison_chart_data emits one
labelled series per member, all on a single shared y-scale — members measure one
identical quantity, so a shared scale is what makes them comparable, unlike the
single-run overlay of different taps (per-series scales). The series align on the
same union-ts spine via join_on_ts, so 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). The view
consumes a set of runs (&[FamilyMember] from TraceStore::read_family, sorted
by key for determinism, C1); a family is its first producer — the deliberate first
step toward the programmable analysis meta-level (C21) without rebuilding the
orchestration axes (that is its own later cycle). Name resolution is made a total
function by the write-guard TraceStore::ensure_name_free, called once per tracing
command (run/run <bp.json>/run --real → Run; sweep/mc/walkforward → Family)
to refuse cross-kind name reuse — so the one ambiguous on-disk state (a name used by
both a run and a family) is unreachable. Every error path exits 2, never panics
(C18/C10 refuse-don't-guess). Engine untouched (C9/C14): the whole change is
aura-registry (the resolver + classifier + guard) + aura-cli (the builder +
emit_chart name-kind branch + --tap); the single-run page is byte-unchanged when
no --tap is given. Still deferred: multi-column tap selection
(build_comparison_chart_data projects column 0 — the comparison taps in scope are
single-column f64; #47); and the orchestration-composability rebuild (sweep/MC/WFO as
composable meta-level tools).
Amendment (served-page hardening — cut 2, #102 + #108, 476342d). The two deferred
follow-ons the #107 amendment named are realized, both confined to aura-cli (engine
- registry untouched, C14). Run-context header (#102): a
ChartMeta(kind/name/commit/window/broker/seed/taps, + member count for a family, + bound params for a single run) is built from theRunManifestinbuild_chart_data/build_comparison_chart_data, injected intowindow.AURA_TRACES.meta, and rendered by a purebuildHeaderinchart-viewer.js(headless.mjs-guarded, likebuildCharts). The family window is the span across members(min from, max to)— the only reading that labels a disjoint walk-forward family's true OOS coverage (it collapses to the shared window for sweep/MC). Decimation (#108): a puredecimate(ChartData, buckets) -> ChartDatamin-max transform thins the served page to ≤ ~2·CHART_DECIMATE_BUCKETSspine slots (per-bucket min+max, nulls preserved), applied inemit_charton both paths — a multi-year M1 family now renders a few-thousand-point page instead of 100s of MB; full recorded data stays on disk (view-only), and the walk-forward null-fill page collapses for free. Deterministic (C1): pure bucketing over the sorted-deduped spine, no-op under budget. Refinement (#111,2957561): the per-bucket reduction is tap-aware — an envelope series (equity) keeps min/max, but a bounded level series (the C10 bias level — pre-reframe: exposure — ∈[-1,+1]) reduces by per-bucket mean (aReduceKindon eachSeries, set byreduce_for_tap). Without it min/max made every multi-year exposure a solid -1..+1 band (each bucket straddles many sign flips), reading as per-point oscillation; the mean shows the net/duty-cycle level. Deferred refinements: rendering the min/max envelope honestly as range bars / OHLC rather than a polyline (#112); a--widthbudget flag and true intra-bucket min/max ordering for the non-default continuous x-mode (#110).