Cycle 1 of the "Cost-model graph (in R)" milestone (#148): a single
ConstantCost node (flat round-trip cost per trade) plus the net-R seam —
an in-graph cost_in_r stream, a net_r_equity sink (sibling of r_equity),
and summarize_r folding that one stream into net_expectancy_r (replacing
the scalar round_trip_cost). A run with no cost stays a byte-identical
gross-R baseline. Defers the general CostNode trait, the cost-graph
composite-builder, data-grounded factors, and the conviction axis.
Auto-signed under /boss on the grounding-check PASS: all load-bearing
existing-behaviour assumptions are ratified by green tests. Fork
decisions recorded on #148.
refs #148
Resolves the four non-bug findings from the inferential-validation milestone
fieldtest (the bug — the broken-pipe panic — is fixed in 1088320):
- [friction] generalize refusals now carry a reason instead of a bare usage dump:
<2 instruments, a multi-value candidate flag, a missing knob, a duplicate
symbol, and a non-stage1-r strategy each get a one-line why (mirroring the
R-only metric refusal). Two parser tests strengthened to pin the message.
- [friction] --select <argmax|plateau:mean|plateau:worst> added to the
walkforward branch of the global USAGE (aura --help) — the plateau objective
was undiscoverable from the only help the CLI prints (the per-subcommand usage
already carried it).
- [spec_gap] C1 gains a scope note: bit-identity is per-run; a derived metric
recomputed by two different command paths may differ by floating-point
reassociation (<=1 ULP), which is not a C1 violation — ratifies the
fieldtester's reading of the sweep-vs-generalize sqn difference.
- [spec_gap] the milestone vocabulary is added to docs/glossary.md:
cross-instrument generalization, deflated score, overfit probability,
neighbourhood score, plateau selection, sign-agreement, worst-case floor.
The fieldtest triage spec (docs/specs/fieldtest-*) is removed now its findings
are resolved; the tracked fieldtests/ scenario corpus stays.
Verified: cargo test --workspace green (0 failed), clippy --workspace
--all-targets -D warnings clean; `aura --help` shows --select; the refusals
print their reason.
refs #146
Milestone-close gate for "Inferential validation (defend against false
discovery at sweep scale)". Downstream-consumer exercise from the public
interface only (CLI --help, ledger, glossary, examples — never the source).
All four end-to-end scenarios PASS — the milestone delivers its promise:
trials-deflation (#144), plateau-over-peak (#145), and cross-instrument
generalization (#146) all reach the output and read sensibly, and the
composed "is this edge real or overfit?" verdict is honest (scenario 4: a
GER40 in-sample winner sqn 0.994 does NOT generalize — worst_case -0.149 on
EURUSD, sign_agreement 2/3 → instrument-specific / likely overfit).
Milestone status roll-up: bugs_found — NOT green, so the milestone is NOT yet
closeable (the green roll-up is the functional leg of the close gate). Findings:
- [bug] a family-emitting command piped into an early-closing reader
(`aura sweep|walkforward|mc|runs family … | head`) panics "Broken pipe
(os error 32)" on stdout (exit 101 under pipefail) instead of exiting
cleanly on EPIPE. Pre-existing (not a #146 regression — reproduced on
`aura sweep | head -1`); surfaced by the milestone commands. -> debug (RED-first).
- [friction] two generalize refusals (single --real symbol; multi-value
candidate flag) dump bare usage with no reason. -> tidy iteration.
- [friction] the --select plateau objective is absent from `aura --help`. -> tidy.
- [spec_gap] sweep vs generalize recompute the same member's sqn with a stable
1-ULP difference; the ledger's C1 / "recomputable" is silent on cross-command
metric agreement. -> ratify (ledger note).
- [spec_gap] the milestone vocabulary (deflated_score / overfit_probability /
worst_case / sign_agreement) is absent from docs/glossary.md. -> docwriter.
Artefacts: docs/specs/fieldtest-milestone-inferential-validation.md +
fieldtests/milestone-inferential-validation/ (README + 4 scenario scripts).
The existing fieldtests/ tree was untouched.
refs #146
Architect drift review (d50a482..feat): drift_found — documentation only,
NO contract violation. C1/C9/C10/C23 verified preserved against the diff:
`generalization` is a pure deterministic fold (registry-side, C9; R-only,
C10); the sole `instrument = Some` production site is `run_generalize`, every
other manifest constructor threads `None` + `skip_serializing_if`, so
sweep/mc/walkforward/standalone manifest bytes are byte-identical (C23); the
compat read-mirror was threaded in lockstep.
Drift fixed in this commit (ledger sync, docs/design/INDEX.md):
- The inferential-honesty thread read "now two of three pieces built" / "#146
remains the last open piece" — false now that #146 landed. Updated to record
the inferential half as structurally complete (all three built, cycle 0078)
+ the #146 realization, including the aggregator/validator-vs-selector
distinction (#146 is a recomputable aggregate, not a FamilySelection
annotation).
- C18 realization block: added the cycle-0078 note — the comparison axis (C12)
realized as FamilyKind::CrossInstrument + the first-class RunManifest.instrument
lineage field.
Regression: no regression scripts configured (project facts name only
Build/Test/Lint/Doc); architect is the gate. cargo test --workspace green
(0 failed) and cargo clippy --workspace --all-targets -D warnings clean,
orchestrator-verified.
Ephemera removed per the project lifecycle convention: docs/specs/0078-* and
docs/plans/0078-* (git rm). Durable rationale lifted to the ledger above.
refs #146
Last piece of the Inferential-validation milestone (#146): grade how
consistently a brought candidate holds across a set of instruments.
Aggregator/validator, not a third selector (user-ratified "mach es so",
decision + derivations minuted on #146): a new `aura generalize` subcommand
runs a single candidate (single-cell stage1-r grid) across an instrument list,
collects per-instrument R-metrics, and reduces them to a worst-case floor
(min over instruments) + a sign-agreement count + the per-instrument breakdown.
R-only (C10 — R is the only cross-instrument-comparable unit); the worst-case
min is the cross-instrument sibling of PlateauMode::Worst and satisfies the
non-domination constraint by construction. New FamilyKind::CrossInstrument (C12)
+ a first-class RunManifest.instrument lineage field (serde-widened, C14/C18);
the reduction lives registry-side beside optimize_deflated/optimize_plateau (C9).
Additive — existing sweep/walkforward/mc paths stay byte-identical (C23).
grounding-check PASS: all 9 current-behaviour assumptions ratified by green tests.
refs #146
Architect drift review over 9c6b9c7..cb32658 returned drift_found (3 items);
all resolved here. What holds (architect): C23 default-argmax byte-identical,
C9 separation (selector in aura-registry, walk_forward selection-agnostic),
C1/C2 (plateau pure/deterministic/in-sample), one higher_is_better direction
source.
Resolutions:
- [medium] Backfilled the pre-0077 FamilySelection legacy-load fixture
(report.rs family_selection_loads_a_pre_0077_legacy_line). The feat commit
and the struct doc-comment claimed "a legacy line still loads (C14/C18)" as
landed, but no test pinned the field-level mapping: bare deflation scalars ->
Some(...), absent plateau keys -> None. families.jsonl/runs.jsonl are
append-only, so a winner stamped pre-0077 must read back; now pinned.
- [low] Synced the ledger's inferential-validation open thread
(docs/design/INDEX.md): #145 plateau-over-peak landed (cycle 0077), selection
is now a pluggable objective (bare argmax -> trials-deflated -> plateau) on
the manifest carrier; only #146 (cross-instrument generalization) remains of
the milestone's three pieces.
- [medium-high] Removed the cycle ephemera (git rm docs/specs/0077-*,
docs/plans/0077-*) per the cycle-close convention.
Regression: full workspace suite green and clippy --all-targets -D warnings
clean (verified at feat-review time). No baseline scripts configured; the test
+ clippy gates are the regression gate. Cycle 0077 tidy (clean) — drift-clean,
not a milestone close (the milestone fieldtest is a separate deliberate gate).
Second cycle of the inferential-validation milestone. Adds an opt-in plateau
selection objective: optimize_plateau beside optimize/optimize_deflated scores
each grid member by the mean/worst of its grid-neighbourhood metric and argmaxes
the smoothed surface (mixed-radix neighbours over GridSpace axis_lens, passed as
an argument not a SweepFamily field). Strictly opt-in via --select
<argmax|plateau:mean|plateau:worst>, default argmax (C23 byte-preserving).
Reconciles the shared RunManifest.selection carrier with #144's landed reality:
the selection RULE (argmax vs plateau) and the deflation ANNOTATION are orthogonal,
so FamilySelection is extended — deflation fields become Option, SelectionMode gains
PlateauMean/PlateauWorst (the reserved slot), and neighbourhood_score/n_neighbours
are added. A small wire change to the one-cycle-old type; legacy lines still load.
RandomSpace plateau refused (exit 2 — no lattice); kNN deferred.
Grounding-check PASS (12 assumptions ratified). refs #145
Architect drift review (c192dfd..a295905): drift_found, medium/low only — no
ledger or hot-path contract crossed. C23-additive, C1-deterministic, C14/C18
back-compat all verified green (full workspace suite + clippy). No regression
scripts configured (test+clippy are the gate).
Resolution:
- [medium] three spec-promised optimize_deflated tests were missing (the
centring control, the all-noise high-p companion, the C2 IS-only read) — the
centring was unprotected ("removing it would pass every test"). Plus an
unguarded dispersion-floor sign assumption. FIXED in 0076.tidy (ecc9541).
- [low, RATIFY] the ledger's "Open architectural threads" inferential entry was
stale on one fact (it described optimize as having no trials penalty). Synced:
it now records that #144's trials-deflation landed this cycle (additive
optimize_deflated, winner unchanged — C23; optimize/rank_by stay bare argmax),
that #139's per-candidate OOS bootstrap landed in 0075, and that #145/#146
remain open. This ratifies the cycle-0076 intent; the gap description is
preserved for the still-open default-ranker path.
Deferred (spec 0076 Out of scope, not drift): n_eff effective-trials advisory,
reload-time recompute of the R-arm statistic, deflated-score re-ranking, CLI
flags for the resampling config.
Ephemeral spec + plan (docs/specs/0076-*, docs/plans/0076-*) removed per the
cycle-close convention. refs #144
First cycle of the inferential-validation milestone. Adds optimize_deflated
beside optimize: an additive selection-provenance record (deflated score +
overfit probability) stamped on the OOS winner's manifest, never re-ranking
(C23). R arm = centred moving-block reality-check reusing the r_bootstrap
kernel; total_pips arm = expected-max-of-K dispersion floor. Shared
RunManifest.selection carrier with a SelectionMode::Plateau slot reserved
for #145.
refs #144
Architect drift review (c99e7b8..2e77ab1) against the design ledger +
CLAUDE.md invariants: drift-clean, debt only — no contract or spec
violation. Verified intact: C18 wire shape (trade_rs is #[serde(skip)] +
excluded from the hand-written PartialEq; runs.jsonl/families.jsonl bytes
and digests unmoved; mc_r_bootstrap is a printed line, never a record),
C1 determinism (seeded r_bootstrap + disjoint walkforward windows, both
pinned by byte-identical-rerun e2e tests), C7 frictionless Stage-1
(round_trip_cost stays 0.0), C8/C12 streaming reduction (IS sweep folds
O(trades)/member, OOS run non-reduce). The verbatim summarize_r /
r_metrics_from_rs float duplication is guarded by the cross-reducer
equality test — sound.
Tidy applied this close:
- doc-link drift: the new E[R] notation in r_bootstrap / RBootstrap /
run_mc_r_bootstrap doc-comments was parsed by rustdoc as an intra-doc
link `[R]` (4 warnings). Wrapped in backticks; doc build now clean.
- coverage micro-gap [low, architect]: added a tripwire asserting a
populated trade_rs is absent from the serialized JSON (the serde(skip)
C18-preservation property had no direct test — only Vec::new() was
serialized before).
Carry-on (benign debt, not fixed): the mc usage literal carries a `usage:`
prefix the sibling sweep/walkforward usages omit — required because the
new `["mc", rest @ ..]` arm now handles the `--real` rejection and the
preserved golden asserts stderr.contains("usage"); churning the golden to
drop the prefix is not worth it.
Removed the cycle's ephemeral spec + plans (docs/specs/0075,
docs/plans/0075, docs/plans/0076) per the lifecycle rule.
Verified: cargo test --workspace 610 passed / 0 failed, clippy -D warnings
clean, cargo doc --no-deps clean.
closes#139
Make the World layer's two OOS validation verbs candidate-aware and
R-reporting instead of locked to the SMA sample blueprint:
- aura walkforward --strategy stage1-r --real <SYM>: roll IS->OOS, sweep
the stage1-r grid in-sample, pick by an R metric, run OOS, report
per-window + across-window-pooled OOS R-metrics.
- aura mc --strategy stage1-r --real <SYM>: moving-block bootstrap of the
pooled OOS per-trade R series -> E[R] distribution / CI.
Frictionless Stage-1 R (costs are Stage-2). Bare walkforward/mc paths and
their goldens preserved byte-for-byte. Grounding-check PASS; signed under
/boss (design forks recorded on the reference issue).
refs #139
Architect audit: drift-clean. The ledger and source now point at the recorded
geometry sidecar; no live reference to the removed authored floor survives in
code or any live contract. Refuse-don't-guess preserved.
Recorded debt (non-blocking, accepted):
- fieldtests/milestone-runway references the removed instrument_spec; a frozen
non-workspace snapshot, left untouched per the tracked-fieldtests convention.
- cli_run.rs test fn names retain unspecced/vetted vocabulary (plan prescribed
substring-only edits); cosmetic.
#124 stays open for the deferred override/floor tiers the Stage-2 broker needs.
Drops the ephemeral cycle spec + plan (rationale lifted to the ledger).
Resolve the real-path pip from the data-server geometry sidecar
(instrument_geometry) and remove the redundant authored floor
(InstrumentSpec / instrument_spec / VETTED_SYMBOLS) plus its table-only
tests. Consumption grep: only pip_size is read in production;
InstrumentSpec.instrument_id has no consumer (the position-event table's
id is a decoupled caller parameter). Speculative geometry fields are
deferred to the Stage-2 broker, read from the sidecar when needed.
Signed via grounding-check PASS (all assumptions tied to currently-green
tests, executed with the archive present). Decisions recorded on #124;
#140 closed as obsolete.
refs #124
Cycle-close audit: drift-clean on all load-bearing invariants (C7/C8/C15 hold; geometry stays non-scalar metadata, the Source seam still carries only (Timestamp, Scalar); scope boundary held — no resolver, quote_currency still &'static str, both #124's).
Tidy: completed the VETTED_SYMBOLS consolidation the feat commit over-claimed — migrated the two remaining inline vetted-symbol arrays in the crate-internal unit tests (instrument_spec_pip_value_matches_contract_times_pip, instrument_spec_ids_are_distinct) to the const, so #140 now extends one list.
Ledger: lifted this cycle's durable rationale into the C15 realization note (docs/design/INDEX.md) — the floor now carries tick_size/digits, and the recorded-metadata tier-2 source (instrument_geometry over symbol_meta -> neutral InstrumentGeometry, cross-checked against the authored floor) now exists at the ingestion edge; the tier-composing resolver remains #124.
Drop the ephemeral cycle spec + plan (docs/specs, docs/plans) per the lifecycle convention; durable rationale now in the ledger + the #143 thread.
refs #143
Consume data-server's neutral InstrumentGeometry at the aura-ingest edge to cross-check the hand-authored vetted InstrumentSpec floor (#113) against provider truth, and add the two deferred deploy-grade fields (tick_size, digits) the sidecars now supply.
Scope fork derived and recorded on #143: this cycle does NOT build #124's runtime resolver and does NOT change quote_currency's &'static str type (the geometry tier carries non-FX currency as String). Auto-signed autonomously under /boss on a grounding-check PASS (6/6 assumptions ratified); reply to the run to veto.
refs #143
The EWMA Bollinger-band mean-reversion fade is refuted: no cross-index
generalization (FRA40 leans positive, GER40 negative — two correlated indices
disagreeing in sign), no OOS-significant cell on either index, and its lone
IS-significant cell is consistent with multiple-testing noise (full verdict on
#137). The machinery (--strategy stage1-meanrev, the composed EWMA Bollinger
band, zero new nodes) is permanent and kept; only the per-cycle spec/plan are
dropped per the ephemeral-artifact lifecycle convention.
refs #137
A third price-only Stage-1 R candidate: an EWMA Bollinger-band mean-reversion
fade (price vs rolling mean +/- k*sigma, latched +-1, sign inverted vs breakout).
Composed entirely from existing aura-std primitives (the vol_stop sigma pattern)
-> zero new nodes. Screens the mean-reversion hypothesis under the same #137
lie-detector bar after the two trend candidates (momentum, breakout) were
refuted. Grounding-check PASS (9/9 load-bearing assumptions ratified).
refs #137
The Donchian channel-breakout Stage-1 R candidate is refuted: no cross-index
generalization, violent IS->OOS sign flips at long channels, no significant
positive cell (full verdict recorded on #137). The breakout machinery
(RollingMax / RollingMin nodes, --strategy stage1-breakout) is permanent and
kept; only the per-cycle spec/plan are dropped per the ephemeral-artifact
lifecycle convention.
refs #137
Donchian channel-breakout as the next Stage-1 R research candidate, after the
SMA-momentum family was refuted (cross-index non-generalization + no within-symbol
significance; #137/#141). Swaps only the signal leg of stage1_r_graph:
close -> Delay(1) -> {RollingMax,RollingMin}(N) -> {Gt,Gt} -> {Latch,Latch} ->
Sub = bias in {-1,0,+1}; the vol-stop defines R unchanged (clean A/B vs momentum).
Two new aura-std nodes (RollingMax/RollingMin, monotonic-deque sliding extremum);
the +/-1 direction latch composes from two existing Latch nodes + Sub. New
--strategy stage1-breakout + --channel grid flag; the family iterates the grid
with fully-bound graphs (compile_with_params(&[]) + Harness::bootstrap), sidestepping
parameter-ganging. Delay(1) is the C2 causality guard (channel excludes current bar).
Grounding-check PASS: all 10 load-bearing assumptions ratified by named green tests.
Auto-signed under /boss (grounding-check PASS = the signature).
refs #137
Cycle 0070 (streaming sink reductions, #138) close. Durable rationale lifted to
the ledger: C8 now records the end-of-stream `finalize` lifecycle (the second node
phase beside `eval`), its `Harness::run` topo-order epilogue, and its C1/C7/C8
reconciliation — with the open `Recorder`->`Probe` / per-cycle-allocation question
(#77) cross-referenced, since `finalize` is the "non-channel exit" that issue's
accumulate-then-read option named as missing. The per-cycle spec and plan are
git-rm'd (ephemeral, valid only for their cycle).
Audit (architect) found no invariant breakage; noted debt, left as-is: the
two-mode `stage1_r_graph` (reduce vs raw sink wiring) rests on the CLI-seam
equivalence test rather than structural unification, and `SeriesReducer.last_ts`
carries the last *warm* cycle's ts (latent only if `--trace` ever folds).
refs #138
M3 fix for BLOCKER #138: an end-of-stream `finalize` lifecycle on sinks
lets reductions fold online into O(trades)+O(1) state, so full-history
stage1-r sweeps run in bounded memory per member instead of OOM-killing.
Grounding-check PASS; signed autonomously under /boss.
refs #138
Architect audit clean (no drift): the derive is the faithful C10 book
first-difference — Close uses the stored book volume (the actual book), never an
exposure delta (the exact inversion of the abandoned 0064 exposure-integral);
post-run reduction, no in-graph node (C14), positional type-erased Scalar reads
(C7), each event's event_ts is its own cycle (C2), the >1-event-per-instant
reversal case is why it is derived not per-eval (C8/C10); aura-engine -> aura-core
only (instrument_id a caller scalar, no InstrumentSpec import).
- Ledger C10: add a "Realization (cycle 0068, #115)" note — derive_position_events
as the first difference of the executed book, the post-reframe replacement for
the rolled-back 0064 derive; fixed-fractional / currency / equity-feedback
sizing and the realistic brokers stay #116.
- Remove the ephemeral spec + plan (cycle closed; rationale in the ledger + git
history).
Accepted low debt (architect [low]): the stop-then-same-cycle-reopen producer path
emits the same closed&&open shape as a bias-flip reversal and the derive handles it
correctly (it keys on closed/open/direction/size, not exit_reason; unit-covered via
the reversal test), but is not separately asserted end-to-end. Cosmetic; the derive
is producer-agnostic.
refs #115
Derive the broker-independent position-event table (#114's PositionEvent rows)
from a completed PositionManagement run, as the first difference of the executed
book (deal = target - book - in_flight; in_flight = 0 for instant backtest
fills). A pure post-run reduction `derive_position_events(record, instrument_id)`
in aura-engine, sibling of summarize_r, reading the 14-column PM record
positionally; a reversal emits Close then the opposite open at one event_ts
(close first); a window-end open position emits its open with no synthetic Close.
Supersedes the rolled-back 0064 exposure-integral derive (abandoned per #117):
this derives from the executed book (the PM record), never from exposure deltas.
Stage-2 fixed-fractional / currency / equity-feedback sizing is out of scope
(#116 owns the equity->Sizer z^-1 register per C10).
Boss-signed: specify Step-5 grounding-check PASS (8/8 load-bearing assumptions
ratified by named green tests). Fork decisions recorded on #115.
refs #115
Architect audit clean (no drift, no debt): raw sqn verified bit-identical,
C18 serde back-compat + C4 SoA seam + C1 determinism + Part-B symmetry all hold.
- Ledger C10: add a "Realization (cycle 0067, #130 + #135)" note — SQN100 as
the turnover-robust opt-in rank key (raw sqn / default ranker byte-unchanged,
serde(default) for back-compat), and per-member --trace now symmetric across
all three sweep strategies via the shared persist_traces_r.
- Glossary: R-metrics list gains sqn_normalized; the SQN entry documents the
SQN100 variant (was "deferred #130").
- Remove the ephemeral spec + plan (cycle closed; rationale lives in the ledger
and git history).
refs #130, refs #135
Two settled, additive follow-ups to the cycle-0065/0066 R-sweep surface,
named together by the user under /boss. Signed on the grounding-check PASS
(9 load-bearing current-behaviour assumptions, each ratified by a named
green test).
Part A (refs #130) — n-normalized SQN (SQN100): a new opt-in ranking metric
sqn_normalized = (mean_R/stdev_R)·√(min(n,100)), turnover-robust across sweep
members with different trade counts. Additive RMetrics field (serde(default)
for C18 back-compat) + a new Metric::SqnNormalized rank key mirroring Sqn.
Raw sqn and the default ranker are unchanged; the 0066 single-run golden is
re-baselined (the r: block gains one field — intended, not drift).
Part B (refs #135) — wire --trace for the stage1-r sweep: stage1_r_sweep_family
honours --trace by persisting each member's equity/exposure/r_equity under
runs/traces/<n>/<member_key>/ via the existing persist_traces_r, mirroring
momentum_sweep_family; the interim refusal guard in run_sweep is dropped.
Makes --trace symmetric across all three sweep strategies.
Derived fork decisions logged on #130 and #135 (reconciliation comments).
Cycle 0066 (#133) closed. The close audit (architect) found the family ratified against C12 axis-2 (rank-by-metric over the C18 family store) and the C10 R framing, with one [high] gap (swept-member manifest omitted the fixed R-defining params) fixed forward in 499121e.
- Ledger: a C10 realization note records the durable rationale -- SQN as the single-number sweep objective, the signal-only grid (the stop defines R, so varying it breaks cross-member SQN comparability -- the SQN100 #130 motivation), the R-metric rank vocabulary, and the manifest-completeness fix.
- Glossary: new 'R metrics' and 'SQN' entries.
- The ephemeral spec + plan are removed; the ledger + git history are the durable record.
Follow-ups filed: #135 (per-member --trace for the stage1-r sweep). refs #133
Cycle 0066, reference issue #133. Settled design (deferred from 0065): make SQN
the single-number objective for ranking a Stage-1 sweep family by R signal
quality.
Two halves:
- aura-registry: metric_cmp learns sqn / expectancy_r / net_expectancy_r (all
higher-is-better, reaching into RunReport.metrics.r); a member with r:None sorts
to the worst position. No persistence change (the family store already carries r).
- aura-cli: --strategy stage1-r sweeps the stage1-r harness over a fast×slow signal
grid (stop & sizing fixed — risk_budget is R-invariant, bias.scale is sign-only
for flat-1R, both degenerate axes; the stop defines the R unit so varying it
breaks cross-member SQN comparability — see the #133 decision log). The stage1-r
topology is extracted into a shared helper so the single run keeps its identical
binding (output unchanged by construction) and only the sweep floats fast/slow.
Signed under /boss via the grounding-check PASS (the autonomous signature). The
first grounding pass BLOCKed a false claim that the stage1-r single-run output was
already byte-pinned; the spec was revised so a golden characterization test is
added against current HEAD before the refactor (verifying byte-preservation rather
than assuming it), and re-grounded to PASS.
refs #133#117
The 'R-based signal quality (Stage 1)' milestone is complete and ratified
(iter-1/2/3 + the corrective primitives mini, the tiny-pip fix, the metric rename;
end-to-end fieldtest green). Closing the cycle:
- Milestone container + issues #119/#126/#127/#128/#129 closed on the tracker.
- The ephemeral per-cycle spec (0065) and plans (0065-0069) are git-rm'd per the
cycle-close convention; the durable rationale lives in the design ledger (the C10
cycle-0065 realization note) plus git history.
Follow-ons tracked: #130 (n-normalized SQN), #131 (CLI UX), #132 (harness polish),
#133 (R-sweep + rank-by-sqn), #134 (strategy-output param-namespace rename).
refs #117
Amend the cycle-0065 spec with the "Iteration 3 - the CLI surface" design:
make the Stage-1 R layer operable from the command line (the research loop's
primary verb, ergonomic for Claude to drive), splitting the CLI/recording
surface out of the originally-lumped iter-2.
Design (forks derived under /boss, user-delegated "du entscheidest das Design",
recorded on #117):
- Promote vol_stop + risk_executor from test fixtures to public aura-engine
composite-builders; the embedded stop generalizes to a StopRule{Fixed,Vol}
enum (a C11 structural axis), vol_stop the volatility-defined default.
- aura run --harness <name>: a compile-time selector (sma|macd|stage1-r) over
Rust-authored built-ins - running, not wiring (C9/C17 intact: no runtime
registry, no DSL). Replaces the run literal-slice arms with a parse_run_args
tokenizer so --harness/--real/--trace compose orthogonally; --macd kept as an
alias.
- The stage1-r harness fans one bias into both SimBroker (pip) and the
RiskExecutor (R), so one RunReport carries both yardsticks honestly; the run
path folds summarize_r -> RunMetrics.r (additive, skip_serializing_if keeps
pip-only/legacy JSON byte-unchanged), round_trip_cost=0.0 (Stage-1 is
frictionless; a --cost knob is a follow-on).
- An r_equity tap (LinComb of cum_realized_r+unrealized_r) persists via an
extended persist_traces and renders through the existing aura chart --tap -
no new viewer code.
Scope line: "operable from the CLI" = run+inspect a Rust-authored harness (the
#109 (a) reading the contracts settle), not authoring topology in the CLI; the
broad #109 meta-level stays a separate future milestone. Deferred follow-ons:
R-sweep families + rank-by-sqn, rich R-record charting, the bias_sign_flips
metric rename (never carried out in iter-0; field is still exposure_sign_flips).
Verified before signing: grounding-check PASS (all substrate + post-patch
current-code claims ratified by green tests) - the autonomous /boss signature;
plus an advisory 5-lens spec-skeptic panel (criterion/invariants/scope SOUND;
ambiguity + plan-readiness BLOCKs resolved inline: the argv tokenizer, the
3-tap persist change, the round_trip_cost value, the dual-tap arg provenance),
re-grounded after the patch.
refs #129#117
VolStop was a design error (a fused node justified by a missing primitive). A
node is a primitive only if not DAG-expressible from other primitives; a missing
primitive is added, not fused away. Adds Mul + Sqrt primitives, removes the fused
VolStop, expresses the vol stop as a composition (rolling EWMA stddev = k *
Sqrt(Ema(Mul(d,d), length))). Spec 0065 b gets a correction note; full record on #117.
refs #117#119
Stage-1 "R-based signal quality" cycle spec (milestone #119/#126/#127/#128/#129,
contract C10). Signal quality measured in R on a strategy's unsized bias stream,
feed-forward (no equity feedback): bias -> stop-rule -> position-management ->
per-trade R-outcomes -> summarize_r fold -> E[R]/SQN.
Design adversarially hardened (12-juror refute panel; decisions logged on #117):
- R is stop-defined and LATCHED at entry (frozen R-denominator, never re-read).
- Stop-outs are NOT capped at -1R (honest loss tail; gap-through -> R < -1).
- Fills one-cycle-lagged (SimBroker no-look-ahead discipline; RED test mandatory).
- First stop is volatility-scaled (VolStop = k * EMA(|d price|)), not constant
(a constant makes R just rescaled pips).
- position-management emits a dense per-cycle R-record (trade ledger + R-equity +
window-end handling), tapped by a Recorder, folded post-run by summarize_r
(a post-run fold, NOT an in-graph node).
- Sizer = load-bearing flat-1R seam; R computed size-invariantly. Veto = doc seam,
not a runtime node (DCE deletes an identity).
- exposure -> bias rename lands first (compiler-driven).
Grounding-check PASS (11 assumptions ratified by green tests; auto-signed under /boss).
refs #117
Cycle-close tidy for broker-foundation (InstrumentSpec deploy metadata #113 +
PositionEvent schema #114).
Architect drift review (3638d48..HEAD): code is drift-clean. PositionEvent matches
the C10 column spec field-for-field (no open_ts, unsigned volume, direction-is-action,
bare-i64 action); the engine run-loop is untouched (C14); InstrumentSpec stays Copy
metadata at the ingestion edge (C7/C15); no instrument identity reaches the hot path.
The only drift was stale ledger prose, fixed here:
- C10 realization note: added a cycle-0063 entry recording that the position-event
*schema* landed (PositionAction/PositionEvent), while derivation (#115) and the
realistic broker nodes (#116) remain the deferred decoupled layer.
- C15 realization note: replaced "minimal today (pip only)" with the six-field
deploy-grade floor and the #124 currency-type forward note (quote_currency is
&'static str; the #124 resolver chooses how its runtime tier carries currency).
Regression (verbatim): cargo test --workspace = 458 passed / 0 failed (exit 0);
cargo clippy --workspace --all-targets -- -D warnings = clean (exit 0).
Carry-on (not actioned, justified): the inline reversal test is a shape-pin only —
the behaviour test arrives with #115's derive, and the E2E twin already exercises the
reversal wire shape. The quote_currency &'static str / #124 resolver concern is logged
on #124 as a deliberate deferred design choice.
This is drift-clean, NOT a milestone close — the realistic-broker milestone needs its
end-to-end fieldtest green, which depends on #115/#116/#120-123 still to land.
Removed the ephemeral cycle artifacts (spec + plan) per the lifecycle convention.
The broker milestone's (C10 A-side) foundation: two pure value-type
deliverables, no broker and no derivation yet.
- #113: extend InstrumentSpec with deploy-grade fields (instrument_id,
contract_size, pip_value_per_lot, min_lot, lot_step, quote_currency) +
populate the vetted table for the five instruments; refuse-don't-guess
arm stays. Permanent authored floor of the override hierarchy (#124).
- #114: PositionAction { Buy, Sell, Close } (closed enum, serde-encoded as
i64) + PositionEvent row in aura-engine, faithful to the C10 column spec
(no open_ts, unsigned volume, direction is the action).
Signed under /boss on a grounding-check PASS (all current-state assumptions
ratified by green tests; the two new types are greenfield, expected).
Derived fork decisions (quote_currency &'static str; action enum encoded as
i64 0/1/2; stable instrument_id + overridable vetted values) recorded on #113.
refs #113#114
Cycle-close tidy for visual-world-cut-2 (decimation #108 + run-context header #102).
Architect drift review (53eeba5..HEAD): the only drift was a STALE LEDGER — the C22
section still listed #102 (run-context header) and #108 (decimation) as deferred/open
after they shipped in 476342d. Resolved in this commit:
- the #101 first-cut note no longer claims "not yet a run-context header";
- a new "served-page hardening — cut 2" amendment records #102 (ChartMeta +
buildHeader, family window = span across members) and #108 (the pure decimate
min-max transform) as landed, both CLI-side (C14);
- the "Open architectural threads" list drops the now-landed run-context header.
Substantive gates (self-verified): C14 honoured — all decimation/header/pixel logic
is in aura-cli (render.rs/main.rs/chart-viewer.js); the only engine touch in range is
the one-line #100 cell.rs doc. C1 preserved — decimate is pure, deterministic
bucketing over the sorted-deduped spine, no-op under budget, monotonic spine (pinned
by a unit test). Regression: cargo test --workspace = 446 passed / 0 failed; cargo
clippy --workspace --all-targets -D warnings clean. No separate baseline script.
Residual debt filed as #110 (idea): the decimation budget is a fixed const (no
--width flag), and the min-then-max sub-pixel ordering is a real ts-displacement in
the non-default continuous x-mode. Both are local aura-cli refinements, neither a bug.
Ephemeral spec/plan removed at cycle close (docs/specs/0062, docs/plans/0062).
Cycle 0062 tidy: drift-clean after this ledger amendment.
Served-page hardening for the families-comparison chart, bundling two issues that
share one code locus (the aura-cli render path) and one purpose (C22/C14: make the
#107 view openable + legible on real multi-year families):
- #108 — serve-time min-max decimation as a pure ChartData->ChartData transform in
emit_chart (both single-run and family paths), bounding the served page to a few
thousand points regardless of the underlying M1 point count; full data stays on
disk; the WFO null-fill page collapses for free. Engine/registry untouched.
- #102 — a ChartMeta threaded from RunManifest into window.AURA_TRACES, rendered by
a new pure buildHeader in chart-viewer.js (headless .mjs guarded), showing
name/window/broker/commit/taps (+ member count for a family).
Signed under /boss on the Step-5 grounding-check PASS (10/10 load-bearing
assumptions ratified by green tests).
refs #108
refs #102
Architect drift review over 99fd32b..HEAD (the housekeeping commits #99/#98 plus
the 0061 cycle): code drift-clean. Engine untouched (aura-engine + aura-core diff
empty — C9/C14); C21 realized faithfully (the comparison view consumes a set of
runs, a family is the first producer, no orchestration-axis rebuild); C18/C10
refuse-don't-guess honoured (every new error path exits 2, never panics; all six
tracing entry points guarded before persist, so name resolution is total). The only
finding was a ledger-realization gap, lifted in this commit.
Ledger (docs/design/INDEX.md): added a C22 amendment recording the
families-comparison view as realized (#107) — aura chart <family> overlays one tap
across a family's members on a shared y-scale, union-ts spine giving one mechanism /
three correct readings, the write-guard totalising name resolution; reconciled the
two stale "families-comparison ... remain open" lines (the #104 note and the
open-threads list) and named the deferred follow-ons (#102 run-context header, #47
multi-column tap selection, the orchestration-composability rebuild).
Regression: cargo test --workspace green (no failures), clippy --workspace
--all-targets -D warnings exit 0, cargo build --workspace clean — verified by the
orchestrator before close. No baseline to update (aura's regression gate is the
gated determinism suite, which is part of the workspace tests).
Retire the cycle's ephemeral spec + plan (0061). refs #107
A read-side cycle: `aura chart <family>` overlays one tap (default equity) of all
members of a sweep / MC / walk-forward family in one self-contained uPlot page,
consistent across kinds via the existing union-ts spine. The view consumes a
"set of runs to compare" (a family is its first producer) — the first step toward
the programmable analysis meta-level (C21), without rebuilding the orchestration
axes. aura-registry gains name_kind / read_family / ensure_name_free (a write-guard
making name resolution a total function); aura-cli gains the comparison ChartData
builder + emit_chart branch + --tap. aura-engine untouched (C9/C14).
Brainstorm-ratified, grounding-check PASS (11/11 current-behaviour assumptions
ratified by green tests). Signed under /boss on the grounding PASS.
refs #107
Architect drift review (b92aab7..HEAD): the cycle is contract-clean — C9 (engine
untouched, whole change CLI-side), C6/C1 (real bars streamed from the pre-recorded
archive, real sweep byte-deterministic), C10 (refuse-don't-guess shared by run
--real and the family path), C12 (MC-exclusion fenced: mc --real exits 2). One
medium ledger-gap, now closed; one low item ratified as carry-on.
- Ledger: docs/design/INDEX.md C22 gains a "real-data family source" amendment
(#106) recording the opt-in real-data axis on sweep/walkforward families, the
DataSource provider, the MC exclusion (Fork A), and the oos{ns} key widening.
This was the only drift — a documentation gap, not code drift.
- Ratified carry-on [low]: the real walk-forward member key oos{from.0} is now an
epoch-ns integer; portable and collision-free, covered in practice by the
walkforward_real test (>=2 distinct oos<ns> dirs) and the 9-window E2E probe.
No explicit ns-uniqueness assertion beyond starts_with("oos") — acceptable; a
dedicated pin is not worth an iteration.
- Retire ephemeral spec/plan 0060 (git rm) per the cycle-artifact lifecycle; the
durable record is this ledger amendment + the git history.
No regression scripts configured (architect is the gate). Full workspace test +
clippy -D warnings green at close.
refs #106
Opt-in `--real <SYMBOL> [--from <ms>] [--to <ms>]` for `aura sweep` and
`aura walkforward`, mirroring `aura run --real`: each family member streams real
M1 close bars from the local data-server archive instead of the synthetic stream.
MC is excluded — its seed varies a synthetic price-walk realization, undefined
over real data (one realization -> identical members; a real MC needs a
bootstrap-resampling axis, its own cycle).
Engine, ingest, and registry untouched: a CLI-side `DataSource` provider replaces
the hardcoded `VecSource`. `M1FieldSource` already impls `Source`; `WindowRoller`
already rolls over epoch-unit timestamps.
Boss-signed: Step-5 grounding-check PASS — all 8 load-bearing current-behaviour
assumptions ratified by named, currently-green tests.
refs #106
Cycle-close tidy for the generic portable sweep member-key + bool-param demo
strategy (spec/plan 0059, reference issue #105, implementations 27f850d + 0b73a75).
Architect drift review (range 1d6fafb..HEAD): clean — no drift, no debt.
- C1 (determinism): varying_axes + member_key are pure functions of the grid
point, no schedule state; the engine carries no trace state (consistent with
the #104 design + the C22/#101 ledger note).
- C7/C8/C9: LongOnly is a clean one-in/one-out f64 node, None until warmed (C8
filter), the first bool *param* through the already-ratified Cell::bool()
path; momentum is an ordinary composite of existing nodes; brokers stay
downstream (C10). C18/C21/C22 upheld.
- The engine accessor exposes the binder's OWN axis-cardinality knowledge (it
holds the value-lists), not a presentation concern — formatting stays in the
CLI. The hand-rolled fnv1a64 mirrors the ledger-blessed SplitMix64 precedent
(harness.rs): tiny, dependency-free, bit-stable-on-purpose (std DefaultHasher
is unstable across releases; on-disk-name stability is the per-case C16
reason). LongOnly is a genuine universal aura-std block (long-only is
asset-agnostic). The ledger amendment matches the code.
Regression: the project declares no dedicated regression command; the gate is
the architect review plus the full suite, verified green at 0b73a75 (cargo test
--workspace, clippy --all-targets -D warnings).
Spec docs/specs/0059 and plan docs/plans/0059 retired (git rm) per the ephemeral
active-cycle artefact lifecycle; the durable rationale (the portable member-key
convention) lives in the ledger amendment (docs/design/INDEX.md C22/#101 note)
landed in 0b73a75.
closes#105
Generalises the family-member trace key (#105 foot-gun): derive it from the
axes that actually vary (not two hardcoded axis names), and render it as a
filesystem-conformant directory name on every major OS — charset [A-Za-z0-9._-],
case-less values (no case-insensitive-FS collision), length-bounded with an
FNV-1a fallback for the unbounded-grid edge.
Adds the first bool *param* node (LongOnly) and a momentum demo strategy
(Ema -> Sub -> Exposure -> LongOnly -> SimBroker) swept via a new
`aura sweep --strategy <sma|momentum>` selector — the empirical proof the
generic key works for a strategy whose params (incl. a bool) differ entirely
from the SMA-cross demo.
Boss-signed on the Step-5 grounding-check PASS (an independent fresh-context
agent): all 9 load-bearing current-behaviour assumptions ratified by green
tests; the end-to-end bool-param sweep path is correctly scoped as new
behaviour proven by new tests, not assumed. Workspace type-checks clean.
refs #105
Cycle-close tidy for family-member trace persistence (spec/plan 0058,
reference issue #104, implementation d3cb5f8).
Architect drift review (range 06fdafa..d3cb5f8): drift_found, low-grade
only — no contract violated. C1 (determinism), C7 (SoA), C8 (sinks),
C18/C21 (registry/families) all upheld; the opt-in --trace path is
genuinely byte-neutral. Two items, both resolved here:
- [high] Ledger gap: the C22/#101 disk-trace note recorded only the
single-run runs/traces/<name>/ layout. This cycle establishes a new,
load-bearing on-disk convention (per-member nested standalone run-dirs
runs/traces/<name>/<member_key>/) that the deferred family-comparison
view will build on. FIXED: amended the C22/#101 realization note in
docs/design/INDEX.md to record the layout, the content-derived
deterministic member keys, and the parallel-safe lock-free write.
- [medium] sweep_member_key hard-codes two grid axes; collision-free only
over today's built-in grid, a latent foot-gun if the grid varies an
unlisted axis. Not a current defect (one grid, covered, fail-loud on a
missing listed axis). CARRY-ON: filed #105 (idea) for generalised key
derivation when the grid generalises.
Regression: the project declares no dedicated regression command; the
gate is the architect review plus the full suite, verified green at
d3cb5f8 (cargo test --workspace, clippy --all-targets -D warnings).
Spec docs/specs/0058 and plan docs/plans/0058 retired (git rm) per the
ephemeral active-cycle artefact lifecycle; durable rationale lifted to
the ledger amendment above.
refs #104
Persist each family member's equity/exposure streams to disk (opt-in
--trace on sweep|mc|walkforward), mirroring the single-run trace path, so
the C21 family-comparison view has per-member data and any single member
is chartable today. Write side only; the comparison view, decimation,
and a binary trace container are deferred.
Autonomously signed under /boss on the Step-5 grounding-check PASS.
refs #104
Architect drift review over 5bff7e0..HEAD: clean, no drift, no debt. C14/C7
firewall intact (main.rs byte-unchanged → build_chart_data still returns
ChartData{xs,series}, the injected window.AURA_TRACES payload shape untouched;
the ordinal index spine is derived client-side). C22 holds — the
ordinal/continuous toggle + wheel-zoom/drag-pan/dblclick-reset are view
affordances over a recorded trace (read-only setScale on the x range), not a
scene editor. The #102 manifest-header gap is neither closed nor regressed,
still tracked.
Regression gate: cargo test --workspace green (incl. the four chart guards +
render inline tests), clippy --all-targets -D warnings clean; aura has no
separate regression script, so the suite + architect are the gates. cycle
clean.
Spec/plan 0057 retired per the ephemeral-artifact lifecycle (durable record:
the feature commit 45cc1c4, the #103 decision-log comments, and git history).
refs #103
Tier-1 comfort pass for the served trace chart (#103): an ordinal x-axis
default (collapses non-trading gaps) with a one-click continuous toggle, plus
wheel-zoom / drag-pan / dblclick-reset, a value-readout legend, and panel
cursor-sync. Confined to the read/render side (chart-viewer.js + render.rs
CHART_HEAD); the Rust serve contract is unchanged.
Boss-signed: grounding-check PASS (five current-behaviour assumptions ratified
by green node guards + render.rs inline tests). Derived-fork decision log in
the #103 comments.
refs #103
Cycle-close audit for the trace-charts visual face (#101, shipped across
8bb5256 persist + 15ee6d5 serve). Architect drift review (13941a2..HEAD)
+ the regression gate (cargo build/test/clippy --workspace, all exit 0,
orchestrator-verified).
What holds (carry-on): C14 clean — aura-engine/aura-core carry zero
UI/HTML/uPlot knowledge; ColumnarTrace does struct->encoding only, file
I/O in aura-registry, rendering in aura-cli. C1/C3 clean — persistence
and serve-time alignment are post-run pure reductions (join_on_ts at
serve time only, never in-graph). C7 clean — on-disk f64 columns +
kinds-tagged base types, the documented coercion honest, no 5th scalar.
Resolutions:
- [fix] Added the C22 realization note now that the web-from-disk seam
shipped (the amendment 3b56efb had deferred it to "when the seam
ships").
- [ratify] The served page injects the chart SERIES but drops the run
manifest (no run-context header) — a deliberate, defensible naive-viewer
scope call (the manifest is on disk in index.json; a header matters most
for the deferred families/comparison work, C21). The deviation from spec
0056 §1/§4 landed unflagged in 15ee6d5; it is now flagged here and
tracked as a follow-up. refs #102
- [tidy] Retired this cycle's ephemera (spec 0056, plans 0055/0056) and
the stale debris from the prior Runway close (spec 0055-fieldtest) per
the ephemeral-artifact convention; the durable record is the ledger +
git history.
Cycle drift-clean (modulo the ratified, tracked manifest-header gap).
refs #101
First cut of aura's visual face (issue #101; the web-from-disk pivot
ratified into C14/C22 this session, commit 3b56efb): tap arbitrary
recorder streams from a run, persist them to disk as columnar (SoA)
JSON, and serve them as a self-contained uPlot HTML chart — feeds
overlaid on a shared timestamp axis, or timestamp-aligned in stacked
panels. `aura run [--macd] [--trace <name>]` and `aura run --real <SYM>
... [--trace <name>]` persist; `aura chart <name> [--panels]` serves.
Both modes share one synthetic-union-spine join_on_ts alignment.
Signed via the specify auto-sign gate: the user is away under an
explicit "work fully autonomously, document decisions" mandate, so no
manual signature was available. All objective gates green (precondition
clean, self-review clean, grounding-check PASS), and a unanimous 5-lens
adversarial spec-skeptic panel (criterion / grounding / scope-fork /
ambiguity / plan-readiness all SOUND).
The panel earned its keep — two self-correction rounds to unanimity:
- Round 0 caught (a) panels shipped NOT timestamp-aligned, contradicting
the user's verbatim "aligned nach Timestamps in Panels" — corrected to
conform to issue #101 Fork A; (b) the USAGE advertised `--macd --trace`
with no dispatch arm — corrected to an orthogonal `--trace` composing
with every run form via a shared persist helper.
- Round 1 caught the union-spine -> join_on_ts mapping under-specified
(which tap is the spine?) — corrected with an explicit 3-step
synthetic-empty-spine mechanism (no privileged tap, no points dropped),
plus a to_rows uniformly-f64 clarification.
The two delegated naive-viewer decisions (per-series y-scale, union
spine) are documented with provenance on issue #101.
refs #101
Milestone-close fieldtest for "Runway — real-data ergonomics & honesty
hardening". Four curated end-to-end scenarios derived top-down from the
milestone promise, run as a downstream consumer against real GER40/EURUSD M1
bars (public interface only; no crates/*/src read):
1. per-instrument pip honesty (#22) — GER40 index pip 1.0 AND EURUSD forex
pip 0.0001 both honest end-to-end; AAPL.US/FOOBAR un-vetted refused
exit 2, no stdout leak
2. real GER40 M1 open via open_ohlc from aura-ingest ALONE (#80/#81/#92) —
no data-server dep, 53173 real bars, C4 O/H/L/C order, ts-window inverse
3. multi-tap ts-join (#93) — two taps at different cadences, 27744 rows,
0 by-ts mismatches; warm-up None rows where a positional zip would corrupt
4. run-registry coherence (#73/#82) — list/rank retired exit 2, families/
family live, two-store Registry read-back + rustdoc
Findings: 4 working, 1 friction, 1 spec_gap.
- friction: Cell exposes only from_i64/from_f64/... while the side-by-side
Scalar is a tagged enum with public I64(..)/F64(..) variants. The natural
param-authoring entry point is Scalar (Cell is the hot-path buffer carrier),
so the wrong reach is a self-correcting compile snag — ergonomic only.
Filed as a follow-up idea.
- spec_gap: unknown family id exits 0 empty — ratified into the C18 note in
the preceding commit (treat-as-empty contract).
Gate verdict: GREEN. All seven axes mapped to a covering scenario and exercised
on real bars; both finding classifications and the one material coverage gap
were adversarially re-verified. That gap — #22's FX-vetted pip face, untested by
the original scenario set — was probed (EURUSD -> sim-optimal(pip_size=0.0001),
FX-scaled, not index-mis-scaled) and folded into scenario 1, so the pip promise
is demonstrated on both positive faces. Every scenario re-run independently on
HEAD before commit.
Audit (architect drift review + cargo test --workspace) clean: code matches the
spec, the engine stayed domain-free (no instrument identity in Ctx / the hot
path), InstrumentSpec sits beside the hot path per C7/C15. No regression scripts
configured (no-op gate); the full suite is green.
Ratify: the architect flagged one ledger-realization gap — C10/C15 described
per-instrument pip as standing design intent but carried no realization note for
the now-shipped channel. Added dated realization notes to C10 (the per-instrument
pip channel + the refuse-don't-guess honesty rule) and C15 (instrument specs as
metadata, first realized by InstrumentSpec/instrument_spec).
Retire the ephemeral cycle artefacts (spec 0054, plan 0054) per the active-cycle
lifecycle. Deferred follow-up: #98 (route the runnable GER40 examples through the
lookup).
refs #22