@feigi/fleet-ctl 3.82.19 → 3.82.21
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-merge-bot.agent.md +6 -6
- package/package.json +1 -1
- package/scripts/tier-outcomes.mjs +46 -6
- package/skills/run-team/SKILL.md +178 -579
- package/skills/run-team/references/ci-and-staleness.md +47 -0
- package/skills/run-team/references/ci-watcher.md +138 -3
- package/skills/run-team/references/close-out.md +34 -1
- package/skills/run-team/references/failure-handling.md +43 -0
- package/skills/run-team/references/finisher.md +185 -0
- package/skills/run-team/references/instruments.md +20 -0
- package/skills/run-team/references/isolation.md +10 -0
- package/skills/run-team/references/ledger.md +4 -0
- package/skills/run-team/references/member-lifecycle.md +18 -0
- package/skills/run-team/references/phase-0.md +50 -0
- package/skills/run-team/references/pull.md +36 -1
- package/skills/run-team/references/reviewers.md +81 -0
- package/skills/run-team/references/shell-traps.md +10 -9
- package/skills/run-team/references/tick-rows.md +35 -0
|
@@ -18,9 +18,9 @@ Your report is the one file you write outside it: `<scratch>/reports/merge-bot-<
|
|
|
18
18
|
the path your brief names, holding every PR you merged in `merged`. Your final
|
|
19
19
|
message is the one line your brief gives, `merge-bot-<n>: done <path>`.
|
|
20
20
|
|
|
21
|
-
The shell traps below are copied from the controller's skill
|
|
22
|
-
`
|
|
23
|
-
|
|
21
|
+
The shell traps below are copied from the controller's skill, `run-team`. In
|
|
22
|
+
them `references/ci-watcher.md` is that skill's file on the controller's CI
|
|
23
|
+
watcher, which you do not run; each trap applies to every shell
|
|
24
24
|
command and loop you run.
|
|
25
25
|
|
|
26
26
|
**zsh word-splits an unquoted command substitution's result; it does NOT split
|
|
@@ -54,7 +54,7 @@ EOF
|
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
The inline `$(printf '%s\n' "$prs")` form splits correctly too and is what the
|
|
57
|
-
watcher block
|
|
57
|
+
watcher block in `references/ci-watcher.md` uses, but it splits on *every* whitespace character, so it is safe only
|
|
58
58
|
for values that cannot contain one — PR numbers cannot, branch names and paths
|
|
59
59
|
can. Measured on a two-line list whose first value is `two words`: three
|
|
60
60
|
iterations through `$(printf …)`, two through `while IFS= read -r`. On an empty
|
|
@@ -70,8 +70,8 @@ word-split bug, so a correct heredoc fix missing this half looks exactly like no
|
|
|
70
70
|
fix at all, and reads as one.
|
|
71
71
|
|
|
72
72
|
**A probe that could not look emits an event; it never `continue`s.** Stated in
|
|
73
|
-
full at *A probe that cannot read is a transition event, not silence*
|
|
74
|
-
and it binds every watcher
|
|
73
|
+
full at *A probe that cannot read is a transition event, not silence* in `references/ci-watcher.md`,
|
|
74
|
+
and it binds every watcher rather than that one block: the third
|
|
75
75
|
value beside pass and fail is what turns a broken loop into a loud failure
|
|
76
76
|
instead of a quiet one. A guard that drops an unreadable probe — `[ -n "$json" ]
|
|
77
77
|
|| continue` — makes *could not look* indistinguishable from *nothing to report*.
|
package/package.json
CHANGED
|
@@ -20,7 +20,12 @@
|
|
|
20
20
|
// lack a member row (nothing from that run was checked). `--live`
|
|
21
21
|
// (a run in progress; CI never passes it) adds a WARNING, never a
|
|
22
22
|
// failure, per PR whose run-ledger row carries `reviewed=` but which has
|
|
23
|
-
// no row here
|
|
23
|
+
// no row here, except a PR that closes no issue: `append` keys a row on
|
|
24
|
+
// exactly one ticket and refuses it, so it is named in a `note` line and
|
|
25
|
+
// never warned about. A read of that PR that does not answer keeps the
|
|
26
|
+
// warning, after a line naming why the read failed. Both reads of a PR's
|
|
27
|
+
// closing references ignore an ambient GH_REPO and give `gh` 60s.
|
|
28
|
+
// `--ledger <path>` names another ledger than the run's own.
|
|
24
29
|
//
|
|
25
30
|
// `tier` resolution, in order (never tie-broken by `run_date`: the two files'
|
|
26
31
|
// `run_date`s mean different things — this file's header measures 92 of 267
|
|
@@ -445,20 +450,37 @@ function readLedger(ledgerFile, requireFile) {
|
|
|
445
450
|
}
|
|
446
451
|
}
|
|
447
452
|
|
|
448
|
-
|
|
453
|
+
// A PR's closingIssuesReferences array, or an Error naming why it could not be
|
|
454
|
+
// read. GH_REPO is blanked so an ambient one naming another repository never
|
|
455
|
+
// decides which PR is read; the timeout keeps a hung `gh` from stalling
|
|
456
|
+
// `append` or a close-out's `check --live`.
|
|
457
|
+
const GH_TIMEOUT_MS = 60_000;
|
|
458
|
+
function closingRefs(pr) {
|
|
449
459
|
let raw;
|
|
450
460
|
try {
|
|
451
|
-
raw = execFileSync("gh", ["pr", "view", pr, "--json", "closingIssuesReferences"], {
|
|
461
|
+
raw = execFileSync("gh", ["pr", "view", String(pr), "--json", "closingIssuesReferences"], {
|
|
462
|
+
encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], env: { ...process.env, GH_REPO: "" }, timeout: GH_TIMEOUT_MS,
|
|
463
|
+
});
|
|
452
464
|
} catch (e) {
|
|
453
|
-
|
|
465
|
+
throw new Error(`gh pr view ${pr} failed: ${e.stderr?.trim() || e.message}`);
|
|
454
466
|
}
|
|
455
467
|
let refs;
|
|
456
468
|
try {
|
|
457
469
|
refs = JSON.parse(raw).closingIssuesReferences;
|
|
458
470
|
} catch (e) {
|
|
459
|
-
|
|
471
|
+
throw new Error(`gh pr view ${pr} returned unreadable JSON: ${e.message}`);
|
|
472
|
+
}
|
|
473
|
+
if (!Array.isArray(refs)) throw new Error(`gh pr view ${pr} returned no closingIssuesReferences array`);
|
|
474
|
+
return refs;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function closingTicket(pr) {
|
|
478
|
+
let refs;
|
|
479
|
+
try {
|
|
480
|
+
refs = closingRefs(pr);
|
|
481
|
+
} catch (e) {
|
|
482
|
+
die(e.message);
|
|
460
483
|
}
|
|
461
|
-
if (!Array.isArray(refs)) die(`gh pr view ${pr} returned no closingIssuesReferences array`);
|
|
462
484
|
const numbers = [...new Set(refs.map((r) => r?.number))];
|
|
463
485
|
if (numbers.length !== 1 || !Number.isInteger(numbers[0])) {
|
|
464
486
|
die(`PR #${pr} closes ${numbers.length === 0 ? "no issue" : `${numbers.length} issues (${numbers.map((n) => `#${n}`).join(", ")})`} per GitHub's closingIssuesReferences — a tier-outcomes row keys on exactly one ticket`);
|
|
@@ -534,6 +556,20 @@ function append(pr, paths) {
|
|
|
534
556
|
console.log(line);
|
|
535
557
|
}
|
|
536
558
|
|
|
559
|
+
// True only when `gh` answered and the PR's closingIssuesReferences is an empty
|
|
560
|
+
// array. Any other outcome (gh failing, unreadable JSON, a non-array) is false
|
|
561
|
+
// after a line naming why: a PR whose closing references cannot be read keeps
|
|
562
|
+
// its warning. That line never reads `WARNING PR #`, which closeout.mjs turns
|
|
563
|
+
// into a NEED.
|
|
564
|
+
function closesNoIssue(pr) {
|
|
565
|
+
try {
|
|
566
|
+
return closingRefs(pr).length === 0;
|
|
567
|
+
} catch (e) {
|
|
568
|
+
console.error(`${NAME}: could not read the closing references of PR #${pr}: ${e.message}`);
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
537
573
|
function check(paths) {
|
|
538
574
|
for (const flag of Object.keys(APPEND_FLAGS)) {
|
|
539
575
|
if (arg(flag) !== null) die(`--${flag} is an append flag`);
|
|
@@ -546,6 +582,10 @@ function check(paths) {
|
|
|
546
582
|
if (has("live")) {
|
|
547
583
|
const ledger = readLedger(paths.ledger, true);
|
|
548
584
|
for (const pr of unrecordedReviewedPrs(ledger, rows)) {
|
|
585
|
+
if (closesNoIssue(pr)) {
|
|
586
|
+
console.error(`${NAME}: note PR #${pr} carries reviewed= on the ledger and closes no issue, so it takes no row in ${paths.file}`);
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
549
589
|
console.error(`${NAME}: WARNING PR #${pr} carries reviewed= on the ledger but has no row in ${paths.file} — run \`tier-outcomes.mjs append ${pr}\` if it is an implementer PR`);
|
|
550
590
|
}
|
|
551
591
|
}
|