From 45934b54a2589900f59d9ea93c69b825bb976b2e Mon Sep 17 00:00:00 2001 From: dan-claude-bot Date: Mon, 27 Jul 2026 21:22:07 +0000 Subject: [PATCH 1/2] =?UTF-8?q?docs(fleet):=20ci-red=20is=20deployed=20eng?= =?UTF-8?q?ine=20=E2=80=94=20advance=20the=20stamp=20to=20crew@4da17c4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #189 — the post-merge, triage-owned task, now reachable: heavy-duty/crew#64 merged at 4da17c49594c2d86bd3793fa3567846cbca38e90. #190 wrote the ci-red wake into FLEET.md deliberately marked on paper, per this file's convention for a spec that is not yet running, and left the reconciliation stamp at crew@01fb49c because advancing it to a SHA that did not carry the wake would be the drift #187 exists to remove. crew#64 has merged, so both halves flip: - The stamp advances to crew@4da17c4, and every crew permalink in the file with it. A stamp and its evidence links naming different trees is worse than no stamp: the reader diffs the wrong engine and finds no drift because the tree they were pointed at is the one the prose was written against. - The four on-paper markers go: the duty-order caveat, the ci-red bullet's parenthetical, the Build bullet's "once ci-red deploys", and the closing paper inventory — which now names one remaining paper wake, the notifier's needs-ruling queue, not two. The Build bullet is not a pure marker removal. crew#64's last review round changed what it has to say: the operator ruled the round gate a whitelist, so the wake admits a green head OR one with no checks configured, and holds a red head AND one whose check has not finished. Copying the old "never a round at a red head" through the flip would have shipped a fresh inaccuracy on the same commit that claims the file is reconciled. The ci-red bullet gains the matching sentence from the other side: an unfinished check is not a red head and wakes nothing, because nothing has failed yet. Verified at the stamped SHA rather than assumed: duty.sh's header carries attention → … → resume → ci-red → build (and says it is what this file is reconciled against), shared/README.md's duty order matches, and notify.sh's only label filter is still state:needs-human — which is what keeps the remaining paper claim true. 18/18 test files pass; changelog_fragment_problem OK (it caught a 387-char entry against the 300 bound, now split); shellcheck, actionlint self-ref and git diff --check clean. Co-Authored-By: Claude Opus 5 --- FLEET.md | 57 ++++++++++++++++++++++++++-------------------- changelog.d/189.md | 8 +++++-- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/FLEET.md b/FLEET.md index ba6e677..f4b8e27 100644 --- a/FLEET.md +++ b/FLEET.md @@ -10,7 +10,7 @@ > into [heavy-duty/crew](https://github.com/heavy-duty/crew) (private to the > org; the fleet can read it), the *mechanism* lives there and this file only > points at it. Last reconciled against the merged engine at -> [`heavy-duty/crew@01fb49c`](https://github.com/heavy-duty/crew/tree/01fb49cd717a0f4c83df286af86411c69e3c2363), +> [`heavy-duty/crew@4da17c4`](https://github.com/heavy-duty/crew/tree/4da17c49594c2d86bd3793fa3567846cbca38e90), > 2026-07-27 — a descriptive file with no reconciliation stamp gives the next > reader nothing to diff, which is exactly how the #149 drift went unnoticed. @@ -30,7 +30,7 @@ network path — GitHub is the only queue. This table is the **as-built** bench — five boxes, two of them dual-role. It is not the same thing as crew's -[`fleet.roster`](https://github.com/heavy-duty/crew/blob/01fb49cd717a0f4c83df286af86411c69e3c2363/fleet.roster), +[`fleet.roster`](https://github.com/heavy-duty/crew/blob/4da17c49594c2d86bd3793fa3567846cbca38e90/fleet.roster), whose own header declares it the **target** environment: seven single-role boxes, the dual-role claude and codex boxes each split into a builder and a reviewer member. The delta is exactly that split (plus each new box needing @@ -49,13 +49,13 @@ second repo is a second thing to keep true — this one drifted (it said cron ran `duty.sh` directly and gave the hygiene sweep its own cron line; crew's `duty.sh` records that separate line as the bug it fixed, sharing `~/duty/work` unlocked). How a tick actually works — cron fires -[`bin/tick.sh`](https://github.com/heavy-duty/crew/blob/01fb49cd717a0f4c83df286af86411c69e3c2363/shared/bin/tick.sh), +[`bin/tick.sh`](https://github.com/heavy-duty/crew/blob/4da17c49594c2d86bd3793fa3567846cbca38e90/shared/bin/tick.sh), the only cron target, which wraps -[`bin/duty.sh`](https://github.com/heavy-duty/crew/blob/01fb49cd717a0f4c83df286af86411c69e3c2363/shared/bin/duty.sh) +[`bin/duty.sh`](https://github.com/heavy-duty/crew/blob/4da17c49594c2d86bd3793fa3567846cbca38e90/shared/bin/duty.sh) in a non-blocking `flock` with one evidence line per boundary; the boot gate and crash recovery; the session runner; backlog hygiene self-scheduling inside the duty tick under the same lock — lives with the code: -[`shared/README.md`](https://github.com/heavy-duty/crew/blob/01fb49cd717a0f4c83df286af86411c69e3c2363/shared/README.md) +[`shared/README.md`](https://github.com/heavy-duty/crew/blob/4da17c49594c2d86bd3793fa3567846cbca38e90/shared/README.md) is the map, provenance table included. Sessions stay stateless and disposable — all state lives on the board (issues, PRs, labels) and in git branches; detection is the engine's, judgment is the session's. @@ -73,7 +73,7 @@ What belongs here is what a wake *means*: write surface the whole org, which no registry could bound: the drill's containment interlock narrowed `repos.txt` and so confined triage and hygiene, but not review, the one module that submits verdicts. - [`lib/duty-review.sh`](https://github.com/heavy-duty/crew/blob/01fb49cd717a0f4c83df286af86411c69e3c2363/shared/lib/duty-review.sh) + [`lib/duty-review.sh`](https://github.com/heavy-duty/crew/blob/4da17c49594c2d86bd3793fa3567846cbca38e90/shared/lib/duty-review.sh) states the rule and implements the WARN. - **One wake is registry-independent, by design: attention** (next section). The registry bounds what a box goes *looking for*, not what is handed to @@ -100,7 +100,7 @@ stated exception. An `attention` assignment is work handed to this identity by name, and **the assignment is what carries the authorization** — there is nothing here for a repo list to scope, because the box is not choosing where to look. -[`lib/duty-attention.sh`](https://github.com/heavy-duty/crew/blob/01fb49cd717a0f4c83df286af86411c69e3c2363/shared/lib/duty-attention.sh) +[`lib/duty-attention.sh`](https://github.com/heavy-duty/crew/blob/4da17c49594c2d86bd3793fa3567846cbca38e90/shared/lib/duty-attention.sh) queries the cross-repo endpoint on purpose and says so in its header; a fix that bounds this wake to the registry would re-create the #16 incident below. (Crew's `conf/repos-default.txt` header currently claims *every* duty module @@ -131,14 +131,13 @@ wake is no longer on paper: `duty-attention.sh` is deployed engine, and `duty.sh` runs it first on every box, whatever its roles. The engine's duty order is fleet-standard -([`bin/duty.sh`](https://github.com/heavy-duty/crew/blob/01fb49cd717a0f4c83df286af86411c69e3c2363/shared/bin/duty.sh)): +([`bin/duty.sh`](https://github.com/heavy-duty/crew/blob/4da17c49594c2d86bd3793fa3567846cbca38e90/shared/bin/duty.sh)): **attention → triage signals → review queue → resume → ci-red → build → handoff → rebase → worktree hygiene → backlog hygiene (hourly)** — attention role-independent and first, then each duty family the box's roles enable. -One position in that order is **on paper**: ci-red is -[crew#64](https://github.com/heavy-duty/crew/pull/64)'s, unmerged at the -stamped SHA, where `duty.sh` still runs resume straight into build — it -reads as deployed engine only once that PR merges. +Every position in that order is deployed engine at the stamped SHA: +[crew#64](https://github.com/heavy-duty/crew/pull/64) merged ci-red between +resume and build, and `duty.sh`'s own header carries the same order. The earlier form of this file folded handoff and rebase into the other builder wakes; they are duties of their own. @@ -167,23 +166,30 @@ builder wakes; they are duties of their own. — a session died between first push and PR creation. A branch whose PR already **merged** is a post-merge wait, never resumed (#172, incubator#55/#64). -- **ci-red** (builders; **on paper** — crew#64's spec, unmerged at the - stamped SHA): a non-draft PR of mine whose check at the current head is - failing. Evaluated before the build wake, so a red PR of mine outranks a - new claim — repairing my own red head comes ahead of new work +- **ci-red** (builders): a non-draft PR of mine whose check at the current + head is failing. Evaluated before the build wake, so a red PR of mine + outranks a new claim — repairing my own red head comes ahead of new work (ceremony#163: full-panel approvals at the head, mergeable, stranded on a transient failure no wake covered). A round owed at a red head is excluded from the build wake below but reported rather than silent, and an unchanged red head goes quiet after one attempt, through the - `report_suppressed` path — suppressed, still said. How a red head is - detected and kept quiet is the engine's mechanism, described in crew's + `report_suppressed` path — suppressed, still said. A check that has not + finished is **not** a red head and wakes nothing here: nothing has failed + yet, so there is no investigation to launch. How a red head is detected + and kept quiet is the engine's mechanism, described in crew's `shared/README.md`, not here. - **Build**: a `ready` **unclaimed** issue (an assignee means mid-claim, not pickable), or a completed review round on my PR — a changes-request with no panel review request still outstanding; whole rounds, never single - verdicts, and — once ci-red deploys — never a round at a red head: that - head has already woken ci-red above, and the excluded round is reported, - not swallowed. + verdicts, and never a round the check at its head does not support. The + wake admits a **green** head, and a head with **no checks configured** — + terminal, not transient, so holding there would retire the round rather + than delay it. It holds a **red** head (already woken ci-red above) and a + head whose check has **not finished** (opening the round there spends the + panel on a head that may go red — crew#45's measured cost — and it admits + itself a tick later once the check settles). Both holds are reported, not + swallowed, and they are reported *differently*: only one of them is the + author's own work to do. - **Handoff**: a round of mine that converged — every panelist's latest opinionated review approves the current head, no panel request outstanding, mergeable right now, `state:needs-human` not already set. @@ -248,10 +254,11 @@ The duty engine is crew's shared tree, one source deployed to every box; makes the registry rule an operator decision rather than a sweep's. Specs written in this file have a record of becoming engine: the attention wake and the reviewers' request sweep both started here as paper (the sweep's -org-wide form was then retired by the 2026-07-25 scope ruling). Two are -still on paper: the notifier's `needs-ruling` queue — at the stamped crew -SHA, `notify.sh`'s only label filter is `state:needs-human` — and the -builders' ci-red wake above, engine the moment crew#64 merges. +org-wide form was then retired by the 2026-07-25 scope ruling), and the +builders' ci-red wake above is the latest: written here as paper while +crew#64 was open, engine at the stamped SHA. One is still on paper: the +notifier's `needs-ruling` queue — at that SHA, `notify.sh`'s only label +filter is `state:needs-human`. ### Conventions on the board diff --git a/changelog.d/189.md b/changelog.d/189.md index d29e76f..1530d28 100644 --- a/changelog.d/189.md +++ b/changelog.d/189.md @@ -8,8 +8,12 @@ of your own PR is picked up before claiming another issue, is never a parked claim, and follows crew#17's recovery path (#189). - `FLEET.md` writes the ci-red wake into the duty order between resume - and build, marked on paper until crew#64 merges, with the - reconciliation stamp unchanged (#189). + and build, now as deployed engine rather than on paper: the + reconciliation stamp advances to the crew SHA carrying crew#64 (#189). +- `FLEET.md` describes the build wake's check gate as the engine + implements it: a green head, or one with no checks configured, opens a + round; a red head and an unfinished one are held and reported + separately (#189). - `BUILDER.md` re-requests by head, not by verdict: a push while answering a round stales every approval, so every panelist is re-requested; only an unchanged head re-requests the non-approvers From 4f3fc2a20b68ad62bc8aaa5959096d26b51b8461 Mon Sep 17 00:00:00 2001 From: dan-claude-bot Date: Mon, 27 Jul 2026 21:36:10 +0000 Subject: [PATCH 2/2] =?UTF-8?q?docs(fleet):=20reconcile=20the=20attention?= =?UTF-8?q?=20wake=20too=20=E2=80=94=20the=20stamp=20covers=20the=20whole?= =?UTF-8?q?=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #189. Round 1 on #191 (codex blocking, kimi nit). codex caught that advancing the stamp to crew@4da17c4 made the attention section false. It did, and the error is mine in kind, not only in detail: a reconciliation stamp is a claim about the WHOLE file against that tree, and I audited only the ci-red surface across a 17-commit advance. That is the #187 failure mode, on the PR that closes #189. What was false at the pinned SHA, all from the crew#66 ruling (danmt, 2026-07-27) landing in d578150e: - "One wake is registry-independent, by design: attention" — no wake is exempt now. _attention_partition splits rows against the registry; OUT rows are reported and never acted on. - "the assignment is what carries the authorization — there is nothing here for a repo list to scope" — this is the position the ruling REJECTED. The cost was argued first: a cross-repo handoff now waits on an operator adding the repo, which is why an out-of-scope demand also pings the operator over the boot-gate channel rather than only reaching duty.log. - "a fix that bounds this wake to the registry would re-create the #16 incident" — that fix landed, with the ping as its mitigation. - The parenthetical calling crew's repos-default.txt header a contradiction "raised there as a discussion" — the discussion is crew#66, it was ruled, and crew's header now names the attention wake explicitly. This file was preserving the losing side of a settled question. Also in range and owned by this section: d849f166 ledgered the wake, so "a session that dies before acking is relaunched; that is the whole crash-recovery story" no longer is. Dying relaunches; COMPLETING without acking is a decline, and the ledger stops it re-firing until the issue moves. Meaning, not mechanism, so it belongs here. Re-audited the rest of the advance rather than spot-fixing: fleet.roster still declares itself the TARGET environment (the roster paragraph holds), and the roster/install commits touch role resolution inside crew, which this file does not describe. kimi's nit, which the earlier rewrite made mine: the paper inventory counted one row while the triage-signals bullet marks a second. Both are needs-ruling rows and both are now named — a number that has to be recounted every time a wake lands is the thing that went wrong. 18/18 test files; changelog_fragment_problem OK; shellcheck, self-ref and git diff --check clean. Co-Authored-By: Claude Opus 5 --- FLEET.md | 57 +++++++++++++++++++++++++++++----------------- changelog.d/189.md | 7 ++++++ 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/FLEET.md b/FLEET.md index f4b8e27..d22bdd4 100644 --- a/FLEET.md +++ b/FLEET.md @@ -75,9 +75,11 @@ What belongs here is what a wake *means*: hygiene, but not review, the one module that submits verdicts. [`lib/duty-review.sh`](https://github.com/heavy-duty/crew/blob/4da17c49594c2d86bd3793fa3567846cbca38e90/shared/lib/duty-review.sh) states the rule and implements the WARN. -- **One wake is registry-independent, by design: attention** (next section). - The registry bounds what a box goes *looking for*, not what is handed to - this identity *by name*. +- **No wake is exempt, including attention** (next section). The attention + *query* is cross-repo by construction — it is one call to the + authenticated-user endpoint, which has no repo filter — but the *action* + it authorizes is bounded like every other. A demand parked on this box in + a repo nobody listed is reported, never worked. ### Wake conditions @@ -92,20 +94,26 @@ very thing that unparks the work resume would otherwise pick up. The query is the authenticated-user endpoint — `gh api "/issues?filter=assigned&state=open&labels=attention"` — one call, no search index (the review queue below already records that the index lags) — -and, **alone among the wakes, it reaches repos `~/duty/repos.txt` does not -name.** +and it **sees** repos `~/duty/repos.txt` does not name, because that endpoint +takes no repo filter. -That reach is deliberate, and it survives the registry rule above as its one -stated exception. An `attention` assignment is work handed to this identity -by name, and **the assignment is what carries the authorization** — there is -nothing here for a repo list to scope, because the box is not choosing where -to look. +**Seeing is not acting, and that is a ruling** (crew#66, danmt, 2026-07-27). +The wake used to work every row it saw, which for a builder meant a clone and +the full worktree and round rule set against a repo no operator had listed — +write authority outside the registry, and the one hole left in the +containment story. Rows are now partitioned against the registry: inside it, +a session as before; outside it, reported and never acted on, exactly like an +out-of-scope review request or authored PR. [`lib/duty-attention.sh`](https://github.com/heavy-duty/crew/blob/4da17c49594c2d86bd3793fa3567846cbca38e90/shared/lib/duty-attention.sh) -queries the cross-repo endpoint on purpose and says so in its header; a fix -that bounds this wake to the registry would re-create the #16 incident below. -(Crew's `conf/repos-default.txt` header currently claims *every* duty module -is registry-bounded — that contradiction is crew's, raised there as a -discussion; this file records the exception as it is implemented today.) +implements the partition and states the ruling in its header. + +The cost was argued before the ruling rather than discovered after it: an +assignment plus a label **is** a targeted authorization, so a cross-repo +handoff now waits on an operator adding the repo, and the box most likely to +be handed work outside its beat is the one that goes quiet. That is why an +out-of-scope demand does not only reach `duty.log` — it pings the operator +over the same channel the boot gate uses. A bounded wake that failed silently +would trade an unbounded write surface for a broken channel to the human. Each demand gets **exactly one session, and the ack bounds it**: the session's first act, before any of the demanded work, is the pickup comment @@ -113,9 +121,13 @@ plus removing the label — [the `attention` contract's](https://github.com/heavy-duty/ceremony/blob/bce09aa7648dbd74b8e91b1d4fbc2fa8d145f705/LABELS.md#L143-L149) ack (#85), which here becomes the session's ack-then-act ordering. Then it acts on the thread and exits — short by construction. Until the label -is removed the flag is still up, so a session that dies before acking is -simply relaunched at the next tick; that is the whole crash-recovery story, -and it is the same crash-only shape as resume below. +is removed the flag is still up, so a session that **dies** before acking is +simply relaunched at the next tick — the same crash-only shape as resume +below. A session that **completes** without acking is a different fact: that +is a decline, and a seen-ledger stops it re-firing until the issue moves. +Dying and declining used to look identical to the engine, which meant a +demand a session had considered and correctly left alone woke a new one every +tick forever. The design this replaces was built and rejected: polling notifications for `reason: mention` re-arms a thread on every comment, so ordinary round @@ -256,9 +268,12 @@ written in this file have a record of becoming engine: the attention wake and the reviewers' request sweep both started here as paper (the sweep's org-wide form was then retired by the 2026-07-25 scope ruling), and the builders' ci-red wake above is the latest: written here as paper while -crew#64 was open, engine at the stamped SHA. One is still on paper: the -notifier's `needs-ruling` queue — at that SHA, `notify.sh`'s only label -filter is `state:needs-human`. +crew#64 was open, engine at the stamped SHA. Two rows are still on paper, and +both are `needs-ruling`: the notifier's queue — at that SHA, `notify.sh`'s +only label filter is `state:needs-human` — and triage's **past 24h** +detection row above, which the triage-signals bullet already marks. Earlier +counts here said "two" while silently excluding the second; naming them is +cheaper than a number that has to be recounted every time a wake lands. ### Conventions on the board diff --git a/changelog.d/189.md b/changelog.d/189.md index 1530d28..76fda86 100644 --- a/changelog.d/189.md +++ b/changelog.d/189.md @@ -14,6 +14,13 @@ implements it: a green head, or one with no checks configured, opens a round; a red head and an unfinished one are held and reported separately (#189). +- `FLEET.md` corrects the attention wake to the crew#66 ruling: the query + is cross-repo, the action is registry-bounded, and an out-of-scope + demand is reported and escalated to the operator rather than worked. It + no longer claims attention is exempt from the registry (#189). +- `FLEET.md` distinguishes an attention session that dies before acking, + which relaunches, from one that completes without acking, which is a + decline a ledger keeps from re-firing (#189). - `BUILDER.md` re-requests by head, not by verdict: a push while answering a round stales every approval, so every panelist is re-requested; only an unchanged head re-requests the non-approvers