@feigi/fleet-ctl 3.82.16 → 3.82.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feigi/fleet-ctl",
3
- "version": "3.82.16",
3
+ "version": "3.82.17",
4
4
  "description": "Agent fleet: run-team controller, merge bot, PR reviewer, and the ticket pipeline they share",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -304,6 +304,31 @@ A row with `review=` and no `reviewed=` is a review in flight, and the tick coun
304
304
  - **The merge bot.** Named `merge-bot-<n>`; `ledger.mjs dispatch merge-bot` numbers it. Never two at once. Render `--member merge-bot-<n> --n <n> --scratch <scratch>`. Dispatch with the printed agent, omit `model`, and dispatch on the first `ready-to-merge` label without waiting for the finisher report.
305
305
  - **The finisher.** Its render and receives are in `references/finisher.md`.
306
306
 
307
+ **A fleet member is never dispatched with omp's `isolated: true`.** See references/isolation.md.
308
+
309
+ **A member's tree is the claimed worktree, addressed by absolute path.** See references/isolation.md.
310
+
311
+ **The controller's own cwd stays the main checkout, unchanged.** See references/isolation.md.
312
+
313
+ **Name every member.**
314
+ Names follow the unit of work: `impl-<issue#>`, `fix-pr-<pr#>`,
315
+ `review-pr-<pr#>`, `finisher-pr-<pr#>`, `merge-bot-<n>`. See
316
+ references/member-lifecycle.md.
317
+
318
+ **Fresh context per member.** One member, one unit of work, gone. Refill =
319
+ **new** member, **new** name. See references/member-lifecycle.md.
320
+
321
+ **The scratch root is computed, never invented and never recorded.** You run
322
+ `~/.fleet/bin/fleet-run scratch-root.mjs`; a non-zero exit is an environment to
323
+ fix, not a cue to pick another root.
324
+ `<scratch>` in this file is the path it prints, resolved before you send anything: every
325
+ brief that carries one carries the absolute path you passed it — an
326
+ implementer's `<scratch>/impl-<N>/`, the recipe deriver's
327
+ `<scratch>/derive-recipe/`, a fix-applier's, a finisher's and a merge bot's
328
+ `<scratch>/pr<N>/…`, and a review runner's `scratch` argument, which is the
329
+ root itself. A member whose prompt names no path asks you by name and never
330
+ makes one up. See references/isolation.md.
331
+
307
332
  ### Reading a review result
308
333
 
309
334
  **You read the digest; the findings are the fix-applier's.**
@@ -439,6 +464,7 @@ It blocks, then prints one line. Which line it is, is the whole protocol:
439
464
  not a reason to end the run; they are the state a newly triaged ticket arrives
440
465
  into. The run ends when you drain it, when the budget goes, or when this
441
466
  context does — not because there was briefly nothing to do.
467
+ Zeroes on the cockpit are not yet proof the pipeline is idle.
442
468
 
443
469
  **When you stop on purpose, say why — one command, and it is the last thing
444
470
  you owe the next run.** See references/ci-and-staleness.md.
@@ -1062,6 +1088,25 @@ vs specific finding with paths → paths win. See references/reviewers.md.
1062
1088
  Suspect a neighbour before a member's own diff — for unexplained failures, and
1063
1089
  equally for any **finding** that came from reading source.
1064
1090
 
1091
+ **Never read a member's silence as assent**, and require members to report their *confirmation state*
1092
+ alongside results — "I escalated X, received no ruling, proceeded on default Y".
1093
+ After any ruling
1094
+ you cannot confirm was received, re-check the artifacts it governed. See references/member-lifecycle.md.
1095
+
1096
+ ### Re-check the instruments before a gate
1097
+
1098
+ **You read your instruments out of a tree every member can write to — re-check them before every gate decision.** See references/instruments.md.
1099
+
1100
+ - **Re-check before you act on any instrument reading** — a SHA acceptance, a CI
1101
+ verdict, the reconcile, a reap, a label gate — from the main checkout phase 0 pinned from:
1102
+ `~/.fleet/bin/fleet-run instruments.sh`. **Exit 0 is the only code that
1103
+ lets a gate proceed. Exit 1 (the set changed) and exit 2 (the check could not
1104
+ answer) both refuse: report what it printed and do NOT re-read the
1105
+ instrument** — a guard that fails open on "could not look" protects nothing.
1106
+ - **Re-pin only after a change you made deliberately.** **A mid-run tooling fix**
1107
+ is the one legitimate writer. Re-pinning to clear a refusal you cannot
1108
+ explain discards the check.
1109
+
1065
1110
  ### Relabel causes
1066
1111
 
1067
1112
  - **Implementer bails before implementing** → `fleet-event.mjs impl-report impl-<N>`
@@ -1078,6 +1123,9 @@ equally for any **finding** that came from reading source.
1078
1123
  The call comments the cause and releases the claim — never when `fixed` neither closes nor relabels N — then ticks:
1079
1124
  the head the tick names next is a *different* ticket.
1080
1125
 
1126
+ A member that bails demotes nothing itself; demotion by cause is yours
1127
+ (**Implementer bails before implementing**).
1128
+
1081
1129
  ### Resolving a finisher halt
1082
1130
 
1083
1131
  A halt at duty 1 reads identical from a bare SHA mismatch whatever caused it,
@@ -1156,210 +1204,6 @@ and flag the PR for a human. A new review of the PR counts again from none.
1156
1204
  | Merge bot's gate finds the merge would remove lines `main` gained after the PR's work began (`main-gain-removed:<path>`, exit 1) | Relay `main-gain-removed:<path>-#<pr>` to a human with the hit list and the acknowledgement keys the bot printed. PR stays queued, label untouched, nothing written to the ledger, and no member is dispatched to restore the lines — the removal may be deliberate. It clears when the PR stops removing them or its body carries `main-gain-removal: <path> <key> - <why>` for each hit |
1157
1205
  | Merge bot's gate cannot answer the main-gain check (`main-gain-unanswerable`, exit 2) | Report `main-gain-unanswerable-#<pr>` with what it printed, PR stays queued, label untouched |
1158
1206
 
1159
- ## Rules that fail silently
1160
-
1161
- These rules fail silently when they are skipped: check each before you act on a member's silence, and stop reading silence as a report or as assent.
1162
-
1163
- Zeroes on the cockpit are not yet proof the pipeline is idle.
1164
-
1165
- **A report is delivered, not implied by ending a turn.** Every member owes
1166
- the controller one report naming its outcome before it exits — including a
1167
- member whose prompt carries no explicit report line. The "report the SHA",
1168
- "report PR number" and "stop and report" instructions below *are* that
1169
- report, not a second obligation on top of it. A member that does the work
1170
- correctly and ends its turn has reported nothing: the controller learns the
1171
- outcome only by re-reading artifacts and pinging, and a finding held only in
1172
- that member's context is lost. Members did exactly this in one run —
1173
- finishers, a reviewer and merge bots alike, the work done and only the
1174
- delivery missing. Every brief's report paragraph says it. See
1175
- references/member-lifecycle.md.
1176
-
1177
- **The implementer, fix-applier, finisher, merge-bot and review-runner briefs
1178
- each tell the member you do not acknowledge reports; you never write that line
1179
- yourself.** A member cannot see whether its report arrived, so silence
1180
- from you is indistinguishable from loss and it re-sends — three members did in
1181
- one run, each burning a turn, and in all three the original had in fact arrived.
1182
- Each of those templates under `briefs/` carries the line: *"The controller does
1183
- not acknowledge reports. Send once and exit; never re-send unless the controller
1184
- asks by name."*
1185
-
1186
- So when a report is missing, **ask by name — that is what the "unless the controller asks by name" clause is for — and do not attribute the gap to the member until it answers.** See references/member-lifecycle.md.
1187
-
1188
- The ask is the controller's move, not the member's. See references/member-lifecycle.md.
1189
-
1190
- **Your OWN messages can vanish the same way, and that half is not recoverable by
1191
- a re-send.** Two controller rulings were lost in one run, both with a
1192
- `success: true` receipt: a member proceeded on its stated default and filed a
1193
- tracker issue the lost ruling had said not to file. So **never read a member's
1194
- silence as assent**, and require members to report their *confirmation state*
1195
- alongside results — "I escalated X, received no ruling, proceeded on default Y".
1196
- That sentence is the only thing that made either loss visible. After any ruling
1197
- you cannot confirm was received, re-check the artifacts it governed.
1198
-
1199
- **Name every member.** The name is the ledger token and the hub address; omit
1200
- it and the ledger cannot match the member.
1201
- Names follow the unit of work: `impl-<issue#>`, `fix-pr-<pr#>`,
1202
- `review-pr-<pr#>`, `finisher-pr-<pr#>`, `merge-bot-<n>`. See
1203
- references/member-lifecycle.md.
1204
-
1205
- **Fresh context per member.** One member, one unit of work, gone. Refill =
1206
- **new** member, **new** name.
1207
-
1208
- See references/member-lifecycle.md.
1209
-
1210
- **You read your instruments out of a tree every member can write to — re-check them before every gate decision.** See references/instruments.md.
1211
-
1212
- - **Pin once**, at phase 0 step 0 above, from the main checkout you are
1213
- running this from — never from a member's worktree, which is gitignored
1214
- and carries no baseline:
1215
- `~/.fleet/bin/fleet-run instruments.sh --pin`. On a workspace that does not
1216
- track `plugin/…`, the pin names the plugin checkout once — `--pin --audit
1217
- <plugin-checkout>` — and every check after it follows the recorded tree.
1218
- - **Re-check before you act on any instrument reading** — a SHA acceptance, a CI
1219
- verdict, the reconcile, a reap, a label gate — from that same main checkout:
1220
- `~/.fleet/bin/fleet-run instruments.sh`. **Exit 0 is the only code that
1221
- lets a gate proceed. Exit 1 (the set changed) and exit 2 (the check could not
1222
- answer) both refuse: report what it printed and do NOT re-read the
1223
- instrument** — a guard that fails open on "could not look" protects nothing.
1224
- **A label gate and a CI verdict are also taken by dispatched members, not
1225
- only by you** — the finisher labels, and the merge bot merges on its own
1226
- reading — and neither is dispatched to read this file, so neither can be
1227
- reached by stating the rule here. Each carries its own copy: the finisher's
1228
- in the instrument re-check block of `agents/fleet-finisher.agent.md`, the merge bot's inside
1229
- `merge-gate.mjs`, step 3 of `run-merge-bot.md`.
1230
- - **Re-pin only after a change you made deliberately.** The mid-run tooling fix
1231
- below is the one legitimate writer. Re-pinning to clear a refusal you cannot
1232
- explain discards the check.
1233
-
1234
- It covers every tracked file under this repo's own plugin component directories
1235
- — `plugin/commands/`, `plugin/scripts/`, `plugin/skills/`, `plugin/agents/`,
1236
- `plugin/workflows/` — the probes, the libraries they source, and these runbooks,
1237
- which you also read from that tree. Not the whole repo: `docs/metrics/` is
1238
- appended by THIS run, so covering it would refuse on the run's own bookkeeping
1239
- every time. It does not cover refs, deliberately: `claim-ticket.sh` creates a
1240
- branch per ticket and `reap.sh` deletes them in the ref store every worktree
1241
- shares, so ordinary work moves refs several times a run and a per-gate
1242
- refusal on that is noise.
1243
-
1244
- **A member runs its test suite in the foreground and blocks on it.** See references/member-lifecycle.md.
1245
-
1246
- **The scratch root is computed, never invented and never recorded.** No harness
1247
- supplies a scratch directory to a member, so a member left to name its own
1248
- invents one — and the liveness read in **The tick's rows**, which looks at the files under the
1249
- directory a member's own prompt assigned it, then watches a directory nobody
1250
- else can find. You run `~/.fleet/bin/fleet-run scratch-root.mjs`: it prints one
1251
- absolute path, `~/.fleet/scratch/<repo-key>/`, the same from the main checkout
1252
- and from every linked worktree, and the same again when a compacted or
1253
- re-invoked controller runs it, so nothing writes it down. It exits non-zero,
1254
- with its reason, when the root would sit inside a git work tree that does not
1255
- ignore it; that is an environment to fix, not a cue to pick another root.
1256
- `<scratch>` in this file is that path, resolved before you send anything: every
1257
- brief that carries one carries the absolute path you passed it — an
1258
- implementer's `<scratch>/impl-<N>/`, the recipe deriver's
1259
- `<scratch>/derive-recipe/`, a fix-applier's, a finisher's and a merge bot's
1260
- `<scratch>/pr<N>/…`, and a review runner's `scratch` argument, which is the
1261
- root itself. A member whose prompt names no path asks you by name and never
1262
- makes one up.
1263
-
1264
- ## Worktree and claim model
1265
-
1266
- Run none of this by hand: act through `claim-ticket.sh`, `release-ticket.sh`, `reap.sh` and `inflight.sh` as their phases say, and dispatch every member into its claimed worktree by absolute path.
1267
-
1268
- **Claim, release and reap are unchanged on omp.** `claim-ticket.sh`,
1269
- `release-ticket.sh`, `reap.sh` and `inflight.sh` are git-native and operate on
1270
- the single shared ref store every worktree and the main checkout read and
1271
- write directly (**Phase 1**, the tick's reap, and `release-ticket.sh` under
1272
- **Failure handling**); nothing in omp's `task` model touches that
1273
- store, so none of the four scripts carries a dialect branch.
1274
-
1275
- **A fleet member is never dispatched with omp's `isolated: true`.** See references/isolation.md.
1276
-
1277
- **A member's tree is the claimed worktree, addressed by absolute path.** See references/isolation.md.
1278
-
1279
- **The one open gap that probe measured: `task` does not accept a per-dispatch working directory.** See references/isolation.md.
1280
-
1281
- **The same gap reaches past the filesystem: `task`-dispatched members share one `eval` kernel.** See references/isolation.md.
1282
-
1283
- **`release-ticket.sh` is not a no-op on omp, and `inflight.sh`'s probes are unaffected by a running member.** See references/isolation.md.
1284
-
1285
- **The controller's own cwd stays the main checkout, unchanged.** Nothing in
1286
- this section's dispatch recipe — the cwd recipe above, the isolated-workspace
1287
- measurement, or the teardown scoping — moves
1288
- where the controller itself runs from.
1289
-
1290
- ## Why the implementer body carries what it does
1291
-
1292
- Read this before you edit any `fleet-implementer-<cell>` file, and stop editing when every cell's body is byte-identical again.
1293
-
1294
- Why the agent body carries what it does — read this before editing any `fleet-implementer-<cell>` file, and keep every cell's body byte-identical. See references/pull.md.
1295
-
1296
- - **Every implementer body carries the claim discipline, because tickets that restate facts ship new wrong claims** — inherited from the ticket, and minted in prose nobody asked for. See references/correction-tickets.md.
1297
- - **The settling command for "which files read X" must never be a literal-path
1298
- grep.** Replace the file with `MUTATED` in a throwaway copy, run the suite, and read off
1299
- which suites red: that answers which ones **depend on its contents**, which is
1300
- the question a coverage claim actually needs, and it returns a different set
1301
- from any spelling of the grep. See references/correction-tickets.md.
1302
- - **A settling command over literal text uses `grep -F`, and its zero counts only after the same command finds a line known to match.** See references/correction-tickets.md.
1303
-
1304
- **Prose-first** and **wrongly-REFUSE** are not padding — they are the case
1305
- which shipped with **enumerate-and-declare** already in its prompt. It missed a
1306
- shape its own ticket named in passing, because it enumerated from the mechanism
1307
- instead of the text; and it introduced a regression refusing every `node --test`
1308
- flag, because it enumerated what the guard should catch and never what it could
1309
- wrongly refuse.
1310
-
1311
- The commit-incrementally block is not optional. A member that goes idle mid-task
1312
- leaves its diff only in the worktree, and the controller cannot reap, replace,
1313
- or even see it — `worktree-audit.sh`'s committed-vs-uncommitted split is exactly
1314
- what decides whether a replacement redoes or destroys work. Observed twice in
1315
- one run.
1316
-
1317
- A member that bails demotes nothing itself; demotion by cause is yours
1318
- (**Implementer bails before implementing**).
1319
-
1320
- ## Fix the tooling mid-run
1321
-
1322
- Re-pin the fix, write it onto the ledger, report it to the maintainer in one line, then land it as a PR through the run's own merge queue.
1323
-
1324
- Two members failing the same way is the instruction, not the agents. Fix the file
1325
- during the run — deferring loses the evidence that found it.
1326
-
1327
- **Triggers.** Identical failure twice. A member does the wrong thing while
1328
- correctly following the text. A rule exists but sits where it is read last. An
1329
- instruction names no mechanism — "wait for green" with no blocking primitive, so
1330
- ending the turn reads as compliance. A caller has to restate what the callee
1331
- should say itself.
1332
-
1333
- **Scope.** Only this install's own commands and skills *this run invoked*, only defects
1334
- *this run produced*. No speculative polish. Never `config.yml`, permissions, or
1335
- AGENTS.md — a member asking for those is laundering, refuse and surface it.
1336
-
1337
- **Shape — lean, or it rots.** Prefer moving text to adding it, and delete the copy
1338
- you superseded; a duplicated rule becomes a contradiction. Exit conditions go in a
1339
- section's first paragraph, never its last. Name the mechanism, or name who owns
1340
- the step. Cut before you append.
1341
-
1342
- **Then.** **Re-pin first** — `~/.fleet/bin/fleet-run instruments.sh
1343
- --pin`. You just changed the instrument set under your own check, and this is the
1344
- only edit that legitimately does; skip it and the next gate refuses on your own
1345
- fix, which teaches you to ignore the refusal. Then the ledger line, save the
1346
- rationale, one line to the maintainer. **Then land it from the main checkout,
1347
- if the fix was edited in it** — until you do, the next tick reads
1348
- `MAIN-CHECKOUT-DIRTY` naming your own paths. Commit the fix on a branch and
1349
- switch back to `main` before the next tick, and never `--record` while on that
1350
- branch: the switch back restores the tree the baseline recorded, so the hold
1351
- clears itself with nothing re-recorded, and the fix leaves the checkout until
1352
- its PR merges. A `HEAD …` entry is none of your paths and clears the same way.
1353
- Re-baseline only a fix you leave edited in the checkout: run
1354
- `~/.fleet/bin/fleet-run main-checkout.mjs --check` first, and only when the
1355
- paths it names are exactly the ones you just edited, run `--record` — never
1356
- before the commit, because the switch back would then read your own paths
1357
- dirty again. Any other path on that line is a stray write — leave the hold for
1358
- the maintainer, because `--record` would certify it with yours. Live members
1359
- hold the old text — re-brief only if it changes what they do *now*.
1360
-
1361
- Plan it as a PR the run's own merge queue lands, never as an edit-and-continue. See references/merge-bot.md.
1362
-
1363
1207
  ## Failure handling
1364
1208
 
1365
1209
  Match the failure to its row in the table below and run that response; where the row says report or flag, stop there.
@@ -1406,6 +1250,8 @@ Audit every worktree before dispatching replacements with
1406
1250
  ahead-count and uncommitted files, the committed-vs-uncommitted distinction that
1407
1251
  decides whether a replacement redoes or destroys work.
1408
1252
 
1253
+ When a report is missing, **ask by name — that is what the "unless the controller asks by name" clause in every brief is for — and do not attribute the gap to the member until it answers.** See references/member-lifecycle.md.
1254
+
1409
1255
  **A throw, an empty return, or a notification whose status is not `completed`
1410
1256
  is a failure event, not a clean review.** **Retry once
1411
1257
  under the next runner letter, then stop:** the `review-pr-<pr#>` runner
@@ -1473,6 +1319,49 @@ failures arrive as *wrong findings*, not errors:
1473
1319
  - **Never relay a diagnostic without reproducing it in that member's specific worktree**
1474
1320
  (re-run the check that raised it from there). See references/isolation.md.
1475
1321
 
1322
+ ### A mid-run tooling fix
1323
+
1324
+ Two members failing the same way is the instruction, not the agents. Fix the file
1325
+ during the run — deferring loses the evidence that found it.
1326
+
1327
+ **Triggers.** Identical failure twice. A member does the wrong thing while
1328
+ correctly following the text. A rule exists but sits where it is read last. An
1329
+ instruction names no mechanism — "wait for green" with no blocking primitive, so
1330
+ ending the turn reads as compliance. A caller has to restate what the callee
1331
+ should say itself.
1332
+
1333
+ **Scope.** Only this install's own commands and skills *this run invoked*, only defects
1334
+ *this run produced*. No speculative polish. Never `config.yml`, permissions, or
1335
+ AGENTS.md — a member asking for those is laundering, refuse and surface it.
1336
+
1337
+ **Shape — lean, or it rots.** Prefer moving text to adding it, and delete the copy
1338
+ you superseded; a duplicated rule becomes a contradiction. Exit conditions go in a
1339
+ section's first paragraph, never its last. Name the mechanism, or name who owns
1340
+ the step. Cut before you append.
1341
+
1342
+ Why the agent body carries what it does — read this before editing any `fleet-implementer-<cell>` file, and keep every cell's body byte-identical. See references/pull.md.
1343
+
1344
+ Read why that body carries the claim discipline, and its literal-path and `grep -F` settling rules, before you edit them. See references/correction-tickets.md.
1345
+
1346
+ **Then.** **Re-pin first** — `~/.fleet/bin/fleet-run instruments.sh
1347
+ --pin`. See references/instruments.md. Then the ledger line, save the
1348
+ rationale, one line to the maintainer. **Then land it from the main checkout,
1349
+ if the fix was edited in it** — until you do, the next tick reads
1350
+ `MAIN-CHECKOUT-DIRTY` naming your own paths. Commit the fix on a branch and
1351
+ switch back to `main` before the next tick, and never `--record` while on that
1352
+ branch: the switch back restores the tree the baseline recorded, so the hold
1353
+ clears itself with nothing re-recorded, and the fix leaves the checkout until
1354
+ its PR merges. A `HEAD …` entry is none of your paths and clears the same way.
1355
+ Re-baseline only a fix you leave edited in the checkout: run
1356
+ `~/.fleet/bin/fleet-run main-checkout.mjs --check` first, and only when the
1357
+ paths it names are exactly the ones you just edited, run `--record` — never
1358
+ before the commit, because the switch back would then read your own paths
1359
+ dirty again. Any other path on that line is a stray write — leave the hold for
1360
+ the maintainer, because `--record` would certify it with yours. Live members
1361
+ hold the old text — re-brief only if it changes what they do *now*.
1362
+
1363
+ Plan it as a PR the run's own merge queue lands, never as an edit-and-continue. See references/merge-bot.md.
1364
+
1476
1365
  ## Red flags
1477
1366
 
1478
1367
  Stop when a thought of yours matches one of these, and act on the line it carries instead.
@@ -64,6 +64,12 @@ under `Verified:` its zero reads as a checked absence.
64
64
 
65
65
  ## The settling command for "which files read X" is never a literal-path grep
66
66
 
67
+ **The settling command for "which files read X" must never be a literal-path
68
+ grep.** Replace the file with `MUTATED` in a throwaway copy, run the suite, and read off
69
+ which suites red: that answers which ones **depend on its contents**, which is
70
+ the question a coverage claim actually needs, and it returns a different set
71
+ from any spelling of the grep.
72
+
67
73
  The path is assembled in more than one spelling here, one of them behind
68
74
  a local `read` helper, so a grep for the path string answers with the files that
69
75
  merely *name* it — comments included — and misses the files that build it.
@@ -1,6 +1,6 @@
1
1
  # Instruments: why they are re-checked
2
2
 
3
- Evidence for the rule SKILL.md states in **Rules that fail silently**, keyed by the rule it justifies.
3
+ Evidence for the rule SKILL.md states in **Re-check the instruments before a gate**, keyed by the rule it justifies.
4
4
 
5
5
  ## Re-check your instruments before every gate decision
6
6
 
@@ -17,3 +17,28 @@ direction — a modified instrument raises no error, shows in no PR and leaves
17
17
  nothing in the ledger, and it still returns a verdict, just not necessarily the
18
18
  right one, so you have no reason to re-check a script you did not know had
19
19
  changed.
20
+
21
+ **A label gate and a CI verdict are also taken by dispatched members, not
22
+ only by you** — the finisher labels, and the merge bot merges on its own
23
+ reading — and neither is dispatched to read SKILL.md, so neither can be
24
+ reached by stating the rule there. Each carries its own copy: the finisher's
25
+ in the instrument re-check block of `agents/fleet-finisher.agent.md`, the merge bot's inside
26
+ `merge-gate.mjs`, step 3 of `run-merge-bot.md`.
27
+
28
+ ## What the instrument set covers
29
+
30
+ It covers every tracked file under this repo's own plugin component directories
31
+ — `plugin/commands/`, `plugin/scripts/`, `plugin/skills/`, `plugin/agents/`,
32
+ `plugin/workflows/` — the probes, the libraries they source, and these runbooks,
33
+ which you also read from that tree. Not the whole repo: `docs/metrics/` is
34
+ appended by THIS run, so covering it would refuse on the run's own bookkeeping
35
+ every time. It does not cover refs, deliberately: `claim-ticket.sh` creates a
36
+ branch per ticket and `reap.sh` deletes them in the ref store every worktree
37
+ shares, so ordinary work moves refs several times a run and a per-gate
38
+ refusal on that is noise.
39
+
40
+ ## Re-pin first after a mid-run tooling fix
41
+
42
+ You just changed the instrument set under your own check, and this is the
43
+ only edit that legitimately does; skip it and the next gate refuses on your own
44
+ fix, which teaches you to ignore the refusal.
@@ -120,6 +120,13 @@ One flat namespace, generic filenames (`b.min.js`, `probe.mjs`) — one agent ov
120
120
 
121
121
  The axis is two-sided: the member partitions, and the parent assigns each child's path. `<scratch>` itself is assigned, never invented: the controller resolves it with `scratch-root.mjs` and writes the absolute path into each member's prompt. A member owns one directory — `<scratch>/impl-<N>/` for an implementer, `<scratch>/pr<N>/…` on the review side — and every agent it dispatches gets a directory under that one, unique to that child and never shared, written into the child's prompt as an absolute path; a child never derives its own. The review side is already laid out this way: `review-core.mjs` makes the run root `<scratch>/pr<N>/run-XXXXXXXX/` with `mktemp` — it appends `pr<N>/` to the scratch root it is handed and refuses a scratch already ending in one, so a review runner is given the root, never `<scratch>/pr<N>` — then writes `<run>/<dim>/` into each specialist's prompt and `<run>/verify-<dim>/f<i>-l<j>/` into each refuter's. A fix-applier makes its own run root the same way — `<scratch>/pr<N>/fix-XXXXXXXX/` with `mktemp` — then resolves `<run>/<finding>/` to an absolute path for the refuter it dispatches on a suggestion; the bare `<scratch>/pr<N>/<finding>/` is not that path, since finding ids restart at 1 each fix-applier round too, and a later round on the same PR would reach for the same directory a prior round's refuter left fixtures in. An implementer's child gets `<scratch>/impl-<N>/<childName>/`, named after its dispatch name. Refuters are grandchildren, not members, which is why the member partition alone is not the rule: the agents that actually collided were ones no member rule reaches, and a parent-assigned path is what reaches them. Read from the object store at a pinned ref, write only under own path.
122
122
 
123
+ ## The scratch root is computed, never invented and never recorded
124
+
125
+ No harness supplies a scratch directory to a member, so a member left to name its own
126
+ invents one — and the liveness read in **The tick's rows**, which looks at the files under the
127
+ directory a member's own prompt assigned it, then watches a directory nobody
128
+ else can find.
129
+
123
130
  ## IDE/harness diagnostics attribute by bare filename, with no path
124
131
 
125
132
  Probe copies carry same filenames as real tree, so specialist's throwaway mutation surfaces as errors that read exactly like live worktree's — and line numbers can plausibly line up with real in-flight edits. Never relay diagnostic without reproducing it in that member's specific worktree (re-run the check that raised it — the typechecker, the linter — from there). Ran twice in one session: clean first time (sibling's probe), genuinely broken second. Telling implementer to chase phantom in file it is mid-rewrite on is expensive failure.
@@ -129,6 +136,15 @@ Probe copies carry same filenames as real tree, so specialist's throwaway mutati
129
136
  Measured 2026-09-09 in a throwaway clone (`/tmp/fleet-probe`)
130
137
  that never touched the real checkout.
131
138
 
139
+ ## Claim, release and reap are unchanged on omp
140
+
141
+ **Claim, release and reap are unchanged on omp.** `claim-ticket.sh`,
142
+ `release-ticket.sh`, `reap.sh` and `inflight.sh` are git-native and operate on
143
+ the single shared ref store every worktree and the main checkout read and
144
+ write directly (**Phase 1**, the tick's reap, and `release-ticket.sh` under
145
+ **Failure handling**); nothing in omp's `task` model touches that
146
+ store, so none of the four scripts carries a dialect branch.
147
+
132
148
  ## A fleet member is never dispatched with omp's `isolated: true`
133
149
 
134
150
  **A fleet member is never dispatched with omp's `isolated: true`.** Measured:
@@ -220,6 +236,12 @@ the worktree, `git worktree list` and `git branch --list` from the main
220
236
  checkout reported the claim unchanged before, during and after — `inflight.sh`
221
237
  hides nothing from omp.
222
238
 
239
+ ## The controller's own cwd stays the main checkout
240
+
241
+ Nothing in the dispatch recipe above — the cwd recipe, the
242
+ isolated-workspace measurement, or the teardown scoping — moves
243
+ where the controller itself runs from.
244
+
223
245
  ## A pattern kill reaches siblings
224
246
 
225
247
  **A pattern kill reaches siblings — the implementer, fix-applier and finisher briefs forbid it.** `pkill -f
@@ -1,6 +1,6 @@
1
1
  # Member lifecycle: naming, fresh context, recovery
2
2
 
3
- Why member name load-bearing, why every member single-use, how a member's job outcome (`completed`/`failed`/`cancelled`) and peer liveness (`running`/`idle`/`parked`) decide its recovery. Assertions these justify live in SKILL.md's "Rules that fail silently", wake table, Reading a review result, Failure handling sections; evidence here.
3
+ Why member name load-bearing, why every member single-use, how a member's job outcome (`completed`/`failed`/`cancelled`) and peer liveness (`running`/`idle`/`parked`) decide its recovery. Assertions these justify live in SKILL.md's wake table, Dispatching the members a wake names, Reading a review result, A member's claims, Failure handling sections; evidence here.
4
4
 
5
5
  ## Every member is named
6
6
 
@@ -58,6 +58,28 @@ Reviewers go idle waiting on CI, won't resume alone — rebased, pushed, stopped
58
58
 
59
59
  Liveness says nothing: `idle` means "not currently executing", not "done"; finished member's finding may never arrive. In one run every member that looked dead had its work in git or on PR — pushed commits, self-removed label, applied ruling. Read `gh pr view`, `gh run view`, `git -C <worktree> status` before messaging: one command settles what round-trip usually doesn't, and member that ignored one ping tends to ignore second. When you message, send specific next action, never "what is your status".
60
60
 
61
+ ## A report is delivered, not implied by ending a turn
62
+
63
+ **A report is delivered, not implied by ending a turn.** Every member owes
64
+ the controller one report naming its outcome before it exits — including a
65
+ member whose prompt carries no explicit report line. A member that does the work
66
+ correctly and ends its turn has reported nothing: the controller learns the
67
+ outcome only by re-reading artifacts and pinging, and a finding held only in
68
+ that member's context is lost. Members did exactly this in one run —
69
+ finishers, a reviewer and merge bots alike, the work done and only the
70
+ delivery missing. Every brief's report paragraph says it.
71
+
72
+ ## You never write the do-not-acknowledge line yourself
73
+
74
+ **The implementer, fix-applier, finisher, merge-bot and review-runner briefs
75
+ each tell the member you do not acknowledge reports; you never write that line
76
+ yourself.** A member cannot see whether its report arrived, so silence
77
+ from you is indistinguishable from loss and it re-sends — three members did in
78
+ one run, each burning a turn, and in all three the original had in fact arrived.
79
+ Each of those templates under `briefs/` carries the line: *"The controller does
80
+ not acknowledge reports. Send once and exit; never re-send unless the controller
81
+ asks by name."*
82
+
61
83
  ## A missing report is not evidence the member failed to send one
62
84
 
63
85
  **But a missing report is NOT evidence the member failed to send one — the
@@ -84,6 +106,15 @@ Note this cuts against the paragraph above only in appearance: the default stays
84
106
  *send once*, because unprompted re-sends were measured 2-to-1 false. The ask is
85
107
  the controller's move, not the member's.
86
108
 
109
+ ## Never read a member's silence as assent
110
+
111
+ **Your OWN messages can vanish the same way, and that half is not recoverable by
112
+ a re-send.** Two controller rulings were lost in one run, both with a
113
+ `success: true` receipt: a member proceeded on its stated default and filed a
114
+ tracker issue the lost ruling had said not to file. The confirmation-state
115
+ sentence SKILL.md's **A member's claims** requires is the only thing that made
116
+ either loss visible.
117
+
87
118
  ## A member runs its test suite in the foreground and blocks on it
88
119
 
89
120
  **A member runs its test suite in the foreground and blocks on it.**
@@ -1,6 +1,6 @@
1
1
  # Landing a change on main
2
2
 
3
- Evidence for the rules SKILL.md states in **Fix the tooling mid-run** and in the `ready-to-merge` wake, keyed by the rule each justifies.
3
+ Evidence for the rules SKILL.md states in **A mid-run tooling fix** and in the `ready-to-merge` wake, keyed by the rule each justifies.
4
4
 
5
5
  ## A mid-run tooling fix lands through a PR
6
6
 
@@ -517,6 +517,19 @@ mention of the controller is not a statement that the reader is one of its
517
517
  members. That skill keeps its condition, so a solo session still runs the
518
518
  interactive path; this block is what puts a member on the fleet side of it.
519
519
 
520
+ **Prose-first** and **wrongly-REFUSE** are not padding — they are the case
521
+ which shipped with **enumerate-and-declare** already in its prompt. It missed a
522
+ shape its own ticket named in passing, because it enumerated from the mechanism
523
+ instead of the text; and it introduced a regression refusing every `node --test`
524
+ flag, because it enumerated what the guard should catch and never what it could
525
+ wrongly refuse.
526
+
527
+ The commit-incrementally block is not optional. A member that goes idle mid-task
528
+ leaves its diff only in the worktree, and the controller cannot reap, replace,
529
+ or even see it — `worktree-audit.sh`'s committed-vs-uncommitted split is exactly
530
+ what decides whether a replacement redoes or destroys work. Observed twice in
531
+ one run.
532
+
520
533
  ## Default: In-flight bound 5
521
534
 
522
535
  **Default: In-flight bound 5, fixed for the run.** The level is set by load, not
@@ -147,8 +147,8 @@ The full text of each row `fleet-tick.mjs` prints and what it asks of the contro
147
147
  HEAD is off the run's branch. Never revert the
148
148
  paths yourself, and never re-baseline over paths nobody has resolved. The
149
149
  changes of yours that trip it are a mid-run tooling fix edited in this
150
- checkout and the close-out commit's round trip through a branch; **Fix the
151
- tooling mid-run** and the commit section of references/close-out.md say how
150
+ checkout and the close-out commit's round trip through a branch; SKILL.md's
151
+ **A mid-run tooling fix** and the commit section of references/close-out.md say how
152
152
  each is cleared.
153
153
  The maintainer may instead ignore a stray path that is untracked and new since the baseline: an entry in `.git/info/exclude` or in the global excludes file hides it from the check, and the hold clears on the next tick with no re-baseline. Ignoring never clears a tracked or staged file's change, which `git status` reports whatever the excludes say, nor a pattern that also matches a path the baseline holds, which then reads as changed. Ignoring is the maintainer's decision; the controller never writes an exclude or a `.gitignore` entry. Keep the pattern narrow, because an ignored path is invisible to this check, so a member write that matches it is never seen.
154
154
  - `MAIN-CHECKOUT-UNKNOWN …` with `HOLD (main checkout unknown)` — the tick