@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
|
@@ -4,7 +4,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
4
4
|
|
|
5
5
|
// src/checks/bin/check-retired-vocabulary.ts
|
|
6
6
|
import { readdirSync as readdirSync2, readFileSync as readFileSync4, statSync } from "node:fs";
|
|
7
|
-
import { join as
|
|
7
|
+
import { join as join6 } from "node:path";
|
|
8
8
|
// ../../packages/aeg-core/src/gate-audience.ts
|
|
9
9
|
var GATE_AUDIENCE = {
|
|
10
10
|
"check-branch-topology": { shippedAs: "branch-topology", ring: 0 },
|
|
@@ -1955,6 +1955,8 @@ function parseInlineFieldList(section) {
|
|
|
1955
1955
|
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
1956
1956
|
var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1957
1957
|
var RULING_ORDINAL_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Ruling ordinal:\s*(\d+)(?!\d)/im;
|
|
1958
|
+
var BRIEF_HASH_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Brief hash:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1959
|
+
var POLICY_DIGEST_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Policy digest:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1958
1960
|
function firstFiveLines(comment) {
|
|
1959
1961
|
return comment.split(`
|
|
1960
1962
|
`).slice(0, 5).join(`
|
|
@@ -1977,6 +1979,26 @@ function extractRulingOrdinal(comment) {
|
|
|
1977
1979
|
const m = firstSevenLines(comment).match(RULING_ORDINAL_PATTERN);
|
|
1978
1980
|
return m ? Number.parseInt(m[1], 10) : null;
|
|
1979
1981
|
}
|
|
1982
|
+
function firstElevenLines(comment) {
|
|
1983
|
+
return comment.split(`
|
|
1984
|
+
`).slice(0, 11).join(`
|
|
1985
|
+
`);
|
|
1986
|
+
}
|
|
1987
|
+
function extractBriefHash(comment) {
|
|
1988
|
+
const m = firstElevenLines(comment).match(BRIEF_HASH_PATTERN);
|
|
1989
|
+
return m ? m[1].toLowerCase() : null;
|
|
1990
|
+
}
|
|
1991
|
+
function extractPolicyDigest(comment) {
|
|
1992
|
+
const m = firstElevenLines(comment).match(POLICY_DIGEST_PATTERN);
|
|
1993
|
+
return m ? m[1].toLowerCase() : null;
|
|
1994
|
+
}
|
|
1995
|
+
var FINDING_SEVERITY_LINE = /^\d+\.\s+\[([A-Z][A-Z]*)\]\s+(.+?)\s+—/gm;
|
|
1996
|
+
function extractFindingSeverities(comment) {
|
|
1997
|
+
return [...comment.matchAll(FINDING_SEVERITY_LINE)].map((m) => ({
|
|
1998
|
+
severity: m[1],
|
|
1999
|
+
location: m[2]
|
|
2000
|
+
}));
|
|
2001
|
+
}
|
|
1980
2002
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
1981
2003
|
const candidates = comments.filter((c) => valuePattern.test(c));
|
|
1982
2004
|
if (candidates.length === 0) {
|
|
@@ -1985,6 +2007,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1985
2007
|
headSha: null,
|
|
1986
2008
|
objectivesVersion: null,
|
|
1987
2009
|
rulingOrdinal: null,
|
|
2010
|
+
briefHash: null,
|
|
2011
|
+
policyDigest: null,
|
|
2012
|
+
findingSeverities: [],
|
|
1988
2013
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
1989
2014
|
};
|
|
1990
2015
|
}
|
|
@@ -1996,6 +2021,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1996
2021
|
headSha: null,
|
|
1997
2022
|
objectivesVersion: null,
|
|
1998
2023
|
rulingOrdinal: null,
|
|
2024
|
+
briefHash: null,
|
|
2025
|
+
policyDigest: null,
|
|
2026
|
+
findingSeverities: [],
|
|
1999
2027
|
danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
|
|
2000
2028
|
};
|
|
2001
2029
|
}
|
|
@@ -2004,6 +2032,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
2004
2032
|
headSha: extractHeadSha(latest),
|
|
2005
2033
|
objectivesVersion: extractObjectivesVersion(latest),
|
|
2006
2034
|
rulingOrdinal: extractRulingOrdinal(latest),
|
|
2035
|
+
briefHash: extractBriefHash(latest),
|
|
2036
|
+
policyDigest: extractPolicyDigest(latest),
|
|
2037
|
+
findingSeverities: extractFindingSeverities(latest),
|
|
2007
2038
|
danglingNote: null
|
|
2008
2039
|
};
|
|
2009
2040
|
}
|
|
@@ -2339,6 +2370,53 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive,
|
|
|
2339
2370
|
}
|
|
2340
2371
|
return out;
|
|
2341
2372
|
}
|
|
2373
|
+
// ../../packages/aeg-core/src/review-policy.ts
|
|
2374
|
+
var CODE_REVIEW_SEVERITY_ORDER = ["BLOCKER", "MAJOR", "MINOR"];
|
|
2375
|
+
var SECURITY_SEVERITY_ORDER = ["CRITICAL", "HIGH", "MEDIUM", "LOW"];
|
|
2376
|
+
var DEFAULT_MAX_ROUNDS = 3;
|
|
2377
|
+
var DEFAULT_REVIEW_POLICY = {
|
|
2378
|
+
codeReviewThreshold: "BLOCKER",
|
|
2379
|
+
securityThreshold: "HIGH",
|
|
2380
|
+
maxRounds: DEFAULT_MAX_ROUNDS
|
|
2381
|
+
};
|
|
2382
|
+
var FILE_SHAPED_LOCATION = /\.[a-zA-Z0-9]{1,10}(:\d+)?\s*$/;
|
|
2383
|
+
var PROSE_LOCATION_PATTERNS = [/\bpr\s*body\b/i, /\bcomment\b/i];
|
|
2384
|
+
var ROLE_FILE_LOCATION = /(^|\/)aeg-root\/roles\//i;
|
|
2385
|
+
function isProseLocation(location) {
|
|
2386
|
+
if (ROLE_FILE_LOCATION.test(location))
|
|
2387
|
+
return true;
|
|
2388
|
+
if (FILE_SHAPED_LOCATION.test(location))
|
|
2389
|
+
return false;
|
|
2390
|
+
return PROSE_LOCATION_PATTERNS.some((pattern) => pattern.test(location));
|
|
2391
|
+
}
|
|
2392
|
+
var PROSE_CAP_SEVERITY = "MINOR";
|
|
2393
|
+
function blockingSeverities(scale, threshold) {
|
|
2394
|
+
const idx = scale.indexOf(threshold);
|
|
2395
|
+
if (idx === -1) {
|
|
2396
|
+
throw new Error(`blockingSeverities: threshold "${threshold}" is not one of ${scale.join(" > ")}`);
|
|
2397
|
+
}
|
|
2398
|
+
return scale.slice(0, idx + 1);
|
|
2399
|
+
}
|
|
2400
|
+
function evaluateReviewFindings(findings, scale, threshold) {
|
|
2401
|
+
const blocking = new Set(blockingSeverities(scale, threshold));
|
|
2402
|
+
const blockingFindings = findings.filter((f) => {
|
|
2403
|
+
if (!scale.includes(f.severity)) {
|
|
2404
|
+
throw new Error(`evaluateReviewFindings: severity "${f.severity}" is not one of ${scale.join(" > ")}`);
|
|
2405
|
+
}
|
|
2406
|
+
const effectiveSeverity = f.location !== undefined && isProseLocation(f.location) ? PROSE_CAP_SEVERITY : f.severity;
|
|
2407
|
+
return blocking.has(effectiveSeverity);
|
|
2408
|
+
});
|
|
2409
|
+
return { outcome: blockingFindings.length > 0 ? "blocked" : "clean", blockingFindings };
|
|
2410
|
+
}
|
|
2411
|
+
function evaluateCodeReview(findings, policy) {
|
|
2412
|
+
return evaluateReviewFindings(findings, CODE_REVIEW_SEVERITY_ORDER, policy.codeReviewThreshold);
|
|
2413
|
+
}
|
|
2414
|
+
function evaluateSecurityReview(findings, policy) {
|
|
2415
|
+
return evaluateReviewFindings(findings, SECURITY_SEVERITY_ORDER, policy.securityThreshold);
|
|
2416
|
+
}
|
|
2417
|
+
function isKnownSeverity(scale, value) {
|
|
2418
|
+
return scale.includes(value);
|
|
2419
|
+
}
|
|
2342
2420
|
// ../../packages/aeg-core/src/pr-tier.ts
|
|
2343
2421
|
var TIER_FIELD = /(\*\*)?\s*Tier\s*(\*\*)?\s*:\s*(\*\*)?\s*([013])\b/i;
|
|
2344
2422
|
function readTierFromPrBody(prBody) {
|
|
@@ -2709,7 +2787,7 @@ function checkForField(prBody) {
|
|
|
2709
2787
|
]
|
|
2710
2788
|
};
|
|
2711
2789
|
}
|
|
2712
|
-
function
|
|
2790
|
+
function checkClosesNPresence(prBody) {
|
|
2713
2791
|
const closesPattern = /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s{0,8}:?\s{0,8}#\d+/i;
|
|
2714
2792
|
if (closesPattern.test(stripCode(prBody))) {
|
|
2715
2793
|
return { status: "pass", errors: [] };
|
|
@@ -2741,10 +2819,22 @@ var COMMIT_TYPES = [
|
|
|
2741
2819
|
"Test"
|
|
2742
2820
|
];
|
|
2743
2821
|
var COMMIT_TYPE_STYLE = new RegExp(`^(${COMMIT_TYPES.join("|")})(\\([a-z0-9-]+\\))?: \\S`);
|
|
2822
|
+
function checkForgeTitle(title) {
|
|
2823
|
+
const taskStyle = /^\[[a-z0-9._-]+\] \S+ — \S/;
|
|
2824
|
+
if (COMMIT_TYPE_STYLE.test(title) || taskStyle.test(title))
|
|
2825
|
+
return { status: "pass", errors: [] };
|
|
2826
|
+
return {
|
|
2827
|
+
status: "fail",
|
|
2828
|
+
errors: [
|
|
2829
|
+
`brief-validation title: "${title}" matches neither title grammar — expected \`Type: description\` / \`Type(scope): description\` (commitlint types + Plan) or \`[tranche] id — description\` (task form).`
|
|
2830
|
+
]
|
|
2831
|
+
};
|
|
2832
|
+
}
|
|
2744
2833
|
var BRIEF_SHAPE_MARKERS = [checkSurfaceMap, checkDocUpdateList, checkStopConditions, checkAutonomyClause];
|
|
2745
2834
|
var TASK_BRANCH_PATTERN = /^task\/[^/]+\/[^/]+$/;
|
|
2835
|
+
var TASK_ISSUE_BRANCH_PATTERN = /^task\/issue-\d+$/;
|
|
2746
2836
|
function isTaskBranch(branch) {
|
|
2747
|
-
return TASK_BRANCH_PATTERN.test(branch);
|
|
2837
|
+
return TASK_BRANCH_PATTERN.test(branch) || TASK_ISSUE_BRANCH_PATTERN.test(branch);
|
|
2748
2838
|
}
|
|
2749
2839
|
function isBriefShaped(prBody) {
|
|
2750
2840
|
const stripped = stripCode(prBody);
|
|
@@ -2866,8 +2956,9 @@ function packagesNamedIn(text) {
|
|
|
2866
2956
|
}
|
|
2867
2957
|
function hasTestPathForConsumer(text, consumerDir) {
|
|
2868
2958
|
const escaped = consumerDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2869
|
-
const
|
|
2870
|
-
|
|
2959
|
+
const filePathRe = new RegExp(`${escaped}\\/[\\w./-]*\\.test\\.[A-Za-z0-9]+`);
|
|
2960
|
+
const testDirRe = new RegExp(`${escaped}\\/(?:[\\w-]+\\/)*(?:tests|specs)(?:\\/|\\b)`);
|
|
2961
|
+
return filePathRe.test(text) || testDirRe.test(text);
|
|
2871
2962
|
}
|
|
2872
2963
|
function checkConsumerTests(prBody, consumersOf) {
|
|
2873
2964
|
const section4 = extractNumberedSection(prBody, 4);
|
|
@@ -2971,7 +3062,7 @@ function checkBriefSections(prBody, readTier, options = {}) {
|
|
|
2971
3062
|
checkConsumerTests(prBody, consumersOf),
|
|
2972
3063
|
checkDefeatCases(prBody),
|
|
2973
3064
|
...issueObjectives !== undefined ? [checkObjectivesCopy(prBody, issueObjectives), checkObjectivesCoverage(prBody)] : [],
|
|
2974
|
-
...requireClosesN ? [
|
|
3065
|
+
...requireClosesN ? [checkClosesNPresence(prBody)] : []
|
|
2975
3066
|
];
|
|
2976
3067
|
return { errors: results.flatMap((r) => r.errors) };
|
|
2977
3068
|
}
|
|
@@ -3261,6 +3352,28 @@ function topLevelSectionText(body, headingName) {
|
|
|
3261
3352
|
const next = /^##[ \t]/m.exec(afterHeading);
|
|
3262
3353
|
return next ? afterHeading.slice(0, next.index) : afterHeading;
|
|
3263
3354
|
}
|
|
3355
|
+
function partHasBacktickedPath(text) {
|
|
3356
|
+
let i = 0;
|
|
3357
|
+
while (i < text.length) {
|
|
3358
|
+
const start = text.indexOf("`", i);
|
|
3359
|
+
if (start === -1)
|
|
3360
|
+
return false;
|
|
3361
|
+
const end = text.indexOf("`", start + 1);
|
|
3362
|
+
if (end === -1)
|
|
3363
|
+
return false;
|
|
3364
|
+
if (text.slice(start + 1, end).includes("/"))
|
|
3365
|
+
return true;
|
|
3366
|
+
i = end + 1;
|
|
3367
|
+
}
|
|
3368
|
+
return false;
|
|
3369
|
+
}
|
|
3370
|
+
var PART_MIN_WORDS_OUTSIDE_BACKTICKS = 3;
|
|
3371
|
+
function stripPartBackticks(text) {
|
|
3372
|
+
return text.replace(/`[^`\n]*`/g, " ");
|
|
3373
|
+
}
|
|
3374
|
+
function partWordCount(text) {
|
|
3375
|
+
return text.split(/\s+/).filter((w) => /[a-z]/i.test(w)).length;
|
|
3376
|
+
}
|
|
3264
3377
|
function looksLikeFilePath(entry2) {
|
|
3265
3378
|
const stripped = entry2.replace(/\/\*\*?$/, "");
|
|
3266
3379
|
const lastSegment = stripped.split("/").pop() ?? stripped;
|
|
@@ -3301,6 +3414,18 @@ function globCoversPath(glob, path) {
|
|
|
3301
3414
|
const p = path.replace(/\/+$/, "");
|
|
3302
3415
|
return g === p || g.startsWith(`${p}/`) || p.startsWith(`${g}/`);
|
|
3303
3416
|
}
|
|
3417
|
+
function checkSurfaceGlobsResolve(body, resolvesToFile) {
|
|
3418
|
+
const surface = parseIssueSurface(body);
|
|
3419
|
+
if (!surface.ok)
|
|
3420
|
+
return { status: "pass", errors: [] };
|
|
3421
|
+
const errors = [];
|
|
3422
|
+
for (const glob of surface.value.in) {
|
|
3423
|
+
if (!resolvesToFile(glob)) {
|
|
3424
|
+
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.`);
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3428
|
+
}
|
|
3304
3429
|
function checkSurfaceScope(changedFiles, outGlobs) {
|
|
3305
3430
|
if (outGlobs.length === 0)
|
|
3306
3431
|
return { ok: true };
|
|
@@ -3312,9 +3437,77 @@ function checkSurfaceScope(changedFiles, outGlobs) {
|
|
|
3312
3437
|
}
|
|
3313
3438
|
return violations.length > 0 ? { ok: false, violations } : { ok: true };
|
|
3314
3439
|
}
|
|
3440
|
+
var ISSUE_PART_LINE_RE = /^Part\s+(\d+)\s*\(([^)]*)\)\s*[-—–]\s*(.*)$/i;
|
|
3441
|
+
function parseIssueParts(body) {
|
|
3442
|
+
const section = topLevelSectionText(body, "Parts");
|
|
3443
|
+
if (section === null)
|
|
3444
|
+
return { ok: false, errors: ["no `## Parts` heading found in the body."] };
|
|
3445
|
+
const lines = section.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
3446
|
+
const parts = [];
|
|
3447
|
+
const errors = [];
|
|
3448
|
+
for (const line of lines) {
|
|
3449
|
+
const m = ISSUE_PART_LINE_RE.exec(line);
|
|
3450
|
+
if (!m) {
|
|
3451
|
+
errors.push(`"${line}" is not a well-formed Parts line — expected \`Part <n> (<refs>) — <outcome>\`.`);
|
|
3452
|
+
continue;
|
|
3453
|
+
}
|
|
3454
|
+
const n = Number.parseInt(m[1], 10);
|
|
3455
|
+
const refs = m[2];
|
|
3456
|
+
const text = m[3].trim();
|
|
3457
|
+
if (text.length === 0) {
|
|
3458
|
+
errors.push(`Part ${n} has no outcome text after the dash — every Part states one observable outcome.`);
|
|
3459
|
+
continue;
|
|
3460
|
+
}
|
|
3461
|
+
if (partHasBacktickedPath(text) && partWordCount(stripPartBackticks(text)) < PART_MIN_WORDS_OUTSIDE_BACKTICKS) {
|
|
3462
|
+
errors.push(`Part ${n} is little more than a file path — a Part names an outcome and symbols, never a bare path.`);
|
|
3463
|
+
continue;
|
|
3464
|
+
}
|
|
3465
|
+
const objectiveIds = [...refs.matchAll(/O(\d+)/g)].map((r) => Number.parseInt(r[1], 10));
|
|
3466
|
+
parts.push({ n, objectiveIds, text });
|
|
3467
|
+
}
|
|
3468
|
+
if (parts.length === 0) {
|
|
3469
|
+
errors.push("the `## Parts` section has no well-formed `Part <n> (<refs>) — <outcome>` lines.");
|
|
3470
|
+
}
|
|
3471
|
+
if (errors.length > 0)
|
|
3472
|
+
return { ok: false, errors };
|
|
3473
|
+
return { ok: true, value: parts };
|
|
3474
|
+
}
|
|
3475
|
+
function checkPartsCiteDefinedObjectives(body) {
|
|
3476
|
+
const parts = parseIssueParts(body);
|
|
3477
|
+
const objectives = objectivesOf(body);
|
|
3478
|
+
if (!parts.ok || !objectives.ok)
|
|
3479
|
+
return { status: "pass", errors: [] };
|
|
3480
|
+
const definedIds = new Set(objectives.objectives.map((o) => Number.parseInt(o.id.slice(1), 10)));
|
|
3481
|
+
const errors = [];
|
|
3482
|
+
for (const part of parts.value) {
|
|
3483
|
+
for (const objectiveId of part.objectiveIds) {
|
|
3484
|
+
if (!definedIds.has(objectiveId)) {
|
|
3485
|
+
errors.push(`issue-validation Parts: Part ${part.n} cites O${objectiveId}, which the Issue's own \`## Objectives\` section does not define.`);
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3490
|
+
}
|
|
3315
3491
|
function isTaskIssueLabelSet(labels) {
|
|
3316
3492
|
return hasLabel("tranche", labels);
|
|
3317
3493
|
}
|
|
3494
|
+
function checkTrancheLabelPresence(_body, _labels) {
|
|
3495
|
+
return { status: "pass", errors: [] };
|
|
3496
|
+
}
|
|
3497
|
+
function checkMilestoneAttach(labels, currentMilestoneTitle, resolvedMilestoneTitle) {
|
|
3498
|
+
if (!isTaskIssueLabelSet(labels))
|
|
3499
|
+
return { status: "pass", errors: [] };
|
|
3500
|
+
if (resolvedMilestoneTitle === null)
|
|
3501
|
+
return { status: "pass", errors: [] };
|
|
3502
|
+
if (currentMilestoneTitle === resolvedMilestoneTitle)
|
|
3503
|
+
return { status: "pass", errors: [] };
|
|
3504
|
+
return {
|
|
3505
|
+
status: "fail",
|
|
3506
|
+
errors: [
|
|
3507
|
+
`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}".`
|
|
3508
|
+
]
|
|
3509
|
+
};
|
|
3510
|
+
}
|
|
3318
3511
|
var TYPE_LABEL_IDS = LABELS.filter((l) => l.category === "type").map((l) => l.id);
|
|
3319
3512
|
function isControlCodePoint(codePoint) {
|
|
3320
3513
|
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
|
|
@@ -3379,6 +3572,19 @@ function checkProjectsRegistered(body, _labels, registeredNames) {
|
|
|
3379
3572
|
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;
|
|
3380
3573
|
var DOC_PATH_RE_GLOBAL = new RegExp(DOC_PATH_RE.source, "gi");
|
|
3381
3574
|
|
|
3575
|
+
// ../../packages/aeg-core/src/task-branch-identity.ts
|
|
3576
|
+
var ISSUE_BRANCH_PATTERN = /^task\/issue-(\d+)$/;
|
|
3577
|
+
var TRANCHE_BRANCH_PATTERN = /^task\/([^/]+)\/([^/]+)$/;
|
|
3578
|
+
function parseTaskBranchIdentity(branch) {
|
|
3579
|
+
const issueMatch = ISSUE_BRANCH_PATTERN.exec(branch);
|
|
3580
|
+
if (issueMatch)
|
|
3581
|
+
return { kind: "issue", issueNumber: Number(issueMatch[1]) };
|
|
3582
|
+
const trancheMatch = TRANCHE_BRANCH_PATTERN.exec(branch);
|
|
3583
|
+
if (trancheMatch)
|
|
3584
|
+
return { kind: "tranche", tranche: trancheMatch[1], taskId: trancheMatch[2] };
|
|
3585
|
+
return null;
|
|
3586
|
+
}
|
|
3587
|
+
|
|
3382
3588
|
// ../../packages/aeg-core/src/coherence-checks.ts
|
|
3383
3589
|
var COHERENCE_ENFORCED_FROM = "2026-07-01";
|
|
3384
3590
|
function isGrandfathered(isoDate) {
|
|
@@ -3399,6 +3605,7 @@ function checkA1(entries, principalAllowlist = PRINCIPAL_ALLOWLIST) {
|
|
|
3399
3605
|
if (handClosed)
|
|
3400
3606
|
continue;
|
|
3401
3607
|
failures.push({
|
|
3608
|
+
code: "closed-without-merge",
|
|
3402
3609
|
issue: e.task.issue,
|
|
3403
3610
|
tranche: e.trancheSlug,
|
|
3404
3611
|
task: e.task.id,
|
|
@@ -3423,6 +3630,7 @@ function checkA3(entries) {
|
|
|
3423
3630
|
continue;
|
|
3424
3631
|
if (e.facts.prState === "merged" && e.facts.issueState !== "closed") {
|
|
3425
3632
|
failures.push({
|
|
3633
|
+
code: "auto-close-misfire",
|
|
3426
3634
|
issue: e.task.issue,
|
|
3427
3635
|
tranche: e.trancheSlug,
|
|
3428
3636
|
task: e.task.id,
|
|
@@ -3447,6 +3655,7 @@ function checkT1(entries) {
|
|
|
3447
3655
|
continue;
|
|
3448
3656
|
if (e.facts === undefined) {
|
|
3449
3657
|
failures.push({
|
|
3658
|
+
code: "phantom-issue-ref",
|
|
3450
3659
|
issue: e.task.issue,
|
|
3451
3660
|
tranche: e.trancheSlug,
|
|
3452
3661
|
task: e.task.id,
|
|
@@ -3465,6 +3674,7 @@ function checkT2(openIssuesBySlug, topologyIssuesBySlug, ciTrancheSlug) {
|
|
|
3465
3674
|
for (const num of openNums) {
|
|
3466
3675
|
if (!topologySet.has(num)) {
|
|
3467
3676
|
failures.push({
|
|
3677
|
+
code: "orphan-task",
|
|
3468
3678
|
issue: num,
|
|
3469
3679
|
tranche: slug,
|
|
3470
3680
|
reason: `Issue #${num} is open and labeled ${trancheLabel(slug)} but does not appear in the topology file`
|
|
@@ -3499,6 +3709,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
|
|
|
3499
3709
|
continue;
|
|
3500
3710
|
if (forgeUnavailableSlugs?.has(e.trancheSlug)) {
|
|
3501
3711
|
failures.push({
|
|
3712
|
+
code: "tbd-in-active-tranche",
|
|
3502
3713
|
issue: null,
|
|
3503
3714
|
tranche: e.trancheSlug,
|
|
3504
3715
|
task: e.task.id,
|
|
@@ -3508,6 +3719,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
|
|
|
3508
3719
|
continue;
|
|
3509
3720
|
}
|
|
3510
3721
|
failures.push({
|
|
3722
|
+
code: "tbd-in-active-tranche",
|
|
3511
3723
|
issue: null,
|
|
3512
3724
|
tranche: e.trancheSlug,
|
|
3513
3725
|
task: e.task.id,
|
|
@@ -3540,6 +3752,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
|
|
|
3540
3752
|
const sameIssue = depEntry.task.issue !== null && e.task.issue !== null && depEntry.task.issue === e.task.issue;
|
|
3541
3753
|
if (sameTask || sameIssue) {
|
|
3542
3754
|
failures.push({
|
|
3755
|
+
code: "d1-self-dependency",
|
|
3543
3756
|
issue: e.task.issue,
|
|
3544
3757
|
tranche: e.trancheSlug,
|
|
3545
3758
|
task: e.task.id,
|
|
@@ -3551,6 +3764,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
|
|
|
3551
3764
|
const depClosed = depFacts?.issueState === "closed";
|
|
3552
3765
|
if (!depClosed) {
|
|
3553
3766
|
failures.push({
|
|
3767
|
+
code: "dispatched-on-unmet-deps",
|
|
3554
3768
|
issue: e.task.issue,
|
|
3555
3769
|
tranche: e.trancheSlug,
|
|
3556
3770
|
task: e.task.id,
|
|
@@ -3576,11 +3790,13 @@ function checkR1(issuesBySlug, grandfatheredIssues, registeredNames = []) {
|
|
|
3576
3790
|
const errors = [
|
|
3577
3791
|
...checkIssueRationale(issue.body).errors,
|
|
3578
3792
|
...checkProjectsRegistered(issue.body, issue.labels, registeredNames).errors,
|
|
3579
|
-
...checkIssueObjectives(issue.body, issue.number).errors
|
|
3793
|
+
...checkIssueObjectives(issue.body, issue.number).errors,
|
|
3794
|
+
...checkPartsCiteDefinedObjectives(issue.body).errors
|
|
3580
3795
|
];
|
|
3581
3796
|
if (errors.length === 0)
|
|
3582
3797
|
continue;
|
|
3583
3798
|
failures.push({
|
|
3799
|
+
code: "missing-rationale-field",
|
|
3584
3800
|
issue: issue.number,
|
|
3585
3801
|
tranche: slug,
|
|
3586
3802
|
reason: `Issue #${issue.number} fails the rationale gate: ${errors.join(" | ")}`,
|
|
@@ -3609,6 +3825,7 @@ function checkL1(files, entriesBySlug) {
|
|
|
3609
3825
|
const allClosed = withFacts.every((e) => e.facts?.issueState === "closed");
|
|
3610
3826
|
if (allClosed) {
|
|
3611
3827
|
failures.push({
|
|
3828
|
+
code: "archive-recommended",
|
|
3612
3829
|
tranche: f.slug,
|
|
3613
3830
|
reason: "Active tranche has no open task-Issues — consider archiving to completed/"
|
|
3614
3831
|
});
|
|
@@ -3640,27 +3857,38 @@ function extractClosesReferences(prBody) {
|
|
|
3640
3857
|
}
|
|
3641
3858
|
return referenced;
|
|
3642
3859
|
}
|
|
3643
|
-
function
|
|
3860
|
+
function checkClosesNTopology(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
3644
3861
|
const referenced = extractClosesReferences(prBody);
|
|
3645
3862
|
if (taskIssueRefs) {
|
|
3646
3863
|
for (const n of referenced) {
|
|
3647
|
-
const
|
|
3648
|
-
if (!
|
|
3864
|
+
const ref2 = taskIssueRefs.get(n);
|
|
3865
|
+
if (!ref2)
|
|
3649
3866
|
continue;
|
|
3650
|
-
const expectedBranch = `task/${
|
|
3867
|
+
const expectedBranch = `task/${ref2.trancheSlug}/${ref2.taskId}`;
|
|
3651
3868
|
if (branch !== expectedBranch) {
|
|
3652
3869
|
return {
|
|
3653
3870
|
ok: false,
|
|
3654
|
-
message: `closes-n-reverse: branch "${branch}" closes #${n} (task ${
|
|
3871
|
+
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.`
|
|
3655
3872
|
};
|
|
3656
3873
|
}
|
|
3657
3874
|
}
|
|
3658
3875
|
}
|
|
3659
|
-
const
|
|
3660
|
-
if (!
|
|
3876
|
+
const ref = parseTaskBranchIdentity(branch);
|
|
3877
|
+
if (!ref)
|
|
3661
3878
|
return { ok: true };
|
|
3662
|
-
|
|
3663
|
-
|
|
3879
|
+
if (ref.kind === "issue") {
|
|
3880
|
+
const expectedIssue2 = ref.issueNumber;
|
|
3881
|
+
if (!referenced.has(expectedIssue2)) {
|
|
3882
|
+
return {
|
|
3883
|
+
ok: false,
|
|
3884
|
+
expectedIssue: expectedIssue2,
|
|
3885
|
+
message: `closes-n: PR body does not contain \`Closes #${expectedIssue2}\` (required for branch "${branch}"). Add it to the PR body Summary section.`
|
|
3886
|
+
};
|
|
3887
|
+
}
|
|
3888
|
+
return { ok: true, expectedIssue: expectedIssue2 };
|
|
3889
|
+
}
|
|
3890
|
+
const trancheSlug = ref.tranche;
|
|
3891
|
+
const taskId = ref.taskId;
|
|
3664
3892
|
const trancheFile = trancheFiles.find((f) => f.slug === trancheSlug);
|
|
3665
3893
|
if (!trancheFile) {
|
|
3666
3894
|
return {
|
|
@@ -3712,34 +3940,67 @@ function extractIssue(body) {
|
|
|
3712
3940
|
const issue = headerNums.length > 0 ? headerNums[0] : bodyNums[0];
|
|
3713
3941
|
return { issue, extraIssues: bodyNums.filter((n) => n !== issue), outsideHeader: headerNums.length === 0 };
|
|
3714
3942
|
}
|
|
3715
|
-
// ../../packages/aeg-core/src/review-
|
|
3716
|
-
|
|
3717
|
-
|
|
3943
|
+
// ../../packages/aeg-core/src/review-input-manifest.ts
|
|
3944
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3945
|
+
function briefHash(brief) {
|
|
3946
|
+
return createHash4("sha256").update(`${brief}
|
|
3947
|
+
`).digest("hex");
|
|
3948
|
+
}
|
|
3949
|
+
function policyDigest(policy) {
|
|
3950
|
+
return createHash4("sha256").update(JSON.stringify({ codeReviewThreshold: policy.codeReviewThreshold, securityThreshold: policy.securityThreshold })).digest("hex");
|
|
3951
|
+
}
|
|
3952
|
+
function isBoundToHead(echoed, headSha) {
|
|
3953
|
+
if (!echoed.headSha)
|
|
3718
3954
|
return false;
|
|
3719
|
-
return headSha.toLowerCase().startsWith(
|
|
3955
|
+
return headSha.toLowerCase().startsWith(echoed.headSha.toLowerCase());
|
|
3720
3956
|
}
|
|
3721
|
-
function isBoundByPatchIdentity(
|
|
3722
|
-
if (patchIdOf === undefined || !
|
|
3957
|
+
function isBoundByPatchIdentity(echoed, headSha, patchIdOf) {
|
|
3958
|
+
if (patchIdOf === undefined || !echoed.headSha)
|
|
3723
3959
|
return false;
|
|
3724
|
-
const judged = patchIdOf(
|
|
3960
|
+
const judged = patchIdOf(echoed.headSha);
|
|
3725
3961
|
const current = patchIdOf(headSha);
|
|
3726
3962
|
if (judged === null || current === null)
|
|
3727
3963
|
return false;
|
|
3728
3964
|
return judged === current;
|
|
3729
3965
|
}
|
|
3730
|
-
function isBoundToPatch(
|
|
3731
|
-
return isBoundToHead(
|
|
3966
|
+
function isBoundToPatch(echoed, headSha, patchIdOf) {
|
|
3967
|
+
return isBoundToHead(echoed, headSha) || isBoundByPatchIdentity(echoed, headSha, patchIdOf);
|
|
3732
3968
|
}
|
|
3733
|
-
function isBoundToObjectives(
|
|
3969
|
+
function isBoundToObjectives(echoed, currentVersion) {
|
|
3734
3970
|
if (currentVersion === null)
|
|
3735
3971
|
return true;
|
|
3736
|
-
return
|
|
3972
|
+
return echoed.objectivesVersion === currentVersion;
|
|
3737
3973
|
}
|
|
3738
|
-
function isBoundToRulings(
|
|
3739
|
-
if (
|
|
3974
|
+
function isBoundToRulings(echoed, currentOrdinal) {
|
|
3975
|
+
if (echoed.rulingOrdinal === null)
|
|
3740
3976
|
return currentOrdinal === 0;
|
|
3741
|
-
return
|
|
3977
|
+
return echoed.rulingOrdinal === currentOrdinal;
|
|
3978
|
+
}
|
|
3979
|
+
function isBoundToBriefHash(echoed, currentHash) {
|
|
3980
|
+
if (currentHash === null)
|
|
3981
|
+
return true;
|
|
3982
|
+
return echoed.briefHash === currentHash;
|
|
3983
|
+
}
|
|
3984
|
+
function isBoundToPolicy(echoed, currentDigest) {
|
|
3985
|
+
return echoed.policyDigest === currentDigest;
|
|
3742
3986
|
}
|
|
3987
|
+
function compareManifest(echoed, current, patchIdOf) {
|
|
3988
|
+
const head = isBoundToPatch(echoed, current.headSha, patchIdOf);
|
|
3989
|
+
const briefHashBound = isBoundToBriefHash(echoed, current.briefHash);
|
|
3990
|
+
const objectivesVersion2 = isBoundToObjectives(echoed, current.objectivesVersion);
|
|
3991
|
+
const rulingOrdinal = isBoundToRulings(echoed, current.rulingOrdinal);
|
|
3992
|
+
const policyDigestBound = isBoundToPolicy(echoed, current.policyDigest);
|
|
3993
|
+
return {
|
|
3994
|
+
bound: head && briefHashBound && objectivesVersion2 && rulingOrdinal && policyDigestBound,
|
|
3995
|
+
head,
|
|
3996
|
+
briefHash: briefHashBound,
|
|
3997
|
+
objectivesVersion: objectivesVersion2,
|
|
3998
|
+
rulingOrdinal,
|
|
3999
|
+
policyDigest: policyDigestBound
|
|
4000
|
+
};
|
|
4001
|
+
}
|
|
4002
|
+
|
|
4003
|
+
// ../../packages/aeg-core/src/review-gate.ts
|
|
3743
4004
|
function checkReviewGate(input) {
|
|
3744
4005
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
3745
4006
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -3762,15 +4023,55 @@ function checkReviewGate(input) {
|
|
|
3762
4023
|
const verifiedBodies = verified.map((c) => c.body);
|
|
3763
4024
|
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
3764
4025
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
3765
|
-
const
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
4026
|
+
const policy = input.policy ?? DEFAULT_REVIEW_POLICY;
|
|
4027
|
+
let codeReviewPolicyEvaluation;
|
|
4028
|
+
let securityPolicyEvaluation;
|
|
4029
|
+
try {
|
|
4030
|
+
codeReviewPolicyEvaluation = evaluateCodeReview(codeReview.findingSeverities, policy);
|
|
4031
|
+
securityPolicyEvaluation = evaluateSecurityReview(security.findingSeverities, policy);
|
|
4032
|
+
} catch (err) {
|
|
4033
|
+
return {
|
|
4034
|
+
verdict: "fail",
|
|
4035
|
+
reason: `a verdict comment carries a finding severity this repository's policy does not recognize: ${err instanceof Error ? err.message : String(err)}`,
|
|
4036
|
+
waived: false
|
|
4037
|
+
};
|
|
4038
|
+
}
|
|
4039
|
+
const codeReviewTextClean = codeReview.value === "APPROVE";
|
|
4040
|
+
const securityTextClean = security.value === "PASS";
|
|
4041
|
+
const codeReviewPolicyClean = codeReviewPolicyEvaluation.outcome === "clean";
|
|
4042
|
+
const securityPolicyClean = securityPolicyEvaluation.outcome === "clean";
|
|
4043
|
+
const codeReviewClean = codeReviewTextClean && codeReviewPolicyClean;
|
|
4044
|
+
const securityClean = securityTextClean && securityPolicyClean;
|
|
4045
|
+
const currentManifest = {
|
|
4046
|
+
headSha: input.headSha,
|
|
4047
|
+
briefHash: input.briefHash ?? null,
|
|
4048
|
+
objectivesVersion: input.objectivesVersion,
|
|
4049
|
+
rulingOrdinal: input.rulingOrdinal,
|
|
4050
|
+
policyDigest: policyDigest(policy)
|
|
4051
|
+
};
|
|
4052
|
+
const codeReviewEchoed = {
|
|
4053
|
+
headSha: codeReview.headSha,
|
|
4054
|
+
briefHash: codeReview.briefHash,
|
|
4055
|
+
objectivesVersion: codeReview.objectivesVersion,
|
|
4056
|
+
rulingOrdinal: codeReview.rulingOrdinal,
|
|
4057
|
+
policyDigest: codeReview.policyDigest
|
|
4058
|
+
};
|
|
4059
|
+
const securityEchoed = {
|
|
4060
|
+
headSha: security.headSha,
|
|
4061
|
+
briefHash: security.briefHash,
|
|
4062
|
+
objectivesVersion: security.objectivesVersion,
|
|
4063
|
+
rulingOrdinal: security.rulingOrdinal,
|
|
4064
|
+
policyDigest: security.policyDigest
|
|
4065
|
+
};
|
|
4066
|
+
const codeReviewBinding = compareManifest(codeReviewEchoed, currentManifest, input.patchIdOf);
|
|
4067
|
+
const securityBinding = compareManifest(securityEchoed, currentManifest, input.patchIdOf);
|
|
4068
|
+
const codeReviewBound = codeReviewBinding.head;
|
|
4069
|
+
const securityBound = securityBinding.head;
|
|
4070
|
+
const codeReviewObjectivesBound = codeReviewBinding.objectivesVersion;
|
|
4071
|
+
const securityObjectivesBound = securityBinding.objectivesVersion;
|
|
4072
|
+
const codeReviewRulingsBound = codeReviewBinding.rulingOrdinal;
|
|
4073
|
+
const securityRulingsBound = securityBinding.rulingOrdinal;
|
|
4074
|
+
if (codeReviewClean && codeReviewBinding.bound && securityClean && securityBinding.bound && mechanicalChecksClean) {
|
|
3774
4075
|
return {
|
|
3775
4076
|
verdict: "pass",
|
|
3776
4077
|
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.`,
|
|
@@ -3778,23 +4079,35 @@ function checkReviewGate(input) {
|
|
|
3778
4079
|
};
|
|
3779
4080
|
}
|
|
3780
4081
|
const problems = [];
|
|
3781
|
-
if (!
|
|
4082
|
+
if (!codeReviewTextClean) {
|
|
3782
4083
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
4084
|
+
} else if (!codeReviewPolicyClean) {
|
|
4085
|
+
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`);
|
|
3783
4086
|
} else if (!codeReviewBound) {
|
|
3784
4087
|
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3785
4088
|
} else if (!codeReviewObjectivesBound) {
|
|
3786
4089
|
problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3787
4090
|
} else if (!codeReviewRulingsBound) {
|
|
3788
4091
|
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`);
|
|
4092
|
+
} else if (!codeReviewBinding.briefHash) {
|
|
4093
|
+
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"}`);
|
|
4094
|
+
} else if (!codeReviewBinding.policyDigest) {
|
|
4095
|
+
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}`);
|
|
3789
4096
|
}
|
|
3790
|
-
if (!
|
|
4097
|
+
if (!securityTextClean) {
|
|
3791
4098
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
4099
|
+
} else if (!securityPolicyClean) {
|
|
4100
|
+
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`);
|
|
3792
4101
|
} else if (!securityBound) {
|
|
3793
4102
|
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3794
4103
|
} else if (!securityObjectivesBound) {
|
|
3795
4104
|
problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3796
4105
|
} else if (!securityRulingsBound) {
|
|
3797
4106
|
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`);
|
|
4107
|
+
} else if (!securityBinding.briefHash) {
|
|
4108
|
+
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"}`);
|
|
4109
|
+
} else if (!securityBinding.policyDigest) {
|
|
4110
|
+
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}`);
|
|
3798
4111
|
}
|
|
3799
4112
|
if (!mechanicalChecksClean) {
|
|
3800
4113
|
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(", ")}`);
|
|
@@ -4486,42 +4799,45 @@ function checkDispatchReadiness(input) {
|
|
|
4486
4799
|
const { trancheSlug, task } = input;
|
|
4487
4800
|
const taskLabel = `task ${task.id} (tranche ${trancheSlug})`;
|
|
4488
4801
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
4489
|
-
const
|
|
4802
|
+
const blockerDetails = [];
|
|
4803
|
+
const push = (blockerClass, message) => {
|
|
4804
|
+
blockerDetails.push({ class: blockerClass, message });
|
|
4805
|
+
};
|
|
4490
4806
|
if (task.issue === null) {
|
|
4491
|
-
|
|
4807
|
+
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.`);
|
|
4492
4808
|
} else if (input.issue === null) {
|
|
4493
|
-
|
|
4809
|
+
push("issue-existence", `dispatch-gate issue-existence: ${taskLabel} names Issue #${task.issue}, but it does not resolve to a real GitHub Issue (phantom reference).`);
|
|
4494
4810
|
}
|
|
4495
4811
|
if (input.issue !== null && !input.issueRationalePass) {
|
|
4496
|
-
|
|
4812
|
+
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.`);
|
|
4497
4813
|
}
|
|
4498
4814
|
for (const dep of input.dependsOn) {
|
|
4499
4815
|
if (isSelfDependency(dep, task, input.issue)) {
|
|
4500
4816
|
const issueStr = input.issue !== null ? `#${input.issue.number}` : "?";
|
|
4501
|
-
|
|
4817
|
+
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.`);
|
|
4502
4818
|
continue;
|
|
4503
4819
|
}
|
|
4504
4820
|
if (dep.resolved === false) {
|
|
4505
|
-
|
|
4821
|
+
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.`);
|
|
4506
4822
|
continue;
|
|
4507
4823
|
}
|
|
4508
4824
|
if (!dep.merged && !isHandClosedByRecognizedPrincipal(dep, principalAllowlist)) {
|
|
4509
4825
|
const issueStr = dep.issue !== null ? ` (#${dep.issue})` : "";
|
|
4510
|
-
|
|
4826
|
+
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.`);
|
|
4511
4827
|
}
|
|
4512
4828
|
}
|
|
4513
4829
|
for (const c of input.conflictsWith) {
|
|
4514
4830
|
if (c.openOrInFlight) {
|
|
4515
4831
|
const issueStr = c.issue !== null ? ` (#${c.issue})` : "";
|
|
4516
|
-
|
|
4832
|
+
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.`);
|
|
4517
4833
|
}
|
|
4518
4834
|
}
|
|
4519
4835
|
for (const proj of input.priorTrancheArchival) {
|
|
4520
4836
|
if (proj.priorTrancheSlug !== null && !proj.archived) {
|
|
4521
|
-
|
|
4837
|
+
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.`);
|
|
4522
4838
|
}
|
|
4523
4839
|
}
|
|
4524
|
-
return { ready:
|
|
4840
|
+
return { ready: blockerDetails.length === 0, blockers: blockerDetails.map((b) => b.message), blockerDetails };
|
|
4525
4841
|
}
|
|
4526
4842
|
// ../../packages/aeg-core/src/single-plan-pr.ts
|
|
4527
4843
|
function trancheSlugFromTopologyPath(path) {
|
|
@@ -4623,6 +4939,91 @@ function checkBranchTopology(input) {
|
|
|
4623
4939
|
reason: `Branch \`${branch}\` matches topology row \`${taskId}\` in ${topoPath}.`
|
|
4624
4940
|
};
|
|
4625
4941
|
}
|
|
4942
|
+
// ../../packages/aeg-core/src/task-tools.ts
|
|
4943
|
+
import { z } from "zod";
|
|
4944
|
+
var TASK_TOOL_ERROR_KINDS = [
|
|
4945
|
+
"validation",
|
|
4946
|
+
"authority",
|
|
4947
|
+
"precondition",
|
|
4948
|
+
"capability",
|
|
4949
|
+
"infrastructure",
|
|
4950
|
+
"cancellation",
|
|
4951
|
+
"timeout",
|
|
4952
|
+
"uncertain_effect"
|
|
4953
|
+
];
|
|
4954
|
+
var TaskToolErrorSchema = z.object({
|
|
4955
|
+
kind: z.enum(TASK_TOOL_ERROR_KINDS),
|
|
4956
|
+
message: z.string().min(1),
|
|
4957
|
+
detail: z.string().optional()
|
|
4958
|
+
});
|
|
4959
|
+
var TaskToolRefSchema = z.union([
|
|
4960
|
+
z.object({ tranche: z.string().min(1), id: z.string().min(1) }),
|
|
4961
|
+
z.object({ issue: z.number().int().positive() })
|
|
4962
|
+
]);
|
|
4963
|
+
var MAX_PAGE_LIMIT = 100;
|
|
4964
|
+
var PageRequestSchema = z.object({
|
|
4965
|
+
cursor: z.string().optional(),
|
|
4966
|
+
limit: z.number().int().positive().max(MAX_PAGE_LIMIT).optional()
|
|
4967
|
+
});
|
|
4968
|
+
var FreshnessSchema = z.enum(["fresh", "stale", "unknown"]);
|
|
4969
|
+
var ObservedSchema = z.object({
|
|
4970
|
+
observedAt: z.string(),
|
|
4971
|
+
freshness: FreshnessSchema
|
|
4972
|
+
});
|
|
4973
|
+
var TaskStatusInputSchema = z.object({
|
|
4974
|
+
task: TaskToolRefSchema.optional()
|
|
4975
|
+
}).merge(PageRequestSchema);
|
|
4976
|
+
var TaskStatusItemSchema = z.object({
|
|
4977
|
+
task: TaskToolRefSchema,
|
|
4978
|
+
issue: z.number().int().positive(),
|
|
4979
|
+
pr: z.number().int().positive().nullable(),
|
|
4980
|
+
state: z.string()
|
|
4981
|
+
}).merge(ObservedSchema);
|
|
4982
|
+
var TaskStatusResultSchema = z.object({
|
|
4983
|
+
items: z.array(TaskStatusItemSchema),
|
|
4984
|
+
nextCursor: z.string().nullable()
|
|
4985
|
+
});
|
|
4986
|
+
var RequestedAuthoritySchema = z.enum(["planner", "principal", "operator", "self"]);
|
|
4987
|
+
var EscalationInputsSchema = z.object({
|
|
4988
|
+
task: z.number().int().positive(),
|
|
4989
|
+
round: z.number().int().nonnegative(),
|
|
4990
|
+
head: z.string(),
|
|
4991
|
+
branch: z.string(),
|
|
4992
|
+
prNumber: z.number().int().positive()
|
|
4993
|
+
});
|
|
4994
|
+
var EscalationEvidenceSchema = z.object({
|
|
4995
|
+
round: z.number().int().nonnegative(),
|
|
4996
|
+
reviewer: z.string().nullable(),
|
|
4997
|
+
security: z.string().nullable()
|
|
4998
|
+
});
|
|
4999
|
+
var TaskEscalationReadInputSchema = z.object({
|
|
5000
|
+
task: TaskToolRefSchema
|
|
5001
|
+
}).merge(PageRequestSchema);
|
|
5002
|
+
var TaskEscalationPacketSchema = z.object({
|
|
5003
|
+
reason: z.string(),
|
|
5004
|
+
detail: z.string().nullable(),
|
|
5005
|
+
inputs: EscalationInputsSchema.nullable(),
|
|
5006
|
+
evidence: EscalationEvidenceSchema.nullable(),
|
|
5007
|
+
attemptedRecovery: z.string(),
|
|
5008
|
+
requestedAuthority: RequestedAuthoritySchema,
|
|
5009
|
+
permittedNextActions: z.array(z.string())
|
|
5010
|
+
}).merge(ObservedSchema);
|
|
5011
|
+
var TaskEscalationReadResultSchema = z.object({
|
|
5012
|
+
items: z.array(TaskEscalationPacketSchema),
|
|
5013
|
+
nextCursor: z.string().nullable()
|
|
5014
|
+
}).merge(ObservedSchema);
|
|
5015
|
+
var TaskStartInputSchema = z.object({
|
|
5016
|
+
tranche: z.string().min(1),
|
|
5017
|
+
id: z.string().min(1)
|
|
5018
|
+
});
|
|
5019
|
+
var TaskResumeInputSchema = z.object({
|
|
5020
|
+
task: TaskToolRefSchema
|
|
5021
|
+
});
|
|
5022
|
+
var TaskCancelInputSchema = z.object({
|
|
5023
|
+
task: TaskToolRefSchema,
|
|
5024
|
+
reason: z.string().min(1)
|
|
5025
|
+
});
|
|
5026
|
+
var NoResultSchema = z.never();
|
|
4626
5027
|
// ../../packages/aeg-core/src/first-push-dispatch-gate.ts
|
|
4627
5028
|
function parseTaskBranch(branch) {
|
|
4628
5029
|
const m = /^task\/([^/]+)\/([^/]+)$/.exec(branch);
|
|
@@ -4706,7 +5107,6 @@ function decideIssueAssignment(input) {
|
|
|
4706
5107
|
};
|
|
4707
5108
|
}
|
|
4708
5109
|
// ../../packages/aeg-core/src/test-plan-gate.ts
|
|
4709
|
-
var TASK_BRANCH_PATTERN2 = /^task\/[^/]+\/[^/]+$/;
|
|
4710
5110
|
function evaluateTestPlanGate(body, branch) {
|
|
4711
5111
|
if (!body) {
|
|
4712
5112
|
return {
|
|
@@ -4719,7 +5119,7 @@ function evaluateTestPlanGate(body, branch) {
|
|
|
4719
5119
|
}
|
|
4720
5120
|
const located = locateTestPlanSection(body);
|
|
4721
5121
|
if (!located.found) {
|
|
4722
|
-
if (
|
|
5122
|
+
if (parseTaskBranchIdentity(branch) !== null) {
|
|
4723
5123
|
return {
|
|
4724
5124
|
verdict: "fail",
|
|
4725
5125
|
messages: [
|
|
@@ -4847,7 +5247,7 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
|
|
|
4847
5247
|
return findings;
|
|
4848
5248
|
}
|
|
4849
5249
|
// ../../packages/aeg-core/src/log/schema.ts
|
|
4850
|
-
import { z } from "zod";
|
|
5250
|
+
import { z as z2 } from "zod";
|
|
4851
5251
|
var ROLE_VALUES = [
|
|
4852
5252
|
"planner",
|
|
4853
5253
|
"developer",
|
|
@@ -4857,136 +5257,172 @@ var ROLE_VALUES = [
|
|
|
4857
5257
|
"archivist",
|
|
4858
5258
|
"architect"
|
|
4859
5259
|
];
|
|
4860
|
-
var RoleSchema =
|
|
5260
|
+
var RoleSchema = z2.enum(ROLE_VALUES);
|
|
4861
5261
|
var HOST_VALUES = ["hook", "ci", "cli", "loop"];
|
|
4862
|
-
var HostSchema =
|
|
5262
|
+
var HostSchema = z2.enum(HOST_VALUES);
|
|
4863
5263
|
var RUN_ID_PATTERN = /^[A-Za-z0-9_.-]{1,128}$/;
|
|
4864
|
-
var
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
doctrine: z.string(),
|
|
5264
|
+
var headerMetaCore = {
|
|
5265
|
+
ts: z2.string(),
|
|
5266
|
+
run_id: z2.string().regex(RUN_ID_PATTERN),
|
|
5267
|
+
seq: z2.number().int().nonnegative(),
|
|
5268
|
+
repo: z2.string().nullable(),
|
|
5269
|
+
vinaya: z2.string(),
|
|
5270
|
+
doctrine: z2.string(),
|
|
4872
5271
|
host: HostSchema,
|
|
4873
|
-
machine:
|
|
5272
|
+
machine: z2.string()
|
|
5273
|
+
};
|
|
5274
|
+
var HeaderMetaV1Schema = z2.object({
|
|
5275
|
+
schema: z2.literal(1),
|
|
5276
|
+
...headerMetaCore
|
|
4874
5277
|
}).strict();
|
|
4875
|
-
var
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
role: z.union([RoleSchema, z.literal("unattributed")]),
|
|
4880
|
-
round: z.number().int().optional(),
|
|
4881
|
-
objectives_version: z.string().optional()
|
|
5278
|
+
var LineageSchema = z2.object({
|
|
5279
|
+
run: z2.string().nullable(),
|
|
5280
|
+
attempt: z2.number().int().nullable(),
|
|
5281
|
+
parent: z2.string().nullable()
|
|
4882
5282
|
}).strict();
|
|
4883
|
-
var
|
|
5283
|
+
var InputVersionsSchema = z2.object({
|
|
5284
|
+
objectives_version: z2.string().nullable(),
|
|
5285
|
+
brief_hash: z2.string().nullable(),
|
|
5286
|
+
ruling_ordinal: z2.number().int().nullable(),
|
|
5287
|
+
policy_digest: z2.string().nullable()
|
|
5288
|
+
}).strict();
|
|
5289
|
+
var ProvenanceSchema = z2.enum(["parent_attributed", "env_correlated", "self_reported", "unavailable"]);
|
|
5290
|
+
var HeaderMetaV2Schema = z2.object({
|
|
5291
|
+
schema: z2.literal(2),
|
|
5292
|
+
...headerMetaCore,
|
|
5293
|
+
event_id: z2.string().min(1),
|
|
5294
|
+
process_id: z2.string().min(1),
|
|
5295
|
+
actor_id: z2.string().nullable(),
|
|
5296
|
+
lineage: LineageSchema,
|
|
5297
|
+
input_versions: InputVersionsSchema,
|
|
5298
|
+
provenance: ProvenanceSchema
|
|
5299
|
+
}).strict();
|
|
5300
|
+
var HeaderMetaSchema = z2.discriminatedUnion("schema", [HeaderMetaV1Schema, HeaderMetaV2Schema]);
|
|
5301
|
+
var SubjectSchema = z2.object({
|
|
5302
|
+
issue: z2.number().int().nullable(),
|
|
5303
|
+
pr: z2.number().int().optional(),
|
|
5304
|
+
sha: z2.string().optional(),
|
|
5305
|
+
role: z2.union([RoleSchema, z2.literal("unattributed")]),
|
|
5306
|
+
round: z2.number().int().optional(),
|
|
5307
|
+
objectives_version: z2.string().optional()
|
|
5308
|
+
}).strict();
|
|
5309
|
+
var HeaderSchema = z2.object({
|
|
4884
5310
|
meta: HeaderMetaSchema,
|
|
4885
5311
|
subject: SubjectSchema
|
|
4886
5312
|
}).strict();
|
|
4887
5313
|
var envelopeTail = {
|
|
4888
|
-
duration_ms:
|
|
4889
|
-
payload:
|
|
5314
|
+
duration_ms: z2.number().nonnegative().optional(),
|
|
5315
|
+
payload: z2.object({}).strict()
|
|
4890
5316
|
};
|
|
4891
|
-
var
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
5317
|
+
var ReviewFindingSchema = z2.object({
|
|
5318
|
+
id: z2.string(),
|
|
5319
|
+
severity: z2.string(),
|
|
5320
|
+
state: z2.string().optional(),
|
|
5321
|
+
severity_scale: z2.string().optional(),
|
|
5322
|
+
policy_treatment: z2.enum(["blocking", "non_blocking", "unavailable"]).optional(),
|
|
5323
|
+
confidence: z2.number().min(0).max(1).optional(),
|
|
5324
|
+
confidence_scale: z2.string().optional(),
|
|
5325
|
+
confidence_source: z2.string().optional()
|
|
5326
|
+
}).strict();
|
|
5327
|
+
var DispatchOutcomeSchema = z2.discriminatedUnion("type", [
|
|
5328
|
+
z2.object({ type: z2.literal("pr_opened"), pr: z2.number().int(), head: z2.string() }).strict(),
|
|
5329
|
+
z2.object({
|
|
5330
|
+
type: z2.literal("round_pushed"),
|
|
5331
|
+
pr: z2.number().int(),
|
|
5332
|
+
head: z2.string(),
|
|
5333
|
+
comment_id: z2.number().int()
|
|
4898
5334
|
}).strict(),
|
|
4899
|
-
|
|
4900
|
-
type:
|
|
4901
|
-
verdict:
|
|
4902
|
-
head:
|
|
4903
|
-
comment_id:
|
|
4904
|
-
objectives:
|
|
4905
|
-
findings:
|
|
5335
|
+
z2.object({
|
|
5336
|
+
type: z2.literal("verdict"),
|
|
5337
|
+
verdict: z2.enum(["APPROVE", "REQUEST CHANGES", "PASS", "FAIL"]),
|
|
5338
|
+
head: z2.string(),
|
|
5339
|
+
comment_id: z2.number().int(),
|
|
5340
|
+
objectives: z2.array(z2.object({ id: z2.string(), met: z2.boolean() }).strict()),
|
|
5341
|
+
findings: z2.array(ReviewFindingSchema)
|
|
4906
5342
|
}).strict(),
|
|
4907
|
-
|
|
4908
|
-
type:
|
|
4909
|
-
class:
|
|
4910
|
-
comment_id:
|
|
5343
|
+
z2.object({
|
|
5344
|
+
type: z2.literal("escalation"),
|
|
5345
|
+
class: z2.enum(["authority", "strategy", "product"]),
|
|
5346
|
+
comment_id: z2.number().int()
|
|
4911
5347
|
}).strict(),
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
5348
|
+
z2.object({ type: z2.literal("brief"), comment_id: z2.number().int(), hash: z2.string() }).strict(),
|
|
5349
|
+
z2.object({ type: z2.literal("plan"), issues: z2.array(z2.number().int()) }).strict(),
|
|
5350
|
+
z2.object({ type: z2.literal("archive"), provenance_comment_id: z2.number().int() }).strict()
|
|
4915
5351
|
]);
|
|
4916
5352
|
var dispatchShared = {
|
|
4917
5353
|
meta: HeaderMetaSchema,
|
|
4918
5354
|
subject: SubjectSchema,
|
|
4919
|
-
kind:
|
|
5355
|
+
kind: z2.literal("dispatch"),
|
|
4920
5356
|
...envelopeTail,
|
|
4921
5357
|
target_role: RoleSchema,
|
|
4922
|
-
model:
|
|
4923
|
-
round:
|
|
4924
|
-
effect_id:
|
|
5358
|
+
model: z2.string(),
|
|
5359
|
+
round: z2.number().int().optional(),
|
|
5360
|
+
effect_id: z2.string()
|
|
4925
5361
|
};
|
|
4926
|
-
var dispatchUsageField =
|
|
4927
|
-
var DispatchEventSchema =
|
|
4928
|
-
|
|
4929
|
-
|
|
5362
|
+
var dispatchUsageField = z2.object({ input: z2.number().nonnegative(), output: z2.number().nonnegative() }).strict().nullable();
|
|
5363
|
+
var DispatchEventSchema = z2.discriminatedUnion("event", [
|
|
5364
|
+
z2.object({ ...dispatchShared, event: z2.literal("dispatched"), prompt_hash: z2.string() }).strict(),
|
|
5365
|
+
z2.object({
|
|
4930
5366
|
...dispatchShared,
|
|
4931
|
-
event:
|
|
5367
|
+
event: z2.literal("outcome_received"),
|
|
4932
5368
|
outcome: DispatchOutcomeSchema,
|
|
4933
5369
|
usage: dispatchUsageField
|
|
4934
5370
|
}).strict(),
|
|
4935
|
-
|
|
5371
|
+
z2.object({
|
|
4936
5372
|
...dispatchShared,
|
|
4937
|
-
event:
|
|
4938
|
-
reason:
|
|
5373
|
+
event: z2.literal("dispatch_failed"),
|
|
5374
|
+
reason: z2.enum(["timeout", "crash", "refused", "unattributed_write"]),
|
|
4939
5375
|
usage: dispatchUsageField
|
|
4940
5376
|
}).strict()
|
|
4941
5377
|
]);
|
|
4942
5378
|
var loopShared = {
|
|
4943
5379
|
meta: HeaderMetaSchema,
|
|
4944
5380
|
subject: SubjectSchema,
|
|
4945
|
-
kind:
|
|
5381
|
+
kind: z2.literal("dev_review_loop"),
|
|
4946
5382
|
...envelopeTail,
|
|
4947
|
-
loop_id:
|
|
5383
|
+
loop_id: z2.string()
|
|
4948
5384
|
};
|
|
4949
|
-
var DevReviewLoopEventSchema =
|
|
4950
|
-
|
|
5385
|
+
var DevReviewLoopEventSchema = z2.discriminatedUnion("event", [
|
|
5386
|
+
z2.object({
|
|
4951
5387
|
...loopShared,
|
|
4952
|
-
event:
|
|
4953
|
-
task:
|
|
4954
|
-
policy:
|
|
4955
|
-
max_rounds:
|
|
4956
|
-
reviewers:
|
|
4957
|
-
models:
|
|
5388
|
+
event: z2.literal("loop_started"),
|
|
5389
|
+
task: z2.number().int(),
|
|
5390
|
+
policy: z2.object({
|
|
5391
|
+
max_rounds: z2.number().int().nonnegative(),
|
|
5392
|
+
reviewers: z2.array(RoleSchema),
|
|
5393
|
+
models: z2.record(RoleSchema, z2.string())
|
|
4958
5394
|
}).strict()
|
|
4959
5395
|
}).strict(),
|
|
4960
|
-
|
|
4961
|
-
|
|
5396
|
+
z2.object({ ...loopShared, event: z2.literal("round_started"), round: z2.number().int(), base_head: z2.string() }).strict(),
|
|
5397
|
+
z2.object({
|
|
4962
5398
|
...loopShared,
|
|
4963
|
-
event:
|
|
4964
|
-
round:
|
|
4965
|
-
head:
|
|
4966
|
-
green:
|
|
5399
|
+
event: z2.literal("gate_result_read"),
|
|
5400
|
+
round: z2.number().int(),
|
|
5401
|
+
head: z2.string(),
|
|
5402
|
+
green: z2.boolean()
|
|
4967
5403
|
}).strict(),
|
|
4968
|
-
|
|
5404
|
+
z2.object({
|
|
4969
5405
|
...loopShared,
|
|
4970
|
-
event:
|
|
4971
|
-
round:
|
|
4972
|
-
head:
|
|
4973
|
-
all_approve:
|
|
4974
|
-
blockers:
|
|
5406
|
+
event: z2.literal("verdicts_read"),
|
|
5407
|
+
round: z2.number().int(),
|
|
5408
|
+
head: z2.string(),
|
|
5409
|
+
all_approve: z2.boolean(),
|
|
5410
|
+
blockers: z2.number().int().nonnegative()
|
|
4975
5411
|
}).strict(),
|
|
4976
|
-
|
|
5412
|
+
z2.object({
|
|
4977
5413
|
...loopShared,
|
|
4978
|
-
event:
|
|
4979
|
-
round:
|
|
4980
|
-
open:
|
|
4981
|
-
resolved:
|
|
4982
|
-
new:
|
|
4983
|
-
recurring:
|
|
5414
|
+
event: z2.literal("findings_compared"),
|
|
5415
|
+
round: z2.number().int(),
|
|
5416
|
+
open: z2.array(z2.string()),
|
|
5417
|
+
resolved: z2.array(z2.string()),
|
|
5418
|
+
new: z2.array(z2.string()),
|
|
5419
|
+
recurring: z2.array(z2.string())
|
|
4984
5420
|
}).strict(),
|
|
4985
|
-
|
|
5421
|
+
z2.object({
|
|
4986
5422
|
...loopShared,
|
|
4987
|
-
event:
|
|
4988
|
-
round:
|
|
4989
|
-
condition:
|
|
5423
|
+
event: z2.literal("stop_condition_met"),
|
|
5424
|
+
round: z2.number().int(),
|
|
5425
|
+
condition: z2.enum([
|
|
4990
5426
|
"green",
|
|
4991
5427
|
"max_rounds",
|
|
4992
5428
|
"no_progress",
|
|
@@ -4996,42 +5432,49 @@ var DevReviewLoopEventSchema = z.discriminatedUnion("event", [
|
|
|
4996
5432
|
"reappearance"
|
|
4997
5433
|
])
|
|
4998
5434
|
}).strict(),
|
|
4999
|
-
|
|
5435
|
+
z2.object({
|
|
5000
5436
|
...loopShared,
|
|
5001
|
-
event:
|
|
5002
|
-
round:
|
|
5003
|
-
reason:
|
|
5437
|
+
event: z2.literal("paused"),
|
|
5438
|
+
round: z2.number().int(),
|
|
5439
|
+
reason: z2.enum(["escalation", "principal_item", "refreeze_needed"])
|
|
5004
5440
|
}).strict(),
|
|
5005
|
-
|
|
5441
|
+
z2.object({
|
|
5006
5442
|
...loopShared,
|
|
5007
|
-
event:
|
|
5008
|
-
round:
|
|
5009
|
-
by:
|
|
5443
|
+
event: z2.literal("resumed"),
|
|
5444
|
+
round: z2.number().int(),
|
|
5445
|
+
by: z2.literal("principal")
|
|
5010
5446
|
}).strict(),
|
|
5011
|
-
|
|
5447
|
+
z2.object({
|
|
5012
5448
|
...loopShared,
|
|
5013
|
-
event:
|
|
5014
|
-
round:
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
files_changed: z.number().int().nonnegative(),
|
|
5018
|
-
insertions: z.number().int().nonnegative(),
|
|
5019
|
-
deletions: z.number().int().nonnegative(),
|
|
5020
|
-
wall_ms: z.number().nonnegative(),
|
|
5021
|
-
outcome: z.enum(["green", "changes_requested", "escalated"])
|
|
5449
|
+
event: z2.literal("unpushed_work_resume"),
|
|
5450
|
+
round: z2.number().int(),
|
|
5451
|
+
branch: z2.string(),
|
|
5452
|
+
detail: z2.string()
|
|
5022
5453
|
}).strict(),
|
|
5023
|
-
|
|
5454
|
+
z2.object({
|
|
5024
5455
|
...loopShared,
|
|
5025
|
-
event:
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5456
|
+
event: z2.literal("round_ended"),
|
|
5457
|
+
round: z2.number().int(),
|
|
5458
|
+
base_head: z2.string(),
|
|
5459
|
+
head: z2.string(),
|
|
5460
|
+
files_changed: z2.number().int().nonnegative(),
|
|
5461
|
+
insertions: z2.number().int().nonnegative(),
|
|
5462
|
+
deletions: z2.number().int().nonnegative(),
|
|
5463
|
+
wall_ms: z2.number().nonnegative(),
|
|
5464
|
+
outcome: z2.enum(["green", "changes_requested", "escalated"])
|
|
5465
|
+
}).strict(),
|
|
5466
|
+
z2.object({
|
|
5467
|
+
...loopShared,
|
|
5468
|
+
event: z2.literal("journal_finalized"),
|
|
5469
|
+
rounds: z2.number().int().nonnegative(),
|
|
5470
|
+
total_wall_ms: z2.number().nonnegative(),
|
|
5471
|
+
time_to_green_ms: z2.number().nonnegative().nullable(),
|
|
5472
|
+
files_changed_total: z2.number().int().nonnegative(),
|
|
5473
|
+
final_head: z2.string(),
|
|
5474
|
+
result: z2.enum(["merged_ready", "stopped"])
|
|
5032
5475
|
}).strict()
|
|
5033
5476
|
]);
|
|
5034
|
-
var ForgeOpSchema =
|
|
5477
|
+
var ForgeOpSchema = z2.enum([
|
|
5035
5478
|
"pr.create",
|
|
5036
5479
|
"pr.comment",
|
|
5037
5480
|
"pr.body.replace",
|
|
@@ -5045,24 +5488,210 @@ var ForgeOpSchema = z.enum([
|
|
|
5045
5488
|
"label.add",
|
|
5046
5489
|
"label.remove"
|
|
5047
5490
|
]);
|
|
5048
|
-
var ForgeWriteTargetSchema =
|
|
5049
|
-
issue:
|
|
5050
|
-
pr:
|
|
5491
|
+
var ForgeWriteTargetSchema = z2.object({
|
|
5492
|
+
issue: z2.number().int().optional(),
|
|
5493
|
+
pr: z2.number().int().optional()
|
|
5051
5494
|
}).strict();
|
|
5052
5495
|
var forgeWriteShared = {
|
|
5053
5496
|
meta: HeaderMetaSchema,
|
|
5054
5497
|
subject: SubjectSchema,
|
|
5055
|
-
kind:
|
|
5498
|
+
kind: z2.literal("forge_write"),
|
|
5056
5499
|
...envelopeTail,
|
|
5057
5500
|
op: ForgeOpSchema,
|
|
5058
5501
|
target: ForgeWriteTargetSchema
|
|
5059
5502
|
};
|
|
5060
|
-
var ForgeWriteEventSchema =
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5503
|
+
var ForgeWriteEventSchema = z2.discriminatedUnion("event", [
|
|
5504
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("validated") }).strict(),
|
|
5505
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("refused"), reason: z2.string() }).strict(),
|
|
5506
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("written"), comment_ids: z2.array(z2.string()) }).strict()
|
|
5507
|
+
]);
|
|
5508
|
+
var GateOutcomeSchema = z2.enum([
|
|
5509
|
+
"pass",
|
|
5510
|
+
"fail",
|
|
5511
|
+
"wait",
|
|
5512
|
+
"skip",
|
|
5513
|
+
"invalid_input",
|
|
5514
|
+
"unavailable_dependency",
|
|
5515
|
+
"timeout",
|
|
5516
|
+
"cancelled"
|
|
5517
|
+
]);
|
|
5518
|
+
var gateShared = {
|
|
5519
|
+
meta: HeaderMetaSchema,
|
|
5520
|
+
subject: SubjectSchema,
|
|
5521
|
+
kind: z2.literal("gate"),
|
|
5522
|
+
...envelopeTail,
|
|
5523
|
+
check: z2.string(),
|
|
5524
|
+
check_version: z2.string().nullable(),
|
|
5525
|
+
policy_version: z2.string().nullable(),
|
|
5526
|
+
input_fingerprint: z2.string().nullable()
|
|
5527
|
+
};
|
|
5528
|
+
var GateEventSchema = z2.discriminatedUnion("event", [
|
|
5529
|
+
z2.object({
|
|
5530
|
+
...gateShared,
|
|
5531
|
+
event: z2.literal("checked"),
|
|
5532
|
+
outcome: GateOutcomeSchema,
|
|
5533
|
+
reason: z2.string().optional()
|
|
5534
|
+
}).strict()
|
|
5535
|
+
]);
|
|
5536
|
+
var OperationResultSchema = z2.enum(["ok", "error", "refused", "timeout", "cancelled", "unavailable"]);
|
|
5537
|
+
var operationShared = {
|
|
5538
|
+
meta: HeaderMetaSchema,
|
|
5539
|
+
subject: SubjectSchema,
|
|
5540
|
+
kind: z2.literal("operation"),
|
|
5541
|
+
...envelopeTail,
|
|
5542
|
+
operation: z2.string(),
|
|
5543
|
+
target: z2.string().nullable()
|
|
5544
|
+
};
|
|
5545
|
+
var OperationEventSchema = z2.discriminatedUnion("event", [
|
|
5546
|
+
z2.object({
|
|
5547
|
+
...operationShared,
|
|
5548
|
+
event: z2.literal("completed"),
|
|
5549
|
+
result: OperationResultSchema,
|
|
5550
|
+
error_class: z2.string().nullable()
|
|
5551
|
+
}).strict()
|
|
5552
|
+
]);
|
|
5553
|
+
var UsageUnitsSchema = z2.object({
|
|
5554
|
+
input: z2.number().nonnegative().nullable(),
|
|
5555
|
+
output: z2.number().nonnegative().nullable(),
|
|
5556
|
+
cache: z2.number().nonnegative().nullable()
|
|
5557
|
+
}).strict();
|
|
5558
|
+
var usageShared = {
|
|
5559
|
+
meta: HeaderMetaSchema,
|
|
5560
|
+
subject: SubjectSchema,
|
|
5561
|
+
kind: z2.literal("usage"),
|
|
5562
|
+
...envelopeTail,
|
|
5563
|
+
model: z2.string().nullable(),
|
|
5564
|
+
source: z2.string(),
|
|
5565
|
+
semantics: z2.enum(["cumulative", "delta"])
|
|
5566
|
+
};
|
|
5567
|
+
var UsageEventSchema = z2.discriminatedUnion("event", [
|
|
5568
|
+
z2.object({
|
|
5569
|
+
...usageShared,
|
|
5570
|
+
event: z2.literal("observed"),
|
|
5571
|
+
units: UsageUnitsSchema,
|
|
5572
|
+
unknown_reason: z2.string().nullable()
|
|
5573
|
+
}).strict()
|
|
5574
|
+
]);
|
|
5575
|
+
var RoleAttemptOutcomeSchema = z2.enum([
|
|
5576
|
+
"completed",
|
|
5577
|
+
"incomplete",
|
|
5578
|
+
"infrastructure_failed",
|
|
5579
|
+
"cancelled",
|
|
5580
|
+
"timed_out",
|
|
5581
|
+
"capability_refused"
|
|
5582
|
+
]);
|
|
5583
|
+
var roleAttemptShared = {
|
|
5584
|
+
meta: HeaderMetaSchema,
|
|
5585
|
+
subject: SubjectSchema,
|
|
5586
|
+
kind: z2.literal("role_attempt"),
|
|
5587
|
+
...envelopeTail,
|
|
5588
|
+
actor: z2.string().nullable(),
|
|
5589
|
+
attempt: z2.number().int().nullable()
|
|
5590
|
+
};
|
|
5591
|
+
var RoleAttemptEventSchema = z2.discriminatedUnion("event", [
|
|
5592
|
+
z2.object({
|
|
5593
|
+
...roleAttemptShared,
|
|
5594
|
+
event: z2.literal("attempted"),
|
|
5595
|
+
outcome: RoleAttemptOutcomeSchema,
|
|
5596
|
+
usage: dispatchUsageField
|
|
5597
|
+
}).strict()
|
|
5598
|
+
]);
|
|
5599
|
+
var handoffShared = {
|
|
5600
|
+
meta: HeaderMetaSchema,
|
|
5601
|
+
subject: SubjectSchema,
|
|
5602
|
+
kind: z2.literal("handoff"),
|
|
5603
|
+
...envelopeTail,
|
|
5604
|
+
class: z2.enum(["authority", "strategy", "product"]),
|
|
5605
|
+
reason: z2.string()
|
|
5606
|
+
};
|
|
5607
|
+
var HandoffEventSchema = z2.discriminatedUnion("event", [
|
|
5608
|
+
z2.object({
|
|
5609
|
+
...handoffShared,
|
|
5610
|
+
event: z2.literal("raised"),
|
|
5611
|
+
requested_decision: z2.string().nullable()
|
|
5612
|
+
}).strict(),
|
|
5613
|
+
z2.object({
|
|
5614
|
+
...handoffShared,
|
|
5615
|
+
event: z2.literal("resolved"),
|
|
5616
|
+
resolution: z2.string().nullable(),
|
|
5617
|
+
resolved_by: z2.string().nullable()
|
|
5618
|
+
}).strict()
|
|
5619
|
+
]);
|
|
5620
|
+
var EffectTargetSchema = z2.object({
|
|
5621
|
+
kind: z2.string(),
|
|
5622
|
+
ref: z2.string()
|
|
5623
|
+
}).strict();
|
|
5624
|
+
var effectShared = {
|
|
5625
|
+
meta: HeaderMetaSchema,
|
|
5626
|
+
subject: SubjectSchema,
|
|
5627
|
+
kind: z2.literal("effect"),
|
|
5628
|
+
...envelopeTail,
|
|
5629
|
+
effect_id: z2.string(),
|
|
5630
|
+
target: EffectTargetSchema
|
|
5631
|
+
};
|
|
5632
|
+
var EffectEventSchema = z2.discriminatedUnion("event", [
|
|
5633
|
+
z2.object({ ...effectShared, event: z2.literal("attempted") }).strict(),
|
|
5634
|
+
z2.object({ ...effectShared, event: z2.literal("observed"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict(),
|
|
5635
|
+
z2.object({ ...effectShared, event: z2.literal("verified"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict()
|
|
5636
|
+
]);
|
|
5637
|
+
var LogEventSchema = z2.union([
|
|
5638
|
+
DispatchEventSchema,
|
|
5639
|
+
DevReviewLoopEventSchema,
|
|
5640
|
+
ForgeWriteEventSchema,
|
|
5641
|
+
GateEventSchema,
|
|
5642
|
+
OperationEventSchema,
|
|
5643
|
+
UsageEventSchema,
|
|
5644
|
+
RoleAttemptEventSchema,
|
|
5645
|
+
HandoffEventSchema,
|
|
5646
|
+
EffectEventSchema
|
|
5064
5647
|
]);
|
|
5065
|
-
|
|
5648
|
+
// ../../packages/aeg-core/src/dev-review-loop/journal-reconstruction.ts
|
|
5649
|
+
var STOPPED_CONDITIONS = new Set(["confidence", "reappearance", "no_progress", "max_rounds"]);
|
|
5650
|
+
// ../../packages/aeg-core/src/control-store/records.ts
|
|
5651
|
+
import { z as z3 } from "zod";
|
|
5652
|
+
var isoTimestamp = z3.string().min(1);
|
|
5653
|
+
var taskId = z3.number().int().positive();
|
|
5654
|
+
var epochNumber = z3.number().int().nonnegative();
|
|
5655
|
+
var RunRecordSchema = z3.object({
|
|
5656
|
+
version: z3.literal(1),
|
|
5657
|
+
kind: z3.literal("run"),
|
|
5658
|
+
task: taskId,
|
|
5659
|
+
runId: z3.string().min(1),
|
|
5660
|
+
pid: z3.number().int().positive(),
|
|
5661
|
+
host: z3.string().min(1),
|
|
5662
|
+
startedAt: isoTimestamp
|
|
5663
|
+
}).strict();
|
|
5664
|
+
var InputRecordSchema = z3.object({
|
|
5665
|
+
version: z3.literal(1),
|
|
5666
|
+
kind: z3.literal("input"),
|
|
5667
|
+
task: taskId,
|
|
5668
|
+
runId: z3.string().min(1),
|
|
5669
|
+
source: z3.union([z3.literal("fresh"), z3.literal("resume")]),
|
|
5670
|
+
pr: z3.number().int().positive().nullable(),
|
|
5671
|
+
round: z3.number().int().nonnegative(),
|
|
5672
|
+
recordedAt: isoTimestamp
|
|
5673
|
+
}).strict();
|
|
5674
|
+
var OwnershipRecordSchema = z3.object({
|
|
5675
|
+
version: z3.literal(1),
|
|
5676
|
+
kind: z3.literal("ownership"),
|
|
5677
|
+
task: taskId,
|
|
5678
|
+
epoch: epochNumber,
|
|
5679
|
+
ownerId: z3.string().min(1),
|
|
5680
|
+
pid: z3.number().int().positive(),
|
|
5681
|
+
host: z3.string().min(1),
|
|
5682
|
+
acquiredAt: isoTimestamp
|
|
5683
|
+
}).strict();
|
|
5684
|
+
var TransitionRecordSchema = z3.object({
|
|
5685
|
+
version: z3.literal(1),
|
|
5686
|
+
kind: z3.literal("transition"),
|
|
5687
|
+
task: taskId,
|
|
5688
|
+
epoch: epochNumber,
|
|
5689
|
+
seq: z3.number().int().nonnegative(),
|
|
5690
|
+
from: z3.string().min(1),
|
|
5691
|
+
to: z3.string().min(1),
|
|
5692
|
+
detail: z3.string().optional(),
|
|
5693
|
+
at: isoTimestamp
|
|
5694
|
+
}).strict();
|
|
5066
5695
|
// src/commands/doctrine.ts
|
|
5067
5696
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
5068
5697
|
import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "node:fs";
|
|
@@ -5121,27 +5750,29 @@ import { execFileSync as execFileSync3 } from "node:child_process";
|
|
|
5121
5750
|
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, realpathSync, writeFileSync } from "node:fs";
|
|
5122
5751
|
import { homedir } from "node:os";
|
|
5123
5752
|
import { dirname as dirname3, join as join3, resolve } from "node:path";
|
|
5124
|
-
import { z as
|
|
5753
|
+
import { z as z4 } from "zod";
|
|
5125
5754
|
|
|
5126
5755
|
// src/lib/agent-vendors.ts
|
|
5127
5756
|
var AGENT_VENDORS = ["skills", "claude", "gemini"];
|
|
5128
5757
|
|
|
5129
5758
|
// src/lib/config.ts
|
|
5130
|
-
var EnvEntrySchema =
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5759
|
+
var EnvEntrySchema = z4.union([
|
|
5760
|
+
z4.literal(true),
|
|
5761
|
+
z4.object({ optional: z4.literal(true) }),
|
|
5762
|
+
z4.object({ anyOf: z4.array(z4.string()).min(2) }),
|
|
5763
|
+
z4.string()
|
|
5135
5764
|
]);
|
|
5136
|
-
var CheckEntrySchema =
|
|
5137
|
-
run:
|
|
5138
|
-
scope:
|
|
5139
|
-
include:
|
|
5140
|
-
args:
|
|
5141
|
-
timeoutMs:
|
|
5142
|
-
env:
|
|
5143
|
-
requiresOpenPr:
|
|
5144
|
-
ownWorkflow:
|
|
5765
|
+
var CheckEntrySchema = z4.object({
|
|
5766
|
+
run: z4.string(),
|
|
5767
|
+
scope: z4.enum(["diff", "full"]),
|
|
5768
|
+
include: z4.array(z4.string()).optional(),
|
|
5769
|
+
args: z4.array(z4.string()).optional(),
|
|
5770
|
+
timeoutMs: z4.number().optional(),
|
|
5771
|
+
env: z4.record(z4.string(), EnvEntrySchema).optional(),
|
|
5772
|
+
requiresOpenPr: z4.boolean().optional(),
|
|
5773
|
+
ownWorkflow: z4.boolean().optional(),
|
|
5774
|
+
principalOwed: z4.literal(true).optional(),
|
|
5775
|
+
validates: z4.enum(["body", "issue"]).optional()
|
|
5145
5776
|
}).superRefine((entry2, ctx) => {
|
|
5146
5777
|
if (!entry2.env)
|
|
5147
5778
|
return;
|
|
@@ -5151,25 +5782,26 @@ var CheckEntrySchema = z2.object({
|
|
|
5151
5782
|
const members = value.anyOf;
|
|
5152
5783
|
if (new Set(members).size !== members.length) {
|
|
5153
5784
|
ctx.addIssue({
|
|
5154
|
-
code:
|
|
5785
|
+
code: z4.ZodIssueCode.custom,
|
|
5155
5786
|
message: `env.${key}.anyOf has duplicate members`,
|
|
5156
5787
|
path: ["env", key, "anyOf"]
|
|
5157
5788
|
});
|
|
5158
5789
|
}
|
|
5159
5790
|
if (!members.includes(key)) {
|
|
5160
5791
|
ctx.addIssue({
|
|
5161
|
-
code:
|
|
5792
|
+
code: z4.ZodIssueCode.custom,
|
|
5162
5793
|
message: `env.${key}.anyOf must include "${key}" itself as a member`,
|
|
5163
5794
|
path: ["env", key, "anyOf"]
|
|
5164
5795
|
});
|
|
5165
5796
|
}
|
|
5166
5797
|
}
|
|
5167
5798
|
});
|
|
5168
|
-
var RoleEntrySchema =
|
|
5169
|
-
contract:
|
|
5799
|
+
var RoleEntrySchema = z4.object({
|
|
5800
|
+
contract: z4.string().refine((p) => p.includes("/"), {
|
|
5170
5801
|
message: 'must be a path (contain at least one "/"), not a bare filename'
|
|
5171
5802
|
})
|
|
5172
5803
|
});
|
|
5804
|
+
var MAX_REPORT_COMMAND_TIMEOUT_MS = 3600000;
|
|
5173
5805
|
var BRIEF_BUILTINS = [
|
|
5174
5806
|
"tier",
|
|
5175
5807
|
"testPlan",
|
|
@@ -5189,17 +5821,17 @@ var BRIEF_BUILTINS = [
|
|
|
5189
5821
|
"briefSections",
|
|
5190
5822
|
"milestoneShape"
|
|
5191
5823
|
];
|
|
5192
|
-
var BriefSectionSchema =
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5824
|
+
var BriefSectionSchema = z4.union([
|
|
5825
|
+
z4.object({ builtin: z4.enum(BRIEF_BUILTINS) }),
|
|
5826
|
+
z4.object({ heading: z4.string().min(1), name: z4.string().optional() }),
|
|
5827
|
+
z4.object({ field: z4.string().min(1), name: z4.string().optional() }),
|
|
5828
|
+
z4.object({ phrase: z4.string().min(1), name: z4.string().optional() })
|
|
5197
5829
|
]);
|
|
5198
|
-
var BriefSchemaSchema =
|
|
5199
|
-
pr:
|
|
5200
|
-
issue:
|
|
5201
|
-
milestone:
|
|
5202
|
-
ack:
|
|
5830
|
+
var BriefSchemaSchema = z4.object({
|
|
5831
|
+
pr: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
|
|
5832
|
+
issue: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
|
|
5833
|
+
milestone: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
|
|
5834
|
+
ack: z4.array(z4.enum(BRIEF_BUILTINS)).optional()
|
|
5203
5835
|
});
|
|
5204
5836
|
function isSafeRepoRelPath(p) {
|
|
5205
5837
|
if (p.length === 0)
|
|
@@ -5208,7 +5840,7 @@ function isSafeRepoRelPath(p) {
|
|
|
5208
5840
|
return false;
|
|
5209
5841
|
return p.split(/[\\/]/).every((seg) => seg !== ".." && seg !== "");
|
|
5210
5842
|
}
|
|
5211
|
-
var SafeRepoRelPath =
|
|
5843
|
+
var SafeRepoRelPath = z4.string().refine(isSafeRepoRelPath, {
|
|
5212
5844
|
message: "must be a repo-root-relative path with no `..` segment or absolute root"
|
|
5213
5845
|
});
|
|
5214
5846
|
var CANONICAL_HOOK_BLOCK_PREFIXES = [".git/", ".husky/", ".vinaya/hooks/", ".claude/hooks/"];
|
|
@@ -5218,22 +5850,22 @@ function isCanonicalHookBlockPath(p) {
|
|
|
5218
5850
|
var ManagedHookBlockPath = SafeRepoRelPath.refine(isCanonicalHookBlockPath, {
|
|
5219
5851
|
message: `must start with one of ${CANONICAL_HOOK_BLOCK_PREFIXES.join(", ")} (byte-exact, case-sensitive)`
|
|
5220
5852
|
});
|
|
5221
|
-
var ManagedBlockRecordSchema =
|
|
5853
|
+
var ManagedBlockRecordSchema = z4.object({
|
|
5222
5854
|
path: ManagedHookBlockPath,
|
|
5223
|
-
marker:
|
|
5224
|
-
comment:
|
|
5855
|
+
marker: z4.string(),
|
|
5856
|
+
comment: z4.enum(["hash", "html"])
|
|
5225
5857
|
});
|
|
5226
|
-
var ManagedManifestSchema =
|
|
5227
|
-
version:
|
|
5228
|
-
files:
|
|
5229
|
-
blocks:
|
|
5230
|
-
labels:
|
|
5231
|
-
agents:
|
|
5858
|
+
var ManagedManifestSchema = z4.object({
|
|
5859
|
+
version: z4.number().int().positive(),
|
|
5860
|
+
files: z4.array(SafeRepoRelPath),
|
|
5861
|
+
blocks: z4.array(ManagedBlockRecordSchema),
|
|
5862
|
+
labels: z4.array(z4.string()),
|
|
5863
|
+
agents: z4.array(z4.enum(AGENT_VENDORS)).optional()
|
|
5232
5864
|
});
|
|
5233
|
-
var ProjectEntrySchema =
|
|
5234
|
-
name:
|
|
5235
|
-
description:
|
|
5236
|
-
path:
|
|
5865
|
+
var ProjectEntrySchema = z4.object({
|
|
5866
|
+
name: z4.string().min(1),
|
|
5867
|
+
description: z4.string().min(1).optional(),
|
|
5868
|
+
path: z4.string().min(1).optional()
|
|
5237
5869
|
});
|
|
5238
5870
|
function parseTokensCollectDeclaration(value) {
|
|
5239
5871
|
const m = value.trim().match(/^(\S+)\s+(\S+)$/);
|
|
@@ -5244,34 +5876,55 @@ function parseTokensCollectDeclaration(value) {
|
|
|
5244
5876
|
return null;
|
|
5245
5877
|
return { interpreter, script };
|
|
5246
5878
|
}
|
|
5247
|
-
var VinayaConfigSchema =
|
|
5248
|
-
rings:
|
|
5249
|
-
ring1_forgeWriteInterception:
|
|
5250
|
-
ring2_asyncAudits:
|
|
5879
|
+
var VinayaConfigSchema = z4.object({
|
|
5880
|
+
rings: z4.object({
|
|
5881
|
+
ring1_forgeWriteInterception: z4.boolean(),
|
|
5882
|
+
ring2_asyncAudits: z4.boolean()
|
|
5251
5883
|
}).optional(),
|
|
5252
|
-
checks:
|
|
5253
|
-
roles:
|
|
5884
|
+
checks: z4.record(z4.string(), CheckEntrySchema).optional(),
|
|
5885
|
+
roles: z4.record(z4.string(), RoleEntrySchema).optional(),
|
|
5254
5886
|
briefSchema: BriefSchemaSchema.optional(),
|
|
5255
5887
|
managed: ManagedManifestSchema.optional(),
|
|
5256
|
-
principals:
|
|
5257
|
-
releaseActor:
|
|
5258
|
-
ci:
|
|
5259
|
-
tokens:
|
|
5260
|
-
collect:
|
|
5888
|
+
principals: z4.array(z4.string()).min(1).optional(),
|
|
5889
|
+
releaseActor: z4.string().min(1).optional(),
|
|
5890
|
+
ci: z4.object({ setup: z4.string().min(1) }).optional(),
|
|
5891
|
+
tokens: z4.object({
|
|
5892
|
+
collect: z4.string().min(1).refine((v) => parseTokensCollectDeclaration(v) !== null, {
|
|
5261
5893
|
message: 'tokens.collect must be exactly "<interpreter> <repo-relative-script-path>" — two whitespace-separated tokens, no flags, no shell syntax, no quoting'
|
|
5262
5894
|
})
|
|
5263
5895
|
}).optional(),
|
|
5264
|
-
blastRadius:
|
|
5265
|
-
proseGates:
|
|
5266
|
-
doctrineRoot:
|
|
5267
|
-
readerFacingPrefix:
|
|
5268
|
-
readerFacingSuffix:
|
|
5269
|
-
legacySlugDir:
|
|
5896
|
+
blastRadius: z4.object({ extraDomains: z4.array(z4.string()).optional() }).optional(),
|
|
5897
|
+
proseGates: z4.object({
|
|
5898
|
+
doctrineRoot: z4.string().min(1).optional(),
|
|
5899
|
+
readerFacingPrefix: z4.string().min(1).optional(),
|
|
5900
|
+
readerFacingSuffix: z4.string().min(1).optional(),
|
|
5901
|
+
legacySlugDir: z4.string().min(1).optional()
|
|
5902
|
+
}).optional(),
|
|
5903
|
+
projects: z4.array(ProjectEntrySchema).optional(),
|
|
5904
|
+
dispatch: z4.object({
|
|
5905
|
+
timeoutMs: z4.number().int().positive().optional(),
|
|
5906
|
+
killGraceMs: z4.number().int().positive().optional(),
|
|
5907
|
+
agent: z4.enum(["claude", "codex", "gemini"]).optional()
|
|
5908
|
+
}).optional(),
|
|
5909
|
+
reviewPolicy: z4.object({
|
|
5910
|
+
codeReviewThreshold: z4.string().min(1).optional(),
|
|
5911
|
+
securityThreshold: z4.string().min(1).optional(),
|
|
5912
|
+
maxRounds: z4.number().optional()
|
|
5913
|
+
}).optional(),
|
|
5914
|
+
prePush: z4.object({
|
|
5915
|
+
alwaysRun: z4.array(z4.string()).optional()
|
|
5270
5916
|
}).optional(),
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5917
|
+
report: z4.object({
|
|
5918
|
+
commandTimeoutMs: z4.number().int().positive().optional()
|
|
5919
|
+
}).superRefine((report, ctx) => {
|
|
5920
|
+
const commandTimeoutMs = report.commandTimeoutMs;
|
|
5921
|
+
if (commandTimeoutMs !== undefined && commandTimeoutMs > MAX_REPORT_COMMAND_TIMEOUT_MS) {
|
|
5922
|
+
ctx.addIssue({
|
|
5923
|
+
code: z4.ZodIssueCode.custom,
|
|
5924
|
+
path: ["commandTimeoutMs"],
|
|
5925
|
+
message: "must be at most 3600000 (1 hour)"
|
|
5926
|
+
});
|
|
5927
|
+
}
|
|
5275
5928
|
}).optional()
|
|
5276
5929
|
});
|
|
5277
5930
|
var GLOBAL_VINAYA_HOME = join3(homedir(), ".vinaya");
|
|
@@ -5346,6 +5999,28 @@ function resolvePrincipalAllowlist(config) {
|
|
|
5346
5999
|
function resolveReleaseActor(config) {
|
|
5347
6000
|
return config?.releaseActor ?? DEFAULT_RELEASE_ACTOR;
|
|
5348
6001
|
}
|
|
6002
|
+
function resolveReviewPolicy(config) {
|
|
6003
|
+
const raw = config?.reviewPolicy;
|
|
6004
|
+
if (!raw)
|
|
6005
|
+
return DEFAULT_REVIEW_POLICY;
|
|
6006
|
+
const codeReviewThreshold = raw.codeReviewThreshold ?? DEFAULT_REVIEW_POLICY.codeReviewThreshold;
|
|
6007
|
+
if (!isKnownSeverity(CODE_REVIEW_SEVERITY_ORDER, codeReviewThreshold)) {
|
|
6008
|
+
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.`);
|
|
6009
|
+
}
|
|
6010
|
+
const securityThreshold = raw.securityThreshold ?? DEFAULT_REVIEW_POLICY.securityThreshold;
|
|
6011
|
+
if (!isKnownSeverity(SECURITY_SEVERITY_ORDER, securityThreshold)) {
|
|
6012
|
+
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.`);
|
|
6013
|
+
}
|
|
6014
|
+
const maxRounds = raw.maxRounds ?? DEFAULT_REVIEW_POLICY.maxRounds;
|
|
6015
|
+
if (!Number.isInteger(maxRounds) || maxRounds < 1) {
|
|
6016
|
+
throw new Error(`vinaya.config.json: reviewPolicy.maxRounds "${maxRounds}" is not a positive integer — fix the config, this never falls back to a default.`);
|
|
6017
|
+
}
|
|
6018
|
+
return {
|
|
6019
|
+
codeReviewThreshold,
|
|
6020
|
+
securityThreshold,
|
|
6021
|
+
maxRounds
|
|
6022
|
+
};
|
|
6023
|
+
}
|
|
5349
6024
|
function trustAnchorRepo() {
|
|
5350
6025
|
const wellFormed = (slug) => /^[^/\s]+\/[^/\s]+$/.test(slug) ? slug : null;
|
|
5351
6026
|
const fromRunner = process.env.GITHUB_REPOSITORY?.trim();
|
|
@@ -5433,8 +6108,11 @@ function emitCheckError(error) {
|
|
|
5433
6108
|
}
|
|
5434
6109
|
|
|
5435
6110
|
// src/lib/diff-evidence.ts
|
|
6111
|
+
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
6112
|
+
import { join as join5, relative, resolve as resolve2 } from "node:path";
|
|
6113
|
+
|
|
6114
|
+
// src/lib/brief-assembly.ts
|
|
5436
6115
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
5437
|
-
import { join as join4, relative, resolve as resolve2 } from "node:path";
|
|
5438
6116
|
|
|
5439
6117
|
// ../../packages/sources/src/commands.ts
|
|
5440
6118
|
var COMMANDS = [
|
|
@@ -5594,6 +6272,17 @@ var COMMANDS = [
|
|
|
5594
6272
|
],
|
|
5595
6273
|
status: "shipped"
|
|
5596
6274
|
},
|
|
6275
|
+
{
|
|
6276
|
+
name: "task status",
|
|
6277
|
+
description: "Every open task with a frozen brief, its pull request, and whether its loop is running, paused, or published",
|
|
6278
|
+
flags: [{ flag: "--json", description: "Enveloped JSON output (schema: 1)" }],
|
|
6279
|
+
details: [
|
|
6280
|
+
"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`.",
|
|
6281
|
+
"`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.",
|
|
6282
|
+
"`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."
|
|
6283
|
+
],
|
|
6284
|
+
status: "shipped"
|
|
6285
|
+
},
|
|
5597
6286
|
{
|
|
5598
6287
|
name: "pr create",
|
|
5599
6288
|
description: "Open a pull request after full brief-schema validation",
|
|
@@ -6103,10 +6792,10 @@ function createForgeSource(config) {
|
|
|
6103
6792
|
};
|
|
6104
6793
|
}
|
|
6105
6794
|
// ../../packages/sources/src/select-source.ts
|
|
6106
|
-
import { z as
|
|
6107
|
-
var StateSourceConfigSchema =
|
|
6108
|
-
|
|
6109
|
-
|
|
6795
|
+
import { z as z5 } from "zod";
|
|
6796
|
+
var StateSourceConfigSchema = z5.discriminatedUnion("kind", [
|
|
6797
|
+
z5.object({ kind: z5.literal("forge"), owner: z5.string(), repo: z5.string() }),
|
|
6798
|
+
z5.object({ kind: z5.literal("file"), root: z5.string().optional() })
|
|
6110
6799
|
]);
|
|
6111
6800
|
// src/checks/edge-resolve.ts
|
|
6112
6801
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
@@ -6226,6 +6915,570 @@ async function resolveEdge(id, taskById, factsByTaskId, repo, resolveSibling = d
|
|
|
6226
6915
|
};
|
|
6227
6916
|
}
|
|
6228
6917
|
|
|
6918
|
+
// src/lib/brief-assembly.ts
|
|
6919
|
+
function git(args) {
|
|
6920
|
+
try {
|
|
6921
|
+
return execFileSync5("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
6922
|
+
} catch {
|
|
6923
|
+
return "";
|
|
6924
|
+
}
|
|
6925
|
+
}
|
|
6926
|
+
function expandGlob(glob) {
|
|
6927
|
+
const out = git(["ls-files", "--", glob]);
|
|
6928
|
+
return out.split(`
|
|
6929
|
+
`).map((s) => s.trim()).filter(Boolean);
|
|
6930
|
+
}
|
|
6931
|
+
var DRAFT_ISSUE_SENTINEL = Number.MAX_SAFE_INTEGER;
|
|
6932
|
+
|
|
6933
|
+
// src/checks/registry.ts
|
|
6934
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
6935
|
+
import { join as join4 } from "node:path";
|
|
6936
|
+
var DIST_BIN_DIR = join4(packageRoot(import.meta.url), "dist", "checks", "bin");
|
|
6937
|
+
var SRC_BIN_DIR = join4(packageRoot(import.meta.url), "src", "checks", "bin");
|
|
6938
|
+
var BIN_DIR = existsSync4(DIST_BIN_DIR) ? DIST_BIN_DIR : SRC_BIN_DIR;
|
|
6939
|
+
var BIN_EXT = BIN_DIR === DIST_BIN_DIR ? ".js" : ".ts";
|
|
6940
|
+
function bin(name) {
|
|
6941
|
+
return join4(BIN_DIR, `${name}${BIN_EXT}`);
|
|
6942
|
+
}
|
|
6943
|
+
var REGISTRY = [
|
|
6944
|
+
[
|
|
6945
|
+
{
|
|
6946
|
+
name: "brief-shape",
|
|
6947
|
+
validates: "body",
|
|
6948
|
+
run: bin("check-brief-shape"),
|
|
6949
|
+
scope: "diff",
|
|
6950
|
+
timeoutMs: 15000,
|
|
6951
|
+
env: {
|
|
6952
|
+
PR_BODY: { optional: true },
|
|
6953
|
+
BRANCH: { optional: true },
|
|
6954
|
+
PR_NUMBER: { optional: true },
|
|
6955
|
+
GITHUB_TOKEN: { optional: true },
|
|
6956
|
+
GH_TOKEN: { optional: true }
|
|
6957
|
+
}
|
|
6958
|
+
},
|
|
6959
|
+
0
|
|
6960
|
+
],
|
|
6961
|
+
[
|
|
6962
|
+
{
|
|
6963
|
+
name: "pr-report-density",
|
|
6964
|
+
validates: "body",
|
|
6965
|
+
run: bin("check-pr-report-density"),
|
|
6966
|
+
scope: "diff",
|
|
6967
|
+
timeoutMs: 15000,
|
|
6968
|
+
env: {
|
|
6969
|
+
PR_BODY: { optional: true }
|
|
6970
|
+
}
|
|
6971
|
+
},
|
|
6972
|
+
0
|
|
6973
|
+
],
|
|
6974
|
+
[
|
|
6975
|
+
{
|
|
6976
|
+
name: "doc-coverage",
|
|
6977
|
+
validates: "body",
|
|
6978
|
+
run: bin("check-doc-coverage"),
|
|
6979
|
+
scope: "diff",
|
|
6980
|
+
timeoutMs: 15000,
|
|
6981
|
+
env: {
|
|
6982
|
+
BASE_SHA: { optional: true },
|
|
6983
|
+
PR_BODY: { optional: true },
|
|
6984
|
+
PR_BODY_FILE: { optional: true },
|
|
6985
|
+
PR_NUMBER: { optional: true },
|
|
6986
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
6987
|
+
GITHUB_TOKEN: { optional: true },
|
|
6988
|
+
GH_TOKEN: { optional: true }
|
|
6989
|
+
}
|
|
6990
|
+
},
|
|
6991
|
+
0
|
|
6992
|
+
],
|
|
6993
|
+
[
|
|
6994
|
+
{
|
|
6995
|
+
name: "coherence",
|
|
6996
|
+
run: bin("check-coherence"),
|
|
6997
|
+
scope: "full",
|
|
6998
|
+
timeoutMs: 30000,
|
|
6999
|
+
env: {
|
|
7000
|
+
AEG_REPO: { optional: true },
|
|
7001
|
+
BRANCH: { optional: true },
|
|
7002
|
+
GITHUB_TOKEN: { optional: true },
|
|
7003
|
+
GH_TOKEN: { optional: true }
|
|
7004
|
+
}
|
|
7005
|
+
},
|
|
7006
|
+
0
|
|
7007
|
+
],
|
|
7008
|
+
[
|
|
7009
|
+
{
|
|
7010
|
+
name: "pr-premise-reassert",
|
|
7011
|
+
validates: "body",
|
|
7012
|
+
run: bin("check-pr-premise-reassert"),
|
|
7013
|
+
scope: "diff",
|
|
7014
|
+
timeoutMs: 15000,
|
|
7015
|
+
env: {
|
|
7016
|
+
PR_BODY: { optional: true }
|
|
7017
|
+
}
|
|
7018
|
+
},
|
|
7019
|
+
0
|
|
7020
|
+
],
|
|
7021
|
+
[
|
|
7022
|
+
{
|
|
7023
|
+
name: "dispatch-readiness",
|
|
7024
|
+
run: bin("check-dispatch-readiness"),
|
|
7025
|
+
scope: "full",
|
|
7026
|
+
timeoutMs: 30000,
|
|
7027
|
+
env: {
|
|
7028
|
+
AEG_REPO: { optional: true },
|
|
7029
|
+
BRANCH: { optional: true },
|
|
7030
|
+
GITHUB_TOKEN: { optional: true },
|
|
7031
|
+
GH_TOKEN: { optional: true },
|
|
7032
|
+
PREMISE_FILE: { optional: true }
|
|
7033
|
+
}
|
|
7034
|
+
},
|
|
7035
|
+
0
|
|
7036
|
+
],
|
|
7037
|
+
[
|
|
7038
|
+
{
|
|
7039
|
+
name: "closes-n",
|
|
7040
|
+
validates: "body",
|
|
7041
|
+
run: bin("check-closes-n"),
|
|
7042
|
+
scope: "diff",
|
|
7043
|
+
timeoutMs: 15000,
|
|
7044
|
+
requiresOpenPr: true,
|
|
7045
|
+
env: {
|
|
7046
|
+
BRANCH: { optional: true },
|
|
7047
|
+
PR_BODY: { optional: true },
|
|
7048
|
+
AEG_REPO: { optional: true },
|
|
7049
|
+
GITHUB_TOKEN: { optional: true },
|
|
7050
|
+
GH_TOKEN: { optional: true }
|
|
7051
|
+
}
|
|
7052
|
+
},
|
|
7053
|
+
1
|
|
7054
|
+
],
|
|
7055
|
+
[
|
|
7056
|
+
{
|
|
7057
|
+
name: "single-plan-pr",
|
|
7058
|
+
run: bin("check-single-plan-pr"),
|
|
7059
|
+
scope: "diff",
|
|
7060
|
+
timeoutMs: 15000,
|
|
7061
|
+
env: {
|
|
7062
|
+
BASE_SHA: { optional: true },
|
|
7063
|
+
PR_NUMBER: { optional: true },
|
|
7064
|
+
GITHUB_TOKEN: { optional: true },
|
|
7065
|
+
GH_TOKEN: { optional: true }
|
|
7066
|
+
}
|
|
7067
|
+
},
|
|
7068
|
+
0
|
|
7069
|
+
],
|
|
7070
|
+
[
|
|
7071
|
+
{
|
|
7072
|
+
name: "surface-scope",
|
|
7073
|
+
run: bin("check-surface-scope"),
|
|
7074
|
+
scope: "diff",
|
|
7075
|
+
timeoutMs: 15000,
|
|
7076
|
+
env: {
|
|
7077
|
+
AEG_REPO: { optional: true },
|
|
7078
|
+
BASE_SHA: { optional: true },
|
|
7079
|
+
BRANCH: { optional: true },
|
|
7080
|
+
GITHUB_TOKEN: { optional: true },
|
|
7081
|
+
GH_TOKEN: { optional: true }
|
|
7082
|
+
}
|
|
7083
|
+
},
|
|
7084
|
+
0
|
|
7085
|
+
],
|
|
7086
|
+
[
|
|
7087
|
+
{
|
|
7088
|
+
name: "test-plan",
|
|
7089
|
+
validates: "body",
|
|
7090
|
+
run: bin("check-test-plan"),
|
|
7091
|
+
scope: "diff",
|
|
7092
|
+
timeoutMs: 15000,
|
|
7093
|
+
requiresOpenPr: true,
|
|
7094
|
+
principalOwed: true,
|
|
7095
|
+
env: {
|
|
7096
|
+
PR_BODY: { optional: true },
|
|
7097
|
+
BRANCH: { optional: true }
|
|
7098
|
+
}
|
|
7099
|
+
},
|
|
7100
|
+
1
|
|
7101
|
+
],
|
|
7102
|
+
[
|
|
7103
|
+
{
|
|
7104
|
+
name: "body-bare-digits",
|
|
7105
|
+
validates: "body",
|
|
7106
|
+
run: bin("check-body-bare-digits"),
|
|
7107
|
+
scope: "diff",
|
|
7108
|
+
timeoutMs: 15000,
|
|
7109
|
+
requiresOpenPr: true,
|
|
7110
|
+
ownWorkflow: true,
|
|
7111
|
+
env: {
|
|
7112
|
+
PR_BODY: { optional: true },
|
|
7113
|
+
PR_NUMBER: { optional: true },
|
|
7114
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
7115
|
+
GITHUB_TOKEN: { optional: true },
|
|
7116
|
+
GH_TOKEN: { optional: true }
|
|
7117
|
+
}
|
|
7118
|
+
},
|
|
7119
|
+
1
|
|
7120
|
+
],
|
|
7121
|
+
[
|
|
7122
|
+
{
|
|
7123
|
+
name: "token-report",
|
|
7124
|
+
validates: "body",
|
|
7125
|
+
run: bin("check-token-report"),
|
|
7126
|
+
scope: "diff",
|
|
7127
|
+
timeoutMs: 15000,
|
|
7128
|
+
requiresOpenPr: true,
|
|
7129
|
+
env: {
|
|
7130
|
+
PR_BODY: { optional: true },
|
|
7131
|
+
BRANCH: { optional: true },
|
|
7132
|
+
CLAUDE_PROJECT_DIR: { optional: true },
|
|
7133
|
+
CLAUDE_CODE_SESSION_ID: { optional: true }
|
|
7134
|
+
}
|
|
7135
|
+
},
|
|
7136
|
+
1
|
|
7137
|
+
],
|
|
7138
|
+
[
|
|
7139
|
+
{
|
|
7140
|
+
name: "no-disk-state",
|
|
7141
|
+
run: bin("check-no-disk-state"),
|
|
7142
|
+
scope: "diff",
|
|
7143
|
+
timeoutMs: 15000,
|
|
7144
|
+
env: { BASE_SHA: { optional: true } }
|
|
7145
|
+
},
|
|
7146
|
+
0
|
|
7147
|
+
],
|
|
7148
|
+
[
|
|
7149
|
+
{
|
|
7150
|
+
name: "registry-gates",
|
|
7151
|
+
run: bin("check-registry-gates"),
|
|
7152
|
+
scope: "full",
|
|
7153
|
+
timeoutMs: 30000,
|
|
7154
|
+
env: {
|
|
7155
|
+
AEG_REPO: { optional: true },
|
|
7156
|
+
GITHUB_TOKEN: { optional: true },
|
|
7157
|
+
GH_TOKEN: { optional: true }
|
|
7158
|
+
}
|
|
7159
|
+
},
|
|
7160
|
+
0
|
|
7161
|
+
],
|
|
7162
|
+
[
|
|
7163
|
+
{
|
|
7164
|
+
name: "review-gate",
|
|
7165
|
+
run: bin("check-review-gate"),
|
|
7166
|
+
scope: "full",
|
|
7167
|
+
timeoutMs: 30000,
|
|
7168
|
+
ownWorkflow: true,
|
|
7169
|
+
env: {
|
|
7170
|
+
PR_NUMBER: { optional: true },
|
|
7171
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
7172
|
+
GITHUB_TOKEN: { optional: true },
|
|
7173
|
+
GH_TOKEN: { optional: true }
|
|
7174
|
+
}
|
|
7175
|
+
},
|
|
7176
|
+
1
|
|
7177
|
+
],
|
|
7178
|
+
[
|
|
7179
|
+
{
|
|
7180
|
+
name: "branch-topology",
|
|
7181
|
+
run: bin("check-branch-topology"),
|
|
7182
|
+
scope: "full",
|
|
7183
|
+
timeoutMs: 30000,
|
|
7184
|
+
env: {
|
|
7185
|
+
BRANCH: { optional: true },
|
|
7186
|
+
AEG_REPO: { optional: true },
|
|
7187
|
+
GITHUB_TOKEN: { optional: true },
|
|
7188
|
+
GH_TOKEN: { optional: true }
|
|
7189
|
+
}
|
|
7190
|
+
},
|
|
7191
|
+
0
|
|
7192
|
+
],
|
|
7193
|
+
[
|
|
7194
|
+
{
|
|
7195
|
+
name: "dead-branch-push",
|
|
7196
|
+
run: bin("check-dead-branch-push"),
|
|
7197
|
+
scope: "full",
|
|
7198
|
+
timeoutMs: 30000,
|
|
7199
|
+
env: {
|
|
7200
|
+
BRANCH: { optional: true },
|
|
7201
|
+
GITHUB_TOKEN: { optional: true },
|
|
7202
|
+
GH_TOKEN: { optional: true }
|
|
7203
|
+
}
|
|
7204
|
+
},
|
|
7205
|
+
0
|
|
7206
|
+
],
|
|
7207
|
+
[
|
|
7208
|
+
{
|
|
7209
|
+
name: "first-push-dispatch",
|
|
7210
|
+
run: bin("check-first-push-dispatch"),
|
|
7211
|
+
scope: "full",
|
|
7212
|
+
timeoutMs: 30000,
|
|
7213
|
+
env: {
|
|
7214
|
+
BRANCH: { optional: true },
|
|
7215
|
+
AEG_REPO: { optional: true },
|
|
7216
|
+
GITHUB_TOKEN: { optional: true },
|
|
7217
|
+
GH_TOKEN: { optional: true }
|
|
7218
|
+
}
|
|
7219
|
+
},
|
|
7220
|
+
0
|
|
7221
|
+
],
|
|
7222
|
+
[
|
|
7223
|
+
{
|
|
7224
|
+
name: "doc-coverage-push",
|
|
7225
|
+
validates: "body",
|
|
7226
|
+
run: bin("check-doc-coverage-push"),
|
|
7227
|
+
scope: "diff",
|
|
7228
|
+
timeoutMs: 15000,
|
|
7229
|
+
env: {
|
|
7230
|
+
OVERRIDE_DOCS: { optional: true },
|
|
7231
|
+
BASE_SHA: { optional: true },
|
|
7232
|
+
PR_BODY: { optional: true },
|
|
7233
|
+
PR_BODY_FILE: { optional: true },
|
|
7234
|
+
PR_LABELS: { optional: true },
|
|
7235
|
+
WAIVER_LABEL_ACTOR: { optional: true },
|
|
7236
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
7237
|
+
GITHUB_TOKEN: { optional: true },
|
|
7238
|
+
GH_TOKEN: { optional: true }
|
|
7239
|
+
}
|
|
7240
|
+
},
|
|
7241
|
+
0
|
|
7242
|
+
],
|
|
7243
|
+
[
|
|
7244
|
+
{
|
|
7245
|
+
name: "issue-assignment",
|
|
7246
|
+
run: bin("check-issue-assignment"),
|
|
7247
|
+
scope: "full",
|
|
7248
|
+
timeoutMs: 30000,
|
|
7249
|
+
env: {
|
|
7250
|
+
AEG_REPO: { optional: true },
|
|
7251
|
+
BRANCH: { optional: true },
|
|
7252
|
+
GITHUB_TOKEN: { optional: true },
|
|
7253
|
+
GH_TOKEN: { optional: true }
|
|
7254
|
+
}
|
|
7255
|
+
},
|
|
7256
|
+
0
|
|
7257
|
+
],
|
|
7258
|
+
[
|
|
7259
|
+
{
|
|
7260
|
+
name: "evidence-fresh",
|
|
7261
|
+
validates: "body",
|
|
7262
|
+
run: bin("check-evidence-fresh"),
|
|
7263
|
+
scope: "diff",
|
|
7264
|
+
timeoutMs: 15000,
|
|
7265
|
+
requiresOpenPr: true,
|
|
7266
|
+
env: {
|
|
7267
|
+
PR_NUMBER: { optional: true },
|
|
7268
|
+
PR_BODY: { optional: true },
|
|
7269
|
+
BASE_SHA: { optional: true },
|
|
7270
|
+
GITHUB_TOKEN: { optional: true },
|
|
7271
|
+
GH_TOKEN: { optional: true }
|
|
7272
|
+
}
|
|
7273
|
+
},
|
|
7274
|
+
1
|
|
7275
|
+
],
|
|
7276
|
+
[
|
|
7277
|
+
{
|
|
7278
|
+
name: "reader-resolvable-prose",
|
|
7279
|
+
run: bin("check-reader-resolvable-prose"),
|
|
7280
|
+
scope: "full",
|
|
7281
|
+
timeoutMs: 30000,
|
|
7282
|
+
env: {},
|
|
7283
|
+
include: [
|
|
7284
|
+
"aeg-root/**/*.md",
|
|
7285
|
+
"apps/cli/src/**",
|
|
7286
|
+
".github/workflows/**",
|
|
7287
|
+
".vinaya/**",
|
|
7288
|
+
"apps/cli/README.md",
|
|
7289
|
+
"packages/sources/README.md"
|
|
7290
|
+
]
|
|
7291
|
+
},
|
|
7292
|
+
0
|
|
7293
|
+
],
|
|
7294
|
+
[
|
|
7295
|
+
{
|
|
7296
|
+
name: "retired-vocabulary",
|
|
7297
|
+
run: bin("check-retired-vocabulary"),
|
|
7298
|
+
scope: "full",
|
|
7299
|
+
timeoutMs: 30000,
|
|
7300
|
+
env: {},
|
|
7301
|
+
include: ["aeg-root/**/*.md"]
|
|
7302
|
+
},
|
|
7303
|
+
0
|
|
7304
|
+
],
|
|
7305
|
+
[
|
|
7306
|
+
{
|
|
7307
|
+
name: "doctrine-portability",
|
|
7308
|
+
run: bin("check-doctrine-portability"),
|
|
7309
|
+
scope: "full",
|
|
7310
|
+
timeoutMs: 30000,
|
|
7311
|
+
env: { BASE_SHA: { optional: true } },
|
|
7312
|
+
include: ["aeg-root/**/*.md"]
|
|
7313
|
+
},
|
|
7314
|
+
0
|
|
7315
|
+
],
|
|
7316
|
+
[
|
|
7317
|
+
{
|
|
7318
|
+
name: "doctrine-no-procedures",
|
|
7319
|
+
run: bin("check-doctrine-no-procedures"),
|
|
7320
|
+
scope: "full",
|
|
7321
|
+
timeoutMs: 30000,
|
|
7322
|
+
env: {},
|
|
7323
|
+
include: ["aeg-root/**/*.md"]
|
|
7324
|
+
},
|
|
7325
|
+
0
|
|
7326
|
+
],
|
|
7327
|
+
[
|
|
7328
|
+
{
|
|
7329
|
+
name: "exec-bits",
|
|
7330
|
+
run: bin("check-exec-bits"),
|
|
7331
|
+
scope: "diff",
|
|
7332
|
+
timeoutMs: 30000,
|
|
7333
|
+
env: {}
|
|
7334
|
+
},
|
|
7335
|
+
0
|
|
7336
|
+
],
|
|
7337
|
+
[
|
|
7338
|
+
{
|
|
7339
|
+
name: "workspace-escape",
|
|
7340
|
+
run: bin("check-workspace-escape"),
|
|
7341
|
+
scope: "full",
|
|
7342
|
+
timeoutMs: 30000,
|
|
7343
|
+
env: {},
|
|
7344
|
+
include: ["aeg-root/**/*.md"]
|
|
7345
|
+
},
|
|
7346
|
+
0
|
|
7347
|
+
],
|
|
7348
|
+
[
|
|
7349
|
+
{
|
|
7350
|
+
name: "changeset-coverage",
|
|
7351
|
+
run: bin("check-changeset-coverage"),
|
|
7352
|
+
scope: "diff",
|
|
7353
|
+
timeoutMs: 15000,
|
|
7354
|
+
env: {}
|
|
7355
|
+
},
|
|
7356
|
+
0
|
|
7357
|
+
],
|
|
7358
|
+
[
|
|
7359
|
+
{
|
|
7360
|
+
name: "quoted-command",
|
|
7361
|
+
run: bin("check-quoted-command"),
|
|
7362
|
+
scope: "diff",
|
|
7363
|
+
timeoutMs: 15000,
|
|
7364
|
+
env: {}
|
|
7365
|
+
},
|
|
7366
|
+
0
|
|
7367
|
+
],
|
|
7368
|
+
[
|
|
7369
|
+
{
|
|
7370
|
+
name: "main-branch-refusal",
|
|
7371
|
+
run: bin("check-main-branch-refusal"),
|
|
7372
|
+
scope: "full",
|
|
7373
|
+
timeoutMs: 15000,
|
|
7374
|
+
env: { VINAYA_PUSH_REFS: { optional: true } }
|
|
7375
|
+
},
|
|
7376
|
+
0
|
|
7377
|
+
],
|
|
7378
|
+
[
|
|
7379
|
+
{
|
|
7380
|
+
name: "token-collection-wired",
|
|
7381
|
+
run: bin("check-token-collection-wired"),
|
|
7382
|
+
scope: "full",
|
|
7383
|
+
timeoutMs: 15000,
|
|
7384
|
+
env: {
|
|
7385
|
+
CLAUDE_PROJECT_DIR: { optional: true },
|
|
7386
|
+
CLAUDE_CODE_SESSION_ID: { optional: true }
|
|
7387
|
+
}
|
|
7388
|
+
},
|
|
7389
|
+
0
|
|
7390
|
+
],
|
|
7391
|
+
[
|
|
7392
|
+
{
|
|
7393
|
+
name: "issue-title-grammar",
|
|
7394
|
+
run: bin("check-issue-title-grammar"),
|
|
7395
|
+
validates: "issue",
|
|
7396
|
+
scope: "full",
|
|
7397
|
+
ownWorkflow: true,
|
|
7398
|
+
timeoutMs: 15000,
|
|
7399
|
+
env: { ISSUE_TITLE: { optional: true } }
|
|
7400
|
+
},
|
|
7401
|
+
1
|
|
7402
|
+
],
|
|
7403
|
+
[
|
|
7404
|
+
{
|
|
7405
|
+
name: "issue-objectives-numbering",
|
|
7406
|
+
run: bin("check-issue-objectives-numbering"),
|
|
7407
|
+
validates: "issue",
|
|
7408
|
+
scope: "full",
|
|
7409
|
+
ownWorkflow: true,
|
|
7410
|
+
timeoutMs: 15000,
|
|
7411
|
+
env: { ISSUE_BODY: { optional: true }, ISSUE_NUMBER: { optional: true } }
|
|
7412
|
+
},
|
|
7413
|
+
1
|
|
7414
|
+
],
|
|
7415
|
+
[
|
|
7416
|
+
{
|
|
7417
|
+
name: "issue-parts-coverage",
|
|
7418
|
+
run: bin("check-issue-parts-coverage"),
|
|
7419
|
+
validates: "issue",
|
|
7420
|
+
scope: "full",
|
|
7421
|
+
ownWorkflow: true,
|
|
7422
|
+
timeoutMs: 15000,
|
|
7423
|
+
env: { ISSUE_BODY: { optional: true } }
|
|
7424
|
+
},
|
|
7425
|
+
1
|
|
7426
|
+
],
|
|
7427
|
+
[
|
|
7428
|
+
{
|
|
7429
|
+
name: "issue-surface-globs",
|
|
7430
|
+
run: bin("check-issue-surface-globs"),
|
|
7431
|
+
validates: "issue",
|
|
7432
|
+
scope: "full",
|
|
7433
|
+
ownWorkflow: true,
|
|
7434
|
+
timeoutMs: 15000,
|
|
7435
|
+
env: { ISSUE_BODY: { optional: true } }
|
|
7436
|
+
},
|
|
7437
|
+
1
|
|
7438
|
+
],
|
|
7439
|
+
[
|
|
7440
|
+
{
|
|
7441
|
+
name: "issue-tranche-label",
|
|
7442
|
+
run: bin("check-issue-tranche-label"),
|
|
7443
|
+
validates: "issue",
|
|
7444
|
+
scope: "full",
|
|
7445
|
+
ownWorkflow: true,
|
|
7446
|
+
timeoutMs: 15000,
|
|
7447
|
+
env: { ISSUE_BODY: { optional: true }, ISSUE_LABELS: { optional: true } }
|
|
7448
|
+
},
|
|
7449
|
+
1
|
|
7450
|
+
],
|
|
7451
|
+
[
|
|
7452
|
+
{
|
|
7453
|
+
name: "issue-milestone-attach",
|
|
7454
|
+
run: bin("check-issue-milestone-attach"),
|
|
7455
|
+
validates: "issue",
|
|
7456
|
+
scope: "full",
|
|
7457
|
+
ownWorkflow: true,
|
|
7458
|
+
timeoutMs: 15000,
|
|
7459
|
+
env: {
|
|
7460
|
+
ISSUE_LABELS: { optional: true },
|
|
7461
|
+
CURRENT_MILESTONE_TITLE: { optional: true },
|
|
7462
|
+
RESOLVED_MILESTONE_TITLE: { optional: true }
|
|
7463
|
+
}
|
|
7464
|
+
},
|
|
7465
|
+
1
|
|
7466
|
+
]
|
|
7467
|
+
];
|
|
7468
|
+
function coreCheckRegistry() {
|
|
7469
|
+
return REGISTRY.map(([spec]) => spec);
|
|
7470
|
+
}
|
|
7471
|
+
var CORE_CHECK_RING = Object.fromEntries(REGISTRY.map(([spec, ring]) => [spec.name, ring]));
|
|
7472
|
+
|
|
7473
|
+
// src/checks/runner.ts
|
|
7474
|
+
var activeKillers = new Set;
|
|
7475
|
+
|
|
7476
|
+
// src/lib/forge-write.ts
|
|
7477
|
+
var INFORMATIONAL_DISPATCH_BLOCKER_CLASSES = new Set([
|
|
7478
|
+
"depends-on-not-merged",
|
|
7479
|
+
"conflicts-with"
|
|
7480
|
+
]);
|
|
7481
|
+
|
|
6229
7482
|
// src/commands/review-post.ts
|
|
6230
7483
|
function parseChangedLineRanges(diffOutput) {
|
|
6231
7484
|
const result = {};
|
|
@@ -6257,7 +7510,7 @@ function parseChangedLineRanges(diffOutput) {
|
|
|
6257
7510
|
function fileDiffAgainst(ref, path) {
|
|
6258
7511
|
let out;
|
|
6259
7512
|
try {
|
|
6260
|
-
out =
|
|
7513
|
+
out = execFileSync6("git", ["diff", `${ref}...HEAD`, "--", path], {
|
|
6261
7514
|
encoding: "utf8",
|
|
6262
7515
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6263
7516
|
}).trim();
|
|
@@ -6268,7 +7521,7 @@ function fileDiffAgainst(ref, path) {
|
|
|
6268
7521
|
}
|
|
6269
7522
|
function revParse(ref) {
|
|
6270
7523
|
try {
|
|
6271
|
-
return
|
|
7524
|
+
return execFileSync6("git", ["rev-parse", "--verify", ref], {
|
|
6272
7525
|
encoding: "utf8",
|
|
6273
7526
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6274
7527
|
}).trim();
|
|
@@ -6278,7 +7531,7 @@ function revParse(ref) {
|
|
|
6278
7531
|
}
|
|
6279
7532
|
function repoRoot() {
|
|
6280
7533
|
try {
|
|
6281
|
-
return
|
|
7534
|
+
return execFileSync6("git", ["rev-parse", "--show-toplevel"], {
|
|
6282
7535
|
encoding: "utf8",
|
|
6283
7536
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6284
7537
|
}).trim();
|
|
@@ -6289,7 +7542,7 @@ function repoRoot() {
|
|
|
6289
7542
|
function changedFiles(base) {
|
|
6290
7543
|
let out;
|
|
6291
7544
|
try {
|
|
6292
|
-
out =
|
|
7545
|
+
out = execFileSync6("git", ["diff", "--name-only", `${base}...HEAD`], {
|
|
6293
7546
|
encoding: "utf8",
|
|
6294
7547
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6295
7548
|
}).trim();
|
|
@@ -6314,7 +7567,7 @@ function resolveDiff(base = process.env.BASE_SHA || "origin/main") {
|
|
|
6314
7567
|
const relPaths = changedFiles(ref);
|
|
6315
7568
|
if (relPaths === null)
|
|
6316
7569
|
continue;
|
|
6317
|
-
return { base: ref, root, files: relPaths.map((p) =>
|
|
7570
|
+
return { base: ref, root, files: relPaths.map((p) => join5(root, p)) };
|
|
6318
7571
|
}
|
|
6319
7572
|
return null;
|
|
6320
7573
|
}
|
|
@@ -6353,7 +7606,7 @@ function resolveCheckDoctrineRoot() {
|
|
|
6353
7606
|
return configured;
|
|
6354
7607
|
const root = repoRoot();
|
|
6355
7608
|
if (root !== null) {
|
|
6356
|
-
const candidate =
|
|
7609
|
+
const candidate = join6(root, "aeg-root");
|
|
6357
7610
|
if (hasDoctrineEntry(candidate))
|
|
6358
7611
|
return candidate;
|
|
6359
7612
|
}
|
|
@@ -6371,7 +7624,7 @@ function collect(dir, out = []) {
|
|
|
6371
7624
|
return out;
|
|
6372
7625
|
}
|
|
6373
7626
|
for (const name of entries) {
|
|
6374
|
-
const full =
|
|
7627
|
+
const full = join6(dir, name);
|
|
6375
7628
|
let isDir;
|
|
6376
7629
|
try {
|
|
6377
7630
|
isDir = statSync(full).isDirectory();
|