@feigi/fleet-ctl 3.82.2 → 3.82.4
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/agents/fleet-finisher.agent.md +1 -1
- package/agents/fleet-merge-bot.agent.md +5 -5
- package/agents/fleet-recipe-deriver.agent.md +4 -3
- package/package.json +1 -1
- package/skills/next-ticket/SKILL.md +2 -0
- package/skills/run-team/SKILL.md +483 -854
- package/skills/run-team/references/ci-and-staleness.md +36 -2
- package/skills/run-team/references/ci-watcher.md +40 -1
- package/skills/run-team/references/correction-tickets.md +6 -0
- package/skills/run-team/references/finisher.md +13 -1
- package/skills/run-team/references/member-lifecycle.md +2 -2
- package/skills/run-team/references/merge-bot.md +6 -1
- package/skills/run-team/references/phase-0.md +6 -6
- package/skills/run-team/references/pull.md +4 -4
- package/skills/run-team/references/reaping.md +1 -1
- package/skills/run-team/references/reviewers.md +36 -1
- package/skills/run-team/references/shell-traps.md +5 -5
- package/skills/run-team/references/tick-rows.md +4 -4
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# CI reading and staleness
|
|
2
2
|
|
|
3
3
|
Why green not verdict, why absence is its own verdict, why conclusion not
|
|
4
|
-
stable, why behind-count only honest signal
|
|
5
|
-
|
|
4
|
+
stable, why behind-count only honest signal, why the tick runs on every wake
|
|
5
|
+
and why the beat is shaped as it is. Assertions live in SKILL.md's wake table,
|
|
6
|
+
**Nothing to do: arm the beat** and **Own the CI waits**; evidence here.
|
|
6
7
|
|
|
7
8
|
## Own the CI waits — members cannot hold across a run
|
|
8
9
|
|
|
@@ -13,6 +14,9 @@ latest runs, keyed `<run-id>:<attempt>:<conclusion>` so each terminal state
|
|
|
13
14
|
fires once, emits behind-count with it: `success` on branch 8 behind not
|
|
14
15
|
actionable, that distinction most of traffic.
|
|
15
16
|
|
|
17
|
+
Members are turn-based and cannot hold across a ten-minute
|
|
18
|
+
run — they rebase, push, stop.
|
|
19
|
+
|
|
16
20
|
## Run-binding: take the whole row from one read
|
|
17
21
|
|
|
18
22
|
Take run id, head, conclusion from **one** `gh run list --json` row. Watcher
|
|
@@ -100,3 +104,33 @@ agent — measured twice, see **A member runs its test suite in the foreground**
|
|
|
100
104
|
roughly two hours — and `run-merge-bot.md`'s CI gate states the general rule:
|
|
101
105
|
"whatever wakes you is external and may never come". So hold the wait yourself,
|
|
102
106
|
exactly the way that gate does:
|
|
107
|
+
|
|
108
|
+
## Tick on every wake
|
|
109
|
+
|
|
110
|
+
**Why every wake.** A merge cascade is a firehose of merges, CI greens and
|
|
111
|
+
rebases that holds your attention on the merge side while the implementer side
|
|
112
|
+
drains to 0 and stays there — 0 implementers emit no completion event, so a
|
|
113
|
+
refill that waited for one would never fire. A tick on every wake you are
|
|
114
|
+
already handling is what makes that drain visible. **Every wake is still an
|
|
115
|
+
edge, so none covers a fully drained queue**: no members and no open PRs is no
|
|
116
|
+
event at all, and the tick goes unrun. That state is the heartbeat's.
|
|
117
|
+
|
|
118
|
+
## The beat's ceiling is a real bound
|
|
119
|
+
|
|
120
|
+
The interval doubles while the fleet asks for nothing and stops at `--ceiling`,
|
|
121
|
+
so a quiet night costs ~26 wakes instead of ~96. **The ceiling is a real bound,
|
|
122
|
+
not a formality**: supply grows from OUTSIDE the fleet — a ticket you triage,
|
|
123
|
+
one a reviewer filed `needs-triage` included — and that arrival emits no
|
|
124
|
+
event either, so the ceiling is your worst-case latency for noticing it.
|
|
125
|
+
Do not raise it past 30 minutes.
|
|
126
|
+
|
|
127
|
+
## When you stop on purpose, say why
|
|
128
|
+
|
|
129
|
+
The beat leaves a dated mark on every hold, so a run
|
|
130
|
+
that dies abruptly (a crashed harness, a closed terminal, an OOM kill) is
|
|
131
|
+
detected by its mark going stale. A run that stops KNOWINGLY can do better
|
|
132
|
+
than being detected, and nothing else can write the reason afterwards:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
~/.fleet/bin/fleet-run fleet-heartbeat.mjs --stop "budget exhausted"
|
|
136
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The CI watcher: why its block is shaped the way it is
|
|
2
2
|
|
|
3
|
-
Evidence for the rules SKILL.md states beside
|
|
3
|
+
Evidence for the rules SKILL.md states beside its watcher block, under **Own the CI waits**, keyed by the rule each justifies.
|
|
4
4
|
|
|
5
5
|
## The pass is budgeted at 4 core REST units per listed PR
|
|
6
6
|
|
|
@@ -97,3 +97,42 @@ fixed interval stalls the fleet longer than the outage itself would have. A
|
|
|
97
97
|
retry-and-wait loop is what turns a 60s outage into a stalled member. Report it,
|
|
98
98
|
keep the local work moving (git, tests and mutation runs are all unaffected),
|
|
99
99
|
re-probe.
|
|
100
|
+
|
|
101
|
+
## A probe that cannot read is a transition event, not silence
|
|
102
|
+
|
|
103
|
+
The natural
|
|
104
|
+
watcher shape — `st=$(ci-state.mjs --pr "$pr" 2>/dev/null) || continue` — fails
|
|
105
|
+
closed correctly and then throws the failure away, so a rate-limited probe emits
|
|
106
|
+
NOTHING and "no event" becomes indistinguishable from "not green yet". Every
|
|
107
|
+
open PR stalls unlabelled while the run reads as merely quiet; observed live
|
|
108
|
+
twice.
|
|
109
|
+
|
|
110
|
+
## It has to latch
|
|
111
|
+
|
|
112
|
+
An unlatched line at a 120s poll gets the Monitor
|
|
113
|
+
auto-stopped for volume during even a two-minute outage, reintroducing the same
|
|
114
|
+
blindness by another route.
|
|
115
|
+
|
|
116
|
+
## Guard every probe, not just the ones with a verdict field
|
|
117
|
+
|
|
118
|
+
The block makes
|
|
119
|
+
several API calls, and `gh pr list` is the one that looks like
|
|
120
|
+
plumbing rather than a probe. A non-quota failure there (secondary/abuse limit,
|
|
121
|
+
5xx, network blip, expired token) does not lower `.resources.core.remaining`, so
|
|
122
|
+
the budget gate clears, and an unguarded `for pr in $(gh pr list …)` then
|
|
123
|
+
iterates zero times over the empty substitution: no error, no latch, no DEGRADED
|
|
124
|
+
line — total silence per tick, which is exactly what this section exists to
|
|
125
|
+
remove.
|
|
126
|
+
|
|
127
|
+
## A readable list can still be a short one
|
|
128
|
+
|
|
129
|
+
`gh pr list` stops at its
|
|
130
|
+
`--limit` — 30 when the flag is absent — with exit 0 and no warning, so a list
|
|
131
|
+
that fills its limit reads exactly like one that happens to be that long, and
|
|
132
|
+
every open PR past it goes unwatched without a line.
|
|
133
|
+
|
|
134
|
+
## Judge `ci-state` on its payload, never its exit code
|
|
135
|
+
|
|
136
|
+
`not-green` is an
|
|
137
|
+
ordinary, frequent state that exits non-zero, so a watcher treating any non-zero
|
|
138
|
+
exit as an outage misfires constantly on PRs that are merely in progress.
|
|
@@ -61,3 +61,9 @@ benign-looking answer. Measured once: `grep -c 'settled by:\s+x'` answers
|
|
|
61
61
|
an absence counts only after the same command, with the same flags, finds a
|
|
62
62
|
line known to match: a command that cannot find a match settles nothing, and
|
|
63
63
|
under `Verified:` its zero reads as a checked absence.
|
|
64
|
+
|
|
65
|
+
## The settling command for "which files read X" is never a literal-path grep
|
|
66
|
+
|
|
67
|
+
The path is assembled in more than one spelling here, one of them behind
|
|
68
|
+
a local `read` helper, so a grep for the path string answers with the files that
|
|
69
|
+
merely *name* it — comments included — and misses the files that build it.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The finisher: why its gate and duties are shaped the way they are
|
|
2
2
|
|
|
3
|
-
Evidence for the finisher rules SKILL.md
|
|
3
|
+
Evidence for the finisher rules the finisher's agent body, `agents/fleet-finisher.agent.md`, and SKILL.md's **The finisher gate** state, keyed by the rule each justifies.
|
|
4
4
|
|
|
5
5
|
## An unanswered question from the member is an outbox item
|
|
6
6
|
|
|
@@ -107,3 +107,15 @@ to the fleet's worktree home, so a tree under `<scratch>` is a `kept` entry
|
|
|
107
107
|
it reports and never collects. No claim built the tree you added, so it has
|
|
108
108
|
`./agent-test` only if the repository tracks one — run that or `<testCmd>`
|
|
109
109
|
in it, per `references/isolation.md`, **A reused worktree may lack the runner**.
|
|
110
|
+
|
|
111
|
+
## Take the label off before you approve any push
|
|
112
|
+
|
|
113
|
+
The label is a
|
|
114
|
+
verdict on the tree the finisher read, and a GitHub label does not follow the
|
|
115
|
+
branch — on one PR it survived a push and came to sit on a commit nobody had
|
|
116
|
+
audited. The token is how the tick tells your
|
|
117
|
+
removal from a finisher that never labelled — a tick run between a removal and
|
|
118
|
+
its token re-dispatches a finisher into your take-off window. The label goes
|
|
119
|
+
before the approval because removing the artifact the merge bot gates on is
|
|
120
|
+
the reliable stop, where messaging the bot races it, and a label has been
|
|
121
|
+
observed holding until after an abort message arrived.
|
|
@@ -1,10 +1,10 @@
|
|
|
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",
|
|
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.
|
|
4
4
|
|
|
5
5
|
## Every member is named
|
|
6
6
|
|
|
7
|
-
The name is the ledger token and the hub address (`hub send <name>`, `agent://<name>`); omit it and the ledger cannot match the member. Names follow unit of work: `impl-<issue#>`, `fix-pr-<pr#>` (default path's applier), `review-pr-<pr#>` (the PR's first reviewer; every later one on the PR — failed-runner retry, past-pin re-review — takes the next letter the PR's row does not carry yet, `review-pr-<pr#>-b`, `-c`, …; a reviewer's launch and result are recorded on the PR's row with `ledger.mjs review <pr#>`), `finisher-pr-<pr#>` (
|
|
7
|
+
The name is the ledger token and the hub address (`hub send <name>`, `agent://<name>`); omit it and the ledger cannot match the member. Names follow unit of work: `impl-<issue#>`, `fix-pr-<pr#>` (default path's applier), `review-pr-<pr#>` (the PR's first reviewer; every later one on the PR — failed-runner retry, past-pin re-review — takes the next letter the PR's row does not carry yet, `review-pr-<pr#>-b`, `-c`, …; a reviewer's launch and result are recorded on the PR's row with `ledger.mjs review <pr#>`), `finisher-pr-<pr#>` (the finisher gate's finisher), `merge-bot-<n>` (one merge pass; `ledger.mjs dispatch merge-bot` numbers it from the ledger's `## Dispatched` list — a per-run counter, never a PR).
|
|
8
8
|
|
|
9
9
|
## The coordination contract
|
|
10
10
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Landing a change on main
|
|
2
2
|
|
|
3
|
-
Evidence for the
|
|
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.
|
|
4
4
|
|
|
5
5
|
## A mid-run tooling fix lands through a PR
|
|
6
6
|
|
|
@@ -12,3 +12,8 @@ end of a 7-PR run. A mid-run tooling fix goes through a PR like every other
|
|
|
12
12
|
change, and that PR faces every required check any other PR faces — the set
|
|
13
13
|
`.github/rulesets/main.json` declares — so it carries a release label too.
|
|
14
14
|
Plan it as a PR the run's own merge queue lands, never as an edit-and-continue.
|
|
15
|
+
|
|
16
|
+
## You own the merge watcher, not the bot
|
|
17
|
+
|
|
18
|
+
A dying member takes a watcher down with it
|
|
19
|
+
and the queue stops silently.
|
|
@@ -72,7 +72,7 @@ walks the run from "could not look" to "certified clean" in one step, so
|
|
|
72
72
|
`--record` itself refuses over a baseline it cannot read, over one a newer
|
|
73
73
|
plugin wrote, and on any git read that fails. The exceptions are an
|
|
74
74
|
older-version baseline and a baseline whose commit is missing from the
|
|
75
|
-
repository;
|
|
75
|
+
repository; the tick's rows say how each is cleared.
|
|
76
76
|
`main-checkout.mjs --check` prints the same answer
|
|
77
77
|
without a tick.
|
|
78
78
|
On `MAIN-CHECKOUT-DIRTY` 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-record. 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, never the controller's: 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 later member write that matches it is never seen.
|
|
@@ -273,15 +273,15 @@ Before the rotation, `phase0.mjs` puts the held-aside receipt (`phase0.previous.
|
|
|
273
273
|
`~/.fleet/bin/fleet-run reclaim-scratch.mjs --root <scratch>
|
|
274
274
|
--after-rotate --apply`, `<scratch>` the path `scratch-root.mjs` prints,
|
|
275
275
|
so a run that merges nothing still reclaims the previous run's leftovers
|
|
276
|
-
(
|
|
276
|
+
(`reclaim-scratch.mjs`'s header says what it removes).
|
|
277
277
|
`--after-rotate` goes on this call always and on no other: rotate leaves
|
|
278
278
|
no ledger behind when it carried nothing, and the flag reads that absence
|
|
279
279
|
as "no live token" — rotate's own verdict — where without it the script
|
|
280
280
|
refuses. A ledger rotate did write is read as usual. Record nothing on the
|
|
281
281
|
ledger and act on nothing in its receipt.
|
|
282
282
|
|
|
283
|
-
Without `--after-rotate` — the per-pass sweep
|
|
284
|
-
merge pass
|
|
283
|
+
Without `--after-rotate` — the per-pass sweep `tick --apply` runs after each
|
|
284
|
+
merge pass — no ledger refuses the call (exit 2, nothing removed):
|
|
285
285
|
mid-run there is always one, so its absence is a fault to look at, never
|
|
286
286
|
permission. A `gh` that cannot answer keeps every PR- and ticket-keyed entry.
|
|
287
287
|
It prints one JSON receipt — `removed`, `kept` and `failed`, each entry with
|
|
@@ -342,8 +342,8 @@ invalid — a command that no longer runs, or an Install step that changed
|
|
|
342
342
|
the tree — is the one mid-run trigger: dispatch `derive-recipe` again
|
|
343
343
|
exactly as above, then retry what refused. The Install step's refusals
|
|
344
344
|
fire after the claim has labelled the ticket and created its worktree, so
|
|
345
|
-
release that claim with `release-ticket.sh` first (
|
|
346
|
-
|
|
345
|
+
release that claim with `release-ticket.sh` first (SKILL.md's Failure handling
|
|
346
|
+
row for a claim abandoned mid-run). A red suite never triggers it: a failing test is
|
|
347
347
|
a finding, not a stale Recipe. **Stall:** `RECIPE NOT PROVEN` from any
|
|
348
348
|
derivation, or a second invalid-cache refusal after this run's one
|
|
349
349
|
re-derivation, halts the run — no Pull, no claim, no review dispatch — and
|
|
@@ -232,7 +232,7 @@ terminating in exactly one dispatch or an empty shortlist:
|
|
|
232
232
|
`tier=<CELL>` exactly when `DRAW` is not `-`; then phase 2 at once: claim
|
|
233
233
|
what you are about to dispatch; dispatch what you have just claimed. Router
|
|
234
234
|
exit 2 → no row and no dispatch: release the claim (`release-ticket.sh`, SKILL.md's
|
|
235
|
-
|
|
235
|
+
Failure handling row for a claim abandoned mid-run) and emit an event, a probe that could not look — a table the router
|
|
236
236
|
refuses refuses every Pull.
|
|
237
237
|
|
|
238
238
|
**The Pull table** — every verdict a Pull can reach, and the one thing each
|
|
@@ -259,7 +259,7 @@ dispatch.
|
|
|
259
259
|
## Relabels and exclusions
|
|
260
260
|
|
|
261
261
|
**A Pull never re-admits a ticket already relabelled.** The shortlist file can
|
|
262
|
-
be older than a relabel — this run's own Pulls, or a
|
|
262
|
+
be older than a relabel — this run's own Pulls, or a bail's relabel under **Relabel causes** — so
|
|
263
263
|
read the ticket's labels in step 3's full read (`--json
|
|
264
264
|
title,body,comments,labels`) and drop it when `ready-for-agent` is gone. A Pull
|
|
265
265
|
that trusted a list built before the relabel landed would hand a ticket the
|
|
@@ -324,8 +324,8 @@ its re-derive-once rule.
|
|
|
324
324
|
|
|
325
325
|
**Materialize the isolation envelope as a file, not a briefing.** The claim
|
|
326
326
|
writes the runner, `agent-test`, into the fresh worktree it creates: an exec of
|
|
327
|
-
the Recipe's Test entrypoint, through the suite-slot wrapper (see
|
|
328
|
-
|
|
327
|
+
the Recipe's Test entrypoint, through the suite-slot wrapper (see `suite-slot.mjs`'s
|
|
328
|
+
header), that exports one port triple derived from `<N>`
|
|
329
329
|
(so collisions stay impossible), kept out of `git status` through the
|
|
330
330
|
repository's exclude file. A repository that tracks its own `agent-test` keeps
|
|
331
331
|
it byte-identical, with whatever isolation that repository built, and takes the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Reaping merged branches and worktrees
|
|
2
2
|
|
|
3
|
-
Why reap runs after each merge pass, why `commit-commands:clean_gone` disqualified, why hand-rolled reap shaped this way (implemented by `reap.sh`), and why a claim that never became a PR needs a different script (`release-ticket.sh`). Assertions these justify live in SKILL.md's
|
|
3
|
+
Why reap runs after each merge pass, why `commit-commands:clean_gone` disqualified, why hand-rolled reap shaped this way (implemented by `reap.sh`), and why a claim that never became a PR needs a different script (`release-ticket.sh`). Assertions these justify live in SKILL.md's **Merge-bot pass reports done** wake and its Failure handling row for a claim abandoned mid-run; evidence here.
|
|
4
4
|
|
|
5
5
|
## Reap after each merge pass, not once at the end
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Reviewers: the evidence behind the review-side rules
|
|
2
2
|
|
|
3
|
-
Evidence for rules SKILL.md states in Phase 2 and **
|
|
3
|
+
Evidence for rules SKILL.md states in Phase 2 and **Reading a review result**, keyed by the rule each justifies.
|
|
4
4
|
|
|
5
5
|
## A sizing verdict is a measurement only if it post-dates the run that produced it
|
|
6
6
|
|
|
@@ -96,3 +96,38 @@ with `--detach`, and there `git branch --show-current` prints nothing and exits
|
|
|
96
96
|
--branch "$(git branch --show-current)"` is handed an empty branch and exits 2
|
|
97
97
|
before pushing anything. A brief rendered without it would leave the push no
|
|
98
98
|
destination at all, which is why the renderer requires it.
|
|
99
|
+
|
|
100
|
+
## A review runs off your turn and relays nothing
|
|
101
|
+
|
|
102
|
+
**`selectDimensions` sizes the fan-out to the diff and the verify budget
|
|
103
|
+
follows severity.** It cuts one immutable
|
|
104
|
+
snapshot, verifies every critical/important finding adversarially,
|
|
105
|
+
re-dispatches each crashed specialist and each crashed refuter pair once before
|
|
106
|
+
it assembles the result, and has `agent()` return **into the script**, so no
|
|
107
|
+
report can go undelivered and you relay nothing — the delivery failure that
|
|
108
|
+
cost one fleet five reports on one PR and four on another. None of it runs on
|
|
109
|
+
your turn: a review takes 20-40 minutes, and a controller that held its turn
|
|
110
|
+
for each one, one at a time, was what left implementer slots empty.
|
|
111
|
+
|
|
112
|
+
## `testEnvironment` says what the shared test run is evidence about
|
|
113
|
+
|
|
114
|
+
The snapshot is `git archive`d and then
|
|
115
|
+
`git init`ed with one commit, and the block that cuts it compares that commit's
|
|
116
|
+
tree hash against the reviewed commit's: verified, a suite run in the
|
|
117
|
+
snapshot collects and runs what a checkout does, and a red is a fact about the
|
|
118
|
+
tree. UNVERIFIED, it is not — tests that ask git what ships decline or fail for
|
|
119
|
+
the environment, and this field carries which line of the cut failed.
|
|
120
|
+
|
|
121
|
+
## `dimensionsUnrun` names a gap; an empty dimension does not
|
|
122
|
+
|
|
123
|
+
The rule this replaces — treat any dimension with nothing in
|
|
124
|
+
`survived`/`refuted`/`unverified` as unrun — was the workaround for having no
|
|
125
|
+
such field, and it over-refuses in the direction that costs work: a dimension
|
|
126
|
+
that ran clean has nothing in those three either.
|
|
127
|
+
|
|
128
|
+
## A failed review is a failure event, not a clean review
|
|
129
|
+
|
|
130
|
+
The review throws on missing `args.pr`/`args.worktree` and on a snapshot agent
|
|
131
|
+
that returned no tree, and a failure surfaces mid-loop, where "react, never
|
|
132
|
+
block" makes it easy to log and carry on — leaving a PR that *reads* as
|
|
133
|
+
reviewed and is not.
|
|
@@ -6,9 +6,9 @@ Monitor and Bash commands run under **zsh**, and the shell in SKILL.md is
|
|
|
6
6
|
prescriptive — copied verbatim into a watcher or into the merge bot's agent
|
|
7
7
|
body. So every trap here is a zsh-vs-bash difference that ships as a
|
|
8
8
|
green-looking gate: correct in the shell you tested in, silently wrong in the
|
|
9
|
-
one it runs in.
|
|
10
|
-
any of it. The merge bot's agent body is the one place this block is
|
|
11
|
-
whole (see **
|
|
9
|
+
one it runs in. **Own the CI waits**'s monitor paragraph points here instead of
|
|
10
|
+
restating any of it. The merge bot's agent body is the one place this block is
|
|
11
|
+
copied whole (see **The merge bot**); the other deliberate copy is named at the end.
|
|
12
12
|
|
|
13
13
|
**zsh word-splits an unquoted command substitution's result; it does NOT split an unquoted parameter expansion.**
|
|
14
14
|
|
|
@@ -26,12 +26,12 @@ $prs
|
|
|
26
26
|
EOF
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
The inline `$(printf '%s\n' "$prs")` form splits correctly too and is what
|
|
29
|
+
The inline `$(printf '%s\n' "$prs")` form splits correctly too and is what the watcher block under **Own the CI waits** uses, but it splits on *every* whitespace character, so it is safe only for values that cannot contain one — PR numbers cannot, branch names and paths can.
|
|
30
30
|
|
|
31
31
|
**`</dev/null` every command inside a `while read` loop.**
|
|
32
32
|
|
|
33
33
|
**A probe that could not look emits an event; it never `continue`s.** Stated in
|
|
34
|
-
full at *A probe that cannot read is a transition event, not silence*
|
|
34
|
+
full at *A probe that cannot read is a transition event, not silence* under **Own the CI waits**,
|
|
35
35
|
and it binds every watcher in SKILL.md rather than that one block: the third
|
|
36
36
|
value beside pass and fail is what turns a broken loop into a loud failure
|
|
37
37
|
instead of a quiet one. A guard that drops an unreadable probe — `[ -n "$json" ]
|
|
@@ -21,14 +21,14 @@ The full text of each row `fleet-tick.mjs` prints and what it asks of the contro
|
|
|
21
21
|
filter kept out of `supply`, read off the shortlist's `dropped`. Never
|
|
22
22
|
actionable. Log them as specs awaiting to-tickets (phase 0), not as no work.
|
|
23
23
|
- `DISPATCH fix-pr PR#<M> …` — a fix-applier per PR, for a review's survived or unverified findings,
|
|
24
|
-
a dispositions mismatch no retry has answered, or a `conflict-hold:#<M>` — the merge bot's, or one you recorded off a `CONFLICT` line (**
|
|
24
|
+
a dispositions mismatch no retry has answered, or a `conflict-hold:#<M>` — the merge bot's, or one you recorded off a `CONFLICT` line (**The fix-applier**). Printed
|
|
25
25
|
ahead of reviews on purpose: finishing what is started beats starting more.
|
|
26
26
|
- `DISPATCH review PR#<M> …` — a review per PR, oldest first, off your turn
|
|
27
|
-
(**
|
|
28
|
-
- `DISPATCH merge-bot` — the next `merge-bot-<n>` (**
|
|
27
|
+
(**The reviewer**).
|
|
28
|
+
- `DISPATCH merge-bot` — the next `merge-bot-<n>` (**The merge bot**).
|
|
29
29
|
- `DISPATCH finisher PR#<M> …` — a finisher settled `labelled`, yet the open
|
|
30
30
|
list shows PR M without `ready-to-merge`, and no `label-off=` (**Run ledger**)
|
|
31
|
-
accounts for the removal. Apply the finisher gate (**
|
|
31
|
+
accounts for the removal. Apply the finisher gate (**The finisher gate**: the
|
|
32
32
|
`check` job green on the current head), then dispatch the next-suffix
|
|
33
33
|
finisher at that head, one letter past the highest the PR has used: render
|
|
34
34
|
its brief with `dispatch-prompt.mjs render finisher --kind repair --member
|