@feigi/fleet-ctl 3.23.10 → 3.23.11

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.
@@ -212,7 +212,7 @@ For each labeled PR clearing the hold rule, lowest first:
212
212
 
213
213
  `--pre` is the head you recorded at **The labelled head**; `--post` is the head step 1's rebase produced — omit it when no rebase ran, which is the same as passing `pre` twice. `pr<N>` is this PR and `merge-bot-<n>` is your own name, so the reading lands under a directory only you write, never in the scratch root by itself: that root is injected into every dispatched member and shared with every sibling. The gate is read-only — it runs the instrument re-check, `gh pr view <pr> --json labels,reviewDecision,headRefOid` and `ci-state.mjs --pr <pr>`, answers their conjunction, and never merges, labels, rebases or waits. Stdout is exactly one JSON line, `{pr, verdict, reason, head, pre, post, behind, instruments, ci}`; stderr is its children's diagnostics, so never fold `2>&1` into the line you read. Read `rc` straight off the call, as above — never through a pipe.
214
214
 
215
- 1. **Run it.** Exit 0 → go to 2. Exit 1 with a `ci:…` `reason`, `ci.runId` set and `ci.status` not `completed` → the run has started but not finished: wait on `ci.runId` (step 2), then run it again. Exit 1 with `ci.runId` **null** (`reason` starts `no CI run whose headSha equals`) → GitHub has not yet registered a run for this head at all — right after `gh pr update-branch --rebase`, the id step 2's wait blocks on does not exist yet, so never route this into step 2's `gh run watch`/`gh run view <run-id>` with a null id. Wait 15s and run the gate again instead, capped at 20 attempts (~5 minutes); no run within that cap → skip the PR and report `<reason>-#<pr>`, the same as any other exit 1. Any other exit 1 → skip the PR, report `<reason>-#<pr>` (`head-moved-after-label-#<pr>`, `label-pulled-#<pr>`, `changes-requested-#<pr>` …), leave the label where it is, and move on. Exit 2 → stop on that PR and report `<reason>-#<pr>`: the gate could not evaluate — a finding about the tree, the tooling or the API, never about the PR. Re-run once only for `rate-limited`, a quota refusal that clears by itself.
215
+ 1. **Run it.** Exit 0 → go to 2. Exit 1 with a `ci:…` `reason`, `ci.runId` set and `ci.status` not `completed` → the run has started but not finished: wait on `ci.runId` (step 2), then run it again. Exit 1 with `ci.runId` **null** (`reason` starts `no CI run whose headSha equals`) → GitHub has not yet registered a run for this head at all — right after `gh pr update-branch --rebase`, the id step 2's wait blocks on does not exist yet, so never route this into step 2's `gh run view <run-id>` poll with a null id. Wait 15s and run the gate again instead, capped at 20 attempts (~5 minutes); no run within that cap → skip the PR and report `<reason>-#<pr>`, the same as any other exit 1. Any other exit 1 → skip the PR, report `<reason>-#<pr>` (`head-moved-after-label-#<pr>`, `label-pulled-#<pr>`, `changes-requested-#<pr>` …), leave the label where it is, and move on. Exit 2 → stop on that PR and report `<reason>-#<pr>`: the gate could not evaluate — a finding about the tree, the tooling or the API, never about the PR. Re-run once only for `rate-limited`, a quota refusal that clears by itself.
216
216
  2. **Run it again immediately before `gh pr merge`, and merge only on that second exit 0.** Anything else takes the same arms as 1, and nothing merges.
217
217
 
218
218
  Each `reason` is a fact the gate now enforces, not a step you run:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feigi/fleet-ctl",
3
- "version": "3.23.10",
3
+ "version": "3.23.11",
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": {
@@ -1273,9 +1273,8 @@ test("step 3 waits on an unfinished run instead of skipping the PR, and only the
1273
1273
  // registered a run for the rebased head: `ci.runId` and `ci.status` both read
1274
1274
  // `null`, which the arm above's "`ci.status` not `completed`" condition also
1275
1275
  // matches — collapsed together, a bot reading this prose is sent to wait on
1276
- // `ci.runId` (step 2) with a `null` id, i.e. `gh run watch null` / `gh run
1277
- // view null`. This pins the no-run case as its own arm, never routed through
1278
- // step 2's blocking wait.
1276
+ // `ci.runId` (step 2) with a `null` id, i.e. `gh run view null`. This pins
1277
+ // the no-run case as its own arm, never routed through step 2's blocking wait.
1279
1278
  test("step 3 waits and re-gates on a null ci.runId instead of routing it into step 2's wait", () => {
1280
1279
  const s = step3();
1281
1280
  assert.match(
@@ -1285,7 +1284,7 @@ test("step 3 waits and re-gates on a null ci.runId instead of routing it into st
1285
1284
  );
1286
1285
  assert.match(
1287
1286
  s,
1288
- phrase("never route this into step 2's `gh run watch`/`gh run view <run-id>` with a null id"),
1287
+ phrase("never route this into step 2's `gh run view <run-id>` poll with a null id"),
1289
1288
  "step 3 no longer warns against waiting on step 2's blocking call with a null run id",
1290
1289
  );
1291
1290
  assert.match(