Bind ingestion sources to the graph by role key, not list position #275

Closed
opened 2026-07-15 17:29:54 +02:00 by claude · 4 comments
Collaborator

Current state

Ingestion sources are bound to the graph purely by list position, at one runtime seam and two construction seams. A source is not a graph node: FlatGraph carries a fourth field sources: Vec<SourceSpec> parallel to nodes (crates/aura-engine/src/harness.rs:210-215), where each SourceSpec { kind, targets: Vec<Target{node, slot}> } (harness.rs:39-52) names, by raw index, which node input slots one source position fans into.

The positional coupling lives at three points (verified against the tree):

  1. RuntimeHarness::run(sources_in: Vec<Box<dyn Source>>) matches producer sources_in[s] to wiring self.sources[s] by index, guarded only by an arity assert (harness.rs:371-378). The per-cycle push forwards the popped value into nodes[t.node].inputs[t.slot] by raw index (harness.rs:411-416). No name, no id.
  2. Compile — each bound root Role { name, targets, source: Option<ScalarKind> } (crates/aura-engine/src/blueprint.rs:127-134) lowers to exactly one SourceSpec in role-declaration order (blueprint.rs:338). The role name is dropped; it never reaches the FlatGraph.
  3. CLI — the only by-name step: column_for_role maps a role name to an M1Field (crates/aura-cli/src/binding.rs:52-60), consumed twice in the same canonical order — to declare the source_roles in wrap_r (crates/aura-cli/src/main.rs:1540) and to open the columns. aura_ingest::open_columns opens one M1FieldSource per field in that order, so "source index i = field i".

The single invariant holding the two positional halves aligned is the canonical M1Field declaration order. A drift among role declaration, column-open order, and SourceSpec order silently mis-wires a run — column X feeds role Y with no panic.

Proposal

Replace the positional bind with a keyed by-name supply, on the binding axis only. The merge and the DAG stay untouched.

  • SourceSpec carries a stable role key (the role name survives as a load-bearing key, not only a debug symbol) and an explicit order: u32 tie-break rank, decoupled from the Vec position.
  • Harness::run takes a keyed supply (role -> Box<dyn Source>) and resolves each SourceSpec by key; a missing or misspelled role is a named error, not silent drift or a contextless arity panic.
  • The k-way merge (harness.rs:387-408) is byte-identical except its tie-break reads spec.order instead of the Vec index.

The CLI drift surface (the canonical-order triple-alignment) disappears: the name carries the role->source assignment.

Why not "sources as input-less nodes"

The reframe considered first — make each source a native input-less FlatGraph node, dissolving the ingestion seam entirely — is incoherent with the engine model, on three verified grounds:

  • An input-less node never fires: fires() iterates a node's firing slice and returns false on an empty one (harness.rs:461-491). This is exactly why Const carries a discarded dummy "clock" input — its own doc-comment: "a zero-input node never evaluates in the total-push engine" (crates/aura-std/src/const_node.rs:4-6).
  • The per-cycle cursor is an output of the k-way merge: Harness::run picks the global-min (timestamp, source_index) and only then is ts fixed and handed to every eval (harness.rs:387-408). A source node cannot be walked in a topological order whose cursor its own emission is required to define.
  • Dissolving the seam forces a causality/merge violation either way: either the merge survives as a pre-graph scheduler (the "node" is a relabel and the reframe cosmetic) or the DAG computes the min across sources' next timestamps — an in-graph peek-the-future that the no-look-ahead invariant (C2) makes physically absent today, and a merge inside the graph that C3 forbids.

So the seam stays; only the binding to it changes.

Guardrails any implementation must keep

  • Merge stays at ingestion (C3 — one merge, at the ingestion boundary; no merge/as-of join inside the graph). The keyed supply changes who supplies stream s, never where the merge runs.
  • Deterministic tie-break preserved (C4 — same-timestamp ties break by a fixed source order; C1 reproducibility). The explicit order rank must be stable and reconstructible; this is the one part that is not free, and it is what makes the change a ledger decision rather than a refactor — the tie-break moves from implicit Vec position to an explicit rank.
  • Data-window stays an axis orthogonal to topology (C12). No source, window, or I/O state is baked into the FlatGraph; sources remain externally supplied to run, so disjoint sims stay concurrently runnable over a shared read-only archive.
  • No I/O in the hot loop. File/decode stays at the aura-ingest boundary; the type-erased per-cycle eval is unchanged.

Impact on strategy development

  • The inner authoring surface — signal-node logic, eval, interior input roles (source: None) — is unchanged.
  • The strategy<->data binding seam becomes order-independent (roles freely reorderable), name-addressable (multi-symbol / multi-feed roles carried by name, e.g. us500.close, not positional slots), and loud on error (a mis-wire becomes a named start-time failure instead of a silently wrong backtest).
  • It opens the by-name door for recorded non-price feeds additively (a sentiment role bound to a recorded stream), without re-taring the positional alignment — the same additive-growth shape #124 anticipates for the DataServer metadata seam.

Open (to be decided before implementation)

  • Where role/order live on SourceSpec and how order is assigned (role-declaration order at lower time is the natural default, but must survive as an explicit field, not an implicit list index).
  • The keyed supply type at the run seam (role -> Box<dyn Source> map vs an ordered keyed list) and how a duplicate / missing / extra key is reported.
  • Whether the CLI ResolvedBinding collapses to a keyed supply directly, retiring columns()'s positional Vec<M1Field>.
  • Ledger placement: this touches the C4 tie-break realization (implicit position -> explicit rank) and the C3/C11 source-seam text; it is a design decision for the ledger, not a refactor.

Provenance: requested 2026-07-15 following an analysis of the ingestion path and the "sources as native nodes" question.

## Current state Ingestion sources are bound to the graph purely by **list position**, at one runtime seam and two construction seams. A source is not a graph node: `FlatGraph` carries a fourth field `sources: Vec<SourceSpec>` parallel to `nodes` (crates/aura-engine/src/harness.rs:210-215), where each `SourceSpec { kind, targets: Vec<Target{node, slot}> }` (harness.rs:39-52) names, by raw index, which node input slots one source position fans into. The positional coupling lives at three points (verified against the tree): 1. **Runtime** — `Harness::run(sources_in: Vec<Box<dyn Source>>)` matches producer `sources_in[s]` to wiring `self.sources[s]` by index, guarded only by an arity assert (harness.rs:371-378). The per-cycle push forwards the popped value into `nodes[t.node].inputs[t.slot]` by raw index (harness.rs:411-416). No name, no id. 2. **Compile** — each bound root `Role { name, targets, source: Option<ScalarKind> }` (crates/aura-engine/src/blueprint.rs:127-134) lowers to exactly one `SourceSpec` in role-declaration order (blueprint.rs:338). The role name is dropped; it never reaches the `FlatGraph`. 3. **CLI** — the only by-name step: `column_for_role` maps a role name to an `M1Field` (crates/aura-cli/src/binding.rs:52-60), consumed twice in the same canonical order — to declare the `source_role`s in `wrap_r` (crates/aura-cli/src/main.rs:1540) and to open the columns. `aura_ingest::open_columns` opens one `M1FieldSource` per field in that order, so "source index i = field i". The single invariant holding the two positional halves aligned is the canonical `M1Field` declaration order. A drift among role declaration, column-open order, and `SourceSpec` order silently mis-wires a run — column X feeds role Y with no panic. ## Proposal Replace the positional bind with a keyed by-name supply, on the binding axis only. The merge and the DAG stay untouched. - `SourceSpec` carries a stable `role` key (the role name survives as a load-bearing key, not only a debug symbol) and an explicit `order: u32` tie-break rank, decoupled from the `Vec` position. - `Harness::run` takes a keyed supply (`role -> Box<dyn Source>`) and resolves each `SourceSpec` by key; a missing or misspelled role is a named error, not silent drift or a contextless arity panic. - The k-way merge (harness.rs:387-408) is byte-identical except its tie-break reads `spec.order` instead of the `Vec` index. The CLI drift surface (the canonical-order triple-alignment) disappears: the name carries the role->source assignment. ## Why not "sources as input-less nodes" The reframe considered first — make each source a native input-less `FlatGraph` node, dissolving the ingestion seam entirely — is incoherent with the engine model, on three verified grounds: - An input-less node never fires: `fires()` iterates a node's firing slice and returns `false` on an empty one (harness.rs:461-491). This is exactly why `Const` carries a discarded dummy "clock" input — its own doc-comment: "a zero-input node never evaluates in the total-push engine" (crates/aura-std/src/const_node.rs:4-6). - The per-cycle cursor is an **output** of the k-way merge: `Harness::run` picks the global-min `(timestamp, source_index)` and only then is `ts` fixed and handed to every `eval` (harness.rs:387-408). A source node cannot be walked in a topological order whose cursor its own emission is required to define. - Dissolving the seam forces a causality/merge violation either way: either the merge survives as a pre-graph scheduler (the "node" is a relabel and the reframe cosmetic) or the DAG computes the min across sources' *next* timestamps — an in-graph peek-the-future that the no-look-ahead invariant (C2) makes physically absent today, and a merge inside the graph that C3 forbids. So the seam stays; only the *binding* to it changes. ## Guardrails any implementation must keep - **Merge stays at ingestion** (C3 — one merge, at the ingestion boundary; no merge/as-of join inside the graph). The keyed supply changes who supplies stream `s`, never where the merge runs. - **Deterministic tie-break preserved** (C4 — same-timestamp ties break by a fixed source order; C1 reproducibility). The explicit `order` rank must be stable and reconstructible; this is the one part that is not free, and it is what makes the change a ledger decision rather than a refactor — the tie-break moves from implicit `Vec` position to an explicit rank. - **Data-window stays an axis orthogonal to topology** (C12). No source, window, or I/O state is baked into the `FlatGraph`; sources remain externally supplied to `run`, so disjoint sims stay concurrently runnable over a shared read-only archive. - **No I/O in the hot loop.** File/decode stays at the `aura-ingest` boundary; the type-erased per-cycle eval is unchanged. ## Impact on strategy development - The inner authoring surface — signal-node logic, `eval`, interior input roles (`source: None`) — is unchanged. - The strategy<->data binding seam becomes order-independent (roles freely reorderable), name-addressable (multi-symbol / multi-feed roles carried by name, e.g. `us500.close`, not positional slots), and loud on error (a mis-wire becomes a named start-time failure instead of a silently wrong backtest). - It opens the by-name door for recorded non-price feeds additively (a `sentiment` role bound to a recorded stream), without re-taring the positional alignment — the same additive-growth shape #124 anticipates for the DataServer metadata seam. ## Open (to be decided before implementation) - Where `role`/`order` live on `SourceSpec` and how `order` is assigned (role-declaration order at lower time is the natural default, but must survive as an explicit field, not an implicit list index). - The keyed supply type at the `run` seam (`role -> Box<dyn Source>` map vs an ordered keyed list) and how a duplicate / missing / extra key is reported. - Whether the CLI `ResolvedBinding` collapses to a keyed supply directly, retiring `columns()`'s positional `Vec<M1Field>`. - Ledger placement: this touches the C4 tie-break realization (implicit position -> explicit rank) and the C3/C11 source-seam text; it is a design decision for the ledger, not a refactor. Provenance: requested 2026-07-15 following an analysis of the ingestion path and the "sources as native nodes" question.
claude added the feature label 2026-07-15 17:29:54 +02:00
claude self-assigned this 2026-07-15 17:35:06 +02:00
Author
Collaborator

Design reconciliation (specify)

Spec: source-role-key-binding. The "Open (to be decided before implementation)"
forks in the issue body are resolved below as derived decisions (a groundable
rationale, not a user preference call), recorded before the spec is written.

  • Fork: layer of the by-name key. The keyed supply resolves above an
    unchanged Harness::run, rather than re-typing the primitive run signature.
    A pure resolver turns a keyed Vec<(role, Box<dyn Source>)> supply into the
    positional Vec<Box<dyn Source>> the existing run consumes, in SourceSpec
    order.
    Basis: derived — C7/C23 make the raw-index positional supply the honest
    primitive where no role name exists; hand-built engine graphs wire
    SourceSpec { kind, targets } by raw Target { node, slot }
    (crates/aura-engine/src/harness.rs:49). By-name binding is meaningful only
    where a Role.name exists — the blueprint lowering that currently drops it
    (crates/aura-engine/src/blueprint.rs:338). Layering the resolver above run
    targets the drift exactly there, leaves the k-way merge byte-identical (the
    merge loop is untouched), and gives the user-facing path a named Result
    error while a raw-index arity mismatch stays an internal panic — a user
    data-binding error and an engine wiring bug are different failure classes.

  • Fork: role representation on SourceSpec. role: Option<String>
    Some(name) for a spec lowered from a bound Role (carried from Role.name),
    None for a hand-built raw-index spec.
    Basis: derived — this realizes "the name survives where it exists"; a mandatory
    String would force "" / synthetic identifiers onto raw-index primitives
    that conceptually have no role.

  • Fork: explicit order: u32, or the SourceSpec Vec index as the rank.
    No separate order field. The tie-break rank is the SourceSpec Vec index;
    the resolver produces the positional supply in SourceSpec order, so the
    merge's existing source-index tie-break resolves on source declaration order,
    independent of how the caller orders the keyed supply.
    Basis: derived — C4 already defines the tie-break as "ties break by source
    declaration order" (docs/design/INDEX.md:157), which the SourceSpec Vec index
    already encodes. An order field equal to that index is a redundant second
    source of truth that can only drift from it. The coupling the issue targets is
    between the caller's supply order and the wiring — dissolved by resolving into
    SourceSpec order, which needs no new field. Byte-identical today (declaration
    order == the historical supply index). This resolves the issue body's own note
    that "the explicit order rank … is the one part that is not free": the rank was
    already present as the SourceSpec Vec order; only the by-name supply is new.

  • Fork: CLI ResolvedBinding / columns(). columns() stays the open-list
    (which archive columns to open). The drift fix pairs each opened source with its
    role name — a Vec<(String, Box<dyn Source>)> supply resolved by name — retiring
    the reliance on open_columns and wrap_r sharing one canonical order
    (crates/aura-cli/src/binding.rs:9-11; crates/aura-cli/src/main.rs:1539, 1729).
    Basis: derived — the by-name match at the run boundary makes the three-point
    canonical-order alignment structurally unnecessary; columns() as the column
    opener is orthogonal to it.

  • Ledger placement. C4's guarantee is unchanged — the tie-break stays "by
    source declaration order", which this design keeps. A realization note records
    that the supply is resolved by name into declaration order, so supply ordering
    is no longer load-bearing. A scoped refinement of the Construction-layer C23
    statement (docs/design/INDEX.md:1653-1656) records that a SourceSpec.role is
    load-bearing for source binding, while every other flat-graph name (edges,
    ports, composite boundaries) stays a non-load-bearing raw-index symbol.

The k-way merge and the per-cycle event loop are not touched; C1 determinism is
preserved by construction (the run loop is byte-identical, verified downstream by
the currently-green multi-source run tests).

## Design reconciliation (specify) Spec: `source-role-key-binding`. The "Open (to be decided before implementation)" forks in the issue body are resolved below as **derived** decisions (a groundable rationale, not a user preference call), recorded before the spec is written. - **Fork: layer of the by-name key.** The keyed supply resolves *above* an unchanged `Harness::run`, rather than re-typing the primitive `run` signature. A pure resolver turns a keyed `Vec<(role, Box<dyn Source>)>` supply into the positional `Vec<Box<dyn Source>>` the existing `run` consumes, in `SourceSpec` order. Basis: derived — C7/C23 make the raw-index positional supply the honest primitive where no role name exists; hand-built engine graphs wire `SourceSpec { kind, targets }` by raw `Target { node, slot }` (crates/aura-engine/src/harness.rs:49). By-name binding is meaningful only where a `Role.name` exists — the blueprint lowering that currently drops it (crates/aura-engine/src/blueprint.rs:338). Layering the resolver above `run` targets the drift exactly there, leaves the k-way merge byte-identical (the merge loop is untouched), and gives the user-facing path a named `Result` error while a raw-index arity mismatch stays an internal panic — a user data-binding error and an engine wiring bug are different failure classes. - **Fork: `role` representation on `SourceSpec`.** `role: Option<String>` — `Some(name)` for a spec lowered from a bound `Role` (carried from `Role.name`), `None` for a hand-built raw-index spec. Basis: derived — this realizes "the name survives where it exists"; a mandatory `String` would force `""` / synthetic identifiers onto raw-index primitives that conceptually have no role. - **Fork: explicit `order: u32`, or the `SourceSpec` Vec index as the rank.** No separate `order` field. The tie-break rank is the `SourceSpec` Vec index; the resolver produces the positional supply in `SourceSpec` order, so the merge's existing source-index tie-break resolves on source *declaration* order, independent of how the caller orders the keyed supply. Basis: derived — C4 already defines the tie-break as "ties break by source declaration order" (docs/design/INDEX.md:157), which the `SourceSpec` Vec index already encodes. An `order` field equal to that index is a redundant second source of truth that can only drift from it. The coupling the issue targets is between the *caller's supply order* and the wiring — dissolved by resolving into `SourceSpec` order, which needs no new field. Byte-identical today (declaration order == the historical supply index). This resolves the issue body's own note that "the explicit order rank … is the one part that is not free": the rank was already present as the `SourceSpec` Vec order; only the by-name supply is new. - **Fork: CLI `ResolvedBinding` / `columns()`.** `columns()` stays the open-list (which archive columns to open). The drift fix pairs each opened source with its role name — a `Vec<(String, Box<dyn Source>)>` supply resolved by name — retiring the reliance on `open_columns` and `wrap_r` sharing one canonical order (crates/aura-cli/src/binding.rs:9-11; crates/aura-cli/src/main.rs:1539, 1729). Basis: derived — the by-name match at the run boundary makes the three-point canonical-order alignment structurally unnecessary; `columns()` as the column opener is orthogonal to it. - **Ledger placement.** C4's guarantee is unchanged — the tie-break stays "by source declaration order", which this design keeps. A realization note records that the supply is resolved by name into declaration order, so supply ordering is no longer load-bearing. A scoped refinement of the Construction-layer C23 statement (docs/design/INDEX.md:1653-1656) records that a `SourceSpec.role` is load-bearing for source binding, while every other flat-graph name (edges, ports, composite boundaries) stays a non-load-bearing raw-index symbol. The k-way merge and the per-cycle event loop are not touched; C1 determinism is preserved by construction (the run loop is byte-identical, verified downstream by the currently-green multi-source run tests).
Author
Collaborator

Spec auto-signed (specify, /boss)

The spec source-role-key-binding was signed autonomously: the Step-5
grounding-check returned PASS — an independent fresh-context agent's verdict that
every load-bearing assumption the spec rests on traces to a currently-green test.
Ratified: SourceSpec is { kind, targets } today (harness.rs:49); the blueprint
lowering drops role.name today (blueprint.rs:338); run pairs sources to specs by
index and the merge picks by (timestamp, source index); the four .sources-equality
twins are green and both sides declare matching root role names (so the role-carrying
equality stays green); and the CLI opens columns and declares roles in one shared
canonical order. No human signed; under autonomous mode the PASS is the signature.

Capability signed: binding a harness's ingestion feeds to the graph by role name
rather than by supply list position — a mis-bind becomes a named start-time error
instead of a silently wrong run, and the k-way merge / event loop are untouched
(C1 byte-identical).

The spec is a git-ignored working file (never committed). The work continues on the
run branch and awaits review before any merge to main.

## Spec auto-signed (specify, /boss) The spec `source-role-key-binding` was signed autonomously: the Step-5 grounding-check returned PASS — an independent fresh-context agent's verdict that every load-bearing assumption the spec rests on traces to a currently-green test. Ratified: `SourceSpec` is `{ kind, targets }` today (harness.rs:49); the blueprint lowering drops `role.name` today (blueprint.rs:338); `run` pairs sources to specs by index and the merge picks by `(timestamp, source index)`; the four `.sources`-equality twins are green and both sides declare matching root role names (so the role-carrying equality stays green); and the CLI opens columns and declares roles in one shared canonical order. No human signed; under autonomous mode the PASS is the signature. Capability signed: binding a harness's ingestion feeds to the graph by role name rather than by supply list position — a mis-bind becomes a named start-time error instead of a silently wrong run, and the k-way merge / event loop are untouched (C1 byte-identical). The spec is a git-ignored working file (never committed). The work continues on the run branch and awaits review before any merge to main.
Author
Collaborator

Scope decisions (planner)

Two decomposition boundaries the file-structure recon surfaced, decided as derived
scope calls:

  • The full production supply path migrates to by-name. Not only the single-run
    entry (run_signal_r), but the sweep/campaign path (run_blueprint_member and
    its run_sources callers; campaign_run) route through the new run_bound. The
    shared supply helpers (resolve_run_data, open_real_source, run_sources)
    return a keyed Vec<(String, Box<dyn Source>)> — each opened column paired with
    its role name (binding.entries() and binding.columns() are the same canonical
    order, so the zip is sound).
    Basis: derived — the silent-mis-wire class #275 removes is most costly on the
    campaign / sweep path (many cells per run; the per-cell-fault incident behind
    #272 is the reference). Migrating only the single-run entry would ship the fix
    where a mis-wire is cheapest and skip it where it is dearest.

  • The 28 fieldtest SourceSpec literals are updated to the raw (no-role) form.
    They sit in separate packages the workspace build does not compile, but they are
    in-tree path-dependencies on aura-engine; a path-dep package that no longer
    compiles against the current engine is in-tree breakage, not a frozen snapshot.
    The update is mechanical (SourceSpec::raw(kind, targets)), isolated in its own
    task, and verified by building the affected fieldtest packages.
    Basis: derived — tree coherence for in-tree path-deps; the alternative (leave
    them) is silent bit-rot of the tracked fieldtest corpus.

The raw-index engine / composites / ingest tests keep the positional run(Vec)
primitive (their specs carry no role); only the ResolvedBinding-bearing production
sites move to run_bound.

## Scope decisions (planner) Two decomposition boundaries the file-structure recon surfaced, decided as derived scope calls: - **The full production supply path migrates to by-name.** Not only the single-run entry (`run_signal_r`), but the sweep/campaign path (`run_blueprint_member` and its `run_sources` callers; `campaign_run`) route through the new `run_bound`. The shared supply helpers (`resolve_run_data`, `open_real_source`, `run_sources`) return a keyed `Vec<(String, Box<dyn Source>)>` — each opened column paired with its role name (`binding.entries()` and `binding.columns()` are the same canonical order, so the zip is sound). Basis: derived — the silent-mis-wire class #275 removes is most costly on the campaign / sweep path (many cells per run; the per-cell-fault incident behind #272 is the reference). Migrating only the single-run entry would ship the fix where a mis-wire is cheapest and skip it where it is dearest. - **The 28 fieldtest `SourceSpec` literals are updated to the raw (no-role) form.** They sit in separate packages the workspace build does not compile, but they are in-tree path-dependencies on `aura-engine`; a path-dep package that no longer compiles against the current engine is in-tree breakage, not a frozen snapshot. The update is mechanical (`SourceSpec::raw(kind, targets)`), isolated in its own task, and verified by building the affected fieldtest packages. Basis: derived — tree coherence for in-tree path-deps; the alternative (leave them) is silent bit-rot of the tracked fieldtest corpus. The raw-index engine / composites / ingest tests keep the positional `run(Vec)` primitive (their specs carry no role); only the ResolvedBinding-bearing production sites move to `run_bound`.
Author
Collaborator

Scope correction (implement) — fieldtest sweep withdrawn

The earlier scope decision to sweep the 28 fieldtest SourceSpec literals rested on
a premise that turned out false. During implementation the fieldtest packages were
found to already fail to build against the current engine API, for reasons wholly
unrelated to SourceSpec: Harness::bootstrap now takes a single FlatGraph
argument (the fieldtests still call the old 3-arg (nodes, sources, edges) form),
aura_core::InputSpec is gone, Recorder::new / SimBroker::new signatures drifted,
RMetrics lost fields these tests reference, and Scalar helpers were renamed. This
was verified to pre-date this change (stashing the SourceSpec edits and rebuilding
reproduces the identical error set).

So the fieldtest corpus is pre-existing bit-rot from earlier cycles, not an
otherwise-buildable in-tree path-dep. A partial SourceSpec migration neither revives
nor further breaks it, and touching it only adds churn (plus regenerated lockfiles) to
non-building packages. The sweep was therefore reverted; reviving the corpus against
the current engine API is separate work, out of scope here.

The engine + CLI change is complete and workspace-green regardless — the fieldtests
are not workspace members, so they never gated the build.

## Scope correction (implement) — fieldtest sweep withdrawn The earlier scope decision to sweep the 28 fieldtest `SourceSpec` literals rested on a premise that turned out false. During implementation the fieldtest packages were found to **already fail to build against the current engine API**, for reasons wholly unrelated to `SourceSpec`: `Harness::bootstrap` now takes a single `FlatGraph` argument (the fieldtests still call the old 3-arg `(nodes, sources, edges)` form), `aura_core::InputSpec` is gone, `Recorder::new` / `SimBroker::new` signatures drifted, `RMetrics` lost fields these tests reference, and `Scalar` helpers were renamed. This was verified to pre-date this change (stashing the `SourceSpec` edits and rebuilding reproduces the identical error set). So the fieldtest corpus is pre-existing bit-rot from earlier cycles, not an otherwise-buildable in-tree path-dep. A partial `SourceSpec` migration neither revives nor further breaks it, and touching it only adds churn (plus regenerated lockfiles) to non-building packages. The sweep was therefore reverted; reviving the corpus against the current engine API is separate work, out of scope here. The engine + CLI change is complete and workspace-green regardless — the fieldtests are not workspace members, so they never gated the build.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Brummel/Aura#275