docs: needs-ruling doctrine — LABELS row + D5–D9 rationale, escalation mechanics in TRIAGE/BUILDER/REVIEWER, changelog (#51)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
claude-bot-andresmgsl 2026-07-23 00:49:21 +00:00
parent 6db45587ed
commit ded7f9ac04
5 changed files with 46 additions and 3 deletions

View file

@ -58,7 +58,13 @@ CONTRIBUTING; everything below is the shared flow.)
3. Never dismiss a review, never merge, never mark your own work as passed.
A blocking point you disagree with is answered with evidence or escalated
in the PR — a maintainer can be asked for a ruling; silence and
force-forward are not options.
force-forward are not options. The ruling ask has mechanics: set
`needs-ruling` on the PR, with one comment carrying the question, the
options, and your recommendation — a panel deadlock is consolidated into
that one question, never forwarded as three phrasings of it. The label
stays until agreement is *reached*, not until the maintainer replies;
you record the ruling, remove the label, and return the PR to its flow
([LABELS.md](LABELS.md)).
## Handoff

View file

@ -6,6 +6,8 @@ so entries say what changed, cite the issue, and stop.
## Unreleased
- `needs-ruling` — the cross-cutting flag for a pending human decision, excluded from `state:needs-human` and from the staleness sweep (#51).
## 0.1.0 — 2026-07-22
- `lib/version.sh` — one version abstraction, `file` and `package-json` backends (#3).

View file

@ -18,7 +18,7 @@ and the reconciler recomputes it from GitHub's own facts.
|---|---|---|
| `state:building` | `#FBCA04` | the builder — PR is a draft |
| `state:bots-reviewing` | `#1D76DB` | the reviewer panel to finish the round (a request is live) |
| `state:addressing` | `#D93F0B` | the builder — round complete without full approval, or nobody was asked, or a blocker is up |
| `state:addressing` | `#D93F0B` | the builder — round complete without full approval, or nobody was asked, or a blocker is up, or a ruling is pending |
| `state:needs-human` | `#8250DF` | the human — **this PR could be merged right now**: zero blockers, whole panel approved the current head |
`bots-reviewing` vs `addressing` is deliberate: staleness in the first means
@ -66,9 +66,31 @@ staleness sweep will reclaim (issue #18); until that machinery exists,
|---|---|---|
| `stale` | `#B60205` | no activity for 48h — sweep-managed, never hand-applied |
| `blocked` | `#6A737D` | (see above — same label serves PRs waiting on another PR/issue; legitimately quiet, the staleness sweep skips it) |
| `needs-ruling` | `#D4C5F9` | a human decision is required; the question, options and a recommendation are in the flagging comment. Set by triage or the builder; a state, not a signal — it clears on agreement, not on a reply |
| `release` | `#0E8A16` | release flow, versioning, packaging work — and the ceremony PR itself |
| `merge-next` | `#0E8A16` | head of the merge queue — merge this one next. Queue order is *intent*: never set by the reconciler, only cleared by it |
`needs-ruling` marks where the human's turn is when the pending thing is a
*decision*, not a merge (#50 settled it, D1D10). It is not
`state:needs-human`: that label means exactly "this PR could be merged right
now", and the retired `state:needs-rebase` is the family's proof that a
label meaning two things lies about both. It is not a `blocker:*` either:
every blocker names work the *builder* owes, a ruling is owed by the human —
and the flag must live on issues too, where blockers do not exist. On issues
it coexists with the queue labels (the one-of-three invariant above ignores
it); its color is the light shade of `state:needs-human`'s, so the human
axis reads as one family. It is a state, not a signal: set only with the
escalation contract (the question, the options, a recommendation — a bare
flag is noise), it stays up until agreement is *reached* — a human reply
alone does not clear it — and its setter closes it out: records the ruling
as a decision in one comment, removes the label, and returns the item to
its flow in that same comment, never as a side effect. If the human
disagrees that agreement was reached, the label goes back on. The machine
reads it and never writes it: the reconciler refuses `state:needs-human`
while it stands (the PR falls to `state:addressing` — the ball on the PR is
the builder's, who carries the ruling in), and the staleness sweep skips
it, because waiting on a human is legitimately quiet.
## Scope — which surface? (PRs and issues, any number)
All scopes share one calm color, `#C5DEF5` — scopes locate, states alert. The

View file

@ -68,3 +68,9 @@ saw Y" outranks one that says "this looks like it might".
with evidence until one side concedes or the builder escalates to the
maintainer for a ruling. Two reviewers pulling a builder in opposite
directions without resolution is a panel failure, not a builder failure.
The escalation is flagged `needs-ruling` — by the **builder**, never by
you: one accountable flag-setter per PR is what keeps the escalation
contract enforceable and hands the human one consolidated question
instead of three phrasings of it. Your job is to state the disagreement
precisely enough that the builder can write that escalation
([LABELS.md](LABELS.md)).

View file

@ -35,7 +35,14 @@ is the failure this whole flow exists to prevent.
3. **Escalate.** The blocker is a *decision* only a human owns — scope,
money, product direction, breaking a public contract. Say precisely what
the decision is, list the options with your recommendation, and name the
decider. The discussion is where humans decide; wait there.
decider. The discussion is where humans decide; wait there. When the
decision blocks something already on the board — an existing issue, or
minted work a discussion's ruling gates — set `needs-ruling` on it too,
so the board shows where the human's turn is; the issue keeps its queue
label. You set the flag, so you close it out ([LABELS.md](LABELS.md)):
judge when agreement is reached, record the ruling as a decision in one
comment, remove the label, and return the issue to its flow in that same
comment.
4. **Decline.** Real idea, wrong repo or wrong time. Say why plainly, link
where it belongs if anywhere, close. A refusal with reasons is a good
outcome; a zombie discussion is not.