@attalabs/vinaya 0.27.0 → 0.28.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/README.md +4 -3
- package/aeg-root/enforcement.md +4 -4
- package/aeg-root/roles/developer.md +23 -17
- package/aeg-root/roles/planner.md +2 -0
- package/aeg-root/roles/principal.md +4 -0
- package/aeg-root/roles/reviewer.md +3 -3
- package/aeg-root/roles/security.md +3 -3
- package/dist/checks/bin/check-body-bare-digits.js +915 -240
- package/dist/checks/bin/check-branch-topology.js +820 -180
- package/dist/checks/bin/check-brief-shape.js +915 -240
- package/dist/checks/bin/check-changeset-coverage.js +1534 -262
- package/dist/checks/bin/check-closes-n.js +824 -184
- package/dist/checks/bin/check-coherence.js +984 -265
- package/dist/checks/bin/check-dead-branch-push.js +805 -176
- package/dist/checks/bin/check-dispatch-readiness.js +1046 -276
- package/dist/checks/bin/check-doc-coverage-push.js +1530 -258
- package/dist/checks/bin/check-doc-coverage.js +1532 -260
- package/dist/checks/bin/check-doctrine-no-procedures.js +915 -240
- package/dist/checks/bin/check-doctrine-portability.js +1529 -257
- package/dist/checks/bin/check-evidence-fresh.js +1875 -258
- package/dist/checks/bin/check-exec-bits.js +1527 -255
- package/dist/checks/bin/check-first-push-dispatch.js +932 -246
- package/dist/checks/bin/check-issue-assignment.js +822 -182
- package/dist/checks/bin/check-issue-milestone-attach.js +5734 -0
- package/dist/checks/bin/check-issue-objectives-numbering.js +5736 -0
- package/dist/checks/bin/check-issue-parts-coverage.js +5736 -0
- package/dist/checks/bin/check-issue-surface-globs.js +6910 -0
- package/dist/checks/bin/check-issue-title-grammar.js +5736 -0
- package/dist/checks/bin/check-issue-tranche-label.js +5736 -0
- package/dist/checks/bin/check-main-branch-refusal.js +805 -176
- package/dist/checks/bin/check-no-disk-state.js +805 -176
- package/dist/checks/bin/check-pr-premise-reassert.js +915 -240
- package/dist/checks/bin/check-pr-report-density.js +805 -176
- package/dist/checks/bin/check-quoted-command.js +1508 -255
- package/dist/checks/bin/check-reader-resolvable-prose.js +1512 -259
- package/dist/checks/bin/check-registry-gates.js +892 -176
- package/dist/checks/bin/check-retired-vocabulary.js +1506 -253
- package/dist/checks/bin/check-review-gate.js +1000 -245
- package/dist/checks/bin/check-single-plan-pr.js +805 -176
- package/dist/checks/bin/check-surface-scope.js +830 -182
- package/dist/checks/bin/check-test-plan.js +834 -189
- package/dist/checks/bin/check-token-collection-wired.js +805 -176
- package/dist/checks/bin/check-token-report.js +805 -176
- package/dist/checks/bin/check-workspace-escape.js +1525 -253
- package/dist/index.js +9835 -5597
- package/dist/lib/pre-push-changed-files.js +57 -0
- package/dist/lib/pre-push-select-tests.js +1473 -0
- package/package.json +4 -2
|
@@ -3,9 +3,9 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
4
4
|
|
|
5
5
|
// src/checks/bin/check-doctrine-portability.ts
|
|
6
|
-
import { execFileSync as
|
|
6
|
+
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
7
7
|
import { readdirSync, readFileSync as readFileSync3, statSync } from "node:fs";
|
|
8
|
-
import { join as
|
|
8
|
+
import { join as join5 } from "node:path";
|
|
9
9
|
// ../../packages/aeg-core/src/gate-audience.ts
|
|
10
10
|
var GATE_AUDIENCE = {
|
|
11
11
|
"check-branch-topology": { shippedAs: "branch-topology", ring: 0 },
|
|
@@ -1956,6 +1956,8 @@ function parseInlineFieldList(section) {
|
|
|
1956
1956
|
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
1957
1957
|
var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1958
1958
|
var RULING_ORDINAL_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Ruling ordinal:\s*(\d+)(?!\d)/im;
|
|
1959
|
+
var BRIEF_HASH_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Brief hash:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1960
|
+
var POLICY_DIGEST_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Policy digest:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1959
1961
|
function firstFiveLines(comment) {
|
|
1960
1962
|
return comment.split(`
|
|
1961
1963
|
`).slice(0, 5).join(`
|
|
@@ -1978,6 +1980,26 @@ function extractRulingOrdinal(comment) {
|
|
|
1978
1980
|
const m = firstSevenLines(comment).match(RULING_ORDINAL_PATTERN);
|
|
1979
1981
|
return m ? Number.parseInt(m[1], 10) : null;
|
|
1980
1982
|
}
|
|
1983
|
+
function firstElevenLines(comment) {
|
|
1984
|
+
return comment.split(`
|
|
1985
|
+
`).slice(0, 11).join(`
|
|
1986
|
+
`);
|
|
1987
|
+
}
|
|
1988
|
+
function extractBriefHash(comment) {
|
|
1989
|
+
const m = firstElevenLines(comment).match(BRIEF_HASH_PATTERN);
|
|
1990
|
+
return m ? m[1].toLowerCase() : null;
|
|
1991
|
+
}
|
|
1992
|
+
function extractPolicyDigest(comment) {
|
|
1993
|
+
const m = firstElevenLines(comment).match(POLICY_DIGEST_PATTERN);
|
|
1994
|
+
return m ? m[1].toLowerCase() : null;
|
|
1995
|
+
}
|
|
1996
|
+
var FINDING_SEVERITY_LINE = /^\d+\.\s+\[([A-Z][A-Z]*)\]\s+(.+?)\s+—/gm;
|
|
1997
|
+
function extractFindingSeverities(comment) {
|
|
1998
|
+
return [...comment.matchAll(FINDING_SEVERITY_LINE)].map((m) => ({
|
|
1999
|
+
severity: m[1],
|
|
2000
|
+
location: m[2]
|
|
2001
|
+
}));
|
|
2002
|
+
}
|
|
1981
2003
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
1982
2004
|
const candidates = comments.filter((c) => valuePattern.test(c));
|
|
1983
2005
|
if (candidates.length === 0) {
|
|
@@ -1986,6 +2008,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1986
2008
|
headSha: null,
|
|
1987
2009
|
objectivesVersion: null,
|
|
1988
2010
|
rulingOrdinal: null,
|
|
2011
|
+
briefHash: null,
|
|
2012
|
+
policyDigest: null,
|
|
2013
|
+
findingSeverities: [],
|
|
1989
2014
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
1990
2015
|
};
|
|
1991
2016
|
}
|
|
@@ -1997,6 +2022,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1997
2022
|
headSha: null,
|
|
1998
2023
|
objectivesVersion: null,
|
|
1999
2024
|
rulingOrdinal: null,
|
|
2025
|
+
briefHash: null,
|
|
2026
|
+
policyDigest: null,
|
|
2027
|
+
findingSeverities: [],
|
|
2000
2028
|
danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
|
|
2001
2029
|
};
|
|
2002
2030
|
}
|
|
@@ -2005,6 +2033,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
2005
2033
|
headSha: extractHeadSha(latest),
|
|
2006
2034
|
objectivesVersion: extractObjectivesVersion(latest),
|
|
2007
2035
|
rulingOrdinal: extractRulingOrdinal(latest),
|
|
2036
|
+
briefHash: extractBriefHash(latest),
|
|
2037
|
+
policyDigest: extractPolicyDigest(latest),
|
|
2038
|
+
findingSeverities: extractFindingSeverities(latest),
|
|
2008
2039
|
danglingNote: null
|
|
2009
2040
|
};
|
|
2010
2041
|
}
|
|
@@ -2340,6 +2371,53 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive,
|
|
|
2340
2371
|
}
|
|
2341
2372
|
return out;
|
|
2342
2373
|
}
|
|
2374
|
+
// ../../packages/aeg-core/src/review-policy.ts
|
|
2375
|
+
var CODE_REVIEW_SEVERITY_ORDER = ["BLOCKER", "MAJOR", "MINOR"];
|
|
2376
|
+
var SECURITY_SEVERITY_ORDER = ["CRITICAL", "HIGH", "MEDIUM", "LOW"];
|
|
2377
|
+
var DEFAULT_MAX_ROUNDS = 3;
|
|
2378
|
+
var DEFAULT_REVIEW_POLICY = {
|
|
2379
|
+
codeReviewThreshold: "BLOCKER",
|
|
2380
|
+
securityThreshold: "HIGH",
|
|
2381
|
+
maxRounds: DEFAULT_MAX_ROUNDS
|
|
2382
|
+
};
|
|
2383
|
+
var FILE_SHAPED_LOCATION = /\.[a-zA-Z0-9]{1,10}(:\d+)?\s*$/;
|
|
2384
|
+
var PROSE_LOCATION_PATTERNS = [/\bpr\s*body\b/i, /\bcomment\b/i];
|
|
2385
|
+
var ROLE_FILE_LOCATION = /(^|\/)aeg-root\/roles\//i;
|
|
2386
|
+
function isProseLocation(location) {
|
|
2387
|
+
if (ROLE_FILE_LOCATION.test(location))
|
|
2388
|
+
return true;
|
|
2389
|
+
if (FILE_SHAPED_LOCATION.test(location))
|
|
2390
|
+
return false;
|
|
2391
|
+
return PROSE_LOCATION_PATTERNS.some((pattern) => pattern.test(location));
|
|
2392
|
+
}
|
|
2393
|
+
var PROSE_CAP_SEVERITY = "MINOR";
|
|
2394
|
+
function blockingSeverities(scale, threshold) {
|
|
2395
|
+
const idx = scale.indexOf(threshold);
|
|
2396
|
+
if (idx === -1) {
|
|
2397
|
+
throw new Error(`blockingSeverities: threshold "${threshold}" is not one of ${scale.join(" > ")}`);
|
|
2398
|
+
}
|
|
2399
|
+
return scale.slice(0, idx + 1);
|
|
2400
|
+
}
|
|
2401
|
+
function evaluateReviewFindings(findings, scale, threshold) {
|
|
2402
|
+
const blocking = new Set(blockingSeverities(scale, threshold));
|
|
2403
|
+
const blockingFindings = findings.filter((f) => {
|
|
2404
|
+
if (!scale.includes(f.severity)) {
|
|
2405
|
+
throw new Error(`evaluateReviewFindings: severity "${f.severity}" is not one of ${scale.join(" > ")}`);
|
|
2406
|
+
}
|
|
2407
|
+
const effectiveSeverity = f.location !== undefined && isProseLocation(f.location) ? PROSE_CAP_SEVERITY : f.severity;
|
|
2408
|
+
return blocking.has(effectiveSeverity);
|
|
2409
|
+
});
|
|
2410
|
+
return { outcome: blockingFindings.length > 0 ? "blocked" : "clean", blockingFindings };
|
|
2411
|
+
}
|
|
2412
|
+
function evaluateCodeReview(findings, policy) {
|
|
2413
|
+
return evaluateReviewFindings(findings, CODE_REVIEW_SEVERITY_ORDER, policy.codeReviewThreshold);
|
|
2414
|
+
}
|
|
2415
|
+
function evaluateSecurityReview(findings, policy) {
|
|
2416
|
+
return evaluateReviewFindings(findings, SECURITY_SEVERITY_ORDER, policy.securityThreshold);
|
|
2417
|
+
}
|
|
2418
|
+
function isKnownSeverity(scale, value) {
|
|
2419
|
+
return scale.includes(value);
|
|
2420
|
+
}
|
|
2343
2421
|
// ../../packages/aeg-core/src/pr-tier.ts
|
|
2344
2422
|
var TIER_FIELD = /(\*\*)?\s*Tier\s*(\*\*)?\s*:\s*(\*\*)?\s*([013])\b/i;
|
|
2345
2423
|
function readTierFromPrBody(prBody) {
|
|
@@ -2710,7 +2788,7 @@ function checkForField(prBody) {
|
|
|
2710
2788
|
]
|
|
2711
2789
|
};
|
|
2712
2790
|
}
|
|
2713
|
-
function
|
|
2791
|
+
function checkClosesNPresence(prBody) {
|
|
2714
2792
|
const closesPattern = /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s{0,8}:?\s{0,8}#\d+/i;
|
|
2715
2793
|
if (closesPattern.test(stripCode(prBody))) {
|
|
2716
2794
|
return { status: "pass", errors: [] };
|
|
@@ -2742,10 +2820,22 @@ var COMMIT_TYPES = [
|
|
|
2742
2820
|
"Test"
|
|
2743
2821
|
];
|
|
2744
2822
|
var COMMIT_TYPE_STYLE = new RegExp(`^(${COMMIT_TYPES.join("|")})(\\([a-z0-9-]+\\))?: \\S`);
|
|
2823
|
+
function checkForgeTitle(title) {
|
|
2824
|
+
const taskStyle = /^\[[a-z0-9._-]+\] \S+ — \S/;
|
|
2825
|
+
if (COMMIT_TYPE_STYLE.test(title) || taskStyle.test(title))
|
|
2826
|
+
return { status: "pass", errors: [] };
|
|
2827
|
+
return {
|
|
2828
|
+
status: "fail",
|
|
2829
|
+
errors: [
|
|
2830
|
+
`brief-validation title: "${title}" matches neither title grammar — expected \`Type: description\` / \`Type(scope): description\` (commitlint types + Plan) or \`[tranche] id — description\` (task form).`
|
|
2831
|
+
]
|
|
2832
|
+
};
|
|
2833
|
+
}
|
|
2745
2834
|
var BRIEF_SHAPE_MARKERS = [checkSurfaceMap, checkDocUpdateList, checkStopConditions, checkAutonomyClause];
|
|
2746
2835
|
var TASK_BRANCH_PATTERN = /^task\/[^/]+\/[^/]+$/;
|
|
2836
|
+
var TASK_ISSUE_BRANCH_PATTERN = /^task\/issue-\d+$/;
|
|
2747
2837
|
function isTaskBranch(branch) {
|
|
2748
|
-
return TASK_BRANCH_PATTERN.test(branch);
|
|
2838
|
+
return TASK_BRANCH_PATTERN.test(branch) || TASK_ISSUE_BRANCH_PATTERN.test(branch);
|
|
2749
2839
|
}
|
|
2750
2840
|
function isBriefShaped(prBody) {
|
|
2751
2841
|
const stripped = stripCode(prBody);
|
|
@@ -2867,8 +2957,9 @@ function packagesNamedIn(text) {
|
|
|
2867
2957
|
}
|
|
2868
2958
|
function hasTestPathForConsumer(text, consumerDir) {
|
|
2869
2959
|
const escaped = consumerDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2870
|
-
const
|
|
2871
|
-
|
|
2960
|
+
const filePathRe = new RegExp(`${escaped}\\/[\\w./-]*\\.test\\.[A-Za-z0-9]+`);
|
|
2961
|
+
const testDirRe = new RegExp(`${escaped}\\/(?:[\\w-]+\\/)*(?:tests|specs)(?:\\/|\\b)`);
|
|
2962
|
+
return filePathRe.test(text) || testDirRe.test(text);
|
|
2872
2963
|
}
|
|
2873
2964
|
function checkConsumerTests(prBody, consumersOf) {
|
|
2874
2965
|
const section4 = extractNumberedSection(prBody, 4);
|
|
@@ -2972,7 +3063,7 @@ function checkBriefSections(prBody, readTier, options = {}) {
|
|
|
2972
3063
|
checkConsumerTests(prBody, consumersOf),
|
|
2973
3064
|
checkDefeatCases(prBody),
|
|
2974
3065
|
...issueObjectives !== undefined ? [checkObjectivesCopy(prBody, issueObjectives), checkObjectivesCoverage(prBody)] : [],
|
|
2975
|
-
...requireClosesN ? [
|
|
3066
|
+
...requireClosesN ? [checkClosesNPresence(prBody)] : []
|
|
2976
3067
|
];
|
|
2977
3068
|
return { errors: results.flatMap((r) => r.errors) };
|
|
2978
3069
|
}
|
|
@@ -3262,6 +3353,28 @@ function topLevelSectionText(body, headingName) {
|
|
|
3262
3353
|
const next = /^##[ \t]/m.exec(afterHeading);
|
|
3263
3354
|
return next ? afterHeading.slice(0, next.index) : afterHeading;
|
|
3264
3355
|
}
|
|
3356
|
+
function partHasBacktickedPath(text) {
|
|
3357
|
+
let i = 0;
|
|
3358
|
+
while (i < text.length) {
|
|
3359
|
+
const start = text.indexOf("`", i);
|
|
3360
|
+
if (start === -1)
|
|
3361
|
+
return false;
|
|
3362
|
+
const end = text.indexOf("`", start + 1);
|
|
3363
|
+
if (end === -1)
|
|
3364
|
+
return false;
|
|
3365
|
+
if (text.slice(start + 1, end).includes("/"))
|
|
3366
|
+
return true;
|
|
3367
|
+
i = end + 1;
|
|
3368
|
+
}
|
|
3369
|
+
return false;
|
|
3370
|
+
}
|
|
3371
|
+
var PART_MIN_WORDS_OUTSIDE_BACKTICKS = 3;
|
|
3372
|
+
function stripPartBackticks(text) {
|
|
3373
|
+
return text.replace(/`[^`\n]*`/g, " ");
|
|
3374
|
+
}
|
|
3375
|
+
function partWordCount(text) {
|
|
3376
|
+
return text.split(/\s+/).filter((w) => /[a-z]/i.test(w)).length;
|
|
3377
|
+
}
|
|
3265
3378
|
function looksLikeFilePath(entry2) {
|
|
3266
3379
|
const stripped = entry2.replace(/\/\*\*?$/, "");
|
|
3267
3380
|
const lastSegment = stripped.split("/").pop() ?? stripped;
|
|
@@ -3302,6 +3415,18 @@ function globCoversPath(glob, path) {
|
|
|
3302
3415
|
const p = path.replace(/\/+$/, "");
|
|
3303
3416
|
return g === p || g.startsWith(`${p}/`) || p.startsWith(`${g}/`);
|
|
3304
3417
|
}
|
|
3418
|
+
function checkSurfaceGlobsResolve(body, resolvesToFile) {
|
|
3419
|
+
const surface = parseIssueSurface(body);
|
|
3420
|
+
if (!surface.ok)
|
|
3421
|
+
return { status: "pass", errors: [] };
|
|
3422
|
+
const errors = [];
|
|
3423
|
+
for (const glob of surface.value.in) {
|
|
3424
|
+
if (!resolvesToFile(glob)) {
|
|
3425
|
+
errors.push(`issue-validation Surface: \`${glob}\` in \`## Surface\`'s \`in:\` list matches no tracked file — a Surface that cannot resolve cannot render a brief's file list.`);
|
|
3426
|
+
}
|
|
3427
|
+
}
|
|
3428
|
+
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3429
|
+
}
|
|
3305
3430
|
function checkSurfaceScope(changedFiles, outGlobs) {
|
|
3306
3431
|
if (outGlobs.length === 0)
|
|
3307
3432
|
return { ok: true };
|
|
@@ -3313,9 +3438,77 @@ function checkSurfaceScope(changedFiles, outGlobs) {
|
|
|
3313
3438
|
}
|
|
3314
3439
|
return violations.length > 0 ? { ok: false, violations } : { ok: true };
|
|
3315
3440
|
}
|
|
3441
|
+
var ISSUE_PART_LINE_RE = /^Part\s+(\d+)\s*\(([^)]*)\)\s*[-—–]\s*(.*)$/i;
|
|
3442
|
+
function parseIssueParts(body) {
|
|
3443
|
+
const section = topLevelSectionText(body, "Parts");
|
|
3444
|
+
if (section === null)
|
|
3445
|
+
return { ok: false, errors: ["no `## Parts` heading found in the body."] };
|
|
3446
|
+
const lines = section.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
3447
|
+
const parts = [];
|
|
3448
|
+
const errors = [];
|
|
3449
|
+
for (const line of lines) {
|
|
3450
|
+
const m = ISSUE_PART_LINE_RE.exec(line);
|
|
3451
|
+
if (!m) {
|
|
3452
|
+
errors.push(`"${line}" is not a well-formed Parts line — expected \`Part <n> (<refs>) — <outcome>\`.`);
|
|
3453
|
+
continue;
|
|
3454
|
+
}
|
|
3455
|
+
const n = Number.parseInt(m[1], 10);
|
|
3456
|
+
const refs = m[2];
|
|
3457
|
+
const text = m[3].trim();
|
|
3458
|
+
if (text.length === 0) {
|
|
3459
|
+
errors.push(`Part ${n} has no outcome text after the dash — every Part states one observable outcome.`);
|
|
3460
|
+
continue;
|
|
3461
|
+
}
|
|
3462
|
+
if (partHasBacktickedPath(text) && partWordCount(stripPartBackticks(text)) < PART_MIN_WORDS_OUTSIDE_BACKTICKS) {
|
|
3463
|
+
errors.push(`Part ${n} is little more than a file path — a Part names an outcome and symbols, never a bare path.`);
|
|
3464
|
+
continue;
|
|
3465
|
+
}
|
|
3466
|
+
const objectiveIds = [...refs.matchAll(/O(\d+)/g)].map((r) => Number.parseInt(r[1], 10));
|
|
3467
|
+
parts.push({ n, objectiveIds, text });
|
|
3468
|
+
}
|
|
3469
|
+
if (parts.length === 0) {
|
|
3470
|
+
errors.push("the `## Parts` section has no well-formed `Part <n> (<refs>) — <outcome>` lines.");
|
|
3471
|
+
}
|
|
3472
|
+
if (errors.length > 0)
|
|
3473
|
+
return { ok: false, errors };
|
|
3474
|
+
return { ok: true, value: parts };
|
|
3475
|
+
}
|
|
3476
|
+
function checkPartsCiteDefinedObjectives(body) {
|
|
3477
|
+
const parts = parseIssueParts(body);
|
|
3478
|
+
const objectives = objectivesOf(body);
|
|
3479
|
+
if (!parts.ok || !objectives.ok)
|
|
3480
|
+
return { status: "pass", errors: [] };
|
|
3481
|
+
const definedIds = new Set(objectives.objectives.map((o) => Number.parseInt(o.id.slice(1), 10)));
|
|
3482
|
+
const errors = [];
|
|
3483
|
+
for (const part of parts.value) {
|
|
3484
|
+
for (const objectiveId of part.objectiveIds) {
|
|
3485
|
+
if (!definedIds.has(objectiveId)) {
|
|
3486
|
+
errors.push(`issue-validation Parts: Part ${part.n} cites O${objectiveId}, which the Issue's own \`## Objectives\` section does not define.`);
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3491
|
+
}
|
|
3316
3492
|
function isTaskIssueLabelSet(labels) {
|
|
3317
3493
|
return hasLabel("tranche", labels);
|
|
3318
3494
|
}
|
|
3495
|
+
function checkTrancheLabelPresence(_body, _labels) {
|
|
3496
|
+
return { status: "pass", errors: [] };
|
|
3497
|
+
}
|
|
3498
|
+
function checkMilestoneAttach(labels, currentMilestoneTitle, resolvedMilestoneTitle) {
|
|
3499
|
+
if (!isTaskIssueLabelSet(labels))
|
|
3500
|
+
return { status: "pass", errors: [] };
|
|
3501
|
+
if (resolvedMilestoneTitle === null)
|
|
3502
|
+
return { status: "pass", errors: [] };
|
|
3503
|
+
if (currentMilestoneTitle === resolvedMilestoneTitle)
|
|
3504
|
+
return { status: "pass", errors: [] };
|
|
3505
|
+
return {
|
|
3506
|
+
status: "fail",
|
|
3507
|
+
errors: [
|
|
3508
|
+
`issue-validation milestone attach: this task Issue's tranche label resolves to Milestone "${resolvedMilestoneTitle}", but its live Milestone is ${currentMilestoneTitle === null ? "unset" : `"${currentMilestoneTitle}"`} — attach it to "${resolvedMilestoneTitle}".`
|
|
3509
|
+
]
|
|
3510
|
+
};
|
|
3511
|
+
}
|
|
3319
3512
|
var TYPE_LABEL_IDS = LABELS.filter((l) => l.category === "type").map((l) => l.id);
|
|
3320
3513
|
function isControlCodePoint(codePoint) {
|
|
3321
3514
|
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
|
|
@@ -3380,6 +3573,19 @@ function checkProjectsRegistered(body, _labels, registeredNames) {
|
|
|
3380
3573
|
var DOC_PATH_RE = /(?:(?:aeg-root|apps|packages|specs|docs|tools|\.claude|\.github)\/[\w./@-]*\.(?:md|mdx)|\.claude\/(?:skills|rules)\/[\w./-]+|\b(?:docs-index|decisions|projects|state-machine|enforcement|process|README|CLAUDE)\.md\b|\b[\w-]+-(?:spec|decisions|backlog)\.md\b)/i;
|
|
3381
3574
|
var DOC_PATH_RE_GLOBAL = new RegExp(DOC_PATH_RE.source, "gi");
|
|
3382
3575
|
|
|
3576
|
+
// ../../packages/aeg-core/src/task-branch-identity.ts
|
|
3577
|
+
var ISSUE_BRANCH_PATTERN = /^task\/issue-(\d+)$/;
|
|
3578
|
+
var TRANCHE_BRANCH_PATTERN = /^task\/([^/]+)\/([^/]+)$/;
|
|
3579
|
+
function parseTaskBranchIdentity(branch) {
|
|
3580
|
+
const issueMatch = ISSUE_BRANCH_PATTERN.exec(branch);
|
|
3581
|
+
if (issueMatch)
|
|
3582
|
+
return { kind: "issue", issueNumber: Number(issueMatch[1]) };
|
|
3583
|
+
const trancheMatch = TRANCHE_BRANCH_PATTERN.exec(branch);
|
|
3584
|
+
if (trancheMatch)
|
|
3585
|
+
return { kind: "tranche", tranche: trancheMatch[1], taskId: trancheMatch[2] };
|
|
3586
|
+
return null;
|
|
3587
|
+
}
|
|
3588
|
+
|
|
3383
3589
|
// ../../packages/aeg-core/src/coherence-checks.ts
|
|
3384
3590
|
var COHERENCE_ENFORCED_FROM = "2026-07-01";
|
|
3385
3591
|
function isGrandfathered(isoDate) {
|
|
@@ -3400,6 +3606,7 @@ function checkA1(entries, principalAllowlist = PRINCIPAL_ALLOWLIST) {
|
|
|
3400
3606
|
if (handClosed)
|
|
3401
3607
|
continue;
|
|
3402
3608
|
failures.push({
|
|
3609
|
+
code: "closed-without-merge",
|
|
3403
3610
|
issue: e.task.issue,
|
|
3404
3611
|
tranche: e.trancheSlug,
|
|
3405
3612
|
task: e.task.id,
|
|
@@ -3424,6 +3631,7 @@ function checkA3(entries) {
|
|
|
3424
3631
|
continue;
|
|
3425
3632
|
if (e.facts.prState === "merged" && e.facts.issueState !== "closed") {
|
|
3426
3633
|
failures.push({
|
|
3634
|
+
code: "auto-close-misfire",
|
|
3427
3635
|
issue: e.task.issue,
|
|
3428
3636
|
tranche: e.trancheSlug,
|
|
3429
3637
|
task: e.task.id,
|
|
@@ -3448,6 +3656,7 @@ function checkT1(entries) {
|
|
|
3448
3656
|
continue;
|
|
3449
3657
|
if (e.facts === undefined) {
|
|
3450
3658
|
failures.push({
|
|
3659
|
+
code: "phantom-issue-ref",
|
|
3451
3660
|
issue: e.task.issue,
|
|
3452
3661
|
tranche: e.trancheSlug,
|
|
3453
3662
|
task: e.task.id,
|
|
@@ -3466,6 +3675,7 @@ function checkT2(openIssuesBySlug, topologyIssuesBySlug, ciTrancheSlug) {
|
|
|
3466
3675
|
for (const num of openNums) {
|
|
3467
3676
|
if (!topologySet.has(num)) {
|
|
3468
3677
|
failures.push({
|
|
3678
|
+
code: "orphan-task",
|
|
3469
3679
|
issue: num,
|
|
3470
3680
|
tranche: slug,
|
|
3471
3681
|
reason: `Issue #${num} is open and labeled ${trancheLabel(slug)} but does not appear in the topology file`
|
|
@@ -3500,6 +3710,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
|
|
|
3500
3710
|
continue;
|
|
3501
3711
|
if (forgeUnavailableSlugs?.has(e.trancheSlug)) {
|
|
3502
3712
|
failures.push({
|
|
3713
|
+
code: "tbd-in-active-tranche",
|
|
3503
3714
|
issue: null,
|
|
3504
3715
|
tranche: e.trancheSlug,
|
|
3505
3716
|
task: e.task.id,
|
|
@@ -3509,6 +3720,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
|
|
|
3509
3720
|
continue;
|
|
3510
3721
|
}
|
|
3511
3722
|
failures.push({
|
|
3723
|
+
code: "tbd-in-active-tranche",
|
|
3512
3724
|
issue: null,
|
|
3513
3725
|
tranche: e.trancheSlug,
|
|
3514
3726
|
task: e.task.id,
|
|
@@ -3541,6 +3753,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
|
|
|
3541
3753
|
const sameIssue = depEntry.task.issue !== null && e.task.issue !== null && depEntry.task.issue === e.task.issue;
|
|
3542
3754
|
if (sameTask || sameIssue) {
|
|
3543
3755
|
failures.push({
|
|
3756
|
+
code: "d1-self-dependency",
|
|
3544
3757
|
issue: e.task.issue,
|
|
3545
3758
|
tranche: e.trancheSlug,
|
|
3546
3759
|
task: e.task.id,
|
|
@@ -3552,6 +3765,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
|
|
|
3552
3765
|
const depClosed = depFacts?.issueState === "closed";
|
|
3553
3766
|
if (!depClosed) {
|
|
3554
3767
|
failures.push({
|
|
3768
|
+
code: "dispatched-on-unmet-deps",
|
|
3555
3769
|
issue: e.task.issue,
|
|
3556
3770
|
tranche: e.trancheSlug,
|
|
3557
3771
|
task: e.task.id,
|
|
@@ -3577,11 +3791,13 @@ function checkR1(issuesBySlug, grandfatheredIssues, registeredNames = []) {
|
|
|
3577
3791
|
const errors = [
|
|
3578
3792
|
...checkIssueRationale(issue.body).errors,
|
|
3579
3793
|
...checkProjectsRegistered(issue.body, issue.labels, registeredNames).errors,
|
|
3580
|
-
...checkIssueObjectives(issue.body, issue.number).errors
|
|
3794
|
+
...checkIssueObjectives(issue.body, issue.number).errors,
|
|
3795
|
+
...checkPartsCiteDefinedObjectives(issue.body).errors
|
|
3581
3796
|
];
|
|
3582
3797
|
if (errors.length === 0)
|
|
3583
3798
|
continue;
|
|
3584
3799
|
failures.push({
|
|
3800
|
+
code: "missing-rationale-field",
|
|
3585
3801
|
issue: issue.number,
|
|
3586
3802
|
tranche: slug,
|
|
3587
3803
|
reason: `Issue #${issue.number} fails the rationale gate: ${errors.join(" | ")}`,
|
|
@@ -3610,6 +3826,7 @@ function checkL1(files, entriesBySlug) {
|
|
|
3610
3826
|
const allClosed = withFacts.every((e) => e.facts?.issueState === "closed");
|
|
3611
3827
|
if (allClosed) {
|
|
3612
3828
|
failures.push({
|
|
3829
|
+
code: "archive-recommended",
|
|
3613
3830
|
tranche: f.slug,
|
|
3614
3831
|
reason: "Active tranche has no open task-Issues — consider archiving to completed/"
|
|
3615
3832
|
});
|
|
@@ -3641,27 +3858,38 @@ function extractClosesReferences(prBody) {
|
|
|
3641
3858
|
}
|
|
3642
3859
|
return referenced;
|
|
3643
3860
|
}
|
|
3644
|
-
function
|
|
3861
|
+
function checkClosesNTopology(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
3645
3862
|
const referenced = extractClosesReferences(prBody);
|
|
3646
3863
|
if (taskIssueRefs) {
|
|
3647
3864
|
for (const n of referenced) {
|
|
3648
|
-
const
|
|
3649
|
-
if (!
|
|
3865
|
+
const ref2 = taskIssueRefs.get(n);
|
|
3866
|
+
if (!ref2)
|
|
3650
3867
|
continue;
|
|
3651
|
-
const expectedBranch = `task/${
|
|
3868
|
+
const expectedBranch = `task/${ref2.trancheSlug}/${ref2.taskId}`;
|
|
3652
3869
|
if (branch !== expectedBranch) {
|
|
3653
3870
|
return {
|
|
3654
3871
|
ok: false,
|
|
3655
|
-
message: `closes-n-reverse: branch "${branch}" closes #${n} (task ${
|
|
3872
|
+
message: `closes-n-reverse: branch "${branch}" closes #${n} (task ${ref2.taskId} of tranche "${ref2.trancheSlug}") but is not named "${expectedBranch}" — rename the branch and re-push, or if this work is intentionally outside AEG's dispatch flow, remove the Closes reference.`
|
|
3656
3873
|
};
|
|
3657
3874
|
}
|
|
3658
3875
|
}
|
|
3659
3876
|
}
|
|
3660
|
-
const
|
|
3661
|
-
if (!
|
|
3877
|
+
const ref = parseTaskBranchIdentity(branch);
|
|
3878
|
+
if (!ref)
|
|
3662
3879
|
return { ok: true };
|
|
3663
|
-
|
|
3664
|
-
|
|
3880
|
+
if (ref.kind === "issue") {
|
|
3881
|
+
const expectedIssue2 = ref.issueNumber;
|
|
3882
|
+
if (!referenced.has(expectedIssue2)) {
|
|
3883
|
+
return {
|
|
3884
|
+
ok: false,
|
|
3885
|
+
expectedIssue: expectedIssue2,
|
|
3886
|
+
message: `closes-n: PR body does not contain \`Closes #${expectedIssue2}\` (required for branch "${branch}"). Add it to the PR body Summary section.`
|
|
3887
|
+
};
|
|
3888
|
+
}
|
|
3889
|
+
return { ok: true, expectedIssue: expectedIssue2 };
|
|
3890
|
+
}
|
|
3891
|
+
const trancheSlug = ref.tranche;
|
|
3892
|
+
const taskId = ref.taskId;
|
|
3665
3893
|
const trancheFile = trancheFiles.find((f) => f.slug === trancheSlug);
|
|
3666
3894
|
if (!trancheFile) {
|
|
3667
3895
|
return {
|
|
@@ -3713,34 +3941,67 @@ function extractIssue(body) {
|
|
|
3713
3941
|
const issue = headerNums.length > 0 ? headerNums[0] : bodyNums[0];
|
|
3714
3942
|
return { issue, extraIssues: bodyNums.filter((n) => n !== issue), outsideHeader: headerNums.length === 0 };
|
|
3715
3943
|
}
|
|
3716
|
-
// ../../packages/aeg-core/src/review-
|
|
3717
|
-
|
|
3718
|
-
|
|
3944
|
+
// ../../packages/aeg-core/src/review-input-manifest.ts
|
|
3945
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3946
|
+
function briefHash(brief) {
|
|
3947
|
+
return createHash4("sha256").update(`${brief}
|
|
3948
|
+
`).digest("hex");
|
|
3949
|
+
}
|
|
3950
|
+
function policyDigest(policy) {
|
|
3951
|
+
return createHash4("sha256").update(JSON.stringify({ codeReviewThreshold: policy.codeReviewThreshold, securityThreshold: policy.securityThreshold })).digest("hex");
|
|
3952
|
+
}
|
|
3953
|
+
function isBoundToHead(echoed, headSha) {
|
|
3954
|
+
if (!echoed.headSha)
|
|
3719
3955
|
return false;
|
|
3720
|
-
return headSha.toLowerCase().startsWith(
|
|
3956
|
+
return headSha.toLowerCase().startsWith(echoed.headSha.toLowerCase());
|
|
3721
3957
|
}
|
|
3722
|
-
function isBoundByPatchIdentity(
|
|
3723
|
-
if (patchIdOf === undefined || !
|
|
3958
|
+
function isBoundByPatchIdentity(echoed, headSha, patchIdOf) {
|
|
3959
|
+
if (patchIdOf === undefined || !echoed.headSha)
|
|
3724
3960
|
return false;
|
|
3725
|
-
const judged = patchIdOf(
|
|
3961
|
+
const judged = patchIdOf(echoed.headSha);
|
|
3726
3962
|
const current = patchIdOf(headSha);
|
|
3727
3963
|
if (judged === null || current === null)
|
|
3728
3964
|
return false;
|
|
3729
3965
|
return judged === current;
|
|
3730
3966
|
}
|
|
3731
|
-
function isBoundToPatch(
|
|
3732
|
-
return isBoundToHead(
|
|
3967
|
+
function isBoundToPatch(echoed, headSha, patchIdOf) {
|
|
3968
|
+
return isBoundToHead(echoed, headSha) || isBoundByPatchIdentity(echoed, headSha, patchIdOf);
|
|
3733
3969
|
}
|
|
3734
|
-
function isBoundToObjectives(
|
|
3970
|
+
function isBoundToObjectives(echoed, currentVersion) {
|
|
3735
3971
|
if (currentVersion === null)
|
|
3736
3972
|
return true;
|
|
3737
|
-
return
|
|
3973
|
+
return echoed.objectivesVersion === currentVersion;
|
|
3738
3974
|
}
|
|
3739
|
-
function isBoundToRulings(
|
|
3740
|
-
if (
|
|
3975
|
+
function isBoundToRulings(echoed, currentOrdinal) {
|
|
3976
|
+
if (echoed.rulingOrdinal === null)
|
|
3741
3977
|
return currentOrdinal === 0;
|
|
3742
|
-
return
|
|
3978
|
+
return echoed.rulingOrdinal === currentOrdinal;
|
|
3979
|
+
}
|
|
3980
|
+
function isBoundToBriefHash(echoed, currentHash) {
|
|
3981
|
+
if (currentHash === null)
|
|
3982
|
+
return true;
|
|
3983
|
+
return echoed.briefHash === currentHash;
|
|
3984
|
+
}
|
|
3985
|
+
function isBoundToPolicy(echoed, currentDigest) {
|
|
3986
|
+
return echoed.policyDigest === currentDigest;
|
|
3987
|
+
}
|
|
3988
|
+
function compareManifest(echoed, current, patchIdOf) {
|
|
3989
|
+
const head = isBoundToPatch(echoed, current.headSha, patchIdOf);
|
|
3990
|
+
const briefHashBound = isBoundToBriefHash(echoed, current.briefHash);
|
|
3991
|
+
const objectivesVersion2 = isBoundToObjectives(echoed, current.objectivesVersion);
|
|
3992
|
+
const rulingOrdinal = isBoundToRulings(echoed, current.rulingOrdinal);
|
|
3993
|
+
const policyDigestBound = isBoundToPolicy(echoed, current.policyDigest);
|
|
3994
|
+
return {
|
|
3995
|
+
bound: head && briefHashBound && objectivesVersion2 && rulingOrdinal && policyDigestBound,
|
|
3996
|
+
head,
|
|
3997
|
+
briefHash: briefHashBound,
|
|
3998
|
+
objectivesVersion: objectivesVersion2,
|
|
3999
|
+
rulingOrdinal,
|
|
4000
|
+
policyDigest: policyDigestBound
|
|
4001
|
+
};
|
|
3743
4002
|
}
|
|
4003
|
+
|
|
4004
|
+
// ../../packages/aeg-core/src/review-gate.ts
|
|
3744
4005
|
function checkReviewGate(input) {
|
|
3745
4006
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
3746
4007
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -3763,15 +4024,55 @@ function checkReviewGate(input) {
|
|
|
3763
4024
|
const verifiedBodies = verified.map((c) => c.body);
|
|
3764
4025
|
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
3765
4026
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
3766
|
-
const
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
4027
|
+
const policy = input.policy ?? DEFAULT_REVIEW_POLICY;
|
|
4028
|
+
let codeReviewPolicyEvaluation;
|
|
4029
|
+
let securityPolicyEvaluation;
|
|
4030
|
+
try {
|
|
4031
|
+
codeReviewPolicyEvaluation = evaluateCodeReview(codeReview.findingSeverities, policy);
|
|
4032
|
+
securityPolicyEvaluation = evaluateSecurityReview(security.findingSeverities, policy);
|
|
4033
|
+
} catch (err) {
|
|
4034
|
+
return {
|
|
4035
|
+
verdict: "fail",
|
|
4036
|
+
reason: `a verdict comment carries a finding severity this repository's policy does not recognize: ${err instanceof Error ? err.message : String(err)}`,
|
|
4037
|
+
waived: false
|
|
4038
|
+
};
|
|
4039
|
+
}
|
|
4040
|
+
const codeReviewTextClean = codeReview.value === "APPROVE";
|
|
4041
|
+
const securityTextClean = security.value === "PASS";
|
|
4042
|
+
const codeReviewPolicyClean = codeReviewPolicyEvaluation.outcome === "clean";
|
|
4043
|
+
const securityPolicyClean = securityPolicyEvaluation.outcome === "clean";
|
|
4044
|
+
const codeReviewClean = codeReviewTextClean && codeReviewPolicyClean;
|
|
4045
|
+
const securityClean = securityTextClean && securityPolicyClean;
|
|
4046
|
+
const currentManifest = {
|
|
4047
|
+
headSha: input.headSha,
|
|
4048
|
+
briefHash: input.briefHash ?? null,
|
|
4049
|
+
objectivesVersion: input.objectivesVersion,
|
|
4050
|
+
rulingOrdinal: input.rulingOrdinal,
|
|
4051
|
+
policyDigest: policyDigest(policy)
|
|
4052
|
+
};
|
|
4053
|
+
const codeReviewEchoed = {
|
|
4054
|
+
headSha: codeReview.headSha,
|
|
4055
|
+
briefHash: codeReview.briefHash,
|
|
4056
|
+
objectivesVersion: codeReview.objectivesVersion,
|
|
4057
|
+
rulingOrdinal: codeReview.rulingOrdinal,
|
|
4058
|
+
policyDigest: codeReview.policyDigest
|
|
4059
|
+
};
|
|
4060
|
+
const securityEchoed = {
|
|
4061
|
+
headSha: security.headSha,
|
|
4062
|
+
briefHash: security.briefHash,
|
|
4063
|
+
objectivesVersion: security.objectivesVersion,
|
|
4064
|
+
rulingOrdinal: security.rulingOrdinal,
|
|
4065
|
+
policyDigest: security.policyDigest
|
|
4066
|
+
};
|
|
4067
|
+
const codeReviewBinding = compareManifest(codeReviewEchoed, currentManifest, input.patchIdOf);
|
|
4068
|
+
const securityBinding = compareManifest(securityEchoed, currentManifest, input.patchIdOf);
|
|
4069
|
+
const codeReviewBound = codeReviewBinding.head;
|
|
4070
|
+
const securityBound = securityBinding.head;
|
|
4071
|
+
const codeReviewObjectivesBound = codeReviewBinding.objectivesVersion;
|
|
4072
|
+
const securityObjectivesBound = securityBinding.objectivesVersion;
|
|
4073
|
+
const codeReviewRulingsBound = codeReviewBinding.rulingOrdinal;
|
|
4074
|
+
const securityRulingsBound = securityBinding.rulingOrdinal;
|
|
4075
|
+
if (codeReviewClean && codeReviewBinding.bound && securityClean && securityBinding.bound && mechanicalChecksClean) {
|
|
3775
4076
|
return {
|
|
3776
4077
|
verdict: "pass",
|
|
3777
4078
|
reason: `code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS, both covering head ${input.headSha}, and every reported mechanical check is green.`,
|
|
@@ -3779,23 +4080,35 @@ function checkReviewGate(input) {
|
|
|
3779
4080
|
};
|
|
3780
4081
|
}
|
|
3781
4082
|
const problems = [];
|
|
3782
|
-
if (!
|
|
4083
|
+
if (!codeReviewTextClean) {
|
|
3783
4084
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
4085
|
+
} else if (!codeReviewPolicyClean) {
|
|
4086
|
+
problems.push(`code-reviewer verdict says APPROVE but carries a finding (${codeReviewPolicyEvaluation.blockingFindings.map((f) => f.severity).join(", ")}) at or above this repository's code-review policy threshold (${policy.codeReviewThreshold}) — a reviewer's own APPROVE never overrides policy`);
|
|
3784
4087
|
} else if (!codeReviewBound) {
|
|
3785
4088
|
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3786
4089
|
} else if (!codeReviewObjectivesBound) {
|
|
3787
4090
|
problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3788
4091
|
} else if (!codeReviewRulingsBound) {
|
|
3789
4092
|
problems.push(`the newest code-review verdict was cast against ruling ordinal ${codeReview.rulingOrdinal ?? "none"}, a newer ruling (ruling ${input.rulingOrdinal}) is now posted on this PR`);
|
|
4093
|
+
} else if (!codeReviewBinding.briefHash) {
|
|
4094
|
+
problems.push(`the newest code-review verdict was cast against brief hash ${codeReview.briefHash ?? "none"}, the frozen brief's current hash is ${currentManifest.briefHash ?? "none"}`);
|
|
4095
|
+
} else if (!codeReviewBinding.policyDigest) {
|
|
4096
|
+
problems.push(`the newest code-review verdict was cast against review policy digest ${codeReview.policyDigest ?? "none"}, this repository's current policy digest is ${currentManifest.policyDigest}`);
|
|
3790
4097
|
}
|
|
3791
|
-
if (!
|
|
4098
|
+
if (!securityTextClean) {
|
|
3792
4099
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
4100
|
+
} else if (!securityPolicyClean) {
|
|
4101
|
+
problems.push(`security-review verdict says PASS but carries a finding (${securityPolicyEvaluation.blockingFindings.map((f) => f.severity).join(", ")}) at or above this repository's security policy threshold (${policy.securityThreshold}) — a reviewer's own PASS never overrides policy`);
|
|
3793
4102
|
} else if (!securityBound) {
|
|
3794
4103
|
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3795
4104
|
} else if (!securityObjectivesBound) {
|
|
3796
4105
|
problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3797
4106
|
} else if (!securityRulingsBound) {
|
|
3798
4107
|
problems.push(`the newest security-review verdict was cast against ruling ordinal ${security.rulingOrdinal ?? "none"}, a newer ruling (ruling ${input.rulingOrdinal}) is now posted on this PR`);
|
|
4108
|
+
} else if (!securityBinding.briefHash) {
|
|
4109
|
+
problems.push(`the newest security-review verdict was cast against brief hash ${security.briefHash ?? "none"}, the frozen brief's current hash is ${currentManifest.briefHash ?? "none"}`);
|
|
4110
|
+
} else if (!securityBinding.policyDigest) {
|
|
4111
|
+
problems.push(`the newest security-review verdict was cast against review policy digest ${security.policyDigest ?? "none"}, this repository's current policy digest is ${currentManifest.policyDigest}`);
|
|
3799
4112
|
}
|
|
3800
4113
|
if (!mechanicalChecksClean) {
|
|
3801
4114
|
problems.push(reportedMechanicalChecks.length === 0 ? "no mechanical checks have reported for this head yet" : `mechanical check(s) not green: ${reportedMechanicalChecks.filter((c) => c.bucket !== "pass").map((c) => `${c.name} (${c.bucket})`).join(", ")}`);
|
|
@@ -4487,42 +4800,45 @@ function checkDispatchReadiness(input) {
|
|
|
4487
4800
|
const { trancheSlug, task } = input;
|
|
4488
4801
|
const taskLabel = `task ${task.id} (tranche ${trancheSlug})`;
|
|
4489
4802
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
4490
|
-
const
|
|
4803
|
+
const blockerDetails = [];
|
|
4804
|
+
const push = (blockerClass, message) => {
|
|
4805
|
+
blockerDetails.push({ class: blockerClass, message });
|
|
4806
|
+
};
|
|
4491
4807
|
if (task.issue === null) {
|
|
4492
|
-
|
|
4808
|
+
push("issue-existence", `dispatch-gate issue-existence: ${taskLabel} has no Issue (#TBD or blank) in the topology — not dispatchable until the Planner cuts the Issue.`);
|
|
4493
4809
|
} else if (input.issue === null) {
|
|
4494
|
-
|
|
4810
|
+
push("issue-existence", `dispatch-gate issue-existence: ${taskLabel} names Issue #${task.issue}, but it does not resolve to a real GitHub Issue (phantom reference).`);
|
|
4495
4811
|
}
|
|
4496
4812
|
if (input.issue !== null && !input.issueRationalePass) {
|
|
4497
|
-
|
|
4813
|
+
push("rationale", `dispatch-gate rationale: Issue #${input.issue.number} for ${taskLabel} fails the rationale gate (checkIssueRationale) — the Planner must complete the eight-field rationale before this task is dispatchable.`);
|
|
4498
4814
|
}
|
|
4499
4815
|
for (const dep of input.dependsOn) {
|
|
4500
4816
|
if (isSelfDependency(dep, task, input.issue)) {
|
|
4501
4817
|
const issueStr = input.issue !== null ? `#${input.issue.number}` : "?";
|
|
4502
|
-
|
|
4818
|
+
push("internal-self-dependency", `dispatch-gate INTERNAL: parsed a self-dependency for task ${task.id} (${issueStr}) — this is a parser bug in parseRationaleDeps, not a real dependency. Please report it upstream. Re-run once the rationale is corrected or the fix ships.`);
|
|
4503
4819
|
continue;
|
|
4504
4820
|
}
|
|
4505
4821
|
if (dep.resolved === false) {
|
|
4506
|
-
|
|
4822
|
+
push("depends-on-unresolvable", `dispatch-gate depends-on: ${taskLabel} depends on "${dep.id}", which is UNRESOLVABLE — the resolver could not find a matching tranche/task/Issue for this edge (not a claim about merge status). Not dispatchable until the edge is corrected.`);
|
|
4507
4823
|
continue;
|
|
4508
4824
|
}
|
|
4509
4825
|
if (!dep.merged && !isHandClosedByRecognizedPrincipal(dep, principalAllowlist)) {
|
|
4510
4826
|
const issueStr = dep.issue !== null ? ` (#${dep.issue})` : "";
|
|
4511
|
-
|
|
4827
|
+
push("depends-on-not-merged", `dispatch-gate depends-on: ${taskLabel} depends on ${dep.id}${issueStr}, whose PR is not merged yet — not dispatchable, it serializes behind it.`);
|
|
4512
4828
|
}
|
|
4513
4829
|
}
|
|
4514
4830
|
for (const c of input.conflictsWith) {
|
|
4515
4831
|
if (c.openOrInFlight) {
|
|
4516
4832
|
const issueStr = c.issue !== null ? ` (#${c.issue})` : "";
|
|
4517
|
-
|
|
4833
|
+
push("conflicts-with", `dispatch-gate conflicts-with: ${taskLabel} conflicts with ${c.id}${issueStr}, whose PR is open or in-flight — not dispatchable until it merges.`);
|
|
4518
4834
|
}
|
|
4519
4835
|
}
|
|
4520
4836
|
for (const proj of input.priorTrancheArchival) {
|
|
4521
4837
|
if (proj.priorTrancheSlug !== null && !proj.archived) {
|
|
4522
|
-
|
|
4838
|
+
push("prior-tranche-archival", `dispatch-gate prior-tranche-archival: project \`${proj.project}\`'s previous tranche \`${proj.priorTrancheSlug}\` is not archived — the Tranche Archivist must run before new work on this product.`);
|
|
4523
4839
|
}
|
|
4524
4840
|
}
|
|
4525
|
-
return { ready:
|
|
4841
|
+
return { ready: blockerDetails.length === 0, blockers: blockerDetails.map((b) => b.message), blockerDetails };
|
|
4526
4842
|
}
|
|
4527
4843
|
// ../../packages/aeg-core/src/single-plan-pr.ts
|
|
4528
4844
|
function trancheSlugFromTopologyPath(path) {
|
|
@@ -4624,6 +4940,91 @@ function checkBranchTopology(input) {
|
|
|
4624
4940
|
reason: `Branch \`${branch}\` matches topology row \`${taskId}\` in ${topoPath}.`
|
|
4625
4941
|
};
|
|
4626
4942
|
}
|
|
4943
|
+
// ../../packages/aeg-core/src/task-tools.ts
|
|
4944
|
+
import { z } from "zod";
|
|
4945
|
+
var TASK_TOOL_ERROR_KINDS = [
|
|
4946
|
+
"validation",
|
|
4947
|
+
"authority",
|
|
4948
|
+
"precondition",
|
|
4949
|
+
"capability",
|
|
4950
|
+
"infrastructure",
|
|
4951
|
+
"cancellation",
|
|
4952
|
+
"timeout",
|
|
4953
|
+
"uncertain_effect"
|
|
4954
|
+
];
|
|
4955
|
+
var TaskToolErrorSchema = z.object({
|
|
4956
|
+
kind: z.enum(TASK_TOOL_ERROR_KINDS),
|
|
4957
|
+
message: z.string().min(1),
|
|
4958
|
+
detail: z.string().optional()
|
|
4959
|
+
});
|
|
4960
|
+
var TaskToolRefSchema = z.union([
|
|
4961
|
+
z.object({ tranche: z.string().min(1), id: z.string().min(1) }),
|
|
4962
|
+
z.object({ issue: z.number().int().positive() })
|
|
4963
|
+
]);
|
|
4964
|
+
var MAX_PAGE_LIMIT = 100;
|
|
4965
|
+
var PageRequestSchema = z.object({
|
|
4966
|
+
cursor: z.string().optional(),
|
|
4967
|
+
limit: z.number().int().positive().max(MAX_PAGE_LIMIT).optional()
|
|
4968
|
+
});
|
|
4969
|
+
var FreshnessSchema = z.enum(["fresh", "stale", "unknown"]);
|
|
4970
|
+
var ObservedSchema = z.object({
|
|
4971
|
+
observedAt: z.string(),
|
|
4972
|
+
freshness: FreshnessSchema
|
|
4973
|
+
});
|
|
4974
|
+
var TaskStatusInputSchema = z.object({
|
|
4975
|
+
task: TaskToolRefSchema.optional()
|
|
4976
|
+
}).merge(PageRequestSchema);
|
|
4977
|
+
var TaskStatusItemSchema = z.object({
|
|
4978
|
+
task: TaskToolRefSchema,
|
|
4979
|
+
issue: z.number().int().positive(),
|
|
4980
|
+
pr: z.number().int().positive().nullable(),
|
|
4981
|
+
state: z.string()
|
|
4982
|
+
}).merge(ObservedSchema);
|
|
4983
|
+
var TaskStatusResultSchema = z.object({
|
|
4984
|
+
items: z.array(TaskStatusItemSchema),
|
|
4985
|
+
nextCursor: z.string().nullable()
|
|
4986
|
+
});
|
|
4987
|
+
var RequestedAuthoritySchema = z.enum(["planner", "principal", "operator", "self"]);
|
|
4988
|
+
var EscalationInputsSchema = z.object({
|
|
4989
|
+
task: z.number().int().positive(),
|
|
4990
|
+
round: z.number().int().nonnegative(),
|
|
4991
|
+
head: z.string(),
|
|
4992
|
+
branch: z.string(),
|
|
4993
|
+
prNumber: z.number().int().positive()
|
|
4994
|
+
});
|
|
4995
|
+
var EscalationEvidenceSchema = z.object({
|
|
4996
|
+
round: z.number().int().nonnegative(),
|
|
4997
|
+
reviewer: z.string().nullable(),
|
|
4998
|
+
security: z.string().nullable()
|
|
4999
|
+
});
|
|
5000
|
+
var TaskEscalationReadInputSchema = z.object({
|
|
5001
|
+
task: TaskToolRefSchema
|
|
5002
|
+
}).merge(PageRequestSchema);
|
|
5003
|
+
var TaskEscalationPacketSchema = z.object({
|
|
5004
|
+
reason: z.string(),
|
|
5005
|
+
detail: z.string().nullable(),
|
|
5006
|
+
inputs: EscalationInputsSchema.nullable(),
|
|
5007
|
+
evidence: EscalationEvidenceSchema.nullable(),
|
|
5008
|
+
attemptedRecovery: z.string(),
|
|
5009
|
+
requestedAuthority: RequestedAuthoritySchema,
|
|
5010
|
+
permittedNextActions: z.array(z.string())
|
|
5011
|
+
}).merge(ObservedSchema);
|
|
5012
|
+
var TaskEscalationReadResultSchema = z.object({
|
|
5013
|
+
items: z.array(TaskEscalationPacketSchema),
|
|
5014
|
+
nextCursor: z.string().nullable()
|
|
5015
|
+
}).merge(ObservedSchema);
|
|
5016
|
+
var TaskStartInputSchema = z.object({
|
|
5017
|
+
tranche: z.string().min(1),
|
|
5018
|
+
id: z.string().min(1)
|
|
5019
|
+
});
|
|
5020
|
+
var TaskResumeInputSchema = z.object({
|
|
5021
|
+
task: TaskToolRefSchema
|
|
5022
|
+
});
|
|
5023
|
+
var TaskCancelInputSchema = z.object({
|
|
5024
|
+
task: TaskToolRefSchema,
|
|
5025
|
+
reason: z.string().min(1)
|
|
5026
|
+
});
|
|
5027
|
+
var NoResultSchema = z.never();
|
|
4627
5028
|
// ../../packages/aeg-core/src/first-push-dispatch-gate.ts
|
|
4628
5029
|
function parseTaskBranch(branch) {
|
|
4629
5030
|
const m = /^task\/([^/]+)\/([^/]+)$/.exec(branch);
|
|
@@ -4707,7 +5108,6 @@ function decideIssueAssignment(input) {
|
|
|
4707
5108
|
};
|
|
4708
5109
|
}
|
|
4709
5110
|
// ../../packages/aeg-core/src/test-plan-gate.ts
|
|
4710
|
-
var TASK_BRANCH_PATTERN2 = /^task\/[^/]+\/[^/]+$/;
|
|
4711
5111
|
function evaluateTestPlanGate(body, branch) {
|
|
4712
5112
|
if (!body) {
|
|
4713
5113
|
return {
|
|
@@ -4720,7 +5120,7 @@ function evaluateTestPlanGate(body, branch) {
|
|
|
4720
5120
|
}
|
|
4721
5121
|
const located = locateTestPlanSection(body);
|
|
4722
5122
|
if (!located.found) {
|
|
4723
|
-
if (
|
|
5123
|
+
if (parseTaskBranchIdentity(branch) !== null) {
|
|
4724
5124
|
return {
|
|
4725
5125
|
verdict: "fail",
|
|
4726
5126
|
messages: [
|
|
@@ -4848,7 +5248,7 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
|
|
|
4848
5248
|
return findings;
|
|
4849
5249
|
}
|
|
4850
5250
|
// ../../packages/aeg-core/src/log/schema.ts
|
|
4851
|
-
import { z } from "zod";
|
|
5251
|
+
import { z as z2 } from "zod";
|
|
4852
5252
|
var ROLE_VALUES = [
|
|
4853
5253
|
"planner",
|
|
4854
5254
|
"developer",
|
|
@@ -4858,136 +5258,172 @@ var ROLE_VALUES = [
|
|
|
4858
5258
|
"archivist",
|
|
4859
5259
|
"architect"
|
|
4860
5260
|
];
|
|
4861
|
-
var RoleSchema =
|
|
5261
|
+
var RoleSchema = z2.enum(ROLE_VALUES);
|
|
4862
5262
|
var HOST_VALUES = ["hook", "ci", "cli", "loop"];
|
|
4863
|
-
var HostSchema =
|
|
5263
|
+
var HostSchema = z2.enum(HOST_VALUES);
|
|
4864
5264
|
var RUN_ID_PATTERN = /^[A-Za-z0-9_.-]{1,128}$/;
|
|
4865
|
-
var
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
doctrine: z.string(),
|
|
5265
|
+
var headerMetaCore = {
|
|
5266
|
+
ts: z2.string(),
|
|
5267
|
+
run_id: z2.string().regex(RUN_ID_PATTERN),
|
|
5268
|
+
seq: z2.number().int().nonnegative(),
|
|
5269
|
+
repo: z2.string().nullable(),
|
|
5270
|
+
vinaya: z2.string(),
|
|
5271
|
+
doctrine: z2.string(),
|
|
4873
5272
|
host: HostSchema,
|
|
4874
|
-
machine:
|
|
5273
|
+
machine: z2.string()
|
|
5274
|
+
};
|
|
5275
|
+
var HeaderMetaV1Schema = z2.object({
|
|
5276
|
+
schema: z2.literal(1),
|
|
5277
|
+
...headerMetaCore
|
|
4875
5278
|
}).strict();
|
|
4876
|
-
var
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
role: z.union([RoleSchema, z.literal("unattributed")]),
|
|
4881
|
-
round: z.number().int().optional(),
|
|
4882
|
-
objectives_version: z.string().optional()
|
|
5279
|
+
var LineageSchema = z2.object({
|
|
5280
|
+
run: z2.string().nullable(),
|
|
5281
|
+
attempt: z2.number().int().nullable(),
|
|
5282
|
+
parent: z2.string().nullable()
|
|
4883
5283
|
}).strict();
|
|
4884
|
-
var
|
|
5284
|
+
var InputVersionsSchema = z2.object({
|
|
5285
|
+
objectives_version: z2.string().nullable(),
|
|
5286
|
+
brief_hash: z2.string().nullable(),
|
|
5287
|
+
ruling_ordinal: z2.number().int().nullable(),
|
|
5288
|
+
policy_digest: z2.string().nullable()
|
|
5289
|
+
}).strict();
|
|
5290
|
+
var ProvenanceSchema = z2.enum(["parent_attributed", "env_correlated", "self_reported", "unavailable"]);
|
|
5291
|
+
var HeaderMetaV2Schema = z2.object({
|
|
5292
|
+
schema: z2.literal(2),
|
|
5293
|
+
...headerMetaCore,
|
|
5294
|
+
event_id: z2.string().min(1),
|
|
5295
|
+
process_id: z2.string().min(1),
|
|
5296
|
+
actor_id: z2.string().nullable(),
|
|
5297
|
+
lineage: LineageSchema,
|
|
5298
|
+
input_versions: InputVersionsSchema,
|
|
5299
|
+
provenance: ProvenanceSchema
|
|
5300
|
+
}).strict();
|
|
5301
|
+
var HeaderMetaSchema = z2.discriminatedUnion("schema", [HeaderMetaV1Schema, HeaderMetaV2Schema]);
|
|
5302
|
+
var SubjectSchema = z2.object({
|
|
5303
|
+
issue: z2.number().int().nullable(),
|
|
5304
|
+
pr: z2.number().int().optional(),
|
|
5305
|
+
sha: z2.string().optional(),
|
|
5306
|
+
role: z2.union([RoleSchema, z2.literal("unattributed")]),
|
|
5307
|
+
round: z2.number().int().optional(),
|
|
5308
|
+
objectives_version: z2.string().optional()
|
|
5309
|
+
}).strict();
|
|
5310
|
+
var HeaderSchema = z2.object({
|
|
4885
5311
|
meta: HeaderMetaSchema,
|
|
4886
5312
|
subject: SubjectSchema
|
|
4887
5313
|
}).strict();
|
|
4888
5314
|
var envelopeTail = {
|
|
4889
|
-
duration_ms:
|
|
4890
|
-
payload:
|
|
5315
|
+
duration_ms: z2.number().nonnegative().optional(),
|
|
5316
|
+
payload: z2.object({}).strict()
|
|
4891
5317
|
};
|
|
4892
|
-
var
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
5318
|
+
var ReviewFindingSchema = z2.object({
|
|
5319
|
+
id: z2.string(),
|
|
5320
|
+
severity: z2.string(),
|
|
5321
|
+
state: z2.string().optional(),
|
|
5322
|
+
severity_scale: z2.string().optional(),
|
|
5323
|
+
policy_treatment: z2.enum(["blocking", "non_blocking", "unavailable"]).optional(),
|
|
5324
|
+
confidence: z2.number().min(0).max(1).optional(),
|
|
5325
|
+
confidence_scale: z2.string().optional(),
|
|
5326
|
+
confidence_source: z2.string().optional()
|
|
5327
|
+
}).strict();
|
|
5328
|
+
var DispatchOutcomeSchema = z2.discriminatedUnion("type", [
|
|
5329
|
+
z2.object({ type: z2.literal("pr_opened"), pr: z2.number().int(), head: z2.string() }).strict(),
|
|
5330
|
+
z2.object({
|
|
5331
|
+
type: z2.literal("round_pushed"),
|
|
5332
|
+
pr: z2.number().int(),
|
|
5333
|
+
head: z2.string(),
|
|
5334
|
+
comment_id: z2.number().int()
|
|
4899
5335
|
}).strict(),
|
|
4900
|
-
|
|
4901
|
-
type:
|
|
4902
|
-
verdict:
|
|
4903
|
-
head:
|
|
4904
|
-
comment_id:
|
|
4905
|
-
objectives:
|
|
4906
|
-
findings:
|
|
5336
|
+
z2.object({
|
|
5337
|
+
type: z2.literal("verdict"),
|
|
5338
|
+
verdict: z2.enum(["APPROVE", "REQUEST CHANGES", "PASS", "FAIL"]),
|
|
5339
|
+
head: z2.string(),
|
|
5340
|
+
comment_id: z2.number().int(),
|
|
5341
|
+
objectives: z2.array(z2.object({ id: z2.string(), met: z2.boolean() }).strict()),
|
|
5342
|
+
findings: z2.array(ReviewFindingSchema)
|
|
4907
5343
|
}).strict(),
|
|
4908
|
-
|
|
4909
|
-
type:
|
|
4910
|
-
class:
|
|
4911
|
-
comment_id:
|
|
5344
|
+
z2.object({
|
|
5345
|
+
type: z2.literal("escalation"),
|
|
5346
|
+
class: z2.enum(["authority", "strategy", "product"]),
|
|
5347
|
+
comment_id: z2.number().int()
|
|
4912
5348
|
}).strict(),
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
5349
|
+
z2.object({ type: z2.literal("brief"), comment_id: z2.number().int(), hash: z2.string() }).strict(),
|
|
5350
|
+
z2.object({ type: z2.literal("plan"), issues: z2.array(z2.number().int()) }).strict(),
|
|
5351
|
+
z2.object({ type: z2.literal("archive"), provenance_comment_id: z2.number().int() }).strict()
|
|
4916
5352
|
]);
|
|
4917
5353
|
var dispatchShared = {
|
|
4918
5354
|
meta: HeaderMetaSchema,
|
|
4919
5355
|
subject: SubjectSchema,
|
|
4920
|
-
kind:
|
|
5356
|
+
kind: z2.literal("dispatch"),
|
|
4921
5357
|
...envelopeTail,
|
|
4922
5358
|
target_role: RoleSchema,
|
|
4923
|
-
model:
|
|
4924
|
-
round:
|
|
4925
|
-
effect_id:
|
|
5359
|
+
model: z2.string(),
|
|
5360
|
+
round: z2.number().int().optional(),
|
|
5361
|
+
effect_id: z2.string()
|
|
4926
5362
|
};
|
|
4927
|
-
var dispatchUsageField =
|
|
4928
|
-
var DispatchEventSchema =
|
|
4929
|
-
|
|
4930
|
-
|
|
5363
|
+
var dispatchUsageField = z2.object({ input: z2.number().nonnegative(), output: z2.number().nonnegative() }).strict().nullable();
|
|
5364
|
+
var DispatchEventSchema = z2.discriminatedUnion("event", [
|
|
5365
|
+
z2.object({ ...dispatchShared, event: z2.literal("dispatched"), prompt_hash: z2.string() }).strict(),
|
|
5366
|
+
z2.object({
|
|
4931
5367
|
...dispatchShared,
|
|
4932
|
-
event:
|
|
5368
|
+
event: z2.literal("outcome_received"),
|
|
4933
5369
|
outcome: DispatchOutcomeSchema,
|
|
4934
5370
|
usage: dispatchUsageField
|
|
4935
5371
|
}).strict(),
|
|
4936
|
-
|
|
5372
|
+
z2.object({
|
|
4937
5373
|
...dispatchShared,
|
|
4938
|
-
event:
|
|
4939
|
-
reason:
|
|
5374
|
+
event: z2.literal("dispatch_failed"),
|
|
5375
|
+
reason: z2.enum(["timeout", "crash", "refused", "unattributed_write"]),
|
|
4940
5376
|
usage: dispatchUsageField
|
|
4941
5377
|
}).strict()
|
|
4942
5378
|
]);
|
|
4943
5379
|
var loopShared = {
|
|
4944
5380
|
meta: HeaderMetaSchema,
|
|
4945
5381
|
subject: SubjectSchema,
|
|
4946
|
-
kind:
|
|
5382
|
+
kind: z2.literal("dev_review_loop"),
|
|
4947
5383
|
...envelopeTail,
|
|
4948
|
-
loop_id:
|
|
5384
|
+
loop_id: z2.string()
|
|
4949
5385
|
};
|
|
4950
|
-
var DevReviewLoopEventSchema =
|
|
4951
|
-
|
|
5386
|
+
var DevReviewLoopEventSchema = z2.discriminatedUnion("event", [
|
|
5387
|
+
z2.object({
|
|
4952
5388
|
...loopShared,
|
|
4953
|
-
event:
|
|
4954
|
-
task:
|
|
4955
|
-
policy:
|
|
4956
|
-
max_rounds:
|
|
4957
|
-
reviewers:
|
|
4958
|
-
models:
|
|
5389
|
+
event: z2.literal("loop_started"),
|
|
5390
|
+
task: z2.number().int(),
|
|
5391
|
+
policy: z2.object({
|
|
5392
|
+
max_rounds: z2.number().int().nonnegative(),
|
|
5393
|
+
reviewers: z2.array(RoleSchema),
|
|
5394
|
+
models: z2.record(RoleSchema, z2.string())
|
|
4959
5395
|
}).strict()
|
|
4960
5396
|
}).strict(),
|
|
4961
|
-
|
|
4962
|
-
|
|
5397
|
+
z2.object({ ...loopShared, event: z2.literal("round_started"), round: z2.number().int(), base_head: z2.string() }).strict(),
|
|
5398
|
+
z2.object({
|
|
4963
5399
|
...loopShared,
|
|
4964
|
-
event:
|
|
4965
|
-
round:
|
|
4966
|
-
head:
|
|
4967
|
-
green:
|
|
5400
|
+
event: z2.literal("gate_result_read"),
|
|
5401
|
+
round: z2.number().int(),
|
|
5402
|
+
head: z2.string(),
|
|
5403
|
+
green: z2.boolean()
|
|
4968
5404
|
}).strict(),
|
|
4969
|
-
|
|
5405
|
+
z2.object({
|
|
4970
5406
|
...loopShared,
|
|
4971
|
-
event:
|
|
4972
|
-
round:
|
|
4973
|
-
head:
|
|
4974
|
-
all_approve:
|
|
4975
|
-
blockers:
|
|
5407
|
+
event: z2.literal("verdicts_read"),
|
|
5408
|
+
round: z2.number().int(),
|
|
5409
|
+
head: z2.string(),
|
|
5410
|
+
all_approve: z2.boolean(),
|
|
5411
|
+
blockers: z2.number().int().nonnegative()
|
|
4976
5412
|
}).strict(),
|
|
4977
|
-
|
|
5413
|
+
z2.object({
|
|
4978
5414
|
...loopShared,
|
|
4979
|
-
event:
|
|
4980
|
-
round:
|
|
4981
|
-
open:
|
|
4982
|
-
resolved:
|
|
4983
|
-
new:
|
|
4984
|
-
recurring:
|
|
5415
|
+
event: z2.literal("findings_compared"),
|
|
5416
|
+
round: z2.number().int(),
|
|
5417
|
+
open: z2.array(z2.string()),
|
|
5418
|
+
resolved: z2.array(z2.string()),
|
|
5419
|
+
new: z2.array(z2.string()),
|
|
5420
|
+
recurring: z2.array(z2.string())
|
|
4985
5421
|
}).strict(),
|
|
4986
|
-
|
|
5422
|
+
z2.object({
|
|
4987
5423
|
...loopShared,
|
|
4988
|
-
event:
|
|
4989
|
-
round:
|
|
4990
|
-
condition:
|
|
5424
|
+
event: z2.literal("stop_condition_met"),
|
|
5425
|
+
round: z2.number().int(),
|
|
5426
|
+
condition: z2.enum([
|
|
4991
5427
|
"green",
|
|
4992
5428
|
"max_rounds",
|
|
4993
5429
|
"no_progress",
|
|
@@ -4997,42 +5433,49 @@ var DevReviewLoopEventSchema = z.discriminatedUnion("event", [
|
|
|
4997
5433
|
"reappearance"
|
|
4998
5434
|
])
|
|
4999
5435
|
}).strict(),
|
|
5000
|
-
|
|
5436
|
+
z2.object({
|
|
5001
5437
|
...loopShared,
|
|
5002
|
-
event:
|
|
5003
|
-
round:
|
|
5004
|
-
reason:
|
|
5438
|
+
event: z2.literal("paused"),
|
|
5439
|
+
round: z2.number().int(),
|
|
5440
|
+
reason: z2.enum(["escalation", "principal_item", "refreeze_needed"])
|
|
5005
5441
|
}).strict(),
|
|
5006
|
-
|
|
5442
|
+
z2.object({
|
|
5007
5443
|
...loopShared,
|
|
5008
|
-
event:
|
|
5009
|
-
round:
|
|
5010
|
-
by:
|
|
5444
|
+
event: z2.literal("resumed"),
|
|
5445
|
+
round: z2.number().int(),
|
|
5446
|
+
by: z2.literal("principal")
|
|
5011
5447
|
}).strict(),
|
|
5012
|
-
|
|
5448
|
+
z2.object({
|
|
5013
5449
|
...loopShared,
|
|
5014
|
-
event:
|
|
5015
|
-
round:
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
files_changed: z.number().int().nonnegative(),
|
|
5019
|
-
insertions: z.number().int().nonnegative(),
|
|
5020
|
-
deletions: z.number().int().nonnegative(),
|
|
5021
|
-
wall_ms: z.number().nonnegative(),
|
|
5022
|
-
outcome: z.enum(["green", "changes_requested", "escalated"])
|
|
5450
|
+
event: z2.literal("unpushed_work_resume"),
|
|
5451
|
+
round: z2.number().int(),
|
|
5452
|
+
branch: z2.string(),
|
|
5453
|
+
detail: z2.string()
|
|
5023
5454
|
}).strict(),
|
|
5024
|
-
|
|
5455
|
+
z2.object({
|
|
5025
5456
|
...loopShared,
|
|
5026
|
-
event:
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5457
|
+
event: z2.literal("round_ended"),
|
|
5458
|
+
round: z2.number().int(),
|
|
5459
|
+
base_head: z2.string(),
|
|
5460
|
+
head: z2.string(),
|
|
5461
|
+
files_changed: z2.number().int().nonnegative(),
|
|
5462
|
+
insertions: z2.number().int().nonnegative(),
|
|
5463
|
+
deletions: z2.number().int().nonnegative(),
|
|
5464
|
+
wall_ms: z2.number().nonnegative(),
|
|
5465
|
+
outcome: z2.enum(["green", "changes_requested", "escalated"])
|
|
5466
|
+
}).strict(),
|
|
5467
|
+
z2.object({
|
|
5468
|
+
...loopShared,
|
|
5469
|
+
event: z2.literal("journal_finalized"),
|
|
5470
|
+
rounds: z2.number().int().nonnegative(),
|
|
5471
|
+
total_wall_ms: z2.number().nonnegative(),
|
|
5472
|
+
time_to_green_ms: z2.number().nonnegative().nullable(),
|
|
5473
|
+
files_changed_total: z2.number().int().nonnegative(),
|
|
5474
|
+
final_head: z2.string(),
|
|
5475
|
+
result: z2.enum(["merged_ready", "stopped"])
|
|
5033
5476
|
}).strict()
|
|
5034
5477
|
]);
|
|
5035
|
-
var ForgeOpSchema =
|
|
5478
|
+
var ForgeOpSchema = z2.enum([
|
|
5036
5479
|
"pr.create",
|
|
5037
5480
|
"pr.comment",
|
|
5038
5481
|
"pr.body.replace",
|
|
@@ -5046,50 +5489,238 @@ var ForgeOpSchema = z.enum([
|
|
|
5046
5489
|
"label.add",
|
|
5047
5490
|
"label.remove"
|
|
5048
5491
|
]);
|
|
5049
|
-
var ForgeWriteTargetSchema =
|
|
5050
|
-
issue:
|
|
5051
|
-
pr:
|
|
5492
|
+
var ForgeWriteTargetSchema = z2.object({
|
|
5493
|
+
issue: z2.number().int().optional(),
|
|
5494
|
+
pr: z2.number().int().optional()
|
|
5052
5495
|
}).strict();
|
|
5053
5496
|
var forgeWriteShared = {
|
|
5054
5497
|
meta: HeaderMetaSchema,
|
|
5055
5498
|
subject: SubjectSchema,
|
|
5056
|
-
kind:
|
|
5499
|
+
kind: z2.literal("forge_write"),
|
|
5057
5500
|
...envelopeTail,
|
|
5058
5501
|
op: ForgeOpSchema,
|
|
5059
5502
|
target: ForgeWriteTargetSchema
|
|
5060
5503
|
};
|
|
5061
|
-
var ForgeWriteEventSchema =
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5504
|
+
var ForgeWriteEventSchema = z2.discriminatedUnion("event", [
|
|
5505
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("validated") }).strict(),
|
|
5506
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("refused"), reason: z2.string() }).strict(),
|
|
5507
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("written"), comment_ids: z2.array(z2.string()) }).strict()
|
|
5508
|
+
]);
|
|
5509
|
+
var GateOutcomeSchema = z2.enum([
|
|
5510
|
+
"pass",
|
|
5511
|
+
"fail",
|
|
5512
|
+
"wait",
|
|
5513
|
+
"skip",
|
|
5514
|
+
"invalid_input",
|
|
5515
|
+
"unavailable_dependency",
|
|
5516
|
+
"timeout",
|
|
5517
|
+
"cancelled"
|
|
5518
|
+
]);
|
|
5519
|
+
var gateShared = {
|
|
5520
|
+
meta: HeaderMetaSchema,
|
|
5521
|
+
subject: SubjectSchema,
|
|
5522
|
+
kind: z2.literal("gate"),
|
|
5523
|
+
...envelopeTail,
|
|
5524
|
+
check: z2.string(),
|
|
5525
|
+
check_version: z2.string().nullable(),
|
|
5526
|
+
policy_version: z2.string().nullable(),
|
|
5527
|
+
input_fingerprint: z2.string().nullable()
|
|
5528
|
+
};
|
|
5529
|
+
var GateEventSchema = z2.discriminatedUnion("event", [
|
|
5530
|
+
z2.object({
|
|
5531
|
+
...gateShared,
|
|
5532
|
+
event: z2.literal("checked"),
|
|
5533
|
+
outcome: GateOutcomeSchema,
|
|
5534
|
+
reason: z2.string().optional()
|
|
5535
|
+
}).strict()
|
|
5536
|
+
]);
|
|
5537
|
+
var OperationResultSchema = z2.enum(["ok", "error", "refused", "timeout", "cancelled", "unavailable"]);
|
|
5538
|
+
var operationShared = {
|
|
5539
|
+
meta: HeaderMetaSchema,
|
|
5540
|
+
subject: SubjectSchema,
|
|
5541
|
+
kind: z2.literal("operation"),
|
|
5542
|
+
...envelopeTail,
|
|
5543
|
+
operation: z2.string(),
|
|
5544
|
+
target: z2.string().nullable()
|
|
5545
|
+
};
|
|
5546
|
+
var OperationEventSchema = z2.discriminatedUnion("event", [
|
|
5547
|
+
z2.object({
|
|
5548
|
+
...operationShared,
|
|
5549
|
+
event: z2.literal("completed"),
|
|
5550
|
+
result: OperationResultSchema,
|
|
5551
|
+
error_class: z2.string().nullable()
|
|
5552
|
+
}).strict()
|
|
5065
5553
|
]);
|
|
5066
|
-
var
|
|
5554
|
+
var UsageUnitsSchema = z2.object({
|
|
5555
|
+
input: z2.number().nonnegative().nullable(),
|
|
5556
|
+
output: z2.number().nonnegative().nullable(),
|
|
5557
|
+
cache: z2.number().nonnegative().nullable()
|
|
5558
|
+
}).strict();
|
|
5559
|
+
var usageShared = {
|
|
5560
|
+
meta: HeaderMetaSchema,
|
|
5561
|
+
subject: SubjectSchema,
|
|
5562
|
+
kind: z2.literal("usage"),
|
|
5563
|
+
...envelopeTail,
|
|
5564
|
+
model: z2.string().nullable(),
|
|
5565
|
+
source: z2.string(),
|
|
5566
|
+
semantics: z2.enum(["cumulative", "delta"])
|
|
5567
|
+
};
|
|
5568
|
+
var UsageEventSchema = z2.discriminatedUnion("event", [
|
|
5569
|
+
z2.object({
|
|
5570
|
+
...usageShared,
|
|
5571
|
+
event: z2.literal("observed"),
|
|
5572
|
+
units: UsageUnitsSchema,
|
|
5573
|
+
unknown_reason: z2.string().nullable()
|
|
5574
|
+
}).strict()
|
|
5575
|
+
]);
|
|
5576
|
+
var RoleAttemptOutcomeSchema = z2.enum([
|
|
5577
|
+
"completed",
|
|
5578
|
+
"incomplete",
|
|
5579
|
+
"infrastructure_failed",
|
|
5580
|
+
"cancelled",
|
|
5581
|
+
"timed_out",
|
|
5582
|
+
"capability_refused"
|
|
5583
|
+
]);
|
|
5584
|
+
var roleAttemptShared = {
|
|
5585
|
+
meta: HeaderMetaSchema,
|
|
5586
|
+
subject: SubjectSchema,
|
|
5587
|
+
kind: z2.literal("role_attempt"),
|
|
5588
|
+
...envelopeTail,
|
|
5589
|
+
actor: z2.string().nullable(),
|
|
5590
|
+
attempt: z2.number().int().nullable()
|
|
5591
|
+
};
|
|
5592
|
+
var RoleAttemptEventSchema = z2.discriminatedUnion("event", [
|
|
5593
|
+
z2.object({
|
|
5594
|
+
...roleAttemptShared,
|
|
5595
|
+
event: z2.literal("attempted"),
|
|
5596
|
+
outcome: RoleAttemptOutcomeSchema,
|
|
5597
|
+
usage: dispatchUsageField
|
|
5598
|
+
}).strict()
|
|
5599
|
+
]);
|
|
5600
|
+
var handoffShared = {
|
|
5601
|
+
meta: HeaderMetaSchema,
|
|
5602
|
+
subject: SubjectSchema,
|
|
5603
|
+
kind: z2.literal("handoff"),
|
|
5604
|
+
...envelopeTail,
|
|
5605
|
+
class: z2.enum(["authority", "strategy", "product"]),
|
|
5606
|
+
reason: z2.string()
|
|
5607
|
+
};
|
|
5608
|
+
var HandoffEventSchema = z2.discriminatedUnion("event", [
|
|
5609
|
+
z2.object({
|
|
5610
|
+
...handoffShared,
|
|
5611
|
+
event: z2.literal("raised"),
|
|
5612
|
+
requested_decision: z2.string().nullable()
|
|
5613
|
+
}).strict(),
|
|
5614
|
+
z2.object({
|
|
5615
|
+
...handoffShared,
|
|
5616
|
+
event: z2.literal("resolved"),
|
|
5617
|
+
resolution: z2.string().nullable(),
|
|
5618
|
+
resolved_by: z2.string().nullable()
|
|
5619
|
+
}).strict()
|
|
5620
|
+
]);
|
|
5621
|
+
var EffectTargetSchema = z2.object({
|
|
5622
|
+
kind: z2.string(),
|
|
5623
|
+
ref: z2.string()
|
|
5624
|
+
}).strict();
|
|
5625
|
+
var effectShared = {
|
|
5626
|
+
meta: HeaderMetaSchema,
|
|
5627
|
+
subject: SubjectSchema,
|
|
5628
|
+
kind: z2.literal("effect"),
|
|
5629
|
+
...envelopeTail,
|
|
5630
|
+
effect_id: z2.string(),
|
|
5631
|
+
target: EffectTargetSchema
|
|
5632
|
+
};
|
|
5633
|
+
var EffectEventSchema = z2.discriminatedUnion("event", [
|
|
5634
|
+
z2.object({ ...effectShared, event: z2.literal("attempted") }).strict(),
|
|
5635
|
+
z2.object({ ...effectShared, event: z2.literal("observed"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict(),
|
|
5636
|
+
z2.object({ ...effectShared, event: z2.literal("verified"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict()
|
|
5637
|
+
]);
|
|
5638
|
+
var LogEventSchema = z2.union([
|
|
5639
|
+
DispatchEventSchema,
|
|
5640
|
+
DevReviewLoopEventSchema,
|
|
5641
|
+
ForgeWriteEventSchema,
|
|
5642
|
+
GateEventSchema,
|
|
5643
|
+
OperationEventSchema,
|
|
5644
|
+
UsageEventSchema,
|
|
5645
|
+
RoleAttemptEventSchema,
|
|
5646
|
+
HandoffEventSchema,
|
|
5647
|
+
EffectEventSchema
|
|
5648
|
+
]);
|
|
5649
|
+
// ../../packages/aeg-core/src/dev-review-loop/journal-reconstruction.ts
|
|
5650
|
+
var STOPPED_CONDITIONS = new Set(["confidence", "reappearance", "no_progress", "max_rounds"]);
|
|
5651
|
+
// ../../packages/aeg-core/src/control-store/records.ts
|
|
5652
|
+
import { z as z3 } from "zod";
|
|
5653
|
+
var isoTimestamp = z3.string().min(1);
|
|
5654
|
+
var taskId = z3.number().int().positive();
|
|
5655
|
+
var epochNumber = z3.number().int().nonnegative();
|
|
5656
|
+
var RunRecordSchema = z3.object({
|
|
5657
|
+
version: z3.literal(1),
|
|
5658
|
+
kind: z3.literal("run"),
|
|
5659
|
+
task: taskId,
|
|
5660
|
+
runId: z3.string().min(1),
|
|
5661
|
+
pid: z3.number().int().positive(),
|
|
5662
|
+
host: z3.string().min(1),
|
|
5663
|
+
startedAt: isoTimestamp
|
|
5664
|
+
}).strict();
|
|
5665
|
+
var InputRecordSchema = z3.object({
|
|
5666
|
+
version: z3.literal(1),
|
|
5667
|
+
kind: z3.literal("input"),
|
|
5668
|
+
task: taskId,
|
|
5669
|
+
runId: z3.string().min(1),
|
|
5670
|
+
source: z3.union([z3.literal("fresh"), z3.literal("resume")]),
|
|
5671
|
+
pr: z3.number().int().positive().nullable(),
|
|
5672
|
+
round: z3.number().int().nonnegative(),
|
|
5673
|
+
recordedAt: isoTimestamp
|
|
5674
|
+
}).strict();
|
|
5675
|
+
var OwnershipRecordSchema = z3.object({
|
|
5676
|
+
version: z3.literal(1),
|
|
5677
|
+
kind: z3.literal("ownership"),
|
|
5678
|
+
task: taskId,
|
|
5679
|
+
epoch: epochNumber,
|
|
5680
|
+
ownerId: z3.string().min(1),
|
|
5681
|
+
pid: z3.number().int().positive(),
|
|
5682
|
+
host: z3.string().min(1),
|
|
5683
|
+
acquiredAt: isoTimestamp
|
|
5684
|
+
}).strict();
|
|
5685
|
+
var TransitionRecordSchema = z3.object({
|
|
5686
|
+
version: z3.literal(1),
|
|
5687
|
+
kind: z3.literal("transition"),
|
|
5688
|
+
task: taskId,
|
|
5689
|
+
epoch: epochNumber,
|
|
5690
|
+
seq: z3.number().int().nonnegative(),
|
|
5691
|
+
from: z3.string().min(1),
|
|
5692
|
+
to: z3.string().min(1),
|
|
5693
|
+
detail: z3.string().optional(),
|
|
5694
|
+
at: isoTimestamp
|
|
5695
|
+
}).strict();
|
|
5067
5696
|
// src/lib/config.ts
|
|
5068
5697
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
5069
5698
|
import { existsSync, mkdirSync, readFileSync as readFileSync2, realpathSync, writeFileSync } from "node:fs";
|
|
5070
5699
|
import { homedir } from "node:os";
|
|
5071
5700
|
import { dirname, join, resolve } from "node:path";
|
|
5072
|
-
import { z as
|
|
5701
|
+
import { z as z4 } from "zod";
|
|
5073
5702
|
|
|
5074
5703
|
// src/lib/agent-vendors.ts
|
|
5075
5704
|
var AGENT_VENDORS = ["skills", "claude", "gemini"];
|
|
5076
5705
|
|
|
5077
5706
|
// src/lib/config.ts
|
|
5078
|
-
var EnvEntrySchema =
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5707
|
+
var EnvEntrySchema = z4.union([
|
|
5708
|
+
z4.literal(true),
|
|
5709
|
+
z4.object({ optional: z4.literal(true) }),
|
|
5710
|
+
z4.object({ anyOf: z4.array(z4.string()).min(2) }),
|
|
5711
|
+
z4.string()
|
|
5083
5712
|
]);
|
|
5084
|
-
var CheckEntrySchema =
|
|
5085
|
-
run:
|
|
5086
|
-
scope:
|
|
5087
|
-
include:
|
|
5088
|
-
args:
|
|
5089
|
-
timeoutMs:
|
|
5090
|
-
env:
|
|
5091
|
-
requiresOpenPr:
|
|
5092
|
-
ownWorkflow:
|
|
5713
|
+
var CheckEntrySchema = z4.object({
|
|
5714
|
+
run: z4.string(),
|
|
5715
|
+
scope: z4.enum(["diff", "full"]),
|
|
5716
|
+
include: z4.array(z4.string()).optional(),
|
|
5717
|
+
args: z4.array(z4.string()).optional(),
|
|
5718
|
+
timeoutMs: z4.number().optional(),
|
|
5719
|
+
env: z4.record(z4.string(), EnvEntrySchema).optional(),
|
|
5720
|
+
requiresOpenPr: z4.boolean().optional(),
|
|
5721
|
+
ownWorkflow: z4.boolean().optional(),
|
|
5722
|
+
principalOwed: z4.literal(true).optional(),
|
|
5723
|
+
validates: z4.enum(["body", "issue"]).optional()
|
|
5093
5724
|
}).superRefine((entry2, ctx) => {
|
|
5094
5725
|
if (!entry2.env)
|
|
5095
5726
|
return;
|
|
@@ -5099,25 +5730,26 @@ var CheckEntrySchema = z2.object({
|
|
|
5099
5730
|
const members = value.anyOf;
|
|
5100
5731
|
if (new Set(members).size !== members.length) {
|
|
5101
5732
|
ctx.addIssue({
|
|
5102
|
-
code:
|
|
5733
|
+
code: z4.ZodIssueCode.custom,
|
|
5103
5734
|
message: `env.${key}.anyOf has duplicate members`,
|
|
5104
5735
|
path: ["env", key, "anyOf"]
|
|
5105
5736
|
});
|
|
5106
5737
|
}
|
|
5107
5738
|
if (!members.includes(key)) {
|
|
5108
5739
|
ctx.addIssue({
|
|
5109
|
-
code:
|
|
5740
|
+
code: z4.ZodIssueCode.custom,
|
|
5110
5741
|
message: `env.${key}.anyOf must include "${key}" itself as a member`,
|
|
5111
5742
|
path: ["env", key, "anyOf"]
|
|
5112
5743
|
});
|
|
5113
5744
|
}
|
|
5114
5745
|
}
|
|
5115
5746
|
});
|
|
5116
|
-
var RoleEntrySchema =
|
|
5117
|
-
contract:
|
|
5747
|
+
var RoleEntrySchema = z4.object({
|
|
5748
|
+
contract: z4.string().refine((p) => p.includes("/"), {
|
|
5118
5749
|
message: 'must be a path (contain at least one "/"), not a bare filename'
|
|
5119
5750
|
})
|
|
5120
5751
|
});
|
|
5752
|
+
var MAX_REPORT_COMMAND_TIMEOUT_MS = 3600000;
|
|
5121
5753
|
var BRIEF_BUILTINS = [
|
|
5122
5754
|
"tier",
|
|
5123
5755
|
"testPlan",
|
|
@@ -5137,17 +5769,17 @@ var BRIEF_BUILTINS = [
|
|
|
5137
5769
|
"briefSections",
|
|
5138
5770
|
"milestoneShape"
|
|
5139
5771
|
];
|
|
5140
|
-
var BriefSectionSchema =
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5772
|
+
var BriefSectionSchema = z4.union([
|
|
5773
|
+
z4.object({ builtin: z4.enum(BRIEF_BUILTINS) }),
|
|
5774
|
+
z4.object({ heading: z4.string().min(1), name: z4.string().optional() }),
|
|
5775
|
+
z4.object({ field: z4.string().min(1), name: z4.string().optional() }),
|
|
5776
|
+
z4.object({ phrase: z4.string().min(1), name: z4.string().optional() })
|
|
5145
5777
|
]);
|
|
5146
|
-
var BriefSchemaSchema =
|
|
5147
|
-
pr:
|
|
5148
|
-
issue:
|
|
5149
|
-
milestone:
|
|
5150
|
-
ack:
|
|
5778
|
+
var BriefSchemaSchema = z4.object({
|
|
5779
|
+
pr: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
|
|
5780
|
+
issue: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
|
|
5781
|
+
milestone: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
|
|
5782
|
+
ack: z4.array(z4.enum(BRIEF_BUILTINS)).optional()
|
|
5151
5783
|
});
|
|
5152
5784
|
function isSafeRepoRelPath(p) {
|
|
5153
5785
|
if (p.length === 0)
|
|
@@ -5156,7 +5788,7 @@ function isSafeRepoRelPath(p) {
|
|
|
5156
5788
|
return false;
|
|
5157
5789
|
return p.split(/[\\/]/).every((seg) => seg !== ".." && seg !== "");
|
|
5158
5790
|
}
|
|
5159
|
-
var SafeRepoRelPath =
|
|
5791
|
+
var SafeRepoRelPath = z4.string().refine(isSafeRepoRelPath, {
|
|
5160
5792
|
message: "must be a repo-root-relative path with no `..` segment or absolute root"
|
|
5161
5793
|
});
|
|
5162
5794
|
var CANONICAL_HOOK_BLOCK_PREFIXES = [".git/", ".husky/", ".vinaya/hooks/", ".claude/hooks/"];
|
|
@@ -5166,22 +5798,22 @@ function isCanonicalHookBlockPath(p) {
|
|
|
5166
5798
|
var ManagedHookBlockPath = SafeRepoRelPath.refine(isCanonicalHookBlockPath, {
|
|
5167
5799
|
message: `must start with one of ${CANONICAL_HOOK_BLOCK_PREFIXES.join(", ")} (byte-exact, case-sensitive)`
|
|
5168
5800
|
});
|
|
5169
|
-
var ManagedBlockRecordSchema =
|
|
5801
|
+
var ManagedBlockRecordSchema = z4.object({
|
|
5170
5802
|
path: ManagedHookBlockPath,
|
|
5171
|
-
marker:
|
|
5172
|
-
comment:
|
|
5803
|
+
marker: z4.string(),
|
|
5804
|
+
comment: z4.enum(["hash", "html"])
|
|
5173
5805
|
});
|
|
5174
|
-
var ManagedManifestSchema =
|
|
5175
|
-
version:
|
|
5176
|
-
files:
|
|
5177
|
-
blocks:
|
|
5178
|
-
labels:
|
|
5179
|
-
agents:
|
|
5806
|
+
var ManagedManifestSchema = z4.object({
|
|
5807
|
+
version: z4.number().int().positive(),
|
|
5808
|
+
files: z4.array(SafeRepoRelPath),
|
|
5809
|
+
blocks: z4.array(ManagedBlockRecordSchema),
|
|
5810
|
+
labels: z4.array(z4.string()),
|
|
5811
|
+
agents: z4.array(z4.enum(AGENT_VENDORS)).optional()
|
|
5180
5812
|
});
|
|
5181
|
-
var ProjectEntrySchema =
|
|
5182
|
-
name:
|
|
5183
|
-
description:
|
|
5184
|
-
path:
|
|
5813
|
+
var ProjectEntrySchema = z4.object({
|
|
5814
|
+
name: z4.string().min(1),
|
|
5815
|
+
description: z4.string().min(1).optional(),
|
|
5816
|
+
path: z4.string().min(1).optional()
|
|
5185
5817
|
});
|
|
5186
5818
|
function parseTokensCollectDeclaration(value) {
|
|
5187
5819
|
const m = value.trim().match(/^(\S+)\s+(\S+)$/);
|
|
@@ -5192,34 +5824,55 @@ function parseTokensCollectDeclaration(value) {
|
|
|
5192
5824
|
return null;
|
|
5193
5825
|
return { interpreter, script };
|
|
5194
5826
|
}
|
|
5195
|
-
var VinayaConfigSchema =
|
|
5196
|
-
rings:
|
|
5197
|
-
ring1_forgeWriteInterception:
|
|
5198
|
-
ring2_asyncAudits:
|
|
5827
|
+
var VinayaConfigSchema = z4.object({
|
|
5828
|
+
rings: z4.object({
|
|
5829
|
+
ring1_forgeWriteInterception: z4.boolean(),
|
|
5830
|
+
ring2_asyncAudits: z4.boolean()
|
|
5199
5831
|
}).optional(),
|
|
5200
|
-
checks:
|
|
5201
|
-
roles:
|
|
5832
|
+
checks: z4.record(z4.string(), CheckEntrySchema).optional(),
|
|
5833
|
+
roles: z4.record(z4.string(), RoleEntrySchema).optional(),
|
|
5202
5834
|
briefSchema: BriefSchemaSchema.optional(),
|
|
5203
5835
|
managed: ManagedManifestSchema.optional(),
|
|
5204
|
-
principals:
|
|
5205
|
-
releaseActor:
|
|
5206
|
-
ci:
|
|
5207
|
-
tokens:
|
|
5208
|
-
collect:
|
|
5836
|
+
principals: z4.array(z4.string()).min(1).optional(),
|
|
5837
|
+
releaseActor: z4.string().min(1).optional(),
|
|
5838
|
+
ci: z4.object({ setup: z4.string().min(1) }).optional(),
|
|
5839
|
+
tokens: z4.object({
|
|
5840
|
+
collect: z4.string().min(1).refine((v) => parseTokensCollectDeclaration(v) !== null, {
|
|
5209
5841
|
message: 'tokens.collect must be exactly "<interpreter> <repo-relative-script-path>" — two whitespace-separated tokens, no flags, no shell syntax, no quoting'
|
|
5210
5842
|
})
|
|
5211
5843
|
}).optional(),
|
|
5212
|
-
blastRadius:
|
|
5213
|
-
proseGates:
|
|
5214
|
-
doctrineRoot:
|
|
5215
|
-
readerFacingPrefix:
|
|
5216
|
-
readerFacingSuffix:
|
|
5217
|
-
legacySlugDir:
|
|
5844
|
+
blastRadius: z4.object({ extraDomains: z4.array(z4.string()).optional() }).optional(),
|
|
5845
|
+
proseGates: z4.object({
|
|
5846
|
+
doctrineRoot: z4.string().min(1).optional(),
|
|
5847
|
+
readerFacingPrefix: z4.string().min(1).optional(),
|
|
5848
|
+
readerFacingSuffix: z4.string().min(1).optional(),
|
|
5849
|
+
legacySlugDir: z4.string().min(1).optional()
|
|
5850
|
+
}).optional(),
|
|
5851
|
+
projects: z4.array(ProjectEntrySchema).optional(),
|
|
5852
|
+
dispatch: z4.object({
|
|
5853
|
+
timeoutMs: z4.number().int().positive().optional(),
|
|
5854
|
+
killGraceMs: z4.number().int().positive().optional(),
|
|
5855
|
+
agent: z4.enum(["claude", "codex", "gemini"]).optional()
|
|
5218
5856
|
}).optional(),
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5857
|
+
reviewPolicy: z4.object({
|
|
5858
|
+
codeReviewThreshold: z4.string().min(1).optional(),
|
|
5859
|
+
securityThreshold: z4.string().min(1).optional(),
|
|
5860
|
+
maxRounds: z4.number().optional()
|
|
5861
|
+
}).optional(),
|
|
5862
|
+
prePush: z4.object({
|
|
5863
|
+
alwaysRun: z4.array(z4.string()).optional()
|
|
5864
|
+
}).optional(),
|
|
5865
|
+
report: z4.object({
|
|
5866
|
+
commandTimeoutMs: z4.number().int().positive().optional()
|
|
5867
|
+
}).superRefine((report, ctx) => {
|
|
5868
|
+
const commandTimeoutMs = report.commandTimeoutMs;
|
|
5869
|
+
if (commandTimeoutMs !== undefined && commandTimeoutMs > MAX_REPORT_COMMAND_TIMEOUT_MS) {
|
|
5870
|
+
ctx.addIssue({
|
|
5871
|
+
code: z4.ZodIssueCode.custom,
|
|
5872
|
+
path: ["commandTimeoutMs"],
|
|
5873
|
+
message: "must be at most 3600000 (1 hour)"
|
|
5874
|
+
});
|
|
5875
|
+
}
|
|
5223
5876
|
}).optional()
|
|
5224
5877
|
});
|
|
5225
5878
|
var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
|
|
@@ -5294,6 +5947,28 @@ function resolvePrincipalAllowlist(config) {
|
|
|
5294
5947
|
function resolveReleaseActor(config) {
|
|
5295
5948
|
return config?.releaseActor ?? DEFAULT_RELEASE_ACTOR;
|
|
5296
5949
|
}
|
|
5950
|
+
function resolveReviewPolicy(config) {
|
|
5951
|
+
const raw = config?.reviewPolicy;
|
|
5952
|
+
if (!raw)
|
|
5953
|
+
return DEFAULT_REVIEW_POLICY;
|
|
5954
|
+
const codeReviewThreshold = raw.codeReviewThreshold ?? DEFAULT_REVIEW_POLICY.codeReviewThreshold;
|
|
5955
|
+
if (!isKnownSeverity(CODE_REVIEW_SEVERITY_ORDER, codeReviewThreshold)) {
|
|
5956
|
+
throw new Error(`vinaya.config.json: reviewPolicy.codeReviewThreshold "${codeReviewThreshold}" is not one of ${CODE_REVIEW_SEVERITY_ORDER.join(" > ")} — fix the config, this never falls back to a default.`);
|
|
5957
|
+
}
|
|
5958
|
+
const securityThreshold = raw.securityThreshold ?? DEFAULT_REVIEW_POLICY.securityThreshold;
|
|
5959
|
+
if (!isKnownSeverity(SECURITY_SEVERITY_ORDER, securityThreshold)) {
|
|
5960
|
+
throw new Error(`vinaya.config.json: reviewPolicy.securityThreshold "${securityThreshold}" is not one of ${SECURITY_SEVERITY_ORDER.join(" > ")} — fix the config, this never falls back to a default.`);
|
|
5961
|
+
}
|
|
5962
|
+
const maxRounds = raw.maxRounds ?? DEFAULT_REVIEW_POLICY.maxRounds;
|
|
5963
|
+
if (!Number.isInteger(maxRounds) || maxRounds < 1) {
|
|
5964
|
+
throw new Error(`vinaya.config.json: reviewPolicy.maxRounds "${maxRounds}" is not a positive integer — fix the config, this never falls back to a default.`);
|
|
5965
|
+
}
|
|
5966
|
+
return {
|
|
5967
|
+
codeReviewThreshold,
|
|
5968
|
+
securityThreshold,
|
|
5969
|
+
maxRounds
|
|
5970
|
+
};
|
|
5971
|
+
}
|
|
5297
5972
|
function trustAnchorRepo() {
|
|
5298
5973
|
const wellFormed = (slug) => /^[^/\s]+\/[^/\s]+$/.test(slug) ? slug : null;
|
|
5299
5974
|
const fromRunner = process.env.GITHUB_REPOSITORY?.trim();
|
|
@@ -5374,8 +6049,11 @@ function loadConfig() {
|
|
|
5374
6049
|
var TOKENS_COLLECT_TRUST_PATH = join(GLOBAL_VINAYA_HOME, "tokens-collect-trust.json");
|
|
5375
6050
|
|
|
5376
6051
|
// src/lib/diff-evidence.ts
|
|
6052
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
6053
|
+
import { join as join4, relative, resolve as resolve2 } from "node:path";
|
|
6054
|
+
|
|
6055
|
+
// src/lib/brief-assembly.ts
|
|
5377
6056
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
5378
|
-
import { join as join2, relative, resolve as resolve2 } from "node:path";
|
|
5379
6057
|
|
|
5380
6058
|
// ../../packages/sources/src/commands.ts
|
|
5381
6059
|
var COMMANDS = [
|
|
@@ -5535,6 +6213,17 @@ var COMMANDS = [
|
|
|
5535
6213
|
],
|
|
5536
6214
|
status: "shipped"
|
|
5537
6215
|
},
|
|
6216
|
+
{
|
|
6217
|
+
name: "task status",
|
|
6218
|
+
description: "Every open task with a frozen brief, its pull request, and whether its loop is running, paused, or published",
|
|
6219
|
+
flags: [{ flag: "--json", description: "Enveloped JSON output (schema: 1)" }],
|
|
6220
|
+
details: [
|
|
6221
|
+
"Read-only: one `gh issue list` for every open task Issue across every tranche (title/label resolved through the same `resolveTaskIssueRef` `list-tasks.ts` already uses), the open pull request per branch, and the driver pid record / pause record / publish effect markers under `<outboxRoot>/dev-review-loop/<task>/` — never a `ps` scan, never a re-parse of posted verdict comments to decide `published`.",
|
|
6222
|
+
"`running` names the driver's pid (`review-validity-v1` task 7's pid record); `paused` names the reason from the pause record; `published` means the newest round's reviewer and security verdict effect markers both read `posted`; `no driver` is the fallback when none of the above holds.",
|
|
6223
|
+
"`vinaya task status <tranche> <n>` narrows to one task and adds the last round's held or published verdict lines (`round-<n>-reviewer.md`/`round-<n>-security.md`, whichever their outbox carries) plus the exact `vinaya dev-review-loop --resume <pr>` command when paused."
|
|
6224
|
+
],
|
|
6225
|
+
status: "shipped"
|
|
6226
|
+
},
|
|
5538
6227
|
{
|
|
5539
6228
|
name: "pr create",
|
|
5540
6229
|
description: "Open a pull request after full brief-schema validation",
|
|
@@ -6044,10 +6733,10 @@ function createForgeSource(config) {
|
|
|
6044
6733
|
};
|
|
6045
6734
|
}
|
|
6046
6735
|
// ../../packages/sources/src/select-source.ts
|
|
6047
|
-
import { z as
|
|
6048
|
-
var StateSourceConfigSchema =
|
|
6049
|
-
|
|
6050
|
-
|
|
6736
|
+
import { z as z5 } from "zod";
|
|
6737
|
+
var StateSourceConfigSchema = z5.discriminatedUnion("kind", [
|
|
6738
|
+
z5.object({ kind: z5.literal("forge"), owner: z5.string(), repo: z5.string() }),
|
|
6739
|
+
z5.object({ kind: z5.literal("file"), root: z5.string().optional() })
|
|
6051
6740
|
]);
|
|
6052
6741
|
// src/checks/edge-resolve.ts
|
|
6053
6742
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
@@ -6167,6 +6856,580 @@ async function resolveEdge(id, taskById, factsByTaskId, repo, resolveSibling = d
|
|
|
6167
6856
|
};
|
|
6168
6857
|
}
|
|
6169
6858
|
|
|
6859
|
+
// src/lib/brief-assembly.ts
|
|
6860
|
+
function git(args) {
|
|
6861
|
+
try {
|
|
6862
|
+
return execFileSync4("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
6863
|
+
} catch {
|
|
6864
|
+
return "";
|
|
6865
|
+
}
|
|
6866
|
+
}
|
|
6867
|
+
function expandGlob(glob) {
|
|
6868
|
+
const out = git(["ls-files", "--", glob]);
|
|
6869
|
+
return out.split(`
|
|
6870
|
+
`).map((s) => s.trim()).filter(Boolean);
|
|
6871
|
+
}
|
|
6872
|
+
var DRAFT_ISSUE_SENTINEL = Number.MAX_SAFE_INTEGER;
|
|
6873
|
+
|
|
6874
|
+
// src/checks/registry.ts
|
|
6875
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
6876
|
+
import { join as join3 } from "node:path";
|
|
6877
|
+
|
|
6878
|
+
// src/lib/package-root.ts
|
|
6879
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
6880
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
6881
|
+
import { fileURLToPath } from "node:url";
|
|
6882
|
+
function packageRoot(moduleUrl) {
|
|
6883
|
+
let dir = dirname2(fileURLToPath(moduleUrl));
|
|
6884
|
+
while (!existsSync2(join2(dir, "package.json"))) {
|
|
6885
|
+
if (existsSync2(join2(dir, ".git")))
|
|
6886
|
+
break;
|
|
6887
|
+
const parent = dirname2(dir);
|
|
6888
|
+
if (parent === dir)
|
|
6889
|
+
break;
|
|
6890
|
+
dir = parent;
|
|
6891
|
+
}
|
|
6892
|
+
return dir;
|
|
6893
|
+
}
|
|
6894
|
+
|
|
6895
|
+
// src/checks/registry.ts
|
|
6896
|
+
var DIST_BIN_DIR = join3(packageRoot(import.meta.url), "dist", "checks", "bin");
|
|
6897
|
+
var SRC_BIN_DIR = join3(packageRoot(import.meta.url), "src", "checks", "bin");
|
|
6898
|
+
var BIN_DIR = existsSync3(DIST_BIN_DIR) ? DIST_BIN_DIR : SRC_BIN_DIR;
|
|
6899
|
+
var BIN_EXT = BIN_DIR === DIST_BIN_DIR ? ".js" : ".ts";
|
|
6900
|
+
function bin(name) {
|
|
6901
|
+
return join3(BIN_DIR, `${name}${BIN_EXT}`);
|
|
6902
|
+
}
|
|
6903
|
+
var REGISTRY = [
|
|
6904
|
+
[
|
|
6905
|
+
{
|
|
6906
|
+
name: "brief-shape",
|
|
6907
|
+
validates: "body",
|
|
6908
|
+
run: bin("check-brief-shape"),
|
|
6909
|
+
scope: "diff",
|
|
6910
|
+
timeoutMs: 15000,
|
|
6911
|
+
env: {
|
|
6912
|
+
PR_BODY: { optional: true },
|
|
6913
|
+
BRANCH: { optional: true },
|
|
6914
|
+
PR_NUMBER: { optional: true },
|
|
6915
|
+
GITHUB_TOKEN: { optional: true },
|
|
6916
|
+
GH_TOKEN: { optional: true }
|
|
6917
|
+
}
|
|
6918
|
+
},
|
|
6919
|
+
0
|
|
6920
|
+
],
|
|
6921
|
+
[
|
|
6922
|
+
{
|
|
6923
|
+
name: "pr-report-density",
|
|
6924
|
+
validates: "body",
|
|
6925
|
+
run: bin("check-pr-report-density"),
|
|
6926
|
+
scope: "diff",
|
|
6927
|
+
timeoutMs: 15000,
|
|
6928
|
+
env: {
|
|
6929
|
+
PR_BODY: { optional: true }
|
|
6930
|
+
}
|
|
6931
|
+
},
|
|
6932
|
+
0
|
|
6933
|
+
],
|
|
6934
|
+
[
|
|
6935
|
+
{
|
|
6936
|
+
name: "doc-coverage",
|
|
6937
|
+
validates: "body",
|
|
6938
|
+
run: bin("check-doc-coverage"),
|
|
6939
|
+
scope: "diff",
|
|
6940
|
+
timeoutMs: 15000,
|
|
6941
|
+
env: {
|
|
6942
|
+
BASE_SHA: { optional: true },
|
|
6943
|
+
PR_BODY: { optional: true },
|
|
6944
|
+
PR_BODY_FILE: { optional: true },
|
|
6945
|
+
PR_NUMBER: { optional: true },
|
|
6946
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
6947
|
+
GITHUB_TOKEN: { optional: true },
|
|
6948
|
+
GH_TOKEN: { optional: true }
|
|
6949
|
+
}
|
|
6950
|
+
},
|
|
6951
|
+
0
|
|
6952
|
+
],
|
|
6953
|
+
[
|
|
6954
|
+
{
|
|
6955
|
+
name: "coherence",
|
|
6956
|
+
run: bin("check-coherence"),
|
|
6957
|
+
scope: "full",
|
|
6958
|
+
timeoutMs: 30000,
|
|
6959
|
+
env: {
|
|
6960
|
+
AEG_REPO: { optional: true },
|
|
6961
|
+
BRANCH: { optional: true },
|
|
6962
|
+
GITHUB_TOKEN: { optional: true },
|
|
6963
|
+
GH_TOKEN: { optional: true }
|
|
6964
|
+
}
|
|
6965
|
+
},
|
|
6966
|
+
0
|
|
6967
|
+
],
|
|
6968
|
+
[
|
|
6969
|
+
{
|
|
6970
|
+
name: "pr-premise-reassert",
|
|
6971
|
+
validates: "body",
|
|
6972
|
+
run: bin("check-pr-premise-reassert"),
|
|
6973
|
+
scope: "diff",
|
|
6974
|
+
timeoutMs: 15000,
|
|
6975
|
+
env: {
|
|
6976
|
+
PR_BODY: { optional: true }
|
|
6977
|
+
}
|
|
6978
|
+
},
|
|
6979
|
+
0
|
|
6980
|
+
],
|
|
6981
|
+
[
|
|
6982
|
+
{
|
|
6983
|
+
name: "dispatch-readiness",
|
|
6984
|
+
run: bin("check-dispatch-readiness"),
|
|
6985
|
+
scope: "full",
|
|
6986
|
+
timeoutMs: 30000,
|
|
6987
|
+
env: {
|
|
6988
|
+
AEG_REPO: { optional: true },
|
|
6989
|
+
BRANCH: { optional: true },
|
|
6990
|
+
GITHUB_TOKEN: { optional: true },
|
|
6991
|
+
GH_TOKEN: { optional: true },
|
|
6992
|
+
PREMISE_FILE: { optional: true }
|
|
6993
|
+
}
|
|
6994
|
+
},
|
|
6995
|
+
0
|
|
6996
|
+
],
|
|
6997
|
+
[
|
|
6998
|
+
{
|
|
6999
|
+
name: "closes-n",
|
|
7000
|
+
validates: "body",
|
|
7001
|
+
run: bin("check-closes-n"),
|
|
7002
|
+
scope: "diff",
|
|
7003
|
+
timeoutMs: 15000,
|
|
7004
|
+
requiresOpenPr: true,
|
|
7005
|
+
env: {
|
|
7006
|
+
BRANCH: { optional: true },
|
|
7007
|
+
PR_BODY: { optional: true },
|
|
7008
|
+
AEG_REPO: { optional: true },
|
|
7009
|
+
GITHUB_TOKEN: { optional: true },
|
|
7010
|
+
GH_TOKEN: { optional: true }
|
|
7011
|
+
}
|
|
7012
|
+
},
|
|
7013
|
+
1
|
|
7014
|
+
],
|
|
7015
|
+
[
|
|
7016
|
+
{
|
|
7017
|
+
name: "single-plan-pr",
|
|
7018
|
+
run: bin("check-single-plan-pr"),
|
|
7019
|
+
scope: "diff",
|
|
7020
|
+
timeoutMs: 15000,
|
|
7021
|
+
env: {
|
|
7022
|
+
BASE_SHA: { optional: true },
|
|
7023
|
+
PR_NUMBER: { optional: true },
|
|
7024
|
+
GITHUB_TOKEN: { optional: true },
|
|
7025
|
+
GH_TOKEN: { optional: true }
|
|
7026
|
+
}
|
|
7027
|
+
},
|
|
7028
|
+
0
|
|
7029
|
+
],
|
|
7030
|
+
[
|
|
7031
|
+
{
|
|
7032
|
+
name: "surface-scope",
|
|
7033
|
+
run: bin("check-surface-scope"),
|
|
7034
|
+
scope: "diff",
|
|
7035
|
+
timeoutMs: 15000,
|
|
7036
|
+
env: {
|
|
7037
|
+
AEG_REPO: { optional: true },
|
|
7038
|
+
BASE_SHA: { optional: true },
|
|
7039
|
+
BRANCH: { optional: true },
|
|
7040
|
+
GITHUB_TOKEN: { optional: true },
|
|
7041
|
+
GH_TOKEN: { optional: true }
|
|
7042
|
+
}
|
|
7043
|
+
},
|
|
7044
|
+
0
|
|
7045
|
+
],
|
|
7046
|
+
[
|
|
7047
|
+
{
|
|
7048
|
+
name: "test-plan",
|
|
7049
|
+
validates: "body",
|
|
7050
|
+
run: bin("check-test-plan"),
|
|
7051
|
+
scope: "diff",
|
|
7052
|
+
timeoutMs: 15000,
|
|
7053
|
+
requiresOpenPr: true,
|
|
7054
|
+
principalOwed: true,
|
|
7055
|
+
env: {
|
|
7056
|
+
PR_BODY: { optional: true },
|
|
7057
|
+
BRANCH: { optional: true }
|
|
7058
|
+
}
|
|
7059
|
+
},
|
|
7060
|
+
1
|
|
7061
|
+
],
|
|
7062
|
+
[
|
|
7063
|
+
{
|
|
7064
|
+
name: "body-bare-digits",
|
|
7065
|
+
validates: "body",
|
|
7066
|
+
run: bin("check-body-bare-digits"),
|
|
7067
|
+
scope: "diff",
|
|
7068
|
+
timeoutMs: 15000,
|
|
7069
|
+
requiresOpenPr: true,
|
|
7070
|
+
ownWorkflow: true,
|
|
7071
|
+
env: {
|
|
7072
|
+
PR_BODY: { optional: true },
|
|
7073
|
+
PR_NUMBER: { optional: true },
|
|
7074
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
7075
|
+
GITHUB_TOKEN: { optional: true },
|
|
7076
|
+
GH_TOKEN: { optional: true }
|
|
7077
|
+
}
|
|
7078
|
+
},
|
|
7079
|
+
1
|
|
7080
|
+
],
|
|
7081
|
+
[
|
|
7082
|
+
{
|
|
7083
|
+
name: "token-report",
|
|
7084
|
+
validates: "body",
|
|
7085
|
+
run: bin("check-token-report"),
|
|
7086
|
+
scope: "diff",
|
|
7087
|
+
timeoutMs: 15000,
|
|
7088
|
+
requiresOpenPr: true,
|
|
7089
|
+
env: {
|
|
7090
|
+
PR_BODY: { optional: true },
|
|
7091
|
+
BRANCH: { optional: true },
|
|
7092
|
+
CLAUDE_PROJECT_DIR: { optional: true },
|
|
7093
|
+
CLAUDE_CODE_SESSION_ID: { optional: true }
|
|
7094
|
+
}
|
|
7095
|
+
},
|
|
7096
|
+
1
|
|
7097
|
+
],
|
|
7098
|
+
[
|
|
7099
|
+
{
|
|
7100
|
+
name: "no-disk-state",
|
|
7101
|
+
run: bin("check-no-disk-state"),
|
|
7102
|
+
scope: "diff",
|
|
7103
|
+
timeoutMs: 15000,
|
|
7104
|
+
env: { BASE_SHA: { optional: true } }
|
|
7105
|
+
},
|
|
7106
|
+
0
|
|
7107
|
+
],
|
|
7108
|
+
[
|
|
7109
|
+
{
|
|
7110
|
+
name: "registry-gates",
|
|
7111
|
+
run: bin("check-registry-gates"),
|
|
7112
|
+
scope: "full",
|
|
7113
|
+
timeoutMs: 30000,
|
|
7114
|
+
env: {
|
|
7115
|
+
AEG_REPO: { optional: true },
|
|
7116
|
+
GITHUB_TOKEN: { optional: true },
|
|
7117
|
+
GH_TOKEN: { optional: true }
|
|
7118
|
+
}
|
|
7119
|
+
},
|
|
7120
|
+
0
|
|
7121
|
+
],
|
|
7122
|
+
[
|
|
7123
|
+
{
|
|
7124
|
+
name: "review-gate",
|
|
7125
|
+
run: bin("check-review-gate"),
|
|
7126
|
+
scope: "full",
|
|
7127
|
+
timeoutMs: 30000,
|
|
7128
|
+
ownWorkflow: true,
|
|
7129
|
+
env: {
|
|
7130
|
+
PR_NUMBER: { optional: true },
|
|
7131
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
7132
|
+
GITHUB_TOKEN: { optional: true },
|
|
7133
|
+
GH_TOKEN: { optional: true }
|
|
7134
|
+
}
|
|
7135
|
+
},
|
|
7136
|
+
1
|
|
7137
|
+
],
|
|
7138
|
+
[
|
|
7139
|
+
{
|
|
7140
|
+
name: "branch-topology",
|
|
7141
|
+
run: bin("check-branch-topology"),
|
|
7142
|
+
scope: "full",
|
|
7143
|
+
timeoutMs: 30000,
|
|
7144
|
+
env: {
|
|
7145
|
+
BRANCH: { optional: true },
|
|
7146
|
+
AEG_REPO: { optional: true },
|
|
7147
|
+
GITHUB_TOKEN: { optional: true },
|
|
7148
|
+
GH_TOKEN: { optional: true }
|
|
7149
|
+
}
|
|
7150
|
+
},
|
|
7151
|
+
0
|
|
7152
|
+
],
|
|
7153
|
+
[
|
|
7154
|
+
{
|
|
7155
|
+
name: "dead-branch-push",
|
|
7156
|
+
run: bin("check-dead-branch-push"),
|
|
7157
|
+
scope: "full",
|
|
7158
|
+
timeoutMs: 30000,
|
|
7159
|
+
env: {
|
|
7160
|
+
BRANCH: { optional: true },
|
|
7161
|
+
GITHUB_TOKEN: { optional: true },
|
|
7162
|
+
GH_TOKEN: { optional: true }
|
|
7163
|
+
}
|
|
7164
|
+
},
|
|
7165
|
+
0
|
|
7166
|
+
],
|
|
7167
|
+
[
|
|
7168
|
+
{
|
|
7169
|
+
name: "first-push-dispatch",
|
|
7170
|
+
run: bin("check-first-push-dispatch"),
|
|
7171
|
+
scope: "full",
|
|
7172
|
+
timeoutMs: 30000,
|
|
7173
|
+
env: {
|
|
7174
|
+
BRANCH: { optional: true },
|
|
7175
|
+
AEG_REPO: { optional: true },
|
|
7176
|
+
GITHUB_TOKEN: { optional: true },
|
|
7177
|
+
GH_TOKEN: { optional: true }
|
|
7178
|
+
}
|
|
7179
|
+
},
|
|
7180
|
+
0
|
|
7181
|
+
],
|
|
7182
|
+
[
|
|
7183
|
+
{
|
|
7184
|
+
name: "doc-coverage-push",
|
|
7185
|
+
validates: "body",
|
|
7186
|
+
run: bin("check-doc-coverage-push"),
|
|
7187
|
+
scope: "diff",
|
|
7188
|
+
timeoutMs: 15000,
|
|
7189
|
+
env: {
|
|
7190
|
+
OVERRIDE_DOCS: { optional: true },
|
|
7191
|
+
BASE_SHA: { optional: true },
|
|
7192
|
+
PR_BODY: { optional: true },
|
|
7193
|
+
PR_BODY_FILE: { optional: true },
|
|
7194
|
+
PR_LABELS: { optional: true },
|
|
7195
|
+
WAIVER_LABEL_ACTOR: { optional: true },
|
|
7196
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
7197
|
+
GITHUB_TOKEN: { optional: true },
|
|
7198
|
+
GH_TOKEN: { optional: true }
|
|
7199
|
+
}
|
|
7200
|
+
},
|
|
7201
|
+
0
|
|
7202
|
+
],
|
|
7203
|
+
[
|
|
7204
|
+
{
|
|
7205
|
+
name: "issue-assignment",
|
|
7206
|
+
run: bin("check-issue-assignment"),
|
|
7207
|
+
scope: "full",
|
|
7208
|
+
timeoutMs: 30000,
|
|
7209
|
+
env: {
|
|
7210
|
+
AEG_REPO: { optional: true },
|
|
7211
|
+
BRANCH: { optional: true },
|
|
7212
|
+
GITHUB_TOKEN: { optional: true },
|
|
7213
|
+
GH_TOKEN: { optional: true }
|
|
7214
|
+
}
|
|
7215
|
+
},
|
|
7216
|
+
0
|
|
7217
|
+
],
|
|
7218
|
+
[
|
|
7219
|
+
{
|
|
7220
|
+
name: "evidence-fresh",
|
|
7221
|
+
validates: "body",
|
|
7222
|
+
run: bin("check-evidence-fresh"),
|
|
7223
|
+
scope: "diff",
|
|
7224
|
+
timeoutMs: 15000,
|
|
7225
|
+
requiresOpenPr: true,
|
|
7226
|
+
env: {
|
|
7227
|
+
PR_NUMBER: { optional: true },
|
|
7228
|
+
PR_BODY: { optional: true },
|
|
7229
|
+
BASE_SHA: { optional: true },
|
|
7230
|
+
GITHUB_TOKEN: { optional: true },
|
|
7231
|
+
GH_TOKEN: { optional: true }
|
|
7232
|
+
}
|
|
7233
|
+
},
|
|
7234
|
+
1
|
|
7235
|
+
],
|
|
7236
|
+
[
|
|
7237
|
+
{
|
|
7238
|
+
name: "reader-resolvable-prose",
|
|
7239
|
+
run: bin("check-reader-resolvable-prose"),
|
|
7240
|
+
scope: "full",
|
|
7241
|
+
timeoutMs: 30000,
|
|
7242
|
+
env: {},
|
|
7243
|
+
include: [
|
|
7244
|
+
"aeg-root/**/*.md",
|
|
7245
|
+
"apps/cli/src/**",
|
|
7246
|
+
".github/workflows/**",
|
|
7247
|
+
".vinaya/**",
|
|
7248
|
+
"apps/cli/README.md",
|
|
7249
|
+
"packages/sources/README.md"
|
|
7250
|
+
]
|
|
7251
|
+
},
|
|
7252
|
+
0
|
|
7253
|
+
],
|
|
7254
|
+
[
|
|
7255
|
+
{
|
|
7256
|
+
name: "retired-vocabulary",
|
|
7257
|
+
run: bin("check-retired-vocabulary"),
|
|
7258
|
+
scope: "full",
|
|
7259
|
+
timeoutMs: 30000,
|
|
7260
|
+
env: {},
|
|
7261
|
+
include: ["aeg-root/**/*.md"]
|
|
7262
|
+
},
|
|
7263
|
+
0
|
|
7264
|
+
],
|
|
7265
|
+
[
|
|
7266
|
+
{
|
|
7267
|
+
name: "doctrine-portability",
|
|
7268
|
+
run: bin("check-doctrine-portability"),
|
|
7269
|
+
scope: "full",
|
|
7270
|
+
timeoutMs: 30000,
|
|
7271
|
+
env: { BASE_SHA: { optional: true } },
|
|
7272
|
+
include: ["aeg-root/**/*.md"]
|
|
7273
|
+
},
|
|
7274
|
+
0
|
|
7275
|
+
],
|
|
7276
|
+
[
|
|
7277
|
+
{
|
|
7278
|
+
name: "doctrine-no-procedures",
|
|
7279
|
+
run: bin("check-doctrine-no-procedures"),
|
|
7280
|
+
scope: "full",
|
|
7281
|
+
timeoutMs: 30000,
|
|
7282
|
+
env: {},
|
|
7283
|
+
include: ["aeg-root/**/*.md"]
|
|
7284
|
+
},
|
|
7285
|
+
0
|
|
7286
|
+
],
|
|
7287
|
+
[
|
|
7288
|
+
{
|
|
7289
|
+
name: "exec-bits",
|
|
7290
|
+
run: bin("check-exec-bits"),
|
|
7291
|
+
scope: "diff",
|
|
7292
|
+
timeoutMs: 30000,
|
|
7293
|
+
env: {}
|
|
7294
|
+
},
|
|
7295
|
+
0
|
|
7296
|
+
],
|
|
7297
|
+
[
|
|
7298
|
+
{
|
|
7299
|
+
name: "workspace-escape",
|
|
7300
|
+
run: bin("check-workspace-escape"),
|
|
7301
|
+
scope: "full",
|
|
7302
|
+
timeoutMs: 30000,
|
|
7303
|
+
env: {},
|
|
7304
|
+
include: ["aeg-root/**/*.md"]
|
|
7305
|
+
},
|
|
7306
|
+
0
|
|
7307
|
+
],
|
|
7308
|
+
[
|
|
7309
|
+
{
|
|
7310
|
+
name: "changeset-coverage",
|
|
7311
|
+
run: bin("check-changeset-coverage"),
|
|
7312
|
+
scope: "diff",
|
|
7313
|
+
timeoutMs: 15000,
|
|
7314
|
+
env: {}
|
|
7315
|
+
},
|
|
7316
|
+
0
|
|
7317
|
+
],
|
|
7318
|
+
[
|
|
7319
|
+
{
|
|
7320
|
+
name: "quoted-command",
|
|
7321
|
+
run: bin("check-quoted-command"),
|
|
7322
|
+
scope: "diff",
|
|
7323
|
+
timeoutMs: 15000,
|
|
7324
|
+
env: {}
|
|
7325
|
+
},
|
|
7326
|
+
0
|
|
7327
|
+
],
|
|
7328
|
+
[
|
|
7329
|
+
{
|
|
7330
|
+
name: "main-branch-refusal",
|
|
7331
|
+
run: bin("check-main-branch-refusal"),
|
|
7332
|
+
scope: "full",
|
|
7333
|
+
timeoutMs: 15000,
|
|
7334
|
+
env: { VINAYA_PUSH_REFS: { optional: true } }
|
|
7335
|
+
},
|
|
7336
|
+
0
|
|
7337
|
+
],
|
|
7338
|
+
[
|
|
7339
|
+
{
|
|
7340
|
+
name: "token-collection-wired",
|
|
7341
|
+
run: bin("check-token-collection-wired"),
|
|
7342
|
+
scope: "full",
|
|
7343
|
+
timeoutMs: 15000,
|
|
7344
|
+
env: {
|
|
7345
|
+
CLAUDE_PROJECT_DIR: { optional: true },
|
|
7346
|
+
CLAUDE_CODE_SESSION_ID: { optional: true }
|
|
7347
|
+
}
|
|
7348
|
+
},
|
|
7349
|
+
0
|
|
7350
|
+
],
|
|
7351
|
+
[
|
|
7352
|
+
{
|
|
7353
|
+
name: "issue-title-grammar",
|
|
7354
|
+
run: bin("check-issue-title-grammar"),
|
|
7355
|
+
validates: "issue",
|
|
7356
|
+
scope: "full",
|
|
7357
|
+
ownWorkflow: true,
|
|
7358
|
+
timeoutMs: 15000,
|
|
7359
|
+
env: { ISSUE_TITLE: { optional: true } }
|
|
7360
|
+
},
|
|
7361
|
+
1
|
|
7362
|
+
],
|
|
7363
|
+
[
|
|
7364
|
+
{
|
|
7365
|
+
name: "issue-objectives-numbering",
|
|
7366
|
+
run: bin("check-issue-objectives-numbering"),
|
|
7367
|
+
validates: "issue",
|
|
7368
|
+
scope: "full",
|
|
7369
|
+
ownWorkflow: true,
|
|
7370
|
+
timeoutMs: 15000,
|
|
7371
|
+
env: { ISSUE_BODY: { optional: true }, ISSUE_NUMBER: { optional: true } }
|
|
7372
|
+
},
|
|
7373
|
+
1
|
|
7374
|
+
],
|
|
7375
|
+
[
|
|
7376
|
+
{
|
|
7377
|
+
name: "issue-parts-coverage",
|
|
7378
|
+
run: bin("check-issue-parts-coverage"),
|
|
7379
|
+
validates: "issue",
|
|
7380
|
+
scope: "full",
|
|
7381
|
+
ownWorkflow: true,
|
|
7382
|
+
timeoutMs: 15000,
|
|
7383
|
+
env: { ISSUE_BODY: { optional: true } }
|
|
7384
|
+
},
|
|
7385
|
+
1
|
|
7386
|
+
],
|
|
7387
|
+
[
|
|
7388
|
+
{
|
|
7389
|
+
name: "issue-surface-globs",
|
|
7390
|
+
run: bin("check-issue-surface-globs"),
|
|
7391
|
+
validates: "issue",
|
|
7392
|
+
scope: "full",
|
|
7393
|
+
ownWorkflow: true,
|
|
7394
|
+
timeoutMs: 15000,
|
|
7395
|
+
env: { ISSUE_BODY: { optional: true } }
|
|
7396
|
+
},
|
|
7397
|
+
1
|
|
7398
|
+
],
|
|
7399
|
+
[
|
|
7400
|
+
{
|
|
7401
|
+
name: "issue-tranche-label",
|
|
7402
|
+
run: bin("check-issue-tranche-label"),
|
|
7403
|
+
validates: "issue",
|
|
7404
|
+
scope: "full",
|
|
7405
|
+
ownWorkflow: true,
|
|
7406
|
+
timeoutMs: 15000,
|
|
7407
|
+
env: { ISSUE_BODY: { optional: true }, ISSUE_LABELS: { optional: true } }
|
|
7408
|
+
},
|
|
7409
|
+
1
|
|
7410
|
+
],
|
|
7411
|
+
[
|
|
7412
|
+
{
|
|
7413
|
+
name: "issue-milestone-attach",
|
|
7414
|
+
run: bin("check-issue-milestone-attach"),
|
|
7415
|
+
validates: "issue",
|
|
7416
|
+
scope: "full",
|
|
7417
|
+
ownWorkflow: true,
|
|
7418
|
+
timeoutMs: 15000,
|
|
7419
|
+
env: {
|
|
7420
|
+
ISSUE_LABELS: { optional: true },
|
|
7421
|
+
CURRENT_MILESTONE_TITLE: { optional: true },
|
|
7422
|
+
RESOLVED_MILESTONE_TITLE: { optional: true }
|
|
7423
|
+
}
|
|
7424
|
+
},
|
|
7425
|
+
1
|
|
7426
|
+
]
|
|
7427
|
+
];
|
|
7428
|
+
function coreCheckRegistry() {
|
|
7429
|
+
return REGISTRY.map(([spec]) => spec);
|
|
7430
|
+
}
|
|
7431
|
+
var CORE_CHECK_RING = Object.fromEntries(REGISTRY.map(([spec, ring]) => [spec.name, ring]));
|
|
7432
|
+
|
|
6170
7433
|
// src/checks/contract.ts
|
|
6171
7434
|
var CHECK_SCHEMA_VERSION = 1;
|
|
6172
7435
|
function emitCheckError(error) {
|
|
@@ -6174,6 +7437,15 @@ function emitCheckError(error) {
|
|
|
6174
7437
|
`);
|
|
6175
7438
|
}
|
|
6176
7439
|
|
|
7440
|
+
// src/checks/runner.ts
|
|
7441
|
+
var activeKillers = new Set;
|
|
7442
|
+
|
|
7443
|
+
// src/lib/forge-write.ts
|
|
7444
|
+
var INFORMATIONAL_DISPATCH_BLOCKER_CLASSES = new Set([
|
|
7445
|
+
"depends-on-not-merged",
|
|
7446
|
+
"conflicts-with"
|
|
7447
|
+
]);
|
|
7448
|
+
|
|
6177
7449
|
// src/commands/review-post.ts
|
|
6178
7450
|
function parseChangedLineRanges(diffOutput) {
|
|
6179
7451
|
const result = {};
|
|
@@ -6205,7 +7477,7 @@ function parseChangedLineRanges(diffOutput) {
|
|
|
6205
7477
|
function fileDiffAgainst(ref, path) {
|
|
6206
7478
|
let out;
|
|
6207
7479
|
try {
|
|
6208
|
-
out =
|
|
7480
|
+
out = execFileSync5("git", ["diff", `${ref}...HEAD`, "--", path], {
|
|
6209
7481
|
encoding: "utf8",
|
|
6210
7482
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6211
7483
|
}).trim();
|
|
@@ -6216,7 +7488,7 @@ function fileDiffAgainst(ref, path) {
|
|
|
6216
7488
|
}
|
|
6217
7489
|
function revParse(ref) {
|
|
6218
7490
|
try {
|
|
6219
|
-
return
|
|
7491
|
+
return execFileSync5("git", ["rev-parse", "--verify", ref], {
|
|
6220
7492
|
encoding: "utf8",
|
|
6221
7493
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6222
7494
|
}).trim();
|
|
@@ -6226,7 +7498,7 @@ function revParse(ref) {
|
|
|
6226
7498
|
}
|
|
6227
7499
|
function repoRoot() {
|
|
6228
7500
|
try {
|
|
6229
|
-
return
|
|
7501
|
+
return execFileSync5("git", ["rev-parse", "--show-toplevel"], {
|
|
6230
7502
|
encoding: "utf8",
|
|
6231
7503
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6232
7504
|
}).trim();
|
|
@@ -6237,7 +7509,7 @@ function repoRoot() {
|
|
|
6237
7509
|
function changedFiles(base) {
|
|
6238
7510
|
let out;
|
|
6239
7511
|
try {
|
|
6240
|
-
out =
|
|
7512
|
+
out = execFileSync5("git", ["diff", "--name-only", `${base}...HEAD`], {
|
|
6241
7513
|
encoding: "utf8",
|
|
6242
7514
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6243
7515
|
}).trim();
|
|
@@ -6262,7 +7534,7 @@ function resolveDiff(base = process.env.BASE_SHA || "origin/main") {
|
|
|
6262
7534
|
const relPaths = changedFiles(ref);
|
|
6263
7535
|
if (relPaths === null)
|
|
6264
7536
|
continue;
|
|
6265
|
-
return { base: ref, root, files: relPaths.map((p) =>
|
|
7537
|
+
return { base: ref, root, files: relPaths.map((p) => join4(root, p)) };
|
|
6266
7538
|
}
|
|
6267
7539
|
return null;
|
|
6268
7540
|
}
|
|
@@ -6297,9 +7569,9 @@ function findingsInThisDiff(findings) {
|
|
|
6297
7569
|
var CHECK_NAME = "doctrine-portability";
|
|
6298
7570
|
var DOCTRINE_ROOT = loadConfig()?.proseGates?.doctrineRoot ?? "aeg-root";
|
|
6299
7571
|
var SHIPS_PREFIX2 = `${DOCTRINE_ROOT}/`;
|
|
6300
|
-
function
|
|
7572
|
+
function git2(args) {
|
|
6301
7573
|
try {
|
|
6302
|
-
return
|
|
7574
|
+
return execFileSync6("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
6303
7575
|
} catch {
|
|
6304
7576
|
return "";
|
|
6305
7577
|
}
|
|
@@ -6312,7 +7584,7 @@ function collectWorkingTree(dir, out = []) {
|
|
|
6312
7584
|
return out;
|
|
6313
7585
|
}
|
|
6314
7586
|
for (const name of entries) {
|
|
6315
|
-
const full =
|
|
7587
|
+
const full = join5(dir, name);
|
|
6316
7588
|
let isDir;
|
|
6317
7589
|
try {
|
|
6318
7590
|
isDir = statSync(full).isDirectory();
|
|
@@ -6333,10 +7605,10 @@ function readWorkingTree(paths) {
|
|
|
6333
7605
|
return paths.map((p) => ({ path: p, content: readFileSync3(p, "utf8") }));
|
|
6334
7606
|
}
|
|
6335
7607
|
function collectAtRef(ref, dir) {
|
|
6336
|
-
const listing =
|
|
7608
|
+
const listing = git2(["ls-tree", "-r", "--name-only", ref, "--", dir]);
|
|
6337
7609
|
const paths = listing.split(`
|
|
6338
7610
|
`).map((p) => p.trim()).filter((p) => p.endsWith(".md"));
|
|
6339
|
-
return paths.map((p) => ({ path: p, content:
|
|
7611
|
+
return paths.map((p) => ({ path: p, content: git2(["show", `${ref}:${p}`]) }));
|
|
6340
7612
|
}
|
|
6341
7613
|
function findingKey(f) {
|
|
6342
7614
|
return `${f.file}:${f.kind}:${f.cited}`;
|