@attalabs/vinaya 0.27.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/aeg-root/enforcement.md +4 -4
- package/aeg-root/roles/developer.md +23 -17
- package/aeg-root/roles/planner.md +2 -0
- package/aeg-root/roles/principal.md +4 -0
- package/aeg-root/roles/reviewer.md +3 -3
- package/aeg-root/roles/security.md +3 -3
- package/dist/checks/bin/check-body-bare-digits.js +915 -240
- package/dist/checks/bin/check-branch-topology.js +820 -180
- package/dist/checks/bin/check-brief-shape.js +915 -240
- package/dist/checks/bin/check-changeset-coverage.js +1534 -262
- package/dist/checks/bin/check-closes-n.js +824 -184
- package/dist/checks/bin/check-coherence.js +984 -265
- package/dist/checks/bin/check-dead-branch-push.js +805 -176
- package/dist/checks/bin/check-dispatch-readiness.js +1046 -276
- package/dist/checks/bin/check-doc-coverage-push.js +1530 -258
- package/dist/checks/bin/check-doc-coverage.js +1532 -260
- package/dist/checks/bin/check-doctrine-no-procedures.js +915 -240
- package/dist/checks/bin/check-doctrine-portability.js +1529 -257
- package/dist/checks/bin/check-evidence-fresh.js +1875 -258
- package/dist/checks/bin/check-exec-bits.js +1527 -255
- package/dist/checks/bin/check-first-push-dispatch.js +932 -246
- package/dist/checks/bin/check-issue-assignment.js +822 -182
- package/dist/checks/bin/check-issue-milestone-attach.js +5734 -0
- package/dist/checks/bin/check-issue-objectives-numbering.js +5736 -0
- package/dist/checks/bin/check-issue-parts-coverage.js +5736 -0
- package/dist/checks/bin/check-issue-surface-globs.js +6910 -0
- package/dist/checks/bin/check-issue-title-grammar.js +5736 -0
- package/dist/checks/bin/check-issue-tranche-label.js +5736 -0
- package/dist/checks/bin/check-main-branch-refusal.js +805 -176
- package/dist/checks/bin/check-no-disk-state.js +805 -176
- package/dist/checks/bin/check-pr-premise-reassert.js +915 -240
- package/dist/checks/bin/check-pr-report-density.js +805 -176
- package/dist/checks/bin/check-quoted-command.js +1508 -255
- package/dist/checks/bin/check-reader-resolvable-prose.js +1512 -259
- package/dist/checks/bin/check-registry-gates.js +892 -176
- package/dist/checks/bin/check-retired-vocabulary.js +1506 -253
- package/dist/checks/bin/check-review-gate.js +1000 -245
- package/dist/checks/bin/check-single-plan-pr.js +805 -176
- package/dist/checks/bin/check-surface-scope.js +830 -182
- package/dist/checks/bin/check-test-plan.js +834 -189
- package/dist/checks/bin/check-token-collection-wired.js +805 -176
- package/dist/checks/bin/check-token-report.js +805 -176
- package/dist/checks/bin/check-workspace-escape.js +1525 -253
- package/dist/index.js +9835 -5597
- package/dist/lib/pre-push-changed-files.js +57 -0
- package/dist/lib/pre-push-select-tests.js +1473 -0
- package/package.json +4 -2
|
@@ -3,7 +3,7 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
4
4
|
|
|
5
5
|
// src/checks/bin/check-exec-bits.ts
|
|
6
|
-
import { execFileSync as
|
|
6
|
+
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
7
7
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
8
8
|
|
|
9
9
|
// src/checks/contract.ts
|
|
@@ -14,8 +14,8 @@ function emitCheckError(error) {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
// src/lib/diff-evidence.ts
|
|
17
|
-
import { execFileSync as
|
|
18
|
-
import { join as
|
|
17
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
18
|
+
import { join as join4, relative, resolve as resolve2 } from "node:path";
|
|
19
19
|
// ../../packages/aeg-core/src/gate-audience.ts
|
|
20
20
|
var GATE_AUDIENCE = {
|
|
21
21
|
"check-branch-topology": { shippedAs: "branch-topology", ring: 0 },
|
|
@@ -1966,6 +1966,8 @@ function parseInlineFieldList(section) {
|
|
|
1966
1966
|
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
1967
1967
|
var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1968
1968
|
var RULING_ORDINAL_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Ruling ordinal:\s*(\d+)(?!\d)/im;
|
|
1969
|
+
var BRIEF_HASH_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Brief hash:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1970
|
+
var POLICY_DIGEST_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Policy digest:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1969
1971
|
function firstFiveLines(comment) {
|
|
1970
1972
|
return comment.split(`
|
|
1971
1973
|
`).slice(0, 5).join(`
|
|
@@ -1988,6 +1990,26 @@ function extractRulingOrdinal(comment) {
|
|
|
1988
1990
|
const m = firstSevenLines(comment).match(RULING_ORDINAL_PATTERN);
|
|
1989
1991
|
return m ? Number.parseInt(m[1], 10) : null;
|
|
1990
1992
|
}
|
|
1993
|
+
function firstElevenLines(comment) {
|
|
1994
|
+
return comment.split(`
|
|
1995
|
+
`).slice(0, 11).join(`
|
|
1996
|
+
`);
|
|
1997
|
+
}
|
|
1998
|
+
function extractBriefHash(comment) {
|
|
1999
|
+
const m = firstElevenLines(comment).match(BRIEF_HASH_PATTERN);
|
|
2000
|
+
return m ? m[1].toLowerCase() : null;
|
|
2001
|
+
}
|
|
2002
|
+
function extractPolicyDigest(comment) {
|
|
2003
|
+
const m = firstElevenLines(comment).match(POLICY_DIGEST_PATTERN);
|
|
2004
|
+
return m ? m[1].toLowerCase() : null;
|
|
2005
|
+
}
|
|
2006
|
+
var FINDING_SEVERITY_LINE = /^\d+\.\s+\[([A-Z][A-Z]*)\]\s+(.+?)\s+—/gm;
|
|
2007
|
+
function extractFindingSeverities(comment) {
|
|
2008
|
+
return [...comment.matchAll(FINDING_SEVERITY_LINE)].map((m) => ({
|
|
2009
|
+
severity: m[1],
|
|
2010
|
+
location: m[2]
|
|
2011
|
+
}));
|
|
2012
|
+
}
|
|
1991
2013
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
1992
2014
|
const candidates = comments.filter((c) => valuePattern.test(c));
|
|
1993
2015
|
if (candidates.length === 0) {
|
|
@@ -1996,6 +2018,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1996
2018
|
headSha: null,
|
|
1997
2019
|
objectivesVersion: null,
|
|
1998
2020
|
rulingOrdinal: null,
|
|
2021
|
+
briefHash: null,
|
|
2022
|
+
policyDigest: null,
|
|
2023
|
+
findingSeverities: [],
|
|
1999
2024
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
2000
2025
|
};
|
|
2001
2026
|
}
|
|
@@ -2007,6 +2032,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
2007
2032
|
headSha: null,
|
|
2008
2033
|
objectivesVersion: null,
|
|
2009
2034
|
rulingOrdinal: null,
|
|
2035
|
+
briefHash: null,
|
|
2036
|
+
policyDigest: null,
|
|
2037
|
+
findingSeverities: [],
|
|
2010
2038
|
danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
|
|
2011
2039
|
};
|
|
2012
2040
|
}
|
|
@@ -2015,6 +2043,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
2015
2043
|
headSha: extractHeadSha(latest),
|
|
2016
2044
|
objectivesVersion: extractObjectivesVersion(latest),
|
|
2017
2045
|
rulingOrdinal: extractRulingOrdinal(latest),
|
|
2046
|
+
briefHash: extractBriefHash(latest),
|
|
2047
|
+
policyDigest: extractPolicyDigest(latest),
|
|
2048
|
+
findingSeverities: extractFindingSeverities(latest),
|
|
2018
2049
|
danglingNote: null
|
|
2019
2050
|
};
|
|
2020
2051
|
}
|
|
@@ -2350,6 +2381,53 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive,
|
|
|
2350
2381
|
}
|
|
2351
2382
|
return out;
|
|
2352
2383
|
}
|
|
2384
|
+
// ../../packages/aeg-core/src/review-policy.ts
|
|
2385
|
+
var CODE_REVIEW_SEVERITY_ORDER = ["BLOCKER", "MAJOR", "MINOR"];
|
|
2386
|
+
var SECURITY_SEVERITY_ORDER = ["CRITICAL", "HIGH", "MEDIUM", "LOW"];
|
|
2387
|
+
var DEFAULT_MAX_ROUNDS = 3;
|
|
2388
|
+
var DEFAULT_REVIEW_POLICY = {
|
|
2389
|
+
codeReviewThreshold: "BLOCKER",
|
|
2390
|
+
securityThreshold: "HIGH",
|
|
2391
|
+
maxRounds: DEFAULT_MAX_ROUNDS
|
|
2392
|
+
};
|
|
2393
|
+
var FILE_SHAPED_LOCATION = /\.[a-zA-Z0-9]{1,10}(:\d+)?\s*$/;
|
|
2394
|
+
var PROSE_LOCATION_PATTERNS = [/\bpr\s*body\b/i, /\bcomment\b/i];
|
|
2395
|
+
var ROLE_FILE_LOCATION = /(^|\/)aeg-root\/roles\//i;
|
|
2396
|
+
function isProseLocation(location) {
|
|
2397
|
+
if (ROLE_FILE_LOCATION.test(location))
|
|
2398
|
+
return true;
|
|
2399
|
+
if (FILE_SHAPED_LOCATION.test(location))
|
|
2400
|
+
return false;
|
|
2401
|
+
return PROSE_LOCATION_PATTERNS.some((pattern) => pattern.test(location));
|
|
2402
|
+
}
|
|
2403
|
+
var PROSE_CAP_SEVERITY = "MINOR";
|
|
2404
|
+
function blockingSeverities(scale, threshold) {
|
|
2405
|
+
const idx = scale.indexOf(threshold);
|
|
2406
|
+
if (idx === -1) {
|
|
2407
|
+
throw new Error(`blockingSeverities: threshold "${threshold}" is not one of ${scale.join(" > ")}`);
|
|
2408
|
+
}
|
|
2409
|
+
return scale.slice(0, idx + 1);
|
|
2410
|
+
}
|
|
2411
|
+
function evaluateReviewFindings(findings, scale, threshold) {
|
|
2412
|
+
const blocking = new Set(blockingSeverities(scale, threshold));
|
|
2413
|
+
const blockingFindings = findings.filter((f) => {
|
|
2414
|
+
if (!scale.includes(f.severity)) {
|
|
2415
|
+
throw new Error(`evaluateReviewFindings: severity "${f.severity}" is not one of ${scale.join(" > ")}`);
|
|
2416
|
+
}
|
|
2417
|
+
const effectiveSeverity = f.location !== undefined && isProseLocation(f.location) ? PROSE_CAP_SEVERITY : f.severity;
|
|
2418
|
+
return blocking.has(effectiveSeverity);
|
|
2419
|
+
});
|
|
2420
|
+
return { outcome: blockingFindings.length > 0 ? "blocked" : "clean", blockingFindings };
|
|
2421
|
+
}
|
|
2422
|
+
function evaluateCodeReview(findings, policy) {
|
|
2423
|
+
return evaluateReviewFindings(findings, CODE_REVIEW_SEVERITY_ORDER, policy.codeReviewThreshold);
|
|
2424
|
+
}
|
|
2425
|
+
function evaluateSecurityReview(findings, policy) {
|
|
2426
|
+
return evaluateReviewFindings(findings, SECURITY_SEVERITY_ORDER, policy.securityThreshold);
|
|
2427
|
+
}
|
|
2428
|
+
function isKnownSeverity(scale, value) {
|
|
2429
|
+
return scale.includes(value);
|
|
2430
|
+
}
|
|
2353
2431
|
// ../../packages/aeg-core/src/pr-tier.ts
|
|
2354
2432
|
var TIER_FIELD = /(\*\*)?\s*Tier\s*(\*\*)?\s*:\s*(\*\*)?\s*([013])\b/i;
|
|
2355
2433
|
function readTierFromPrBody(prBody) {
|
|
@@ -2720,7 +2798,7 @@ function checkForField(prBody) {
|
|
|
2720
2798
|
]
|
|
2721
2799
|
};
|
|
2722
2800
|
}
|
|
2723
|
-
function
|
|
2801
|
+
function checkClosesNPresence(prBody) {
|
|
2724
2802
|
const closesPattern = /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s{0,8}:?\s{0,8}#\d+/i;
|
|
2725
2803
|
if (closesPattern.test(stripCode(prBody))) {
|
|
2726
2804
|
return { status: "pass", errors: [] };
|
|
@@ -2752,10 +2830,22 @@ var COMMIT_TYPES = [
|
|
|
2752
2830
|
"Test"
|
|
2753
2831
|
];
|
|
2754
2832
|
var COMMIT_TYPE_STYLE = new RegExp(`^(${COMMIT_TYPES.join("|")})(\\([a-z0-9-]+\\))?: \\S`);
|
|
2833
|
+
function checkForgeTitle(title) {
|
|
2834
|
+
const taskStyle = /^\[[a-z0-9._-]+\] \S+ — \S/;
|
|
2835
|
+
if (COMMIT_TYPE_STYLE.test(title) || taskStyle.test(title))
|
|
2836
|
+
return { status: "pass", errors: [] };
|
|
2837
|
+
return {
|
|
2838
|
+
status: "fail",
|
|
2839
|
+
errors: [
|
|
2840
|
+
`brief-validation title: "${title}" matches neither title grammar — expected \`Type: description\` / \`Type(scope): description\` (commitlint types + Plan) or \`[tranche] id — description\` (task form).`
|
|
2841
|
+
]
|
|
2842
|
+
};
|
|
2843
|
+
}
|
|
2755
2844
|
var BRIEF_SHAPE_MARKERS = [checkSurfaceMap, checkDocUpdateList, checkStopConditions, checkAutonomyClause];
|
|
2756
2845
|
var TASK_BRANCH_PATTERN = /^task\/[^/]+\/[^/]+$/;
|
|
2846
|
+
var TASK_ISSUE_BRANCH_PATTERN = /^task\/issue-\d+$/;
|
|
2757
2847
|
function isTaskBranch(branch) {
|
|
2758
|
-
return TASK_BRANCH_PATTERN.test(branch);
|
|
2848
|
+
return TASK_BRANCH_PATTERN.test(branch) || TASK_ISSUE_BRANCH_PATTERN.test(branch);
|
|
2759
2849
|
}
|
|
2760
2850
|
function isBriefShaped(prBody) {
|
|
2761
2851
|
const stripped = stripCode(prBody);
|
|
@@ -2877,8 +2967,9 @@ function packagesNamedIn(text) {
|
|
|
2877
2967
|
}
|
|
2878
2968
|
function hasTestPathForConsumer(text, consumerDir) {
|
|
2879
2969
|
const escaped = consumerDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2880
|
-
const
|
|
2881
|
-
|
|
2970
|
+
const filePathRe = new RegExp(`${escaped}\\/[\\w./-]*\\.test\\.[A-Za-z0-9]+`);
|
|
2971
|
+
const testDirRe = new RegExp(`${escaped}\\/(?:[\\w-]+\\/)*(?:tests|specs)(?:\\/|\\b)`);
|
|
2972
|
+
return filePathRe.test(text) || testDirRe.test(text);
|
|
2882
2973
|
}
|
|
2883
2974
|
function checkConsumerTests(prBody, consumersOf) {
|
|
2884
2975
|
const section4 = extractNumberedSection(prBody, 4);
|
|
@@ -2982,7 +3073,7 @@ function checkBriefSections(prBody, readTier, options = {}) {
|
|
|
2982
3073
|
checkConsumerTests(prBody, consumersOf),
|
|
2983
3074
|
checkDefeatCases(prBody),
|
|
2984
3075
|
...issueObjectives !== undefined ? [checkObjectivesCopy(prBody, issueObjectives), checkObjectivesCoverage(prBody)] : [],
|
|
2985
|
-
...requireClosesN ? [
|
|
3076
|
+
...requireClosesN ? [checkClosesNPresence(prBody)] : []
|
|
2986
3077
|
];
|
|
2987
3078
|
return { errors: results.flatMap((r) => r.errors) };
|
|
2988
3079
|
}
|
|
@@ -3272,6 +3363,28 @@ function topLevelSectionText(body, headingName) {
|
|
|
3272
3363
|
const next = /^##[ \t]/m.exec(afterHeading);
|
|
3273
3364
|
return next ? afterHeading.slice(0, next.index) : afterHeading;
|
|
3274
3365
|
}
|
|
3366
|
+
function partHasBacktickedPath(text) {
|
|
3367
|
+
let i = 0;
|
|
3368
|
+
while (i < text.length) {
|
|
3369
|
+
const start = text.indexOf("`", i);
|
|
3370
|
+
if (start === -1)
|
|
3371
|
+
return false;
|
|
3372
|
+
const end = text.indexOf("`", start + 1);
|
|
3373
|
+
if (end === -1)
|
|
3374
|
+
return false;
|
|
3375
|
+
if (text.slice(start + 1, end).includes("/"))
|
|
3376
|
+
return true;
|
|
3377
|
+
i = end + 1;
|
|
3378
|
+
}
|
|
3379
|
+
return false;
|
|
3380
|
+
}
|
|
3381
|
+
var PART_MIN_WORDS_OUTSIDE_BACKTICKS = 3;
|
|
3382
|
+
function stripPartBackticks(text) {
|
|
3383
|
+
return text.replace(/`[^`\n]*`/g, " ");
|
|
3384
|
+
}
|
|
3385
|
+
function partWordCount(text) {
|
|
3386
|
+
return text.split(/\s+/).filter((w) => /[a-z]/i.test(w)).length;
|
|
3387
|
+
}
|
|
3275
3388
|
function looksLikeFilePath(entry2) {
|
|
3276
3389
|
const stripped = entry2.replace(/\/\*\*?$/, "");
|
|
3277
3390
|
const lastSegment = stripped.split("/").pop() ?? stripped;
|
|
@@ -3312,6 +3425,18 @@ function globCoversPath(glob, path) {
|
|
|
3312
3425
|
const p = path.replace(/\/+$/, "");
|
|
3313
3426
|
return g === p || g.startsWith(`${p}/`) || p.startsWith(`${g}/`);
|
|
3314
3427
|
}
|
|
3428
|
+
function checkSurfaceGlobsResolve(body, resolvesToFile) {
|
|
3429
|
+
const surface = parseIssueSurface(body);
|
|
3430
|
+
if (!surface.ok)
|
|
3431
|
+
return { status: "pass", errors: [] };
|
|
3432
|
+
const errors = [];
|
|
3433
|
+
for (const glob of surface.value.in) {
|
|
3434
|
+
if (!resolvesToFile(glob)) {
|
|
3435
|
+
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.`);
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3439
|
+
}
|
|
3315
3440
|
function checkSurfaceScope(changedFiles, outGlobs) {
|
|
3316
3441
|
if (outGlobs.length === 0)
|
|
3317
3442
|
return { ok: true };
|
|
@@ -3323,9 +3448,77 @@ function checkSurfaceScope(changedFiles, outGlobs) {
|
|
|
3323
3448
|
}
|
|
3324
3449
|
return violations.length > 0 ? { ok: false, violations } : { ok: true };
|
|
3325
3450
|
}
|
|
3451
|
+
var ISSUE_PART_LINE_RE = /^Part\s+(\d+)\s*\(([^)]*)\)\s*[-—–]\s*(.*)$/i;
|
|
3452
|
+
function parseIssueParts(body) {
|
|
3453
|
+
const section = topLevelSectionText(body, "Parts");
|
|
3454
|
+
if (section === null)
|
|
3455
|
+
return { ok: false, errors: ["no `## Parts` heading found in the body."] };
|
|
3456
|
+
const lines = section.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
3457
|
+
const parts = [];
|
|
3458
|
+
const errors = [];
|
|
3459
|
+
for (const line of lines) {
|
|
3460
|
+
const m = ISSUE_PART_LINE_RE.exec(line);
|
|
3461
|
+
if (!m) {
|
|
3462
|
+
errors.push(`"${line}" is not a well-formed Parts line — expected \`Part <n> (<refs>) — <outcome>\`.`);
|
|
3463
|
+
continue;
|
|
3464
|
+
}
|
|
3465
|
+
const n = Number.parseInt(m[1], 10);
|
|
3466
|
+
const refs = m[2];
|
|
3467
|
+
const text = m[3].trim();
|
|
3468
|
+
if (text.length === 0) {
|
|
3469
|
+
errors.push(`Part ${n} has no outcome text after the dash — every Part states one observable outcome.`);
|
|
3470
|
+
continue;
|
|
3471
|
+
}
|
|
3472
|
+
if (partHasBacktickedPath(text) && partWordCount(stripPartBackticks(text)) < PART_MIN_WORDS_OUTSIDE_BACKTICKS) {
|
|
3473
|
+
errors.push(`Part ${n} is little more than a file path — a Part names an outcome and symbols, never a bare path.`);
|
|
3474
|
+
continue;
|
|
3475
|
+
}
|
|
3476
|
+
const objectiveIds = [...refs.matchAll(/O(\d+)/g)].map((r) => Number.parseInt(r[1], 10));
|
|
3477
|
+
parts.push({ n, objectiveIds, text });
|
|
3478
|
+
}
|
|
3479
|
+
if (parts.length === 0) {
|
|
3480
|
+
errors.push("the `## Parts` section has no well-formed `Part <n> (<refs>) — <outcome>` lines.");
|
|
3481
|
+
}
|
|
3482
|
+
if (errors.length > 0)
|
|
3483
|
+
return { ok: false, errors };
|
|
3484
|
+
return { ok: true, value: parts };
|
|
3485
|
+
}
|
|
3486
|
+
function checkPartsCiteDefinedObjectives(body) {
|
|
3487
|
+
const parts = parseIssueParts(body);
|
|
3488
|
+
const objectives = objectivesOf(body);
|
|
3489
|
+
if (!parts.ok || !objectives.ok)
|
|
3490
|
+
return { status: "pass", errors: [] };
|
|
3491
|
+
const definedIds = new Set(objectives.objectives.map((o) => Number.parseInt(o.id.slice(1), 10)));
|
|
3492
|
+
const errors = [];
|
|
3493
|
+
for (const part of parts.value) {
|
|
3494
|
+
for (const objectiveId of part.objectiveIds) {
|
|
3495
|
+
if (!definedIds.has(objectiveId)) {
|
|
3496
|
+
errors.push(`issue-validation Parts: Part ${part.n} cites O${objectiveId}, which the Issue's own \`## Objectives\` section does not define.`);
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
}
|
|
3500
|
+
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3501
|
+
}
|
|
3326
3502
|
function isTaskIssueLabelSet(labels) {
|
|
3327
3503
|
return hasLabel("tranche", labels);
|
|
3328
3504
|
}
|
|
3505
|
+
function checkTrancheLabelPresence(_body, _labels) {
|
|
3506
|
+
return { status: "pass", errors: [] };
|
|
3507
|
+
}
|
|
3508
|
+
function checkMilestoneAttach(labels, currentMilestoneTitle, resolvedMilestoneTitle) {
|
|
3509
|
+
if (!isTaskIssueLabelSet(labels))
|
|
3510
|
+
return { status: "pass", errors: [] };
|
|
3511
|
+
if (resolvedMilestoneTitle === null)
|
|
3512
|
+
return { status: "pass", errors: [] };
|
|
3513
|
+
if (currentMilestoneTitle === resolvedMilestoneTitle)
|
|
3514
|
+
return { status: "pass", errors: [] };
|
|
3515
|
+
return {
|
|
3516
|
+
status: "fail",
|
|
3517
|
+
errors: [
|
|
3518
|
+
`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}".`
|
|
3519
|
+
]
|
|
3520
|
+
};
|
|
3521
|
+
}
|
|
3329
3522
|
var TYPE_LABEL_IDS = LABELS.filter((l) => l.category === "type").map((l) => l.id);
|
|
3330
3523
|
function isControlCodePoint(codePoint) {
|
|
3331
3524
|
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
|
|
@@ -3390,6 +3583,19 @@ function checkProjectsRegistered(body, _labels, registeredNames) {
|
|
|
3390
3583
|
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;
|
|
3391
3584
|
var DOC_PATH_RE_GLOBAL = new RegExp(DOC_PATH_RE.source, "gi");
|
|
3392
3585
|
|
|
3586
|
+
// ../../packages/aeg-core/src/task-branch-identity.ts
|
|
3587
|
+
var ISSUE_BRANCH_PATTERN = /^task\/issue-(\d+)$/;
|
|
3588
|
+
var TRANCHE_BRANCH_PATTERN = /^task\/([^/]+)\/([^/]+)$/;
|
|
3589
|
+
function parseTaskBranchIdentity(branch) {
|
|
3590
|
+
const issueMatch = ISSUE_BRANCH_PATTERN.exec(branch);
|
|
3591
|
+
if (issueMatch)
|
|
3592
|
+
return { kind: "issue", issueNumber: Number(issueMatch[1]) };
|
|
3593
|
+
const trancheMatch = TRANCHE_BRANCH_PATTERN.exec(branch);
|
|
3594
|
+
if (trancheMatch)
|
|
3595
|
+
return { kind: "tranche", tranche: trancheMatch[1], taskId: trancheMatch[2] };
|
|
3596
|
+
return null;
|
|
3597
|
+
}
|
|
3598
|
+
|
|
3393
3599
|
// ../../packages/aeg-core/src/coherence-checks.ts
|
|
3394
3600
|
var COHERENCE_ENFORCED_FROM = "2026-07-01";
|
|
3395
3601
|
function isGrandfathered(isoDate) {
|
|
@@ -3410,6 +3616,7 @@ function checkA1(entries, principalAllowlist = PRINCIPAL_ALLOWLIST) {
|
|
|
3410
3616
|
if (handClosed)
|
|
3411
3617
|
continue;
|
|
3412
3618
|
failures.push({
|
|
3619
|
+
code: "closed-without-merge",
|
|
3413
3620
|
issue: e.task.issue,
|
|
3414
3621
|
tranche: e.trancheSlug,
|
|
3415
3622
|
task: e.task.id,
|
|
@@ -3434,6 +3641,7 @@ function checkA3(entries) {
|
|
|
3434
3641
|
continue;
|
|
3435
3642
|
if (e.facts.prState === "merged" && e.facts.issueState !== "closed") {
|
|
3436
3643
|
failures.push({
|
|
3644
|
+
code: "auto-close-misfire",
|
|
3437
3645
|
issue: e.task.issue,
|
|
3438
3646
|
tranche: e.trancheSlug,
|
|
3439
3647
|
task: e.task.id,
|
|
@@ -3458,6 +3666,7 @@ function checkT1(entries) {
|
|
|
3458
3666
|
continue;
|
|
3459
3667
|
if (e.facts === undefined) {
|
|
3460
3668
|
failures.push({
|
|
3669
|
+
code: "phantom-issue-ref",
|
|
3461
3670
|
issue: e.task.issue,
|
|
3462
3671
|
tranche: e.trancheSlug,
|
|
3463
3672
|
task: e.task.id,
|
|
@@ -3476,6 +3685,7 @@ function checkT2(openIssuesBySlug, topologyIssuesBySlug, ciTrancheSlug) {
|
|
|
3476
3685
|
for (const num of openNums) {
|
|
3477
3686
|
if (!topologySet.has(num)) {
|
|
3478
3687
|
failures.push({
|
|
3688
|
+
code: "orphan-task",
|
|
3479
3689
|
issue: num,
|
|
3480
3690
|
tranche: slug,
|
|
3481
3691
|
reason: `Issue #${num} is open and labeled ${trancheLabel(slug)} but does not appear in the topology file`
|
|
@@ -3510,6 +3720,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
|
|
|
3510
3720
|
continue;
|
|
3511
3721
|
if (forgeUnavailableSlugs?.has(e.trancheSlug)) {
|
|
3512
3722
|
failures.push({
|
|
3723
|
+
code: "tbd-in-active-tranche",
|
|
3513
3724
|
issue: null,
|
|
3514
3725
|
tranche: e.trancheSlug,
|
|
3515
3726
|
task: e.task.id,
|
|
@@ -3519,6 +3730,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
|
|
|
3519
3730
|
continue;
|
|
3520
3731
|
}
|
|
3521
3732
|
failures.push({
|
|
3733
|
+
code: "tbd-in-active-tranche",
|
|
3522
3734
|
issue: null,
|
|
3523
3735
|
tranche: e.trancheSlug,
|
|
3524
3736
|
task: e.task.id,
|
|
@@ -3551,6 +3763,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
|
|
|
3551
3763
|
const sameIssue = depEntry.task.issue !== null && e.task.issue !== null && depEntry.task.issue === e.task.issue;
|
|
3552
3764
|
if (sameTask || sameIssue) {
|
|
3553
3765
|
failures.push({
|
|
3766
|
+
code: "d1-self-dependency",
|
|
3554
3767
|
issue: e.task.issue,
|
|
3555
3768
|
tranche: e.trancheSlug,
|
|
3556
3769
|
task: e.task.id,
|
|
@@ -3562,6 +3775,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
|
|
|
3562
3775
|
const depClosed = depFacts?.issueState === "closed";
|
|
3563
3776
|
if (!depClosed) {
|
|
3564
3777
|
failures.push({
|
|
3778
|
+
code: "dispatched-on-unmet-deps",
|
|
3565
3779
|
issue: e.task.issue,
|
|
3566
3780
|
tranche: e.trancheSlug,
|
|
3567
3781
|
task: e.task.id,
|
|
@@ -3587,11 +3801,13 @@ function checkR1(issuesBySlug, grandfatheredIssues, registeredNames = []) {
|
|
|
3587
3801
|
const errors = [
|
|
3588
3802
|
...checkIssueRationale(issue.body).errors,
|
|
3589
3803
|
...checkProjectsRegistered(issue.body, issue.labels, registeredNames).errors,
|
|
3590
|
-
...checkIssueObjectives(issue.body, issue.number).errors
|
|
3804
|
+
...checkIssueObjectives(issue.body, issue.number).errors,
|
|
3805
|
+
...checkPartsCiteDefinedObjectives(issue.body).errors
|
|
3591
3806
|
];
|
|
3592
3807
|
if (errors.length === 0)
|
|
3593
3808
|
continue;
|
|
3594
3809
|
failures.push({
|
|
3810
|
+
code: "missing-rationale-field",
|
|
3595
3811
|
issue: issue.number,
|
|
3596
3812
|
tranche: slug,
|
|
3597
3813
|
reason: `Issue #${issue.number} fails the rationale gate: ${errors.join(" | ")}`,
|
|
@@ -3620,6 +3836,7 @@ function checkL1(files, entriesBySlug) {
|
|
|
3620
3836
|
const allClosed = withFacts.every((e) => e.facts?.issueState === "closed");
|
|
3621
3837
|
if (allClosed) {
|
|
3622
3838
|
failures.push({
|
|
3839
|
+
code: "archive-recommended",
|
|
3623
3840
|
tranche: f.slug,
|
|
3624
3841
|
reason: "Active tranche has no open task-Issues — consider archiving to completed/"
|
|
3625
3842
|
});
|
|
@@ -3651,27 +3868,38 @@ function extractClosesReferences(prBody) {
|
|
|
3651
3868
|
}
|
|
3652
3869
|
return referenced;
|
|
3653
3870
|
}
|
|
3654
|
-
function
|
|
3871
|
+
function checkClosesNTopology(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
3655
3872
|
const referenced = extractClosesReferences(prBody);
|
|
3656
3873
|
if (taskIssueRefs) {
|
|
3657
3874
|
for (const n of referenced) {
|
|
3658
|
-
const
|
|
3659
|
-
if (!
|
|
3875
|
+
const ref2 = taskIssueRefs.get(n);
|
|
3876
|
+
if (!ref2)
|
|
3660
3877
|
continue;
|
|
3661
|
-
const expectedBranch = `task/${
|
|
3878
|
+
const expectedBranch = `task/${ref2.trancheSlug}/${ref2.taskId}`;
|
|
3662
3879
|
if (branch !== expectedBranch) {
|
|
3663
3880
|
return {
|
|
3664
3881
|
ok: false,
|
|
3665
|
-
message: `closes-n-reverse: branch "${branch}" closes #${n} (task ${
|
|
3882
|
+
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.`
|
|
3666
3883
|
};
|
|
3667
3884
|
}
|
|
3668
3885
|
}
|
|
3669
3886
|
}
|
|
3670
|
-
const
|
|
3671
|
-
if (!
|
|
3887
|
+
const ref = parseTaskBranchIdentity(branch);
|
|
3888
|
+
if (!ref)
|
|
3672
3889
|
return { ok: true };
|
|
3673
|
-
|
|
3674
|
-
|
|
3890
|
+
if (ref.kind === "issue") {
|
|
3891
|
+
const expectedIssue2 = ref.issueNumber;
|
|
3892
|
+
if (!referenced.has(expectedIssue2)) {
|
|
3893
|
+
return {
|
|
3894
|
+
ok: false,
|
|
3895
|
+
expectedIssue: expectedIssue2,
|
|
3896
|
+
message: `closes-n: PR body does not contain \`Closes #${expectedIssue2}\` (required for branch "${branch}"). Add it to the PR body Summary section.`
|
|
3897
|
+
};
|
|
3898
|
+
}
|
|
3899
|
+
return { ok: true, expectedIssue: expectedIssue2 };
|
|
3900
|
+
}
|
|
3901
|
+
const trancheSlug = ref.tranche;
|
|
3902
|
+
const taskId = ref.taskId;
|
|
3675
3903
|
const trancheFile = trancheFiles.find((f) => f.slug === trancheSlug);
|
|
3676
3904
|
if (!trancheFile) {
|
|
3677
3905
|
return {
|
|
@@ -3723,34 +3951,67 @@ function extractIssue(body) {
|
|
|
3723
3951
|
const issue = headerNums.length > 0 ? headerNums[0] : bodyNums[0];
|
|
3724
3952
|
return { issue, extraIssues: bodyNums.filter((n) => n !== issue), outsideHeader: headerNums.length === 0 };
|
|
3725
3953
|
}
|
|
3726
|
-
// ../../packages/aeg-core/src/review-
|
|
3727
|
-
|
|
3728
|
-
|
|
3954
|
+
// ../../packages/aeg-core/src/review-input-manifest.ts
|
|
3955
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3956
|
+
function briefHash(brief) {
|
|
3957
|
+
return createHash4("sha256").update(`${brief}
|
|
3958
|
+
`).digest("hex");
|
|
3959
|
+
}
|
|
3960
|
+
function policyDigest(policy) {
|
|
3961
|
+
return createHash4("sha256").update(JSON.stringify({ codeReviewThreshold: policy.codeReviewThreshold, securityThreshold: policy.securityThreshold })).digest("hex");
|
|
3962
|
+
}
|
|
3963
|
+
function isBoundToHead(echoed, headSha) {
|
|
3964
|
+
if (!echoed.headSha)
|
|
3729
3965
|
return false;
|
|
3730
|
-
return headSha.toLowerCase().startsWith(
|
|
3966
|
+
return headSha.toLowerCase().startsWith(echoed.headSha.toLowerCase());
|
|
3731
3967
|
}
|
|
3732
|
-
function isBoundByPatchIdentity(
|
|
3733
|
-
if (patchIdOf === undefined || !
|
|
3968
|
+
function isBoundByPatchIdentity(echoed, headSha, patchIdOf) {
|
|
3969
|
+
if (patchIdOf === undefined || !echoed.headSha)
|
|
3734
3970
|
return false;
|
|
3735
|
-
const judged = patchIdOf(
|
|
3971
|
+
const judged = patchIdOf(echoed.headSha);
|
|
3736
3972
|
const current = patchIdOf(headSha);
|
|
3737
3973
|
if (judged === null || current === null)
|
|
3738
3974
|
return false;
|
|
3739
3975
|
return judged === current;
|
|
3740
3976
|
}
|
|
3741
|
-
function isBoundToPatch(
|
|
3742
|
-
return isBoundToHead(
|
|
3977
|
+
function isBoundToPatch(echoed, headSha, patchIdOf) {
|
|
3978
|
+
return isBoundToHead(echoed, headSha) || isBoundByPatchIdentity(echoed, headSha, patchIdOf);
|
|
3743
3979
|
}
|
|
3744
|
-
function isBoundToObjectives(
|
|
3980
|
+
function isBoundToObjectives(echoed, currentVersion) {
|
|
3745
3981
|
if (currentVersion === null)
|
|
3746
3982
|
return true;
|
|
3747
|
-
return
|
|
3983
|
+
return echoed.objectivesVersion === currentVersion;
|
|
3748
3984
|
}
|
|
3749
|
-
function isBoundToRulings(
|
|
3750
|
-
if (
|
|
3985
|
+
function isBoundToRulings(echoed, currentOrdinal) {
|
|
3986
|
+
if (echoed.rulingOrdinal === null)
|
|
3751
3987
|
return currentOrdinal === 0;
|
|
3752
|
-
return
|
|
3988
|
+
return echoed.rulingOrdinal === currentOrdinal;
|
|
3753
3989
|
}
|
|
3990
|
+
function isBoundToBriefHash(echoed, currentHash) {
|
|
3991
|
+
if (currentHash === null)
|
|
3992
|
+
return true;
|
|
3993
|
+
return echoed.briefHash === currentHash;
|
|
3994
|
+
}
|
|
3995
|
+
function isBoundToPolicy(echoed, currentDigest) {
|
|
3996
|
+
return echoed.policyDigest === currentDigest;
|
|
3997
|
+
}
|
|
3998
|
+
function compareManifest(echoed, current, patchIdOf) {
|
|
3999
|
+
const head = isBoundToPatch(echoed, current.headSha, patchIdOf);
|
|
4000
|
+
const briefHashBound = isBoundToBriefHash(echoed, current.briefHash);
|
|
4001
|
+
const objectivesVersion2 = isBoundToObjectives(echoed, current.objectivesVersion);
|
|
4002
|
+
const rulingOrdinal = isBoundToRulings(echoed, current.rulingOrdinal);
|
|
4003
|
+
const policyDigestBound = isBoundToPolicy(echoed, current.policyDigest);
|
|
4004
|
+
return {
|
|
4005
|
+
bound: head && briefHashBound && objectivesVersion2 && rulingOrdinal && policyDigestBound,
|
|
4006
|
+
head,
|
|
4007
|
+
briefHash: briefHashBound,
|
|
4008
|
+
objectivesVersion: objectivesVersion2,
|
|
4009
|
+
rulingOrdinal,
|
|
4010
|
+
policyDigest: policyDigestBound
|
|
4011
|
+
};
|
|
4012
|
+
}
|
|
4013
|
+
|
|
4014
|
+
// ../../packages/aeg-core/src/review-gate.ts
|
|
3754
4015
|
function checkReviewGate(input) {
|
|
3755
4016
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
3756
4017
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -3773,15 +4034,55 @@ function checkReviewGate(input) {
|
|
|
3773
4034
|
const verifiedBodies = verified.map((c) => c.body);
|
|
3774
4035
|
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
3775
4036
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
3776
|
-
const
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
4037
|
+
const policy = input.policy ?? DEFAULT_REVIEW_POLICY;
|
|
4038
|
+
let codeReviewPolicyEvaluation;
|
|
4039
|
+
let securityPolicyEvaluation;
|
|
4040
|
+
try {
|
|
4041
|
+
codeReviewPolicyEvaluation = evaluateCodeReview(codeReview.findingSeverities, policy);
|
|
4042
|
+
securityPolicyEvaluation = evaluateSecurityReview(security.findingSeverities, policy);
|
|
4043
|
+
} catch (err) {
|
|
4044
|
+
return {
|
|
4045
|
+
verdict: "fail",
|
|
4046
|
+
reason: `a verdict comment carries a finding severity this repository's policy does not recognize: ${err instanceof Error ? err.message : String(err)}`,
|
|
4047
|
+
waived: false
|
|
4048
|
+
};
|
|
4049
|
+
}
|
|
4050
|
+
const codeReviewTextClean = codeReview.value === "APPROVE";
|
|
4051
|
+
const securityTextClean = security.value === "PASS";
|
|
4052
|
+
const codeReviewPolicyClean = codeReviewPolicyEvaluation.outcome === "clean";
|
|
4053
|
+
const securityPolicyClean = securityPolicyEvaluation.outcome === "clean";
|
|
4054
|
+
const codeReviewClean = codeReviewTextClean && codeReviewPolicyClean;
|
|
4055
|
+
const securityClean = securityTextClean && securityPolicyClean;
|
|
4056
|
+
const currentManifest = {
|
|
4057
|
+
headSha: input.headSha,
|
|
4058
|
+
briefHash: input.briefHash ?? null,
|
|
4059
|
+
objectivesVersion: input.objectivesVersion,
|
|
4060
|
+
rulingOrdinal: input.rulingOrdinal,
|
|
4061
|
+
policyDigest: policyDigest(policy)
|
|
4062
|
+
};
|
|
4063
|
+
const codeReviewEchoed = {
|
|
4064
|
+
headSha: codeReview.headSha,
|
|
4065
|
+
briefHash: codeReview.briefHash,
|
|
4066
|
+
objectivesVersion: codeReview.objectivesVersion,
|
|
4067
|
+
rulingOrdinal: codeReview.rulingOrdinal,
|
|
4068
|
+
policyDigest: codeReview.policyDigest
|
|
4069
|
+
};
|
|
4070
|
+
const securityEchoed = {
|
|
4071
|
+
headSha: security.headSha,
|
|
4072
|
+
briefHash: security.briefHash,
|
|
4073
|
+
objectivesVersion: security.objectivesVersion,
|
|
4074
|
+
rulingOrdinal: security.rulingOrdinal,
|
|
4075
|
+
policyDigest: security.policyDigest
|
|
4076
|
+
};
|
|
4077
|
+
const codeReviewBinding = compareManifest(codeReviewEchoed, currentManifest, input.patchIdOf);
|
|
4078
|
+
const securityBinding = compareManifest(securityEchoed, currentManifest, input.patchIdOf);
|
|
4079
|
+
const codeReviewBound = codeReviewBinding.head;
|
|
4080
|
+
const securityBound = securityBinding.head;
|
|
4081
|
+
const codeReviewObjectivesBound = codeReviewBinding.objectivesVersion;
|
|
4082
|
+
const securityObjectivesBound = securityBinding.objectivesVersion;
|
|
4083
|
+
const codeReviewRulingsBound = codeReviewBinding.rulingOrdinal;
|
|
4084
|
+
const securityRulingsBound = securityBinding.rulingOrdinal;
|
|
4085
|
+
if (codeReviewClean && codeReviewBinding.bound && securityClean && securityBinding.bound && mechanicalChecksClean) {
|
|
3785
4086
|
return {
|
|
3786
4087
|
verdict: "pass",
|
|
3787
4088
|
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.`,
|
|
@@ -3789,23 +4090,35 @@ function checkReviewGate(input) {
|
|
|
3789
4090
|
};
|
|
3790
4091
|
}
|
|
3791
4092
|
const problems = [];
|
|
3792
|
-
if (!
|
|
4093
|
+
if (!codeReviewTextClean) {
|
|
3793
4094
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
4095
|
+
} else if (!codeReviewPolicyClean) {
|
|
4096
|
+
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`);
|
|
3794
4097
|
} else if (!codeReviewBound) {
|
|
3795
4098
|
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3796
4099
|
} else if (!codeReviewObjectivesBound) {
|
|
3797
4100
|
problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3798
4101
|
} else if (!codeReviewRulingsBound) {
|
|
3799
4102
|
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`);
|
|
4103
|
+
} else if (!codeReviewBinding.briefHash) {
|
|
4104
|
+
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"}`);
|
|
4105
|
+
} else if (!codeReviewBinding.policyDigest) {
|
|
4106
|
+
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}`);
|
|
3800
4107
|
}
|
|
3801
|
-
if (!
|
|
4108
|
+
if (!securityTextClean) {
|
|
3802
4109
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
4110
|
+
} else if (!securityPolicyClean) {
|
|
4111
|
+
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`);
|
|
3803
4112
|
} else if (!securityBound) {
|
|
3804
4113
|
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3805
4114
|
} else if (!securityObjectivesBound) {
|
|
3806
4115
|
problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3807
4116
|
} else if (!securityRulingsBound) {
|
|
3808
4117
|
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`);
|
|
4118
|
+
} else if (!securityBinding.briefHash) {
|
|
4119
|
+
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"}`);
|
|
4120
|
+
} else if (!securityBinding.policyDigest) {
|
|
4121
|
+
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}`);
|
|
3809
4122
|
}
|
|
3810
4123
|
if (!mechanicalChecksClean) {
|
|
3811
4124
|
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(", ")}`);
|
|
@@ -4497,42 +4810,45 @@ function checkDispatchReadiness(input) {
|
|
|
4497
4810
|
const { trancheSlug, task } = input;
|
|
4498
4811
|
const taskLabel = `task ${task.id} (tranche ${trancheSlug})`;
|
|
4499
4812
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
4500
|
-
const
|
|
4813
|
+
const blockerDetails = [];
|
|
4814
|
+
const push = (blockerClass, message) => {
|
|
4815
|
+
blockerDetails.push({ class: blockerClass, message });
|
|
4816
|
+
};
|
|
4501
4817
|
if (task.issue === null) {
|
|
4502
|
-
|
|
4818
|
+
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.`);
|
|
4503
4819
|
} else if (input.issue === null) {
|
|
4504
|
-
|
|
4820
|
+
push("issue-existence", `dispatch-gate issue-existence: ${taskLabel} names Issue #${task.issue}, but it does not resolve to a real GitHub Issue (phantom reference).`);
|
|
4505
4821
|
}
|
|
4506
4822
|
if (input.issue !== null && !input.issueRationalePass) {
|
|
4507
|
-
|
|
4823
|
+
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.`);
|
|
4508
4824
|
}
|
|
4509
4825
|
for (const dep of input.dependsOn) {
|
|
4510
4826
|
if (isSelfDependency(dep, task, input.issue)) {
|
|
4511
4827
|
const issueStr = input.issue !== null ? `#${input.issue.number}` : "?";
|
|
4512
|
-
|
|
4828
|
+
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.`);
|
|
4513
4829
|
continue;
|
|
4514
4830
|
}
|
|
4515
4831
|
if (dep.resolved === false) {
|
|
4516
|
-
|
|
4832
|
+
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.`);
|
|
4517
4833
|
continue;
|
|
4518
4834
|
}
|
|
4519
4835
|
if (!dep.merged && !isHandClosedByRecognizedPrincipal(dep, principalAllowlist)) {
|
|
4520
4836
|
const issueStr = dep.issue !== null ? ` (#${dep.issue})` : "";
|
|
4521
|
-
|
|
4837
|
+
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.`);
|
|
4522
4838
|
}
|
|
4523
4839
|
}
|
|
4524
4840
|
for (const c of input.conflictsWith) {
|
|
4525
4841
|
if (c.openOrInFlight) {
|
|
4526
4842
|
const issueStr = c.issue !== null ? ` (#${c.issue})` : "";
|
|
4527
|
-
|
|
4843
|
+
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.`);
|
|
4528
4844
|
}
|
|
4529
4845
|
}
|
|
4530
4846
|
for (const proj of input.priorTrancheArchival) {
|
|
4531
4847
|
if (proj.priorTrancheSlug !== null && !proj.archived) {
|
|
4532
|
-
|
|
4848
|
+
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.`);
|
|
4533
4849
|
}
|
|
4534
4850
|
}
|
|
4535
|
-
return { ready:
|
|
4851
|
+
return { ready: blockerDetails.length === 0, blockers: blockerDetails.map((b) => b.message), blockerDetails };
|
|
4536
4852
|
}
|
|
4537
4853
|
// ../../packages/aeg-core/src/single-plan-pr.ts
|
|
4538
4854
|
function trancheSlugFromTopologyPath(path) {
|
|
@@ -4634,6 +4950,91 @@ function checkBranchTopology(input) {
|
|
|
4634
4950
|
reason: `Branch \`${branch}\` matches topology row \`${taskId}\` in ${topoPath}.`
|
|
4635
4951
|
};
|
|
4636
4952
|
}
|
|
4953
|
+
// ../../packages/aeg-core/src/task-tools.ts
|
|
4954
|
+
import { z } from "zod";
|
|
4955
|
+
var TASK_TOOL_ERROR_KINDS = [
|
|
4956
|
+
"validation",
|
|
4957
|
+
"authority",
|
|
4958
|
+
"precondition",
|
|
4959
|
+
"capability",
|
|
4960
|
+
"infrastructure",
|
|
4961
|
+
"cancellation",
|
|
4962
|
+
"timeout",
|
|
4963
|
+
"uncertain_effect"
|
|
4964
|
+
];
|
|
4965
|
+
var TaskToolErrorSchema = z.object({
|
|
4966
|
+
kind: z.enum(TASK_TOOL_ERROR_KINDS),
|
|
4967
|
+
message: z.string().min(1),
|
|
4968
|
+
detail: z.string().optional()
|
|
4969
|
+
});
|
|
4970
|
+
var TaskToolRefSchema = z.union([
|
|
4971
|
+
z.object({ tranche: z.string().min(1), id: z.string().min(1) }),
|
|
4972
|
+
z.object({ issue: z.number().int().positive() })
|
|
4973
|
+
]);
|
|
4974
|
+
var MAX_PAGE_LIMIT = 100;
|
|
4975
|
+
var PageRequestSchema = z.object({
|
|
4976
|
+
cursor: z.string().optional(),
|
|
4977
|
+
limit: z.number().int().positive().max(MAX_PAGE_LIMIT).optional()
|
|
4978
|
+
});
|
|
4979
|
+
var FreshnessSchema = z.enum(["fresh", "stale", "unknown"]);
|
|
4980
|
+
var ObservedSchema = z.object({
|
|
4981
|
+
observedAt: z.string(),
|
|
4982
|
+
freshness: FreshnessSchema
|
|
4983
|
+
});
|
|
4984
|
+
var TaskStatusInputSchema = z.object({
|
|
4985
|
+
task: TaskToolRefSchema.optional()
|
|
4986
|
+
}).merge(PageRequestSchema);
|
|
4987
|
+
var TaskStatusItemSchema = z.object({
|
|
4988
|
+
task: TaskToolRefSchema,
|
|
4989
|
+
issue: z.number().int().positive(),
|
|
4990
|
+
pr: z.number().int().positive().nullable(),
|
|
4991
|
+
state: z.string()
|
|
4992
|
+
}).merge(ObservedSchema);
|
|
4993
|
+
var TaskStatusResultSchema = z.object({
|
|
4994
|
+
items: z.array(TaskStatusItemSchema),
|
|
4995
|
+
nextCursor: z.string().nullable()
|
|
4996
|
+
});
|
|
4997
|
+
var RequestedAuthoritySchema = z.enum(["planner", "principal", "operator", "self"]);
|
|
4998
|
+
var EscalationInputsSchema = z.object({
|
|
4999
|
+
task: z.number().int().positive(),
|
|
5000
|
+
round: z.number().int().nonnegative(),
|
|
5001
|
+
head: z.string(),
|
|
5002
|
+
branch: z.string(),
|
|
5003
|
+
prNumber: z.number().int().positive()
|
|
5004
|
+
});
|
|
5005
|
+
var EscalationEvidenceSchema = z.object({
|
|
5006
|
+
round: z.number().int().nonnegative(),
|
|
5007
|
+
reviewer: z.string().nullable(),
|
|
5008
|
+
security: z.string().nullable()
|
|
5009
|
+
});
|
|
5010
|
+
var TaskEscalationReadInputSchema = z.object({
|
|
5011
|
+
task: TaskToolRefSchema
|
|
5012
|
+
}).merge(PageRequestSchema);
|
|
5013
|
+
var TaskEscalationPacketSchema = z.object({
|
|
5014
|
+
reason: z.string(),
|
|
5015
|
+
detail: z.string().nullable(),
|
|
5016
|
+
inputs: EscalationInputsSchema.nullable(),
|
|
5017
|
+
evidence: EscalationEvidenceSchema.nullable(),
|
|
5018
|
+
attemptedRecovery: z.string(),
|
|
5019
|
+
requestedAuthority: RequestedAuthoritySchema,
|
|
5020
|
+
permittedNextActions: z.array(z.string())
|
|
5021
|
+
}).merge(ObservedSchema);
|
|
5022
|
+
var TaskEscalationReadResultSchema = z.object({
|
|
5023
|
+
items: z.array(TaskEscalationPacketSchema),
|
|
5024
|
+
nextCursor: z.string().nullable()
|
|
5025
|
+
}).merge(ObservedSchema);
|
|
5026
|
+
var TaskStartInputSchema = z.object({
|
|
5027
|
+
tranche: z.string().min(1),
|
|
5028
|
+
id: z.string().min(1)
|
|
5029
|
+
});
|
|
5030
|
+
var TaskResumeInputSchema = z.object({
|
|
5031
|
+
task: TaskToolRefSchema
|
|
5032
|
+
});
|
|
5033
|
+
var TaskCancelInputSchema = z.object({
|
|
5034
|
+
task: TaskToolRefSchema,
|
|
5035
|
+
reason: z.string().min(1)
|
|
5036
|
+
});
|
|
5037
|
+
var NoResultSchema = z.never();
|
|
4637
5038
|
// ../../packages/aeg-core/src/first-push-dispatch-gate.ts
|
|
4638
5039
|
function parseTaskBranch(branch) {
|
|
4639
5040
|
const m = /^task\/([^/]+)\/([^/]+)$/.exec(branch);
|
|
@@ -4717,7 +5118,6 @@ function decideIssueAssignment(input) {
|
|
|
4717
5118
|
};
|
|
4718
5119
|
}
|
|
4719
5120
|
// ../../packages/aeg-core/src/test-plan-gate.ts
|
|
4720
|
-
var TASK_BRANCH_PATTERN2 = /^task\/[^/]+\/[^/]+$/;
|
|
4721
5121
|
function evaluateTestPlanGate(body, branch) {
|
|
4722
5122
|
if (!body) {
|
|
4723
5123
|
return {
|
|
@@ -4730,7 +5130,7 @@ function evaluateTestPlanGate(body, branch) {
|
|
|
4730
5130
|
}
|
|
4731
5131
|
const located = locateTestPlanSection(body);
|
|
4732
5132
|
if (!located.found) {
|
|
4733
|
-
if (
|
|
5133
|
+
if (parseTaskBranchIdentity(branch) !== null) {
|
|
4734
5134
|
return {
|
|
4735
5135
|
verdict: "fail",
|
|
4736
5136
|
messages: [
|
|
@@ -4858,7 +5258,7 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
|
|
|
4858
5258
|
return findings;
|
|
4859
5259
|
}
|
|
4860
5260
|
// ../../packages/aeg-core/src/log/schema.ts
|
|
4861
|
-
import { z } from "zod";
|
|
5261
|
+
import { z as z2 } from "zod";
|
|
4862
5262
|
var ROLE_VALUES = [
|
|
4863
5263
|
"planner",
|
|
4864
5264
|
"developer",
|
|
@@ -4868,136 +5268,172 @@ var ROLE_VALUES = [
|
|
|
4868
5268
|
"archivist",
|
|
4869
5269
|
"architect"
|
|
4870
5270
|
];
|
|
4871
|
-
var RoleSchema =
|
|
5271
|
+
var RoleSchema = z2.enum(ROLE_VALUES);
|
|
4872
5272
|
var HOST_VALUES = ["hook", "ci", "cli", "loop"];
|
|
4873
|
-
var HostSchema =
|
|
5273
|
+
var HostSchema = z2.enum(HOST_VALUES);
|
|
4874
5274
|
var RUN_ID_PATTERN = /^[A-Za-z0-9_.-]{1,128}$/;
|
|
4875
|
-
var
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
doctrine: z.string(),
|
|
5275
|
+
var headerMetaCore = {
|
|
5276
|
+
ts: z2.string(),
|
|
5277
|
+
run_id: z2.string().regex(RUN_ID_PATTERN),
|
|
5278
|
+
seq: z2.number().int().nonnegative(),
|
|
5279
|
+
repo: z2.string().nullable(),
|
|
5280
|
+
vinaya: z2.string(),
|
|
5281
|
+
doctrine: z2.string(),
|
|
4883
5282
|
host: HostSchema,
|
|
4884
|
-
machine:
|
|
5283
|
+
machine: z2.string()
|
|
5284
|
+
};
|
|
5285
|
+
var HeaderMetaV1Schema = z2.object({
|
|
5286
|
+
schema: z2.literal(1),
|
|
5287
|
+
...headerMetaCore
|
|
5288
|
+
}).strict();
|
|
5289
|
+
var LineageSchema = z2.object({
|
|
5290
|
+
run: z2.string().nullable(),
|
|
5291
|
+
attempt: z2.number().int().nullable(),
|
|
5292
|
+
parent: z2.string().nullable()
|
|
5293
|
+
}).strict();
|
|
5294
|
+
var InputVersionsSchema = z2.object({
|
|
5295
|
+
objectives_version: z2.string().nullable(),
|
|
5296
|
+
brief_hash: z2.string().nullable(),
|
|
5297
|
+
ruling_ordinal: z2.number().int().nullable(),
|
|
5298
|
+
policy_digest: z2.string().nullable()
|
|
5299
|
+
}).strict();
|
|
5300
|
+
var ProvenanceSchema = z2.enum(["parent_attributed", "env_correlated", "self_reported", "unavailable"]);
|
|
5301
|
+
var HeaderMetaV2Schema = z2.object({
|
|
5302
|
+
schema: z2.literal(2),
|
|
5303
|
+
...headerMetaCore,
|
|
5304
|
+
event_id: z2.string().min(1),
|
|
5305
|
+
process_id: z2.string().min(1),
|
|
5306
|
+
actor_id: z2.string().nullable(),
|
|
5307
|
+
lineage: LineageSchema,
|
|
5308
|
+
input_versions: InputVersionsSchema,
|
|
5309
|
+
provenance: ProvenanceSchema
|
|
4885
5310
|
}).strict();
|
|
4886
|
-
var
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
5311
|
+
var HeaderMetaSchema = z2.discriminatedUnion("schema", [HeaderMetaV1Schema, HeaderMetaV2Schema]);
|
|
5312
|
+
var SubjectSchema = z2.object({
|
|
5313
|
+
issue: z2.number().int().nullable(),
|
|
5314
|
+
pr: z2.number().int().optional(),
|
|
5315
|
+
sha: z2.string().optional(),
|
|
5316
|
+
role: z2.union([RoleSchema, z2.literal("unattributed")]),
|
|
5317
|
+
round: z2.number().int().optional(),
|
|
5318
|
+
objectives_version: z2.string().optional()
|
|
4893
5319
|
}).strict();
|
|
4894
|
-
var HeaderSchema =
|
|
5320
|
+
var HeaderSchema = z2.object({
|
|
4895
5321
|
meta: HeaderMetaSchema,
|
|
4896
5322
|
subject: SubjectSchema
|
|
4897
5323
|
}).strict();
|
|
4898
5324
|
var envelopeTail = {
|
|
4899
|
-
duration_ms:
|
|
4900
|
-
payload:
|
|
5325
|
+
duration_ms: z2.number().nonnegative().optional(),
|
|
5326
|
+
payload: z2.object({}).strict()
|
|
4901
5327
|
};
|
|
4902
|
-
var
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
5328
|
+
var ReviewFindingSchema = z2.object({
|
|
5329
|
+
id: z2.string(),
|
|
5330
|
+
severity: z2.string(),
|
|
5331
|
+
state: z2.string().optional(),
|
|
5332
|
+
severity_scale: z2.string().optional(),
|
|
5333
|
+
policy_treatment: z2.enum(["blocking", "non_blocking", "unavailable"]).optional(),
|
|
5334
|
+
confidence: z2.number().min(0).max(1).optional(),
|
|
5335
|
+
confidence_scale: z2.string().optional(),
|
|
5336
|
+
confidence_source: z2.string().optional()
|
|
5337
|
+
}).strict();
|
|
5338
|
+
var DispatchOutcomeSchema = z2.discriminatedUnion("type", [
|
|
5339
|
+
z2.object({ type: z2.literal("pr_opened"), pr: z2.number().int(), head: z2.string() }).strict(),
|
|
5340
|
+
z2.object({
|
|
5341
|
+
type: z2.literal("round_pushed"),
|
|
5342
|
+
pr: z2.number().int(),
|
|
5343
|
+
head: z2.string(),
|
|
5344
|
+
comment_id: z2.number().int()
|
|
4909
5345
|
}).strict(),
|
|
4910
|
-
|
|
4911
|
-
type:
|
|
4912
|
-
verdict:
|
|
4913
|
-
head:
|
|
4914
|
-
comment_id:
|
|
4915
|
-
objectives:
|
|
4916
|
-
findings:
|
|
5346
|
+
z2.object({
|
|
5347
|
+
type: z2.literal("verdict"),
|
|
5348
|
+
verdict: z2.enum(["APPROVE", "REQUEST CHANGES", "PASS", "FAIL"]),
|
|
5349
|
+
head: z2.string(),
|
|
5350
|
+
comment_id: z2.number().int(),
|
|
5351
|
+
objectives: z2.array(z2.object({ id: z2.string(), met: z2.boolean() }).strict()),
|
|
5352
|
+
findings: z2.array(ReviewFindingSchema)
|
|
4917
5353
|
}).strict(),
|
|
4918
|
-
|
|
4919
|
-
type:
|
|
4920
|
-
class:
|
|
4921
|
-
comment_id:
|
|
5354
|
+
z2.object({
|
|
5355
|
+
type: z2.literal("escalation"),
|
|
5356
|
+
class: z2.enum(["authority", "strategy", "product"]),
|
|
5357
|
+
comment_id: z2.number().int()
|
|
4922
5358
|
}).strict(),
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
5359
|
+
z2.object({ type: z2.literal("brief"), comment_id: z2.number().int(), hash: z2.string() }).strict(),
|
|
5360
|
+
z2.object({ type: z2.literal("plan"), issues: z2.array(z2.number().int()) }).strict(),
|
|
5361
|
+
z2.object({ type: z2.literal("archive"), provenance_comment_id: z2.number().int() }).strict()
|
|
4926
5362
|
]);
|
|
4927
5363
|
var dispatchShared = {
|
|
4928
5364
|
meta: HeaderMetaSchema,
|
|
4929
5365
|
subject: SubjectSchema,
|
|
4930
|
-
kind:
|
|
5366
|
+
kind: z2.literal("dispatch"),
|
|
4931
5367
|
...envelopeTail,
|
|
4932
5368
|
target_role: RoleSchema,
|
|
4933
|
-
model:
|
|
4934
|
-
round:
|
|
4935
|
-
effect_id:
|
|
5369
|
+
model: z2.string(),
|
|
5370
|
+
round: z2.number().int().optional(),
|
|
5371
|
+
effect_id: z2.string()
|
|
4936
5372
|
};
|
|
4937
|
-
var dispatchUsageField =
|
|
4938
|
-
var DispatchEventSchema =
|
|
4939
|
-
|
|
4940
|
-
|
|
5373
|
+
var dispatchUsageField = z2.object({ input: z2.number().nonnegative(), output: z2.number().nonnegative() }).strict().nullable();
|
|
5374
|
+
var DispatchEventSchema = z2.discriminatedUnion("event", [
|
|
5375
|
+
z2.object({ ...dispatchShared, event: z2.literal("dispatched"), prompt_hash: z2.string() }).strict(),
|
|
5376
|
+
z2.object({
|
|
4941
5377
|
...dispatchShared,
|
|
4942
|
-
event:
|
|
5378
|
+
event: z2.literal("outcome_received"),
|
|
4943
5379
|
outcome: DispatchOutcomeSchema,
|
|
4944
5380
|
usage: dispatchUsageField
|
|
4945
5381
|
}).strict(),
|
|
4946
|
-
|
|
5382
|
+
z2.object({
|
|
4947
5383
|
...dispatchShared,
|
|
4948
|
-
event:
|
|
4949
|
-
reason:
|
|
5384
|
+
event: z2.literal("dispatch_failed"),
|
|
5385
|
+
reason: z2.enum(["timeout", "crash", "refused", "unattributed_write"]),
|
|
4950
5386
|
usage: dispatchUsageField
|
|
4951
5387
|
}).strict()
|
|
4952
5388
|
]);
|
|
4953
5389
|
var loopShared = {
|
|
4954
5390
|
meta: HeaderMetaSchema,
|
|
4955
5391
|
subject: SubjectSchema,
|
|
4956
|
-
kind:
|
|
5392
|
+
kind: z2.literal("dev_review_loop"),
|
|
4957
5393
|
...envelopeTail,
|
|
4958
|
-
loop_id:
|
|
5394
|
+
loop_id: z2.string()
|
|
4959
5395
|
};
|
|
4960
|
-
var DevReviewLoopEventSchema =
|
|
4961
|
-
|
|
5396
|
+
var DevReviewLoopEventSchema = z2.discriminatedUnion("event", [
|
|
5397
|
+
z2.object({
|
|
4962
5398
|
...loopShared,
|
|
4963
|
-
event:
|
|
4964
|
-
task:
|
|
4965
|
-
policy:
|
|
4966
|
-
max_rounds:
|
|
4967
|
-
reviewers:
|
|
4968
|
-
models:
|
|
5399
|
+
event: z2.literal("loop_started"),
|
|
5400
|
+
task: z2.number().int(),
|
|
5401
|
+
policy: z2.object({
|
|
5402
|
+
max_rounds: z2.number().int().nonnegative(),
|
|
5403
|
+
reviewers: z2.array(RoleSchema),
|
|
5404
|
+
models: z2.record(RoleSchema, z2.string())
|
|
4969
5405
|
}).strict()
|
|
4970
5406
|
}).strict(),
|
|
4971
|
-
|
|
4972
|
-
|
|
5407
|
+
z2.object({ ...loopShared, event: z2.literal("round_started"), round: z2.number().int(), base_head: z2.string() }).strict(),
|
|
5408
|
+
z2.object({
|
|
4973
5409
|
...loopShared,
|
|
4974
|
-
event:
|
|
4975
|
-
round:
|
|
4976
|
-
head:
|
|
4977
|
-
green:
|
|
5410
|
+
event: z2.literal("gate_result_read"),
|
|
5411
|
+
round: z2.number().int(),
|
|
5412
|
+
head: z2.string(),
|
|
5413
|
+
green: z2.boolean()
|
|
4978
5414
|
}).strict(),
|
|
4979
|
-
|
|
5415
|
+
z2.object({
|
|
4980
5416
|
...loopShared,
|
|
4981
|
-
event:
|
|
4982
|
-
round:
|
|
4983
|
-
head:
|
|
4984
|
-
all_approve:
|
|
4985
|
-
blockers:
|
|
5417
|
+
event: z2.literal("verdicts_read"),
|
|
5418
|
+
round: z2.number().int(),
|
|
5419
|
+
head: z2.string(),
|
|
5420
|
+
all_approve: z2.boolean(),
|
|
5421
|
+
blockers: z2.number().int().nonnegative()
|
|
4986
5422
|
}).strict(),
|
|
4987
|
-
|
|
5423
|
+
z2.object({
|
|
4988
5424
|
...loopShared,
|
|
4989
|
-
event:
|
|
4990
|
-
round:
|
|
4991
|
-
open:
|
|
4992
|
-
resolved:
|
|
4993
|
-
new:
|
|
4994
|
-
recurring:
|
|
5425
|
+
event: z2.literal("findings_compared"),
|
|
5426
|
+
round: z2.number().int(),
|
|
5427
|
+
open: z2.array(z2.string()),
|
|
5428
|
+
resolved: z2.array(z2.string()),
|
|
5429
|
+
new: z2.array(z2.string()),
|
|
5430
|
+
recurring: z2.array(z2.string())
|
|
4995
5431
|
}).strict(),
|
|
4996
|
-
|
|
5432
|
+
z2.object({
|
|
4997
5433
|
...loopShared,
|
|
4998
|
-
event:
|
|
4999
|
-
round:
|
|
5000
|
-
condition:
|
|
5434
|
+
event: z2.literal("stop_condition_met"),
|
|
5435
|
+
round: z2.number().int(),
|
|
5436
|
+
condition: z2.enum([
|
|
5001
5437
|
"green",
|
|
5002
5438
|
"max_rounds",
|
|
5003
5439
|
"no_progress",
|
|
@@ -5007,42 +5443,49 @@ var DevReviewLoopEventSchema = z.discriminatedUnion("event", [
|
|
|
5007
5443
|
"reappearance"
|
|
5008
5444
|
])
|
|
5009
5445
|
}).strict(),
|
|
5010
|
-
|
|
5446
|
+
z2.object({
|
|
5447
|
+
...loopShared,
|
|
5448
|
+
event: z2.literal("paused"),
|
|
5449
|
+
round: z2.number().int(),
|
|
5450
|
+
reason: z2.enum(["escalation", "principal_item", "refreeze_needed"])
|
|
5451
|
+
}).strict(),
|
|
5452
|
+
z2.object({
|
|
5011
5453
|
...loopShared,
|
|
5012
|
-
event:
|
|
5013
|
-
round:
|
|
5014
|
-
|
|
5454
|
+
event: z2.literal("resumed"),
|
|
5455
|
+
round: z2.number().int(),
|
|
5456
|
+
by: z2.literal("principal")
|
|
5015
5457
|
}).strict(),
|
|
5016
|
-
|
|
5458
|
+
z2.object({
|
|
5017
5459
|
...loopShared,
|
|
5018
|
-
event:
|
|
5019
|
-
round:
|
|
5020
|
-
|
|
5460
|
+
event: z2.literal("unpushed_work_resume"),
|
|
5461
|
+
round: z2.number().int(),
|
|
5462
|
+
branch: z2.string(),
|
|
5463
|
+
detail: z2.string()
|
|
5021
5464
|
}).strict(),
|
|
5022
|
-
|
|
5465
|
+
z2.object({
|
|
5023
5466
|
...loopShared,
|
|
5024
|
-
event:
|
|
5025
|
-
round:
|
|
5026
|
-
base_head:
|
|
5027
|
-
head:
|
|
5028
|
-
files_changed:
|
|
5029
|
-
insertions:
|
|
5030
|
-
deletions:
|
|
5031
|
-
wall_ms:
|
|
5032
|
-
outcome:
|
|
5467
|
+
event: z2.literal("round_ended"),
|
|
5468
|
+
round: z2.number().int(),
|
|
5469
|
+
base_head: z2.string(),
|
|
5470
|
+
head: z2.string(),
|
|
5471
|
+
files_changed: z2.number().int().nonnegative(),
|
|
5472
|
+
insertions: z2.number().int().nonnegative(),
|
|
5473
|
+
deletions: z2.number().int().nonnegative(),
|
|
5474
|
+
wall_ms: z2.number().nonnegative(),
|
|
5475
|
+
outcome: z2.enum(["green", "changes_requested", "escalated"])
|
|
5033
5476
|
}).strict(),
|
|
5034
|
-
|
|
5477
|
+
z2.object({
|
|
5035
5478
|
...loopShared,
|
|
5036
|
-
event:
|
|
5037
|
-
rounds:
|
|
5038
|
-
total_wall_ms:
|
|
5039
|
-
time_to_green_ms:
|
|
5040
|
-
files_changed_total:
|
|
5041
|
-
final_head:
|
|
5042
|
-
result:
|
|
5479
|
+
event: z2.literal("journal_finalized"),
|
|
5480
|
+
rounds: z2.number().int().nonnegative(),
|
|
5481
|
+
total_wall_ms: z2.number().nonnegative(),
|
|
5482
|
+
time_to_green_ms: z2.number().nonnegative().nullable(),
|
|
5483
|
+
files_changed_total: z2.number().int().nonnegative(),
|
|
5484
|
+
final_head: z2.string(),
|
|
5485
|
+
result: z2.enum(["merged_ready", "stopped"])
|
|
5043
5486
|
}).strict()
|
|
5044
5487
|
]);
|
|
5045
|
-
var ForgeOpSchema =
|
|
5488
|
+
var ForgeOpSchema = z2.enum([
|
|
5046
5489
|
"pr.create",
|
|
5047
5490
|
"pr.comment",
|
|
5048
5491
|
"pr.body.replace",
|
|
@@ -5056,50 +5499,238 @@ var ForgeOpSchema = z.enum([
|
|
|
5056
5499
|
"label.add",
|
|
5057
5500
|
"label.remove"
|
|
5058
5501
|
]);
|
|
5059
|
-
var ForgeWriteTargetSchema =
|
|
5060
|
-
issue:
|
|
5061
|
-
pr:
|
|
5502
|
+
var ForgeWriteTargetSchema = z2.object({
|
|
5503
|
+
issue: z2.number().int().optional(),
|
|
5504
|
+
pr: z2.number().int().optional()
|
|
5062
5505
|
}).strict();
|
|
5063
5506
|
var forgeWriteShared = {
|
|
5064
5507
|
meta: HeaderMetaSchema,
|
|
5065
5508
|
subject: SubjectSchema,
|
|
5066
|
-
kind:
|
|
5509
|
+
kind: z2.literal("forge_write"),
|
|
5067
5510
|
...envelopeTail,
|
|
5068
5511
|
op: ForgeOpSchema,
|
|
5069
5512
|
target: ForgeWriteTargetSchema
|
|
5070
5513
|
};
|
|
5071
|
-
var ForgeWriteEventSchema =
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5514
|
+
var ForgeWriteEventSchema = z2.discriminatedUnion("event", [
|
|
5515
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("validated") }).strict(),
|
|
5516
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("refused"), reason: z2.string() }).strict(),
|
|
5517
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("written"), comment_ids: z2.array(z2.string()) }).strict()
|
|
5518
|
+
]);
|
|
5519
|
+
var GateOutcomeSchema = z2.enum([
|
|
5520
|
+
"pass",
|
|
5521
|
+
"fail",
|
|
5522
|
+
"wait",
|
|
5523
|
+
"skip",
|
|
5524
|
+
"invalid_input",
|
|
5525
|
+
"unavailable_dependency",
|
|
5526
|
+
"timeout",
|
|
5527
|
+
"cancelled"
|
|
5528
|
+
]);
|
|
5529
|
+
var gateShared = {
|
|
5530
|
+
meta: HeaderMetaSchema,
|
|
5531
|
+
subject: SubjectSchema,
|
|
5532
|
+
kind: z2.literal("gate"),
|
|
5533
|
+
...envelopeTail,
|
|
5534
|
+
check: z2.string(),
|
|
5535
|
+
check_version: z2.string().nullable(),
|
|
5536
|
+
policy_version: z2.string().nullable(),
|
|
5537
|
+
input_fingerprint: z2.string().nullable()
|
|
5538
|
+
};
|
|
5539
|
+
var GateEventSchema = z2.discriminatedUnion("event", [
|
|
5540
|
+
z2.object({
|
|
5541
|
+
...gateShared,
|
|
5542
|
+
event: z2.literal("checked"),
|
|
5543
|
+
outcome: GateOutcomeSchema,
|
|
5544
|
+
reason: z2.string().optional()
|
|
5545
|
+
}).strict()
|
|
5546
|
+
]);
|
|
5547
|
+
var OperationResultSchema = z2.enum(["ok", "error", "refused", "timeout", "cancelled", "unavailable"]);
|
|
5548
|
+
var operationShared = {
|
|
5549
|
+
meta: HeaderMetaSchema,
|
|
5550
|
+
subject: SubjectSchema,
|
|
5551
|
+
kind: z2.literal("operation"),
|
|
5552
|
+
...envelopeTail,
|
|
5553
|
+
operation: z2.string(),
|
|
5554
|
+
target: z2.string().nullable()
|
|
5555
|
+
};
|
|
5556
|
+
var OperationEventSchema = z2.discriminatedUnion("event", [
|
|
5557
|
+
z2.object({
|
|
5558
|
+
...operationShared,
|
|
5559
|
+
event: z2.literal("completed"),
|
|
5560
|
+
result: OperationResultSchema,
|
|
5561
|
+
error_class: z2.string().nullable()
|
|
5562
|
+
}).strict()
|
|
5075
5563
|
]);
|
|
5076
|
-
var
|
|
5564
|
+
var UsageUnitsSchema = z2.object({
|
|
5565
|
+
input: z2.number().nonnegative().nullable(),
|
|
5566
|
+
output: z2.number().nonnegative().nullable(),
|
|
5567
|
+
cache: z2.number().nonnegative().nullable()
|
|
5568
|
+
}).strict();
|
|
5569
|
+
var usageShared = {
|
|
5570
|
+
meta: HeaderMetaSchema,
|
|
5571
|
+
subject: SubjectSchema,
|
|
5572
|
+
kind: z2.literal("usage"),
|
|
5573
|
+
...envelopeTail,
|
|
5574
|
+
model: z2.string().nullable(),
|
|
5575
|
+
source: z2.string(),
|
|
5576
|
+
semantics: z2.enum(["cumulative", "delta"])
|
|
5577
|
+
};
|
|
5578
|
+
var UsageEventSchema = z2.discriminatedUnion("event", [
|
|
5579
|
+
z2.object({
|
|
5580
|
+
...usageShared,
|
|
5581
|
+
event: z2.literal("observed"),
|
|
5582
|
+
units: UsageUnitsSchema,
|
|
5583
|
+
unknown_reason: z2.string().nullable()
|
|
5584
|
+
}).strict()
|
|
5585
|
+
]);
|
|
5586
|
+
var RoleAttemptOutcomeSchema = z2.enum([
|
|
5587
|
+
"completed",
|
|
5588
|
+
"incomplete",
|
|
5589
|
+
"infrastructure_failed",
|
|
5590
|
+
"cancelled",
|
|
5591
|
+
"timed_out",
|
|
5592
|
+
"capability_refused"
|
|
5593
|
+
]);
|
|
5594
|
+
var roleAttemptShared = {
|
|
5595
|
+
meta: HeaderMetaSchema,
|
|
5596
|
+
subject: SubjectSchema,
|
|
5597
|
+
kind: z2.literal("role_attempt"),
|
|
5598
|
+
...envelopeTail,
|
|
5599
|
+
actor: z2.string().nullable(),
|
|
5600
|
+
attempt: z2.number().int().nullable()
|
|
5601
|
+
};
|
|
5602
|
+
var RoleAttemptEventSchema = z2.discriminatedUnion("event", [
|
|
5603
|
+
z2.object({
|
|
5604
|
+
...roleAttemptShared,
|
|
5605
|
+
event: z2.literal("attempted"),
|
|
5606
|
+
outcome: RoleAttemptOutcomeSchema,
|
|
5607
|
+
usage: dispatchUsageField
|
|
5608
|
+
}).strict()
|
|
5609
|
+
]);
|
|
5610
|
+
var handoffShared = {
|
|
5611
|
+
meta: HeaderMetaSchema,
|
|
5612
|
+
subject: SubjectSchema,
|
|
5613
|
+
kind: z2.literal("handoff"),
|
|
5614
|
+
...envelopeTail,
|
|
5615
|
+
class: z2.enum(["authority", "strategy", "product"]),
|
|
5616
|
+
reason: z2.string()
|
|
5617
|
+
};
|
|
5618
|
+
var HandoffEventSchema = z2.discriminatedUnion("event", [
|
|
5619
|
+
z2.object({
|
|
5620
|
+
...handoffShared,
|
|
5621
|
+
event: z2.literal("raised"),
|
|
5622
|
+
requested_decision: z2.string().nullable()
|
|
5623
|
+
}).strict(),
|
|
5624
|
+
z2.object({
|
|
5625
|
+
...handoffShared,
|
|
5626
|
+
event: z2.literal("resolved"),
|
|
5627
|
+
resolution: z2.string().nullable(),
|
|
5628
|
+
resolved_by: z2.string().nullable()
|
|
5629
|
+
}).strict()
|
|
5630
|
+
]);
|
|
5631
|
+
var EffectTargetSchema = z2.object({
|
|
5632
|
+
kind: z2.string(),
|
|
5633
|
+
ref: z2.string()
|
|
5634
|
+
}).strict();
|
|
5635
|
+
var effectShared = {
|
|
5636
|
+
meta: HeaderMetaSchema,
|
|
5637
|
+
subject: SubjectSchema,
|
|
5638
|
+
kind: z2.literal("effect"),
|
|
5639
|
+
...envelopeTail,
|
|
5640
|
+
effect_id: z2.string(),
|
|
5641
|
+
target: EffectTargetSchema
|
|
5642
|
+
};
|
|
5643
|
+
var EffectEventSchema = z2.discriminatedUnion("event", [
|
|
5644
|
+
z2.object({ ...effectShared, event: z2.literal("attempted") }).strict(),
|
|
5645
|
+
z2.object({ ...effectShared, event: z2.literal("observed"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict(),
|
|
5646
|
+
z2.object({ ...effectShared, event: z2.literal("verified"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict()
|
|
5647
|
+
]);
|
|
5648
|
+
var LogEventSchema = z2.union([
|
|
5649
|
+
DispatchEventSchema,
|
|
5650
|
+
DevReviewLoopEventSchema,
|
|
5651
|
+
ForgeWriteEventSchema,
|
|
5652
|
+
GateEventSchema,
|
|
5653
|
+
OperationEventSchema,
|
|
5654
|
+
UsageEventSchema,
|
|
5655
|
+
RoleAttemptEventSchema,
|
|
5656
|
+
HandoffEventSchema,
|
|
5657
|
+
EffectEventSchema
|
|
5658
|
+
]);
|
|
5659
|
+
// ../../packages/aeg-core/src/dev-review-loop/journal-reconstruction.ts
|
|
5660
|
+
var STOPPED_CONDITIONS = new Set(["confidence", "reappearance", "no_progress", "max_rounds"]);
|
|
5661
|
+
// ../../packages/aeg-core/src/control-store/records.ts
|
|
5662
|
+
import { z as z3 } from "zod";
|
|
5663
|
+
var isoTimestamp = z3.string().min(1);
|
|
5664
|
+
var taskId = z3.number().int().positive();
|
|
5665
|
+
var epochNumber = z3.number().int().nonnegative();
|
|
5666
|
+
var RunRecordSchema = z3.object({
|
|
5667
|
+
version: z3.literal(1),
|
|
5668
|
+
kind: z3.literal("run"),
|
|
5669
|
+
task: taskId,
|
|
5670
|
+
runId: z3.string().min(1),
|
|
5671
|
+
pid: z3.number().int().positive(),
|
|
5672
|
+
host: z3.string().min(1),
|
|
5673
|
+
startedAt: isoTimestamp
|
|
5674
|
+
}).strict();
|
|
5675
|
+
var InputRecordSchema = z3.object({
|
|
5676
|
+
version: z3.literal(1),
|
|
5677
|
+
kind: z3.literal("input"),
|
|
5678
|
+
task: taskId,
|
|
5679
|
+
runId: z3.string().min(1),
|
|
5680
|
+
source: z3.union([z3.literal("fresh"), z3.literal("resume")]),
|
|
5681
|
+
pr: z3.number().int().positive().nullable(),
|
|
5682
|
+
round: z3.number().int().nonnegative(),
|
|
5683
|
+
recordedAt: isoTimestamp
|
|
5684
|
+
}).strict();
|
|
5685
|
+
var OwnershipRecordSchema = z3.object({
|
|
5686
|
+
version: z3.literal(1),
|
|
5687
|
+
kind: z3.literal("ownership"),
|
|
5688
|
+
task: taskId,
|
|
5689
|
+
epoch: epochNumber,
|
|
5690
|
+
ownerId: z3.string().min(1),
|
|
5691
|
+
pid: z3.number().int().positive(),
|
|
5692
|
+
host: z3.string().min(1),
|
|
5693
|
+
acquiredAt: isoTimestamp
|
|
5694
|
+
}).strict();
|
|
5695
|
+
var TransitionRecordSchema = z3.object({
|
|
5696
|
+
version: z3.literal(1),
|
|
5697
|
+
kind: z3.literal("transition"),
|
|
5698
|
+
task: taskId,
|
|
5699
|
+
epoch: epochNumber,
|
|
5700
|
+
seq: z3.number().int().nonnegative(),
|
|
5701
|
+
from: z3.string().min(1),
|
|
5702
|
+
to: z3.string().min(1),
|
|
5703
|
+
detail: z3.string().optional(),
|
|
5704
|
+
at: isoTimestamp
|
|
5705
|
+
}).strict();
|
|
5077
5706
|
// src/lib/config.ts
|
|
5078
5707
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
5079
5708
|
import { existsSync, mkdirSync, readFileSync as readFileSync2, realpathSync, writeFileSync } from "node:fs";
|
|
5080
5709
|
import { homedir } from "node:os";
|
|
5081
5710
|
import { dirname, join, resolve } from "node:path";
|
|
5082
|
-
import { z as
|
|
5711
|
+
import { z as z4 } from "zod";
|
|
5083
5712
|
|
|
5084
5713
|
// src/lib/agent-vendors.ts
|
|
5085
5714
|
var AGENT_VENDORS = ["skills", "claude", "gemini"];
|
|
5086
5715
|
|
|
5087
5716
|
// src/lib/config.ts
|
|
5088
|
-
var EnvEntrySchema =
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5717
|
+
var EnvEntrySchema = z4.union([
|
|
5718
|
+
z4.literal(true),
|
|
5719
|
+
z4.object({ optional: z4.literal(true) }),
|
|
5720
|
+
z4.object({ anyOf: z4.array(z4.string()).min(2) }),
|
|
5721
|
+
z4.string()
|
|
5093
5722
|
]);
|
|
5094
|
-
var CheckEntrySchema =
|
|
5095
|
-
run:
|
|
5096
|
-
scope:
|
|
5097
|
-
include:
|
|
5098
|
-
args:
|
|
5099
|
-
timeoutMs:
|
|
5100
|
-
env:
|
|
5101
|
-
requiresOpenPr:
|
|
5102
|
-
ownWorkflow:
|
|
5723
|
+
var CheckEntrySchema = z4.object({
|
|
5724
|
+
run: z4.string(),
|
|
5725
|
+
scope: z4.enum(["diff", "full"]),
|
|
5726
|
+
include: z4.array(z4.string()).optional(),
|
|
5727
|
+
args: z4.array(z4.string()).optional(),
|
|
5728
|
+
timeoutMs: z4.number().optional(),
|
|
5729
|
+
env: z4.record(z4.string(), EnvEntrySchema).optional(),
|
|
5730
|
+
requiresOpenPr: z4.boolean().optional(),
|
|
5731
|
+
ownWorkflow: z4.boolean().optional(),
|
|
5732
|
+
principalOwed: z4.literal(true).optional(),
|
|
5733
|
+
validates: z4.enum(["body", "issue"]).optional()
|
|
5103
5734
|
}).superRefine((entry2, ctx) => {
|
|
5104
5735
|
if (!entry2.env)
|
|
5105
5736
|
return;
|
|
@@ -5109,25 +5740,26 @@ var CheckEntrySchema = z2.object({
|
|
|
5109
5740
|
const members = value.anyOf;
|
|
5110
5741
|
if (new Set(members).size !== members.length) {
|
|
5111
5742
|
ctx.addIssue({
|
|
5112
|
-
code:
|
|
5743
|
+
code: z4.ZodIssueCode.custom,
|
|
5113
5744
|
message: `env.${key}.anyOf has duplicate members`,
|
|
5114
5745
|
path: ["env", key, "anyOf"]
|
|
5115
5746
|
});
|
|
5116
5747
|
}
|
|
5117
5748
|
if (!members.includes(key)) {
|
|
5118
5749
|
ctx.addIssue({
|
|
5119
|
-
code:
|
|
5750
|
+
code: z4.ZodIssueCode.custom,
|
|
5120
5751
|
message: `env.${key}.anyOf must include "${key}" itself as a member`,
|
|
5121
5752
|
path: ["env", key, "anyOf"]
|
|
5122
5753
|
});
|
|
5123
5754
|
}
|
|
5124
5755
|
}
|
|
5125
5756
|
});
|
|
5126
|
-
var RoleEntrySchema =
|
|
5127
|
-
contract:
|
|
5757
|
+
var RoleEntrySchema = z4.object({
|
|
5758
|
+
contract: z4.string().refine((p) => p.includes("/"), {
|
|
5128
5759
|
message: 'must be a path (contain at least one "/"), not a bare filename'
|
|
5129
5760
|
})
|
|
5130
5761
|
});
|
|
5762
|
+
var MAX_REPORT_COMMAND_TIMEOUT_MS = 3600000;
|
|
5131
5763
|
var BRIEF_BUILTINS = [
|
|
5132
5764
|
"tier",
|
|
5133
5765
|
"testPlan",
|
|
@@ -5147,17 +5779,17 @@ var BRIEF_BUILTINS = [
|
|
|
5147
5779
|
"briefSections",
|
|
5148
5780
|
"milestoneShape"
|
|
5149
5781
|
];
|
|
5150
|
-
var BriefSectionSchema =
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5782
|
+
var BriefSectionSchema = z4.union([
|
|
5783
|
+
z4.object({ builtin: z4.enum(BRIEF_BUILTINS) }),
|
|
5784
|
+
z4.object({ heading: z4.string().min(1), name: z4.string().optional() }),
|
|
5785
|
+
z4.object({ field: z4.string().min(1), name: z4.string().optional() }),
|
|
5786
|
+
z4.object({ phrase: z4.string().min(1), name: z4.string().optional() })
|
|
5155
5787
|
]);
|
|
5156
|
-
var BriefSchemaSchema =
|
|
5157
|
-
pr:
|
|
5158
|
-
issue:
|
|
5159
|
-
milestone:
|
|
5160
|
-
ack:
|
|
5788
|
+
var BriefSchemaSchema = z4.object({
|
|
5789
|
+
pr: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
|
|
5790
|
+
issue: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
|
|
5791
|
+
milestone: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
|
|
5792
|
+
ack: z4.array(z4.enum(BRIEF_BUILTINS)).optional()
|
|
5161
5793
|
});
|
|
5162
5794
|
function isSafeRepoRelPath(p) {
|
|
5163
5795
|
if (p.length === 0)
|
|
@@ -5166,7 +5798,7 @@ function isSafeRepoRelPath(p) {
|
|
|
5166
5798
|
return false;
|
|
5167
5799
|
return p.split(/[\\/]/).every((seg) => seg !== ".." && seg !== "");
|
|
5168
5800
|
}
|
|
5169
|
-
var SafeRepoRelPath =
|
|
5801
|
+
var SafeRepoRelPath = z4.string().refine(isSafeRepoRelPath, {
|
|
5170
5802
|
message: "must be a repo-root-relative path with no `..` segment or absolute root"
|
|
5171
5803
|
});
|
|
5172
5804
|
var CANONICAL_HOOK_BLOCK_PREFIXES = [".git/", ".husky/", ".vinaya/hooks/", ".claude/hooks/"];
|
|
@@ -5176,22 +5808,22 @@ function isCanonicalHookBlockPath(p) {
|
|
|
5176
5808
|
var ManagedHookBlockPath = SafeRepoRelPath.refine(isCanonicalHookBlockPath, {
|
|
5177
5809
|
message: `must start with one of ${CANONICAL_HOOK_BLOCK_PREFIXES.join(", ")} (byte-exact, case-sensitive)`
|
|
5178
5810
|
});
|
|
5179
|
-
var ManagedBlockRecordSchema =
|
|
5811
|
+
var ManagedBlockRecordSchema = z4.object({
|
|
5180
5812
|
path: ManagedHookBlockPath,
|
|
5181
|
-
marker:
|
|
5182
|
-
comment:
|
|
5813
|
+
marker: z4.string(),
|
|
5814
|
+
comment: z4.enum(["hash", "html"])
|
|
5183
5815
|
});
|
|
5184
|
-
var ManagedManifestSchema =
|
|
5185
|
-
version:
|
|
5186
|
-
files:
|
|
5187
|
-
blocks:
|
|
5188
|
-
labels:
|
|
5189
|
-
agents:
|
|
5816
|
+
var ManagedManifestSchema = z4.object({
|
|
5817
|
+
version: z4.number().int().positive(),
|
|
5818
|
+
files: z4.array(SafeRepoRelPath),
|
|
5819
|
+
blocks: z4.array(ManagedBlockRecordSchema),
|
|
5820
|
+
labels: z4.array(z4.string()),
|
|
5821
|
+
agents: z4.array(z4.enum(AGENT_VENDORS)).optional()
|
|
5190
5822
|
});
|
|
5191
|
-
var ProjectEntrySchema =
|
|
5192
|
-
name:
|
|
5193
|
-
description:
|
|
5194
|
-
path:
|
|
5823
|
+
var ProjectEntrySchema = z4.object({
|
|
5824
|
+
name: z4.string().min(1),
|
|
5825
|
+
description: z4.string().min(1).optional(),
|
|
5826
|
+
path: z4.string().min(1).optional()
|
|
5195
5827
|
});
|
|
5196
5828
|
function parseTokensCollectDeclaration(value) {
|
|
5197
5829
|
const m = value.trim().match(/^(\S+)\s+(\S+)$/);
|
|
@@ -5202,34 +5834,55 @@ function parseTokensCollectDeclaration(value) {
|
|
|
5202
5834
|
return null;
|
|
5203
5835
|
return { interpreter, script };
|
|
5204
5836
|
}
|
|
5205
|
-
var VinayaConfigSchema =
|
|
5206
|
-
rings:
|
|
5207
|
-
ring1_forgeWriteInterception:
|
|
5208
|
-
ring2_asyncAudits:
|
|
5837
|
+
var VinayaConfigSchema = z4.object({
|
|
5838
|
+
rings: z4.object({
|
|
5839
|
+
ring1_forgeWriteInterception: z4.boolean(),
|
|
5840
|
+
ring2_asyncAudits: z4.boolean()
|
|
5209
5841
|
}).optional(),
|
|
5210
|
-
checks:
|
|
5211
|
-
roles:
|
|
5842
|
+
checks: z4.record(z4.string(), CheckEntrySchema).optional(),
|
|
5843
|
+
roles: z4.record(z4.string(), RoleEntrySchema).optional(),
|
|
5212
5844
|
briefSchema: BriefSchemaSchema.optional(),
|
|
5213
5845
|
managed: ManagedManifestSchema.optional(),
|
|
5214
|
-
principals:
|
|
5215
|
-
releaseActor:
|
|
5216
|
-
ci:
|
|
5217
|
-
tokens:
|
|
5218
|
-
collect:
|
|
5846
|
+
principals: z4.array(z4.string()).min(1).optional(),
|
|
5847
|
+
releaseActor: z4.string().min(1).optional(),
|
|
5848
|
+
ci: z4.object({ setup: z4.string().min(1) }).optional(),
|
|
5849
|
+
tokens: z4.object({
|
|
5850
|
+
collect: z4.string().min(1).refine((v) => parseTokensCollectDeclaration(v) !== null, {
|
|
5219
5851
|
message: 'tokens.collect must be exactly "<interpreter> <repo-relative-script-path>" — two whitespace-separated tokens, no flags, no shell syntax, no quoting'
|
|
5220
5852
|
})
|
|
5221
5853
|
}).optional(),
|
|
5222
|
-
blastRadius:
|
|
5223
|
-
proseGates:
|
|
5224
|
-
doctrineRoot:
|
|
5225
|
-
readerFacingPrefix:
|
|
5226
|
-
readerFacingSuffix:
|
|
5227
|
-
legacySlugDir:
|
|
5854
|
+
blastRadius: z4.object({ extraDomains: z4.array(z4.string()).optional() }).optional(),
|
|
5855
|
+
proseGates: z4.object({
|
|
5856
|
+
doctrineRoot: z4.string().min(1).optional(),
|
|
5857
|
+
readerFacingPrefix: z4.string().min(1).optional(),
|
|
5858
|
+
readerFacingSuffix: z4.string().min(1).optional(),
|
|
5859
|
+
legacySlugDir: z4.string().min(1).optional()
|
|
5228
5860
|
}).optional(),
|
|
5229
|
-
projects:
|
|
5230
|
-
dispatch:
|
|
5231
|
-
timeoutMs:
|
|
5232
|
-
|
|
5861
|
+
projects: z4.array(ProjectEntrySchema).optional(),
|
|
5862
|
+
dispatch: z4.object({
|
|
5863
|
+
timeoutMs: z4.number().int().positive().optional(),
|
|
5864
|
+
killGraceMs: z4.number().int().positive().optional(),
|
|
5865
|
+
agent: z4.enum(["claude", "codex", "gemini"]).optional()
|
|
5866
|
+
}).optional(),
|
|
5867
|
+
reviewPolicy: z4.object({
|
|
5868
|
+
codeReviewThreshold: z4.string().min(1).optional(),
|
|
5869
|
+
securityThreshold: z4.string().min(1).optional(),
|
|
5870
|
+
maxRounds: z4.number().optional()
|
|
5871
|
+
}).optional(),
|
|
5872
|
+
prePush: z4.object({
|
|
5873
|
+
alwaysRun: z4.array(z4.string()).optional()
|
|
5874
|
+
}).optional(),
|
|
5875
|
+
report: z4.object({
|
|
5876
|
+
commandTimeoutMs: z4.number().int().positive().optional()
|
|
5877
|
+
}).superRefine((report, ctx) => {
|
|
5878
|
+
const commandTimeoutMs = report.commandTimeoutMs;
|
|
5879
|
+
if (commandTimeoutMs !== undefined && commandTimeoutMs > MAX_REPORT_COMMAND_TIMEOUT_MS) {
|
|
5880
|
+
ctx.addIssue({
|
|
5881
|
+
code: z4.ZodIssueCode.custom,
|
|
5882
|
+
path: ["commandTimeoutMs"],
|
|
5883
|
+
message: "must be at most 3600000 (1 hour)"
|
|
5884
|
+
});
|
|
5885
|
+
}
|
|
5233
5886
|
}).optional()
|
|
5234
5887
|
});
|
|
5235
5888
|
var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
|
|
@@ -5304,6 +5957,28 @@ function resolvePrincipalAllowlist(config) {
|
|
|
5304
5957
|
function resolveReleaseActor(config) {
|
|
5305
5958
|
return config?.releaseActor ?? DEFAULT_RELEASE_ACTOR;
|
|
5306
5959
|
}
|
|
5960
|
+
function resolveReviewPolicy(config) {
|
|
5961
|
+
const raw = config?.reviewPolicy;
|
|
5962
|
+
if (!raw)
|
|
5963
|
+
return DEFAULT_REVIEW_POLICY;
|
|
5964
|
+
const codeReviewThreshold = raw.codeReviewThreshold ?? DEFAULT_REVIEW_POLICY.codeReviewThreshold;
|
|
5965
|
+
if (!isKnownSeverity(CODE_REVIEW_SEVERITY_ORDER, codeReviewThreshold)) {
|
|
5966
|
+
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.`);
|
|
5967
|
+
}
|
|
5968
|
+
const securityThreshold = raw.securityThreshold ?? DEFAULT_REVIEW_POLICY.securityThreshold;
|
|
5969
|
+
if (!isKnownSeverity(SECURITY_SEVERITY_ORDER, securityThreshold)) {
|
|
5970
|
+
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.`);
|
|
5971
|
+
}
|
|
5972
|
+
const maxRounds = raw.maxRounds ?? DEFAULT_REVIEW_POLICY.maxRounds;
|
|
5973
|
+
if (!Number.isInteger(maxRounds) || maxRounds < 1) {
|
|
5974
|
+
throw new Error(`vinaya.config.json: reviewPolicy.maxRounds "${maxRounds}" is not a positive integer — fix the config, this never falls back to a default.`);
|
|
5975
|
+
}
|
|
5976
|
+
return {
|
|
5977
|
+
codeReviewThreshold,
|
|
5978
|
+
securityThreshold,
|
|
5979
|
+
maxRounds
|
|
5980
|
+
};
|
|
5981
|
+
}
|
|
5307
5982
|
function trustAnchorRepo() {
|
|
5308
5983
|
const wellFormed = (slug) => /^[^/\s]+\/[^/\s]+$/.test(slug) ? slug : null;
|
|
5309
5984
|
const fromRunner = process.env.GITHUB_REPOSITORY?.trim();
|
|
@@ -5383,6 +6058,9 @@ function loadConfig() {
|
|
|
5383
6058
|
}
|
|
5384
6059
|
var TOKENS_COLLECT_TRUST_PATH = join(GLOBAL_VINAYA_HOME, "tokens-collect-trust.json");
|
|
5385
6060
|
|
|
6061
|
+
// src/lib/brief-assembly.ts
|
|
6062
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
6063
|
+
|
|
5386
6064
|
// ../../packages/sources/src/commands.ts
|
|
5387
6065
|
var COMMANDS = [
|
|
5388
6066
|
{
|
|
@@ -5541,6 +6219,17 @@ var COMMANDS = [
|
|
|
5541
6219
|
],
|
|
5542
6220
|
status: "shipped"
|
|
5543
6221
|
},
|
|
6222
|
+
{
|
|
6223
|
+
name: "task status",
|
|
6224
|
+
description: "Every open task with a frozen brief, its pull request, and whether its loop is running, paused, or published",
|
|
6225
|
+
flags: [{ flag: "--json", description: "Enveloped JSON output (schema: 1)" }],
|
|
6226
|
+
details: [
|
|
6227
|
+
"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`.",
|
|
6228
|
+
"`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.",
|
|
6229
|
+
"`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."
|
|
6230
|
+
],
|
|
6231
|
+
status: "shipped"
|
|
6232
|
+
},
|
|
5544
6233
|
{
|
|
5545
6234
|
name: "pr create",
|
|
5546
6235
|
description: "Open a pull request after full brief-schema validation",
|
|
@@ -6050,10 +6739,10 @@ function createForgeSource(config) {
|
|
|
6050
6739
|
};
|
|
6051
6740
|
}
|
|
6052
6741
|
// ../../packages/sources/src/select-source.ts
|
|
6053
|
-
import { z as
|
|
6054
|
-
var StateSourceConfigSchema =
|
|
6055
|
-
|
|
6056
|
-
|
|
6742
|
+
import { z as z5 } from "zod";
|
|
6743
|
+
var StateSourceConfigSchema = z5.discriminatedUnion("kind", [
|
|
6744
|
+
z5.object({ kind: z5.literal("forge"), owner: z5.string(), repo: z5.string() }),
|
|
6745
|
+
z5.object({ kind: z5.literal("file"), root: z5.string().optional() })
|
|
6057
6746
|
]);
|
|
6058
6747
|
// src/checks/edge-resolve.ts
|
|
6059
6748
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
@@ -6173,6 +6862,589 @@ async function resolveEdge(id, taskById, factsByTaskId, repo, resolveSibling = d
|
|
|
6173
6862
|
};
|
|
6174
6863
|
}
|
|
6175
6864
|
|
|
6865
|
+
// src/lib/brief-assembly.ts
|
|
6866
|
+
function git(args) {
|
|
6867
|
+
try {
|
|
6868
|
+
return execFileSync4("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
6869
|
+
} catch {
|
|
6870
|
+
return "";
|
|
6871
|
+
}
|
|
6872
|
+
}
|
|
6873
|
+
function expandGlob(glob) {
|
|
6874
|
+
const out = git(["ls-files", "--", glob]);
|
|
6875
|
+
return out.split(`
|
|
6876
|
+
`).map((s) => s.trim()).filter(Boolean);
|
|
6877
|
+
}
|
|
6878
|
+
var DRAFT_ISSUE_SENTINEL = Number.MAX_SAFE_INTEGER;
|
|
6879
|
+
|
|
6880
|
+
// src/checks/registry.ts
|
|
6881
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
6882
|
+
import { join as join3 } from "node:path";
|
|
6883
|
+
|
|
6884
|
+
// src/lib/package-root.ts
|
|
6885
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
6886
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
6887
|
+
import { fileURLToPath } from "node:url";
|
|
6888
|
+
function packageRoot(moduleUrl) {
|
|
6889
|
+
let dir = dirname2(fileURLToPath(moduleUrl));
|
|
6890
|
+
while (!existsSync2(join2(dir, "package.json"))) {
|
|
6891
|
+
if (existsSync2(join2(dir, ".git")))
|
|
6892
|
+
break;
|
|
6893
|
+
const parent = dirname2(dir);
|
|
6894
|
+
if (parent === dir)
|
|
6895
|
+
break;
|
|
6896
|
+
dir = parent;
|
|
6897
|
+
}
|
|
6898
|
+
return dir;
|
|
6899
|
+
}
|
|
6900
|
+
|
|
6901
|
+
// src/checks/registry.ts
|
|
6902
|
+
var DIST_BIN_DIR = join3(packageRoot(import.meta.url), "dist", "checks", "bin");
|
|
6903
|
+
var SRC_BIN_DIR = join3(packageRoot(import.meta.url), "src", "checks", "bin");
|
|
6904
|
+
var BIN_DIR = existsSync3(DIST_BIN_DIR) ? DIST_BIN_DIR : SRC_BIN_DIR;
|
|
6905
|
+
var BIN_EXT = BIN_DIR === DIST_BIN_DIR ? ".js" : ".ts";
|
|
6906
|
+
function bin(name) {
|
|
6907
|
+
return join3(BIN_DIR, `${name}${BIN_EXT}`);
|
|
6908
|
+
}
|
|
6909
|
+
var REGISTRY = [
|
|
6910
|
+
[
|
|
6911
|
+
{
|
|
6912
|
+
name: "brief-shape",
|
|
6913
|
+
validates: "body",
|
|
6914
|
+
run: bin("check-brief-shape"),
|
|
6915
|
+
scope: "diff",
|
|
6916
|
+
timeoutMs: 15000,
|
|
6917
|
+
env: {
|
|
6918
|
+
PR_BODY: { optional: true },
|
|
6919
|
+
BRANCH: { optional: true },
|
|
6920
|
+
PR_NUMBER: { optional: true },
|
|
6921
|
+
GITHUB_TOKEN: { optional: true },
|
|
6922
|
+
GH_TOKEN: { optional: true }
|
|
6923
|
+
}
|
|
6924
|
+
},
|
|
6925
|
+
0
|
|
6926
|
+
],
|
|
6927
|
+
[
|
|
6928
|
+
{
|
|
6929
|
+
name: "pr-report-density",
|
|
6930
|
+
validates: "body",
|
|
6931
|
+
run: bin("check-pr-report-density"),
|
|
6932
|
+
scope: "diff",
|
|
6933
|
+
timeoutMs: 15000,
|
|
6934
|
+
env: {
|
|
6935
|
+
PR_BODY: { optional: true }
|
|
6936
|
+
}
|
|
6937
|
+
},
|
|
6938
|
+
0
|
|
6939
|
+
],
|
|
6940
|
+
[
|
|
6941
|
+
{
|
|
6942
|
+
name: "doc-coverage",
|
|
6943
|
+
validates: "body",
|
|
6944
|
+
run: bin("check-doc-coverage"),
|
|
6945
|
+
scope: "diff",
|
|
6946
|
+
timeoutMs: 15000,
|
|
6947
|
+
env: {
|
|
6948
|
+
BASE_SHA: { optional: true },
|
|
6949
|
+
PR_BODY: { optional: true },
|
|
6950
|
+
PR_BODY_FILE: { optional: true },
|
|
6951
|
+
PR_NUMBER: { optional: true },
|
|
6952
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
6953
|
+
GITHUB_TOKEN: { optional: true },
|
|
6954
|
+
GH_TOKEN: { optional: true }
|
|
6955
|
+
}
|
|
6956
|
+
},
|
|
6957
|
+
0
|
|
6958
|
+
],
|
|
6959
|
+
[
|
|
6960
|
+
{
|
|
6961
|
+
name: "coherence",
|
|
6962
|
+
run: bin("check-coherence"),
|
|
6963
|
+
scope: "full",
|
|
6964
|
+
timeoutMs: 30000,
|
|
6965
|
+
env: {
|
|
6966
|
+
AEG_REPO: { optional: true },
|
|
6967
|
+
BRANCH: { optional: true },
|
|
6968
|
+
GITHUB_TOKEN: { optional: true },
|
|
6969
|
+
GH_TOKEN: { optional: true }
|
|
6970
|
+
}
|
|
6971
|
+
},
|
|
6972
|
+
0
|
|
6973
|
+
],
|
|
6974
|
+
[
|
|
6975
|
+
{
|
|
6976
|
+
name: "pr-premise-reassert",
|
|
6977
|
+
validates: "body",
|
|
6978
|
+
run: bin("check-pr-premise-reassert"),
|
|
6979
|
+
scope: "diff",
|
|
6980
|
+
timeoutMs: 15000,
|
|
6981
|
+
env: {
|
|
6982
|
+
PR_BODY: { optional: true }
|
|
6983
|
+
}
|
|
6984
|
+
},
|
|
6985
|
+
0
|
|
6986
|
+
],
|
|
6987
|
+
[
|
|
6988
|
+
{
|
|
6989
|
+
name: "dispatch-readiness",
|
|
6990
|
+
run: bin("check-dispatch-readiness"),
|
|
6991
|
+
scope: "full",
|
|
6992
|
+
timeoutMs: 30000,
|
|
6993
|
+
env: {
|
|
6994
|
+
AEG_REPO: { optional: true },
|
|
6995
|
+
BRANCH: { optional: true },
|
|
6996
|
+
GITHUB_TOKEN: { optional: true },
|
|
6997
|
+
GH_TOKEN: { optional: true },
|
|
6998
|
+
PREMISE_FILE: { optional: true }
|
|
6999
|
+
}
|
|
7000
|
+
},
|
|
7001
|
+
0
|
|
7002
|
+
],
|
|
7003
|
+
[
|
|
7004
|
+
{
|
|
7005
|
+
name: "closes-n",
|
|
7006
|
+
validates: "body",
|
|
7007
|
+
run: bin("check-closes-n"),
|
|
7008
|
+
scope: "diff",
|
|
7009
|
+
timeoutMs: 15000,
|
|
7010
|
+
requiresOpenPr: true,
|
|
7011
|
+
env: {
|
|
7012
|
+
BRANCH: { optional: true },
|
|
7013
|
+
PR_BODY: { optional: true },
|
|
7014
|
+
AEG_REPO: { optional: true },
|
|
7015
|
+
GITHUB_TOKEN: { optional: true },
|
|
7016
|
+
GH_TOKEN: { optional: true }
|
|
7017
|
+
}
|
|
7018
|
+
},
|
|
7019
|
+
1
|
|
7020
|
+
],
|
|
7021
|
+
[
|
|
7022
|
+
{
|
|
7023
|
+
name: "single-plan-pr",
|
|
7024
|
+
run: bin("check-single-plan-pr"),
|
|
7025
|
+
scope: "diff",
|
|
7026
|
+
timeoutMs: 15000,
|
|
7027
|
+
env: {
|
|
7028
|
+
BASE_SHA: { optional: true },
|
|
7029
|
+
PR_NUMBER: { optional: true },
|
|
7030
|
+
GITHUB_TOKEN: { optional: true },
|
|
7031
|
+
GH_TOKEN: { optional: true }
|
|
7032
|
+
}
|
|
7033
|
+
},
|
|
7034
|
+
0
|
|
7035
|
+
],
|
|
7036
|
+
[
|
|
7037
|
+
{
|
|
7038
|
+
name: "surface-scope",
|
|
7039
|
+
run: bin("check-surface-scope"),
|
|
7040
|
+
scope: "diff",
|
|
7041
|
+
timeoutMs: 15000,
|
|
7042
|
+
env: {
|
|
7043
|
+
AEG_REPO: { optional: true },
|
|
7044
|
+
BASE_SHA: { optional: true },
|
|
7045
|
+
BRANCH: { optional: true },
|
|
7046
|
+
GITHUB_TOKEN: { optional: true },
|
|
7047
|
+
GH_TOKEN: { optional: true }
|
|
7048
|
+
}
|
|
7049
|
+
},
|
|
7050
|
+
0
|
|
7051
|
+
],
|
|
7052
|
+
[
|
|
7053
|
+
{
|
|
7054
|
+
name: "test-plan",
|
|
7055
|
+
validates: "body",
|
|
7056
|
+
run: bin("check-test-plan"),
|
|
7057
|
+
scope: "diff",
|
|
7058
|
+
timeoutMs: 15000,
|
|
7059
|
+
requiresOpenPr: true,
|
|
7060
|
+
principalOwed: true,
|
|
7061
|
+
env: {
|
|
7062
|
+
PR_BODY: { optional: true },
|
|
7063
|
+
BRANCH: { optional: true }
|
|
7064
|
+
}
|
|
7065
|
+
},
|
|
7066
|
+
1
|
|
7067
|
+
],
|
|
7068
|
+
[
|
|
7069
|
+
{
|
|
7070
|
+
name: "body-bare-digits",
|
|
7071
|
+
validates: "body",
|
|
7072
|
+
run: bin("check-body-bare-digits"),
|
|
7073
|
+
scope: "diff",
|
|
7074
|
+
timeoutMs: 15000,
|
|
7075
|
+
requiresOpenPr: true,
|
|
7076
|
+
ownWorkflow: true,
|
|
7077
|
+
env: {
|
|
7078
|
+
PR_BODY: { optional: true },
|
|
7079
|
+
PR_NUMBER: { optional: true },
|
|
7080
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
7081
|
+
GITHUB_TOKEN: { optional: true },
|
|
7082
|
+
GH_TOKEN: { optional: true }
|
|
7083
|
+
}
|
|
7084
|
+
},
|
|
7085
|
+
1
|
|
7086
|
+
],
|
|
7087
|
+
[
|
|
7088
|
+
{
|
|
7089
|
+
name: "token-report",
|
|
7090
|
+
validates: "body",
|
|
7091
|
+
run: bin("check-token-report"),
|
|
7092
|
+
scope: "diff",
|
|
7093
|
+
timeoutMs: 15000,
|
|
7094
|
+
requiresOpenPr: true,
|
|
7095
|
+
env: {
|
|
7096
|
+
PR_BODY: { optional: true },
|
|
7097
|
+
BRANCH: { optional: true },
|
|
7098
|
+
CLAUDE_PROJECT_DIR: { optional: true },
|
|
7099
|
+
CLAUDE_CODE_SESSION_ID: { optional: true }
|
|
7100
|
+
}
|
|
7101
|
+
},
|
|
7102
|
+
1
|
|
7103
|
+
],
|
|
7104
|
+
[
|
|
7105
|
+
{
|
|
7106
|
+
name: "no-disk-state",
|
|
7107
|
+
run: bin("check-no-disk-state"),
|
|
7108
|
+
scope: "diff",
|
|
7109
|
+
timeoutMs: 15000,
|
|
7110
|
+
env: { BASE_SHA: { optional: true } }
|
|
7111
|
+
},
|
|
7112
|
+
0
|
|
7113
|
+
],
|
|
7114
|
+
[
|
|
7115
|
+
{
|
|
7116
|
+
name: "registry-gates",
|
|
7117
|
+
run: bin("check-registry-gates"),
|
|
7118
|
+
scope: "full",
|
|
7119
|
+
timeoutMs: 30000,
|
|
7120
|
+
env: {
|
|
7121
|
+
AEG_REPO: { optional: true },
|
|
7122
|
+
GITHUB_TOKEN: { optional: true },
|
|
7123
|
+
GH_TOKEN: { optional: true }
|
|
7124
|
+
}
|
|
7125
|
+
},
|
|
7126
|
+
0
|
|
7127
|
+
],
|
|
7128
|
+
[
|
|
7129
|
+
{
|
|
7130
|
+
name: "review-gate",
|
|
7131
|
+
run: bin("check-review-gate"),
|
|
7132
|
+
scope: "full",
|
|
7133
|
+
timeoutMs: 30000,
|
|
7134
|
+
ownWorkflow: true,
|
|
7135
|
+
env: {
|
|
7136
|
+
PR_NUMBER: { optional: true },
|
|
7137
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
7138
|
+
GITHUB_TOKEN: { optional: true },
|
|
7139
|
+
GH_TOKEN: { optional: true }
|
|
7140
|
+
}
|
|
7141
|
+
},
|
|
7142
|
+
1
|
|
7143
|
+
],
|
|
7144
|
+
[
|
|
7145
|
+
{
|
|
7146
|
+
name: "branch-topology",
|
|
7147
|
+
run: bin("check-branch-topology"),
|
|
7148
|
+
scope: "full",
|
|
7149
|
+
timeoutMs: 30000,
|
|
7150
|
+
env: {
|
|
7151
|
+
BRANCH: { optional: true },
|
|
7152
|
+
AEG_REPO: { optional: true },
|
|
7153
|
+
GITHUB_TOKEN: { optional: true },
|
|
7154
|
+
GH_TOKEN: { optional: true }
|
|
7155
|
+
}
|
|
7156
|
+
},
|
|
7157
|
+
0
|
|
7158
|
+
],
|
|
7159
|
+
[
|
|
7160
|
+
{
|
|
7161
|
+
name: "dead-branch-push",
|
|
7162
|
+
run: bin("check-dead-branch-push"),
|
|
7163
|
+
scope: "full",
|
|
7164
|
+
timeoutMs: 30000,
|
|
7165
|
+
env: {
|
|
7166
|
+
BRANCH: { optional: true },
|
|
7167
|
+
GITHUB_TOKEN: { optional: true },
|
|
7168
|
+
GH_TOKEN: { optional: true }
|
|
7169
|
+
}
|
|
7170
|
+
},
|
|
7171
|
+
0
|
|
7172
|
+
],
|
|
7173
|
+
[
|
|
7174
|
+
{
|
|
7175
|
+
name: "first-push-dispatch",
|
|
7176
|
+
run: bin("check-first-push-dispatch"),
|
|
7177
|
+
scope: "full",
|
|
7178
|
+
timeoutMs: 30000,
|
|
7179
|
+
env: {
|
|
7180
|
+
BRANCH: { optional: true },
|
|
7181
|
+
AEG_REPO: { optional: true },
|
|
7182
|
+
GITHUB_TOKEN: { optional: true },
|
|
7183
|
+
GH_TOKEN: { optional: true }
|
|
7184
|
+
}
|
|
7185
|
+
},
|
|
7186
|
+
0
|
|
7187
|
+
],
|
|
7188
|
+
[
|
|
7189
|
+
{
|
|
7190
|
+
name: "doc-coverage-push",
|
|
7191
|
+
validates: "body",
|
|
7192
|
+
run: bin("check-doc-coverage-push"),
|
|
7193
|
+
scope: "diff",
|
|
7194
|
+
timeoutMs: 15000,
|
|
7195
|
+
env: {
|
|
7196
|
+
OVERRIDE_DOCS: { optional: true },
|
|
7197
|
+
BASE_SHA: { optional: true },
|
|
7198
|
+
PR_BODY: { optional: true },
|
|
7199
|
+
PR_BODY_FILE: { optional: true },
|
|
7200
|
+
PR_LABELS: { optional: true },
|
|
7201
|
+
WAIVER_LABEL_ACTOR: { optional: true },
|
|
7202
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
7203
|
+
GITHUB_TOKEN: { optional: true },
|
|
7204
|
+
GH_TOKEN: { optional: true }
|
|
7205
|
+
}
|
|
7206
|
+
},
|
|
7207
|
+
0
|
|
7208
|
+
],
|
|
7209
|
+
[
|
|
7210
|
+
{
|
|
7211
|
+
name: "issue-assignment",
|
|
7212
|
+
run: bin("check-issue-assignment"),
|
|
7213
|
+
scope: "full",
|
|
7214
|
+
timeoutMs: 30000,
|
|
7215
|
+
env: {
|
|
7216
|
+
AEG_REPO: { optional: true },
|
|
7217
|
+
BRANCH: { optional: true },
|
|
7218
|
+
GITHUB_TOKEN: { optional: true },
|
|
7219
|
+
GH_TOKEN: { optional: true }
|
|
7220
|
+
}
|
|
7221
|
+
},
|
|
7222
|
+
0
|
|
7223
|
+
],
|
|
7224
|
+
[
|
|
7225
|
+
{
|
|
7226
|
+
name: "evidence-fresh",
|
|
7227
|
+
validates: "body",
|
|
7228
|
+
run: bin("check-evidence-fresh"),
|
|
7229
|
+
scope: "diff",
|
|
7230
|
+
timeoutMs: 15000,
|
|
7231
|
+
requiresOpenPr: true,
|
|
7232
|
+
env: {
|
|
7233
|
+
PR_NUMBER: { optional: true },
|
|
7234
|
+
PR_BODY: { optional: true },
|
|
7235
|
+
BASE_SHA: { optional: true },
|
|
7236
|
+
GITHUB_TOKEN: { optional: true },
|
|
7237
|
+
GH_TOKEN: { optional: true }
|
|
7238
|
+
}
|
|
7239
|
+
},
|
|
7240
|
+
1
|
|
7241
|
+
],
|
|
7242
|
+
[
|
|
7243
|
+
{
|
|
7244
|
+
name: "reader-resolvable-prose",
|
|
7245
|
+
run: bin("check-reader-resolvable-prose"),
|
|
7246
|
+
scope: "full",
|
|
7247
|
+
timeoutMs: 30000,
|
|
7248
|
+
env: {},
|
|
7249
|
+
include: [
|
|
7250
|
+
"aeg-root/**/*.md",
|
|
7251
|
+
"apps/cli/src/**",
|
|
7252
|
+
".github/workflows/**",
|
|
7253
|
+
".vinaya/**",
|
|
7254
|
+
"apps/cli/README.md",
|
|
7255
|
+
"packages/sources/README.md"
|
|
7256
|
+
]
|
|
7257
|
+
},
|
|
7258
|
+
0
|
|
7259
|
+
],
|
|
7260
|
+
[
|
|
7261
|
+
{
|
|
7262
|
+
name: "retired-vocabulary",
|
|
7263
|
+
run: bin("check-retired-vocabulary"),
|
|
7264
|
+
scope: "full",
|
|
7265
|
+
timeoutMs: 30000,
|
|
7266
|
+
env: {},
|
|
7267
|
+
include: ["aeg-root/**/*.md"]
|
|
7268
|
+
},
|
|
7269
|
+
0
|
|
7270
|
+
],
|
|
7271
|
+
[
|
|
7272
|
+
{
|
|
7273
|
+
name: "doctrine-portability",
|
|
7274
|
+
run: bin("check-doctrine-portability"),
|
|
7275
|
+
scope: "full",
|
|
7276
|
+
timeoutMs: 30000,
|
|
7277
|
+
env: { BASE_SHA: { optional: true } },
|
|
7278
|
+
include: ["aeg-root/**/*.md"]
|
|
7279
|
+
},
|
|
7280
|
+
0
|
|
7281
|
+
],
|
|
7282
|
+
[
|
|
7283
|
+
{
|
|
7284
|
+
name: "doctrine-no-procedures",
|
|
7285
|
+
run: bin("check-doctrine-no-procedures"),
|
|
7286
|
+
scope: "full",
|
|
7287
|
+
timeoutMs: 30000,
|
|
7288
|
+
env: {},
|
|
7289
|
+
include: ["aeg-root/**/*.md"]
|
|
7290
|
+
},
|
|
7291
|
+
0
|
|
7292
|
+
],
|
|
7293
|
+
[
|
|
7294
|
+
{
|
|
7295
|
+
name: "exec-bits",
|
|
7296
|
+
run: bin("check-exec-bits"),
|
|
7297
|
+
scope: "diff",
|
|
7298
|
+
timeoutMs: 30000,
|
|
7299
|
+
env: {}
|
|
7300
|
+
},
|
|
7301
|
+
0
|
|
7302
|
+
],
|
|
7303
|
+
[
|
|
7304
|
+
{
|
|
7305
|
+
name: "workspace-escape",
|
|
7306
|
+
run: bin("check-workspace-escape"),
|
|
7307
|
+
scope: "full",
|
|
7308
|
+
timeoutMs: 30000,
|
|
7309
|
+
env: {},
|
|
7310
|
+
include: ["aeg-root/**/*.md"]
|
|
7311
|
+
},
|
|
7312
|
+
0
|
|
7313
|
+
],
|
|
7314
|
+
[
|
|
7315
|
+
{
|
|
7316
|
+
name: "changeset-coverage",
|
|
7317
|
+
run: bin("check-changeset-coverage"),
|
|
7318
|
+
scope: "diff",
|
|
7319
|
+
timeoutMs: 15000,
|
|
7320
|
+
env: {}
|
|
7321
|
+
},
|
|
7322
|
+
0
|
|
7323
|
+
],
|
|
7324
|
+
[
|
|
7325
|
+
{
|
|
7326
|
+
name: "quoted-command",
|
|
7327
|
+
run: bin("check-quoted-command"),
|
|
7328
|
+
scope: "diff",
|
|
7329
|
+
timeoutMs: 15000,
|
|
7330
|
+
env: {}
|
|
7331
|
+
},
|
|
7332
|
+
0
|
|
7333
|
+
],
|
|
7334
|
+
[
|
|
7335
|
+
{
|
|
7336
|
+
name: "main-branch-refusal",
|
|
7337
|
+
run: bin("check-main-branch-refusal"),
|
|
7338
|
+
scope: "full",
|
|
7339
|
+
timeoutMs: 15000,
|
|
7340
|
+
env: { VINAYA_PUSH_REFS: { optional: true } }
|
|
7341
|
+
},
|
|
7342
|
+
0
|
|
7343
|
+
],
|
|
7344
|
+
[
|
|
7345
|
+
{
|
|
7346
|
+
name: "token-collection-wired",
|
|
7347
|
+
run: bin("check-token-collection-wired"),
|
|
7348
|
+
scope: "full",
|
|
7349
|
+
timeoutMs: 15000,
|
|
7350
|
+
env: {
|
|
7351
|
+
CLAUDE_PROJECT_DIR: { optional: true },
|
|
7352
|
+
CLAUDE_CODE_SESSION_ID: { optional: true }
|
|
7353
|
+
}
|
|
7354
|
+
},
|
|
7355
|
+
0
|
|
7356
|
+
],
|
|
7357
|
+
[
|
|
7358
|
+
{
|
|
7359
|
+
name: "issue-title-grammar",
|
|
7360
|
+
run: bin("check-issue-title-grammar"),
|
|
7361
|
+
validates: "issue",
|
|
7362
|
+
scope: "full",
|
|
7363
|
+
ownWorkflow: true,
|
|
7364
|
+
timeoutMs: 15000,
|
|
7365
|
+
env: { ISSUE_TITLE: { optional: true } }
|
|
7366
|
+
},
|
|
7367
|
+
1
|
|
7368
|
+
],
|
|
7369
|
+
[
|
|
7370
|
+
{
|
|
7371
|
+
name: "issue-objectives-numbering",
|
|
7372
|
+
run: bin("check-issue-objectives-numbering"),
|
|
7373
|
+
validates: "issue",
|
|
7374
|
+
scope: "full",
|
|
7375
|
+
ownWorkflow: true,
|
|
7376
|
+
timeoutMs: 15000,
|
|
7377
|
+
env: { ISSUE_BODY: { optional: true }, ISSUE_NUMBER: { optional: true } }
|
|
7378
|
+
},
|
|
7379
|
+
1
|
|
7380
|
+
],
|
|
7381
|
+
[
|
|
7382
|
+
{
|
|
7383
|
+
name: "issue-parts-coverage",
|
|
7384
|
+
run: bin("check-issue-parts-coverage"),
|
|
7385
|
+
validates: "issue",
|
|
7386
|
+
scope: "full",
|
|
7387
|
+
ownWorkflow: true,
|
|
7388
|
+
timeoutMs: 15000,
|
|
7389
|
+
env: { ISSUE_BODY: { optional: true } }
|
|
7390
|
+
},
|
|
7391
|
+
1
|
|
7392
|
+
],
|
|
7393
|
+
[
|
|
7394
|
+
{
|
|
7395
|
+
name: "issue-surface-globs",
|
|
7396
|
+
run: bin("check-issue-surface-globs"),
|
|
7397
|
+
validates: "issue",
|
|
7398
|
+
scope: "full",
|
|
7399
|
+
ownWorkflow: true,
|
|
7400
|
+
timeoutMs: 15000,
|
|
7401
|
+
env: { ISSUE_BODY: { optional: true } }
|
|
7402
|
+
},
|
|
7403
|
+
1
|
|
7404
|
+
],
|
|
7405
|
+
[
|
|
7406
|
+
{
|
|
7407
|
+
name: "issue-tranche-label",
|
|
7408
|
+
run: bin("check-issue-tranche-label"),
|
|
7409
|
+
validates: "issue",
|
|
7410
|
+
scope: "full",
|
|
7411
|
+
ownWorkflow: true,
|
|
7412
|
+
timeoutMs: 15000,
|
|
7413
|
+
env: { ISSUE_BODY: { optional: true }, ISSUE_LABELS: { optional: true } }
|
|
7414
|
+
},
|
|
7415
|
+
1
|
|
7416
|
+
],
|
|
7417
|
+
[
|
|
7418
|
+
{
|
|
7419
|
+
name: "issue-milestone-attach",
|
|
7420
|
+
run: bin("check-issue-milestone-attach"),
|
|
7421
|
+
validates: "issue",
|
|
7422
|
+
scope: "full",
|
|
7423
|
+
ownWorkflow: true,
|
|
7424
|
+
timeoutMs: 15000,
|
|
7425
|
+
env: {
|
|
7426
|
+
ISSUE_LABELS: { optional: true },
|
|
7427
|
+
CURRENT_MILESTONE_TITLE: { optional: true },
|
|
7428
|
+
RESOLVED_MILESTONE_TITLE: { optional: true }
|
|
7429
|
+
}
|
|
7430
|
+
},
|
|
7431
|
+
1
|
|
7432
|
+
]
|
|
7433
|
+
];
|
|
7434
|
+
function coreCheckRegistry() {
|
|
7435
|
+
return REGISTRY.map(([spec]) => spec);
|
|
7436
|
+
}
|
|
7437
|
+
var CORE_CHECK_RING = Object.fromEntries(REGISTRY.map(([spec, ring]) => [spec.name, ring]));
|
|
7438
|
+
|
|
7439
|
+
// src/checks/runner.ts
|
|
7440
|
+
var activeKillers = new Set;
|
|
7441
|
+
|
|
7442
|
+
// src/lib/forge-write.ts
|
|
7443
|
+
var INFORMATIONAL_DISPATCH_BLOCKER_CLASSES = new Set([
|
|
7444
|
+
"depends-on-not-merged",
|
|
7445
|
+
"conflicts-with"
|
|
7446
|
+
]);
|
|
7447
|
+
|
|
6176
7448
|
// src/commands/review-post.ts
|
|
6177
7449
|
function parseChangedLineRanges(diffOutput) {
|
|
6178
7450
|
const result = {};
|
|
@@ -6204,7 +7476,7 @@ function parseChangedLineRanges(diffOutput) {
|
|
|
6204
7476
|
function fileDiffAgainst(ref, path) {
|
|
6205
7477
|
let out;
|
|
6206
7478
|
try {
|
|
6207
|
-
out =
|
|
7479
|
+
out = execFileSync5("git", ["diff", `${ref}...HEAD`, "--", path], {
|
|
6208
7480
|
encoding: "utf8",
|
|
6209
7481
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6210
7482
|
}).trim();
|
|
@@ -6215,7 +7487,7 @@ function fileDiffAgainst(ref, path) {
|
|
|
6215
7487
|
}
|
|
6216
7488
|
function revParse(ref) {
|
|
6217
7489
|
try {
|
|
6218
|
-
return
|
|
7490
|
+
return execFileSync5("git", ["rev-parse", "--verify", ref], {
|
|
6219
7491
|
encoding: "utf8",
|
|
6220
7492
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6221
7493
|
}).trim();
|
|
@@ -6225,7 +7497,7 @@ function revParse(ref) {
|
|
|
6225
7497
|
}
|
|
6226
7498
|
function repoRoot() {
|
|
6227
7499
|
try {
|
|
6228
|
-
return
|
|
7500
|
+
return execFileSync5("git", ["rev-parse", "--show-toplevel"], {
|
|
6229
7501
|
encoding: "utf8",
|
|
6230
7502
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6231
7503
|
}).trim();
|
|
@@ -6236,7 +7508,7 @@ function repoRoot() {
|
|
|
6236
7508
|
function changedFiles(base) {
|
|
6237
7509
|
let out;
|
|
6238
7510
|
try {
|
|
6239
|
-
out =
|
|
7511
|
+
out = execFileSync5("git", ["diff", "--name-only", `${base}...HEAD`], {
|
|
6240
7512
|
encoding: "utf8",
|
|
6241
7513
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6242
7514
|
}).trim();
|
|
@@ -6261,7 +7533,7 @@ function resolveDiff(base = process.env.BASE_SHA || "origin/main") {
|
|
|
6261
7533
|
const relPaths = changedFiles(ref);
|
|
6262
7534
|
if (relPaths === null)
|
|
6263
7535
|
continue;
|
|
6264
|
-
return { base: ref, root, files: relPaths.map((p) =>
|
|
7536
|
+
return { base: ref, root, files: relPaths.map((p) => join4(root, p)) };
|
|
6265
7537
|
}
|
|
6266
7538
|
return null;
|
|
6267
7539
|
}
|
|
@@ -6295,15 +7567,15 @@ function findingsInThisDiff(findings) {
|
|
|
6295
7567
|
// src/checks/bin/check-exec-bits.ts
|
|
6296
7568
|
var CHECK_NAME = "exec-bits";
|
|
6297
7569
|
var EXECUTABLE_MODE = "100755";
|
|
6298
|
-
function
|
|
7570
|
+
function git2(args) {
|
|
6299
7571
|
try {
|
|
6300
|
-
return
|
|
7572
|
+
return execFileSync6("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
6301
7573
|
} catch {
|
|
6302
7574
|
return null;
|
|
6303
7575
|
}
|
|
6304
7576
|
}
|
|
6305
7577
|
function indexMode(path) {
|
|
6306
|
-
const out =
|
|
7578
|
+
const out = git2(["ls-files", "-s", "--", path]);
|
|
6307
7579
|
if (out === null || out === "")
|
|
6308
7580
|
return null;
|
|
6309
7581
|
const mode = out.split(/\s+/)[0] ?? "";
|