@feigi/fleet-ctl 3.102.1 → 3.102.3
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 +1 -1
- package/scripts/tier-outcomes.mjs +48 -33
package/package.json
CHANGED
|
@@ -98,6 +98,10 @@
|
|
|
98
98
|
// that is missing, refuses. So does a record entry that names no finding
|
|
99
99
|
// of the review, or a second entry for a finding an earlier entry
|
|
100
100
|
// answers. A dimension that never ran does not: the note names it.
|
|
101
|
+
// - the Ruling itself is deriveRuling's, exported and pure: it takes the
|
|
102
|
+
// review's run and head, its findings and a record lookup, and reads no
|
|
103
|
+
// file. `append` reads the review and the record and ties them to the
|
|
104
|
+
// ledger, then calls it once.
|
|
101
105
|
// The note reads `review <run> at <head7>, record <file>: <bucket>[<index>]
|
|
102
106
|
// <charge> "<claim>"; …; none: <bucket>[<index>] <reason>, …; cleared:
|
|
103
107
|
// survived[<index>] false-rationale, …; unrun: <dimension>, …`, and ` — <--note>`
|
|
@@ -130,7 +134,8 @@ const LEDGER_SCRIPT = join(SCRIPT_DIR, "ledger.mjs");
|
|
|
130
134
|
// ---------------------------------------------------------------------------
|
|
131
135
|
// pure core — exercised through the CLI by its tests; only
|
|
132
136
|
// COLUMNS, LEGACY_WIDTH, TIER_SWITCH_DATE, parseTierOutcomes, rulingsByTicket,
|
|
133
|
-
// rulingFor and
|
|
137
|
+
// rulingFor, lastPullByTicket and deriveRuling are imported directly, the rest
|
|
138
|
+
// through argv
|
|
134
139
|
// ---------------------------------------------------------------------------
|
|
135
140
|
|
|
136
141
|
// APPENDED TO, never inserted into: every awk read-out in the file's header
|
|
@@ -632,7 +637,7 @@ function recordFiles(scratch, pr) {
|
|
|
632
637
|
.sort((a, b) => (a.letter < b.letter ? 1 : a.letter > b.letter ? -1 : 0));
|
|
633
638
|
}
|
|
634
639
|
|
|
635
|
-
// The last record answering review run `run`: `{file,
|
|
640
|
+
// The last record answering review run `run`: `{file, path, value}`, `{missing}`
|
|
636
641
|
// naming why there is none, a retry's record replacing the first attempt's on
|
|
637
642
|
// the same run and a record answering any other run changing nothing. A file
|
|
638
643
|
// met before the answering one that cannot be read, or is not valid JSON,
|
|
@@ -649,7 +654,7 @@ function recordFor(scratch, pr, run) {
|
|
|
649
654
|
const path = join(scratch, file);
|
|
650
655
|
const read = readJson(path);
|
|
651
656
|
if (read.problem !== undefined) return { missing: read.problem };
|
|
652
|
-
if (read.value?.run === run) return { file, path,
|
|
657
|
+
if (read.value?.run === run) return { file, path, value: read.value };
|
|
653
658
|
}
|
|
654
659
|
return { missing: `no ${join(scratch, `dispositions-fix-pr-${pr}[-x].json`)} names review ${run} as the run it answers` };
|
|
655
660
|
}
|
|
@@ -704,42 +709,28 @@ function chargesOf(review, record) {
|
|
|
704
709
|
const FAILS_CLOSED = ["unmet", "both"];
|
|
705
710
|
const MINTS = ["false-claim", "both"];
|
|
706
711
|
|
|
707
|
-
//
|
|
708
|
-
//
|
|
709
|
-
//
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
if (scratch.unresolvable !== undefined) return { missing: `no scratch root to look in: ${scratch.unresolvable}` };
|
|
716
|
-
const reviewPath = join(scratch.root, `pr${pr}`, review.run, "review.json");
|
|
717
|
-
const read = readJson(reviewPath);
|
|
718
|
-
if (read.code === "ENOENT") return { missing: `review ${review.run}'s result file ${reviewPath} is missing` };
|
|
719
|
-
if (read.problem !== undefined) return { missing: read.problem };
|
|
720
|
-
const file = read.value;
|
|
721
|
-
const problem = reviewProblem(file);
|
|
722
|
-
if (problem !== null) return { missing: `${reviewPath} ${problem}` };
|
|
723
|
-
if (!sameHead(String(file.head).toLowerCase(), review.head)) {
|
|
724
|
-
return { missing: `${reviewPath} is the review of head ${file.head}, not the ledger's review ${review.run} at ${review.head}` };
|
|
725
|
-
}
|
|
726
|
-
if (file.survived.length !== review.survived || file.unverified.length !== review.unverified) {
|
|
727
|
-
return { missing: `${reviewPath} holds ${file.survived.length} survived and ${file.unverified.length} unverified, not the ${review.survived} and ${review.unverified} the ledger's review ${review.run} counted` };
|
|
728
|
-
}
|
|
712
|
+
// The Ruling of a first review, pure: `{closed_own_ticket, minted_false_claim,
|
|
713
|
+
// note}`, or `{missing}` saying what is not there to read. `run` and `head` are
|
|
714
|
+
// the ledger token's; `review` is the findings as `review.json` holds them
|
|
715
|
+
// (`survived`, `refuted`, `unverified`, `dimensionsUnrun`), assumed well-formed
|
|
716
|
+
// as reviewProblem guarantees. `record` is called, at most once, only when a
|
|
717
|
+
// survived or unverified finding needs a record: it returns `{file, path,
|
|
718
|
+
// value}` or `{missing}`. Reads no file and runs no command.
|
|
719
|
+
export function deriveRuling({ run, head, review, record }) {
|
|
729
720
|
// An entry that is neither a dimension's name nor {dimension} is named as
|
|
730
721
|
// the JSON it is, so the note never reads as a blank dimension.
|
|
731
|
-
const unrun = (Array.isArray(
|
|
722
|
+
const unrun = (Array.isArray(review.dimensionsUnrun) ? review.dimensionsUnrun : []).map((d) => oneLine(
|
|
732
723
|
typeof d === "string" ? d : typeof d?.dimension === "string" ? d.dimension : JSON.stringify(d ?? null),
|
|
733
724
|
));
|
|
734
|
-
const opening = `review ${
|
|
725
|
+
const opening = `review ${run} at ${head.slice(0, 7)}`;
|
|
735
726
|
const noted = (parts) => (unrun.length === 0 ? parts : [...parts, `unrun: ${unrun.join(", ")}`]).join("; ");
|
|
736
727
|
|
|
737
|
-
if (review.survived === 0 && review.unverified === 0) {
|
|
738
|
-
return {
|
|
728
|
+
if (review.survived.length === 0 && review.unverified.length === 0) {
|
|
729
|
+
return { closed_own_ticket: "yes", minted_false_claim: "no", note: `${opening}, no record: ${noted(["counted no survived and no unverified finding"])}` };
|
|
739
730
|
}
|
|
740
|
-
const found =
|
|
731
|
+
const found = record();
|
|
741
732
|
if (found.missing !== undefined) return { missing: found.missing };
|
|
742
|
-
const charges = chargesOf(
|
|
733
|
+
const charges = chargesOf(review, found.value);
|
|
743
734
|
if (charges.broken.length > 0) {
|
|
744
735
|
return { missing: `${found.path} has ${charges.broken.length === 1 ? "an entry" : "entries"} a Ruling cannot read: ${charges.broken.join("; ")}` };
|
|
745
736
|
}
|
|
@@ -753,10 +744,34 @@ function derivedRuling(pr, ledger, scratch) {
|
|
|
753
744
|
closed_own_ticket: charges.charged.some((c) => FAILS_CLOSED.includes(c.charge)) ? "no" : "yes",
|
|
754
745
|
minted_false_claim: charges.charged.some((c) => MINTS.includes(c.charge)) ? "yes" : "no",
|
|
755
746
|
note: `${opening}, record ${found.file}: ${noted(parts.length === 0 ? ["no finding stands"] : parts)}`,
|
|
756
|
-
review: review.run,
|
|
757
747
|
};
|
|
758
748
|
}
|
|
759
749
|
|
|
750
|
+
// PR `pr`'s first review read from scratch and tied to the ledger, then ruled
|
|
751
|
+
// by deriveRuling: its Ruling plus `review`, the review's run, or `{missing}`
|
|
752
|
+
// saying what is not there to read. `scratch` is `{root}` or `{unresolvable}`.
|
|
753
|
+
function scratchRuling(pr, ledger, scratch) {
|
|
754
|
+
const review = firstReview(prTokens(ledger?.rows ?? [], Number(pr)));
|
|
755
|
+
if (review === null) {
|
|
756
|
+
return { missing: `PR #${pr} has no first review on the ledger: no \`reviewed=\` token of its rows counted no survived and no unverified finding, or was answered by a fix-applier` };
|
|
757
|
+
}
|
|
758
|
+
if (scratch.unresolvable !== undefined) return { missing: `no scratch root to look in: ${scratch.unresolvable}` };
|
|
759
|
+
const reviewPath = join(scratch.root, `pr${pr}`, review.run, "review.json");
|
|
760
|
+
const read = readJson(reviewPath);
|
|
761
|
+
if (read.code === "ENOENT") return { missing: `review ${review.run}'s result file ${reviewPath} is missing` };
|
|
762
|
+
if (read.problem !== undefined) return { missing: read.problem };
|
|
763
|
+
const file = read.value;
|
|
764
|
+
const problem = reviewProblem(file);
|
|
765
|
+
if (problem !== null) return { missing: `${reviewPath} ${problem}` };
|
|
766
|
+
if (!sameHead(String(file.head).toLowerCase(), review.head)) {
|
|
767
|
+
return { missing: `${reviewPath} is the review of head ${file.head}, not the ledger's review ${review.run} at ${review.head}` };
|
|
768
|
+
}
|
|
769
|
+
if (file.survived.length !== review.survived || file.unverified.length !== review.unverified) {
|
|
770
|
+
return { missing: `${reviewPath} holds ${file.survived.length} survived and ${file.unverified.length} unverified, not the ${review.survived} and ${review.unverified} the ledger's review ${review.run} counted` };
|
|
771
|
+
}
|
|
772
|
+
return { ...deriveRuling({ run: review.run, head: review.head, review: file, record: () => recordFor(scratch.root, pr, review.run) }), review: review.run };
|
|
773
|
+
}
|
|
774
|
+
|
|
760
775
|
// The step a refusal hands the controller. A merged PR's evidence is
|
|
761
776
|
// reclaimed with its scratch, so the verdicts are given by hand; any other
|
|
762
777
|
// PR's is still wanted, and a ruling is never typed for it.
|
|
@@ -831,7 +846,7 @@ function append(pr, paths) {
|
|
|
831
846
|
die(`PR #${pr} is ${state === "OPEN" ? "open" : "closed without merging"}, and verdicts are given by hand only for a merged PR — leave out --closed-own-ticket and --minted-false-claim, and \`append\` works them out from the record: ${nextStep(pr, state)}`);
|
|
832
847
|
}
|
|
833
848
|
} else {
|
|
834
|
-
const ruled =
|
|
849
|
+
const ruled = scratchRuling(pr, ledger, scratch);
|
|
835
850
|
if (ruled.missing !== undefined) {
|
|
836
851
|
let state = null;
|
|
837
852
|
let unread = "";
|