Skip to content

/nm: next unmodelled item (FSM leak) — next_leak tool + command - #227

Merged
thedavidmeister merged 5 commits into
mainfrom
2026-08-06-nm-command
Aug 7, 2026
Merged

thedavidmeister merged 5 commits into
mainfrom
2026-08-06-nm-command

Conversation

@thedavidmeister

@thedavidmeister thedavidmeister commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Closes #222

/nm — the next unmodelled item for the human to review, fourth sibling of /nr, /ncc and /nd (#220). The subjects are the FSM-conformance leaks: open producer PRs the lane classifier buckets into NO modeled state — a trusted 🤖 ai:producer note implies a state, no ai:* label records one. The dashboard's leaks box, the one that should read zero.

The next_leak MCP tool

  • The population is classify_lane's own Leak verdict. Issue 222's two clauses — "the same machinery the leaks array comes from" and "the PRs the lane classifier buckets into NO modeled state" — were in conflict, because the pre-existing enumeration was neither: it was the set "carries no ai:* label". human:* labels are not ai:*-prefixed, so every PR parked in the human-decisions lane satisfied it while sitting in a perfectly modeled state. Measured over the pipeline's orgs on 2026-08-06: four of the five reported leaks were human:design PRs waiting on a ruling from the same human reading the leak box, and the one PR genuinely in no state (rain.flow#475) sorted last behind them, below the page cap of 3 — so at the default limit: 1 the tool handed back a PR whose true answer is none of nm.md's three findings. counts.leaks and the dashboard panel read the same array and were wrong in the same four places. The conflict is resolved toward the classifier: is_leak_candidate asks classify_lane itself, and a repo_root_tests pin requires the enumeration to select through it — the cc_gate precedent one lane over, where the vetter's flag inbox and the human's are pinned to one classifier for exactly this reason. Fixing the population fixes the dashboard too.
  • Shared enumeration, never a parallel search. producer_pr_inventory() (the one gh search prs --author read, archived withholding per next_close_candidate offers flags from archived repos, where no ruling can be written #206) and leak_scan() (the trusted-comment read over leak_reason) are called by both human-queue and the tool.
  • Oldest first, and the row says the age. The rows previously arrived in gh search prs order — newest-first — so the longest-unmodelled PR sank below the cap; on the measured population the oldest leak was also the only genuine one. rank_leaks orders oldest-first with the argument stated: a leak is in nobody's queue (not the producer's, not the vetter's), so the harm is exactly how long it sits. createdAt is on every row and on the dashboard array.
  • The row is the leak's evidence. has.producerNote (truncation flagged), lacks.aiStateLabel + the full label list, and classifier.lane/classifier.state — the classifier's verdict recomputed live, leak/leak on an honest row. That last field replaces a drift check that could not work: lacks.aiStateLabel was structurally always null, which is precisely why the wrong population was invisible on the artefact.
  • limit refused out of range (1..=3, default 1), never clamped, mirroring next_ready_limit.
  • Empty is the HEALTHY answer, said in as many words: health reads healthy-zero-leaks-every-open-producer-pr-is-in-a-modeled-state only at zero leaks over a fully read population. Fail-safe on the other side: candidates whose comment read failed are counted (counts.leakUnknown), named (fetchErrors), and a zero over them reports as zero-leaks-found-but-unread-prs-remain-not-proven-health. leak_scan_with takes the comment read as an injected seam so that branch is reachable by test — it previously had none, and deleting it left the whole suite green.
  • Byte budget mirrors the siblings: per-field raw caps, compile-time const assertion that a maximal page plus the withheld list cannot reach MCP_MAX_RESULT_BYTES, adversarial-input test exercising it.
  • Registered on the Human profile only, narrows: limit; table/profile counts in the drift tests updated (20 → 21 tools, 5 → 6 narrowing).

plugins/human-fsm/commands/nm.md

Modeled on nr.md/ncc.md: same LIMIT convention (argument passed verbatim, the binary's refusal relayed rather than rounded), same typed-reads/no-shell declaration, same second-opinion posture. The command's core is the issue's three-way split — a leak is a defect to locate, in exactly one of three places, and the deliverable is naming which:

  • the PR's state record is wrong → the state it belongs in and the ONE command that files it there;
  • the machine's vocabulary is missing a state → the missing state and its consuming transition, via the design path (which, post-RULED: an answered design question routes straight to producer rework, same as a rejection #219, carries its answer straight to the producer as the work order — no human:design, no park spelling anywhere in the file);
  • the classifier is wrong → the fix is the classifier, never the instance; the finding is stated precisely enough to file as an issue, and the classifier's actual rule is quoted in the command so the row's evidence is checked against the rule as written before any conclusion.

The healthy-empty answer is presented as the good outcome it is, with the leakUnknown caveat carried alongside.

Plugin manifest and the enumerations

human-fsm 0.11.0 → 0.13.0, in both the plugin manifest and the marketplace listing (the plugin-version-lockstep gate), with /nm in both descriptions. 0.13.0 rather than 0.12.0 because the sibling /nd PR (#226) claims 0.12.0: two open PRs cannot ship one version string, since /plugin detects updates by comparing versions and the second to land would serve new content under a version installers have already seen. 0.13.0 is past it whichever order the two land in.

The three places that ENUMERATE the human's surface now name next_leak / /nm, exactly as #226 names next_design / /nd: CLAUDE.md's profile table, README.md's human-profile paragraph, and plugins/human-fsm/README.md's command table and reads section. An enumeration that omits a shipped tool is wrong the moment its PR lands. These lines are the expected merge conflict with 226 — resolution is to keep both tools in each list; main gets merged in, never rebased.

Merged with main (#224, #225)

Both landed under this PR and both interact with its subject, so the merge is part of the change rather than a rebase over it. human:design (#224) and ai:blocked-deploy (#225) are DELETED; main's deletions won everywhere, and nothing here reintroduces either in any spelling. Conflicts were two files, both plugin-version JSONs, both trivial — this branch is 0.13.0 and remains distinct and ahead of main's 0.12.0. main.rs auto-merged clean, which is exactly the case that has bitten twice today, so the work was in what the markers could not show:

  • The suite caught one: the drift witness in a_row_carries_the_leaks_own_evidence asserted a human:design PR classifies into the human-decisions lane. That state is gone, so it now classifies as a leak and the assertion failed on the merge commit. Repointed at human:reject, the lane's surviving state.
  • The compiler caught one: is_leak_candidate's test referenced the deleted RETIRED_BLOCKED_DEPLOY_LABEL.
  • Only reading ai:blocked-deploy: migrated as a once-off, state deleted #225's own commit caught the third, and no existing test would have: fdf2ee0 ruled that a PR hand-wearing a deleted ai:* string lands un-vetted, not leak — justified by the caller-side ai_state_label partition that this PR replaces. As auto-merged, such a PR became a leak again, silently overturning a ruling that landed hours earlier. is_leak_candidate now carries that ruling as an explicit conjunct with the reasoning recorded, and the registry property below pins it. It cannot reintroduce the defect this predicate exists to fix: that defect was human:* labels parked in a real lane, which the classifier arm excludes.

Three pieces of coverage so none of that is hand-verified next time (the human's call — the checks themselves become tests):

  1. every_lane_is_reachable_from_some_label_set — one witness per Lane, driven off a new Lane::ALL, so an arm going unreachable behind a precedence change fails, and a lane added later is covered on the day it is added.
  2. DELETED_LABELS + a_deleted_label_models_nothing_anywhere — one property over a registry, replacing the two bespoke per-deletion tests RULED: an answered design question routes straight to producer rework, same as a rejection #219 and RULED: migrate the ai:blocked-deploy residue to ai:reject — rework each PR for the split release lifecycle #221 each shipped. Per label: the ordinary unmodelled fallthrough, dominates nothing, no lane cell (checked across Lane::ALL), no counts key. The next deletion adds a row of DATA.
  3. lanes_doc_emits_every_state_with_the_right_members derives every total from per-row expectations instead of literals — that assertion is the one that read 3 on two branches and was wrong at 4 once both landed, with nothing pointing at it.

The docs now state the post-deletion truth: a RETIRED label stays bucketed and never leaks; a deleted ai:* label is absorbed as un-vetted; a deleted human:* label DOES leak, because the vetter's sweep is ai:*-scoped and nothing else claims it.

Coordination

Sibling work in flight in this same file: #219, #130, #220, #221. This PR scopes to next_leak + nm.md + the manifest bump; on conflict, main gets merged in.

QA

  • Discriminating tests: next_leak_tests::{the_leak_population_is_the_classifiers_own_verdict_not_a_label_spelling, a_failed_comment_read_is_unknown_and_never_a_clean_bill, the_leak_queue_is_oldest_first_and_the_page_is_a_prefix_of_it, the_page_size_defaults_to_one_and_is_refused_outside_its_range, a_row_carries_the_leaks_own_evidence, an_oversized_note_and_label_list_are_clipped_and_say_so, an_empty_queue_is_the_healthy_answer_and_says_so, a_populated_queue_reports_leaking_and_what_the_page_left_behind, a_maximal_page_of_adversarial_rows_still_fits_the_budget, the_fixed_allowances_cover_a_row_a_withheld_entry_and_an_envelope} plus repo_root_tests::the_leak_population_is_derived_from_the_lane_classifier. Each fails on base because the functions they exercise do not exist there (the suite does not compile at base); the mutation table below shows each failing the moment its behavior is broken while the rest stay green. The two population tests are the BLOCKER's own regression: reverting is_leak_candidate to the ai:*-label rule fails both. Full suite on the branch: 1013 passed / 0 failed in the main binary (+3/9/47/8/7/4 integration), up from 1010 — the three new tests are the population, the fail-safe seam and the ordering.
  • Mutations applied (16, each on the committed tree, suite run, restored; 16 killed, 0 survived): is_leak_candidateai_state_label(labels).is_none() (the exact pre-existing defect) → killed by the_leak_population_is_the_classifiers_own_verdict_not_a_label_spelling + the repo_root_tests pin; candidate filter → if true → killed by the pin; drop unreadable.push (the sole survivor of the previous pass) → killed by a_failed_comment_read_is_unknown_and_never_a_clean_bill; leak_queue_health ignores unreadable → killed by that test + an_empty_queue_is_the_healthy_answer_and_says_so; drop rank_leaks → killed; reverse the comparator to newest-first → killed by the_leak_queue_is_oldest_first_…; hardcode classifier to leak/leak → killed by a_row_carries_the_leaks_own_evidence; clamp the limit → killed; blank producerNote → killed; lie on producerNoteTruncated → killed; drop the note clip → killed (incl. the budget test); zero the more arithmetic → killed; blank createdAt → killed.
  • Oracle: issue 222's own definition ("the PRs the lane classifier buckets into NO modeled state"), resolved against the classifier where its two clauses conflicted; the sibling tools' established contracts (next_ready's prefix property, next_close_candidate's deliberate-and-argued ordering, cc_gate's one-classifier pin); and hand-built fixtures whose expected JSON is written out literally, independent of the builders. The live population was verified twice against the binary's own search args before and after the fix.
  • Category check: issue asks (1) a next_leak MCP tool off the same leak-detection machinery with typed result, refused limit, evidence rows, truncation flags, healthy-empty answer; (2) nm.md on the nr/ncc model with the given frontmatter, LIMIT convention and three-way defect-location posture in post-RULED: an answered design question routes straight to producer rework, same as a rejection #219 vocabulary; (3) plugin version bump — covered (1), (2), (3). Also fixed, in scope because it IS the issue's own third finding class turned on this code: the leak population, the queue order, and the untested fail-safe. Not covered by design: the sibling PRs' scopes (RULED: an answered design question routes straight to producer rework, same as a rejection #219, human-queue --json emits counts but not the state vocabulary, so consumers hand-copy it and drift silently #130, /nd: next ai:design item to rule on — next_design tool + command, sibling of /nr and /ncc #220, RULED: migrate the ai:blocked-deploy residue to ai:reject — rework each PR for the split release lifecycle #221).
  • Gates, all run locally in the foreground: cargo test (1013+78 green); CI-exact nix develop github:rainlanguage/rainix/53e96a7…#rust-shell -c rainix-rs-static (rustfmt --check + clippy -D warnings -D clippy::all, changed file touched first) exit 0; full pre-commit run --all-files bundle clean and idempotent on re-run (denofmt is what made the first static job red); nix build .#pr-review-report exit 0, which runs the release-profile suite inside the derivation.
  • Review note: CodeRabbit was rate-limited on this PR rather than reviewing it, so "0 unresolved threads" here is vacuous — a green rollup with nothing behind it. Flagging it explicitly because that is exactly the distinction next_ready encodes and this PR should not benefit from the ambiguity.

🤖 Generated with Claude Code

thedavidmeister and others added 2 commits August 6, 2026 11:04
The FSM-conformance leaks — open producer PRs the lane classifier buckets
into NO modeled state — had a canonical view (human-queue --json's `leaks`
array, the dashboard box that should read zero) and no one-call entry point:
locating a leak started from the whole org-wide queue render.

next_leak (Human profile): the producer-PR enumeration behind that array is
extracted into producer_pr_inventory() + leak_scan(), shared by human-queue
and the tool, so the two are one computation and the tool answers a PREFIX
of the canonical array in its own order — never a parallel search, never a
second ordering (#121). The row carries the leak's evidence, both halves:
has.producerNote (the trusted note implying a state, truncation flagged) and
lacks.aiStateLabel (the classifier's own answer, computed rather than
asserted null, the full label list beside it). limit is refused out of
1..=3, never clamped. Empty is the HEALTHY answer and the typed `health`
field says so; unlabeled PRs whose comment read failed are counted and
named, and a zero over them reports as unproven, never as health.
human-queue's own emission is unchanged.

nm.md: the command's posture is the issue's three-way split — a leak is a
defect to LOCATE, in exactly one of three places, and the deliverable is
naming which: the PR's state record (the state it belongs in + the ONE
command that files it there), the machine's vocabulary (the missing state +
its consuming transition, via the design path), or the classifier (fix the
classifier, never the instance; the finding filed as an issue). Written
against the post-#219 world: no human:design, no park spelling; a design
ruling carries its answer to the producer as the work order. Same LIMIT
convention and refusal-relay language as nr.md/ncc.md, same typed-reads /
no-shell declaration.

human-fsm 0.11.0 → 0.12.0 in the plugin manifest and the marketplace
listing (plugin-version-lockstep).

Closes #222

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nd set

The frontmatter conformance test pins the exact command set the plugin ships
(a rename or an unshipped file fails there rather than at install); nm joins
the expected list now that nm.md ships. Formatting is cargo fmt's own.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@thedavidmeister thedavidmeister self-assigned this Aug 6, 2026
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@thedavidmeister, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 50 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 65051e47-1567-4e6f-8dd4-35496687e3b7

📥 Commits

Reviewing files that changed from the base of the PR and between 171efa6 and b90d064.

📒 Files selected for processing (7)
  • .claude-plugin/marketplace.json
  • CLAUDE.md
  • README.md
  • plugins/human-fsm/.claude-plugin/plugin.json
  • plugins/human-fsm/README.md
  • plugins/human-fsm/commands/nm.md
  • pr-review-report-rs/src/main.rs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…e siblings are listed

Three things, all reconciliation with what landed and what CI actually runs.

The rust workflow's static job runs `pre-commit run --all-files` after
rainix-rs-static, and its `denofmt` hook formats markdown. It skipped
locally while nm.md was untracked, so the reflow only surfaced in CI as a
red static job. The hook's own output is now committed, and a second run
is clean.

/nd (PR 226) bumped human-fsm to 0.12.0 for its own command, which is the
version this branch had claimed. Two open PRs cannot ship the same
version — `/plugin` detects updates by comparing version STRINGS, so the
second to land would serve stale content under a version installers have
already seen. This goes to 0.13.0, which is past /nd's whichever order the
two land in.

And the three places that ENUMERATE the human's surface — CLAUDE.md's
profile table, README's human-profile paragraph, the plugin README's
command table and reads section — now name next_leak and /nm, as /nd named
next_design and /nd. An enumeration that omits a shipped tool is wrong the
moment its PR lands, and these lists are what a reader consults instead of
the tool table.

nm.md's "wrong state record" finding also picks up the retired-label case:
a label the FSM has deleted lands a PR in this queue exactly as an absent
one does. Written as the property rather than naming a label, because the
deletion in flight (#225's ai:blocked-deploy) is not the only one that will
ever happen and this command should not need editing for the next.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
thedavidmeister added a commit that referenced this pull request Aug 7, 2026
…n flight

Three open PRs bump human-fsm from 0.11.0 and two of them claimed
0.12.0. `/plugin` detects an update by comparing version STRINGS, so
the second of a duplicate pair to land serves new content under a
version installers have already seen — the stale-content bug the
version-hygiene gate exists to catch.

Claims are now distinct and monotonic in merge order: #224 (0.12.0),
#227 (0.13.0), this (0.14.0). This PR merges last of the three; landing
it earlier means re-bumping past whatever main then carries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
thedavidmeister and others added 2 commits August 7, 2026 07:22
…ding of the labels

The BLOCKER an independent review found, and it is this command's own third
finding class turned on the command: the classifier was wrong, so the fix is
the classifier and not the instance.

`next_leak` and `human-queue` both enumerated leaks as "carries no `ai:*`
label". The DEFINITION everywhere else — the tool description, nm.md, CLAUDE.md,
README, the dashboard box — is "the PRs `classify_lane` buckets into no modeled
state". Those are different sets: `human:*` labels are not `ai:*`-prefixed, so
every PR parked in the human-decisions lane satisfied the first while sitting in
a perfectly modeled state, waiting on a ruling from the same human reading the
leak box. Measured over the pipeline's orgs on 2026-08-06: four of five reported
leaks were `human:design` PRs, and the ONE PR genuinely in no state sorted last
behind them, below the page cap of 3. At `limit: 1` the tool handed back a PR
whose true answer — "no defect, this is waiting on you" — is not one of the
three findings nm.md forces a diagnosis into. `counts.leaks` and the dashboard
panel read the same array, so they were wrong in the same four places.

`is_leak_candidate` now asks `classify_lane` itself, and a `repo_root_tests`
pin requires the enumeration to select through it — the `cc_gate` precedent one
lane over, where the vetter's flag inbox and the human's are pinned to one
classifier for the same reason. A precedence change in the classifier now
carries itself into this population with nothing here to update.

Beyond the population:

- ORDER. The rows arrived in `gh search prs` order, which is newest-first, so
  the longest-unmodelled PR sank below the cap — on the measured population the
  oldest leak was also the only real one. `rank_leaks` makes it oldest-first and
  argues why: a leak is in nobody else's queue, so the harm is how long it sits.
  `createdAt` rides on every row (and on the dashboard's array) so a human can
  re-rank a page by eye.
- THE FAIL-SAFE IS NOW REACHABLE BY TEST. `leak_scan`'s `unreadable` push had no
  test at all — deleting it left the whole suite green. It is the only producer
  of `leakUnknown`, `fetchErrors` and the non-healthy verdict at zero leaks, so
  losing it would report a run where every comment fetch failed as "healthy —
  zero leaks". `leak_scan_with` takes the comment read as a seam, and the test
  drives a total read failure through to the document's own `health`.
- THE ROW STATES THE CLASSIFIER'S VERDICT. `lacks.aiStateLabel` was billed as
  drift detection and was structurally always null, which is exactly why the
  wrong population was invisible on the artefact. `classifier.lane`/`.state`
  read `leak`/`leak` on an honest row and name the real state when they do not.
- nm.md named `/human-close`, which is not a shipped command. The close path is
  `/human-fsm:close-candidate <ref> uphold <note>` — it runs `human-close`,
  resolves PR-or-issue by lookup and needs no pre-existing flag, which is what
  makes it reachable for a leaked PR. Every filing command is now written in
  full invocation shape.
- nm.md's retired-label property was false: a RETIRED `ai:*` label is still
  bucketed on purpose, so its PRs never leak. What leaks is a state DELETED from
  the classifier's vocabulary — corrected, and stated as the property.
- README gains the `next_leak` section its siblings each have; the error message
  is caller-neutral now that two registers consume it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…) into /nm

Both deletions land under a PR whose whole subject is "which PRs are in no
modeled state", so the interactions are the merge, not a side effect of it.

CONFLICT SHAPE: two files, both the plugin version JSONs, both trivial —
main carries #224's 0.12.0 and this branch 0.13.0, and mine is the superset
(it names /nm) and is already ahead. main.rs auto-merged CLEAN, which is
exactly the case that has bitten twice today, so the real work was hunting
what the markers could not show.

WHAT THE SUITE CAUGHT THAT THE MARKERS DID NOT:

- The drift witness in `a_row_carries_the_leaks_own_evidence` asserted a
  `human:design` PR classifies into the human-decisions lane. #224 deleted
  that state, so it now classifies as a LEAK and the assertion failed on the
  merge commit. Repointed at `human:reject` — the lane's one surviving state,
  and still the live instance of the hazard the field exists for (a `human:*`
  label carries no `ai:*` prefix, which is what the old population wrongly
  admitted).
- `is_leak_candidate` referenced `RETIRED_BLOCKED_DEPLOY_LABEL`, deleted by
  #225: a compile error, fixed by dropping the row.

WHAT ONLY READING #225's OWN COMMIT CAUGHT — and no test would have:

`fdf2ee0` ruled that a PR hand-wearing a DELETED `ai:*` string lands
`un-vetted`, not `leak`, because the vetter absorbs it and the verdict that
judges it strips the dead label. That ruling was justified by the caller-side
`ai_state_label` partition — the exact thing this PR replaced with a
classifier-derived population. Under the merge as it auto-resolved, such a PR
became a leak again and silently overturned a ruling that landed hours ago.
The `lanes_doc` fixture cannot catch it (it feeds `producer_commented`
directly), so this is added as a conjunct on `is_leak_candidate` with the
ruling cited, plus an assertion over the registry below. It cannot reintroduce
the defect this predicate exists to fix: that defect was `human:*` labels
parked in a real lane, and the classifier arm is what excludes those.

THE THREE PIECES OF COVERAGE THE HUMAN CALLED FOR, so none of the above is
re-verified by hand at the next merge:

1. `every_lane_is_reachable_from_some_label_set` — one witness per `Lane`,
   driven off a new `Lane::ALL`, so an arm going unreachable behind a
   precedence change is a failure and a lane added later is covered on the day
   it is added.
2. `DELETED_LABELS` + `a_deleted_label_models_nothing_anywhere` — one property
   over a registry, replacing the two bespoke per-deletion tests #219 and #221
   each shipped. For every deleted label: the ordinary unmodelled fallthrough,
   dominates nothing, occupies no lane cell (checked across `Lane::ALL`), and
   no `counts` key names it. The next deletion adds a row of DATA.
3. `lanes_doc_emits_every_state_with_the_right_members` now derives every
   total from per-row expectations instead of literals. That assertion is the
   one that read `3` on two branches and was wrong at `4` once both landed,
   with nothing pointing at it; an expectation beside its input cannot go
   stale that way.

nm.md, the README and the doc comments now state the post-deletion truth: a
RETIRED label stays bucketed and never leaks; a deleted `ai:*` label is
absorbed as un-vetted; a deleted `human:*` label DOES leak, because the
vetter's sweep is `ai:*`-scoped and nothing else claims it. The classifier
rule nm.md tells the human to check is now its three real clauses rather than
the `ai:*`-only paraphrase.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
thedavidmeister added a commit that referenced this pull request Aug 7, 2026
…) into /nd

Conflicts, and how each resolved:

- `.claude-plugin/marketplace.json` + `plugins/human-fsm/.claude-plugin/plugin.json`
  — both sides bumped the plugin. OURS: 0.14.0 stays, distinct from and above
  main's 0.12.0 (#224) and the 0.13.0 #227 still claims, and the description
  keeps the third read.
- `plugins/human-fsm/README.md` — the whole command table. MAIN's table taken
  wholesale, because #224 rewrote the `/design` row and resolving toward ours
  would restore the `human:design` / `--park` prose #219 deleted; the `/nd` row
  is spliced back into it. Both intents, main's row verbatim.

Everything else auto-merged. Main's deletions stand: nothing here reintroduces
`human:design` or `ai:blocked-deploy` in any spelling — every remaining mention
in the tree is main's own assertion that the deleted state is inert.

nd.md needed no correction against merged main: it describes the post-#219 exit
(`ai:reject` with the answer as the trusted work order, one call at the head
anchor, no parked spelling), and `human-rule … design --rework` now does exactly
that. What was aspirational when written is now what the code does.

Coverage rather than hand-verification, per the standing call — two hand-written
totals near the merged region are now DERIVED from their fixtures:

- the withheld-overflow expectation is `raised - ND_MAX_WITHHELD` off a fixture
  sized from the cap, not a literal `2`;
- the row and envelope digit reserves count their numeric fields from the
  emitted document (`numeric_fields`) instead of a number typed in a comment.
  That is the exact shape that survives review and breaks on a merge: a count
  key added on one branch and a reserve typed on another are each correct alone
  and conflict with nothing when both land.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@thedavidmeister

Copy link
Copy Markdown
Contributor Author

Reviewed b90d064: approve — read the diff and verified the merge independently. /nm surfaces the FSM-conformance leaks, and the blocker its review found is fixed at the root: is_leak_candidate is now matches!(classify_lane(labels, None, true), (Lane::Leak, _)) — the classifier's own verdict rather than an ai:* label partition. That mattered live: four of five reported leaks were PRs in a perfectly modeled state while the org's one genuine leak sorted fifth, unreachable at a page cap of three. The same array feeds the dashboard, so that box is fixed by the same change. The row now states classifier: {lane, state} — the old drift field was structurally always null, which is exactly why the wrong population was invisible. Ordering is oldest-first and argued (a leak sits in nobody's queue, so its cost is how long it sits), with createdAt on the row. leak_scan_with gives the unreadable-fetch fail-safe a seam, closing a path where a run with every fetch failing would have reported 'healthy, zero leaks'. Merge verified by hand: zero occurrences of RETIRED_BLOCKED_DEPLOY_LABEL, HUMAN_DECISION_LABELS or RulingWork::Park, so both landed deletions survived. 1021 tests, 16/16 mutants, 19 checks green.
Rulings-conformance: checked against CLAUDE.md and every ruling stated for this work. (1) '/nm alongside /ncc and /nr and /nd for the next unmodelled item' — delivered as the fourth read, same second-opinion register as its siblings. (2) 'can we get test coverage rather than manual verification' — the three hand-checks I had briefed are now tests: every_lane_is_reachable_from_some_label_set, a DELETED_LABELS registry property that subsumed and replaced the two bespoke per-deletion tests, and a derived fixture total that reproduces and kills the PR 224 merge bug. (3) issue-examples-arent-exhaustive — the classifier fix covers the category, not just the reported instance. (4) removal-is-semantic — the superseded one-off tests were deleted, not left as duplication. (5) The merge preserved PR 225's landed ruling that a PR wearing a deleted ai:* label lands un-vetted rather than leak; it was silently overturned by the clean auto-merge, caught by reading 225's commit, and is now pinned as an explicit conjunct with a mutant. The artifact obeys each.

@thedavidmeister
thedavidmeister merged commit de65b91 into main Aug 7, 2026
21 checks passed
thedavidmeister added a commit that referenced this pull request Aug 7, 2026
#227 landed `next_leak` / `/nm` in exactly the seams `/nd` occupies, so every
conflict was the same shape: main's next_leak against this branch's
next_design, two independent siblings. Union throughout — nothing of either
side is dropped.

18 hunks in main.rs, resolved by taking main's file whole and re-applying this
branch's next_design work onto it: a blunt union spliced the two test modules
into each other (mismatched delimiters), because the conflict boundaries cut
through shared helper context rather than between the modules.

What the markers did NOT point at, and the suite did:

- `tools.len()` and the narrowing-tool count. BOTH branches bumped 20 -> 21 and
  5 -> 6, so git took the identical literal with no conflict at all — and the
  merged surface has 22 tools and 7 narrowing ones. A hand-written total,
  individually correct on each side, wrong once both land.
- the `nd_population` / archived-set conformance pins landed inside #227's new
  test at the end of the same module, where `reads` is not in scope.
- the shipped-command list is SORTED: the union had to place "nd" before "nm".

Versions: 0.14.0 stays — distinct from and above main's 0.13.0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
thedavidmeister added a commit that referenced this pull request Aug 7, 2026
Main added two MCP tools and their commands; this branch changes no tool
surface, so main's tool table, command lists, plugin manifest and the counts
that measure them (22 tools, 7 narrowing) carry over untouched.

The one conflict is `lanes_doc_emits_every_state_with_the_right_members`,
where both sides replaced the same hand-written totals for the same reason —
a literal that is right on each side of a merge and wrong once both land.
Main's is the more general mechanism: each fixture row carries its own
expected `(lane, state)` and every total is counted off those annotations, so
it is kept and used throughout. This branch's two semantic claims are applied
on top of it:

- the `leak` bucket emits NO lane cell (#130 clarification 2) — its inventory
  is the top-level `leaks` array, which is what its descriptor declares as its
  occupancy. Row #14 is therefore annotated `NO_LANE`, the same hand-off #7
  already had, and the retirement is asserted both ways: the annotations
  expect no `leak` cell, and the emitted doc has no `leak` key at all;
- the un-vetted total keeps its second, independent reading off the
  `DELETED_STATE_LABELS` registry beside main's annotation count. The
  annotations say where each PR lands; the registry says why the bucket is
  that size, so deleting a state without retiring its label fails here instead
  of quietly re-annotating itself.

`next_leak` is unaffected by the retired lane cell: it builds its rows from
`producer_pr_inventory` through `is_leak_candidate` and `classify_lane`, never
from `lanes_doc` — which is the property #222's own test pins.

The cross-lane total is now counted off the `NO_LANE` annotations rather than
written as `prs.len() - 2`, because that subtraction is exactly the shape that
survives a merge silently while the fixture hands off a different number.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

/nm: next unmodelled item (FSM leak) for the human to review — next_leak tool + command, sibling of /nr, /ncc, /nd

1 participant