From c39f5e47628debb3c35cbe77f0929c97f382a782 Mon Sep 17 00:00:00 2001 From: claude Date: Sun, 26 Jul 2026 12:10:43 +0200 Subject: [PATCH] =?UTF-8?q?feat(aura-cli,=20aura-engine):=20graph=20intros?= =?UTF-8?q?pect=20--taps=20=E2=80=94=20positive=20declared-tap=20discovery?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Harvest sweep, batch 3 of 6 (first half). Composite::declared_taps() walks the blueprint depth-first collecting (tap name, source wire, column kind), names bare at every depth — mirroring inline_composite's hoist, where a tap's name IS the flat graph's load-bearing identifier — and bounds-total over invalid wires (compile's validate_wiring stays the real gate). The CLI view loads like --params (FILE gates the authored root name, a content id does not), prints one 'name node.field Kind' row per tap, and renders the tap-less case as a stderr note with empty stdout, exit 0. Closes the recovery-only discovery loop: since #333 the only way to learn a blueprint's declared tap names was provoking the undeclared-tap refusal roster. closes #337 --- crates/aura-cli/src/graph_construct.rs | 49 ++++++++++-- crates/aura-cli/src/main.rs | 6 ++ crates/aura-cli/tests/graph_construct.rs | 48 ++++++++++++ crates/aura-engine/src/blueprint.rs | 99 ++++++++++++++++++++++++ 4 files changed, 197 insertions(+), 5 deletions(-) diff --git a/crates/aura-cli/src/graph_construct.rs b/crates/aura-cli/src/graph_construct.rs index 7ef9fc3..ff21f89 100644 --- a/crates/aura-cli/src/graph_construct.rs +++ b/crates/aura-cli/src/graph_construct.rs @@ -589,10 +589,11 @@ fn introspect_registered(env: &aura_runner::project::Env) -> Result` / `--unwired` / `--params ` / the id -/// group must be set; zero or more than one is the usage error (exit 2). The id -/// group is `--content-id [FILE]` and/or `--identity-id` — the two id flags may -/// combine (one build, both ids, content id first). +/// `--vocabulary` / `--node ` / `--unwired` / `--params ` / +/// `--taps ` / the id group must be set; zero or more than one is the +/// usage error (exit 2). The id group is `--content-id [FILE]` and/or +/// `--identity-id` — the two id flags may combine (one build, both ids, +/// content id first). pub fn introspect_cmd(cmd: crate::GraphIntrospectCmd, env: &aura_runner::project::Env) { let count = cmd.vocabulary as usize + cmd.node.is_some() as usize @@ -600,10 +601,11 @@ pub fn introspect_cmd(cmd: crate::GraphIntrospectCmd, env: &aura_runner::project + cmd.folds as usize + cmd.registered as usize + cmd.params.is_some() as usize + + cmd.taps.is_some() as usize + (cmd.content_id.is_some() || cmd.identity_id) as usize; if count != 1 { eprintln!( - "aura: Usage: aura graph introspect --vocabulary | --node | --unwired | --folds | --registered | --params | --content-id [FILE] | --identity-id (the two id flags may be combined)" + "aura: Usage: aura graph introspect --vocabulary | --node | --unwired | --folds | --registered | --params | --taps | --content-id [FILE] | --identity-id (the two id flags may be combined)" ); std::process::exit(2); } @@ -675,6 +677,18 @@ pub fn introspect_cmd(cmd: crate::GraphIntrospectCmd, env: &aura_runner::project std::process::exit(1); } } + } else if let Some(target) = cmd.taps.as_deref() { + // --taps (#337): the positive declared-tap discovery view — + // #333 shipped only the refusal roster (`bind_tap_plan`'s + // `UnknownTap`), so provoking that refusal was the only way to learn + // a blueprint's declared tap names. + match taps_lines(target, env) { + Ok(s) => print!("{s}"), + Err(m) => { + eprintln!("aura: {m}"); + std::process::exit(1); + } + } } else { // --content-id [FILE] / --identity-id (combinable): one build, then each // requested id on its own line, content id first. With a FILE value the @@ -997,6 +1011,31 @@ fn params_lines(target: &str, env: &aura_runner::project::Env) -> Result` (#337): one row per declared tap +/// — ` . ` — the positive discovery view #333's +/// refusal roster (`bind_tap_plan`'s `UnknownTap`) deliberately deferred: +/// before this, the only way to learn a blueprint's declared tap names was +/// provoking that refusal. Loads exactly like `--params` (FILE gates the +/// authored root name, a store content id does not, C29). A tap-less +/// blueprint prints nothing to stdout — nothing was declared to list — plus a +/// stderr note (the `bind_tap_plan` unbound-this-run note's own "aura: note:" +/// convention), exit 0: a listing, not a fault. +fn taps_lines(target: &str, env: &aura_runner::project::Env) -> Result { + use std::fmt::Write as _; + let (text, is_file) = resolve_blueprint_text(target, env)?; + let composite = if is_file { composite_from_authored_text(&text, env)? } else { composite_from_any(&text, env)? }; + let taps = composite.declared_taps(); + if taps.is_empty() { + eprintln!("aura: note: {target} declares no taps"); + return Ok(String::new()); + } + let mut out = String::new(); + for (name, wire, kind) in taps { + let _ = writeln!(out, "{name} {wire} {kind:?}"); + } + Ok(out) +} + /// `aura graph register [--name