@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
|
@@ -1954,6 +1954,8 @@ function parseInlineFieldList(section) {
|
|
|
1954
1954
|
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
1955
1955
|
var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1956
1956
|
var RULING_ORDINAL_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Ruling ordinal:\s*(\d+)(?!\d)/im;
|
|
1957
|
+
var BRIEF_HASH_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Brief hash:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1958
|
+
var POLICY_DIGEST_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Policy digest:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1957
1959
|
function firstFiveLines(comment) {
|
|
1958
1960
|
return comment.split(`
|
|
1959
1961
|
`).slice(0, 5).join(`
|
|
@@ -1976,6 +1978,26 @@ function extractRulingOrdinal(comment) {
|
|
|
1976
1978
|
const m = firstSevenLines(comment).match(RULING_ORDINAL_PATTERN);
|
|
1977
1979
|
return m ? Number.parseInt(m[1], 10) : null;
|
|
1978
1980
|
}
|
|
1981
|
+
function firstElevenLines(comment) {
|
|
1982
|
+
return comment.split(`
|
|
1983
|
+
`).slice(0, 11).join(`
|
|
1984
|
+
`);
|
|
1985
|
+
}
|
|
1986
|
+
function extractBriefHash(comment) {
|
|
1987
|
+
const m = firstElevenLines(comment).match(BRIEF_HASH_PATTERN);
|
|
1988
|
+
return m ? m[1].toLowerCase() : null;
|
|
1989
|
+
}
|
|
1990
|
+
function extractPolicyDigest(comment) {
|
|
1991
|
+
const m = firstElevenLines(comment).match(POLICY_DIGEST_PATTERN);
|
|
1992
|
+
return m ? m[1].toLowerCase() : null;
|
|
1993
|
+
}
|
|
1994
|
+
var FINDING_SEVERITY_LINE = /^\d+\.\s+\[([A-Z][A-Z]*)\]\s+(.+?)\s+—/gm;
|
|
1995
|
+
function extractFindingSeverities(comment) {
|
|
1996
|
+
return [...comment.matchAll(FINDING_SEVERITY_LINE)].map((m) => ({
|
|
1997
|
+
severity: m[1],
|
|
1998
|
+
location: m[2]
|
|
1999
|
+
}));
|
|
2000
|
+
}
|
|
1979
2001
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
1980
2002
|
const candidates = comments.filter((c) => valuePattern.test(c));
|
|
1981
2003
|
if (candidates.length === 0) {
|
|
@@ -1984,6 +2006,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1984
2006
|
headSha: null,
|
|
1985
2007
|
objectivesVersion: null,
|
|
1986
2008
|
rulingOrdinal: null,
|
|
2009
|
+
briefHash: null,
|
|
2010
|
+
policyDigest: null,
|
|
2011
|
+
findingSeverities: [],
|
|
1987
2012
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
1988
2013
|
};
|
|
1989
2014
|
}
|
|
@@ -1995,6 +2020,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1995
2020
|
headSha: null,
|
|
1996
2021
|
objectivesVersion: null,
|
|
1997
2022
|
rulingOrdinal: null,
|
|
2023
|
+
briefHash: null,
|
|
2024
|
+
policyDigest: null,
|
|
2025
|
+
findingSeverities: [],
|
|
1998
2026
|
danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
|
|
1999
2027
|
};
|
|
2000
2028
|
}
|
|
@@ -2003,6 +2031,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
2003
2031
|
headSha: extractHeadSha(latest),
|
|
2004
2032
|
objectivesVersion: extractObjectivesVersion(latest),
|
|
2005
2033
|
rulingOrdinal: extractRulingOrdinal(latest),
|
|
2034
|
+
briefHash: extractBriefHash(latest),
|
|
2035
|
+
policyDigest: extractPolicyDigest(latest),
|
|
2036
|
+
findingSeverities: extractFindingSeverities(latest),
|
|
2006
2037
|
danglingNote: null
|
|
2007
2038
|
};
|
|
2008
2039
|
}
|
|
@@ -2338,6 +2369,53 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive,
|
|
|
2338
2369
|
}
|
|
2339
2370
|
return out;
|
|
2340
2371
|
}
|
|
2372
|
+
// ../../packages/aeg-core/src/review-policy.ts
|
|
2373
|
+
var CODE_REVIEW_SEVERITY_ORDER = ["BLOCKER", "MAJOR", "MINOR"];
|
|
2374
|
+
var SECURITY_SEVERITY_ORDER = ["CRITICAL", "HIGH", "MEDIUM", "LOW"];
|
|
2375
|
+
var DEFAULT_MAX_ROUNDS = 3;
|
|
2376
|
+
var DEFAULT_REVIEW_POLICY = {
|
|
2377
|
+
codeReviewThreshold: "BLOCKER",
|
|
2378
|
+
securityThreshold: "HIGH",
|
|
2379
|
+
maxRounds: DEFAULT_MAX_ROUNDS
|
|
2380
|
+
};
|
|
2381
|
+
var FILE_SHAPED_LOCATION = /\.[a-zA-Z0-9]{1,10}(:\d+)?\s*$/;
|
|
2382
|
+
var PROSE_LOCATION_PATTERNS = [/\bpr\s*body\b/i, /\bcomment\b/i];
|
|
2383
|
+
var ROLE_FILE_LOCATION = /(^|\/)aeg-root\/roles\//i;
|
|
2384
|
+
function isProseLocation(location) {
|
|
2385
|
+
if (ROLE_FILE_LOCATION.test(location))
|
|
2386
|
+
return true;
|
|
2387
|
+
if (FILE_SHAPED_LOCATION.test(location))
|
|
2388
|
+
return false;
|
|
2389
|
+
return PROSE_LOCATION_PATTERNS.some((pattern) => pattern.test(location));
|
|
2390
|
+
}
|
|
2391
|
+
var PROSE_CAP_SEVERITY = "MINOR";
|
|
2392
|
+
function blockingSeverities(scale, threshold) {
|
|
2393
|
+
const idx = scale.indexOf(threshold);
|
|
2394
|
+
if (idx === -1) {
|
|
2395
|
+
throw new Error(`blockingSeverities: threshold "${threshold}" is not one of ${scale.join(" > ")}`);
|
|
2396
|
+
}
|
|
2397
|
+
return scale.slice(0, idx + 1);
|
|
2398
|
+
}
|
|
2399
|
+
function evaluateReviewFindings(findings, scale, threshold) {
|
|
2400
|
+
const blocking = new Set(blockingSeverities(scale, threshold));
|
|
2401
|
+
const blockingFindings = findings.filter((f) => {
|
|
2402
|
+
if (!scale.includes(f.severity)) {
|
|
2403
|
+
throw new Error(`evaluateReviewFindings: severity "${f.severity}" is not one of ${scale.join(" > ")}`);
|
|
2404
|
+
}
|
|
2405
|
+
const effectiveSeverity = f.location !== undefined && isProseLocation(f.location) ? PROSE_CAP_SEVERITY : f.severity;
|
|
2406
|
+
return blocking.has(effectiveSeverity);
|
|
2407
|
+
});
|
|
2408
|
+
return { outcome: blockingFindings.length > 0 ? "blocked" : "clean", blockingFindings };
|
|
2409
|
+
}
|
|
2410
|
+
function evaluateCodeReview(findings, policy) {
|
|
2411
|
+
return evaluateReviewFindings(findings, CODE_REVIEW_SEVERITY_ORDER, policy.codeReviewThreshold);
|
|
2412
|
+
}
|
|
2413
|
+
function evaluateSecurityReview(findings, policy) {
|
|
2414
|
+
return evaluateReviewFindings(findings, SECURITY_SEVERITY_ORDER, policy.securityThreshold);
|
|
2415
|
+
}
|
|
2416
|
+
function isKnownSeverity(scale, value) {
|
|
2417
|
+
return scale.includes(value);
|
|
2418
|
+
}
|
|
2341
2419
|
// ../../packages/aeg-core/src/pr-tier.ts
|
|
2342
2420
|
var TIER_FIELD = /(\*\*)?\s*Tier\s*(\*\*)?\s*:\s*(\*\*)?\s*([013])\b/i;
|
|
2343
2421
|
function readTierFromPrBody(prBody) {
|
|
@@ -2708,7 +2786,7 @@ function checkForField(prBody) {
|
|
|
2708
2786
|
]
|
|
2709
2787
|
};
|
|
2710
2788
|
}
|
|
2711
|
-
function
|
|
2789
|
+
function checkClosesNPresence(prBody) {
|
|
2712
2790
|
const closesPattern = /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s{0,8}:?\s{0,8}#\d+/i;
|
|
2713
2791
|
if (closesPattern.test(stripCode(prBody))) {
|
|
2714
2792
|
return { status: "pass", errors: [] };
|
|
@@ -2740,10 +2818,22 @@ var COMMIT_TYPES = [
|
|
|
2740
2818
|
"Test"
|
|
2741
2819
|
];
|
|
2742
2820
|
var COMMIT_TYPE_STYLE = new RegExp(`^(${COMMIT_TYPES.join("|")})(\\([a-z0-9-]+\\))?: \\S`);
|
|
2821
|
+
function checkForgeTitle(title) {
|
|
2822
|
+
const taskStyle = /^\[[a-z0-9._-]+\] \S+ — \S/;
|
|
2823
|
+
if (COMMIT_TYPE_STYLE.test(title) || taskStyle.test(title))
|
|
2824
|
+
return { status: "pass", errors: [] };
|
|
2825
|
+
return {
|
|
2826
|
+
status: "fail",
|
|
2827
|
+
errors: [
|
|
2828
|
+
`brief-validation title: "${title}" matches neither title grammar — expected \`Type: description\` / \`Type(scope): description\` (commitlint types + Plan) or \`[tranche] id — description\` (task form).`
|
|
2829
|
+
]
|
|
2830
|
+
};
|
|
2831
|
+
}
|
|
2743
2832
|
var BRIEF_SHAPE_MARKERS = [checkSurfaceMap, checkDocUpdateList, checkStopConditions, checkAutonomyClause];
|
|
2744
2833
|
var TASK_BRANCH_PATTERN = /^task\/[^/]+\/[^/]+$/;
|
|
2834
|
+
var TASK_ISSUE_BRANCH_PATTERN = /^task\/issue-\d+$/;
|
|
2745
2835
|
function isTaskBranch(branch) {
|
|
2746
|
-
return TASK_BRANCH_PATTERN.test(branch);
|
|
2836
|
+
return TASK_BRANCH_PATTERN.test(branch) || TASK_ISSUE_BRANCH_PATTERN.test(branch);
|
|
2747
2837
|
}
|
|
2748
2838
|
function isBriefShaped(prBody) {
|
|
2749
2839
|
const stripped = stripCode(prBody);
|
|
@@ -2865,8 +2955,9 @@ function packagesNamedIn(text) {
|
|
|
2865
2955
|
}
|
|
2866
2956
|
function hasTestPathForConsumer(text, consumerDir) {
|
|
2867
2957
|
const escaped = consumerDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2868
|
-
const
|
|
2869
|
-
|
|
2958
|
+
const filePathRe = new RegExp(`${escaped}\\/[\\w./-]*\\.test\\.[A-Za-z0-9]+`);
|
|
2959
|
+
const testDirRe = new RegExp(`${escaped}\\/(?:[\\w-]+\\/)*(?:tests|specs)(?:\\/|\\b)`);
|
|
2960
|
+
return filePathRe.test(text) || testDirRe.test(text);
|
|
2870
2961
|
}
|
|
2871
2962
|
function checkConsumerTests(prBody, consumersOf) {
|
|
2872
2963
|
const section4 = extractNumberedSection(prBody, 4);
|
|
@@ -2970,7 +3061,7 @@ function checkBriefSections(prBody, readTier, options = {}) {
|
|
|
2970
3061
|
checkConsumerTests(prBody, consumersOf),
|
|
2971
3062
|
checkDefeatCases(prBody),
|
|
2972
3063
|
...issueObjectives !== undefined ? [checkObjectivesCopy(prBody, issueObjectives), checkObjectivesCoverage(prBody)] : [],
|
|
2973
|
-
...requireClosesN ? [
|
|
3064
|
+
...requireClosesN ? [checkClosesNPresence(prBody)] : []
|
|
2974
3065
|
];
|
|
2975
3066
|
return { errors: results.flatMap((r) => r.errors) };
|
|
2976
3067
|
}
|
|
@@ -3260,6 +3351,28 @@ function topLevelSectionText(body, headingName) {
|
|
|
3260
3351
|
const next = /^##[ \t]/m.exec(afterHeading);
|
|
3261
3352
|
return next ? afterHeading.slice(0, next.index) : afterHeading;
|
|
3262
3353
|
}
|
|
3354
|
+
function partHasBacktickedPath(text) {
|
|
3355
|
+
let i = 0;
|
|
3356
|
+
while (i < text.length) {
|
|
3357
|
+
const start = text.indexOf("`", i);
|
|
3358
|
+
if (start === -1)
|
|
3359
|
+
return false;
|
|
3360
|
+
const end = text.indexOf("`", start + 1);
|
|
3361
|
+
if (end === -1)
|
|
3362
|
+
return false;
|
|
3363
|
+
if (text.slice(start + 1, end).includes("/"))
|
|
3364
|
+
return true;
|
|
3365
|
+
i = end + 1;
|
|
3366
|
+
}
|
|
3367
|
+
return false;
|
|
3368
|
+
}
|
|
3369
|
+
var PART_MIN_WORDS_OUTSIDE_BACKTICKS = 3;
|
|
3370
|
+
function stripPartBackticks(text) {
|
|
3371
|
+
return text.replace(/`[^`\n]*`/g, " ");
|
|
3372
|
+
}
|
|
3373
|
+
function partWordCount(text) {
|
|
3374
|
+
return text.split(/\s+/).filter((w) => /[a-z]/i.test(w)).length;
|
|
3375
|
+
}
|
|
3263
3376
|
function looksLikeFilePath(entry2) {
|
|
3264
3377
|
const stripped = entry2.replace(/\/\*\*?$/, "");
|
|
3265
3378
|
const lastSegment = stripped.split("/").pop() ?? stripped;
|
|
@@ -3300,6 +3413,18 @@ function globCoversPath(glob, path) {
|
|
|
3300
3413
|
const p = path.replace(/\/+$/, "");
|
|
3301
3414
|
return g === p || g.startsWith(`${p}/`) || p.startsWith(`${g}/`);
|
|
3302
3415
|
}
|
|
3416
|
+
function checkSurfaceGlobsResolve(body, resolvesToFile) {
|
|
3417
|
+
const surface = parseIssueSurface(body);
|
|
3418
|
+
if (!surface.ok)
|
|
3419
|
+
return { status: "pass", errors: [] };
|
|
3420
|
+
const errors = [];
|
|
3421
|
+
for (const glob of surface.value.in) {
|
|
3422
|
+
if (!resolvesToFile(glob)) {
|
|
3423
|
+
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.`);
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3427
|
+
}
|
|
3303
3428
|
function checkSurfaceScope(changedFiles, outGlobs) {
|
|
3304
3429
|
if (outGlobs.length === 0)
|
|
3305
3430
|
return { ok: true };
|
|
@@ -3311,9 +3436,77 @@ function checkSurfaceScope(changedFiles, outGlobs) {
|
|
|
3311
3436
|
}
|
|
3312
3437
|
return violations.length > 0 ? { ok: false, violations } : { ok: true };
|
|
3313
3438
|
}
|
|
3439
|
+
var ISSUE_PART_LINE_RE = /^Part\s+(\d+)\s*\(([^)]*)\)\s*[-—–]\s*(.*)$/i;
|
|
3440
|
+
function parseIssueParts(body) {
|
|
3441
|
+
const section = topLevelSectionText(body, "Parts");
|
|
3442
|
+
if (section === null)
|
|
3443
|
+
return { ok: false, errors: ["no `## Parts` heading found in the body."] };
|
|
3444
|
+
const lines = section.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
3445
|
+
const parts = [];
|
|
3446
|
+
const errors = [];
|
|
3447
|
+
for (const line of lines) {
|
|
3448
|
+
const m = ISSUE_PART_LINE_RE.exec(line);
|
|
3449
|
+
if (!m) {
|
|
3450
|
+
errors.push(`"${line}" is not a well-formed Parts line — expected \`Part <n> (<refs>) — <outcome>\`.`);
|
|
3451
|
+
continue;
|
|
3452
|
+
}
|
|
3453
|
+
const n = Number.parseInt(m[1], 10);
|
|
3454
|
+
const refs = m[2];
|
|
3455
|
+
const text = m[3].trim();
|
|
3456
|
+
if (text.length === 0) {
|
|
3457
|
+
errors.push(`Part ${n} has no outcome text after the dash — every Part states one observable outcome.`);
|
|
3458
|
+
continue;
|
|
3459
|
+
}
|
|
3460
|
+
if (partHasBacktickedPath(text) && partWordCount(stripPartBackticks(text)) < PART_MIN_WORDS_OUTSIDE_BACKTICKS) {
|
|
3461
|
+
errors.push(`Part ${n} is little more than a file path — a Part names an outcome and symbols, never a bare path.`);
|
|
3462
|
+
continue;
|
|
3463
|
+
}
|
|
3464
|
+
const objectiveIds = [...refs.matchAll(/O(\d+)/g)].map((r) => Number.parseInt(r[1], 10));
|
|
3465
|
+
parts.push({ n, objectiveIds, text });
|
|
3466
|
+
}
|
|
3467
|
+
if (parts.length === 0) {
|
|
3468
|
+
errors.push("the `## Parts` section has no well-formed `Part <n> (<refs>) — <outcome>` lines.");
|
|
3469
|
+
}
|
|
3470
|
+
if (errors.length > 0)
|
|
3471
|
+
return { ok: false, errors };
|
|
3472
|
+
return { ok: true, value: parts };
|
|
3473
|
+
}
|
|
3474
|
+
function checkPartsCiteDefinedObjectives(body) {
|
|
3475
|
+
const parts = parseIssueParts(body);
|
|
3476
|
+
const objectives = objectivesOf(body);
|
|
3477
|
+
if (!parts.ok || !objectives.ok)
|
|
3478
|
+
return { status: "pass", errors: [] };
|
|
3479
|
+
const definedIds = new Set(objectives.objectives.map((o) => Number.parseInt(o.id.slice(1), 10)));
|
|
3480
|
+
const errors = [];
|
|
3481
|
+
for (const part of parts.value) {
|
|
3482
|
+
for (const objectiveId of part.objectiveIds) {
|
|
3483
|
+
if (!definedIds.has(objectiveId)) {
|
|
3484
|
+
errors.push(`issue-validation Parts: Part ${part.n} cites O${objectiveId}, which the Issue's own \`## Objectives\` section does not define.`);
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3489
|
+
}
|
|
3314
3490
|
function isTaskIssueLabelSet(labels) {
|
|
3315
3491
|
return hasLabel("tranche", labels);
|
|
3316
3492
|
}
|
|
3493
|
+
function checkTrancheLabelPresence(_body, _labels) {
|
|
3494
|
+
return { status: "pass", errors: [] };
|
|
3495
|
+
}
|
|
3496
|
+
function checkMilestoneAttach(labels, currentMilestoneTitle, resolvedMilestoneTitle) {
|
|
3497
|
+
if (!isTaskIssueLabelSet(labels))
|
|
3498
|
+
return { status: "pass", errors: [] };
|
|
3499
|
+
if (resolvedMilestoneTitle === null)
|
|
3500
|
+
return { status: "pass", errors: [] };
|
|
3501
|
+
if (currentMilestoneTitle === resolvedMilestoneTitle)
|
|
3502
|
+
return { status: "pass", errors: [] };
|
|
3503
|
+
return {
|
|
3504
|
+
status: "fail",
|
|
3505
|
+
errors: [
|
|
3506
|
+
`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}".`
|
|
3507
|
+
]
|
|
3508
|
+
};
|
|
3509
|
+
}
|
|
3317
3510
|
var TYPE_LABEL_IDS = LABELS.filter((l) => l.category === "type").map((l) => l.id);
|
|
3318
3511
|
function isControlCodePoint(codePoint) {
|
|
3319
3512
|
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
|
|
@@ -3378,6 +3571,19 @@ function checkProjectsRegistered(body, _labels, registeredNames) {
|
|
|
3378
3571
|
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;
|
|
3379
3572
|
var DOC_PATH_RE_GLOBAL = new RegExp(DOC_PATH_RE.source, "gi");
|
|
3380
3573
|
|
|
3574
|
+
// ../../packages/aeg-core/src/task-branch-identity.ts
|
|
3575
|
+
var ISSUE_BRANCH_PATTERN = /^task\/issue-(\d+)$/;
|
|
3576
|
+
var TRANCHE_BRANCH_PATTERN = /^task\/([^/]+)\/([^/]+)$/;
|
|
3577
|
+
function parseTaskBranchIdentity(branch) {
|
|
3578
|
+
const issueMatch = ISSUE_BRANCH_PATTERN.exec(branch);
|
|
3579
|
+
if (issueMatch)
|
|
3580
|
+
return { kind: "issue", issueNumber: Number(issueMatch[1]) };
|
|
3581
|
+
const trancheMatch = TRANCHE_BRANCH_PATTERN.exec(branch);
|
|
3582
|
+
if (trancheMatch)
|
|
3583
|
+
return { kind: "tranche", tranche: trancheMatch[1], taskId: trancheMatch[2] };
|
|
3584
|
+
return null;
|
|
3585
|
+
}
|
|
3586
|
+
|
|
3381
3587
|
// ../../packages/aeg-core/src/coherence-checks.ts
|
|
3382
3588
|
var COHERENCE_ENFORCED_FROM = "2026-07-01";
|
|
3383
3589
|
function isGrandfathered(isoDate) {
|
|
@@ -3398,6 +3604,7 @@ function checkA1(entries, principalAllowlist = PRINCIPAL_ALLOWLIST) {
|
|
|
3398
3604
|
if (handClosed)
|
|
3399
3605
|
continue;
|
|
3400
3606
|
failures.push({
|
|
3607
|
+
code: "closed-without-merge",
|
|
3401
3608
|
issue: e.task.issue,
|
|
3402
3609
|
tranche: e.trancheSlug,
|
|
3403
3610
|
task: e.task.id,
|
|
@@ -3422,6 +3629,7 @@ function checkA3(entries) {
|
|
|
3422
3629
|
continue;
|
|
3423
3630
|
if (e.facts.prState === "merged" && e.facts.issueState !== "closed") {
|
|
3424
3631
|
failures.push({
|
|
3632
|
+
code: "auto-close-misfire",
|
|
3425
3633
|
issue: e.task.issue,
|
|
3426
3634
|
tranche: e.trancheSlug,
|
|
3427
3635
|
task: e.task.id,
|
|
@@ -3446,6 +3654,7 @@ function checkT1(entries) {
|
|
|
3446
3654
|
continue;
|
|
3447
3655
|
if (e.facts === undefined) {
|
|
3448
3656
|
failures.push({
|
|
3657
|
+
code: "phantom-issue-ref",
|
|
3449
3658
|
issue: e.task.issue,
|
|
3450
3659
|
tranche: e.trancheSlug,
|
|
3451
3660
|
task: e.task.id,
|
|
@@ -3464,6 +3673,7 @@ function checkT2(openIssuesBySlug, topologyIssuesBySlug, ciTrancheSlug) {
|
|
|
3464
3673
|
for (const num of openNums) {
|
|
3465
3674
|
if (!topologySet.has(num)) {
|
|
3466
3675
|
failures.push({
|
|
3676
|
+
code: "orphan-task",
|
|
3467
3677
|
issue: num,
|
|
3468
3678
|
tranche: slug,
|
|
3469
3679
|
reason: `Issue #${num} is open and labeled ${trancheLabel(slug)} but does not appear in the topology file`
|
|
@@ -3498,6 +3708,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
|
|
|
3498
3708
|
continue;
|
|
3499
3709
|
if (forgeUnavailableSlugs?.has(e.trancheSlug)) {
|
|
3500
3710
|
failures.push({
|
|
3711
|
+
code: "tbd-in-active-tranche",
|
|
3501
3712
|
issue: null,
|
|
3502
3713
|
tranche: e.trancheSlug,
|
|
3503
3714
|
task: e.task.id,
|
|
@@ -3507,6 +3718,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
|
|
|
3507
3718
|
continue;
|
|
3508
3719
|
}
|
|
3509
3720
|
failures.push({
|
|
3721
|
+
code: "tbd-in-active-tranche",
|
|
3510
3722
|
issue: null,
|
|
3511
3723
|
tranche: e.trancheSlug,
|
|
3512
3724
|
task: e.task.id,
|
|
@@ -3539,6 +3751,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
|
|
|
3539
3751
|
const sameIssue = depEntry.task.issue !== null && e.task.issue !== null && depEntry.task.issue === e.task.issue;
|
|
3540
3752
|
if (sameTask || sameIssue) {
|
|
3541
3753
|
failures.push({
|
|
3754
|
+
code: "d1-self-dependency",
|
|
3542
3755
|
issue: e.task.issue,
|
|
3543
3756
|
tranche: e.trancheSlug,
|
|
3544
3757
|
task: e.task.id,
|
|
@@ -3550,6 +3763,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
|
|
|
3550
3763
|
const depClosed = depFacts?.issueState === "closed";
|
|
3551
3764
|
if (!depClosed) {
|
|
3552
3765
|
failures.push({
|
|
3766
|
+
code: "dispatched-on-unmet-deps",
|
|
3553
3767
|
issue: e.task.issue,
|
|
3554
3768
|
tranche: e.trancheSlug,
|
|
3555
3769
|
task: e.task.id,
|
|
@@ -3575,11 +3789,13 @@ function checkR1(issuesBySlug, grandfatheredIssues, registeredNames = []) {
|
|
|
3575
3789
|
const errors = [
|
|
3576
3790
|
...checkIssueRationale(issue.body).errors,
|
|
3577
3791
|
...checkProjectsRegistered(issue.body, issue.labels, registeredNames).errors,
|
|
3578
|
-
...checkIssueObjectives(issue.body, issue.number).errors
|
|
3792
|
+
...checkIssueObjectives(issue.body, issue.number).errors,
|
|
3793
|
+
...checkPartsCiteDefinedObjectives(issue.body).errors
|
|
3579
3794
|
];
|
|
3580
3795
|
if (errors.length === 0)
|
|
3581
3796
|
continue;
|
|
3582
3797
|
failures.push({
|
|
3798
|
+
code: "missing-rationale-field",
|
|
3583
3799
|
issue: issue.number,
|
|
3584
3800
|
tranche: slug,
|
|
3585
3801
|
reason: `Issue #${issue.number} fails the rationale gate: ${errors.join(" | ")}`,
|
|
@@ -3608,6 +3824,7 @@ function checkL1(files, entriesBySlug) {
|
|
|
3608
3824
|
const allClosed = withFacts.every((e) => e.facts?.issueState === "closed");
|
|
3609
3825
|
if (allClosed) {
|
|
3610
3826
|
failures.push({
|
|
3827
|
+
code: "archive-recommended",
|
|
3611
3828
|
tranche: f.slug,
|
|
3612
3829
|
reason: "Active tranche has no open task-Issues — consider archiving to completed/"
|
|
3613
3830
|
});
|
|
@@ -3639,27 +3856,38 @@ function extractClosesReferences(prBody) {
|
|
|
3639
3856
|
}
|
|
3640
3857
|
return referenced;
|
|
3641
3858
|
}
|
|
3642
|
-
function
|
|
3859
|
+
function checkClosesNTopology(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
3643
3860
|
const referenced = extractClosesReferences(prBody);
|
|
3644
3861
|
if (taskIssueRefs) {
|
|
3645
3862
|
for (const n of referenced) {
|
|
3646
|
-
const
|
|
3647
|
-
if (!
|
|
3863
|
+
const ref2 = taskIssueRefs.get(n);
|
|
3864
|
+
if (!ref2)
|
|
3648
3865
|
continue;
|
|
3649
|
-
const expectedBranch = `task/${
|
|
3866
|
+
const expectedBranch = `task/${ref2.trancheSlug}/${ref2.taskId}`;
|
|
3650
3867
|
if (branch !== expectedBranch) {
|
|
3651
3868
|
return {
|
|
3652
3869
|
ok: false,
|
|
3653
|
-
message: `closes-n-reverse: branch "${branch}" closes #${n} (task ${
|
|
3870
|
+
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.`
|
|
3654
3871
|
};
|
|
3655
3872
|
}
|
|
3656
3873
|
}
|
|
3657
3874
|
}
|
|
3658
|
-
const
|
|
3659
|
-
if (!
|
|
3875
|
+
const ref = parseTaskBranchIdentity(branch);
|
|
3876
|
+
if (!ref)
|
|
3660
3877
|
return { ok: true };
|
|
3661
|
-
|
|
3662
|
-
|
|
3878
|
+
if (ref.kind === "issue") {
|
|
3879
|
+
const expectedIssue2 = ref.issueNumber;
|
|
3880
|
+
if (!referenced.has(expectedIssue2)) {
|
|
3881
|
+
return {
|
|
3882
|
+
ok: false,
|
|
3883
|
+
expectedIssue: expectedIssue2,
|
|
3884
|
+
message: `closes-n: PR body does not contain \`Closes #${expectedIssue2}\` (required for branch "${branch}"). Add it to the PR body Summary section.`
|
|
3885
|
+
};
|
|
3886
|
+
}
|
|
3887
|
+
return { ok: true, expectedIssue: expectedIssue2 };
|
|
3888
|
+
}
|
|
3889
|
+
const trancheSlug = ref.tranche;
|
|
3890
|
+
const taskId = ref.taskId;
|
|
3663
3891
|
const trancheFile = trancheFiles.find((f) => f.slug === trancheSlug);
|
|
3664
3892
|
if (!trancheFile) {
|
|
3665
3893
|
return {
|
|
@@ -3711,34 +3939,67 @@ function extractIssue(body) {
|
|
|
3711
3939
|
const issue = headerNums.length > 0 ? headerNums[0] : bodyNums[0];
|
|
3712
3940
|
return { issue, extraIssues: bodyNums.filter((n) => n !== issue), outsideHeader: headerNums.length === 0 };
|
|
3713
3941
|
}
|
|
3714
|
-
// ../../packages/aeg-core/src/review-
|
|
3715
|
-
|
|
3716
|
-
|
|
3942
|
+
// ../../packages/aeg-core/src/review-input-manifest.ts
|
|
3943
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3944
|
+
function briefHash(brief) {
|
|
3945
|
+
return createHash4("sha256").update(`${brief}
|
|
3946
|
+
`).digest("hex");
|
|
3947
|
+
}
|
|
3948
|
+
function policyDigest(policy) {
|
|
3949
|
+
return createHash4("sha256").update(JSON.stringify({ codeReviewThreshold: policy.codeReviewThreshold, securityThreshold: policy.securityThreshold })).digest("hex");
|
|
3950
|
+
}
|
|
3951
|
+
function isBoundToHead(echoed, headSha) {
|
|
3952
|
+
if (!echoed.headSha)
|
|
3717
3953
|
return false;
|
|
3718
|
-
return headSha.toLowerCase().startsWith(
|
|
3954
|
+
return headSha.toLowerCase().startsWith(echoed.headSha.toLowerCase());
|
|
3719
3955
|
}
|
|
3720
|
-
function isBoundByPatchIdentity(
|
|
3721
|
-
if (patchIdOf === undefined || !
|
|
3956
|
+
function isBoundByPatchIdentity(echoed, headSha, patchIdOf) {
|
|
3957
|
+
if (patchIdOf === undefined || !echoed.headSha)
|
|
3722
3958
|
return false;
|
|
3723
|
-
const judged = patchIdOf(
|
|
3959
|
+
const judged = patchIdOf(echoed.headSha);
|
|
3724
3960
|
const current = patchIdOf(headSha);
|
|
3725
3961
|
if (judged === null || current === null)
|
|
3726
3962
|
return false;
|
|
3727
3963
|
return judged === current;
|
|
3728
3964
|
}
|
|
3729
|
-
function isBoundToPatch(
|
|
3730
|
-
return isBoundToHead(
|
|
3965
|
+
function isBoundToPatch(echoed, headSha, patchIdOf) {
|
|
3966
|
+
return isBoundToHead(echoed, headSha) || isBoundByPatchIdentity(echoed, headSha, patchIdOf);
|
|
3731
3967
|
}
|
|
3732
|
-
function isBoundToObjectives(
|
|
3968
|
+
function isBoundToObjectives(echoed, currentVersion) {
|
|
3733
3969
|
if (currentVersion === null)
|
|
3734
3970
|
return true;
|
|
3735
|
-
return
|
|
3971
|
+
return echoed.objectivesVersion === currentVersion;
|
|
3736
3972
|
}
|
|
3737
|
-
function isBoundToRulings(
|
|
3738
|
-
if (
|
|
3973
|
+
function isBoundToRulings(echoed, currentOrdinal) {
|
|
3974
|
+
if (echoed.rulingOrdinal === null)
|
|
3739
3975
|
return currentOrdinal === 0;
|
|
3740
|
-
return
|
|
3976
|
+
return echoed.rulingOrdinal === currentOrdinal;
|
|
3977
|
+
}
|
|
3978
|
+
function isBoundToBriefHash(echoed, currentHash) {
|
|
3979
|
+
if (currentHash === null)
|
|
3980
|
+
return true;
|
|
3981
|
+
return echoed.briefHash === currentHash;
|
|
3982
|
+
}
|
|
3983
|
+
function isBoundToPolicy(echoed, currentDigest) {
|
|
3984
|
+
return echoed.policyDigest === currentDigest;
|
|
3741
3985
|
}
|
|
3986
|
+
function compareManifest(echoed, current, patchIdOf) {
|
|
3987
|
+
const head = isBoundToPatch(echoed, current.headSha, patchIdOf);
|
|
3988
|
+
const briefHashBound = isBoundToBriefHash(echoed, current.briefHash);
|
|
3989
|
+
const objectivesVersion2 = isBoundToObjectives(echoed, current.objectivesVersion);
|
|
3990
|
+
const rulingOrdinal = isBoundToRulings(echoed, current.rulingOrdinal);
|
|
3991
|
+
const policyDigestBound = isBoundToPolicy(echoed, current.policyDigest);
|
|
3992
|
+
return {
|
|
3993
|
+
bound: head && briefHashBound && objectivesVersion2 && rulingOrdinal && policyDigestBound,
|
|
3994
|
+
head,
|
|
3995
|
+
briefHash: briefHashBound,
|
|
3996
|
+
objectivesVersion: objectivesVersion2,
|
|
3997
|
+
rulingOrdinal,
|
|
3998
|
+
policyDigest: policyDigestBound
|
|
3999
|
+
};
|
|
4000
|
+
}
|
|
4001
|
+
|
|
4002
|
+
// ../../packages/aeg-core/src/review-gate.ts
|
|
3742
4003
|
function checkReviewGate(input) {
|
|
3743
4004
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
3744
4005
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -3761,15 +4022,55 @@ function checkReviewGate(input) {
|
|
|
3761
4022
|
const verifiedBodies = verified.map((c) => c.body);
|
|
3762
4023
|
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
3763
4024
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
3764
|
-
const
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
4025
|
+
const policy = input.policy ?? DEFAULT_REVIEW_POLICY;
|
|
4026
|
+
let codeReviewPolicyEvaluation;
|
|
4027
|
+
let securityPolicyEvaluation;
|
|
4028
|
+
try {
|
|
4029
|
+
codeReviewPolicyEvaluation = evaluateCodeReview(codeReview.findingSeverities, policy);
|
|
4030
|
+
securityPolicyEvaluation = evaluateSecurityReview(security.findingSeverities, policy);
|
|
4031
|
+
} catch (err) {
|
|
4032
|
+
return {
|
|
4033
|
+
verdict: "fail",
|
|
4034
|
+
reason: `a verdict comment carries a finding severity this repository's policy does not recognize: ${err instanceof Error ? err.message : String(err)}`,
|
|
4035
|
+
waived: false
|
|
4036
|
+
};
|
|
4037
|
+
}
|
|
4038
|
+
const codeReviewTextClean = codeReview.value === "APPROVE";
|
|
4039
|
+
const securityTextClean = security.value === "PASS";
|
|
4040
|
+
const codeReviewPolicyClean = codeReviewPolicyEvaluation.outcome === "clean";
|
|
4041
|
+
const securityPolicyClean = securityPolicyEvaluation.outcome === "clean";
|
|
4042
|
+
const codeReviewClean = codeReviewTextClean && codeReviewPolicyClean;
|
|
4043
|
+
const securityClean = securityTextClean && securityPolicyClean;
|
|
4044
|
+
const currentManifest = {
|
|
4045
|
+
headSha: input.headSha,
|
|
4046
|
+
briefHash: input.briefHash ?? null,
|
|
4047
|
+
objectivesVersion: input.objectivesVersion,
|
|
4048
|
+
rulingOrdinal: input.rulingOrdinal,
|
|
4049
|
+
policyDigest: policyDigest(policy)
|
|
4050
|
+
};
|
|
4051
|
+
const codeReviewEchoed = {
|
|
4052
|
+
headSha: codeReview.headSha,
|
|
4053
|
+
briefHash: codeReview.briefHash,
|
|
4054
|
+
objectivesVersion: codeReview.objectivesVersion,
|
|
4055
|
+
rulingOrdinal: codeReview.rulingOrdinal,
|
|
4056
|
+
policyDigest: codeReview.policyDigest
|
|
4057
|
+
};
|
|
4058
|
+
const securityEchoed = {
|
|
4059
|
+
headSha: security.headSha,
|
|
4060
|
+
briefHash: security.briefHash,
|
|
4061
|
+
objectivesVersion: security.objectivesVersion,
|
|
4062
|
+
rulingOrdinal: security.rulingOrdinal,
|
|
4063
|
+
policyDigest: security.policyDigest
|
|
4064
|
+
};
|
|
4065
|
+
const codeReviewBinding = compareManifest(codeReviewEchoed, currentManifest, input.patchIdOf);
|
|
4066
|
+
const securityBinding = compareManifest(securityEchoed, currentManifest, input.patchIdOf);
|
|
4067
|
+
const codeReviewBound = codeReviewBinding.head;
|
|
4068
|
+
const securityBound = securityBinding.head;
|
|
4069
|
+
const codeReviewObjectivesBound = codeReviewBinding.objectivesVersion;
|
|
4070
|
+
const securityObjectivesBound = securityBinding.objectivesVersion;
|
|
4071
|
+
const codeReviewRulingsBound = codeReviewBinding.rulingOrdinal;
|
|
4072
|
+
const securityRulingsBound = securityBinding.rulingOrdinal;
|
|
4073
|
+
if (codeReviewClean && codeReviewBinding.bound && securityClean && securityBinding.bound && mechanicalChecksClean) {
|
|
3773
4074
|
return {
|
|
3774
4075
|
verdict: "pass",
|
|
3775
4076
|
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.`,
|
|
@@ -3777,23 +4078,35 @@ function checkReviewGate(input) {
|
|
|
3777
4078
|
};
|
|
3778
4079
|
}
|
|
3779
4080
|
const problems = [];
|
|
3780
|
-
if (!
|
|
4081
|
+
if (!codeReviewTextClean) {
|
|
3781
4082
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
4083
|
+
} else if (!codeReviewPolicyClean) {
|
|
4084
|
+
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`);
|
|
3782
4085
|
} else if (!codeReviewBound) {
|
|
3783
4086
|
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3784
4087
|
} else if (!codeReviewObjectivesBound) {
|
|
3785
4088
|
problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3786
4089
|
} else if (!codeReviewRulingsBound) {
|
|
3787
4090
|
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`);
|
|
4091
|
+
} else if (!codeReviewBinding.briefHash) {
|
|
4092
|
+
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"}`);
|
|
4093
|
+
} else if (!codeReviewBinding.policyDigest) {
|
|
4094
|
+
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}`);
|
|
3788
4095
|
}
|
|
3789
|
-
if (!
|
|
4096
|
+
if (!securityTextClean) {
|
|
3790
4097
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
4098
|
+
} else if (!securityPolicyClean) {
|
|
4099
|
+
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`);
|
|
3791
4100
|
} else if (!securityBound) {
|
|
3792
4101
|
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3793
4102
|
} else if (!securityObjectivesBound) {
|
|
3794
4103
|
problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3795
4104
|
} else if (!securityRulingsBound) {
|
|
3796
4105
|
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`);
|
|
4106
|
+
} else if (!securityBinding.briefHash) {
|
|
4107
|
+
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"}`);
|
|
4108
|
+
} else if (!securityBinding.policyDigest) {
|
|
4109
|
+
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}`);
|
|
3797
4110
|
}
|
|
3798
4111
|
if (!mechanicalChecksClean) {
|
|
3799
4112
|
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(", ")}`);
|
|
@@ -4485,42 +4798,45 @@ function checkDispatchReadiness(input) {
|
|
|
4485
4798
|
const { trancheSlug, task } = input;
|
|
4486
4799
|
const taskLabel = `task ${task.id} (tranche ${trancheSlug})`;
|
|
4487
4800
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
4488
|
-
const
|
|
4801
|
+
const blockerDetails = [];
|
|
4802
|
+
const push = (blockerClass, message) => {
|
|
4803
|
+
blockerDetails.push({ class: blockerClass, message });
|
|
4804
|
+
};
|
|
4489
4805
|
if (task.issue === null) {
|
|
4490
|
-
|
|
4806
|
+
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.`);
|
|
4491
4807
|
} else if (input.issue === null) {
|
|
4492
|
-
|
|
4808
|
+
push("issue-existence", `dispatch-gate issue-existence: ${taskLabel} names Issue #${task.issue}, but it does not resolve to a real GitHub Issue (phantom reference).`);
|
|
4493
4809
|
}
|
|
4494
4810
|
if (input.issue !== null && !input.issueRationalePass) {
|
|
4495
|
-
|
|
4811
|
+
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.`);
|
|
4496
4812
|
}
|
|
4497
4813
|
for (const dep of input.dependsOn) {
|
|
4498
4814
|
if (isSelfDependency(dep, task, input.issue)) {
|
|
4499
4815
|
const issueStr = input.issue !== null ? `#${input.issue.number}` : "?";
|
|
4500
|
-
|
|
4816
|
+
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.`);
|
|
4501
4817
|
continue;
|
|
4502
4818
|
}
|
|
4503
4819
|
if (dep.resolved === false) {
|
|
4504
|
-
|
|
4820
|
+
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.`);
|
|
4505
4821
|
continue;
|
|
4506
4822
|
}
|
|
4507
4823
|
if (!dep.merged && !isHandClosedByRecognizedPrincipal(dep, principalAllowlist)) {
|
|
4508
4824
|
const issueStr = dep.issue !== null ? ` (#${dep.issue})` : "";
|
|
4509
|
-
|
|
4825
|
+
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.`);
|
|
4510
4826
|
}
|
|
4511
4827
|
}
|
|
4512
4828
|
for (const c of input.conflictsWith) {
|
|
4513
4829
|
if (c.openOrInFlight) {
|
|
4514
4830
|
const issueStr = c.issue !== null ? ` (#${c.issue})` : "";
|
|
4515
|
-
|
|
4831
|
+
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.`);
|
|
4516
4832
|
}
|
|
4517
4833
|
}
|
|
4518
4834
|
for (const proj of input.priorTrancheArchival) {
|
|
4519
4835
|
if (proj.priorTrancheSlug !== null && !proj.archived) {
|
|
4520
|
-
|
|
4836
|
+
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.`);
|
|
4521
4837
|
}
|
|
4522
4838
|
}
|
|
4523
|
-
return { ready:
|
|
4839
|
+
return { ready: blockerDetails.length === 0, blockers: blockerDetails.map((b) => b.message), blockerDetails };
|
|
4524
4840
|
}
|
|
4525
4841
|
// ../../packages/aeg-core/src/single-plan-pr.ts
|
|
4526
4842
|
function trancheSlugFromTopologyPath(path) {
|
|
@@ -4622,6 +4938,91 @@ function checkBranchTopology(input) {
|
|
|
4622
4938
|
reason: `Branch \`${branch}\` matches topology row \`${taskId}\` in ${topoPath}.`
|
|
4623
4939
|
};
|
|
4624
4940
|
}
|
|
4941
|
+
// ../../packages/aeg-core/src/task-tools.ts
|
|
4942
|
+
import { z } from "zod";
|
|
4943
|
+
var TASK_TOOL_ERROR_KINDS = [
|
|
4944
|
+
"validation",
|
|
4945
|
+
"authority",
|
|
4946
|
+
"precondition",
|
|
4947
|
+
"capability",
|
|
4948
|
+
"infrastructure",
|
|
4949
|
+
"cancellation",
|
|
4950
|
+
"timeout",
|
|
4951
|
+
"uncertain_effect"
|
|
4952
|
+
];
|
|
4953
|
+
var TaskToolErrorSchema = z.object({
|
|
4954
|
+
kind: z.enum(TASK_TOOL_ERROR_KINDS),
|
|
4955
|
+
message: z.string().min(1),
|
|
4956
|
+
detail: z.string().optional()
|
|
4957
|
+
});
|
|
4958
|
+
var TaskToolRefSchema = z.union([
|
|
4959
|
+
z.object({ tranche: z.string().min(1), id: z.string().min(1) }),
|
|
4960
|
+
z.object({ issue: z.number().int().positive() })
|
|
4961
|
+
]);
|
|
4962
|
+
var MAX_PAGE_LIMIT = 100;
|
|
4963
|
+
var PageRequestSchema = z.object({
|
|
4964
|
+
cursor: z.string().optional(),
|
|
4965
|
+
limit: z.number().int().positive().max(MAX_PAGE_LIMIT).optional()
|
|
4966
|
+
});
|
|
4967
|
+
var FreshnessSchema = z.enum(["fresh", "stale", "unknown"]);
|
|
4968
|
+
var ObservedSchema = z.object({
|
|
4969
|
+
observedAt: z.string(),
|
|
4970
|
+
freshness: FreshnessSchema
|
|
4971
|
+
});
|
|
4972
|
+
var TaskStatusInputSchema = z.object({
|
|
4973
|
+
task: TaskToolRefSchema.optional()
|
|
4974
|
+
}).merge(PageRequestSchema);
|
|
4975
|
+
var TaskStatusItemSchema = z.object({
|
|
4976
|
+
task: TaskToolRefSchema,
|
|
4977
|
+
issue: z.number().int().positive(),
|
|
4978
|
+
pr: z.number().int().positive().nullable(),
|
|
4979
|
+
state: z.string()
|
|
4980
|
+
}).merge(ObservedSchema);
|
|
4981
|
+
var TaskStatusResultSchema = z.object({
|
|
4982
|
+
items: z.array(TaskStatusItemSchema),
|
|
4983
|
+
nextCursor: z.string().nullable()
|
|
4984
|
+
});
|
|
4985
|
+
var RequestedAuthoritySchema = z.enum(["planner", "principal", "operator", "self"]);
|
|
4986
|
+
var EscalationInputsSchema = z.object({
|
|
4987
|
+
task: z.number().int().positive(),
|
|
4988
|
+
round: z.number().int().nonnegative(),
|
|
4989
|
+
head: z.string(),
|
|
4990
|
+
branch: z.string(),
|
|
4991
|
+
prNumber: z.number().int().positive()
|
|
4992
|
+
});
|
|
4993
|
+
var EscalationEvidenceSchema = z.object({
|
|
4994
|
+
round: z.number().int().nonnegative(),
|
|
4995
|
+
reviewer: z.string().nullable(),
|
|
4996
|
+
security: z.string().nullable()
|
|
4997
|
+
});
|
|
4998
|
+
var TaskEscalationReadInputSchema = z.object({
|
|
4999
|
+
task: TaskToolRefSchema
|
|
5000
|
+
}).merge(PageRequestSchema);
|
|
5001
|
+
var TaskEscalationPacketSchema = z.object({
|
|
5002
|
+
reason: z.string(),
|
|
5003
|
+
detail: z.string().nullable(),
|
|
5004
|
+
inputs: EscalationInputsSchema.nullable(),
|
|
5005
|
+
evidence: EscalationEvidenceSchema.nullable(),
|
|
5006
|
+
attemptedRecovery: z.string(),
|
|
5007
|
+
requestedAuthority: RequestedAuthoritySchema,
|
|
5008
|
+
permittedNextActions: z.array(z.string())
|
|
5009
|
+
}).merge(ObservedSchema);
|
|
5010
|
+
var TaskEscalationReadResultSchema = z.object({
|
|
5011
|
+
items: z.array(TaskEscalationPacketSchema),
|
|
5012
|
+
nextCursor: z.string().nullable()
|
|
5013
|
+
}).merge(ObservedSchema);
|
|
5014
|
+
var TaskStartInputSchema = z.object({
|
|
5015
|
+
tranche: z.string().min(1),
|
|
5016
|
+
id: z.string().min(1)
|
|
5017
|
+
});
|
|
5018
|
+
var TaskResumeInputSchema = z.object({
|
|
5019
|
+
task: TaskToolRefSchema
|
|
5020
|
+
});
|
|
5021
|
+
var TaskCancelInputSchema = z.object({
|
|
5022
|
+
task: TaskToolRefSchema,
|
|
5023
|
+
reason: z.string().min(1)
|
|
5024
|
+
});
|
|
5025
|
+
var NoResultSchema = z.never();
|
|
4625
5026
|
// ../../packages/aeg-core/src/first-push-dispatch-gate.ts
|
|
4626
5027
|
function parseTaskBranch(branch) {
|
|
4627
5028
|
const m = /^task\/([^/]+)\/([^/]+)$/.exec(branch);
|
|
@@ -4705,7 +5106,6 @@ function decideIssueAssignment(input) {
|
|
|
4705
5106
|
};
|
|
4706
5107
|
}
|
|
4707
5108
|
// ../../packages/aeg-core/src/test-plan-gate.ts
|
|
4708
|
-
var TASK_BRANCH_PATTERN2 = /^task\/[^/]+\/[^/]+$/;
|
|
4709
5109
|
function evaluateTestPlanGate(body, branch) {
|
|
4710
5110
|
if (!body) {
|
|
4711
5111
|
return {
|
|
@@ -4718,7 +5118,7 @@ function evaluateTestPlanGate(body, branch) {
|
|
|
4718
5118
|
}
|
|
4719
5119
|
const located = locateTestPlanSection(body);
|
|
4720
5120
|
if (!located.found) {
|
|
4721
|
-
if (
|
|
5121
|
+
if (parseTaskBranchIdentity(branch) !== null) {
|
|
4722
5122
|
return {
|
|
4723
5123
|
verdict: "fail",
|
|
4724
5124
|
messages: [
|
|
@@ -4846,7 +5246,7 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
|
|
|
4846
5246
|
return findings;
|
|
4847
5247
|
}
|
|
4848
5248
|
// ../../packages/aeg-core/src/log/schema.ts
|
|
4849
|
-
import { z } from "zod";
|
|
5249
|
+
import { z as z2 } from "zod";
|
|
4850
5250
|
var ROLE_VALUES = [
|
|
4851
5251
|
"planner",
|
|
4852
5252
|
"developer",
|
|
@@ -4856,136 +5256,172 @@ var ROLE_VALUES = [
|
|
|
4856
5256
|
"archivist",
|
|
4857
5257
|
"architect"
|
|
4858
5258
|
];
|
|
4859
|
-
var RoleSchema =
|
|
5259
|
+
var RoleSchema = z2.enum(ROLE_VALUES);
|
|
4860
5260
|
var HOST_VALUES = ["hook", "ci", "cli", "loop"];
|
|
4861
|
-
var HostSchema =
|
|
5261
|
+
var HostSchema = z2.enum(HOST_VALUES);
|
|
4862
5262
|
var RUN_ID_PATTERN = /^[A-Za-z0-9_.-]{1,128}$/;
|
|
4863
|
-
var
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
doctrine: z.string(),
|
|
5263
|
+
var headerMetaCore = {
|
|
5264
|
+
ts: z2.string(),
|
|
5265
|
+
run_id: z2.string().regex(RUN_ID_PATTERN),
|
|
5266
|
+
seq: z2.number().int().nonnegative(),
|
|
5267
|
+
repo: z2.string().nullable(),
|
|
5268
|
+
vinaya: z2.string(),
|
|
5269
|
+
doctrine: z2.string(),
|
|
4871
5270
|
host: HostSchema,
|
|
4872
|
-
machine:
|
|
5271
|
+
machine: z2.string()
|
|
5272
|
+
};
|
|
5273
|
+
var HeaderMetaV1Schema = z2.object({
|
|
5274
|
+
schema: z2.literal(1),
|
|
5275
|
+
...headerMetaCore
|
|
4873
5276
|
}).strict();
|
|
4874
|
-
var
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
role: z.union([RoleSchema, z.literal("unattributed")]),
|
|
4879
|
-
round: z.number().int().optional(),
|
|
4880
|
-
objectives_version: z.string().optional()
|
|
5277
|
+
var LineageSchema = z2.object({
|
|
5278
|
+
run: z2.string().nullable(),
|
|
5279
|
+
attempt: z2.number().int().nullable(),
|
|
5280
|
+
parent: z2.string().nullable()
|
|
4881
5281
|
}).strict();
|
|
4882
|
-
var
|
|
5282
|
+
var InputVersionsSchema = z2.object({
|
|
5283
|
+
objectives_version: z2.string().nullable(),
|
|
5284
|
+
brief_hash: z2.string().nullable(),
|
|
5285
|
+
ruling_ordinal: z2.number().int().nullable(),
|
|
5286
|
+
policy_digest: z2.string().nullable()
|
|
5287
|
+
}).strict();
|
|
5288
|
+
var ProvenanceSchema = z2.enum(["parent_attributed", "env_correlated", "self_reported", "unavailable"]);
|
|
5289
|
+
var HeaderMetaV2Schema = z2.object({
|
|
5290
|
+
schema: z2.literal(2),
|
|
5291
|
+
...headerMetaCore,
|
|
5292
|
+
event_id: z2.string().min(1),
|
|
5293
|
+
process_id: z2.string().min(1),
|
|
5294
|
+
actor_id: z2.string().nullable(),
|
|
5295
|
+
lineage: LineageSchema,
|
|
5296
|
+
input_versions: InputVersionsSchema,
|
|
5297
|
+
provenance: ProvenanceSchema
|
|
5298
|
+
}).strict();
|
|
5299
|
+
var HeaderMetaSchema = z2.discriminatedUnion("schema", [HeaderMetaV1Schema, HeaderMetaV2Schema]);
|
|
5300
|
+
var SubjectSchema = z2.object({
|
|
5301
|
+
issue: z2.number().int().nullable(),
|
|
5302
|
+
pr: z2.number().int().optional(),
|
|
5303
|
+
sha: z2.string().optional(),
|
|
5304
|
+
role: z2.union([RoleSchema, z2.literal("unattributed")]),
|
|
5305
|
+
round: z2.number().int().optional(),
|
|
5306
|
+
objectives_version: z2.string().optional()
|
|
5307
|
+
}).strict();
|
|
5308
|
+
var HeaderSchema = z2.object({
|
|
4883
5309
|
meta: HeaderMetaSchema,
|
|
4884
5310
|
subject: SubjectSchema
|
|
4885
5311
|
}).strict();
|
|
4886
5312
|
var envelopeTail = {
|
|
4887
|
-
duration_ms:
|
|
4888
|
-
payload:
|
|
5313
|
+
duration_ms: z2.number().nonnegative().optional(),
|
|
5314
|
+
payload: z2.object({}).strict()
|
|
4889
5315
|
};
|
|
4890
|
-
var
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
5316
|
+
var ReviewFindingSchema = z2.object({
|
|
5317
|
+
id: z2.string(),
|
|
5318
|
+
severity: z2.string(),
|
|
5319
|
+
state: z2.string().optional(),
|
|
5320
|
+
severity_scale: z2.string().optional(),
|
|
5321
|
+
policy_treatment: z2.enum(["blocking", "non_blocking", "unavailable"]).optional(),
|
|
5322
|
+
confidence: z2.number().min(0).max(1).optional(),
|
|
5323
|
+
confidence_scale: z2.string().optional(),
|
|
5324
|
+
confidence_source: z2.string().optional()
|
|
5325
|
+
}).strict();
|
|
5326
|
+
var DispatchOutcomeSchema = z2.discriminatedUnion("type", [
|
|
5327
|
+
z2.object({ type: z2.literal("pr_opened"), pr: z2.number().int(), head: z2.string() }).strict(),
|
|
5328
|
+
z2.object({
|
|
5329
|
+
type: z2.literal("round_pushed"),
|
|
5330
|
+
pr: z2.number().int(),
|
|
5331
|
+
head: z2.string(),
|
|
5332
|
+
comment_id: z2.number().int()
|
|
4897
5333
|
}).strict(),
|
|
4898
|
-
|
|
4899
|
-
type:
|
|
4900
|
-
verdict:
|
|
4901
|
-
head:
|
|
4902
|
-
comment_id:
|
|
4903
|
-
objectives:
|
|
4904
|
-
findings:
|
|
5334
|
+
z2.object({
|
|
5335
|
+
type: z2.literal("verdict"),
|
|
5336
|
+
verdict: z2.enum(["APPROVE", "REQUEST CHANGES", "PASS", "FAIL"]),
|
|
5337
|
+
head: z2.string(),
|
|
5338
|
+
comment_id: z2.number().int(),
|
|
5339
|
+
objectives: z2.array(z2.object({ id: z2.string(), met: z2.boolean() }).strict()),
|
|
5340
|
+
findings: z2.array(ReviewFindingSchema)
|
|
4905
5341
|
}).strict(),
|
|
4906
|
-
|
|
4907
|
-
type:
|
|
4908
|
-
class:
|
|
4909
|
-
comment_id:
|
|
5342
|
+
z2.object({
|
|
5343
|
+
type: z2.literal("escalation"),
|
|
5344
|
+
class: z2.enum(["authority", "strategy", "product"]),
|
|
5345
|
+
comment_id: z2.number().int()
|
|
4910
5346
|
}).strict(),
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
5347
|
+
z2.object({ type: z2.literal("brief"), comment_id: z2.number().int(), hash: z2.string() }).strict(),
|
|
5348
|
+
z2.object({ type: z2.literal("plan"), issues: z2.array(z2.number().int()) }).strict(),
|
|
5349
|
+
z2.object({ type: z2.literal("archive"), provenance_comment_id: z2.number().int() }).strict()
|
|
4914
5350
|
]);
|
|
4915
5351
|
var dispatchShared = {
|
|
4916
5352
|
meta: HeaderMetaSchema,
|
|
4917
5353
|
subject: SubjectSchema,
|
|
4918
|
-
kind:
|
|
5354
|
+
kind: z2.literal("dispatch"),
|
|
4919
5355
|
...envelopeTail,
|
|
4920
5356
|
target_role: RoleSchema,
|
|
4921
|
-
model:
|
|
4922
|
-
round:
|
|
4923
|
-
effect_id:
|
|
5357
|
+
model: z2.string(),
|
|
5358
|
+
round: z2.number().int().optional(),
|
|
5359
|
+
effect_id: z2.string()
|
|
4924
5360
|
};
|
|
4925
|
-
var dispatchUsageField =
|
|
4926
|
-
var DispatchEventSchema =
|
|
4927
|
-
|
|
4928
|
-
|
|
5361
|
+
var dispatchUsageField = z2.object({ input: z2.number().nonnegative(), output: z2.number().nonnegative() }).strict().nullable();
|
|
5362
|
+
var DispatchEventSchema = z2.discriminatedUnion("event", [
|
|
5363
|
+
z2.object({ ...dispatchShared, event: z2.literal("dispatched"), prompt_hash: z2.string() }).strict(),
|
|
5364
|
+
z2.object({
|
|
4929
5365
|
...dispatchShared,
|
|
4930
|
-
event:
|
|
5366
|
+
event: z2.literal("outcome_received"),
|
|
4931
5367
|
outcome: DispatchOutcomeSchema,
|
|
4932
5368
|
usage: dispatchUsageField
|
|
4933
5369
|
}).strict(),
|
|
4934
|
-
|
|
5370
|
+
z2.object({
|
|
4935
5371
|
...dispatchShared,
|
|
4936
|
-
event:
|
|
4937
|
-
reason:
|
|
5372
|
+
event: z2.literal("dispatch_failed"),
|
|
5373
|
+
reason: z2.enum(["timeout", "crash", "refused", "unattributed_write"]),
|
|
4938
5374
|
usage: dispatchUsageField
|
|
4939
5375
|
}).strict()
|
|
4940
5376
|
]);
|
|
4941
5377
|
var loopShared = {
|
|
4942
5378
|
meta: HeaderMetaSchema,
|
|
4943
5379
|
subject: SubjectSchema,
|
|
4944
|
-
kind:
|
|
5380
|
+
kind: z2.literal("dev_review_loop"),
|
|
4945
5381
|
...envelopeTail,
|
|
4946
|
-
loop_id:
|
|
5382
|
+
loop_id: z2.string()
|
|
4947
5383
|
};
|
|
4948
|
-
var DevReviewLoopEventSchema =
|
|
4949
|
-
|
|
5384
|
+
var DevReviewLoopEventSchema = z2.discriminatedUnion("event", [
|
|
5385
|
+
z2.object({
|
|
4950
5386
|
...loopShared,
|
|
4951
|
-
event:
|
|
4952
|
-
task:
|
|
4953
|
-
policy:
|
|
4954
|
-
max_rounds:
|
|
4955
|
-
reviewers:
|
|
4956
|
-
models:
|
|
5387
|
+
event: z2.literal("loop_started"),
|
|
5388
|
+
task: z2.number().int(),
|
|
5389
|
+
policy: z2.object({
|
|
5390
|
+
max_rounds: z2.number().int().nonnegative(),
|
|
5391
|
+
reviewers: z2.array(RoleSchema),
|
|
5392
|
+
models: z2.record(RoleSchema, z2.string())
|
|
4957
5393
|
}).strict()
|
|
4958
5394
|
}).strict(),
|
|
4959
|
-
|
|
4960
|
-
|
|
5395
|
+
z2.object({ ...loopShared, event: z2.literal("round_started"), round: z2.number().int(), base_head: z2.string() }).strict(),
|
|
5396
|
+
z2.object({
|
|
4961
5397
|
...loopShared,
|
|
4962
|
-
event:
|
|
4963
|
-
round:
|
|
4964
|
-
head:
|
|
4965
|
-
green:
|
|
5398
|
+
event: z2.literal("gate_result_read"),
|
|
5399
|
+
round: z2.number().int(),
|
|
5400
|
+
head: z2.string(),
|
|
5401
|
+
green: z2.boolean()
|
|
4966
5402
|
}).strict(),
|
|
4967
|
-
|
|
5403
|
+
z2.object({
|
|
4968
5404
|
...loopShared,
|
|
4969
|
-
event:
|
|
4970
|
-
round:
|
|
4971
|
-
head:
|
|
4972
|
-
all_approve:
|
|
4973
|
-
blockers:
|
|
5405
|
+
event: z2.literal("verdicts_read"),
|
|
5406
|
+
round: z2.number().int(),
|
|
5407
|
+
head: z2.string(),
|
|
5408
|
+
all_approve: z2.boolean(),
|
|
5409
|
+
blockers: z2.number().int().nonnegative()
|
|
4974
5410
|
}).strict(),
|
|
4975
|
-
|
|
5411
|
+
z2.object({
|
|
4976
5412
|
...loopShared,
|
|
4977
|
-
event:
|
|
4978
|
-
round:
|
|
4979
|
-
open:
|
|
4980
|
-
resolved:
|
|
4981
|
-
new:
|
|
4982
|
-
recurring:
|
|
5413
|
+
event: z2.literal("findings_compared"),
|
|
5414
|
+
round: z2.number().int(),
|
|
5415
|
+
open: z2.array(z2.string()),
|
|
5416
|
+
resolved: z2.array(z2.string()),
|
|
5417
|
+
new: z2.array(z2.string()),
|
|
5418
|
+
recurring: z2.array(z2.string())
|
|
4983
5419
|
}).strict(),
|
|
4984
|
-
|
|
5420
|
+
z2.object({
|
|
4985
5421
|
...loopShared,
|
|
4986
|
-
event:
|
|
4987
|
-
round:
|
|
4988
|
-
condition:
|
|
5422
|
+
event: z2.literal("stop_condition_met"),
|
|
5423
|
+
round: z2.number().int(),
|
|
5424
|
+
condition: z2.enum([
|
|
4989
5425
|
"green",
|
|
4990
5426
|
"max_rounds",
|
|
4991
5427
|
"no_progress",
|
|
@@ -4995,42 +5431,49 @@ var DevReviewLoopEventSchema = z.discriminatedUnion("event", [
|
|
|
4995
5431
|
"reappearance"
|
|
4996
5432
|
])
|
|
4997
5433
|
}).strict(),
|
|
4998
|
-
|
|
5434
|
+
z2.object({
|
|
5435
|
+
...loopShared,
|
|
5436
|
+
event: z2.literal("paused"),
|
|
5437
|
+
round: z2.number().int(),
|
|
5438
|
+
reason: z2.enum(["escalation", "principal_item", "refreeze_needed"])
|
|
5439
|
+
}).strict(),
|
|
5440
|
+
z2.object({
|
|
4999
5441
|
...loopShared,
|
|
5000
|
-
event:
|
|
5001
|
-
round:
|
|
5002
|
-
|
|
5442
|
+
event: z2.literal("resumed"),
|
|
5443
|
+
round: z2.number().int(),
|
|
5444
|
+
by: z2.literal("principal")
|
|
5003
5445
|
}).strict(),
|
|
5004
|
-
|
|
5446
|
+
z2.object({
|
|
5005
5447
|
...loopShared,
|
|
5006
|
-
event:
|
|
5007
|
-
round:
|
|
5008
|
-
|
|
5448
|
+
event: z2.literal("unpushed_work_resume"),
|
|
5449
|
+
round: z2.number().int(),
|
|
5450
|
+
branch: z2.string(),
|
|
5451
|
+
detail: z2.string()
|
|
5009
5452
|
}).strict(),
|
|
5010
|
-
|
|
5453
|
+
z2.object({
|
|
5011
5454
|
...loopShared,
|
|
5012
|
-
event:
|
|
5013
|
-
round:
|
|
5014
|
-
base_head:
|
|
5015
|
-
head:
|
|
5016
|
-
files_changed:
|
|
5017
|
-
insertions:
|
|
5018
|
-
deletions:
|
|
5019
|
-
wall_ms:
|
|
5020
|
-
outcome:
|
|
5455
|
+
event: z2.literal("round_ended"),
|
|
5456
|
+
round: z2.number().int(),
|
|
5457
|
+
base_head: z2.string(),
|
|
5458
|
+
head: z2.string(),
|
|
5459
|
+
files_changed: z2.number().int().nonnegative(),
|
|
5460
|
+
insertions: z2.number().int().nonnegative(),
|
|
5461
|
+
deletions: z2.number().int().nonnegative(),
|
|
5462
|
+
wall_ms: z2.number().nonnegative(),
|
|
5463
|
+
outcome: z2.enum(["green", "changes_requested", "escalated"])
|
|
5021
5464
|
}).strict(),
|
|
5022
|
-
|
|
5465
|
+
z2.object({
|
|
5023
5466
|
...loopShared,
|
|
5024
|
-
event:
|
|
5025
|
-
rounds:
|
|
5026
|
-
total_wall_ms:
|
|
5027
|
-
time_to_green_ms:
|
|
5028
|
-
files_changed_total:
|
|
5029
|
-
final_head:
|
|
5030
|
-
result:
|
|
5467
|
+
event: z2.literal("journal_finalized"),
|
|
5468
|
+
rounds: z2.number().int().nonnegative(),
|
|
5469
|
+
total_wall_ms: z2.number().nonnegative(),
|
|
5470
|
+
time_to_green_ms: z2.number().nonnegative().nullable(),
|
|
5471
|
+
files_changed_total: z2.number().int().nonnegative(),
|
|
5472
|
+
final_head: z2.string(),
|
|
5473
|
+
result: z2.enum(["merged_ready", "stopped"])
|
|
5031
5474
|
}).strict()
|
|
5032
5475
|
]);
|
|
5033
|
-
var ForgeOpSchema =
|
|
5476
|
+
var ForgeOpSchema = z2.enum([
|
|
5034
5477
|
"pr.create",
|
|
5035
5478
|
"pr.comment",
|
|
5036
5479
|
"pr.body.replace",
|
|
@@ -5044,24 +5487,210 @@ var ForgeOpSchema = z.enum([
|
|
|
5044
5487
|
"label.add",
|
|
5045
5488
|
"label.remove"
|
|
5046
5489
|
]);
|
|
5047
|
-
var ForgeWriteTargetSchema =
|
|
5048
|
-
issue:
|
|
5049
|
-
pr:
|
|
5490
|
+
var ForgeWriteTargetSchema = z2.object({
|
|
5491
|
+
issue: z2.number().int().optional(),
|
|
5492
|
+
pr: z2.number().int().optional()
|
|
5050
5493
|
}).strict();
|
|
5051
5494
|
var forgeWriteShared = {
|
|
5052
5495
|
meta: HeaderMetaSchema,
|
|
5053
5496
|
subject: SubjectSchema,
|
|
5054
|
-
kind:
|
|
5497
|
+
kind: z2.literal("forge_write"),
|
|
5055
5498
|
...envelopeTail,
|
|
5056
5499
|
op: ForgeOpSchema,
|
|
5057
5500
|
target: ForgeWriteTargetSchema
|
|
5058
5501
|
};
|
|
5059
|
-
var ForgeWriteEventSchema =
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5502
|
+
var ForgeWriteEventSchema = z2.discriminatedUnion("event", [
|
|
5503
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("validated") }).strict(),
|
|
5504
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("refused"), reason: z2.string() }).strict(),
|
|
5505
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("written"), comment_ids: z2.array(z2.string()) }).strict()
|
|
5063
5506
|
]);
|
|
5064
|
-
var
|
|
5507
|
+
var GateOutcomeSchema = z2.enum([
|
|
5508
|
+
"pass",
|
|
5509
|
+
"fail",
|
|
5510
|
+
"wait",
|
|
5511
|
+
"skip",
|
|
5512
|
+
"invalid_input",
|
|
5513
|
+
"unavailable_dependency",
|
|
5514
|
+
"timeout",
|
|
5515
|
+
"cancelled"
|
|
5516
|
+
]);
|
|
5517
|
+
var gateShared = {
|
|
5518
|
+
meta: HeaderMetaSchema,
|
|
5519
|
+
subject: SubjectSchema,
|
|
5520
|
+
kind: z2.literal("gate"),
|
|
5521
|
+
...envelopeTail,
|
|
5522
|
+
check: z2.string(),
|
|
5523
|
+
check_version: z2.string().nullable(),
|
|
5524
|
+
policy_version: z2.string().nullable(),
|
|
5525
|
+
input_fingerprint: z2.string().nullable()
|
|
5526
|
+
};
|
|
5527
|
+
var GateEventSchema = z2.discriminatedUnion("event", [
|
|
5528
|
+
z2.object({
|
|
5529
|
+
...gateShared,
|
|
5530
|
+
event: z2.literal("checked"),
|
|
5531
|
+
outcome: GateOutcomeSchema,
|
|
5532
|
+
reason: z2.string().optional()
|
|
5533
|
+
}).strict()
|
|
5534
|
+
]);
|
|
5535
|
+
var OperationResultSchema = z2.enum(["ok", "error", "refused", "timeout", "cancelled", "unavailable"]);
|
|
5536
|
+
var operationShared = {
|
|
5537
|
+
meta: HeaderMetaSchema,
|
|
5538
|
+
subject: SubjectSchema,
|
|
5539
|
+
kind: z2.literal("operation"),
|
|
5540
|
+
...envelopeTail,
|
|
5541
|
+
operation: z2.string(),
|
|
5542
|
+
target: z2.string().nullable()
|
|
5543
|
+
};
|
|
5544
|
+
var OperationEventSchema = z2.discriminatedUnion("event", [
|
|
5545
|
+
z2.object({
|
|
5546
|
+
...operationShared,
|
|
5547
|
+
event: z2.literal("completed"),
|
|
5548
|
+
result: OperationResultSchema,
|
|
5549
|
+
error_class: z2.string().nullable()
|
|
5550
|
+
}).strict()
|
|
5551
|
+
]);
|
|
5552
|
+
var UsageUnitsSchema = z2.object({
|
|
5553
|
+
input: z2.number().nonnegative().nullable(),
|
|
5554
|
+
output: z2.number().nonnegative().nullable(),
|
|
5555
|
+
cache: z2.number().nonnegative().nullable()
|
|
5556
|
+
}).strict();
|
|
5557
|
+
var usageShared = {
|
|
5558
|
+
meta: HeaderMetaSchema,
|
|
5559
|
+
subject: SubjectSchema,
|
|
5560
|
+
kind: z2.literal("usage"),
|
|
5561
|
+
...envelopeTail,
|
|
5562
|
+
model: z2.string().nullable(),
|
|
5563
|
+
source: z2.string(),
|
|
5564
|
+
semantics: z2.enum(["cumulative", "delta"])
|
|
5565
|
+
};
|
|
5566
|
+
var UsageEventSchema = z2.discriminatedUnion("event", [
|
|
5567
|
+
z2.object({
|
|
5568
|
+
...usageShared,
|
|
5569
|
+
event: z2.literal("observed"),
|
|
5570
|
+
units: UsageUnitsSchema,
|
|
5571
|
+
unknown_reason: z2.string().nullable()
|
|
5572
|
+
}).strict()
|
|
5573
|
+
]);
|
|
5574
|
+
var RoleAttemptOutcomeSchema = z2.enum([
|
|
5575
|
+
"completed",
|
|
5576
|
+
"incomplete",
|
|
5577
|
+
"infrastructure_failed",
|
|
5578
|
+
"cancelled",
|
|
5579
|
+
"timed_out",
|
|
5580
|
+
"capability_refused"
|
|
5581
|
+
]);
|
|
5582
|
+
var roleAttemptShared = {
|
|
5583
|
+
meta: HeaderMetaSchema,
|
|
5584
|
+
subject: SubjectSchema,
|
|
5585
|
+
kind: z2.literal("role_attempt"),
|
|
5586
|
+
...envelopeTail,
|
|
5587
|
+
actor: z2.string().nullable(),
|
|
5588
|
+
attempt: z2.number().int().nullable()
|
|
5589
|
+
};
|
|
5590
|
+
var RoleAttemptEventSchema = z2.discriminatedUnion("event", [
|
|
5591
|
+
z2.object({
|
|
5592
|
+
...roleAttemptShared,
|
|
5593
|
+
event: z2.literal("attempted"),
|
|
5594
|
+
outcome: RoleAttemptOutcomeSchema,
|
|
5595
|
+
usage: dispatchUsageField
|
|
5596
|
+
}).strict()
|
|
5597
|
+
]);
|
|
5598
|
+
var handoffShared = {
|
|
5599
|
+
meta: HeaderMetaSchema,
|
|
5600
|
+
subject: SubjectSchema,
|
|
5601
|
+
kind: z2.literal("handoff"),
|
|
5602
|
+
...envelopeTail,
|
|
5603
|
+
class: z2.enum(["authority", "strategy", "product"]),
|
|
5604
|
+
reason: z2.string()
|
|
5605
|
+
};
|
|
5606
|
+
var HandoffEventSchema = z2.discriminatedUnion("event", [
|
|
5607
|
+
z2.object({
|
|
5608
|
+
...handoffShared,
|
|
5609
|
+
event: z2.literal("raised"),
|
|
5610
|
+
requested_decision: z2.string().nullable()
|
|
5611
|
+
}).strict(),
|
|
5612
|
+
z2.object({
|
|
5613
|
+
...handoffShared,
|
|
5614
|
+
event: z2.literal("resolved"),
|
|
5615
|
+
resolution: z2.string().nullable(),
|
|
5616
|
+
resolved_by: z2.string().nullable()
|
|
5617
|
+
}).strict()
|
|
5618
|
+
]);
|
|
5619
|
+
var EffectTargetSchema = z2.object({
|
|
5620
|
+
kind: z2.string(),
|
|
5621
|
+
ref: z2.string()
|
|
5622
|
+
}).strict();
|
|
5623
|
+
var effectShared = {
|
|
5624
|
+
meta: HeaderMetaSchema,
|
|
5625
|
+
subject: SubjectSchema,
|
|
5626
|
+
kind: z2.literal("effect"),
|
|
5627
|
+
...envelopeTail,
|
|
5628
|
+
effect_id: z2.string(),
|
|
5629
|
+
target: EffectTargetSchema
|
|
5630
|
+
};
|
|
5631
|
+
var EffectEventSchema = z2.discriminatedUnion("event", [
|
|
5632
|
+
z2.object({ ...effectShared, event: z2.literal("attempted") }).strict(),
|
|
5633
|
+
z2.object({ ...effectShared, event: z2.literal("observed"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict(),
|
|
5634
|
+
z2.object({ ...effectShared, event: z2.literal("verified"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict()
|
|
5635
|
+
]);
|
|
5636
|
+
var LogEventSchema = z2.union([
|
|
5637
|
+
DispatchEventSchema,
|
|
5638
|
+
DevReviewLoopEventSchema,
|
|
5639
|
+
ForgeWriteEventSchema,
|
|
5640
|
+
GateEventSchema,
|
|
5641
|
+
OperationEventSchema,
|
|
5642
|
+
UsageEventSchema,
|
|
5643
|
+
RoleAttemptEventSchema,
|
|
5644
|
+
HandoffEventSchema,
|
|
5645
|
+
EffectEventSchema
|
|
5646
|
+
]);
|
|
5647
|
+
// ../../packages/aeg-core/src/dev-review-loop/journal-reconstruction.ts
|
|
5648
|
+
var STOPPED_CONDITIONS = new Set(["confidence", "reappearance", "no_progress", "max_rounds"]);
|
|
5649
|
+
// ../../packages/aeg-core/src/control-store/records.ts
|
|
5650
|
+
import { z as z3 } from "zod";
|
|
5651
|
+
var isoTimestamp = z3.string().min(1);
|
|
5652
|
+
var taskId = z3.number().int().positive();
|
|
5653
|
+
var epochNumber = z3.number().int().nonnegative();
|
|
5654
|
+
var RunRecordSchema = z3.object({
|
|
5655
|
+
version: z3.literal(1),
|
|
5656
|
+
kind: z3.literal("run"),
|
|
5657
|
+
task: taskId,
|
|
5658
|
+
runId: z3.string().min(1),
|
|
5659
|
+
pid: z3.number().int().positive(),
|
|
5660
|
+
host: z3.string().min(1),
|
|
5661
|
+
startedAt: isoTimestamp
|
|
5662
|
+
}).strict();
|
|
5663
|
+
var InputRecordSchema = z3.object({
|
|
5664
|
+
version: z3.literal(1),
|
|
5665
|
+
kind: z3.literal("input"),
|
|
5666
|
+
task: taskId,
|
|
5667
|
+
runId: z3.string().min(1),
|
|
5668
|
+
source: z3.union([z3.literal("fresh"), z3.literal("resume")]),
|
|
5669
|
+
pr: z3.number().int().positive().nullable(),
|
|
5670
|
+
round: z3.number().int().nonnegative(),
|
|
5671
|
+
recordedAt: isoTimestamp
|
|
5672
|
+
}).strict();
|
|
5673
|
+
var OwnershipRecordSchema = z3.object({
|
|
5674
|
+
version: z3.literal(1),
|
|
5675
|
+
kind: z3.literal("ownership"),
|
|
5676
|
+
task: taskId,
|
|
5677
|
+
epoch: epochNumber,
|
|
5678
|
+
ownerId: z3.string().min(1),
|
|
5679
|
+
pid: z3.number().int().positive(),
|
|
5680
|
+
host: z3.string().min(1),
|
|
5681
|
+
acquiredAt: isoTimestamp
|
|
5682
|
+
}).strict();
|
|
5683
|
+
var TransitionRecordSchema = z3.object({
|
|
5684
|
+
version: z3.literal(1),
|
|
5685
|
+
kind: z3.literal("transition"),
|
|
5686
|
+
task: taskId,
|
|
5687
|
+
epoch: epochNumber,
|
|
5688
|
+
seq: z3.number().int().nonnegative(),
|
|
5689
|
+
from: z3.string().min(1),
|
|
5690
|
+
to: z3.string().min(1),
|
|
5691
|
+
detail: z3.string().optional(),
|
|
5692
|
+
at: isoTimestamp
|
|
5693
|
+
}).strict();
|
|
5065
5694
|
// ../../packages/sources/src/commands.ts
|
|
5066
5695
|
var COMMANDS = [
|
|
5067
5696
|
{
|
|
@@ -5220,6 +5849,17 @@ var COMMANDS = [
|
|
|
5220
5849
|
],
|
|
5221
5850
|
status: "shipped"
|
|
5222
5851
|
},
|
|
5852
|
+
{
|
|
5853
|
+
name: "task status",
|
|
5854
|
+
description: "Every open task with a frozen brief, its pull request, and whether its loop is running, paused, or published",
|
|
5855
|
+
flags: [{ flag: "--json", description: "Enveloped JSON output (schema: 1)" }],
|
|
5856
|
+
details: [
|
|
5857
|
+
"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`.",
|
|
5858
|
+
"`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.",
|
|
5859
|
+
"`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."
|
|
5860
|
+
],
|
|
5861
|
+
status: "shipped"
|
|
5862
|
+
},
|
|
5223
5863
|
{
|
|
5224
5864
|
name: "pr create",
|
|
5225
5865
|
description: "Open a pull request after full brief-schema validation",
|
|
@@ -5729,10 +6369,10 @@ function createForgeSource(config) {
|
|
|
5729
6369
|
};
|
|
5730
6370
|
}
|
|
5731
6371
|
// ../../packages/sources/src/select-source.ts
|
|
5732
|
-
import { z as
|
|
5733
|
-
var StateSourceConfigSchema =
|
|
5734
|
-
|
|
5735
|
-
|
|
6372
|
+
import { z as z4 } from "zod";
|
|
6373
|
+
var StateSourceConfigSchema = z4.discriminatedUnion("kind", [
|
|
6374
|
+
z4.object({ kind: z4.literal("forge"), owner: z4.string(), repo: z4.string() }),
|
|
6375
|
+
z4.object({ kind: z4.literal("file"), root: z4.string().optional() })
|
|
5736
6376
|
]);
|
|
5737
6377
|
// src/checks/contract.ts
|
|
5738
6378
|
var CHECK_SCHEMA_VERSION = 1;
|
|
@@ -5771,9 +6411,9 @@ async function main() {
|
|
|
5771
6411
|
const prBody = process.env.PR_BODY ?? "";
|
|
5772
6412
|
const repo = resolveRepo2();
|
|
5773
6413
|
const files = [];
|
|
5774
|
-
const
|
|
5775
|
-
if (
|
|
5776
|
-
const slug =
|
|
6414
|
+
const ref = parseTaskBranchIdentity(branch);
|
|
6415
|
+
if (ref?.kind === "tranche" && repo) {
|
|
6416
|
+
const slug = ref.tranche;
|
|
5777
6417
|
try {
|
|
5778
6418
|
const source = createForgeSource({ owner: repo.owner, repo: repo.repo });
|
|
5779
6419
|
const tranche = await source.getTranche(slug);
|
|
@@ -5782,7 +6422,7 @@ async function main() {
|
|
|
5782
6422
|
}
|
|
5783
6423
|
const referenced = [...extractClosesReferences(prBody)];
|
|
5784
6424
|
const taskIssueRefs = repo && referenced.length > 0 ? await fetchTaskIssueRefs(repo.owner, repo.repo, referenced) : undefined;
|
|
5785
|
-
const result =
|
|
6425
|
+
const result = checkClosesNTopology(branch, prBody, files, taskIssueRefs);
|
|
5786
6426
|
if (!result.ok) {
|
|
5787
6427
|
emitCheckError({
|
|
5788
6428
|
schema: CHECK_SCHEMA_VERSION,
|