Architect review over 9636b00..9221bcd. What holds, confirmed against the
diff rather than the commit body: no stored record shape moved (neither
`aura-engine/src/report.rs` nor the registry compat mirror appears in the
change at all, and the byte pins pass unedited); the handle travels the
beside-the-report route C27/#297 established for the unbound-tap names, with
the library still printing nothing; trace enumeration landed inside
`aura-registry` where C22 puts trace file I/O; no C14 exit class moved, both
refusal arms falling through to the same exit 1.
Two high drift items, both fixed here:
- The corrected refusal claimed a campaign run prints its handle as
`trace_name` on stdout. It prints `campaign_run.trace_name` — the campaign
leg wraps its record, as the glossary states outright — so the chain the
refusal advertised returns null there. This is the third instance of one
error class in this cycle: a false claim inside the very prose meant to
stop misdirecting callers. Grounding caught the first two, review the
third.
- The measurement leg shipped without the handle pin the spec asked for. All
three new pins drove the strategy path, so the measurement leg emitted a
handle nothing asserted. It has its own test now, on the shared bind pair
the two legs cannot drift across.
Ledger and vocabulary brought into lockstep: C27's current state records the
`RunOutcome` carrier that replaced the pair; the glossary's tap entry names
the single-run handle and warns that a family id is not one; the authoring
guide's own single-run tap example now says the name is printed rather than
guessed. The suggestion line gained the `aura:` prefix every other stderr
line carries.
Bench: all five fingerprints OK — the decisive signal, since they hang on the
record bytes this cycle was required not to move. Timing deltas are load
noise (loadavg 8.7 under concurrent agents), not regressions.
refs #309
A single run persisted its taps and then said nothing about where: the only
way to learn the directory name was `ls runs/traces/`, and the chart intake's
not-found refusal pointed at "the handle a sweep/walk-forward/campaign run
printed" — two verbs retired with #319, and a single run printed no handle at
all. A caller holding a family id from a families listing was stuck: that id
is not a trace handle, and nothing said so.
The handle now rides BESIDE the report, not inside it. Both declared-tap entry
points return `RunOutcome { report, skipped, trace_name }` in place of the
`(report, skipped)` pair, and the CLI composes report + handle into one stdout
object through a `serde(flatten)` wrapper. `RunReport`, `MeasurementReport`,
`RunManifest` and the registry's compat mirror are untouched, so no stored
record shape moves and a tap-free run's line stays byte-identical.
Placement is the load-bearing decision, and it follows a precedent rather than
inventing one: the report is the durable C18 run record — its manifest states
what the run *was* — while a trace directory is where its output went. The
project settled this exact question one cycle earlier for the sibling value,
where the unbound-tap names ride beside the report so the CLI, not the
library, prints the note (C27/#297). An embedding host gets the handle as a
value, never as text to parse back.
The chart refusal gains a second arm. A family id
(`{campaign8}-{strategy_ordinal}-{instrument}-w..-r..-s..-{run}`) is
recognised syntactically — the trailing segment shape plus an 8-hex head, the
form `derive_trace_name` mints — and answered with the campaign's REAL
recorded handles, filtered to those `chart` would accept, suggesting one only
when exactly one matches. It never derives a handle by truncating the id: the
id's second segment counts strategies while the handle's counts runs, and one
campaign run mints family ids at several strategy ordinals whose traces all
live under that single run's handle. `TraceStore::names()` supplies the
enumeration, keeping trace file I/O in the store where C22 puts it.
Verification caught three errors worth recording. The first spec draft claimed
the handle was the id's leading pair — refuted by a green test where a run-0
campaign mints ordinal-1 ids. The second draft's replacement refusal said a
campaign run "prints one handle per family"; it prints one per run. Review
then found the `NotFound` filter untested — deleting it left the suite green —
so the case now has a test that fails without it.
closes#309
RED-first fixes for the two code bugs the safe-to-embed milestone
fieldtest caught: (1) run_signal_r panicked (exit 101) on a blueprint
exposing neither bias nor a declared tap — the CLI pre-validated but the
library seam, the very surface the milestone promises is kill-free, did
not; the guard now refuses class 2 with the CLI's prose family before
wrap_r (run_measurement has no wrap seam and no hole). (2) The override
unknown-param refusal exited 1 through the one inline site the #297
adjudication missed; it and its wrapped-retired sibling are class 2 now,
three existing pins re-pinned with the adjudication comment.
Doc truth from the same fieldtest: the guide's campaign no-data class
corrected (contained cell faults exit 3, not 1), TapPlanError's rustdoc
no longer advertises the retired exit-1 register, and C30 records that
rev + the embedding's committed lockfile are the reproducible-build
contract (branch-referenced engine deps resolve at lock time).
refs #296
Architect review over 024e865..HEAD (issues #296/#348/#297/#299). Holds
confirmed: zero process::exit in the assembly crate with the milestone
promise pinned in-process; C1/C18 record shapes untouched with guard and
stamp sharing one label constructor; the C30/#348 re-arm transition-safe
with no stale references. Drift resolved in this commit: the C14 contract
now records the context-borne reproduce guard classes (#299), C30 carries
the source-level-identity limit in the contract itself (not only in
build.rs), the guide's exit-class prose no longer names the retired run
verb, C28 durably enumerates the deliberate print residuum, and the
pip_or_refuse doc names reproduce (not 'the CLI shell') as the from_choice
home.
Bench: all 5 fingerprints OK (engine/ingest/campaign_sweep/campaign_heavy/
cli_fixed_cost); metric deltas within load noise on a busy box (loadavg
12 warning), report-only, no baseline moved.
reproduce_family returns Result<ReproduceReport, RunnerError> — divergence
is a result the embedding caller reads (all_identical()), never an error;
the CLI renders the per-member lines and summary byte-identically and owns
the exit (1 on divergence, no stderr line). The library's print block and
the empty-message refusal are gone; exit_on_runner_error always prints
(no empty producer remains).
reproduce_family_in gains the identity/pip guard, firing before any store
fetch: an instrument mismatch (incl. synthetic-for-real) and a broker/pip
mismatch — forward-built label compare against the stored broker string
(RunManifest carries no pip field; the mint's label constructors became
shared pub(crate) helpers, so guard and stamp cannot drift). The class is
context-borne: the explicit-source seam refuses 2 (caller-named source),
the derived simple path refuses 1 (data drift), same prose. A window
guard is deliberately absent — no reproduce path takes a caller window.
In-process tests pin the promise: divergence returns Ok, drift refuses
class 1, the host survives. Fork minutes: issues/299#issuecomment-4875,
-4877, -4879.
closes#299
All 14 process::exit sites in aura-runner (member x8, measure x5,
translate x1) convert to RunnerError { exit_code, message } propagation;
run_signal_r / run_measurement return Result tuples carrying the
skipped-tap names beside the C18 reports (record shapes untouched), and
the CLI owns printing via the shared exit_on_runner_error arm (the
dispatch_reproduce pattern — reproduce now uses the same helper).
cost_knob becomes fallible: the sweep worker maps it into the cell's
MemberFault (closing the one hole in 'a worker never process-exits'),
the persist path keeps its string channel. The skipped-tap note is
CLI-printed from returned data, byte-identical.
The one deliberate behaviour change is the minuted C14 adjudication:
refusals in the content of what argv named (binding, synthetic mismatch,
compile, tap-bind content, exposes-neither) move exit 1 -> 2; the
boundary is FORM vs VALUE — an out-of-domain override value keeps the
runtime class via the panic containment, matching the campaign leg.
Environment/data/IO refusals stay 1 (TapPlanError::exit_class is the
single source). Named re-pins in exec.rs, run_refuses_unrunnable_
blueprint.rs, tap_recording.rs, graph_construct.rs; new library-level
test pins the milestone promise (a refusal returns in-process, the
embedding host survives). Prose is byte-identical everywhere.
Docs in lockstep: C28 deferred block closed (14 sites, hole closed),
C14 partition + form-vs-value boundary, C27 note emission, guide exit
classes. Fork minutes: issues/297#issuecomment-4868 and -4873.
closes#297
- parse_override_tokens distinguishes the grammar fault (no '=' / no
dotted path — prose unchanged) from a shape-valid token whose VALUE
fails the scalar-literal parse: the refusal now names the value and
the literal requirement (styled after the kind-mismatch sibling)
instead of falsely claiming a NODE.PARAM=VALUE violation. RED-first;
exit stays 2 on both branches.
- the authoring guide's multi-regime family ids corrected to the
executor's real shape (infix -r{k} after the window segment, -r0
explicit on the default regime), and §1's name-op paragraph no
longer claims naming dissolves same-name trace overwriting (that
trace-dir question is tracked on #311, not resolved here).
Harvest sweep, whole-diff review fix block.
The review refuted the #343 ratification's factual premise: the
campaign leg does NOT mint a reopened member's hash — runner.rs passes
the stored strategy_id as topo even for reopened members (reference
semantics). Decision revised on the issue (fix, not ratify): exec's
override run now stamps the LOADED base document's content id
(computed before reopen_all; RED-first — the inverted pin failed on
the old reopened-hash behaviour), keeping topology_hash
store-resolvable and identical in meaning across both legs; the
variation lives in manifest.params. C24/C12/C18 rewritten to the
corrected reading.
Also per review: C24's retired build-free-introspection sentence
updated (old wording archived in the history sidecar, #339 item 4);
C14's partition key precisely scoped to the routing seam, naming the
legs' differing content-validation exits honestly; measure.rs's false
unreachability comment corrected; --taps added to the guide's
introspect cheat-sheet and pinned on the content-id path; the
zero-trade-cell note routed through the diag macro; doc-comment,
issue-ref, and gate-name corrections; the Fixed-regime NaN branch
pinned.
refs #339
refs #342
refs #343
Harvest sweep, batch 6 of 6.
- #343 ratified (per the decision minuted on the issue): an override IS
a topology-parameter variation — the executed run's topology_hash is
the reopened topology's own, deliberately not required to resolve in
the store; the manifest's params map plus the base document
reconstruct the variant. C24 carries the full clause, C12/C18
cross-reference it; pinned by a bare-vs-noop-override hash test
(distinct, and stable across runs).
- #342 item 5: C14 pins exec's unresolved-target exit partition on the
existing usage/runtime line — file-content faults (not-JSON,
op-script, wrong-kind) exit 2, missing-state faults (unreadable
non-id target, unregistered content id) exit 1. One sanctioned
realignment: the wrong-kind refusal moves 1 → 2 to join its
file-content siblings (RED-first on the updated pin); the
unregistered-id row gains its missing sibling pin.
- #341 item 4: the authoring guide gains a worked cost-wiring op-script
(ConstantCost → CostSum via PositionManagement geometry, built and
verified), honest about being a wiring reference — production cost
models still come from a campaign document's cost block (C10).
closes#341closes#342closes#343
Harvest sweep, batch 5 of 6.
- introspect --unwired resolves use refs through the registry store:
the parse+resolve phase is extracted from graph build's path into the
shared parse_and_resolve_ops, so a use-bearing document introspects
identically to how it builds (fetch, C29 gate, label resolution) —
retiring both the hard miss and the 'content id' mislabeling of
by-name refs. The engine's subgraph closure contract is unchanged
(store-freedom binds the engine, not the CLI caller).
- ganging a spliced instance's member path refuses with the rule
(OpError::GangOfSplicedInstance) instead of the typo-shaped
no-such-param prose; the leaf-typo case keeps its UnknownParam shape
under a new sibling pin.
- the --node pending note names the follow-up moves on the current
surface (build, then introspect --params / --unwired), and the
authoring guide gains a worked LinComb op-script example
(args-then-bind order, term[i]/weights[i], verified by building it).
refs #339
refs #341
Slice 8, closing the #319 retirement's prose and bench debt. Bench argv
rides exec (fixed_cost single-run probe, campaign surfaces, and the seed
helper that still called the retired sweep at runtime — caught by the
bench run itself); all five fingerprints unchanged against the committed
baselines, confirming record-line parity through the retirement. Live
docs (README, authoring guide, project layout, glossary) describe only
exec + campaign documents + graph introspect --params; three glossary
entries explicitly mark their verb retired. Ledger amendments per the C29
discipline — C25's executor-verb-set re-settled (exec + the --override
residue), C14's dual grammar reduced to the one dispatch, C24's
document-built runs/families + gated-intake route list (lockstep with the
code comment), C12's override clause and identity anchor repointed to the
runner-layer hash computation, C18/C22/C27/C01 mention rewrites —
superseded sentences moved verbatim to the history sidecars (c25's
created). Stale dual-grammar doc comment on is_blueprint_file rewritten.
refs #319
The authoring guide's ops table grows the name row (ten -> eleven) plus
a paragraph on what the graph default costs a multi-strategy project
(indistinguishable store entries, one shared trace directory, colliding
use-splice instance defaults). C24's op-script grammar records the
eleventh verb and the two-seam shape gate with its C29 read-back
exemption; the glossary's blueprint-label entry now separates the
render name (op-settable, keys trace runs and instance defaults) from
the registry label (register --name, explicit, orthogonal).
refs #331
Ratifies the fieldtest spec-gap: the transcript abbreviated the
strategy reference to a 4-hex placeholder while the binary prints the
full 64-hex content id — the quoted line now carries the real id from
the committed cycle-328 fixture, keeping the transcript byte-honest.
refs #328
The C24 axis-discovery paragraph loses its obsolete rationale (names
prefixed by the wrapping, discovery pinned to the sweep verb) in favour
of the one-raw-namespace statement; C18 records the ratified #246
bound-override coincidence contract (param_space OR bound_param_space,
no schema flag) as the document-side absorption #328 demanded; the
glossary sweep and use entries, the authoring-guide worked block, and
the README axis examples move to raw names. Refusal transcripts quote
the byte-actual binary output (review caught an invented aura: error:
prefix; the campaign-validate transcript shows the real fault_block
shape). Frozen corpora (fieldtests/, *.history.md) keep their
historical wrapped prose.
refs #328
Ratifies the m37 milestone-fieldtest concern: the guide recommended a
once-per-bar trigger without saying whether bars_since_open depends on
it. It does not — the index is clock-derived (ctx.now()), so a denser
trigger stream (raw m1 price, as the shipped corpus wires it) emits
the same per-bar index more often. The once-per-bar wiring is a
convention for one-emission-per-bar, not a correctness requirement.
Fieldtest spec-gap ratified: Count is a plain positive decimal with no
sign and no leading zeros ('03' refuses) — and the per-kind asymmetry
against TimeOfDay's required zero-pad is deliberate, pinned with its
rationale: canonical is each domain's conventional notation,
fixed-width for wall-clock times, minimal for numbers.
refs #271
C24 gains the add-op args clause and the data-driven format-version
paragraph (tier-2 discipline with id-stability); C15's current state
records Session as args-authorable; the glossary defines construction
arg and arg kind (and de-stales the session-node entry to aura-market);
the authoring guide fixes the moved roster path, extends the
Session-anchoring section with the args path, and teaches the add-op
args row; the README op-list sentence notes args on arg-bearing types.
refs #271
Fieldtest truth pass (composites-use, B1/S1/S2/S3):
- The "running an open pattern standalone refuses" sentence was wrong — and
it was this cycle's own ratification prose, not code. Reality (ratified
as-is): the harness binds input roles to archive columns BY NAME (C26), so
an open pattern whose roles match the data runs standalone as a matter of
design; what refuses, by name, is a role the harness cannot bind, or the
run surface's other gates (bias/tap, free knobs). The authoring guide's
input row now states exactly that; C24's input row ("only compile/
bootstrap require it bound") and the glossary's "requires them bound"
were already accurate and stand.
- README enumerated nine op kinds — the tenth (use) added (S2).
- The use-seam C29 doc gate is stated as what it is: a backstop — the
register verb already gates both input forms, so it fires only for store
content written before C29 or through the raw in-crate path (S3,
ratified).
B1's fieldtest classification was bug->debug; overridden to ratify+document:
the observed behaviour is C26-coherent and useful (quick-testing a pattern
against real data), the defect was the sentence. Minuted on #317.
refs #317, refs #339
C24: the op-script grammar section is rewritten to the ten verbs — repairing a
pre-existing drift on the way (it read "The eight verbs" and omitted the doc
op shipped with #316) — with the new use row and the open-pattern reading of
input roles; a "Registered-blueprint splice" paragraph records the resolution
split (all store I/O CLI-side at DTO conversion; the engine's second injected
closure mirrors vocab, the §"Enforcement shift" permission extended to
registered-blueprint references); the construction-gates paragraph moves
root-role boundness from the holistic finalize list to compile/bootstrap only
(#317 comment 4627).
C29: the register-seam wording drops "named" — the code gates EVERY nested
composite, recon-confirmed pre-existing prose/code mismatch — and the use seam
joins the seam list (build-time gate on fetched store content; no retroactive
invalidation of existing reads).
Glossary: use (op) and blueprint label entries. Authoring guide: ten ops
(the stale "nine ops" heading + the input row's obsolete finalize-refusal
claim corrected), the use row, and the register --name / discovery paragraph.
Prose truth per review: instance params are sweepable, NOT gangable (the gang
op refuses a composite instance's member path) — stated as such everywhere;
the gangability of spliced instances is recorded residue, not a claim.
refs #317
The binary is the only always-present teacher (C29); the 2026-07-22 external
field test showed the help teaching nothing but the verb inventory. This
cycle makes every closed vocabulary the binary ships listable with a one-line
meaning, and opens the help with the concepts the field agent had to discover
forensically.
#315 — help + introspection:
- `aura --help` opens with the two-layer concepts paragraph (research verbs
as sugar over registered process/campaign documents; the directly
authorable data plane; bias-as-target-position + protective-stop execution
model in R; how traces come to exist).
- Each document-bridged sugar verb's long help names the process shape it
desugars to (sweep -> std::sweep; walkforward -> [std::grid,
std::walk_forward]; mc -> + std::monte_carlo; generalize -> [std::sweep
(selection), std::generalize]). `run` is not verb_sugar-bridged, so its
help points at the canonical document-first form instead of claiming a
desugaring it does not perform.
- `graph introspect --vocabulary` lists each node type with its schema doc
(bare names taught nothing); `--node <T>`'s head line carries the meaning.
- `graph introspect --folds` (new): the tap-fold vocabulary with bind rule,
output kind, and meaning — it existed only as source comments. The rows
live in aura-std (`fold_vocabulary`), unit-pinned against
`fold_binds_at`/`fold_output_kind` so the prose cannot drift from the
executable rules, and doc_gate-checked (C29 entry seam). The discovery
surface is graph introspect because folds bind at graph-declared taps
(C27); the document layer still refuses a folds slot pending #310.
- `process introspect --metrics` lines carry each metric's meaning (the doc
column existed since #316 but was never printed).
- `campaign introspect --block std::presentation` lists the four persisted
taps' meanings under the persist_taps slot.
- The scaffolded project CLAUDE.md teaches the execution semantics (bias as
held target, protective stop, R) and the document data plane.
#323 — C29 authoring surfaces:
- `introspect --unwired` enumerates the optional `description` envelope slot
for both document kinds (absent-only, like risk/cost); the campaign lib
fixture gains a description, and the risk/cost-bound e2e binds description
too so "no open slots" keeps meaning complete.
- `graph build --help` carries the op-list reference: all nine op kinds with
fields and a worked element each (OP_REFERENCE lives beside OpDoc so a new
variant is one screen from its help line).
- The authoring guide's S0 worked literal is byte-pinned to the scaffold's
LIB_RS template (rendered with the guide's my_lab namespace); the scaffold
e2es compile that template for real, so the pin is a transitive compile
guard. The template gains the guide's C29 comment, the guide gains the
template's rename note — one worked example, two surfaces, zero drift.
Deliberately out of scope (ratified #319, 2026-07-24): no doc-strings for
the quintet's 48 flag fields — the sugar surface retires; only the pointer
lines above bridge toward the document layer.
Verification: full workspace suite green, clippy -D warnings clean,
aura-bench exit 0 with all fingerprints OK (help_ms +1.6% — the longer help
text, in tolerance), cargo doc warning count unchanged (7 pre-existing).
9 new e2e pins in tests/help_self_description.rs; guide + CLAUDE.md pins in
scaffold unit tests; fold-vocabulary rules + doc_gate unit-pinned in
aura-std.
closes#315closes#323
Three of the four immediate fieldtest findings for the self-description
cycle:
- authoring-guide S0: the worked Scale literal gains the required
NodeSchema.doc line (+ the C29 comment) -- the shipped example was a
hard E0063 for anyone pasting it (the exact #314 teaching-failure
class, caught from the consumer seat).
- The op table becomes "The nine ops": the C29 doc op is documented
with its JSON shape, the register requirement, the at-most-once rule
and the gate; the README op-kinds list names it too. The fieldtester
had to guess the shape from commit bodies -- now it is on the public
surface.
- The store-gate refusal's fix hint is surface-neutral: builder
.doc(...), blueprint "doc" member, or op-script {"op":"doc"} --
previously it prescribed the Rust builder to a JSON author.
Gates: registry, graph_construct (63) and project_load (14) suites
green; refusal-prose pins unchanged (pinned substrings untouched).
refs #316
Architect review (cycle 757e3ac): core clean — show is pure shell
dispatch over Env::registry() (C28); both ledger amendments match the
F1-F8 decision log exactly; document schema untouched; the #164
no-framing pin is stricter than the spec snippet asked. Four peripheral
drift items, resolved:
- fix: docs/authoring-guide.md verb rosters now name show (process) and
show|runs (campaign) — the guide teaches exactly the
generate->retrieve->extend loop this cycle closed.
- fix: docs/glossary.md campaign/process document entries carry the
settled verb sets (record-reality glossary write).
- fix: show_process's not-found refusal now sources its prose from
ref_fault_prose(ProcessNotFound) instead of a hand-copied format!
(byte-identical, test-pinned; the campaign-side prose has no RefFault
sibling and stays the single campaign instance).
- ratify (log-only): the shipped show helpers use print! (no framing,
#164) where the spec snippet sketched println! — the code is the
stricter, correct form; recorded on the reference issue's decision
log. The spec is a git-ignored working file swept at this close.
Regression: aura-bench report-only run, all fingerprints OK, deltas in
noise (host load 9.6 noted; engine/ingest/campaign/cli lanes all within
±5%). Full aura-cli gates re-run green after the tidy edits.
refs #300
Surfaced by the measurement-milestone fieldtest: a downstream author
reaching for session anchoring or declared taps had no public statement of
either — the semantics lived only in node source / the design ledger.
SessionFrankfurt / Session output semantics:
- glossary `session node`: corrected — it claimed three streams
(`bars_since_open`, `in_session`, `session_open_ts`), but the shipped node
emits ONE `i64` field `bars_since_open`. Now states the real contract: the
count of completed bar-periods since the local (tz-aware, DST-correct)
session open, close-instant indexed (09:15->1, 09:45->3; pre-open <=0), with
the Frankfurt 09:00 Europe/Berlin open + `period_minutes` knob + the
value-ignored `trigger`.
- authoring-guide: a "Session anchoring: the SessionFrankfurt preset"
subsection — schema (trigger / period_minutes / bars_since_open), the
close-instant multiples, the EqConst gating pattern, DST handling, and the
add-snippet. Verified against `aura graph introspect --node SessionFrankfurt`.
Declared-tap authoring (C27 / the #284 tap op):
- authoring-guide: the `tap` op-table row (seven ops -> eight) + a worked
"declaring a measurement tap" example. The example is verified: piping it
through `aura graph build` emits
`taps:[{"name":"spread","from":{"node":2,"field":0}}]`.
- design ledger op-list (INDEX.md): a `tap` bullet (seven verbs -> eight);
also corrected the `expose` "only verb that keeps `as`" claim — `tap` keeps
it too.
- README op-kind enumeration: adds `tap`.
Docs only; no code change. Both worked examples were run through the built
`aura` binary; the SessionFrankfurt schema was checked against introspect.
closes#285
The two fieldtest findings, resolved:
- friction: --parallel-instruments 0 now refuses in domain terms ("must be
a whole number of at least 1 — it bounds how many distinct instruments
are resident in parallel") via a custom clap value-parser, instead of
leaking Rust's NonZeroUsize wording; the zero-bound test pins the prose.
- spec_gap: the authoring guide's exit-code contract now states that a
gate-emptied cell (a std::gate leaving no survivors) is a successful
cell — exit 0 with an informational aura: note and a truncated
realization — and that exit 3 is reserved for cells that could not be
evaluated. Records observed behaviour; no code change.
Gates re-verified: workspace suite green, clippy -D warnings clean.
refs #277
closes#272
A member fault (no-data, bind, run, or a caught panic) is now a recorded
per-cell outcome instead of aborting the whole campaign and discarding every
already-computed cell. The incident that motivated this (a 22-instrument
campaign lost ~36 healthy cells ~6.7 min in because Copper had an archive gap)
now completes: the healthy cells persist, the gap cell is recorded as failed,
and the run exits 3.
Direction (owner decision 2026-07-14): run to completion and report
compromised results; no coverage preflight, no window synthesis.
Containment granularity:
- The CELL for a sweep-stage member fault (a grid hole structurally
compromises winner selection, so the whole cell fails).
- The FOLD for a walk_forward member fault (independent time windows): the
surviving folds pool into the family, failed folds are recorded as
StageRealization.window_faults, and the summary names the ratio.
- aura-registry: additive CellFault / CellFaultKind (closed:
no_data|bind|run|panic|window) / WindowFault / CellCoverage, plus
fault/coverage fields on CellRealization and window_faults on
StageRealization — all serde-default-skipped, so pre-#272 campaign_runs
lines parse and round-trip byte-identical.
- aura-campaign: run_cell returns a fault-annotated CellRealization instead of
Err (execute's accumulate-then-append-once tail is unchanged and now
persists every healthy cell + the one run record); a `contain` split keeps
ExecFault::Registry and doc-shape preflight faults global while Member/Window
become per-cell/per-fold. Member panics are caught with
catch_unwind(AssertUnwindSafe) at all three member-run sites (sweep IS/OOS)
and recorded as MemberFault::Panic — a member panic no longer aborts the
process. The wf stage partitions Registry faults (global) from Member/Window
(per-fold) and filters faulted-fold placeholders (the
"faulted-member-placeholder" broker sentinel) out of the persisted family.
- aura-cli: exec_fault_prose gains the Panic arm; CliMemberRunner::window_coverage
derives effective bounds + interior gap months from the #264 archive
primitives; present_campaign prints per-cell failure notes + a completion
summary and threads the failed-cell count; a run with >=1 failed cell exits 3
("completed with failed cells") uniformly across `aura campaign run` and the
dissolved sweep/walkforward/mc/generalize verbs (exit_on_campaign_result).
Usage stays 2, refused-before-running stays 1, clean stays 0.
Tests: the global-abort pins flip to containment (execute + the two wf fault
tests → fold-containment + all-folds-fail-the-cell); new panic-containment
tests on both the sweep path (PanicRunner) and the wf path (this commit adds
the wf mirror the loop left uncovered); a new gapped-archive e2e (one covered
cell + one gap cell → exit 3); the ~14 CLI exit-1 pins move to the exit-3
register; a pre-#272-line byte-identical round-trip guard.
Suite: cargo test --workspace green (1309 tests, 0 failed); clippy clean.
Decision log: #272 comments (fork rationale, the fold Registry/Member split,
the placeholder sentinel, uniform exit-3).
Follow-up (minor, not blocking): the plan under-scoped Task 1 to aura-registry
though the additive fields also touch aura-campaign's exec.rs literals — the
loop absorbed it mechanically; a future plan for a cross-crate additive-field
change should scope every crate's construction sites in the first task.
closes#256
Fork B (owner decision 2026-07-14): the dissolved walkforward/mc
translations' leading sweep executed the full grid over the whole campaign
window and persisted a Sweep family, yet only the enumerated parameter
points ever crossed the stage seam (the wf stage re-sweeps them per IS
window itself). The leading stage is now the fieldless vocabulary block
std::grid: it enumerates, executes nothing, persists nothing.
- aura-research: StageBlock::Grid ({"block":"std::grid"}), schema-strict
parse arm (empty slot list: every key but "block" is refused by the
generic unknown-slot check), PROCESS_BLOCKS entry, intrinsic-tier no-op
arm; the vocabulary test's non-empty-slots guard carries a pinned
std::grid-only exception (a nominal slot would misdescribe the
vocabulary to describe_block consumers).
- aura-campaign: the inter-stage seam is a typed two-armed StageFlow
(points-only vs executed members); gate / mc-per-survivor fence the
points-only arm with defensive PipelineShape faults; preflight admits
std::grid only as the first stage and only immediately before
std::walk_forward (every other neighbor consumes executed reports).
- aura-cli: translate_walkforward / translate_mc lead with
StageBlock::Grid; the two family-shape E2E pins flip to zero Sweep
families; translate_generalize keeps its executed sweep(argmax) — its
generalize stage consumes the argmax winner report as the cell nominee.
- docs: dated #256 amendment in the ledger's verb-dissolution narrative;
the authoring-guide vocabulary transcript gains the std::grid line.
Behaviour preservation: the exact-grade real-data pins
(walkforward_real_e2e_pins_the_exact_current_grade,
mc_r_bootstrap_real_e2e_pins_the_exact_current_grade) pass unmodified —
survivor points reach the wf stage in the same odometer order as before.
Measured (the #256 acceptance measurement; debug build, real GER40 2025,
2x2 grid, `aura walkforward --real`, 3 runs each):
before (a55e4cf): 6.27 / 6.18 / 6.18 s
after: 4.52 / 4.49 / 4.45 s (~ -27% wall clock)
Suite: cargo test --workspace green (0 failed); clippy clean.
Decision log: #256 comments (fork rationale incl. the StageFlow seam and
the slot-guard exception).
The second risk-regime variant vol_tf{period_minutes, length, k}
computes the vol estimator over completed time buckets: an H1 signal
gets an H1-matched stop in the research matrix instead of a hand-scaled
minute stop (the issue's k-inflation workaround retires). Additive
externally-tagged serde (stored Vol documents keep their content ids);
the executor arm wires the VolTfStop primitive via the builder+bind
chain; validate checks period_minutes/length/k positivity per regime.
The member-manifest round-trip holds for both variants (C1): vol_tf
members stamp stop_period_minutes/stop_length/stop_k and the reproduce
path re-derives the VolTf stop whenever stop_period_minutes is present
— never a silent default-Vol fallback. The Regimes slot label and the
unit notes name the new variant (period_minutes IS the stop's
timescale); glossary, authoring guide, and design ledger record the
second variant. Default regime and sugar paths byte-untouched.
Coverage: node units (rollover-only emission, bucket-delta math, the
constant-|delta| correspondence with the per-cycle regime), executor
fold, serde/validate units, the manifest round-trip unit, and a
hostless two-regime e2e (distinct ordinals; vol_tf members stamp their
timescale, vol members do not).
closes#262
Per-cycle fieldtest over the #246 surface, consumer-perspective, public
interface only: the scaffold quickstart end to end (aura new -> run ->
--list-axes -> override sweep), --list-axes on a partially-open
blueprint, an override family reproduced bit-identically (3/3 members),
and the error surface (wrong axis name, kind-mismatched value — both
exit 2). Corpus under fieldtests/cycle-246-bound-override/.
Dispositions: the one bug (F6) is fixed in this commit — authoring-guide
§1 still asserted a bound param 'never shows up in --list-axes' and 'no
aura sweep --axis can reopen it', contradicting the shipped #246
semantics and the C12 amendment; the three-states paragraph now reads
open = must-bind, bound = overridable default. The friction finding (F5,
KindMismatch/MissingKnob printed as raw Debug structs beside polished
prose) is filed as #247.
refs #246
Task 7 of the bound-override cycle: the design ledger's C12 records the
bound-as-default semantics (axis 1 may name a bound param; identity reads
the authored document; the retired axes-bind-only-open-knobs restriction
was an implementation consequence, not a recorded decision), the glossary's
blueprint entry carries the same sentence, and the authoring-guide's
data-only starter section explains the one-file run+sweep quickstart.
closes#246
Task 6 of the bound-override cycle: SIGNAL_OPEN_JSON_STD and its write
retire — aura new scaffolds signal.json alone, and the project CLAUDE.md
quickstart advertises run AND sweep against that one file (the sweep line
overrides the bound fast.length; --list-axes shows open + bound axes).
The data-only e2e loop proves it end to end: scaffold, run the closed
starter, sweep it via an override axis, and assert signal.json is the
only starter blueprint. The authoring-guide's starter-pair sentence is
retargeted; the last signal_open reference outside the unrelated #196
fixture string is gone.
refs #246
- CLAUDE.md invariant 9 amended end to end: a project is a directory
anchored by a static Aura.toml, data-only by default; native node
logic lives in attached node crates (the nodes/ antecedent removed —
user decision 2026-07-12, re-opened by the role model's diagnosis).
- docs/project-layout.md reworked to the two tiers (data-only default
tree + sibling node crate, workspace note under the crate half); the
day-in-the-life walkthrough now attaches the project's node crate
once and wires the namespace the shown command actually produces.
- docs/authoring-guide.md: data-only quickstart (closed run target,
open sweep target) + `aura nodes new` as the native entry; section
cross-reference updated.
- docs/glossary.md: new "data-only project" and "node crate" entries;
Aura.toml and manifest entries carry the tier split.
- docs/design/INDEX.md: realization note (wiring-only tier, #241) in
the project-environment section.
- rustdoc: `[nodes]` intra-doc-link escapes in project.rs/main.rs.
Full gates green: workspace suite (all binaries), clippy -D warnings,
doc build without new warnings.
closes#241
The NotFound arm of emit_chart now queries the recorded campaign runs:
records with a persisted trace_name whose stored campaign document
carries name == ARG. A unique match re-dispatches the existing charting
path onto the resolved handle; several matches refuse listing the
candidate handles in append order (refuse-don't-guess — re-running a
named invocation legitimately yields {campaign8}-0, -1, ...); zero
matches keep the not-found message verbatim. The exact trace-store
handle always wins; the name never becomes a store key (C18 untouched).
Guide §3 and the glossary tap entry document the name alternative.
closes#238
- glossary "tap" + authoring-guide §3: the chart handle of a
sweep/walkforward --trace family is the printed family handle
(members keyed <cell>/<member>) — the campaign trace_name analog was
documented, the sweep case was not (fieldtest spec_gap).
- authoring-guide §0: state the input window's financial-style ordering
(index 0 = newest, index k = k cycles back) where the node pattern is
taught — previously only in the design ledger's RingBuffer note.
- authoring-guide §3: the #234 cost block joins the open-slot list, the
worked campaign example, and the prose (charge modes, net-vs-gross
default, manifest stamping) — it was discoverable only via
campaign introspect --unwired.
Fieldtest fixtures: 09da04f.
New top-level section covering the three-part shape every node type
follows — Node impl (lookbacks/eval/label with the C8 warm-up filter),
PrimitiveBuilder recipe (NodeSchema + positional build closure), and
rostering (project-side aura_project! vs std-side roster macro) — with
the aura-new Scale starter as the verbatim worked example and the five
fresh #236 nodes as arity-variant references. Cross-linked from the
op-script vocabulary note, project-layout, and the README.
closes#228
The two open examples now expose their author-intended single knobs — the
closed builders always bound these pairs to one value by hand; the open forms
falsely offered them as independent axes:
- r_breakout_open: channel_hi.length + channel_lo.length -> channel_length
(the Donchian channel is structurally ONE parameter)
- r_meanrev_open: mean_window.length + var_window.length -> window; the band
factor stays an independent axis
Regenerated via the emitters (never hand-edited); the closed examples are
byte-unchanged. The carve builders gang in the open branch only.
Test migration: the two axis-namespace pins, five --real e2e invocations
(single gang axis, the 10,20 diagonal for the campaign pair — the mismatched
20,40/10,20 grid was exactly the configuration space the gang retires), and
the param_stability row counts (4 -> 3). The r-sma walkforward golden anchor
is untouched (it never swept a ganged pair).
Docs: authoring-guide gains the seventh op + the third param state
(open/bound/ganged) + the gang wrap note; README op list + Axis concept;
glossary gang entry; ledger C24 records the gang verb and the pre-ship
Tier-2 dormancy (no format-version bump while no out-of-repo reader exists).
Verified: full workspace suite 1104/0 (--real e2e included, local data
present), clippy -D warnings clean, cargo doc clean; live acceptance:
introspect --params prints channel_length:I64 alone / window:I64 +
band.factor:F64.
closes#61
The #28 cycle-close audit found aura graph <file> silently rendered the embedded
sample at exit 0 when the positional named an unreadable blueprint (typo,
nonexistent path, wrong extension): is_blueprint_file returns None both for "no
positional" and for a bad arg, and dispatch_graph's None arm conflated them —
re-introducing the exact #28 "I asked for my graph, got the sample" surprise in
the error path, and diverging from every sibling verb (a bad blueprint arg is a
usage fault, exit 2, per C14). Split the None arm: no positional -> the sample
(unchanged); a positional that is not a readable .json -> a usage error naming
the arg, exit 2, mirroring dispatch_run.
Also close the paired doc drift the audit flagged: the README blueprint-verb
table and the authoring guide now document the aura graph <file> render surface,
absent while graph was the last sample-bound verb.
refs #28
refs #216
Architect drift review over 68317ec..6e22c98 (two items, both fixed here;
code and ledger verified drift-clean):
- The authoring guide's 'Register and run' transcript still showed the
pre-regime world (2 cells, regime-less family ids) directly under the
worked example this cycle made two-regime / 4-cell. The transcript now
shows the -r1 family-id suffix, per-cell regime fields (ordinal 0
serialize-skipped, the established byte-stability pattern), the four
cells, per-regime generalization entries, and a prose paragraph tying
the 4 cells to the validate count (regimes compared, never pooled).
- The fresh glossary entry declared 'Avoid: stop regime' while this very
cycle ships 'stop regimes' in its hint strings and guide prose — an
Avoid line the codebase itself violates records intent, not reality.
'stop regime' is dropped from Avoid ('risk section' stays: it wrongly
suggests a section rather than an axis list).
What holds (architect): content-id parity untouched (probe is read-only,
canonical bytes unchanged); the validate cell product mirrors the exec
cross-product exactly; all six spec acceptance criteria shipped.
Regression: cargo test --workspace 62 result groups / 0 failures; clippy
-D warnings clean; cargo doc 0 warnings (all at 6e22c98; this commit is
docs-only).
closes#216
The remaining introspection e2e pins: the bare-{} envelope probe (the
issue's exact reproduction) lists the optional risk slot; a bound
(non-empty) risk list closes it (no open slots); an EXPLICIT empty
'risk': [] still counts as open (pins the non-emptiness branch, which an
is_none()-based probe would get wrong); the --block std::risk assert is
sharpened to pin the optional rendering and the block doc line (the old
bare contains("risk") matched std::risk trivially).
Docs: the authoring guide's bare-{} transcript gains the risk line, the
§3 worked example gains a two-regime risk section (heading updated), a
prose paragraph explains the structural risk axis (stop defines R;
absent = the same default vol regime the verbs bind), and both validate
transcripts show the new matrix counts. The glossary gains the
'risk regime' entry (canonical term; avoid: stop regime, risk section).
Verified: cargo test --workspace 62 result groups / 0 failures; clippy
-D warnings clean; cargo doc 0 warnings; the guide's transcript strings
grep-match the shipped hint/summary bytes (1/2/1).
Authoring guide §1: the inline SMA-crossover op-script no longer binds
fast/slow lengths — it is byte-identical to the corpus file it claims
to match (content id 597d719b… verified against the binary), and the
previously unwritten rule is stated: an op-script bind pins a param
OUT of the sweepable space (closed — no campaign axis or --axis can
reopen it). New passage + example distinguishing the raw param-space
namespace (--params, campaign-document axes) from the wrapped
--list-axes/--axis surface; glossary sweep entry carries the same
one-sentence distinction.
refs #210
Architect review (scope e7c7bde..b7aaa0b) found three items; resolutions:
- fix: docs/authoring-guide.md + glossary aligned with the shipped
vocabulary (std::sweep selection group optional/all-or-nothing,
selection-free = terminal-only, worked example verified against the
live binary).
- fix: the #203 wrapped/raw axis-name convention is now single-sourced
in campaign_run.rs (wrapped_to_raw_axis + raw_matches_wrapped, a
cross-documented inverse pair; dispatch_sweep and both bind sites
call through it; inverse-property unit test). Ratify note: this
unifies two previously different algorithms onto the documented
first-segment semantics — the old loose ends_with suffix-match also
accepted sub-segment shorthands (e.g. axis "length" against
"sma_signal.fast.length") that no stored document or test used;
such shorthands now refuse loudly (unbound/unknown axis) instead of
matching, the refuse-don't-guess reading of #203.
- carry: run_blueprint_sweep/blueprint_sweep_family remain generically
real-capable though the dispatch no longer routes real data to them;
the residual capability is shared DataSource machinery, its removal
falls out with the built-in/synthetic branch retirement (#159), and
the synthetic-only status is documented at the fn.
What holds: C24 canonical form (flatten group leaves every stored
content id unchanged, golden pin untouched), C18 lineage (topology_hash
IS content_id_of, single store write resolves the strategy ref;
selection-free arm still persists the full family), C25 closed-
vocabulary discipline (all-or-nothing group, Blockly-clean), C3 (ms/ns
conversion through the ingest seam's own named fn).
Ledger: #109 open-thread status lifted to cycles 0107-0110 reality
(executor shipped, amendment package landed, verb dissolution running
as milestone #210 with sweep dissolved).
Regression gates: cargo test --workspace 1056/0; clippy -D warnings
clean; cargo doc clean. Cycle spec+plan removed at close per
convention.
refs #210
Close two milestone-fieldtest doc-gaps (F6/F8) with one pass now that the
surfaces are stable. A newcomer could only learn the three headless JSON
artifact kinds from the fieldtest corpus: the op-script grammar was
undocumented (#208) and there was no worked process/campaign example nor a
pointer to the discoverable metric vocabulary (#197).
Add docs/authoring-guide.md — a worked, verified example per artifact kind
(op-script -> graph build; process document -> process validate/register;
campaign document -> campaign validate/register/run), every command run
against the binary and its output transcribed. Documents all six ops
(source/input/add/feed/connect/expose) with their JSON shapes, and teaches
the metric roster split (rankable / gate / | generalize) pointing at
'aura process introspect --metrics' as the source of truth rather than
hand-copying the rosters. A short paragraph in project-layout.md links it.
Docs only; no code or CLI change (the surfaces already suffice). Grammar
grounded on construction.rs; examples faithful to the milestone corpus.
closes#197closes#208