Files
Aura/fieldtests/milestone-project-environment/FINDINGS.md
T
Brummel ad83a4b170 fieldtest: milestone project-environment — GREEN (authoring loop end-to-end from public docs)
Milestone-close gate for 'Project environment — the project-as-crate
authoring loop' (#180), run as a downstream consumer from the public
interface only (README, project-layout, glossary, ledger, --help; no
crates/*/src, no crates/*/tests). Verdict: GREEN — 0 bugs.

Tasks (all ran, evidence in the corpus): (1) aura new -> cargo build ->
run twice byte-identical -> manifest project provenance
{namespace, dylib_sha256}; (2) a REAL authored node (Negate) in the
scaffolded lib.rs -> rebuild -> graph build resolves the project type ->
run flips total_pips sign, dylib_sha256 tracks the fresh build; (3)
renamed-twin id semantics: content-ids differ, identity-ids equal,
combinable output content-first, content-id == manifest topology_hash;
(4) misuse paths: charter violation named + refused exit 1,
missing --release build names path + fix, outside-project refused.

Findings (non-blocking, triaged separately): F1 friction (aura new git
init nests a gitlink under the engine tree), F2 doc-gap (no public
example of a bound-param build closure — the Identity template is
param-less), F3 doc-gap (glossary manifest entry lacks the project
provenance block), F4 friction (run leaks raw UnknownNodeType Debug
where graph build phrases house-style), F5 doc-gap (outside-project run
surfaces UnknownNodeType instead of a no-Aura.toml hint), F6 doc-gap
(scaffolded Aura.toml omits the documented data-archive-root key).

refs #180
2026-07-02 22:22:55 +02:00

11 KiB

Fieldtest — milestone "Project environment — the project-as-crate authoring loop" (#180) — 2026-07-02

Status: Draft — awaiting orchestrator triage Author: fieldtester (dispatched as the milestone-close gate)

Scope

The milestone-close gate for the project-as-crate authoring loop: aura new scaffolds a buildable cdylib project; inside it the aura host discovers Aura.toml, cargo-builds + loads the project cdylib, merges the project vocabulary with aura-std, and runs blueprints with project provenance in the manifest; aura graph introspect --vocabulary | --content-id | --identity-id introspects the merged surface and topology ids.

Run entirely from the public interfaceREADME.md, docs/project-layout.md, docs/glossary.md, docs/design/INDEX.md (C13/C16/C17/C24), aura … --help, and the fieldtests corpus shape. No crates/*/src and no crates/*/tests were read; no rustdoc was consulted. The only node-authoring template available was the scaffolded Identity node. Artefact under test: target/debug/aura, rebuilt from HEAD via cargo build -p aura-cli before the run; the project cdylib rebuilt with cargo build in the project between edits.

Tasks

All fixtures under fieldtests/milestone-project-environment/. The scaffolded project is momentum-lab/.

Task 1 — scaffold -> build -> run -> run-again -> provenance — WORKS

Commands (verbatim):

aura new momentum-lab                       # -> "created project `momentum-lab` (namespace `momentum_lab`)"
cd momentum-lab && cargo build              # clean
aura run blueprints/signal.json             # -> s1_starter_run.json
aura run blueprints/signal.json  (x2, diff) # byte-IDENTICAL (determinism)
  • The emitted crate is buildable as-is: crate-type=["cdylib"], an empty [workspace] table (so it builds under the engine tree), path-dep on aura-core, a CLAUDE.md, .gitignore, an Aura.toml ([paths] runs), a src/lib.rs with a worked Identity node registered via aura_project!, and a starter blueprints/signal.json that already wires the project's own momentum_lab::Identity node at the graph tail — so the very first run exercises the merged vocabulary, not just std.
  • The run report's manifest carries the milestone's headline provenance: "project":{"namespace":"momentum_lab","dylib_sha256":"ceda123f…"}. Two runs are byte-identical (C1). Evidence: s1_starter_run.json.

Task 2 — author your own node (the real authoring loop) — WORKS

Edited momentum-lab/src/lib.rs to add a Negate (f64 sign-flip) node following the scaffolded Identity template, registered it in vocabulary()/type_ids(), then:

cargo build                                                  # clean
aura graph introspect --vocabulary                           # now lists momentum_lab::Negate
aura graph build < s2_negate.ops.json > s2_negate.bp.json    # resolves the project node
aura run s2_negate.bp.json                                   # -> s2_negate_run.json
  • The op-list (s2_negate.ops.json, canonical authoring form per README) wires price -> SMA/SMA -> Sub -> Bias -> momentum_lab::Negate. graph build resolved the project node and emitted a runnable blueprint (s2_negate.bp.json).
  • The new node visibly changed behaviour: total_pips went +0.34185 -> -0.34185 (exact sign flip), the R block changed accordingly, and the manifest's dylib_sha256 updated (ceda123f… -> 70263c83…) — proof the host loaded the fresh build per invocation. Deterministic on re-run. Evidence: s2_negate_run.json.

Task 3 — introspection: --vocabulary / --content-id / --identity-id — WORKS

aura graph introspect --vocabulary                    # 2 project + 22 std = 24 lines
aura graph introspect --node momentum_lab::Negate     # in value:F64 / out value:F64
aura graph introspect --content-id  < s2_negate.ops.json         (A)
aura graph introspect --content-id  < s3_negate_renamed.ops.json (B)
aura graph introspect --identity-id < {A,B}
aura graph introspect --content-id --identity-id < A            # both, content first
  • --vocabulary lists project types first, then std alphabetically; --node resolves a project node's ports. Renamed-twin test (A vs B = same topology, different node names): content-ids differ (bd6c70… vs 9dc729…), identity-ids are equal (b02fbb…), and the combined flag prints content-id first — exactly as README / the identity id glossary entry document. Cross-check: content-id(A) equals the topology_hash stamped in s2_negate_run.json's manifest. Evidence: s3_id_comparison.out.

Task 4 — misuse paths a newcomer hits — WORKS (one friction)

  • Charter violation (registered a non-momentum_lab::-prefixed type id, rebuilt): refused at load, exit 1, aura: project vocabulary rejected: type id + "NoNamespace" + lacks the project prefix + "momentum_lab::" + — names both sides, house-style prose. Reverted after capture. Evidence:s4_charter_violation.err`.
  • --release with only a debug build: exit 1, names the exact missing path + the fix (run cargo build in the project first). Evidence: s4_release_missing.err.
  • Outside any project: --vocabulary -> std-only (22); a project blueprint is refused (see Finding F5). Walk-up discovery from a nested subdir (momentum-lab/blueprints/) correctly finds Aura.toml (24 entries).

Findings

[friction] F1 — aura new leaves a nested .git that snags the enclosing tree

  • Task 1. aura new runs git init; the scaffold contains momentum-lab/.git.
  • Correct for a real standalone project (C16: "its own git history"). But docs/project-layout.md explicitly supports scaffolding under the engine tree "before it moves to its own repo" — the exact reason the empty [workspace] table is emitted. That handles cargo's walk-up; nothing handles git's walk-up: the nested repo becomes an embedded gitlink in the enclosing repo, so git add fieldtests/…/momentum-lab would add a submodule pointer, not the files. I had to rm -rf momentum-lab/.git for the corpus to be committable.
  • Recommended: skip git init when an enclosing .git is detected, or document the scaffold-under-a-repo wrinkle beside the [workspace] note.

[doc-gap] F2 — no worked example of a node that reads a bound param

  • Task 2. The only authoring template is the scaffolded Identity (param-less; build closure |_| Box::new(..)). No public doc (README, project-layout, ledger, --help) shows how a PrimitiveBuilder build closure consumes a bound param — the natural next step (a tunable, sweepable node). I could author a param-less node from the surface alone, but a first parametric node cannot be written from the public docs without reading engine source/rustdoc.
  • Recommended: have aura new emit one param-bearing node in the template (or document the build-closure param-read in project-layout.md). Highest-value gap.

[doc-gap] F3 — the manifest project provenance block is undocumented

  • Task 1. The manifest's new "project":{namespace,dylib_sha256} block is the milestone's headline provenance, but the glossary manifest entry still defines a manifest as "(node-commit + params + data-window + seed + broker profile)" — no project. A consumer inspecting the JSON meets an undocumented field.
  • Recommended: add project (namespace + dylib hash) to the glossary manifest definition.

[friction] F4 — run leaks a raw Rust Debug enum for an unknown node type

  • Task 4. Same logical error, two phrasings: graph build (stdin) -> op 5 (add): unknown node type "momentum_lab::Negate" (exit 1, prose); run (argv file) -> aura: s2_negate.bp.json: UnknownNodeType("momentum_lab::Negate") (exit 2, raw Debug-formatted enum variant). The exit-code split is correct and documented (C14: argv-file content = exit 2, stdin data = exit 1), but the run path's UnknownNodeType("…") breaks the lowercase house style every other diagnostic uses. Evidence: s4_outside_run.err, s4_outside_build.err.
  • Recommended: render the loader error on the run path in house-style prose (e.g. unknown node type "momentum_lab::Negate").

[doc-gap] F5 — "outside a project" is not distinctly diagnosed

  • Task 4. Running a project blueprint with no Aura.toml up-tree surfaces the downstream symptom (UnknownNodeType), not the likely cause ("not inside a project / no Aura.toml found"). A newcomer who cd'd out of their project reads "unknown node type" rather than "you are not in a project".
  • Recommended: when a blueprint names a namespace::Type and no project was discovered, hint that no Aura.toml was found up-tree.

[doc-gap] F6 — scaffolded Aura.toml omits the documented data-archive-root path

  • Task 1. Glossary/project-layout.md say Aura.toml carries "the data archive root and the runs dir"; the emitted file has only [paths] runs = "runs". No concrete key name for the data archive root is documented or scaffolded, so a newcomer wanting aura run --real <symbol> has no scaffolded slot to point at their recorded data. Low severity for a synthetic-default v1.
  • Recommended: scaffold the data-archive-root key (commented) or drop it from the Aura.toml prose until a consumer reads it.

[working] W1 — the merged-vocabulary authoring loop is real and clean

  • Tasks 1-3. Edit lib.rs -> cargo build -> the next aura invocation loads the fresh dylib (manifest dylib_sha256 tracks the rebuild), merges 2 project + 22 std types, resolves the project node in graph build, run, and every introspect mode, and produces deterministic runs. Walk-up Aura.toml discovery works from nested subdirs. The whole authoring loop the milestone promises works end-to-end from the public docs.

[working] W2 — id semantics and diagnostics are precise

  • Tasks 3-4. --content-id/--identity-id behave exactly as documented (name-blind identity id equal across renamed twins; byte-exact content id differs; combinable, content-first; content-id == manifest topology_hash). The charter refusal and the --release-missing message are both clear, exit-code-correct, and actionable. One micro-nit inside W2's otherwise-great --release message: it says "or pass --release …" even when --release was already passed (the generic hint doesn't adapt to the current mode).

Milestone verdict: GREEN

A downstream consumer can run the entire project-as-crate authoring loop — aura new -> cargo build -> run (deterministic, with project provenance) -> author a real node -> rebuild -> introspect the merged vocabulary -> topology ids — from the public docs alone, with zero behavioural bugs. All six findings are friction/doc-gap polish, not loop blockers:

Finding Class Recommended action
F1 nested .git snags enclosing tree friction plan (skip git-init under a repo, or document)
F2 no param-node authoring example doc-gap plan (scaffold a param node) — highest-value gap
F3 manifest project block undocumented doc-gap tighten glossary
F4 run leaks Debug enum on unknown type friction plan (house-style the loader error)
F5 "outside a project" not distinctly diagnosed doc-gap plan (hint on missing Aura.toml)
F6 Aura.toml data-archive-root un-scaffolded/undocumented doc-gap tighten docs
W1/W2 loop + id semantics correct working carry-on