feat(engine+cli): topology-identity hash — --identity-id beside --content-id
Engine (aura-engine/blueprint_serde): blueprint_to_json factored into build_doc/serialize_doc (byte-preserving — canonical golden unchanged) and blueprint_identity_json added: the canonical document with every non-load-bearing debug symbol (C23) blanked (composite name, instance names, bound-param names, role names, output re-export names) while everything load-bearing survives (type ids, node order, edges, role targets/order, output pairs/order, bound pos/kind/value — openness stays identity-bearing). Re-exported from lib.rs. Six property tests: renamed twins (equal identity, unequal canonical), open-vs-bound, bound-value, edge-swap, nested-composite interior names, role/output renames — the last two are additive beyond the plan and cover the recursion and role/output arms the planned four did not. CLI (aura-cli): graph introspect gains --identity-id, a sibling of --content-id through the same shared content_id SHA-256 primitive; composite_from_str factored out of build_from_str (fault strings byte-identical). The exactly-one introspect dispatch is DELIBERATELY relaxed: the two id flags form one group and may combine — one build, both ids, one per line, content id first. In-crate cross-path twin test (Rust sma_signal vs op-script twin: distinct content ids, one identity id) beside the existing cross-surface pins; four e2e tests incl. the previously uncovered count!=1 usage exit-2 path. topology_hash, the blueprint store, reproduce, and every --content-id byte stay untouched (spec acceptance 3; all existing pins green). Verification: cargo build clean; cargo test --workspace 884 passed / 0 failed (873 baseline + 11 new); clippy -D warnings clean; doc build 0 warnings. The implement-loop's Task-4 spec-compliance block was a plan-byte count mismatch only (plan under-counted pre-existing blueprint_serde tests 6-vs-7 and did not anticipate the two sibling-accepted extra tests); gates re-run by hand, all green. closes #171, refs #180
This commit is contained in:
@@ -3817,6 +3817,9 @@ struct GraphIntrospectCmd {
|
||||
/// Print the graph's content id (topology hash).
|
||||
#[arg(long)]
|
||||
content_id: bool,
|
||||
/// Print the graph's topology-identity id (debug names stripped).
|
||||
#[arg(long)]
|
||||
identity_id: bool,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
@@ -6015,6 +6018,50 @@ mod tests {
|
||||
assert_eq!(topology_hash(&sig), content_id(&blueprint_to_json(&sig).expect("serializes")));
|
||||
}
|
||||
|
||||
/// The op-script twin of `sma_signal(Some(2), Some(4))`: same topology —
|
||||
/// SMA(2)/SMA(4) over one `price` role, spread, Bias with `scale` BOUND to
|
||||
/// 0.5 (= `R_SMA_BIAS_SCALE`; boundness is identity-bearing) — differing
|
||||
/// only in debug names (composite "graph" vs "sma_signal", unnamed Bias vs
|
||||
/// `.named("bias")`).
|
||||
const IDENTITY_TWIN_DOC: &str = r#"[
|
||||
{"op":"source","role":"price","kind":"F64"},
|
||||
{"op":"add","type":"SMA","name":"fast","bind":{"length":{"I64":2}}},
|
||||
{"op":"add","type":"SMA","name":"slow","bind":{"length":{"I64":4}}},
|
||||
{"op":"add","type":"Sub"},
|
||||
{"op":"add","type":"Bias","bind":{"scale":{"F64":0.5}}},
|
||||
{"op":"feed","role":"price","into":["fast.series","slow.series"]},
|
||||
{"op":"connect","from":"fast.value","to":"sub.lhs"},
|
||||
{"op":"connect","from":"slow.value","to":"sub.rhs"},
|
||||
{"op":"connect","from":"sub.value","to":"bias.signal"},
|
||||
{"op":"expose","from":"bias.bias","as":"bias"}
|
||||
]"#;
|
||||
|
||||
/// #171 acc 1 (cross-path identity): the Rust `sma_signal` builder and its
|
||||
/// op-script twin differ in canonical bytes (debug names) — distinct content
|
||||
/// ids — but project to the same identity JSON, hence one identity id across
|
||||
/// authoring paths.
|
||||
#[test]
|
||||
fn identity_id_bridges_the_rust_builder_and_op_script_paths() {
|
||||
let rust_built = sma_signal(Some(2), Some(4));
|
||||
let env = project::Env::std();
|
||||
let json = crate::graph_construct::build_from_str(IDENTITY_TWIN_DOC, &env)
|
||||
.expect("op-script twin builds");
|
||||
assert_ne!(
|
||||
content_id(&json),
|
||||
topology_hash(&rust_built),
|
||||
"authoring paths keep distinct content ids"
|
||||
);
|
||||
let loaded = blueprint_from_json(&json, &|t| std_vocabulary(t)).expect("twin reloads");
|
||||
let identity = |c: &Composite| {
|
||||
content_id(&aura_engine::blueprint_identity_json(c).expect("identity-serializes"))
|
||||
};
|
||||
assert_eq!(
|
||||
identity(&loaded),
|
||||
identity(&rust_built),
|
||||
"one topology -> one identity id across authoring paths"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mc_r_bootstrap_json_carries_every_bootstrap_field_under_the_mc_r_bootstrap_key() {
|
||||
// Property: the `mc_r_bootstrap` output line is the full RBootstrap shape —
|
||||
|
||||
Reference in New Issue
Block a user