@feigi/fleet-ctl 3.88.13 → 3.89.0
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
package/scripts/ledger.mjs
CHANGED
|
@@ -846,7 +846,7 @@ if (cmd === "read") {
|
|
|
846
846
|
} else if (cmd === "check") {
|
|
847
847
|
runCheck();
|
|
848
848
|
} else if (cmd === "dispatch") {
|
|
849
|
-
runDispatch();
|
|
849
|
+
await runDispatch();
|
|
850
850
|
} else if (cmd === "settle") {
|
|
851
851
|
if (FROM_REPORT) await runSettleFromReport();
|
|
852
852
|
else runSettle();
|
|
@@ -1894,6 +1894,36 @@ function dispositionsRefusal(pr, review) {
|
|
|
1894
1894
|
return null;
|
|
1895
1895
|
}
|
|
1896
1896
|
|
|
1897
|
+
// Why PR `pr`'s finisher waits on its ruling, or null when it does not: the
|
|
1898
|
+
// PR is an implementer's — a settled `impl-<N>=PR#<pr>` token on a row names
|
|
1899
|
+
// it, the token tier-outcomes.mjs `append` resolves a ticket from — and the
|
|
1900
|
+
// main checkout's tier-outcomes.tsv has no row whose `pr` is it. Any row
|
|
1901
|
+
// passes, whatever its date or verdicts, so a replacement finisher is never
|
|
1902
|
+
// held once the first passed. The ledger lives in the main checkout's
|
|
1903
|
+
// `.fleet/` (fleet-dir.mjs), so the file is found beside that directory —
|
|
1904
|
+
// no git, no gh. A missing file is no row (`append` creates it); one that
|
|
1905
|
+
// cannot be read or parsed refuses, naming it, and is never a pass.
|
|
1906
|
+
// tier-outcomes.mjs is imported here, not at the top: its import graph is
|
|
1907
|
+
// several modules no other subcommand loads.
|
|
1908
|
+
async function rulingRefusal(pr) {
|
|
1909
|
+
const { parseTierOutcomes, implementerClaims } = await import("./tier-outcomes.mjs");
|
|
1910
|
+
const tickets = [...new Set(implementerClaims(data, pr).map((c) => c.ticket))];
|
|
1911
|
+
if (tickets.length === 0) return null;
|
|
1912
|
+
const tsv = join(dirname(dirname(resolve(file))), "docs", "metrics", "tier-outcomes.tsv");
|
|
1913
|
+
if (existsSync(tsv)) {
|
|
1914
|
+
let rows;
|
|
1915
|
+
try {
|
|
1916
|
+
rows = parseTierOutcomes(readFileSync(tsv, "utf8"));
|
|
1917
|
+
} catch (e) {
|
|
1918
|
+
return `cannot read the PR's ruling off ${tsv}: ${e.code ?? e.message} — fix the file, then dispatch the finisher again`;
|
|
1919
|
+
}
|
|
1920
|
+
if (rows.some((r) => r.pr === String(pr))) return null;
|
|
1921
|
+
}
|
|
1922
|
+
return `PR #${pr}, the implementer PR of ${tickets.length === 1 ? "ticket" : "tickets"} ${tickets.map((n) => `#${n}`).join(", ")}, has no ruling — `
|
|
1923
|
+
+ `${tsv} has no row for it; rule its review with \`~/.fleet/bin/fleet-run tier-outcomes.mjs append ${pr} --closed-own-ticket yes|no `
|
|
1924
|
+
+ `--minted-false-claim yes|no --note "<text>"\`, then dispatch the finisher again`;
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1897
1927
|
// The open `ready-to-merge` PRs, ascending, as gh answers from the ledger's
|
|
1898
1928
|
// own directory with runCheck()'s environment: the queue a merge bot is
|
|
1899
1929
|
// dispatched against, recorded on its `## Dispatched` entry. Anything but a
|
|
@@ -1925,7 +1955,7 @@ function queueSnapshot() {
|
|
|
1925
1955
|
return [...new Set(prs)].sort((a, b) => a - b);
|
|
1926
1956
|
}
|
|
1927
1957
|
|
|
1928
|
-
function runDispatch() {
|
|
1958
|
+
async function runDispatch() {
|
|
1929
1959
|
const usage = "usage: ledger.mjs dispatch <ticket|pr> <member>, or dispatch merge-bot";
|
|
1930
1960
|
if (rest.length === 0 || rest.length > 2) die(usage);
|
|
1931
1961
|
const [key, name] = rest.length === 2 ? rest : [null, rest[0]];
|
|
@@ -2047,8 +2077,11 @@ function runDispatch() {
|
|
|
2047
2077
|
// run the tick reads — and no fix-applier on the PR is still live. A PR
|
|
2048
2078
|
// whose latest review counts no survived and no unverified finding had
|
|
2049
2079
|
// nothing for a fix-applier to rule on, and one never reviewed has no
|
|
2050
|
-
// review file: neither is gated on a verdict.
|
|
2051
|
-
//
|
|
2080
|
+
// review file: neither is gated on a verdict. Then an implementer's PR
|
|
2081
|
+
// waits on its ruling, its tier-outcomes row, written while the PR is open
|
|
2082
|
+
// and its review evidence still in scratch; see rulingRefusal(). Refused
|
|
2083
|
+
// here, before anything is written, so no check can be skipped by
|
|
2084
|
+
// forgetting to run it.
|
|
2052
2085
|
if (member.family === "finisher-pr") {
|
|
2053
2086
|
const fold = tickFold("conflict hold and dispositions verdict");
|
|
2054
2087
|
if (fold.conflictHeld.includes(member.number)) {
|
|
@@ -2056,7 +2089,7 @@ function runDispatch() {
|
|
|
2056
2089
|
+ `and let it settle applied:/no-op, then dispatch the finisher again — not dispatching ${member.name}`);
|
|
2057
2090
|
}
|
|
2058
2091
|
const review = fold.reviewed.find((r) => r.pr === member.number);
|
|
2059
|
-
const refusal = dispositionsRefusal(member.number, review);
|
|
2092
|
+
const refusal = dispositionsRefusal(member.number, review) ?? await rulingRefusal(member.number);
|
|
2060
2093
|
if (refusal !== null) die(`${member.name}: ${refusal} — not dispatching ${member.name}`);
|
|
2061
2094
|
}
|
|
2062
2095
|
// Every error caught here carries its own cause and remedy — a row's `tier=`
|
|
@@ -495,7 +495,8 @@ function closingNumbers(pr) {
|
|
|
495
495
|
|
|
496
496
|
// Every settled `impl-<N>=PR#<pr>` token in the run ledger's rows, each with the
|
|
497
497
|
// row that carries it and its ticket N. The dispatched list is not read.
|
|
498
|
-
|
|
498
|
+
// Exported for ledger.mjs's finisher dispatch, which gates the same PRs.
|
|
499
|
+
export function implementerClaims(ledger, pr) {
|
|
499
500
|
const claims = [];
|
|
500
501
|
for (const text of ledger?.rows ?? []) {
|
|
501
502
|
const key = text.split(/\s/)[0];
|
package/skills/run-team/SKILL.md
CHANGED
|
@@ -195,9 +195,9 @@ owes beyond its call:
|
|
|
195
195
|
once the run is draining with no open PR and no live implementer.
|
|
196
196
|
3. `~/.fleet/bin/fleet-run closeout.mjs finish --reason "<reason>"`, which
|
|
197
197
|
then records `fleet-heartbeat.mjs --stop` last, because every reader takes
|
|
198
|
-
a recorded stop as the end of the run. A `NEED tier-outcomes append
|
|
199
|
-
|
|
200
|
-
the verdict you rule (**Guard: accumulate per PR**) and run `finish` again.
|
|
198
|
+
a recorded stop as the end of the run. A `NEED tier-outcomes append <pr>`
|
|
199
|
+
is a reviewed PR with no verdict row — for an implementer's, one that never
|
|
200
|
+
reached its finisher: append it with the verdict you rule (**Guard: accumulate per PR**) and run `finish` again.
|
|
201
201
|
A PR closing none or several: a row only if an impl token names it.
|
|
202
202
|
A step it fails is explained in references/close-out.md. Exit 0 is the run
|
|
203
203
|
closed.
|
|
@@ -280,10 +280,10 @@ digest asks of you:
|
|
|
280
280
|
**A refutation resting on an injection nothing proved landed is not a refutation — it is a cell that never ran.** See references/reviewers.md.
|
|
281
281
|
|
|
282
282
|
**Guard: accumulate per PR, never conclude inside one run.** **Append one row to
|
|
283
|
-
`docs/metrics/tier-outcomes.tsv` when you rule each PR's review, with
|
|
283
|
+
`docs/metrics/tier-outcomes.tsv` when you rule each PR's review, before its finisher, with
|
|
284
284
|
`~/.fleet/bin/fleet-run tier-outcomes.mjs append <pr> --closed-own-ticket yes|no --minted-false-claim yes|no --note "<text>"`,
|
|
285
285
|
never by hand** (that file's header carries the column meanings). The script
|
|
286
|
-
fills `tier` itself; never type one in. That append is the whole duty. See references/pull.md.
|
|
286
|
+
fills `tier` itself; never type one in. `ledger.mjs dispatch` refuses the finisher until the row exists. That append is the whole duty. See references/pull.md.
|
|
287
287
|
|
|
288
288
|
**A verdict is a measurement only if it post-dates the run that produced it.** See references/reviewers.md.
|
|
289
289
|
|
|
@@ -520,7 +520,7 @@ Pointers: the `/resume` rules are in `references/phase-0.md`; the tick HOLD tier
|
|
|
520
520
|
|
|
521
521
|
## The finisher gate
|
|
522
522
|
|
|
523
|
-
Dispatch a finisher only when every test here passes at once — the review returned, the fix-applier's report arrived, your outbox to it is empty and
|
|
523
|
+
Dispatch a finisher only when every test here passes at once — the review returned, the fix-applier's report arrived, your outbox to it is empty, no reply is owed and the PR's ruling is recorded; hold otherwise, and on a repo with no CI and no declaration report it and add no label.
|
|
524
524
|
|
|
525
525
|
### The tests
|
|
526
526
|
|
|
@@ -542,8 +542,8 @@ Dispatch a finisher only when every test here passes at once — the review retu
|
|
|
542
542
|
by name. An empty outbox catches neither — *vacuously* empty on the
|
|
543
543
|
implementer's firing, *genuinely* empty on a mid-work push. **The outbox is
|
|
544
544
|
the last test, not the only one, and it stands wherever a fix-applier
|
|
545
|
-
exists**, so never dispatch off it while you do owe it a
|
|
546
|
-
first — including any
|
|
545
|
+
exists**, so never dispatch off it while you do owe it a reply: empty it
|
|
546
|
+
first — including any reply you have withdrawn or reversed — then dispatch
|
|
547
547
|
(below); a final report is not proof it stopped. A PR that needs nothing fixed
|
|
548
548
|
reaches a finisher through the **fix-applier reports `no-op`** bullet below,
|
|
549
549
|
never by waiting for a second CI event; a red that fired before the review
|
|
@@ -560,7 +560,7 @@ Dispatch a finisher only when every test here passes at once — the review retu
|
|
|
560
560
|
itself, so nothing further is owed and no further event is coming. Edit either bullet without the other and what strands is exactly the PR that needed no fixes.
|
|
561
561
|
- **`ci-state.mjs --pr <N>` reads `verdict: "no-ci"`** → the CI edge never
|
|
562
562
|
fires: dispatch the finisher off the reviewer's final verdict the moment it
|
|
563
|
-
lands, never while you still owe it a
|
|
563
|
+
lands, never while you still owe it a reply. Its gate is then the
|
|
564
564
|
`--declare-no-ci` declaration, not a `check` job: with it, label off the
|
|
565
565
|
reviewer's verified suite run; **without it, do not label** — report that this
|
|
566
566
|
repo has no CI configured and no declaration, and stop. See `review-and-fix.md` step 6.
|
|
@@ -593,9 +593,9 @@ with `dispatch-prompt.mjs render finisher --kind fresh --member finisher-pr-<pr#
|
|
|
593
593
|
(`--kind repair` once an earlier finisher reported `labelled` without the label landing), record it with
|
|
594
594
|
`ledger.mjs dispatch <pr#> finisher-pr-<pr#>` before the call, then **dispatch
|
|
595
595
|
it with the `agent` that command printed and omit `model` on the call**. **Never dispatch
|
|
596
|
-
one while you still owe the fix-applier a
|
|
596
|
+
one while you still owe the fix-applier a reply. The test is your OUTBOX, not the
|
|
597
597
|
member's last message** — anything you have decided that it has not received,
|
|
598
|
-
including a
|
|
598
|
+
including a reply you have since **withdrawn or reversed**, a withdrawal
|
|
599
599
|
recorded only in the ledger included. **That is the test of THIS condition
|
|
600
600
|
only**: the report condition above is the other. Relay everything — reversals
|
|
601
601
|
too — then dispatch. See references/finisher.md.
|
|
@@ -642,7 +642,7 @@ merge, and a finisher on it would race the conflict fix-applier rebasing it.
|
|
|
642
642
|
Dispatch that fix-applier, let it settle `applied:`/`no-op`, then dispatch the
|
|
643
643
|
finisher again.
|
|
644
644
|
|
|
645
|
-
**An unanswered question from the member is an outbox item, and it blocks dispatch with the same weight as a
|
|
645
|
+
**An unanswered question from the member is an outbox item, and it blocks dispatch with the same weight as a reply you have already decided on.** See references/finisher.md.
|
|
646
646
|
|
|
647
647
|
**A final report is not proof the member stopped.** **The standing CI facts are
|
|
648
648
|
in the fix-applier body, not in per-event messages.**
|
|
@@ -22,8 +22,10 @@
|
|
|
22
22
|
metrics, commits the three metrics files and opens their PR, sets the
|
|
23
23
|
phase-0 receipt aside as `.fleet/phase0.previous.json`, then records
|
|
24
24
|
`fleet-heartbeat.mjs --stop` last, because every reader takes a recorded
|
|
25
|
-
stop as the end of the run. A `NEED tier-outcomes append <pr>` is a
|
|
26
|
-
|
|
25
|
+
stop as the end of the run. A `NEED tier-outcomes append <pr>` is a
|
|
26
|
+
reviewed PR with no verdict row — for an implementer's PR, one that never
|
|
27
|
+
reached its finisher, since `ledger.mjs dispatch` refuses that finisher
|
|
28
|
+
until the row exists: append it with the verdict
|
|
27
29
|
you rule (**Guard: accumulate per PR**) and run `finish` again. A reviewed
|
|
28
30
|
PR that closes no issue, or several, with no settled `impl-<N>=PR#<pr>`
|
|
29
31
|
token naming it takes no row: the check names it in a `note` line and
|
|
@@ -28,10 +28,10 @@ Its four duties are a checklist — audit the
|
|
|
28
28
|
worktree, confirm every deferral has a tracker home and re-run the acceptance
|
|
29
29
|
mutation, apply one release label, report — and the
|
|
30
30
|
merge gate downstream still catches whatever it gets wrong. **Never dispatch
|
|
31
|
-
one while you still owe the fix-applier a
|
|
31
|
+
one while you still owe the fix-applier a reply**: the pinned SHA is only as good
|
|
32
32
|
as the guarantee nothing else is inbound. **The test is your OUTBOX, not the
|
|
33
33
|
member's last message** — anything you have decided that it has not received,
|
|
34
|
-
including a
|
|
34
|
+
including a reply you have since **withdrawn or reversed**. **That is the test
|
|
35
35
|
of THIS condition only**: an empty outbox says nothing is inbound from you, and
|
|
36
36
|
nothing about whether the member considers itself finished, which is what the
|
|
37
37
|
report condition above is for. A withdrawal you
|
|
@@ -71,9 +71,9 @@ and will act on it. Relay everything — reversals too — then dispatch.
|
|
|
71
71
|
anything to, so the outbox is *vacuously* empty, and on a fix-applier's
|
|
72
72
|
mid-work push nothing has been sent since the dispatch, so it is *genuinely*
|
|
73
73
|
empty. **The outbox is the last test, not the only one, and it stands wherever
|
|
74
|
-
a fix-applier exists** — that is exactly where a
|
|
74
|
+
a fix-applier exists** — that is exactly where a reply you still owe it is
|
|
75
75
|
outstanding, so never dispatch off it while you do. Empty your outbox to it
|
|
76
|
-
first — including any
|
|
76
|
+
first — including any reply you have withdrawn or reversed — then dispatch
|
|
77
77
|
(*The fix-applier pushes and exits; the finisher is dispatched fresh*); a final report is not proof it stopped. A PR that needs nothing fixed
|
|
78
78
|
reaches a finisher through the **fix-applier reports `no-op`** bullet below,
|
|
79
79
|
never by waiting for a second CI event this head will never produce. The
|
|
@@ -102,12 +102,21 @@ and will act on it. Relay everything — reversals too — then dispatch.
|
|
|
102
102
|
waiting for it stalls the whole PR — the same silent-stall shape reported
|
|
103
103
|
before this verdict existed. Dispatch the finisher off the reviewer's final
|
|
104
104
|
verdict instead, the moment it lands — and, same as above, never while you still
|
|
105
|
-
owe it a
|
|
105
|
+
owe it a reply — or hold anything else it has not received, a reply you have
|
|
106
106
|
withdrawn or reversed included. The finisher's gate is then the
|
|
107
107
|
`--declare-no-ci` declaration, not a `check` job: with it, label off the
|
|
108
108
|
reviewer's verified suite run; **without it, do not label** — report that this
|
|
109
109
|
repo has no CI configured and no declaration, and stop. Absence never reads as
|
|
110
110
|
pass. See `review-and-fix.md` step 6.
|
|
111
|
+
- **The PR's ruling is recorded** → an implementer's PR, one a settled
|
|
112
|
+
`impl-<N>=PR#<M>` token on the ledger names, gets its finisher only once the
|
|
113
|
+
main checkout's `docs/metrics/tier-outcomes.tsv` has a row for it, appended
|
|
114
|
+
when you rule its review (SKILL.md's **Guard: accumulate per PR**).
|
|
115
|
+
`ledger.mjs dispatch` refuses the finisher until that row exists, after its
|
|
116
|
+
conflict-hold and dispositions refusals, and prints the `tier-outcomes.mjs
|
|
117
|
+
append` command to run. Any row for the PR passes, so a replacement finisher
|
|
118
|
+
is never held once the first passed; a PR no such token names is not gated.
|
|
119
|
+
No flag skips it: a PR `append` refuses is fixed on the ledger, as at close-out.
|
|
111
120
|
|
|
112
121
|
Gate on the `check` job, **not** on `ci-state --quiet` exit 0: a behind PR never
|
|
113
122
|
reaches full green, so an exit-0 gate strands it unlabelled. The finisher reads
|
|
@@ -138,7 +147,7 @@ no verified suite run on record` and add no label.
|
|
|
138
147
|
## An unanswered question from the member is an outbox item
|
|
139
148
|
|
|
140
149
|
**An unanswered question from the member is an outbox item, and it blocks
|
|
141
|
-
dispatch with the same weight as a
|
|
150
|
+
dispatch with the same weight as a reply you have already decided on.** It does not
|
|
142
151
|
feel like one — you have decided nothing yet, so the outbox reads empty — but a
|
|
143
152
|
member waiting on you is a member that will push once you answer, and the SHA
|
|
144
153
|
you are about to pin is exactly what that push moves. Measured on one run:
|