@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
|
@@ -3,7 +3,7 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
4
4
|
|
|
5
5
|
// src/checks/bin/check-evidence-fresh.ts
|
|
6
|
-
import { execFileSync as
|
|
6
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
7
7
|
// ../../packages/aeg-core/src/gate-audience.ts
|
|
8
8
|
var GATE_AUDIENCE = {
|
|
9
9
|
"check-branch-topology": { shippedAs: "branch-topology", ring: 0 },
|
|
@@ -27,6 +27,15 @@ var GATE_AUDIENCE = {
|
|
|
27
27
|
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."
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
|
+
var NON_GATE_BINS = [
|
|
31
|
+
"archive-task",
|
|
32
|
+
"assign-task-issue",
|
|
33
|
+
"dead-branch-audit",
|
|
34
|
+
"eval-agent-compliance",
|
|
35
|
+
"open-issue",
|
|
36
|
+
"open-pr",
|
|
37
|
+
"report-tokens"
|
|
38
|
+
];
|
|
30
39
|
function isShipped(a) {
|
|
31
40
|
return "shippedAs" in a;
|
|
32
41
|
}
|
|
@@ -294,7 +303,7 @@ function issueListByLabelArgs(owner, repo, label) {
|
|
|
294
303
|
"--state",
|
|
295
304
|
"all",
|
|
296
305
|
"--json",
|
|
297
|
-
"number,title,body,state,labels,milestone",
|
|
306
|
+
"number,title,body,state,labels,milestone,stateReason",
|
|
298
307
|
"--limit",
|
|
299
308
|
"200"
|
|
300
309
|
];
|
|
@@ -387,7 +396,7 @@ var LABELS = [
|
|
|
387
396
|
id: "vinaya/needs:brief-correction",
|
|
388
397
|
category: "needs",
|
|
389
398
|
form: "literal",
|
|
390
|
-
carries: "Waiting on the
|
|
399
|
+
carries: "Waiting on the Planner — the brief contradicts the surface it describes."
|
|
391
400
|
},
|
|
392
401
|
{
|
|
393
402
|
key: "waiver-docs",
|
|
@@ -640,14 +649,19 @@ function resolveTaskIssueRef(title, labels) {
|
|
|
640
649
|
var INTENTS_HEADING = /^#{1,6}\s*Tranche intents\s*$/im;
|
|
641
650
|
var NEXT_HEADING = /^#{1,6}\s+\S/m;
|
|
642
651
|
var INTENT_BULLET = /^-\s+([a-z0-9][a-z0-9-]*)\s*:\s*(.+)$/i;
|
|
643
|
-
function
|
|
644
|
-
const text = stripCode(description, { inlineSpans: "keep" });
|
|
652
|
+
function intentsSection(text) {
|
|
645
653
|
const start = text.match(INTENTS_HEADING);
|
|
646
654
|
if (!start || start.index === undefined)
|
|
647
|
-
return
|
|
655
|
+
return null;
|
|
648
656
|
const rest = text.slice(start.index + start[0].length);
|
|
649
657
|
const next = rest.match(NEXT_HEADING);
|
|
650
|
-
|
|
658
|
+
return rest.slice(0, next && next.index !== undefined ? next.index : rest.length);
|
|
659
|
+
}
|
|
660
|
+
function intentGoalForSlug(description, slug) {
|
|
661
|
+
const text = stripCode(description, { inlineSpans: "keep" });
|
|
662
|
+
const section = intentsSection(text);
|
|
663
|
+
if (section === null)
|
|
664
|
+
return "";
|
|
651
665
|
for (const line of section.split(`
|
|
652
666
|
`)) {
|
|
653
667
|
const trimmed = line.trim();
|
|
@@ -1939,6 +1953,7 @@ function parseInlineFieldList(section) {
|
|
|
1939
1953
|
// ../../packages/aeg-core/src/verdict-extraction.ts
|
|
1940
1954
|
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
1941
1955
|
var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1956
|
+
var RULING_ORDINAL_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Ruling ordinal:\s*(\d+)(?!\d)/im;
|
|
1942
1957
|
function firstFiveLines(comment) {
|
|
1943
1958
|
return comment.split(`
|
|
1944
1959
|
`).slice(0, 5).join(`
|
|
@@ -1952,6 +1967,15 @@ function extractObjectivesVersion(comment) {
|
|
|
1952
1967
|
const m = firstFiveLines(comment).match(OBJECTIVES_VERSION_PATTERN);
|
|
1953
1968
|
return m ? m[1].toLowerCase() : null;
|
|
1954
1969
|
}
|
|
1970
|
+
function firstSevenLines(comment) {
|
|
1971
|
+
return comment.split(`
|
|
1972
|
+
`).slice(0, 7).join(`
|
|
1973
|
+
`);
|
|
1974
|
+
}
|
|
1975
|
+
function extractRulingOrdinal(comment) {
|
|
1976
|
+
const m = firstSevenLines(comment).match(RULING_ORDINAL_PATTERN);
|
|
1977
|
+
return m ? Number.parseInt(m[1], 10) : null;
|
|
1978
|
+
}
|
|
1955
1979
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
1956
1980
|
const candidates = comments.filter((c) => valuePattern.test(c));
|
|
1957
1981
|
if (candidates.length === 0) {
|
|
@@ -1959,6 +1983,7 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1959
1983
|
value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
|
|
1960
1984
|
headSha: null,
|
|
1961
1985
|
objectivesVersion: null,
|
|
1986
|
+
rulingOrdinal: null,
|
|
1962
1987
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
1963
1988
|
};
|
|
1964
1989
|
}
|
|
@@ -1969,6 +1994,7 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1969
1994
|
value: `the most recent ${missingLabel} comment's VERDICT line is not within its first five lines — DANGLING, see below`,
|
|
1970
1995
|
headSha: null,
|
|
1971
1996
|
objectivesVersion: null,
|
|
1997
|
+
rulingOrdinal: null,
|
|
1972
1998
|
danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
|
|
1973
1999
|
};
|
|
1974
2000
|
}
|
|
@@ -1976,6 +2002,7 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1976
2002
|
value: m[1].toUpperCase().replace(/[_-]/g, " "),
|
|
1977
2003
|
headSha: extractHeadSha(latest),
|
|
1978
2004
|
objectivesVersion: extractObjectivesVersion(latest),
|
|
2005
|
+
rulingOrdinal: extractRulingOrdinal(latest),
|
|
1979
2006
|
danglingNote: null
|
|
1980
2007
|
};
|
|
1981
2008
|
}
|
|
@@ -2337,6 +2364,9 @@ import { createHash as createHash2 } from "node:crypto";
|
|
|
2337
2364
|
var HEADING_RE = /^##[ \t]*Objectives[ \t]*$/im;
|
|
2338
2365
|
var NEXT_HEADING_RE = /^##[ \t]/m;
|
|
2339
2366
|
var OBJECTIVE_LINE_RE = /^O(\d+)\.[ \t]*(.*)$/;
|
|
2367
|
+
function maskedForHeadingSearch(body) {
|
|
2368
|
+
return maskDetailsBlocks(maskCode(body));
|
|
2369
|
+
}
|
|
2340
2370
|
function hasBacktickedPath(text) {
|
|
2341
2371
|
let i = 0;
|
|
2342
2372
|
while (i < text.length) {
|
|
@@ -2359,16 +2389,22 @@ function stripObjectiveBackticks(text) {
|
|
|
2359
2389
|
function wordCount(text) {
|
|
2360
2390
|
return text.split(/\s+/).filter((w) => /[a-z]/i.test(w)).length;
|
|
2361
2391
|
}
|
|
2362
|
-
function
|
|
2363
|
-
const
|
|
2392
|
+
function objectivesSectionBounds(body) {
|
|
2393
|
+
const masked = maskedForHeadingSearch(body);
|
|
2394
|
+
const heading = HEADING_RE.exec(masked);
|
|
2364
2395
|
if (!heading)
|
|
2365
2396
|
return null;
|
|
2366
|
-
const
|
|
2397
|
+
const start = heading.index + heading[0].length;
|
|
2398
|
+
const afterHeading = masked.slice(start);
|
|
2367
2399
|
const next = NEXT_HEADING_RE.exec(afterHeading);
|
|
2368
|
-
return next ?
|
|
2400
|
+
return { start, end: next ? start + next.index : body.length };
|
|
2401
|
+
}
|
|
2402
|
+
function objectivesSectionText(body) {
|
|
2403
|
+
const bounds = objectivesSectionBounds(body);
|
|
2404
|
+
return bounds === null ? null : body.slice(bounds.start, bounds.end);
|
|
2369
2405
|
}
|
|
2370
2406
|
function hasObjectivesHeading(body) {
|
|
2371
|
-
return HEADING_RE.test(body);
|
|
2407
|
+
return HEADING_RE.test(maskedForHeadingSearch(body));
|
|
2372
2408
|
}
|
|
2373
2409
|
function objectivesOf(body) {
|
|
2374
2410
|
const section = objectivesSectionText(body);
|
|
@@ -2394,7 +2430,7 @@ function objectivesOf(body) {
|
|
|
2394
2430
|
continue;
|
|
2395
2431
|
}
|
|
2396
2432
|
if (hasBacktickedPath(text) && wordCount(stripObjectiveBackticks(text)) < MIN_WORDS_OUTSIDE_BACKTICKS) {
|
|
2397
|
-
errors.push(`O${n} is little more than a file path — an objective states an observable outcome, never a bare path (the
|
|
2433
|
+
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).`);
|
|
2398
2434
|
continue;
|
|
2399
2435
|
}
|
|
2400
2436
|
objectives.push({ id: `O${n}`, text });
|
|
@@ -2428,6 +2464,15 @@ function isIssueNotFoundError(err) {
|
|
|
2428
2464
|
`);
|
|
2429
2465
|
return /could not resolve to an (?:issue|pull request)|\b404\b|not found/i.test(haystack);
|
|
2430
2466
|
}
|
|
2467
|
+
function resolveObjectivesSource(prBody, issue, cutoverIssue) {
|
|
2468
|
+
if (issue !== null && issue < cutoverIssue)
|
|
2469
|
+
return { kind: "none" };
|
|
2470
|
+
if (issue !== null)
|
|
2471
|
+
return { kind: "issue", issue };
|
|
2472
|
+
if (hasObjectivesHeading(prBody))
|
|
2473
|
+
return { kind: "body" };
|
|
2474
|
+
return { kind: "none" };
|
|
2475
|
+
}
|
|
2431
2476
|
|
|
2432
2477
|
// ../../packages/aeg-core/src/premise-check.ts
|
|
2433
2478
|
import { createHash as createHash3 } from "node:crypto";
|
|
@@ -2606,18 +2651,14 @@ function checkTestPlanExclusivity(prBody) {
|
|
|
2606
2651
|
function checkPrincipalPlaceholder(prBody) {
|
|
2607
2652
|
const region = testPlanRegion(prBody);
|
|
2608
2653
|
const lineRe = /^-\s*\[[ xX]\]\s*\*{2}\[principal\]\*{2}(.*)$/gim;
|
|
2654
|
+
const errors = [];
|
|
2609
2655
|
for (const m of region.matchAll(lineRe)) {
|
|
2610
2656
|
const content = m[1] ?? "";
|
|
2611
2657
|
if (/^\s*None\b/i.test(content)) {
|
|
2612
|
-
|
|
2613
|
-
status: "fail",
|
|
2614
|
-
errors: [
|
|
2615
|
-
'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.'
|
|
2616
|
-
]
|
|
2617
|
-
};
|
|
2658
|
+
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.`);
|
|
2618
2659
|
}
|
|
2619
2660
|
}
|
|
2620
|
-
return { status: "pass", errors: [] };
|
|
2661
|
+
return errors.length > 0 ? { status: "fail", errors } : { status: "pass", errors: [] };
|
|
2621
2662
|
}
|
|
2622
2663
|
function checkSurfaceMap(prBody) {
|
|
2623
2664
|
return headingCheck(prBody, "(?:technical\\s+)?surface map", "Technical surface map");
|
|
@@ -2933,6 +2974,41 @@ function checkBriefSections(prBody, readTier, options = {}) {
|
|
|
2933
2974
|
];
|
|
2934
2975
|
return { errors: results.flatMap((r) => r.errors) };
|
|
2935
2976
|
}
|
|
2977
|
+
function contentAfterNLines(body, n) {
|
|
2978
|
+
let idx = -1;
|
|
2979
|
+
for (let i = 0;i < n; i++) {
|
|
2980
|
+
idx = body.indexOf(`
|
|
2981
|
+
`, idx + 1);
|
|
2982
|
+
if (idx === -1)
|
|
2983
|
+
return "";
|
|
2984
|
+
}
|
|
2985
|
+
return body.slice(idx + 1);
|
|
2986
|
+
}
|
|
2987
|
+
var BRIEF_MARKER_LINE_RE = /^<!-- aeg:brief:v(\d+) -->$/;
|
|
2988
|
+
function parseBriefMarkerVersion(firstLine) {
|
|
2989
|
+
const m = BRIEF_MARKER_LINE_RE.exec(firstLine.trim());
|
|
2990
|
+
if (!m)
|
|
2991
|
+
return null;
|
|
2992
|
+
const version = Number.parseInt(m[1], 10);
|
|
2993
|
+
return Number.isInteger(version) && version >= 1 ? version : null;
|
|
2994
|
+
}
|
|
2995
|
+
function frozenBriefContent(body, version) {
|
|
2996
|
+
return contentAfterNLines(body, version === 1 ? 2 : 3);
|
|
2997
|
+
}
|
|
2998
|
+
function resolveNewestFrozenBrief(comments, allowlist) {
|
|
2999
|
+
let best = null;
|
|
3000
|
+
for (const c of comments) {
|
|
3001
|
+
if (!isPrincipal(c.author, allowlist))
|
|
3002
|
+
continue;
|
|
3003
|
+
const version = parseBriefMarkerVersion(c.body.split(`
|
|
3004
|
+
`)[0] ?? "");
|
|
3005
|
+
if (version === null)
|
|
3006
|
+
continue;
|
|
3007
|
+
if (best === null || version > best.version)
|
|
3008
|
+
best = { ...c, version };
|
|
3009
|
+
}
|
|
3010
|
+
return best === null ? null : { ...best, content: frozenBriefContent(best.body, best.version) };
|
|
3011
|
+
}
|
|
2936
3012
|
// ../../packages/aeg-core/src/doctrine-portability.ts
|
|
2937
3013
|
var DEFAULT_SHIPS_PREFIX = "aeg-root/";
|
|
2938
3014
|
var STATIC_PORTABLE_PREFIXES = [
|
|
@@ -3175,6 +3251,66 @@ function checkIssueObjectives(body, issueNumber) {
|
|
|
3175
3251
|
return { status: "pass", errors: [] };
|
|
3176
3252
|
return { status: "fail", errors: result.errors.map((e) => `issue-validation objectives: ${e}`) };
|
|
3177
3253
|
}
|
|
3254
|
+
function topLevelSectionText(body, headingName) {
|
|
3255
|
+
const headingRe = new RegExp(`^##[ \\t]*${headingName}[ \\t]*$`, "im");
|
|
3256
|
+
const heading = headingRe.exec(body);
|
|
3257
|
+
if (!heading)
|
|
3258
|
+
return null;
|
|
3259
|
+
const afterHeading = body.slice(heading.index + heading[0].length);
|
|
3260
|
+
const next = /^##[ \t]/m.exec(afterHeading);
|
|
3261
|
+
return next ? afterHeading.slice(0, next.index) : afterHeading;
|
|
3262
|
+
}
|
|
3263
|
+
function looksLikeFilePath(entry2) {
|
|
3264
|
+
const stripped = entry2.replace(/\/\*\*?$/, "");
|
|
3265
|
+
const lastSegment = stripped.split("/").pop() ?? stripped;
|
|
3266
|
+
const extMatch = /\.[A-Za-z0-9]{1,6}$/.exec(lastSegment);
|
|
3267
|
+
return extMatch !== null && extMatch.index > 0;
|
|
3268
|
+
}
|
|
3269
|
+
function splitGlobList(raw) {
|
|
3270
|
+
return raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
3271
|
+
}
|
|
3272
|
+
function parseIssueSurface(body) {
|
|
3273
|
+
const section = topLevelSectionText(body, "Surface");
|
|
3274
|
+
if (section === null)
|
|
3275
|
+
return { ok: false, errors: ["no `## Surface` heading found in the body."] };
|
|
3276
|
+
const inLine = /^in:\s*(.+)$/im.exec(section);
|
|
3277
|
+
const outLine = /^out:\s*(.+)$/im.exec(section);
|
|
3278
|
+
const errors = [];
|
|
3279
|
+
if (!inLine)
|
|
3280
|
+
errors.push("`## Surface` has no `in:` line.");
|
|
3281
|
+
if (!outLine)
|
|
3282
|
+
errors.push("`## Surface` has no `out:` line.");
|
|
3283
|
+
if (errors.length > 0)
|
|
3284
|
+
return { ok: false, errors };
|
|
3285
|
+
const inGlobs = splitGlobList(inLine[1]);
|
|
3286
|
+
const outGlobs = splitGlobList(outLine[1]);
|
|
3287
|
+
for (const g of [...inGlobs, ...outGlobs]) {
|
|
3288
|
+
if (looksLikeFilePath(g)) {
|
|
3289
|
+
errors.push(`\`${g}\` in \`## Surface\` looks like a file path — Surface entries are directory-level globs, never file paths.`);
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
if (inGlobs.length === 0)
|
|
3293
|
+
errors.push("`## Surface`'s `in:` line resolved to zero globs.");
|
|
3294
|
+
if (errors.length > 0)
|
|
3295
|
+
return { ok: false, errors };
|
|
3296
|
+
return { ok: true, value: { in: inGlobs, out: outGlobs } };
|
|
3297
|
+
}
|
|
3298
|
+
function globCoversPath(glob, path) {
|
|
3299
|
+
const g = glob.replace(/\/\*\*?$/, "").replace(/\/+$/, "");
|
|
3300
|
+
const p = path.replace(/\/+$/, "");
|
|
3301
|
+
return g === p || g.startsWith(`${p}/`) || p.startsWith(`${g}/`);
|
|
3302
|
+
}
|
|
3303
|
+
function checkSurfaceScope(changedFiles, outGlobs) {
|
|
3304
|
+
if (outGlobs.length === 0)
|
|
3305
|
+
return { ok: true };
|
|
3306
|
+
const violations = [];
|
|
3307
|
+
for (const file of changedFiles) {
|
|
3308
|
+
const glob = outGlobs.find((g) => globCoversPath(g, file));
|
|
3309
|
+
if (glob)
|
|
3310
|
+
violations.push({ file, glob });
|
|
3311
|
+
}
|
|
3312
|
+
return violations.length > 0 ? { ok: false, violations } : { ok: true };
|
|
3313
|
+
}
|
|
3178
3314
|
function isTaskIssueLabelSet(labels) {
|
|
3179
3315
|
return hasLabel("tranche", labels);
|
|
3180
3316
|
}
|
|
@@ -3239,6 +3375,8 @@ function checkProjectsRegistered(body, _labels, registeredNames) {
|
|
|
3239
3375
|
}
|
|
3240
3376
|
return errors.length > 0 ? { status: "fail", errors } : { status: "pass", errors: [] };
|
|
3241
3377
|
}
|
|
3378
|
+
var DOC_PATH_RE = /(?:(?:aeg-root|apps|packages|specs|docs|tools|\.claude|\.github)\/[\w./@-]*\.(?:md|mdx)|\.claude\/(?:skills|rules)\/[\w./-]+|\b(?:docs-index|decisions|projects|state-machine|enforcement|process|README|CLAUDE)\.md\b|\b[\w-]+-(?:spec|decisions|backlog)\.md\b)/i;
|
|
3379
|
+
var DOC_PATH_RE_GLOBAL = new RegExp(DOC_PATH_RE.source, "gi");
|
|
3242
3380
|
|
|
3243
3381
|
// ../../packages/aeg-core/src/coherence-checks.ts
|
|
3244
3382
|
var COHERENCE_ENFORCED_FROM = "2026-07-01";
|
|
@@ -3596,6 +3734,11 @@ function isBoundToObjectives(extraction, currentVersion) {
|
|
|
3596
3734
|
return true;
|
|
3597
3735
|
return extraction.objectivesVersion === currentVersion;
|
|
3598
3736
|
}
|
|
3737
|
+
function isBoundToRulings(extraction, currentOrdinal) {
|
|
3738
|
+
if (extraction.rulingOrdinal === null)
|
|
3739
|
+
return currentOrdinal === 0;
|
|
3740
|
+
return extraction.rulingOrdinal === currentOrdinal;
|
|
3741
|
+
}
|
|
3599
3742
|
function checkReviewGate(input) {
|
|
3600
3743
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
3601
3744
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -3624,7 +3767,9 @@ function checkReviewGate(input) {
|
|
|
3624
3767
|
const securityBound = isBoundToPatch(security, input.headSha, input.patchIdOf);
|
|
3625
3768
|
const codeReviewObjectivesBound = isBoundToObjectives(codeReview, input.objectivesVersion);
|
|
3626
3769
|
const securityObjectivesBound = isBoundToObjectives(security, input.objectivesVersion);
|
|
3627
|
-
|
|
3770
|
+
const codeReviewRulingsBound = isBoundToRulings(codeReview, input.rulingOrdinal);
|
|
3771
|
+
const securityRulingsBound = isBoundToRulings(security, input.rulingOrdinal);
|
|
3772
|
+
if (codeReviewClean && codeReviewBound && codeReviewObjectivesBound && codeReviewRulingsBound && securityClean && securityBound && securityObjectivesBound && securityRulingsBound && mechanicalChecksClean) {
|
|
3628
3773
|
return {
|
|
3629
3774
|
verdict: "pass",
|
|
3630
3775
|
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.`,
|
|
@@ -3638,6 +3783,8 @@ function checkReviewGate(input) {
|
|
|
3638
3783
|
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3639
3784
|
} else if (!codeReviewObjectivesBound) {
|
|
3640
3785
|
problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3786
|
+
} else if (!codeReviewRulingsBound) {
|
|
3787
|
+
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`);
|
|
3641
3788
|
}
|
|
3642
3789
|
if (!securityClean) {
|
|
3643
3790
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
@@ -3645,6 +3792,8 @@ function checkReviewGate(input) {
|
|
|
3645
3792
|
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3646
3793
|
} else if (!securityObjectivesBound) {
|
|
3647
3794
|
problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3795
|
+
} else if (!securityRulingsBound) {
|
|
3796
|
+
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`);
|
|
3648
3797
|
}
|
|
3649
3798
|
if (!mechanicalChecksClean) {
|
|
3650
3799
|
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(", ")}`);
|
|
@@ -3661,6 +3810,24 @@ var DEFAULT_RELEASE_ACTOR = "github-actions[bot]";
|
|
|
3661
3810
|
function isChangesetsReleasePr(branch, author, expectedAuthor) {
|
|
3662
3811
|
return branch === CHANGESET_RELEASE_BRANCH && author === expectedAuthor;
|
|
3663
3812
|
}
|
|
3813
|
+
// ../../packages/aeg-core/src/ruling-ordinal.ts
|
|
3814
|
+
var RULING_MARKER_ORDINAL = /^<!-- aeg:principal:ruling:\d+-(\d+) -->$/;
|
|
3815
|
+
function newestPrincipalRulingOrdinal(comments, allowlist) {
|
|
3816
|
+
let best = 0;
|
|
3817
|
+
for (const c of comments) {
|
|
3818
|
+
if (!isPrincipal(c.author, allowlist))
|
|
3819
|
+
continue;
|
|
3820
|
+
const firstLine = (c.body.split(`
|
|
3821
|
+
`)[0] ?? "").trim();
|
|
3822
|
+
const m = RULING_MARKER_ORDINAL.exec(firstLine);
|
|
3823
|
+
if (!m)
|
|
3824
|
+
continue;
|
|
3825
|
+
const k = Number.parseInt(m[1], 10);
|
|
3826
|
+
if (k > best)
|
|
3827
|
+
best = k;
|
|
3828
|
+
}
|
|
3829
|
+
return best;
|
|
3830
|
+
}
|
|
3664
3831
|
// ../../packages/aeg-core/src/markdown-table.ts
|
|
3665
3832
|
function splitRow(line) {
|
|
3666
3833
|
const trimmed = line.trim().replace(/^\|/, "").replace(/\|$/, "");
|
|
@@ -3768,16 +3935,33 @@ function checkG1(rows, existsFn) {
|
|
|
3768
3935
|
return { check: "G1", status: findings.length > 0 ? "fail" : "pass", findings };
|
|
3769
3936
|
}
|
|
3770
3937
|
var SCAFFOLD_PLACEHOLDER = "[undocumented — fill in why]";
|
|
3938
|
+
var NON_GATE_BIN_NAMES = NON_GATE_BINS;
|
|
3939
|
+
var TWIN_CANDIDATE_EXCEPTIONS = {
|
|
3940
|
+
"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)',
|
|
3941
|
+
"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)`,
|
|
3942
|
+
"apps/cli/src/checks/bin/check-closes-n.ts": 'Ring 1 "Closes linkage" (implementation: verify-coherence.ts)'
|
|
3943
|
+
};
|
|
3944
|
+
var NON_GATE_HOOK_SCRIPTS = [".claude/hooks/track-transcript.sh"];
|
|
3771
3945
|
function checkG2(rows, candidateFiles) {
|
|
3772
3946
|
const implementations = new Set(rows.map((r) => r.implementation).filter((p) => p !== ""));
|
|
3947
|
+
const claimedNames = new Set(rows.flatMap((r) => claimedCheckNames(r.implementation)));
|
|
3773
3948
|
const findings = [];
|
|
3774
3949
|
for (const path of candidateFiles) {
|
|
3775
|
-
if (
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3950
|
+
if (implementations.has(path))
|
|
3951
|
+
continue;
|
|
3952
|
+
if (path.startsWith(AEG_CORE_BIN_PATH_PREFIX) && NON_GATE_BIN_NAMES.includes(basenameNoExt(path)))
|
|
3953
|
+
continue;
|
|
3954
|
+
if (NON_GATE_HOOK_SCRIPTS.includes(path))
|
|
3955
|
+
continue;
|
|
3956
|
+
if (path in TWIN_CANDIDATE_EXCEPTIONS)
|
|
3957
|
+
continue;
|
|
3958
|
+
const candidateNames = claimedCheckNames(path);
|
|
3959
|
+
if (candidateNames.length > 0 && candidateNames.some((n) => claimedNames.has(n)))
|
|
3960
|
+
continue;
|
|
3961
|
+
findings.push({
|
|
3962
|
+
path,
|
|
3963
|
+
reason: `"${path}" is not named as the implementation of any row in enforcement.md's ring tables`
|
|
3964
|
+
});
|
|
3781
3965
|
}
|
|
3782
3966
|
for (const row of rows) {
|
|
3783
3967
|
const carriesPlaceholder = [row.summary, row.description, row.spec].some((cell) => cell === SCAFFOLD_PLACEHOLDER);
|
|
@@ -3789,7 +3973,7 @@ function checkG2(rows, candidateFiles) {
|
|
|
3789
3973
|
});
|
|
3790
3974
|
}
|
|
3791
3975
|
}
|
|
3792
|
-
return { check: "G2", status: findings.length > 0 ? "
|
|
3976
|
+
return { check: "G2", status: findings.length > 0 ? "fail" : "pass", findings };
|
|
3793
3977
|
}
|
|
3794
3978
|
function checkG3(ring0Rows, crossingFiles) {
|
|
3795
3979
|
const ring0Implementations = new Set(ring0Rows.map((r) => r.implementation));
|
|
@@ -4665,7 +4849,6 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
|
|
|
4665
4849
|
import { z } from "zod";
|
|
4666
4850
|
var ROLE_VALUES = [
|
|
4667
4851
|
"planner",
|
|
4668
|
-
"brief-author",
|
|
4669
4852
|
"developer",
|
|
4670
4853
|
"code-reviewer",
|
|
4671
4854
|
"security",
|
|
@@ -4739,18 +4922,20 @@ var dispatchShared = {
|
|
|
4739
4922
|
round: z.number().int().optional(),
|
|
4740
4923
|
effect_id: z.string()
|
|
4741
4924
|
};
|
|
4925
|
+
var dispatchUsageField = z.object({ input: z.number().nonnegative(), output: z.number().nonnegative() }).strict().nullable();
|
|
4742
4926
|
var DispatchEventSchema = z.discriminatedUnion("event", [
|
|
4743
4927
|
z.object({ ...dispatchShared, event: z.literal("dispatched"), prompt_hash: z.string() }).strict(),
|
|
4744
4928
|
z.object({
|
|
4745
4929
|
...dispatchShared,
|
|
4746
4930
|
event: z.literal("outcome_received"),
|
|
4747
4931
|
outcome: DispatchOutcomeSchema,
|
|
4748
|
-
usage:
|
|
4932
|
+
usage: dispatchUsageField
|
|
4749
4933
|
}).strict(),
|
|
4750
4934
|
z.object({
|
|
4751
4935
|
...dispatchShared,
|
|
4752
4936
|
event: z.literal("dispatch_failed"),
|
|
4753
|
-
reason: z.enum(["timeout", "crash", "refused", "unattributed_write"])
|
|
4937
|
+
reason: z.enum(["timeout", "crash", "refused", "unattributed_write"]),
|
|
4938
|
+
usage: dispatchUsageField
|
|
4754
4939
|
}).strict()
|
|
4755
4940
|
]);
|
|
4756
4941
|
var loopShared = {
|
|
@@ -4877,6 +5062,478 @@ var ForgeWriteEventSchema = z.discriminatedUnion("event", [
|
|
|
4877
5062
|
z.object({ ...forgeWriteShared, event: z.literal("written"), comment_ids: z.array(z.string()) }).strict()
|
|
4878
5063
|
]);
|
|
4879
5064
|
var LogEventSchema = z.union([DispatchEventSchema, DevReviewLoopEventSchema, ForgeWriteEventSchema]);
|
|
5065
|
+
// src/checks/registry.ts
|
|
5066
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
5067
|
+
import { join as join2 } from "node:path";
|
|
5068
|
+
|
|
5069
|
+
// src/lib/package-root.ts
|
|
5070
|
+
import { existsSync } from "node:fs";
|
|
5071
|
+
import { dirname, join } from "node:path";
|
|
5072
|
+
import { fileURLToPath } from "node:url";
|
|
5073
|
+
function packageRoot(moduleUrl) {
|
|
5074
|
+
let dir = dirname(fileURLToPath(moduleUrl));
|
|
5075
|
+
while (!existsSync(join(dir, "package.json"))) {
|
|
5076
|
+
if (existsSync(join(dir, ".git")))
|
|
5077
|
+
break;
|
|
5078
|
+
const parent = dirname(dir);
|
|
5079
|
+
if (parent === dir)
|
|
5080
|
+
break;
|
|
5081
|
+
dir = parent;
|
|
5082
|
+
}
|
|
5083
|
+
return dir;
|
|
5084
|
+
}
|
|
5085
|
+
|
|
5086
|
+
// src/checks/registry.ts
|
|
5087
|
+
var DIST_BIN_DIR = join2(packageRoot(import.meta.url), "dist", "checks", "bin");
|
|
5088
|
+
var SRC_BIN_DIR = join2(packageRoot(import.meta.url), "src", "checks", "bin");
|
|
5089
|
+
var BIN_DIR = existsSync2(DIST_BIN_DIR) ? DIST_BIN_DIR : SRC_BIN_DIR;
|
|
5090
|
+
var BIN_EXT = BIN_DIR === DIST_BIN_DIR ? ".js" : ".ts";
|
|
5091
|
+
function bin(name) {
|
|
5092
|
+
return join2(BIN_DIR, `${name}${BIN_EXT}`);
|
|
5093
|
+
}
|
|
5094
|
+
var REGISTRY = [
|
|
5095
|
+
[
|
|
5096
|
+
{
|
|
5097
|
+
name: "brief-shape",
|
|
5098
|
+
run: bin("check-brief-shape"),
|
|
5099
|
+
scope: "diff",
|
|
5100
|
+
timeoutMs: 15000,
|
|
5101
|
+
env: {
|
|
5102
|
+
PR_BODY: { optional: true },
|
|
5103
|
+
BRANCH: { optional: true },
|
|
5104
|
+
PR_NUMBER: { optional: true },
|
|
5105
|
+
GITHUB_TOKEN: { optional: true },
|
|
5106
|
+
GH_TOKEN: { optional: true }
|
|
5107
|
+
}
|
|
5108
|
+
},
|
|
5109
|
+
0
|
|
5110
|
+
],
|
|
5111
|
+
[
|
|
5112
|
+
{
|
|
5113
|
+
name: "pr-report-density",
|
|
5114
|
+
run: bin("check-pr-report-density"),
|
|
5115
|
+
scope: "diff",
|
|
5116
|
+
timeoutMs: 15000,
|
|
5117
|
+
env: {
|
|
5118
|
+
PR_BODY: { optional: true }
|
|
5119
|
+
}
|
|
5120
|
+
},
|
|
5121
|
+
0
|
|
5122
|
+
],
|
|
5123
|
+
[
|
|
5124
|
+
{
|
|
5125
|
+
name: "doc-coverage",
|
|
5126
|
+
run: bin("check-doc-coverage"),
|
|
5127
|
+
scope: "diff",
|
|
5128
|
+
timeoutMs: 15000,
|
|
5129
|
+
env: {
|
|
5130
|
+
BASE_SHA: { optional: true },
|
|
5131
|
+
PR_BODY: { optional: true },
|
|
5132
|
+
PR_BODY_FILE: { optional: true },
|
|
5133
|
+
PR_NUMBER: { optional: true },
|
|
5134
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
5135
|
+
GITHUB_TOKEN: { optional: true },
|
|
5136
|
+
GH_TOKEN: { optional: true }
|
|
5137
|
+
}
|
|
5138
|
+
},
|
|
5139
|
+
0
|
|
5140
|
+
],
|
|
5141
|
+
[
|
|
5142
|
+
{
|
|
5143
|
+
name: "coherence",
|
|
5144
|
+
run: bin("check-coherence"),
|
|
5145
|
+
scope: "full",
|
|
5146
|
+
timeoutMs: 30000,
|
|
5147
|
+
env: {
|
|
5148
|
+
AEG_REPO: { optional: true },
|
|
5149
|
+
BRANCH: { optional: true },
|
|
5150
|
+
GITHUB_TOKEN: { optional: true },
|
|
5151
|
+
GH_TOKEN: { optional: true }
|
|
5152
|
+
}
|
|
5153
|
+
},
|
|
5154
|
+
0
|
|
5155
|
+
],
|
|
5156
|
+
[
|
|
5157
|
+
{
|
|
5158
|
+
name: "pr-premise-reassert",
|
|
5159
|
+
run: bin("check-pr-premise-reassert"),
|
|
5160
|
+
scope: "diff",
|
|
5161
|
+
timeoutMs: 15000,
|
|
5162
|
+
env: {
|
|
5163
|
+
PR_BODY: { optional: true }
|
|
5164
|
+
}
|
|
5165
|
+
},
|
|
5166
|
+
0
|
|
5167
|
+
],
|
|
5168
|
+
[
|
|
5169
|
+
{
|
|
5170
|
+
name: "dispatch-readiness",
|
|
5171
|
+
run: bin("check-dispatch-readiness"),
|
|
5172
|
+
scope: "full",
|
|
5173
|
+
timeoutMs: 30000,
|
|
5174
|
+
env: {
|
|
5175
|
+
AEG_REPO: { optional: true },
|
|
5176
|
+
BRANCH: { optional: true },
|
|
5177
|
+
GITHUB_TOKEN: { optional: true },
|
|
5178
|
+
GH_TOKEN: { optional: true },
|
|
5179
|
+
PREMISE_FILE: { optional: true }
|
|
5180
|
+
}
|
|
5181
|
+
},
|
|
5182
|
+
0
|
|
5183
|
+
],
|
|
5184
|
+
[
|
|
5185
|
+
{
|
|
5186
|
+
name: "closes-n",
|
|
5187
|
+
run: bin("check-closes-n"),
|
|
5188
|
+
scope: "diff",
|
|
5189
|
+
timeoutMs: 15000,
|
|
5190
|
+
requiresOpenPr: true,
|
|
5191
|
+
env: {
|
|
5192
|
+
BRANCH: { optional: true },
|
|
5193
|
+
PR_BODY: { optional: true },
|
|
5194
|
+
AEG_REPO: { optional: true },
|
|
5195
|
+
GITHUB_TOKEN: { optional: true },
|
|
5196
|
+
GH_TOKEN: { optional: true }
|
|
5197
|
+
}
|
|
5198
|
+
},
|
|
5199
|
+
1
|
|
5200
|
+
],
|
|
5201
|
+
[
|
|
5202
|
+
{
|
|
5203
|
+
name: "single-plan-pr",
|
|
5204
|
+
run: bin("check-single-plan-pr"),
|
|
5205
|
+
scope: "diff",
|
|
5206
|
+
timeoutMs: 15000,
|
|
5207
|
+
env: {
|
|
5208
|
+
BASE_SHA: { optional: true },
|
|
5209
|
+
PR_NUMBER: { optional: true },
|
|
5210
|
+
GITHUB_TOKEN: { optional: true },
|
|
5211
|
+
GH_TOKEN: { optional: true }
|
|
5212
|
+
}
|
|
5213
|
+
},
|
|
5214
|
+
0
|
|
5215
|
+
],
|
|
5216
|
+
[
|
|
5217
|
+
{
|
|
5218
|
+
name: "surface-scope",
|
|
5219
|
+
run: bin("check-surface-scope"),
|
|
5220
|
+
scope: "diff",
|
|
5221
|
+
timeoutMs: 15000,
|
|
5222
|
+
env: {
|
|
5223
|
+
AEG_REPO: { optional: true },
|
|
5224
|
+
BASE_SHA: { optional: true },
|
|
5225
|
+
BRANCH: { optional: true },
|
|
5226
|
+
GITHUB_TOKEN: { optional: true },
|
|
5227
|
+
GH_TOKEN: { optional: true }
|
|
5228
|
+
}
|
|
5229
|
+
},
|
|
5230
|
+
0
|
|
5231
|
+
],
|
|
5232
|
+
[
|
|
5233
|
+
{
|
|
5234
|
+
name: "test-plan",
|
|
5235
|
+
run: bin("check-test-plan"),
|
|
5236
|
+
scope: "diff",
|
|
5237
|
+
timeoutMs: 15000,
|
|
5238
|
+
requiresOpenPr: true,
|
|
5239
|
+
env: {
|
|
5240
|
+
PR_BODY: { optional: true },
|
|
5241
|
+
BRANCH: { optional: true }
|
|
5242
|
+
}
|
|
5243
|
+
},
|
|
5244
|
+
1
|
|
5245
|
+
],
|
|
5246
|
+
[
|
|
5247
|
+
{
|
|
5248
|
+
name: "body-bare-digits",
|
|
5249
|
+
run: bin("check-body-bare-digits"),
|
|
5250
|
+
scope: "diff",
|
|
5251
|
+
timeoutMs: 15000,
|
|
5252
|
+
requiresOpenPr: true,
|
|
5253
|
+
ownWorkflow: true,
|
|
5254
|
+
env: {
|
|
5255
|
+
PR_BODY: { optional: true },
|
|
5256
|
+
PR_NUMBER: { optional: true },
|
|
5257
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
5258
|
+
GITHUB_TOKEN: { optional: true },
|
|
5259
|
+
GH_TOKEN: { optional: true }
|
|
5260
|
+
}
|
|
5261
|
+
},
|
|
5262
|
+
1
|
|
5263
|
+
],
|
|
5264
|
+
[
|
|
5265
|
+
{
|
|
5266
|
+
name: "token-report",
|
|
5267
|
+
run: bin("check-token-report"),
|
|
5268
|
+
scope: "diff",
|
|
5269
|
+
timeoutMs: 15000,
|
|
5270
|
+
requiresOpenPr: true,
|
|
5271
|
+
env: {
|
|
5272
|
+
PR_BODY: { optional: true },
|
|
5273
|
+
BRANCH: { optional: true },
|
|
5274
|
+
CLAUDE_PROJECT_DIR: { optional: true },
|
|
5275
|
+
CLAUDE_CODE_SESSION_ID: { optional: true }
|
|
5276
|
+
}
|
|
5277
|
+
},
|
|
5278
|
+
1
|
|
5279
|
+
],
|
|
5280
|
+
[
|
|
5281
|
+
{
|
|
5282
|
+
name: "no-disk-state",
|
|
5283
|
+
run: bin("check-no-disk-state"),
|
|
5284
|
+
scope: "diff",
|
|
5285
|
+
timeoutMs: 15000,
|
|
5286
|
+
env: { BASE_SHA: { optional: true } }
|
|
5287
|
+
},
|
|
5288
|
+
0
|
|
5289
|
+
],
|
|
5290
|
+
[
|
|
5291
|
+
{
|
|
5292
|
+
name: "registry-gates",
|
|
5293
|
+
run: bin("check-registry-gates"),
|
|
5294
|
+
scope: "full",
|
|
5295
|
+
timeoutMs: 30000,
|
|
5296
|
+
env: {
|
|
5297
|
+
AEG_REPO: { optional: true },
|
|
5298
|
+
GITHUB_TOKEN: { optional: true },
|
|
5299
|
+
GH_TOKEN: { optional: true }
|
|
5300
|
+
}
|
|
5301
|
+
},
|
|
5302
|
+
0
|
|
5303
|
+
],
|
|
5304
|
+
[
|
|
5305
|
+
{
|
|
5306
|
+
name: "review-gate",
|
|
5307
|
+
run: bin("check-review-gate"),
|
|
5308
|
+
scope: "full",
|
|
5309
|
+
timeoutMs: 30000,
|
|
5310
|
+
ownWorkflow: true,
|
|
5311
|
+
env: {
|
|
5312
|
+
PR_NUMBER: { optional: true },
|
|
5313
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
5314
|
+
GITHUB_TOKEN: { optional: true },
|
|
5315
|
+
GH_TOKEN: { optional: true }
|
|
5316
|
+
}
|
|
5317
|
+
},
|
|
5318
|
+
1
|
|
5319
|
+
],
|
|
5320
|
+
[
|
|
5321
|
+
{
|
|
5322
|
+
name: "branch-topology",
|
|
5323
|
+
run: bin("check-branch-topology"),
|
|
5324
|
+
scope: "full",
|
|
5325
|
+
timeoutMs: 30000,
|
|
5326
|
+
env: {
|
|
5327
|
+
BRANCH: { optional: true },
|
|
5328
|
+
AEG_REPO: { optional: true },
|
|
5329
|
+
GITHUB_TOKEN: { optional: true },
|
|
5330
|
+
GH_TOKEN: { optional: true }
|
|
5331
|
+
}
|
|
5332
|
+
},
|
|
5333
|
+
0
|
|
5334
|
+
],
|
|
5335
|
+
[
|
|
5336
|
+
{
|
|
5337
|
+
name: "dead-branch-push",
|
|
5338
|
+
run: bin("check-dead-branch-push"),
|
|
5339
|
+
scope: "full",
|
|
5340
|
+
timeoutMs: 30000,
|
|
5341
|
+
env: {
|
|
5342
|
+
BRANCH: { optional: true },
|
|
5343
|
+
GITHUB_TOKEN: { optional: true },
|
|
5344
|
+
GH_TOKEN: { optional: true }
|
|
5345
|
+
}
|
|
5346
|
+
},
|
|
5347
|
+
0
|
|
5348
|
+
],
|
|
5349
|
+
[
|
|
5350
|
+
{
|
|
5351
|
+
name: "first-push-dispatch",
|
|
5352
|
+
run: bin("check-first-push-dispatch"),
|
|
5353
|
+
scope: "full",
|
|
5354
|
+
timeoutMs: 30000,
|
|
5355
|
+
env: {
|
|
5356
|
+
BRANCH: { optional: true },
|
|
5357
|
+
AEG_REPO: { optional: true },
|
|
5358
|
+
GITHUB_TOKEN: { optional: true },
|
|
5359
|
+
GH_TOKEN: { optional: true }
|
|
5360
|
+
}
|
|
5361
|
+
},
|
|
5362
|
+
0
|
|
5363
|
+
],
|
|
5364
|
+
[
|
|
5365
|
+
{
|
|
5366
|
+
name: "doc-coverage-push",
|
|
5367
|
+
run: bin("check-doc-coverage-push"),
|
|
5368
|
+
scope: "diff",
|
|
5369
|
+
timeoutMs: 15000,
|
|
5370
|
+
env: {
|
|
5371
|
+
OVERRIDE_DOCS: { optional: true },
|
|
5372
|
+
BASE_SHA: { optional: true },
|
|
5373
|
+
PR_BODY: { optional: true },
|
|
5374
|
+
PR_BODY_FILE: { optional: true },
|
|
5375
|
+
PR_LABELS: { optional: true },
|
|
5376
|
+
WAIVER_LABEL_ACTOR: { optional: true },
|
|
5377
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
5378
|
+
GITHUB_TOKEN: { optional: true },
|
|
5379
|
+
GH_TOKEN: { optional: true }
|
|
5380
|
+
}
|
|
5381
|
+
},
|
|
5382
|
+
0
|
|
5383
|
+
],
|
|
5384
|
+
[
|
|
5385
|
+
{
|
|
5386
|
+
name: "issue-assignment",
|
|
5387
|
+
run: bin("check-issue-assignment"),
|
|
5388
|
+
scope: "full",
|
|
5389
|
+
timeoutMs: 30000,
|
|
5390
|
+
env: {
|
|
5391
|
+
AEG_REPO: { optional: true },
|
|
5392
|
+
BRANCH: { optional: true },
|
|
5393
|
+
GITHUB_TOKEN: { optional: true },
|
|
5394
|
+
GH_TOKEN: { optional: true }
|
|
5395
|
+
}
|
|
5396
|
+
},
|
|
5397
|
+
0
|
|
5398
|
+
],
|
|
5399
|
+
[
|
|
5400
|
+
{
|
|
5401
|
+
name: "evidence-fresh",
|
|
5402
|
+
run: bin("check-evidence-fresh"),
|
|
5403
|
+
scope: "diff",
|
|
5404
|
+
timeoutMs: 15000,
|
|
5405
|
+
requiresOpenPr: true,
|
|
5406
|
+
env: {
|
|
5407
|
+
PR_NUMBER: { optional: true },
|
|
5408
|
+
PR_BODY: { optional: true },
|
|
5409
|
+
BASE_SHA: { optional: true },
|
|
5410
|
+
GITHUB_TOKEN: { optional: true },
|
|
5411
|
+
GH_TOKEN: { optional: true }
|
|
5412
|
+
}
|
|
5413
|
+
},
|
|
5414
|
+
1
|
|
5415
|
+
],
|
|
5416
|
+
[
|
|
5417
|
+
{
|
|
5418
|
+
name: "reader-resolvable-prose",
|
|
5419
|
+
run: bin("check-reader-resolvable-prose"),
|
|
5420
|
+
scope: "full",
|
|
5421
|
+
timeoutMs: 30000,
|
|
5422
|
+
env: {},
|
|
5423
|
+
include: [
|
|
5424
|
+
"aeg-root/**/*.md",
|
|
5425
|
+
"apps/cli/src/**",
|
|
5426
|
+
".github/workflows/**",
|
|
5427
|
+
".vinaya/**",
|
|
5428
|
+
"apps/cli/README.md",
|
|
5429
|
+
"packages/sources/README.md"
|
|
5430
|
+
]
|
|
5431
|
+
},
|
|
5432
|
+
0
|
|
5433
|
+
],
|
|
5434
|
+
[
|
|
5435
|
+
{
|
|
5436
|
+
name: "retired-vocabulary",
|
|
5437
|
+
run: bin("check-retired-vocabulary"),
|
|
5438
|
+
scope: "full",
|
|
5439
|
+
timeoutMs: 30000,
|
|
5440
|
+
env: {},
|
|
5441
|
+
include: ["aeg-root/**/*.md"]
|
|
5442
|
+
},
|
|
5443
|
+
0
|
|
5444
|
+
],
|
|
5445
|
+
[
|
|
5446
|
+
{
|
|
5447
|
+
name: "doctrine-portability",
|
|
5448
|
+
run: bin("check-doctrine-portability"),
|
|
5449
|
+
scope: "full",
|
|
5450
|
+
timeoutMs: 30000,
|
|
5451
|
+
env: { BASE_SHA: { optional: true } },
|
|
5452
|
+
include: ["aeg-root/**/*.md"]
|
|
5453
|
+
},
|
|
5454
|
+
0
|
|
5455
|
+
],
|
|
5456
|
+
[
|
|
5457
|
+
{
|
|
5458
|
+
name: "doctrine-no-procedures",
|
|
5459
|
+
run: bin("check-doctrine-no-procedures"),
|
|
5460
|
+
scope: "full",
|
|
5461
|
+
timeoutMs: 30000,
|
|
5462
|
+
env: {},
|
|
5463
|
+
include: ["aeg-root/**/*.md"]
|
|
5464
|
+
},
|
|
5465
|
+
0
|
|
5466
|
+
],
|
|
5467
|
+
[
|
|
5468
|
+
{
|
|
5469
|
+
name: "exec-bits",
|
|
5470
|
+
run: bin("check-exec-bits"),
|
|
5471
|
+
scope: "diff",
|
|
5472
|
+
timeoutMs: 30000,
|
|
5473
|
+
env: {}
|
|
5474
|
+
},
|
|
5475
|
+
0
|
|
5476
|
+
],
|
|
5477
|
+
[
|
|
5478
|
+
{
|
|
5479
|
+
name: "workspace-escape",
|
|
5480
|
+
run: bin("check-workspace-escape"),
|
|
5481
|
+
scope: "full",
|
|
5482
|
+
timeoutMs: 30000,
|
|
5483
|
+
env: {},
|
|
5484
|
+
include: ["aeg-root/**/*.md"]
|
|
5485
|
+
},
|
|
5486
|
+
0
|
|
5487
|
+
],
|
|
5488
|
+
[
|
|
5489
|
+
{
|
|
5490
|
+
name: "changeset-coverage",
|
|
5491
|
+
run: bin("check-changeset-coverage"),
|
|
5492
|
+
scope: "diff",
|
|
5493
|
+
timeoutMs: 15000,
|
|
5494
|
+
env: {}
|
|
5495
|
+
},
|
|
5496
|
+
0
|
|
5497
|
+
],
|
|
5498
|
+
[
|
|
5499
|
+
{
|
|
5500
|
+
name: "quoted-command",
|
|
5501
|
+
run: bin("check-quoted-command"),
|
|
5502
|
+
scope: "diff",
|
|
5503
|
+
timeoutMs: 15000,
|
|
5504
|
+
env: {}
|
|
5505
|
+
},
|
|
5506
|
+
0
|
|
5507
|
+
],
|
|
5508
|
+
[
|
|
5509
|
+
{
|
|
5510
|
+
name: "main-branch-refusal",
|
|
5511
|
+
run: bin("check-main-branch-refusal"),
|
|
5512
|
+
scope: "full",
|
|
5513
|
+
timeoutMs: 15000,
|
|
5514
|
+
env: { VINAYA_PUSH_REFS: { optional: true } }
|
|
5515
|
+
},
|
|
5516
|
+
0
|
|
5517
|
+
],
|
|
5518
|
+
[
|
|
5519
|
+
{
|
|
5520
|
+
name: "token-collection-wired",
|
|
5521
|
+
run: bin("check-token-collection-wired"),
|
|
5522
|
+
scope: "full",
|
|
5523
|
+
timeoutMs: 15000,
|
|
5524
|
+
env: {
|
|
5525
|
+
CLAUDE_PROJECT_DIR: { optional: true },
|
|
5526
|
+
CLAUDE_CODE_SESSION_ID: { optional: true }
|
|
5527
|
+
}
|
|
5528
|
+
},
|
|
5529
|
+
0
|
|
5530
|
+
]
|
|
5531
|
+
];
|
|
5532
|
+
function coreCheckRegistry() {
|
|
5533
|
+
return REGISTRY.map(([spec]) => spec);
|
|
5534
|
+
}
|
|
5535
|
+
var CORE_CHECK_RING = Object.fromEntries(REGISTRY.map(([spec, ring]) => [spec.name, ring]));
|
|
5536
|
+
|
|
4880
5537
|
// src/checks/contract.ts
|
|
4881
5538
|
var CHECK_SCHEMA_VERSION = 1;
|
|
4882
5539
|
function emitCheckError(error) {
|
|
@@ -4904,8 +5561,8 @@ function summariseNumstat(numstat) {
|
|
|
4904
5561
|
deleted += Number(d) || 0;
|
|
4905
5562
|
}
|
|
4906
5563
|
const files = `${rows.length} file${rows.length === 1 ? "" : "s"} changed`;
|
|
4907
|
-
const
|
|
4908
|
-
return `${files}, ${added} insertion${added === 1 ? "" : "s"}(+), ${deleted} deletion${deleted === 1 ? "" : "s"}(-)${
|
|
5564
|
+
const bin2 = binary > 0 ? `, ${binary} binary` : "";
|
|
5565
|
+
return `${files}, ${added} insertion${added === 1 ? "" : "s"}(+), ${deleted} deletion${deleted === 1 ? "" : "s"}(-)${bin2}`;
|
|
4909
5566
|
}
|
|
4910
5567
|
var EVIDENCE_SUMMARY_PREFIX = "Summary: ";
|
|
4911
5568
|
|
|
@@ -4962,28 +5619,11 @@ function summaryLineIndex(maskedRegion) {
|
|
|
4962
5619
|
return index === -1 ? null : index;
|
|
4963
5620
|
}
|
|
4964
5621
|
|
|
4965
|
-
// src/lib/package-root.ts
|
|
4966
|
-
import { existsSync } from "node:fs";
|
|
4967
|
-
import { dirname, join } from "node:path";
|
|
4968
|
-
import { fileURLToPath } from "node:url";
|
|
4969
|
-
function packageRoot(moduleUrl) {
|
|
4970
|
-
let dir = dirname(fileURLToPath(moduleUrl));
|
|
4971
|
-
while (!existsSync(join(dir, "package.json"))) {
|
|
4972
|
-
if (existsSync(join(dir, ".git")))
|
|
4973
|
-
break;
|
|
4974
|
-
const parent = dirname(dir);
|
|
4975
|
-
if (parent === dir)
|
|
4976
|
-
break;
|
|
4977
|
-
dir = parent;
|
|
4978
|
-
}
|
|
4979
|
-
return dir;
|
|
4980
|
-
}
|
|
4981
|
-
|
|
4982
5622
|
// src/lib/config.ts
|
|
4983
5623
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
4984
|
-
import { existsSync as
|
|
5624
|
+
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, realpathSync, writeFileSync } from "node:fs";
|
|
4985
5625
|
import { homedir } from "node:os";
|
|
4986
|
-
import { dirname as dirname2, join as
|
|
5626
|
+
import { dirname as dirname2, join as join3, resolve } from "node:path";
|
|
4987
5627
|
import { z as z2 } from "zod";
|
|
4988
5628
|
|
|
4989
5629
|
// src/lib/agent-vendors.ts
|
|
@@ -5137,16 +5777,16 @@ var VinayaConfigSchema = z2.object({
|
|
|
5137
5777
|
agent: z2.enum(["claude", "codex", "gemini"]).optional()
|
|
5138
5778
|
}).optional()
|
|
5139
5779
|
});
|
|
5140
|
-
var GLOBAL_VINAYA_HOME =
|
|
5141
|
-
var GLOBAL_CONFIG_PATH =
|
|
5780
|
+
var GLOBAL_VINAYA_HOME = join3(homedir(), ".vinaya");
|
|
5781
|
+
var GLOBAL_CONFIG_PATH = join3(GLOBAL_VINAYA_HOME, "config.json");
|
|
5142
5782
|
var LOCAL_CONFIG_FILENAME = "vinaya.config.json";
|
|
5143
5783
|
function findLocalConfig() {
|
|
5144
5784
|
let dir = process.cwd();
|
|
5145
5785
|
while (true) {
|
|
5146
|
-
const candidate =
|
|
5147
|
-
if (
|
|
5786
|
+
const candidate = join3(dir, LOCAL_CONFIG_FILENAME);
|
|
5787
|
+
if (existsSync3(candidate))
|
|
5148
5788
|
return candidate;
|
|
5149
|
-
if (
|
|
5789
|
+
if (existsSync3(join3(dir, ".git")))
|
|
5150
5790
|
return null;
|
|
5151
5791
|
const parent = dirname2(dir);
|
|
5152
5792
|
if (parent === dir)
|
|
@@ -5158,7 +5798,7 @@ function configPath() {
|
|
|
5158
5798
|
const local = findLocalConfig();
|
|
5159
5799
|
if (local)
|
|
5160
5800
|
return local;
|
|
5161
|
-
if (
|
|
5801
|
+
if (existsSync3(GLOBAL_CONFIG_PATH))
|
|
5162
5802
|
return GLOBAL_CONFIG_PATH;
|
|
5163
5803
|
return null;
|
|
5164
5804
|
}
|
|
@@ -5286,7 +5926,7 @@ function loadConfig() {
|
|
|
5286
5926
|
return null;
|
|
5287
5927
|
}
|
|
5288
5928
|
}
|
|
5289
|
-
var TOKENS_COLLECT_TRUST_PATH =
|
|
5929
|
+
var TOKENS_COLLECT_TRUST_PATH = join3(GLOBAL_VINAYA_HOME, "tokens-collect-trust.json");
|
|
5290
5930
|
|
|
5291
5931
|
// src/commands/tokens.ts
|
|
5292
5932
|
var USAGE = [
|
|
@@ -5309,6 +5949,10 @@ var USAGE = [
|
|
|
5309
5949
|
`);
|
|
5310
5950
|
|
|
5311
5951
|
// src/commands/pr-report.ts
|
|
5952
|
+
var BODY_READING_CHECK_NAMES = new Set(coreCheckRegistry().filter((spec) => {
|
|
5953
|
+
const decl = spec.env?.PR_BODY;
|
|
5954
|
+
return decl === true || typeof decl === "object" && decl !== null && "optional" in decl;
|
|
5955
|
+
}).map((spec) => spec.name));
|
|
5312
5956
|
var FAILING_STATUSES = new Set(["fail", "error", "timeout"]);
|
|
5313
5957
|
function extractAgentCommandLines(prBody) {
|
|
5314
5958
|
const located = locateTestPlanSection(prBody);
|
|
@@ -5344,10 +5988,36 @@ var TOKEN_ROW_REMEDY = [
|
|
|
5344
5988
|
`);
|
|
5345
5989
|
var USAGE2 = "Usage: vinaya pr report [--write <body-file> | --push <pr>] [--phase <phase>] [--role <role>] " + "[--model <id>] [--transcript <path>]";
|
|
5346
5990
|
|
|
5991
|
+
// src/lib/patch-id.ts
|
|
5992
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
5993
|
+
function patchIdAt(base, sha) {
|
|
5994
|
+
try {
|
|
5995
|
+
execFileSync3("git", ["fetch", "--quiet", "origin", sha], { stdio: ["ignore", "ignore", "ignore"] });
|
|
5996
|
+
} catch {}
|
|
5997
|
+
try {
|
|
5998
|
+
const diff = execFileSync3("git", ["diff", `origin/${base}...${sha}`], {
|
|
5999
|
+
encoding: "utf8",
|
|
6000
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
6001
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
6002
|
+
});
|
|
6003
|
+
if (diff === "")
|
|
6004
|
+
return null;
|
|
6005
|
+
const out = execFileSync3("git", ["patch-id", "--stable"], {
|
|
6006
|
+
input: diff,
|
|
6007
|
+
encoding: "utf8",
|
|
6008
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
6009
|
+
}).trim();
|
|
6010
|
+
const id = out.split(/\s+/)[0] ?? "";
|
|
6011
|
+
return id === "" ? null : id;
|
|
6012
|
+
} catch {
|
|
6013
|
+
return null;
|
|
6014
|
+
}
|
|
6015
|
+
}
|
|
6016
|
+
|
|
5347
6017
|
// src/checks/evidence-fresh-logic.ts
|
|
5348
6018
|
var HEAD_LINE = /^Head:\s*([0-9a-f]{7,40})\s*$/m;
|
|
5349
6019
|
var FENCE = /```[^\n]*\n?([\s\S]*?)```/g;
|
|
5350
|
-
function compareEvidenceBlock(resolved, resolvedHead, actualNumstat, expectedGroupCCommandLines) {
|
|
6020
|
+
function compareEvidenceBlock(resolved, resolvedHead, actualNumstat, expectedGroupCCommandLines, patchIdOf) {
|
|
5351
6021
|
const region = resolved.region;
|
|
5352
6022
|
const headMatch = region.match(HEAD_LINE);
|
|
5353
6023
|
const fences = [...region.matchAll(FENCE)].map((m) => (m[1] ?? "").trim());
|
|
@@ -5364,7 +6034,16 @@ function compareEvidenceBlock(resolved, resolvedHead, actualNumstat, expectedGro
|
|
|
5364
6034
|
const actual = actualNumstat.trim();
|
|
5365
6035
|
const errors = [];
|
|
5366
6036
|
if (storedHead !== resolvedHead) {
|
|
5367
|
-
|
|
6037
|
+
let samePatch = false;
|
|
6038
|
+
if (patchIdOf) {
|
|
6039
|
+
const storedPatchId = patchIdOf(storedHead);
|
|
6040
|
+
const resolvedPatchId = patchIdOf(resolvedHead);
|
|
6041
|
+
samePatch = storedPatchId !== null && resolvedPatchId !== null && storedPatchId === resolvedPatchId;
|
|
6042
|
+
}
|
|
6043
|
+
if (!samePatch) {
|
|
6044
|
+
const patchNote = patchIdOf ? ", and the patch changed" : "";
|
|
6045
|
+
errors.push(`evidence-fresh: Group B is stale — the block's Head (${storedHead}) does not match the PR's real head (${resolvedHead})${patchNote}. Re-run \`vinaya pr report --write\` against the current head, commit, and push again.`);
|
|
6046
|
+
}
|
|
5368
6047
|
}
|
|
5369
6048
|
if (storedNumstat !== actual) {
|
|
5370
6049
|
errors.push([
|
|
@@ -5422,7 +6101,7 @@ function compareEvidenceBlock(resolved, resolvedHead, actualNumstat, expectedGro
|
|
|
5422
6101
|
var CHECK_NAME = "evidence-fresh";
|
|
5423
6102
|
function git(args) {
|
|
5424
6103
|
try {
|
|
5425
|
-
return
|
|
6104
|
+
return execFileSync4("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
5426
6105
|
} catch {
|
|
5427
6106
|
return "";
|
|
5428
6107
|
}
|
|
@@ -5441,7 +6120,7 @@ class GitCommandError extends Error {
|
|
|
5441
6120
|
}
|
|
5442
6121
|
function gitStrict(args) {
|
|
5443
6122
|
try {
|
|
5444
|
-
return
|
|
6123
|
+
return execFileSync4("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
5445
6124
|
} catch (err) {
|
|
5446
6125
|
const stderr = err.stderr;
|
|
5447
6126
|
throw new GitCommandError(args, String(stderr ?? err.message).trim() || "non-zero exit");
|
|
@@ -5457,13 +6136,16 @@ function resolveMergeBase(head) {
|
|
|
5457
6136
|
}
|
|
5458
6137
|
throw new UnresolvableMergeBaseError(tried);
|
|
5459
6138
|
}
|
|
5460
|
-
function
|
|
6139
|
+
function fetchPrRefs(prNumber) {
|
|
5461
6140
|
try {
|
|
5462
|
-
const out =
|
|
6141
|
+
const out = execFileSync4("gh", ["pr", "view", String(prNumber), "--json", "headRefOid,baseRefName"], {
|
|
5463
6142
|
encoding: "utf8",
|
|
5464
6143
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5465
|
-
})
|
|
5466
|
-
|
|
6144
|
+
});
|
|
6145
|
+
const parsed = JSON.parse(out);
|
|
6146
|
+
if (!parsed.headRefOid || !parsed.baseRefName)
|
|
6147
|
+
return null;
|
|
6148
|
+
return { head: parsed.headRefOid, base: parsed.baseRefName };
|
|
5467
6149
|
} catch {
|
|
5468
6150
|
return null;
|
|
5469
6151
|
}
|
|
@@ -5491,17 +6173,18 @@ function main() {
|
|
|
5491
6173
|
if (!prNumberStr) {
|
|
5492
6174
|
process.exit(0);
|
|
5493
6175
|
}
|
|
5494
|
-
const
|
|
5495
|
-
if (
|
|
6176
|
+
const prRefs = fetchPrRefs(Number(prNumberStr));
|
|
6177
|
+
if (prRefs === null) {
|
|
5496
6178
|
emitCheckError({
|
|
5497
6179
|
schema: CHECK_SCHEMA_VERSION,
|
|
5498
6180
|
check: CHECK_NAME,
|
|
5499
6181
|
severity: "error",
|
|
5500
|
-
message: `evidence-fresh: could not resolve PR #${prNumberStr}'s head via \`gh pr view --json headRefOid\`.`,
|
|
6182
|
+
message: `evidence-fresh: could not resolve PR #${prNumberStr}'s head and base via \`gh pr view --json headRefOid,baseRefName\`.`,
|
|
5501
6183
|
agent_recovery_prompt: "Confirm `gh auth status` passes and PR_NUMBER is correct, then re-run `vinaya check evidence-fresh`."
|
|
5502
6184
|
});
|
|
5503
6185
|
process.exit(1);
|
|
5504
6186
|
}
|
|
6187
|
+
const resolvedHead = prRefs.head;
|
|
5505
6188
|
let base;
|
|
5506
6189
|
let actualNumstat;
|
|
5507
6190
|
try {
|
|
@@ -5518,7 +6201,8 @@ function main() {
|
|
|
5518
6201
|
process.exit(1);
|
|
5519
6202
|
}
|
|
5520
6203
|
const expectedGroupCCommandLines = extractAgentCommandLines(body).map(agentCommandText);
|
|
5521
|
-
const
|
|
6204
|
+
const patchIdOf = (sha) => patchIdAt(prRefs.base, sha);
|
|
6205
|
+
const result = compareEvidenceBlock(resolved, resolvedHead, actualNumstat, expectedGroupCCommandLines, patchIdOf);
|
|
5522
6206
|
if (result.status === "fail") {
|
|
5523
6207
|
for (const message of result.errors) {
|
|
5524
6208
|
emitCheckError({
|