Skip to content

Rename the send-back state from reject to needs-work — label through verb through docs - #231

Merged
thedavidmeister merged 4 commits into
mainfrom
2026-08-07-rename-reject-to-needs-work
Aug 7, 2026
Merged

Rename the send-back state from reject to needs-work — label through verb through docs#231
thedavidmeister merged 4 commits into
mainfrom
2026-08-07-rename-reject-to-needs-work

Conversation

@thedavidmeister

@thedavidmeister thedavidmeister commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Closes #230

"needs work" is a more standard tag than rejected

Renames the send-back state everywhere it is spelled: label, ruling verb, vetter
verdict, slash command, counts key, lane state, descriptor, docs and prompts.
The name is the accurate one — the state is a transient send-back that carries a
work order, the producer reworks that same PR and branch, and the push is the
transition
. "Rejected" named a terminal judgement the FSM does not have.

READ THIS FIRST — the live labels are ALREADY renamed, so main is currently blind

This is not a hypothetical migration. As of writing, across
rainlanguage + cyclofinance + S01-Issuer:

label subjects (all states) open subjects
ai:needs-work 91 79 open PRs
human:needs-work 23 21 open issues
ai:reject 1 1 open PR
human:reject 0 0

main's code still reads ai:reject / human:reject. Those constants match
one archived-repo PR and nothing else, so until this lands the pipeline
does not see 79 open PRs and 21 open issues that are sitting in the send-back
state. That is the argument for landing it promptly once the stack ahead of it
clears — it is not a cosmetic rename any more, it is the code catching up to the
labels.

Contains PR 223 — and deliberately not 224

histFold and the stateDescriptors table arrive with
#223, and the history-continuity
half of #230 is unimplementable without them, so 223's branch is merged into this
one (commit b5038bb). Once 223 lands on main this PR's diff reduces to the
rename alone. To review only my work:

git diff b5038bb..HEAD

#224 is deliberately NOT merged
in. It carries NextAction::ReworkReject, but it also deletes the
human:design state that 223 adds a descriptor for, and the two conflict
semantically in human_queue_doc's counts block and in HUMAN_DECISION_LABELS.
Arbitrating that inside a rename PR would be the wrong place to settle it, so
ReworkReject is left for a merge-main once 224 lands. Judgment call stated
up front: when it arrives it should become ReworkNeedsWork / "rework-needs-work",

because exact 1:1 correspondence between the variant and the state key is the
property #230 is buying; the mild stutter is a cosmetic cost worth paying. (Note
NextAction::ReworkRuling / "rework-ruling" already on main is NOT renamed —
that is #111's delegated-design work order, a different state.)

What changed

  • Labelsai:rejectai:needs-work, human:rejecthuman:needs-work.
  • Ruling verb — falls out of the label sets rather than being typed beside
    them: ruling_verb derives the verb from the label's namespace suffix, so
    HUMAN_PR_RULING_LABELS / HUMAN_RULING_LABELS moving is the whole change and
    the verb list cannot drift from the labels it writes.
  • Vetter verdictVETTER_VERDICTS, verdict_label, and the
    Reviewed <sha>: needs-work — … comment.
  • Slash commandplugins/human-fsm/commands/reject.mdneeds-work.md
    (git-tracked rename), plugin manifest and marketplace listing both to
    0.15.0, distinct from every open sibling's claim (224 → 0.12.0,
    227 → 0.13.0, 226 → 0.14.0).
  • Counts / lane / descriptorcounts.rejectcounts.needsWork, the
    vetter-verdicts lane state, and STATE_REJECTSTATE_NEEDS_WORK.
  • migrate-rejectmigrate-needs-work (with MigrateRejectPlan,
    migrate_reject_plan, migrate_reject_mode). After this rename it moves
    human:needs-work onto ai:needs-work, so the verb has to say so.
  • Prose — README FSM diagram + text, CLAUDE.md, campaign-prompt.txt,
    review-prompt.txt, plugin READMEs.

History continuity

const STATE_NEEDS_WORK: StateDescriptor = StateDescriptor {
    key: "ai:needs-work",
    hist: Some("needsWork"),
    hist_fold: &["reject", "humanReject", "relink"],
    ...

"reject" is this state's OWN pre-rename key and it is the load-bearing entry.
Measured against the committed human-queue-history.jsonl (230 rollup lines):

key samples newest
reject 216 2026-08-07T09:31:05Z
humanReject 216 2026-08-07T09:31:05Z
relink 216 2026-08-07T09:31:05Z
needsWork 0

So the new series has no committed samples of its own: without the fold it
would not merely lose its past, it would start empty and render the state as
newly created — the exact #130 defect hist_fold exists for.
human-queue-history.jsonl is not rewritten.

New test the_needs_work_series_draws_every_spelling_the_state_has_had pins it,
asserting structurally off the descriptor table first (so it cannot pass
vacuously in the flake sandbox, where the repo-root read is filtered out and
repo_root_text returns None), then corroborating against the real committed
history — that the three fold keys each have real samples, and that needsWork
has none, which is what makes the fold load-bearing rather than decorative.

Live label migration — already performed; verified, not re-run

The gh label edit <old> --name <new> migration had already been carried out
before this PR reached that step. Re-running it would be pointless and
delete-and-recreate would be destructive, so this PR made no label writes at
all
— it verified the outcome instead.

Full read-only census across the three orgs:

  • Repos scanned: 172 — rainlanguage 148, cyclofinance 9, S01-Issuer 15
    (38 archived).
  • Repos defining any of the four labels: 31 — 19 define both new names,
    9 define ai:needs-work only, 2 define human:needs-work only.
  • Repos renamed by this PR: 0 (already done upstream of it).
  • Repos still defining an OLD name: 1
    rainlanguage/rain.webapp, archived: true.

Population reconciliation, which is the check that matters:

before (repo's own committed human-queue.json) after (live)
send-back PRs counts.reject = 79 ai:needs-work open PRs = 79
human send-back issues 21 (per #230 at filing) human:needs-work open issues = 21

Nothing was lost. The counts match exactly, which is what distinguishes a
rename from a delete-and-recreate.

The single straggler is rainlanguage/rain.webapp#354 (open PR, still
ai:reject), and it is unmigratable by construction: rain.webapp is
archived, therefore read-only, so gh label edit cannot rename a label there.
That is also why exactly one subject was left behind rather than an arbitrary
number — it is the one repo the migration could not write to. No write was
attempted against it. The repo already models this: an archived-repo PR is
inventoried in archivedRepoPrs rather than in a lane
(#206), so it is accounted
for without being actionable.

Deliberately NOT renamed

A blind grep -ri reject sweep would have corrupted all of these, which is why
this was done constants-first and compiler-led, with the grep as the last pass:

  • Close-candidate flag verdictsCC_VERDICTS = ["uphold", "reject"],
    CcGate::RejectedStillFlagged, CC_CLEAR_REJECTED,
    record-close-candidate-verdict … reject, /close-candidate <n> reject.
    These judge a flag, write no label and create no state, so they are not
    the send-back state this ruling renames.
  • The "refuse" senseResultRejected, iso_to_epoch_ms_rejects_malformed,
    blocked_by_ref_rejects_garbage, the rejected rate-limit status, a
    non-fast-forward push, forge fmt --check rejecting a diff.
  • reworked-reject — a subcommand that is GONE; renaming it would invent a
    history in which it was called something it never was. A test asserts it stays
    unparseable.
  • counts.humanReject — the retired-but-kept series the dashboard reads AND
    a histFold key. Renaming it would break both.

Sequencing

Merges LAST, after
#223,
#224,
#226 and
#227 — it touches the same
regions as all four. Based on main; I will merge main in as each sibling
lands (never rebase over review history), and re-apply the rename to whatever
prose they bring with them. 226 and 227 both edit the marketplace description
line and the /reject command reference, so expect a conflict there.

The dashboard follows on its own once this emits the new vocabulary, per
#130 descriptors, but
rain-org-health's FROZEN fallback table still spells the old name and needs the
same rename for descriptor-less snapshots.

QA

  • Discriminating tests: the_needs_work_series_draws_every_spelling_the_state_has_had (new), state_descriptors_emit_the_ratified_shape, verdict_label_maps_the_four_verdicts, human_ruling_vocabulary_is_derived_from_the_label_sets, the_vetter_prompt_teaches_four_verdicts_and_routes_linkage_to_needs_work, every_descriptor_occupancy_source_resolves_in_the_emitted_document — each verified to fail on base by mutation rather than by assertion: M1–M5 below re-introduce the pre-rename spelling at one site each (which IS the base state for that site) and every one of these tests goes red, then green again on restore. The new continuity test additionally fails if human-queue-history.jsonl is ever rewritten to carry needsWork samples.
  • Mutations applied: hist_fold: &["reject",…] -> drop "reject" -> the_needs_work_series_draws_every_spelling_the_state_has_had + state_descriptors_emit_the_ratified_shape; verdict_label("needs-work") -> Some("ai:reject") -> verdict_label_maps_the_four_verdicts; VETTER_VERDICTS -> "reject" -> a_valid_verdict_reaches_the_write_exactly_as_given + tools_list_is_exactly_the_vetter_fsm_surface + the_vetter_prompt_teaches_four_verdicts_and_routes_linkage_to_needs_work; HUMAN_PR_RULING_LABELS -> "ai:reject" -> 9 tests incl. every_pr_ruling_label_lands_in_a_modeled_lane; STATE_NEEDS_WORK.occupancy.counts -> "reject" -> every_descriptor_occupancy_source_resolves_in_the_emitted_document. No survivors; all restored byte-identical (table below).
  • Oracle: the committed human-queue-history.jsonl measured independently of this code (230 rollup lines: reject/humanReject/relink = 216 samples each, newest 2026-08-07T09:31:05Z; needsWork = 0), and the LIVE GitHub label populations read via gh api search/issues (ai:needs-work = 79 open PRs, human:needs-work = 21 open issues) checked against the repo's own pre-rename snapshot human-queue.json counts.reject = 79. Expected values come from those measurements, not from the implementation's own emission.
  • Category check: issue asks for label + ruling verb + vetter verdict + slash command + counts key/lane state/history + prose. Covered: labels (both), ruling verb (both HUMAN_*_RULINGS, derived from the label sets), vetter verdict (VETTER_VERDICTS + the Reviewed <sha>: comment), slash command (reject.md -> needs-work.md, manifest + marketplace to 0.15.0), counts.reject -> counts.needsWork + vetter-verdicts lane state + histFold, and all six prose surfaces. NextAction::ReworkReject is Refs-not-Closes because it does not exist on this base — it arrives with PR 224, which is deliberately not merged in (semantic conflict with 223 over human:design); the chosen spelling ReworkNeedsWork is stated above and applied at merge-main. Close-candidate flag verdicts are out of category by design, reasoned above.

Toolchain via nix throughout. Gates run at commit 5931e04,
pr-review-report-rs/src/main.rs sha256
2f9bee0109be83322e3b4fffd144b8ab5101267e17d7cfc5e15843f66dca7df8, working tree
clean. Exit codes captured unpiped.

1. Full test suitenix develop github:rainlanguage/rainix/53e96a7d0a97d7c7c75c3b2412521324776fdac6#rust-shell -c cargo test --manifest-path pr-review-report-rs/Cargo.toml

CARGO_TEST_EXIT=0
test result: ok. 1012 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 3 passed; 0 failed    (distill_trace)
test result: ok. 9 passed; 0 failed    (refresh_human_queue)
test result: ok. 47 passed; 0 failed   (require_qa_block)
test result: ok. 8 passed; 0 failed    (run_timings)
test result: ok. 7 passed; 0 failed    (run_usage)
test result: ok. 4 passed; 0 failed    (usage_gate_skip)

2. CI-exact static, changed file touched first so a warm target cannot serve
a false pass — touch pr-review-report-rs/src/main.rs && nix develop github:rainlanguage/rainix/53e96a7d0a97d7c7c75c3b2412521324776fdac6#rust-shell -c rainix-rs-static

Checking pr-review-report v0.1.0
Finished `dev` profile [unoptimized + debuginfo] target(s) in 13.44s
STATIC-EXIT:0

Caught a real failure first time round: needs-work is four characters longer
than reject, which pushed several lines past the width limit. Fixed with
cargo fmt, not by shortening anything.

3. rainix pre-commit bundlenix develop …#rust-shell -c pre-commit run --all-files

deadnix..............................Passed      no-consumer-prettier.....Passed
denofmt..............................Passed      prettier-rainix..........Passed
nil..................................Passed      rustfmt..................Passed
nixfmt...............................Passed      shellcheck...............Passed
statix...............................Passed      taplo....................Passed
yamlfmt..............................Passed
PRECOMMIT-ALL-EXIT:0

4. Flake packagenix build .#pr-review-report --print-build-logs

shrinking /nix/store/hrh6a9bp9k247rpbfq5zvswk08kzvvgb-pr-review-report-0.1.0/bin/pr-review-report
NIXBUILD-EXIT:0

Adversarial mutation pass

Run after committing the real work, so git checkout -- cleanup could not
eat uncommitted edits. Each mutant: apply, run the whole suite, restore, verify
the restored file's sha256 matches the pre-mutation sha.

# mutation result killed by
M1 drop "reject" from STATE_NEEDS_WORK.hist_fold KILLED (2) the_needs_work_series_draws_every_spelling_the_state_has_had, state_descriptors_emit_the_ratified_shape
M2 verb writes a label whose name no longer matches it — verdict_label("needs-work")Some("ai:reject") KILLED (1) verdict_label_maps_the_four_verdicts
M3 one spelling left behind in a code path — VETTER_VERDICTS back to "reject" KILLED (3) a_valid_verdict_reaches_the_write_exactly_as_given, tools_list_is_exactly_the_vetter_fsm_surface, the_vetter_prompt_teaches_four_verdicts_and_routes_linkage_to_needs_work
M4 old label left in HUMAN_PR_RULING_LABELS KILLED (9) incl. human_ruling_vocabulary_is_derived_from_the_label_sets, every_pr_ruling_label_lands_in_a_modeled_lane, the_human_rule_schemas_pin_the_sacred_label_sets
M5 counts key left at old spelling while hist is needsWork KILLED (1) every_descriptor_occupancy_source_resolves_in_the_emitted_document

No survivors. All five restored byte-identical to
2f9bee0109be83322e3b4fffd144b8ab5101267e17d7cfc5e15843f66dca7df8.

M1 is the one the issue asks for specifically, and it behaves as required: the
history truncates and a test catches it. M3 is the drift the issue names — a
verb and a label that stop agreeing — and the prompt-conformance test fires,
which means the vocabulary the vetter is taught is pinned to the vocabulary the
guard enforces. M5 is worth noting because its failure message is exactly
`ai:needs-work` names history key `needsWork` but `counts` has no such key
— the descriptor and the emitted document disagreeing, caught structurally.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Replaced rejection workflows with the clearer needs-work send-back state.
    • Added the /needs-work command for returning pull requests and issues with required rework notes.
    • Updated design and review workflows to route applicable work into the unified needs-work state.
  • Documentation
    • Updated commands, labels, migration guidance, workflow instructions, and review terminology to consistently use needs-work.
    • Renamed the migration command to migrate-needs-work.
  • Chores
    • Bumped the human-fsm plugin version to 0.15.0.

thedavidmeister and others added 2 commits August 7, 2026 10:09
"needs work" is a more standard tag than rejected, and it is the accurate
name: the state is a transient send-back, not a verdict against the work.
A send-back carries a work order, the producer reworks that same PR and
branch, and the push is the transition — the head moves, the verdict stops
being current, and the next vet clears the label. "Rejected" named a
terminal judgement the FSM does not have.

The spelling changes together, because a verb that writes a differently
named label is the drift this repo keeps paying for:

- labels `ai:reject` -> `ai:needs-work`, `human:reject` -> `human:needs-work`
- the ruling verb on `human-rule` / `human-rule-issue`, which follows from
  the label sets: `ruling_verb` derives the verb from the label's namespace
  suffix, so the vocabulary moves with the constant rather than beside it
- the vetter verdict `record-verdict … needs-work` and the `Reviewed <sha>:
  needs-work — …` comment it writes
- the slash command, `plugins/human-fsm/commands/needs-work.md`, with the
  plugin manifest and marketplace listing bumped to 0.15.0 in lockstep
- `counts.reject` -> `counts.needsWork`, the vetter-verdicts lane state, and
  the `migrate-reject` one-shot -> `migrate-needs-work`, which after this
  rename moves `human:needs-work` to `ai:needs-work` and has to say so
- README FSM diagram and text, CLAUDE.md, campaign-prompt.txt,
  review-prompt.txt, plugin READMEs

HISTORY CONTINUITY. `STATE_NEEDS_WORK` carries `hist: Some("needsWork")` and
`hist_fold: &["reject", "humanReject", "relink"]`. `reject` is this state's
own pre-rename key, and it is the load-bearing entry: the committed
`human-queue-history.jsonl` holds 216 samples under it, the newest measured
2026-08-07T09:31:05Z, and ZERO under `needsWork`. Without the fold the
series would not merely lose its past — it would start empty and render the
state as newly created, which is the #130 defect `hist_fold` exists for.
`human-queue-history.jsonl` is NOT rewritten; the old lines are real
measurements of the machine as it then was.

`the_needs_work_series_draws_every_spelling_the_state_has_had` pins it,
structurally off the descriptor table (so it cannot pass vacuously in the
flake sandbox where the repo-root read is filtered out) and then against the
real committed history.

Deliberately NOT renamed: the close-candidate flag verdicts (`CC_VERDICTS`,
`CcGate::RejectedStillFlagged`, `clear-stranded-rejected`,
`record-close-candidate-verdict … reject`). Those judge a FLAG, write no
label and create no state, so they are not the send-back state. Nor is the
"refuse" sense (`ResultRejected`, `iso_to_epoch_ms_rejects_malformed`, the
`rejected` rate-limit status, a non-fast-forward push), nor `reworked-reject`
and `humanReject`, which name things that are gone or historical and would
become lies under the new spelling.

Closes #230

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@thedavidmeister thedavidmeister self-assigned this Aug 7, 2026
Main is 31 commits ahead of this branch's base and every one of the five PRs
that landed in between touches what this renames, so main wins on structure and
this branch wins on the spelling. Where a hunk carried both, the merged code
takes both: 224's verb->target tables and typed worklist row, 223's state
descriptors, 225's deleted `ai:blocked-deploy`, and 226/227's `/nd` and `/nm`,
all spelled `needs-work`.

The rename had to reach code that did not exist when this branch was written:

- `NextAction::ReworkReject` -> `ReworkNeedsWork`, string `rework-reject` ->
  `rework-needs-work`. The action vocabulary is DERIVED from the enum (#224), so
  `ALL_ACTIONS`, `ACTIONABLE_ACTIONS` and `action_rank` follow from the variant.
- `RejectState` -> `NeedsWorkState` with `NotRejected` -> `NotNeedsWork`,
  `reject_state` -> `needs_work_state`, `reject_instruction` ->
  `needs_work_instruction`, and the `PrSignals.reject` field -> `needs_work` —
  224's typed row that replaced the producer prompt's raw label search.
- The producer prompt's step 3, which 224 rewrote around that typed row.
- review-prompt's #219 sentence, `design.md`, and 226/227's `nd.md` / `nm.md`.

Deliberately NOT renamed, because a blind sweep would have corrupted them: the
close-candidate FLAG verdicts (`CC_VERDICTS`, `cc_verdict_plan`, `cc_gate`,
`CcGate::RejectedStillFlagged`, `/close-candidate <n> reject`) judge a flag and
write no state; the "refuse" sense (`ResultRejected`, the rate-limit `rejected`,
a non-fast-forward push); `reworked-reject`, a subcommand that is GONE; and
`counts.humanReject`, which is both a kept series and a `histFold` key.

History continuity is measured, not assumed. `STATE_NEEDS_WORK.hist_fold` keeps
`"reject"` alongside `humanReject`/`relink`, and against the committed
`human-queue-history.jsonl` (232 rollup lines) those three keys carry 218
samples each, newest 2026-08-07T11:31:03Z, while `needsWork` has none — so the
whole drawn series comes through the fold. The 14 lines that fold nothing are
the contiguous oldest prefix, predating the state itself. The history file is
not rewritten; `the_needs_work_series_draws_every_spelling_the_state_has_had`
pins this structurally first so it cannot pass vacuously in the flake sandbox.

Three things the merge markers did not cover and the suite did:

- `every_shipped_command_carries_its_frontmatter` — the command set is compared
  SORTED, and `needs-work` sorts between `nd` and `nm` where `reject` sorted
  first. Carrying the old order across would have been green-looking nonsense.
- `a_cc_verdict_carries_the_machines_citation_read_under_the_vetters_note` — the
  close-candidate comment's expected string had been swept into `needs-work`;
  the CC verb is not this state.
- `the_vetter_prompt_makes_the_note_say_what_it_read` — same, on a prompt bullet
  about rejecting a FLAG.

Counts re-derived from merged reality rather than carried across: 22 tools
declared, the shipped command set is 8 files, and no count assertion was
retyped. Gates at this commit: `cargo test` 1052+3+9+47+8+7+4 passed 0 failed;
CI-exact `rainix-rs-static` at rainix 53e96a7 with the changed file touched
first; `pre-commit run --all-files` all hooks passed; `nix build
.#pr-review-report`.

Note for the record: this branch renames `migrate-reject` to
`migrate-needs-work` as a permanent subcommand. The live labels were already
migrated before this PR reached that step, so the verb is spent — under the
standing ruling that a migration is an execution vehicle, it should come out,
which is a separate change from this merge.

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

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The PR renames reject-oriented states, commands, labels, and workflow terminology to needs-work. It updates FSM documentation, the human-fsm plugin, campaign and review prompts, and plugin version metadata.

Changes

Needs-work terminology migration

Layer / File(s) Summary
FSM contract and state documentation
CLAUDE.md, README.md
FSM states, human rulings, migration commands, queue keys, and workflow descriptions now use needs-work terminology.
Plugin command updates
.claude-plugin/marketplace.json, plugins/human-fsm/...
The plugin version increases to 0.15.0. The /reject command and related outcomes are documented as /needs-work and ai:needs-work.
Campaign and review workflow documentation
campaign-prompt.txt, review-prompt.txt, review-run.sh
Operational instructions update send-back actions, workflow sequencing, evidence rules, verdict counts, and prior-run terminology.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested labels: ai:ready

Suggested reviewers: claude

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The summaries show documentation and plugin metadata updates, but no evidence of the required label migration, command, verdict, count, history, or migration changes [#230]. Implement and verify the complete reject to needs-work rename, including labels, rulings, verdicts, counts, history continuity, and live migration [#230].
Out of Scope Changes check ⚠️ Warning The campaign prompt includes workflow, conflict, and screenshot-processing changes beyond the linked issue's state-rename scope [#230]. Remove unrelated workflow, conflict, and screenshot-process edits, or link an issue that explicitly requires them.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change: renaming the send-back state from reject to needs-work across labels, verbs, and documentation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 2026-08-07-rename-reject-to-needs-work

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.45.0)
pr-review-report-rs/src/main.rs

ast-grep timed out on this file


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@campaign-prompt.txt`:
- Line 58: Update the producer instructions in step 3d and lines 78–82 to route
every GitHub input/output and pipeline transition through typed pr-review-report
subcommands, including fetching baseRefName, resolving conflicts, pushing
screenshot branches, posting comments, and creating or reading migration issues.
Enumerate these subcommands in the producer profile before referencing them, and
remove raw gh/git commands so all state changes are recorded and guarded by FSM
transitions.

In `@plugins/human-fsm/README.md`:
- Line 19: Update the /needs-work documentation row to distinguish the issue
path from the pull-request path: retain ai:needs-work for pull requests and
explicitly state that human-rule-issue writes human:needs-work, matching the
behavior documented in needs-work.md.

In `@review-prompt.txt`:
- Around line 15-16: Update the requirement governing needs-work note contents
near the review verdict rules so it explicitly permits review-blocking evidence
failures, including missing or false QA evidence and missing screenshots,
alongside code defects. Preserve the existing mandatory evidence gates and their
specified notes, ensuring valid evidence failures can produce honest needs-work
notes without requiring a misleading defect description.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9a7385e1-523b-4aed-8630-19310427da87

📥 Commits

Reviewing files that changed from the base of the PR and between c7a8ad8 and 997be30.

📒 Files selected for processing (15)
  • .claude-plugin/marketplace.json
  • CLAUDE.md
  • README.md
  • campaign-prompt.txt
  • plugins/human-fsm/.claude-plugin/plugin.json
  • plugins/human-fsm/README.md
  • plugins/human-fsm/commands/close-candidate.md
  • plugins/human-fsm/commands/design.md
  • plugins/human-fsm/commands/nd.md
  • plugins/human-fsm/commands/needs-work.md
  • plugins/human-fsm/commands/nm.md
  • plugins/human-fsm/commands/nr.md
  • pr-review-report-rs/src/main.rs
  • review-prompt.txt
  • review-run.sh

Comment thread campaign-prompt.txt
(iv) MIGRATION — the deploy-shaped case, triggered by EITHER (a) a red prod-pin / `testProdDeploy*` / deploy-pinned-constant check (REPLACES (ii)/(iii) for it: there is no code fix to push), OR (b) a GREEN PR carrying a legacy `REQUIRES redeploy at land` marker in its body or a trusted note. Under the split deploy/abstract release lifecycle NO merge waits on a deploy (#162): deploy repos freeze per-tag records and publish on `rainix-tag-release`, the on-chain deploy is a separate manual workflow dispatch decoupled from publishing, and library repos never deploy — so either trigger firing is evidence of exactly ONE thing: THIS REPO HAS NOT MIGRATED to the split lifecycle. The PR is blocked on that migration, not on any deploy, and you deploy NOTHING, EVER (there is no producer deploy path; deploys are a human's decoupled manual dispatch). Route it: (1) find the repo's lifecycle-migration issue/PR (search the repo's open issues/PRs for the split-lifecycle / release-workflow migration); (2) if none exists, FILE it — `gh issue create` in that repo, assignee {{ASSIGNEE}}, shaped as the audit skill's lifecycle-split finding (the repo keeps a single "current" deployment pin that premerge CI tests against, so every bytecode-changing PR goes red until an out-of-band deploy; the fix is the split lifecycle: per-tag frozen deploy records + `rainix-tag-release` publishing, with the on-chain deploy a decoupled manual dispatch) — this is the ONE sanctioned `gh issue create` and the only exception to step 4's no-new-issues posture; (3) TRANSITION the PR: `pr-review-report flag-blocked-on <owner/repo> <n> "repo not migrated to the split release lifecycle: <the red pin check / the legacy marker>" --blocked-by <owner/repo#n of the migration issue/PR>` — the typed dep makes clearance automatic: the vetter clears the flag the run after the migration lands and the PR re-enters vetting fresh. An APPROVED PR is human-sacred and cannot be flagged — report it as blocked-on-migration in the run summary instead (still do (1)/(2) so the dep exists). DEDUP: if the PR already carries `ai:blocked-on` naming the migration, it is parked — do not re-flag. There is no blocked-deploy state and no producer deploy any more (#162, deleted outright by #221): `flag-blocked-deploy` refuses with exactly this replacement, and `ai:blocked-deploy` is not a state the machine models — a PR still wearing the string is an unmodelled label, so treat it as any other PR and route it by the rules above. Still NEVER merge; landing stays interactive on the human's explicit word.
3c. BACKFILL MISSING SCREENSHOTS on your OWN open UI PRs (still existing-work — after 3b's red fixes, before step 4 opens anything new): a UI PR with no screenshot is, like a red PR, in-flight work that is NOT review-ready (step 5), so finish it before starting new work. YOUR LIST IS STEP 2a's `worklist` ROWS WHOSE `nextAction` IS `screenshot-3c`, never a path list written here: `worklist`'s `is_ui_path` is the ONE definition of which paths fall under this requirement — raindex's `packages/webapp` and `packages/ui-components`, everything under a `site/` dashboard tree INCLUDING the `health.json` its panels draw, and every `.svelte`/`.css`/`.html` file wherever a repo keeps it (`cyclo.site` keeps its components in `src/lib/components/`, which is where both of #140's PRs lived) — and a second enumeration in this step is a second definition, free to drift from it while reading as though it agrees. The row's `markers.uiTouch` tells you which answer routed it: `yes` a path under the requirement is in the changed list, `unknown` the changed list could not be ruled out — BOTH route here, because `unknown` means nothing ruled UI out, not that there is none. For each, judge FROM THE DIFF whether it changes RENDERED OUTPUT a user sees (per step 5: `.svelte` markup, a visible component/element, user-visible styling, a non-`.svelte` service whose output is directly displayed — chart data / labels / formatters — OR a `site/` dashboard page's visible markup/section). A change that ONLY touches `<script>`/store/load/pure-logic with NO visible effect does NOT need a screenshot — skip it. If it needs one, render + post it via step 5's harness — a row that reached this step has neither a shot nor a waiver on it already, which is exactly what routed it here (`markers.screenshotSettled` is `false`). BACK-OFF: at most ONE screenshot attempt per PR per run; the screenshot comment IS your cross-run marker, and a PR carrying one never routes here again — a trusted comment embedding ANY `pr-screenshots/…png` on the PR settles it, whatever step 5 named the file, so do not go looking for one particular filename. If after a focused attempt the component genuinely cannot be rendered in the harness, ASK THE HUMAN FOR THE HARNESS: `pr-review-report flag-design <owner/repo> <n> "unrenderable-render: <the stack, e.g. cyclo.site SvelteKit/Tailwind> — needs a harness that can mount it"`. A stack the harness has NEVER been able to render is not a passing outage, it is a standing gap only a person can close, so it is a design question. Its `ai:design` label surfaces it in `human-queue` and is the cross-run marker; skip that PR on future runs (never re-attempt a PR already carrying its screenshot or that state).
3d. RESOLVE MERGE CONFLICTS on your OWN open PRs so they stay landable for the human (still existing-work — after 3b's reds and 3c's screenshots, before step 4 opens anything new). The merge cron does NOT resolve conflicts: a PR whose `mergeStateStatus` is `DIRTY`/`CONFLICTING` (or one the merge cron logged as NEEDS-REBASE / un-mergeable, e.g. BEHIND in a repo that requires up-to-date branches) is STUCK until its conflicts with the base are resolved, and that is YOUR job — overlapping PRs (e.g. many test PRs touching the same file) routinely go conflicting as siblings merge ahead of them. YOUR LIST IS A TYPED ROW, NOT A SEARCH: step 2 already computed it — every fleet row whose `nextAction` is `conflict-3d` is one of your open PRs whose `mergeState` is `DIRTY` or `BEHIND`, and the row carries that `mergeState` so you never re-ask which it is. Do NOT re-derive the set with `gh search prs`+`gh pr view`: that is one search plus a fetch PER PR to rebuild a classification `state-load` already handed you, and a second read of the fleet is a second answer free to disagree with the first — the same defect as step 3's old reject query. The ONE detail this step needs that the row does not carry is `baseRefName`, the branch to merge in; fetch it with `gh pr view <n> -R <owner>/<repo> --json baseRefName` for the PRs you ACTUALLY work, never for the fleet (step 2a's rule: fetch a single PR only for a detail the row does not carry). For each `conflict-3d` row, in order:
3d. RESOLVE MERGE CONFLICTS on your OWN open PRs so they stay landable for the human (still existing-work — after 3b's reds and 3c's screenshots, before step 4 opens anything new). The merge cron does NOT resolve conflicts: a PR whose `mergeStateStatus` is `DIRTY`/`CONFLICTING` (or one the merge cron logged as NEEDS-REBASE / un-mergeable, e.g. BEHIND in a repo that requires up-to-date branches) is STUCK until its conflicts with the base are resolved, and that is YOUR job — overlapping PRs (e.g. many test PRs touching the same file) routinely go conflicting as siblings merge ahead of them. YOUR LIST IS A TYPED ROW, NOT A SEARCH: step 2 already computed it — every fleet row whose `nextAction` is `conflict-3d` is one of your open PRs whose `mergeState` is `DIRTY` or `BEHIND`, and the row carries that `mergeState` so you never re-ask which it is. Do NOT re-derive the set with `gh search prs`+`gh pr view`: that is one search plus a fetch PER PR to rebuild a classification `state-load` already handed you, and a second read of the fleet is a second answer free to disagree with the first — the same defect as step 3's old send-back query. The ONE detail this step needs that the row does not carry is `baseRefName`, the branch to merge in; fetch it with `gh pr view <n> -R <owner>/<repo> --json baseRefName` for the PRs you ACTUALLY work, never for the fleet (step 2a's rule: fetch a single PR only for a detail the row does not carry). For each `conflict-3d` row, in order:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Route every producer GitHub operation through a tool.

Line 58 still instructs the producer to use gh pr view for GitHub input. Lines 78-82 also instruct the producer to push a screenshot branch, post comments, create a migration issue, and read issue state with raw GitHub commands.

These operations bypass tested FSM transitions. They can create state that pr-review-report does not record or guard. Add typed subcommands and enumerate them in the producer profile before keeping these instructions.

As per coding guidelines: producer prompts must perform all GitHub input and output through pr-review-report subcommands and must not use raw gh or git as pipeline transitions.

Also applies to: 78-82

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@campaign-prompt.txt` at line 58, Update the producer instructions in step 3d
and lines 78–82 to route every GitHub input/output and pipeline transition
through typed pr-review-report subcommands, including fetching baseRefName,
resolving conflicts, pushing screenshot branches, posting comments, and creating
or reading migration issues. Enumerate these subcommands in the producer profile
before referencing them, and remove raw gh/git commands so all state changes are
recorded and guarded by FSM transitions.

Source: Coding guidelines

| `/close-candidate <owner/repo#n> reject "…"` | `record-close-candidate-verdict … reject` — drop the flag, back to the producer (issue-only) |
| `/reject <owner/repo#n> "…"` | `human-rule … reject --rework` / `human-rule-issue … reject --rework` — the send-back: `ai:reject` (PR) plus the trusted `Rework note` work order, one call, pinned to the head sha or the issue. `--rework` is REQUIRED on either subject; there is no parked reject |
| `/design <owner/repo#n> "…"` | `human-rule … design --rework` / `human-rule-issue … design --rework` — the answer, delegated as a work order: `ai:reject` on a PR (the same send-back a rejection is, #219), comment-only on an issue. `--rework` is REQUIRED; there is no parked spelling |
| `/needs-work <owner/repo#n> "…"` | `human-rule … needs-work --rework` / `human-rule-issue … needs-work --rework` — the send-back: `ai:needs-work` (PR) plus the trusted `Rework note` work order, one call, pinned to the head sha or the issue. `--rework` is REQUIRED on either subject; there is no parked needs-work |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Document the issue-side state explicitly.

The /needs-work row names human-rule-issue but documents only ai:needs-work, which applies to pull requests. State that the issue path writes human:needs-work, consistent with plugins/human-fsm/commands/needs-work.md.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/human-fsm/README.md` at line 19, Update the /needs-work documentation
row to distinguish the issue path from the pull-request path: retain
ai:needs-work for pull requests and explicitly state that human-rule-issue
writes human:needs-work, matching the behavior documented in needs-work.md.

Comment thread review-prompt.txt
Comment on lines +15 to +16
- **QA GATE (mandatory):** a PR whose body lacks the QA-GUIDE.md section-8 evidence block, or whose claims in it do not hold, is `needs-work` with note "missing/false QA evidence" — regardless of how correct the diff looks. Correct-looking and unpinned is the failure mode this gate exists for.
- **SCREENSHOT GATE (mandatory):** a PR that changes RENDERED OUTPUT a user sees (`.svelte` markup, a visible component, user-visible styling in packages/webapp or packages/ui-components, a non-`.svelte` service whose output is displayed, or a `site/` dashboard page's visible markup) is NOT review-ready without VISUAL evidence: `needs-work` with note "UI change, no screenshot — not review-ready", UNLESS a trusted producer comment in `pr_context` embeds a `pr-screenshots/shots/…png` OR carries a VALID `screenshot pending (manual): <reason>`. **A WAIVER IS VALID ONLY WHEN ITS REASON IS A RENDER THAT WAS ATTEMPTED AND FAILED**, and the reason has to NAME THE ATTEMPT: WHAT WAS RUN (the component or page it mounted, the command) AND WHERE IT STOPPED (the throw, the gate, the fixture that was missing) — "mounted `RewardsCard.svelte` in the Vite harness, it throws without a `@tanstack/svelte-query` context", "loaded `site/index.html` in headless chromium, the changed section renders empty without a live `health.json`", "the harness reaches the connect-wallet screen and the signature dialog is unreachable headless". **A STATEMENT ABOUT THE WORLD IS NOT AN ATTEMPT:** "the stack has never rendered in it", "this stack is unsupported", "there is no harness for this repo" are true whether or not anybody TRIED, so they name no attempt and waive nothing — `needs-work` on the same ground as a claim about the output. And where the attempt shows a WHOLE STACK has no render path at all, the waiver is not the end of it: that is a standing gap only a person can close, so the verdict is `design` with that gap as the note (the producer's side raises `flag-design "unrenderable-render: …"` for the same reason) — never a `ready` that banks the waiver and leaves the gap unrecorded. **A JUDGEMENT ABOUT THE RENDERED OUTPUT IS NEVER A WHY-NOT:** "pixel-identical", "no visible effect", "cosmetic only", "the browser ignores this attribute anyway" are the CONCLUSIONS a render exists to support or refute, so they waive the evidence with the claim it was supposed to produce — and a change nobody TRIED to render is not pending, it is MISSING. That waiver is `needs-work` with note "screenshot waived on a claim about the render, not a failed attempt". Both of 2026-07-29's human send-backs are this shape: cyclo.site#431 waived on "rendered output is pixel-identical" while removing attributes from an input carrying a long conditional Tailwind class list (`[appearance:textfield]`, webkit spin-button resets) — identical is precisely what an unintended shift hides behind; cyclo.site#408 carried a screenshot-pending marker through THREE vetter passes, and the render nobody made would have shown in one look that its rewritten rewards card lists four epochs that have ALL ENDED — the defect all three verdicts missed. Judge whether a USER sees the change, not the file extension. The gate fires on a GUI CLAIM, not only a changed UI file: if the PR body or its QA evidence ASSERTS a rendered outcome ("renders/displays/shows X", "the toast now reads Y", "the modal shows both balances") — a claim about what a user SEES — that claim needs a screenshot backing it (or a waiver meeting the bar above); an unbacked GUI claim is `needs-work` with note "GUI claim, no visual proof". A grepped code path is not evidence a user sees the result — hold every GUI claim (producer close-candidate flags included, when you evaluate them) to a screenshot or a FAILED RENDER ATTEMPT, symmetric with the producer's step-7a GUI-claim rule.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Allow evidence failures to produce honest needs-work notes.

Lines 15-16 make missing or false QA evidence and missing screenshots valid needs-work grounds. Line 24 then requires every needs-work note to name a code defect. This conflict can force a misleading note or make a valid evidence repair unusable.

Change the requirement to cover review-blocking evidence failures as well as code defects.

Proposed wording
-   - The `needs-work` note MUST name a specific code defect that would fail review EVEN IF CI WERE GREEN
+   - The `needs-work` note MUST name a specific review-blocking defect or evidence failure; do not use CI status or merge conflicts as the ground

Also applies to: 24-24

🧰 Tools
🪛 LanguageTool

[style] ~16-~16: Consider shortening this phrase to just ‘whether’, unless you mean ‘regardless of whether’.
Context: ...e is no harness for this repo" are true whether or not anybody TRIED, so they name no attempt ...

(WHETHER)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@review-prompt.txt` around lines 15 - 16, Update the requirement governing
needs-work note contents near the review verdict rules so it explicitly permits
review-blocking evidence failures, including missing or false QA evidence and
missing screenshots, alongside code defects. Preserve the existing mandatory
evidence gates and their specified notes, ensuring valid evidence failures can
produce honest needs-work notes without requiring a misleading defect
description.

… drain

`migrate-needs-work` was the #133 one-shot: move every open PR still carrying
the RETIRED PR-side `human:needs-work` onto `ai:needs-work`. It has run. Across
`rainlanguage` + `cyclofinance` + `S01-Issuer` the open-PR population on that
label is now ZERO (the two remaining subjects are closed PRs), which is the same
residue-zero bar #225 held `migrate-blocked-deploy` to. Per the standing ruling
on #221 a migration is an
execution vehicle rather than permanent machinery, so the verb comes out with
the state it drained — exactly as `migrate-blocked-deploy` (#225) and
`migrate-design` (#224) did, each leaving only a test that the subcommand no
longer parses.

There is no separate `migrate-reject`: this branch had renamed that verb to
`migrate-needs-work`, so both spellings go with this one deletion.

Done compiler-first — the `Cmd` variant, its dispatch arm, the plan/mode
machinery and its tests, then `RETIRED_HUMAN_NEEDS_WORK_LABEL` itself, following
every rustc error rather than re-literalising anything to keep the tree
compiling. That cascade is larger than the subcommand, because the retired label
was the last thing holding the PR-side sacred-label machinery up:

- `PR_SACRED_LABELS` had one entry, so it and `has_human_override` are gone. A
  PR's human-sacredness is now exactly what #133/#219 said it should be: a
  native review, or a `👤 human` ruling pinned to the current head. No label.
- `PrSignals.human_parked` and `next_action`'s parking arm — #225's list names
  this one explicitly. Parking is now a modeled `ai:*` state, not a namespace.
- `STATE_HUMAN_NEEDS_WORK`, its `classify_lane` arm, its daily-review section,
  and `Lane::HumanDecisions` — the lane held that one state, so it drained to
  nothing and came out with it.
- `DesignHit::HumanRuled` and `/nd`'s `humanRuled` count, whose only producer
  was `has_human_override`. `aiDesign`'s stated partition loses that term.
- The worklist row's `humanOverride` key, for the same reason. Nothing outside
  the binary read it.

`human:needs-work` is NOT added to `DELETED_STATE_LABELS`, deliberately. That
registry claims no transition can write the string again, and on an ISSUE that
is false: `human-rule-issue needs-work` still writes it and 21 open issues carry
it today. Only the PR-side state was removed, so the lane-arithmetic test names
the string locally and says why.

What the compiler could NOT see, and the suite caught: fourteen tests built
their human-sacred witness from the string literal `"human:needs-work"` rather
than the constant, so they compiled clean and then failed — the verdict/scope/
lens/convention gates, the MCP refusal, `producer_state_plan`, the state-load
fixture's 36-PR cohort, and the golden descriptor and lane rows. Each now uses a
form that is still a human decision (a native review), or asserts the new truth.
The last grep sweep then found what neither reached: prose in README.md,
CLAUDE.md, `keep-open.md` and `nm.md`, plus the subcommand's row in CLAUDE.md's
table — seven documentation sites with no compiler or test coverage at all.

Counts re-derived from the merged table rather than carried across:
`STATE_DESCRIPTORS` 12 -> 11, `REVIEW_LANE_SECTIONS` 8 -> 7, `Lane::ALL` 6 -> 5.

Gates: `cargo test` 1047+3+9+47+8+7+4 passed 0 failed; CI-exact
`rainix-rs-static` at rainix 53e96a7 with the changed file touched first;
`pre-commit run --all-files`; `nix build .#pr-review-report`.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
README.md (1)

224-225: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use one spelling for “modeled”.

Use the repository’s selected spelling consistently. The document currently mixes modeled and modelled.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 224 - 225, Update README.md to use the repository’s
selected spelling consistently, replacing the alternate “modeled”/“modelled”
variant wherever it appears in the document.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Around line 1457-1464: Update the README passage describing deleted labels so
it preserves the namespace-specific behavior: deleted human:* labels must remain
leaks, while deleted ai:* labels fall through due to the ai-scoped vetter sweep.
Remove or revise the statement equating deleted labels across both namespaces,
keeping the surrounding human-decisions explanation intact.

---

Outside diff comments:
In `@README.md`:
- Around line 224-225: Update README.md to use the repository’s selected
spelling consistently, replacing the alternate “modeled”/“modelled” variant
wherever it appears in the document.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: eaf8840b-8e9e-4bfc-b0cb-250276c48313

📥 Commits

Reviewing files that changed from the base of the PR and between 997be30 and 4125ec3.

📒 Files selected for processing (5)
  • CLAUDE.md
  • README.md
  • plugins/human-fsm/commands/keep-open.md
  • plugins/human-fsm/commands/nm.md
  • pr-review-report-rs/src/main.rs

Comment thread README.md
Comment on lines +1457 to +1464
`ai:*` label.) (There is no **human-decisions** lane. It held exactly one
state — the RETIRED PR-side `human:needs-work` (#133) — and once
`migrate-needs-work` had moved that population to `ai:needs-work` the state,
the lane and the migration verb all came out together. A live human decision
on a PR is a comment or a native review, never a label; `human:design` is
DELETED (#219) too. A deleted label behaves the same way in either namespace:
like the deploy string above, residue wearing it models nothing and falls
through.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve namespace-specific leak behavior.

This text says deleted human:* labels fall through like deleted ai:* labels. Earlier in README.md, Lines 664-670 state that deleted human:* labels remain leaks because the vetter sweep is ai:*-scoped. Keep these behaviors separate.

Proposed correction
- A deleted label behaves the same way in either namespace: ... residue wearing it models nothing and falls through.
+ Deleted `ai:*` labels fall through to `un-vetted` and are absorbed by the next vetter verdict.
+ Deleted `human:*` labels remain leak candidates because the vetter sweep is `ai:*`-scoped.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
`ai:*` label.) (There is no **human-decisions** lane. It held exactly one
state — the RETIRED PR-side `human:needs-work` (#133) — and once
`migrate-needs-work` had moved that population to `ai:needs-work` the state,
the lane and the migration verb all came out together. A live human decision
on a PR is a comment or a native review, never a label; `human:design` is
DELETED (#219) too. A deleted label behaves the same way in either namespace:
like the deploy string above, residue wearing it models nothing and falls
through.
`ai:*` label.) (There is no **human-decisions** lane. It held exactly one
state — the RETIRED PR-side `human:needs-work` (`#133`) — and once
`migrate-needs-work` had moved that population to `ai:needs-work` the state,
the lane and the migration verb all came out together. A live human decision
on a PR is a comment or a native review, never a label; `human:design` is
DELETED (`#219`) too. Deleted `ai:*` labels fall through to `un-vetted` and are absorbed by the next vetter verdict.
Deleted `human:*` labels remain leak candidates because the vetter sweep is
`ai:*`-scoped.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 1457 - 1464, Update the README passage describing
deleted labels so it preserves the namespace-specific behavior: deleted human:*
labels must remain leaks, while deleted ai:* labels fall through due to the
ai-scoped vetter sweep. Remove or revise the statement equating deleted labels
across both namespaces, keeping the surrounding human-decisions explanation
intact.

@thedavidmeister

Copy link
Copy Markdown
Contributor Author

Reviewed 4125ec3: approve — read the diff and verified the merge independently. This renames the send-back state from reject to needs-work across the label, the ruling verb, the vetter verdict, the slash command, the counts key, the lane state and the prose, closing the divergence that has been live since the labels were migrated this morning: GitHub carries ai:needs-work on 91 subjects while main's classifier still spelled ai:reject in 49 places, so the producer's rework queue read empty and the dashboard showed a drained lane that was not drained.

The merge was the hard part — based on 71ac743 with main 31 commits ahead, 8 files and 67 hunks in main.rs. Resolution took main's file whole (main was structurally right in nearly every hunk; this branch contributed spelling) and rebuilt the rename compiler-first on merged reality, then ported back the one thing the branch genuinely added: the histFold "reject" entry and its continuity test. The rename reached ten symbols that post-date the branch, all from #224 — NextAction::ReworkReject and its rework-reject string, and the whole RejectState typed row with its predicates and PrSignals field.

Verified by hand at this head: HUMAN_PR_RULINGS maps both verbs to ai:needs-work, so no PR-side transition can write a human:* label — which is why deleting the spent migrate-needs-work cascaded correctly into the sacred-label machinery, the human-decisions lane and /nd's humanRuled count; each guarded a state nothing could create. The issue side is untouched and still writes human:needs-work and human:keep-open. Live confirms: 0 open PRs on human:needs-work, 21 open issues.

History continuity measured rather than assumed: reject/humanReject/relink carry 218 samples each in the rollup and needsWork has zero, so the entire drawn series arrives through the fold; human-queue-history.jsonl is not rewritten. 1047 tests green, CI-exact static, pre-commit and nix build all 0, mutants killed on the deletion targets, 19 checks green.
Rulings-conformance: checked against CLAUDE.md and every ruling stated for this work. (1) '"needs work" is a more standard tag than rejected' — renamed consistently, label through verb through docs, so no verb writes a differently-named label. (2) 'a subcommand? … once off' — the spent migrate-needs-work is deleted, leaving only a test that it no longer parses, exactly as #225 and #224 ended. (3) removal-is-semantic-not-grep — deletion done compiler-first without re-literalising; ~46 compiler sites, 14 that compiled clean and failed in the suite, 15 reachable only by grep. (4) 'the prs are open, trying to get them right' — both findings were folded in rather than filed. (5) 'coverage rather than manual verification' — the fold continuity is a test, not a checked claim. (6) The agent declined to add human:needs-work to DELETED_STATE_LABELS because that registry asserts no transition can write the string, which is false on the issue side — correct, and the alternative would have been a lie a passing test enshrined. The artifact obeys each.

@thedavidmeister
thedavidmeister merged commit 39b92a3 into main Aug 7, 2026
21 checks passed
thedavidmeister added a commit that referenced this pull request Aug 8, 2026
The gate asserted that no committed rollup line carries the post-rename
`needsWork` key. That was the same fact as "the fold is load-bearing"
only until the first rollup after #231 was committed; sixteen such lines
are on main today, so the gate fires on the date rather than on a defect.

It now asserts the property that is actually invariant over an
append-only history: each folded spelling was measured on committed lines
where `needsWork` is absent, which is exactly the span a series folding
nothing would lose. Those lines exist and no later rollup can un-write
them.

Co-Authored-By: Claude Opus 5 (1M context) <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.

RULED: rename the send-back state from reject to needs-work, label through verb through docs

2 participants