Add a wiring-only project tier: Aura.toml without a crate is the smallest project #241

Closed
opened 2026-07-12 12:39:49 +02:00 by Brummel · 6 comments
Owner

aura new

A quick data-only experiment — a throwaway project running std-vocabulary blueprints through the dissolved verbs — is impossible today; both entry routes are closed:

  • Outside a project, the dissolved verbs refuse up front by design: aura sweep … --real GER40 in a bare directory exits 1 with "sweep needs a project" and leaves no store (#218's refuse-don't-litter gate; validate_and_register_axes, crates/aura-cli/src/main.rs:2976; pinned by sweep_outside_project_refuses_and_leaves_no_store, crates/aura-cli/tests/cli_run.rs:57).
  • Inside a project, the load boundary hard-requires a built cdylib: an Aura.toml without a crate dies at the metadata probe — aura: cargo metadata failed: error: could not find 'Cargo.toml' in …, exit 1 (crates/aura-cli/src/project.rs:228-240) — and with a crate but no built artifact it refuses with the ArtifactMissing message (project.rs:447).

The smallest runnable project is therefore a full Rust crate plus a cargo build — role 2 (extension dev) ceremony imposed on a user who only takes roles 5-7 (methodology / strategy / campaign design over the std vocabulary). #188's ratified diagnosis already named this ("the scaffold makes every project look like an extension project") and derived the fix: a wiring-only project tier (Aura.toml + blueprints/ + runs/, no crate ceremony; the crate joins when the first native node arrives).

Decision (user, 2026-07-12)

The wiring-only (data-only) tier is the intended first step of every new project — small steps that deepen as the project grows, the same rationale that produced the dissolved-verb sugar. The full Rust environment joins only when project-specific native nodes are needed; at that moment the trader decides whether to author the native components or to outsource that role. Genuinely new standard nodes are engine work (aura-std), not project work. This deliberately amends invariant 9's "a project is always a Rust crate" reading (already re-opened by #188).

Sketch (direction, not a spec)

  • Load boundary: an Aura.toml whose directory holds no cargo crate resolves to a data-only Env — std vocabulary only, project-local runs/ (paths.runs), paths.data honored; run-manifest provenance carries the project commit, no dylib fields.
  • aura new scaffolds the wiring-only tier by default (Aura.toml, a std-only starter blueprint, CLAUDE.md, .gitignore, git init); the current crate scaffold becomes the native tier, reached by an explicit, visible upgrade act. The current starter blueprint binds the project-namespaced Scale node (crates/aura-cli/src/scaffold.rs:185) and needs a std-only sibling.
  • The dylib staleness warning and the ABI/charter checks apply to the native tier only.

Open forks (for the design pass, not settled here): the shape of the upgrade act (aura new --native on scaffold vs a dedicated upgrade verb on an existing project); whether a data-only project carries a namespace at all; what the data-only manifest provenance looks like.

## aura new A quick data-only experiment — a throwaway project running std-vocabulary blueprints through the dissolved verbs — is impossible today; both entry routes are closed: - Outside a project, the dissolved verbs refuse up front by design: `aura sweep … --real GER40` in a bare directory exits 1 with "sweep needs a project" and leaves no store (#218's refuse-don't-litter gate; `validate_and_register_axes`, `crates/aura-cli/src/main.rs:2976`; pinned by `sweep_outside_project_refuses_and_leaves_no_store`, `crates/aura-cli/tests/cli_run.rs:57`). - Inside a project, the load boundary hard-requires a built cdylib: an `Aura.toml` without a crate dies at the metadata probe — `aura: cargo metadata failed: error: could not find 'Cargo.toml' in …`, exit 1 (`crates/aura-cli/src/project.rs:228-240`) — and with a crate but no built artifact it refuses with the ArtifactMissing message (`project.rs:447`). The smallest runnable project is therefore a full Rust crate plus a `cargo build` — role 2 (extension dev) ceremony imposed on a user who only takes roles 5-7 (methodology / strategy / campaign design over the std vocabulary). #188's ratified diagnosis already named this ("the scaffold makes every project look like an extension project") and derived the fix: a wiring-only project tier (`Aura.toml` + `blueprints/` + `runs/`, no crate ceremony; the crate joins when the first native node arrives). ## Decision (user, 2026-07-12) The wiring-only (data-only) tier is the intended first step of every new project — small steps that deepen as the project grows, the same rationale that produced the dissolved-verb sugar. The full Rust environment joins only when *project-specific* native nodes are needed; at that moment the trader decides whether to author the native components or to outsource that role. Genuinely new *standard* nodes are engine work (`aura-std`), not project work. This deliberately amends invariant 9's "a project is always a Rust crate" reading (already re-opened by #188). ## Sketch (direction, not a spec) - Load boundary: an `Aura.toml` whose directory holds no cargo crate resolves to a data-only `Env` — std vocabulary only, project-local `runs/` (`paths.runs`), `paths.data` honored; run-manifest provenance carries the project commit, no dylib fields. - `aura new` scaffolds the wiring-only tier by default (`Aura.toml`, a std-only starter blueprint, `CLAUDE.md`, `.gitignore`, git init); the current crate scaffold becomes the native tier, reached by an explicit, visible upgrade act. The current starter blueprint binds the project-namespaced `Scale` node (`crates/aura-cli/src/scaffold.rs:185`) and needs a std-only sibling. - The dylib staleness warning and the ABI/charter checks apply to the native tier only. Open forks (for the design pass, not settled here): the shape of the upgrade act (`aura new --native` on scaffold vs a dedicated upgrade verb on an existing project); whether a data-only project carries a namespace at all; what the data-only manifest provenance looks like.
Brummel added the feature label 2026-07-12 12:39:49 +02:00
Author
Owner

Fork package: the role-switch act (design settled)

User decisions (2026-07-12), settling the forks left open in the issue body's sketch:

  • Fork: shape of the role-switch act → a dedicated verb, not a flag on aura new. The act addresses a different role — the extension developer (role 2 in the nine-role model, #188) — than the trader running the research project (roles 5-7). A dedicated verb makes the switch the visible act the role model requires, and it creates that role's own artifact home instead of mutating the trader's project.
  • Fork: native development lives beside the research project, not inside it. A node crate has zero data-plane dependencies — verified: the crate scaffold depends only on aura-core (crates/aura-cli/src/scaffold.rs:87-101); nodes are pure stream transformers over read-only input windows; the only couplings are (a) the <ns>::TypeId strings blueprints reference by name and (b) the engine-ABI stamp checked at load (crates/aura-cli/src/project.rs:308-347). Native development is therefore its own project beside the research project — the natural outsourcing boundary, and cross-project reuse (invariant 9's shared node crates) falls out for free.
  • Consequence: the "upgrade act" dissolves. A research project never changes tier — it stays data-only for its whole life. The verb creates and attaches: it scaffolds a node crate beside the project and records a pointer in the research project's Aura.toml (a [nodes] path — a research-time path pointer, like the engine path dependency).

Derived (orchestrator, 2026-07-12):

  • The Aura.toml format carries a list of node-crate pointers; the first ship binds exactly one. The vocabulary-merge and collision machinery already exists (charter check: project-first resolution, no std collision), but a single-crate load keeps the boundary change minimal; the list form avoids a format break when multi-crate support lands.
  • Backward compatibility is additive (direction — the spec fixes the exact rules): no pointer + a crate in the project root = today's native project, unchanged; no pointer + no crate = the new data-only tier; a pointer = load from there.

Status: design settled — ready for spec production.

## Fork package: the role-switch act (design settled) User decisions (2026-07-12), settling the forks left open in the issue body's sketch: - **Fork: shape of the role-switch act → a dedicated verb, not a flag on `aura new`.** The act addresses a different role — the extension developer (role 2 in the nine-role model, #188) — than the trader running the research project (roles 5-7). A dedicated verb makes the switch the visible act the role model requires, and it creates that role's own artifact home instead of mutating the trader's project. - **Fork: native development lives beside the research project, not inside it.** A node crate has zero data-plane dependencies — verified: the crate scaffold depends only on `aura-core` (`crates/aura-cli/src/scaffold.rs:87-101`); nodes are pure stream transformers over read-only input windows; the only couplings are (a) the `<ns>::TypeId` strings blueprints reference by name and (b) the engine-ABI stamp checked at load (`crates/aura-cli/src/project.rs:308-347`). Native development is therefore its own project **beside** the research project — the natural outsourcing boundary, and cross-project reuse (invariant 9's shared node crates) falls out for free. - **Consequence: the "upgrade act" dissolves.** A research project never changes tier — it stays data-only for its whole life. The verb *creates and attaches*: it scaffolds a node crate beside the project and records a pointer in the research project's `Aura.toml` (a `[nodes]` path — a research-time path pointer, like the engine path dependency). Derived (orchestrator, 2026-07-12): - **The `Aura.toml` format carries a list of node-crate pointers; the first ship binds exactly one.** The vocabulary-merge and collision machinery already exists (charter check: project-first resolution, no std collision), but a single-crate load keeps the boundary change minimal; the list form avoids a format break when multi-crate support lands. - **Backward compatibility is additive** (direction — the spec fixes the exact rules): no pointer + a crate in the project root = today's native project, unchanged; no pointer + no crate = the new data-only tier; a pointer = load from there. Status: design settled — ready for spec production.
Author
Owner

Spec auto-signed (grounding-check PASS, no human in the loop)

The spec for this issue (working file docs/specs/wiring-only-project-tier.md, git-ignored) was signed autonomously on 2026-07-12: an independent fresh-context grounding check extracted its 13 load-bearing assumptions about current codebase behaviour (load boundary, discovery, crate pipeline, provenance stamping, scaffold shape, the outside-project refusal gate, std vocabulary) and ratified every one against a named, currently-green test. No assumption was unratified; the new behaviours themselves (tier selection, pointer errors, Option-widened provenance serde, std-only starter blueprint) are the spec's own commitments and enter as tests during implementation.

Scope signed: data-only tier at the load boundary ([nodes] pointer list in Aura.toml, one entry honored), data-only default scaffold for aura new (flags --engine-path/--namespace move to the new verb), aura nodes new (sibling node-crate scaffold + attach, load-phase bypass), ProjectProvenance fields widened to optional (additive serde), three-way unresolved-namespace hint, docs/ledger/invariant-9 amendments per the fork package recorded in this thread on 2026-07-12.

Status: spec signed — ready for planning.

## Spec auto-signed (grounding-check PASS, no human in the loop) The spec for this issue (working file `docs/specs/wiring-only-project-tier.md`, git-ignored) was signed autonomously on 2026-07-12: an independent fresh-context grounding check extracted its 13 load-bearing assumptions about current codebase behaviour (load boundary, discovery, crate pipeline, provenance stamping, scaffold shape, the outside-project refusal gate, std vocabulary) and ratified every one against a named, currently-green test. No assumption was unratified; the new behaviours themselves (tier selection, pointer errors, Option-widened provenance serde, std-only starter blueprint) are the spec's own commitments and enter as tests during implementation. Scope signed: data-only tier at the load boundary ([nodes] pointer list in Aura.toml, one entry honored), data-only default scaffold for `aura new` (flags `--engine-path`/`--namespace` move to the new verb), `aura nodes new` (sibling node-crate scaffold + attach, load-phase bypass), ProjectProvenance fields widened to optional (additive serde), three-way unresolved-namespace hint, docs/ledger/invariant-9 amendments per the fork package recorded in this thread on 2026-07-12. Status: spec signed — ready for planning.
Author
Owner

Implementation decisions (derived, 2026-07-12) — plan corrected to engine reality

Three of the signed spec/plan's literal claims about current behaviour turned out wrong during implementation; each was verified live and the plan corrected (the shipped code follows reality, not the stale plan bytes):

  • An unknown node type on aura run exits 2, not 1 (usage class, C14 partition) — verified by running a data-only project with a nosuch::Node blueprint. The new data-only hint test asserts exit 2.
  • Plain aura run persists nothing. The report (incl. the manifest) goes to stdout; only dissolved verbs/campaigns write the runs/ store. The scaffold-run headline test asserts the commit-only provenance out of stdout and that runs/ does NOT exist after a plain run; the sweep headline owns the store assertion.
  • One starter blueprint cannot serve both quickstart lines: aura run requires a CLOSED blueprint (refuses free params) while aura sweep requires OPEN knobs ("this blueprint is fully bound; nothing to sweep"). Decision: aura new scaffolds a closed/open starter pair — blueprints/signal.json (run target) and blueprints/signal_open.json (fast.length free) — and the scaffolded CLAUDE.md advertises aura sweep blueprints/signal_open.json --axis <name>_signal_open.fast.length=2,4,8, which is pinned by an e2e test driving exactly the advertised line. This makes the closed/open distinction (run vs sweep) visible in the very first project a user touches.

Status: load-boundary tier selection, provenance widening, the tiered namespace hint, and the data-only aura new are implemented and green; the attach verb and docs remain open.

## Implementation decisions (derived, 2026-07-12) — plan corrected to engine reality Three of the signed spec/plan's literal claims about current behaviour turned out wrong during implementation; each was verified live and the plan corrected (the shipped code follows reality, not the stale plan bytes): - **An unknown node type on `aura run` exits 2, not 1** (usage class, C14 partition) — verified by running a data-only project with a `nosuch::Node` blueprint. The new data-only hint test asserts exit 2. - **Plain `aura run` persists nothing.** The report (incl. the manifest) goes to stdout; only dissolved verbs/campaigns write the `runs/` store. The scaffold-run headline test asserts the commit-only provenance out of stdout and that `runs/` does NOT exist after a plain run; the sweep headline owns the store assertion. - **One starter blueprint cannot serve both quickstart lines**: `aura run` requires a CLOSED blueprint (refuses free params) while `aura sweep` requires OPEN knobs ("this blueprint is fully bound; nothing to sweep"). Decision: `aura new` scaffolds a closed/open starter pair — `blueprints/signal.json` (run target) and `blueprints/signal_open.json` (`fast.length` free) — and the scaffolded `CLAUDE.md` advertises `aura sweep blueprints/signal_open.json --axis <name>_signal_open.fast.length=2,4,8`, which is pinned by an e2e test driving exactly the advertised line. This makes the closed/open distinction (run vs sweep) visible in the very first project a user touches. Status: load-boundary tier selection, provenance widening, the tiered namespace hint, and the data-only `aura new` are implemented and green; the attach verb and docs remain open.
Author
Owner

User reservation (2026-07-12): the closed/open starter-blueprint pair

The two-starter-blueprint decision recorded earlier in this thread (closed signal.json for run, open signal_open.json for sweep) is flagged for a future design conversation — the user finds the pair somewhat inelegant (2026-07-12): it reads as a workaround for the run-requires-closed / sweep-requires-open split rather than a designed surface.

Candidate directions for that conversation (unevaluated, for the record):

  • (a) sweep axes may override bound params — a bound value becomes a default, every closed blueprint is sweepable, the second file disappears; touches axis semantics and blueprint-intent identity, so it is a real design pass, not a patch;
  • (b) params carry a default so run can execute open blueprints — one open starter serves both verbs;
  • (c) keep the explicit pair — the status quo, which at least makes the closed/open distinction visible to a newcomer.

No work is scheduled; the shipped pair stands until that conversation happens.

## User reservation (2026-07-12): the closed/open starter-blueprint pair The two-starter-blueprint decision recorded earlier in this thread (closed `signal.json` for `run`, open `signal_open.json` for `sweep`) is flagged for a future design conversation — the user finds the pair somewhat inelegant (2026-07-12): it reads as a workaround for the run-requires-closed / sweep-requires-open split rather than a designed surface. Candidate directions for that conversation (unevaluated, for the record): - (a) sweep axes may **override bound params** — a bound value becomes a default, every closed blueprint is sweepable, the second file disappears; touches axis semantics and blueprint-intent identity, so it is a real design pass, not a patch; - (b) params carry a **default** so `run` can execute open blueprints — one open starter serves both verbs; - (c) keep the explicit pair — the status quo, which at least makes the closed/open distinction visible to a newcomer. No work is scheduled; the shipped pair stands until that conversation happens.
Author
Owner

Shipped (local commits, 2026-07-12) — with fieldtest and follow-ups

The two-tier project model is implemented and green across the full pipeline, in 14 local commits (43a427b..0ad8fc6; the closes fire on push):

  • Data-only tier: the load boundary tier-selects (no [nodes], no root crate → std-vocabulary project; root Cargo.toml → pre-#241 native project, unchanged; [nodes] pointer → load that crate). Provenance widened additively (commit-only for data-only runs).
  • aura new scaffolds the data-only tier — Aura.toml, a closed run-target blueprint plus an open sweep-target sibling (run requires closed, sweep requires open knobs; the pair decision and the user's elegance reservation are recorded earlier in this thread), CLAUDE.md quickstart with verified command lines, git init + initial commit.
  • aura nodes new scaffolds a sibling node crate and attaches it via the [nodes] pointer; refusals for outside-project / already-attached / existing destination; the full role-2 loop (scaffold → cargo build → namespace resolves) is pinned e2e.
  • Docs/ledger: project-layout (two tiers), authoring guide, glossary (data-only project, node crate), C16 realization note, invariant 9 amended.
  • Consumer fieldtest (4 axes): quickstart, role-2 loop, real-data in a data-only project, refusal surfaces — all working; findings dispositioned: two doc/help fixes inline, #242 (empty --real window misnamed the symbol — fixed, RED-first), #243 (fresh scaffold stamps resolvable provenance from run one — fixed), #244 (tier hint on the graph-build path — fixed), #245 (clean missing-[nodes]-dir refusal — fixed).

Full gates green: workspace suite, clippy -D warnings, doc build. Status: implementation complete — awaiting push (closes for this issue and #242-#245 fire then).

## Shipped (local commits, 2026-07-12) — with fieldtest and follow-ups The two-tier project model is implemented and green across the full pipeline, in 14 local commits (43a427b..0ad8fc6; the closes fire on push): - **Data-only tier:** the load boundary tier-selects (no `[nodes]`, no root crate → std-vocabulary project; root `Cargo.toml` → pre-#241 native project, unchanged; `[nodes]` pointer → load that crate). Provenance widened additively (commit-only for data-only runs). - **`aura new`** scaffolds the data-only tier — Aura.toml, a closed run-target blueprint plus an open sweep-target sibling (`run` requires closed, `sweep` requires open knobs; the pair decision and the user's elegance reservation are recorded earlier in this thread), CLAUDE.md quickstart with verified command lines, git init + initial commit. - **`aura nodes new`** scaffolds a sibling node crate and attaches it via the `[nodes]` pointer; refusals for outside-project / already-attached / existing destination; the full role-2 loop (scaffold → cargo build → namespace resolves) is pinned e2e. - **Docs/ledger:** project-layout (two tiers), authoring guide, glossary (data-only project, node crate), C16 realization note, invariant 9 amended. - **Consumer fieldtest (4 axes):** quickstart, role-2 loop, real-data in a data-only project, refusal surfaces — all working; findings dispositioned: two doc/help fixes inline, #242 (empty --real window misnamed the symbol — fixed, RED-first), #243 (fresh scaffold stamps resolvable provenance from run one — fixed), #244 (tier hint on the graph-build path — fixed), #245 (clean missing-[nodes]-dir refusal — fixed). Full gates green: workspace suite, clippy -D warnings, doc build. Status: implementation complete — awaiting push (closes for this issue and #242-#245 fire then).
Author
Owner

signal_open reservation — resolved

The starter-pair reservation recorded on this issue (closed signal.json for run + open signal_open.json for sweep, flagged as inelegant) is resolved by a user decision of 2026-07-12: sweep axes may override bound params — a bound value is the param's default — so the open sibling leaves the aura new scaffold. Decision record and implementation: #246.

## signal_open reservation — resolved The starter-pair reservation recorded on this issue (closed `signal.json` for `run` + open `signal_open.json` for `sweep`, flagged as inelegant) is resolved by a user decision of 2026-07-12: sweep axes may override bound params — a bound value is the param's default — so the open sibling leaves the `aura new` scaffold. Decision record and implementation: #246.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Brummel/Aura#241