ad83a4b170
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
192 lines
11 KiB
Markdown
192 lines
11 KiB
Markdown
# 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 interface** — `README.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 |
|