feat(cli): one bare content-id display form; CLI targets tolerate the content: prefix (0106 F3 / 0107 F6)

The copyable token is now the valid ref form everywhere: all three
register lines print 'registered <family> <id> (<path>)' and both doc
introspect --content-id modes print the bare 64-hex id (converging on
graph introspect's existing form). CLI FILE-or-id targets (campaign
run, graph introspect --params) strip a leading 'content:' before
resolving — a CLI arg is ephemeral input, leniency costs nothing. Doc
ref FIELDS stay bare-only (two accepted spellings would make
semantically identical docs hash apart — canonical-form byte
stability outranks input leniency); instead the referential not-found
prose appends 'refs use the bare 64-hex id — drop the content: prefix'
when the offending ref carries it.

RED-first (tdd-author handoff): eight same-seam assertions across the
three surfaces observed failing on the prefixed forms, then the five
display sites, two target ladders, and the conditional hint landed.

Gates: workspace 993/0, clippy -D warnings clean.

closes #194
This commit is contained in:
2026-07-03 23:01:20 +02:00
parent c3d62f2ce3
commit 63d2657850
5 changed files with 151 additions and 27 deletions
+7 -1
View File
@@ -347,6 +347,12 @@ fn composite_from_any(text: &str, env: &crate::project::Env) -> Result<Composite
/// `campaign run`'s target resolution uses, so the two FILE-or-id surfaces
/// cannot drift apart on what counts as a store address.
fn resolve_blueprint_text(target: &str, env: &crate::project::Env) -> Result<String, String> {
// A CLI arg tolerates the `content:` display prefix (#194); doc ref
// fields stay bare-only.
let target = target
.strip_prefix("content:")
.filter(|t| crate::campaign_run::is_content_id(t))
.unwrap_or(target);
let path = Path::new(target);
if path.is_file() {
return std::fs::read_to_string(path)
@@ -405,7 +411,7 @@ fn register_blueprint(file: &Path, env: &crate::project::Env) -> Result<String,
let registry = env.registry();
registry.put_blueprint(&id, &canonical).map_err(|e| e.to_string())?;
Ok(format!(
"registered blueprint content:{id} ({})",
"registered blueprint {id} ({})",
registry.blueprint_path(&id).display()
))
}