Skip to content

/nd: next ai:design PR to rule on — next_design tool + command - #226

Merged
thedavidmeister merged 8 commits into
mainfrom
2026-08-06-nd-command
Aug 7, 2026
Merged

/nd: next ai:design PR to rule on — next_design tool + command#226
thedavidmeister merged 8 commits into
mainfrom
2026-08-06-nd-command

Conversation

@thedavidmeister

@thedavidmeister thedavidmeister commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Closes #220

/nd — the next ai:design PR for the human to rule on, third sibling of /nr and /ncc.

next_design MCP tool (pr-review-report-rs/src/main.rs)

Mirrors next_ready's discipline exactly, with next_close_candidate's ordering argument where it transfers:

  • Typed result off the shared enumeration machinery: the same gh search prs --label + org_owner_args() shape presentable_queue uses (never a checks qualifier — the measured 93-vs-203 lesson), archived repos withheld before the per-PR fetch and counted (next_close_candidate offers flags from archived repos, where no ruling can be written #206), drafts and human:*-overridden PRs excluded through the same has_human_override predicate the ai:ready queue reads.
  • limit refused out of range rather than clamped (next_design_limit, 1..=3, default 1), same wording shape as next_ready_limit.
  • Oldest question first (design_order_key: raising-comment timestamp, ties broken by PR ref) — flag_order_key's FIFO argument transfers whole: no cost signal exists, and the label parks the PR outside every AI actor's queue, so FIFO bounds the limbo.
  • The question carried from the trusted comment that raised it: the vetter's record-verdict design note (Reviewed <sha>: design, close-candidate twin excluded by its own token) or the producer's flag-design note (Design-question:), most-recent-wins, author-gated through trusted_comments so a spoofed marker raises nothing. question.source is the typed discriminant; a vetter-raised question reports sha/atHead, a producer-raised one reports null rather than a bool asserting a comparison nothing performed.
  • Truncation flagged like noteTruncated (note/noteBytes/noteTruncated at a 2,600-byte cap), with the full per-field byte-cap + compile-time budget guarantee both siblings carry (ND_* constants, const _: () = assert!(...)).
  • Withheld rows are visible: counts.noQuestion + a capped list for labelled PRs with no trusted raising comment (the design twin of CcGate::NoFlag), fetchErrors likewise.

Wired through the same seams as the siblings: McpProfile::Human tool list, mcp_all_tools entry (narrows: limit), McpCall::NextDesign, validate_call, mcp_exec, minimal_args, and the pinned counts (21 tools, six narrowing).

plugins/human-fsm/commands/nd.md

Modeled on nr.md/ncc.md — same register, LIMIT convention, refusal-relay language, typed-reads/no-shell contract, and second-opinion posture. The raised question is a claim to check (issue → diff → tree via pr_context / pr_checkout), with three findings, each a different deliverable: a genuine question (presented with the code-constrained option space and a recommendation), already answered (the pointer IS the finding), or misrouted (stated with what the correct state would have been).

Written against the post-#219 state (#219, ruled): the exit it points at is the design ruling that routes the PR immediately back to the producer as ai:reject + the answer as the trusted work order, one call. human:design and every park spelling appear nowhere in it.

Grant pinned by test (nd_grants_the_queue_the_pr_the_source_and_the_lens): next_design, pr_context, pr_checkout, clone_release + Skill, Read.

Plugin release

plugins/human-fsm/.claude-plugin/plugin.json and .claude-plugin/marketplace.json bumped 0.11.0 → 0.14.0 in lockstep; descriptions now name the third read. The three doc surfaces listing the human profile (CLAUDE.md, README.md, plugins/human-fsm/README.md) updated to the three-inbox shape.

Version coordination — this PR should merge LAST of the three human-fsm bumps in flight. Three open PRs bump the plugin from 0.11.0, and two cannot ship one version string: /plugin detects updates by comparing version STRINGS, so the second to land under a duplicate version would serve new content under a version installers have already seen. The claims are distinct and monotonic in merge order:

Merge order PR Version
1 #224 (human:design deletion) 0.12.0
2 #227 (/nm) 0.13.0
3 this PR 0.14.0

If this lands earlier than that order, its version must be re-bumped past whatever main then carries — a lower or equal number against main is the same stale-content bug, and the version hygiene job's lockstep + bump checks are what catch it.

The stronger reason to merge last is SEMANTIC, not the version string. nd.md and the next_design tool description both describe the post-#219 exit: answering routes the PR back as ai:reject + the work order in one call, with no parked spelling and no waiting state. main does not implement that yet — today /design writes human:design and takes --rework | --park. If this PR lands before #224, plugin 0.14.0 ships a command whose stated exit its own ruling command does not perform: /nd would tell the human the answer sends the PR back to the producer, and /design would park it under a label the FSM still has. The version ordering is a consequence of that dependency, not the reason for it.

Coordination hazard — the /design row of plugins/human-fsm/README.md. #224 rewrites that row (it deletes the human:design / --park prose); this PR reformats the surrounding table and the paragraphs under it. The conflict is guaranteed. 224's version of that row wins — resolving toward this side would restore the human:design and --park prose #219 deleted, which is the exact regression the deletion PR exists to prevent. Take 224's row verbatim; only the /nd row and the three-inbox prose come from here.

Tests

Mirror next_ready's: limit guard boundaries (default/null/1/max, refusals at 0, max+1, -1, "2", 1.5), row shape for both sources, empty-queue answer, truncation flag, prefix-of-ranked-order paging, adversarial byte-budget page, measured fixed allowances, question-source provenance (spoofed author, adjacent trusted comments, most-recent-wins), and the population query pinned as a value. Conformance: design_open_prs_args registered in every_org_wide_enumeration_withholds_archived_repos as a PURE argv builder whose live caller withholds.

Coordination note: based on main; the #219 deletion PR and the #130 stateDescriptors PR are in flight in the same file — this PR touches neither's paths (no human:design machinery, no human-queue emission); on conflict, main gets merged in.

QA

  • Discriminating tests: 19 in next_design_tests — the limit guard, the row shape for both question sources, the empty-queue answer, the truncation flag, prefix-of-ranked-order paging, the adversarial byte budget, the measured fixed allowances, the population query as a value, and (added in review) an_archived_repo_hit_is_withheld_from_the_population_and_counted, every_withheld_hit_class_is_named_rather_than_filtered_away, a_withheld_hit_is_both_counted_and_named, the_counts_partition_the_whole_population, the_claim_in_force_is_the_array_tail_not_the_newest_timestamp — plus the extended pins (the_human_profile_is_exactly_read_the_subject_and_rule_on_it, every_shipped_command_carries_its_frontmatter, nd_grants_the_queue_the_pr_the_source_and_the_lens, tool-count/narrows counts, and the two new positive pins inside every_org_wide_enumeration_withholds_archived_repos). Each fails on base because the functions, the tool and nd.md do not exist there.
  • Mutations applied (each alone against the committed tree, whole-binary suite, then restored) — 8/8 killed:
    • next_design_fetch: nd_population(...) -> (arr.clone(), Vec::new()), i.e. the next_close_candidate offers flags from archived repos, where no ruling can be written #206 withholding deleted -> KILLED by every_org_wide_enumeration_withholds_archived_repos (this is the mutant that SURVIVED before this round; it is the reason the population filter is now a named, pinned, unit-tested function)
    • nd_population body gutted to (hits, Vec::new()) -> KILLED by an_archived_repo_hit_is_withheld_from_the_population_and_counted
    • nd_apply_hit: draft counted but not pushed to the withheld list -> KILLED by a_withheld_hit_is_both_counted_and_named (survived first pass; the fold was extracted from the network function and tested to kill it)
    • nd_hit_class: has_human_override arm removed -> KILLED by every_withheld_hit_class_is_named_rather_than_filtered_away
    • last_design_question .next_back() -> .next() -> KILLED by the_claim_in_force_is_the_array_tail_not_the_newest_timestamp + the_question_is_the_most_recent_trusted_raising_comment
    • next_design_limit out-of-range arm -> clamp instead of refuse -> KILLED by the_page_size_defaults_to_one_and_is_refused_outside_its_range
    • next_design_row "noteTruncated" -> hardcoded false -> KILLED by an_oversized_question_is_clipped_and_says_so
    • last_design_question: trusted-author filter dropped -> KILLED by a_spoofed_marker_from_an_untrusted_author_raises_nothing
  • Oracle: fixture comments are built by the REAL writers (verdict_comment for the vetter's Reviewed <sha>: design, state_comment("ai:design", …) for the producer's Design-question:), so expected note bodies/pins come from the binary's own record format, not from the reader under test; byte-budget expectations come from the ND_* cap constants and MCP_MAX_RESULT_BYTES, the same arithmetic the compile-time assert checks; ordering expectations are hand-derived FIFO over explicit timestamps.
  • Category check: issue asks (1) next_design tool mirroring next_ready — typed result, refused limit, existing FIFO ordering, question from the trusted raising comment with truncation flagged, shared enumeration: covered; (2) nd.md on the nr/ncc model with the stated grant, LIMIT convention, three-findings posture, post-RULED: an answered design question routes straight to producer rework, same as a rejection #219 exit: covered; (3) plugin version bump: covered (0.14.0, lockstep with the marketplace listing). Rust tests mirror next_ready's four named categories (limit guard, row shape, empty queue, truncation): covered.

Refs: #219, #130

🤖 Generated with Claude Code

next_design MCP tool on the human profile, mirroring next_ready's
discipline: typed result off the shared label-search enumeration
(org scope, archived repos withheld, human overrides excluded), limit
refused out of range rather than clamped, oldest-question-first FIFO
keyed by the trusted raising comment (vetter 'Reviewed <sha>: design'
or producer 'Design-question:', author-gated, most-recent-wins), the
question carried whole with noteTruncated, per-field byte caps under
the one compile-checked budget.

plugins/human-fsm/commands/nd.md modeled on nr.md/ncc.md: same LIMIT
convention and refusal relay, second-opinion posture over the raised
question (genuine / already-answered / misrouted), exit described
against the #219 post-state — an answered design question routes the
PR back to the producer as ai:reject + the answer as the work order,
one call.

Plugin 0.11.0 -> 0.12.0 in lockstep with the marketplace listing;
human-profile doc surfaces updated to the three-inbox shape.

Closes #220

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: 26 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: 4c70101e-888a-4b6d-9a9c-b79655f3235c

📥 Commits

Reviewing files that changed from the base of the PR and between de65b91 and e507174.

📒 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/nd.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.

thedavidmeister and others added 4 commits August 7, 2026 06:50
…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>
Review found the archived-repo withholding pinned by NOTHING for this
lane: `every_org_wide_enumeration_withholds_archived_repos` builds its
population by scanning for `org_owner_args()`, and `next_design_fetch`
reaches the org scope through `design_open_prs_args`, so the fetch is
invisible to that scan and the exemption added beside
`flagged_subjects_args` was an unbacked claim. Deleting the withholding
left the whole suite green.

- `nd_population` is now a named pure function with its own behavior
  test (the ncc_population shape), so the filter's DELETION fails a
  test rather than passing silently;
- the exemption is backed by two positive pins beside the
  `flagged_open_subjects` one: the fetch READS the archived set, and
  the fetch APPLIES the filter;
- `nd_hit_class` replaces the filter chain, so every withholding has a
  NAME: draft (listed, with its own justification — a draft's question
  is answerable, the code under it is not settled), human-ruled, and
  unaddressable (the class a bare `excluded` count made invisible);
- counts are a PARTITION of aiDesign, asserted as arithmetic;
- the array-tail contract is pinned by a fixture whose timestamps
  disagree with its array order.

nd.md bounds the already-answered finding to the surfaces the grant can
actually reach and requires a recalled precedent to be marked
unverified. READMEs: the three-inbox sweep finished, a next_design
ordering section added, and the #206 count enumeration extended.

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

The mutation pass found one survivor: dropping the withheld-list push
for a draft, keeping the count, left the suite green. The push lived
only inside `next_design_fetch`, which no unit test can reach, so
'counted' and 'counted and listed by name' were indistinguishable to
the suite — while to a human they are a row they can act on versus a
number they cannot.

`nd_apply_hit` is that fold, split out on the `apply_outcome`
precedent, and tested: draft and unaddressable are counted AND named,
human-ruled is counted and deliberately not (a ruled subject is a
decision made, not an inbox item), a candidate is neither.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
thedavidmeister and others added 2 commits August 7, 2026 10:40
…) 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>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
#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

Copy link
Copy Markdown
Contributor Author

Reviewed e507174: approve — read the diff and verified the merge independently. /nd is the design-question read, the fourth sibling of /nr, /ncc and /nm, and it matches their discipline: enumeration derived from the shared machinery (ORGS scope, archived-repo withholding, the same human-override predicate presentable_queue reads), limit refused rather than clamped at both boundaries, a compile-time byte budget with allowances measured by test, and every withheld row counted and named so the inbox cannot silently shrink. Ordering is oldest-question-first and argued on the label's own semantics — ai:design parks the PR outside every AI actor's queue, so the wait is the cost and FIFO bounds it. The question arrives typed: source says whether the vetter's verdict or the producer's flag raised it, a vetter-raised question carries the sha it pinned with atHead beside it, and a producer flag reports null rather than asserting a comparison nobody performed. The #206 archived-repo withholding is now pinned as tested behaviour via nd_population, closing a hole where deleting the filter left the whole suite green. nd.md is bounded to reads its grant can actually perform and describes the post-#219 exit, which main now implements.
Rulings-conformance: checked against CLAUDE.md and every ruling stated for this work. (1) 'file and agent an /nd command like /nr and /ncc for the next design human item' — delivered as that sibling, same register, same second-opinion posture. (2) 'can we get test coverage rather than manual verification' — the archived-repo check became a tested pure function rather than an asserted wiring, and the merge's hand-checks became the full-suite-on-merge-commit run. (3) 'the prs are open, trying to get them right' — every review finding was folded into this PR rather than filed. (4) issue-examples-arent-exhaustive — withheld classes are enumerated as a partition, not a sample. (5) The plugin version is distinct and monotonic at 0.14.0 above main's 0.13.0, and 224's version of the /design README row was preserved rather than reverted by this merge. The artifact obeys each.
Merge note: the suite on the merge commit caught two counts git could not — both branches bumped tools.len() to the same literal 21 and narrowing to 6, while the true merged values with both next_leak and next_design present are 22 and 7. Third instance of that class today; the derived-total rule now in the sibling PRs prevents it.

@thedavidmeister
thedavidmeister merged commit dd46f24 into main Aug 7, 2026
21 checks passed
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.

/nd: next ai:design item to rule on — next_design tool + command, sibling of /nr and /ncc

1 participant