forked from heavy-duty/ceremony
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:
parent
6db45587ed
commit
ded7f9ac04
5 changed files with 46 additions and 3 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
24
LABELS.md
24
LABELS.md
|
|
@ -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, D1–D10). 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
|
||||
|
|
|
|||
|
|
@ -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)).
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue