From 0ad8fc6ad24d30afdb5e115538dc035a543053c7 Mon Sep 17 00:00:00 2001 From: claude Date: Sun, 12 Jul 2026 20:12:55 +0200 Subject: [PATCH] feat(cli): the tier-aware namespace hint fires on the graph-build path too The op-script error seam (OpError::UnknownNodeType) now consults the same tier-hint logic as the blueprint load path: outside a project the hint names the missing Aura.toml, inside a data-only project it names the missing node crate and the attach verb. Hint texts shared, load-path behaviour and the op-error prose byte-stable, exit codes unchanged. closes #244 --- crates/aura-cli/src/graph_construct.rs | 59 ++++++++++++++++++-------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/crates/aura-cli/src/graph_construct.rs b/crates/aura-cli/src/graph_construct.rs index 439be80..b41bbaa 100644 --- a/crates/aura-cli/src/graph_construct.rs +++ b/crates/aura-cli/src/graph_construct.rs @@ -133,7 +133,18 @@ fn composite_from_str(doc: &str, env: &crate::project::Env) -> Result = docs.iter().map(OpDoc::kind_label).collect(); let ops: Vec = docs.into_iter().map(Op::from).collect(); replay("graph", ops, &|t| env.resolve(t)).map_err(|(idx, err)| { - let cause = format_op_error(&err); + let mut cause = format_op_error(&err); + // #244: the op-script path reports an unresolvable namespaced type as + // `OpError::UnknownNodeType`, a different error family than the + // blueprint LOAD path's `LoadError::UnknownNodeType` — so without this, + // `unresolved_namespace_hint` is never consulted here. Same tier hint, + // appended the same way. + if let OpError::UnknownNodeType(t) = &err + && let Some(hint) = tier_hint_for_type_id(t, env) + { + cause.push_str(" — "); + cause.push_str(&hint); + } match labels.get(idx) { Some(kind) => format!("op {idx} ({kind}): {cause}"), None => format!("finalize: {cause}"), @@ -376,25 +387,37 @@ fn gang_fault_prose(e: &CompileError) -> String { } } -/// (#185/#241) When an unresolved type id LOOKS project-namespaced (`::`), -/// the likeliest causes are environmental, tiered: outside any project the -/// hint points at the missing `Aura.toml`; inside a data-only project it -/// points at the missing node crate. Bare std ids (no `::`) and invocations -/// with a loaded node crate get no hint (the vocabulary error carries the -/// message). +/// (#185/#241/#244) When an unresolved type id LOOKS project-namespaced +/// (`::`), the likeliest causes are environmental, tiered: outside any +/// project the hint points at the missing `Aura.toml`; inside a data-only +/// project it points at the missing node crate. Bare std ids (no `::`) and +/// invocations with a loaded node crate get no hint (the vocabulary error +/// carries the message). Shared core: both the blueprint LOAD path +/// (`unresolved_namespace_hint`, over `LoadError`) and the op-script `graph +/// build` path (`composite_from_str`, over `OpError`) call this same helper, +/// so the tier texts cannot drift between the two error families. +fn tier_hint_for_type_id(type_id: &str, env: &crate::project::Env) -> Option { + if !type_id.contains("::") { + return None; + } + match env.provenance() { + None => Some( + "type id looks project-namespaced but no Aura.toml was found — run inside a project directory" + .to_string(), + ), + Some(p) if p.namespace.is_none() => Some( + "type id looks project-namespaced but this project binds no node crate — attach one with `aura nodes new `" + .to_string(), + ), + Some(_) => None, + } +} + +/// (#185/#241) See [`tier_hint_for_type_id`] for the tier rules; this is the +/// blueprint LOAD path's entry point over `LoadError`. pub(crate) fn unresolved_namespace_hint(e: &LoadError, env: &crate::project::Env) -> Option { match e { - LoadError::UnknownNodeType(t) if t.contains("::") => match env.provenance() { - None => Some( - "type id looks project-namespaced but no Aura.toml was found — run inside a project directory" - .to_string(), - ), - Some(p) if p.namespace.is_none() => Some( - "type id looks project-namespaced but this project binds no node crate — attach one with `aura nodes new `" - .to_string(), - ), - Some(_) => None, - }, + LoadError::UnknownNodeType(t) => tier_hint_for_type_id(t, env), _ => None, } }