main
247 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9cfe2965c0 |
feat(aura-cli, aura-runner): JSON data namespace — info verb, NDJSON list, coverage retired
Reshape the aura data namespace around one principle: every verb emits JSON, no format flag (the headless-agent posture #264 named). - aura data info <symbol> (new): one flat JSON object — symbol plus the six neutral geometry fields (digits, pipSize, tickSize, lotSize, baseAsset, quoteAsset) from DataServer::symbol_meta when the <SYMBOL>.meta.json sidecar yields geometry; a symbol is known via bar files OR sidecar (a sidecar-only symbol still reports its geometry); an unknown symbol refuses with prose, exit 1. description stays out until the neutral reader exposes it (data-server#4). - aura data list: NDJSON — one JSON string per line; an empty archive emits zero stdout lines, with the human affordance moved to a stderr note in the #278 class vocabulary. - aura data coverage: retired — #272's per-cell fault isolation subsumed the prophylactic pre-run check; aura-runner's data_coverage_report and its presentation helpers go with it, while interior_gap_months stays as the campaign path's single gap walk (#295). The three derived design decisions (description omitted, broader refusal prose, empty-archive stderr notice) are minuted on the issue. closes #273 |
||
|
|
564a767974 |
fix(aura-cli): sweep --list-axes joins the gated intake class
Fieldtest finding B1: the one remaining authored-envelope FILE route, sweep --list-axes, skipped the root-name gate — a hand-edited "name":"../escape" listed mangled, non-bindable wrapped axis names at exit 0 while every sibling intake refused. The discovery surface now gates before any axis line prints (exit 1, shared prose), restoring both the #331 every-authored-intake claim and #328's discovery-surface identity. RED-first: the e2e refusal test was written and observed failing (exit 0, axis lines printed) before the fix. Re-enumeration of every read_to_string-fed envelope load confirms the class is now closed; C24 additionally ratifies the fieldtest's SG1 — the refusal core sentence is byte-uniform across sites while the leading context prefix varies by site convention. refs #331, #328 |
||
|
|
4ff85b94e5 |
audit: #331 cycle close — every authored intake gates the root name, cli_fixed_cost re-pinned
Architect drift review (drift_found) resolved fix-path on all three items. [high] The sweep/walkforward/mc family builders and validate_and_register_axes read authored envelope files and put_blueprint'd them ungated — falsifying the C29 exemption's premise that the store is populated through gated intakes; all four sites now gate the root name before the family/axis build (placing the gate after the build produced a misleading unknown-axis error instead of the gate refusal — RED-verified), with an e2e pin on the synthetic sweep route. The decision was gate-the-routes, not weaken-the-prose: the exemption's premise is the invariant, not a wording choice. [medium] C24 now states the gate rule as the class it is (the op intake plus every CLI intake reading an authored envelope from a file; store read-back exempt, C29). [low] C23 reconciles names-as-debug-symbols with the render name's one operational role (trace-directory key — never compilation, identity, or execution semantics). Ratify: aura-bench cli_fixed_cost baseline re-pinned (run_line_fnv 6bb0d796f760d140 -> 9bdbc3acf7b2926a). The moved metric was caused by the #328 tidy ( |
||
|
|
a851af993a |
feat(aura-engine, aura-cli): the blueprint name op — name_gate on every authored intake
An eleventh op-script op {"op":"name","name":"<n>"} sets the composite
render name (engine Op::Name + CLI OpDoc mirror); omitting it keeps the
default "graph" byte-identically. The default leaked everywhere one
research project has more than one strategy: every store entry named
graph, every default tap recording sharing traces/graph/, and two
use-splices of unnamed blueprints colliding on the default instance
identifier — the authored name dissolves all three through existing
mechanics (no downstream site edited).
Mechanics: at-most-once per script (doc-op precedent), position-free
(read only at finish). A shared deterministic name_gate (aura-engine:
non-empty, no path separators, not . or ..) guards every seam where a
name is born from authored data, because the name keys a trace
directory unsanitized: the op intake (GraphSession::set_name) and all
four CLI fresh-file envelope intakes (register, introspect
--content-id FILE, the bare graph FILE viewer, aura run FILE — the run
route reached begin_run(signal.name()) ungated, and introspect
--params FILE rode the same parse). Store read-back (reproduce, use
resolution, introspect/params by content id) stays deliberately
ungated — C29: registered artifacts are never retroactively
invalidated — pinned by a test that plants a bad-root-name blueprint
via the registry API and asserts introspect --params still answers.
Refusal prose is single-sourced (name_gate_fault_prose) so every seam
reads identically, op-indexed on the op route.
Identity semantics: the authored name hashes into the content id (a
named document is a different document) and never into the identity id
(names are stripped as debug symbols, C23) — pinned by a twin test.
The previously untested use-splice instance-name default
(construction.rs) got its ratifying pin before the collision claim
leans on it. The registry label (register --name) stays orthogonal.
Review rounds caught and fixed: the envelope gate initially fired on
store read-back (C29 violation at the introspect surface), and the
run/params fresh-file routes were unenumerated intake seams — closed
with the call-site classification now recorded in the wrapper docs.
Verification: cargo build/test/clippy -D warnings all green (99 test
targets, 0 failures, independently re-run); the new run-route test was
RED-verified by hand-removing the gate.
closes #331
refs #328, #311
Spec: blueprint-name-op (fork minutes on #331)
|
||
|
|
4474814fa7 |
audit: #328 cycle close — manifests record raw on every route, walkforward intake refuses wrapped
Architect drift review (cycle a3785a6..b3b7115) found the cycle's raw switch incomplete on two high items; both fixed forward here rather than minuted as residue, since #319 builds on this namespace: - The real/campaign executor still minted WRAPPED manifest.params and manifest.defaults (AC2 overclaim in the feat commit body). The mint seams now reshape to raw on every route (member.rs raw_bound_defaults, runner.rs manifest_space mirror of the family.rs pattern); the fix surfaced a latent regression in persist_campaign_traces, which keyed point_from_params by exact wrapped name and is now translation-aware (same recipe as reproduce_family_in). Old on-disk families stay replayable (C29; reproduce reads both shapes). - The walkforward --axis intake silently accepted wrapped names — the silent alias fork 2 rejected. It now runs the same refuse_wrapped_synthetic_axes preflight as the plain synthetic sweep (new e2e: aura_walkforward_synthetic_blueprint_refuses_a_wrapped_form _axis_name); family builders translate raw names onto wrapped SweepBinder slots on all three entry points. C24's "retired from the surface" claim is thereby made true instead of softened. - Low items: stale graph_construct docstring updated; new graph_params_and_sweep_list_axes_are_line_identical pins the two discovery surfaces against format drift. - Bench infra: aura-bench's campaign_sweep surface seeded wrapped axis literals and was correctly refused by the new intake — seeds converted to raw; full bench rerun: all 5 surfaces fingerprint OK, deltas within load noise (report-only, no baseline update needed). Library-unit tests calling family builders directly keep wrapped input (valid: translation is idempotent on exact wrapped names; intake-level refusal is the user contract). Suite + clippy verified green post-fix. refs #328 |
||
|
|
3e1e7e21da |
feat(aura-runner, aura-cli, aura-registry): one raw axis namespace end to end
Reconciles the sweep-axis namespace (closes #328): the raw form <node>.<param> (splice paths keep their interior path) is now the only user-facing axis name — printed by --list-axes, accepted by --axis on BOTH sweep routes, recorded in the synthetic run manifest, discovered by graph introspect --params (bound params included, default= lexicon), and validated by campaign documents. The wrapped <blueprint>.<...> form is retired with translation refusals on both seams. Mechanics: - classify_axis_intake (aura-runner axes.rs): explicit raw-first acceptance predicate shared by both --axis intake routes. Deliberately not built on raw_matches_wrapped, whose equality branch would have accepted the retired form silently (skeptic finding, minuted on #328). Wrapped hit -> translation refusal naming the raw candidate (shared prose builder wrapped_axis_refusal, one literal for both routes). - The synthetic route resolution moves to the raw frame the real route (bind_axes) already uses: override_paths/wrapped_bound_overrides_of match via raw_matches_wrapped (also removing a latent slicing panic), blueprint_sweep_family translates raw names onto wrapped SweepBinder slots, manifest.params records raw keys (name-only reshaping, zip is positional). The two sweep routes now share one convention. - reproduce translates recorded manifest names raw-or-wrapped onto the wrapped space: old registered artifacts stay replayable (C29), new raw manifests round-trip. - introspect --params gains the bound default= lines via the same render_value the --list-axes bound pass uses - the two discovery surfaces are byte-identical by construction. - AxisNotInParamSpace carries raw_candidate; ref_fault_prose renders the did-you-mean iff present. Sweep-terminal bind errors (MissingKnob/KindMismatch) render the raw knob name (render seam only; acceptance criterion: no user-facing surface prints a wrapped axis name). - Downstream consequence fixes: scaffold quickstart example spoke wrapped; ~90 wrapped test literals converted to raw across six test files (deliberately-bogus refusal names and the untouched internal walkforward wrapped route kept, with comments). Fork decisions and the skeptic correction are minuted on #328 (comments 2026-07-24/25). Grounding-check PASS first attempt (12 assumptions ratified). Review (opus): translation ambiguity cleared (uniform single prefix per blueprint), reproduce both-shapes traced; repair pass added the synthetic-route refusal e2e + the shared prose builder. Verified: cargo test --workspace green (99 targets, 0 failures), clippy -D warnings clean. refs #246, refs #319, refs #331 |
||
|
|
a8b1ba45c5 |
feat(aura-engine, aura-vocabulary, aura-cli): the args channel end to end
The data plane reaches non-scalar structural config (closes #271, the final Data-authorability-boundary item). Op::Add gains args (string pairs, applied via try_args after resolve and before the bind loop — no pending builder ever enters a graph; an arg-bearing type without args refuses as MissingArg). OpError::BadArg carries the precise ArgOpError; the CLI renders it as prose naming the arg, its kind, and the kind's closed hint (exit 1, #175 content-fault class). Serde: PrimitiveData.args (skip-if-empty — args-free documents stay byte-identical, golden-pinned, so every existing content id is stable per C18) with a data-driven format version: the writer emits 1 for args-free documents and 2 when any primitive recursively carries args; the loader accepts 1..=2. The old v2-refusal pin flips deliberately to v3 (named contract change). Identity JSON keeps args — they are structural, and the Rust configured() path and the args op-script bridge to the same identity id (pinned). Roster: Session / LinComb / CostSum enter (33 -> 36, both count pins; the LinComb-rejection assertion flips to the resolved side). introspect --node shows arg rows (name, kind, hint) plus the pending note; OP_REFERENCE's add row teaches the args form. Registry comment de-staled (LinComb is now roster-reachable). Sequencing note: this commit lands op seam and roster together so no intermediate tree state exposes a pending builder. |
||
|
|
623d304b7f |
feat(aura-cli): use op on the data plane — register --name, resolution echo, discovery
The op-script gains its tenth verb end-to-end: OpDoc::Use carries a tagged ref
({"content_id": id-or-unique-prefix} | {"name": label}); DTO conversion resolves
it (label sidecar / #302 prefix semantics / verbatim id), fetches, deserializes
eagerly (a vocab/parse failure fails fast naming the id prefix — review
finding, no swallowed .ok()), re-walks the C29 doc gate over the fetched
composite (a doc-less store entry cannot enter a NEW composition; existing
reads stay valid — no retroactive invalidation), echoes every resolution as
the existing benign marker (aura: note: use "<instance>": <ref> -> <id>,
stderr; stdout stays clean payload), and hands the session a pure cached
lookup closure.
graph register <file> --name <label> records the label and echoes repoints
(label "x" -> <new> (was <old>)); graph introspect --registered lists
label / 12-char id prefix / root doc line — the discovery surface for what
use can reference. Refusals are by-identifier, name their rule, and enumerate
the label set (empty store reads as prose); all op-list content faults are
runtime exit 1 per the pinned #175 attribution, argv misuse stays exit 2.
Ten new/extended e2e tests cover label round-trip + repoint, echo discipline,
unknown-label enumeration, the C29 refusal shape, end-to-end sweep axes
through a spliced instance (graph.<instance>.<node>.<param>), the open-pattern
build flow, and the standalone-run bootstrap refusal (asserts the existing
Debug-form fault; an index->name rendering at the run boundary is recorded
residue, not silently claimed). OP_REFERENCE and its help pin move 9 -> 10.
Empirical note for the record: aura run's bias arm re-roots the loaded signal
via wrap_r, so the standalone-run refusal for an open pattern is exercised on
the bare-tap measurement path; the bias path surfaces openness as a role-
binding refusal instead (pre-existing shape, untouched).
closes #317
|
||
|
|
fa7453dd9f |
feat(aura-runner): skipped-tap note; undeclared-tap refusal enumerates declared taps
Two fold-selector fieldtest findings on the --tap seam, RED-first. Skipped-tap note (#334): an explicit --tap plan replaces the record-all default entirely (C27: unbound is inert) but gave no runtime signal that unlisted declared taps went unbound — a forgotten tap surfaced only as a missing trace file. bind_tap_plan's unbound arm now emits the C14 benign note (aura: note: declared tap "<name>" unbound this run) per skipped tap, exit unaffected. No new carrier: the arm is only reachable when plan.default_named is None (an explicit plan) — under record-all it is structurally dead, so the default emits nothing. Emitted runner-side beside the existing eprintln registers; the runner->CLI print migration is #297. Undeclared-tap refusal (#333): --tap on a tap the blueprint does not declare was refused without naming the valid taps, forcing the author back into the blueprint JSON. TapPlanError::UnknownTap now carries the declared-tap roster and Display appends it, mirroring UnknownLabel's fold-roster idiom (C29: a refusal names the closed set it checks against). Decision minuted on #333 (refusal roster over a new introspect view). Rider (#335): the fold roster doc lines now state the summary-row timestamp — the accumulator folds (count/sum/mean/min/max) land one row at the last warm (contributing) value's ts, and last gains the same at-its-own-timestamp wording first already carried. The ratification record lands in the sibling ledger/glossary commit. closes #334 closes #333 |
||
|
|
8dbca82756 |
fix(aura-cli): --folds renders the registry roster; op-lists refuse unknown keys
Two fold-selector/op-script surface-honesty bugs from the 2026-07-24 fieldtests, both RED-first. --folds (#332): graph introspect --folds rendered the aura-std FoldKind table — capitalized ids (Mean) and no record entry — while aura run --tap resolves labels against the fold-registry roster (lowercase, record included). The discovery surface the --tap help points at thus misdescribed the accepted input twice. It now renders FoldRegistry::core().roster() (label + doc per entry); the pinned FoldKind-rendering test was rewritten to pin the roster form (8 rows, no capitalized id leaks, record present). Unknown op-list keys (#326): a typo'd key in an op-list element ("params" for "bind") was silently dropped and the wrong graph built with zero signal, contradicting the closed-vocabulary posture (C25). OpDoc now carries serde deny_unknown_fields; the refusal names the offending key. Exit class is 1, not the issue's suggested 2: op-lists arrive on stdin, and the pinned #175 attribution principle classes stdin-content faults as runtime — the new refusal fires from that same deserialize. Blast radius verified: all 21 committed *.ops.json under fieldtests/ still build cleanly. closes #332 closes #326 |
||
|
|
73ad87b08a |
fix(aura-research): process.ref hint states the tagged { content_id } shape
campaign introspect (--block std::process_ref and --unwired) described
the slot as "content id of a process document" — a plausible bare-string
reading the validator rejects; the required {"content_id":"..."} tagging
appeared in no introspection output (binary-only M1 fieldtest finding).
The SlotKind::ContentRef label and the open-slot hint now lead with the
tagged { content_id } shape. Deliberately narrower than the issue's
suggestion of mirroring std::strategy's { content_id } | { identity_id }
form: validate_campaign refuses an identity_id process ref
(DocFault::ProcessRefMustBeContentId) and a pinned test forbids
advertising it, so the hint states the one accepted key only (C29
honesty; correction minuted on the issue). RED-first: the extended
process_ref_describe_advertises_content_id_only assertion failed against
the old label before the fix.
closes #329
|
||
|
|
1baee774bb |
feat(aura-cli): --tap TAP=FOLD fold-subscription selector on aura run
Decide #310's data-authorability fork as (b), split by run-mode authority: selecting a Named fold subscription is data-reachable on the one-shot run path; adding a fold stays a Rust entry (role 2, C25). `aura run` gains a repeatable --tap TAP=FOLD selector feeding the existing TapPlan seam: no flag keeps the record-all default; any flag replaces the plan entirely (unlisted taps stay unbound — C27 inert, non-error). Validation stays in bind_tap_plan (roster-enumerating label refusal, typed UndeclaredTap, both before store I/O); parse-level refusals (malformed pair, duplicate tap) exit 2 via the verb's usage convention. Rejected alternatives (minuted on #310 with the skeptic-pass rationale): an op-script/blueprint carrier (C27 forbids recording policy in the serialized fragment); ratifying Rust-only (softens a designed-in promise with effort as the only rationale). The campaign/document carrier and the persist_taps/declared-tap namespace reconciliation are deferred to the Measurement-reachable milestone (#312/#327, minuted on #312). Ledger: C27 Current state records the boundary; the superseded sentence moved verbatim to the new c27-declared-taps.history.md sidecar; the glossary tap-plan paragraph updated, fixing the pre-existing inaccuracy that named `aura measure` a record-all-passing verb (it is the post-hoc IC analysis and constructs no tap plan). Verification: 5 new unit tests (parse branches), 5 new binary-level selector tests incl. a byte-identity pin of an explicit all-record plan vs the record-all default (C1), 1 measurement-arm test; full workspace suite green; clippy -D warnings clean. Independent opus diff review: approved, no Important/Minor findings (nits: inert-arm generic refusal, a=b=c falling to the roster refusal — accepted as-is). closes #310 |
||
|
|
829a1984e6 |
feat(cli, std, research): self-describing surfaces — two-layer help, per-entry meanings, op reference
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 #315 closes #323 |
||
|
|
59f547e313 |
test(cli): RED -- mc synthetic member panic is contained like its siblings
Orchestrator probe after
|
||
|
|
bd6ddf9efe |
test(cli): RED -- synthetic member panics are contained like the real path
Milestone-fieldtest bug (source-blind run, 2026-07-24): a member fault on the synthetic walkforward/sweep path escapes as an uncaught panic (exit 101, no class marker, internal source path leaked) while the real path contains the identical fault via the #272 boundary as aura: warning: + exit 3. Cause: the family builders in aura-runner/src/family.rs run members bare, without the catch_unwind containment that lives only in aura-campaign/src/exec.rs. Two RED e2es pin the derived contract (decision log on #278): no exit 101, a warning-marked fault line, no panic text or crates/ path on stderr, exit 3 per the C14 partition. refs #278 |
||
|
|
1102d776df |
feat(runner, cli): runner-side marker literals + the zero-trade walk-forward notice
Iteration stderr-markers-2, tasks 3-4 of the stderr-class-markers plan. aura-runner's four continuing-run diagnostics join the class grammar as hand-written literals (the #295 boundary keeps the macro owner in the presentation crate): the stale-dylib warning gains the `aura: ` namespace, and the three tap/no-nominee notices become `aura: note: ` — the latter two extracted into pure note-builder fns with unit pins. The two e2e pins on the cost-model tap string moved with the retag (research_docs.rs). A new project_nodes e2e exercises the real stale- dylib load path: fresh build emits nothing, a source newer than the dylib emits exactly one class-marked warning and still succeeds. The #313 notice ships on both walk-forward paths: `aura: note: all N walk-forward windows recorded zero trades` before the summary JSON, exit 0 untouched (a null result is a valid research result, #198). Wording and condition live in one shared diag helper taking the per-window trade counts (orchestrator tidy of the reviewer's duplicated-predicate residue); the call sites keep only the path-specific field extraction. Three e2es pin it: the equal-length degenerate on the synthetic path (exactly one note, no warnings, exit 0, summary unchanged), a traded negative twin, and the real/sugar-path degenerate gated on the GER40 archive. refs #278, refs #313 |
||
|
|
34987be389 |
feat(cli): stderr class markers — diag module, campaign/verb-sugar retags
Iteration stderr-markers-1, tasks 1-2 of the stderr-class-markers plan (spec signed via grounding-check PASS, decisions logged on #278). The two stderr diagnostic classes become machine-separable (refs #278): a new crate-internal diag module owns the grammar as note!/warning! macros (`aura: note: <text>` benign continuing-run diagnostic, `aura: warning: <text>` recorded fault the run survives); the four undifferentiated sites are retagged — gate-emptied cell -> note, failed cell / walkforward cell / mc cell -> warning — and the four scaffold literals migrate onto the shared macro (output bytes unchanged). The always-on record summary line gains the bare `aura: ` namespace it was missing; bare-prefix stays the vocabulary for error lines accompanying a non-zero exit (C14 partition) and plain info lines. Exit codes unchanged throughout. Verification: RED->GREEN warning pin on the per-cell-fault e2e; new gate-emptied e2e (impossible n_trades threshold) pins the note marker, exit 0, and the summary-line prefix; scaffold e2es and the full workspace suite green; clippy -D warnings clean on aura-cli. The C28 shell-module roster test gained the diag.rs entry. Known residue for the next slices: aura-runner literals (stale-dylib warning, three tap/no-nominee notes) and the #313 zero-trade notice follow in tasks 3-5. The verb_sugar warning call sites remain e2e- uncovered (a hostless walkforward fault fixture is structurally unreachable — single-instrument validation refuses before the cell loop); their grammar is pinned via the shared macro and the campaign warning e2e. refs #278 |
||
|
|
7126886b81 |
feat(engine, cli): op-script doc slot -- the vocabulary grows the C29 meaning line
Iteration 3 completion (tasks 5-6): the construction vocabulary gains the slot its own #125 scope-cut comment anticipated. Op::Doc { text } is the op-script twin of the builder's .doc(...) -- a closed, typed metadata construct (C25, no logic content); at most one per script, a second refuses via OpError::DuplicateDoc (the existing duplicate-fault family), rendered "a doc op may appear at most once". GraphSession threads the text through Composite::with_doc at finish, so op-built composites pass the C29 store gate like builder-built ones. The op-script register test runs on the described variant (SIGNAL_DOC_DESCRIBED) for both shapes; the doc-less script now pins the C29 refusal, and the E2E phase added the RestatesName op-script case. Full gates: cargo test --workspace green (98 suites, 0 failed); clippy -D warnings clean. refs #316 |
||
|
|
593e233e66 |
test(cli): C29 register-refusal e2e -- doc-less root, doc-less nested, described twin
Iteration 3 task 4: register_refuses_undescribed_composites_end_to_end pins the store gate's verb surface -- a doc-less root refuses naming `sma_signal` and the rule, a doc-less named nested composite refuses naming `inner`, the described shipped example registers (exit codes and stderr through the C14 contract). The pre-existing twin pin graph_doc_field_moves_content_id_but_not_identity_id stays green. Checkpoint note: the loop escalated this task spec-ambiguous because its plan gate demanded a fully green graph_construct suite while the op-script register test is known-open until the doc-op task lands (a plan-sequencing defect, orchestrator's authoring error); the task's own work was verified green and is committed here, the doc-op task follows. refs #316 |
||
|
|
750ee93180 |
feat(research, registry, cli, runner): C29 register seam -- description gate, store-boundary doc gate, authored root docs
Iteration 3 of the self-description plan, tasks 1-3 (checkpoint commit
mid-iteration; the register e2e, the op-script doc slot, and the full
gates follow):
- aura-research: DocFault::BadDescription { subject, fault }; a present
document description passes doc_gate in validate_process /
validate_campaign (absent is never a fault and stays byte-identical);
prose arm in doc_fault_prose; tests incl. content-id participation.
- aura-registry: the store boundary gates admission -- the public
put_blueprint parses the canonical bytes and walks root + named
nested composite docs (None refuses like ""), refusing via
UndescribedComposite { name, fault } / MalformedBlueprint; the raw
write survives as private put_blueprint_unchecked (store-mechanics
tests + the never-retroactive witness: doc-less pre-C29 entries stay
readable). Registry-internal seeders triaged junk-vs-real.
- authored root docs: the 4 example blueprints + their 4 open-fixture
twins, the scaffold starter template, the scaled_signal and hl_signal
inline fixtures, and the three programmatic member.rs builders
(breakout, meanrev, hl_channel) via GraphBuilder::doc -- mechanical
propagation of the authored lines.
Recovery note: the implement run's task 3 discarded tasks 1+2's edits
in three files via a file-level checkout (the issue-#23 guard shape);
the loop's boundary snapshot caught it and the content was restored
from recovery snapshot 11a6ae2, then re-verified (research 81 green,
registry 51 green, cli 58/59).
Known-open at this checkpoint: the op-script register test is red --
op-built composites have no doc surface (#125 scope cut); the op
vocabulary grows the anticipated doc slot in the next task of this
iteration (decision logged on the issue).
refs #316
|
||
|
|
d6694d0641 |
feat(runner, cli): C29 load seam -- undescribed extension vocabulary refuses at load
Iteration 2 of the self-description plan: field presence is compile-
enforced since iteration 1, but an extension crate can still ship the
empty-string alibi (or a name-restating doc) the compiler cannot see.
load_crate now walks every charter-checked type id, resolves its
builder, and runs aura_core::doc_gate over schema.doc; a fault refuses
the load via ProjectError::UndescribedVocabularyEntry { type_id, fault }
(exit 1 through the CLI's Display-driven surface, C14).
The variant carries the DocGateFault beyond the spec's minimal sketch
because the spec's error-handling section requires the refusal to name
entry + field + rule -- with only the type id the prose could not
distinguish the two faults (decision logged on #316).
Two gate-failing fixture crates drive the e2e, modelled on
badcharter-project: undescribed-project (und::Opaque, doc: "") for the
Empty arm, restated-project (restated::Echo, doc: "Restated Echo") for
the RestatesName arm -- the second contributed by the E2E phase to
close the only-unit-pinned gap on that arm. A runner unit test pins
both rendered Display arms directly. The described twins (demo-project,
nested-nodes-project) stay green and are now load-bearing for the gate.
Gates: cargo test -p aura-cli --test project_load (14 green, incl. both
refusal e2es); cargo test --workspace green; clippy -D warnings clean.
refs #316
|
||
|
|
ab3f16879b |
feat(runner, cli, research): metric/tap schema carriers + the C29 coverage walk
C29 compile/unit seam, tasks 6-10 -- iteration 1 of the self-description plan complete: every engine-shipped vocabulary entry now carries a gate-clean one-line meaning. - aura-runner: the internal tap recording sink threads its doc (the last unthreaded NodeSchema site) -- first full workspace build since the field became required. - aura-cli scaffold: the sample node's template carries a doc line plus a maintenance comment, so every scaffolded extension crate starts gate-clean; both extension-vocabulary fixture exemplars thread real meaning lines (the load seam walks these in iteration 2). New E2E: the scaffold's own doc must pass its own gate -- an alibi doc in the template would teach the wrong pattern to every new project, and the compiler checks only the field's presence, never its shape. - aura-research: metric_vocabulary()/tap_vocabulary() promote from bare name arrays to MetricSchema/TapSchema carriers (id + doc, 17 + 4 authored lines); callers adapt to .id with byte-identical output (introspection goldens unchanged). The #190 guard keeps its triple -- the doc column adds no fourth roster site. - Coverage: tests/self_description.rs walks all five vocabularies (blocks, metrics, taps, folds, std nodes) through doc_gate -- the guard that keeps any future edit from blanking a doc. Adjudicated review residue: the 17 metric doc strings are surfaced by no CLI path yet -- deliberate; task 8 pins callers byte-identical, the surfacing belongs to the CLI self-description work (#315). Plan-cited coordinates lib.rs:1003/:1283 resolved to the real caller sites (:1019/:1299); the plan's literal test code was reconciled to the real accessors (FoldRegistry::core().roster(), Env::std()). Gates: cargo test --workspace green (0 failed, incl. the new coverage walk + scaffold E2E); cargo clippy --workspace --all-targets -- -D warnings clean. refs #316 |
||
|
|
6e3f394b48 |
feat(aura-runner, aura-cli): run_measurement on the shared tap pair; integration + register coverage (closes #283, closes #77)
Completes the #283 registry cycle. run_measurement drops its duplicated Recorder-bind + post-run try_iter drain for the shared bind_tap_plan/BoundTaps pair — the mirror of run_signal_r is now the same code, so the two declared-tap entry points cannot drift; the measure CLI arm passes TapPlan::record_all() (semantics unchanged, constant memory). Measure-side library tests pin a fold plan's one-row trace (streamed mean bit-equal to the slice mean over the synthetic prices) and the tap-free write-nothing guarantee through the shared pair. Run-side integration tests pin the #283 acceptance surface: fold and live plans agree exactly with the recorded series of the same blueprint (C1, three runs, three drain policies); a live-only plan persists nothing; plan refusals (unknown tap, roster-enumerating unknown label) fire typed before any store I/O; two identical record-all runs produce byte-identical tap files. The previously untested `aura: writing tap traces failed` register gains end-to-end coverage on both arms: store root occupied by a file (pre-run refusal) and a read-only run dir (deferred initialize-open failure — the run completes, the failure surfaces terminally, no index.json is written, the store's treat-as-absent crash shape). Two forced deviations from the plan's scripted literals, both verified equivalent: an explicit match replaces expect_err (BoundTaps is deliberately not Debug — it carries a TraceStreamer), and the test helper takes &Path, not &PathBuf (clippy::ptr_arg under -D warnings). Closes #77 as specced: zero per-cycle heap on the tap path ((Timestamp, Cell) payload, committed earlier this branch) and the Recorder→Probe rename retired (no Probe symbol anywhere; decision recorded on #77, 2026-07-21). Verification: cargo test --workspace — 1517 passed, 0 failed; cargo clippy --workspace --all-targets -- -D warnings clean; tap_recording 5/5 (byte-compat anchors + both register arms). refs #283 refs #77 |
||
|
|
6b994cead3 |
test: red for show resolving a unique content-id prefix (refs #302)
aura {process|campaign} show is exact-match-only while every surface
prints 8-hex prefixes and reproduce has resolved prefixes since #298;
the fieldtest's prefix paste (df_4) hits the same flat not-found prose
as a genuinely-unknown id. The E2E test pins the wanted headline: a
unique 8-hex prefix of a registered id prints byte-identically what the
full-id show prints (#164 byte discipline), both verbs. The ambiguity
refusal is deliberately left to a unit test at the resolution seam —
hash-derived ids make a shared-prefix E2E fixture impractical.
|
||
|
|
0a70f67711 |
fix: register outside a project refuses instead of creating a stray store (closes #305)
register_process and register_campaign now carry the same env.provenance() no-project guard as their show/runs siblings, refusing with exit 1 and the missing-Aura.toml prose instead of letting Env::runs_root()'s relative fallback materialise ./runs/ in an innocent cwd. Five pre-existing register tests ran from a bare temp cwd and were reaching their asserted behaviour only through this bug; they now run inside the fresh_project() fixture, preserving their original property (content addressing, intrinsic-validation refusals) in a real project. Verified: the RED E2E test (register_outside_a_project_refuses_and_ leaves_no_store, both verbs, refusal + no side-effect store) observed GREEN by the loop's independent verify alongside the full suite. |
||
|
|
27ace153f2 |
test: red for register's stray-store creation outside a project (refs #305)
register_process/register_campaign call env.registry() without the no-project guard their show/runs siblings carry, so Env::runs_root()'s relative fallback silently creates ./runs/ in an innocent cwd and registers the document there (exit 0). The E2E test pins both verbs and both halves of the wanted refusal: exit 1 naming the missing Aura.toml, and no store directory created as a side effect. |
||
|
|
757e3ac1bd |
feat: the document-first surface closes — show read-back, typed stops, bare plateau
The #300 keystone cycle: the residual run-quintet flags were already document vocabulary on the --real arm (#210/#220); this closes the loop and ratifies the surface. - `aura process|campaign show <content-id>` prints a registered document's canonical bytes (print!, no framing — #164), so generate -> retrieve -> hand-extend -> re-register needs no direct store filesystem access. Refusals reuse the store-hint prose, exit 1. - walkforward/mc --stop-length/--stop-k become clap-typed i64/f64 (generalize's form); stop_knob_or and its parse_csv_list helper are deleted (multi-value/invalid input is now clap's exit-2 rejection — the two dissolved-verb refusal tests retarget, the type-dead unit test is deleted). - --select accepts bare `plateau` as the documented default plateau:mean (#227 carry); document schema untouched — the two spellings generate content-id-identical campaigns (test-pinned). - family.rs's quadruplicated demo stop literal collapses onto one DEFAULT_STOP const (maintainer-lens drift finding). - Ledger: C25's "which projection next" line resolved (document-first delivered, per-verb identity re-ratified — F8; host/MCP stay demand-driven); C18 records the read-back. Design triage, fork decisions F1-F8, and the auto-signed spec live on the reference issue. Verification: full workspace suite 1482 passed / 0 failed with the real GER40 archive exercised (the byte-identity round-trips ran, not skipped); clippy -D warnings clean. closes #300 |
||
|
|
0651e16146 |
feat: the IC becomes the second metric vocabulary; rosters single-sourced
The A1 cut of #147 item 2, part 2 of 2 (the consumers), completing what part 1 (the MetricVocabulary substrate) set up: - aura-campaign's RANKABLE_METRICS hand-copy becomes a re-export of the backtest vocabulary's roster; the #190 guard test extends to pin the roster nesting (rankable == vocabulary roster, rankable within per-member, per-member within the research vocabulary) and the legacy exposure_sign_flips alias, so any drift fails a test loudly. The research and per-member rosters themselves stay hand-copies by ratified isolation (#190 accepted residual) — now oracle-pinned instead of trusted. - aura-cli gains IcMetrics + IcKey implementing MetricVocabulary: the second PRODUCTION implementor. The standalone `aura measure ic` reduction now computes its permutation p via the shared null_draw + one_sided_p_laplace building blocks (independent draws instead of chained shuffles — statistically equivalent, deterministic per seed; emitted p values may differ in the last digits from #290's). - Acceptance proven at the registry boundary: an engineered SweepFamily<IcMetrics> deflates through optimize_deflated to a LOW overfit probability and a noise family to a HIGH one (the permutation null, dispatched by the vocabulary — the deflation machinery is no longer baked-in R-only); same seed reproduces the identical FamilySelection; cross-vocabulary names are refused with the family's own roster in the prose (the C10 guard). A new cli_run E2E exercises the legacy alias through the real `aura runs family … rank` boundary. Deviation from the plan, forced by the compiler: IcMetrics additionally derives Debug (unwrap_err on the deflation Result needs it; mirrors RunMetrics). Verification: full workspace suite green (no FAILED, no compile errors), clippy -D warnings clean, C18 goldens (cli_run, measure_ic, c28_layering) green unchanged. A2 — measurement runs as sweep-family citizens — stays deliberately deferred until a concrete campaign demand exists. closes #147 |
||
|
|
a9d36ddd70 |
feat: measurement's first deflatable metric — the Information Coefficient
Give a measurement run a standalone post-run quality score: the Information
Coefficient (IC), corr(signal_t, forward_return_{t+h}), with a permutation
null model. Before this a measurement run persisted only tap names and series
(MeasurementReport) — inspectable but not rankable or deflatable. IC is the
first metric measurement supplies: the tap-to-scalar reduction the C28
process-column / metric-interface seam anticipates ("a named metric vocabulary
supplied by measurement and backtest instead of baked in R-only").
Surface: `aura measure ic <run> --signal <tap> --price <tap> [--horizon]
[--permutations] [--seed]`. It reads the run's two recorded tap traces, builds
the forward-return series offline over the recorded price spine, aligns
signal -> return by exact timestamp, computes the IC, and prints it with a
one-sided permutation-null overfit probability (Laplace-smoothed, mirroring the
R deflation arm's formula in optimize_deflated).
Placement follows the C28 ladder: the generic pieces — pearson_corr and a
Fisher-Yates permute over SplitMix64 — land in aura-analysis (the domain-free
statistics foundation, beside resample_block); the IC semantics (forward-return
horizon, the offline join) and the CLI verb land in aura-cli (the shell). The
run path (run_measurement), MeasurementReport, the trace store, and the registry
metric vocabulary are untouched — existing `aura run` output stays byte-identical
(the C18 golden run_prints_json_and_exits_zero stays green).
Causality (C2): the forward-return read close_{t+h} is over a completed run's
recorded trace, not an in-graph node. A look-ahead signal is structurally
impossible in a causal run, so the engineered-vs-noise acceptance property is
unit-tested over hand-built offline series (perfect signal -> IC ~= 1, p < 0.05;
a varying but exactly-uncorrelated signal -> IC = 0, not significant;
constant/degenerate -> the (0.0, 1.0) floor), while the E2E validates the CLI
wiring, report well-formedness, determinism, and the error paths over a real run.
This lands the second, structurally distinct null-model computation (permutation,
beside the moving-block bootstrap behind R). That makes the deferred registry
deflation-vocabulary abstraction (#147 item 2) demand-driven rather than
speculative — the next cycle.
Verified: cargo test --workspace green (incl. the new aura-analysis unit tests,
the aura-cli ic_* unit tests, and the measure_ic E2E); cargo clippy --workspace
--all-targets -D warnings clean; the C18 byte-identity golden unchanged.
closes #290
refs #147
|
||
|
|
b39fd63396 |
refactor: split the aura-std roster into C28 layer crates
Phase 4 of the Stratification milestone. aura-std held four C28 ladder layers in one roster; this cuts them into layer-aligned, aura-core-only node crates so the import direction is enforced by the crate graph: - aura-std — engine nodes only (arithmetic/logic/rolling + sinks) - aura-market — session, resample - aura-strategy — bias, stops, sizer, cost-model machinery - aura-backtest — sim_broker, position_management - aura-vocabulary — the relocated closed std_vocabulary roster Node modules move verbatim (byte-identical renames); consumers are rewired by import path only. A new structural test (aura-vocabulary/tests/c28_layering.rs) asserts each node crate's [dependencies] stay within its C28-permitted inner set, catching the acyclic-but-outward violation the compiler misses. Behaviour byte-identical: full workspace suite green (1448 tests), no golden edited, clippy -D warnings clean. C28 Status block updated. closes #288 |
||
|
|
34ff539143 |
feat: measurement run verb — shape-dispatch aura run on blueprint output
Phase 3 of the Stratification milestone (contract C28). `aura run` now
shape-dispatches on the loaded blueprint: a `bias` output takes the
existing strategy path (wrap_r + R evaluation), byte-identical; a
blueprint that declares taps but exposes no `bias` runs bare via the new
`run_measurement` (bootstrap, run, drain the declared taps) with no
SimBroker, no risk executor, and no per-cycle equity/exposure/r
recorders; a blueprint exposing neither is refused. This gives the
measurement layer its own run verb and removes the O(cycles) recording
retention (the measured ~2 GB peak RSS over 12y UK100 M1) the mandatory
R scaffold imposed on a measurement-shaped run.
A new `MeasurementReport { manifest, taps }` (aura-engine::report, beside
RunReport) is the stdout/record shape — no R metrics, since a bare run
has no broker. The manifest's `broker` carries an interim "measurement"
sentinel; the honest `Option<String>` model is deferred to the #147
metric-genericity block, keeping this phase #147-free and the
strategy-path C18 goldens byte-identical.
`run_signal_r` is untouched (the tap machinery is duplicated into
`run_measurement`, not extracted). The measurement arm's closed-blueprint
guard uses `signal.param_space()` rather than `blueprint_axis_probe`,
which welds wrap_r and would panic on a no-`bias` signal.
refs #286
|
||
|
|
43a1cc44d3 |
feat(graph): name-addressed tap construction op — declared taps without raw indices
Declared taps (C27, #282) were authorable only by hand-patching the raw blueprint envelope's `taps` array, where `node` is a raw interior index — an index miscount was the expected mistake and the root friction behind the two `aura run` panics the measurement-milestone fieldtest found. Add a `tap` op to the construction op-script vocabulary — the exact output-side twin of `expose`. It names its source wire by the dotted `"node.field"` identifier the op surface uses everywhere (`split_port` + `resolve_output_field` + `node_index`), so `{"op":"tap","from":"fast.value","as":"fast_ma"}` replaces patching `{"node":14,"field":0}`. Engine (construction.rs): `Op::Tap`, its `tap()` handler (a verbatim twin of `expose` — same resolvers, same name-dedup, appending a `Tap` to a new `taps`/`tap_names` namespace), and `OpError::DuplicateTap` (the twin of `DuplicateOutput`, stricter than the raw envelope, which enforces no tap-name uniqueness). `finish()` now threads `.with_taps(self.taps)` into the built `Composite` — fixing a latent drop #282 left (it built `.with_gangs` but never `.with_taps`, so op-built composites silently lost their taps). CLI (graph_construct.rs): `OpDoc::Tap` (externally tagged, `"as"`-renamed like `expose`) + the `kind_label` / `From` / `format_op_error` arms (the last compiler-enumerated by the exhaustive match). No change to the `Tap` / `FlatTap` / `bind_tap` / `aura run` machinery — the op-declared tap flows through the existing #282 compile + record path unchanged. Verification: 7 new tests green (4 engine: resolve+append, bad-port / ghost-node refusal, duplicate-name refusal, finish-threads-into-FlatGraph; 1 CLI serde round-trip; 2 CLI E2E over the real `aura graph build` / `aura run` subprocess — one pinning the exact serialized `"taps":[{"name":"fast_ma","from":{"node":0,"field":0}}]`, one proving an op-authored tap round-trips through build -> run -> persisted trace). Full workspace suite green; clippy clean. The RED-before-GREEN step was verified by a temporary stash of the production edits (the `Tap` / `with_taps` machinery pre-existed from #282, so a fresh RED-only test could not be authored) — the new tests fail without the edits, pass with them. Docs (the authoring-guide `tap` row, the ledger op-list `tap` line, and a worked tap-authoring example) are deliberately out of this commit's scope — they land in #285, the docs follow-up in this same cycle. closes #284 |
||
|
|
e768072ed0 |
test: red for aura-run panics on an unrunnable blueprint (#282)
Two RED tests pinning that `aura run` refuses cleanly (exit 1 + an aura: message) rather than panicking on a user-authorable blueprint: a tap wire pointing out of range (run_signal_r's compile .expect swallows TapWireOutOfRange into a panic, main.rs:1753) and an exposed output not named bias (wrap_r's g.build().expect panics on UnknownOutPort from the hardcoded bias weld, main.rs:1667). Surfaced by the measurement-milestone fieldtest. refs #282 |
||
|
|
2e8d74903c |
feat(cli): record declared taps on the single-run path (#282)
The payoff: a hand-authored blueprint's declared taps become recorded output from one `aura run`. run_signal_r binds each tap (hoisted into flat.taps after wrap_r nests the signal) to a fresh Recorder before bootstrap, drains the series after the run, and persists each as a ColumnarTrace via the existing env.trace_store() — the same trace surface the campaign path feeds, so the existing chart tooling reads them. Duplicate tap names are refused (the caller-owned DuplicateBind dedup) before anything persists. A tap-free run writes no trace store — byte-identical to today. Sweep/reduce leaves taps unbound (run_blueprint_member untouched). Covered end-to-end: a persisted tap series, the tap-free no-write guard, and the duplicate-name refusal. refs #282 |
||
|
|
c8dc57e2aa |
feat(std): CumSum and When cells; roster complete at 33
Second half of the #281 clock-enable set. CumSum is the generic run-length accumulator with O(1) Neumaier-compensated state — the branching Neumaier variant, not Sma's plain Kahan, so it stays exact when a large running total absorbs a tiny sample (the standalone accumulator has no windowed-magnitude guarantee; module doc explains why the helper is not reused). When is the clock-enable driver: forwards value iff gate, else a quiet cycle (eval->None), so every stateful reducer composes gated unmodified — SMA(When(x,g),N) is the gated SMA. Rosters CumSum and When (both count pins 31->33) and ships two engine E2E fixtures: When forwarding/quiet-downstream through the real GraphBuilder->compile->run seam, and a quiet When leg suppressing a whole Resample Barrier(0) group (the deliberate stall semantics). refs #281 |
||
|
|
40e962db89 |
feat(std): Sign and Select cells — the Bool→F64 bridge pair
First half of the #281 clock-enable set: Sign promotes the private sign0() semantics to vocabulary (strict comparisons; NaN and -0.0 fall to 0.0), Select is the stateless 2:1 mux that consumes Bool back into the F64 plane. Both zero-arg, Firing::Any, rostered (count pins 29→31); an engine E2E drives both through the real GraphBuilder→compile→run seam. refs #281 |
||
|
|
88a1c28954 |
feat(registry): index-first identity resolution with self-repairing sidecar
Replace the per-call O(store) scan in find_blueprint_by_identity with a persistent identity-id -> content-id sidecar index (blueprint_identity_index.jsonl, sibling of runs.jsonl). Lookups consult the index first and VERIFY every hit by loading that one blueprint under the current roster and recomputing its identity id -- the index is a cache, never an oracle, so results stay scan-identical under roster drift, store surgery, or index corruption (up to the same-identity-twin choice, unspecified in both paths and documented on the fn). Any miss or failed verification runs the old scan as a full-store repair pass: no early return, best-effort append of every absent-or-different mapping, last line wins so later repairs heal stale lines. No write-path, engine, or caller changes; a pre-index store backfills itself on the first miss; a read-only store keeps scanning as before. Decided against put-time index maintenance: it would need a roster-free doc-level identity function whose equivalence to the loaded-composite path no green test ratifies (the composite path canonicalizes structurally on load); the repair path is the load-bearing mechanism either way. Decision log: the issue's comments. Verification: 7 new aura-registry tests (backfill, verified-hit-no-walk via a poison-dir probe, one-pass legacy backfill, stale-line healing, unloadable-under-roster preservation, garbage tolerance) plus a CLI e2e (campaign validate resolving an identity ref cold then warm); workspace suite 1346 passed / 0 failed; clippy -D warnings clean; doc build clean. closes #191 |
||
|
|
bfb8648925 |
tidy: fieldtest follow-ups — domain prose for the zero bound, gate-emptied exit semantics documented
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
|
||
|
|
69bb2fc978 |
audit: cycle-close tidy for #277 — preflight duplicate refusal, zero-bound pin, C1 realization note
Resolutions for the architect's four drift items (all fix/document, none ratified away): - [medium] execute enforced family-name uniqueness only via the CLI's validate tier: preflight now refuses duplicate campaign instruments itself (defense in depth for direct callers), RED-first (execute_refuses_duplicate_instruments). - [medium] the parallel cell loop's C1 relationship lived only in the git-ignored spec: C1 gains a realization note (docs/design/INDEX.md) recording the chunked instrument-major schedule, the structural residency bound, and the two scheduling-dependent fatal-path carve-outs (fault attribution among completed cells; already-written family lines) — both inert and outside the success-path bit-identity. - [low] the fatal-path orphan-line honesty is part of that note. - [low] the --parallel-instruments zero-reject acceptance criterion had no protecting test: campaign_run_rejects_a_zero_parallel_instruments_bound pins clap's NonZeroUsize usage error (exit 2). Gates re-verified: workspace suite green, clippy -D warnings clean. refs #277 |
||
|
|
cf94377f30 |
feat(campaign): parallel cell loop on the shared rayon pool
The flip: within each K-instrument chunk (the structural residency bound from the previous commit), all cells run concurrently on rayon's process- global pool — the same pool the engine's member/window fan-out already shares, so cells x members x windows feed one work-stealing scheduler and the per-cell serial seams (ingest transpose, single-threaded bootstrap) overlap with other cells' work. Results land in index-addressed slots and the ordered outputs are rebuilt in document order: byte-identical outcomes across worker counts and bounds (C1). Fault routing, restructured for a loop that cannot mid-iterate abort: member/window faults stay #272-contained inside run_cell; non-containable faults (incl. registry writes — infrastructure, not cell pathology) latch an atomic abort flag so unstarted cells never run, and after the join the lowest document-order fault among completed cells propagates as the run's error. Nothing partial is persisted at run level on the fatal path. Property pins: determinism across thread counts and bounds (1-thread/K=1 == 8-thread/K=2), distinct live instruments never exceed K (refcounting stub runner, 8-thread pool), a dead family store is run-fatal with no partial campaign-run record, member faults stay contained under the parallel loop. E2E fixtures (previous commit) pin the CLI prose and document-order persistence across K. Verified: workspace suite green, clippy -D warnings clean. rayon enters aura-campaign under the amended C16 per-case policy (comment in Cargo.toml, mirroring aura-engine). closes #277 |
||
|
|
7fa3ef4e26 |
feat(research,campaign,cli): chunked instrument-major cell walk + duplicate-instrument refusal
Two pieces of the parallel-cell-loop iteration (spec: parallel-cell-loop), still sequential — behaviour-identical results, suite green unchanged: - validate_campaign refuses a campaign document listing the same instrument twice (DocFault::DuplicateInstrument + CLI prose). A duplicate enumerates two identical cells and is the one input that could make two cells share a registry family name; with distinct instruments the per-cell family name (all four cell axes + stage ordinal) is unique by construction, so the upcoming parallel appends can never race one name's run-index assignment. - aura_campaign::execute flattens the 4-nested cell loop into a document- order-indexed cell list, groups it by instrument ordinal, and walks sequential chunks of K instrument groups; results land in index-addressed slots and the ordered outputs (outcomes, realizations, nominee groups) are rebuilt in document order. K arrives as a new NonZeroUsize parameter (default DEFAULT_PARALLEL_INSTRUMENTS = 4), exposed as --parallel-instruments on aura campaign run and threaded through the run_campaign chain; the dissolved-verb sugar paths pass the default. The chunk walk is the structural RAM bound for the parallel flip that follows: no cell of an instrument outside the current chunk can be in flight at all (chosen over a semaphore gate — blocking inside pool tasks risks worker starvation; decision log on #277). Verified: workspace suite green unchanged, clippy -D warnings clean, E2E fixtures pin the CLI prose and the document-order persistence across K. refs #277 |
||
|
|
9e30805fcc |
feat(cli,ledger): supply CLI run sources by role key; ledger note
closes #275 The CLI half of by-name source binding, plus the ledger record. Every production run site that carries a ResolvedBinding — `run_signal_r`, `run_blueprint_member` (sweep / reproduction), and the campaign re-run/trace path — now keys its opened sources by role name via `key_supply(binding, sources)` and drives the harness through `Harness::run_bound` instead of the positional `run(sources)`. `key_supply` pairs each opened column with its declared role from `binding.entries()` — the one place open-order and role-order meet, made explicit so `bind_sources` verifies the wiring↔supply role match by name rather than by a maintained canonical-order convention. Because the current single-binding CLI derives both the `SourceSpec` roles (via `wrap_r`) and the supply roles (via `key_supply`) from the same `binding.entries()`, the bind cannot fail on this path — so the call site asserts the invariant with `.expect`, matching the adjacent `close_handle.expect("ResolvedBinding guarantees a close entry")` idiom. The named `SourceBindError` refusal path lives in `bind_sources` for future decoupled-supply callers (independently-built multi-feed / recorded sources, #124), where a mismatch becomes reachable. Ledger: a C4 realization note (supply resolved by name into declaration order, so supply order is no longer load-bearing; the tie-break guarantee is unchanged) and a scoped C23 refinement (a `SourceSpec.role` is load-bearing for source binding; every other flat-graph name stays a non-load-bearing raw-index symbol). The fieldtest-corpus `SourceSpec` sweep (planned Task 5) was reverted: the fieldtest packages already fail to build against the current engine API for reasons unrelated to `SourceSpec` (bootstrap arity, removed `InputSpec`, drifted `Recorder`/`SimBroker`/`RMetrics`, renamed `Scalar` helpers) — pre-existing bit-rot from earlier cycles. A partial `SourceSpec` migration neither revives nor further breaks them, so the scope decision to touch them (premised on their being otherwise-buildable) was withdrawn; reviving the corpus is separate from #275. Verification: `cargo test --workspace` green (0 failed; the real-data OHLC channel e2e exercises the migrated `run_bound` path byte-identically); `cargo build --workspace --all-targets` clean; `cargo clippy --workspace --all-targets -D warnings` clean. |
||
|
|
d3b1a1aead |
feat(campaign,registry,cli): per-cell fault isolation — a failed cell is recorded, never a global abort
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. |
||
|
|
ea4e79d73f |
feat(research,campaign,cli): std::grid — the enumerate-only leading stage
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 (
|
||
|
|
d1b3a3dd31 |
feat(research,composites,cli,docs): vol_tf — the timescale-matched stop regime
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
|
||
|
|
e00e264252 |
feat(research,cli): per-instrument cost factors in one campaign document
Each cost component's knob (cost_per_trade / slip_vol_mult / carry_per_cycle) now accepts one number for every cell — today's form, byte-identical on the wire so stored documents keep their content ids (CostValue's custom serde, the Axis precedent) — or an instrument-keyed map resolved per cell at member construction. A mixed-scale matrix (GER40 ~2e4, EURUSD ~1.1) keeps consistent cost units in ONE document: the N-copies workaround retires and generalize-under-constant-costs becomes expressible again. Validation is strict both ways at the intrinsic tier: a map missing a campaign instrument, or naming one the campaign does not list, refuses at validate with prose naming the component and instruments (no silent default — a partial map would reproduce exactly the silent unit inconsistency the issue reports). The instrument threads through cost_knob/cost_nodes_for/run_blueprint_member from the cell (run side and persist re-run side receive the same value — the C1 drift-alarm lockstep); the manifest stamps the resolved per-cell value under the unchanged cost[k].<knob> key, and the sugar/reproduce paths pass an inert instrument (their specs are scalar by construction). Fork decisions and the discarded first attempt: issue #260 comments. New coverage: serde round-trips (map form; scalar stays bare), validate cross-check units, both prose directions pinned, and two hostless e2es over the synthetic SYMA+SYMB archive — per-cell resolution visible in the member manifests, and the validate refusal. closes #260 |
||
|
|
ca4a89864c |
feat(std,engine): SessionFrankfurt — the Session node enters the closed roster
A Frankfurt-baked zero-arg Session preset (open 09:00 Europe/Berlin as literals) joins the std vocabulary as SessionFrankfurt, declaring period_minutes: I64 (default 15) as its one scalar knob — the single-knob roster pattern the cost nodes established. A data-only blueprint can now reach a session/time-of-day stream (bars_since_open), unblocking time-of-day conditioners for the confirm/refute research model. Roster count pins bump 28 -> 29 in both conscious-act sites. Fork decision on the issue: the preset variant over a param-vocabulary extension — a timezone param would need a string kind, breaching the four-scalar discipline; the preset keeps the vocabulary closed and is the additive extension the roster-exclusion comment anticipates. The 4-arg Session::builder stays untouched; in_session/session_open_ts stay deferred (#154). closes #261 |
||
|
|
75bfb9326f |
audit: batch tidy — exhaustive bind-error prose + the carry unit note
Cycle-close audit over the /boss batch 84e1075..bdafbde (#258 #259 #264 #266 #247 #265 #269). Architect verdict: substance clean — C1 byte-identity (conduit serde-skipped, pinned floats verbatim, uncosted bit-identical), C10 one-home-for-cost (all four conduit consumers read the already-netted series), C18 (conduit off the wire, --version and manifest single-sourced) all hold. Two medium debt items, both fixed here rather than carried: - render_bind_error's catch-all Debug-framed the remaining BindError variants, making #269's no-identifier-on-stderr claim argv-gated rather than structural. The match is now exhaustive: DuplicateBinding, EmptyAxis, EmptyRange render as axis-usage prose; a Compile fault — a blueprint defect, not a usage error — renders as a prose frame with the compile detail explicitly labelled internal (per-variant CompileError prose belongs to the graph-build surface, not this boundary). Unit tests pin the arms. - The #265 knob-unit notes covered constant and vol_slippage but not carry.carry_per_cycle, which carries the identical price-unit-vs-R trap; the note set and its pins now cover all three cost components. No regression scripts are configured (the architect is the sole gate); full workspace suite green, clippy clean. Spec and plan working files discarded per the audit lifecycle. refs #265 refs #269 |
||
|
|
bdafbdef59 |
fix(cli): unwrap the walkforward UnknownKnob Debug frame into bare prose
render_bind_error's catch-all Debug-framed UnknownKnob although its payload is already a fully-prosed message; the variant now renders the payload alone. The two tests that pinned the frame verbatim repoint to the prose fragments. Exit 2 unchanged. Completes the #247 defect family: no Rust identifier reaches a user's stderr on either verb path. closes #269 |
||
|
|
3d422341eb |
test: red executable-spec for #269 — walkforward unknown-axis rejection as bare prose
The rejection must reach stderr as the prose payload alone — no UnknownKnob(..) Debug frame around it. Fails on today's frame; the prose fragments already pass, so the sole pinned defect is the frame. refs #269 |