From b4f9f8c3fef85c83be56a96ccd73750c42550e9a Mon Sep 17 00:00:00 2001 From: Scott Hall Date: Mon, 26 Oct 2026 11:58:07 -0500 Subject: [PATCH 1/6] MANAGER.md: brief at full speed; retire the brief-NOTHING WIP limit Owner ruling 2026-10-26, in session: a work-in-progress limit keyed to the Lander's merge rate "is NEVER a thing in our processes". Builders and Managers work at full speed, Managers batch into combined PRs, a merge queue develops, and the Lander works it down. - Section 2a: retire "Worked example, 2026-09-05, on when to brief NOTHING", quote what it said, state the ruling and the rule now. Keep the 2026-09-05 figures as a reading table, not a dispatch input. - Standing rules: add "Brief at full speed (owner ruling 2026-10-26)". - Section 2: drop "or Clog the Queue" from the heading, with a dated CORRECTED note. - Section 2a table: rename "Verify the remote before sizing a wave" so the open-PR count is not a dispatch input; keep the wrong-repo lesson. Proposed PR title: MANAGER.md: brief at full speed, retire the brief-NOTHING WIP limit (owner ruling 2026-10-26) --- roles/MANAGER.md | 47 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/roles/MANAGER.md b/roles/MANAGER.md index a7cc68a..a5d791c 100644 --- a/roles/MANAGER.md +++ b/roles/MANAGER.md @@ -29,6 +29,7 @@ Within the limits of the following rules, you SHOULD ALWAYS BE PROACTIVE IN YOUR | **RETIRED 2026-09-18: the pool check before opening a pull request** | Owner ruling. **Open the pull request when your own work is ready**, then tell the Lander. *Do not check the pool before you open* carries why. | | What that row read | *"Do not create more PRs than the Lander can handle. Find the Lander and communicate with it before creating a PR."* | | You still do not manage the merge queue or the repository | That half of the retired row survives. Leave both to the Lander. | +| **Brief at full speed (owner ruling 2026-10-26)** | Never limit briefing by the open pull request count, the queue's depth, or the Lander's merge rate. Batch into combined pull requests; the Lander works the queue down. Section 2a carries the ruling. | | Every brief ends with push, report, exit. | Do not lose work. Subagents die when you do, so unpushed work is destroyed silently. See *Your work has to survive your exit*. | | You do not edit another Manager's worktree, or the primary checkout | *Never Do These* carries it, with what would end it. | | Conflicts between this file and COMMON | **Raise it to the owner,** unless a recorded delegated decision already resolves it. No seat picks a winner. [COMMON.md](COMMON.md), *Where a role playbook and this file disagree, the owner decides*, carries the 2026-08-28 owner ruling verbatim. | @@ -365,11 +366,15 @@ What bound that run was the repository. See, in the constitution: --- -## 2. Never Conflict with the Lander or Clog the Queue +## 2. Never Conflict with the Lander -**The queue half of this heading narrowed on 2026-09-18.** You no longer hold a pull request back on -what the queue looks like. See *Do not check the pool before you open*. What survives is granularity, -decided at dispatch in section 2a and at step 9 in *When to cut a pull request*. +**CORRECTED 2026-10-26 by owner ruling.** This heading read *"Never Conflict with the Lander or Clog +the Queue"*. A queue of open pull requests is expected, and the Lander works it down. Section 2a, +under its RETIRED 2026-10-26 note, carries the ruling. + +**The queue half of the old heading narrowed on 2026-09-18.** You no longer hold a pull request +back on what the queue looks like. See *Do not check the pool before you open*. What survives is +granularity, decided at dispatch in section 2a and at step 9 in *When to cut a pull request*. **An engine pull request no longer edits the ledger.** The ledger moved to the vault on 2026-09-13 (BACKLOG #1250). The Lander writes each banner there after the merge, from the text in the Builder's @@ -459,23 +464,39 @@ carried here rather than re-derived by this seat. | The worked example | PR 832 spread its `docs/BACKLOG.md` edits through code commits and needed a hand-resolved merge. | | Announce the files the pull requests will CHANGE, never the items' SUBJECTS | A dispatch announce naming `auth/service.py`, `api/app.py` and `config/settings.py` was read downstream as a collision forecast. The pull requests touched `docs/BACKLOG.md` and one test file; those paths were what the items were ABOUT. | | So, for a research wave | The changed file is almost always the ledger alone. Say that. | -| Verify the remote before sizing a wave | `gh` answers plausibly against the wrong repository rather than failing. | +| Verify the remote before you act on a `gh` read | `gh` answers plausibly against the wrong repository rather than failing. | | The measured case | A Manager ran `gh pr list` against the vault, read **1** open pull request, and sized a ten-subagent dispatch on it. The engine had **38**. | +| **CORRECTED 2026-10-26 by owner ruling** | The row *Verify the remote before you act on a `gh` read* was named *"Verify the remote before sizing a wave"*. The open pull request count is not a dispatch input. Size a wave by its work. | | So | Pass `--repo MEFORORG/MessageFoundry` explicitly on anything you will act on. The vault was renamed to `wshallwshall/MessageFoundry-vault` the same day and the old slug STILL REDIRECTS, so a stale `--repo wshallwshall/MessageFoundry` still answers, and answers about the vault. | | **CORRECTED 2026-09-24: the vault's slug is `MEFORORG/MessageFoundry-vault`** | The row above keeps what it said. `gh repo view --json nameWithOwner` in the vault checkout returns `MEFORORG/MessageFoundry-vault`, and `wshallwshall/MessageFoundry-vault` redirects there too. The advice holds: name the repository with `--repo`. | | **EXPIRY** | This holds only while the ledger is one file whose new rows land at one tail, and while conflict resolution is serial and manual. | | How to check the expiry | Re-examine it if `docs/BACKLOG.md` is split, if a merge driver is adopted for it, or if rows stop being appended in number order. | -**Worked example, 2026-09-05, on when to brief NOTHING.** 62 open non-draft pull requests against a -merge rate of 2.2 to 2.7 an hour. 29 opened in one three-hour window, or 9.7 an hour. That is a -utilisation of 3.6 to 5.0. +**RETIRED 2026-10-26 by owner ruling: the worked example on when to brief NOTHING.** It set a +work-in-progress limit keyed to the Lander's merge rate. It ended: *"At that point briefing anything +new subtracts from the fleet's throughput."* + +The owner's words, in session: *"This is NEVER a thing in our processes. Builders and managers work +at full speed. Managers batch work into combined PRs as much as possible. A merge queue develops. +The lander works it down."* -**Above 1.0 a queue does not settle at some depth. It grows without bound.** Median age at merge was -already 15 hours and the oldest open item was two days. **At that point briefing anything new -subtracts from the fleet's throughput.** +**The rule now: brief at full speed.** Never hold a brief or shrink a wave for the open pull request +count, the queue's depth, or the Lander's merge rate. Batch each wave into combined pull requests, +by *When to cut a pull request*. The Lander works the queue down. -**Why this is a Manager rule and not a Builder one.** A Builder cannot batch its own output: it is -dispatched against one item and its process ends before any pull request exists. +**The 2026-09-05 figures stay as a reading, not a rule.** They are not a dispatch input. + +| Reading, 2026-09-05 | Value | +| --- | --- | +| Open non-draft pull requests | 62 | +| Merge rate | 2.2 to 2.7 an hour | +| Opened | 29 in one three-hour window, or 9.7 an hour | +| Utilisation | 3.6 to 5.0 | +| Median age at merge | 15 hours | +| Oldest open item | Two days | + +**Why batching is a Manager rule and not a Builder one.** A Builder cannot batch its own output: it +is dispatched against one item and its process ends before any pull request exists. **Granularity is decided at dispatch and at step 9, and nowhere else.** A convention written into a Builder brief binds nobody after the fact, because there is nobody left to bind. From 873b1f9e012fdcfc96de1eb06a9bfe1110e3f392 Mon Sep 17 00:00:00 2001 From: Scott Hall Date: Mon, 26 Oct 2026 12:12:10 -0500 Subject: [PATCH 2/6] MANAGER.md: move the full-speed ruling beside the pool check; review fixes Code-review round 1 (xhigh, round-2f04017e) fixes: - Make *Do not check the pool before you open* the source of record for the 2026-10-26 ruling; section 2a was scoped to vault ledger waves. Standing row and section 2 note now point there by heading name. - State that the usage hold in COMMON and *When to cut a pull request* still bind. - Keep the retired "Above 1.0 ... grows without bound" sentence quoted; mark the 3.6 to 5.0 utilisation as derived (9.7 / 2.2 gives 4.4). - Correct the "queue still diverges" / "heavy oversubscription" rows. - Retire section 3's "If a batch cannot land today, do not file the rows today", a hold on the merge rate. - Move the 2a gh-remote CORRECTED row below the rows it follows. - fleet-spawn-workers: repoint a row that cited a MANAGER.md heading that does not exist, and say the merge queue never bounds a fan-out. --- .claude/skills/fleet-spawn-workers/SKILL.md | 4 +- roles/MANAGER.md | 47 +++++++++++++-------- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/.claude/skills/fleet-spawn-workers/SKILL.md b/.claude/skills/fleet-spawn-workers/SKILL.md index a17fbc8..fff03ad 100644 --- a/.claude/skills/fleet-spawn-workers/SKILL.md +++ b/.claude/skills/fleet-spawn-workers/SKILL.md @@ -98,6 +98,8 @@ bound.** These rows are the part that does not. | The outside ladder is a shape to copy, not a size to adopt | Anthropic's write-up of its multi-agent research system keeps the sizing rule in the orchestrator. It calls the lack of one a common failure mode, and records fifty subagents spawned for one simple query. | | Its integers do not come with it | The ladder runs from one agent for simple fact-finding, through two to four for a direct comparison, to ten or more for complex research. | | The condition that stops them transferring | **Those were measured on research tasks, and the same post says coding parallelises worse.** No number in that ladder has been measured on this fleet. | -| What bounds YOUR number is in your own playbook | [MANAGER.md](../../../roles/MANAGER.md), *A Manager's fan-out is bounded by one window and by shared runners*. The retired [CONSOLE.md](../../../roles/retired/CONSOLE.md) bounded a fan-out by accounts and by what the Lander could land; that is record, not a live bound. | +| What bounds YOUR number | The work and your account's usage, never the merge queue. [MANAGER.md](../../../roles/MANAGER.md), *Do not check the pool before you open*, carries the 2026-10-26 owner ruling. | +| **CORRECTED 2026-10-26** | This row cited *"A Manager's fan-out is bounded by one window and by shared runners"*, a MANAGER.md heading that does not exist. | +| The retired bound | [CONSOLE.md](../../../roles/retired/CONSOLE.md) bounded a fan-out by accounts and by what the Lander could land. That is record, not a live bound. | --- diff --git a/roles/MANAGER.md b/roles/MANAGER.md index a5d791c..b818355 100644 --- a/roles/MANAGER.md +++ b/roles/MANAGER.md @@ -29,7 +29,7 @@ Within the limits of the following rules, you SHOULD ALWAYS BE PROACTIVE IN YOUR | **RETIRED 2026-09-18: the pool check before opening a pull request** | Owner ruling. **Open the pull request when your own work is ready**, then tell the Lander. *Do not check the pool before you open* carries why. | | What that row read | *"Do not create more PRs than the Lander can handle. Find the Lander and communicate with it before creating a PR."* | | You still do not manage the merge queue or the repository | That half of the retired row survives. Leave both to the Lander. | -| **Brief at full speed (owner ruling 2026-10-26)** | Never limit briefing by the open pull request count, the queue's depth, or the Lander's merge rate. Batch into combined pull requests; the Lander works the queue down. Section 2a carries the ruling. | +| **Brief at full speed (owner ruling 2026-10-26)** | The merge queue never limits briefing. *Do not check the pool before you open* carries the ruling. | | Every brief ends with push, report, exit. | Do not lose work. Subagents die when you do, so unpushed work is destroyed silently. See *Your work has to survive your exit*. | | You do not edit another Manager's worktree, or the primary checkout | *Never Do These* carries it, with what would end it. | | Conflicts between this file and COMMON | **Raise it to the owner,** unless a recorded delegated decision already resolves it. No seat picks a winner. [COMMON.md](COMMON.md), *Where a role playbook and this file disagree, the owner decides*, carries the 2026-08-28 owner ruling verbatim. | @@ -150,6 +150,18 @@ result and requires it be reported, so "nothing found" and "nothing said" are di it has room, and do not hold a finished branch for a quiet window. Holding one for its own wave is different, and bounded: *When to cut a pull request*. +**Nor before you brief: brief at full speed (owner ruling 2026-10-26).** Never hold a brief or +shrink a wave for the open pull request count, the merge queue's depth, or the Lander's merge rate. +Batch each wave into combined pull requests. A queue forms, and the Lander works it down. + +The owner's words, in session: *"This is NEVER a thing in our processes. Builders and managers work +at full speed. Managers batch work into combined PRs as much as possible. A merge queue develops. +The lander works it down."* + +**The ruling is about the queue, not quota.** The usage hold in [COMMON.md](COMMON.md), +*Prohibitions that bind before any task starts*, still binds. *When to cut a pull request* still +sets how a batch is cut. + **Why the pool check was retired 2026-09-18.** Five Managers reading one shared pool all read "clear" at the same moment and all open together. The check manufactures the burst it exists to prevent, because none of the five can see the other four deciding. @@ -369,8 +381,8 @@ What bound that run was the repository. See, in the constitution: ## 2. Never Conflict with the Lander **CORRECTED 2026-10-26 by owner ruling.** This heading read *"Never Conflict with the Lander or Clog -the Queue"*. A queue of open pull requests is expected, and the Lander works it down. Section 2a, -under its RETIRED 2026-10-26 note, carries the ruling. +the Queue"*. A queue of open pull requests is expected, and the Lander works it down. *Do not check +the pool before you open* carries the ruling. **The queue half of the old heading narrowed on 2026-09-18.** You no longer hold a pull request back on what the queue looks like. See *Do not check the pool before you open*. What survives is @@ -456,6 +468,7 @@ carried here rather than re-derived by this seat. | **SUPERSEDED 2026-09-13** | Banners no longer ride with code: the Lander writes them in the vault after the merge. So those 36 no longer touch the ledger at all. | | So | Collapsing the 12 moves arrivals from about 9.7/hour to 7.7/hour. **The queue still diverges.** Worth doing, and not a queue fix. | | And batching CORRELATES FAILURE | One red check or one conflict blocks every item in the wave, where a bad row today blocks only itself. At heavy oversubscription that is a real trade. | +| **CORRECTED 2026-10-26 by owner ruling** | *"The queue still diverges"* and *"heavy oversubscription"* treat a growing queue as a Manager's problem. It is not: brief at full speed and batch, and the Lander works the queue down. The failure-correlation trade stands. | | The measurement | Two dispatch waves added 17 pull requests in about 35 minutes. Open non-draft went 35 to 54 in one hour, and **zero** merged in it. **24 of the 54 were DIRTY**, overwhelmingly on the ledger tail. | | What those seven actually were | 13 to 69 lines each, of `docs/BACKLOG.md` only. They could have been one pull request. | | If an item must be its own pull request | The ledger edit is a FINAL COMMIT, ALONE. Section 2 states that rule, under *put your ledger row in its OWN commit, LAST*. | @@ -466,23 +479,18 @@ carried here rather than re-derived by this seat. | So, for a research wave | The changed file is almost always the ledger alone. Say that. | | Verify the remote before you act on a `gh` read | `gh` answers plausibly against the wrong repository rather than failing. | | The measured case | A Manager ran `gh pr list` against the vault, read **1** open pull request, and sized a ten-subagent dispatch on it. The engine had **38**. | -| **CORRECTED 2026-10-26 by owner ruling** | The row *Verify the remote before you act on a `gh` read* was named *"Verify the remote before sizing a wave"*. The open pull request count is not a dispatch input. Size a wave by its work. | | So | Pass `--repo MEFORORG/MessageFoundry` explicitly on anything you will act on. The vault was renamed to `wshallwshall/MessageFoundry-vault` the same day and the old slug STILL REDIRECTS, so a stale `--repo wshallwshall/MessageFoundry` still answers, and answers about the vault. | | **CORRECTED 2026-09-24: the vault's slug is `MEFORORG/MessageFoundry-vault`** | The row above keeps what it said. `gh repo view --json nameWithOwner` in the vault checkout returns `MEFORORG/MessageFoundry-vault`, and `wshallwshall/MessageFoundry-vault` redirects there too. The advice holds: name the repository with `--repo`. | +| **CORRECTED 2026-10-26 by owner ruling** | The row *Verify the remote before you act on a `gh` read* was named *"Verify the remote before sizing a wave"*. The open pull request count is not a dispatch input. The measured case's error was the wrong repository. | | **EXPIRY** | This holds only while the ledger is one file whose new rows land at one tail, and while conflict resolution is serial and manual. | | How to check the expiry | Re-examine it if `docs/BACKLOG.md` is split, if a merge driver is adopted for it, or if rows stop being appended in number order. | **RETIRED 2026-10-26 by owner ruling: the worked example on when to brief NOTHING.** It set a -work-in-progress limit keyed to the Lander's merge rate. It ended: *"At that point briefing anything -new subtracts from the fleet's throughput."* +work-in-progress limit keyed to the Lander's merge rate. *Do not check the pool before you open* +carries the ruling and the rule now. -The owner's words, in session: *"This is NEVER a thing in our processes. Builders and managers work -at full speed. Managers batch work into combined PRs as much as possible. A merge queue develops. -The lander works it down."* - -**The rule now: brief at full speed.** Never hold a brief or shrink a wave for the open pull request -count, the queue's depth, or the Lander's merge rate. Batch each wave into combined pull requests, -by *When to cut a pull request*. The Lander works the queue down. +It reasoned: *"Above 1.0 a queue does not settle at some depth. It grows without bound."* It ended: +*"At that point briefing anything new subtracts from the fleet's throughput."* **The 2026-09-05 figures stay as a reading, not a rule.** They are not a dispatch input. @@ -491,7 +499,7 @@ by *When to cut a pull request*. The Lander works the queue down. | Open non-draft pull requests | 62 | | Merge rate | 2.2 to 2.7 an hour | | Opened | 29 in one three-hour window, or 9.7 an hour | -| Utilisation | 3.6 to 5.0 | +| Utilisation, as the retired text gave it | 3.6 to 5.0. Derived, not read; 9.7 over 2.2 gives 4.4. | | Median age at merge | 15 hours | | Oldest open item | Two days | @@ -539,11 +547,14 @@ were dirty. Of those trailing by one or none, 0 of 10 were. No misclassification The dirty five were 25 to 28 hours old and the clean ten were under two. The fleet lands a ledger commit about every 35 minutes, so a pull request that waits overnight wakes behind 30 of them. -**So do not cut a ledger row you cannot land soon.** A row landing within about two hours collides -with nothing; the same row a day later collides with everything. If a batch cannot land today, do not -file the rows today. +A row landing within about two hours collides with nothing; the same row a day later collides with +everything. + +**RETIRED 2026-10-26 by owner ruling.** This read *"So do not cut a ledger row you cannot land +soon"*. It added *"If a batch cannot land today, do not file the rows today"*. File rows when the +work is ready, batched into one pull request. The Lander works the queue down, conflicts included. -**Nothing enforces this today.** The claim registry covers items. Until it covers paths, this is +**Nothing enforces the path check today.** The claim registry covers items. Until it covers paths, this is yours to do by reading, and a brief that names no paths has skipped it rather than passed it. **A claim on a whole batch is necessary, and it is not sufficient.** Take the batch-level claim From cbf36b3f40c8f0d826bfa00ae6272637c30d30e6 Mon Sep 17 00:00:00 2001 From: Scott Hall Date: Mon, 26 Oct 2026 12:35:27 -0500 Subject: [PATCH 3/6] MANAGER.md: give the full-speed ruling its own heading; round-2 fixes Code-review round 2 (xhigh, round-29f511a1) fixes: - New heading *Brief at full speed* is the source of record; every pointer now names it rather than the pool-check heading. - Name the real usage holds that still bind: STEWARD.md 3g HOLD NEW WORK and the 90 percent Workflow gate in COMMON. - Quote what the owner's "This" referred to. - Record the five-item-cut tension as an open owner question rather than settling it. - Keep the original 2a row label; the correction row says sizing on an open-PR count is retired on any repository. - Utilisation: 3.6 to 4.4 from the table's own inputs; 5.0 had no instrument. - Section 3 ledger-row retirement says it applies the ruling and the owner may rule otherwise. - Fold section 2's two heading notes into one. - fleet-spawn-workers: keep "your own playbook" scope, Manager-scoped. - docs/roles/manager.card.md: add the briefing rule. - tests/test_role_cards.py: retired-rule row for "lander can land". --- .claude/skills/fleet-spawn-workers/SKILL.md | 4 +- docs/roles/manager.card.md | 3 + roles/MANAGER.md | 66 +++++++++++---------- tests/test_role_cards.py | 4 ++ 4 files changed, 44 insertions(+), 33 deletions(-) diff --git a/.claude/skills/fleet-spawn-workers/SKILL.md b/.claude/skills/fleet-spawn-workers/SKILL.md index fff03ad..a5766dc 100644 --- a/.claude/skills/fleet-spawn-workers/SKILL.md +++ b/.claude/skills/fleet-spawn-workers/SKILL.md @@ -98,8 +98,8 @@ bound.** These rows are the part that does not. | The outside ladder is a shape to copy, not a size to adopt | Anthropic's write-up of its multi-agent research system keeps the sizing rule in the orchestrator. It calls the lack of one a common failure mode, and records fifty subagents spawned for one simple query. | | Its integers do not come with it | The ladder runs from one agent for simple fact-finding, through two to four for a direct comparison, to ten or more for complex research. | | The condition that stops them transferring | **Those were measured on research tasks, and the same post says coding parallelises worse.** No number in that ladder has been measured on this fleet. | -| What bounds YOUR number | The work and your account's usage, never the merge queue. [MANAGER.md](../../../roles/MANAGER.md), *Do not check the pool before you open*, carries the 2026-10-26 owner ruling. | -| **CORRECTED 2026-10-26** | This row cited *"A Manager's fan-out is bounded by one window and by shared runners"*, a MANAGER.md heading that does not exist. | +| What bounds YOUR number is in your own playbook | For a Manager, [MANAGER.md](../../../roles/MANAGER.md), *Brief at full speed*: usage holds bound it, and the merge queue does not (owner ruling 2026-10-26). | +| **CORRECTED 2026-10-26** | The row above cited *"A Manager's fan-out is bounded by one window and by shared runners"*, a MANAGER.md heading that does not exist. | | The retired bound | [CONSOLE.md](../../../roles/retired/CONSOLE.md) bounded a fan-out by accounts and by what the Lander could land. That is record, not a live bound. | --- diff --git a/docs/roles/manager.card.md b/docs/roles/manager.card.md index daa299f..1c178ee 100644 --- a/docs/roles/manager.card.md +++ b/docs/roles/manager.card.md @@ -47,6 +47,9 @@ before assigning files. - Do not check the pool before you open a PR. Owner ruling 2026-09-18, which retired the check that told you to. Five Managers all reading "clear" open together, which manufactures the burst the check was meant to prevent. Open when your own work is ready. +- Do not limit briefing by the open PR count, the merge queue's depth, or the Lander's merge rate. + Owner ruling 2026-10-26: brief at full speed and batch; the Lander works the queue down. Usage + holds still bind. `roles/MANAGER.md`, *Brief at full speed*. - Do not assume you are the only Manager. Check other workers' holdings before assigning a file. - Never infer an account roster. Only the Owner assigns it. diff --git a/roles/MANAGER.md b/roles/MANAGER.md index b818355..d68a5d4 100644 --- a/roles/MANAGER.md +++ b/roles/MANAGER.md @@ -29,7 +29,7 @@ Within the limits of the following rules, you SHOULD ALWAYS BE PROACTIVE IN YOUR | **RETIRED 2026-09-18: the pool check before opening a pull request** | Owner ruling. **Open the pull request when your own work is ready**, then tell the Lander. *Do not check the pool before you open* carries why. | | What that row read | *"Do not create more PRs than the Lander can handle. Find the Lander and communicate with it before creating a PR."* | | You still do not manage the merge queue or the repository | That half of the retired row survives. Leave both to the Lander. | -| **Brief at full speed (owner ruling 2026-10-26)** | The merge queue never limits briefing. *Do not check the pool before you open* carries the ruling. | +| **Brief at full speed (owner ruling 2026-10-26)** | The merge queue never limits briefing; usage holds still do. *Brief at full speed* carries the ruling. | | Every brief ends with push, report, exit. | Do not lose work. Subagents die when you do, so unpushed work is destroyed silently. See *Your work has to survive your exit*. | | You do not edit another Manager's worktree, or the primary checkout | *Never Do These* carries it, with what would end it. | | Conflicts between this file and COMMON | **Raise it to the owner,** unless a recorded delegated decision already resolves it. No seat picks a winner. [COMMON.md](COMMON.md), *Where a role playbook and this file disagree, the owner decides*, carries the 2026-08-28 owner ruling verbatim. | @@ -144,24 +144,30 @@ gh pr comment --body "" **A Builder that reports no QA line has not told you the step ran.** `BUILDER.md` 4c permits an empty result and requires it be reported, so "nothing found" and "nothing said" are different answers. +### Brief at full speed + +**Owner ruling 2026-10-26.** Never hold a brief or shrink a wave for the open pull request count, +the merge queue's depth, or the Lander's merge rate. Batch each wave into combined pull requests. A +queue forms, and the Lander works it down. + +The owner quoted *"Don't brief more than the Lander can land"*, then ruled: *"This is NEVER a thing +in our processes. Builders and managers work at full speed. Managers batch work into combined PRs +as much as possible. A merge queue develops. The lander works it down."* + +**The ruling is about the queue, not usage.** A Steward's HOLD NEW WORK, in +[STEWARD.md](STEWARD.md) *3g. What each state asks for*, still stops new briefs. The 90 percent +gate on a new Workflow, in [COMMON.md](COMMON.md), *Prohibitions that bind before any task starts*, +still binds too. + +**Open question for the owner, raised 2026-10-26.** *"As much as possible"* sits beside the +five-item cut in *When to cut a pull request*. The ruling did not say whether it lifts that cut. + ### Do not check the pool before you open **Open when your own work is ready.** Do not count open pull requests, do not ask the Lander whether it has room, and do not hold a finished branch for a quiet window. Holding one for its own wave is different, and bounded: *When to cut a pull request*. -**Nor before you brief: brief at full speed (owner ruling 2026-10-26).** Never hold a brief or -shrink a wave for the open pull request count, the merge queue's depth, or the Lander's merge rate. -Batch each wave into combined pull requests. A queue forms, and the Lander works it down. - -The owner's words, in session: *"This is NEVER a thing in our processes. Builders and managers work -at full speed. Managers batch work into combined PRs as much as possible. A merge queue develops. -The lander works it down."* - -**The ruling is about the queue, not quota.** The usage hold in [COMMON.md](COMMON.md), -*Prohibitions that bind before any task starts*, still binds. *When to cut a pull request* still -sets how a batch is cut. - **Why the pool check was retired 2026-09-18.** Five Managers reading one shared pool all read "clear" at the same moment and all open together. The check manufactures the burst it exists to prevent, because none of the five can see the other four deciding. @@ -381,12 +387,11 @@ What bound that run was the repository. See, in the constitution: ## 2. Never Conflict with the Lander **CORRECTED 2026-10-26 by owner ruling.** This heading read *"Never Conflict with the Lander or Clog -the Queue"*. A queue of open pull requests is expected, and the Lander works it down. *Do not check -the pool before you open* carries the ruling. +the Queue"*. The queue half narrowed on 2026-09-18 (*Do not check the pool before you open*) and +went on 2026-10-26 (*Brief at full speed*). -**The queue half of the old heading narrowed on 2026-09-18.** You no longer hold a pull request -back on what the queue looks like. See *Do not check the pool before you open*. What survives is -granularity, decided at dispatch in section 2a and at step 9 in *When to cut a pull request*. +What survives is granularity, decided at dispatch in section 2a and at step 9 in *When to cut a pull +request*. **An engine pull request no longer edits the ledger.** The ledger moved to the vault on 2026-09-13 (BACKLOG #1250). The Lander writes each banner there after the merge, from the text in the Builder's @@ -468,7 +473,7 @@ carried here rather than re-derived by this seat. | **SUPERSEDED 2026-09-13** | Banners no longer ride with code: the Lander writes them in the vault after the merge. So those 36 no longer touch the ledger at all. | | So | Collapsing the 12 moves arrivals from about 9.7/hour to 7.7/hour. **The queue still diverges.** Worth doing, and not a queue fix. | | And batching CORRELATES FAILURE | One red check or one conflict blocks every item in the wave, where a bad row today blocks only itself. At heavy oversubscription that is a real trade. | -| **CORRECTED 2026-10-26 by owner ruling** | *"The queue still diverges"* and *"heavy oversubscription"* treat a growing queue as a Manager's problem. It is not: brief at full speed and batch, and the Lander works the queue down. The failure-correlation trade stands. | +| **CORRECTED 2026-10-26 by owner ruling** | *"The queue still diverges"* and *"heavy oversubscription"* treat a growing queue as a Manager's problem. *Brief at full speed* says it is not. The failure-correlation trade stands. | | The measurement | Two dispatch waves added 17 pull requests in about 35 minutes. Open non-draft went 35 to 54 in one hour, and **zero** merged in it. **24 of the 54 were DIRTY**, overwhelmingly on the ledger tail. | | What those seven actually were | 13 to 69 lines each, of `docs/BACKLOG.md` only. They could have been one pull request. | | If an item must be its own pull request | The ledger edit is a FINAL COMMIT, ALONE. Section 2 states that rule, under *put your ledger row in its OWN commit, LAST*. | @@ -477,29 +482,28 @@ carried here rather than re-derived by this seat. | The worked example | PR 832 spread its `docs/BACKLOG.md` edits through code commits and needed a hand-resolved merge. | | Announce the files the pull requests will CHANGE, never the items' SUBJECTS | A dispatch announce naming `auth/service.py`, `api/app.py` and `config/settings.py` was read downstream as a collision forecast. The pull requests touched `docs/BACKLOG.md` and one test file; those paths were what the items were ABOUT. | | So, for a research wave | The changed file is almost always the ledger alone. Say that. | -| Verify the remote before you act on a `gh` read | `gh` answers plausibly against the wrong repository rather than failing. | +| Verify the remote before sizing a wave | `gh` answers plausibly against the wrong repository rather than failing. | | The measured case | A Manager ran `gh pr list` against the vault, read **1** open pull request, and sized a ten-subagent dispatch on it. The engine had **38**. | | So | Pass `--repo MEFORORG/MessageFoundry` explicitly on anything you will act on. The vault was renamed to `wshallwshall/MessageFoundry-vault` the same day and the old slug STILL REDIRECTS, so a stale `--repo wshallwshall/MessageFoundry` still answers, and answers about the vault. | | **CORRECTED 2026-09-24: the vault's slug is `MEFORORG/MessageFoundry-vault`** | The row above keeps what it said. `gh repo view --json nameWithOwner` in the vault checkout returns `MEFORORG/MessageFoundry-vault`, and `wshallwshall/MessageFoundry-vault` redirects there too. The advice holds: name the repository with `--repo`. | -| **CORRECTED 2026-10-26 by owner ruling** | The row *Verify the remote before you act on a `gh` read* was named *"Verify the remote before sizing a wave"*. The open pull request count is not a dispatch input. The measured case's error was the wrong repository. | +| **CORRECTED 2026-10-26 by owner ruling** | *Verify the remote before sizing a wave* and *The measured case* keep what they said. Sizing a wave on an open pull request count is itself retired, on any repository: *Brief at full speed*. The `--repo` lesson stands. | | **EXPIRY** | This holds only while the ledger is one file whose new rows land at one tail, and while conflict resolution is serial and manual. | | How to check the expiry | Re-examine it if `docs/BACKLOG.md` is split, if a merge driver is adopted for it, or if rows stop being appended in number order. | **RETIRED 2026-10-26 by owner ruling: the worked example on when to brief NOTHING.** It set a -work-in-progress limit keyed to the Lander's merge rate. *Do not check the pool before you open* -carries the ruling and the rule now. +work-in-progress limit keyed to the Lander's merge rate. *Brief at full speed* carries the ruling. It reasoned: *"Above 1.0 a queue does not settle at some depth. It grows without bound."* It ended: *"At that point briefing anything new subtracts from the fleet's throughput."* -**The 2026-09-05 figures stay as a reading, not a rule.** They are not a dispatch input. +**The 2026-09-05 figures stay as a record, not a rule.** -| Reading, 2026-09-05 | Value | +| Figure, 2026-09-05 | Value | | --- | --- | | Open non-draft pull requests | 62 | | Merge rate | 2.2 to 2.7 an hour | | Opened | 29 in one three-hour window, or 9.7 an hour | -| Utilisation, as the retired text gave it | 3.6 to 5.0. Derived, not read; 9.7 over 2.2 gives 4.4. | +| Utilisation | 3.6 to 4.4: opened over merge rate. The retired text gave 3.6 to 5.0, with no instrument for 5.0. | | Median age at merge | 15 hours | | Oldest open item | Two days | @@ -550,12 +554,12 @@ commit about every 35 minutes, so a pull request that waits overnight wakes behi A row landing within about two hours collides with nothing; the same row a day later collides with everything. -**RETIRED 2026-10-26 by owner ruling.** This read *"So do not cut a ledger row you cannot land -soon"*. It added *"If a batch cannot land today, do not file the rows today"*. File rows when the -work is ready, batched into one pull request. The Lander works the queue down, conflicts included. +**RETIRED 2026-10-26, applying *Brief at full speed*.** This read *"So do not cut a ledger row you +cannot land soon"*. File rows when the work is ready. The ruling named the briefing limit, not this +row, so the owner may rule otherwise. -**Nothing enforces the path check today.** The claim registry covers items. Until it covers paths, this is -yours to do by reading, and a brief that names no paths has skipped it rather than passed it. +**Nothing enforces the path check today.** The claim registry covers items. Until it covers paths, +this is yours to do by reading, and a brief that names no paths has skipped it rather than passed it. **A claim on a whole batch is necessary, and it is not sufficient.** Take the batch-level claim before you dispatch, because a check of per-item claims does not see it. Then, before you cut the diff --git a/tests/test_role_cards.py b/tests/test_role_cards.py index eed0fc8..d25428e 100644 --- a/tests/test_role_cards.py +++ b/tests/test_role_cards.py @@ -707,6 +707,10 @@ class RetiredRulesDoNotSurviveInACard(unittest.TestCase): #: (pattern, why it is retired). Matched case-insensitively, line by line. RETIRED = [ ("reviewed", "the `reviewed` merge gate was removed 2026-09-04; an unlabelled PR merges"), + ( + "lander can land", + "owner ruling 2026-10-26 retired the briefing limit keyed to the Lander's merge rate", + ), ] def _live_hits(self, text: str) -> list[str]: From 8747360fcee24a14744e535b7df07c264f2eac7a Mon Sep 17 00:00:00 2001 From: Scott Hall Date: Mon, 26 Oct 2026 12:54:10 -0500 Subject: [PATCH 4/6] Record the owner's three answers on the full-speed ruling The owner answered PR 246's three open questions on 2026-10-26, in session, in the question dialog. 1. "Remove the cap": MANAGER.md, When to cut a pull request, drops the five-item cut and retires "Why five". The own-PR exceptions stay. manager.card.md and docs/KORUS-BUILD.md follow. 2. "Confirm retired": MANAGER.md section 3's ledger-row hold note now records the confirmation. 3. "Retire it": BUILDER.md retires the four-item lane ceiling and the start throttle by burn (lead line, the START THROTTLE row, 5d). Usage holds still bind. Brief at full speed lists the three answers. test_role_cards pins the five-item cut and "most you oversee" as retired on cards. --- docs/KORUS-BUILD.md | 2 +- docs/roles/manager.card.md | 2 +- roles/BUILDER.md | 46 ++++++++++++++++++++++++-------------- roles/MANAGER.md | 21 +++++++++++------ tests/test_role_cards.py | 7 ++++++ 5 files changed, 52 insertions(+), 26 deletions(-) diff --git a/docs/KORUS-BUILD.md b/docs/KORUS-BUILD.md index 286e36b..c85df29 100644 --- a/docs/KORUS-BUILD.md +++ b/docs/KORUS-BUILD.md @@ -167,7 +167,7 @@ Read each builder's result. Check the branch reached the remote yourself: You decide when to cut a pull request. Default to one per wave: merge the wave's branches onto a fresh branch from origin/main with git merge --no-ff, run the checks once on the combined tree, and open one pull request. Build the batch in a throwaway worktree, never your own. Cut it when -every builder has reported, when it holds five items, or before you close, whichever comes first. +every builder has reported or before you close, whichever comes first. There is no item cap. Give an item its own pull request if it fixes a red main, changes a security control, supersedes an ADR, or must land in order against another open pull request. Send an item that is red or conflicts back to a builder. Never write a conflict resolution yourself. roles/MANAGER.md, "When to cut a pull request", holds diff --git a/docs/roles/manager.card.md b/docs/roles/manager.card.md index 1c178ee..f0405e9 100644 --- a/docs/roles/manager.card.md +++ b/docs/roles/manager.card.md @@ -15,7 +15,7 @@ pull request (PR), label it `qa` with the Builder's QA line under it, and hand i *When to cut a pull request*, holds the rules. - Default to one PR per wave, merged in a throwaway worktree and checked as one tree. -- Cut it when every Builder has reported, at five items, or before you close. +- Cut it when every Builder has reported, or before you close. No item cap (Owner ruling 2026-10-26). - Its own PR: a red-`main` fix, a security control, an ADR supersession, an ordering constraint. - An item that is red or conflicts goes back to a Builder. Never write that resolution yourself. diff --git a/roles/BUILDER.md b/roles/BUILDER.md index 1774a3f..d6249d6 100644 --- a/roles/BUILDER.md +++ b/roles/BUILDER.md @@ -46,8 +46,9 @@ a gate, and never mislead a teammate or the owner about what you built. **A team building nothing at all is a failure to raise at once, not a quiet lane.** Say so to the Manager, the seat that can act on it and the seat that opens the pull request. -**Two to four is the most you oversee, not the least you must reach.** Owner ruling 2026-08-28. See -*A claim that outlives the work is a slot nobody can see*, which holds the rest of this rule. +**RETIRED 2026-10-26 by owner ruling: the lane ceiling.** This read *"Two to four is the most you +oversee, not the least you must reach."* Work at full speed. *A claim that outlives the work is a +slot nobody can see* names the two usage controls that still stop a start. **This file carries no live state on purpose.** Item numbers, lane assignments, queue tables, pull request numbers and "pick up here" lists belong in a dated episode note. See *The role file holds @@ -109,12 +110,13 @@ Console, and do not wait on one. | Where a question actually lands | **In your report to the Manager, which carries it onto the pull request it opens.** Findings on a pull request outlive the session; findings in mail do not. | | Why you cannot post it yourself | For a NEW branch the pull request does not exist until you have exited. | | The one case where you can | A follow-up brief onto a branch whose pull request is already open. Then the brief names the number, and you comment there as well as reporting. | -| Sections below that still name a Dispatcher or a Console | A record of how the fleet ran, not a live route. **Who holds the START THROTTLE now is OPEN.** | +| Sections below that still name a Dispatcher or a Console | A record of how the fleet ran, not a live route. | +| **CORRECTED 2026-10-26 by owner ruling: the START THROTTLE** | This row also read *"Who holds the START THROTTLE now is OPEN."* The owner retired the throttle, so no seat holds it. 5d names the two usage controls that still stop a start. | | Expiry | This retires when the owner names a successor seat or restores one of the three. | -Do not assume the throttle moved to the Manager, and do not read a quiet lane as a throttled one on -that assumption. Mail waits for the recipient's next turn. See [COMMON.md](COMMON.md), *Mail is a -mailbox, not a doorbell, and it expires in 72 hours*. +**Do not read a quiet lane as a throttled one.** Since 2026-10-26 nothing holds a start but a usage +control. Mail waits for the recipient's next turn. See [COMMON.md](COMMON.md), *Mail is a mailbox, +not a doorbell, and it expires in 72 hours*. ### If the brief does not answer something you must know, do not guess and do not wait @@ -595,7 +597,7 @@ claim id, a pull request number, a count from `claim.ps1 -List`, a timestamp. checkable; "Mixed, working hard" is not. **On a "no": say the number and what you can take.** "I hold 2, I can take 2 more, my lane is " -is actionable; "not enough" is not, to anyone replenishing four lanes. +is actionable; "not enough" is not, to anyone replenishing lanes. Reasoning goes in the prose above the table. **A dashboard that has to be read is not a dashboard.** @@ -1270,19 +1272,29 @@ ever fire step 14 on them. Release those claims before you exit, and say you did. The same list is in [COMMON.md](COMMON.md), *What a Builder still releases*, and in 4b; if the three disagree, COMMON governs. -**An item counts against your four while it is being *worked*, not while its claim is held.** -Built-and-awaiting-merge is zero occupancy. Say so, so others count it the same way. +**An item is occupancy while it is being *worked*, not while its claim is held.** +Built-and-awaiting-merge is zero occupancy. Say so, so others count it the same way. This read +*"counts against your four"* until 2026-10-26, when the four went. -**And four is the MOST YOU OVERSEE, not the least you must reach.** Owner ruling 2026-08-28: starts -are throttled by burn, and **that throttle is not yours.** +**RETIRED 2026-10-26 by owner ruling: the four-item ceiling and the throttle on starts.** This read +*"four is the MOST YOU OVERSEE"*. An owner ruling of 2026-08-28 added *"starts are throttled by +burn"*. Asked about both, the owner selected *"Retire it"*. -The ruling named the DISPATCHER as its holder. That seat was retired 2026-09-01 and the owner has -not named a successor, so **who holds it is OPEN.** A lane holding four items with one running -because its starts are held is *compliant*, and should report the reason, not a shortfall. +That ruling named the Dispatcher as the throttle's holder. Nobody held it after that seat retired on +2026-09-01. -**So separate the two lanes that both look like "under four".** One is running less because it was -told to. The other could start something and has not. **This trap is about the second.** Reporting -the first as a failure buries the second, which is the one worth finding. +**Work at full speed. Two usage controls still stop a start.** + +| Control | Where it lives | +| --- | --- | +| A Steward's HOLD NEW WORK | [STEWARD.md](STEWARD.md), *3g. What each state asks for* | +| The 90 percent gate on a new Workflow | [COMMON.md](COMMON.md), *Prohibitions that bind before any task starts* | + +A lane running less because one of them fired is *compliant*. It reports the hold, not a shortfall. + +**So separate the two quiet lanes.** One is running less because a hold told it to. The other could +start something and has not. **This trap is about the second.** Reporting the first as a failure +buries the second, which is the one worth finding. Measured 2026-08-13: a builder concluded two items into an armed pull request. It correctly could not release the claims, and correctly stated both as deliberately held in three places. **Then it diff --git a/roles/MANAGER.md b/roles/MANAGER.md index d68a5d4..1ea4550 100644 --- a/roles/MANAGER.md +++ b/roles/MANAGER.md @@ -159,8 +159,14 @@ as much as possible. A merge queue develops. The lander works it down."* gate on a new Workflow, in [COMMON.md](COMMON.md), *Prohibitions that bind before any task starts*, still binds too. -**Open question for the owner, raised 2026-10-26.** *"As much as possible"* sits beside the -five-item cut in *When to cut a pull request*. The ruling did not say whether it lifts that cut. +**The three questions this ruling raised were answered on 2026-10-26.** The owner selected these +answers in the question dialog, in session. + +| Question | The owner's selection | +| --- | --- | +| Does *"as much as possible"* lift the five-item cut in *When to cut a pull request*? | *"Remove the cap"*. Batch every item the wave holds. The own-pull-request exceptions stay. | +| Does the ruling retire section 3's hold on filing ledger rows you cannot land soon? | *"Confirm retired"*. | +| Does it retire [BUILDER.md](BUILDER.md)'s four-item lane ceiling and its throttle on starts by burn? | *"Retire it"*. Builders work at full speed. The usage holds above are the only brake. | ### Do not check the pool before you open @@ -256,9 +262,10 @@ measurements. | Item | Rule | | --- | --- | | The default | One pull request per wave. A wave is the set of Builders you dispatched together. | -| Cut it at the FIRST of | Every Builder in the wave has reported and its branch is on the remote. Five items are ready. You are about to close this instance. | +| Cut it at the FIRST of | Every Builder in the wave has reported and its branch is on the remote. You are about to close this instance. | +| **CORRECTED 2026-10-26 by owner ruling: no item cap** | That row also read *"Five items are ready."* The owner selected *"Remove the cap"*. Batch every item the wave holds. *Brief at full speed* records the question. | | A Builder still running | About 30 minutes after the rest reported, cut without it. It rides the next batch. | -| Why five | One red item blocks the whole batch until it is dropped. Five bounds that and keeps the diff readable for the Lander. | +| **RETIRED 2026-10-26: why five** | It read *"Five bounds that and keeps the diff readable for the Lander."* The trade it named stands: one red item blocks the whole batch until it is dropped. Leaving red and conflicting items out, below, bounds it now. | | Its own pull request, never batched | An item that fixes a red `main` or a broken gate: ship it now. An item that changes a security control. | | Also its own pull request | An item that reverses a recorded posture or supersedes an ADR; [LANDER.md](LANDER.md) 4i requires its own title. An item that must land in order against another open pull request. | | But two items in one wave | Two items that must land in order may share one batch. | @@ -554,9 +561,9 @@ commit about every 35 minutes, so a pull request that waits overnight wakes behi A row landing within about two hours collides with nothing; the same row a day later collides with everything. -**RETIRED 2026-10-26, applying *Brief at full speed*.** This read *"So do not cut a ledger row you -cannot land soon"*. File rows when the work is ready. The ruling named the briefing limit, not this -row, so the owner may rule otherwise. +**RETIRED 2026-10-26, applying *Brief at full speed*, and confirmed by the owner the same day.** This +read *"So do not cut a ledger row you cannot land soon"*. File rows when the work is ready. Asked +whether the ruling reached this rule, the owner selected *"Confirm retired"*. **Nothing enforces the path check today.** The claim registry covers items. Until it covers paths, this is yours to do by reading, and a brief that names no paths has skipped it rather than passed it. diff --git a/tests/test_role_cards.py b/tests/test_role_cards.py index d25428e..a491e36 100644 --- a/tests/test_role_cards.py +++ b/tests/test_role_cards.py @@ -711,6 +711,8 @@ class RetiredRulesDoNotSurviveInACard(unittest.TestCase): "lander can land", "owner ruling 2026-10-26 retired the briefing limit keyed to the Lander's merge rate", ), + ("five items", "owner ruling 2026-10-26 removed the five-item cut on a batched PR"), + ("most you oversee", "owner ruling 2026-10-26 retired the Builder's four-item lane ceiling"), ] def _live_hits(self, text: str) -> list[str]: @@ -746,6 +748,11 @@ def test_the_scan_fires_on_the_shape_it_exists_to_catch(self): "the scan did not fire on the exact line that shipped to main, so it guards nothing", ) + def test_the_scan_fires_on_the_retired_five_item_cut(self): + """The manager card's line before owner ruling 2026-10-26, verbatim.""" + planted = "- Cut it when every Builder has reported, at five items, or before you close." + self.assertTrue(self._live_hits(planted), "the scan did not fire on the retired five-item cut") + def test_the_scan_accepts_a_correctly_retracted_line(self): """The negative arm. A retraction names the rule on purpose and must not be flagged.""" retracted = "- **Wait for a `reviewed` label. RETIRED 2026-09-04.** An unlabelled PR merges." From 6e4924dc7e0e465606e2b59b710354f380d392fc Mon Sep 17 00:00:00 2001 From: Scott Hall Date: Mon, 26 Oct 2026 13:07:11 -0500 Subject: [PATCH 5/6] Review round 1 on the owner's answers: one control list, controls per row - MANAGER.md Brief at full speed holds the one list of usage controls that still stop a start, with all three Steward states; BUILDER.md 5d links to it. The lead no longer says the ruling leaves usage alone. - Why five: say plainly that a hosted-leg red still blocks the batch. - Section 3 quotes both retired sentences. Heading 2 in sentence case. - BUILDER.md keeps the 2026-08-28 date and attributes both the ceiling and the throttle to it; the "I can take 2 more" example drops the old ceiling's arithmetic. - manager.card.md and KORUS-BUILD.md keep the 30-minute straggler cut. - LANDER.md 4f: never ask a Manager to brief less. - test_role_cards: every retired pattern carries its own planted live line, checked per row against its own pattern. --- docs/KORUS-BUILD.md | 3 +- docs/roles/manager.card.md | 1 + roles/BUILDER.md | 29 ++++++++----------- roles/LANDER.md | 1 + roles/MANAGER.md | 26 ++++++++++------- tests/test_role_cards.py | 57 ++++++++++++++++++++++++-------------- 6 files changed, 68 insertions(+), 49 deletions(-) diff --git a/docs/KORUS-BUILD.md b/docs/KORUS-BUILD.md index c85df29..b4bf0d9 100644 --- a/docs/KORUS-BUILD.md +++ b/docs/KORUS-BUILD.md @@ -167,7 +167,8 @@ Read each builder's result. Check the branch reached the remote yourself: You decide when to cut a pull request. Default to one per wave: merge the wave's branches onto a fresh branch from origin/main with git merge --no-ff, run the checks once on the combined tree, and open one pull request. Build the batch in a throwaway worktree, never your own. Cut it when -every builder has reported or before you close, whichever comes first. There is no item cap. +every builder has reported or before you close, whichever comes first. There is no item cap. Cut +without a builder still running about 30 minutes after the rest reported. Give an item its own pull request if it fixes a red main, changes a security control, supersedes an ADR, or must land in order against another open pull request. Send an item that is red or conflicts back to a builder. Never write a conflict resolution yourself. roles/MANAGER.md, "When to cut a pull request", holds diff --git a/docs/roles/manager.card.md b/docs/roles/manager.card.md index f0405e9..09ccf5e 100644 --- a/docs/roles/manager.card.md +++ b/docs/roles/manager.card.md @@ -16,6 +16,7 @@ pull request (PR), label it `qa` with the Builder's QA line under it, and hand i - Default to one PR per wave, merged in a throwaway worktree and checked as one tree. - Cut it when every Builder has reported, or before you close. No item cap (Owner ruling 2026-10-26). + Cut without a Builder still running about 30 minutes after the rest reported. - Its own PR: a red-`main` fix, a security control, an ADR supersession, an ordering constraint. - An item that is red or conflicts goes back to a Builder. Never write that resolution yourself. diff --git a/roles/BUILDER.md b/roles/BUILDER.md index d6249d6..dfcc479 100644 --- a/roles/BUILDER.md +++ b/roles/BUILDER.md @@ -47,8 +47,8 @@ a gate, and never mislead a teammate or the owner about what you built. Manager, the seat that can act on it and the seat that opens the pull request. **RETIRED 2026-10-26 by owner ruling: the lane ceiling.** This read *"Two to four is the most you -oversee, not the least you must reach."* Work at full speed. *A claim that outlives the work is a -slot nobody can see* names the two usage controls that still stop a start. +oversee, not the least you must reach. Owner ruling 2026-08-28."* Work at full speed. *A claim that +outlives the work is a slot nobody can see* says what still stops a start. **This file carries no live state on purpose.** Item numbers, lane assignments, queue tables, pull request numbers and "pick up here" lists belong in a dated episode note. See *The role file holds @@ -111,12 +111,12 @@ Console, and do not wait on one. | Why you cannot post it yourself | For a NEW branch the pull request does not exist until you have exited. | | The one case where you can | A follow-up brief onto a branch whose pull request is already open. Then the brief names the number, and you comment there as well as reporting. | | Sections below that still name a Dispatcher or a Console | A record of how the fleet ran, not a live route. | -| **CORRECTED 2026-10-26 by owner ruling: the START THROTTLE** | This row also read *"Who holds the START THROTTLE now is OPEN."* The owner retired the throttle, so no seat holds it. 5d names the two usage controls that still stop a start. | +| **CORRECTED 2026-10-26 by owner ruling: the START THROTTLE** | This row also read *"Who holds the START THROTTLE now is OPEN."* The owner retired the throttle, so no seat holds it. 5d says what still stops a start. | | Expiry | This retires when the owner names a successor seat or restores one of the three. | -**Do not read a quiet lane as a throttled one.** Since 2026-10-26 nothing holds a start but a usage -control. Mail waits for the recipient's next turn. See [COMMON.md](COMMON.md), *Mail is a mailbox, -not a doorbell, and it expires in 72 hours*. +**Do not assume a quiet lane was throttled.** Since 2026-10-26 no seat throttles your starts, +though the usage controls 5d points to still stop one. Mail waits for the recipient's next turn. See +[COMMON.md](COMMON.md), *Mail is a mailbox, not a doorbell, and it expires in 72 hours*. ### If the brief does not answer something you must know, do not guess and do not wait @@ -596,7 +596,7 @@ claim id, a pull request number, a count from `claim.ps1 -List`, a timestamp. **Never a self-assessment of effort.** "No -- 1 of 4 moving, 3 blocked on the parser item" is checkable; "Mixed, working hard" is not. -**On a "no": say the number and what you can take.** "I hold 2, I can take 2 more, my lane is " +**On a "no": say the number and what you can take.** "I hold 2, I can take more now, my lane is " is actionable; "not enough" is not, to anyone replenishing lanes. Reasoning goes in the prose above the table. **A dashboard that has to be read is not a dashboard.** @@ -1277,20 +1277,15 @@ Built-and-awaiting-merge is zero occupancy. Say so, so others count it the same *"counts against your four"* until 2026-10-26, when the four went. **RETIRED 2026-10-26 by owner ruling: the four-item ceiling and the throttle on starts.** This read -*"four is the MOST YOU OVERSEE"*. An owner ruling of 2026-08-28 added *"starts are throttled by -burn"*. Asked about both, the owner selected *"Retire it"*. +*"four is the MOST YOU OVERSEE"* and *"starts are throttled by burn"*. An owner ruling of 2026-08-28 +set both. Asked about both, the owner selected *"Retire it"*. That ruling named the Dispatcher as the throttle's holder. Nobody held it after that seat retired on 2026-09-01. -**Work at full speed. Two usage controls still stop a start.** - -| Control | Where it lives | -| --- | --- | -| A Steward's HOLD NEW WORK | [STEWARD.md](STEWARD.md), *3g. What each state asks for* | -| The 90 percent gate on a new Workflow | [COMMON.md](COMMON.md), *Prohibitions that bind before any task starts* | - -A lane running less because one of them fired is *compliant*. It reports the hold, not a shortfall. +**Work at full speed. Usage controls still stop a start.** [MANAGER.md](MANAGER.md), *Brief at full +speed*, holds the one list of them. A lane running less because one fired is *compliant*. It +reports the control, not a shortfall. **So separate the two quiet lanes.** One is running less because a hold told it to. The other could start something and has not. **This trap is about the second.** Reporting the first as a failure diff --git a/roles/LANDER.md b/roles/LANDER.md index 717c18e..4059396 100644 --- a/roles/LANDER.md +++ b/roles/LANDER.md @@ -1251,6 +1251,7 @@ has happened: 13 PRs merged in one drain while the open count still grew from 3 | Re-measure any cost model | The old docs-only figures (2, 2, 2 and 13 minutes) were measured against an older required set and no longer hold. Re-measure with `max(completedAt) - min(startedAt)` over the REQUIRED contexts only. | | Do this | Batch independent code changes into one PR. Keep at most ONE code PR in flight and hold the rest as pushed branches. Serialise only genuine ordering constraints. | | Whose decisions that row governs | Yours, in the queue. Since 2026-09-23 it does not govern a Manager's opening: a Manager does not count open PRs before it opens one. | +| Never ask a Manager to brief less -- ADDED 2026-10-26 | Owner ruling: Managers brief at full speed, and you work the queue down. Batching is this section's remedy, not fewer briefs. [MANAGER.md](MANAGER.md), *Brief at full speed*. | | Who batches -- ADDED 2026-09-23 | **The Manager, at step 9.** Owner ruling. Before it, this table said to batch and named no seat to do it, while MANAGER.md said to keep independent changes apart. [MANAGER.md](MANAGER.md), *When to cut a pull request*, now holds the rule. | | Do NOT tell Builders "small and independent is the right shape" | That is correct for avoiding CONFLICTS and exactly wrong for a queue rate-limited by PR COUNT. The two pieces of advice look identical at the branch level and diverge only at the PR level. | | Batching trap 1 | `git cherry-pick` does not run pre-commit, so batched commits pass no local gate on creation. Run the ledger and backlog checks by hand, plus the affected tests. | diff --git a/roles/MANAGER.md b/roles/MANAGER.md index 1ea4550..9e8bd8c 100644 --- a/roles/MANAGER.md +++ b/roles/MANAGER.md @@ -154,10 +154,16 @@ The owner quoted *"Don't brief more than the Lander can land"*, then ruled: *"Th in our processes. Builders and managers work at full speed. Managers batch work into combined PRs as much as possible. A merge queue develops. The lander works it down."* -**The ruling is about the queue, not usage.** A Steward's HOLD NEW WORK, in -[STEWARD.md](STEWARD.md) *3g. What each state asks for*, still stops new briefs. The 90 percent -gate on a new Workflow, in [COMMON.md](COMMON.md), *Prohibitions that bind before any task starts*, -still binds too. +**The queue never limits a start. At least these usage controls still stop one.** They are the +single list; [BUILDER.md](BUILDER.md) 5d links here. + +| Control | Where it lives | +| --- | --- | +| A Steward's HOLD NEW WORK, PROTECT AND WRAP or URGENT STOP | [STEWARD.md](STEWARD.md), *3g. What each state asks for* | +| The 90 percent gate on a new Workflow | [COMMON.md](COMMON.md), *Prohibitions that bind before any task starts* | + +The burn throttle in the third answer below was a separate, standing limit on starts. It is +retired; these controls are not. **The three questions this ruling raised were answered on 2026-10-26.** The owner selected these answers in the question dialog, in session. @@ -166,7 +172,7 @@ answers in the question dialog, in session. | --- | --- | | Does *"as much as possible"* lift the five-item cut in *When to cut a pull request*? | *"Remove the cap"*. Batch every item the wave holds. The own-pull-request exceptions stay. | | Does the ruling retire section 3's hold on filing ledger rows you cannot land soon? | *"Confirm retired"*. | -| Does it retire [BUILDER.md](BUILDER.md)'s four-item lane ceiling and its throttle on starts by burn? | *"Retire it"*. Builders work at full speed. The usage holds above are the only brake. | +| Does it retire [BUILDER.md](BUILDER.md)'s four-item lane ceiling and its throttle on starts by burn? | *"Retire it"*. Builders work at full speed. The usage controls above still stop a start. | ### Do not check the pool before you open @@ -265,7 +271,7 @@ measurements. | Cut it at the FIRST of | Every Builder in the wave has reported and its branch is on the remote. You are about to close this instance. | | **CORRECTED 2026-10-26 by owner ruling: no item cap** | That row also read *"Five items are ready."* The owner selected *"Remove the cap"*. Batch every item the wave holds. *Brief at full speed* records the question. | | A Builder still running | About 30 minutes after the rest reported, cut without it. It rides the next batch. | -| **RETIRED 2026-10-26: why five** | It read *"Five bounds that and keeps the diff readable for the Lander."* The trade it named stands: one red item blocks the whole batch until it is dropped. Leaving red and conflicting items out, below, bounds it now. | +| **RETIRED 2026-10-26: why five** | It read *"Five bounds that and keeps the diff readable for the Lander."* One red item still blocks the batch. Steps 4 and 6 drop the conflicts and reds you catch; a hosted-leg red blocks every item. | | Its own pull request, never batched | An item that fixes a red `main` or a broken gate: ship it now. An item that changes a security control. | | Also its own pull request | An item that reverses a recorded posture or supersedes an ADR; [LANDER.md](LANDER.md) 4i requires its own title. An item that must land in order against another open pull request. | | But two items in one wave | Two items that must land in order may share one batch. | @@ -391,7 +397,7 @@ What bound that run was the repository. See, in the constitution: --- -## 2. Never Conflict with the Lander +## 2. Never conflict with the Lander **CORRECTED 2026-10-26 by owner ruling.** This heading read *"Never Conflict with the Lander or Clog the Queue"*. The queue half narrowed on 2026-09-18 (*Do not check the pool before you open*) and @@ -561,9 +567,9 @@ commit about every 35 minutes, so a pull request that waits overnight wakes behi A row landing within about two hours collides with nothing; the same row a day later collides with everything. -**RETIRED 2026-10-26, applying *Brief at full speed*, and confirmed by the owner the same day.** This -read *"So do not cut a ledger row you cannot land soon"*. File rows when the work is ready. Asked -whether the ruling reached this rule, the owner selected *"Confirm retired"*. +**RETIRED 2026-10-26 by owner ruling, confirmed the same day.** This read *"So do not cut a ledger +row you cannot land soon"*. It went on: *"If a batch cannot land today, do not file the rows +today"*. File rows when the work is ready. The owner selected *"Confirm retired"*. **Nothing enforces the path check today.** The claim registry covers items. Until it covers paths, this is yours to do by reading, and a brief that names no paths has skipped it rather than passed it. diff --git a/tests/test_role_cards.py b/tests/test_role_cards.py index a491e36..882c250 100644 --- a/tests/test_role_cards.py +++ b/tests/test_role_cards.py @@ -699,28 +699,44 @@ class RetiredRulesDoNotSurviveInACard(unittest.TestCase): to make, and the card supplies an authoritative-sounding reason. `docs/ROLE-CARDS.md` names the mechanism -- a card is injected at SessionStart, so it outranks the document that corrects it. - NARROW ON PURPOSE. This guards one retired rule by name rather than pretending to a general - test, because no registry of retired rules exists to check against. Widen it by adding a row - when the next rule retires, and the pair below keeps it honest meanwhile. + NARROW ON PURPOSE. This guards named retired rules rather than pretending to a general test, + because no registry of retired rules exists to check against. Widen it by adding a row when the + next rule retires. Every row carries its own planted live line, and the positive control below + runs each one, so no pattern can be added without a control. """ - #: (pattern, why it is retired). Matched case-insensitively, line by line. + #: (pattern, why it is retired, a live line it must catch). Matched case-insensitively, line by + #: line. The planted line is the rule as it was last stated live, so the control is real text. RETIRED = [ - ("reviewed", "the `reviewed` merge gate was removed 2026-09-04; an unlabelled PR merges"), + ( + "reviewed", + "the `reviewed` merge gate was removed 2026-09-04; an unlabelled PR merges", + "- **Merge an unlabelled PR.** `reviewed` is the gate, so re-check after any push.", + ), ( "lander can land", "owner ruling 2026-10-26 retired the briefing limit keyed to the Lander's merge rate", + "- **Brief more than the Lander can land.**", + ), + ( + "five items", + "owner ruling 2026-10-26 removed the five-item cut on a batched PR", + "- Cut it when every Builder has reported, at five items, or before you close.", + ), + ( + "most you oversee", + "owner ruling 2026-10-26 retired the Builder's four-item lane ceiling", + "**Two to four is the most you oversee, not the least you must reach.** Owner ruling 2026-08-28.", ), - ("five items", "owner ruling 2026-10-26 removed the five-item cut on a batched PR"), - ("most you oversee", "owner ruling 2026-10-26 retired the Builder's four-item lane ceiling"), ] - def _live_hits(self, text: str) -> list[str]: + def _live_hits(self, text: str, patterns: list[str] | None = None) -> list[str]: """Lines naming a retired rule without marking it retired.""" + wanted = [row[0] for row in self.RETIRED] if patterns is None else patterns out = [] for line in text.splitlines(): low = line.lower() - for pattern, _ in self.RETIRED: + for pattern in wanted: if pattern in low and "retired" not in low and "read *\"" not in line: out.append(line.strip()) return out @@ -737,21 +753,20 @@ def test_no_card_asserts_a_retired_rule_as_live(self): "a role card names a retired rule without marking it retired:\n" + "\n".join(f" {name}: {h}" for name, hs in offenders.items() for h in hs) + "\n\nThe reasons: " - + "; ".join(why for _, why in self.RETIRED), + + "; ".join(why for _, why, _ in self.RETIRED), ) def test_the_scan_fires_on_the_shape_it_exists_to_catch(self): - """The positive control. Without it, a pattern that matches nothing passes silently.""" - planted = "- **Merge an unlabelled PR.** `reviewed` is the gate, so re-check after any push." - self.assertTrue( - self._live_hits(planted), - "the scan did not fire on the exact line that shipped to main, so it guards nothing", - ) - - def test_the_scan_fires_on_the_retired_five_item_cut(self): - """The manager card's line before owner ruling 2026-10-26, verbatim.""" - planted = "- Cut it when every Builder has reported, at five items, or before you close." - self.assertTrue(self._live_hits(planted), "the scan did not fire on the retired five-item cut") + """The positive control, per row. Without it, a pattern that matches nothing passes silently. + + Each planted line must fire on ITS OWN pattern, so a row whose pattern has a typo goes red + even when another row's pattern happens to match the same line.""" + for pattern, _, planted in self.RETIRED: + with self.subTest(pattern=pattern): + self.assertTrue( + self._live_hits(planted, [pattern]), + f"the scan for {pattern!r} did not fire on its planted live line, so it guards nothing", + ) def test_the_scan_accepts_a_correctly_retracted_line(self): """The negative arm. A retraction names the rule on purpose and must not be flagged.""" From 7655805cdf1f6b50b6f450e2d38d525fba3d77cf Mon Sep 17 00:00:00 2001 From: Scott Hall Date: Mon, 26 Oct 2026 13:45:01 -0500 Subject: [PATCH 6/6] Review round 2 and Lander item 2: point to STEWARD 3a and 3d - MANAGER.md Brief at full speed no longer paraphrases the Steward ladder. It points to STEWARD.md 3a (only the 5-hour window carries the stop) and 3d (the stop half is contested), says a high weekly rung with a low 5-hour rung does not stop briefing, and drops the "single list" claim. It names the own-PR exceptions where it says to batch, and says path contention still shapes a brief. - BUILDER.md: the START THROTTLE correction names the row above; the mail citation points at the fleet-message-a-peer skill, where that heading lives; the capacity example keeps a number; the throttle holder reads as recorded OPEN, not as a fact about the owner. - LANDER.md 4f: the remedy is batching, never fewer producers. --- roles/BUILDER.md | 20 ++++++++++---------- roles/LANDER.md | 3 +++ roles/MANAGER.md | 16 +++++++++------- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/roles/BUILDER.md b/roles/BUILDER.md index dfcc479..2c178c1 100644 --- a/roles/BUILDER.md +++ b/roles/BUILDER.md @@ -111,12 +111,12 @@ Console, and do not wait on one. | Why you cannot post it yourself | For a NEW branch the pull request does not exist until you have exited. | | The one case where you can | A follow-up brief onto a branch whose pull request is already open. Then the brief names the number, and you comment there as well as reporting. | | Sections below that still name a Dispatcher or a Console | A record of how the fleet ran, not a live route. | -| **CORRECTED 2026-10-26 by owner ruling: the START THROTTLE** | This row also read *"Who holds the START THROTTLE now is OPEN."* The owner retired the throttle, so no seat holds it. 5d says what still stops a start. | +| **CORRECTED 2026-10-26 by owner ruling: the START THROTTLE** | The row above also read *"Who holds the START THROTTLE now is OPEN."* The owner retired the throttle, so no seat holds it. 5d says what still stops a start. | | Expiry | This retires when the owner names a successor seat or restores one of the three. | -**Do not assume a quiet lane was throttled.** Since 2026-10-26 no seat throttles your starts, -though the usage controls 5d points to still stop one. Mail waits for the recipient's next turn. See -[COMMON.md](COMMON.md), *Mail is a mailbox, not a doorbell, and it expires in 72 hours*. +**Do not assume a quiet lane was throttled.** Since 2026-10-26 only the usage controls in 5d stop a +start. Mail waits for the recipient's next turn: [fleet-message-a-peer](../.claude/skills/fleet-message-a-peer/SKILL.md), +*Mail is a mailbox, not a doorbell, and it expires in 72 hours*. ### If the brief does not answer something you must know, do not guess and do not wait @@ -596,7 +596,7 @@ claim id, a pull request number, a count from `claim.ps1 -List`, a timestamp. **Never a self-assessment of effort.** "No -- 1 of 4 moving, 3 blocked on the parser item" is checkable; "Mixed, working hard" is not. -**On a "no": say the number and what you can take.** "I hold 2, I can take more now, my lane is " +**On a "no": say the number and what you can take.** "I hold 2, I can start 3 more now, my lane is " is actionable; "not enough" is not, to anyone replenishing lanes. Reasoning goes in the prose above the table. **A dashboard that has to be read is not a dashboard.** @@ -1280,12 +1280,12 @@ Built-and-awaiting-merge is zero occupancy. Say so, so others count it the same *"four is the MOST YOU OVERSEE"* and *"starts are throttled by burn"*. An owner ruling of 2026-08-28 set both. Asked about both, the owner selected *"Retire it"*. -That ruling named the Dispatcher as the throttle's holder. Nobody held it after that seat retired on -2026-09-01. +That ruling named the Dispatcher as the throttle's holder. After that seat retired on 2026-09-01, +this file recorded who held it as OPEN. -**Work at full speed. Usage controls still stop a start.** [MANAGER.md](MANAGER.md), *Brief at full -speed*, holds the one list of them. A lane running less because one fired is *compliant*. It -reports the control, not a shortfall. +**Work at full speed. Usage controls can still stop a start.** [MANAGER.md](MANAGER.md), *Brief at +full speed*, points to them; section 8 here covers the Workflow gate. A lane held by one is +*compliant*, and reports the control, not a shortfall. **So separate the two quiet lanes.** One is running less because a hold told it to. The other could start something and has not. **This trap is about the second.** Reporting the first as a failure diff --git a/roles/LANDER.md b/roles/LANDER.md index 4059396..e8af68f 100644 --- a/roles/LANDER.md +++ b/roles/LANDER.md @@ -1237,6 +1237,9 @@ Coordination can order a queue; it cannot widen one. The most expensive mistake to run parallel producers into a serialised queue and then spend the session ordering the pile-up. It has happened: 13 PRs merged in one drain while the open count still grew from 3 to 6. +**The remedy is batching, never fewer producers** (owner ruling 2026-10-26). Managers brief at full +speed; the second table's *Never ask a Manager to brief less* row says so. + | Fact | Consequence | | --- | --- | | `ci.yml` triggers on `pull_request` and `push: branches: [main]` only | A feature-branch push runs NOTHING of `ci.yml`. Only `branch-leak-scan.yml` fires, and it is small. Unopened branches are nearly free to hold. **CORRECTED 2026-09-23**: this read *"runs NOTHING"*, which ignored that scan. | diff --git a/roles/MANAGER.md b/roles/MANAGER.md index 9e8bd8c..23d5812 100644 --- a/roles/MANAGER.md +++ b/roles/MANAGER.md @@ -147,23 +147,25 @@ result and requires it be reported, so "nothing found" and "nothing said" are di ### Brief at full speed **Owner ruling 2026-10-26.** Never hold a brief or shrink a wave for the open pull request count, -the merge queue's depth, or the Lander's merge rate. Batch each wave into combined pull requests. A -queue forms, and the Lander works it down. +the merge queue's depth, or the Lander's merge rate. Batch each wave, less the own-pull-request items +in *When to cut a pull request*. The Lander works the queue down. The owner quoted *"Don't brief more than the Lander can land"*, then ruled: *"This is NEVER a thing in our processes. Builders and managers work at full speed. Managers batch work into combined PRs as much as possible. A merge queue develops. The lander works it down."* -**The queue never limits a start. At least these usage controls still stop one.** They are the -single list; [BUILDER.md](BUILDER.md) 5d links here. +**The merge queue never limits a start. Usage controls still can, at least these two.** Read each +where it lives; do not act on a paraphrase. [BUILDER.md](BUILDER.md) 5d links here. | Control | Where it lives | | --- | --- | -| A Steward's HOLD NEW WORK, PROTECT AND WRAP or URGENT STOP | [STEWARD.md](STEWARD.md), *3g. What each state asks for* | +| A Steward's stop on starting | [STEWARD.md](STEWARD.md), *3a. The worse window sets the STATE; only the 5-hour window carries the STOP*, and *3d. A live owner ruling can suspend the stop half without suspending the warning* | | The 90 percent gate on a new Workflow | [COMMON.md](COMMON.md), *Prohibitions that bind before any task starts* | -The burn throttle in the third answer below was a separate, standing limit on starts. It is -retired; these controls are not. +A high weekly rung with a low 5-hour rung does not stop briefing (3a). The burn throttle, in the +third answer below, was a separate limit, and it is retired. + +Path contention, in section 3, still shapes what you brief. **The three questions this ruling raised were answered on 2026-10-26.** The owner selected these answers in the question dialog, in session.