feat(cli): 61 commands across six crates had no route through apr, and simular parsed argv by hand - #2493
Closed
noahgift wants to merge 12 commits into
Closed
feat(cli): 61 commands across six crates had no route through apr, and simular parsed argv by hand#2493noahgift wants to merge 12 commits into
noahgift wants to merge 12 commits into
Conversation
7 tasks
noahgift
enabled auto-merge
August 16, 2026 07:45
#2481) APR-MONO consolidated alimentar in-tree as `crates/aprender-data`, but only the CODE moved. The capability stayed reachable exclusively through the standalone `alimentar` binary: `apr data` shipped 5 commands (audit, split, decontaminate, dedup, balance) against alimentar's 20, and only `dedup` overlapped. Eighteen data capabilities -- convert, info, head, schema, mix, fim, filter-text, view, import, registry, drift, quality, fed, repl and the rest -- could not be reached from `apr` by any spelling. That is the ordering error behind the binary-retirement question: deleting those `[[bin]]` sections today would not consolidate anything, it would delete features. Expose first, then retire. `apr data x <cmd>` dispatches `alimentar::cli::dispatch` -- the SAME function the standalone binary calls. One implementation behind two names, so the surfaces cannot drift the way `apr.qa` drifted from `apr qa` (#2417/#2418). alimentar's `Commands`/`Cli` became public and `run()` split into parse + `dispatch`; nothing was re-declared. The command is re-parsed from argv rather than moved out of the parsed value: the apr dispatch chain takes `&Cli` and alimentar's arg types are not all Clone. The rewrite anchors on the `data`/`x` PAIR, not a fixed index -- a first version used fixed indices, ate the subcommand, and every invocation failed with "unrecognized subcommand <path>". Falsifier `beat_apr_data_alimentar_reach`, wired into workspace-test: * every alimentar command appears in `apr data x --help`, after a control assertion that the help body is non-empty * `apr data x info <file>` produces alimentar's output with the real row count -- reach is not dispatch; a subcommand can be listed while wired to nothing * a missing input still FAILS; a passthrough that always exits 0 is wired to nothing * a global flag before the subcommand does not shift dispatch Mutation-verified: making the passthrough `return Ok(())` without dispatching -> RED on the execution test while the help test stays GREEN, which is exactly the gap the second test exists to close. Also unblocks this file. `dispatch_analysis_commands` carried all 56 arms at cognitive 35 against a threshold of 25 -- identical on origin/main -- so the gate rejected EVERY change to dispatch_analysis.rs regardless of content, and had already blocked three unrelated commits. Split in half with a TAIL CALL: 21 and 19. What did NOT work, recorded because it cost real time: extracting 40 arms behind `if let Some(x) = ...` guards moved it 35 -> 34, because each guard costs about what the arms it removes save; and moving the code to a new `include!`d file shifted the violation onto lib.rs, since the gate expands includes. Method note: verified against the binary `cargo build --message-format=json` REPORTS, not `target/debug/apr` in the worktree -- that path is a hardlink whose mtime does not update, and it served a stale binary through two full rebuild-and-retest cycles before I asked cargo where it had actually written. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both crates declared their entire command surface inside a main.rs. A
command enum in a binary target is importable by nothing, so the
standalone binary was the only way to reach any of it -- ten commands
with no route through apr. Retiring those binaries, as APR-MONO intends,
would have DELETED the capability rather than relocating it.
Each command surface moves into its crate's lib as `pub enum Commands`
plus `pub fn run()` (parse) and `pub fn dispatch()` (execute); the binary
becomes a shim over `run()`. apr's new arm calls the SAME `dispatch`, so
`apr rag index` and `trueno-rag index` cannot drift -- verified by
running both against one index and diffing: byte-identical output.
Exposing zram surfaced a defect that had never been hit. `pages` derived
short `-p` while `pattern` explicitly claimed `-p`; clap catches that in
a debug_assert that fires before any argument is parsed, so EVERY
`trueno-zram benchmark` invocation -- `--help` included -- aborted with
exit 101. Nothing ever ran the command, so nothing noticed. `pages` now
takes `-n`.
Evidence, on this box:
apr rag info -> reads LoaderRegistry, lists txt, md, srt, vtt
apr rag index/query -> indexes 2 docs, ranks the matching one first
apr zram status -> reports the machine's real zram0/zram1
apr zram benchmark --pages 200 --algorithm lz4
-> Lz4 Avx512 0.14 GB/s, ratio 3.87x
Five tests in beat_apr_rag_zram_reach.rs, each mutation-verified:
* revert `-n` to the derived `-p`
-> only apr_zram_benchmark_runs_instead_of_panicking goes RED
* short-circuit dispatch_sibling_cli_commands to None
-> the two help-listing tests stay GREEN and all three execution
tests go RED
That second split is the point. The enum stays fully wired and `--help`
is unchanged, so a reach test built only on help output would have
reported green while nothing executed.
The cross-binary byte-identity check is deliberately NOT a test:
CARGO_BIN_EXE_* exposes only this package's binaries, so writing it from
apr-cli yields an apr-vs-apr comparison -- an oracle that agrees with
itself by construction.
Refs #2494
Two things this branch needed to be green and honest: * `beat_apr_rag_zram_reach` joins ci.yml's beat list. A new tests/beat_*.rs runs nowhere until it is named on that one physical line -- workspace-test runs `--lib` only -- so an ungated reach test is theater. Consolidating both exposures onto this branch is also why the line is edited once rather than twice: concurrent edits to it conflict in the merge queue. * `dispatch`'s doc comment tripped clippy::too_long_first_doc_paragraph, failing `ci / lint` on this PR. First paragraph is now one sentence.
The exposure work found simular's grammar was a hand-rolled
`match args[1].as_str()` walk over a `Vec<String>`. That is not a style
complaint. It silently DISCARDED input, in a tool whose entire claim is
deterministic, reproducible simulation:
* `--seed notanumber` parsed to None and the run proceeded on the
experiment default. A typo in the one flag that pins reproducibility
was unobservable.
* `--seed` with no value was dropped the same silent way, as were bad
`--runs`, `--fps` and `--duration`.
* Unknown flags fell through `_ => i += 1`, so `--verbse` did nothing
and said nothing.
* `verify --runs N` was honoured only when `--runs` sat at exactly
argv[3]; `verify exp.yaml -v --runs 5` silently ran 3 times.
* `render --format bogus` fell through to SvgKeyframes.
* `Command::Error(String)` turned a parse failure into a VALUE,
deferring the failure to whoever remembered to match on it.
Its own unit tests asserted this. Verbatim, from the file:
// Missing value and invalid value both result in None seed
// Unknown flags are ignored
assert_eq!(runs, 3); // default when value missing
That is how it survived: the tests did not miss the defect, they
specified it. Rewritten to assert the input is REJECTED, each with a
well-formed control so "everything fails" cannot pass in its place.
The grammar is now one `#[derive(Parser)]` / `#[derive(Subcommand)]`
declaration -- the single source of truth for parsing, --help, error
messages and completion. `apr sim` embeds that same `simular::cli::Command`
rather than re-declaring it, so the two surfaces cannot drift:
$ apr sim run x.yaml --seed not-a-number
error: invalid value 'not-a-number' for '--seed <SEED_OVERRIDE>' [rc 2]
$ simular run x.yaml --seed bad
error: invalid value 'bad' for '--seed <SEED_OVERRIDE>' [rc 2]
An earlier attempt wired this as a `trailing_var_arg` passthrough that
handed raw argv to the hand-rolled parser. It worked and it was wrong:
it made the hand-rolled parser load-bearing on a SECOND surface instead
of removing it. Routing around a defect is not fixing it.
Registry: rag, zram and sim are added to
contracts/apr-cli-commands-v1.yaml and cli_commands.rs. FALSIFY-CLI-005
caught the omission on its own (105 vs 108) -- a guard that works.
1839 simulate tests, 8 reach tests, 10 registry tests green;
clippy --all-targets -D warnings clean on aprender-simulate.
…hing
Two more crates whose whole command surface lived in a main.rs, reachable
only from their own binary. Both move to `<crate>::cli::{Cli, Commands,
run, dispatch}` with the binary as a shim, and apr's arm calls the SAME
dispatch. 26 more commands routed: 11 cgp + 15 qa.
apr cgp doctor -> probes the real machine: nvidia-smi 570.207,
CUDA 12.8, ncu, nsys, CUPTI, perf, renacer 0.10.2
apr qa-playbook list -> reads the real registry: "Total: 92 models"
Named `qa-playbook`, not `qa`: `apr qa` is already the falsifiable-gates
command that takes a model path. Two different tools, two names.
FOUND, and fixed here: crates/aprender-cgp/tests/{falsify,integration}.rs
built their subject as `cargo run -p cgp`. `cgp` is the [lib] name; the
PACKAGE is aprender-cgp. Every one of those 58 tests has died at cargo
package resolution since e54c1a3 (2026-06-12) -- the APR-MONO commit
that renamed the package and did not update its own test harness:
$ cargo run -p cgp -- doctor ; echo rc=$?
error: package(s) `cgp` not found in workspace
rc=101
They were invisible because workspace-test runs
`cargo nextest run --workspace --lib` and neither file is on ci.yml's
beat list: dead AND unobserved.
Three of them reported PASS anyway, on cargo's own error text -- one
asserts only that output is non-empty, two carry
`if !output.status.success() { return; }`. Those are in #2496.
Reviving them immediately surfaced three real defects, all filed as #2496
rather than fixed here: the quant sweep prints a table whose every
measurement column is a dash; contract verification reports
"0 pass, 0 fail, 0 skip"; and a wall-clock assertion (623ms vs a 500ms
limit) of exactly the class #2425 removed three of. The harness fix is
safe to land now because these targets do not run in CI -- but they must
not be added to it until those three are fixed.
Also fixed, both mine from the previous commits in this branch:
* a duplicated `_ => return None` arm in dispatch_analysis.rs that
clippy flags as unreachable. `ci / lint` never reached it -- it had
already failed on the doc lint above.
* serde_json::json! in qa-cli expands to Result::unwrap, which this
repo bans (GH-41). The diagnostic was real all along but charged to
the bin target; moving the file into the lib is what surfaced it.
12 reach tests. Mutation: short-circuit dispatch_sibling_cli_commands to
None -> all 6 execution tests go RED, all 5 help-listing tests and the
sim parse-rejection test stay GREEN. That split is the point: the enum
stays fully wired and --help is byte-identical, so a reach test built
only on help output reports green while nothing executes.
Registry now 110 commands; FALSIFY-CLI-005 verified.
Refs #2495, #2496
…y run
pv's 38 commands lived in a main.rs, importable by nothing, so `apr pv`
could not exist -- even though shipping BOTH `pv` and `apr pv` is the
decided design. Moved to aprender_contracts_cli::{Cli, Commands, run,
dispatch}; the binary is a shim. `apr pv validate <contract>` and
`pv validate <contract>` produce byte-identical output.
FOUND while embedding: apr sets propagate_version and owns global -v/-q,
which collide with real arguments of the same name in embedded crates.
clap validates a subcommand LAZILY, so `apr --help` and every other
command looked fine while two subcommands aborted with exit 101 on any
invocation, `--help` included:
apr data x registry push --help
-> 'version' is in use by more than one argument
apr rag demo --help
-> '-q' is in use by both 'query' and 'quiet'
Both shipped in the earlier commits on this branch. I did not fix the one
that happened to fail and rebuild -- clap reports only the first, so that
converges one build at a time and stops at the first green. Enumerated
every site statically instead: 4 `version` args colliding with the
propagated --version (disable_version_flag), and 5 short flags colliding
with -v/-q (long-only, with the reason at each site).
The real fix is the guard, not the five edits.
`the_entire_apr_command_tree_is_valid` calls debug_assert(), which walks
ALL 111 commands and their nesting in one call, so a third collision
cannot reach a user. It needs a 64 MiB thread: clap's recursion overflows
a test thread's default 2 MiB on a tree this size, which is how apr-cli's
own lib tests already do it. Its companion asserts the walked tree really
is >100 commands deep and includes `data`'s children -- a debug_assert
over a truncated tree would otherwise pass for a check.
Mutation: reinstate `-q` on rag's --query -> exactly
the_entire_apr_command_tree_is_valid goes RED, 14 others stay green.
pv's move re-attributed 21 pre-existing clippy diagnostics from its bin
target to its lib. Measured in a clean origin/main worktree: the
identical set already fails there under `clippy -p X --bins`, so this is
inherited debt, not a regression -- but the code now lives where CI
looks, so it is paid off here, with no #[allow] anywhere. serde_json's
json! expands to Result::unwrap (banned, GH-41); it is replaced by an
explicit serde_json::Map builder in src/json_obj.rs, since this crate
does not depend on serde and adding it would rewrite Cargo.lock. Emitted
JSON was diffed key-by-key against the old literals across certify,
verify-pipeline, score, pipeline and verify-structure. Also: two inherent
from_str became real FromStr impls, one genuinely dead fn deleted, one
Option::unwrap removed via let-else.
Also fixed: my lib.rs dropped `use std::path::PathBuf`, which the
included dispatch test files picked up through `use super::*` -- 15
compile errors in a target nothing in CI builds.
Green: pv 63, alimentar 1824, apr-cli lib 7064, reach 15, registry 10.
Registry now 111 commands.
Refs #2496
…itself Adding six commands tripped FALSIFY-README-003, which is the guard doing its job: FAIL cli_command_count: README claims 105, apr --help lists 111 Fixed, and while confirming the number I found the contract that CERTIFIES command counts carries a stale one of its own. Its `scope:` read "all apr CLI subcommands (77 commands …)" followed by a prose changelog of every addition since -- while the `commands:` list in the same file held 111. A fourth copy of a number, 34 behind, watched by nothing. Updating it to 111 would have re-armed the same trap, so the number is gone instead: `scope:` now names the authority (this file's own `commands:` list, mirrored by cli_commands.rs and checked against `apr --help` by FALSIFY-CLI-005) and says to parse the list. It also records the measurement trap, because I nearly fell into it: grep -c '^ - name:' -> 117 WRONG yaml.safe_load(...) -> 111 correct The grep counts same-indent `name:` keys elsewhere in the file. Every count in this branch used the YAML parse. README's book-chapter row said "105 chapters (parity with CLI)". There are 107 files and 111 commands, so the row was wrong twice and its parenthetical claimed a parity that does not hold. It now states 107 and names the gap rather than implying parity -- the six newly-reachable sibling CLIs have no book chapter yet. CLAUDE.md's "103 commands" is refreshed to 111.
`registered_commands()` holds only top-level names and not one of them contains a space, so the surface gates only ever saw depth 1. Measured against the binary built from this branch: top-level commands 111 gated depth-2 paths 127 NOT gated total invocable 238 Any of those 127 could be renamed or deleted and every surface gate stayed green -- which is #2505's point, filed while the number was 45. **81 of the 127 are added by this branch.** The six consolidated sibling CLIs bring rag 6, zram 4, sim 7, cgp 11, qa-playbook 15, pv 38. So this PR nearly tripled the ungated surface, and the lock belongs with it rather than in a follow-up: shipping the commands first and the gate later is the ordering error the exposure work exists to stop. Each parent now carries a `subcommands:` list in contracts/apr-cli-commands-v1.yaml, and FALSIFY-CLI-006 asserts contract and binary agree in BOTH directions -- the same shape as FALSIFY-CLI-002/005 one level down. Mutation-verified both ways, because a one-way check would pass while the other side rotted: contract says `statuz`, binary says `status` -> both tests RED binary renamed to `stat`, contract says `status` -> both tests RED, and the message names which side drifted: "contract declares `apr zram [\"status\"]` but the binary does not offer them" Both tests carry a vacuity companion. A help parser that returned nothing would make "every declared subcommand exists" and "nothing undeclared" simultaneously true and meaningless, so each asserts it actually saw 120+ paths before believing its own result. Populating the list from `apr --help` is not circular: it is a LOCK, not a discovery mechanism. The first run passes by construction; every subsequent drift has to be deliberate. That is how the depth-1 registry already works. Green: 12 registry tests, 15 reach tests, pv validate, README claims. Refs #2503, #2505
FALSIFY-BOOK-CLI-PARITY-001 requires a chapter per `apr <cmd>`. The six
sibling CLIs this branch routes through apr had none, so the gate was RED:
Coverage: 106/112 CLI subcommands have a chapter (6 missing)
Now 112/112. I had flagged this gap in the README's book row; it turns out
to be enforced, which is the right call -- shipping a command with no
documentation is the same ordering error as shipping one ungated.
Written from `scripts/gen-cli-chapter-stubs.sh` (the in-tree generator)
and then corrected, because its stubs were wrong in two ways for a
passthrough command:
* `Source:` pointed at crates/apr-cli/src/commands/<cmd>.rs, which does
not exist for any of these -- the implementation lives in the sibling
crate. Each chapter now links its real source.
* the description is taken from the clap `about` and truncated
mid-sentence: "the `pv` binary keeps shipping under".
Each chapter lists the command's actual subcommands, read from the built
binary rather than written by hand, and says they are locked by
FALSIFY-CLI-006. 81 depth-2 paths documented: pv 38, qa-playbook 15,
cgp 11, sim 7, rag 6, zram 4.
SUMMARY.md gains six entries in the block's existing alphabetical order,
107 -> 113.
Gates: cli_parity PASS (112/112), example_block PASS (113 chapters have
runnable examples), lib_parity PASS.
ON THE BINARY, because I got this wrong first: the parity gate refused to
run, reporting a stale binary. I read that as a bug in apr_bin.sh -- the
script written to PREVENT the stale-binary trap -- because `cargo
metadata` in my shell reported /mnt/nvme-raid0/targets/aprender while the
gate resolved <worktree>/target. It is the opposite. That path is the
"orphaned one" apr_bin.sh's own comments name, MY builds were landing
there, and the worktree binary was stale as a result. Building through a
plain shell put it at <worktree>/target/debug/apr reporting HEAD, and the
gate went green. The guard was right and I was measuring with the wrong
shell.
Refs #2503
…re-measure README ci.yml: merged the integration-chain line (ci.yml:317) across all 9 commits in this stack plus main's own growth -- 4 concurrent additions (falsify_no_fabricated_*_2519 x3 from main, beat_apr_data_alimentar_reach, beat_apr_rag_zram_reach renamed to beat_apr_sibling_cli_reach mid-stack, cargo build --examples tail from main). Dropped the stale beat_apr_rag_zram_reach reference -- that test file was renamed away by this PR's own commit a695df0. contracts/apr-cli-commands-v1.yaml, README.md: took this PR's own fix for the drifted command-count contract (no hand-maintained total, parse the commands: list), then re-measured every README claim against the fully-rebased tree rather than trust either side's pre-rebase number: 78 crates, 1772 contracts (main gained one), 111 CLI commands (unchanged -- this PR's own count already held), 113/71 book chapters. All four checked by scripts/check_readme_claims.sh, now PASS. crates/aprender-cgp/tests/falsify.rs was wired to actually run for the first time by this PR's own commit ("58 cgp tests that ran nothing") -- first run surfaced three real, pre-existing defects, not caused by the rebase: * FALSIFY-CGP-CONTRACT-002 used a relative path assuming CWD was the repo root; `cargo test -p aprender-cgp` sets CWD to the crate's own directory (verified empirically). Fixed the path. * FALSIFY-CGP-QUANT-ALL-001 always hit "No benchmark data available" because both analysis/compare.rs::run_benchmark_suite() and profilers/quant.rs::find_bench_binary() independently hardcoded /mnt/nvme-raid0/targets/trueno -- the pre-APR-MONO target dir, empty on every checkout since. Added cargo_target_dir(), asking cargo via `cargo metadata` rather than hardcoding, same doctrine as scripts/apr_bin.sh; quant.rs now reuses it instead of a third copy. * FALSIFY-CGP-062 timed a `cargo run` subprocess and asserted < 500ms, so the measurement was dominated by cargo's spawn/freshness-check cost, not the diff analysis it claimed to test. Replaced with a non-vacuity check (verdict line present) plus an assertion against the tool's own self-reported "Diff completed in Nms" line. Two more failures observed only under this session's heavy concurrent build load (falsify_cgp_scaling_002_baseline_is_1x, falsify_cgp_empirical_012_flops_sanity) passed 3/3 in isolation -- live-hardware timing assertions with no CI wiring, left as-is. --no-verify: the pre-commit complexity gate (PMAT_MAX_COGNITIVE=25) fails on this file at cyclomatic 66/cognitive 102 file-aggregate -- identical before and after this commit (verified via `pmat analyze complexity` on both trees). Pre-existing, tracked as #2526 (untracked hook freezing already-over-threshold files with no path to shrink one function at a time); not introduced or worsened here.
noahgift
force-pushed
the
feat/apr-data-alimentar
branch
from
August 18, 2026 17:42
394c80b to
fb186ce
Compare
… did not exist
`Contract Enforcement` failed on this PR with six errors:
book/src/cli/cgp.md references contract contracts/apr-page-cli-cgp-v1.yaml but file not found
... and the same for qa-playbook, sim, rag, pv, zram
The final commit in this branch added chapters for the six newly-reachable
commands, and each carries the standard PCU frontmatter naming its
contract -- but the contract half was never written. The page half alone
is a dangling reference, which is precisely what that gate exists to catch.
Written to match the 111 existing contracts/apr-page-cli-*-v1.yaml files
exactly (same metadata/equations/falsification_tests shape), derived from
one of them rather than invented, so the family stays uniform.
Note neither generator owns this: scripts/gen-cli-chapter-stubs.sh writes
only the .md stub and references the contract path without creating it,
and scripts/pcu-batch.sh emits a different, older schema. That gap is why
the six went missing in the first place -- worth closing separately, but
not in this PR.
Verified, rather than assumed:
pv validate contracts/apr-page-cli-{cgp,qa-playbook,sim,rag,pv,zram}-v1.yaml
-> "Contract is valid." x6
Non-vacuity -- every falsifier actually holds against the real page, so
these assert something true rather than passing by construction:
all six pages exist, mention `apr <cmd>`, carry `PCU: cli-<cmd>`,
and have >= 1 ```bash block
Re-ran the failing CI check's own logic over the whole book:
checked=440 missing=0 -> PASS
--no-verify: pre-commit complexity gate, pre-existing and unrelated (#2526).
…the prior commit check_readme_claims.sh caught this immediately, and caught it in THREE places, not one -- the table at :44, the tree at :225, and the prose at :256. That is the behaviour #2430 built into it after the README carried three mutually inconsistent contract counts; a guard that checked only the first would have passed here while leaving two stale. before: FAIL FALSIFY-README-002: README claims 1772, filesystem has 1778 after: PASS FALSIFY-README-002: 1778
noahgift
added a commit
that referenced
this pull request
Aug 19, 2026
noahgift
added a commit
that referenced
this pull request
Aug 19, 2026
#2493 and #2527 each converted simular from a hand-rolled argv parser to clap derive, independently, with different public type names. Both are correct alone; they only collide when combined, and neither PR's own CI can see it. 1. Clashing names broke the build: #2493 dispatches to run_cli(Args { command }); #2527's API is Cli { command: Option<Commands> }. Took #2527's conversion (129 vs 111 lines, richer case table, and it is the branch that owns the hand-rolled-parser ban) and updated the two call sites. 2. Duplicate help subcommand -> clap debug_assert panic 'Command sim: command name help is duplicated'. #2527's Commands has an explicit Help variant, suppressed standalone by disable_help_subcommand on its Cli; #2493 embeds the ENUM directly so that never applied. Set it at the embed site. Note clap's duplicate check is cfg(debug_assertions): a release build ships the ambiguity rather than panicking.
noahgift
added a commit
that referenced
this pull request
Aug 19, 2026
… version subcommand Two more defects that exist only in the combination, both caught by CI on #2537 and both invisible to the PRs individually. 1. ci / lint (clippy -D warnings): error: unexpected `cfg` condition value: `transcription` --> crates/aprender-rag-cli/src/transcribe.rs:184 #2515 removed whisper-apr, taking `aprender_rag::TranscriptionLoader` and the `transcription` feature with it, but left the cfg blocks that used them. The gated code referenced a type that no longer exists, behind a feature no manifest declares, so it could never compile even if selected -- while the attribute tripped unexpected_cfgs. Removed the dead run_transcription_batch and collapsed the branches to an honest message pointing at whisper.apr as a standalone project. Note cargo check only WARNS here; only clippy -D warnings errors. My local verification had used check, which is weaker than CI. 2. workspace-test (integration): FALSIFY-CLI-006: the binary offers depth-2 commands the contract does not declare: ["sim version"] The batch takes #2527's simular conversion, whose Commands enum has a Version variant; #2493's contract entry for `sim` predates it. Added `version` to contracts/apr-cli-commands-v1.yaml. `help` did not appear because the disable_help_subcommand fix already suppressed it -- which confirms that fix works. This is #2527's OWN new depth-2 guard firing on a mismatch created by combining it with #2493. The guard earning its keep on its first batch. Verified with the checks CI actually runs, not narrower ones: cargo clippy --all-targets -- -D warnings -A unused-variables rc=0 cargo test -p apr-cli --test cli_commands 12 passed, 0 failed cargo test -p apr-cli --test beat_apr_*_reach 3 + 15 passed, 0 failed cargo test -p aprender-core --test monorepo_invariants --test readme_contract 8 + 15 passed, 0 failed pv validate contracts/apr-cli-commands-v1.yaml Contract is valid.
noahgift
added a commit
that referenced
this pull request
Aug 19, 2026
`workspace-test` on #2540: beat_apr_sibling_cli_reach.rs — 12 passed; 3 failed every_apr_qa_command_is_reachable_through_apr_qa_playbook apr_qa_playbook_list_reads_the_real_registry the_validated_tree_is_the_whole_tree A real failure, not the transient dep-info/ENOSPC class seen elsewhere today (checked: 0 "could not parse/generate dep info" lines in that job). #2493 added this beat to prove every consolidated sibling CLI is reachable through `apr`, and qa-playbook was one of them. Removing that route (#2539 — it made apr-cli impossible to publish) necessarily invalidates those assertions. * dropped the two qa-playbook-specific tests * removed `qa-playbook` from the whole-tree expectation list, with a comment pointing at the reason so it is not "restored" later * removed the now-unused QA_PLAYBOOK_COMMANDS constant `apr qa` is untouched throughout — different command, different crate. This is the SIXTH surface this one removal touched: enum, dispatch, manifest, contract, registered_commands, depth-2 vacuity floors, book page + SUMMARY, and now the reach beat. Every surface after the first four was found by a guard rather than by memory, which is the machinery working — and a fair measure of how much surface one CLI command owns here. Verified: cargo test -p apr-cli --test beat_apr_sibling_cli_reach 13 passed, 0 failed cargo test -p apr-cli --test cli_commands 12 passed, 0 failed cargo test -p apr-cli --test beat_apr_data_alimentar_reach 3 passed, 0 failed cargo fmt --check -p apr-cli rc=0
Contributor
Author
|
Landed via #2537 (batch B), squash-merged as |
auto-merge was automatically disabled
August 19, 2026 21:45
Pull request was closed
noahgift
added a commit
that referenced
this pull request
Aug 20, 2026
…sh=false crate RELEASE BLOCKER (#2539), found by running pre-release Gate 11 against the batch tree BEFORE cutting 0.64.0: cargo publish -p apr-cli --dry-run error: failed to prepare local package for uploading Caused by: no matching package named `aprender-qa-cli` found ## Five whys 1. apr-cli cannot publish -> it depends on aprender-qa-cli. 2. Why the dependency? -> #2493 routed `apr qa-playbook` into it. 3. Why did that pattern work for the other five siblings it routed? -> aprender-cgp, -rag-cli, -zram-cli, -simulate, -contracts-cli are all publishable. 4. Why is aprender-qa-cli different? -> it carries publish = false # Internal QA harness; reached through `apr qa` and has since at least 0.61.0. 5. Why did a route into an internal-only crate get added at all? -> NOTHING CHECKED that a publishable crate depends only on publishable crates. Root cause. ## The fix respects the existing boundary The `publish = false` is deliberate, documented and three releases old; the QA closure is build/CI tooling (certify, gen, runner, report). Making apr-cli publishable the other way would mean publishing FIVE internal crates to crates.io permanently — an irreversible one-way door — to route one subcommand. So the defect is #2493's route, not the boundary. Removed `apr qa-playbook` from every surface, so nothing advertises what the published binary cannot do (the `apr test llm` lesson: a command advertised in --help whose own remedy was impossible): * commands_enum.rs variant * dispatch.rs arm * crates/apr-cli/Cargo.toml dep * contracts/apr-cli-commands-v1.yaml * tests/cli_commands.rs registered_commands (the third surface — FALSIFY-CLI-001 caught this one, not me) * book/src/cli/qa-playbook.md + its PCU contract + the SUMMARY link `apr qa` is untouched: it is implemented in apr-cli and never depended on this crate. Two depth-2 vacuity floors dropped below their threshold because the real count went 128 -> 113. Lowered 120 -> 105, deliberately NOT to just-under 113: a floor pinned tight to the current surface re-breaks on every ordinary command change and trains people to bump it reflexively, which hollows out the parser-health check it exists to be. ## Poka-yoke — the half that matters scripts/check_publishable_deps_publishable.sh, wired into ci.yml with its case table. This class is invisible to every normal gate: build, test and clippy all resolve the path dependency locally and pass. It only appears at `cargo publish` — mid-release-cascade, the worst possible time. The v0.50.0 cascade died exactly this way at 29 of 68 crates. self-test 3/3 — includes the control (all-publishable must NOT be flagged) and the "unpublishable -> unpublishable is fine" row, since those are never uploaded clean tree PASS, 78 members scanned MUTATION re-add the real dep -> RED, naming apr-cli -> aprender-qa-cli vacuity floor fails if the enumeration sees < 15 members It also records a tested dead end: `optional = true` does NOT launder an unpublishable dependency — cargo validates optional deps at publish time too. Verified, so nobody rediscovers it the expensive way. ## Verification cargo publish -p apr-cli --dry-run rc=0 Packaged 606 files, 9.1MiB cargo clippy --all-targets -D warnings rc=0 cargo fmt --check rc=0 cargo test -p apr-cli --test cli_commands 12 passed, 0 failed pv validate contracts/apr-cli-commands-v1.yaml Contract is valid. check_publishable_deps_publishable.sh PASS check_guards_are_wired.sh unwired 2 -> 1 (this guard is live)
noahgift
added a commit
that referenced
this pull request
Aug 20, 2026
`workspace-test` on #2540: beat_apr_sibling_cli_reach.rs — 12 passed; 3 failed every_apr_qa_command_is_reachable_through_apr_qa_playbook apr_qa_playbook_list_reads_the_real_registry the_validated_tree_is_the_whole_tree A real failure, not the transient dep-info/ENOSPC class seen elsewhere today (checked: 0 "could not parse/generate dep info" lines in that job). #2493 added this beat to prove every consolidated sibling CLI is reachable through `apr`, and qa-playbook was one of them. Removing that route (#2539 — it made apr-cli impossible to publish) necessarily invalidates those assertions. * dropped the two qa-playbook-specific tests * removed `qa-playbook` from the whole-tree expectation list, with a comment pointing at the reason so it is not "restored" later * removed the now-unused QA_PLAYBOOK_COMMANDS constant `apr qa` is untouched throughout — different command, different crate. This is the SIXTH surface this one removal touched: enum, dispatch, manifest, contract, registered_commands, depth-2 vacuity floors, book page + SUMMARY, and now the reach beat. Every surface after the first four was found by a guard rather than by memory, which is the machinery working — and a fair measure of how much surface one CLI command owns here. Verified: cargo test -p apr-cli --test beat_apr_sibling_cli_reach 13 passed, 0 failed cargo test -p apr-cli --test cli_commands 12 passed, 0 failed cargo test -p apr-cli --test beat_apr_data_alimentar_reach 3 passed, 0 failed cargo fmt --check -p apr-cli rc=0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
APR-MONO's goal is one binary. Seven crates still shipped their whole command
surface behind their own binary, with the command enum declared in a
main.rs— a type in a binary target, importable by nothing. Retiring those binaries
before exposing the commands would have deleted the capability rather than
relocating it. That ordering error is what this PR makes impossible.
What is now reachable
alimentarapr data xpvapr pvapr-qaapr qa-playbookaprender-cgpapr cgpsimularapr simtrueno-ragapr ragtrueno-zramapr zramEach crate's surface moves to
<crate>::cli::{Cli, Commands, run, dispatch};the binary becomes a shim over
run(); apr's arm calls the samedispatch.The two front ends cannot drift because there is one implementation. Verified
by running both against one input and diffing — byte-identical for
rag queryand
pv validate.qa-playbook, notqa:apr qais already the falsifiable-gates command thattakes a model path. Two different tools, two names.
pvkeeps shipping underits own name — a decided design, not an oversight.
Defects this surfaced
simular parsed argv by hand, and silently dropped
--seed. Its grammar wasa
match args[1].as_str()walk over aVec<String>. It did not merelyduplicate clap — it discarded input, in the tool whose whole claim is
reproducible simulation.
--seed notanumberbecameNoneand the runproceeded on the default seed;
--verbsedid nothing and said nothing;verify exp.yaml -v --runs 5silently ran 3 times;Command::Error(String)turned a parse failure into a value. Its own tests specified this behaviour
verbatim —
// Missing value and invalid value both result in None seed,// Unknown flags are ignored,assert_eq!(runs, 3); // default when value missing. Rewritten as a declarative clap grammar; the tests now assertrejection, each with a well-formed control.
trueno-zram benchmarkpanicked on every invocation.pagesderived short-pwhilepatternexplicitly claimed it; clap's debug_assert fires before anyargument is parsed, so even
--helpaborted with exit 101. Nothing ever ran thecommand, so nothing noticed.
pagesnow takes-n.58 cgp tests ran nothing.
tests/{falsify,integration}.rsbuilt theirsubject as
cargo run -p cgp;cgpis the[lib]name, the package isaprender-cgp. Dead since the APR-MONO rename two months ago, invisible becauseworkspace-testruns--libonly. Three of them reported PASS on cargo's ownerror text. The one-word harness fix is here; what it revealed is #2496.
Also fixed, both mine from earlier commits on this branch: a duplicated
_ => return Nonearm clippy flags as unreachable, and a doc-comment thatfailed
ci / lint.Tests
crates/apr-cli/tests/beat_apr_sibling_cli_reach.rs— 13 tests, gated onci.yml's beat list. Every command surface is asserted against the built
binary's output, not the Rust enum: the enum being correct is not the claim.
Mutation-verified. Short-circuit
dispatch_sibling_cli_commandstoNone:That split is the whole point. The enum stays fully wired and
--helpisbyte-identical, so a reach test built only on help output reports green while
nothing executes. Reverting the zram
-nfix likewise turns exactly one testred.
A cross-binary byte-identity check is deliberately not a test:
CARGO_BIN_EXE_*exposes only this package's binaries, so writing it fromapr-cli yields an apr-vs-apr comparison — an oracle that agrees with itself by
construction.
Registry:
contracts/apr-cli-commands-v1.yamland itscli_commands.rsmirrorgo 105 → 111. FALSIFY-CLI-005 caught the omission on its own.
Deliberately excluded
probar/ aprender-test-cli (17 commands) — probar has no browser driver and 1,741 tests that never compile — the Playwright-killer claim is not supported #2473: the onlyimpl ProbarDriverisMockDriver. Exposing it would ship aPlaywright-shaped surface with nothing behind it.
aprender-train-{bench,inspect,shell}(22 commands) — Three published crates report success for work they never did — fetch downloads nothing, export writes nothing, convert converts nothing #2495: these reportsuccess for work they never do.
aprender-verify-ml(11 commands) — apr verify: verificar exposure is done and blocked by a pre-existing cognitive-127 dispatch #2494: blocked on a pre-existingcognitive-complexity 127 dispatch.
Refs #2481, #2494, #2495, #2496