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