feat(cli): envelope-probe discoverability + kind-key refusal prose (#193 residue)
The --unwired help line names the whole-envelope starting move (start
from a bare {}), closing the 0106-F1 discoverability gap the narrowed
issue kept open; the wrong-kind refusal names the exact key a blind
author must fix ('the "kind" key must be "process"') instead of
describing the mismatch abstractly. The third original item (block-ids
vs field names in --vocabulary) is sufficiently served by the
envelope probe per the issue's narrowing comment.
RED-first (tdd-author handoff): both pins observed failing on the old
help text / prose.
Gates: workspace 1043/0, clippy -D warnings clean.
closes #193
This commit is contained in:
@@ -39,7 +39,7 @@ pub struct DocIntrospectCmd {
|
||||
/// Describe one block's typed slots
|
||||
#[arg(long, value_name = "ID")]
|
||||
pub block: Option<String>,
|
||||
/// List the open slots of a (partial) document
|
||||
/// List the open slots of a (partial) document — start from a bare {} to see the whole envelope
|
||||
#[arg(long, value_name = "FILE")]
|
||||
pub unwired: Option<PathBuf>,
|
||||
/// Print the content id of a valid document
|
||||
@@ -112,7 +112,7 @@ pub(crate) fn doc_error_prose(what: &str, e: &DocError) -> String {
|
||||
DocKind::Process => "process",
|
||||
DocKind::Campaign => "campaign",
|
||||
};
|
||||
format!("{what}: wrong document kind (expected \"{want}\")")
|
||||
format!("{what}: the \"kind\" key must be \"{want}\"")
|
||||
}
|
||||
DocError::Malformed(msg) => format!("{what}: {msg}"),
|
||||
}
|
||||
|
||||
@@ -648,6 +648,51 @@ fn campaign_introspect_block_presentation_names_the_tap_vocabulary() {
|
||||
assert!(out.contains("persist_taps"), "stdout/stderr: {out}");
|
||||
}
|
||||
|
||||
/// #193 (narrowed scope, cycle-0107 F3): the whole document envelope IS
|
||||
/// derivable — `introspect --unwired` over a bare `{}` enumerates every
|
||||
/// top-level field (seed, data, strategies, process.ref, presentation) as an
|
||||
/// open slot — but nothing pointed the author there, so the 0106 round
|
||||
/// iterated blind `validate` errors instead of starting from the skeleton.
|
||||
/// The shared `--unwired` help line now carries the skeleton hint naming that
|
||||
/// starting move, making it discoverable from `--help` alone (the process and
|
||||
/// campaign families share `DocIntrospectCmd`, so pinning one covers both).
|
||||
/// Whitespace is normalized before matching so clap's help-column wrapping
|
||||
/// (terminal-width dependent) can never split the pinned phrase.
|
||||
#[test]
|
||||
fn introspect_help_hints_the_bare_envelope_probe() {
|
||||
let (out, code) = run_code(&["campaign", "introspect", "--help"]);
|
||||
assert_eq!(code, Some(0), "clap --help exits 0: {out}");
|
||||
let flat = out.split_whitespace().collect::<Vec<_>>().join(" ");
|
||||
assert!(
|
||||
flat.contains("start from a bare {}"),
|
||||
"the --unwired help line must point at the bare-{{}} envelope probe: {out}"
|
||||
);
|
||||
}
|
||||
|
||||
/// #193 (narrowed scope, cycle-0106 F1): a document whose `kind` key carries
|
||||
/// the wrong value — the blind-author trap of pointing `process validate` at
|
||||
/// a campaign-kind file — is refused with prose that NAMES the `kind` key the
|
||||
/// author must fix ("the \"kind\" key must be \"process\""), instead of the
|
||||
/// opaque "wrong document kind" that never told them WHICH field to change.
|
||||
/// Autonomous: a minimal campaign-kind envelope written inline, refused at the
|
||||
/// envelope check before any deserialization (exit 1), Debug-free.
|
||||
#[test]
|
||||
fn wrong_kind_refusal_names_the_kind_key() {
|
||||
let dir = temp_cwd("wrong-kind-names-key");
|
||||
write_doc(
|
||||
&dir,
|
||||
"mislabeled.process.json",
|
||||
r#"{ "format_version": 1, "kind": "campaign", "name": "x", "pipeline": [] }"#,
|
||||
);
|
||||
let (out, code) = run_code_in(&dir, &["process", "validate", "mislabeled.process.json"]);
|
||||
assert_eq!(code, Some(1), "stdout/stderr: {out}");
|
||||
assert!(
|
||||
out.contains("the \"kind\" key must be \"process\""),
|
||||
"the refusal must name the kind key the author must fix: {out}"
|
||||
);
|
||||
assert!(!out.contains("WrongKind"), "Debug leak: {out}");
|
||||
}
|
||||
|
||||
/// Register must be a gate, not a passthrough: an invalid document is
|
||||
/// refused with prose (exit 1) and — the property that matters for a
|
||||
/// content-addressed store — no file is ever written under `runs/campaigns/`
|
||||
|
||||
Reference in New Issue
Block a user