@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
|
@@ -24,6 +24,15 @@ var GATE_AUDIENCE = {
|
|
|
24
24
|
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."
|
|
25
25
|
}
|
|
26
26
|
};
|
|
27
|
+
var NON_GATE_BINS = [
|
|
28
|
+
"archive-task",
|
|
29
|
+
"assign-task-issue",
|
|
30
|
+
"dead-branch-audit",
|
|
31
|
+
"eval-agent-compliance",
|
|
32
|
+
"open-issue",
|
|
33
|
+
"open-pr",
|
|
34
|
+
"report-tokens"
|
|
35
|
+
];
|
|
27
36
|
function isShipped(a) {
|
|
28
37
|
return "shippedAs" in a;
|
|
29
38
|
}
|
|
@@ -291,7 +300,7 @@ function issueListByLabelArgs(owner, repo, label) {
|
|
|
291
300
|
"--state",
|
|
292
301
|
"all",
|
|
293
302
|
"--json",
|
|
294
|
-
"number,title,body,state,labels,milestone",
|
|
303
|
+
"number,title,body,state,labels,milestone,stateReason",
|
|
295
304
|
"--limit",
|
|
296
305
|
"200"
|
|
297
306
|
];
|
|
@@ -384,7 +393,7 @@ var LABELS = [
|
|
|
384
393
|
id: "vinaya/needs:brief-correction",
|
|
385
394
|
category: "needs",
|
|
386
395
|
form: "literal",
|
|
387
|
-
carries: "Waiting on the
|
|
396
|
+
carries: "Waiting on the Planner — the brief contradicts the surface it describes."
|
|
388
397
|
},
|
|
389
398
|
{
|
|
390
399
|
key: "waiver-docs",
|
|
@@ -637,14 +646,19 @@ function resolveTaskIssueRef(title, labels) {
|
|
|
637
646
|
var INTENTS_HEADING = /^#{1,6}\s*Tranche intents\s*$/im;
|
|
638
647
|
var NEXT_HEADING = /^#{1,6}\s+\S/m;
|
|
639
648
|
var INTENT_BULLET = /^-\s+([a-z0-9][a-z0-9-]*)\s*:\s*(.+)$/i;
|
|
640
|
-
function
|
|
641
|
-
const text = stripCode(description, { inlineSpans: "keep" });
|
|
649
|
+
function intentsSection(text) {
|
|
642
650
|
const start = text.match(INTENTS_HEADING);
|
|
643
651
|
if (!start || start.index === undefined)
|
|
644
|
-
return
|
|
652
|
+
return null;
|
|
645
653
|
const rest = text.slice(start.index + start[0].length);
|
|
646
654
|
const next = rest.match(NEXT_HEADING);
|
|
647
|
-
|
|
655
|
+
return rest.slice(0, next && next.index !== undefined ? next.index : rest.length);
|
|
656
|
+
}
|
|
657
|
+
function intentGoalForSlug(description, slug) {
|
|
658
|
+
const text = stripCode(description, { inlineSpans: "keep" });
|
|
659
|
+
const section = intentsSection(text);
|
|
660
|
+
if (section === null)
|
|
661
|
+
return "";
|
|
648
662
|
for (const line of section.split(`
|
|
649
663
|
`)) {
|
|
650
664
|
const trimmed = line.trim();
|
|
@@ -1936,6 +1950,7 @@ function parseInlineFieldList(section) {
|
|
|
1936
1950
|
// ../../packages/aeg-core/src/verdict-extraction.ts
|
|
1937
1951
|
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
1938
1952
|
var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1953
|
+
var RULING_ORDINAL_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Ruling ordinal:\s*(\d+)(?!\d)/im;
|
|
1939
1954
|
function firstFiveLines(comment) {
|
|
1940
1955
|
return comment.split(`
|
|
1941
1956
|
`).slice(0, 5).join(`
|
|
@@ -1949,6 +1964,15 @@ function extractObjectivesVersion(comment) {
|
|
|
1949
1964
|
const m = firstFiveLines(comment).match(OBJECTIVES_VERSION_PATTERN);
|
|
1950
1965
|
return m ? m[1].toLowerCase() : null;
|
|
1951
1966
|
}
|
|
1967
|
+
function firstSevenLines(comment) {
|
|
1968
|
+
return comment.split(`
|
|
1969
|
+
`).slice(0, 7).join(`
|
|
1970
|
+
`);
|
|
1971
|
+
}
|
|
1972
|
+
function extractRulingOrdinal(comment) {
|
|
1973
|
+
const m = firstSevenLines(comment).match(RULING_ORDINAL_PATTERN);
|
|
1974
|
+
return m ? Number.parseInt(m[1], 10) : null;
|
|
1975
|
+
}
|
|
1952
1976
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
1953
1977
|
const candidates = comments.filter((c) => valuePattern.test(c));
|
|
1954
1978
|
if (candidates.length === 0) {
|
|
@@ -1956,6 +1980,7 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1956
1980
|
value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
|
|
1957
1981
|
headSha: null,
|
|
1958
1982
|
objectivesVersion: null,
|
|
1983
|
+
rulingOrdinal: null,
|
|
1959
1984
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
1960
1985
|
};
|
|
1961
1986
|
}
|
|
@@ -1966,6 +1991,7 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1966
1991
|
value: `the most recent ${missingLabel} comment's VERDICT line is not within its first five lines — DANGLING, see below`,
|
|
1967
1992
|
headSha: null,
|
|
1968
1993
|
objectivesVersion: null,
|
|
1994
|
+
rulingOrdinal: null,
|
|
1969
1995
|
danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
|
|
1970
1996
|
};
|
|
1971
1997
|
}
|
|
@@ -1973,6 +1999,7 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1973
1999
|
value: m[1].toUpperCase().replace(/[_-]/g, " "),
|
|
1974
2000
|
headSha: extractHeadSha(latest),
|
|
1975
2001
|
objectivesVersion: extractObjectivesVersion(latest),
|
|
2002
|
+
rulingOrdinal: extractRulingOrdinal(latest),
|
|
1976
2003
|
danglingNote: null
|
|
1977
2004
|
};
|
|
1978
2005
|
}
|
|
@@ -2334,6 +2361,9 @@ import { createHash as createHash2 } from "node:crypto";
|
|
|
2334
2361
|
var HEADING_RE = /^##[ \t]*Objectives[ \t]*$/im;
|
|
2335
2362
|
var NEXT_HEADING_RE = /^##[ \t]/m;
|
|
2336
2363
|
var OBJECTIVE_LINE_RE = /^O(\d+)\.[ \t]*(.*)$/;
|
|
2364
|
+
function maskedForHeadingSearch(body) {
|
|
2365
|
+
return maskDetailsBlocks(maskCode(body));
|
|
2366
|
+
}
|
|
2337
2367
|
function hasBacktickedPath(text) {
|
|
2338
2368
|
let i = 0;
|
|
2339
2369
|
while (i < text.length) {
|
|
@@ -2356,16 +2386,22 @@ function stripObjectiveBackticks(text) {
|
|
|
2356
2386
|
function wordCount(text) {
|
|
2357
2387
|
return text.split(/\s+/).filter((w) => /[a-z]/i.test(w)).length;
|
|
2358
2388
|
}
|
|
2359
|
-
function
|
|
2360
|
-
const
|
|
2389
|
+
function objectivesSectionBounds(body) {
|
|
2390
|
+
const masked = maskedForHeadingSearch(body);
|
|
2391
|
+
const heading = HEADING_RE.exec(masked);
|
|
2361
2392
|
if (!heading)
|
|
2362
2393
|
return null;
|
|
2363
|
-
const
|
|
2394
|
+
const start = heading.index + heading[0].length;
|
|
2395
|
+
const afterHeading = masked.slice(start);
|
|
2364
2396
|
const next = NEXT_HEADING_RE.exec(afterHeading);
|
|
2365
|
-
return next ?
|
|
2397
|
+
return { start, end: next ? start + next.index : body.length };
|
|
2398
|
+
}
|
|
2399
|
+
function objectivesSectionText(body) {
|
|
2400
|
+
const bounds = objectivesSectionBounds(body);
|
|
2401
|
+
return bounds === null ? null : body.slice(bounds.start, bounds.end);
|
|
2366
2402
|
}
|
|
2367
2403
|
function hasObjectivesHeading(body) {
|
|
2368
|
-
return HEADING_RE.test(body);
|
|
2404
|
+
return HEADING_RE.test(maskedForHeadingSearch(body));
|
|
2369
2405
|
}
|
|
2370
2406
|
function objectivesOf(body) {
|
|
2371
2407
|
const section = objectivesSectionText(body);
|
|
@@ -2391,7 +2427,7 @@ function objectivesOf(body) {
|
|
|
2391
2427
|
continue;
|
|
2392
2428
|
}
|
|
2393
2429
|
if (hasBacktickedPath(text) && wordCount(stripObjectiveBackticks(text)) < MIN_WORDS_OUTSIDE_BACKTICKS) {
|
|
2394
|
-
errors.push(`O${n} is little more than a file path — an objective states an observable outcome, never a bare path (the
|
|
2430
|
+
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).`);
|
|
2395
2431
|
continue;
|
|
2396
2432
|
}
|
|
2397
2433
|
objectives.push({ id: `O${n}`, text });
|
|
@@ -2425,6 +2461,15 @@ function isIssueNotFoundError(err) {
|
|
|
2425
2461
|
`);
|
|
2426
2462
|
return /could not resolve to an (?:issue|pull request)|\b404\b|not found/i.test(haystack);
|
|
2427
2463
|
}
|
|
2464
|
+
function resolveObjectivesSource(prBody, issue, cutoverIssue) {
|
|
2465
|
+
if (issue !== null && issue < cutoverIssue)
|
|
2466
|
+
return { kind: "none" };
|
|
2467
|
+
if (issue !== null)
|
|
2468
|
+
return { kind: "issue", issue };
|
|
2469
|
+
if (hasObjectivesHeading(prBody))
|
|
2470
|
+
return { kind: "body" };
|
|
2471
|
+
return { kind: "none" };
|
|
2472
|
+
}
|
|
2428
2473
|
|
|
2429
2474
|
// ../../packages/aeg-core/src/premise-check.ts
|
|
2430
2475
|
import { createHash as createHash3 } from "node:crypto";
|
|
@@ -2603,18 +2648,14 @@ function checkTestPlanExclusivity(prBody) {
|
|
|
2603
2648
|
function checkPrincipalPlaceholder(prBody) {
|
|
2604
2649
|
const region = testPlanRegion(prBody);
|
|
2605
2650
|
const lineRe = /^-\s*\[[ xX]\]\s*\*{2}\[principal\]\*{2}(.*)$/gim;
|
|
2651
|
+
const errors = [];
|
|
2606
2652
|
for (const m of region.matchAll(lineRe)) {
|
|
2607
2653
|
const content = m[1] ?? "";
|
|
2608
2654
|
if (/^\s*None\b/i.test(content)) {
|
|
2609
|
-
|
|
2610
|
-
status: "fail",
|
|
2611
|
-
errors: [
|
|
2612
|
-
'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.'
|
|
2613
|
-
]
|
|
2614
|
-
};
|
|
2655
|
+
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.`);
|
|
2615
2656
|
}
|
|
2616
2657
|
}
|
|
2617
|
-
return { status: "pass", errors: [] };
|
|
2658
|
+
return errors.length > 0 ? { status: "fail", errors } : { status: "pass", errors: [] };
|
|
2618
2659
|
}
|
|
2619
2660
|
function checkSurfaceMap(prBody) {
|
|
2620
2661
|
return headingCheck(prBody, "(?:technical\\s+)?surface map", "Technical surface map");
|
|
@@ -2930,6 +2971,41 @@ function checkBriefSections(prBody, readTier, options = {}) {
|
|
|
2930
2971
|
];
|
|
2931
2972
|
return { errors: results.flatMap((r) => r.errors) };
|
|
2932
2973
|
}
|
|
2974
|
+
function contentAfterNLines(body, n) {
|
|
2975
|
+
let idx = -1;
|
|
2976
|
+
for (let i = 0;i < n; i++) {
|
|
2977
|
+
idx = body.indexOf(`
|
|
2978
|
+
`, idx + 1);
|
|
2979
|
+
if (idx === -1)
|
|
2980
|
+
return "";
|
|
2981
|
+
}
|
|
2982
|
+
return body.slice(idx + 1);
|
|
2983
|
+
}
|
|
2984
|
+
var BRIEF_MARKER_LINE_RE = /^<!-- aeg:brief:v(\d+) -->$/;
|
|
2985
|
+
function parseBriefMarkerVersion(firstLine) {
|
|
2986
|
+
const m = BRIEF_MARKER_LINE_RE.exec(firstLine.trim());
|
|
2987
|
+
if (!m)
|
|
2988
|
+
return null;
|
|
2989
|
+
const version = Number.parseInt(m[1], 10);
|
|
2990
|
+
return Number.isInteger(version) && version >= 1 ? version : null;
|
|
2991
|
+
}
|
|
2992
|
+
function frozenBriefContent(body, version) {
|
|
2993
|
+
return contentAfterNLines(body, version === 1 ? 2 : 3);
|
|
2994
|
+
}
|
|
2995
|
+
function resolveNewestFrozenBrief(comments, allowlist) {
|
|
2996
|
+
let best = null;
|
|
2997
|
+
for (const c of comments) {
|
|
2998
|
+
if (!isPrincipal(c.author, allowlist))
|
|
2999
|
+
continue;
|
|
3000
|
+
const version = parseBriefMarkerVersion(c.body.split(`
|
|
3001
|
+
`)[0] ?? "");
|
|
3002
|
+
if (version === null)
|
|
3003
|
+
continue;
|
|
3004
|
+
if (best === null || version > best.version)
|
|
3005
|
+
best = { ...c, version };
|
|
3006
|
+
}
|
|
3007
|
+
return best === null ? null : { ...best, content: frozenBriefContent(best.body, best.version) };
|
|
3008
|
+
}
|
|
2933
3009
|
// ../../packages/aeg-core/src/doctrine-portability.ts
|
|
2934
3010
|
var DEFAULT_SHIPS_PREFIX = "aeg-root/";
|
|
2935
3011
|
var STATIC_PORTABLE_PREFIXES = [
|
|
@@ -3172,6 +3248,66 @@ function checkIssueObjectives(body, issueNumber) {
|
|
|
3172
3248
|
return { status: "pass", errors: [] };
|
|
3173
3249
|
return { status: "fail", errors: result.errors.map((e) => `issue-validation objectives: ${e}`) };
|
|
3174
3250
|
}
|
|
3251
|
+
function topLevelSectionText(body, headingName) {
|
|
3252
|
+
const headingRe = new RegExp(`^##[ \\t]*${headingName}[ \\t]*$`, "im");
|
|
3253
|
+
const heading = headingRe.exec(body);
|
|
3254
|
+
if (!heading)
|
|
3255
|
+
return null;
|
|
3256
|
+
const afterHeading = body.slice(heading.index + heading[0].length);
|
|
3257
|
+
const next = /^##[ \t]/m.exec(afterHeading);
|
|
3258
|
+
return next ? afterHeading.slice(0, next.index) : afterHeading;
|
|
3259
|
+
}
|
|
3260
|
+
function looksLikeFilePath(entry2) {
|
|
3261
|
+
const stripped = entry2.replace(/\/\*\*?$/, "");
|
|
3262
|
+
const lastSegment = stripped.split("/").pop() ?? stripped;
|
|
3263
|
+
const extMatch = /\.[A-Za-z0-9]{1,6}$/.exec(lastSegment);
|
|
3264
|
+
return extMatch !== null && extMatch.index > 0;
|
|
3265
|
+
}
|
|
3266
|
+
function splitGlobList(raw) {
|
|
3267
|
+
return raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
3268
|
+
}
|
|
3269
|
+
function parseIssueSurface(body) {
|
|
3270
|
+
const section = topLevelSectionText(body, "Surface");
|
|
3271
|
+
if (section === null)
|
|
3272
|
+
return { ok: false, errors: ["no `## Surface` heading found in the body."] };
|
|
3273
|
+
const inLine = /^in:\s*(.+)$/im.exec(section);
|
|
3274
|
+
const outLine = /^out:\s*(.+)$/im.exec(section);
|
|
3275
|
+
const errors = [];
|
|
3276
|
+
if (!inLine)
|
|
3277
|
+
errors.push("`## Surface` has no `in:` line.");
|
|
3278
|
+
if (!outLine)
|
|
3279
|
+
errors.push("`## Surface` has no `out:` line.");
|
|
3280
|
+
if (errors.length > 0)
|
|
3281
|
+
return { ok: false, errors };
|
|
3282
|
+
const inGlobs = splitGlobList(inLine[1]);
|
|
3283
|
+
const outGlobs = splitGlobList(outLine[1]);
|
|
3284
|
+
for (const g of [...inGlobs, ...outGlobs]) {
|
|
3285
|
+
if (looksLikeFilePath(g)) {
|
|
3286
|
+
errors.push(`\`${g}\` in \`## Surface\` looks like a file path — Surface entries are directory-level globs, never file paths.`);
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
if (inGlobs.length === 0)
|
|
3290
|
+
errors.push("`## Surface`'s `in:` line resolved to zero globs.");
|
|
3291
|
+
if (errors.length > 0)
|
|
3292
|
+
return { ok: false, errors };
|
|
3293
|
+
return { ok: true, value: { in: inGlobs, out: outGlobs } };
|
|
3294
|
+
}
|
|
3295
|
+
function globCoversPath(glob, path) {
|
|
3296
|
+
const g = glob.replace(/\/\*\*?$/, "").replace(/\/+$/, "");
|
|
3297
|
+
const p = path.replace(/\/+$/, "");
|
|
3298
|
+
return g === p || g.startsWith(`${p}/`) || p.startsWith(`${g}/`);
|
|
3299
|
+
}
|
|
3300
|
+
function checkSurfaceScope(changedFiles, outGlobs) {
|
|
3301
|
+
if (outGlobs.length === 0)
|
|
3302
|
+
return { ok: true };
|
|
3303
|
+
const violations = [];
|
|
3304
|
+
for (const file of changedFiles) {
|
|
3305
|
+
const glob = outGlobs.find((g) => globCoversPath(g, file));
|
|
3306
|
+
if (glob)
|
|
3307
|
+
violations.push({ file, glob });
|
|
3308
|
+
}
|
|
3309
|
+
return violations.length > 0 ? { ok: false, violations } : { ok: true };
|
|
3310
|
+
}
|
|
3175
3311
|
function isTaskIssueLabelSet(labels) {
|
|
3176
3312
|
return hasLabel("tranche", labels);
|
|
3177
3313
|
}
|
|
@@ -3236,6 +3372,8 @@ function checkProjectsRegistered(body, _labels, registeredNames) {
|
|
|
3236
3372
|
}
|
|
3237
3373
|
return errors.length > 0 ? { status: "fail", errors } : { status: "pass", errors: [] };
|
|
3238
3374
|
}
|
|
3375
|
+
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
|
+
var DOC_PATH_RE_GLOBAL = new RegExp(DOC_PATH_RE.source, "gi");
|
|
3239
3377
|
|
|
3240
3378
|
// ../../packages/aeg-core/src/coherence-checks.ts
|
|
3241
3379
|
var COHERENCE_ENFORCED_FROM = "2026-07-01";
|
|
@@ -3593,6 +3731,11 @@ function isBoundToObjectives(extraction, currentVersion) {
|
|
|
3593
3731
|
return true;
|
|
3594
3732
|
return extraction.objectivesVersion === currentVersion;
|
|
3595
3733
|
}
|
|
3734
|
+
function isBoundToRulings(extraction, currentOrdinal) {
|
|
3735
|
+
if (extraction.rulingOrdinal === null)
|
|
3736
|
+
return currentOrdinal === 0;
|
|
3737
|
+
return extraction.rulingOrdinal === currentOrdinal;
|
|
3738
|
+
}
|
|
3596
3739
|
function checkReviewGate(input) {
|
|
3597
3740
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
3598
3741
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -3621,7 +3764,9 @@ function checkReviewGate(input) {
|
|
|
3621
3764
|
const securityBound = isBoundToPatch(security, input.headSha, input.patchIdOf);
|
|
3622
3765
|
const codeReviewObjectivesBound = isBoundToObjectives(codeReview, input.objectivesVersion);
|
|
3623
3766
|
const securityObjectivesBound = isBoundToObjectives(security, input.objectivesVersion);
|
|
3624
|
-
|
|
3767
|
+
const codeReviewRulingsBound = isBoundToRulings(codeReview, input.rulingOrdinal);
|
|
3768
|
+
const securityRulingsBound = isBoundToRulings(security, input.rulingOrdinal);
|
|
3769
|
+
if (codeReviewClean && codeReviewBound && codeReviewObjectivesBound && codeReviewRulingsBound && securityClean && securityBound && securityObjectivesBound && securityRulingsBound && mechanicalChecksClean) {
|
|
3625
3770
|
return {
|
|
3626
3771
|
verdict: "pass",
|
|
3627
3772
|
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.`,
|
|
@@ -3635,6 +3780,8 @@ function checkReviewGate(input) {
|
|
|
3635
3780
|
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3636
3781
|
} else if (!codeReviewObjectivesBound) {
|
|
3637
3782
|
problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3783
|
+
} else if (!codeReviewRulingsBound) {
|
|
3784
|
+
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`);
|
|
3638
3785
|
}
|
|
3639
3786
|
if (!securityClean) {
|
|
3640
3787
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
@@ -3642,6 +3789,8 @@ function checkReviewGate(input) {
|
|
|
3642
3789
|
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3643
3790
|
} else if (!securityObjectivesBound) {
|
|
3644
3791
|
problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3792
|
+
} else if (!securityRulingsBound) {
|
|
3793
|
+
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`);
|
|
3645
3794
|
}
|
|
3646
3795
|
if (!mechanicalChecksClean) {
|
|
3647
3796
|
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(", ")}`);
|
|
@@ -3658,6 +3807,24 @@ var DEFAULT_RELEASE_ACTOR = "github-actions[bot]";
|
|
|
3658
3807
|
function isChangesetsReleasePr(branch, author, expectedAuthor) {
|
|
3659
3808
|
return branch === CHANGESET_RELEASE_BRANCH && author === expectedAuthor;
|
|
3660
3809
|
}
|
|
3810
|
+
// ../../packages/aeg-core/src/ruling-ordinal.ts
|
|
3811
|
+
var RULING_MARKER_ORDINAL = /^<!-- aeg:principal:ruling:\d+-(\d+) -->$/;
|
|
3812
|
+
function newestPrincipalRulingOrdinal(comments, allowlist) {
|
|
3813
|
+
let best = 0;
|
|
3814
|
+
for (const c of comments) {
|
|
3815
|
+
if (!isPrincipal(c.author, allowlist))
|
|
3816
|
+
continue;
|
|
3817
|
+
const firstLine = (c.body.split(`
|
|
3818
|
+
`)[0] ?? "").trim();
|
|
3819
|
+
const m = RULING_MARKER_ORDINAL.exec(firstLine);
|
|
3820
|
+
if (!m)
|
|
3821
|
+
continue;
|
|
3822
|
+
const k = Number.parseInt(m[1], 10);
|
|
3823
|
+
if (k > best)
|
|
3824
|
+
best = k;
|
|
3825
|
+
}
|
|
3826
|
+
return best;
|
|
3827
|
+
}
|
|
3661
3828
|
// ../../packages/aeg-core/src/markdown-table.ts
|
|
3662
3829
|
function splitRow(line) {
|
|
3663
3830
|
const trimmed = line.trim().replace(/^\|/, "").replace(/\|$/, "");
|
|
@@ -3765,16 +3932,33 @@ function checkG1(rows, existsFn) {
|
|
|
3765
3932
|
return { check: "G1", status: findings.length > 0 ? "fail" : "pass", findings };
|
|
3766
3933
|
}
|
|
3767
3934
|
var SCAFFOLD_PLACEHOLDER = "[undocumented — fill in why]";
|
|
3935
|
+
var NON_GATE_BIN_NAMES = NON_GATE_BINS;
|
|
3936
|
+
var TWIN_CANDIDATE_EXCEPTIONS = {
|
|
3937
|
+
"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)',
|
|
3938
|
+
"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)`,
|
|
3939
|
+
"apps/cli/src/checks/bin/check-closes-n.ts": 'Ring 1 "Closes linkage" (implementation: verify-coherence.ts)'
|
|
3940
|
+
};
|
|
3941
|
+
var NON_GATE_HOOK_SCRIPTS = [".claude/hooks/track-transcript.sh"];
|
|
3768
3942
|
function checkG2(rows, candidateFiles) {
|
|
3769
3943
|
const implementations = new Set(rows.map((r) => r.implementation).filter((p) => p !== ""));
|
|
3944
|
+
const claimedNames = new Set(rows.flatMap((r) => claimedCheckNames(r.implementation)));
|
|
3770
3945
|
const findings = [];
|
|
3771
3946
|
for (const path of candidateFiles) {
|
|
3772
|
-
if (
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3947
|
+
if (implementations.has(path))
|
|
3948
|
+
continue;
|
|
3949
|
+
if (path.startsWith(AEG_CORE_BIN_PATH_PREFIX) && NON_GATE_BIN_NAMES.includes(basenameNoExt(path)))
|
|
3950
|
+
continue;
|
|
3951
|
+
if (NON_GATE_HOOK_SCRIPTS.includes(path))
|
|
3952
|
+
continue;
|
|
3953
|
+
if (path in TWIN_CANDIDATE_EXCEPTIONS)
|
|
3954
|
+
continue;
|
|
3955
|
+
const candidateNames = claimedCheckNames(path);
|
|
3956
|
+
if (candidateNames.length > 0 && candidateNames.some((n) => claimedNames.has(n)))
|
|
3957
|
+
continue;
|
|
3958
|
+
findings.push({
|
|
3959
|
+
path,
|
|
3960
|
+
reason: `"${path}" is not named as the implementation of any row in enforcement.md's ring tables`
|
|
3961
|
+
});
|
|
3778
3962
|
}
|
|
3779
3963
|
for (const row of rows) {
|
|
3780
3964
|
const carriesPlaceholder = [row.summary, row.description, row.spec].some((cell) => cell === SCAFFOLD_PLACEHOLDER);
|
|
@@ -3786,7 +3970,7 @@ function checkG2(rows, candidateFiles) {
|
|
|
3786
3970
|
});
|
|
3787
3971
|
}
|
|
3788
3972
|
}
|
|
3789
|
-
return { check: "G2", status: findings.length > 0 ? "
|
|
3973
|
+
return { check: "G2", status: findings.length > 0 ? "fail" : "pass", findings };
|
|
3790
3974
|
}
|
|
3791
3975
|
function checkG3(ring0Rows, crossingFiles) {
|
|
3792
3976
|
const ring0Implementations = new Set(ring0Rows.map((r) => r.implementation));
|
|
@@ -4662,7 +4846,6 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
|
|
|
4662
4846
|
import { z } from "zod";
|
|
4663
4847
|
var ROLE_VALUES = [
|
|
4664
4848
|
"planner",
|
|
4665
|
-
"brief-author",
|
|
4666
4849
|
"developer",
|
|
4667
4850
|
"code-reviewer",
|
|
4668
4851
|
"security",
|
|
@@ -4736,18 +4919,20 @@ var dispatchShared = {
|
|
|
4736
4919
|
round: z.number().int().optional(),
|
|
4737
4920
|
effect_id: z.string()
|
|
4738
4921
|
};
|
|
4922
|
+
var dispatchUsageField = z.object({ input: z.number().nonnegative(), output: z.number().nonnegative() }).strict().nullable();
|
|
4739
4923
|
var DispatchEventSchema = z.discriminatedUnion("event", [
|
|
4740
4924
|
z.object({ ...dispatchShared, event: z.literal("dispatched"), prompt_hash: z.string() }).strict(),
|
|
4741
4925
|
z.object({
|
|
4742
4926
|
...dispatchShared,
|
|
4743
4927
|
event: z.literal("outcome_received"),
|
|
4744
4928
|
outcome: DispatchOutcomeSchema,
|
|
4745
|
-
usage:
|
|
4929
|
+
usage: dispatchUsageField
|
|
4746
4930
|
}).strict(),
|
|
4747
4931
|
z.object({
|
|
4748
4932
|
...dispatchShared,
|
|
4749
4933
|
event: z.literal("dispatch_failed"),
|
|
4750
|
-
reason: z.enum(["timeout", "crash", "refused", "unattributed_write"])
|
|
4934
|
+
reason: z.enum(["timeout", "crash", "refused", "unattributed_write"]),
|
|
4935
|
+
usage: dispatchUsageField
|
|
4751
4936
|
}).strict()
|
|
4752
4937
|
]);
|
|
4753
4938
|
var loopShared = {
|