Skip to content

Remove every human-ruled STATE: a human decision is a transition, not a bucket - #247

Merged
thedavidmeister merged 1 commit into
mainfrom
remove-human-ruled-state-244
Aug 9, 2026
Merged

thedavidmeister merged 1 commit into
mainfrom
remove-human-ruled-state-244

Conversation

@thedavidmeister

Copy link
Copy Markdown
Contributor

Closes #244

human-ruled is not a state. A human decision is a transition INTO a state, so no bucket may mean "a human already ruled here". The PR side had already finished this argument; the issue side still parked, and this deletes that park.

What cc_gate looks like now

fn cc_gate(
    repo_archived: bool,
    flag_at: &str,
    verdict: Option<(String, String)>,
    vetter_close: bool,
    torn_ruling: bool,
) -> CcGate {
    if repo_archived {
        return CcGate::RepoArchived;
    }
    if torn_ruling {
        return CcGate::TornHumanRuling;
    }
    if flag_at.is_empty() { … }
    match verdict { … }
}

No HumanRuled variant, no human_ruled parameter, no "human-ruled" name, no "skip-human-decided" action. RepoArchived stays first — an archived repo refuses every write, and deciding writability before deciding what to write is deliberate and documented.

The ordering is the fix: RepoArchived → HumanRuled → TornHumanClose meant the park dominated the self-heal by arm order alone, so the completion could never fire.

The six items

  1. CcGate::HumanRuled deleted — variant, name, action, parameter, arm. Downstream of the action's deletion, the vetter state-load's "skip-human-decided" => n_human += 1 arm and its skipHumanDecided key go too: with nothing producing the action, the key would be a constant zero, which is the deleted bucket wearing a number. (VetAction::SkipHuman on the PR lane is untouched — different lane, retained by the issue.)

  2. Already true, and now pinned as a property. CLAUDE.md's claim holds in code exactly as written. On an issue with a live producer flag, human_issue_rule_plan consults FLAG_DISPOSING_RULINGS = ["close-candidate", "keep-open"]:

    • needs-work / design → HumanRulePlan::StrandsFlag, refused outright, nothing written;
    • keep-open → lands, and clears: match (ruling, live_flag.is_some()) { ("keep-open", true) => vec!["ai:close-candidate"] };
    • human-close → lands, and human_close_plan pushes PENDING_CLOSE_FLAG into clears.

    So no issue ruling can land while leaving a live flag — the property was total already. No production change was made here. What was missing was the total statement: each verb had its own test, nothing asserted there was no third side. no_issue_ruling_lands_while_leaving_a_live_flag now drives the vocabulary table plus human-close's plan word and asserts every verb is on one of the two sides.

  3. TornHumanClose → TornHumanRuling. The state now covers both ways a ruling lands half-written: a recorded 👤 human close on a still-open subject (human:close-candidate splits decide-from-do, a phase no other state models: fuse close rulings into human-close, machinery completes tears #213), and a sacred human:* label standing beside the live ai:close-candidate it was supposed to retire. Both are torn writes; the state-load COMPLETES them. Which write is owed is a typed, pure decision — TornRulingCompletion::{CloseThenRetireFlag, RetireFlag} — extracted precisely because getting it backwards would CLOSE an issue a human ruled keep-open, and a branch inside the network call is a branch no test can reach.

    The label half is asked only of a live flag, which is load-bearing twice: a ruling label with no producer claim under it is already NoFlag, whose close-candidate flags can reach two states with no transition that clears them #179 clearance consumes it; and on a PR the un-flagged label is VetterClose, a verdict in force that nothing here may retire (Close-candidate vetting misses PR-shaped flags: gh search issues scopes to type:issue #211). Reading a stray human:* as a tear there would erase the vetter's own judgement.

    Where both marks somehow coexist, the close wins — not a new precedent, but the one human_close_plan already writes down by superseding every standing human:* ruling.

  4. Counts bucket removed, invariant kept. Field, "humanRuled" emission, increment arm, key-list entry, doc line, and both test fixtures. tornHumanClose → tornHumanRuling; completedHumanClose/humanCloseCompletionFailed and their arrays follow the rename (no consumer outside main.rs — checked across the whole repo). The partition assertion survives in reduced form and is what proves the deleted rows were re-homed rather than dropped:

    let parts = c.presentable + c.vetter_close + c.torn_human_ruling
              + c.unvetted + c.no_flag + c.rejected_still_flagged + c.fetch_errors;
    assert_eq!(c.flagged, parts, "flagged must be provably the sum of its parts: …");
    assert_eq!((c.presentable, c.no_flag, c.unvetted, c.torn_human_ruling), (1, 1, 1, 1));

    The fixture's o/ruled row (ai:close-candidate + human:keep-open + a live flag) is the row that used to sit in human_ruled; the tuple assert is what names where it went. Strengthened on the wire side too: counts must not emit humanRuled at all, and the emitted key count is asserted against the partition list rather than trusted to have been kept in step.

  5. README fossil deleted. humanRuled named a bucket with no code — DesignQueueCounts has no such field, DesignHit has three arms, nd_hit_class never reads labels. Since Carry next_design's split on the human-queue design lane, so the dashboard can show the inbox apart from the defect #243 emits the design breakdown for the dashboard, it would have had someone wiring a field that never arrives.

  6. /ndd stale sentence deleted. There is no excluded key; replaced with the shape that is actually emitted (counts.draft, listed in withheld). human-fsm bumped 0.16.0 → 0.16.1 in both the manifest and the marketplace listing, per the version-hygiene gate.

Retained exactly as the issue directs: pr_human_sacred / human_ruled_at_head / humanSacred / humanRuledAtHead, has_human_ruling in the flag-refusal path (and as the humanSacred row field), and TornHumanClose as a concept. Nothing needed to be raised — no retained item had to move for the rest to cohere.

QA

  • Discriminating tests: queue_tests::a_human_ruling_beside_a_live_flag_is_a_torn_write_the_state_load_completes (the issue's stated acceptance case), human_rule_tests::the_completion_closes_only_where_a_close_was_actually_ruled, human_rule_tests::no_issue_ruling_lands_while_leaving_a_live_flag, next_close_candidate_tests::{a_torn_human_ruling_dominates_the_flag_lifecycle, a_torn_ruling_dominates_and_a_flagless_label_is_stranded, the_flag_counts_partition_the_whole_population_after_the_fan_out, the_envelope_states_what_the_page_left_behind, a_pr_close_verdict_is_human_owned_not_stranded_and_not_cleared}, human_rule_tests::every_tear_point_lands_where_machinery_can_finish_it. Fails on base — verified as mutant M1, which restores the deleted park as its own cc_gate arm and is therefore literally the pre-change behaviour: it kills 4 tests including the acceptance test. A base checkout cannot be used directly here because the change alters cc_gate's arity, so the new tests do not compile against it; M1 is the same proof without that confound. Suite: 1084 passed, 0 failed.

  • Mutations applied (15 applied, 15 killed, 0 survivors; each run is the WHOLE suite with the number of tests actually run asserted non-zero — a name filter that matches nothing exits 0 and reads as "survived", which is how the first pass of this harness lied about M1–M6/M9/M12):

    # line → mutation killing test
    M1 cc_gate torn arm → the old park (pre-change behaviour) a_human_ruling_beside_a_live_flag_is_a_torn_write_… (+3)
    M2 cc_gate torn arm deleted every_tear_point_lands_where_machinery_can_finish_it (+5)
    M3 cc_gate torn arm demoted below the lifecycle a_torn_human_ruling_dominates_the_flag_lifecycle (+5)
    M4 torn_human_ruling drops the recorded-close half every_tear_point_lands_where_machinery_can_finish_it
    M5 torn_human_ruling drops the ruling-label half (Remove every human-ruled STATE: a human decision is a transition, not a bucket #244's half) the_completion_closes_only_where_a_close_was_actually_ruled (+3)
    M6 torn_human_ruling drops the live-flag guard a_pr_close_verdict_is_human_owned_not_stranded_and_not_cleared
    M7 torn_ruling_completion branch inverted the_completion_closes_only_where_a_close_was_actually_ruled
    M8 torn_ruling_completion never closes the_completion_closes_only_where_a_close_was_actually_ruled
    M9 cc_row reports the torn state as an ordinary skip a_human_ruling_beside_a_live_flag_is_a_torn_write_… (+1)
    M10 torn count absorbed into unvetted (sum still holds) the_flag_counts_partition_the_whole_population_after_the_fan_out
    M11 humanRuled re-emitted as a constant zero the_envelope_states_what_the_page_left_behind (+1)
    M12 torn state made label-clearable a_clearance_comment_is_not_a_flag_a_verdict_or_a_prior_note (+2)
    M13 keep-open stops clearing the flag it contradicts no_issue_ruling_lands_while_leaving_a_live_flag (+1)
    M14 FLAG_DISPOSING_RULINGS widened to let needs-work land no_issue_ruling_lands_while_leaving_a_live_flag (+2)
    M15 human_close_plan stops retiring the pending flag no_issue_ruling_lands_while_leaving_a_live_flag (+1)

    M10 is the one that matters most for item 4: it preserves the partition sum, so only the per-bucket assertion can catch a bucket quietly absorbing the deleted one's rows — and it does.

  • Oracle: the issue's own ruling text (thedavidmeister, 2026-08-09) — "human decisions are transitions to a state, not a separate state, so nothing may sit in a bucket whose meaning is 'a human already ruled here'" — plus CcGate::TornHumanClose's pre-existing doc, which already stated the target posture verbatim ("the vetter's state-load EXECUTES the recorded close … and the human's queue never sees the subject"). Expected values are derived from those statements and from the write ORDER human_rule_steps / human_close_steps already pin, never recomputed with the classifier under test.

  • Category check: issue enumerates six items; all six covered (1 delete, 2 verified-already-true + property pinned, 3 generalise+rename, 4 counts+invariant, 5 README, 6 /ndd doc). The three explicitly-retained items are untouched, verified by reading each rather than by grep.

Verification: nix develop .#rust -c cargo test (1084 passed), cargo fmt --all -- --check (clean — confirmed clean on main first, so no reformat is included), and CI's exact static invocation cargo clippy --all-targets --all-features -- -D warnings -D clippy::all after touching the file so a warm target cannot mask a lint. pre-commit is not wired in this repo (no .pre-commit-config.yaml).

🤖 Generated with Claude Code

`CcGate::HumanRuled` parked every flagged subject carrying a `human:*`
ruling label: skipped on every state-load, counted under `humanRuled`,
transitioning nowhere, while the label kept the issue out of the
producer's backlog AND `record_close_candidate_verdict` refused to judge
the flag because a human had ruled. The PR side deleted its equivalent
park; the issue side still stood.

The arm is deleted and `TornHumanClose` generalised to
`TornHumanRuling`, which is what the pair actually is — a torn write.
`human-rule-issue keep-open` retires `ai:close-candidate` in the same
call, so the pair can only exist where that call tore between its label
write and its label clear; the state-load now COMPLETES it (retire the
flag, or execute a recorded close) instead of counting it. Ordering is
the whole fix: the park dominated the completion, so the self-heal could
never fire.

`humanRuled` is gone from the counts, and the partition invariant is
still asserted over the reduced set — that assertion is what proves the
deleted bucket's rows were re-homed rather than absorbed.

Closes #244

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

coderabbitai Bot commented Aug 9, 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: 6 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: 35c3e312-ce9d-4a26-bcee-696d8ec3e795

📥 Commits

Reviewing files that changed from the base of the PR and between 74b49ff and 10d405a.

📒 Files selected for processing (5)
  • .claude-plugin/marketplace.json
  • README.md
  • plugins/human-fsm/.claude-plugin/plugin.json
  • plugins/human-fsm/commands/ndd.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

Copy link
Copy Markdown
Contributor Author

Reviewed 10d405a: pass

Verified against the PR head blob rather than the report. grep for human_ruled\b|HumanRuled|humanRuled|skip-human-decided returns only: historical prose in the repo's established issue-citing style, a test asserting humanRuled is NOT emitted, and VetAction::SkipHuman's action string on the PR side — which is the retained head-pinned guard, not the deleted issue-side park. cc_gate has no HumanRuled arm and no human_ruled parameter; RepoArchived stays first. The partition assertion survives in reduced form with a per-bucket check, which matters because one mutant absorbs the torn count into unvetted and preserves the sum.

Rulings-conformance: checked against CLAUDE.md's rulings section and every ruling the human stated for this work.

  • "human ruled is not a thing, because human decisions are transitions to a state not a separate state" (human, 2026-08-09, the ruling recorded in Remove every human-ruled STATE: a human decision is a transition, not a bucket #244). OBEYED: the park is deleted and replaced by a completion. TornHumanRuling executes the write the ruling half-landed, which is the transition model the ruling states, and #133/#219 already applied on the PR side.
  • "remove ALL human ruled states, thoroughly" (same ruling) with the standing rule that removal is SEMANTIC, not grep — machinery gone and read-verified. OBEYED, and I verified it myself as above rather than accepting a grep-zero claim.
  • The three items the issue explicitly retained (pr_human_sacred/human_ruled_at_head and the payload fields, has_human_ruling in the flag-refusal path, TornHumanClose as a concept) were to stay unless raised as a question. OBEYED: 33 references remain across those three, none moved, and nothing was raised — the generalisation renamed the torn state rather than deleting it.
  • The partition invariant must still be ASSERTED over the reduced set, never weakened or dropped alongside the bucket (my own instruction to the implementer). OBEYED, and strengthened: the sum assertion is joined by a per-bucket tuple assertion and a wire-level assert that counts does not emit humanRuled.
  • "every state needs a consuming transition" (CLAUDE.md). OBEYED and this is the substance: the deleted park had no consuming transition at all, which is why an issue carrying human:keep-open plus ai:close-candidate sat for ever. The judgement call the implementer flagged — asking the label half only of a LIVE flag — is correct and protects #211: without it a stray human:* on a PR whose label is the vetter's own close verdict would be "completed" by retiring a verdict in force.
  • "do not run a formatter this repo is not already clean under" (after the 2176-line spurious rewrap earlier today). OBEYED: cargo fmt --check was verified clean on main first, so no reformat rides along.

CI: every check passes. The single non-pass is a plugin change bumps its version reporting skipping on one of two duplicate runs; the same check passes on the other, and the bump is real — plugin and marketplace both move 0.16.0 to 0.16.1 in the diff. Nothing red to account for.

Merging with --merge --admin per the standing no-squash rule.

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

Remove every human-ruled STATE: a human decision is a transition, not a bucket

2 participants