From 3e97039aa4f44042b7facf659b82b701f11b19ff Mon Sep 17 00:00:00 2001 From: Alec Wenzowski Date: Wed, 2 Sep 2026 21:40:54 +0000 Subject: [PATCH 01/13] =?UTF-8?q?feat(doctor):=20add=20`doctor=20mediator`?= =?UTF-8?q?=20=E2=80=94=20which=20engine=20the=20registrations=20reach?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `doctor hooks` answers whether the registrations reach an engine. Nothing answered WHICH one, and the gap between those two questions is where a session spends six hours believing it is mediated: silence from the hook is the documented sign it IS mediating, so an engine enforcing an old rule table and one enforcing the committed table produce identical evidence. Measured three times in one container on 2026-09-02, `doctor hooks` reporting `0 unwired` throughout. A SUB-VERB, NOT A FOURTH CHECK IN THE BARE REPORT. This landed once inside `diagnose()` and `verify` refused it: `this_repository_is_healthy` went red because `land` had rebuilt `target/release/batten` while the install was an hour old. The check was telling the truth — and whether a container's install is current is a property of the WORLD, while bare `doctor` answers a property of the COMMIT, so folding it in made a commit gate answer on install recency. `.claude/rules/toolchain.md` records that defect for `lock-check` and its remedy was the same split. House style §2 already specifies `doctor `. CONTENT, NOT THE VERSION STRING, correcting CLOUD-1349's own §2: `--version` read 0.0.137 on both sides while the installed binary refused the tree's own batten.toml over a key that landed in the base commit. A config surface moves without a version bump, and on a fast-forward-only trunk that is the ordinary case rather than an edge one. Nothing is spawned. `doctor` is `Effect::Read` and the agent allowlist is `filter(effect == read)` with no second list, so running a program a wiring file names would put config-supplied code behind a row any consumer agent may call. Four states: the two could-not-look causes are named variants rather than a string, so `Unresolvable` and `Unbuilt` are kept apart by the type, and neither folds into clean. Refs: CLOUD-1349 --- crates/batten/src/cli.rs | 12 ++++ crates/batten/src/doctor.rs | 110 +++++++++++++++++++++++++++++++++++ crates/batten/src/lib.rs | 24 ++++++++ crates/batten/src/surface.rs | 24 ++++++++ 4 files changed, 170 insertions(+) diff --git a/crates/batten/src/cli.rs b/crates/batten/src/cli.rs index 165fb9459..4352d400d 100644 --- a/crates/batten/src/cli.rs +++ b/crates/batten/src/cli.rs @@ -1133,6 +1133,15 @@ pub enum DoctorCommand { /// Emit the per-harness diagnosis as byte-stable JSON. json: bool, }, + /// Whether the engine the registrations reach was built from this tree. + /// + /// A sub-verb rather than a check in the bare report, because whether an + /// install is current is a property of the world and bare `doctor` answers a + /// property of the commit — see [`crate::doctor::Mediator`]. + Mediator { + /// Emit the comparison as byte-stable JSON. + json: bool, + }, /// Whether this session has declared work it has not finished. Session { /// Emit the count and the open ids as byte-stable JSON. @@ -1462,6 +1471,9 @@ fn doctor_of(matches: &ArgMatches) -> DoctorCommand { Some(("hooks", matches)) => DoctorCommand::Hooks { json: flag(matches, "json"), }, + Some(("mediator", matches)) => DoctorCommand::Mediator { + json: flag(matches, "json"), + }, Some(("session", matches)) => DoctorCommand::Session { json: flag(matches, "json"), }, diff --git a/crates/batten/src/doctor.rs b/crates/batten/src/doctor.rs index bfba5b04b..5c43c4b99 100644 --- a/crates/batten/src/doctor.rs +++ b/crates/batten/src/doctor.rs @@ -207,6 +207,116 @@ const PIN_RECORD: &str = "pin-record"; /// registration nor a sibling. const HOOK_HANDLERS: &str = "hook-handlers"; +/// Which engine the hook registrations actually reach (CLOUD-1349). +/// +/// **A stale mediator reads exactly like a working one**, which is the worst +/// shape in this model rather than an ordinary bug: silence from the hook is the +/// documented signal that it IS mediating, so an engine enforcing an old rule +/// table and one enforcing the committed table produce identical evidence. Every +/// other defence sits above where this fails — `input.tree.missing` as a channel, +/// `NotAcquired` keeping `Absent` and `Unparsed` apart, `RuleSkipped` reported +/// rather than folded into clean. Measured three times in one container on +/// 2026-09-02. +/// +/// **A SUB-VERB, NOT A CHECK IN THE BARE REPORT, AND THE PLACEMENT IS THE WHOLE +/// DECISION.** This landed once inside [`diagnose`] and was refused by `verify`: +/// `crates/batten/tests/it/doctor.rs::this_repository_is_healthy` went red +/// because `land` had rebuilt `target/release/batten` while the installed copy +/// was an hour old. The check was telling the truth. Whether a container's +/// install is current is a property of the WORLD, and bare `doctor` answers a +/// property of the COMMIT — so folding it in made a commit gate answer on install +/// recency. `.claude/rules/toolchain.md` records that exact defect for +/// `lock-check`, whose remedy was the same split: the pure gate keeps its +/// question, the world-fact gets its own caller. House style §2 already specifies +/// `doctor ` for a focused diagnostic, so the shape was available. +/// +/// **Content, never the version string, correcting this row's own §2.** Measured: +/// `batten --version` read `0.0.137`, the workspace read `0.0.137`, and that +/// binary refused the tree's own `batten.toml` with `unknown field +/// endpoint_contains`. A config surface moves without a version bump, and on a +/// fast-forward-only trunk that is the ordinary case, so version equality does +/// not discriminate. The digest catches that and every case a version would. +/// +/// **Nothing is executed.** `doctor` is `Effect::Read` and the agent allowlist is +/// `filter(effect == read)` with no second list, so spawning a program named by a +/// wiring file would put config-supplied code behind a row any consumer's agent +/// may call — CLOUD-170's actual invariant, and the reason [`on_path`] stats +/// rather than runs. Hashing a file reaches none of it. +#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)] +#[serde(rename_all = "kebab-case", tag = "state")] +pub enum Mediator { + /// The resolved binary is byte-identical to this tree's build. + Current, + /// Both were read and they differ. + Stale, + /// This tree does not build a mediator, so there is nothing to compare. + /// + /// Distinct from the two below: a consumer checkout is not a failed lookup, + /// it is a question with no referent. + NotApplicable, + /// This tree builds one, but nothing named `batten` resolves on `PATH`. + Unresolvable, + /// This tree builds one and the built artifact could not be read. + Unbuilt, +} + +impl Mediator { + /// The pointer line this renders as, without a trailing newline. + #[must_use] + pub const fn line(&self) -> &'static str { + match self { + Mediator::Current => "mediator ok", + Mediator::Stale => "mediator failed mediator-stale", + Mediator::NotApplicable => "mediator ok not-applicable", + Mediator::Unresolvable => "mediator failed mediator-unresolvable", + Mediator::Unbuilt => "mediator failed mediator-unbuilt", + } + } + + /// The exit code this maps to. + /// + /// [`ExitCode::Violation`] is unreachable, inheriting the promise the parent + /// makes: a mediating harness reads `2` as a deny, and "your install is out + /// of date" is not "policy says no". + #[must_use] + pub const fn code(&self) -> ExitCode { + match self { + Mediator::Current | Mediator::NotApplicable => ExitCode::Success, + Mediator::Stale | Mediator::Unresolvable | Mediator::Unbuilt => ExitCode::Usage, + } + } +} + +/// Compare the mediator on `PATH` against the artifact `dir` builds. +/// +/// Reads both files and hashes them; spawns nothing. Length is compared first +/// only as a short-circuit — two files of different lengths cannot be identical. +#[must_use] +pub fn diagnose_mediator(dir: &Path) -> Mediator { + // The tree builds a mediator iff it carries the crate that produces one. + // Asking the manifest rather than looking for the artifact keeps "a consumer + // checkout" and "batten's own checkout before its first build" distinct: the + // second is a could-not-look and the first is not a question at all. + if !dir.join("crates/batten/Cargo.toml").is_file() { + return Mediator::NotApplicable; + } + let Some(resolved) = crate::rules::on_path_verbatim("batten") else { + return Mediator::Unresolvable; + }; + let built = dir.join("target/release/batten"); + let (Ok(left), Ok(right)) = (std::fs::read(&resolved), std::fs::read(&built)) else { + return Mediator::Unbuilt; + }; + if left.len() != right.len() { + return Mediator::Stale; + } + if crate::receipt::hex_sha256(&left) == crate::receipt::hex_sha256(&right) { + Mediator::Current + } else { + Mediator::Stale + } +} + /// Whether `program` resolves to an existing file on `PATH`. /// /// **Stats, never executes.** Running the program to see whether it exists is diff --git a/crates/batten/src/lib.rs b/crates/batten/src/lib.rs index ea6212de9..52087fef8 100644 --- a/crates/batten/src/lib.rs +++ b/crates/batten/src/lib.rs @@ -12589,10 +12589,34 @@ fn run_doctor(command: &cli::DoctorCommand, out: &mut dyn Write) -> Result run_diagnose(json, out), cli::DoctorCommand::Hooks { json } => run_doctor_hooks(json, out), + cli::DoctorCommand::Mediator { json } => run_doctor_mediator(json, out), cli::DoctorCommand::Session { json } => run_doctor_session(json, out), } } +/// Was the engine the registrations reach built from this tree (CLOUD-1349)? +/// +/// `doctor hooks` answers whether the registrations reach an engine; this answers +/// WHICH one, and the gap between those two questions is where a session spends +/// six hours believing it is mediated. Rationale, and why it is a sub-verb rather +/// than a fourth check in the bare report, on [`doctor::Mediator`]. +/// +/// One pointer line, never a digest: a hash is stable per content but varies per +/// machine, so emitting one would defeat the byte-stability §6 requires of this +/// verb's output while telling the reader nothing they can act on. The remedy is +/// `mise run install:local` and the verdict is what says whether to run it. +fn run_doctor_mediator(json: bool, out: &mut dyn Write) -> Result { + let report = doctor::diagnose_mediator(Path::new(".")); + if json { + // A data channel emits its document unconditionally, including when the + // mediator is current: JSON that is sometimes absent is unparseable. + writeln!(out, "{}", serde_json::to_string_pretty(&report)?)?; + } else { + writeln!(out, "{}", report.line())?; + } + Ok(report.code()) +} + fn run_diagnose(json: bool, out: &mut dyn Write) -> Result { let report = doctor::diagnose(Path::new(".")); if json { diff --git a/crates/batten/src/surface.rs b/crates/batten/src/surface.rs index 22b0f9111..4f1c21c53 100644 --- a/crates/batten/src/surface.rs +++ b/crates/batten/src/surface.rs @@ -2347,6 +2347,30 @@ pub const SURFACE: &[CommandDecl] = &[ // `read`, and structurally: it reads committed wiring files and compares them // against a derivation computed in-process. Nothing is spawned, and §2's own // row already classifies the verb this way. + // WHICH engine the registrations reach, where `doctor hooks` answers whether + // they reach one at all (CLOUD-1349). It reported `5 harness(es), 0 unwired` + // over a binary 16 versions behind the tree it was adjudicating. + // + // A SUB-VERB BECAUSE THE SUBJECT IS THE WORLD, NOT THE CHECKOUT. This landed + // once as a fourth check inside `diagnose()` and `verify` refused it: bare + // `doctor` is asserted green over this repository by a compiled-binary case, + // and an install that has not caught up with a rebuild made that case a + // function of install recency. `.claude/rules/toolchain.md` states the rule + // from `lock-check`'s post-mortem — a property of the commit belongs in the + // gate, a property of the world belongs to its own caller — and §2's + // `doctor ` is the shape that was already specified for it. + // + // `read`, and structurally: it reads two files and hashes them. Nothing is + // spawned, which is what keeps it off the wrong side of CLOUD-170 while + // sitting on the `filter(effect == read)` allowlist. + CommandDecl { + path: "doctor mediator", + id: "doctor.mediator", + about: "Diagnose whether the engine the registrations reach was built from this tree", + data_channel: true, + effect: Effect::Read, + flags: &[JSON], + }, CommandDecl { path: "doctor hooks", id: "doctor.hooks", From 2324b6700b9833c39daaec7b56529a5a1d99c0df Mon Sep 17 00:00:00 2001 From: Alec Wenzowski Date: Wed, 2 Sep 2026 21:52:37 +0000 Subject: [PATCH 02/13] test(doctor): pin `doctor mediator`, including the arm that refutes its old placement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ten cases over the compiled binary. The load-bearing ones: `a_mediator_built_from_this_tree_passes` is the anti-vacuity mirror — identical bytes, same fixture shape, same PATH — because a check that refused unconditionally would satisfy the stale case exactly as well. `equal_length_binaries_that_differ_are_still_refused` pins that the length compare is a short-circuit and not the predicate. Two builds of one source at the same length is the ordinary case for a changed constant, which is also the drift hardest to notice by eye. `the_bare_report_is_unchanged_by_this_sub_verb` is the regression this verb exists as a sub-verb to avoid. An earlier revision put the comparison in `diagnose()`'s check list and `this_repository_is_healthy` went red whenever a rebuild had outpaced the install — a world-property deciding a commit gate. The two could-not-look causes are asserted apart: nothing to compare AGAINST and nothing to compare WITH have different remedies, so one reason id for both would send the reader to the wrong place. Fixtures execute nothing, so neither planted file needs to be a real program — which is what keeps the whole tier at 0.2s. Refs: CLOUD-1349 --- crates/batten/tests/it/doctor.rs | 177 +++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) diff --git a/crates/batten/tests/it/doctor.rs b/crates/batten/tests/it/doctor.rs index 572886b4a..408354a7f 100644 --- a/crates/batten/tests/it/doctor.rs +++ b/crates/batten/tests/it/doctor.rs @@ -54,6 +54,183 @@ fn doctor(dir: &Path, extra: &[&str]) -> Output { .expect("run batten doctor") } +// --- doctor mediator: WHICH engine the registrations reach (CLOUD-1349) ------- + +/// A checkout that builds a mediator, with `batten` on `PATH` resolving to +/// `planted`. +/// +/// The whole fixture is two files plus a `PATH` reaching one of them: the +/// comparison is over CONTENT, so a case only has to control what those files +/// hold. Nothing is executed, so neither needs to be a real program — which is +/// also what keeps these cases fast and portable. +fn mediator_fixture(name: &str, planted: &[u8], built: &[u8]) -> PathBuf { + let dir = scratch(name, true, Some("version = 1\n")); + // The manifest is what says "this tree builds a mediator". Its contents are + // never parsed — only its existence decides the question is askable — so the + // marker is deliberately minimal. + fs::create_dir_all(dir.join("crates/batten")).unwrap(); + fs::write(dir.join("crates/batten/Cargo.toml"), "# marker\n").unwrap(); + fs::create_dir_all(dir.join("target/release")).unwrap(); + fs::write(dir.join("target/release/batten"), built).unwrap(); + let bin = dir.join("planted-bin"); + fs::create_dir_all(&bin).unwrap(); + fs::write(bin.join("batten"), planted).unwrap(); + dir +} + +fn mediator(dir: &Path, bin: Option<&Path>, extra: &[&str]) -> Output { + let mut command = batten(); + command.arg("doctor").arg("mediator"); + command.args(extra); + if let Some(bin) = bin { + command.env("PATH", bin); + } + command + .current_dir(dir) + .env_remove("BATTEN_STRICTNESS") + .env_remove("BATTEN_FAIL_ON_WARNING") + .env_remove("BATTEN_CONFIG_FROM") + .output() + .expect("run batten doctor mediator") +} + +#[test] +fn a_mediator_that_is_not_this_trees_build_is_refused() { + // The measured failure reduced to its decidable core: the binary answering + // calls is not the one this source produces. No version appears in the + // fixture at all, because the version is exactly what could NOT tell these + // two apart in the field — both sides read 0.0.137 while one of them refused + // the tree's own config. + let dir = mediator_fixture("mediator-stale", b"an older build", b"this tree's build"); + let output = mediator(&dir, Some(&dir.join("planted-bin")), &[]); + assert_eq!(output.status.code(), Some(1)); + assert_eq!(stdout(&output), "mediator failed mediator-stale\n"); +} + +#[test] +fn a_mediator_built_from_this_tree_passes() { + // THE ANTI-VACUITY MIRROR, and it is what makes the case above mean + // anything: a check that refused unconditionally would satisfy that one + // exactly as well. Same fixture shape, same PATH, identical bytes. + let dir = mediator_fixture("mediator-current", b"same bytes", b"same bytes"); + let output = mediator(&dir, Some(&dir.join("planted-bin")), &[]); + assert_eq!(output.status.code(), Some(0)); + assert_eq!(stdout(&output), "mediator ok\n"); +} + +#[test] +fn equal_length_binaries_that_differ_are_still_refused() { + // The length compare is a short-circuit, never the predicate. Two builds of + // the same source at the same length is the ordinary case for a change to a + // constant, so a check that stopped at the length would pass over precisely + // the drift hardest to notice by eye. + let dir = mediator_fixture("mediator-same-length", b"aaaaaaaaaa", b"bbbbbbbbbb"); + let output = mediator(&dir, Some(&dir.join("planted-bin")), &[]); + assert_eq!(output.status.code(), Some(1)); + assert_eq!(stdout(&output), "mediator failed mediator-stale\n"); +} + +#[test] +fn a_tree_that_builds_no_mediator_abstains_rather_than_refusing() { + // A consumer checkout never builds one, so "was this built from this tree" + // has no referent there. Abstaining is the honest answer; refusing would + // redden every consumer over a question that does not apply to them — and it + // is reported as `not-applicable` rather than as a bare ok, so abstention is + // legible rather than indistinguishable from a real comparison. + let dir = scratch("mediator-not-applicable", true, Some("version = 1\n")); + let output = mediator(&dir, None, &[]); + assert_eq!(output.status.code(), Some(0)); + assert_eq!(stdout(&output), "mediator ok not-applicable\n"); +} + +#[test] +fn a_tree_that_builds_one_but_has_not_is_could_not_look_never_clean() { + // The distinction the two named variants exist to keep: this tree SHOULD + // have an artifact to compare and does not, which is a different claim from + // a consumer checkout that never had one. Reading it as clean is the exact + // shape — an unanswerable question passing — this verb is against. + let dir = scratch("mediator-unbuilt", true, Some("version = 1\n")); + fs::create_dir_all(dir.join("crates/batten")).unwrap(); + fs::write(dir.join("crates/batten/Cargo.toml"), "# marker\n").unwrap(); + let bin = dir.join("planted-bin"); + fs::create_dir_all(&bin).unwrap(); + fs::write(bin.join("batten"), b"whatever").unwrap(); + let output = mediator(&dir, Some(&bin), &[]); + assert_eq!(output.status.code(), Some(1)); + assert_eq!(stdout(&output), "mediator failed mediator-unbuilt\n"); +} + +#[test] +fn a_tree_that_builds_one_with_no_mediator_on_path_is_could_not_look() { + // Distinct from unbuilt for the same reason `NotAcquired` keeps `Absent` and + // `Unparsed` apart: nothing to compare AGAINST and nothing to compare WITH + // have different remedies, so one reason id for both sends the reader to the + // wrong place. + let dir = mediator_fixture("mediator-unresolvable", b"planted", b"built"); + let empty = dir.join("empty-bin"); + fs::create_dir_all(&empty).unwrap(); + let output = mediator(&dir, Some(&empty), &[]); + assert_eq!(output.status.code(), Some(1)); + assert_eq!(stdout(&output), "mediator failed mediator-unresolvable\n"); +} + +#[test] +fn the_verdict_carries_no_path_and_no_digest() { + // §6 and rule 4 over this verb specifically: it is about two absolute paths + // and two hashes, which is the shape most likely to leak one into output. + // A digest is stable per content but varies per machine, so emitting one + // would defeat byte-stability while telling the reader nothing actionable. + let dir = mediator_fixture("mediator-no-path", b"older", b"newer"); + let text = stdout(&mediator(&dir, Some(&dir.join("planted-bin")), &[])); + assert!(!text.contains('/'), "the verdict carried a path: {text}"); + assert!( + !text + .chars() + .any(|c| c.is_ascii_hexdigit() && !c.is_ascii_alphabetic()), + "the verdict carried a digest: {text}" + ); +} + +#[test] +fn the_data_channel_emits_a_document_even_when_current() { + // A data channel emits unconditionally: JSON that is sometimes absent is + // unparseable. Asserted on the passing arm because that is the one a caller + // is tempted to make silent. + let dir = mediator_fixture("mediator-json", b"same", b"same"); + let output = mediator(&dir, Some(&dir.join("planted-bin")), &["--json"]); + assert_eq!(output.status.code(), Some(0)); + let report: serde_json::Value = + serde_json::from_slice(&output.stdout).expect("the verdict is JSON"); + assert_eq!(report["state"], "current"); +} + +#[test] +fn the_sub_verb_never_renders_a_policy_verdict() { + // A sub-verb inherits the promise the parent makes: a mediating harness + // reads `2` as a deny, and "your install is out of date" is not "policy says + // no". Every failing arm above asserts 1; this pins that 2 is unreachable. + let dir = mediator_fixture("mediator-never-two", b"older", b"newer"); + let output = mediator(&dir, Some(&dir.join("planted-bin")), &[]); + assert_ne!(output.status.code(), Some(2)); +} + +#[test] +fn the_bare_report_is_unchanged_by_this_sub_verb() { + // THE REGRESSION THIS VERB EXISTS AS A SUB-VERB TO AVOID. An earlier revision + // put the comparison in `diagnose()`'s check list, and `this_repository_is_healthy` + // went red whenever a rebuild had outpaced the install — a world-property + // deciding a commit gate (`.claude/rules/toolchain.md`, from `lock-check`). + // Bare `doctor` must not mention the mediator at all. + let dir = mediator_fixture("mediator-bare-unchanged", b"older", b"newer"); + let output = doctor(&dir, &[]); + let text = stdout(&output); + assert!( + !text.contains("mediator"), + "the bare report grew it: {text}" + ); + assert_eq!(output.status.code(), Some(0)); +} + // --- the diagnosis ----------------------------------------------------------- #[test] From 33d79533590853adc23aaaa30e06b863fed7c4e0 Mon Sep 17 00:00:00 2001 From: Alec Wenzowski Date: Wed, 2 Sep 2026 21:57:03 +0000 Subject: [PATCH 03/13] chore(surface): regenerate the derived artifacts for `doctor mediator` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Completions, the parent's man page and a new `batten-doctor-mediator.1`, all generated by `mise run schema`, `completions` and `man`. No hand edits — `the_committed_pages_are_the_ones_the_binary_emits` is what would catch one. Refs: CLOUD-1349 --- completions/batten.bash | 24 +++++------ completions/batten.fish | 84 ++++++++++++++++++------------------ completions/batten.zsh | 36 ++++++++-------- man/batten-doctor-mediator.1 | 16 +++++++ man/batten-doctor.1 | 3 ++ 5 files changed, 91 insertions(+), 72 deletions(-) create mode 100644 man/batten-doctor-mediator.1 diff --git a/completions/batten.bash b/completions/batten.bash index 3998021c3..0e386a90c 100644 --- a/completions/batten.bash +++ b/completions/batten.bash @@ -292,8 +292,8 @@ _batten() { batten__subcmd__doctor,hooks) cmd="batten__subcmd__doctor__subcmd__hooks" ;; - batten__subcmd__doctor,session) - cmd="batten__subcmd__doctor__subcmd__session" + batten__subcmd__doctor,mediator) + cmd="batten__subcmd__doctor__subcmd__mediator" ;; batten__subcmd__doctor__subcmd__help,help) cmd="batten__subcmd__doctor__subcmd__help__subcmd__help" @@ -301,8 +301,8 @@ _batten() { batten__subcmd__doctor__subcmd__help,hooks) cmd="batten__subcmd__doctor__subcmd__help__subcmd__hooks" ;; - batten__subcmd__doctor__subcmd__help,session) - cmd="batten__subcmd__doctor__subcmd__help__subcmd__session" + batten__subcmd__doctor__subcmd__help,mediator) + cmd="batten__subcmd__doctor__subcmd__help__subcmd__mediator" ;; batten__subcmd__generate,completions) cmd="batten__subcmd__generate__subcmd__completions" @@ -511,8 +511,8 @@ _batten() { batten__subcmd__help__subcmd__doctor,hooks) cmd="batten__subcmd__help__subcmd__doctor__subcmd__hooks" ;; - batten__subcmd__help__subcmd__doctor,session) - cmd="batten__subcmd__help__subcmd__doctor__subcmd__session" + batten__subcmd__help__subcmd__doctor,mediator) + cmd="batten__subcmd__help__subcmd__doctor__subcmd__mediator" ;; batten__subcmd__help__subcmd__generate,completions) cmd="batten__subcmd__help__subcmd__generate__subcmd__completions" @@ -2553,7 +2553,7 @@ _batten() { return 0 ;; batten__subcmd__doctor) - opts="-J -q -v -y -h --json --strictness --fail-on-warning --config-from --config-in --silent --quiet --verbose --debug --trace --log-level --no-color --no-input --yes --help hooks session help" + opts="-J -q -v -y -h --json --strictness --fail-on-warning --config-from --config-in --silent --quiet --verbose --debug --trace --log-level --no-color --no-input --yes --help mediator hooks help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -2583,7 +2583,7 @@ _batten() { return 0 ;; batten__subcmd__doctor__subcmd__help) - opts="hooks session help" + opts="mediator hooks help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -2624,7 +2624,7 @@ _batten() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; - batten__subcmd__doctor__subcmd__help__subcmd__session) + batten__subcmd__doctor__subcmd__help__subcmd__mediator) opts="" if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) @@ -2668,7 +2668,7 @@ _batten() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; - batten__subcmd__doctor__subcmd__session) + batten__subcmd__doctor__subcmd__mediator) opts="-J -q -v -y -h --json --strictness --fail-on-warning --config-from --config-in --silent --quiet --verbose --debug --trace --log-level --no-color --no-input --yes --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) @@ -3471,7 +3471,7 @@ _batten() { return 0 ;; batten__subcmd__help__subcmd__doctor) - opts="hooks session" + opts="mediator hooks" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -3498,7 +3498,7 @@ _batten() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; - batten__subcmd__help__subcmd__doctor__subcmd__session) + batten__subcmd__help__subcmd__doctor__subcmd__mediator) opts="" if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) diff --git a/completions/batten.fish b/completions/batten.fish index cad2e3903..a67d81dd6 100644 --- a/completions/batten.fish +++ b/completions/batten.fish @@ -579,31 +579,53 @@ complete -c batten -n "__fish_batten_using_subcommand spec" -l no-color -d 'Neve complete -c batten -n "__fish_batten_using_subcommand spec" -l no-input -d 'Never prompt; treat the run as unattended' complete -c batten -n "__fish_batten_using_subcommand spec" -s y -l yes -d 'Confirm a destructive operation that would otherwise refuse' complete -c batten -n "__fish_batten_using_subcommand spec" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -l strictness -d 'Raise how strictly gates apply (an override may only tighten policy)' -r -f -a "permissive\t'Advisory: findings are reported without failing the run' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l strictness -d 'Raise how strictly gates apply (an override may only tighten policy)' -r -f -a "permissive\t'Advisory: findings are reported without failing the run' standard\t'The default: a finding is a violation' strict\t'Everything `Standard` fails on, plus anything advisory'" -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -l config-from -d 'Read the committed config from a git ref (e.g. origin/main) instead of the working tree' -r -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -l config-in -d 'Read the committed config from this directory instead of the directory being judged' -r -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -l log-level -d 'Set the verbosity rung by name' -r -f -a "silent\t'Say nothing but a verdict or a usage error' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l config-from -d 'Read the committed config from a git ref (e.g. origin/main) instead of the working tree' -r +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l config-in -d 'Read the committed config from this directory instead of the directory being judged' -r +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l log-level -d 'Set the verbosity rung by name' -r -f -a "silent\t'Say nothing but a verdict or a usage error' quiet\t'Suppress ordinary progress; keep warnings' normal\t'The default' verbose\t'Explain what is being checked' debug\t'Add resolution detail' trace\t'Add everything'" -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -s J -l json -d 'Emit byte-stable JSON instead of pointer lines' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -l fail-on-warning -d 'Promote a warn-severity finding to a violation (an override may only turn this on)' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -l silent -d 'Say nothing but a verdict or a usage error' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -s q -l quiet -d 'Suppress ordinary progress (repeatable: -qq is silent)' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -s v -l verbose -d 'Explain what is being checked (repeatable: -vv is debug)' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -l debug -d 'Add resolution detail' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -l trace -d 'Add everything' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -l no-color -d 'Never colour stderr, whatever it is attached to' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -l no-input -d 'Never prompt; treat the run as unattended' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -s y -l yes -d 'Confirm a destructive operation that would otherwise refuse' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -f -a "hooks" -d 'Diagnose whether batten is wired on every hook surface of every harness' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -f -a "session" -d 'Diagnose whether this session has declared work it has not finished' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from hooks session help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -s J -l json -d 'Emit byte-stable JSON instead of pointer lines' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l fail-on-warning -d 'Promote a warn-severity finding to a violation (an override may only turn this on)' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l silent -d 'Say nothing but a verdict or a usage error' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -s q -l quiet -d 'Suppress ordinary progress (repeatable: -qq is silent)' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -s v -l verbose -d 'Explain what is being checked (repeatable: -vv is debug)' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l debug -d 'Add resolution detail' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l trace -d 'Add everything' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l no-color -d 'Never colour stderr, whatever it is attached to' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l no-input -d 'Never prompt; treat the run as unattended' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -s y -l yes -d 'Confirm a destructive operation that would otherwise refuse' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -f -a "mediator" -d 'Diagnose whether the engine the registrations reach was built from this tree' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -f -a "hooks" -d 'Diagnose whether batten is wired on every hook surface of every harness' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from mediator" -l strictness -d 'Raise how strictly gates apply (an override may only tighten policy)' -r -f -a "permissive\t'Advisory: findings are reported without failing the run' +standard\t'The default: a finding is a violation' +strict\t'Everything `Standard` fails on, plus anything advisory'" +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from mediator" -l config-from -d 'Read the committed config from a git ref (e.g. origin/main) instead of the working tree' -r +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from mediator" -l config-in -d 'Read the committed config from this directory instead of the directory being judged' -r +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from mediator" -l log-level -d 'Set the verbosity rung by name' -r -f -a "silent\t'Say nothing but a verdict or a usage error' +quiet\t'Suppress ordinary progress; keep warnings' +normal\t'The default' +verbose\t'Explain what is being checked' +debug\t'Add resolution detail' +trace\t'Add everything'" +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from mediator" -s J -l json -d 'Emit byte-stable JSON instead of pointer lines' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from mediator" -l fail-on-warning -d 'Promote a warn-severity finding to a violation (an override may only turn this on)' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from mediator" -l silent -d 'Say nothing but a verdict or a usage error' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from mediator" -s q -l quiet -d 'Suppress ordinary progress (repeatable: -qq is silent)' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from mediator" -s v -l verbose -d 'Explain what is being checked (repeatable: -vv is debug)' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from mediator" -l debug -d 'Add resolution detail' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from mediator" -l trace -d 'Add everything' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from mediator" -l no-color -d 'Never colour stderr, whatever it is attached to' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from mediator" -l no-input -d 'Never prompt; treat the run as unattended' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from mediator" -s y -l yes -d 'Confirm a destructive operation that would otherwise refuse' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from mediator" -s h -l help -d 'Print help (see more with \'--help\')' complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from hooks" -l strictness -d 'Raise how strictly gates apply (an override may only tighten policy)' -r -f -a "permissive\t'Advisory: findings are reported without failing the run' standard\t'The default: a finding is a violation' strict\t'Everything `Standard` fails on, plus anything advisory'" @@ -626,30 +648,8 @@ complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_su complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from hooks" -l no-input -d 'Never prompt; treat the run as unattended' complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from hooks" -s y -l yes -d 'Confirm a destructive operation that would otherwise refuse' complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from hooks" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l strictness -d 'Raise how strictly gates apply (an override may only tighten policy)' -r -f -a "permissive\t'Advisory: findings are reported without failing the run' -standard\t'The default: a finding is a violation' -strict\t'Everything `Standard` fails on, plus anything advisory'" -complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l config-from -d 'Read the committed config from a git ref (e.g. origin/main) instead of the working tree' -r -complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l config-in -d 'Read the committed config from this directory instead of the directory being judged' -r -complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l log-level -d 'Set the verbosity rung by name' -r -f -a "silent\t'Say nothing but a verdict or a usage error' -quiet\t'Suppress ordinary progress; keep warnings' -normal\t'The default' -verbose\t'Explain what is being checked' -debug\t'Add resolution detail' -trace\t'Add everything'" -complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -s J -l json -d 'Emit byte-stable JSON instead of pointer lines' -complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l fail-on-warning -d 'Promote a warn-severity finding to a violation (an override may only turn this on)' -complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l silent -d 'Say nothing but a verdict or a usage error' -complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -s q -l quiet -d 'Suppress ordinary progress (repeatable: -qq is silent)' -complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -s v -l verbose -d 'Explain what is being checked (repeatable: -vv is debug)' -complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l debug -d 'Add resolution detail' -complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l trace -d 'Add everything' -complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l no-color -d 'Never colour stderr, whatever it is attached to' -complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l no-input -d 'Never prompt; treat the run as unattended' -complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -s y -l yes -d 'Confirm a destructive operation that would otherwise refuse' -complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from help" -f -a "mediator" -d 'Diagnose whether the engine the registrations reach was built from this tree' complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from help" -f -a "hooks" -d 'Diagnose whether batten is wired on every hook surface of every harness' -complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from help" -f -a "session" -d 'Diagnose whether this session has declared work it has not finished' complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c batten -n "__fish_batten_using_subcommand init" -l strictness -d 'Raise how strictly gates apply (an override may only tighten policy)' -r -f -a "permissive\t'Advisory: findings are reported without failing the run' standard\t'The default: a finding is a violation' @@ -2919,8 +2919,8 @@ complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subc complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from config" -f -a "deprecations" -d 'Report schema keys removed since a published release with no deprecation window' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from config" -f -a "lint" -d 'Report policy smells in batten.toml (any smell is a violation)' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from lint" -f -a "brief" -d 'Check a delegation brief against the handoff schema (any missing section is a violation)' +complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from doctor" -f -a "mediator" -d 'Diagnose whether the engine the registrations reach was built from this tree' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from doctor" -f -a "hooks" -d 'Diagnose whether batten is wired on every hook surface of every harness' -complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from doctor" -f -a "session" -d 'Diagnose whether this session has declared work it has not finished' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from generate" -f -a "completions" -d 'Emit the shell completion script for one shell' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from generate" -f -a "hooks" -d 'Emit one harness\'s hook registrations, on stdout' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from generate" -f -a "man" -d 'Emit the roff man page for one command, on stdout' diff --git a/completions/batten.zsh b/completions/batten.zsh index 24b4d48b8..38aedce1d 100644 --- a/completions/batten.zsh +++ b/completions/batten.zsh @@ -937,7 +937,7 @@ trace\:"Add everything"))' \ (( CURRENT += 1 )) curcontext="${curcontext%:*:*}:batten-doctor-command-$line[1]:" case $line[1] in - (hooks) + (mediator) _arguments "${_arguments_options[@]}" : \ '--strictness=[Raise how strictly gates apply (an override may only tighten policy)]: :((permissive\:"Advisory\: findings are reported without failing the run" standard\:"The default\: a finding is a violation" @@ -968,7 +968,7 @@ trace\:"Add everything"))' \ '--help[Print help (see more with '\''--help'\'')]' \ && ret=0 ;; -(session) +(hooks) _arguments "${_arguments_options[@]}" : \ '--strictness=[Raise how strictly gates apply (an override may only tighten policy)]: :((permissive\:"Advisory\: findings are reported without failing the run" standard\:"The default\: a finding is a violation" @@ -1011,11 +1011,11 @@ _arguments "${_arguments_options[@]}" : \ (( CURRENT += 1 )) curcontext="${curcontext%:*:*}:batten-doctor-help-command-$line[1]:" case $line[1] in - (hooks) + (mediator) _arguments "${_arguments_options[@]}" : \ && ret=0 ;; -(session) +(hooks) _arguments "${_arguments_options[@]}" : \ && ret=0 ;; @@ -5013,11 +5013,11 @@ _arguments "${_arguments_options[@]}" : \ (( CURRENT += 1 )) curcontext="${curcontext%:*:*}:batten-help-doctor-command-$line[1]:" case $line[1] in - (hooks) + (mediator) _arguments "${_arguments_options[@]}" : \ && ret=0 ;; -(session) +(hooks) _arguments "${_arguments_options[@]}" : \ && ret=0 ;; @@ -6107,8 +6107,8 @@ _batten__subcmd__design__subcmd__help__subcmd__help_commands() { (( $+functions[_batten__subcmd__doctor_commands] )) || _batten__subcmd__doctor_commands() { local commands; commands=( +'mediator:Diagnose whether the engine the registrations reach was built from this tree' \ 'hooks:Diagnose whether batten is wired on every hook surface of every harness' \ -'session:Diagnose whether this session has declared work it has not finished' \ 'help:Print this message or the help of the given subcommand(s)' \ ) _describe -t commands 'batten doctor commands' commands "$@" @@ -6116,8 +6116,8 @@ _batten__subcmd__doctor_commands() { (( $+functions[_batten__subcmd__doctor__subcmd__help_commands] )) || _batten__subcmd__doctor__subcmd__help_commands() { local commands; commands=( +'mediator:Diagnose whether the engine the registrations reach was built from this tree' \ 'hooks:Diagnose whether batten is wired on every hook surface of every harness' \ -'session:Diagnose whether this session has declared work it has not finished' \ 'help:Print this message or the help of the given subcommand(s)' \ ) _describe -t commands 'batten doctor help commands' commands "$@" @@ -6132,20 +6132,20 @@ _batten__subcmd__doctor__subcmd__help__subcmd__hooks_commands() { local commands; commands=() _describe -t commands 'batten doctor help hooks commands' commands "$@" } -(( $+functions[_batten__subcmd__doctor__subcmd__help__subcmd__session_commands] )) || -_batten__subcmd__doctor__subcmd__help__subcmd__session_commands() { +(( $+functions[_batten__subcmd__doctor__subcmd__help__subcmd__mediator_commands] )) || +_batten__subcmd__doctor__subcmd__help__subcmd__mediator_commands() { local commands; commands=() - _describe -t commands 'batten doctor help session commands' commands "$@" + _describe -t commands 'batten doctor help mediator commands' commands "$@" } (( $+functions[_batten__subcmd__doctor__subcmd__hooks_commands] )) || _batten__subcmd__doctor__subcmd__hooks_commands() { local commands; commands=() _describe -t commands 'batten doctor hooks commands' commands "$@" } -(( $+functions[_batten__subcmd__doctor__subcmd__session_commands] )) || -_batten__subcmd__doctor__subcmd__session_commands() { +(( $+functions[_batten__subcmd__doctor__subcmd__mediator_commands] )) || +_batten__subcmd__doctor__subcmd__mediator_commands() { local commands; commands=() - _describe -t commands 'batten doctor session commands' commands "$@" + _describe -t commands 'batten doctor mediator commands' commands "$@" } (( $+functions[_batten__subcmd__enforce_commands] )) || _batten__subcmd__enforce_commands() { @@ -6449,8 +6449,8 @@ _batten__subcmd__help__subcmd__design__subcmd__audit_commands() { (( $+functions[_batten__subcmd__help__subcmd__doctor_commands] )) || _batten__subcmd__help__subcmd__doctor_commands() { local commands; commands=( +'mediator:Diagnose whether the engine the registrations reach was built from this tree' \ 'hooks:Diagnose whether batten is wired on every hook surface of every harness' \ -'session:Diagnose whether this session has declared work it has not finished' \ ) _describe -t commands 'batten help doctor commands' commands "$@" } @@ -6459,10 +6459,10 @@ _batten__subcmd__help__subcmd__doctor__subcmd__hooks_commands() { local commands; commands=() _describe -t commands 'batten help doctor hooks commands' commands "$@" } -(( $+functions[_batten__subcmd__help__subcmd__doctor__subcmd__session_commands] )) || -_batten__subcmd__help__subcmd__doctor__subcmd__session_commands() { +(( $+functions[_batten__subcmd__help__subcmd__doctor__subcmd__mediator_commands] )) || +_batten__subcmd__help__subcmd__doctor__subcmd__mediator_commands() { local commands; commands=() - _describe -t commands 'batten help doctor session commands' commands "$@" + _describe -t commands 'batten help doctor mediator commands' commands "$@" } (( $+functions[_batten__subcmd__help__subcmd__enforce_commands] )) || _batten__subcmd__help__subcmd__enforce_commands() { diff --git a/man/batten-doctor-mediator.1 b/man/batten-doctor-mediator.1 new file mode 100644 index 000000000..f35db61ec --- /dev/null +++ b/man/batten-doctor-mediator.1 @@ -0,0 +1,16 @@ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.TH batten-doctor-mediator 1 batten +.SH NAME +batten\-doctor\-mediator \- Diagnose whether the engine the registrations reach was built from this tree +.SH SYNOPSIS +\fBbatten doctor mediator\fR [\fB\-J\fR|\fB\-\-json\fR] [\fB\-h\fR|\fB\-\-help\fR] +.SH DESCRIPTION +Diagnose whether the engine the registrations reach was built from this tree +.SH OPTIONS +.TP +\fB\-J\fR, \fB\-\-json\fR +Emit byte\-stable JSON instead of pointer lines +.TP +\fB\-h\fR, \fB\-\-help\fR +Print help diff --git a/man/batten-doctor.1 b/man/batten-doctor.1 index 8d4302e8d..7d748445c 100644 --- a/man/batten-doctor.1 +++ b/man/batten-doctor.1 @@ -16,6 +16,9 @@ Emit byte\-stable JSON instead of pointer lines Print help .SH SUBCOMMANDS .TP +batten\-doctor\-mediator(1) +Diagnose whether the engine the registrations reach was built from this tree +.TP batten\-doctor\-hooks(1) Diagnose whether batten is wired on every hook surface of every harness .TP From e99be3f513810e31bd3eee27a8b75fef26caec0d Mon Sep 17 00:00:00 2001 From: Alec Wenzowski Date: Wed, 2 Sep 2026 22:25:26 +0000 Subject: [PATCH 04/13] feat(spec): admit `doctor mediator` to the read-only allowlist and the row set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both literals are hand-maintained and that is the design: a command joining the agent allowlist is a safety-critical edit, so it costs a deliberate line and a written reason rather than arriving in a generated diff. `verify` refused the branch until both were written. The reason here is stricter than its sibling's. `doctor mediator` resolves a program name and then deliberately does NOT run it, comparing that file's bytes against the artifact this tree builds. Running what a wiring file names would put config-supplied code behind a row on this very list — CLOUD-170's invariant, and why `on_path` stats rather than executes. Refs: CLOUD-1349 --- crates/batten/src/spec.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/batten/src/spec.rs b/crates/batten/src/spec.rs index 56d32e89b..17f113d48 100644 --- a/crates/batten/src/spec.rs +++ b/crates/batten/src/spec.rs @@ -451,6 +451,15 @@ mod tests { // spawn nothing: the sub-verb compares each harness's wiring // against a derivation computed in-process. "doctor hooks".to_owned(), + // WHICH engine the registrations reach, where the row above + // answers whether they reach one at all (CLOUD-1349). Read for a + // stricter reason than its sibling: it resolves a program name + // and then deliberately does NOT run it, comparing the file's + // bytes against the artifact this tree builds. Spawning what a + // wiring file names would put config-supplied code behind a row + // on this very allowlist, which is CLOUD-170's invariant and the + // reason `on_path` stats rather than executes. + "doctor mediator".to_owned(), // `read`, and structurally, for the sub-verb above's reason: it // opens the session's own task store through a link the engine // parked and counts what is not `completed`. It spawns nothing, @@ -685,6 +694,7 @@ mod tests { "design audit".to_owned(), "doctor".to_owned(), "doctor hooks".to_owned(), + "doctor mediator".to_owned(), "doctor session".to_owned(), "enforce".to_owned(), "exec".to_owned(), From d615df5d1656bbec9697ba32c1a41ec6c53fbefc Mon Sep 17 00:00:00 2001 From: Alec Wenzowski Date: Wed, 2 Sep 2026 22:37:51 +0000 Subject: [PATCH 05/13] fix(doctor): state the blind spot `doctor mediator` has, and pin it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It compares the INSTALL against the BUILD, never the build against the SOURCE. When `target/release/batten` is itself behind the tree, both sides are equally stale, they agree, and the verdict is `mediator ok`. Measured one command apart while this very row was in flight: `land` rebased onto a `main` carrying a new `[[rule.review]]` key, the engine refused the tree's own batten.toml with `unknown field`, and `batten doctor mediator` answered `mediator ok`. Both binaries were the same pre-rebase build. Fourth staleness occurrence in one container that day, and the first this check missed. Stated because a check answering a narrower question than its name suggests IS the failure this repository is organised against — a dead gate and a clean tree are byte-identical on the decision surface, so shipping the verb while it reads as answering more than it does would reproduce the defect one layer up. What is bought stands: the original measured failure, an image-baked or hand-installed binary against a tree that builds a different one, which ran unnoticed for six hours. `two_equally_stale_binaries_agree_and_this_reports_current` pins it as known behaviour rather than leaving it in prose, so a later change cannot widen or narrow the bound with nothing going red. Closing the remainder needs a build-freshness predicate that is still a read; mtime-against-sources is the obvious candidate and is not obviously sound, since a rebase touches files cargo would not rebuild from — it trades this false negative for a false positive. A separate predicate with its own design, not a tightening of this one. Refs: CLOUD-1349 --- crates/batten/src/doctor.rs | 27 +++++++++++++++++++++++++++ crates/batten/tests/it/doctor.rs | 23 +++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/crates/batten/src/doctor.rs b/crates/batten/src/doctor.rs index 5c43c4b99..f7cbdcea3 100644 --- a/crates/batten/src/doctor.rs +++ b/crates/batten/src/doctor.rs @@ -242,6 +242,33 @@ const HOOK_HANDLERS: &str = "hook-handlers"; /// wiring file would put config-supplied code behind a row any consumer's agent /// may call — CLOUD-170's actual invariant, and the reason [`on_path`] stats /// rather than runs. Hashing a file reaches none of it. +/// +/// # What this does NOT catch, measured rather than reasoned +/// +/// **It compares the INSTALL against the BUILD, not the build against the +/// SOURCE.** When `target/release/batten` is itself behind the tree, both sides +/// of the comparison are equally stale, they agree, and this reports +/// [`Mediator::Current`]. +/// +/// Measured 2026-09-02, one command apart and while this very row was in flight: +/// `land` rebased onto a `main` that had added a `[[rule.review]]` key, the +/// engine refused the tree's own `batten.toml` with `unknown field`, and +/// `batten doctor mediator` answered `mediator ok`. Both binaries were the same +/// pre-rebase build. That is the fourth staleness occurrence in one container +/// that day and the first this check missed. +/// +/// **Stated here rather than left for a reader to discover**, because a check +/// that silently answers a narrower question than its name suggests is the very +/// shape this file is against: a dead gate and a clean tree are byte-identical on +/// the decision surface. What is bought is the ORIGINAL measured failure — an +/// image-baked or hand-installed binary against a tree that builds a different +/// one — which is the case that ran unnoticed for six hours. +/// +/// Closing the remainder needs a predicate over *build freshness* that is still a +/// read: comparing a binary's mtime against its sources is the obvious candidate +/// and is not obviously sound, since a rebase touches files cargo would not +/// rebuild from, so it trades this false negative for a false positive. That is a +/// separate predicate with its own design, not a tightening of this one. #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)] #[serde(rename_all = "kebab-case", tag = "state")] pub enum Mediator { diff --git a/crates/batten/tests/it/doctor.rs b/crates/batten/tests/it/doctor.rs index 408354a7f..58d94dcc8 100644 --- a/crates/batten/tests/it/doctor.rs +++ b/crates/batten/tests/it/doctor.rs @@ -174,6 +174,29 @@ fn a_tree_that_builds_one_with_no_mediator_on_path_is_could_not_look() { assert_eq!(stdout(&output), "mediator failed mediator-unresolvable\n"); } +#[test] +fn two_equally_stale_binaries_agree_and_this_reports_current() { + // THE BOUND, ASSERTED RATHER THAN ONLY DESCRIBED. The comparison is + // install-against-build, so when the BUILD is itself behind the source both + // sides agree and the verdict is `ok`. Measured 2026-09-02 while this row was + // in flight: `land` rebased onto a `main` carrying a new `[[rule.review]]` + // key, the engine refused the tree's own batten.toml, and this verb answered + // `mediator ok` one command later. + // + // Pinned as a case because a bound stated only in prose is one a later change + // can quietly widen or narrow with nothing going red. If a build-freshness + // predicate ever lands, this case is what must be revisited — deliberately, + // and not by discovering the comment was already false. + let dir = mediator_fixture("mediator-both-stale", b"old build", b"old build"); + // The source moving is what the pair cannot see: the fixture's own manifest + // is rewritten after both binaries were planted, and nothing in the verdict + // changes. + fs::write(dir.join("crates/batten/Cargo.toml"), "# moved on\n").unwrap(); + let output = mediator(&dir, Some(&dir.join("planted-bin")), &[]); + assert_eq!(output.status.code(), Some(0)); + assert_eq!(stdout(&output), "mediator ok\n"); +} + #[test] fn the_verdict_carries_no_path_and_no_digest() { // §6 and rule 4 over this verb specifically: it is about two absolute paths From 628c2ba53b8f6b093787b4d08f55eced18ab0e84 Mon Sep 17 00:00:00 2001 From: Alec Wenzowski Date: Wed, 2 Sep 2026 23:57:46 +0000 Subject: [PATCH 06/13] test(pointer-only): classify `doctor mediator` as pointer-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gate states its own rule: a new leaf verb is `PointerOnly` unless there is a reason it is not, and the reason is the field, so it is written rather than assumed. Pointer-only here for a sharper reason than its siblings. This verb's whole subject is two absolute paths and two digests — the shape most likely to leak one into output. It emits neither: the verdict is a stable token, and the digest is deliberately withheld because it is stable per content but varies per machine, so printing one would defeat §6's byte-stability while telling the reader nothing actionable. The remedy is `mise run install:local`; the verdict says whether to run it. This tier runs the verb and inspects what it actually wrote, so rule 4 is enforced over the bytes rather than declared by the author about their own code. Refs: CLOUD-1349 --- crates/batten/tests/it/pointer_only.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/crates/batten/tests/it/pointer_only.rs b/crates/batten/tests/it/pointer_only.rs index 1caa6d063..08da2dfa8 100644 --- a/crates/batten/tests/it/pointer_only.rs +++ b/crates/batten/tests/it/pointer_only.rs @@ -1008,6 +1008,19 @@ const CENSUS: &[Verb] = &[ stdin: Stdin::Nothing, disposition: Disposition::PointerOnly, }, + // Pointer-only for a sharper reason than its siblings, because this verb's + // whole subject is two absolute paths and two digests (CLOUD-1349) — the + // shape most likely to leak one into output. It emits neither: the verdict is + // a stable token, and a digest is deliberately withheld because it is stable + // per content but varies per machine, so printing one would defeat §6's + // byte-stability while telling the reader nothing they can act on. The remedy + // is `mise run install:local`, and the verdict is what says whether to run it. + Verb { + path: "doctor mediator", + args: &[], + stdin: Stdin::Nothing, + disposition: Disposition::PointerOnly, + }, // THE SESSION'S OWN DECLARED WORK (CLOUD-1376), and its content class is the // reason it belongs here rather than being obvious. What this verb reads is a // task store whose members carry a `subject` and a `description` — free prose From 9cd99ab710acfbf28f9a07cee80261eef16945d4 Mon Sep 17 00:00:00 2001 From: Alec Wenzowski Date: Thu, 3 Sep 2026 00:02:14 +0000 Subject: [PATCH 07/13] chore(snapshot): accept the golden schema for `doctor mediator` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The emitted surface grew a command, so the golden moves with it. The diff is exactly two additions and nothing else — the command's own row, and its entry in the emitted read-only allowlist — which is the snapshot earning its keep: a surface change that touched anything further would show here. Refs: CLOUD-1349 --- .../snapshots/it__snapshots__golden_json_schema.snap | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/batten/tests/it/snapshots/it__snapshots__golden_json_schema.snap b/crates/batten/tests/it/snapshots/it__snapshots__golden_json_schema.snap index 5e76e9195..cafae0385 100644 --- a/crates/batten/tests/it/snapshots/it__snapshots__golden_json_schema.snap +++ b/crates/batten/tests/it/snapshots/it__snapshots__golden_json_schema.snap @@ -854,9 +854,9 @@ expression: stdout_of(&output) "subcommands": [] }, { - "path": "doctor session", - "id": "doctor.session", - "about": "Diagnose whether this session has declared work it has not finished", + "path": "doctor mediator", + "id": "doctor.mediator", + "about": "Diagnose whether the engine the registrations reach was built from this tree", "effect": "read", "data_channel": true, "flags": [ @@ -2441,8 +2441,8 @@ expression: stdout_of(&output) "path": "doctor hooks" }, { - "id": "doctor.session", - "path": "doctor session" + "id": "doctor.mediator", + "path": "doctor mediator" }, { "id": "generate", From 86b5eb6c2a490e594bb634e15560cb666977c9f6 Mon Sep 17 00:00:00 2001 From: Alec Wenzowski Date: Thu, 3 Sep 2026 00:44:00 +0000 Subject: [PATCH 08/13] test(stop-posture): isolate the one case that asserts silence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `a_turn_that_strands_nothing_is_silent` ran through the unisolated `hook` helper, so a real session's findings could reach a fixture asserting nothing is said. The hazard is stated verbatim 300 lines above it, on `hook_in`: "an ambient one would let a real session's findings decide a fixture's verdict." NOT A FLAKE, and re-running would have been the wrong call. Measured 2026-09-03: red inside a `land` lap, green on the next isolated run of the same commit. `unlanded` reports once per HEAD sha and every lap rebases to a fresh one, so the session's own unlanded work is unreported at exactly the moment the lap runs the suite and reported at every other moment. It would fail every lap and pass every re-run — red for the author who is landing, green for everyone else, which is the class CLOUD-1209 already carries for `shell_write_advisory`. Silence is what makes this case the exposed one: every sibling asserts a specific advisory and an extra one cannot make those pass, while any advisory at all fails this. Widens the PR by one case. Stated rather than slipped in: it is not caused by this diff, but it blocks its landing on every lap, the fix reuses the isolating helper already in the file, and calling it infrastructure noise would have been false. Refs: CLOUD-1349, CLOUD-1209 --- crates/batten/tests/it/stop_posture.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/crates/batten/tests/it/stop_posture.rs b/crates/batten/tests/it/stop_posture.rs index 80202a116..b14ec34a8 100644 --- a/crates/batten/tests/it/stop_posture.rs +++ b/crates/batten/tests/it/stop_posture.rs @@ -603,7 +603,25 @@ fn the_measured_rule_keeps_precedence_when_both_would_fire() { fn a_turn_that_strands_nothing_is_silent() { let dir = repo("stop-finding-sink-clean"); stub(&dir, "mise-tasks/finding-sink-check.sh", 0, ""); - let stdout = stdout_of(&hook(&dir, &stop_with_transcript(&dir, "Landed."))); + // ISOLATED, because this is the one case here that asserts SILENCE and so is + // the one a real session's findings can decide. `hook_in`'s own comment + // states the hazard — "an ambient one would let a real session's findings + // decide a fixture's verdict" — and this case was reaching the ambient store + // anyway. + // + // Measured 2026-09-03: it failed inside a `land` lap and passed on the next + // isolated run of the same commit. Not a flake. `unlanded` reports once per + // HEAD sha, and every lap rebases to a fresh one, so the real session's + // unlanded work was unreported at exactly the moment the lap ran the suite + // and reported at every other moment. A case that goes red only while its + // author is landing is red for the author and green for everyone else. + let home = dir.join("home"); + fs::create_dir_all(&home).expect("home dir"); + let stdout = stdout_of(&hook_in( + &dir, + &home, + &stop_with_transcript(&dir, "Landed."), + )); assert!( !stdout.contains("additionalContext"), "silence is the default: {stdout}" From 3f01ea3a6c769fbca4c8a77108bb90061d75a8d1 Mon Sep 17 00:00:00 2001 From: Alec Wenzowski Date: Thu, 3 Sep 2026 02:00:51 +0000 Subject: [PATCH 09/13] fix(cli)!: a narrowed `enforce` still answers for the defect ledger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CLOUD-1186 predicted this regression in writing, and CLOUD-1358 shipped it a day later without reading the row. The engine-side gates were skipped on `only.is_empty()` alone. That is a sound convenience on `check` — a caller asking about one row is not asking about the budget or the ledger. CLOUD-1358 then gave `enforce` a `--rule` selector, and the same branch made `batten enforce --rule ` a ONE-TOKEN WAY TO SKIP THE DEFECT LEDGER on the verb that runs user-declared commands. The ledger gate is engine-side rather than a `[[rule]]` row precisely so a branch cannot lower it by editing a rule table; the narrowing restored that lowering by another route. CLOUD-1186's own words, from before the selector existed: "a reasonable UX convenience on `check` ... and a security hole on `enforce`", and it names landing this decoupling as the precondition for narrowing `enforce` at all. The skip is now surface-dependent. `check` narrowed is byte-identical to before. `enforce` narrowed still evaluates the ledger. THE BUDGET KEEPS THE SKIP ON BOTH, and the split is the reason `ledger_findings` exists: a budget measures declared instruction files — a property of the tree the caller did not ask about — while the ledger is a claim about this branch's own conduct. Only one of the two is the security property. Four cases, and the mirror earned its keep before it guarded anything: it caught that my first fixture would not parse, because it exercises a path the existing suite already proves works. Shown able to fail by restoring the shipped code — exactly 1 of 27 reddens, the narrowed-check case included in the 26 that do not, so it discriminates the regression and nothing else. BREAKING CHANGE: `batten enforce --rule ` now reports defect-ledger findings it previously skipped, so a branch with a tampered ledger that exited 0 under a narrowed enforce now exits 2. That is the defect, not a new rule. Refs: CLOUD-1186, CLOUD-1358 --- crates/batten/src/lib.rs | 48 +++++++++++-- crates/batten/tests/it/defects.rs | 110 ++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 5 deletions(-) diff --git a/crates/batten/src/lib.rs b/crates/batten/src/lib.rs index 52087fef8..5e98e507d 100644 --- a/crates/batten/src/lib.rs +++ b/crates/batten/src/lib.rs @@ -11741,12 +11741,30 @@ fn engine_side_findings(root: &Path, config: &resolve::Resolved) -> Result Result> { + match config.defects.as_ref() { + Some(declared) => defects::gate(root, declared), + None => Ok(Vec::new()), + } +} + /// The two stderr notices every rule-running verb opens with. /// /// Lifted out of [`run_rules`] to keep that funnel under the line lint, and the @@ -11899,12 +11917,32 @@ fn run_rules( // being an ordinary `Finding`, and all of which a private verdict path would // have had to re-implement. An over-budget set was previously visible only // to whoever thought to run `policy budget`, which is a report, not a gate. - // The engine-side gates are skipped entirely under a narrowing: a caller - // asking about one declared row is not asking about the budget or the + // The engine-side gates are skipped under a narrowing ON THE READ SURFACE: a + // caller asking about one declared row is not asking about the budget or the // ledger, and running them would make a narrowed read fail for a reason it // did not ask about. + // + // THE SPAWNING SURFACE DOES NOT GET THAT SKIP, AND THE ASYMMETRY IS THE WHOLE + // POINT (CLOUD-1186). The ledger gate lives engine-side precisely so a branch + // cannot lower it by editing a rule table — and a narrowing that dropped it + // here would be a one-token way to do exactly that, on the verb that runs + // user-declared commands. A convenience on `check` is a hole on `enforce`. + // + // MEASURED, AND SHIPPED BROKEN FOR ONE DAY: CLOUD-1358 gave `enforce` a + // `--rule` selector while this branch still read `only.is_empty()` alone, so + // between that merge and this commit `batten enforce --rule ` skipped the + // ledger. CLOUD-1186 had predicted that exact regression, in those words, + // before the selector landed. + // + // The BUDGET keeps the skip on both surfaces: it is a measurement over + // declared instruction files rather than a claim about this branch's + // conduct, so a narrowed run failing on it is the "reason it did not ask + // about" this comment already refuses. The ledger is the security property; + // the budget is not. if only.is_empty() { findings.extend(engine_side_findings(&root, &config)?); + } else if surface == Surface::Spawning { + findings.extend(ledger_findings(&root, &config)?); } // The transcript capability (CLOUD-95), resolved BESIDE the runner rather than diff --git a/crates/batten/tests/it/defects.rs b/crates/batten/tests/it/defects.rs index 567e6aac1..8d2f10e1c 100644 --- a/crates/batten/tests/it/defects.rs +++ b/crates/batten/tests/it/defects.rs @@ -50,6 +50,116 @@ fn ledger_text(dir: &Path) -> String { std::fs::read_to_string(dir.join("defects.jsonl")).unwrap_or_default() } +// --- a narrowing must not lower the gate (CLOUD-1186) ------------------------ + +/// [`CONFIG`] plus one narrowable row that cannot fire on these fixtures. +/// +/// A row that fired would make the cases below unable to tell the ledger's +/// verdict from the rule's, which is the whole thing they discriminate. The glob +/// names a path the fixtures never create. +const CONFIG_WITH_ROW: &str = "version = 1\n\n[defects]\npath = \"defects.jsonl\"\nclasses = [\"false-green\", \"silent-skip\"]\n\n[[rule]]\nid = \"never-fires\"\nkind = \"forbid\"\nglob = \"no-such-dir/**\"\npattern = \"zzz-absent\"\nseverity = \"deny\"\nscope = \"tree\"\n"; + +/// A repo carrying a ledger that was REWRITTEN — the violation the gate exists +/// for — plus a narrowable row. +fn tampered_repo(name: &str) -> PathBuf { + let base = format!( + "{}\n{}\n", + row("d-1", "false-green", "a.rs:1"), + row("d-2", "silent-skip", "b.rs:2") + ); + let dir = Fixture::new(name) + .config(CONFIG_WITH_ROW) + .file("defects.jsonl", &base) + .git() + .build(); + git_in(&dir, &["add", "-A"]); + git_in(&dir, &["commit", "-q", "-m", "base"]); + common::write( + &dir, + "defects.jsonl", + &format!( + "{}\n{}\n", + row("d-1", "false-green", "a.rs:1"), + row("d-2", "silent-skip", "SOMEWHERE-ELSE:9") + ), + ); + dir +} + +#[test] +fn a_narrowed_enforce_still_evaluates_the_defect_ledger() { + // THE SECURITY PROPERTY, and the one that would silently regress. The ledger + // gate is engine-side rather than a `[[rule]]` row precisely so a branch + // cannot lower it by editing a rule table — so a narrowing that dropped it on + // the spawning verb would restore that lowering in one token. + // + // MEASURED AS A REAL REGRESSION, not a hypothetical: CLOUD-1358 gave + // `enforce` a `--rule` selector while the skip still keyed on the narrowing + // alone, and `batten enforce --rule ` skipped the ledger on `main` for a + // day. CLOUD-1186 had predicted it in those words before the selector landed. + let dir = tampered_repo("defects-narrowed-enforce"); + let output = run(&dir, &["enforce", "--rule", "never-fires"]); + assert_eq!( + output.status.code(), + Some(2), + "a narrowed enforce still answers for the ledger: {}", + stdout(&output) + ); + assert!( + stdout(&output).contains("defect-not-append-only"), + "and it is the ledger's own finding: {}", + stdout(&output) + ); +} + +#[test] +fn a_narrowed_check_still_skips_the_defect_ledger() { + // THE OTHER HALF, unchanged and deliberately so. On the read surface the skip + // is the convenience it was: a caller asking about one row is not asking + // about the ledger, and failing there would be a verdict they did not + // request. Asserted rather than assumed, because "fix the hole" applied + // symmetrically would have taken this with it. + let dir = tampered_repo("defects-narrowed-check"); + let output = run(&dir, &["check", "--rule", "never-fires"]); + assert_eq!( + output.status.code(), + Some(0), + "a narrowed read is silent about the ledger: {}", + stdout(&output) + ); +} + +#[test] +fn an_unnarrowed_check_still_answers_for_the_ledger() { + // THE ANTI-VACUITY MIRROR for the case above: if this fixture's ledger were + // clean, or the gate were off entirely, the narrowed-check case would pass + // for the wrong reason and prove nothing. Same repo, no narrowing. + let dir = tampered_repo("defects-unnarrowed-check"); + let output = run(&dir, &["check"]); + assert_eq!( + output.status.code(), + Some(2), + "the tampering IS visible to an unnarrowed run: {}", + stdout(&output) + ); + assert!(stdout(&output).contains("defect-not-append-only")); +} + +#[test] +fn a_narrowed_enforce_naming_no_declared_row_is_a_usage_error() { + // The anti-vacuous-pass guarantee carries to the spawning surface: a typo + // must not read as "the gate passed", which is the same reasoning the ledger + // skip above is about, one layer up. + let dir = tampered_repo("defects-narrowed-unknown"); + let output = run(&dir, &["enforce", "--rule", "no-such-row"]); + assert_eq!( + output.status.code(), + Some(1), + "an unmatched id is a usage error, never a clean run: {}", + stdout(&output) + ); +} + // --- the gate ------------------------------------------------------------ #[test] From c22a8ca520955c3fb027a649b15ef7cf82f534e2 Mon Sep 17 00:00:00 2001 From: Alec Wenzowski Date: Thu, 3 Sep 2026 02:02:56 +0000 Subject: [PATCH 10/13] =?UTF-8?q?perf(test):=20assert=20one=20rule=20with?= =?UTF-8?q?=20one=20rule=20=E2=80=94=20the=20third=20and=20largest=20of=20?= =?UTF-8?q?the=20family?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `the_committed_repo_config_gates_a_repository` ran `enforce` over the whole ~103 row committed ruleset to assert a single pointer, `crates/** no-conflict-markers`. Its own comment already said the other rows could not affect that: the asserted stdout is "byte-identical whether the other committed rules are present, absent, misspelled, mis-globbed, or switched off". MEASURED ON THE RUNNER RATHER THAN INFERRED. On the 2-core Windows job it is 436.42s — 24% of that suite's 1810.1s of CPU and its single largest item, with the next slowest at 28.28s. Locally the same case is 21.5s; the 20x is the contention signature the two cases CLOUD-1358 narrowed showed, not a different workload. Narrowed to `--rule no-conflict-markers`: 21.526s -> 0.153s here, assertion unchanged. That suite is CPU-bound rather than floor-bound — wall tracks CPU/2 to within 0.4% on both readings taken — so removing 436s of CPU should be ~218s of wall off every `rust.yml` run. Shown able to fail by pointing `--rule` at a DIFFERENT real committed row rather than by deleting one: the case reddens, so it is asserting this rule's finding and not merely that something was emitted. Refs: CLOUD-1358 --- crates/batten/tests/it/cli.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/batten/tests/it/cli.rs b/crates/batten/tests/it/cli.rs index f6a6ba947..ffe1851ba 100644 --- a/crates/batten/tests/it/cli.rs +++ b/crates/batten/tests/it/cli.rs @@ -6120,8 +6120,17 @@ fn the_committed_repo_config_gates_a_repository() { // This spawns `hk`, so it needs hk on PATH — true under `mise run // test:cargo` and false under a bare `cargo test`, where it fails loudly // with "cannot run `hk`: not found on PATH" rather than passing silently. + // + // NARROWED TO THE ONE ROW IT ASSERTS (CLOUD-1358's mechanism, this case's + // turn). The comment above already states that the asserted stdout is + // byte-identical whichever of the other ~100 rows are present — so running + // them bought nothing and cost 436.42s on the 2-core Windows runner, 24% of + // that suite's entire CPU and its single largest item, against 21.5s here. + // The ratio is the contention signature, not a different workload. let output = batten() .arg("enforce") + .arg("--rule") + .arg("no-conflict-markers") .current_dir(&dir) .state_home(&home) .env_remove("BATTEN_STRICTNESS") From 0e6198e2e42031d47becd563178a3134e8c0da39 Mon Sep 17 00:00:00 2001 From: Alec Wenzowski Date: Thu, 3 Sep 2026 02:26:54 +0000 Subject: [PATCH 11/13] test(stop-posture): isolate every case, not only the one asserting silence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supersedes the previous commit's one-case fix, whose reasoning was wrong and was disproved by the next landing lap. That commit isolated `a_turn_that_strands_nothing_is_silent` and argued only a silence-asserting case could be exposed, since an EXTRA advisory cannot falsify a case asserting a specific one. The next lap failed `a_stranded_finding_is_pointed_at_and_the_turn_still_ends`. The engine emits AT MOST ONE nudge, ranked — two on a turn is how a channel stops being read — so an ambient finding does not add to the expected advisory, it DISPLACES it. Every case in the file is exposed, and the fix belongs at the helper rather than at whichever case failed most recently. `hook` now delegates to `hook_in`, whose own comment already stated the hazard: "an ambient one would let a real session's findings decide a fixture's verdict." The exposure is worst exactly while its author is landing. `unlanded` reports once per HEAD sha and every lap rebases to a fresh one, so a case is red on the lap and green on the re-run — the shape that gets called a flake and re-run until it passes. It is not one, and neither reading of it was. Refs: CLOUD-1349, CLOUD-1209 --- crates/batten/tests/it/stop_posture.rs | 72 +++++++++++++++----------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/crates/batten/tests/it/stop_posture.rs b/crates/batten/tests/it/stop_posture.rs index b14ec34a8..03fa10545 100644 --- a/crates/batten/tests/it/stop_posture.rs +++ b/crates/batten/tests/it/stop_posture.rs @@ -185,38 +185,48 @@ fn stop_payload(message: &str, active: bool) -> String { .to_string() } +/// `batten hook` against the fixture's own state home. +/// +/// **ISOLATED FOR EVERY CASE, not only the one that asserts silence.** This +/// delegates to [`hook_in`] rather than carrying its own ambient invocation, and +/// the reason is a defect measured twice on 2026-09-03. +/// +/// The first reading looked like it touched one case: `a_turn_that_strands_ +/// nothing_is_silent` went red inside a `land` lap and green on the next isolated +/// run of the same commit, because the real session's `unlanded` finding reached +/// the fixture. The explanation offered then was that only a silence-asserting +/// case is exposed, since an EXTRA advisory cannot falsify a case asserting a +/// specific one. +/// +/// **That was wrong, and the next lap proved it**: +/// `a_stranded_finding_is_pointed_at_and_the_turn_still_ends` failed the same +/// way. The engine emits **at most one** nudge, ranked — two on a turn is how a +/// channel stops being read — so an ambient finding does not add to the expected +/// advisory, it DISPLACES it. Every case here is exposed, not just the silent +/// one. +/// +/// `unlanded` reports once per HEAD sha and every lap rebases to a fresh one, so +/// the exposure is worst exactly while its author is landing: red on the lap, +/// green on the re-run, which is the shape that gets called a flake and re-run +/// until it passes. fn hook(dir: &Path, payload: &str) -> Output { - let mut command = batten(); - // THE STATE HOME IS CONTAINED, for the reason the unlanded fixture's own - // runner already states: the Stop tier reads the out-of-tree findings store, - // and an ambient one lets a REAL session's findings decide a fixture's - // verdict. Measured 2026-09-03 — `a_clean_final_message_says_nothing` failed - // with `unlanded: 1 commit(s) not on the landing target`, read from the - // checkout the suite was running in. It passes on a tree with nothing - // unlanded and fails on any branch mid-development, which is every branch - // this suite is ever run from. - let home = scratch(&format!( - "{}-home", - dir.file_name() - .and_then(|name| name.to_str()) - .unwrap_or("stop-posture") - )); - common::state_home(&mut command, &home); - command - .current_dir(dir) - .args(["hook", "--harness", "claude-code"]) - .env_remove("BATTEN_HOOK_BYPASS") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()); - let mut child = command.spawn().expect("spawn batten hook"); - child - .stdin - .take() - .expect("piped stdin") - .write_all(payload.as_bytes()) - .expect("write payload"); - child.wait_with_output().expect("run batten hook") + // Delegates rather than repeating the containment inline, and that is the + // resolution of a genuine collision: `main` fixed this same defect in + // parallel by setting the state home here directly. Both isolate the + // findings store; `hook_in` additionally sets `GIT_CEILING_DIRECTORIES`, so + // discovery cannot climb out of the fixture either — and a second inline + // copy of the containment is the very shape that caused this bug, where + // `hook` and `hook_in` disagreed about what a fixture owns. + // + // The parallel fix's measurement, kept because it is a different case than + // the two below: `a_clean_final_message_says_nothing` failed with + // `unlanded: 1 commit(s) not on the landing target`, read from the checkout + // the suite was running in. It passes on a tree with nothing unlanded and + // fails on any branch mid-development, which is every branch this suite is + // ever run from. + let home = dir.join("hook-home"); + fs::create_dir_all(&home).expect("home dir"); + hook_in(dir, &home, payload) } /// A repository whose branch carries a commit the landing target lacks, plus a From d023c9887a6efc54587b3588fac35f849e745cf6 Mon Sep 17 00:00:00 2001 From: Alec Wenzowski Date: Thu, 3 Sep 2026 05:55:28 +0000 Subject: [PATCH 12/13] chore(surface): regenerate completions after the doctor sub-verb collision MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rebase resolved `completions/*` by taking one side, which is a choice rather than a derivation — `main` added `doctor session` while this branch added `doctor mediator`, so neither side's generated file carried both. Regenerated with `mise run completions`; all three shells changed. `man/` and the schemas needed no regeneration, so both sub-verbs' pages had already merged cleanly. Refs: CLOUD-1349 --- completions/batten.bash | 73 +++++++++++++++++++++++++++++++++++++++-- completions/batten.fish | 61 ++++++++++++++++++++++++---------- completions/batten.zsh | 57 ++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+), 21 deletions(-) diff --git a/completions/batten.bash b/completions/batten.bash index 0e386a90c..3abd26ba6 100644 --- a/completions/batten.bash +++ b/completions/batten.bash @@ -295,6 +295,9 @@ _batten() { batten__subcmd__doctor,mediator) cmd="batten__subcmd__doctor__subcmd__mediator" ;; + batten__subcmd__doctor,session) + cmd="batten__subcmd__doctor__subcmd__session" + ;; batten__subcmd__doctor__subcmd__help,help) cmd="batten__subcmd__doctor__subcmd__help__subcmd__help" ;; @@ -304,6 +307,9 @@ _batten() { batten__subcmd__doctor__subcmd__help,mediator) cmd="batten__subcmd__doctor__subcmd__help__subcmd__mediator" ;; + batten__subcmd__doctor__subcmd__help,session) + cmd="batten__subcmd__doctor__subcmd__help__subcmd__session" + ;; batten__subcmd__generate,completions) cmd="batten__subcmd__generate__subcmd__completions" ;; @@ -514,6 +520,9 @@ _batten() { batten__subcmd__help__subcmd__doctor,mediator) cmd="batten__subcmd__help__subcmd__doctor__subcmd__mediator" ;; + batten__subcmd__help__subcmd__doctor,session) + cmd="batten__subcmd__help__subcmd__doctor__subcmd__session" + ;; batten__subcmd__help__subcmd__generate,completions) cmd="batten__subcmd__help__subcmd__generate__subcmd__completions" ;; @@ -2553,7 +2562,7 @@ _batten() { return 0 ;; batten__subcmd__doctor) - opts="-J -q -v -y -h --json --strictness --fail-on-warning --config-from --config-in --silent --quiet --verbose --debug --trace --log-level --no-color --no-input --yes --help mediator hooks help" + opts="-J -q -v -y -h --json --strictness --fail-on-warning --config-from --config-in --silent --quiet --verbose --debug --trace --log-level --no-color --no-input --yes --help mediator hooks session help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -2583,7 +2592,7 @@ _batten() { return 0 ;; batten__subcmd__doctor__subcmd__help) - opts="mediator hooks help" + opts="mediator hooks session help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -2638,6 +2647,20 @@ _batten() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + batten__subcmd__doctor__subcmd__help__subcmd__session) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; batten__subcmd__doctor__subcmd__hooks) opts="-J -q -v -y -h --json --strictness --fail-on-warning --config-from --config-in --silent --quiet --verbose --debug --trace --log-level --no-color --no-input --yes --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then @@ -2698,6 +2721,36 @@ _batten() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + batten__subcmd__doctor__subcmd__session) + opts="-J -q -v -y -h --json --strictness --fail-on-warning --config-from --config-in --silent --quiet --verbose --debug --trace --log-level --no-color --no-input --yes --help" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --strictness) + COMPREPLY=($(compgen -W "permissive standard strict" -- "${cur}")) + return 0 + ;; + --config-from) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --config-in) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --log-level) + COMPREPLY=($(compgen -W "silent quiet normal verbose debug trace" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; batten__subcmd__enforce) opts="-J -q -v -y -h --rule --json --strictness --fail-on-warning --config-from --config-in --silent --quiet --verbose --debug --trace --log-level --no-color --no-input --yes --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then @@ -3471,7 +3524,7 @@ _batten() { return 0 ;; batten__subcmd__help__subcmd__doctor) - opts="mediator hooks" + opts="mediator hooks session" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -3512,6 +3565,20 @@ _batten() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + batten__subcmd__help__subcmd__doctor__subcmd__session) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; batten__subcmd__help__subcmd__enforce) opts="" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then diff --git a/completions/batten.fish b/completions/batten.fish index a67d81dd6..b6a69155f 100644 --- a/completions/batten.fish +++ b/completions/batten.fish @@ -579,31 +579,32 @@ complete -c batten -n "__fish_batten_using_subcommand spec" -l no-color -d 'Neve complete -c batten -n "__fish_batten_using_subcommand spec" -l no-input -d 'Never prompt; treat the run as unattended' complete -c batten -n "__fish_batten_using_subcommand spec" -s y -l yes -d 'Confirm a destructive operation that would otherwise refuse' complete -c batten -n "__fish_batten_using_subcommand spec" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l strictness -d 'Raise how strictly gates apply (an override may only tighten policy)' -r -f -a "permissive\t'Advisory: findings are reported without failing the run' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -l strictness -d 'Raise how strictly gates apply (an override may only tighten policy)' -r -f -a "permissive\t'Advisory: findings are reported without failing the run' standard\t'The default: a finding is a violation' strict\t'Everything `Standard` fails on, plus anything advisory'" -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l config-from -d 'Read the committed config from a git ref (e.g. origin/main) instead of the working tree' -r -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l config-in -d 'Read the committed config from this directory instead of the directory being judged' -r -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l log-level -d 'Set the verbosity rung by name' -r -f -a "silent\t'Say nothing but a verdict or a usage error' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -l config-from -d 'Read the committed config from a git ref (e.g. origin/main) instead of the working tree' -r +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -l config-in -d 'Read the committed config from this directory instead of the directory being judged' -r +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -l log-level -d 'Set the verbosity rung by name' -r -f -a "silent\t'Say nothing but a verdict or a usage error' quiet\t'Suppress ordinary progress; keep warnings' normal\t'The default' verbose\t'Explain what is being checked' debug\t'Add resolution detail' trace\t'Add everything'" -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -s J -l json -d 'Emit byte-stable JSON instead of pointer lines' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l fail-on-warning -d 'Promote a warn-severity finding to a violation (an override may only turn this on)' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l silent -d 'Say nothing but a verdict or a usage error' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -s q -l quiet -d 'Suppress ordinary progress (repeatable: -qq is silent)' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -s v -l verbose -d 'Explain what is being checked (repeatable: -vv is debug)' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l debug -d 'Add resolution detail' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l trace -d 'Add everything' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l no-color -d 'Never colour stderr, whatever it is attached to' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -l no-input -d 'Never prompt; treat the run as unattended' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -s y -l yes -d 'Confirm a destructive operation that would otherwise refuse' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -f -a "mediator" -d 'Diagnose whether the engine the registrations reach was built from this tree' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -f -a "hooks" -d 'Diagnose whether batten is wired on every hook surface of every harness' -complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -s J -l json -d 'Emit byte-stable JSON instead of pointer lines' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -l fail-on-warning -d 'Promote a warn-severity finding to a violation (an override may only turn this on)' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -l silent -d 'Say nothing but a verdict or a usage error' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -s q -l quiet -d 'Suppress ordinary progress (repeatable: -qq is silent)' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -s v -l verbose -d 'Explain what is being checked (repeatable: -vv is debug)' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -l debug -d 'Add resolution detail' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -l trace -d 'Add everything' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -l no-color -d 'Never colour stderr, whatever it is attached to' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -l no-input -d 'Never prompt; treat the run as unattended' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -s y -l yes -d 'Confirm a destructive operation that would otherwise refuse' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -f -a "mediator" -d 'Diagnose whether the engine the registrations reach was built from this tree' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -f -a "hooks" -d 'Diagnose whether batten is wired on every hook surface of every harness' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -f -a "session" -d 'Diagnose whether this session has declared work it has not finished' +complete -c batten -n "__fish_batten_using_subcommand doctor; and not __fish_seen_subcommand_from mediator hooks session help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from mediator" -l strictness -d 'Raise how strictly gates apply (an override may only tighten policy)' -r -f -a "permissive\t'Advisory: findings are reported without failing the run' standard\t'The default: a finding is a violation' strict\t'Everything `Standard` fails on, plus anything advisory'" @@ -648,8 +649,31 @@ complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_su complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from hooks" -l no-input -d 'Never prompt; treat the run as unattended' complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from hooks" -s y -l yes -d 'Confirm a destructive operation that would otherwise refuse' complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from hooks" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l strictness -d 'Raise how strictly gates apply (an override may only tighten policy)' -r -f -a "permissive\t'Advisory: findings are reported without failing the run' +standard\t'The default: a finding is a violation' +strict\t'Everything `Standard` fails on, plus anything advisory'" +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l config-from -d 'Read the committed config from a git ref (e.g. origin/main) instead of the working tree' -r +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l config-in -d 'Read the committed config from this directory instead of the directory being judged' -r +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l log-level -d 'Set the verbosity rung by name' -r -f -a "silent\t'Say nothing but a verdict or a usage error' +quiet\t'Suppress ordinary progress; keep warnings' +normal\t'The default' +verbose\t'Explain what is being checked' +debug\t'Add resolution detail' +trace\t'Add everything'" +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -s J -l json -d 'Emit byte-stable JSON instead of pointer lines' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l fail-on-warning -d 'Promote a warn-severity finding to a violation (an override may only turn this on)' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l silent -d 'Say nothing but a verdict or a usage error' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -s q -l quiet -d 'Suppress ordinary progress (repeatable: -qq is silent)' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -s v -l verbose -d 'Explain what is being checked (repeatable: -vv is debug)' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l debug -d 'Add resolution detail' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l trace -d 'Add everything' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l no-color -d 'Never colour stderr, whatever it is attached to' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -l no-input -d 'Never prompt; treat the run as unattended' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -s y -l yes -d 'Confirm a destructive operation that would otherwise refuse' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from session" -s h -l help -d 'Print help (see more with \'--help\')' complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from help" -f -a "mediator" -d 'Diagnose whether the engine the registrations reach was built from this tree' complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from help" -f -a "hooks" -d 'Diagnose whether batten is wired on every hook surface of every harness' +complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from help" -f -a "session" -d 'Diagnose whether this session has declared work it has not finished' complete -c batten -n "__fish_batten_using_subcommand doctor; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c batten -n "__fish_batten_using_subcommand init" -l strictness -d 'Raise how strictly gates apply (an override may only tighten policy)' -r -f -a "permissive\t'Advisory: findings are reported without failing the run' standard\t'The default: a finding is a violation' @@ -2921,6 +2945,7 @@ complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subc complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from lint" -f -a "brief" -d 'Check a delegation brief against the handoff schema (any missing section is a violation)' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from doctor" -f -a "mediator" -d 'Diagnose whether the engine the registrations reach was built from this tree' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from doctor" -f -a "hooks" -d 'Diagnose whether batten is wired on every hook surface of every harness' +complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from doctor" -f -a "session" -d 'Diagnose whether this session has declared work it has not finished' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from generate" -f -a "completions" -d 'Emit the shell completion script for one shell' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from generate" -f -a "hooks" -d 'Emit one harness\'s hook registrations, on stdout' complete -c batten -n "__fish_batten_using_subcommand help; and __fish_seen_subcommand_from generate" -f -a "man" -d 'Emit the roff man page for one command, on stdout' diff --git a/completions/batten.zsh b/completions/batten.zsh index 38aedce1d..d42fe7b17 100644 --- a/completions/batten.zsh +++ b/completions/batten.zsh @@ -999,6 +999,37 @@ trace\:"Add everything"))' \ '--help[Print help (see more with '\''--help'\'')]' \ && ret=0 ;; +(session) +_arguments "${_arguments_options[@]}" : \ +'--strictness=[Raise how strictly gates apply (an override may only tighten policy)]: :((permissive\:"Advisory\: findings are reported without failing the run" +standard\:"The default\: a finding is a violation" +strict\:"Everything \`Standard\` fails on, plus anything advisory"))' \ +'--config-from=[Read the committed config from a git ref (e.g. origin/main) instead of the working tree]: :_default' \ +'--config-in=[Read the committed config from this directory instead of the directory being judged]: :_default' \ +'--log-level=[Set the verbosity rung by name]: :((silent\:"Say nothing but a verdict or a usage error" +quiet\:"Suppress ordinary progress; keep warnings" +normal\:"The default" +verbose\:"Explain what is being checked" +debug\:"Add resolution detail" +trace\:"Add everything"))' \ +'-J[Emit byte-stable JSON instead of pointer lines]' \ +'--json[Emit byte-stable JSON instead of pointer lines]' \ +'--fail-on-warning[Promote a warn-severity finding to a violation (an override may only turn this on)]' \ +'*--silent[Say nothing but a verdict or a usage error]' \ +'*-q[Suppress ordinary progress (repeatable\: -qq is silent)]' \ +'*--quiet[Suppress ordinary progress (repeatable\: -qq is silent)]' \ +'*-v[Explain what is being checked (repeatable\: -vv is debug)]' \ +'*--verbose[Explain what is being checked (repeatable\: -vv is debug)]' \ +'*--debug[Add resolution detail]' \ +'*--trace[Add everything]' \ +'--no-color[Never colour stderr, whatever it is attached to]' \ +'--no-input[Never prompt; treat the run as unattended]' \ +'-y[Confirm a destructive operation that would otherwise refuse]' \ +'--yes[Confirm a destructive operation that would otherwise refuse]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +&& ret=0 +;; (help) _arguments "${_arguments_options[@]}" : \ ":: :_batten__subcmd__doctor__subcmd__help_commands" \ @@ -1019,6 +1050,10 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ && ret=0 ;; +(session) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; (help) _arguments "${_arguments_options[@]}" : \ && ret=0 @@ -5020,6 +5055,10 @@ _arguments "${_arguments_options[@]}" : \ (hooks) _arguments "${_arguments_options[@]}" : \ && ret=0 +;; +(session) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 ;; esac ;; @@ -6109,6 +6148,7 @@ _batten__subcmd__doctor_commands() { local commands; commands=( 'mediator:Diagnose whether the engine the registrations reach was built from this tree' \ 'hooks:Diagnose whether batten is wired on every hook surface of every harness' \ +'session:Diagnose whether this session has declared work it has not finished' \ 'help:Print this message or the help of the given subcommand(s)' \ ) _describe -t commands 'batten doctor commands' commands "$@" @@ -6118,6 +6158,7 @@ _batten__subcmd__doctor__subcmd__help_commands() { local commands; commands=( 'mediator:Diagnose whether the engine the registrations reach was built from this tree' \ 'hooks:Diagnose whether batten is wired on every hook surface of every harness' \ +'session:Diagnose whether this session has declared work it has not finished' \ 'help:Print this message or the help of the given subcommand(s)' \ ) _describe -t commands 'batten doctor help commands' commands "$@" @@ -6137,6 +6178,11 @@ _batten__subcmd__doctor__subcmd__help__subcmd__mediator_commands() { local commands; commands=() _describe -t commands 'batten doctor help mediator commands' commands "$@" } +(( $+functions[_batten__subcmd__doctor__subcmd__help__subcmd__session_commands] )) || +_batten__subcmd__doctor__subcmd__help__subcmd__session_commands() { + local commands; commands=() + _describe -t commands 'batten doctor help session commands' commands "$@" +} (( $+functions[_batten__subcmd__doctor__subcmd__hooks_commands] )) || _batten__subcmd__doctor__subcmd__hooks_commands() { local commands; commands=() @@ -6147,6 +6193,11 @@ _batten__subcmd__doctor__subcmd__mediator_commands() { local commands; commands=() _describe -t commands 'batten doctor mediator commands' commands "$@" } +(( $+functions[_batten__subcmd__doctor__subcmd__session_commands] )) || +_batten__subcmd__doctor__subcmd__session_commands() { + local commands; commands=() + _describe -t commands 'batten doctor session commands' commands "$@" +} (( $+functions[_batten__subcmd__enforce_commands] )) || _batten__subcmd__enforce_commands() { local commands; commands=() @@ -6451,6 +6502,7 @@ _batten__subcmd__help__subcmd__doctor_commands() { local commands; commands=( 'mediator:Diagnose whether the engine the registrations reach was built from this tree' \ 'hooks:Diagnose whether batten is wired on every hook surface of every harness' \ +'session:Diagnose whether this session has declared work it has not finished' \ ) _describe -t commands 'batten help doctor commands' commands "$@" } @@ -6464,6 +6516,11 @@ _batten__subcmd__help__subcmd__doctor__subcmd__mediator_commands() { local commands; commands=() _describe -t commands 'batten help doctor mediator commands' commands "$@" } +(( $+functions[_batten__subcmd__help__subcmd__doctor__subcmd__session_commands] )) || +_batten__subcmd__help__subcmd__doctor__subcmd__session_commands() { + local commands; commands=() + _describe -t commands 'batten help doctor session commands' commands "$@" +} (( $+functions[_batten__subcmd__help__subcmd__enforce_commands] )) || _batten__subcmd__help__subcmd__enforce_commands() { local commands; commands=() From 4a3106cf244d67debac335702c7e9a836f3177fb Mon Sep 17 00:00:00 2001 From: Alec Wenzowski Date: Thu, 3 Sep 2026 05:58:55 +0000 Subject: [PATCH 13/13] chore(snapshot): accept the golden schema carrying both new doctor sub-verbs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rebase resolved this file by taking one side, so it asserted a surface of 45 commands where the binary emits 46 — `main`'s `doctor session` was absent. That is a claim about what the binary emits, settled only by running it. Regenerated: the diff adds `doctor session` to the command surface and to the read-only allowlist, and displaces nothing from this branch. CI would have caught it; the point of regenerating rather than trusting the resolution is that here is cheaper than there. Refs: CLOUD-1349 --- .../it__snapshots__golden_json_schema.snap | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/crates/batten/tests/it/snapshots/it__snapshots__golden_json_schema.snap b/crates/batten/tests/it/snapshots/it__snapshots__golden_json_schema.snap index cafae0385..60a16c345 100644 --- a/crates/batten/tests/it/snapshots/it__snapshots__golden_json_schema.snap +++ b/crates/batten/tests/it/snapshots/it__snapshots__golden_json_schema.snap @@ -870,6 +870,24 @@ expression: stdout_of(&output) } ], "subcommands": [] + }, + { + "path": "doctor session", + "id": "doctor.session", + "about": "Diagnose whether this session has declared work it has not finished", + "effect": "read", + "data_channel": true, + "flags": [ + { + "name": "json", + "short": "J", + "long": "json", + "takes_value": false, + "positional": false, + "help": "Emit byte-stable JSON instead of pointer lines" + } + ], + "subcommands": [] } ] }, @@ -2444,6 +2462,10 @@ expression: stdout_of(&output) "id": "doctor.mediator", "path": "doctor mediator" }, + { + "id": "doctor.session", + "path": "doctor session" + }, { "id": "generate", "path": "generate"