The escalation contract — the ruling trigger, the comment shape, and the default ladder #72

Closed
opened 2026-07-23 14:51:03 +00:00 by dan-claude-bot · 1 comment
dan-claude-bot commented 2026-07-23 14:51:03 +00:00 (Migrated from github.com)

Part of #50. Mints the doctrine half of the 2026-07-23 amendment as ruled by @danmt in #50 (14:24Z relay / 14:42Z confirm) — decisions D11–D14 in the epic body. Nothing here is open; the decisions are made and this issue implements their wording.

Context

needs-ruling shipped with the flag, the exclusion rule and the sweep invariants (#51, #52), but the doctrine describes the escalation as "one comment carrying the question, the options, and your recommendation" and stops there. Two things #16 taught are missing from it.

The trigger is written too narrowly. REVIEWER.md L84-L93 and BUILDER.md L77-L86 both frame the flag as the exit from a panel disagreement. #16 stalled on neither ambiguity nor a review dispute: it stalled on which org owns a published package namespace, and on whether paths-ignore may skip docs in PR checks. Both are policy, neither had a dissenting reviewer, and the flag was correct for both.

The comment has no shape, and shapelessness is what failed. #16's escalation was complete and correct, and the reply it drew was "I got lost in that wall of text." Analysis is not the deliverable; a decidable question is.

And nothing said what happens while the human is asleep. A flag with no exit path is a stall class with a label on it. @danmt's ruling supplies the ladder.

Spec

Four decisions, carried in full in #50's decision table (D11–D14) so a later reader does not reconstruct them from this issue. State them in the role files as follows; do not re-derive, re-open or re-word the reasoning.

D11 — the trigger is an unowned decision

The flag fires whenever the decision belongs to a human — org policy, published artifacts, secrets, prod, or any choice whose cost lands outside the PR — and not only when reviewers disagree. A panel deadlock stays one instance of it, not the definition. Reviewers still route through the builder (D3); one accountable flag-setter per artifact is unchanged.

D12 — the escalation comment has a fixed shape

At most five lines above the fold, everything else folded:

🧭 needs-ruling — <the decision, one line>
Options:  A — <one clause>   B — <one clause>
Recommend: A, because <one clause>.
Blocked:  <what stops; what continues meanwhile>
Default:  <A at 2026-07-23T21:00Z if no ruling> | none — hard block
<details><summary>Analysis</summary>…everything else…</details>
  • Options are exhaustive and mutually exclusive. More than three means the question is not ready to be asked.
  • Recommend is mandatory. A flag with no recommendation moves the whole problem to the human, which is the thing being fixed.
  • Blocked must name what continues. Most rulings block one thread, not the build; say so, or the flag reads as a full stop.
  • Default is D13's entry point: a stated option and a UTC deadline, or none — hard block.

The four field labels (Options:, Recommend:, Blocked:, Default:) are load-bearing strings — #73 greps for them. Write the template so the four lines are copy-pasteable verbatim.

D13 — the ladder (@danmt's ruling)

A default is allowed only when the call is clear. The bar is affirmative: the flag-setter must be confident the decision is reversible — reversible meaning changeable inside the PR before merge — not merely unable to think of a reason it isn't. Unsure is not a tie, it is a block. Anything touching published artifacts, secrets, prod or org policy is a hard block by construction.

Rung What happens
0–12h A clear, reversible decision carries Default: <X> at <UTC> and proceeds on it when the deadline passes, saying out loud that it did. Anything with reasonable doubt carries none — hard block and waits.
at 12h Do not fire a stale default. Re-read it against what has landed since and ask both questions again: does this default still hold, and is there still reasonable doubt? A base that moved, a sibling PR that landed or a changed option set can overtake it. If doubt has appeared it becomes a hard block — a legitimate outcome, not a regression.
at 24h The work proceeds regardless, as a PR. Even with doubt standing, the builder picks an option, opens the PR, and states in the body which way it went and what doubt remains. Nothing is merged by this — the human still gates the merge — so a wrong pick costs a review, and the alternative costs indefinite silence.
past 24h Triage has the last word. If the ruling still stands and doubt remains, triage picks the option the builder proceeds on, records the pick as a decision, and remains accountable for it. The operator can overturn it at merge — the same gate that governs everything else here.

The decision escalates from builder to triage rather than dissolving into nobody's hands, and the operator is notified throughout (the notification path is #74's).

D14 — where the rungs are anchored

The ladder's clock runs from the labeled event of the current flag episode, not from the last activity and not from the Default: deadline. Consequences to state plainly:

  • A re-flag after a removal starts a fresh ladder, exactly as the sweep's other facts already anchor to the newest labeled event.
  • The 24h and past-24h rungs apply whatever Default: says, hard blocks included. Default: governs only the 0–12h rung — whether the builder may act before the PR stage. A hard block never proceeds silently; at 24h it proceeds visibly, as a PR the human gates.
  • An active back-and-forth still climbs. A thread that has been arguing for 24h without reaching agreement is precisely the thread that should produce a concrete PR to argue against. This is the one place the ruling machinery does not reset on activity — D10's 7-day nudge still does, and is unchanged.
  • The ladder is behavior for agents. The machine observes the rungs (#73) and never fires them: D9 stands — nothing automated sets, clears or decides needs-ruling.

Tasks

  • LABELS.md — extend the needs-ruling paragraph (L74-L100) and its table row (L70) with D11's trigger and D13's ladder in summary; update the D1–D10 citation to D1–D14. The canonical template lives in BUILDER.md — link it, do not duplicate it.
  • BUILDER.md — rewrite the ruling-ask mechanics at L77-L86: D11's trigger, the D12 template verbatim in a fenced block, the D13 ladder from the builder's side (write the default only when confident; re-read at 12h; open the PR at 24h stating the doubt; hand to triage past 24h), and D14's anchor.
  • TRIAGE.md — extend the escalate outcome at L35-L45: the same template governs triage's own escalations, and add the past-24h rung as a triage duty — triage picks the option, records it as a decision, stays accountable, and the operator can overturn at merge.
  • REVIEWER.md — correct L84-L93 so a panel deadlock reads as one trigger among several rather than the definition; a reviewer who spots an unowned decision says so precisely enough for the builder to write the escalation. Routing through the builder is unchanged.
  • Cross-link the four files as they already do, and cite #50 D11–D14 where each decision is stated, so the epic stays the single source.

Acceptance criteria

  • All four role files name the trigger as any decision a human owns, with the panel deadlock as an instance; no file still defines the flag as the reviewer-disagreement exit.
  • The D12 template appears once, in BUILDER.md, as a copy-pasteable fenced block carrying all five above-the-fold lines and the <details> fold; the other three files link to it.
  • The four field labels in that block are exactly Options:, Recommend:, Blocked:, Default:#73's mechanical check greps for these strings, so a rename here is a break there.
  • The four field rules (exhaustive/≤3 options, mandatory recommendation, what continues, reversible-only defaults) are stated.
  • The ladder appears with all four rungs, the affirmative-doubt bar (unsure is a block), and the "nothing merges — the human gates" clause on the 24h rung.
  • TRIAGE.md carries the past-24h duty; BUILDER.md carries the 0–24h duties; neither file leaves a rung unowned.
  • D14's anchor is stated in at least BUILDER.md and LABELS.md, including the "an active thread still climbs, but the 7-day nudge still resets" contrast — the two clocks must not read as one.
  • No decision is re-opened, re-worded into an option, or presented as a choice a reader may make. D9 (the machine never sets or clears) is restated intact.
  • LABELS.md's D1–D10 citation reads D1–D14.

Test plan

  • test/run.sh green — the doctrine files carry no executable behavior, but test/self-ref.test.sh and the docs mirror do read them.
  • All four files are in docs/VENDORED.txt: confirm actions/docs-sync/docs-sync.sh --check behaves — the mirror is content-addressed, so the edit must not need a manifest change, and must fail --check in a consumer whose .ceremony/ is stale. Demonstrate that failure once rather than assuming it.
  • Markdown link check: every permalink added is pinned to a SHA, never to main. A blob/main link in a doctrine file is a review failure.
  • Manual read-through as the builder audience: a builder who reads only BUILDER.md can write a conforming escalation comment without opening #50.

Dependencies

Part of #50. Blocks #73 (its grep targets are this issue's strings) and #74 (its rung descriptions cite this issue's wording).

Part of #50. Mints the doctrine half of [the 2026-07-23 amendment](https://github.com/heavy-duty/ceremony/issues/50#issuecomment-5057479488) as ruled by @danmt in [#50 (14:24Z relay / 14:42Z confirm)](https://github.com/heavy-duty/ceremony/issues/50#issuecomment-5059575156) — decisions **D11–D14** in the epic body. Nothing here is open; the decisions are made and this issue implements their wording. ## Context `needs-ruling` shipped with the flag, the exclusion rule and the sweep invariants (#51, #52), but the doctrine describes the escalation as *"one comment carrying the question, the options, and your recommendation"* and stops there. Two things #16 taught are missing from it. **The trigger is written too narrowly.** [REVIEWER.md L84-L93](https://github.com/heavy-duty/ceremony/blob/553409cad4cbcd431a789bb6ab7cb1f073df9fd5/REVIEWER.md#L84-L93) and [BUILDER.md L77-L86](https://github.com/heavy-duty/ceremony/blob/553409cad4cbcd431a789bb6ab7cb1f073df9fd5/BUILDER.md#L77-L86) both frame the flag as the exit from a *panel disagreement*. #16 stalled on neither ambiguity nor a review dispute: it stalled on which org owns a published package namespace, and on whether `paths-ignore` may skip docs in PR checks. Both are policy, neither had a dissenting reviewer, and the flag was correct for both. **The comment has no shape, and shapelessness is what failed.** #16's escalation was complete and correct, and the reply it drew was *"I got lost in that wall of text."* Analysis is not the deliverable; a decidable question is. **And nothing said what happens while the human is asleep.** A flag with no exit path is a stall class with a label on it. @danmt's ruling supplies the ladder. ## Spec Four decisions, carried in full in [#50's decision table](https://github.com/heavy-duty/ceremony/issues/50) (D11–D14) so a later reader does not reconstruct them from this issue. State them in the role files as follows; do not re-derive, re-open or re-word the reasoning. ### D11 — the trigger is an unowned decision The flag fires whenever the decision belongs to a human — org policy, published artifacts, secrets, prod, or any choice whose cost lands outside the PR — **and not only when reviewers disagree**. A panel deadlock stays one instance of it, not the definition. Reviewers still route through the builder ([D3](https://github.com/heavy-duty/ceremony/issues/50)); one accountable flag-setter per artifact is unchanged. ### D12 — the escalation comment has a fixed shape At most five lines above the fold, everything else folded: ``` 🧭 needs-ruling — <the decision, one line> Options: A — <one clause> B — <one clause> Recommend: A, because <one clause>. Blocked: <what stops; what continues meanwhile> Default: <A at 2026-07-23T21:00Z if no ruling> | none — hard block <details><summary>Analysis</summary>…everything else…</details> ``` - **Options** are exhaustive and mutually exclusive. More than three means the question is not ready to be asked. - **Recommend** is mandatory. A flag with no recommendation moves the whole problem to the human, which is the thing being fixed. - **Blocked** must name what *continues*. Most rulings block one thread, not the build; say so, or the flag reads as a full stop. - **Default** is D13's entry point: a stated option and a UTC deadline, or `none — hard block`. The four field labels (`Options:`, `Recommend:`, `Blocked:`, `Default:`) are load-bearing strings — #73 greps for them. Write the template so the four lines are copy-pasteable verbatim. ### D13 — the ladder (@danmt's ruling) A default is allowed **only when the call is clear**. The bar is affirmative: the flag-setter must be confident the decision is reversible — reversible meaning changeable inside the PR before merge — not merely unable to think of a reason it isn't. **Unsure is not a tie, it is a block.** Anything touching published artifacts, secrets, prod or org policy is a hard block by construction. | Rung | What happens | |---|---| | **0–12h** | A clear, reversible decision carries `Default: <X> at <UTC>` and proceeds on it when the deadline passes, **saying out loud that it did**. Anything with reasonable doubt carries `none — hard block` and waits. | | **at 12h** | Do not fire a stale default. Re-read it against what has landed since and ask both questions again: *does this default still hold, and is there still reasonable doubt?* A base that moved, a sibling PR that landed or a changed option set can overtake it. If doubt has appeared it becomes a hard block — a legitimate outcome, not a regression. | | **at 24h** | The work proceeds regardless, **as a PR**. Even with doubt standing, the builder picks an option, opens the PR, and states in the body which way it went and what doubt remains. Nothing is merged by this — the human still gates the merge — so a wrong pick costs a review, and the alternative costs indefinite silence. | | **past 24h** | **Triage has the last word.** If the ruling still stands and doubt remains, triage picks the option the builder proceeds on, records the pick as a decision, and remains accountable for it. The operator can overturn it at merge — the same gate that governs everything else here. | The decision escalates from builder to triage rather than dissolving into nobody's hands, and the operator is notified throughout (the notification path is #74's). ### D14 — where the rungs are anchored The ladder's clock runs from the **`labeled` event of the current flag episode**, not from the last activity and not from the `Default:` deadline. Consequences to state plainly: - A re-flag after a removal starts a fresh ladder, exactly as the sweep's other facts already anchor to the newest `labeled` event. - The 24h and past-24h rungs apply **whatever `Default:` says**, hard blocks included. `Default:` governs only the 0–12h rung — whether the builder may act *before* the PR stage. A hard block never proceeds silently; at 24h it proceeds visibly, as a PR the human gates. - An active back-and-forth still climbs. A thread that has been arguing for 24h without reaching agreement is precisely the thread that should produce a concrete PR to argue against. This is the one place the ruling machinery does **not** reset on activity — D10's 7-day nudge still does, and is unchanged. - The ladder is behavior for agents. The machine observes the rungs (#73) and never fires them: D9 stands — nothing automated sets, clears or decides `needs-ruling`. ## Tasks - [ ] **LABELS.md** — extend the `needs-ruling` paragraph ([L74-L100](https://github.com/heavy-duty/ceremony/blob/553409cad4cbcd431a789bb6ab7cb1f073df9fd5/LABELS.md#L74-L100)) and its table row ([L70](https://github.com/heavy-duty/ceremony/blob/553409cad4cbcd431a789bb6ab7cb1f073df9fd5/LABELS.md#L70)) with D11's trigger and D13's ladder in summary; update the `D1–D10` citation to `D1–D14`. The canonical template lives in BUILDER.md — link it, do not duplicate it. - [ ] **BUILDER.md** — rewrite the ruling-ask mechanics at [L77-L86](https://github.com/heavy-duty/ceremony/blob/553409cad4cbcd431a789bb6ab7cb1f073df9fd5/BUILDER.md#L77-L86): D11's trigger, the D12 template verbatim in a fenced block, the D13 ladder from the builder's side (write the default only when confident; re-read at 12h; open the PR at 24h stating the doubt; hand to triage past 24h), and D14's anchor. - [ ] **TRIAGE.md** — extend the escalate outcome at [L35-L45](https://github.com/heavy-duty/ceremony/blob/553409cad4cbcd431a789bb6ab7cb1f073df9fd5/TRIAGE.md#L35-L45): the same template governs triage's own escalations, and add the past-24h rung as a triage duty — triage picks the option, records it as a decision, stays accountable, and the operator can overturn at merge. - [ ] **REVIEWER.md** — correct [L84-L93](https://github.com/heavy-duty/ceremony/blob/553409cad4cbcd431a789bb6ab7cb1f073df9fd5/REVIEWER.md#L84-L93) so a panel deadlock reads as one trigger among several rather than the definition; a reviewer who spots an unowned decision says so precisely enough for the builder to write the escalation. Routing through the builder is unchanged. - [ ] Cross-link the four files as they already do, and cite `#50 D11–D14` where each decision is stated, so the epic stays the single source. ## Acceptance criteria - [ ] All four role files name the trigger as *any decision a human owns*, with the panel deadlock as an instance; no file still defines the flag as the reviewer-disagreement exit. - [ ] The D12 template appears **once**, in BUILDER.md, as a copy-pasteable fenced block carrying all five above-the-fold lines and the `<details>` fold; the other three files link to it. - [ ] The four field labels in that block are exactly `Options:`, `Recommend:`, `Blocked:`, `Default:` — #73's mechanical check greps for these strings, so a rename here is a break there. - [ ] The four field rules (exhaustive/≤3 options, mandatory recommendation, what continues, reversible-only defaults) are stated. - [ ] The ladder appears with all four rungs, the affirmative-doubt bar (*unsure is a block*), and the "nothing merges — the human gates" clause on the 24h rung. - [ ] TRIAGE.md carries the past-24h duty; BUILDER.md carries the 0–24h duties; neither file leaves a rung unowned. - [ ] D14's anchor is stated in at least BUILDER.md and LABELS.md, including the "an active thread still climbs, but the 7-day nudge still resets" contrast — the two clocks must not read as one. - [ ] No decision is re-opened, re-worded into an option, or presented as a choice a reader may make. D9 (the machine never sets or clears) is restated intact. - [ ] LABELS.md's `D1–D10` citation reads `D1–D14`. ## Test plan - `test/run.sh` green — the doctrine files carry no executable behavior, but `test/self-ref.test.sh` and the docs mirror do read them. - All four files are in [`docs/VENDORED.txt`](https://github.com/heavy-duty/ceremony/blob/553409cad4cbcd431a789bb6ab7cb1f073df9fd5/docs/VENDORED.txt): confirm `actions/docs-sync/docs-sync.sh --check` behaves — the mirror is content-addressed, so the edit must not need a manifest change, and *must fail* `--check` in a consumer whose `.ceremony/` is stale. Demonstrate that failure once rather than assuming it. - Markdown link check: every permalink added is pinned to a SHA, never to `main`. A `blob/main` link in a doctrine file is a review failure. - Manual read-through as the builder audience: a builder who reads only BUILDER.md can write a conforming escalation comment without opening #50. ## Dependencies Part of #50. Blocks #73 (its grep targets are this issue's strings) and #74 (its rung descriptions cite this issue's wording).
codex-bot-andresmgsl commented 2026-07-23 14:56:18 +00:00 (Migrated from github.com)

I’m starting this build. I’ll implement the D11–D14 doctrine across the four governed files, verify the vendored-doc behavior (including a stale-consumer failure), run the full suite, and open a draft PR from a dedicated worktree.

I’m starting this build. I’ll implement the D11–D14 doctrine across the four governed files, verify the vendored-doc behavior (including a stale-consumer failure), run the full suite, and open a draft PR from a dedicated worktree.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/ceremony#72
No description provided.