test(cli): C29 register-refusal e2e -- doc-less root, doc-less nested, described twin

Iteration 3 task 4: register_refuses_undescribed_composites_end_to_end
pins the store gate's verb surface -- a doc-less root refuses naming
`sma_signal` and the rule, a doc-less named nested composite refuses
naming `inner`, the described shipped example registers (exit codes and
stderr through the C14 contract). The pre-existing twin pin
graph_doc_field_moves_content_id_but_not_identity_id stays green.

Checkpoint note: the loop escalated this task spec-ambiguous because
its plan gate demanded a fully green graph_construct suite while the
op-script register test is known-open until the doc-op task lands (a
plan-sequencing defect, orchestrator's authoring error); the task's own
work was verified green and is committed here, the doc-op task follows.

refs #316
This commit is contained in:
2026-07-23 19:53:49 +02:00
parent 750ee93180
commit 593e233e66
+41
View File
@@ -462,6 +462,47 @@ fn graph_register_is_idempotent() {
assert_eq!(registered_id(&out1), registered_id(&out2), "same id twice");
}
/// C29 register seam end-to-end (#316): a doc-less root composite refuses at
/// `aura graph register` naming the composite and the rule (exit 1, C14); a
/// doc-less named nested composite refuses naming the nested name; the
/// described shipped example registers. The gate lives at the store write
/// every register path shares — this pins its verb surface.
#[test]
fn register_refuses_undescribed_composites_end_to_end() {
let dir = temp_cwd("register-doc-gate");
// doc-less root: the shipped example minus its doc member
let text = std::fs::read_to_string(example("r_sma.json")).expect("read example");
let mut v: serde_json::Value = serde_json::from_str(&text).expect("example parses");
v["blueprint"].as_object_mut().expect("blueprint object").remove("doc");
let docless = dir.join("docless.json");
std::fs::write(&docless, serde_json::to_string(&v).unwrap()).expect("write fixture");
let (out, err, code) = run_in(&dir, &["graph", "register", docless.to_str().unwrap()]);
assert_eq!(code, Some(1), "doc-less root refuses: {out} {err}");
assert!(
err.contains("composite `sma_signal` carries no doc"),
"stderr names the composite and the rule: {err}"
);
assert!(err.contains("C29"), "stderr cites the contract: {err}");
// doc-less named nested composite inside a described root
v["blueprint"]["doc"] = serde_json::Value::String("a described root".into());
v["blueprint"]["nodes"]
.as_array_mut()
.expect("nodes array")
.push(serde_json::json!({"composite": {"name": "inner", "nodes": []}}));
let nested = dir.join("nested.json");
std::fs::write(&nested, serde_json::to_string(&v).unwrap()).expect("write fixture");
let (out, err, code) = run_in(&dir, &["graph", "register", nested.to_str().unwrap()]);
assert_eq!(code, Some(1), "doc-less nested refuses: {out} {err}");
assert!(
err.contains("composite `inner` carries no doc"),
"stderr names the nested composite: {err}"
);
// the described shipped example registers
let (out, err, code) = run_in(&dir, &["graph", "register", &example("r_sma.json")]);
assert_eq!(code, Some(0), "described example registers: {out} {err}");
assert!(out.starts_with("registered blueprint "), "prints the register line: {out}");
}
/// Property (#196, the campaign-axis namespace): `--params <FILE>` prints the
/// RAW composite param space — one `{name}:{kind:?}` line per open param, in
/// lowering order, WITHOUT the harness-wrap prefix `aura sweep --list-axes`