@attalabs/vinaya 0.25.0 → 0.27.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 +6 -4
- package/aeg-root/contracts/architect-planner.md +5 -5
- package/aeg-root/contracts/developer-reviewer.md +7 -7
- package/aeg-root/contracts/planner-developer.md +142 -0
- package/aeg-root/contracts/reviewer-archivist.md +2 -2
- package/aeg-root/contracts/tranche-archivist-planner.md +12 -11
- package/aeg-root/enforcement.md +17 -7
- package/aeg-root/milestone-model.md +2 -0
- package/aeg-root/process.md +53 -75
- package/aeg-root/roles/archivist.md +3 -3
- package/aeg-root/roles/developer.md +10 -10
- package/aeg-root/roles/planner.md +90 -23
- package/aeg-root/roles/principal.md +15 -13
- package/aeg-root/roles/reviewer.md +16 -12
- package/aeg-root/roles/security.md +13 -9
- package/aeg-root/roles/tranche-archivist.md +2 -2
- package/aeg-root/skills/aeg/SKILL.md +5 -5
- package/aeg-root/skills/aeg-roles/SKILL.md +7 -7
- package/aeg-root/state-machine.md +35 -34
- package/aeg-root/task-model.md +3 -3
- package/aeg-root/templates/brief-template.md +2 -2
- package/aeg-root/templates/issue-rationale-template.md +3 -3
- package/aeg-root/tranche-model.md +21 -21
- package/dist/checks/bin/check-body-bare-digits.js +233 -30
- package/dist/checks/bin/check-branch-topology.js +253 -32
- package/dist/checks/bin/check-brief-shape.js +291 -873
- package/dist/checks/bin/check-changeset-coverage.js +1012 -37
- package/dist/checks/bin/check-closes-n.js +253 -32
- package/dist/checks/bin/check-coherence.js +253 -32
- package/dist/checks/bin/check-dead-branch-push.js +215 -30
- package/dist/checks/bin/check-dispatch-readiness.js +256 -35
- package/dist/checks/bin/check-doc-coverage-push.js +1012 -37
- package/dist/checks/bin/check-doc-coverage.js +1014 -39
- package/dist/checks/bin/check-doctrine-no-procedures.js +215 -30
- package/dist/checks/bin/check-doctrine-portability.js +1012 -37
- package/dist/checks/bin/check-evidence-fresh.js +755 -71
- package/dist/checks/bin/check-exec-bits.js +1012 -37
- package/dist/checks/bin/check-first-push-dispatch.js +253 -32
- package/dist/checks/bin/check-issue-assignment.js +253 -32
- package/dist/checks/bin/check-main-branch-refusal.js +216 -31
- package/dist/checks/bin/check-no-disk-state.js +215 -30
- package/dist/checks/bin/check-pr-premise-reassert.js +5489 -0
- package/dist/checks/bin/check-pr-report-density.js +215 -30
- package/dist/checks/bin/check-quoted-command.js +1010 -35
- package/dist/checks/bin/check-reader-resolvable-prose.js +1010 -35
- package/dist/checks/bin/check-registry-gates.js +246 -32
- package/dist/checks/bin/check-retired-vocabulary.js +1010 -35
- package/dist/checks/bin/check-review-gate.js +274 -85
- package/dist/checks/bin/check-single-plan-pr.js +215 -30
- package/dist/checks/bin/check-surface-scope.js +5846 -0
- package/dist/checks/bin/check-test-plan.js +215 -30
- package/dist/checks/bin/check-token-collection-wired.js +215 -30
- package/dist/checks/bin/check-token-report.js +227 -37
- package/dist/checks/bin/check-workspace-escape.js +1010 -35
- package/dist/index.js +2407 -607
- package/package.json +1 -1
- package/aeg-root/contracts/brief-developer.md +0 -141
- package/aeg-root/contracts/planner-brief.md +0 -143
- package/aeg-root/roles/brief-author.md +0 -116
- package/aeg-root/skills/brief-authoring/SKILL.md +0 -509
|
@@ -29,6 +29,15 @@ var GATE_AUDIENCE = {
|
|
|
29
29
|
internal: "A composite that shells this repo's own `typecheck`/`lint`/`test`/`build` scripts by name. Those names are this monorepo's toolchain (bun + turbo + biome), not an adopter's; the shipped equivalent of its intent is `vinaya check --all`, which is portable by construction."
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
|
+
var NON_GATE_BINS = [
|
|
33
|
+
"archive-task",
|
|
34
|
+
"assign-task-issue",
|
|
35
|
+
"dead-branch-audit",
|
|
36
|
+
"eval-agent-compliance",
|
|
37
|
+
"open-issue",
|
|
38
|
+
"open-pr",
|
|
39
|
+
"report-tokens"
|
|
40
|
+
];
|
|
32
41
|
function isShipped(a) {
|
|
33
42
|
return "shippedAs" in a;
|
|
34
43
|
}
|
|
@@ -296,7 +305,7 @@ function issueListByLabelArgs(owner, repo, label) {
|
|
|
296
305
|
"--state",
|
|
297
306
|
"all",
|
|
298
307
|
"--json",
|
|
299
|
-
"number,title,body,state,labels,milestone",
|
|
308
|
+
"number,title,body,state,labels,milestone,stateReason",
|
|
300
309
|
"--limit",
|
|
301
310
|
"200"
|
|
302
311
|
];
|
|
@@ -389,7 +398,7 @@ var LABELS = [
|
|
|
389
398
|
id: "vinaya/needs:brief-correction",
|
|
390
399
|
category: "needs",
|
|
391
400
|
form: "literal",
|
|
392
|
-
carries: "Waiting on the
|
|
401
|
+
carries: "Waiting on the Planner — the brief contradicts the surface it describes."
|
|
393
402
|
},
|
|
394
403
|
{
|
|
395
404
|
key: "waiver-docs",
|
|
@@ -642,14 +651,19 @@ function resolveTaskIssueRef(title, labels) {
|
|
|
642
651
|
var INTENTS_HEADING = /^#{1,6}\s*Tranche intents\s*$/im;
|
|
643
652
|
var NEXT_HEADING = /^#{1,6}\s+\S/m;
|
|
644
653
|
var INTENT_BULLET = /^-\s+([a-z0-9][a-z0-9-]*)\s*:\s*(.+)$/i;
|
|
645
|
-
function
|
|
646
|
-
const text = stripCode(description, { inlineSpans: "keep" });
|
|
654
|
+
function intentsSection(text) {
|
|
647
655
|
const start = text.match(INTENTS_HEADING);
|
|
648
656
|
if (!start || start.index === undefined)
|
|
649
|
-
return
|
|
657
|
+
return null;
|
|
650
658
|
const rest = text.slice(start.index + start[0].length);
|
|
651
659
|
const next = rest.match(NEXT_HEADING);
|
|
652
|
-
|
|
660
|
+
return rest.slice(0, next && next.index !== undefined ? next.index : rest.length);
|
|
661
|
+
}
|
|
662
|
+
function intentGoalForSlug(description, slug) {
|
|
663
|
+
const text = stripCode(description, { inlineSpans: "keep" });
|
|
664
|
+
const section = intentsSection(text);
|
|
665
|
+
if (section === null)
|
|
666
|
+
return "";
|
|
653
667
|
for (const line of section.split(`
|
|
654
668
|
`)) {
|
|
655
669
|
const trimmed = line.trim();
|
|
@@ -1941,6 +1955,7 @@ function parseInlineFieldList(section) {
|
|
|
1941
1955
|
// ../../packages/aeg-core/src/verdict-extraction.ts
|
|
1942
1956
|
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
1943
1957
|
var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1958
|
+
var RULING_ORDINAL_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Ruling ordinal:\s*(\d+)(?!\d)/im;
|
|
1944
1959
|
function firstFiveLines(comment) {
|
|
1945
1960
|
return comment.split(`
|
|
1946
1961
|
`).slice(0, 5).join(`
|
|
@@ -1954,6 +1969,15 @@ function extractObjectivesVersion(comment) {
|
|
|
1954
1969
|
const m = firstFiveLines(comment).match(OBJECTIVES_VERSION_PATTERN);
|
|
1955
1970
|
return m ? m[1].toLowerCase() : null;
|
|
1956
1971
|
}
|
|
1972
|
+
function firstSevenLines(comment) {
|
|
1973
|
+
return comment.split(`
|
|
1974
|
+
`).slice(0, 7).join(`
|
|
1975
|
+
`);
|
|
1976
|
+
}
|
|
1977
|
+
function extractRulingOrdinal(comment) {
|
|
1978
|
+
const m = firstSevenLines(comment).match(RULING_ORDINAL_PATTERN);
|
|
1979
|
+
return m ? Number.parseInt(m[1], 10) : null;
|
|
1980
|
+
}
|
|
1957
1981
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
1958
1982
|
const candidates = comments.filter((c) => valuePattern.test(c));
|
|
1959
1983
|
if (candidates.length === 0) {
|
|
@@ -1961,6 +1985,7 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1961
1985
|
value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
|
|
1962
1986
|
headSha: null,
|
|
1963
1987
|
objectivesVersion: null,
|
|
1988
|
+
rulingOrdinal: null,
|
|
1964
1989
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
1965
1990
|
};
|
|
1966
1991
|
}
|
|
@@ -1971,6 +1996,7 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1971
1996
|
value: `the most recent ${missingLabel} comment's VERDICT line is not within its first five lines — DANGLING, see below`,
|
|
1972
1997
|
headSha: null,
|
|
1973
1998
|
objectivesVersion: null,
|
|
1999
|
+
rulingOrdinal: null,
|
|
1974
2000
|
danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
|
|
1975
2001
|
};
|
|
1976
2002
|
}
|
|
@@ -1978,6 +2004,7 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1978
2004
|
value: m[1].toUpperCase().replace(/[_-]/g, " "),
|
|
1979
2005
|
headSha: extractHeadSha(latest),
|
|
1980
2006
|
objectivesVersion: extractObjectivesVersion(latest),
|
|
2007
|
+
rulingOrdinal: extractRulingOrdinal(latest),
|
|
1981
2008
|
danglingNote: null
|
|
1982
2009
|
};
|
|
1983
2010
|
}
|
|
@@ -2339,6 +2366,9 @@ import { createHash as createHash2 } from "node:crypto";
|
|
|
2339
2366
|
var HEADING_RE = /^##[ \t]*Objectives[ \t]*$/im;
|
|
2340
2367
|
var NEXT_HEADING_RE = /^##[ \t]/m;
|
|
2341
2368
|
var OBJECTIVE_LINE_RE = /^O(\d+)\.[ \t]*(.*)$/;
|
|
2369
|
+
function maskedForHeadingSearch(body) {
|
|
2370
|
+
return maskDetailsBlocks(maskCode(body));
|
|
2371
|
+
}
|
|
2342
2372
|
function hasBacktickedPath(text) {
|
|
2343
2373
|
let i = 0;
|
|
2344
2374
|
while (i < text.length) {
|
|
@@ -2361,16 +2391,22 @@ function stripObjectiveBackticks(text) {
|
|
|
2361
2391
|
function wordCount(text) {
|
|
2362
2392
|
return text.split(/\s+/).filter((w) => /[a-z]/i.test(w)).length;
|
|
2363
2393
|
}
|
|
2364
|
-
function
|
|
2365
|
-
const
|
|
2394
|
+
function objectivesSectionBounds(body) {
|
|
2395
|
+
const masked = maskedForHeadingSearch(body);
|
|
2396
|
+
const heading = HEADING_RE.exec(masked);
|
|
2366
2397
|
if (!heading)
|
|
2367
2398
|
return null;
|
|
2368
|
-
const
|
|
2399
|
+
const start = heading.index + heading[0].length;
|
|
2400
|
+
const afterHeading = masked.slice(start);
|
|
2369
2401
|
const next = NEXT_HEADING_RE.exec(afterHeading);
|
|
2370
|
-
return next ?
|
|
2402
|
+
return { start, end: next ? start + next.index : body.length };
|
|
2403
|
+
}
|
|
2404
|
+
function objectivesSectionText(body) {
|
|
2405
|
+
const bounds = objectivesSectionBounds(body);
|
|
2406
|
+
return bounds === null ? null : body.slice(bounds.start, bounds.end);
|
|
2371
2407
|
}
|
|
2372
2408
|
function hasObjectivesHeading(body) {
|
|
2373
|
-
return HEADING_RE.test(body);
|
|
2409
|
+
return HEADING_RE.test(maskedForHeadingSearch(body));
|
|
2374
2410
|
}
|
|
2375
2411
|
function objectivesOf(body) {
|
|
2376
2412
|
const section = objectivesSectionText(body);
|
|
@@ -2396,7 +2432,7 @@ function objectivesOf(body) {
|
|
|
2396
2432
|
continue;
|
|
2397
2433
|
}
|
|
2398
2434
|
if (hasBacktickedPath(text) && wordCount(stripObjectiveBackticks(text)) < MIN_WORDS_OUTSIDE_BACKTICKS) {
|
|
2399
|
-
errors.push(`O${n} is little more than a file path — an objective states an observable outcome, never a bare path (the
|
|
2435
|
+
errors.push(`O${n} is little more than a file path — an objective states an observable outcome, never a bare path (the Planner maps it to files).`);
|
|
2400
2436
|
continue;
|
|
2401
2437
|
}
|
|
2402
2438
|
objectives.push({ id: `O${n}`, text });
|
|
@@ -2430,6 +2466,15 @@ function isIssueNotFoundError(err) {
|
|
|
2430
2466
|
`);
|
|
2431
2467
|
return /could not resolve to an (?:issue|pull request)|\b404\b|not found/i.test(haystack);
|
|
2432
2468
|
}
|
|
2469
|
+
function resolveObjectivesSource(prBody, issue, cutoverIssue) {
|
|
2470
|
+
if (issue !== null && issue < cutoverIssue)
|
|
2471
|
+
return { kind: "none" };
|
|
2472
|
+
if (issue !== null)
|
|
2473
|
+
return { kind: "issue", issue };
|
|
2474
|
+
if (hasObjectivesHeading(prBody))
|
|
2475
|
+
return { kind: "body" };
|
|
2476
|
+
return { kind: "none" };
|
|
2477
|
+
}
|
|
2433
2478
|
|
|
2434
2479
|
// ../../packages/aeg-core/src/premise-check.ts
|
|
2435
2480
|
import { createHash as createHash3 } from "node:crypto";
|
|
@@ -2608,18 +2653,14 @@ function checkTestPlanExclusivity(prBody) {
|
|
|
2608
2653
|
function checkPrincipalPlaceholder(prBody) {
|
|
2609
2654
|
const region = testPlanRegion(prBody);
|
|
2610
2655
|
const lineRe = /^-\s*\[[ xX]\]\s*\*{2}\[principal\]\*{2}(.*)$/gim;
|
|
2656
|
+
const errors = [];
|
|
2611
2657
|
for (const m of region.matchAll(lineRe)) {
|
|
2612
2658
|
const content = m[1] ?? "";
|
|
2613
2659
|
if (/^\s*None\b/i.test(content)) {
|
|
2614
|
-
|
|
2615
|
-
status: "fail",
|
|
2616
|
-
errors: [
|
|
2617
|
-
'brief-validation Test Plan shape: a `**[principal]**` checkbox item is a "None" placeholder — if there is no principal-runnable surface, omit the item entirely; an untickable placeholder box blocks the merge gate forever.'
|
|
2618
|
-
]
|
|
2619
|
-
};
|
|
2660
|
+
errors.push(`brief-validation Test Plan shape: a \`**[principal]**\` checkbox item is a "None" placeholder ("${content.trim()}") — if there is no principal-runnable surface, omit the item entirely; an untickable placeholder box blocks the merge gate forever.`);
|
|
2620
2661
|
}
|
|
2621
2662
|
}
|
|
2622
|
-
return { status: "pass", errors: [] };
|
|
2663
|
+
return errors.length > 0 ? { status: "fail", errors } : { status: "pass", errors: [] };
|
|
2623
2664
|
}
|
|
2624
2665
|
function checkSurfaceMap(prBody) {
|
|
2625
2666
|
return headingCheck(prBody, "(?:technical\\s+)?surface map", "Technical surface map");
|
|
@@ -2935,6 +2976,41 @@ function checkBriefSections(prBody, readTier, options = {}) {
|
|
|
2935
2976
|
];
|
|
2936
2977
|
return { errors: results.flatMap((r) => r.errors) };
|
|
2937
2978
|
}
|
|
2979
|
+
function contentAfterNLines(body, n) {
|
|
2980
|
+
let idx = -1;
|
|
2981
|
+
for (let i = 0;i < n; i++) {
|
|
2982
|
+
idx = body.indexOf(`
|
|
2983
|
+
`, idx + 1);
|
|
2984
|
+
if (idx === -1)
|
|
2985
|
+
return "";
|
|
2986
|
+
}
|
|
2987
|
+
return body.slice(idx + 1);
|
|
2988
|
+
}
|
|
2989
|
+
var BRIEF_MARKER_LINE_RE = /^<!-- aeg:brief:v(\d+) -->$/;
|
|
2990
|
+
function parseBriefMarkerVersion(firstLine) {
|
|
2991
|
+
const m = BRIEF_MARKER_LINE_RE.exec(firstLine.trim());
|
|
2992
|
+
if (!m)
|
|
2993
|
+
return null;
|
|
2994
|
+
const version = Number.parseInt(m[1], 10);
|
|
2995
|
+
return Number.isInteger(version) && version >= 1 ? version : null;
|
|
2996
|
+
}
|
|
2997
|
+
function frozenBriefContent(body, version) {
|
|
2998
|
+
return contentAfterNLines(body, version === 1 ? 2 : 3);
|
|
2999
|
+
}
|
|
3000
|
+
function resolveNewestFrozenBrief(comments, allowlist) {
|
|
3001
|
+
let best = null;
|
|
3002
|
+
for (const c of comments) {
|
|
3003
|
+
if (!isPrincipal(c.author, allowlist))
|
|
3004
|
+
continue;
|
|
3005
|
+
const version = parseBriefMarkerVersion(c.body.split(`
|
|
3006
|
+
`)[0] ?? "");
|
|
3007
|
+
if (version === null)
|
|
3008
|
+
continue;
|
|
3009
|
+
if (best === null || version > best.version)
|
|
3010
|
+
best = { ...c, version };
|
|
3011
|
+
}
|
|
3012
|
+
return best === null ? null : { ...best, content: frozenBriefContent(best.body, best.version) };
|
|
3013
|
+
}
|
|
2938
3014
|
// ../../packages/aeg-core/src/doctrine-portability.ts
|
|
2939
3015
|
var DEFAULT_SHIPS_PREFIX = "aeg-root/";
|
|
2940
3016
|
var STATIC_PORTABLE_PREFIXES = [
|
|
@@ -3177,6 +3253,66 @@ function checkIssueObjectives(body, issueNumber) {
|
|
|
3177
3253
|
return { status: "pass", errors: [] };
|
|
3178
3254
|
return { status: "fail", errors: result.errors.map((e) => `issue-validation objectives: ${e}`) };
|
|
3179
3255
|
}
|
|
3256
|
+
function topLevelSectionText(body, headingName) {
|
|
3257
|
+
const headingRe = new RegExp(`^##[ \\t]*${headingName}[ \\t]*$`, "im");
|
|
3258
|
+
const heading = headingRe.exec(body);
|
|
3259
|
+
if (!heading)
|
|
3260
|
+
return null;
|
|
3261
|
+
const afterHeading = body.slice(heading.index + heading[0].length);
|
|
3262
|
+
const next = /^##[ \t]/m.exec(afterHeading);
|
|
3263
|
+
return next ? afterHeading.slice(0, next.index) : afterHeading;
|
|
3264
|
+
}
|
|
3265
|
+
function looksLikeFilePath(entry2) {
|
|
3266
|
+
const stripped = entry2.replace(/\/\*\*?$/, "");
|
|
3267
|
+
const lastSegment = stripped.split("/").pop() ?? stripped;
|
|
3268
|
+
const extMatch = /\.[A-Za-z0-9]{1,6}$/.exec(lastSegment);
|
|
3269
|
+
return extMatch !== null && extMatch.index > 0;
|
|
3270
|
+
}
|
|
3271
|
+
function splitGlobList(raw) {
|
|
3272
|
+
return raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
3273
|
+
}
|
|
3274
|
+
function parseIssueSurface(body) {
|
|
3275
|
+
const section = topLevelSectionText(body, "Surface");
|
|
3276
|
+
if (section === null)
|
|
3277
|
+
return { ok: false, errors: ["no `## Surface` heading found in the body."] };
|
|
3278
|
+
const inLine = /^in:\s*(.+)$/im.exec(section);
|
|
3279
|
+
const outLine = /^out:\s*(.+)$/im.exec(section);
|
|
3280
|
+
const errors = [];
|
|
3281
|
+
if (!inLine)
|
|
3282
|
+
errors.push("`## Surface` has no `in:` line.");
|
|
3283
|
+
if (!outLine)
|
|
3284
|
+
errors.push("`## Surface` has no `out:` line.");
|
|
3285
|
+
if (errors.length > 0)
|
|
3286
|
+
return { ok: false, errors };
|
|
3287
|
+
const inGlobs = splitGlobList(inLine[1]);
|
|
3288
|
+
const outGlobs = splitGlobList(outLine[1]);
|
|
3289
|
+
for (const g of [...inGlobs, ...outGlobs]) {
|
|
3290
|
+
if (looksLikeFilePath(g)) {
|
|
3291
|
+
errors.push(`\`${g}\` in \`## Surface\` looks like a file path — Surface entries are directory-level globs, never file paths.`);
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
if (inGlobs.length === 0)
|
|
3295
|
+
errors.push("`## Surface`'s `in:` line resolved to zero globs.");
|
|
3296
|
+
if (errors.length > 0)
|
|
3297
|
+
return { ok: false, errors };
|
|
3298
|
+
return { ok: true, value: { in: inGlobs, out: outGlobs } };
|
|
3299
|
+
}
|
|
3300
|
+
function globCoversPath(glob, path) {
|
|
3301
|
+
const g = glob.replace(/\/\*\*?$/, "").replace(/\/+$/, "");
|
|
3302
|
+
const p = path.replace(/\/+$/, "");
|
|
3303
|
+
return g === p || g.startsWith(`${p}/`) || p.startsWith(`${g}/`);
|
|
3304
|
+
}
|
|
3305
|
+
function checkSurfaceScope(changedFiles, outGlobs) {
|
|
3306
|
+
if (outGlobs.length === 0)
|
|
3307
|
+
return { ok: true };
|
|
3308
|
+
const violations = [];
|
|
3309
|
+
for (const file of changedFiles) {
|
|
3310
|
+
const glob = outGlobs.find((g) => globCoversPath(g, file));
|
|
3311
|
+
if (glob)
|
|
3312
|
+
violations.push({ file, glob });
|
|
3313
|
+
}
|
|
3314
|
+
return violations.length > 0 ? { ok: false, violations } : { ok: true };
|
|
3315
|
+
}
|
|
3180
3316
|
function isTaskIssueLabelSet(labels) {
|
|
3181
3317
|
return hasLabel("tranche", labels);
|
|
3182
3318
|
}
|
|
@@ -3241,6 +3377,8 @@ function checkProjectsRegistered(body, _labels, registeredNames) {
|
|
|
3241
3377
|
}
|
|
3242
3378
|
return errors.length > 0 ? { status: "fail", errors } : { status: "pass", errors: [] };
|
|
3243
3379
|
}
|
|
3380
|
+
var DOC_PATH_RE = /(?:(?:aeg-root|apps|packages|specs|docs|tools|\.claude|\.github)\/[\w./@-]*\.(?:md|mdx)|\.claude\/(?:skills|rules)\/[\w./-]+|\b(?:docs-index|decisions|projects|state-machine|enforcement|process|README|CLAUDE)\.md\b|\b[\w-]+-(?:spec|decisions|backlog)\.md\b)/i;
|
|
3381
|
+
var DOC_PATH_RE_GLOBAL = new RegExp(DOC_PATH_RE.source, "gi");
|
|
3244
3382
|
|
|
3245
3383
|
// ../../packages/aeg-core/src/coherence-checks.ts
|
|
3246
3384
|
var COHERENCE_ENFORCED_FROM = "2026-07-01";
|
|
@@ -3598,6 +3736,11 @@ function isBoundToObjectives(extraction, currentVersion) {
|
|
|
3598
3736
|
return true;
|
|
3599
3737
|
return extraction.objectivesVersion === currentVersion;
|
|
3600
3738
|
}
|
|
3739
|
+
function isBoundToRulings(extraction, currentOrdinal) {
|
|
3740
|
+
if (extraction.rulingOrdinal === null)
|
|
3741
|
+
return currentOrdinal === 0;
|
|
3742
|
+
return extraction.rulingOrdinal === currentOrdinal;
|
|
3743
|
+
}
|
|
3601
3744
|
function checkReviewGate(input) {
|
|
3602
3745
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
3603
3746
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -3626,7 +3769,9 @@ function checkReviewGate(input) {
|
|
|
3626
3769
|
const securityBound = isBoundToPatch(security, input.headSha, input.patchIdOf);
|
|
3627
3770
|
const codeReviewObjectivesBound = isBoundToObjectives(codeReview, input.objectivesVersion);
|
|
3628
3771
|
const securityObjectivesBound = isBoundToObjectives(security, input.objectivesVersion);
|
|
3629
|
-
|
|
3772
|
+
const codeReviewRulingsBound = isBoundToRulings(codeReview, input.rulingOrdinal);
|
|
3773
|
+
const securityRulingsBound = isBoundToRulings(security, input.rulingOrdinal);
|
|
3774
|
+
if (codeReviewClean && codeReviewBound && codeReviewObjectivesBound && codeReviewRulingsBound && securityClean && securityBound && securityObjectivesBound && securityRulingsBound && mechanicalChecksClean) {
|
|
3630
3775
|
return {
|
|
3631
3776
|
verdict: "pass",
|
|
3632
3777
|
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.`,
|
|
@@ -3640,6 +3785,8 @@ function checkReviewGate(input) {
|
|
|
3640
3785
|
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3641
3786
|
} else if (!codeReviewObjectivesBound) {
|
|
3642
3787
|
problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3788
|
+
} else if (!codeReviewRulingsBound) {
|
|
3789
|
+
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`);
|
|
3643
3790
|
}
|
|
3644
3791
|
if (!securityClean) {
|
|
3645
3792
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
@@ -3647,6 +3794,8 @@ function checkReviewGate(input) {
|
|
|
3647
3794
|
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3648
3795
|
} else if (!securityObjectivesBound) {
|
|
3649
3796
|
problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3797
|
+
} else if (!securityRulingsBound) {
|
|
3798
|
+
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`);
|
|
3650
3799
|
}
|
|
3651
3800
|
if (!mechanicalChecksClean) {
|
|
3652
3801
|
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(", ")}`);
|
|
@@ -3663,6 +3812,24 @@ var DEFAULT_RELEASE_ACTOR = "github-actions[bot]";
|
|
|
3663
3812
|
function isChangesetsReleasePr(branch, author, expectedAuthor) {
|
|
3664
3813
|
return branch === CHANGESET_RELEASE_BRANCH && author === expectedAuthor;
|
|
3665
3814
|
}
|
|
3815
|
+
// ../../packages/aeg-core/src/ruling-ordinal.ts
|
|
3816
|
+
var RULING_MARKER_ORDINAL = /^<!-- aeg:principal:ruling:\d+-(\d+) -->$/;
|
|
3817
|
+
function newestPrincipalRulingOrdinal(comments, allowlist) {
|
|
3818
|
+
let best = 0;
|
|
3819
|
+
for (const c of comments) {
|
|
3820
|
+
if (!isPrincipal(c.author, allowlist))
|
|
3821
|
+
continue;
|
|
3822
|
+
const firstLine = (c.body.split(`
|
|
3823
|
+
`)[0] ?? "").trim();
|
|
3824
|
+
const m = RULING_MARKER_ORDINAL.exec(firstLine);
|
|
3825
|
+
if (!m)
|
|
3826
|
+
continue;
|
|
3827
|
+
const k = Number.parseInt(m[1], 10);
|
|
3828
|
+
if (k > best)
|
|
3829
|
+
best = k;
|
|
3830
|
+
}
|
|
3831
|
+
return best;
|
|
3832
|
+
}
|
|
3666
3833
|
// ../../packages/aeg-core/src/markdown-table.ts
|
|
3667
3834
|
function splitRow(line) {
|
|
3668
3835
|
const trimmed = line.trim().replace(/^\|/, "").replace(/\|$/, "");
|
|
@@ -3770,16 +3937,33 @@ function checkG1(rows, existsFn) {
|
|
|
3770
3937
|
return { check: "G1", status: findings.length > 0 ? "fail" : "pass", findings };
|
|
3771
3938
|
}
|
|
3772
3939
|
var SCAFFOLD_PLACEHOLDER = "[undocumented — fill in why]";
|
|
3940
|
+
var NON_GATE_BIN_NAMES = NON_GATE_BINS;
|
|
3941
|
+
var TWIN_CANDIDATE_EXCEPTIONS = {
|
|
3942
|
+
"apps/cli/src/checks/bin/check-dead-branch-push.ts": 'Ring 0 "Pushing to a branch whose pull request already resolved" (implementation: check-push-target.ts)',
|
|
3943
|
+
"apps/cli/src/checks/bin/check-issue-assignment.ts": `Ring 0 "A task branch's first push (Issue self-assignment)" (implementation: assign-task-issue.ts)`,
|
|
3944
|
+
"apps/cli/src/checks/bin/check-closes-n.ts": 'Ring 1 "Closes linkage" (implementation: verify-coherence.ts)'
|
|
3945
|
+
};
|
|
3946
|
+
var NON_GATE_HOOK_SCRIPTS = [".claude/hooks/track-transcript.sh"];
|
|
3773
3947
|
function checkG2(rows, candidateFiles) {
|
|
3774
3948
|
const implementations = new Set(rows.map((r) => r.implementation).filter((p) => p !== ""));
|
|
3949
|
+
const claimedNames = new Set(rows.flatMap((r) => claimedCheckNames(r.implementation)));
|
|
3775
3950
|
const findings = [];
|
|
3776
3951
|
for (const path of candidateFiles) {
|
|
3777
|
-
if (
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3952
|
+
if (implementations.has(path))
|
|
3953
|
+
continue;
|
|
3954
|
+
if (path.startsWith(AEG_CORE_BIN_PATH_PREFIX) && NON_GATE_BIN_NAMES.includes(basenameNoExt(path)))
|
|
3955
|
+
continue;
|
|
3956
|
+
if (NON_GATE_HOOK_SCRIPTS.includes(path))
|
|
3957
|
+
continue;
|
|
3958
|
+
if (path in TWIN_CANDIDATE_EXCEPTIONS)
|
|
3959
|
+
continue;
|
|
3960
|
+
const candidateNames = claimedCheckNames(path);
|
|
3961
|
+
if (candidateNames.length > 0 && candidateNames.some((n) => claimedNames.has(n)))
|
|
3962
|
+
continue;
|
|
3963
|
+
findings.push({
|
|
3964
|
+
path,
|
|
3965
|
+
reason: `"${path}" is not named as the implementation of any row in enforcement.md's ring tables`
|
|
3966
|
+
});
|
|
3783
3967
|
}
|
|
3784
3968
|
for (const row of rows) {
|
|
3785
3969
|
const carriesPlaceholder = [row.summary, row.description, row.spec].some((cell) => cell === SCAFFOLD_PLACEHOLDER);
|
|
@@ -3791,7 +3975,7 @@ function checkG2(rows, candidateFiles) {
|
|
|
3791
3975
|
});
|
|
3792
3976
|
}
|
|
3793
3977
|
}
|
|
3794
|
-
return { check: "G2", status: findings.length > 0 ? "
|
|
3978
|
+
return { check: "G2", status: findings.length > 0 ? "fail" : "pass", findings };
|
|
3795
3979
|
}
|
|
3796
3980
|
function checkG3(ring0Rows, crossingFiles) {
|
|
3797
3981
|
const ring0Implementations = new Set(ring0Rows.map((r) => r.implementation));
|
|
@@ -4667,7 +4851,6 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
|
|
|
4667
4851
|
import { z } from "zod";
|
|
4668
4852
|
var ROLE_VALUES = [
|
|
4669
4853
|
"planner",
|
|
4670
|
-
"brief-author",
|
|
4671
4854
|
"developer",
|
|
4672
4855
|
"code-reviewer",
|
|
4673
4856
|
"security",
|
|
@@ -4741,18 +4924,20 @@ var dispatchShared = {
|
|
|
4741
4924
|
round: z.number().int().optional(),
|
|
4742
4925
|
effect_id: z.string()
|
|
4743
4926
|
};
|
|
4927
|
+
var dispatchUsageField = z.object({ input: z.number().nonnegative(), output: z.number().nonnegative() }).strict().nullable();
|
|
4744
4928
|
var DispatchEventSchema = z.discriminatedUnion("event", [
|
|
4745
4929
|
z.object({ ...dispatchShared, event: z.literal("dispatched"), prompt_hash: z.string() }).strict(),
|
|
4746
4930
|
z.object({
|
|
4747
4931
|
...dispatchShared,
|
|
4748
4932
|
event: z.literal("outcome_received"),
|
|
4749
4933
|
outcome: DispatchOutcomeSchema,
|
|
4750
|
-
usage:
|
|
4934
|
+
usage: dispatchUsageField
|
|
4751
4935
|
}).strict(),
|
|
4752
4936
|
z.object({
|
|
4753
4937
|
...dispatchShared,
|
|
4754
4938
|
event: z.literal("dispatch_failed"),
|
|
4755
|
-
reason: z.enum(["timeout", "crash", "refused", "unattributed_write"])
|
|
4939
|
+
reason: z.enum(["timeout", "crash", "refused", "unattributed_write"]),
|
|
4940
|
+
usage: dispatchUsageField
|
|
4756
4941
|
}).strict()
|
|
4757
4942
|
]);
|
|
4758
4943
|
var loopShared = {
|
|
@@ -4977,6 +5162,18 @@ var REGISTRY = [
|
|
|
4977
5162
|
},
|
|
4978
5163
|
0
|
|
4979
5164
|
],
|
|
5165
|
+
[
|
|
5166
|
+
{
|
|
5167
|
+
name: "pr-premise-reassert",
|
|
5168
|
+
run: bin("check-pr-premise-reassert"),
|
|
5169
|
+
scope: "diff",
|
|
5170
|
+
timeoutMs: 15000,
|
|
5171
|
+
env: {
|
|
5172
|
+
PR_BODY: { optional: true }
|
|
5173
|
+
}
|
|
5174
|
+
},
|
|
5175
|
+
0
|
|
5176
|
+
],
|
|
4980
5177
|
[
|
|
4981
5178
|
{
|
|
4982
5179
|
name: "dispatch-readiness",
|
|
@@ -5025,6 +5222,22 @@ var REGISTRY = [
|
|
|
5025
5222
|
},
|
|
5026
5223
|
0
|
|
5027
5224
|
],
|
|
5225
|
+
[
|
|
5226
|
+
{
|
|
5227
|
+
name: "surface-scope",
|
|
5228
|
+
run: bin("check-surface-scope"),
|
|
5229
|
+
scope: "diff",
|
|
5230
|
+
timeoutMs: 15000,
|
|
5231
|
+
env: {
|
|
5232
|
+
AEG_REPO: { optional: true },
|
|
5233
|
+
BASE_SHA: { optional: true },
|
|
5234
|
+
BRANCH: { optional: true },
|
|
5235
|
+
GITHUB_TOKEN: { optional: true },
|
|
5236
|
+
GH_TOKEN: { optional: true }
|
|
5237
|
+
}
|
|
5238
|
+
},
|
|
5239
|
+
0
|
|
5240
|
+
],
|
|
5028
5241
|
[
|
|
5029
5242
|
{
|
|
5030
5243
|
name: "test-plan",
|
|
@@ -5066,6 +5279,7 @@ var REGISTRY = [
|
|
|
5066
5279
|
requiresOpenPr: true,
|
|
5067
5280
|
env: {
|
|
5068
5281
|
PR_BODY: { optional: true },
|
|
5282
|
+
BRANCH: { optional: true },
|
|
5069
5283
|
CLAUDE_PROJECT_DIR: { optional: true },
|
|
5070
5284
|
CLAUDE_CODE_SESSION_ID: { optional: true }
|
|
5071
5285
|
}
|
|
@@ -5488,13 +5702,13 @@ async function main() {
|
|
|
5488
5702
|
const g3 = checkG3(ring0Rows, crossingFiles);
|
|
5489
5703
|
const g5 = checkG5(roles, contracts);
|
|
5490
5704
|
const g6 = checkG6(rows, new Set(coreCheckRegistry().map((s) => s.name)));
|
|
5491
|
-
emitResult(g2, false);
|
|
5492
5705
|
emitResult(g1, true);
|
|
5706
|
+
emitResult(g2, true);
|
|
5493
5707
|
emitResult(g3, true);
|
|
5494
5708
|
emitResult(g4Result, true);
|
|
5495
5709
|
emitResult(g5, true);
|
|
5496
5710
|
emitResult(g6, true);
|
|
5497
|
-
const blockingFailed = [g1, g3, g4Result, g5, g6].some((r) => r.status === "fail");
|
|
5711
|
+
const blockingFailed = [g1, g2, g3, g4Result, g5, g6].some((r) => r.status === "fail");
|
|
5498
5712
|
process.exit(blockingFailed ? 1 : 0);
|
|
5499
5713
|
}
|
|
5500
5714
|
main();
|