feat(cli): graph register / --params accept the op-script form (fieldtest 0107 F5)
Both verbs now route through the same composite_from_any shape
discrimination file-mode --content-id shipped with (op-script array ->
one-build tail; envelope object -> loader), so the three on-ramp verbs
agree on accepted shapes and an op-script registers under the same
shape-invariant content id as its built envelope — no more raw serde
refusal ('invalid type: map, expected u32') hiding the graph build
bridge.
RED-first (tdd-author handoff): two same-seam behavioural pins
(register stores the envelope id for an op-script; --params matches
the envelope's axis lines) observed failing on the serde refusal, then
the two call sites rerouted.
Gates: workspace 990/0, clippy -D warnings clean.
closes #202
This commit is contained in:
@@ -370,8 +370,9 @@ fn resolve_blueprint_text(target: &str, env: &crate::project::Env) -> Result<Str
|
|||||||
fn params_lines(target: &str, env: &crate::project::Env) -> Result<String, String> {
|
fn params_lines(target: &str, env: &crate::project::Env) -> Result<String, String> {
|
||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
let text = resolve_blueprint_text(target, env)?;
|
let text = resolve_blueprint_text(target, env)?;
|
||||||
let composite =
|
// Shape-discriminated like file-mode --content-id: an op-script (array)
|
||||||
blueprint_from_json(&text, &|t| env.resolve(t)).map_err(|e| blueprint_load_prose(&e))?;
|
// builds through the one-build tail, an envelope (object) loads (#202).
|
||||||
|
let composite = composite_from_any(&text, env)?;
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
for p in composite.param_space() {
|
for p in composite.param_space() {
|
||||||
let _ = writeln!(out, "{}:{:?}", p.name, p.kind);
|
let _ = writeln!(out, "{}:{:?}", p.name, p.kind);
|
||||||
@@ -396,8 +397,9 @@ pub fn register_cmd(file: &Path, env: &crate::project::Env) {
|
|||||||
fn register_blueprint(file: &Path, env: &crate::project::Env) -> Result<String, String> {
|
fn register_blueprint(file: &Path, env: &crate::project::Env) -> Result<String, String> {
|
||||||
let text = std::fs::read_to_string(file)
|
let text = std::fs::read_to_string(file)
|
||||||
.map_err(|e| format!("cannot read {}: {e}", file.display()))?;
|
.map_err(|e| format!("cannot read {}: {e}", file.display()))?;
|
||||||
let composite =
|
// Shape-discriminated like file-mode --content-id (#202): either shape
|
||||||
blueprint_from_json(&text, &|t| env.resolve(t)).map_err(|e| blueprint_load_prose(&e))?;
|
// canonicalizes to the envelope form, so the stored id is shape-invariant.
|
||||||
|
let composite = composite_from_any(&text, env)?;
|
||||||
let canonical = blueprint_to_json(&composite).map_err(|e| format!("serialize error: {e:?}"))?;
|
let canonical = blueprint_to_json(&composite).map_err(|e| format!("serialize error: {e:?}"))?;
|
||||||
let id = crate::content_id(&canonical);
|
let id = crate::content_id(&canonical);
|
||||||
let registry = env.registry();
|
let registry = env.registry();
|
||||||
|
|||||||
@@ -605,3 +605,64 @@ fn graph_register_rejects_an_unknown_node_type_with_prose() {
|
|||||||
"names the unknown node type: {stderr}"
|
"names the unknown node type: {stderr}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Property (#202 headline, the on-ramp is shape-invariant): `aura graph register`
|
||||||
|
/// accepts an op-script document (a JSON array) exactly as it accepts a #155
|
||||||
|
/// blueprint envelope (a JSON object) — it shape-discriminates the array form,
|
||||||
|
/// builds it through the same one-build tail, and stores it under the SAME content
|
||||||
|
/// id it stores that op-script's `graph build` envelope under. The content id is
|
||||||
|
/// shape-invariant (an op-script and its built envelope hash identically), so the
|
||||||
|
/// two on-ramp shapes must agree on the store key; `register` must not reject the
|
||||||
|
/// op-script with the raw serde `invalid type: map, expected u32` message the
|
||||||
|
/// direct-`blueprint_from_json` path currently leaks.
|
||||||
|
#[test]
|
||||||
|
fn graph_register_accepts_an_op_script_and_stores_the_envelope_content_id() {
|
||||||
|
let dir = temp_cwd("register-op-script");
|
||||||
|
// The op-script form (a JSON array of ops), written to a file.
|
||||||
|
let op_script = dir.join("op-script.json");
|
||||||
|
std::fs::write(&op_script, SIGNAL_DOC).expect("write op-script fixture");
|
||||||
|
// Its `graph build` envelope (a JSON object), the shape `register` already accepts.
|
||||||
|
let (envelope_bytes, _e, built) = run(&["graph", "build"], SIGNAL_DOC);
|
||||||
|
assert!(built, "graph build produces the envelope");
|
||||||
|
let envelope = dir.join("envelope.json");
|
||||||
|
std::fs::write(&envelope, &envelope_bytes).expect("write envelope fixture");
|
||||||
|
|
||||||
|
// The envelope shape registers today (green sibling); the op-script must too.
|
||||||
|
let (env_out, env_err, env_code) =
|
||||||
|
run_in(&dir, &["graph", "register", envelope.to_str().unwrap()]);
|
||||||
|
assert_eq!(env_code, Some(0), "register envelope: {env_out} {env_err}");
|
||||||
|
let (op_out, op_err, op_code) =
|
||||||
|
run_in(&dir, &["graph", "register", op_script.to_str().unwrap()]);
|
||||||
|
assert_eq!(op_code, Some(0), "register op-script exits 0: {op_out} {op_err}");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
registered_id(&op_out),
|
||||||
|
registered_id(&env_out),
|
||||||
|
"op-script and its built envelope register to the same content id"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Property (#202, the same on-ramp seam at the sibling verb): `aura graph
|
||||||
|
/// introspect --params` accepts an op-script exactly as it accepts a blueprint
|
||||||
|
/// envelope — the raw axis namespace it prints for the op-script equals the one it
|
||||||
|
/// prints for that op-script's built envelope, both exit 0. Currently `--params`
|
||||||
|
/// rejects the op-script with the same raw serde `invalid type: map, expected u32`
|
||||||
|
/// message `register` does (both route through the direct `blueprint_from_json`).
|
||||||
|
#[test]
|
||||||
|
fn graph_params_accepts_an_op_script_matching_its_envelope() {
|
||||||
|
let dir = temp_cwd("params-op-script");
|
||||||
|
let op_script = dir.join("op-script.json");
|
||||||
|
std::fs::write(&op_script, SIGNAL_DOC).expect("write op-script fixture");
|
||||||
|
let (envelope_bytes, _e, built) = run(&["graph", "build"], SIGNAL_DOC);
|
||||||
|
assert!(built, "graph build produces the envelope");
|
||||||
|
let envelope = dir.join("envelope.json");
|
||||||
|
std::fs::write(&envelope, &envelope_bytes).expect("write envelope fixture");
|
||||||
|
|
||||||
|
let (env_out, env_err, env_code) =
|
||||||
|
run_in(&dir, &["graph", "introspect", "--params", envelope.to_str().unwrap()]);
|
||||||
|
assert_eq!(env_code, Some(0), "params on envelope: {env_out} {env_err}");
|
||||||
|
let (op_out, op_err, op_code) =
|
||||||
|
run_in(&dir, &["graph", "introspect", "--params", op_script.to_str().unwrap()]);
|
||||||
|
assert_eq!(op_code, Some(0), "params on op-script exits 0: {op_out} {op_err}");
|
||||||
|
assert_eq!(op_out, env_out, "op-script and envelope agree on the raw axis namespace");
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user