@feigi/fleet-ctl 3.86.0 → 3.86.1

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.
@@ -76,7 +76,7 @@ jq '.refuted' "$R" # refuted: your
76
76
 
77
77
  `eval` loading `scripts/review-eval.mjs` through the Resolver (`fleet-run --path review-eval.mjs`) and calling `runReviewOnOmp({pr, branch, worktree, testCmd, scratch})` is the fleet's **default** review path. Its `scratch` is the scratch root, never `<scratch>/pr<N>`: the review creates that partition itself and refuses a `scratch` already ending in one, or one that is not an absolute path.
78
78
 
79
- `$(~/.fleet/bin/fleet-run --root)/skills/run-team/SKILL.md`'s Reviewers section owns its dispatch. It cuts the snapshot, sizes the fan-out, adversarially verifies every **critical/important** finding, and has `agent()` return **into the script**, so no report can go undelivered, the failure that cost one fleet five reports on one PR and four on another. It does **not** verify the `suggestion` band — that gets 0 refuters by policy, which is why step 2 checks each one itself — an in-scope one before applying it, an out-of-scope one above the claim bar before filing it. **You cannot run it** — the one member that runs it is the `review-pr-<pr#>` runner (`agents/fleet-review-runner.agent.md`), never a reader of this file.
79
+ `$(~/.fleet/bin/fleet-run --root)/skills/run-team/SKILL.md`'s **The reviewer** bullet owns its dispatch, and `references/reviewers.md` (**A review runs off your turn and relays nothing**) says what it does. It cuts the snapshot, sizes the fan-out, adversarially verifies every **critical/important** finding, and has `agent()` return **into the script**, so no report can go undelivered, the failure that cost one fleet five reports on one PR and four on another. It does **not** verify the `suggestion` band — that gets 0 refuters by policy, which is why step 2 checks each one itself — an in-scope one before applying it, an out-of-scope one above the claim bar before filing it. **You cannot run it** — the one member that runs it is the `review-pr-<pr#>` runner (`agents/fleet-review-runner.agent.md`), never a reader of this file.
80
80
 
81
81
  It cannot ask the user anything and cannot wait on CI — rebasing, watching checks, binding and labelling stay here.
82
82
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feigi/fleet-ctl",
3
- "version": "3.86.0",
3
+ "version": "3.86.1",
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": {
@@ -167,8 +167,8 @@ function roleFromNamePatterns(hay) {
167
167
  // dispatch against an ALREADY-OPEN PR, not new ticket work — measured
168
168
  // meta.json descriptions "Resolve conflict on PR <n>" and "Rebase and
169
169
  // resolve conflicts for PR #<n>", the same rebase/conflict-resolver shape
170
- // `run-team/SKILL.md` calls "a rebase-resolver" sent into an open PR's
171
- // worktree. It carries no ticket, no dispatch counter and no new-work verb —
170
+ // `run-team/references/isolation.md` calls "a rebase-resolver" in a prior-run
171
+ // worktree, here sent into an open PR's worktree. It carries no ticket, no dispatch counter and no new-work verb —
172
172
  // only a PR number — so it is remediation on that PR's path to merge,
173
173
  // exactly the category `fix-pr-<n>`'s applier already occupies here. It is
174
174
  // NOT `merge-bot`: that bucket's number is a per-run dispatch counter, never
@@ -13,7 +13,7 @@
13
13
  // WHY A HOLD AND NOT A WATCHER — the ruling, measured before it was made:
14
14
  //
15
15
  // A backgrounded watcher does not wake an idle agent. Measured twice in one
16
- // run (run-team/SKILL.md, "A member runs its test suite in the foreground"):
16
+ // run (run-team/references/member-lifecycle.md, "A member runs its test suite in the foreground"):
17
17
  // `fix-pr-1184` backgrounded its suite plus a Monitor and sat idle for
18
18
  // roughly two hours; neither ever woke it. run-merge-bot.md's CI gate states
19
19
  // the same rule as a rule — "whatever wakes you is external and may never
@@ -28,7 +28,7 @@
28
28
  // do.
29
29
  //
30
30
  // WHY IT TAKES NO CONTROLLER STATE — every flag below has a default. A monitor
31
- // event is a wake-up, never a verdict (SKILL.md's Monitor rules), so this
31
+ // event is a wake-up, never a verdict (run-team/SKILL.md, "A monitor event is a wake-up, never a verdict"), so this
32
32
  // script never reads or reports fleet state: counts captured before a
33
33
  // twenty-minute hold and printed after it are a stale verdict, and a stale
34
34
  // verdict is how a reconcile prints an ACTION nobody can take. The controller
@@ -239,8 +239,9 @@ const mkRow = (role, actual, target, detail) => (action, { acts = false, extra =
239
239
  // transcript; the session directory is the phase-0 receipt's, which
240
240
  // tier-check.mjs reads itself when an entry omits it. Without a receipt of
241
241
  // this run, `<session>` stays a placeholder the controller fills in, as
242
- // `<file>` does — one batch file, one entry per held member (SKILL.md's
243
- // phase-2 tier check).
242
+ // `<file>` does — one batch file, one entry per held member, the shape under
243
+ // **The batch file is a JSON array, one entry per dispatched member** in
244
+ // run-team/references/tick-rows.md.
244
245
  const tierCheckStep = (members, session) => "run ~/.fleet/bin/fleet-run tier-check.mjs --batch <file> with "
245
246
  + JSON.stringify(members.map((member) => (session ? { member } : { member, session: "<session>" })));
246
247
 
@@ -293,15 +293,16 @@ if (!cmd) die("usage: ledger.mjs [--file <path>] [--require-file] row|token|file
293
293
  // the documented convention IS one quoted argument, and `check` is the only
294
294
  // subcommand where it is: every caller that instructs running it spells it
295
295
  // `ledger.mjs check "<subject>"` (the finisher's deferred-finding filing duty,
296
- // review-and-fix's filing step), while run-team/SKILL.md's ledger section
297
- // spells the other three `filed <issue> <subject>`, `row <ticket> <text>` and
298
- // `ruled <pr> <decision>` — a bare multi-word tail, as did this file's own
299
- // usage strings for them. Applied there, the rule refused what the docs
300
- // prescribe: measured on the tree ahead of this narrowing, `filed 999 the
296
+ // review-and-fix's filing step), while run-team/SKILL.md, as it stood when
297
+ // this was measured, spelled the other three `filed <issue> <subject>`,
298
+ // `row <ticket> <text>` and `ruled <pr> <decision>` — a bare multi-word tail,
299
+ // as did this file's own usage strings for them. Applied there, the rule
300
+ // refused what the docs prescribed: measured on the tree ahead of this
301
+ // narrowing, `filed 999 the
301
302
  // --basee flag is unread`, `row 42 the --basee flag is unread` and `ruled 77
302
303
  // the --basee flag is unread` each exited 2, where the tree before this guard
303
304
  // existed answered all three at exit 0. The guard was ruled back to
304
- // `check` rather than respelling three conventions SKILL.md carries, two
305
+ // `check` rather than respelling three conventions SKILL.md then carried, two
305
306
  // other tickets holding that file open.
306
307
  //
307
308
  // Stated here and nowhere else. This paragraph is the one copy of why
@@ -329,9 +330,9 @@ if (!cmd) die("usage: ledger.mjs [--file <path>] [--require-file] row|token|file
329
330
  // 2 now. Quoting the subject accepts it unchanged. The trade taken
330
331
  // is that this refusal is loud and recoverable where the wrong-subject
331
332
  // answer it replaces was silent. One residual of that trade is not this
332
- // file's to close: SKILL.md's ledger section spells `check <subject>`
333
- // unquoted inside the same entry that spells `filed <issue> <subject>`, where
334
- // the instructions that tell a caller to RUN it quote the subject.
333
+ // file's to close: run-team/references/ledger.md's `filed and ruled` section
334
+ // spells `check <subject>` unquoted, where the instructions that tell a caller
335
+ // to RUN it quote the subject.
335
336
  //
336
337
  // Narrowed on `check` too, not closed: a lone stray with no subject beside it
337
338
  // is a one-element tail, so it is still taken as the subject and answered at
@@ -652,15 +653,15 @@ async function acquireLock() {
652
653
  // indistinguishable at this instant from a live reaper mid-takeover.
653
654
  // Naming the pid alone points an operator at a process that no longer
654
655
  // exists; naming `${REAP}` is what lets them find the file
655
- // `run-team/SKILL.md`'s runbook tells them to remove by hand, once
656
- // nothing is writing.
656
+ // run-team/references/ledger.md's Writer policy section tells them to
657
+ // remove by hand, once nothing is writing.
657
658
  if (dead) {
658
659
  die(`cannot lock ${file} — held by pid ${pid}, which is dead; ${REAP} is blocking its takeover — if nothing is currently writing, remove it by hand`);
659
660
  }
660
661
  // No pid at all is the other permanent wedge pid-lock.mjs's header describes: an
661
662
  // empty or partial `${LOCK}` whose writer died before writing its pid,
662
663
  // read as live forever by design. There is no process to point at, so
663
- // name `${LOCK}` itself — the file the same runbook says to remove.
664
+ // name `${LOCK}` itself — the file the same Writer policy section says to remove.
664
665
  if (pid === null) {
665
666
  die(`cannot lock ${file} — ${LOCK} holds ${JSON.stringify(holder)}, not a pid, so it is never taken over — if nothing is currently writing, remove it by hand`);
666
667
  }
@@ -2087,8 +2088,9 @@ function runDispatch() {
2087
2088
  line = `#${member.number} ${member.name}`;
2088
2089
  data.rows.push(line);
2089
2090
  } else if (i !== -1) {
2090
- // A row `row` already wrote the live token into (SKILL.md's phase 2
2091
- // does) keeps it as it is rather than gaining a second copy.
2091
+ // A row `row` already wrote the live token into (run-team/SKILL.md's
2092
+ // `NEED resume` bullet records the row with `ledger.mjs row` before
2093
+ // `ledger.mjs dispatch`) keeps it as it is rather than gaining a second copy.
2092
2094
  const row = data.rows[i];
2093
2095
  line = carries(row, member.name, true) ? row : `${row} · ${member.name}`;
2094
2096
  data.rows[i] = line;
@@ -125,8 +125,8 @@ if (!path) die("--path <path> is required");
125
125
  // both would refuse, the more specific wording wins, and nothing above here
126
126
  // has run a git call.
127
127
  //
128
- // One consequence the caller has to be told about, so run-team/SKILL.md says
129
- // it too: a needle that starts with `--`, given BARE, never reaches this
128
+ // One consequence the caller has to be told about, so run-team/references/pull.md
129
+ // says it too: a needle that starts with `--`, given BARE, never reaches this
130
130
  // script's question — `arg()` refuses it as a missing value, at exit 2, which
131
131
  // is the verdict that keeps the ticket in the queue. `--gone -- '<value>'`
132
132
  // (separatedNeedles() above) is the opt-in past that, one flag at a time.
@@ -5,7 +5,7 @@
5
5
  // what omp's own record says it RESOLVED, for every member dispatched by
6
6
  // named definition. Not a lint — a refusal, run after every Pull's dispatch,
7
7
  // whose verdict on an implementer lands on the ledger, where fleet-tick.mjs
8
- // holds the next Pull until there is one (run-team/SKILL.md's phase 2).
8
+ // holds the next Pull until there is one (run-team/SKILL.md, **The tier check**).
9
9
  //
10
10
  // Declared is the definition's own frontmatter — `model: "@<role>:<level>"`,
11
11
  // a fleet tier route, never a vendor id. Resolved is read back
@@ -48,10 +48,11 @@
48
48
  // A batch file, not one member: `--batch` takes a JSON array, and the
49
49
  // failure line — `member: declared <m>/<l> resolved <m>/<l>` — is printed
50
50
  // once per member that mismatched, not once per invocation. Under Pull,
51
- // run-team's phase 2 calls this after every Pull's dispatch with a batch of
52
- // one, the member just dispatched. See run-team/SKILL.md's phase-2 dispatch
53
- // paragraph for the batch file's exact entry shape and how the controller
54
- // obtains each field.
51
+ // run-team runs this after every Pull's dispatch with a batch of one, the
52
+ // member just dispatched (run-team/SKILL.md, **The tier check**). The batch
53
+ // file's exact entry shape and how the controller obtains each field are under
54
+ // **The batch file is a JSON array, one entry per dispatched member** in
55
+ // run-team/references/tick-rows.md.
55
56
  //
56
57
  // An implementer (`impl-<N>`, ledger-grammar.mjs's name) is judged on the
57
58
  // ledger's terms, not the caller's: the definition it should have
@@ -194,12 +195,14 @@ export function formatOmpExpectation(r) {
194
195
  // held rather than replacing it, since `row` rewrites the whole line
195
196
  // (ledger.mjs's `data.rows[i] = line`) and a bare mismatch note would drop
196
197
  // `class=`, `KILLED` or `→ PR#` tokens a prior `row` call wrote for the same
197
- // ticket. Idempotent on the identical note: the documented loop is check,
198
- // fix, re-check (SKILL.md's "fix the definition or the dispatch and re-run
199
- // the check before continuing"), and a member that still mismatches for the
200
- // SAME reason on a later run must not grow the row a second time — a
201
- // DIFFERENT note (a changed pair) still appends, since that is new
202
- // information the row does not carry yet.
198
+ // ticket. Idempotent on the identical note: the check can run again for a
199
+ // member it already judged, while what clears a mismatch is a new member
200
+ // (run-team/SKILL.md, **The tier check**: "Clear a mismatch by fixing the
201
+ // definition or the dispatch and dispatching the replacement at the right
202
+ // tier"), so a re-run that finds the SAME pair for the SAME member adds
203
+ // nothing and must not grow the row a second time — a DIFFERENT note (a
204
+ // changed pair, or the replacement's own line, which names the replacement)
205
+ // still appends, since that is new information the row does not carry yet.
203
206
  export function appendedLedgerText(existingText, mismatchNote) {
204
207
  const trimmed = String(existingText ?? "").trim();
205
208
  if (!trimmed) return mismatchNote;
@@ -92,7 +92,8 @@ const LEDGER_SCRIPT = join(SCRIPT_DIR, "ledger.mjs");
92
92
  // ---------------------------------------------------------------------------
93
93
 
94
94
  // APPENDED TO, never inserted into: every awk read-out in the file's header
95
- // and in run-team/SKILL.md indexes by position.
95
+ // and in run-team/references/pull.md (**Accumulate per PR, never conclude a
96
+ // tier result inside one run**) indexes by position.
96
97
  export const COLUMNS = Object.freeze([
97
98
  "run_date", "pr", "ticket", "class", "tier", "closed_own_ticket",
98
99
  "minted_false_claim", "note", "sizing", "profile", "loc", "files",