From 6ebdd2efd9f57e2d6c3cb7c802ad2d5921684d74 Mon Sep 17 00:00:00 2001 From: cndgrr <59120057+cndgrr@users.noreply.github.com> Date: Tue, 4 Aug 2026 13:11:32 +0000 Subject: [PATCH 01/15] =?UTF-8?q?docs(builder):=20WIP=20=E2=80=94=20slim?= =?UTF-8?q?=20BUILDER.md=20to=20the=20rules,=20bare=20local=20cites?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BUILDER.md | 580 +++++++++++++++++++-------------------------- changelog.d/281.md | 5 + 2 files changed, 243 insertions(+), 342 deletions(-) create mode 100644 changelog.d/281.md diff --git a/BUILDER.md b/BUILDER.md index bc57d26..a8f7620 100644 --- a/BUILDER.md +++ b/BUILDER.md @@ -7,226 +7,169 @@ triage bug, and the move is to say so on the issue, not to guess. ## Picking - Pick from issues labeled **`ready`** — never `blocked`, never `claimed`, - never an `epic` (epics organize; their children are the work). -- Respect dependency order: inside an epic, take the earliest unblocked - unclaimed child. Between epics and strays, prefer the issue that unblocks - the most other work. -- In a repository that adopts version epics, read [RELEASES.md](RELEASES.md) - before choosing among release-window members. -- **Your own red head outranks a new claim.** A failing check at the head - of a PR you authored is picked up **before claiming another issue** — - repairing your own red PR comes ahead of new work, which is why the - engine's duty order evaluates ci-red between resume and build (crew#17: - ceremony#163 sat with full-panel approvals at its head, mergeable, and - stranded on an HTTP 429 in a job that never ran the PR's code, because no - wake covered a red head that owed no round and had no conflict). Red and - green here are the ruled terms of the review round below: a cancelled or - stale check is not a green head; a skipped or neutral one is. The - recovery path (crew#17): inspect the check at the head and record the - failing check and its failure class; rerun a clearly retryable - infrastructure failure without changing code; when the failure belongs to - the branch, return to the normal fix-round and worklog discipline; leave - visible evidence when a rerun cannot be started or the cause is - uncertain; never repeatedly rerun a deterministic branch failure without - a corrective commit; and proceed to handoff once the check is green and - current-head approvals stand. A PR of yours with a red head is **not - parked** — the next move is yours, whatever the round's verdict state - says (shape 2 below carves this out explicitly). How the engine detects a red - head — its ledger, its quiet rules, the rollup's node shapes — is crew's - to describe, not this file's. + never an `epic` (epics organize; their children are the work). Inside an + epic take the earliest unblocked unclaimed child; between epics and strays + prefer the issue that unblocks the most other work. Where a repository + adopts version epics, [RELEASES.md](RELEASES.md) governs the choice among + release-window members. +- **Your own red head outranks a new claim.** A failing check at the head of + a PR you authored is picked up before claiming another issue; a red head + that owes no round and holds no conflict is otherwise nobody's next move, + and the PR strands mergeable (#163). The recovery path: record the failing + check and its failure class; rerun a clearly retryable infrastructure + failure without changing code; when the failure belongs to the branch, + return to the normal fix-round and worklog discipline; leave visible + evidence when a rerun cannot be started or the cause is uncertain; never + repeatedly rerun a deterministic branch failure without a corrective + commit; and hand off once the check is green and current-head approvals + stand. Such a PR is **not parked** — the next move is yours whatever the + round's verdict state says. Red and green are the review round's ruled + terms below; how the engine detects a red head is crew's to describe, not + this file's. - **One build at a time.** You hold at most one issue on which you are writing or revising a deliverable — finish or release that work before starting new work. The rule counts build work in flight, not claims: a - claim does not consume the slot while it is **parked**, meaning the next - move belongs to someone else. Exactly five shapes qualify: + **parked** claim, one whose next move belongs to someone else, does not + consume the slot. Exactly five shapes park: 1. the issue carries `needs-ruling`, its escalation names a decider, and its `Blocked:` line stops the remaining work; - 2. the deliverable is in a review round where every outstanding verdict - belongs to someone else — either the round is awaiting its first - verdicts, or it was answered whole and the owed re-requests posted — - by head, not by verdict: every panelist after a push, the - non-approvers alone at an unchanged head (the review round, steps - 1–2). This is the *live* round; shape 4 is - the *passed* one — they are sequential and do not overlap. A red - check at the current head takes the deliverable **out of this - shape**: mid-round CI going red is exactly the state that reads as - "waiting on the panel" and is not — the next move is yours (the - red-head rule above), and reading it as parked is what strands the - PR; + 2. the deliverable is in a **live** review round, every outstanding + verdict someone else's — awaiting its first verdicts, or answered whole + with the owed re-requests posted, by head and not by verdict (steps 1–2 + below). A red check at the current head takes it **out of this shape**: + that state reads as waiting on the panel and is not, the next move is + yours, and reading it as parked strands the PR; 3. every remaining acceptance criterion is operator-owned, stated as such by triage on the issue; 4. the deliverable is **handed off** — the round passed, no `blocker:*` - stands, and you set `state:needs-human` per Handoff (below). The - remaining move is the human's merge. - 5. the claim is **held by directive** — triage or the operator has told - you to stop, the direction names what the hold waits on, and that thing - is not yours to move. This is not "waiting for a good moment": somebody - else has decided the work must not proceed, and only they end it. - And it ends the same way it started: **on the labels.** When the queue - labels and any prose — an issue body header, a triage comment, an - operator's comment — disagree about whether a hold stands, the most - recent queue-label event by the hold's owner governs, and the prose is - stale until someone corrects it. So before standing down *or* standing - up on a hold, read the issue's **label events** + stands, `state:needs-human` is set per Handoff (below), and the merge + is the human's. Shapes 2 and 4 are sequential and never overlap; + 5. the claim is **held by directive** — triage or the operator stopped the + work, the direction names what the hold waits on, and only they end it; + this is never "waiting for a good moment". A hold ends the way it + started, **on the labels**: where the queue labels and any prose + disagree about whether it stands, the most recent queue-label event by + the hold's owner governs and the prose is stale until corrected, an + operator being free to lift by label alone (#149, #151). So before + standing down *or* standing up, read the issue's **label events** (`gh api /repos/{owner}/{repo}/issues/{n}/timeline`), not only its - comments: an operator may lift by label alone, and on 2026-07-24 did, - twice, on [#149](https://github.com/heavy-duty/ceremony/issues/149) - and [#151](https://github.com/heavy-duty/ceremony/issues/151). Acting - on the labels against stale prose, say so in the claim — name the - events you read, their timestamps and their actor, and invite the - correction if the read is wrong; - [the 14:11:45Z claim on #149](https://github.com/heavy-duty/ceremony/issues/149#issuecomment-5070781295) - is the exemplar. Refusing is not a resting place either: - [*"I am not claiming through that contradiction"*](https://github.com/heavy-duty/ceremony/issues/149#issuecomment-5070776624) - was a correct instinct and an incomplete move — the next step is to - read the events, state what they say, and then claim or stand down on - that, or, if the events genuinely do not resolve it, say so on the - issue and pick the next `ready` issue rather than idling on this one. - Not parked — these are what the rule defends against: waiting on - yourself, waiting on CI (a red head is your own work, above; a pending - one resolves without you), or waiting for a good moment. An issue you have - simply stopped working on is not parked either — that is abandonment, - and its move is unchanged: unassign and restore `ready` (Claiming, - below). - The 2026-07-23 board is why the rule counts work and not claims: one - builder correctly held - [#15](https://github.com/heavy-duty/ceremony/issues/15) (`offsite`, - round answered whole, one verdict outstanding) and - [#16](https://github.com/heavy-duty/ceremony/issues/16) (`needs-ruling` - hard block, triage said hold) parked beside the one active build, - [#73](https://github.com/heavy-duty/ceremony/issues/73). + comments; acting on the labels against stale prose, say so in the claim + — name the events, their timestamps and their actor, and invite the + correction. Refusing to claim through the contradiction is not a + resting place either: where the events genuinely do not resolve it, say + so on the issue and pick the next `ready` issue. + Not parked: waiting on yourself, waiting on CI (a red head is your own + work; a pending one resolves without you), or waiting for a good moment. + An issue you have simply stopped working on is abandoned, not parked — + unassign and restore `ready` (Claiming, below). The rule counts work and + not claims because parked claims are legitimately held beside the one + active build (#15, #16, #73). ## Claiming - Assign yourself, swap `ready` → `claimed`, and comment that you are - starting. The claim is a promise of a draft PR soon — a claim with no PR - and no activity is what the staleness sweep reclaims unless `offsite` - records that its PR lives in another repository. -- **A park is declared, never inferred.** When your claim enters a parked - shape (Picking, above), say so in a comment on that issue, naming what it - waits on and who owns the next move. No new label: the comment is - activity, so it feeds the same reclaim clock the `needs-ruling` - ([#52](https://github.com/heavy-duty/ceremony/issues/52)) and `offsite` - ([#68](https://github.com/heavy-duty/ceremony/issues/68)) exemptions - already guard — a parked claim nobody can name is an abandoned one. - Shape 4 alone is exempt from the separate comment: the factual handoff - comment plus the `state:needs-human` write *is* its declaration — both - halves are already there, what the claim waits on (the merge) and who - owns the next move (the human), and both are visible to any scan as a - `labeled` event with the comment beside it. No second comment is owed on - the issue. Every other shape still declares as above. - Declared once, the declaration **stands** until the park's facts change: - a resumption that finds nothing changed posts nothing — the standing - declaration is the record, and silence while parked is compliant, not - abandonment-shaped. Re-declaring on every resume is the flood - [rig#145](https://github.com/heavy-duty/rig/pull/145) drowned in — 38 - near-identical audits in one night, each saying nothing changed - ([#177](https://github.com/heavy-duty/ceremony/discussions/177)). What - re-opens the duty to comment is the facts changing — the named wait - resolves or changes hands, the parked shape changes, or the claim - unparks — and each owes one new comment. The one place silence has a - cost: a parked claim with **no open PR** still feeds the 48-hour - reclaim clock, so there the builder refreshes the declaration before - the window closes. That refresh is the only repeat a park ever owes, - and its cadence is the reclaim window's, not any duty loop's. None of - this loosens the abandonment rule below: a claim that was never parked - and has simply stopped moving is abandoned, not silent. -- **Pick up `attention` before anything else.** On your claim, first post a - short pickup comment and remove `attention`; the removal is the ack. A - demand on a parked claim is usually its unpark, so take the slot back under - the existing rule below rather than leaving the demand parked. A demand - that *is* the park is different: the pickup comment is the declaration, - so one comment does both jobs, and the demand does not take the slot back. -- **A directed hold keeps its bookkeeping visible.** The PR carries `blocked` - with a comment naming what it waits on; the issue stays `claimed` and - carries `attention` until the builder acknowledges it. Nobody unassigns - the issue, and the 48-hour reclaim does not fire because the claim has an - open PR. Unparking follows the existing rule below. -- **Unparking is a claim like any other.** When the wait ends, the parked - issue is work again and takes the slot. If you are already active - elsewhere, finish or release that work first, and say which you did on - both issues — the slot is still one. Nothing counts claims per builder - and no reconciler path enforces any of this: `claim_decision()` sees one - issue at a time by construction, and no such machinery should be built - expecting it to have been specified here. The discipline is the - declaration, not a counter. -- **Abandoning is fine; ghosting is not.** If you stop, say where you got to, - push the branch if it holds anything useful, unassign, and restore - `ready`. + starting. The claim promises a draft PR soon: a claim with no PR and no + activity is what the staleness sweep reclaims, unless `offsite` records + that its PR lives in another repository. +- **A park is declared, never inferred.** Comment on the issue naming what + the claim waits on and who owns the next move; no new label, because the + comment is the activity that feeds the same reclaim clock the + `needs-ruling` (#52) and `offsite` (#68) exemptions already guard. Shape 4 + alone owes no separate comment — the factual handoff comment plus the + `state:needs-human` write already name the wait (the merge) and its owner + (the human), both visible to any scan. +- **A declaration stands until the park's facts change.** A resumption that + finds nothing changed posts nothing, because re-declaring on every resume + floods the record with audits each saying nothing changed (#177); silence + while parked is compliant, not abandonment-shaped. One new comment is owed + each time the facts change — the named wait resolves or changes hands, the + parked shape changes, or the claim unparks. The one place silence costs: a + parked claim with **no open PR** still feeds the 48-hour reclaim clock, so + refresh the declaration before that window closes; that refresh is the + only repeat a park ever owes, at the reclaim window's cadence and not any + duty loop's. +- **Pick up `attention` before anything else.** Post a short pickup comment + and remove `attention`; the removal is the ack. A demand on a parked claim + is usually its unpark, so take the slot back rather than leaving the + demand parked — unless the demand *is* the park, where the pickup comment + doubles as the declaration and the slot stays free. +- **A directed hold keeps its bookkeeping visible.** The PR carries + `blocked` with a comment naming what it waits on; the issue stays + `claimed` and carries `attention` until the builder acknowledges it. + Nobody unassigns the issue, and the 48-hour reclaim does not fire because + the claim has an open PR. +- **Unparking is a claim like any other.** The parked issue is work again + and takes the slot; if you are already active elsewhere, finish or release + that work first and say which you did on both issues. Nothing counts + claims per builder and no reconciler path enforces any of this — the + discipline is the declaration, not a counter, and no such machinery should + be built expecting it to have been specified here. +- **Abandoning is fine; ghosting is not.** Say where you got to, push the + branch if it holds anything useful, unassign, and restore `ready`. ## Building - Branch per issue; open the PR **as a draft early**, `Closes #N` in the - body. `Closes #N` does not cross repos: when the PR is in a different repo - from its authorizing issue, use `Part of /#N` instead, and - in the same step set `offsite` and comment on that issue with the draft PR - link as soon as the draft opens. - Triage closes the authorizing issue by hand when its acceptance criteria - are met; at that handoff the builder reports whether the cross-repo PR - merged or closed and clears `offsite` in the same comment. The cross-repo - merge never closes the authorizing issue. This codifies the linkage - builders already used on rig#112 and ceremony #13/#16 rather than adding a - new review obligation. - `Closes #N` also does not survive a post-merge criterion: when the issue's - body states that an acceptance criterion can only be checked after the - merge — a live proof of a workflow trigger, a released-artifact check, - anything whose subject does not exist until the change is on the base - branch — the same-repo PR uses `Refs #N` instead, and triage closes the - issue by hand on the evidence, exactly as it does for cross-repo work. The + body. Drafts are invisible to the reviewer panel on purpose: the draft + phase is yours. +- **`Closes #N` does not cross repos.** A PR in a different repo from its + authorizing issue says `Part of /#N`, sets `offsite`, and + comments the draft PR link on that issue in the same step; triage closes + that issue by hand once its acceptance criteria are met, and at that + handoff the builder reports whether the PR merged or closed and clears + `offsite` in the same comment. The cross-repo merge never closes the + authorizing issue (#13, #16). +- **`Closes #N` does not survive a post-merge criterion.** Where the issue's + body states that a criterion can only be checked after the merge — a live + proof of a workflow trigger, a released-artifact check, anything whose + subject does not exist until the change is on the base branch — the + same-repo PR says `Refs #N` and triage closes by hand on the evidence. The merge releases the claim: the issue moves to `post-merge`, the builder - walks away, and triage owns verification and closure. If evidence later - requires corrective build work, triage returns it to `ready` or mints a - fresh `ready` issue; any builder claims from current `main`, and the - original builder has no special standing. - The issue body is what says so; you never judge which issues qualify, and - absent that instruction `Closes #N` remains the default. The exception was - bought the hard way: #143 carried `Closes #137` as doctrine then required, - and the merge closed #137 with its post-merge criterion unmet (#151). - On a `Refs #N` PR, never put a closing keyword (`close`, `closes`, + walks away, and triage owns verification and closure; corrective work is a + fresh `ready` issue any builder claims from current `main`, the original + builder holding no special standing. The issue body is what says so — you + never judge which issues qualify, and absent that instruction `Closes #N` + remains the default (#151). +- On a `Refs #N` PR, never put a closing keyword (`close`, `closes`, `closed`, `fix`, `fixes`, `fixed`, `resolve`, `resolves`, `resolved`) - immediately before `#N` anywhere in the body — including the sentence - explaining why the PR does not close it. GitHub reads the whole body by - adjacency, not intent. Put the number first (`#N is closed by hand`) or - omit it (`triage closes the issue by hand`). A code span does not protect - the phrase: a backticked `Closes #199` still closed #199 (#200, #218). - Drafts are invisible to the reviewer panel on - purpose — the draft phase is yours. + immediately before `#N` anywhere in the body, including the sentence + explaining why the PR does not close it: GitHub reads the whole body by + adjacency, not intent, and a code span does not protect the phrase (#200, + #218). Put the number first (`#N is closed by hand`) or omit it. - **The issue's acceptance criteria are your definition of done.** Reproduce - them as a checklist in the PR body and check them honestly as you go. If - one turns out to be wrong or unreachable, say so on the issue and get it - amended by triage — do not silently ship less than the issue says. -- Every behavior change writes one fragment, `changelog.d/.md`, - named for the authorizing issue (`-.md` when the work is - cross-repo) — the exact prose that will be published, nothing else: `- ` - bullets, and in a grouped repo the `### Added` / `### Changed` / - `### Fixed` headings inside the fragment, creating a rarer kind only when - a change genuinely is one. An entry is at most 300 characters — the - fragment guard reds longer (#167) — so a genuinely long change ships - several short entries, never one long one; wrapping an entry over - continuation lines is fine and never counts against it. Every entry - **ends with its issue citation**, and the same guard reds an entry - without one: a single `(` group of `#N`, `repo#N` or `owner/repo#N` - references separated by `, `, then `)`, then the final `.` and nothing - after it — `(#262).` locally, `(#236, #250).` when one entry honestly - lands two. The citation need not name the fragment's own issue, because - the filename already carries the authorizing one (#262). Never edit - `CHANGELOG.md` for an entry — the - release PR assembles the section from the fragments (#112); the monotonic - guard still refuses anything that deletes a shipped heading. + them as a checklist in the PR body and check them honestly as you go; a + criterion that turns out wrong or unreachable goes back to triage to be + amended, never silently shipped short. +- **Every behavior change writes one fragment**, `changelog.d/.md`, + named for the authorizing issue (`-.md` cross-repo): the + exact prose that will be published and nothing else — `- ` bullets, and in + a grouped repo the `### Added` / `### Changed` / `### Fixed` headings + inside the fragment, a rarer kind only when a change genuinely is one. An + entry is at most 300 characters, so a genuinely long change ships several + short entries, never one long one; wrapping an entry over continuation + lines is fine and never counts against it (#167). Every entry **ends with + its issue citation**: a single `(` group of `#N`, `repo#N` or + `owner/repo#N` references separated by `, `, then `)`, then the final `.` + and nothing after it — `(#262).` locally, `(#236, #250).` when one entry + honestly lands two. The citation need not name the fragment's own issue, + which the filename already carries (#262). The fragment guard reds a + longer entry and an uncited one alike. Never edit `CHANGELOG.md` for an + entry: the release PR assembles the section from the fragments (#112), and + the monotonic guard refuses anything that deletes a shipped heading. - Follow the repo's conventions file and match the code you touch. Tests are not optional: the issue's test plan is the floor, not the ceiling. - **A write-capable job gets a repo-owned script, not a third-party action.** - If the job's token can write (`packages: write`, `contents: write`, + Where the job's token can write (`packages: write`, `contents: write`, `id-token: write`, deploy secrets), default to a script in the repo that a test can drive; a third-party action there needs an established publisher - and a full-commit-SHA pin. Read-only jobs still SHA-pin. The full rule and - the red-flag profile a reviewer will apply are in REVIEWER.md §What you - review against, item 2 (incubator#53/#54; #216). + and a full-commit-SHA pin, and read-only jobs still SHA-pin. The full rule + and the red-flag profile a reviewer will apply are in REVIEWER.md §What + you review against, item 2 (#216). - **Scope discipline: the PR does the issue — whole, and nothing else.** - Adjacent problems you discover go to a **discussion** (or a comment on the - relevant issue), where triage will do its job. You do not mint issues — + Adjacent problems you discover go to a **discussion**, or a comment on the + relevant issue, where triage will do its job. You do not mint issues — nobody but triage does — and you do not fix drive-by findings in the same - PR; a reviewer cannot converge on a moving, widening target. + PR, because a reviewer cannot converge on a widening target. ## The review round @@ -234,149 +177,105 @@ triage bug, and the move is to say so on the issue, not to guess. repo-specific facts such as the panel roster live in that repo's own CONTRIBUTING; the shared flow lives here and is not restated there.) -1. Mark ready-for-review; request **the whole panel**. The panel is the PR - repo's `panel[]=` line if it defines one, else its `panel=` - line; minus the author in either case (#224) — and never the roster of - the repo the issue is in. The PR repo's `.github/labels.conf` is the - machine's answer; its CONTRIBUTING roster is the human-readable answer, - and the conf governs if they disagree because that is what the state - machine reads. If the PR repo names no roster, ask triage on the - authorizing issue before marking ready-for-review; do not guess. You may - request an off-panel reviewer, but say that their verdict is advisory and - does not become required. On rig#112 this distinction mattered: requesting - codex and grok was correct for rig's panel even though ceremony's bench was - larger, and the doctrine had not said which roster governed. - **A review request requires a green check at the head.** A red check is - the author's own signal, not the panel's work: if the check is red, that - is your next task, not the panel's — fix it and push, then request. This - binds *you*, whether or not any engine enforces it. "My local suite - passed" is evidence about your machine; the check at the head is the - shared artifact the panel actually reads, and a reviewer's first act is - to read it. The one exception is a failure genuinely outside the PR — a - runner outage, a flaky dependency, a failure already present on the - default branch — and it is an exception only if the request says so - explicitly and names the evidence (e.g. "the same job fails identically - on `origin/main` at ``"). Silence about a red check is what is - prohibited; an argued exception shifts the burden to the author. - *Green* is a ruled term (operator, 2026-07-27), and it is read in two - steps, because a head carries more rollup entries than it has checks: - first pick the entry that is a check's word at this head, then - classify that entry. **A check's word at a head is its newest entry - by start time, and a `CANCELLED` entry is not that word while the - same check carries a non-cancelled entry at the same head.** The - survivor is the verdict about these bytes; the entry it displaced - reported nothing about them. Say **start** time and mean it: a - cancelled run does not stop the moment its replacement begins, so the - dead run's completion routinely postdates the live run's start, and a - reader who dates entries by completion picks the corpse. When *every* - entry a check has at the head is cancelled, nothing survives to be - its word: that check has not reported at all, and it stays not-green - by the classes below — the all-cancelled context is the case this - leaves exactly where it was. This states a collapse and not a new - class: `checks_state`'s carve-out drops a cancelled entry only where - its context keeps a non-cancelled survivor, and leaves an - all-cancelled context intact and still blocking, so doctrine and gate - partition alike on a mixed context (#139, #276). What the *machine* - drops from the rollup before it grades anything is a different - question, and crew's to describe rather than this file's. - Then classify that entry, and classify it from its **`conclusion`**, - never its `status`: a check carrying a terminal conclusion is green or - not-green by that conclusion whatever its `status` field still - reports — the two can disagree, and on #259 a finished job's `status` - lagged its own `conclusion: success` at the head. A check with no - conclusion at all is neither class: a configured run still in progress - is not green, and waiting for it is compliance, not a stall. Picking - the newest entry never settles a live one: where the survivor is the - run still going, the head is not green and you wait on it exactly as - you would have. A **cancelled or stale** check is not a green head — +1. Mark ready-for-review; request **the whole panel**: the PR repo's + `panel[]=` line if it defines one, else its `panel=` line, + minus the author in either case (#224) — never the roster of the repo the + issue is in. That repo's `.github/labels.conf` governs over its + CONTRIBUTING roster, being what the state machine reads; where the PR + repo names no roster, ask triage on the authorizing issue before marking + ready-for-review rather than guessing. You may request an off-panel + reviewer, saying that their verdict is advisory and does not become + required. + **A review request requires a green check at the head**, and this binds + you whether or not any engine enforces it: a red check is the author's + own signal, not the panel's work, so fix it and push, then request. The + one exception is a failure genuinely outside the PR — a runner outage, a + flaky dependency, a failure already present on the default branch — and + only if the request says so explicitly and names the evidence ("the same + job fails identically on `origin/main` at ``"); silence about a red + check is what is prohibited, while an argued exception shifts the burden + to the author. + *Green* is a ruled term (operator, 2026-07-27), read in two steps, + because a head carries more rollup entries than it has checks. **First + pick the entry that is a check's word at this head: its newest entry by + start time, a `CANCELLED` entry never being that word while the same + check carries a non-cancelled entry at the same head** — say *start* and + mean it, since a cancelled run does not stop when its replacement begins + and a reader who dates entries by completion picks the corpse. Where + *every* entry a check has at the head is cancelled, nothing survives to + be its word: that check has not reported, and it stays not-green by the + classes below. That is a collapse and not a new class — the gate's + carve-out likewise drops a cancelled entry only where its context keeps a + non-cancelled survivor, leaving an all-cancelled context blocking, so + doctrine and gate partition alike on a mixed context (#139, #276). + **Then classify that entry from its `conclusion`, never its `status`**, + which can still disagree with it (#259). An entry with no conclusion is + neither class: a configured run still in progress is not green, and + waiting for it is compliance, not a stall, so picking the newest entry + never settles a live one. **Cancelled or stale** is not a green head — *stale* means a check belonging to a superseded head, which the head-scoped rollup does not show anyway, so what survives there is - same-head cancellation, never a same-head node whose `status` lags its - conclusion — while a **skipped or neutral** one *is* green: those are - deliberate "passed / not applicable" conclusions, and reddening them - would red every conditional job the fleet skips on purpose. And a head - with **no checks configured** is the third ruled case, not an argued - exception: nothing is configured, so there is nothing to wait for — - the precondition is satisfied and the request goes out straight away, - no evidence or explanation owed, because the argued-exception path - above exists for a check that ran and came up red. This rules - nothing-configured, never nothing-answered-yet: a pending run has an - owner, CI, and is waited on as above. The machine partitions the same - way — `blocker:unrequested` admits the ask on `SUCCESS` and on `NONE` - alike (#236) — so doctrine and gate state one rule and each points at - the other. The costs behind the line are asymmetric: a false green - spends a three-reviewer round; a false red spends one author session. + same-head cancellation — while **skipped or neutral** *is* green, those + being deliberate "passed / not applicable" conclusions whose reddening + would red every conditional job the fleet skips on purpose. A head with + **no checks configured** is the third ruled case, not an argued + exception: nothing is configured, so there is nothing to wait for and the + request goes out straight away with no evidence owed, the + argued-exception path existing for a check that ran and came up red. That + rules nothing-configured, never nothing-answered-yet: a pending run has + an owner, CI, and is waited on as above, and the machine partitions the + same way, admitting the ask on `SUCCESS` and on `NONE` alike (#236). The + costs behind the line are asymmetric: a false green spends a + three-reviewer round; a false red spends one author session. What the + *machine* drops from the rollup before grading is crew's to describe. 2. **Wait for every verdict, then answer the round whole** — one reply - covering every point and stating what changed and what was verified. - That reply is the written round record: the engine mirrors it under the - PR body's **Round log**, newest last, so the builder owes the reply and - no separate body edit. At re-request time the engine takes the author's - comments posted after the newest verdict in the round and appends them - with ``; an existing marker makes a retry a - no-op. If the builder posted no reply, the engine records that the round - passed without one and never blocks handoff on the omission. Then push - the fixes, then re-request **by head, not by verdict**: if answering the - round pushed any commit, every - panelist's approval is now stale — an approval is of a specific tree, - and the handoff predicate counts only approvals at the current head — - so **every panelist is re-requested, the approvers included**; a - panelist left un-re-requested after a push can never approve the tree - you shipped, and the PR sits looking finished with a full set of - verdicts and nothing owed by anyone, the same silent-stall shape as - [#26](https://github.com/heavy-duty/ceremony/issues/26)/[#39](https://github.com/heavy-duty/ceremony/issues/39). - Only when the head did not move — the round was answered with argument - or evidence and nothing was pushed — do you re-request just the - non-approvers: a standing approval already covers this exact head, and - the engine absorbs a re-request at an unchanged head (the re-request - rule, [#94](https://github.com/heavy-duty/ceremony/issues/94); its + covering every point and stating what changed and what was verified. That + reply is the written round record: the engine mirrors it under the PR + body's **Round log**, newest last, appending the author's comments posted + after the round's newest verdict with `` (an + existing marker makes a retry a no-op), so the builder owes the reply and + no separate body edit; a round the builder left unanswered is recorded as + such and never blocks handoff. + Then push the fixes, and re-request **by head, not by verdict**. A push + makes every approval stale — an approval is of a specific tree, and the + handoff predicate counts only approvals at the current head — so **every + panelist is re-requested, the approvers included**; a panelist left + un-re-requested after a push can never approve the tree you shipped, and + the PR sits looking finished with a full set of verdicts and nothing owed + by anyone (#26, #39). Only where the head did not move — the round + answered with argument or evidence, nothing pushed — do you re-request + just the non-approvers, a standing approval already covering this exact + head and the engine absorbing a re-request at an unchanged one (#94; its mechanism is crew's to describe). **The re-request carries the same - green-check-at-head precondition as the first request**, argued - exception included. This is where the measured cost landed: crew#40 - burned two consecutive heads and four reviewer-rounds, every one - relaying a CI failure already visible in the job log (crew#45). A fix - push whose check comes up red is not ready to go back to the panel; it - is your next fix. Prefer verification over argument: when a - reviewer doubts behavior, add the test that settles it. + green-check-at-head precondition as the first request**, argued exception + included: a fix push whose check comes up red is your next fix, not the + panel's. Prefer verification over argument — when a reviewer doubts + behavior, add the test that settles it. 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 — silence and force-forward are not options. A panel deadlock - is one kind of human-owned decision; use the ruling ask below - ([#50 D11](https://github.com/heavy-duty/ceremony/issues/50)). + in the PR; silence and force-forward are not options, and a panel + deadlock is one kind of human-owned decision (#50 D11). -**A fix round may ride a draft.** An engine may convert a PR back to draft -when a round closes; crew#139 proposes exactly that, and is still an open -proposal. What it names is the status quo without it: where an engine's own -rules make a builder's mid-round pushes *saves* rather than proposals, every -one of those saves fires CI while the PR sits ready — 41 of 106 commits -across crew's last 25 PRs, by that issue's measurement — and converting back -to draft is what would stop them. Ceremony implements no such conversion and -this passage specifies none; it is written down because a builder or a -reviewer who meets a mid-round draft has to find a state the doctrine -describes. What it means is what a draft already meant while you were -building, extended and not changed: the draft phase is yours and the panel -cannot see it (Building, above). Whose ball it is does not change either — -the round outranks the draft, so you still owe it whole, the fixes and the -reply and the flip. The label axis says the same thing in the machine's -voice rather than in this one, and [LABELS.md](LABELS.md)'s `state:building` -row is where to read it (#205). - -**Ready-for-review is the act that ends the round, and it is the builder's -alone.** No engine marks a PR ready. The flip asserts that the round was -answered whole, and that assertion is the one judgement about a round its -author cannot delegate to a machine: an engine may draft a PR, which is what -crew#139 proposes engines do, but only the builder undrafts it. - -**Where a draft suppressed the checks, green is proven at the flip and the -request still follows it.** Step 1's precondition is the whole rule and this -adds no second one — it says only *when* the head answers: marking ready is -what runs the checks the draft held back, so the order is flip, let the head -answer, then request, and the argued exception stays the only way past a red -one. Waiting there is compliance, not a stall, and the machine reads it that -way too: `blocker:unrequested` does not fire while a head's checks are pending -or red, because the one blocker that demands an act has to know when the act -is permitted (#236 — crew#318 carried it at ~12:44Z on 2026-08-03 while its -head's run was still in progress, which is the label flagging a builder for -obeying this section). +**A fix round may ride a draft**, and the draft changes nothing about who +owes what. An engine may convert a PR back to draft when a round closes, so +that mid-round saves stop firing CI on a ready PR; ceremony implements no +such conversion and this passage specifies none, but whoever meets a +mid-round draft reads it as the draft always read — the draft phase is yours +and the panel cannot see it (Building, above) — while the round outranks the +draft, so you still owe it whole, the fixes and the reply and the flip +([LABELS.md](LABELS.md)'s `state:building` row says the same in the +machine's voice, #205). **Ready-for-review is the act that ends the round, +and it is the builder's alone**: the flip asserts that the round was +answered whole, which is the one judgement about a round its author cannot +delegate to a machine, so an engine may draft a PR but only the builder +undrafts it. **Where a draft suppressed the checks, green is proven at the +flip and the request still follows it**: marking ready is what runs the +checks the draft held back, so the order is flip, let the head answer, then +request — step 1's precondition and not a second one — and the argued +exception stays the only way past a red one. Waiting there is compliance, +not a stall, and the machine reads it the same way: `blocker:unrequested` +does not fire while a head's checks are pending or red, because the one +blocker that demands an act has to know when the act is permitted (#236). ## The ruling ask @@ -384,12 +283,11 @@ Set `needs-ruling` whenever a decision belongs to a human: org policy, published artifacts, secrets, prod, or any choice whose cost lands outside the PR. A panel deadlock is one instance, not the definition. The builder is the accountable flag-setter on a PR and consolidates the decision into one -comment rather than forwarding several reviewers' phrasings -([#50 D11](https://github.com/heavy-duty/ceremony/issues/50)). +comment rather than forwarding several reviewers' phrasings (#50 D11). Keep at most these five lines above the fold and put all other analysis inside the fold. The field labels are fixed because the ruling machinery -checks for them ([#50 D12](https://github.com/heavy-duty/ceremony/issues/50)): +checks for them (#50 D12): ```text 🧭 needs-ruling — @@ -406,11 +304,10 @@ whole problem to the human. `Blocked:` names both what stops and what continues. Write a timed `Default:` only when you are affirmatively confident the decision is reversible inside the PR before merge. Unsure is not a tie: it is a hard block. Published artifacts, secrets, prod, and org policy are -hard blocks by construction ([#50 D12–D13](https://github.com/heavy-duty/ceremony/issues/50)). +hard blocks by construction (#50 D12–D13). The ladder is anchored to the current episode's `needs-ruling` **`labeled` -event**, not its `Default:` deadline or the last activity -([#50 D13–D14](https://github.com/heavy-duty/ceremony/issues/50)): +event**, not its `Default:` deadline or the last activity (#50 D13–D14): - **0–12h:** proceed when a still-clear, reversible default expires, and say out loud that you did. A hard block waits. @@ -446,10 +343,9 @@ builder's behalf, in order: the head SHA, and a pointer to the PR body's **Round log**. The builder composes no new summary at handoff: the authored record already -lives in the Round log, mirrored mechanically from each whole-round reply as -specified above. The label write is optimistic — the reconciler validates -it, and takes it back if the PR is not actually mergeable-right-now. Then -stop: the PR is the human's. The claim is now parked as shape 4 (Picking, -above) — the handoff you just posted is its declaration, and your build slot -is free. Address what comes back (`state:addressing`) and re-hand-off the -same way. +lives in the Round log, mirrored mechanically from each whole-round reply. +The label write is optimistic — the reconciler validates it and takes it +back if the PR is not actually mergeable-right-now. Then stop: the PR is the +human's, and the claim is now parked as shape 4 (Picking, above), the +handoff comment being its declaration and your build slot free. Address what +comes back (`state:addressing`) and re-hand-off the same way. diff --git a/changelog.d/281.md b/changelog.d/281.md new file mode 100644 index 0000000..f53fca6 --- /dev/null +++ b/changelog.d/281.md @@ -0,0 +1,5 @@ +### Changed + +- `BUILDER.md` states its rules and cites their record bare: the incident + narratives, the links into issue comments and the cross-repo issue cites + leave the normative text, which no rule leaves with them (#281). From e3ec95bb37aff41759f4f9cc142b1db1ae2eaa27 Mon Sep 17 00:00:00 2001 From: cndgrr <59120057+cndgrr@users.noreply.github.com> Date: Tue, 4 Aug 2026 13:14:35 +0000 Subject: [PATCH 02/15] =?UTF-8?q?docs(builder):=20WIP=20=E2=80=94=20tighte?= =?UTF-8?q?r=20register=20across=20every=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BUILDER.md | 321 +++++++++++++++++++++++++---------------------------- 1 file changed, 150 insertions(+), 171 deletions(-) diff --git a/BUILDER.md b/BUILDER.md index a8f7620..5c95465 100644 --- a/BUILDER.md +++ b/BUILDER.md @@ -12,58 +12,53 @@ triage bug, and the move is to say so on the issue, not to guess. prefer the issue that unblocks the most other work. Where a repository adopts version epics, [RELEASES.md](RELEASES.md) governs the choice among release-window members. -- **Your own red head outranks a new claim.** A failing check at the head of - a PR you authored is picked up before claiming another issue; a red head - that owes no round and holds no conflict is otherwise nobody's next move, - and the PR strands mergeable (#163). The recovery path: record the failing - check and its failure class; rerun a clearly retryable infrastructure - failure without changing code; when the failure belongs to the branch, - return to the normal fix-round and worklog discipline; leave visible - evidence when a rerun cannot be started or the cause is uncertain; never - repeatedly rerun a deterministic branch failure without a corrective - commit; and hand off once the check is green and current-head approvals - stand. Such a PR is **not parked** — the next move is yours whatever the - round's verdict state says. Red and green are the review round's ruled - terms below; how the engine detects a red head is crew's to describe, not - this file's. -- **One build at a time.** You hold at most one issue on which you are - writing or revising a deliverable — finish or release that work before - starting new work. The rule counts build work in flight, not claims: a - **parked** claim, one whose next move belongs to someone else, does not - consume the slot. Exactly five shapes park: - 1. the issue carries `needs-ruling`, its escalation names a decider, and - its `Blocked:` line stops the remaining work; - 2. the deliverable is in a **live** review round, every outstanding +- **Your own red head outranks a new claim.** Pick up a failing check at the + head of a PR you authored before claiming another issue: a red head that + owes no round and holds no conflict is otherwise nobody's next move, and + the PR strands mergeable (#163). Record the failing check and its failure + class; rerun a clearly retryable infrastructure failure without changing + code; return to the normal fix-round and worklog discipline when the + failure belongs to the branch; leave visible evidence when a rerun cannot + be started or the cause is uncertain; never repeatedly rerun a + deterministic branch failure without a corrective commit; hand off once + the check is green and current-head approvals stand. Such a PR is **not + parked**, whatever the round's verdict state says. Red and green are the + review round's ruled terms below; how the engine detects a red head is + crew's to describe, not this file's. +- **One build at a time**: at most one issue on which you are writing or + revising a deliverable, finished or released before you start new work. + The rule counts build work in flight, not claims — a **parked** claim, + whose next move belongs to someone else, does not consume the slot. + Exactly five shapes park: + 1. `needs-ruling` is set, the escalation names a decider, and its + `Blocked:` line stops the remaining work; + 2. a **live** review round holds the deliverable, every outstanding verdict someone else's — awaiting its first verdicts, or answered whole with the owed re-requests posted, by head and not by verdict (steps 1–2 - below). A red check at the current head takes it **out of this shape**: - that state reads as waiting on the panel and is not, the next move is - yours, and reading it as parked strands the PR; + below). A red check at the current head takes it out of this shape: the + next move is yours, and reading that as parked strands the PR; 3. every remaining acceptance criterion is operator-owned, stated as such by triage on the issue; 4. the deliverable is **handed off** — the round passed, no `blocker:*` - stands, `state:needs-human` is set per Handoff (below), and the merge - is the human's. Shapes 2 and 4 are sequential and never overlap; + stands, `state:needs-human` is set per Handoff, and the merge is the + human's. Shapes 2 and 4 are sequential and never overlap; 5. the claim is **held by directive** — triage or the operator stopped the - work, the direction names what the hold waits on, and only they end it; - this is never "waiting for a good moment". A hold ends the way it - started, **on the labels**: where the queue labels and any prose - disagree about whether it stands, the most recent queue-label event by - the hold's owner governs and the prose is stale until corrected, an - operator being free to lift by label alone (#149, #151). So before - standing down *or* standing up, read the issue's **label events** + work, the direction names what the hold waits on, and only they end it. + A hold ends the way it started, **on the labels**: where labels and + prose disagree the most recent queue-label event by the hold's owner + governs, an operator being free to lift by label alone (#149, #151). So + read the issue's label events (`gh api /repos/{owner}/{repo}/issues/{n}/timeline`), not only its - comments; acting on the labels against stale prose, say so in the claim - — name the events, their timestamps and their actor, and invite the - correction. Refusing to claim through the contradiction is not a - resting place either: where the events genuinely do not resolve it, say - so on the issue and pick the next `ready` issue. - Not parked: waiting on yourself, waiting on CI (a red head is your own - work; a pending one resolves without you), or waiting for a good moment. - An issue you have simply stopped working on is abandoned, not parked — - unassign and restore `ready` (Claiming, below). The rule counts work and - not claims because parked claims are legitimately held beside the one - active build (#15, #16, #73). + comments, before standing down *or* standing up; where you act on the + labels against stale prose, say so in the claim — name the events, their + timestamps and their actor, and invite the correction. Refusing to claim + through the contradiction is no resting place either: where the events + do not resolve it, say so on the issue and pick the next `ready` issue. + Not parked: waiting on yourself, on CI (a red head is your own work; a + pending one resolves without you), or for a good moment. An issue you have + simply stopped working on is abandoned, not parked — unassign and restore + `ready`. The rule counts work and not claims because parked claims are + legitimately held beside the one active build (#15, #16, #73). ## Claiming @@ -72,38 +67,34 @@ triage bug, and the move is to say so on the issue, not to guess. activity is what the staleness sweep reclaims, unless `offsite` records that its PR lives in another repository. - **A park is declared, never inferred.** Comment on the issue naming what - the claim waits on and who owns the next move; no new label, because the - comment is the activity that feeds the same reclaim clock the - `needs-ruling` (#52) and `offsite` (#68) exemptions already guard. Shape 4 - alone owes no separate comment — the factual handoff comment plus the - `state:needs-human` write already name the wait (the merge) and its owner - (the human), both visible to any scan. + the claim waits on and who owns the next move; no new label, the comment + being the activity that feeds the same reclaim clock the `needs-ruling` + (#52) and `offsite` (#68) exemptions guard. Shape 4 owes no separate + comment: the handoff comment plus the `state:needs-human` write already + name the wait (the merge) and its owner (the human). - **A declaration stands until the park's facts change.** A resumption that finds nothing changed posts nothing, because re-declaring on every resume - floods the record with audits each saying nothing changed (#177); silence - while parked is compliant, not abandonment-shaped. One new comment is owed - each time the facts change — the named wait resolves or changes hands, the - parked shape changes, or the claim unparks. The one place silence costs: a - parked claim with **no open PR** still feeds the 48-hour reclaim clock, so + floods the record with audits each saying nothing changed (#177). One new + comment is owed each time the facts change — the named wait resolves or + changes hands, the parked shape changes, or the claim unparks. A parked + claim with **no open PR** still feeds the 48-hour reclaim clock, so refresh the declaration before that window closes; that refresh is the - only repeat a park ever owes, at the reclaim window's cadence and not any - duty loop's. + only repeat a park owes, at the reclaim window's cadence. - **Pick up `attention` before anything else.** Post a short pickup comment and remove `attention`; the removal is the ack. A demand on a parked claim - is usually its unpark, so take the slot back rather than leaving the - demand parked — unless the demand *is* the park, where the pickup comment - doubles as the declaration and the slot stays free. + is usually its unpark, so take the slot back — unless the demand *is* the + park, where the pickup comment doubles as the declaration and the slot + stays free. - **A directed hold keeps its bookkeeping visible.** The PR carries `blocked` with a comment naming what it waits on; the issue stays `claimed` and carries `attention` until the builder acknowledges it. Nobody unassigns the issue, and the 48-hour reclaim does not fire because the claim has an open PR. -- **Unparking is a claim like any other.** The parked issue is work again - and takes the slot; if you are already active elsewhere, finish or release - that work first and say which you did on both issues. Nothing counts - claims per builder and no reconciler path enforces any of this — the - discipline is the declaration, not a counter, and no such machinery should - be built expecting it to have been specified here. +- **Unparking is a claim like any other** and takes the slot: if you are + active elsewhere, finish or release that work first and say which you did + on both issues. Nothing counts claims per builder and no reconciler path + enforces this — the discipline is the declaration, not a counter, and no + such machinery should be built expecting it to have been specified here. - **Abandoning is fine; ghosting is not.** Say where you got to, push the branch if it holds anything useful, unassign, and restore `ready`. @@ -114,48 +105,47 @@ triage bug, and the move is to say so on the issue, not to guess. phase is yours. - **`Closes #N` does not cross repos.** A PR in a different repo from its authorizing issue says `Part of /#N`, sets `offsite`, and - comments the draft PR link on that issue in the same step; triage closes - that issue by hand once its acceptance criteria are met, and at that - handoff the builder reports whether the PR merged or closed and clears - `offsite` in the same comment. The cross-repo merge never closes the - authorizing issue (#13, #16). + comments the draft PR link on that issue in the same step. Triage closes + that issue by hand once its acceptance criteria are met; at that handoff + the builder reports whether the PR merged or closed and clears `offsite` + in the same comment. The cross-repo merge never closes the authorizing + issue (#13, #16). - **`Closes #N` does not survive a post-merge criterion.** Where the issue's body states that a criterion can only be checked after the merge — a live proof of a workflow trigger, a released-artifact check, anything whose subject does not exist until the change is on the base branch — the same-repo PR says `Refs #N` and triage closes by hand on the evidence. The merge releases the claim: the issue moves to `post-merge`, the builder - walks away, and triage owns verification and closure; corrective work is a - fresh `ready` issue any builder claims from current `main`, the original - builder holding no special standing. The issue body is what says so — you - never judge which issues qualify, and absent that instruction `Closes #N` - remains the default (#151). + walks away, and triage owns verification and closure, returning it to + `ready` or minting a fresh issue where corrective work is needed, which + any builder claims from current `main`. The issue body is what says so — + you never judge which issues qualify, and absent that instruction + `Closes #N` is the default (#151). - On a `Refs #N` PR, never put a closing keyword (`close`, `closes`, `closed`, `fix`, `fixes`, `fixed`, `resolve`, `resolves`, `resolved`) immediately before `#N` anywhere in the body, including the sentence - explaining why the PR does not close it: GitHub reads the whole body by + explaining why the PR does not close it: GitHub reads the body by adjacency, not intent, and a code span does not protect the phrase (#200, #218). Put the number first (`#N is closed by hand`) or omit it. - **The issue's acceptance criteria are your definition of done.** Reproduce them as a checklist in the PR body and check them honestly as you go; a criterion that turns out wrong or unreachable goes back to triage to be amended, never silently shipped short. -- **Every behavior change writes one fragment**, `changelog.d/.md`, +- **Every behavior change writes one fragment**, `changelog.d/.md` named for the authorizing issue (`-.md` cross-repo): the - exact prose that will be published and nothing else — `- ` bullets, and in - a grouped repo the `### Added` / `### Changed` / `### Fixed` headings - inside the fragment, a rarer kind only when a change genuinely is one. An - entry is at most 300 characters, so a genuinely long change ships several - short entries, never one long one; wrapping an entry over continuation - lines is fine and never counts against it (#167). Every entry **ends with - its issue citation**: a single `(` group of `#N`, `repo#N` or - `owner/repo#N` references separated by `, `, then `)`, then the final `.` - and nothing after it — `(#262).` locally, `(#236, #250).` when one entry - honestly lands two. The citation need not name the fragment's own issue, - which the filename already carries (#262). The fragment guard reds a - longer entry and an uncited one alike. Never edit `CHANGELOG.md` for an - entry: the release PR assembles the section from the fragments (#112), and - the monotonic guard refuses anything that deletes a shipped heading. + exact prose to be published and nothing else — `- ` bullets, plus in a + grouped repo the `### Added` / `### Changed` / `### Fixed` headings inside + the fragment, a rarer kind only where a change genuinely is one. An entry + is at most 300 characters, so a genuinely long change ships several short + entries; wrapping one over continuation lines never counts against it. It + **ends with its issue citation**: one `(` group of `#N`, `repo#N` or + `owner/repo#N` separated by `, `, then `)`, then the final `.` and nothing + after it — `(#262).`, or `(#236, #250).` where an entry honestly lands + two — and it need not name the fragment's own issue, which the filename + carries. The fragment guard reds a longer entry (#167) and an uncited one + (#262) alike. Never edit `CHANGELOG.md` for an entry: the release PR + assembles the section from the fragments (#112), and the monotonic guard + refuses anything that deletes a shipped heading. - Follow the repo's conventions file and match the code you touch. Tests are not optional: the issue's test plan is the floor, not the ceiling. - **A write-capable job gets a repo-owned script, not a third-party action.** @@ -163,19 +153,19 @@ triage bug, and the move is to say so on the issue, not to guess. `id-token: write`, deploy secrets), default to a script in the repo that a test can drive; a third-party action there needs an established publisher and a full-commit-SHA pin, and read-only jobs still SHA-pin. The full rule - and the red-flag profile a reviewer will apply are in REVIEWER.md §What - you review against, item 2 (#216). + and the red-flag profile a reviewer applies are in REVIEWER.md §What you + review against, item 2 (#216). - **Scope discipline: the PR does the issue — whole, and nothing else.** - Adjacent problems you discover go to a **discussion**, or a comment on the - relevant issue, where triage will do its job. You do not mint issues — - nobody but triage does — and you do not fix drive-by findings in the same - PR, because a reviewer cannot converge on a widening target. + Adjacent problems go to a **discussion**, or a comment on the relevant + issue, where triage does its job. You do not mint issues — nobody but + triage does — and you do not fix drive-by findings in the same PR, because + a reviewer cannot converge on a widening target. ## The review round -(If you are reading this as `.ceremony/BUILDER.md` in a governed repo: -repo-specific facts such as the panel roster live in that repo's own -CONTRIBUTING; the shared flow lives here and is not restated there.) +(Read as `.ceremony/BUILDER.md` in a governed repo: repo-specific facts such +as the panel roster live in that repo's own CONTRIBUTING, and the shared +flow lives here.) 1. Mark ready-for-review; request **the whole panel**: the PR repo's `panel[]=` line if it defines one, else its `panel=` line, @@ -186,70 +176,61 @@ CONTRIBUTING; the shared flow lives here and is not restated there.) ready-for-review rather than guessing. You may request an off-panel reviewer, saying that their verdict is advisory and does not become required. - **A review request requires a green check at the head**, and this binds + **A review request requires a green check at the head**, and that binds you whether or not any engine enforces it: a red check is the author's own signal, not the panel's work, so fix it and push, then request. The one exception is a failure genuinely outside the PR — a runner outage, a flaky dependency, a failure already present on the default branch — and - only if the request says so explicitly and names the evidence ("the same - job fails identically on `origin/main` at ``"); silence about a red - check is what is prohibited, while an argued exception shifts the burden - to the author. + only where the request says so explicitly and names the evidence ("the + same job fails identically on `origin/main` at ``"). Silence about a + red check is what is prohibited; an argued exception shifts the burden to + the author. *Green* is a ruled term (operator, 2026-07-27), read in two steps, because a head carries more rollup entries than it has checks. **First - pick the entry that is a check's word at this head: its newest entry by - start time, a `CANCELLED` entry never being that word while the same - check carries a non-cancelled entry at the same head** — say *start* and - mean it, since a cancelled run does not stop when its replacement begins - and a reader who dates entries by completion picks the corpse. Where - *every* entry a check has at the head is cancelled, nothing survives to - be its word: that check has not reported, and it stays not-green by the - classes below. That is a collapse and not a new class — the gate's - carve-out likewise drops a cancelled entry only where its context keeps a - non-cancelled survivor, leaving an all-cancelled context blocking, so - doctrine and gate partition alike on a mixed context (#139, #276). + find the check's word at this head**: its newest entry by start time, + except that a `CANCELLED` entry is never the word while the same check + has a non-cancelled entry at that head. Date entries by start and not by + completion — a cancelled run outlives its replacement's start, so the + other reading picks the corpse. A check whose every entry at the head is + cancelled has not reported at all and stays not-green by the classes + below; that is a collapse, not a new class, and the gate partitions the + same way, dropping a cancelled entry only where its context keeps a + non-cancelled survivor (#139, #276). **Then classify that entry from its `conclusion`, never its `status`**, - which can still disagree with it (#259). An entry with no conclusion is - neither class: a configured run still in progress is not green, and - waiting for it is compliance, not a stall, so picking the newest entry - never settles a live one. **Cancelled or stale** is not a green head — - *stale* means a check belonging to a superseded head, which the - head-scoped rollup does not show anyway, so what survives there is - same-head cancellation — while **skipped or neutral** *is* green, those - being deliberate "passed / not applicable" conclusions whose reddening - would red every conditional job the fleet skips on purpose. A head with - **no checks configured** is the third ruled case, not an argued - exception: nothing is configured, so there is nothing to wait for and the - request goes out straight away with no evidence owed, the - argued-exception path existing for a check that ran and came up red. That - rules nothing-configured, never nothing-answered-yet: a pending run has - an owner, CI, and is waited on as above, and the machine partitions the - same way, admitting the ask on `SUCCESS` and on `NONE` alike (#236). The - costs behind the line are asymmetric: a false green spends a - three-reviewer round; a false red spends one author session. What the - *machine* drops from the rollup before grading is crew's to describe. + which can still disagree with it (#259). No conclusion at all is neither + class: a configured run still in progress is not green, and waiting on it + is compliance, not a stall. **Cancelled or stale** is not green — *stale* + means a superseded head's check, which a head-scoped rollup never shows, + so what survives there is same-head cancellation. **Skipped or neutral** + *is* green: those are deliberate "passed / not applicable" conclusions, + and reddening them would red every conditional job the fleet skips on + purpose. **No checks configured** is the third ruled case, not an argued + exception: nothing is configured, so nothing is waited for and the + request goes out at once, no evidence owed. That rules + nothing-configured, never nothing-answered-yet — a pending run has an + owner, CI — and the machine partitions alike, admitting the ask on + `SUCCESS` and on `NONE` (#236). The costs behind the line are asymmetric: + a false green spends a three-reviewer round, a false red one author + session. What the *machine* drops from the rollup before grading is + crew's to describe. 2. **Wait for every verdict, then answer the round whole** — one reply covering every point and stating what changed and what was verified. That reply is the written round record: the engine mirrors it under the PR - body's **Round log**, newest last, appending the author's comments posted - after the round's newest verdict with `` (an - existing marker makes a retry a no-op), so the builder owes the reply and - no separate body edit; a round the builder left unanswered is recorded as - such and never blocks handoff. - Then push the fixes, and re-request **by head, not by verdict**. A push + body's **Round log**, newest last, so the builder owes the reply and no + separate body edit, and a round answered without one is recorded as such + and never blocks handoff. + Then push the fixes and re-request **by head, not by verdict**. A push makes every approval stale — an approval is of a specific tree, and the handoff predicate counts only approvals at the current head — so **every - panelist is re-requested, the approvers included**; a panelist left - un-re-requested after a push can never approve the tree you shipped, and - the PR sits looking finished with a full set of verdicts and nothing owed - by anyone (#26, #39). Only where the head did not move — the round - answered with argument or evidence, nothing pushed — do you re-request - just the non-approvers, a standing approval already covering this exact - head and the engine absorbing a re-request at an unchanged one (#94; its - mechanism is crew's to describe). **The re-request carries the same + panelist is re-requested, the approvers included**; one left + un-re-requested can never approve the tree you shipped, and the PR sits + looking finished with nothing owed by anyone (#26, #39). Only where the + head did not move — the round answered with argument or evidence, nothing + pushed — do you re-request just the non-approvers, a standing approval + already covering this exact head (#94). **The re-request carries the same green-check-at-head precondition as the first request**, argued exception included: a fix push whose check comes up red is your next fix, not the - panel's. Prefer verification over argument — when a reviewer doubts + panel's. Prefer verification over argument — where a reviewer doubts behavior, add the test that settles it. 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 @@ -260,22 +241,20 @@ CONTRIBUTING; the shared flow lives here and is not restated there.) owes what. An engine may convert a PR back to draft when a round closes, so that mid-round saves stop firing CI on a ready PR; ceremony implements no such conversion and this passage specifies none, but whoever meets a -mid-round draft reads it as the draft always read — the draft phase is yours -and the panel cannot see it (Building, above) — while the round outranks the -draft, so you still owe it whole, the fixes and the reply and the flip -([LABELS.md](LABELS.md)'s `state:building` row says the same in the -machine's voice, #205). **Ready-for-review is the act that ends the round, -and it is the builder's alone**: the flip asserts that the round was -answered whole, which is the one judgement about a round its author cannot -delegate to a machine, so an engine may draft a PR but only the builder +mid-round draft reads it as a draft always read — the draft phase is yours +and the panel cannot see it — while the round outranks the draft, so you +still owe it whole, the fixes and the reply and the flip +([LABELS.md](LABELS.md)'s `state:building` row, #205). **Ready-for-review is +the act that ends the round, and it is the builder's alone**: the flip +asserts that the round was answered whole, the one judgement about a round +its author cannot delegate, so an engine may draft a PR but only the builder undrafts it. **Where a draft suppressed the checks, green is proven at the flip and the request still follows it**: marking ready is what runs the checks the draft held back, so the order is flip, let the head answer, then -request — step 1's precondition and not a second one — and the argued -exception stays the only way past a red one. Waiting there is compliance, -not a stall, and the machine reads it the same way: `blocker:unrequested` -does not fire while a head's checks are pending or red, because the one -blocker that demands an act has to know when the act is permitted (#236). +request — step 1's precondition, not a second one. Waiting there is +compliance, not a stall, and `blocker:unrequested` does not fire while a +head's checks are pending or red, because the one blocker that demands an +act has to know when the act is permitted (#236). ## The ruling ask @@ -343,9 +322,9 @@ builder's behalf, in order: the head SHA, and a pointer to the PR body's **Round log**. The builder composes no new summary at handoff: the authored record already -lives in the Round log, mirrored mechanically from each whole-round reply. -The label write is optimistic — the reconciler validates it and takes it -back if the PR is not actually mergeable-right-now. Then stop: the PR is the -human's, and the claim is now parked as shape 4 (Picking, above), the -handoff comment being its declaration and your build slot free. Address what -comes back (`state:addressing`) and re-hand-off the same way. +lives in the Round log, mirrored from each whole-round reply. The label write +is optimistic — the reconciler validates it and takes it back if the PR is +not actually mergeable-right-now. Then stop: the PR is the human's, and the +claim is parked as shape 4 (Picking, above), that handoff comment being its +declaration and your build slot free. Address what comes back +(`state:addressing`) and re-hand-off the same way. From d25cbb04b76108961acb22ef6eb5470e83f1ec87 Mon Sep 17 00:00:00 2001 From: cndgrr <59120057+cndgrr@users.noreply.github.com> Date: Tue, 4 Aug 2026 13:16:37 +0000 Subject: [PATCH 03/15] =?UTF-8?q?docs(builder):=20WIP=20=E2=80=94=20furthe?= =?UTF-8?q?r=20compression,=20ruling=20ladder=20tightened?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BUILDER.md | 144 ++++++++++++++++++++++++----------------------------- 1 file changed, 66 insertions(+), 78 deletions(-) diff --git a/BUILDER.md b/BUILDER.md index 5c95465..d3e055e 100644 --- a/BUILDER.md +++ b/BUILDER.md @@ -13,18 +13,17 @@ triage bug, and the move is to say so on the issue, not to guess. adopts version epics, [RELEASES.md](RELEASES.md) governs the choice among release-window members. - **Your own red head outranks a new claim.** Pick up a failing check at the - head of a PR you authored before claiming another issue: a red head that - owes no round and holds no conflict is otherwise nobody's next move, and - the PR strands mergeable (#163). Record the failing check and its failure + head of a PR you authored before claiming another issue, or it strands + mergeable and unattended (#163). Record the failing check and its failure class; rerun a clearly retryable infrastructure failure without changing - code; return to the normal fix-round and worklog discipline when the - failure belongs to the branch; leave visible evidence when a rerun cannot + code; return to the normal fix-round and worklog discipline where the + failure belongs to the branch; leave visible evidence where a rerun cannot be started or the cause is uncertain; never repeatedly rerun a deterministic branch failure without a corrective commit; hand off once the check is green and current-head approvals stand. Such a PR is **not parked**, whatever the round's verdict state says. Red and green are the - review round's ruled terms below; how the engine detects a red head is - crew's to describe, not this file's. + ruled terms of the review round below; how the engine detects a red head + is crew's to describe, not this file's. - **One build at a time**: at most one issue on which you are writing or revising a deliverable, finished or released before you start new work. The rule counts build work in flight, not claims — a **parked** claim, @@ -45,20 +44,19 @@ triage bug, and the move is to say so on the issue, not to guess. 5. the claim is **held by directive** — triage or the operator stopped the work, the direction names what the hold waits on, and only they end it. A hold ends the way it started, **on the labels**: where labels and - prose disagree the most recent queue-label event by the hold's owner + prose disagree, the most recent queue-label event by the hold's owner governs, an operator being free to lift by label alone (#149, #151). So - read the issue's label events - (`gh api /repos/{owner}/{repo}/issues/{n}/timeline`), not only its - comments, before standing down *or* standing up; where you act on the - labels against stale prose, say so in the claim — name the events, their - timestamps and their actor, and invite the correction. Refusing to claim - through the contradiction is no resting place either: where the events - do not resolve it, say so on the issue and pick the next `ready` issue. + read the label events (`gh api /repos/{owner}/{repo}/issues/{n}/timeline`), + not only the comments, before standing down *or* standing up, and where + you act against stale prose say so in the claim, naming the events, + their timestamps and their actor. Refusing to claim through the + contradiction is no resting place: where the events do not resolve it, + say so on the issue and take the next `ready` issue. Not parked: waiting on yourself, on CI (a red head is your own work; a pending one resolves without you), or for a good moment. An issue you have simply stopped working on is abandoned, not parked — unassign and restore - `ready`. The rule counts work and not claims because parked claims are - legitimately held beside the one active build (#15, #16, #73). + `ready`. The rule counts work because parked claims are legitimately held + beside the one active build (#15, #16, #73). ## Claiming @@ -106,10 +104,9 @@ triage bug, and the move is to say so on the issue, not to guess. - **`Closes #N` does not cross repos.** A PR in a different repo from its authorizing issue says `Part of /#N`, sets `offsite`, and comments the draft PR link on that issue in the same step. Triage closes - that issue by hand once its acceptance criteria are met; at that handoff - the builder reports whether the PR merged or closed and clears `offsite` - in the same comment. The cross-repo merge never closes the authorizing - issue (#13, #16). + that issue by hand once its acceptance criteria are met, and at that + handoff the builder reports whether the PR merged or closed and clears + `offsite` in the same comment (#13, #16). - **`Closes #N` does not survive a post-merge criterion.** Where the issue's body states that a criterion can only be checked after the merge — a live proof of a workflow trigger, a released-artifact check, anything whose @@ -117,10 +114,10 @@ triage bug, and the move is to say so on the issue, not to guess. same-repo PR says `Refs #N` and triage closes by hand on the evidence. The merge releases the claim: the issue moves to `post-merge`, the builder walks away, and triage owns verification and closure, returning it to - `ready` or minting a fresh issue where corrective work is needed, which - any builder claims from current `main`. The issue body is what says so — - you never judge which issues qualify, and absent that instruction - `Closes #N` is the default (#151). + `ready` or minting a fresh issue for corrective work that any builder + claims from current `main`. The issue body is what says so — you never + judge which issues qualify, and absent that instruction `Closes #N` is the + default (#151). - On a `Refs #N` PR, never put a closing keyword (`close`, `closes`, `closed`, `fix`, `fixes`, `fixed`, `resolve`, `resolves`, `resolved`) immediately before `#N` anywhere in the body, including the sentence @@ -140,12 +137,12 @@ triage bug, and the move is to say so on the issue, not to guess. entries; wrapping one over continuation lines never counts against it. It **ends with its issue citation**: one `(` group of `#N`, `repo#N` or `owner/repo#N` separated by `, `, then `)`, then the final `.` and nothing - after it — `(#262).`, or `(#236, #250).` where an entry honestly lands - two — and it need not name the fragment's own issue, which the filename - carries. The fragment guard reds a longer entry (#167) and an uncited one - (#262) alike. Never edit `CHANGELOG.md` for an entry: the release PR - assembles the section from the fragments (#112), and the monotonic guard - refuses anything that deletes a shipped heading. + after it — `(#262).`, or `(#236, #250).` where an entry honestly lands two + — and it need not name the fragment's own issue, which the filename + carries. The guard reds a longer entry (#167) and an uncited one (#262) + alike. Never edit `CHANGELOG.md` for an entry: the release PR assembles + the section from the fragments (#112), and the monotonic guard refuses + anything that deletes a shipped heading. - Follow the repo's conventions file and match the code you touch. Tests are not optional: the issue's test plan is the floor, not the ceiling. - **A write-capable job gets a repo-owned script, not a third-party action.** @@ -163,39 +160,32 @@ triage bug, and the move is to say so on the issue, not to guess. ## The review round -(Read as `.ceremony/BUILDER.md` in a governed repo: repo-specific facts such -as the panel roster live in that repo's own CONTRIBUTING, and the shared -flow lives here.) - 1. Mark ready-for-review; request **the whole panel**: the PR repo's `panel[]=` line if it defines one, else its `panel=` line, minus the author in either case (#224) — never the roster of the repo the issue is in. That repo's `.github/labels.conf` governs over its CONTRIBUTING roster, being what the state machine reads; where the PR - repo names no roster, ask triage on the authorizing issue before marking - ready-for-review rather than guessing. You may request an off-panel - reviewer, saying that their verdict is advisory and does not become - required. + repo names no roster, ask triage on the authorizing issue rather than + guessing. An off-panel reviewer may be requested, saying that their + verdict is advisory and does not become required. **A review request requires a green check at the head**, and that binds you whether or not any engine enforces it: a red check is the author's own signal, not the panel's work, so fix it and push, then request. The one exception is a failure genuinely outside the PR — a runner outage, a flaky dependency, a failure already present on the default branch — and only where the request says so explicitly and names the evidence ("the - same job fails identically on `origin/main` at ``"). Silence about a - red check is what is prohibited; an argued exception shifts the burden to - the author. + same job fails identically on `origin/main` at ``"); silence about a + red check is what is prohibited. *Green* is a ruled term (operator, 2026-07-27), read in two steps, because a head carries more rollup entries than it has checks. **First find the check's word at this head**: its newest entry by start time, except that a `CANCELLED` entry is never the word while the same check - has a non-cancelled entry at that head. Date entries by start and not by + has a non-cancelled entry at that head. Date entries by start, not by completion — a cancelled run outlives its replacement's start, so the other reading picks the corpse. A check whose every entry at the head is - cancelled has not reported at all and stays not-green by the classes - below; that is a collapse, not a new class, and the gate partitions the - same way, dropping a cancelled entry only where its context keeps a - non-cancelled survivor (#139, #276). + cancelled has not reported and stays not-green by the classes below; that + is a collapse and not a new class, the gate likewise dropping a cancelled + entry only where its context keeps a non-cancelled survivor (#139, #276). **Then classify that entry from its `conclusion`, never its `status`**, which can still disagree with it (#259). No conclusion at all is neither class: a configured run still in progress is not green, and waiting on it @@ -228,10 +218,10 @@ flow lives here.) head did not move — the round answered with argument or evidence, nothing pushed — do you re-request just the non-approvers, a standing approval already covering this exact head (#94). **The re-request carries the same - green-check-at-head precondition as the first request**, argued exception - included: a fix push whose check comes up red is your next fix, not the - panel's. Prefer verification over argument — where a reviewer doubts - behavior, add the test that settles it. + green-check-at-head precondition**, argued exception included: a fix push + whose check comes up red is your next fix, not the panel's. Prefer + verification over argument — where a reviewer doubts behavior, add the + test that settles it. 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; silence and force-forward are not options, and a panel @@ -240,21 +230,20 @@ flow lives here.) **A fix round may ride a draft**, and the draft changes nothing about who owes what. An engine may convert a PR back to draft when a round closes, so that mid-round saves stop firing CI on a ready PR; ceremony implements no -such conversion and this passage specifies none, but whoever meets a -mid-round draft reads it as a draft always read — the draft phase is yours -and the panel cannot see it — while the round outranks the draft, so you -still owe it whole, the fixes and the reply and the flip -([LABELS.md](LABELS.md)'s `state:building` row, #205). **Ready-for-review is -the act that ends the round, and it is the builder's alone**: the flip -asserts that the round was answered whole, the one judgement about a round -its author cannot delegate, so an engine may draft a PR but only the builder -undrafts it. **Where a draft suppressed the checks, green is proven at the -flip and the request still follows it**: marking ready is what runs the -checks the draft held back, so the order is flip, let the head answer, then -request — step 1's precondition, not a second one. Waiting there is -compliance, not a stall, and `blocker:unrequested` does not fire while a -head's checks are pending or red, because the one blocker that demands an -act has to know when the act is permitted (#236). +such conversion, but whoever meets a mid-round draft reads it as a draft +always read — the draft phase is yours and the panel cannot see it — while +the round outranks the draft, so you still owe it whole, the fixes and the +reply and the flip ([LABELS.md](LABELS.md)'s `state:building` row, #205). +**Ready-for-review is the act that ends the round, and it is the builder's +alone**: the flip asserts that the round was answered whole, the one +judgement about a round its author cannot delegate, so an engine may draft a +PR but only the builder undrafts it. **Where a draft suppressed the checks, +green is proven at the flip and the request still follows it**: marking +ready is what runs the checks the draft held back, so the order is flip, let +the head answer, then request — step 1's precondition, not a second one — +and waiting there is compliance, not a stall, which is why +`blocker:unrequested` does not fire while a head's checks are pending or red +(#236). ## The ruling ask @@ -289,21 +278,20 @@ The ladder is anchored to the current episode's `needs-ruling` **`labeled` event**, not its `Default:` deadline or the last activity (#50 D13–D14): - **0–12h:** proceed when a still-clear, reversible default expires, and say - out loud that you did. A hard block waits. -- **at 12h:** do not fire a stale default. Re-read it against what has landed - and ask whether it still holds and whether reasonable doubt remains. If - doubt has appeared, make it a hard block. -- **at 24h:** proceed regardless, **as a PR**. Pick an option and state in the - PR body which way you went and what doubt remains. Nothing merges by this; - the human still gates the merge. -- **past 24h:** hand the choice to triage. Triage picks the option, records it - as a decision, and remains accountable; the operator can overturn it at + out loud that you did; a hard block waits. +- **at 12h:** do not fire a stale default — re-read it against what has + landed, and where doubt has appeared, make it a hard block. +- **at 24h:** proceed regardless, **as a PR**: pick an option and state in + the PR body which way you went and what doubt remains. Nothing merges by + this; the human still gates the merge. +- **past 24h:** hand the choice to triage, which picks the option, records + it as a decision, and remains accountable; the operator can overturn it at merge. A re-flag starts a fresh ladder. The ladder applies whatever `Default:` says, -including a hard block, and an active back-and-forth still climbs it. This is -different from the 7-day nudge, which resets on real activity. The machine -observes both clocks but never sets, clears, or decides `needs-ruling`. +including a hard block, and an active back-and-forth still climbs it — unlike +the 7-day nudge, which resets on real activity. The machine observes both +clocks but never sets, clears, or decides `needs-ruling`. The label stays until agreement is *reached*, not until the maintainer replies. The setter records the ruling, removes the label, and returns the From 92ff82c1c653bf3c151f8dc459b23948337a3272 Mon Sep 17 00:00:00 2001 From: cndgrr <59120057+cndgrr@users.noreply.github.com> Date: Tue, 4 Aug 2026 13:18:31 +0000 Subject: [PATCH 04/15] =?UTF-8?q?docs(builder):=20WIP=20=E2=80=94=20minima?= =?UTF-8?q?l-statement=20register?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BUILDER.md | 138 +++++++++++++++++++++++++---------------------------- 1 file changed, 65 insertions(+), 73 deletions(-) diff --git a/BUILDER.md b/BUILDER.md index d3e055e..74f9fbf 100644 --- a/BUILDER.md +++ b/BUILDER.md @@ -12,18 +12,17 @@ triage bug, and the move is to say so on the issue, not to guess. prefer the issue that unblocks the most other work. Where a repository adopts version epics, [RELEASES.md](RELEASES.md) governs the choice among release-window members. -- **Your own red head outranks a new claim.** Pick up a failing check at the +- **Your own red head outranks a new claim**: repair a failing check at the head of a PR you authored before claiming another issue, or it strands mergeable and unattended (#163). Record the failing check and its failure class; rerun a clearly retryable infrastructure failure without changing - code; return to the normal fix-round and worklog discipline where the - failure belongs to the branch; leave visible evidence where a rerun cannot - be started or the cause is uncertain; never repeatedly rerun a - deterministic branch failure without a corrective commit; hand off once - the check is green and current-head approvals stand. Such a PR is **not - parked**, whatever the round's verdict state says. Red and green are the - ruled terms of the review round below; how the engine detects a red head - is crew's to describe, not this file's. + code; treat a failure belonging to the branch as an ordinary fix round, + worklog and all; leave visible evidence where a rerun cannot be started or + the cause is uncertain; never rerun a deterministic branch failure without + a corrective commit; hand off once the check is green and current-head + approvals stand. Such a PR is **not parked**, whatever the round's verdict + state says. Red and green are the review round's ruled terms below; how + the engine detects a red head is crew's to describe, not this file's. - **One build at a time**: at most one issue on which you are writing or revising a deliverable, finished or released before you start new work. The rule counts build work in flight, not claims — a **parked** claim, @@ -35,7 +34,7 @@ triage bug, and the move is to say so on the issue, not to guess. verdict someone else's — awaiting its first verdicts, or answered whole with the owed re-requests posted, by head and not by verdict (steps 1–2 below). A red check at the current head takes it out of this shape: the - next move is yours, and reading that as parked strands the PR; + next move is yours; 3. every remaining acceptance criterion is operator-owned, stated as such by triage on the issue; 4. the deliverable is **handed off** — the round passed, no `blocker:*` @@ -54,9 +53,9 @@ triage bug, and the move is to say so on the issue, not to guess. say so on the issue and take the next `ready` issue. Not parked: waiting on yourself, on CI (a red head is your own work; a pending one resolves without you), or for a good moment. An issue you have - simply stopped working on is abandoned, not parked — unassign and restore - `ready`. The rule counts work because parked claims are legitimately held - beside the one active build (#15, #16, #73). + simply stopped working on is abandoned — unassign and restore `ready`. The + rule counts work because parked claims are legitimately held beside the + one active build (#15, #16, #73). ## Claiming @@ -70,14 +69,13 @@ triage bug, and the move is to say so on the issue, not to guess. (#52) and `offsite` (#68) exemptions guard. Shape 4 owes no separate comment: the handoff comment plus the `state:needs-human` write already name the wait (the merge) and its owner (the human). -- **A declaration stands until the park's facts change.** A resumption that - finds nothing changed posts nothing, because re-declaring on every resume - floods the record with audits each saying nothing changed (#177). One new - comment is owed each time the facts change — the named wait resolves or - changes hands, the parked shape changes, or the claim unparks. A parked - claim with **no open PR** still feeds the 48-hour reclaim clock, so - refresh the declaration before that window closes; that refresh is the - only repeat a park owes, at the reclaim window's cadence. +- **A declaration stands until the park's facts change**, so a resumption + that finds nothing changed posts nothing (#177). One new comment is owed + each time the facts change — the named wait resolves or changes hands, the + parked shape changes, or the claim unparks. A parked claim with **no open + PR** still feeds the 48-hour reclaim clock, so refresh the declaration + before that window closes; that refresh is the only repeat a park owes, at + the reclaim window's cadence. - **Pick up `attention` before anything else.** Post a short pickup comment and remove `attention`; the removal is the ack. A demand on a parked claim is usually its unpark, so take the slot back — unless the demand *is* the @@ -133,9 +131,9 @@ triage bug, and the move is to say so on the issue, not to guess. exact prose to be published and nothing else — `- ` bullets, plus in a grouped repo the `### Added` / `### Changed` / `### Fixed` headings inside the fragment, a rarer kind only where a change genuinely is one. An entry - is at most 300 characters, so a genuinely long change ships several short - entries; wrapping one over continuation lines never counts against it. It - **ends with its issue citation**: one `(` group of `#N`, `repo#N` or + is at most 300 characters, so a long change ships several short entries; + wrapping one over continuation lines never counts against it. It **ends + with its issue citation**: one `(` group of `#N`, `repo#N` or `owner/repo#N` separated by `, `, then `)`, then the final `.` and nothing after it — `(#262).`, or `(#236, #250).` where an entry honestly lands two — and it need not name the fragment's own issue, which the filename @@ -170,39 +168,35 @@ triage bug, and the move is to say so on the issue, not to guess. verdict is advisory and does not become required. **A review request requires a green check at the head**, and that binds you whether or not any engine enforces it: a red check is the author's - own signal, not the panel's work, so fix it and push, then request. The - one exception is a failure genuinely outside the PR — a runner outage, a - flaky dependency, a failure already present on the default branch — and - only where the request says so explicitly and names the evidence ("the - same job fails identically on `origin/main` at ``"); silence about a - red check is what is prohibited. + own signal, so fix it and push, then request. The one exception is a + failure genuinely outside the PR — a runner outage, a flaky dependency, a + failure already present on the default branch — and only where the + request says so explicitly and names the evidence ("the same job fails + identically on `origin/main` at ``"); silence about a red check is + what is prohibited. *Green* is a ruled term (operator, 2026-07-27), read in two steps, because a head carries more rollup entries than it has checks. **First - find the check's word at this head**: its newest entry by start time, - except that a `CANCELLED` entry is never the word while the same check - has a non-cancelled entry at that head. Date entries by start, not by - completion — a cancelled run outlives its replacement's start, so the - other reading picks the corpse. A check whose every entry at the head is - cancelled has not reported and stays not-green by the classes below; that - is a collapse and not a new class, the gate likewise dropping a cancelled - entry only where its context keeps a non-cancelled survivor (#139, #276). + take the check's word at this head**: its newest entry by start time, + never a `CANCELLED` entry while the same check has a non-cancelled one at + that head. Date entries by start, not completion — a cancelled run + outlives its replacement's start. A check whose every entry at the head + is cancelled has not reported at all and stays not-green by the classes + below, the gate collapsing the same way (#139, #276). **Then classify that entry from its `conclusion`, never its `status`**, which can still disagree with it (#259). No conclusion at all is neither class: a configured run still in progress is not green, and waiting on it is compliance, not a stall. **Cancelled or stale** is not green — *stale* means a superseded head's check, which a head-scoped rollup never shows, so what survives there is same-head cancellation. **Skipped or neutral** - *is* green: those are deliberate "passed / not applicable" conclusions, - and reddening them would red every conditional job the fleet skips on - purpose. **No checks configured** is the third ruled case, not an argued + *is* green, those being deliberate "passed / not applicable" conclusions. + **No checks configured** is the third ruled case, not an argued exception: nothing is configured, so nothing is waited for and the - request goes out at once, no evidence owed. That rules - nothing-configured, never nothing-answered-yet — a pending run has an - owner, CI — and the machine partitions alike, admitting the ask on - `SUCCESS` and on `NONE` (#236). The costs behind the line are asymmetric: - a false green spends a three-reviewer round, a false red one author - session. What the *machine* drops from the rollup before grading is - crew's to describe. + request goes out at once, no evidence owed — which rules + nothing-configured, never nothing-answered-yet, and the machine + partitions alike, admitting the ask on `SUCCESS` and on `NONE` (#236). + The costs behind the line are asymmetric: a false green spends a + three-reviewer round, a false red one author session. What the *machine* + drops from the rollup before grading is crew's to describe. 2. **Wait for every verdict, then answer the round whole** — one reply covering every point and stating what changed and what was verified. That reply is the written round record: the engine mirrors it under the PR @@ -213,35 +207,33 @@ triage bug, and the move is to say so on the issue, not to guess. makes every approval stale — an approval is of a specific tree, and the handoff predicate counts only approvals at the current head — so **every panelist is re-requested, the approvers included**; one left - un-re-requested can never approve the tree you shipped, and the PR sits - looking finished with nothing owed by anyone (#26, #39). Only where the - head did not move — the round answered with argument or evidence, nothing - pushed — do you re-request just the non-approvers, a standing approval - already covering this exact head (#94). **The re-request carries the same - green-check-at-head precondition**, argued exception included: a fix push - whose check comes up red is your next fix, not the panel's. Prefer - verification over argument — where a reviewer doubts behavior, add the - test that settles it. + un-re-requested can never approve the tree you shipped (#26, #39). Only + where the head did not move — the round answered with argument or + evidence, nothing pushed — do you re-request just the non-approvers, a + standing approval already covering this exact head (#94). **The + re-request carries the same green-check-at-head precondition**, argued + exception included: a fix push whose check comes up red is your next fix, + not the panel's. Prefer verification over argument — where a reviewer + doubts behavior, add the test that settles it. 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; silence and force-forward are not options, and a panel deadlock is one kind of human-owned decision (#50 D11). **A fix round may ride a draft**, and the draft changes nothing about who -owes what. An engine may convert a PR back to draft when a round closes, so -that mid-round saves stop firing CI on a ready PR; ceremony implements no -such conversion, but whoever meets a mid-round draft reads it as a draft -always read — the draft phase is yours and the panel cannot see it — while -the round outranks the draft, so you still owe it whole, the fixes and the -reply and the flip ([LABELS.md](LABELS.md)'s `state:building` row, #205). -**Ready-for-review is the act that ends the round, and it is the builder's -alone**: the flip asserts that the round was answered whole, the one -judgement about a round its author cannot delegate, so an engine may draft a -PR but only the builder undrafts it. **Where a draft suppressed the checks, -green is proven at the flip and the request still follows it**: marking -ready is what runs the checks the draft held back, so the order is flip, let -the head answer, then request — step 1's precondition, not a second one — -and waiting there is compliance, not a stall, which is why +owes what: an engine may convert a PR back to draft when a round closes, and +ceremony implements no such conversion, but whoever meets a mid-round draft +reads it as a draft always read — the draft phase is yours and the panel +cannot see it — while the round outranks the draft, so you still owe it +whole, the fixes and the reply and the flip ([LABELS.md](LABELS.md)'s +`state:building` row, #205). **Ready-for-review is the act that ends the +round, and it is the builder's alone**: the flip asserts that the round was +answered whole, the one judgement its author cannot delegate, so an engine +may draft a PR but only the builder undrafts it. **Where a draft suppressed +the checks, green is proven at the flip and the request still follows it** — +marking ready is what runs the checks the draft held back, so the order is +flip, let the head answer, then request, which is step 1's precondition and +not a second one. Waiting there is compliance, not a stall, and `blocker:unrequested` does not fire while a head's checks are pending or red (#236). @@ -271,8 +263,8 @@ the question is not ready. `Recommend:` is mandatory — omitting it hands the whole problem to the human. `Blocked:` names both what stops and what continues. Write a timed `Default:` only when you are affirmatively confident the decision is reversible inside the PR before merge. Unsure is not a tie: -it is a hard block. Published artifacts, secrets, prod, and org policy are -hard blocks by construction (#50 D12–D13). +it is a hard block, as published artifacts, secrets, prod and org policy are +by construction (#50 D12–D13). The ladder is anchored to the current episode's `needs-ruling` **`labeled` event**, not its `Default:` deadline or the last activity (#50 D13–D14): From 1b6143f4aab7c0f6d5737f01c97cd098aedbb715 Mon Sep 17 00:00:00 2001 From: cndgrr <59120057+cndgrr@users.noreply.github.com> Date: Tue, 4 Aug 2026 13:21:26 +0000 Subject: [PATCH 05/15] =?UTF-8?q?docs(builder):=20WIP=20=E2=80=94=20drop?= =?UTF-8?q?=20optional=20whys,=20classes=20as=20a=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BUILDER.md | 118 ++++++++++++++++++++++++++--------------------------- 1 file changed, 57 insertions(+), 61 deletions(-) diff --git a/BUILDER.md b/BUILDER.md index 74f9fbf..bb22122 100644 --- a/BUILDER.md +++ b/BUILDER.md @@ -14,15 +14,14 @@ triage bug, and the move is to say so on the issue, not to guess. release-window members. - **Your own red head outranks a new claim**: repair a failing check at the head of a PR you authored before claiming another issue, or it strands - mergeable and unattended (#163). Record the failing check and its failure - class; rerun a clearly retryable infrastructure failure without changing - code; treat a failure belonging to the branch as an ordinary fix round, - worklog and all; leave visible evidence where a rerun cannot be started or - the cause is uncertain; never rerun a deterministic branch failure without - a corrective commit; hand off once the check is green and current-head - approvals stand. Such a PR is **not parked**, whatever the round's verdict - state says. Red and green are the review round's ruled terms below; how - the engine detects a red head is crew's to describe, not this file's. + mergeable and unattended (#163). Record the check and its failure class; + rerun a clearly retryable infrastructure failure unchanged; treat a branch + failure as an ordinary fix round, worklog and all; leave visible evidence + where a rerun cannot be started or the cause is uncertain; never rerun a + deterministic branch failure without a corrective commit; hand off once + the check is green and current-head approvals stand. Such a PR is **not + parked**, whatever the round's verdict state says. How the engine detects + a red head is crew's to describe, not this file's. - **One build at a time**: at most one issue on which you are writing or revising a deliverable, finished or released before you start new work. The rule counts build work in flight, not claims — a **parked** claim, @@ -44,18 +43,19 @@ triage bug, and the move is to say so on the issue, not to guess. work, the direction names what the hold waits on, and only they end it. A hold ends the way it started, **on the labels**: where labels and prose disagree, the most recent queue-label event by the hold's owner - governs, an operator being free to lift by label alone (#149, #151). So - read the label events (`gh api /repos/{owner}/{repo}/issues/{n}/timeline`), - not only the comments, before standing down *or* standing up, and where - you act against stale prose say so in the claim, naming the events, - their timestamps and their actor. Refusing to claim through the - contradiction is no resting place: where the events do not resolve it, - say so on the issue and take the next `ready` issue. + governs, an operator being free to lift by label alone (#149, #151). + Read the label events (`gh api + /repos/{owner}/{repo}/issues/{n}/timeline`), not only the comments, + before standing down *or* standing up, and where you act against stale + prose say so in the claim, naming the events, their timestamps and + their actor. Refusing to claim through the contradiction is no resting + place: where the events do not resolve it, say so on the issue and take + the next `ready` issue. Not parked: waiting on yourself, on CI (a red head is your own work; a pending one resolves without you), or for a good moment. An issue you have - simply stopped working on is abandoned — unassign and restore `ready`. The - rule counts work because parked claims are legitimately held beside the - one active build (#15, #16, #73). + simply stopped working on is abandoned — unassign and restore `ready`. + Parked claims are legitimately held beside the one active build (#15, #16, + #73). ## Claiming @@ -71,11 +71,10 @@ triage bug, and the move is to say so on the issue, not to guess. name the wait (the merge) and its owner (the human). - **A declaration stands until the park's facts change**, so a resumption that finds nothing changed posts nothing (#177). One new comment is owed - each time the facts change — the named wait resolves or changes hands, the + each time they do change — the named wait resolves or changes hands, the parked shape changes, or the claim unparks. A parked claim with **no open PR** still feeds the 48-hour reclaim clock, so refresh the declaration - before that window closes; that refresh is the only repeat a park owes, at - the reclaim window's cadence. + before that window closes; that refresh is the only repeat a park owes. - **Pick up `attention` before anything else.** Post a short pickup comment and remove `attention`; the removal is the ack. A demand on a parked claim is usually its unpark, so take the slot back — unless the demand *is* the @@ -84,13 +83,13 @@ triage bug, and the move is to say so on the issue, not to guess. - **A directed hold keeps its bookkeeping visible.** The PR carries `blocked` with a comment naming what it waits on; the issue stays `claimed` and carries `attention` until the builder acknowledges it. - Nobody unassigns the issue, and the 48-hour reclaim does not fire because + Nobody unassigns the issue, and the 48-hour reclaim does not fire while the claim has an open PR. - **Unparking is a claim like any other** and takes the slot: if you are active elsewhere, finish or release that work first and say which you did - on both issues. Nothing counts claims per builder and no reconciler path - enforces this — the discipline is the declaration, not a counter, and no - such machinery should be built expecting it to have been specified here. + on both issues. No machinery counts claims per builder, and none should be + built expecting this section to have specified one — the discipline is the + declaration, not a counter. - **Abandoning is fine; ghosting is not.** Say where you got to, push the branch if it holds anything useful, unassign, and restore `ready`. @@ -133,10 +132,10 @@ triage bug, and the move is to say so on the issue, not to guess. the fragment, a rarer kind only where a change genuinely is one. An entry is at most 300 characters, so a long change ships several short entries; wrapping one over continuation lines never counts against it. It **ends - with its issue citation**: one `(` group of `#N`, `repo#N` or - `owner/repo#N` separated by `, `, then `)`, then the final `.` and nothing - after it — `(#262).`, or `(#236, #250).` where an entry honestly lands two - — and it need not name the fragment's own issue, which the filename + with its issue citation** — one parenthesised group of `#N`, `repo#N` or + `owner/repo#N` references separated by `, `, then the final `.` and + nothing after it: `(#262).`, or `(#236, #250).` where an entry honestly + lands two — and need not name the fragment's own issue, which the filename carries. The guard reds a longer entry (#167) and an uncited one (#262) alike. Never edit `CHANGELOG.md` for an entry: the release PR assembles the section from the fragments (#112), and the monotonic guard refuses @@ -153,8 +152,7 @@ triage bug, and the move is to say so on the issue, not to guess. - **Scope discipline: the PR does the issue — whole, and nothing else.** Adjacent problems go to a **discussion**, or a comment on the relevant issue, where triage does its job. You do not mint issues — nobody but - triage does — and you do not fix drive-by findings in the same PR, because - a reviewer cannot converge on a widening target. + triage does — and you do not fix drive-by findings in the same PR. ## The review round @@ -174,26 +172,26 @@ triage bug, and the move is to say so on the issue, not to guess. request says so explicitly and names the evidence ("the same job fails identically on `origin/main` at ``"); silence about a red check is what is prohibited. - *Green* is a ruled term (operator, 2026-07-27), read in two steps, - because a head carries more rollup entries than it has checks. **First - take the check's word at this head**: its newest entry by start time, - never a `CANCELLED` entry while the same check has a non-cancelled one at - that head. Date entries by start, not completion — a cancelled run - outlives its replacement's start. A check whose every entry at the head - is cancelled has not reported at all and stays not-green by the classes - below, the gate collapsing the same way (#139, #276). - **Then classify that entry from its `conclusion`, never its `status`**, - which can still disagree with it (#259). No conclusion at all is neither - class: a configured run still in progress is not green, and waiting on it - is compliance, not a stall. **Cancelled or stale** is not green — *stale* - means a superseded head's check, which a head-scoped rollup never shows, - so what survives there is same-head cancellation. **Skipped or neutral** - *is* green, those being deliberate "passed / not applicable" conclusions. - **No checks configured** is the third ruled case, not an argued - exception: nothing is configured, so nothing is waited for and the - request goes out at once, no evidence owed — which rules - nothing-configured, never nothing-answered-yet, and the machine - partitions alike, admitting the ask on `SUCCESS` and on `NONE` (#236). + *Green* is a ruled term (operator, 2026-07-27), read in two steps. + **First take the check's word at this head**: its newest entry by start + time — not by completion, a cancelled run outliving its replacement's + start — and never a `CANCELLED` entry while the same check has a + non-cancelled one at that head. A check whose every entry at the head is + cancelled has not reported at all and is not green, the gate collapsing + the same way (#139, #276). **Then classify that entry from its + `conclusion`, never its `status`**, which can still disagree with it + (#259): + - no conclusion at all — not green: a configured run still in progress is + waited on, and waiting is compliance, not a stall; + - cancelled or stale — not green, *stale* meaning a superseded head's + check, which a head-scoped rollup never shows; + - skipped or neutral — green, those being deliberate "passed / not + applicable" conclusions; + - no checks configured at the head — green, the third ruled case and not + an argued exception: the request goes out at once, no evidence owed. + That never covers nothing-answered-yet, and the machine partitions + alike, admitting the ask on `SUCCESS` and on `NONE` (#236). + The costs behind the line are asymmetric: a false green spends a three-reviewer round, a false red one author session. What the *machine* drops from the rollup before grading is crew's to describe. @@ -209,12 +207,11 @@ triage bug, and the move is to say so on the issue, not to guess. panelist is re-requested, the approvers included**; one left un-re-requested can never approve the tree you shipped (#26, #39). Only where the head did not move — the round answered with argument or - evidence, nothing pushed — do you re-request just the non-approvers, a - standing approval already covering this exact head (#94). **The - re-request carries the same green-check-at-head precondition**, argued - exception included: a fix push whose check comes up red is your next fix, - not the panel's. Prefer verification over argument — where a reviewer - doubts behavior, add the test that settles it. + evidence, nothing pushed — do you re-request just the non-approvers (#94). + **The re-request carries the same green-check-at-head precondition**, + argued exception included: a fix push whose check comes up red is your + next fix, not the panel's. Prefer verification over argument — where a + reviewer doubts behavior, add the test that settles it. 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; silence and force-forward are not options, and a panel @@ -233,9 +230,8 @@ may draft a PR but only the builder undrafts it. **Where a draft suppressed the checks, green is proven at the flip and the request still follows it** — marking ready is what runs the checks the draft held back, so the order is flip, let the head answer, then request, which is step 1's precondition and -not a second one. Waiting there is compliance, not a stall, and -`blocker:unrequested` does not fire while a head's checks are pending or red -(#236). +not a second one. Waiting there is compliance, and `blocker:unrequested` +does not fire while a head's checks are pending or red (#236). ## The ruling ask From e1c3e3e9af71aa49c43c23610ba47c1ee3fa3958 Mon Sep 17 00:00:00 2001 From: cndgrr <59120057+cndgrr@users.noreply.github.com> Date: Tue, 4 Aug 2026 13:23:56 +0000 Subject: [PATCH 06/15] =?UTF-8?q?docs(builder):=20WIP=20=E2=80=94=20terse?= =?UTF-8?q?=20register=20throughout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BUILDER.md | 382 +++++++++++++++++++++++++---------------------------- 1 file changed, 181 insertions(+), 201 deletions(-) diff --git a/BUILDER.md b/BUILDER.md index bb22122..789835d 100644 --- a/BUILDER.md +++ b/BUILDER.md @@ -6,244 +6,225 @@ triage bug, and the move is to say so on the issue, not to guess. ## Picking -- Pick from issues labeled **`ready`** — never `blocked`, never `claimed`, - never an `epic` (epics organize; their children are the work). Inside an - epic take the earliest unblocked unclaimed child; between epics and strays - prefer the issue that unblocks the most other work. Where a repository - adopts version epics, [RELEASES.md](RELEASES.md) governs the choice among - release-window members. -- **Your own red head outranks a new claim**: repair a failing check at the - head of a PR you authored before claiming another issue, or it strands - mergeable and unattended (#163). Record the check and its failure class; - rerun a clearly retryable infrastructure failure unchanged; treat a branch - failure as an ordinary fix round, worklog and all; leave visible evidence - where a rerun cannot be started or the cause is uncertain; never rerun a - deterministic branch failure without a corrective commit; hand off once - the check is green and current-head approvals stand. Such a PR is **not - parked**, whatever the round's verdict state says. How the engine detects - a red head is crew's to describe, not this file's. -- **One build at a time**: at most one issue on which you are writing or - revising a deliverable, finished or released before you start new work. - The rule counts build work in flight, not claims — a **parked** claim, - whose next move belongs to someone else, does not consume the slot. - Exactly five shapes park: +- Pick from issues labeled **`ready`** — never `blocked`, `claimed`, or an + `epic` (epics organize; their children are the work). Inside an epic take + the earliest unblocked unclaimed child; otherwise prefer the issue that + unblocks the most work. Where a repo adopts version epics, + [RELEASES.md](RELEASES.md) governs the choice among window members. +- **Your own red head outranks a new claim**: repair a failing check at your + PR's head before claiming another issue, or it strands mergeable and + unattended (#163). Record the check and its failure class; rerun a clearly + retryable infrastructure failure unchanged; treat a branch failure as an + ordinary fix round, worklog and all; leave evidence where a rerun cannot + start or the cause is unclear; never rerun a deterministic failure without + a corrective commit; hand off once green with current-head approvals. Such + a PR is **never parked**, whatever the round's verdict state says. How the + engine detects a red head is crew's to describe. +- **One build at a time**: one issue on which you are writing or revising a + deliverable, finished or released before you start more. The rule counts + work in flight, not claims — a **parked** claim, whose next move is + someone else's, does not hold the slot. Five shapes park: 1. `needs-ruling` is set, the escalation names a decider, and its - `Blocked:` line stops the remaining work; - 2. a **live** review round holds the deliverable, every outstanding - verdict someone else's — awaiting its first verdicts, or answered whole - with the owed re-requests posted, by head and not by verdict (steps 1–2 - below). A red check at the current head takes it out of this shape: the - next move is yours; - 3. every remaining acceptance criterion is operator-owned, stated as such - by triage on the issue; - 4. the deliverable is **handed off** — the round passed, no `blocker:*` - stands, `state:needs-human` is set per Handoff, and the merge is the - human's. Shapes 2 and 4 are sequential and never overlap; + `Blocked:` line stops the rest; + 2. a **live** review round holds it, every outstanding verdict someone + else's — awaiting first verdicts, or answered whole with the owed + re-requests posted, by head and not by verdict (steps 1–2). A red check + at the head takes it out of this shape: the next move is yours; + 3. every remaining acceptance criterion is operator-owned, stated so by + triage on the issue; + 4. it is **handed off** — round passed, no `blocker:*` standing, + `state:needs-human` set per Handoff, the merge the human's. Shapes 2 + and 4 are sequential and never overlap; 5. the claim is **held by directive** — triage or the operator stopped the - work, the direction names what the hold waits on, and only they end it. - A hold ends the way it started, **on the labels**: where labels and - prose disagree, the most recent queue-label event by the hold's owner - governs, an operator being free to lift by label alone (#149, #151). - Read the label events (`gh api - /repos/{owner}/{repo}/issues/{n}/timeline`), not only the comments, - before standing down *or* standing up, and where you act against stale - prose say so in the claim, naming the events, their timestamps and - their actor. Refusing to claim through the contradiction is no resting - place: where the events do not resolve it, say so on the issue and take - the next `ready` issue. - Not parked: waiting on yourself, on CI (a red head is your own work; a - pending one resolves without you), or for a good moment. An issue you have - simply stopped working on is abandoned — unassign and restore `ready`. - Parked claims are legitimately held beside the one active build (#15, #16, - #73). + work, named what the hold waits on, and only they end it. A hold ends + as it started, **on the labels**: where labels and prose disagree, the + most recent queue-label event by the hold's owner governs, and an + operator may lift by label alone (#149, #151). So read the label events + (`gh api /repos/{owner}/{repo}/issues/{n}/timeline`), not just the + comments, before standing down *or* up; acting against stale prose, say + so in the claim, naming the events, their timestamps and their actor. + Refusing to claim through the contradiction is no resting place: where + the events do not resolve it, say so and take the next `ready` issue. + Not parked: waiting on yourself, on CI (a red head is yours; a pending one + resolves without you), or for a good moment. An issue you simply stopped + working on is abandoned — unassign and restore `ready`. Parked claims are + legitimately held beside the one active build (#15, #16, #73). ## Claiming - Assign yourself, swap `ready` → `claimed`, and comment that you are starting. The claim promises a draft PR soon: a claim with no PR and no activity is what the staleness sweep reclaims, unless `offsite` records - that its PR lives in another repository. -- **A park is declared, never inferred.** Comment on the issue naming what - the claim waits on and who owns the next move; no new label, the comment - being the activity that feeds the same reclaim clock the `needs-ruling` - (#52) and `offsite` (#68) exemptions guard. Shape 4 owes no separate - comment: the handoff comment plus the `state:needs-human` write already - name the wait (the merge) and its owner (the human). + that its PR lives in another repo. +- **A park is declared, never inferred.** Comment naming what the claim + waits on and who owns the next move — no new label; the comment is the + activity the reclaim clock reads, as for `needs-ruling` (#52) and + `offsite` (#68). Shape 4 is exempt: the handoff comment and + `state:needs-human` already say both. - **A declaration stands until the park's facts change**, so a resumption - that finds nothing changed posts nothing (#177). One new comment is owed - each time they do change — the named wait resolves or changes hands, the - parked shape changes, or the claim unparks. A parked claim with **no open - PR** still feeds the 48-hour reclaim clock, so refresh the declaration - before that window closes; that refresh is the only repeat a park owes. -- **Pick up `attention` before anything else.** Post a short pickup comment - and remove `attention`; the removal is the ack. A demand on a parked claim - is usually its unpark, so take the slot back — unless the demand *is* the - park, where the pickup comment doubles as the declaration and the slot - stays free. + finding nothing changed posts nothing (#177). Each change owes one comment + — the wait resolves or changes hands, the shape changes, the claim + unparks. A parked claim with **no open PR** still feeds the 48-hour + reclaim clock, so refresh the declaration before that window closes; that + is the only repeat a park owes. +- **Pick up `attention` before anything else**: post a short pickup comment + and remove the label, which is the ack. A demand on a parked claim is + usually its unpark, so take the slot back — unless the demand *is* the + park, where the pickup comment doubles as the declaration. - **A directed hold keeps its bookkeeping visible.** The PR carries `blocked` with a comment naming what it waits on; the issue stays - `claimed` and carries `attention` until the builder acknowledges it. - Nobody unassigns the issue, and the 48-hour reclaim does not fire while - the claim has an open PR. + `claimed` and carries `attention` until the builder acks. Nobody unassigns + it, and the 48-hour reclaim does not fire while the claim has an open PR. - **Unparking is a claim like any other** and takes the slot: if you are - active elsewhere, finish or release that work first and say which you did - on both issues. No machinery counts claims per builder, and none should be - built expecting this section to have specified one — the discipline is the - declaration, not a counter. + active elsewhere, finish or release that work first and say which on both + issues. No machinery counts claims per builder, and none should be built + expecting this section to have specified one. - **Abandoning is fine; ghosting is not.** Say where you got to, push the - branch if it holds anything useful, unassign, and restore `ready`. + branch if it holds anything useful, unassign, restore `ready`. ## Building - Branch per issue; open the PR **as a draft early**, `Closes #N` in the - body. Drafts are invisible to the reviewer panel on purpose: the draft - phase is yours. + body. Drafts are invisible to the panel on purpose: the draft phase is + yours. - **`Closes #N` does not cross repos.** A PR in a different repo from its - authorizing issue says `Part of /#N`, sets `offsite`, and - comments the draft PR link on that issue in the same step. Triage closes - that issue by hand once its acceptance criteria are met, and at that - handoff the builder reports whether the PR merged or closed and clears - `offsite` in the same comment (#13, #16). + issue says `Part of /#N`, sets `offsite`, and comments the + draft link on that issue in the same step. Triage closes that issue by + hand once its criteria are met; at that handoff the builder reports + whether the PR merged or closed and clears `offsite` (#13, #16). - **`Closes #N` does not survive a post-merge criterion.** Where the issue's - body states that a criterion can only be checked after the merge — a live - proof of a workflow trigger, a released-artifact check, anything whose - subject does not exist until the change is on the base branch — the - same-repo PR says `Refs #N` and triage closes by hand on the evidence. The - merge releases the claim: the issue moves to `post-merge`, the builder - walks away, and triage owns verification and closure, returning it to - `ready` or minting a fresh issue for corrective work that any builder - claims from current `main`. The issue body is what says so — you never - judge which issues qualify, and absent that instruction `Closes #N` is the - default (#151). + body says a criterion can only be checked after the merge — a workflow + trigger proved live, a released artifact, anything whose subject does not + exist until the change is on the base branch — the same-repo PR says + `Refs #N` and triage closes by hand on the evidence. The merge releases + the claim: the issue goes `post-merge`, the builder walks away, triage + owns verification and closure, and corrective work is a fresh issue any + builder claims from current `main`. The issue body is what says so — you + never judge which issues qualify, and absent that instruction `Closes #N` + is the default (#151). - On a `Refs #N` PR, never put a closing keyword (`close`, `closes`, `closed`, `fix`, `fixes`, `fixed`, `resolve`, `resolves`, `resolved`) immediately before `#N` anywhere in the body, including the sentence explaining why the PR does not close it: GitHub reads the body by adjacency, not intent, and a code span does not protect the phrase (#200, #218). Put the number first (`#N is closed by hand`) or omit it. -- **The issue's acceptance criteria are your definition of done.** Reproduce - them as a checklist in the PR body and check them honestly as you go; a - criterion that turns out wrong or unreachable goes back to triage to be - amended, never silently shipped short. +- **The issue's acceptance criteria are your definition of done**: reproduce + them as a checklist in the PR body and check them honestly. A criterion + that turns out wrong or unreachable goes back to triage to be amended, + never silently shipped short. - **Every behavior change writes one fragment**, `changelog.d/.md` named for the authorizing issue (`-.md` cross-repo): the exact prose to be published and nothing else — `- ` bullets, plus in a - grouped repo the `### Added` / `### Changed` / `### Fixed` headings inside - the fragment, a rarer kind only where a change genuinely is one. An entry - is at most 300 characters, so a long change ships several short entries; - wrapping one over continuation lines never counts against it. It **ends - with its issue citation** — one parenthesised group of `#N`, `repo#N` or - `owner/repo#N` references separated by `, `, then the final `.` and - nothing after it: `(#262).`, or `(#236, #250).` where an entry honestly - lands two — and need not name the fragment's own issue, which the filename - carries. The guard reds a longer entry (#167) and an uncited one (#262) - alike. Never edit `CHANGELOG.md` for an entry: the release PR assembles - the section from the fragments (#112), and the monotonic guard refuses - anything that deletes a shipped heading. + grouped repo the `### Added` / `### Changed` / `### Fixed` headings, a + rarer kind only where a change genuinely is one. An entry is at most 300 + characters, so a long change ships several short entries; wrapping one + over continuation lines never counts against it. It **ends with its issue + citation** — a parenthesised group of `#N`, `repo#N` or `owner/repo#N` + separated by `, `, then the final `.` and nothing after: `(#262).`, or + `(#236, #250).` where an entry honestly lands two — and need not name the + fragment's own issue, which the filename carries. The guard reds a longer + entry (#167) and an uncited one (#262). Never edit `CHANGELOG.md` for an + entry: the release PR assembles the section from fragments (#112), and the + monotonic guard refuses anything deleting a shipped heading. - Follow the repo's conventions file and match the code you touch. Tests are not optional: the issue's test plan is the floor, not the ceiling. - **A write-capable job gets a repo-owned script, not a third-party action.** - Where the job's token can write (`packages: write`, `contents: write`, - `id-token: write`, deploy secrets), default to a script in the repo that a - test can drive; a third-party action there needs an established publisher - and a full-commit-SHA pin, and read-only jobs still SHA-pin. The full rule - and the red-flag profile a reviewer applies are in REVIEWER.md §What you - review against, item 2 (#216). + Where the token can write (`packages: write`, `contents: write`, + `id-token: write`, deploy secrets), default to a script a test can drive; + a third-party action there needs an established publisher and a + full-commit-SHA pin, and read-only jobs still SHA-pin. The full rule and + its red-flag profile are in REVIEWER.md §What you review against, item 2 + (#216). - **Scope discipline: the PR does the issue — whole, and nothing else.** - Adjacent problems go to a **discussion**, or a comment on the relevant - issue, where triage does its job. You do not mint issues — nobody but - triage does — and you do not fix drive-by findings in the same PR. + Adjacent problems go to a discussion, or a comment on the relevant issue. + You do not mint issues — nobody but triage does — and you do not fix + drive-by findings in the same PR. ## The review round 1. Mark ready-for-review; request **the whole panel**: the PR repo's `panel[]=` line if it defines one, else its `panel=` line, - minus the author in either case (#224) — never the roster of the repo the - issue is in. That repo's `.github/labels.conf` governs over its - CONTRIBUTING roster, being what the state machine reads; where the PR - repo names no roster, ask triage on the authorizing issue rather than - guessing. An off-panel reviewer may be requested, saying that their - verdict is advisory and does not become required. - **A review request requires a green check at the head**, and that binds - you whether or not any engine enforces it: a red check is the author's - own signal, so fix it and push, then request. The one exception is a - failure genuinely outside the PR — a runner outage, a flaky dependency, a - failure already present on the default branch — and only where the - request says so explicitly and names the evidence ("the same job fails - identically on `origin/main` at ``"); silence about a red check is - what is prohibited. - *Green* is a ruled term (operator, 2026-07-27), read in two steps. - **First take the check's word at this head**: its newest entry by start - time — not by completion, a cancelled run outliving its replacement's - start — and never a `CANCELLED` entry while the same check has a - non-cancelled one at that head. A check whose every entry at the head is - cancelled has not reported at all and is not green, the gate collapsing - the same way (#139, #276). **Then classify that entry from its - `conclusion`, never its `status`**, which can still disagree with it - (#259): - - no conclusion at all — not green: a configured run still in progress is - waited on, and waiting is compliance, not a stall; - - cancelled or stale — not green, *stale* meaning a superseded head's - check, which a head-scoped rollup never shows; - - skipped or neutral — green, those being deliberate "passed / not - applicable" conclusions; - - no checks configured at the head — green, the third ruled case and not - an argued exception: the request goes out at once, no evidence owed. - That never covers nothing-answered-yet, and the machine partitions - alike, admitting the ask on `SUCCESS` and on `NONE` (#236). + minus the author (#224) — never the roster of the repo the issue is in. + That repo's `.github/labels.conf` governs over its CONTRIBUTING roster, + being what the state machine reads; where it names no roster, ask triage + on the authorizing issue rather than guess. An off-panel reviewer may be + requested, said to be advisory and not required. + **A review request requires a green check at the head**, whether or not + an engine enforces it: a red check is the author's own signal, so fix it + and push, then request. The one exception is a failure genuinely outside + the PR — a runner outage, a flaky dependency, a failure already on the + default branch — and only where the request says so and names the + evidence ("the same job fails identically on `origin/main` at ``"); + silence about a red check is what is prohibited. + *Green* is a ruled term, read in two steps. **First take the check's word + at this head**: its newest entry by start time — not completion, a + cancelled run outliving its replacement's start — and never a `CANCELLED` + entry while the same check has a non-cancelled one there. A check whose + entries at the head are all cancelled has not reported at all and is not + green, the gate collapsing alike (#139, #276). **Then classify that entry + by `conclusion`, never `status`**, which can disagree with it (#259): + - no conclusion — not green; a configured run in progress is waited on, + and waiting is compliance, not a stall; + - cancelled or stale — not green (*stale* is a superseded head's check, + which a head-scoped rollup never shows); + - skipped or neutral — green, being deliberate "passed / not applicable" + conclusions; + - no checks configured — green: the third ruled case, not an argued + exception, so the request goes out at once with no evidence owed. It + never covers nothing-answered-yet, and the machine partitions alike, + admitting the ask on `SUCCESS` and `NONE` (#236). The costs behind the line are asymmetric: a false green spends a - three-reviewer round, a false red one author session. What the *machine* + three-reviewer round, a false red one author session. What the machine drops from the rollup before grading is crew's to describe. 2. **Wait for every verdict, then answer the round whole** — one reply - covering every point and stating what changed and what was verified. That - reply is the written round record: the engine mirrors it under the PR - body's **Round log**, newest last, so the builder owes the reply and no - separate body edit, and a round answered without one is recorded as such - and never blocks handoff. + covering every point, stating what changed and what was verified. That + reply is the written record: the engine mirrors it under the PR body's + **Round log**, newest last, so you owe the reply and no body edit, and a + round answered without one is recorded as such and never blocks handoff. Then push the fixes and re-request **by head, not by verdict**. A push makes every approval stale — an approval is of a specific tree, and the handoff predicate counts only approvals at the current head — so **every - panelist is re-requested, the approvers included**; one left - un-re-requested can never approve the tree you shipped (#26, #39). Only - where the head did not move — the round answered with argument or - evidence, nothing pushed — do you re-request just the non-approvers (#94). - **The re-request carries the same green-check-at-head precondition**, - argued exception included: a fix push whose check comes up red is your - next fix, not the panel's. Prefer verification over argument — where a - reviewer doubts behavior, add the test that settles it. + panelist is re-requested, approvers included**; one left un-re-requested + can never approve the tree you shipped (#26, #39). Only where the head + did not move — answered with argument or evidence, nothing pushed — do + you re-request just the non-approvers (#94). **The re-request carries the + same green-check-at-head precondition**, argued exception included: a fix + push whose check comes up red is your next fix, not the panel's. Prefer + verification over argument — where a reviewer doubts behavior, add the + test that settles it. 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; silence and force-forward are not options, and a panel deadlock is one kind of human-owned decision (#50 D11). **A fix round may ride a draft**, and the draft changes nothing about who -owes what: an engine may convert a PR back to draft when a round closes, and -ceremony implements no such conversion, but whoever meets a mid-round draft -reads it as a draft always read — the draft phase is yours and the panel -cannot see it — while the round outranks the draft, so you still owe it -whole, the fixes and the reply and the flip ([LABELS.md](LABELS.md)'s -`state:building` row, #205). **Ready-for-review is the act that ends the -round, and it is the builder's alone**: the flip asserts that the round was -answered whole, the one judgement its author cannot delegate, so an engine -may draft a PR but only the builder undrafts it. **Where a draft suppressed -the checks, green is proven at the flip and the request still follows it** — -marking ready is what runs the checks the draft held back, so the order is -flip, let the head answer, then request, which is step 1's precondition and -not a second one. Waiting there is compliance, and `blocker:unrequested` -does not fire while a head's checks are pending or red (#236). +owes what: an engine may draft a PR when a round closes, ceremony implements +no such conversion, and whoever meets a mid-round draft reads it as a draft +always read — the phase is yours, the panel cannot see it — while the round +outranks the draft, so you owe it whole, the fixes and the reply and the +flip ([LABELS.md](LABELS.md)'s `state:building` row, #205). +**Ready-for-review is the act that ends the round, and it is the builder's +alone**: the flip asserts the round was answered whole, the one judgement +its author cannot delegate, so an engine may draft a PR but only the builder +undrafts it. **Where a draft suppressed the checks, green is proven at the +flip and the request still follows it** — marking ready runs the checks the +draft held back, so the order is flip, let the head answer, then request, +which is step 1's precondition and not a second one. Waiting there is +compliance, and `blocker:unrequested` does not fire while a head's checks +are pending or red (#236). ## The ruling ask Set `needs-ruling` whenever a decision belongs to a human: org policy, published artifacts, secrets, prod, or any choice whose cost lands outside -the PR. A panel deadlock is one instance, not the definition. The builder is -the accountable flag-setter on a PR and consolidates the decision into one -comment rather than forwarding several reviewers' phrasings (#50 D11). +the PR — a panel deadlock is one instance, not the definition. The builder +is the accountable flag-setter on a PR and consolidates the decision into +one comment rather than forwarding several reviewers' phrasings (#50 D11). -Keep at most these five lines above the fold and put all other analysis -inside the fold. The field labels are fixed because the ruling machinery -checks for them (#50 D12): +Keep at most these five lines above the fold, all other analysis inside it. +The field labels are fixed because the ruling machinery checks for them +(#50 D12): ```text 🧭 needs-ruling — @@ -276,31 +257,30 @@ event**, not its `Default:` deadline or the last activity (#50 D13–D14): it as a decision, and remains accountable; the operator can overturn it at merge. -A re-flag starts a fresh ladder. The ladder applies whatever `Default:` says, -including a hard block, and an active back-and-forth still climbs it — unlike -the 7-day nudge, which resets on real activity. The machine observes both -clocks but never sets, clears, or decides `needs-ruling`. - -The label stays until agreement is *reached*, not until the maintainer -replies. The setter records the ruling, removes the label, and returns the -item to its flow in the same comment ([LABELS.md](LABELS.md)). +A re-flag starts a fresh ladder. It applies whatever `Default:` says, +including a hard block, and an active back-and-forth still climbs it — +unlike the 7-day nudge, which resets on real activity. The machine observes +both clocks but never sets, clears, or decides `needs-ruling`. The label +stays until agreement is *reached*, not until the maintainer replies: the +setter records the ruling, removes the label, and returns the item to its +flow in the same comment ([LABELS.md](LABELS.md)). ## Handoff -When the round passes — every panel verdict approves the **current head**, -and no `blocker:*` stands (conflicts rebased, CI green, drill recorded if -this is a release PR) — the engine performs these mechanical steps on the -builder's behalf, in order: +When the round passes — every panel verdict approving the **current head**, +no `blocker:*` standing (conflicts rebased, CI green, drill recorded if this +is a release PR) — the engine does these mechanical steps for the builder, +in order: 1. request the human's review; 2. set `state:needs-human`; 3. post the engine-rendered handoff comment: approvals at the current head, the head SHA, and a pointer to the PR body's **Round log**. -The builder composes no new summary at handoff: the authored record already -lives in the Round log, mirrored from each whole-round reply. The label write -is optimistic — the reconciler validates it and takes it back if the PR is -not actually mergeable-right-now. Then stop: the PR is the human's, and the -claim is parked as shape 4 (Picking, above), that handoff comment being its -declaration and your build slot free. Address what comes back -(`state:addressing`) and re-hand-off the same way. +The builder composes no new summary: the authored record already lives in +the Round log, mirrored from each whole-round reply. The label write is +optimistic — the reconciler validates it and takes it back if the PR is not +mergeable-right-now. Then stop: the PR is the human's, and the claim is +parked as shape 4 (Picking, above), that handoff comment being its +declaration and your slot free. Address what comes back (`state:addressing`) +and re-hand-off the same way. From cef7ea206ba42e7bff4200ced6351b7cc2875a0a Mon Sep 17 00:00:00 2001 From: cndgrr <59120057+cndgrr@users.noreply.github.com> Date: Tue, 4 Aug 2026 13:27:52 +0000 Subject: [PATCH 07/15] =?UTF-8?q?docs(builder):=20WIP=20=E2=80=94=20propos?= =?UTF-8?q?al=20biography=20and=20list=20scaffolding=20out?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BUILDER.md | 217 +++++++++++++++++++++++++---------------------------- 1 file changed, 103 insertions(+), 114 deletions(-) diff --git a/BUILDER.md b/BUILDER.md index 789835d..4dff496 100644 --- a/BUILDER.md +++ b/BUILDER.md @@ -12,14 +12,14 @@ triage bug, and the move is to say so on the issue, not to guess. unblocks the most work. Where a repo adopts version epics, [RELEASES.md](RELEASES.md) governs the choice among window members. - **Your own red head outranks a new claim**: repair a failing check at your - PR's head before claiming another issue, or it strands mergeable and - unattended (#163). Record the check and its failure class; rerun a clearly - retryable infrastructure failure unchanged; treat a branch failure as an - ordinary fix round, worklog and all; leave evidence where a rerun cannot - start or the cause is unclear; never rerun a deterministic failure without - a corrective commit; hand off once green with current-head approvals. Such - a PR is **never parked**, whatever the round's verdict state says. How the - engine detects a red head is crew's to describe. + PR's head before claiming another issue (#163). Record the check and its + failure class; rerun a clearly retryable infrastructure failure unchanged; + treat a branch failure as an ordinary fix round, worklog and all; leave + evidence where a rerun cannot start or the cause is unclear; never rerun a + deterministic failure without a corrective commit; hand off once green + with current-head approvals. Such a PR is **never parked**, whatever the + verdict state says; how the engine detects a red head is crew's to + describe. - **One build at a time**: one issue on which you are writing or revising a deliverable, finished or released before you start more. The rule counts work in flight, not claims — a **parked** claim, whose next move is @@ -41,14 +41,14 @@ triage bug, and the move is to say so on the issue, not to guess. most recent queue-label event by the hold's owner governs, and an operator may lift by label alone (#149, #151). So read the label events (`gh api /repos/{owner}/{repo}/issues/{n}/timeline`), not just the - comments, before standing down *or* up; acting against stale prose, say - so in the claim, naming the events, their timestamps and their actor. - Refusing to claim through the contradiction is no resting place: where - the events do not resolve it, say so and take the next `ready` issue. + comments, before standing down *or* up, and say in the claim which you + read, their timestamps and their actor. Refusing to claim through the + contradiction is no resting place: where the events do not resolve it, + say so and take the next `ready` issue. Not parked: waiting on yourself, on CI (a red head is yours; a pending one - resolves without you), or for a good moment. An issue you simply stopped - working on is abandoned — unassign and restore `ready`. Parked claims are - legitimately held beside the one active build (#15, #16, #73). + resolves without you), or for a good moment. An issue you stopped working + on is abandoned — unassign and restore `ready`. Parked claims are held + beside the one active build (#15, #16, #73). ## Claiming @@ -65,8 +65,8 @@ triage bug, and the move is to say so on the issue, not to guess. finding nothing changed posts nothing (#177). Each change owes one comment — the wait resolves or changes hands, the shape changes, the claim unparks. A parked claim with **no open PR** still feeds the 48-hour - reclaim clock, so refresh the declaration before that window closes; that - is the only repeat a park owes. + reclaim clock, so refresh the declaration before it closes; that is a + park's only repeat. - **Pick up `attention` before anything else**: post a short pickup comment and remove the label, which is the ack. A demand on a parked claim is usually its unpark, so take the slot back — unless the demand *is* the @@ -89,19 +89,18 @@ triage bug, and the move is to say so on the issue, not to guess. yours. - **`Closes #N` does not cross repos.** A PR in a different repo from its issue says `Part of /#N`, sets `offsite`, and comments the - draft link on that issue in the same step. Triage closes that issue by - hand once its criteria are met; at that handoff the builder reports - whether the PR merged or closed and clears `offsite` (#13, #16). -- **`Closes #N` does not survive a post-merge criterion.** Where the issue's + draft link on that issue in the same step; triage closes that issue by + hand once its criteria are met, the builder reporting there whether the PR + merged or closed and clearing `offsite` in the same comment (#13, #16). +- **`Closes #N` does not survive a post-merge criterion.** Where the issue body says a criterion can only be checked after the merge — a workflow trigger proved live, a released artifact, anything whose subject does not - exist until the change is on the base branch — the same-repo PR says - `Refs #N` and triage closes by hand on the evidence. The merge releases - the claim: the issue goes `post-merge`, the builder walks away, triage - owns verification and closure, and corrective work is a fresh issue any - builder claims from current `main`. The issue body is what says so — you - never judge which issues qualify, and absent that instruction `Closes #N` - is the default (#151). + exist until the change is on the base branch — the same-repo PR says `Refs + #N`; the issue goes `post-merge` at the merge, the builder walks away, + triage owns verification and closure on the evidence, and corrective work + is a fresh issue any builder claims from current `main`. The issue body is + what says so — you never judge which qualify, and absent that instruction + `Closes #N` is the default (#151). - On a `Refs #N` PR, never put a closing keyword (`close`, `closes`, `closed`, `fix`, `fixes`, `fixed`, `resolve`, `resolves`, `resolved`) immediately before `#N` anywhere in the body, including the sentence @@ -109,35 +108,34 @@ triage bug, and the move is to say so on the issue, not to guess. adjacency, not intent, and a code span does not protect the phrase (#200, #218). Put the number first (`#N is closed by hand`) or omit it. - **The issue's acceptance criteria are your definition of done**: reproduce - them as a checklist in the PR body and check them honestly. A criterion - that turns out wrong or unreachable goes back to triage to be amended, - never silently shipped short. + them as a checklist in the PR body and check them honestly. One that turns + out wrong or unreachable goes back to triage to be amended, never silently + shipped short. - **Every behavior change writes one fragment**, `changelog.d/.md` named for the authorizing issue (`-.md` cross-repo): the - exact prose to be published and nothing else — `- ` bullets, plus in a - grouped repo the `### Added` / `### Changed` / `### Fixed` headings, a - rarer kind only where a change genuinely is one. An entry is at most 300 - characters, so a long change ships several short entries; wrapping one - over continuation lines never counts against it. It **ends with its issue - citation** — a parenthesised group of `#N`, `repo#N` or `owner/repo#N` - separated by `, `, then the final `.` and nothing after: `(#262).`, or - `(#236, #250).` where an entry honestly lands two — and need not name the - fragment's own issue, which the filename carries. The guard reds a longer - entry (#167) and an uncited one (#262). Never edit `CHANGELOG.md` for an - entry: the release PR assembles the section from fragments (#112), and the - monotonic guard refuses anything deleting a shipped heading. + prose to be published and nothing else — `- ` bullets, plus in a grouped + repo `### Added` / `### Changed` / `### Fixed` headings, a rarer kind only + where a change genuinely is one. An entry is at most 300 characters, so a + long change ships several short ones (wrapping over continuation lines is + free), and it **ends with its issue citation**: a parenthesised group of + `#N`, `repo#N` or `owner/repo#N` separated by `, `, then the final `.` and + nothing after — `(#262).`, `(#236, #250).` — which need not name the + fragment's own issue, the filename carrying it. The guard reds a long + entry (#167) and an uncited one (#262). Never edit `CHANGELOG.md`: the + release PR assembles the section from fragments (#112), and the monotonic + guard refuses anything deleting a shipped heading. - Follow the repo's conventions file and match the code you touch. Tests are not optional: the issue's test plan is the floor, not the ceiling. -- **A write-capable job gets a repo-owned script, not a third-party action.** - Where the token can write (`packages: write`, `contents: write`, +- **A write-capable job gets a repo-owned script, not a third-party + action.** Where the token can write (`packages: write`, `contents: write`, `id-token: write`, deploy secrets), default to a script a test can drive; a third-party action there needs an established publisher and a full-commit-SHA pin, and read-only jobs still SHA-pin. The full rule and its red-flag profile are in REVIEWER.md §What you review against, item 2 (#216). - **Scope discipline: the PR does the issue — whole, and nothing else.** - Adjacent problems go to a discussion, or a comment on the relevant issue. - You do not mint issues — nobody but triage does — and you do not fix + Adjacent problems go to a discussion, or a comment on the relevant issue; + you do not mint issues — nobody but triage does — and you do not fix drive-by findings in the same PR. ## The review round @@ -148,33 +146,29 @@ triage bug, and the move is to say so on the issue, not to guess. That repo's `.github/labels.conf` governs over its CONTRIBUTING roster, being what the state machine reads; where it names no roster, ask triage on the authorizing issue rather than guess. An off-panel reviewer may be - requested, said to be advisory and not required. - **A review request requires a green check at the head**, whether or not - an engine enforces it: a red check is the author's own signal, so fix it - and push, then request. The one exception is a failure genuinely outside - the PR — a runner outage, a flaky dependency, a failure already on the - default branch — and only where the request says so and names the - evidence ("the same job fails identically on `origin/main` at ``"); - silence about a red check is what is prohibited. - *Green* is a ruled term, read in two steps. **First take the check's word - at this head**: its newest entry by start time — not completion, a - cancelled run outliving its replacement's start — and never a `CANCELLED` - entry while the same check has a non-cancelled one there. A check whose - entries at the head are all cancelled has not reported at all and is not - green, the gate collapsing alike (#139, #276). **Then classify that entry - by `conclusion`, never `status`**, which can disagree with it (#259): - - no conclusion — not green; a configured run in progress is waited on, - and waiting is compliance, not a stall; - - cancelled or stale — not green (*stale* is a superseded head's check, - which a head-scoped rollup never shows); - - skipped or neutral — green, being deliberate "passed / not applicable" - conclusions; - - no checks configured — green: the third ruled case, not an argued - exception, so the request goes out at once with no evidence owed. It - never covers nothing-answered-yet, and the machine partitions alike, - admitting the ask on `SUCCESS` and `NONE` (#236). - - The costs behind the line are asymmetric: a false green spends a + requested, said to be advisory and not required. **A review request + requires a green check at the head**, whether or not an engine enforces + it: a red check is the author's own signal, so fix it and push, then + request. The one exception is a failure genuinely outside the PR — a + runner outage, a flaky dependency, a failure already on the default + branch — and only where the request says so and names the evidence ("the + same job fails identically on `origin/main` at ``"). *Green* is a + ruled term (operator, 2026-07-27), read in two steps. **First take the + check's word at this head**: its newest entry by start time — not + completion, a cancelled run outliving its replacement's start — and never + a `CANCELLED` entry while the same check has a non-cancelled one there. A + check whose entries at the head are all cancelled has not reported at all + and is not green, the gate collapsing alike (#139, #276). **Then classify + that entry by `conclusion`, never `status`**, which can disagree with it + (#259). No conclusion is not green: a configured run in progress is + waited on, and waiting is compliance, not a stall. Cancelled or stale is + not green, *stale* being a superseded head's check, which a head-scoped + rollup never shows. Skipped or neutral is green, those being deliberate + "passed / not applicable" conclusions. No checks configured is green — + the third ruled case, not an argued exception, so the request goes out at + once with no evidence owed; that never covers nothing-answered-yet, and + the machine partitions alike, admitting the ask on `SUCCESS` and `NONE` + (#236). The costs behind the line are asymmetric: a false green spends a three-reviewer round, a false red one author session. What the machine drops from the rollup before grading is crew's to describe. 2. **Wait for every verdict, then answer the round whole** — one reply @@ -191,40 +185,37 @@ triage bug, and the move is to say so on the issue, not to guess. you re-request just the non-approvers (#94). **The re-request carries the same green-check-at-head precondition**, argued exception included: a fix push whose check comes up red is your next fix, not the panel's. Prefer - verification over argument — where a reviewer doubts behavior, add the - test that settles it. + verification over argument — add the test that settles the doubt. 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; silence and force-forward are not options, and a panel deadlock is one kind of human-owned decision (#50 D11). **A fix round may ride a draft**, and the draft changes nothing about who -owes what: an engine may draft a PR when a round closes, ceremony implements -no such conversion, and whoever meets a mid-round draft reads it as a draft -always read — the phase is yours, the panel cannot see it — while the round -outranks the draft, so you owe it whole, the fixes and the reply and the -flip ([LABELS.md](LABELS.md)'s `state:building` row, #205). -**Ready-for-review is the act that ends the round, and it is the builder's -alone**: the flip asserts the round was answered whole, the one judgement -its author cannot delegate, so an engine may draft a PR but only the builder -undrafts it. **Where a draft suppressed the checks, green is proven at the -flip and the request still follows it** — marking ready runs the checks the -draft held back, so the order is flip, let the head answer, then request, -which is step 1's precondition and not a second one. Waiting there is -compliance, and `blocker:unrequested` does not fire while a head's checks -are pending or red (#236). +owes what: a mid-round draft reads as a draft always read — the phase is +yours, the panel cannot see it — while the round outranks it, so you owe the +round whole, the fixes and the reply and the flip ([LABELS.md](LABELS.md)'s +`state:building` row, #205). **Ready-for-review is the act that ends the +round, and it is the builder's alone**: the flip asserts the round was +answered whole, the one judgement its author cannot delegate, so an engine +may draft a PR but only the builder undrafts it. **Where a draft suppressed +the checks, green is proven at the flip and the request still follows it** — +marking ready runs the checks the draft held back, so the order is flip, let +the head answer, then request, step 1's precondition and not a second one. +Waiting there is compliance, and `blocker:unrequested` does not fire while a +head's checks are pending or red (#236). ## The ruling ask Set `needs-ruling` whenever a decision belongs to a human: org policy, published artifacts, secrets, prod, or any choice whose cost lands outside the PR — a panel deadlock is one instance, not the definition. The builder -is the accountable flag-setter on a PR and consolidates the decision into -one comment rather than forwarding several reviewers' phrasings (#50 D11). +is the PR's accountable flag-setter and consolidates the decision into one +comment rather than forwarding several reviewers' phrasings (#50 D11). Keep at most these five lines above the fold, all other analysis inside it. -The field labels are fixed because the ruling machinery checks for them -(#50 D12): +The field labels are fixed because the ruling machinery checks for them (#50 +D12): ```text 🧭 needs-ruling — @@ -238,32 +229,30 @@ Default: | none — hard block The options must be exhaustive and mutually exclusive; more than three means the question is not ready. `Recommend:` is mandatory — omitting it hands the whole problem to the human. `Blocked:` names both what stops and what -continues. Write a timed `Default:` only when you are affirmatively confident -the decision is reversible inside the PR before merge. Unsure is not a tie: -it is a hard block, as published artifacts, secrets, prod and org policy are -by construction (#50 D12–D13). +continues. Write a timed `Default:` only when affirmatively confident the +decision is reversible inside the PR before merge; unsure is not a tie but a +hard block, as published artifacts, secrets, prod and org policy are by +construction (#50 D12–D13). The ladder is anchored to the current episode's `needs-ruling` **`labeled` event**, not its `Default:` deadline or the last activity (#50 D13–D14): -- **0–12h:** proceed when a still-clear, reversible default expires, and say +- **0–12h:** proceed when a still-clear, reversible default expires, saying out loud that you did; a hard block waits. - **at 12h:** do not fire a stale default — re-read it against what has landed, and where doubt has appeared, make it a hard block. -- **at 24h:** proceed regardless, **as a PR**: pick an option and state in - the PR body which way you went and what doubt remains. Nothing merges by - this; the human still gates the merge. +- **at 24h:** proceed regardless, **as a PR**: pick an option and say in the + body which way you went and what doubt remains. Nothing merges by this. - **past 24h:** hand the choice to triage, which picks the option, records - it as a decision, and remains accountable; the operator can overturn it at - merge. + it as a decision, and stays accountable; the operator can overturn it. -A re-flag starts a fresh ladder. It applies whatever `Default:` says, -including a hard block, and an active back-and-forth still climbs it — -unlike the 7-day nudge, which resets on real activity. The machine observes -both clocks but never sets, clears, or decides `needs-ruling`. The label -stays until agreement is *reached*, not until the maintainer replies: the -setter records the ruling, removes the label, and returns the item to its -flow in the same comment ([LABELS.md](LABELS.md)). +A re-flag starts a fresh ladder, which applies whatever `Default:` says, +hard block included, and an active back-and-forth still climbs it — unlike +the 7-day nudge, which resets on real activity. The machine observes both +clocks but never sets, clears, or decides `needs-ruling`. The label stays +until agreement is *reached*, not until the maintainer replies: the setter +records the ruling, removes the label, and returns the item to its flow in +the same comment ([LABELS.md](LABELS.md)). ## Handoff @@ -281,6 +270,6 @@ The builder composes no new summary: the authored record already lives in the Round log, mirrored from each whole-round reply. The label write is optimistic — the reconciler validates it and takes it back if the PR is not mergeable-right-now. Then stop: the PR is the human's, and the claim is -parked as shape 4 (Picking, above), that handoff comment being its -declaration and your slot free. Address what comes back (`state:addressing`) -and re-hand-off the same way. +parked as shape 4 (Picking, above), that comment being its declaration and +your slot free. Address what comes back (`state:addressing`) and re-hand-off +the same way. From 4100121c7acca7da355d56b08f2e3cc7bb034fe3 Mon Sep 17 00:00:00 2001 From: cndgrr <59120057+cndgrr@users.noreply.github.com> Date: Tue, 4 Aug 2026 13:30:12 +0000 Subject: [PATCH 08/15] =?UTF-8?q?docs(builder):=20final=20squeeze=20?= =?UTF-8?q?=E2=80=94=20272=20lines,=20every=20D2=20rule=20intact?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BUILDER.md | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/BUILDER.md b/BUILDER.md index 4dff496..c7ebbd8 100644 --- a/BUILDER.md +++ b/BUILDER.md @@ -8,9 +8,9 @@ triage bug, and the move is to say so on the issue, not to guess. - Pick from issues labeled **`ready`** — never `blocked`, `claimed`, or an `epic` (epics organize; their children are the work). Inside an epic take - the earliest unblocked unclaimed child; otherwise prefer the issue that - unblocks the most work. Where a repo adopts version epics, - [RELEASES.md](RELEASES.md) governs the choice among window members. + the earliest unblocked unclaimed child, otherwise the issue that unblocks + the most work; where a repo adopts version epics, + [RELEASES.md](RELEASES.md) governs among window members. - **Your own red head outranks a new claim**: repair a failing check at your PR's head before claiming another issue (#163). Record the check and its failure class; rerun a clearly retryable infrastructure failure unchanged; @@ -42,9 +42,9 @@ triage bug, and the move is to say so on the issue, not to guess. operator may lift by label alone (#149, #151). So read the label events (`gh api /repos/{owner}/{repo}/issues/{n}/timeline`), not just the comments, before standing down *or* up, and say in the claim which you - read, their timestamps and their actor. Refusing to claim through the - contradiction is no resting place: where the events do not resolve it, - say so and take the next `ready` issue. + read, their timestamps and their actor. Where they do not resolve the + contradiction, say so and take the next `ready` issue; refusing is no + resting place. Not parked: waiting on yourself, on CI (a red head is yours; a pending one resolves without you), or for a good moment. An issue you stopped working on is abandoned — unassign and restore `ready`. Parked claims are held @@ -53,7 +53,7 @@ triage bug, and the move is to say so on the issue, not to guess. ## Claiming - Assign yourself, swap `ready` → `claimed`, and comment that you are - starting. The claim promises a draft PR soon: a claim with no PR and no + starting. The claim promises a draft PR soon: one with no PR and no activity is what the staleness sweep reclaims, unless `offsite` records that its PR lives in another repo. - **A park is declared, never inferred.** Comment naming what the claim @@ -70,7 +70,7 @@ triage bug, and the move is to say so on the issue, not to guess. - **Pick up `attention` before anything else**: post a short pickup comment and remove the label, which is the ack. A demand on a parked claim is usually its unpark, so take the slot back — unless the demand *is* the - park, where the pickup comment doubles as the declaration. + park, the pickup comment then doubling as the declaration. - **A directed hold keeps its bookkeeping visible.** The PR carries `blocked` with a comment naming what it waits on; the issue stays `claimed` and carries `attention` until the builder acks. Nobody unassigns @@ -85,8 +85,7 @@ triage bug, and the move is to say so on the issue, not to guess. ## Building - Branch per issue; open the PR **as a draft early**, `Closes #N` in the - body. Drafts are invisible to the panel on purpose: the draft phase is - yours. + body. Drafts are invisible to the panel on purpose: that phase is yours. - **`Closes #N` does not cross repos.** A PR in a different repo from its issue says `Part of /#N`, sets `offsite`, and comments the draft link on that issue in the same step; triage closes that issue by @@ -95,12 +94,12 @@ triage bug, and the move is to say so on the issue, not to guess. - **`Closes #N` does not survive a post-merge criterion.** Where the issue body says a criterion can only be checked after the merge — a workflow trigger proved live, a released artifact, anything whose subject does not - exist until the change is on the base branch — the same-repo PR says `Refs - #N`; the issue goes `post-merge` at the merge, the builder walks away, - triage owns verification and closure on the evidence, and corrective work - is a fresh issue any builder claims from current `main`. The issue body is - what says so — you never judge which qualify, and absent that instruction - `Closes #N` is the default (#151). + exist until the change is on the base branch — the same-repo PR says + `Refs #N`; the issue goes `post-merge` at the merge, the builder walks + away, triage owns verification and closure on the evidence, and corrective + work is a fresh issue any builder claims from current `main`. The issue + body says so — you never judge which qualify — and absent it `Closes #N` + is the default (#151). - On a `Refs #N` PR, never put a closing keyword (`close`, `closes`, `closed`, `fix`, `fixes`, `fixed`, `resolve`, `resolves`, `resolved`) immediately before `#N` anywhere in the body, including the sentence @@ -258,8 +257,7 @@ the same comment ([LABELS.md](LABELS.md)). When the round passes — every panel verdict approving the **current head**, no `blocker:*` standing (conflicts rebased, CI green, drill recorded if this -is a release PR) — the engine does these mechanical steps for the builder, -in order: +is a release PR) — the engine does these steps for the builder, in order: 1. request the human's review; 2. set `state:needs-human`; @@ -269,7 +267,6 @@ in order: The builder composes no new summary: the authored record already lives in the Round log, mirrored from each whole-round reply. The label write is optimistic — the reconciler validates it and takes it back if the PR is not -mergeable-right-now. Then stop: the PR is the human's, and the claim is -parked as shape 4 (Picking, above), that comment being its declaration and -your slot free. Address what comes back (`state:addressing`) and re-hand-off -the same way. +mergeable-right-now. Then stop: the PR is the human's, and the claim parks +as shape 4 (Picking, above), that comment its declaration and your slot +free. Address what comes back (`state:addressing`) and re-hand-off alike. From 46aeb02078640bfe19e1e4d4a34234965893559a Mon Sep 17 00:00:00 2001 From: cndgrr <59120057+cndgrr@users.noreply.github.com> Date: Tue, 4 Aug 2026 13:31:48 +0000 Subject: [PATCH 09/15] docs(builder): restore five rules the squeeze had compressed away --- BUILDER.md | 77 +++++++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/BUILDER.md b/BUILDER.md index c7ebbd8..29a084b 100644 --- a/BUILDER.md +++ b/BUILDER.md @@ -90,16 +90,18 @@ triage bug, and the move is to say so on the issue, not to guess. issue says `Part of /#N`, sets `offsite`, and comments the draft link on that issue in the same step; triage closes that issue by hand once its criteria are met, the builder reporting there whether the PR - merged or closed and clearing `offsite` in the same comment (#13, #16). + merged or closed and clearing `offsite` in the same comment. The + cross-repo merge never closes the authorizing issue (#13, #16). - **`Closes #N` does not survive a post-merge criterion.** Where the issue body says a criterion can only be checked after the merge — a workflow trigger proved live, a released artifact, anything whose subject does not - exist until the change is on the base branch — the same-repo PR says - `Refs #N`; the issue goes `post-merge` at the merge, the builder walks - away, triage owns verification and closure on the evidence, and corrective - work is a fresh issue any builder claims from current `main`. The issue - body says so — you never judge which qualify — and absent it `Closes #N` - is the default (#151). + exist until the change is on the base branch — the same-repo PR says `Refs + #N`; the issue goes `post-merge` at the merge, the builder walks away, and + triage owns verification and closure on the evidence, returning the issue + to `ready` or minting a fresh one where corrective work is needed — + claimable by any builder from current `main`, the original having no + special standing. The issue body says so — you never judge which qualify — + and absent it `Closes #N` is the default (#151). - On a `Refs #N` PR, never put a closing keyword (`close`, `closes`, `closed`, `fix`, `fixes`, `fixed`, `resolve`, `resolves`, `resolved`) immediately before `#N` anywhere in the body, including the sentence @@ -139,6 +141,9 @@ triage bug, and the move is to say so on the issue, not to guess. ## The review round +(In a governed repo this file is `.ceremony/BUILDER.md`: repo-specific facts +such as the panel roster live in that repo's own CONTRIBUTING.) + 1. Mark ready-for-review; request **the whole panel**: the PR repo's `panel[]=` line if it defines one, else its `panel=` line, minus the author (#224) — never the roster of the repo the issue is in. @@ -151,39 +156,41 @@ triage bug, and the move is to say so on the issue, not to guess. request. The one exception is a failure genuinely outside the PR — a runner outage, a flaky dependency, a failure already on the default branch — and only where the request says so and names the evidence ("the - same job fails identically on `origin/main` at ``"). *Green* is a - ruled term (operator, 2026-07-27), read in two steps. **First take the - check's word at this head**: its newest entry by start time — not - completion, a cancelled run outliving its replacement's start — and never - a `CANCELLED` entry while the same check has a non-cancelled one there. A - check whose entries at the head are all cancelled has not reported at all - and is not green, the gate collapsing alike (#139, #276). **Then classify - that entry by `conclusion`, never `status`**, which can disagree with it - (#259). No conclusion is not green: a configured run in progress is - waited on, and waiting is compliance, not a stall. Cancelled or stale is - not green, *stale* being a superseded head's check, which a head-scoped - rollup never shows. Skipped or neutral is green, those being deliberate - "passed / not applicable" conclusions. No checks configured is green — - the third ruled case, not an argued exception, so the request goes out at - once with no evidence owed; that never covers nothing-answered-yet, and - the machine partitions alike, admitting the ask on `SUCCESS` and `NONE` - (#236). The costs behind the line are asymmetric: a false green spends a + same job fails identically on `origin/main` at ``"); silence about a + red check is what is prohibited. *Green* is a ruled term (operator, + 2026-07-27), read in two steps. **First take the check's word at this + head**: its newest entry by start time — not completion, a cancelled run + outliving its replacement's start — and never a `CANCELLED` entry while + the same check has a non-cancelled one there. A check whose entries at + the head are all cancelled has not reported at all and is not green, the + gate collapsing alike (#139, #276). **Then classify that entry by + `conclusion`, never `status`**, which can disagree with it (#259). No + conclusion is not green: a configured run in progress is waited on, and + waiting is compliance, not a stall. Cancelled or stale is not green, + *stale* being a superseded head's check, which a head-scoped rollup never + shows. Skipped or neutral is green, those being deliberate "passed / not + applicable" conclusions. No checks configured is green — the third ruled + case, not an argued exception, so the request goes out at once with no + evidence owed; that never covers nothing-answered-yet, and the machine + partitions alike, admitting the ask on `SUCCESS` and `NONE` (#236). The + costs behind the line are asymmetric: a false green spends a three-reviewer round, a false red one author session. What the machine drops from the rollup before grading is crew's to describe. 2. **Wait for every verdict, then answer the round whole** — one reply covering every point, stating what changed and what was verified. That reply is the written record: the engine mirrors it under the PR body's - **Round log**, newest last, so you owe the reply and no body edit, and a - round answered without one is recorded as such and never blocks handoff. - Then push the fixes and re-request **by head, not by verdict**. A push - makes every approval stale — an approval is of a specific tree, and the - handoff predicate counts only approvals at the current head — so **every - panelist is re-requested, approvers included**; one left un-re-requested - can never approve the tree you shipped (#26, #39). Only where the head - did not move — answered with argument or evidence, nothing pushed — do - you re-request just the non-approvers (#94). **The re-request carries the - same green-check-at-head precondition**, argued exception included: a fix - push whose check comes up red is your next fix, not the panel's. Prefer + **Round log**, newest last, marked with the round's head so a retry is a + no-op, so you owe the reply and no body edit; a round answered without + one is recorded as such and never blocks handoff. Then push the fixes and + re-request **by head, not by verdict**. A push makes every approval stale + — an approval is of a specific tree, and the handoff predicate counts + only approvals at the current head — so **every panelist is re-requested, + approvers included**; one left un-re-requested can never approve the tree + you shipped (#26, #39). Only where the head did not move — answered with + argument or evidence, nothing pushed — do you re-request just the + non-approvers (#94). **The re-request carries the same + green-check-at-head precondition**, argued exception included: a fix push + whose check comes up red is your next fix, not the panel's. Prefer verification over argument — add the test that settles the doubt. 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 From b4779bbc18abbfdcf3dd4f220ae131adacd39249 Mon Sep 17 00:00:00 2001 From: cndgrr <59120057+cndgrr@users.noreply.github.com> Date: Tue, 4 Aug 2026 13:32:36 +0000 Subject: [PATCH 10/15] docs(builder): keep code spans unbroken across the wrap --- BUILDER.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/BUILDER.md b/BUILDER.md index 29a084b..eb11e9f 100644 --- a/BUILDER.md +++ b/BUILDER.md @@ -95,13 +95,13 @@ triage bug, and the move is to say so on the issue, not to guess. - **`Closes #N` does not survive a post-merge criterion.** Where the issue body says a criterion can only be checked after the merge — a workflow trigger proved live, a released artifact, anything whose subject does not - exist until the change is on the base branch — the same-repo PR says `Refs - #N`; the issue goes `post-merge` at the merge, the builder walks away, and - triage owns verification and closure on the evidence, returning the issue - to `ready` or minting a fresh one where corrective work is needed — - claimable by any builder from current `main`, the original having no - special standing. The issue body says so — you never judge which qualify — - and absent it `Closes #N` is the default (#151). + exist until the change is on the base branch — the same-repo PR says + `Refs #N`; the issue goes `post-merge` at the merge, the builder walks + away, and triage owns verification and closure on the evidence, returning + the issue to `ready` or minting a fresh one where corrective work is + needed — claimable by any builder from current `main`, the original having + no special standing. The issue body says so — you never judge which + qualify — and absent it `Closes #N` is the default (#151). - On a `Refs #N` PR, never put a closing keyword (`close`, `closes`, `closed`, `fix`, `fixes`, `fixed`, `resolve`, `resolves`, `resolved`) immediately before `#N` anywhere in the body, including the sentence From 4800aef363b3342a574d979227fbb4a329c40cb1 Mon Sep 17 00:00:00 2001 From: cndgrr <59120057+cndgrr@users.noreply.github.com> Date: Tue, 4 Aug 2026 13:36:41 +0000 Subject: [PATCH 11/15] docs(builder): restore the re-request deferral and three clauses the audit found --- BUILDER.md | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/BUILDER.md b/BUILDER.md index eb11e9f..a1b8650 100644 --- a/BUILDER.md +++ b/BUILDER.md @@ -157,13 +157,14 @@ such as the panel roster live in that repo's own CONTRIBUTING.) runner outage, a flaky dependency, a failure already on the default branch — and only where the request says so and names the evidence ("the same job fails identically on `origin/main` at ``"); silence about a - red check is what is prohibited. *Green* is a ruled term (operator, - 2026-07-27), read in two steps. **First take the check's word at this - head**: its newest entry by start time — not completion, a cancelled run - outliving its replacement's start — and never a `CANCELLED` entry while - the same check has a non-cancelled one there. A check whose entries at - the head are all cancelled has not reported at all and is not green, the - gate collapsing alike (#139, #276). **Then classify that entry by + red check is what is prohibited, and an argued exception shifts the + burden to the author. *Green* is a ruled term (operator, 2026-07-27), + read in two steps. **First take the check's word at this head**: its + newest entry by start time — not completion, a cancelled run outliving + its replacement's start — and never a `CANCELLED` entry while the same + check has a non-cancelled one there. A check whose entries at the head + are all cancelled has not reported at all and is not green, the gate + collapsing alike (#139, #276). **Then classify that entry by `conclusion`, never `status`**, which can disagree with it (#259). No conclusion is not green: a configured run in progress is waited on, and waiting is compliance, not a stall. Cancelled or stale is not green, @@ -188,7 +189,8 @@ such as the panel roster live in that repo's own CONTRIBUTING.) approvers included**; one left un-re-requested can never approve the tree you shipped (#26, #39). Only where the head did not move — answered with argument or evidence, nothing pushed — do you re-request just the - non-approvers (#94). **The re-request carries the same + non-approvers, the engine absorbing a re-request at an unchanged head — + its mechanism crew's to describe (#94). **The re-request carries the same green-check-at-head precondition**, argued exception included: a fix push whose check comes up red is your next fix, not the panel's. Prefer verification over argument — add the test that settles the doubt. @@ -248,9 +250,11 @@ event**, not its `Default:` deadline or the last activity (#50 D13–D14): - **at 12h:** do not fire a stale default — re-read it against what has landed, and where doubt has appeared, make it a hard block. - **at 24h:** proceed regardless, **as a PR**: pick an option and say in the - body which way you went and what doubt remains. Nothing merges by this. + body which way you went and what doubt remains. Nothing merges by this; + the human still gates the merge. - **past 24h:** hand the choice to triage, which picks the option, records - it as a decision, and stays accountable; the operator can overturn it. + it as a decision, and stays accountable; the operator can overturn it at + merge. A re-flag starts a fresh ladder, which applies whatever `Default:` says, hard block included, and an active back-and-forth still climbs it — unlike @@ -276,4 +280,5 @@ the Round log, mirrored from each whole-round reply. The label write is optimistic — the reconciler validates it and takes it back if the PR is not mergeable-right-now. Then stop: the PR is the human's, and the claim parks as shape 4 (Picking, above), that comment its declaration and your slot -free. Address what comes back (`state:addressing`) and re-hand-off alike. +free. Address what comes back (`state:addressing`) and re-hand-off the same +way. From 43d845575af32e212ef1e9af0ebf1de1a0e0c0e3 Mon Sep 17 00:00:00 2001 From: cndgrr <59120057+cndgrr@users.noreply.github.com> Date: Tue, 4 Aug 2026 13:39:06 +0000 Subject: [PATCH 12/15] docs(builder): state #276's collapse rule in full, not by allusion --- BUILDER.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/BUILDER.md b/BUILDER.md index a1b8650..2aa0929 100644 --- a/BUILDER.md +++ b/BUILDER.md @@ -163,9 +163,11 @@ such as the panel roster live in that repo's own CONTRIBUTING.) newest entry by start time — not completion, a cancelled run outliving its replacement's start — and never a `CANCELLED` entry while the same check has a non-cancelled one there. A check whose entries at the head - are all cancelled has not reported at all and is not green, the gate - collapsing alike (#139, #276). **Then classify that entry by - `conclusion`, never `status`**, which can disagree with it (#259). No + are all cancelled has not reported at all and is not green — a collapse, + not a new class, and the gate partitions alike, dropping a cancelled + entry only where a non-cancelled survivor remains and leaving an + all-cancelled context blocking (#139, #276). **Then classify that entry + by `conclusion`, never `status`**, which can disagree with it (#259). No conclusion is not green: a configured run in progress is waited on, and waiting is compliance, not a stall. Cancelled or stale is not green, *stale* being a superseded head's check, which a head-scoped rollup never From 900963d6539c3d7e68831ad129dce00c349646d4 Mon Sep 17 00:00:00 2001 From: cndgrr <59120057+cndgrr@users.noreply.github.com> Date: Tue, 4 Aug 2026 13:40:08 +0000 Subject: [PATCH 13/15] docs(builder): name the subject in the staleness-sweep clause --- BUILDER.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BUILDER.md b/BUILDER.md index 2aa0929..8c3a35d 100644 --- a/BUILDER.md +++ b/BUILDER.md @@ -53,7 +53,7 @@ triage bug, and the move is to say so on the issue, not to guess. ## Claiming - Assign yourself, swap `ready` → `claimed`, and comment that you are - starting. The claim promises a draft PR soon: one with no PR and no + starting. The claim promises a draft PR soon: a claim with no PR and no activity is what the staleness sweep reclaims, unless `offsite` records that its PR lives in another repo. - **A park is declared, never inferred.** Comment naming what the claim From 49e1fe2e6ef82de15365c5ec317e9f75f6ce0150 Mon Sep 17 00:00:00 2001 From: cndgrr <59120057+cndgrr@users.noreply.github.com> Date: Tue, 4 Aug 2026 13:40:51 +0000 Subject: [PATCH 14/15] docs(builder): give step 1 its paragraph breaks back; fix two doubled connectives --- BUILDER.md | 90 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/BUILDER.md b/BUILDER.md index 8c3a35d..243da93 100644 --- a/BUILDER.md +++ b/BUILDER.md @@ -150,52 +150,56 @@ such as the panel roster live in that repo's own CONTRIBUTING.) That repo's `.github/labels.conf` governs over its CONTRIBUTING roster, being what the state machine reads; where it names no roster, ask triage on the authorizing issue rather than guess. An off-panel reviewer may be - requested, said to be advisory and not required. **A review request - requires a green check at the head**, whether or not an engine enforces - it: a red check is the author's own signal, so fix it and push, then - request. The one exception is a failure genuinely outside the PR — a - runner outage, a flaky dependency, a failure already on the default - branch — and only where the request says so and names the evidence ("the - same job fails identically on `origin/main` at ``"); silence about a - red check is what is prohibited, and an argued exception shifts the - burden to the author. *Green* is a ruled term (operator, 2026-07-27), - read in two steps. **First take the check's word at this head**: its - newest entry by start time — not completion, a cancelled run outliving - its replacement's start — and never a `CANCELLED` entry while the same - check has a non-cancelled one there. A check whose entries at the head - are all cancelled has not reported at all and is not green — a collapse, - not a new class, and the gate partitions alike, dropping a cancelled - entry only where a non-cancelled survivor remains and leaving an - all-cancelled context blocking (#139, #276). **Then classify that entry - by `conclusion`, never `status`**, which can disagree with it (#259). No - conclusion is not green: a configured run in progress is waited on, and - waiting is compliance, not a stall. Cancelled or stale is not green, - *stale* being a superseded head's check, which a head-scoped rollup never - shows. Skipped or neutral is green, those being deliberate "passed / not - applicable" conclusions. No checks configured is green — the third ruled - case, not an argued exception, so the request goes out at once with no - evidence owed; that never covers nothing-answered-yet, and the machine - partitions alike, admitting the ask on `SUCCESS` and `NONE` (#236). The - costs behind the line are asymmetric: a false green spends a - three-reviewer round, a false red one author session. What the machine - drops from the rollup before grading is crew's to describe. + requested, said to be advisory and not required. + + **A review request requires a green check at the head**, whether or not + an engine enforces it: a red check is the author's own signal, so fix it + and push, then request. The one exception is a failure genuinely outside + the PR — a runner outage, a flaky dependency, a failure already on the + default branch — and only where the request says so and names the + evidence ("the same job fails identically on `origin/main` at ``"); + silence about a red check is what is prohibited, and an argued exception + shifts the burden to the author. + + *Green* is a ruled term (operator, 2026-07-27), read in two steps. + **First take the check's word at this head**: its newest entry by start + time — not completion, a cancelled run outliving its replacement's start + — and never a `CANCELLED` entry while the same check has a non-cancelled + one there. A check whose entries at the head are all cancelled has not + reported at all and is not green — a collapse, not a new class, and the + gate partitions alike, dropping a cancelled entry only where a + non-cancelled survivor remains and leaving an all-cancelled context + blocking (#139, #276). **Then classify that entry by `conclusion`, never + `status`**, which can disagree with it (#259). No conclusion is not + green: a configured run in progress is waited on, and waiting is + compliance, not a stall. Cancelled or stale is not green, *stale* being a + superseded head's check, which a head-scoped rollup never shows. Skipped + or neutral is green, those being deliberate "passed / not applicable" + conclusions. No checks configured is green — the third ruled case, not an + argued exception, so the request goes out at once with no evidence owed; + that never covers nothing-answered-yet, and the machine partitions alike, + admitting the ask on `SUCCESS` and `NONE` (#236). The costs behind the + line are asymmetric: a false green spends a three-reviewer round, a false + red one author session. What the machine drops from the rollup before + grading is crew's to describe. 2. **Wait for every verdict, then answer the round whole** — one reply covering every point, stating what changed and what was verified. That reply is the written record: the engine mirrors it under the PR body's - **Round log**, newest last, marked with the round's head so a retry is a - no-op, so you owe the reply and no body edit; a round answered without - one is recorded as such and never blocks handoff. Then push the fixes and - re-request **by head, not by verdict**. A push makes every approval stale - — an approval is of a specific tree, and the handoff predicate counts - only approvals at the current head — so **every panelist is re-requested, - approvers included**; one left un-re-requested can never approve the tree - you shipped (#26, #39). Only where the head did not move — answered with - argument or evidence, nothing pushed — do you re-request just the - non-approvers, the engine absorbing a re-request at an unchanged head — - its mechanism crew's to describe (#94). **The re-request carries the same - green-check-at-head precondition**, argued exception included: a fix push - whose check comes up red is your next fix, not the panel's. Prefer - verification over argument — add the test that settles the doubt. + **Round log**, newest last and marked with the round's head, which makes + a retry a no-op; you owe the reply and no body edit, and a round answered + without one is recorded as such and never blocks handoff. Then push the + fixes and re-request **by head, not by verdict**. A push makes every + approval stale — an approval is of a specific tree, and the handoff + predicate counts only approvals at the current head — so **every panelist + is re-requested, approvers included**; one left un-re-requested can never + approve the tree you shipped (#26, #39). Only where the head did not move + — answered with argument or evidence, nothing pushed — do you re-request + just the non-approvers; the engine absorbs a re-request at an unchanged + head, and its mechanism is crew's to describe (#94). **The re-request + carries the same green-check-at-head precondition**, argued exception + included: a fix push whose check comes up red is your next fix, not the + panel's. Prefer verification over argument — add the test that settles + the doubt. 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; silence and force-forward are not options, and a panel From 92c0e7c6f0189ca7d3e2e88e6152787ea3e0d575 Mon Sep 17 00:00:00 2001 From: cndgrr <59120057+cndgrr@users.noreply.github.com> Date: Tue, 4 Aug 2026 14:13:34 +0000 Subject: [PATCH 15/15] docs(builder): bind the red-head trigger to the round's ruled terms The red-head rule in Picking carried its own trigger definition at the pre-slim head; the squeeze took it, leaving 'failing check' and 'red check' classified only forward in the review round. At an all-cancelled head that let parked shape 2 read satisfied on its face, parking a claim the rule says is never parked (#163, #276). Co-Authored-By: Claude Opus 5 (1M context) --- BUILDER.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/BUILDER.md b/BUILDER.md index 243da93..4429bbc 100644 --- a/BUILDER.md +++ b/BUILDER.md @@ -12,14 +12,16 @@ triage bug, and the move is to say so on the issue, not to guess. the most work; where a repo adopts version epics, [RELEASES.md](RELEASES.md) governs among window members. - **Your own red head outranks a new claim**: repair a failing check at your - PR's head before claiming another issue (#163). Record the check and its - failure class; rerun a clearly retryable infrastructure failure unchanged; - treat a branch failure as an ordinary fix round, worklog and all; leave - evidence where a rerun cannot start or the cause is unclear; never rerun a - deterministic failure without a corrective commit; hand off once green - with current-head approvals. Such a PR is **never parked**, whatever the - verdict state says; how the engine detects a red head is crew's to - describe. + PR's head before claiming another issue (#163). Red and green here are the + review round's ruled terms: cancelled, stale, or unreported — every entry + at the head cancelled — is not green; skipped or neutral is. Record the + check and its failure class; rerun a clearly retryable infrastructure + failure unchanged; treat a branch failure as an ordinary fix round, + worklog and all; leave evidence where a rerun cannot start or the cause is + unclear; never rerun a deterministic failure without a corrective commit; + hand off once green with current-head approvals. Such a PR is **never + parked**, whatever the verdict state says; how the engine detects a red + head is crew's to describe. - **One build at a time**: one issue on which you are writing or revising a deliverable, finished or released before you start more. The rule counts work in flight, not claims — a **parked** claim, whose next move is