Generalise sweep member-key derivation beyond the two hardcoded grid axes #105
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
sweep_member_key(crates/aura-cli/src/main.rs:186-196) derives a familymember's on-disk trace key from two hard-coded axis paths,
signals.trend.fast.lengthandsignals.trend.slow.length, renderingf{fast}s{slow}. This is deterministic and collision-free over today'sbuilt-in grid (the only grid that exists — it varies exactly those two axes;
the rest are singletons), and it panics (naming the axis) if either listed axis
is absent.
The latent foot-gun: the key is collision-free only over that exact grid, not
over "a sweep" generically. If a future grid varies a different axis (e.g.
exposure.scale, or a momentum leg) while keepingtrend.fast/trend.slowfixed, two distinct grid points map to the same
f…s…dir and silentlyoverwrite each other's traces. The panic guards a missing listed axis, not an
unlisted varying one.
This is not a current defect (there is one grid, and it is covered), so it is
filed rather than fixed in-cycle. When the sweep grid generalises, the key
derivation should become collision-free for any set of varying axes — e.g.
derive it from the full set of swept (varying) param values, or from a stable
encoding of the whole grid point, rather than two named axes.
Surfaced by the 0058 cycle-close architect review (family-member trace
persistence, #104). Severity: medium / latent.
context: depends on the sweep grid generalising beyond the built-in demo axes.
Design record + reconciliation (specify entry, /boss)
This issue is the reference issue for the cycle that fixes the foot-gun it
describes AND ships a demo strategy proving the fix is generic. The design was
settled in an in-context discussion, approved by the user ("mach es so!"), then
refined with the portability constraint below. Recording the resolved
load-bearing forks before the spec is written.
Scope. Two coupled deliverables, in this order:
not just the two hardcoded axes
signals.trend.fast.length/signals.trend.slow.length.empirical proof that (1) is generic.
Fork: key derivation source -> the key names the varying axes (grid axes
with >1 value), in param-space slot order, with their values.
Basis: derived. Varying axes are the only thing that distinguishes members
within a family; pinned singletons are constant and carry no information.
Which-axis-varies is grid-global knowledge (axis value-counts live in
GridSpace), so the varying mask is computed where the grid is known and theper-member key is built from it. Decouples the key from any specific axis name
-> generic over any strategy.
Fork: on-disk key format (portability) -> the key is a filesystem-conformant
directory name on all major OSes (Linux, Windows, macOS). Charset restricted to
[A-Za-z0-9._-]. Per varying axis a tokenname-value; tokens joined by_;any char outside the portable set is sanitised. Values are rendered caselessly
(integers; bool as
true/false; decimal floats with NO scientific notation,matching Rust's f64
Display) so two members of one family can never differ onlyby case -> no silent overwrite on case-insensitive filesystems (NTFS/APFS
default). Key length is capped to the per-component / path limit. This replaces
the earlier
name=value,...sketch, whose=/,were the user's portabilityconcern (bare-FS-legal but not robust under cloud-sync/tooling).
Basis: user constraint ("konforme Verzeichnis-namen fuer alle wichtigen
Betriebssysteme"), made concrete + derived case-insensitive-FS safety.
Fork: demo strategy shape -> EMA-distance momentum:
price -> Ema(length:i64) -> Sub(price - ema) -> Exposure(scale:f64) -> LongOnly(enabled:bool) -> SimBroker.LongOnlyis a new node carrying the boolparam (true -> clamp short exposure to >=0; false -> pass-through). Three swept
params of three kinds incl. a bool; genuinely different from the SMA-cross demo.
The new strategy's sweep, persisted with
--trace, yields member dirs whosenames carry the bool (e.g.
..._longonly.enabled-true), which is the live proofthe generic key works.
Basis: user-approved ("Bau eine Strategie ... mehr als zwei und ein bool als
Parameter").
Provenance: forks settled in-context with the user and approved via
/boss mach es so!plus the portability refinement; recorded here as the run's decision log.Resolved — cycle 0059 (audit drift-clean)
The foot-gun is fixed and proven generic. The sweep member-key is no longer
derived from two hardcoded axis names; it is derived from the axes that actually
VARY (
SweepBinder::varying_axes) and rendered as a filesystem-conformantdirectory component:
[A-Za-z0-9._-](valid on Linux/Windows/macOS, URL- and cloud-sync-safe);sci-notation) -> two members of one family never differ only by case -> no
silent overwrite on case-insensitive filesystems (NTFS/APFS);
Proven generic by a new demo strategy whose params are unlike the SMA-cross
demo, incl. a bool:
momentum(Ema -> Sub(price-ema) -> Exposure -> LongOnly ->SimBroker), swept via
aura sweep --strategy momentum. Member dirs now read e.g.ema.length-5_exposure.scale-0.5_longonly.enabled-true(the bool conformant inthe dir name), each chartable.
LongOnlyis the first aura-std node with a boolparam. The SMA sweep's dirs moved from
f2s4to the portable form too.Commits (local, unpushed): spec
a8ba019, plan44e3a1a, impl27f850d+0b73a75,audit-close
b92aab7(architect drift-clean; full workspace suite + clippy green).The C22/#101 ledger member-key note's sweep clause was amended to the new form.