fieldtest: milestone safe-to-embed — 4 examples, 4 bugs / 4 friction / 2 spec-gaps / 6 working
Source-blind milestone fieldtest of the embedding promise (range
024e865..32f6be8): a path-dep World program driving refusals as values
and a live in-process tap, reproduce-as-data with both guard classes and
a tampered-metric divergence probe, 22 CLI exit-class probes derived
from the docs beforehand, and the stale-dylib handshake via the nodes-new
scaffold plus a rev-pinned external crate. Replayable via m4_0_run_all.sh.
Findings: 2 code bugs fixed in-cycle RED-first (99e9300 — the library
exposes-neither panic was the one real promise breach), 2 doc bugs fixed
in the same commit, 4 friction items bundled as #349, 2 spec gaps
ratified into C30 (rev+lockfile clause; the scaffold's absolute path also
in #349 — the committed lab-nodes fixture is relativized), 6 working.
refs #296
This commit is contained in:
+1279
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "embed-host"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
# A node crate is its own workspace (docs/project-layout.md); an external
|
||||
# embedding under the engine checkout hits the same cargo trap, so the same
|
||||
# empty table applies here.
|
||||
[workspace]
|
||||
|
||||
# C30 point 2: an external embedding in its own repo pins the engine as a git
|
||||
# dependency at a rev. This fixture lives INSIDE the engine checkout, so it
|
||||
# uses the path form of the same edge — the node-crate mechanism.
|
||||
[dependencies]
|
||||
aura-core = { path = "../../../crates/aura-core" }
|
||||
aura-engine = { path = "../../../crates/aura-engine" }
|
||||
aura-runner = { path = "../../../crates/aura-runner" }
|
||||
@@ -0,0 +1,123 @@
|
||||
//! Fieldtest, milestone 39 (safe to embed), axis 1 — the embedding consumer.
|
||||
//!
|
||||
//! A downstream "World program": it runs one signal blueprint through the
|
||||
//! engine's library surface (no `aura` binary involved), drains one tap
|
||||
//! in-process, and then deliberately walks into three refusals — one
|
||||
//! argv-named-content class, one environment class, one tap-name class.
|
||||
//! The promise under test: every refusal comes back as a `RunnerError`
|
||||
//! VALUE, the host keeps running, and the host itself decides what to do
|
||||
//! with the exit class.
|
||||
//!
|
||||
//! Run it from inside the scaffolded project: `cd lab && cargo run
|
||||
//! --manifest-path ../embed-host/Cargo.toml --bin m4_1_refusals_are_values`.
|
||||
|
||||
use aura_engine::blueprint_from_json;
|
||||
use aura_runner::member::{run_signal_r, RunData};
|
||||
use aura_runner::project;
|
||||
use aura_runner::tap_plan::{TapPlan, TapSubscription};
|
||||
use aura_runner::RunnerError;
|
||||
use std::path::Path;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
fn main() {
|
||||
let root = project::discover_from(Path::new("."))
|
||||
.expect("run this from inside a project (an Aura.toml directory)");
|
||||
let penv = project::load(&root, false).expect("data-only project loads");
|
||||
let env = project::Env::with_project(penv);
|
||||
let doc = std::fs::read_to_string(root.join("blueprints/embed-probe.json"))
|
||||
.expect("the op-script-built blueprint is on disk");
|
||||
|
||||
let load = |env: &project::Env| {
|
||||
blueprint_from_json(&doc, &|type_id| env.resolve(type_id))
|
||||
.expect("blueprint parses against the project vocabulary")
|
||||
};
|
||||
|
||||
// ---- 1. the happy path, with the tap drained IN PROCESS -------------
|
||||
let seen = Arc::new(AtomicUsize::new(0));
|
||||
let counter = Arc::clone(&seen);
|
||||
let mut plan = TapPlan::empty();
|
||||
plan.subscribe(
|
||||
"spread",
|
||||
TapSubscription::live(move |_ts, _cell| {
|
||||
counter.fetch_add(1, Ordering::Relaxed);
|
||||
}),
|
||||
);
|
||||
match run_signal_r(load(&env), &[], RunData::Synthetic, 0, &env, plan, None) {
|
||||
Ok((report, notes)) => println!(
|
||||
"[1] ok: {} tap samples reached this process, notes={:?}\n manifest.topology_hash={:?}",
|
||||
seen.load(Ordering::Relaxed),
|
||||
notes,
|
||||
report.manifest.topology_hash
|
||||
),
|
||||
Err(e) => println!("[1] UNEXPECTED refusal: exit {} — {}", e.exit_code, e.message),
|
||||
}
|
||||
|
||||
// ---- 2. a fold label the registry does not carry --------------------
|
||||
let mut plan = TapPlan::empty();
|
||||
plan.subscribe("spread", TapSubscription::named("median"));
|
||||
report_refusal(
|
||||
"[2] unknown fold label (argv-named content -> expect class 2)",
|
||||
run_signal_r(load(&env), &[], RunData::Synthetic, 0, &env, plan, None),
|
||||
);
|
||||
|
||||
// ---- 3. a tap name the blueprint does not declare -------------------
|
||||
let mut plan = TapPlan::empty();
|
||||
plan.subscribe("spred", TapSubscription::named("mean"));
|
||||
report_refusal(
|
||||
"[3] unknown tap name (argv-named content -> expect class 2)",
|
||||
run_signal_r(load(&env), &[], RunData::Synthetic, 0, &env, plan, None),
|
||||
);
|
||||
|
||||
// ---- 4. a symbol the archive does not have --------------------------
|
||||
let data = RunData::Real {
|
||||
symbol: "NOSUCHSYMBOL".to_string(),
|
||||
from: None,
|
||||
to: None,
|
||||
};
|
||||
report_refusal(
|
||||
"[4] symbol absent from the archive (environment -> expect class 1)",
|
||||
run_signal_r(load(&env), &[], data, 0, &env, TapPlan::empty(), None),
|
||||
);
|
||||
|
||||
// ---- 5. the success path: does the library print into MY stdout? ----
|
||||
println!("[5] --- begin record_all run; anything between here and [5-end] is not mine");
|
||||
match run_signal_r(
|
||||
load(&env),
|
||||
&[],
|
||||
RunData::Synthetic,
|
||||
0,
|
||||
&env,
|
||||
TapPlan::record_all(),
|
||||
None,
|
||||
) {
|
||||
Ok((_, notes)) => println!("[5-end] ok, returned notes={notes:?}"),
|
||||
Err(e) => println!("[5-end] refused: exit {} — {}", e.exit_code, e.message),
|
||||
}
|
||||
|
||||
// ---- 6..8. refusals the CLI reports as class 2 on this same leg -----
|
||||
// Do they reach an embedder as VALUES too, or only as CLI-side checks?
|
||||
// NOTE (fieldtest run, 2026-07-26): step [8] never printed — the call
|
||||
// panicked inside the library and took this host down with exit 101.
|
||||
for (label, file) in [
|
||||
("[6] open blueprint, free knobs", "blueprints/open-probe.json"),
|
||||
("[7] root role no column binds", "blueprints/unbindable-probe.json"),
|
||||
("[8] exposes neither bias nor tap", "blueprints/blind-probe.json"),
|
||||
] {
|
||||
let src = std::fs::read_to_string(root.join(file)).expect("fixture on disk");
|
||||
let bp = blueprint_from_json(&src, &|t| env.resolve(t)).expect("parses");
|
||||
report_refusal(
|
||||
label,
|
||||
run_signal_r(bp, &[], RunData::Synthetic, 0, &env, TapPlan::empty(), None),
|
||||
);
|
||||
}
|
||||
|
||||
println!("[host] still alive after 3 refusals — the milestone promise holds");
|
||||
}
|
||||
|
||||
fn report_refusal<T>(what: &str, outcome: Result<T, RunnerError>) {
|
||||
match outcome {
|
||||
Ok(_) => println!("{what}\n UNEXPECTED: ran to completion, no refusal"),
|
||||
Err(e) => println!("{what}\n exit_code={} message={:?}", e.exit_code, e.message),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
//! Fieldtest, milestone 39 (safe to embed), axis 1 — reproduce as a value.
|
||||
//!
|
||||
//! The downstream task: a World program wants to re-verify a persisted
|
||||
//! family before building on it — "is this stored result still the result
|
||||
//! this engine produces?" — and wants the answer as DATA, not as a process
|
||||
//! exit. It then walks into the two mismatch-guard classes: an explicit
|
||||
//! source that contradicts the stored members (caller's fault, class 2) and
|
||||
//! an id nothing is stored under (environment, class 1).
|
||||
//!
|
||||
//! Usage: `cd lab && cargo run --manifest-path ../embed-host/Cargo.toml \
|
||||
//! --bin m4_2_reproduce_as_data -- <family-id>`
|
||||
|
||||
use aura_runner::family::{DataChoice, DataSource};
|
||||
use aura_runner::project;
|
||||
use aura_runner::reproduce::{reproduce_family, reproduce_family_in};
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
let id = std::env::args().nth(1).expect("usage: <family-id>");
|
||||
let root = project::discover_from(Path::new(".")).expect("run inside the project");
|
||||
let penv = project::load(&root, false).expect("data-only project loads");
|
||||
let env = project::Env::with_project(penv);
|
||||
|
||||
// ---- 1. the answer is a report, divergence included -----------------
|
||||
match reproduce_family(&id, &env) {
|
||||
Ok(report) => {
|
||||
println!("[1] reproduce_family -> Ok, all_identical={}", report.all_identical());
|
||||
for (member, identical) in &report.outcomes {
|
||||
println!(" {member} -> bit-identical={identical}");
|
||||
}
|
||||
}
|
||||
Err(e) => println!("[1] refused: exit_code={} message={:?}", e.exit_code, e.message),
|
||||
}
|
||||
|
||||
// ---- 2. explicit source contradicting the stored members ------------
|
||||
// The family above was recorded over GER40; hand it EURUSD instead.
|
||||
let wrong = DataSource::from_choice(
|
||||
DataChoice::Real { symbol: "EURUSD".to_string(), from_ms: None, to_ms: None },
|
||||
&env,
|
||||
)
|
||||
.expect("EURUSD has recorded geometry");
|
||||
match reproduce_family_in(&env.registry(), &id, &wrong, &env) {
|
||||
Ok(report) => println!(
|
||||
"[2] UNEXPECTED: ran against the wrong instrument, all_identical={}",
|
||||
report.all_identical()
|
||||
),
|
||||
Err(e) => println!(
|
||||
"[2] explicit-source mismatch (expect class 2): exit_code={} message={:?}",
|
||||
e.exit_code, e.message
|
||||
),
|
||||
}
|
||||
|
||||
// ---- 3. an id nothing is stored under -------------------------------
|
||||
match reproduce_family("no-such-family-0", &env) {
|
||||
Ok(_) => println!("[3] UNEXPECTED: reproduced a family that does not exist"),
|
||||
Err(e) => println!(
|
||||
"[3] unknown family id (expect class 1): exit_code={} message={:?}",
|
||||
e.exit_code, e.message
|
||||
),
|
||||
}
|
||||
|
||||
println!("[host] still alive — reproduce never took the process down");
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "external-embed"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[workspace]
|
||||
|
||||
# C30 point 2, consumer class 2: "an external embedding (a World program in
|
||||
# its own repo) consumes the workspace crates as a cargo git dependency
|
||||
# pinned to a rev." This is that mechanism, exercised literally — the rev is
|
||||
# the newest commit published on origin/main at fieldtest time.
|
||||
[dependencies]
|
||||
aura-runner = { git = "http://192.168.178.103:3000/Brummel/Aura.git", rev = "024e8652c0afc186b2718c0c0f5189a6874fa5d7" }
|
||||
aura-engine = { git = "http://192.168.178.103:3000/Brummel/Aura.git", rev = "024e8652c0afc186b2718c0c0f5189a6874fa5d7" }
|
||||
@@ -0,0 +1,18 @@
|
||||
//! Fieldtest, milestone 39, axis 3 — the external-embedding mechanism.
|
||||
//!
|
||||
//! The smallest possible World program of C30's second consumer class: it
|
||||
//! does not live in the engine checkout and does not use the `aura` binary;
|
||||
//! it pins the engine at a rev and calls the library. All it has to do to
|
||||
//! count is BUILD and reach one library entry point.
|
||||
|
||||
use aura_runner::project::Env;
|
||||
|
||||
fn main() {
|
||||
let env = Env::std();
|
||||
let types = env.type_ids();
|
||||
println!(
|
||||
"external embedding alive: {} node types visible, runs root {:?}",
|
||||
types.len(),
|
||||
env.runs_root()
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
/target
|
||||
Cargo.lock
|
||||
@@ -0,0 +1,11 @@
|
||||
# lab-nodes — an aura node crate
|
||||
|
||||
Native node logic for an aura research project (extension-dev role). This
|
||||
crate is attached to its project via `[nodes]` in the project's `Aura.toml`.
|
||||
|
||||
- Build: `cargo build` (the next `aura` invocation in the project loads the
|
||||
fresh dylib)
|
||||
- Each node is registered in `vocabulary()`/`type_ids()` under the
|
||||
`lab_nodes::` namespace prefix; blueprints reference nodes by that id.
|
||||
- This crate has no data-plane dependencies — nodes are pure stream
|
||||
transformers over read-only input windows.
|
||||
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "lab-nodes"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
# The scaffold emits an ABSOLUTE engine path here (fieldtest finding S2);
|
||||
# relativized for the committed corpus so the fixture survives the
|
||||
# worktree it was minted in.
|
||||
aura-core = { path = "../../../crates/aura-core" }
|
||||
|
||||
# Standalone workspace root: never a member of an enclosing workspace.
|
||||
[workspace]
|
||||
@@ -0,0 +1,77 @@
|
||||
//! lab-nodes — an aura research project: node logic + the exported vocabulary.
|
||||
//!
|
||||
//! Every node type this crate provides is registered in `vocabulary()` /
|
||||
//! `type_ids()` under the `lab_nodes::` namespace prefix; the `aura` host loads
|
||||
//! this cdylib per invocation and merges it with the std vocabulary.
|
||||
|
||||
use aura_core::{
|
||||
Cell, Ctx, FieldSpec, Firing, Node, NodeSchema, ParamSpec, PortSpec, PrimitiveBuilder,
|
||||
ScalarKind,
|
||||
};
|
||||
|
||||
/// One-input scalar gain: emits `input * factor`. Emits `None` until its
|
||||
/// input has a value (warm-up filter, C8).
|
||||
pub struct Scale {
|
||||
factor: f64,
|
||||
out: [Cell; 1],
|
||||
}
|
||||
|
||||
impl Scale {
|
||||
pub fn new(factor: f64) -> Self {
|
||||
Self { factor, out: [Cell::from_f64(0.0)] }
|
||||
}
|
||||
// Replace `doc` below when renaming this sample node — it must keep
|
||||
// describing the node's own behaviour, not this scaffolding step.
|
||||
pub fn builder() -> PrimitiveBuilder {
|
||||
PrimitiveBuilder::new(
|
||||
"lab_nodes::Scale",
|
||||
NodeSchema {
|
||||
inputs: vec![PortSpec {
|
||||
kind: ScalarKind::F64,
|
||||
firing: Firing::Any,
|
||||
name: "value".into(),
|
||||
}],
|
||||
output: vec![FieldSpec { name: "value".into(), kind: ScalarKind::F64 }],
|
||||
params: vec![ParamSpec { name: "factor".into(), kind: ScalarKind::F64 }],
|
||||
// C29: every vocabulary entry ships a one-line meaning — the
|
||||
// doc field is required (E0063 without it) and gated at load.
|
||||
doc: "scalar gain: emits the input times the factor param",
|
||||
},
|
||||
|p| Box::new(Scale::new(p[0].f64())),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for Scale {
|
||||
fn lookbacks(&self) -> Vec<usize> {
|
||||
vec![1]
|
||||
}
|
||||
fn eval(&mut self, ctx: Ctx<'_>) -> Option<&[Cell]> {
|
||||
let w = ctx.f64_in(0);
|
||||
if w.is_empty() {
|
||||
return None;
|
||||
}
|
||||
self.out[0] = Cell::from_f64(w[0] * self.factor);
|
||||
Some(&self.out)
|
||||
}
|
||||
fn label(&self) -> String {
|
||||
format!("lab_nodes::Scale({})", self.factor)
|
||||
}
|
||||
}
|
||||
|
||||
fn vocabulary(type_id: &str) -> Option<PrimitiveBuilder> {
|
||||
match type_id {
|
||||
"lab_nodes::Scale" => Some(Scale::builder()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn type_ids() -> &'static [&'static str] {
|
||||
&["lab_nodes::Scale"]
|
||||
}
|
||||
|
||||
aura_core::aura_project! {
|
||||
namespace: "lab_nodes",
|
||||
vocabulary: vocabulary,
|
||||
type_ids: type_ids,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/runs
|
||||
@@ -0,0 +1,7 @@
|
||||
# Static project context only (C17); paths only.
|
||||
[paths]
|
||||
runs = "runs"
|
||||
# data = "/path/to/archive" # the recorded-data root; defaults to the built-in path
|
||||
|
||||
[nodes]
|
||||
crates = ["../lab-nodes"]
|
||||
@@ -0,0 +1,24 @@
|
||||
# lab — an aura research project (data-only)
|
||||
|
||||
This directory is an aura project: blueprints + research documents over the
|
||||
std vocabulary, anchored by `Aura.toml`. There is no crate and no build step.
|
||||
|
||||
- Run: `aura exec blueprints/signal.json` (single smoke run, synthetic stream
|
||||
— the starter is closed; all params bound; bound values are defaults —
|
||||
`--override NODE.PARAM=VALUE` may override one for this run (#246))
|
||||
- Campaign: `aura exec <campaign.json>` executes a registered campaign
|
||||
document (file or content id) — the multi-cell/axis surface
|
||||
- Axes: `aura graph introspect --params blueprints/signal.json` lists the
|
||||
open + bound-overridable axes, RAW `<node>.<param>` names (#328)
|
||||
- Native nodes: when the project needs its first project-specific node,
|
||||
`aura nodes new <name>` scaffolds a node crate beside this project and
|
||||
attaches it via `[nodes]` in `Aura.toml` (build it with `cargo build`).
|
||||
- Topology is data (`blueprints/*.json`); results land in `runs/`.
|
||||
- Execution model: a strategy emits a bias in [-1,+1] per cycle, held as the
|
||||
continuously-tracked target position; a protective stop defines the risk
|
||||
unit R, and quality metrics are R-based. Entry signals become held state
|
||||
via the signal-side latch/edge-pulse idiom (see
|
||||
`aura graph introspect --vocabulary`).
|
||||
- Data plane: process/campaign documents are authored directly with
|
||||
`aura process` / `aura campaign`, growing one from a bare `{}` via
|
||||
`aura campaign introspect --unwired`.
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version":1,"blueprint":{"name":"blind-probe","doc":"the same graph with neither a bias output nor a declared tap","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":3}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":8}}]}},{"primitive":{"type":"Sub","name":"sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version":1,"blueprint":{"name":"embed-probe","doc":"fast/slow SMA spread clamped to a bias, with the raw spread tapped","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":3}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":8}}]}},{"primitive":{"type":"Sub","name":"sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}],"taps":[{"name":"spread","from":{"node":2,"field":0}}]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version":1,"blueprint":{"name":"open-probe","doc":"the same spread-to-bias signal with every knob left open","nodes":[{"primitive":{"type":"SMA","name":"fast"}},{"primitive":{"type":"SMA","name":"slow"}},{"primitive":{"type":"Sub","name":"sub"}},{"primitive":{"type":"Bias","name":"bias"}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}],"taps":[{"name":"spread","from":{"node":2,"field":0}}]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version":1,"blueprint":{"name":"lab_signal","doc":"fast/slow SMA difference clamped into a directional bias","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":2}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":4}}]}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"format_version":1,"blueprint":{"name":"unbindable-probe","doc":"the same signal fed by a root role no archive column carries","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":3}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":8}}]}},{"primitive":{"type":"Sub","name":"sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"sentiment","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}],"taps":[{"name":"spread","from":{"node":2,"field":0}}]}}
|
||||
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
# Fieldtest, milestone 39 ("safe to embed") — the whole session, replayed.
|
||||
#
|
||||
# Reproduces every example in this directory from a clean checkout, in the
|
||||
# order a downstream consumer met them. Run it from THIS directory:
|
||||
#
|
||||
# bash m4_0_run_all.sh
|
||||
#
|
||||
# Prerequisites: `cargo build --workspace --release` at the repo root, the
|
||||
# Pepperstone archive mounted (GER40 M1 for Sep 2024), and — for example 4's
|
||||
# stale-dylib probe — `git worktree add --detach /tmp/aura-stale d87f534`.
|
||||
set -u
|
||||
HERE=$(cd "$(dirname "$0")" && pwd)
|
||||
AURA=$HERE/../../target/release/aura
|
||||
|
||||
cd "$HERE"
|
||||
[ -d lab ] || $AURA new lab
|
||||
cd lab
|
||||
|
||||
echo "=== build the fixtures (canonical authoring form: op-scripts) ==="
|
||||
for s in tapped open blind unbindable; do
|
||||
case $s in
|
||||
tapped) src=m4_1_tapped_signal.ops.json; out=embed-probe.json ;;
|
||||
open) src=m4_1_open_signal.ops.json; out=open-probe.json ;;
|
||||
blind) src=m4_1_blind_signal.ops.json; out=blind-probe.json ;;
|
||||
unbindable) src=m4_1_unbindable_signal.ops.json; out=unbindable-probe.json ;;
|
||||
esac
|
||||
$AURA graph build < "../$src" > "blueprints/$out" || echo "graph build failed for $src"
|
||||
done
|
||||
$AURA graph register blueprints/embed-probe.json
|
||||
|
||||
echo
|
||||
echo "=== example 1 — refusals are values (library face) ==="
|
||||
cargo run -q --manifest-path ../embed-host/Cargo.toml --bin m4_1_refusals_are_values
|
||||
echo "host exit: $?"
|
||||
|
||||
echo
|
||||
echo "=== example 2 — reproduce returns its report ==="
|
||||
$AURA process register ../m4_2_process_sweep_only.json
|
||||
$AURA campaign register ../m4_2_campaign_ger40.json
|
||||
$AURA exec ../m4_2_campaign_ger40.json > /dev/null
|
||||
FAMILY=$($AURA runs families | tail -1 | sed 's/.*"family_id":"\([^"]*\)".*/\1/')
|
||||
echo "family: $FAMILY"
|
||||
cargo run -q --manifest-path ../embed-host/Cargo.toml --bin m4_2_reproduce_as_data -- "$FAMILY"
|
||||
bash ../m4_2_divergence_probe.sh "$FAMILY"
|
||||
|
||||
echo
|
||||
echo "=== example 3 — the CLI exit-class partition ==="
|
||||
bash ../m4_3_exit_classes.sh
|
||||
|
||||
echo
|
||||
echo "=== example 4 — the compatibility contract ==="
|
||||
$AURA nodes new lab-nodes 2>/dev/null || echo "(node crate already attached)"
|
||||
bash ../m4_4_stale_dylib_probe.sh
|
||||
echo "--- and the external, rev-pinned embedding:"
|
||||
( cd ../external-embed && CARGO_TARGET_DIR=/tmp/m4-ext-target cargo run -q )
|
||||
@@ -0,0 +1,73 @@
|
||||
[
|
||||
{
|
||||
"op": "name",
|
||||
"name": "blind-probe"
|
||||
},
|
||||
{
|
||||
"op": "doc",
|
||||
"text": "the same graph with neither a bias output nor a declared tap"
|
||||
},
|
||||
{
|
||||
"op": "source",
|
||||
"role": "price",
|
||||
"kind": "F64"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "SMA",
|
||||
"name": "fast",
|
||||
"bind": {
|
||||
"length": {
|
||||
"I64": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "SMA",
|
||||
"name": "slow",
|
||||
"bind": {
|
||||
"length": {
|
||||
"I64": 8
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "feed",
|
||||
"role": "price",
|
||||
"into": [
|
||||
"fast.series",
|
||||
"slow.series"
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "Sub",
|
||||
"name": "sub"
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "fast.value",
|
||||
"to": "sub.lhs"
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "slow.value",
|
||||
"to": "sub.rhs"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "Bias",
|
||||
"name": "bias",
|
||||
"bind": {
|
||||
"scale": {
|
||||
"F64": 0.5
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "sub.value",
|
||||
"to": "bias.signal"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,68 @@
|
||||
[
|
||||
{
|
||||
"op": "name",
|
||||
"name": "open-probe"
|
||||
},
|
||||
{
|
||||
"op": "doc",
|
||||
"text": "the same spread-to-bias signal with every knob left open"
|
||||
},
|
||||
{
|
||||
"op": "source",
|
||||
"role": "price",
|
||||
"kind": "F64"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "SMA",
|
||||
"name": "fast"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "SMA",
|
||||
"name": "slow"
|
||||
},
|
||||
{
|
||||
"op": "feed",
|
||||
"role": "price",
|
||||
"into": [
|
||||
"fast.series",
|
||||
"slow.series"
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "Sub",
|
||||
"name": "sub"
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "fast.value",
|
||||
"to": "sub.lhs"
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "slow.value",
|
||||
"to": "sub.rhs"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "Bias",
|
||||
"name": "bias"
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "sub.value",
|
||||
"to": "bias.signal"
|
||||
},
|
||||
{
|
||||
"op": "tap",
|
||||
"from": "sub.value",
|
||||
"as": "spread"
|
||||
},
|
||||
{
|
||||
"op": "expose",
|
||||
"from": "bias.bias",
|
||||
"as": "bias"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,15 @@
|
||||
[
|
||||
{"op": "name", "name": "embed-probe"},
|
||||
{"op": "doc", "text": "fast/slow SMA spread clamped to a bias, with the raw spread tapped"},
|
||||
{"op": "source", "role": "price", "kind": "F64"},
|
||||
{"op": "add", "type": "SMA", "name": "fast", "bind": {"length": {"I64": 3}}},
|
||||
{"op": "add", "type": "SMA", "name": "slow", "bind": {"length": {"I64": 8}}},
|
||||
{"op": "feed", "role": "price", "into": ["fast.series", "slow.series"]},
|
||||
{"op": "add", "type": "Sub", "name": "sub"},
|
||||
{"op": "connect", "from": "fast.value", "to": "sub.lhs"},
|
||||
{"op": "connect", "from": "slow.value", "to": "sub.rhs"},
|
||||
{"op": "add", "type": "Bias", "name": "bias", "bind": {"scale": {"F64": 0.5}}},
|
||||
{"op": "connect", "from": "sub.value", "to": "bias.signal"},
|
||||
{"op": "tap", "from": "sub.value", "as": "spread"},
|
||||
{"op": "expose", "from": "bias.bias", "as": "bias"}
|
||||
]
|
||||
@@ -0,0 +1,83 @@
|
||||
[
|
||||
{
|
||||
"op": "name",
|
||||
"name": "unbindable-probe"
|
||||
},
|
||||
{
|
||||
"op": "doc",
|
||||
"text": "the same signal fed by a root role no archive column carries"
|
||||
},
|
||||
{
|
||||
"op": "source",
|
||||
"role": "sentiment",
|
||||
"kind": "F64"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "SMA",
|
||||
"name": "fast",
|
||||
"bind": {
|
||||
"length": {
|
||||
"I64": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "SMA",
|
||||
"name": "slow",
|
||||
"bind": {
|
||||
"length": {
|
||||
"I64": 8
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "feed",
|
||||
"role": "sentiment",
|
||||
"into": [
|
||||
"fast.series",
|
||||
"slow.series"
|
||||
]
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "Sub",
|
||||
"name": "sub"
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "fast.value",
|
||||
"to": "sub.lhs"
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "slow.value",
|
||||
"to": "sub.rhs"
|
||||
},
|
||||
{
|
||||
"op": "add",
|
||||
"type": "Bias",
|
||||
"name": "bias",
|
||||
"bind": {
|
||||
"scale": {
|
||||
"F64": 0.5
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"op": "connect",
|
||||
"from": "sub.value",
|
||||
"to": "bias.signal"
|
||||
},
|
||||
{
|
||||
"op": "tap",
|
||||
"from": "sub.value",
|
||||
"as": "spread"
|
||||
},
|
||||
{
|
||||
"op": "expose",
|
||||
"from": "bias.bias",
|
||||
"as": "bias"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "campaign",
|
||||
"name": "m4-embed-reproduce-probe",
|
||||
"seed": 7,
|
||||
"data": {
|
||||
"instruments": ["GER40"],
|
||||
"windows": [ { "from_ms": 1725148800000, "to_ms": 1725753600000 } ]
|
||||
},
|
||||
"strategies": [
|
||||
{
|
||||
"ref": { "content_id": "00fd2bc044fe4c9b947aac97da740e6d08bf4103ec376058f0cf52590f7863aa" },
|
||||
"axes": {
|
||||
"fast.length": { "kind": "I64", "values": [2, 3] }
|
||||
}
|
||||
}
|
||||
],
|
||||
"process": { "ref": { "content_id": "e4a97c684686c2982f034a27dabb7f26e54c76d7578dd985eaa41ef67c77f275" } },
|
||||
"presentation": { "persist_taps": [], "emit": ["family_table"] }
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
# Fieldtest, milestone 39, axis 1 — divergence must come back as DATA.
|
||||
#
|
||||
# A downstream consumer's realistic worry: the stored family in the registry
|
||||
# was recorded by a different engine state, so re-deriving it no longer
|
||||
# matches. We simulate exactly that observable — a stored metric that the
|
||||
# re-run does not reproduce — by editing the run registry's stored value
|
||||
# (the registry is a plain JSONL artifact, C18), then asking both faces the
|
||||
# same question: the embedding library and the `aura` binary.
|
||||
#
|
||||
# Run from the `lab/` project directory.
|
||||
set -u
|
||||
AURA=${AURA:-../../../target/release/aura}
|
||||
FAMILY=${1:-ff72bba2-0-GER40-w0-r0-s0-0}
|
||||
STORE=runs/families.jsonl
|
||||
|
||||
cp "$STORE" "$STORE.orig"
|
||||
python3 - "$STORE" "$FAMILY" <<'PY'
|
||||
import json, sys
|
||||
p, handle = sys.argv[1], sys.argv[2]
|
||||
family, run = handle.rsplit("-", 1)
|
||||
lines = open(p).read().splitlines()
|
||||
for i, line in enumerate(lines):
|
||||
d = json.loads(line)
|
||||
if d["family"] == family and str(d["run"]) == run and d["ordinal"] == 0:
|
||||
# nudge one stored metric of member 0 — nothing else
|
||||
d["report"]["metrics"]["r"]["expectancy_r"] += 1.0
|
||||
lines[i] = json.dumps(d)
|
||||
break
|
||||
else:
|
||||
sys.exit(f"no member 0 of {handle} in {p}")
|
||||
open(p, "w").write("\n".join(lines) + "\n")
|
||||
PY
|
||||
echo "--- tampered: member 0's stored expectancy_r shifted by +1.0"
|
||||
|
||||
echo "--- library face (embedding host):"
|
||||
cargo run -q --manifest-path ../embed-host/Cargo.toml --bin m4_2_reproduce_as_data -- "$FAMILY" 2>&1 | sed -n '1,4p'
|
||||
echo " host exit: $?"
|
||||
|
||||
echo "--- CLI face:"
|
||||
"$AURA" reproduce "$FAMILY"
|
||||
echo " cli exit: $?"
|
||||
|
||||
mv "$STORE.orig" "$STORE"
|
||||
echo "--- restored"
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"format_version": 1,
|
||||
"kind": "process",
|
||||
"name": "m4-explore-only-sweep",
|
||||
"description": "Selection-free sweep: the family itself is the result — the smallest thing that persists a reproducible family.",
|
||||
"pipeline": [ { "block": "std::sweep" } ]
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env bash
|
||||
# Fieldtest, milestone 39, axis 2 — the CLI exit-class partition.
|
||||
#
|
||||
# The downstream task: I am writing a CI wrapper around `aura` and I want to
|
||||
# branch on the failure CLASS without parsing stderr, exactly as C14 promises
|
||||
# (2 = the content of what argv named, 1 = the environment/recorded state,
|
||||
# 3 = campaign completed with failed cells, 0 = success). Every probe below
|
||||
# is a refusal a real author trips over; `want` is the class I derived from
|
||||
# docs/authoring-guide.md's exit-code passage + C14 BEFORE running it.
|
||||
#
|
||||
# Run from the `lab/` project directory.
|
||||
set -u
|
||||
AURA=${AURA:-../../../target/release/aura}
|
||||
BP=blueprints/embed-probe.json
|
||||
tmp=$(mktemp -d)
|
||||
|
||||
pass=0; fail=0
|
||||
probe() { # probe <want> <label> -- <cmd...>
|
||||
local want=$1 label=$2; shift 3
|
||||
local out; out=$("$@" 2>&1 >/dev/null); local got=$?
|
||||
local verdict="OK "; if [ "$got" != "$want" ]; then verdict="DIFF"; fi
|
||||
if [ "$got" = "$want" ]; then pass=$((pass+1)); else fail=$((fail+1)); fi
|
||||
printf '%s want=%s got=%s %-46s | %s\n' "$verdict" "$want" "$got" "$label" "$(echo "$out" | head -1)"
|
||||
}
|
||||
|
||||
# --- 0: success -------------------------------------------------------------
|
||||
probe 0 "closed blueprint, one synthetic run" -- $AURA exec $BP
|
||||
probe 0 "blueprint + declared tap folded" -- $AURA exec $BP --tap spread=mean
|
||||
|
||||
# --- 2: the content of what argv named --------------------------------------
|
||||
printf 'not json at all\n' > "$tmp/notjson.json"
|
||||
probe 2 "target file is not JSON" -- $AURA exec "$tmp/notjson.json"
|
||||
cp ../m4_1_tapped_signal.ops.json "$tmp/oplist.json"
|
||||
probe 2 "op-script array handed to exec" -- $AURA exec "$tmp/oplist.json"
|
||||
probe 2 "process document handed to exec" -- $AURA exec ../m4_2_process_sweep_only.json
|
||||
printf '{"format_version":1,"blueprint":{"nodes":[],"edges":[]}}\n' > "$tmp/broken.json"
|
||||
probe 2 "blueprint envelope that will not build" -- $AURA exec "$tmp/broken.json"
|
||||
python3 - <<PY
|
||||
import json
|
||||
ops = json.load(open("../m4_1_tapped_signal.ops.json"))
|
||||
op = [dict(o) for o in ops]
|
||||
for o in op:
|
||||
o.pop("bind", None)
|
||||
if o.get("op") == "name": o["name"] = "open-probe"
|
||||
json.dump(op, open("$tmp/open.ops.json", "w"))
|
||||
bl = [o for o in ops if o.get("op") not in ("expose", "tap")]
|
||||
for o in bl:
|
||||
if o.get("op") == "name": o["name"] = "blind-probe"
|
||||
json.dump(bl, open("$tmp/blind.ops.json", "w"))
|
||||
PY
|
||||
$AURA graph build < "$tmp/open.ops.json" > "$tmp/open.bp.json"
|
||||
$AURA graph build < "$tmp/blind.ops.json" > "$tmp/blind.bp.json"
|
||||
probe 2 "open blueprint (free knobs) run directly" -- $AURA exec "$tmp/open.bp.json"
|
||||
probe 2 "blueprint exposing neither bias nor tap" -- $AURA exec "$tmp/blind.bp.json"
|
||||
probe 2 "unknown fold label on a declared tap" -- $AURA exec $BP --tap spread=median
|
||||
probe 2 "tap name the blueprint does not declare" -- $AURA exec $BP --tap spred=mean
|
||||
probe 2 "same tap subscribed twice" -- $AURA exec $BP --tap spread=mean --tap spread=count
|
||||
probe 2 "override token with no '='" -- $AURA exec $BP --override fast.length
|
||||
probe 2 "override value that is not a literal" -- $AURA exec $BP --override fast.length=abc
|
||||
probe 2 "override names no param of the graph" -- $AURA exec $BP --override nope.length=3
|
||||
probe 2 "override value of the wrong kind" -- $AURA exec $BP --override fast.length=1.5
|
||||
|
||||
# --- 1: the environment / recorded state ------------------------------------
|
||||
BPID=00fd2bc044fe4c9b947aac97da740e6d08bf4103ec376058f0cf52590f7863aa
|
||||
PRID=e4a97c684686c2982f034a27dabb7f26e54c76d7578dd985eaa41ef67c77f275
|
||||
# same campaign as axis 1, but a window the archive cannot serve (year 2124)
|
||||
sed -e 's/"from_ms": 1725148800000/"from_ms": 4870281600000/' \
|
||||
-e 's/"to_ms": 1725753600000/"to_ms": 4870886400000/' \
|
||||
-e 's/"name": "m4-embed-reproduce-probe"/"name": "m4-window-with-no-data"/' \
|
||||
../m4_2_campaign_ger40.json > "$tmp/nodata.json"
|
||||
# ... one over an instrument the archive has never heard of
|
||||
sed -e 's/"GER40"/"NOSUCHSYM"/' \
|
||||
-e 's/"name": "m4-embed-reproduce-probe"/"name": "m4-absent-symbol"/' \
|
||||
../m4_2_campaign_ger40.json > "$tmp/absent.json"
|
||||
# ... and one whose axis carries no values at all
|
||||
sed -e 's/"values": \[2, 3\]/"values": []/' \
|
||||
-e 's/"name": "m4-embed-reproduce-probe"/"name": "m4-empty-axis"/' \
|
||||
../m4_2_campaign_ger40.json > "$tmp/emptyaxis.json"
|
||||
|
||||
probe 1 "override value out of the node's domain" -- $AURA exec $BP --override fast.length=-1
|
||||
probe 1 "target is neither file nor content id" -- $AURA exec no-such-target
|
||||
probe 1 "well-formed but unregistered content id" -- $AURA exec 0000000000000000000000000000000000000000000000000000000000000000
|
||||
probe 1 "reproduce an unknown family id" -- $AURA reproduce no-such-family-0
|
||||
probe 1 "campaign over a window the archive lacks" -- $AURA exec "$tmp/nodata.json"
|
||||
probe 1 "campaign over a symbol the archive lacks" -- $AURA exec "$tmp/absent.json"
|
||||
probe 1 "campaign document content fails its gate" -- $AURA exec "$tmp/emptyaxis.json"
|
||||
|
||||
echo "--- $pass matched the class I derived from the docs, $fail did not"
|
||||
rm -rf "$tmp"
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
# Fieldtest, milestone 39, axis 3 — the compatibility contract (C30).
|
||||
#
|
||||
# The downstream scenario C30 point 3 describes: my node crate rides the
|
||||
# engine checkout as a path dep, I pull a newer engine, and my previously
|
||||
# built cdylib is now stale. The contract promises the load handshake
|
||||
# refuses it ("a dylib from any other engine state is refused at load, never
|
||||
# trusted") instead of trusting a frozen version stamp.
|
||||
#
|
||||
# Simulated the only way a consumer can simulate it without touching engine
|
||||
# sources: a second checkout of the engine at an older commit
|
||||
# (`git worktree add --detach /tmp/aura-stale d87f534`, the commit right
|
||||
# before #348 re-armed the stamp), the node crate rebuilt against THAT
|
||||
# aura-core, and the current host asked to load the result.
|
||||
#
|
||||
# Run from the `lab/` project directory.
|
||||
set -u
|
||||
AURA=${AURA:-../../../target/release/aura}
|
||||
STALE=${STALE:-/tmp/aura-stale}
|
||||
NODES=../lab-nodes
|
||||
|
||||
echo "--- baseline: node crate built against the checkout it is attached to"
|
||||
( cd $NODES && cargo build -q 2>&1 | tail -2 )
|
||||
$AURA graph introspect --node lab_nodes::Scale
|
||||
echo " exit: $?"
|
||||
|
||||
echo "--- now rebuild the very same node crate against $STALE (an older engine state)"
|
||||
cp $NODES/Cargo.toml $NODES/Cargo.toml.orig
|
||||
sed -i "s#path = \"[^\"]*crates/aura-core\"#path = \"$STALE/crates/aura-core\"#" $NODES/Cargo.toml
|
||||
( cd $NODES && cargo build -q 2>&1 | tail -3 )
|
||||
$AURA graph introspect --node lab_nodes::Scale
|
||||
echo " exit: $?"
|
||||
|
||||
mv $NODES/Cargo.toml.orig $NODES/Cargo.toml
|
||||
( cd $NODES && cargo build -q 2>&1 | tail -1 )
|
||||
echo "--- restored"
|
||||
Reference in New Issue
Block a user