@attalabs/vinaya 0.24.1 → 0.26.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 +26 -3
- package/aeg-root/contracts/architect-planner.md +5 -5
- package/aeg-root/contracts/developer-reviewer.md +10 -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 +25 -11
- package/aeg-root/process.md +53 -75
- package/aeg-root/roles/archivist.md +3 -3
- package/aeg-root/roles/developer.md +30 -24
- package/aeg-root/roles/planner.md +100 -21
- package/aeg-root/roles/principal.md +14 -11
- package/aeg-root/roles/reviewer.md +33 -17
- package/aeg-root/roles/security.md +28 -13
- 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 +36 -35
- package/aeg-root/task-model.md +3 -3
- package/aeg-root/templates/brief-template.md +22 -6
- package/aeg-root/templates/issue-rationale-template.md +35 -2
- package/aeg-root/templates/pr-report-template.md +5 -13
- package/aeg-root/tranche-model.md +21 -21
- package/dist/checks/bin/check-body-bare-digits.js +684 -142
- package/dist/checks/bin/check-branch-topology.js +741 -88
- package/dist/checks/bin/check-brief-shape.js +708 -83
- package/dist/checks/bin/check-changeset-coverage.js +1445 -149
- package/dist/checks/bin/check-closes-n.js +741 -88
- package/dist/checks/bin/check-coherence.js +808 -149
- package/dist/checks/bin/check-dead-branch-push.js +617 -81
- package/dist/checks/bin/check-dispatch-readiness.js +811 -152
- package/dist/checks/bin/check-doc-coverage-push.js +1445 -149
- package/dist/checks/bin/check-doc-coverage.js +1447 -151
- package/dist/checks/bin/check-doctrine-no-procedures.js +684 -142
- package/dist/checks/bin/check-doctrine-portability.js +1445 -149
- package/dist/checks/bin/check-evidence-fresh.js +684 -142
- package/dist/checks/bin/check-exec-bits.js +1445 -149
- package/dist/checks/bin/check-first-push-dispatch.js +808 -149
- package/dist/checks/bin/check-issue-assignment.js +741 -88
- package/dist/checks/bin/check-main-branch-refusal.js +618 -82
- package/dist/checks/bin/check-no-disk-state.js +617 -81
- package/dist/checks/bin/check-pr-premise-reassert.js +5401 -0
- package/dist/checks/bin/check-pr-report-density.js +617 -81
- package/dist/checks/bin/check-quoted-command.js +1443 -147
- package/dist/checks/bin/check-reader-resolvable-prose.js +1481 -153
- package/dist/checks/bin/check-registry-gates.js +659 -85
- package/dist/checks/bin/check-retired-vocabulary.js +1443 -147
- package/dist/checks/bin/check-review-gate.js +758 -145
- package/dist/checks/bin/check-single-plan-pr.js +617 -81
- package/dist/checks/bin/check-surface-scope.js +5722 -0
- package/dist/checks/bin/check-test-plan.js +617 -81
- package/dist/checks/bin/check-token-collection-wired.js +617 -81
- package/dist/checks/bin/check-token-report.js +629 -88
- package/dist/checks/bin/check-workspace-escape.js +1443 -147
- package/dist/index.js +8548 -4335
- package/package.json +1 -1
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/brief-render.ts +2 -2
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/main-branch-refusal.ts +26 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/BUILD_ID +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/build-manifest.json +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/prerender-manifest.json +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.html +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/api/coherence/route.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/1q96_modules_@clerk_nextjs_dist_esm_app-router_client_keyless-creator-reader_0lom2js.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0053k9k._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0112h-k._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0o771t1._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__1pndh3_._.js → [root-of-the-server]__0p8q38b._.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0puovz5._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1hs0dcu._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1wc4-ip._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_03x_w6q._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0gvm3og._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0lwxg63._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0wxycau._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1n0cnq-._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_1vo08dj._.js +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/packages_ui_topbar_index_tsx_1h1gs1y._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/middleware-build-manifest.js +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/server/pages/500.html +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.json +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{3m1kgax7j2vgs.js → 0bn8c8v5q429o.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{0doqwpd81sjzn.js → 0keji7wvbe1d1.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/1maoxbrl3pv5d.css +1 -0
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{1uogpj5w2n5ju.js → 3-6a3tinbdb-7.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{0zebsmmk1bvnb.js → 3_9xytdmiv1xi.js} +4 -4
- package/studio-standalone/apps/vinaya-studio/web/package.json +2 -2
- package/aeg-root/contracts/brief-developer.md +0 -141
- package/aeg-root/contracts/planner-brief.md +0 -130
- package/aeg-root/roles/brief-author.md +0 -114
- package/aeg-root/skills/brief-authoring/SKILL.md +0 -509
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/3qc67qhcvbg0o.css +0 -1
- /package/studio-standalone/_node_modules/@attalabs/vinaya/studio-standalone/_node_modules/@attalabs/vinaya/studio-standalone/_node_modules/@attalabs/vinaya/studio-standalone/_node_modules/@attalabs/{aeg-core → vinaya/studio-standalone/_node_modules/@attalabs/aeg-core}/bin/verify-coherence.ts +0 -0
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{QY2GhiI47-765D9DHGy1I → DMofV2-9FrHE7100GfrpI}/_buildManifest.js +0 -0
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{QY2GhiI47-765D9DHGy1I → DMofV2-9FrHE7100GfrpI}/_clientMiddlewareManifest.js +0 -0
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{QY2GhiI47-765D9DHGy1I → DMofV2-9FrHE7100GfrpI}/_ssgManifest.js +0 -0
|
@@ -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
|
}
|
|
@@ -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",
|
|
@@ -1940,13 +1949,18 @@ function parseInlineFieldList(section) {
|
|
|
1940
1949
|
}
|
|
1941
1950
|
// ../../packages/aeg-core/src/verdict-extraction.ts
|
|
1942
1951
|
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
1943
|
-
|
|
1952
|
+
var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1953
|
+
function firstFiveLines(comment) {
|
|
1944
1954
|
return comment.split(`
|
|
1945
|
-
`).slice(0,
|
|
1955
|
+
`).slice(0, 5).join(`
|
|
1946
1956
|
`);
|
|
1947
1957
|
}
|
|
1948
1958
|
function extractHeadSha(comment) {
|
|
1949
|
-
const m =
|
|
1959
|
+
const m = firstFiveLines(comment).match(HEAD_SHA_PATTERN);
|
|
1960
|
+
return m ? m[1].toLowerCase() : null;
|
|
1961
|
+
}
|
|
1962
|
+
function extractObjectivesVersion(comment) {
|
|
1963
|
+
const m = firstFiveLines(comment).match(OBJECTIVES_VERSION_PATTERN);
|
|
1950
1964
|
return m ? m[1].toLowerCase() : null;
|
|
1951
1965
|
}
|
|
1952
1966
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
@@ -1955,21 +1969,24 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1955
1969
|
return {
|
|
1956
1970
|
value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
|
|
1957
1971
|
headSha: null,
|
|
1972
|
+
objectivesVersion: null,
|
|
1958
1973
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
1959
1974
|
};
|
|
1960
1975
|
}
|
|
1961
1976
|
const latest = candidates[candidates.length - 1];
|
|
1962
|
-
const m =
|
|
1977
|
+
const m = firstFiveLines(latest).match(valuePattern);
|
|
1963
1978
|
if (!m) {
|
|
1964
1979
|
return {
|
|
1965
|
-
value: `the most recent ${missingLabel} comment's VERDICT line is not within its first
|
|
1980
|
+
value: `the most recent ${missingLabel} comment's VERDICT line is not within its first five lines — DANGLING, see below`,
|
|
1966
1981
|
headSha: null,
|
|
1967
|
-
|
|
1982
|
+
objectivesVersion: null,
|
|
1983
|
+
danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
|
|
1968
1984
|
};
|
|
1969
1985
|
}
|
|
1970
1986
|
return {
|
|
1971
1987
|
value: m[1].toUpperCase().replace(/[_-]/g, " "),
|
|
1972
1988
|
headSha: extractHeadSha(latest),
|
|
1989
|
+
objectivesVersion: extractObjectivesVersion(latest),
|
|
1973
1990
|
danglingNote: null
|
|
1974
1991
|
};
|
|
1975
1992
|
}
|
|
@@ -2326,8 +2343,105 @@ function overrideActive(opts) {
|
|
|
2326
2343
|
return true;
|
|
2327
2344
|
return false;
|
|
2328
2345
|
}
|
|
2329
|
-
// ../../packages/aeg-core/src/
|
|
2346
|
+
// ../../packages/aeg-core/src/objectives.ts
|
|
2330
2347
|
import { createHash as createHash2 } from "node:crypto";
|
|
2348
|
+
var HEADING_RE = /^##[ \t]*Objectives[ \t]*$/im;
|
|
2349
|
+
var NEXT_HEADING_RE = /^##[ \t]/m;
|
|
2350
|
+
var OBJECTIVE_LINE_RE = /^O(\d+)\.[ \t]*(.*)$/;
|
|
2351
|
+
function hasBacktickedPath(text) {
|
|
2352
|
+
let i = 0;
|
|
2353
|
+
while (i < text.length) {
|
|
2354
|
+
const start = text.indexOf("`", i);
|
|
2355
|
+
if (start === -1)
|
|
2356
|
+
return false;
|
|
2357
|
+
const end = text.indexOf("`", start + 1);
|
|
2358
|
+
if (end === -1)
|
|
2359
|
+
return false;
|
|
2360
|
+
if (text.slice(start + 1, end).includes("/"))
|
|
2361
|
+
return true;
|
|
2362
|
+
i = end + 1;
|
|
2363
|
+
}
|
|
2364
|
+
return false;
|
|
2365
|
+
}
|
|
2366
|
+
var MIN_WORDS_OUTSIDE_BACKTICKS = 3;
|
|
2367
|
+
function stripObjectiveBackticks(text) {
|
|
2368
|
+
return text.replace(/`[^`\n]*`/g, " ");
|
|
2369
|
+
}
|
|
2370
|
+
function wordCount(text) {
|
|
2371
|
+
return text.split(/\s+/).filter((w) => /[a-z]/i.test(w)).length;
|
|
2372
|
+
}
|
|
2373
|
+
function objectivesSectionText(body) {
|
|
2374
|
+
const heading = HEADING_RE.exec(body);
|
|
2375
|
+
if (!heading)
|
|
2376
|
+
return null;
|
|
2377
|
+
const afterHeading = body.slice(heading.index + heading[0].length);
|
|
2378
|
+
const next = NEXT_HEADING_RE.exec(afterHeading);
|
|
2379
|
+
return next ? afterHeading.slice(0, next.index) : afterHeading;
|
|
2380
|
+
}
|
|
2381
|
+
function hasObjectivesHeading(body) {
|
|
2382
|
+
return HEADING_RE.test(body);
|
|
2383
|
+
}
|
|
2384
|
+
function objectivesOf(body) {
|
|
2385
|
+
const section = objectivesSectionText(body);
|
|
2386
|
+
if (section === null) {
|
|
2387
|
+
return { ok: false, errors: ["no `## Objectives` heading found in the body."] };
|
|
2388
|
+
}
|
|
2389
|
+
const lines = section.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
2390
|
+
if (lines.length === 0) {
|
|
2391
|
+
return { ok: false, errors: ["the `## Objectives` section has no `O<n>.` lines."] };
|
|
2392
|
+
}
|
|
2393
|
+
const objectives = [];
|
|
2394
|
+
const errors = [];
|
|
2395
|
+
for (const line of lines) {
|
|
2396
|
+
const m = OBJECTIVE_LINE_RE.exec(line);
|
|
2397
|
+
if (!m) {
|
|
2398
|
+
errors.push(`"${line}" is not a well-formed objective line — expected \`O<n>. <sentence>\`, e.g. \`O1. …\`.`);
|
|
2399
|
+
continue;
|
|
2400
|
+
}
|
|
2401
|
+
const n = m[1];
|
|
2402
|
+
const text = m[2].trim();
|
|
2403
|
+
if (text.length === 0) {
|
|
2404
|
+
errors.push(`O${n} has no sentence — every objective is one observable sentence.`);
|
|
2405
|
+
continue;
|
|
2406
|
+
}
|
|
2407
|
+
if (hasBacktickedPath(text) && wordCount(stripObjectiveBackticks(text)) < MIN_WORDS_OUTSIDE_BACKTICKS) {
|
|
2408
|
+
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).`);
|
|
2409
|
+
continue;
|
|
2410
|
+
}
|
|
2411
|
+
objectives.push({ id: `O${n}`, text });
|
|
2412
|
+
}
|
|
2413
|
+
for (let i = 0;i < objectives.length; i++) {
|
|
2414
|
+
const expected = i + 1;
|
|
2415
|
+
const actual = Number.parseInt(objectives[i].id.slice(1), 10);
|
|
2416
|
+
if (actual !== expected) {
|
|
2417
|
+
errors.push(`numbering is not contiguous from O1 — expected O${expected}, found ${objectives[i].id}.`);
|
|
2418
|
+
break;
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
if (errors.length > 0)
|
|
2422
|
+
return { ok: false, errors };
|
|
2423
|
+
return { ok: true, objectives };
|
|
2424
|
+
}
|
|
2425
|
+
function normalizeText(text) {
|
|
2426
|
+
return text.trim().replace(/\s+/g, " ");
|
|
2427
|
+
}
|
|
2428
|
+
function objectivesVersion(objectives) {
|
|
2429
|
+
const normalized = objectives.map((o) => `${o.id} ${normalizeText(o.text)}`).join(`
|
|
2430
|
+
`);
|
|
2431
|
+
return createHash2("sha256").update(normalized).digest("hex");
|
|
2432
|
+
}
|
|
2433
|
+
function isIssueNotFoundError(err) {
|
|
2434
|
+
const stderr = err?.stderr;
|
|
2435
|
+
const haystack = [
|
|
2436
|
+
err?.message ?? "",
|
|
2437
|
+
typeof stderr === "string" ? stderr : stderr?.toString() ?? ""
|
|
2438
|
+
].join(`
|
|
2439
|
+
`);
|
|
2440
|
+
return /could not resolve to an (?:issue|pull request)|\b404\b|not found/i.test(haystack);
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
// ../../packages/aeg-core/src/premise-check.ts
|
|
2444
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
2331
2445
|
var ASSERTION_KINDS = new Set(["contains", "absent", "sha256"]);
|
|
2332
2446
|
function isPremiseHeader(line) {
|
|
2333
2447
|
const stripped = line.replace(/[*#]/g, "").trim();
|
|
@@ -2398,7 +2512,7 @@ function checkPremises(assertions, fileReader) {
|
|
|
2398
2512
|
} else if (a.kind === "absent" && content.includes(a.value)) {
|
|
2399
2513
|
failures.push(`premise-check: ${a.path} now contains "${a.value}", but the brief pinned it absent — the premise has moved; re-dig before proceeding.`);
|
|
2400
2514
|
} else if (a.kind === "sha256") {
|
|
2401
|
-
const actual =
|
|
2515
|
+
const actual = createHash3("sha256").update(content).digest("hex");
|
|
2402
2516
|
if (actual !== a.value.toLowerCase()) {
|
|
2403
2517
|
failures.push(`premise-check: ${a.path} sha256 mismatch (brief pinned ${a.value}, file is now ${actual}) — the file changed since this brief was authored; re-dig before proceeding.`);
|
|
2404
2518
|
}
|
|
@@ -2503,18 +2617,14 @@ function checkTestPlanExclusivity(prBody) {
|
|
|
2503
2617
|
function checkPrincipalPlaceholder(prBody) {
|
|
2504
2618
|
const region = testPlanRegion(prBody);
|
|
2505
2619
|
const lineRe = /^-\s*\[[ xX]\]\s*\*{2}\[principal\]\*{2}(.*)$/gim;
|
|
2620
|
+
const errors = [];
|
|
2506
2621
|
for (const m of region.matchAll(lineRe)) {
|
|
2507
2622
|
const content = m[1] ?? "";
|
|
2508
2623
|
if (/^\s*None\b/i.test(content)) {
|
|
2509
|
-
|
|
2510
|
-
status: "fail",
|
|
2511
|
-
errors: [
|
|
2512
|
-
'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.'
|
|
2513
|
-
]
|
|
2514
|
-
};
|
|
2624
|
+
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.`);
|
|
2515
2625
|
}
|
|
2516
2626
|
}
|
|
2517
|
-
return { status: "pass", errors: [] };
|
|
2627
|
+
return errors.length > 0 ? { status: "fail", errors } : { status: "pass", errors: [] };
|
|
2518
2628
|
}
|
|
2519
2629
|
function checkSurfaceMap(prBody) {
|
|
2520
2630
|
return headingCheck(prBody, "(?:technical\\s+)?surface map", "Technical surface map");
|
|
@@ -2759,8 +2869,55 @@ function checkDefeatCases(prBody) {
|
|
|
2759
2869
|
]
|
|
2760
2870
|
};
|
|
2761
2871
|
}
|
|
2872
|
+
var PART_CITATION_RE = /Part\s+\d+\s*\(([^)]*)\)/gi;
|
|
2873
|
+
var OBJECTIVE_REF_RE = /O(\d+)/g;
|
|
2874
|
+
function citedObjectiveIds(section6) {
|
|
2875
|
+
const ids = new Set;
|
|
2876
|
+
for (const m of section6.matchAll(PART_CITATION_RE)) {
|
|
2877
|
+
for (const r of m[1].matchAll(OBJECTIVE_REF_RE))
|
|
2878
|
+
ids.add(Number.parseInt(r[1], 10));
|
|
2879
|
+
}
|
|
2880
|
+
return ids;
|
|
2881
|
+
}
|
|
2882
|
+
function checkObjectivesCopy(prBody, issueObjectives) {
|
|
2883
|
+
const parsed = objectivesOf(prBody);
|
|
2884
|
+
if (!parsed.ok) {
|
|
2885
|
+
return { status: "fail", errors: parsed.errors.map((e) => `brief-validation objectives copy: ${e}`) };
|
|
2886
|
+
}
|
|
2887
|
+
if (objectivesVersion(parsed.objectives) !== objectivesVersion(issueObjectives)) {
|
|
2888
|
+
return {
|
|
2889
|
+
status: "fail",
|
|
2890
|
+
errors: [
|
|
2891
|
+
"brief-validation objectives copy: the brief's `## Objectives` section does not match the Issue's — copy the Issue's `## Objectives` section (aeg-root/roles/developer.md)."
|
|
2892
|
+
]
|
|
2893
|
+
};
|
|
2894
|
+
}
|
|
2895
|
+
return { status: "pass", errors: [] };
|
|
2896
|
+
}
|
|
2897
|
+
function checkObjectivesCoverage(prBody) {
|
|
2898
|
+
const parsed = objectivesOf(prBody);
|
|
2899
|
+
if (!parsed.ok) {
|
|
2900
|
+
return { status: "fail", errors: parsed.errors.map((e) => `brief-validation objectives coverage: ${e}`) };
|
|
2901
|
+
}
|
|
2902
|
+
const declaredIds = new Set(parsed.objectives.map((o) => Number.parseInt(o.id.slice(1), 10)));
|
|
2903
|
+
const section6 = extractNumberedSection(prBody, 6) ?? "";
|
|
2904
|
+
const cited = citedObjectiveIds(section6);
|
|
2905
|
+
const maxDeclared = Math.max(...declaredIds, 0);
|
|
2906
|
+
const errors = [];
|
|
2907
|
+
for (const id of declaredIds) {
|
|
2908
|
+
if (!cited.has(id)) {
|
|
2909
|
+
errors.push(`brief-validation objectives coverage: O${id} is not cited by any Part in §6 — every objective must be cited by at least one Part.`);
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
for (const id of cited) {
|
|
2913
|
+
if (!declaredIds.has(id)) {
|
|
2914
|
+
errors.push(`brief-validation objectives coverage: a Part in §6 cites O${id}, but the Objectives section ends at O${maxDeclared}.`);
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
return errors.length === 0 ? { status: "pass", errors: [] } : { status: "fail", errors };
|
|
2918
|
+
}
|
|
2762
2919
|
function checkBriefSections(prBody, readTier, options = {}) {
|
|
2763
|
-
const { requireClosesN = true, consumersOf = () => [] } = options;
|
|
2920
|
+
const { requireClosesN = true, consumersOf = () => [], issueObjectives } = options;
|
|
2764
2921
|
const results = [
|
|
2765
2922
|
checkTierField(prBody, readTier),
|
|
2766
2923
|
checkTestPlan(prBody),
|
|
@@ -2778,10 +2935,23 @@ function checkBriefSections(prBody, readTier, options = {}) {
|
|
|
2778
2935
|
checkCommandsCarryOutput(prBody),
|
|
2779
2936
|
checkConsumerTests(prBody, consumersOf),
|
|
2780
2937
|
checkDefeatCases(prBody),
|
|
2938
|
+
...issueObjectives !== undefined ? [checkObjectivesCopy(prBody, issueObjectives), checkObjectivesCoverage(prBody)] : [],
|
|
2781
2939
|
...requireClosesN ? [checkClosesN(prBody)] : []
|
|
2782
2940
|
];
|
|
2783
2941
|
return { errors: results.flatMap((r) => r.errors) };
|
|
2784
2942
|
}
|
|
2943
|
+
var AEG_BRIEF_V1_MARKER = "<!-- aeg:brief:v1 -->";
|
|
2944
|
+
function contentAfterTwoLines(body) {
|
|
2945
|
+
const first = body.indexOf(`
|
|
2946
|
+
`);
|
|
2947
|
+
if (first === -1)
|
|
2948
|
+
return "";
|
|
2949
|
+
const second = body.indexOf(`
|
|
2950
|
+
`, first + 1);
|
|
2951
|
+
if (second === -1)
|
|
2952
|
+
return "";
|
|
2953
|
+
return body.slice(second + 1);
|
|
2954
|
+
}
|
|
2785
2955
|
// ../../packages/aeg-core/src/doctrine-portability.ts
|
|
2786
2956
|
var DEFAULT_SHIPS_PREFIX = "aeg-root/";
|
|
2787
2957
|
var STATIC_PORTABLE_PREFIXES = [
|
|
@@ -2976,14 +3146,14 @@ function checkSectionDensity(prBody, heading) {
|
|
|
2976
3146
|
]
|
|
2977
3147
|
};
|
|
2978
3148
|
}
|
|
2979
|
-
function
|
|
2980
|
-
return checkSectionDensity(prBody, "
|
|
3149
|
+
function checkDecisionsDensity(prBody) {
|
|
3150
|
+
return checkSectionDensity(prBody, "Decisions");
|
|
2981
3151
|
}
|
|
2982
3152
|
function checkScopeDensity(prBody) {
|
|
2983
3153
|
return checkSectionDensity(prBody, "Scope");
|
|
2984
3154
|
}
|
|
2985
3155
|
function checkPrReportDensity(prBody) {
|
|
2986
|
-
const results = [
|
|
3156
|
+
const results = [checkDecisionsDensity(prBody), checkScopeDensity(prBody)];
|
|
2987
3157
|
return { errors: results.flatMap((r) => r.errors) };
|
|
2988
3158
|
}
|
|
2989
3159
|
// ../../packages/aeg-core/src/issue-validation.ts
|
|
@@ -3015,6 +3185,75 @@ function checkIssueRationale(body) {
|
|
|
3015
3185
|
}
|
|
3016
3186
|
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3017
3187
|
}
|
|
3188
|
+
var OBJECTIVES_SINCE_ISSUE = 404;
|
|
3189
|
+
function checkIssueObjectives(body, issueNumber) {
|
|
3190
|
+
if (issueNumber !== null && issueNumber < OBJECTIVES_SINCE_ISSUE)
|
|
3191
|
+
return { status: "pass", errors: [] };
|
|
3192
|
+
const result = objectivesOf(body);
|
|
3193
|
+
if (result.ok)
|
|
3194
|
+
return { status: "pass", errors: [] };
|
|
3195
|
+
return { status: "fail", errors: result.errors.map((e) => `issue-validation objectives: ${e}`) };
|
|
3196
|
+
}
|
|
3197
|
+
function topLevelSectionText(body, headingName) {
|
|
3198
|
+
const headingRe = new RegExp(`^##[ \\t]*${headingName}[ \\t]*$`, "im");
|
|
3199
|
+
const heading = headingRe.exec(body);
|
|
3200
|
+
if (!heading)
|
|
3201
|
+
return null;
|
|
3202
|
+
const afterHeading = body.slice(heading.index + heading[0].length);
|
|
3203
|
+
const next = /^##[ \t]/m.exec(afterHeading);
|
|
3204
|
+
return next ? afterHeading.slice(0, next.index) : afterHeading;
|
|
3205
|
+
}
|
|
3206
|
+
function looksLikeFilePath(entry2) {
|
|
3207
|
+
const stripped = entry2.replace(/\/\*\*?$/, "");
|
|
3208
|
+
const lastSegment = stripped.split("/").pop() ?? stripped;
|
|
3209
|
+
const extMatch = /\.[A-Za-z0-9]{1,6}$/.exec(lastSegment);
|
|
3210
|
+
return extMatch !== null && extMatch.index > 0;
|
|
3211
|
+
}
|
|
3212
|
+
function splitGlobList(raw) {
|
|
3213
|
+
return raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
3214
|
+
}
|
|
3215
|
+
function parseIssueSurface(body) {
|
|
3216
|
+
const section = topLevelSectionText(body, "Surface");
|
|
3217
|
+
if (section === null)
|
|
3218
|
+
return { ok: false, errors: ["no `## Surface` heading found in the body."] };
|
|
3219
|
+
const inLine = /^in:\s*(.+)$/im.exec(section);
|
|
3220
|
+
const outLine = /^out:\s*(.+)$/im.exec(section);
|
|
3221
|
+
const errors = [];
|
|
3222
|
+
if (!inLine)
|
|
3223
|
+
errors.push("`## Surface` has no `in:` line.");
|
|
3224
|
+
if (!outLine)
|
|
3225
|
+
errors.push("`## Surface` has no `out:` line.");
|
|
3226
|
+
if (errors.length > 0)
|
|
3227
|
+
return { ok: false, errors };
|
|
3228
|
+
const inGlobs = splitGlobList(inLine[1]);
|
|
3229
|
+
const outGlobs = splitGlobList(outLine[1]);
|
|
3230
|
+
for (const g of [...inGlobs, ...outGlobs]) {
|
|
3231
|
+
if (looksLikeFilePath(g)) {
|
|
3232
|
+
errors.push(`\`${g}\` in \`## Surface\` looks like a file path — Surface entries are directory-level globs, never file paths.`);
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
if (inGlobs.length === 0)
|
|
3236
|
+
errors.push("`## Surface`'s `in:` line resolved to zero globs.");
|
|
3237
|
+
if (errors.length > 0)
|
|
3238
|
+
return { ok: false, errors };
|
|
3239
|
+
return { ok: true, value: { in: inGlobs, out: outGlobs } };
|
|
3240
|
+
}
|
|
3241
|
+
function globCoversPath(glob, path) {
|
|
3242
|
+
const g = glob.replace(/\/\*\*?$/, "").replace(/\/+$/, "");
|
|
3243
|
+
const p = path.replace(/\/+$/, "");
|
|
3244
|
+
return g === p || g.startsWith(`${p}/`) || p.startsWith(`${g}/`);
|
|
3245
|
+
}
|
|
3246
|
+
function checkSurfaceScope(changedFiles, outGlobs) {
|
|
3247
|
+
if (outGlobs.length === 0)
|
|
3248
|
+
return { ok: true };
|
|
3249
|
+
const violations = [];
|
|
3250
|
+
for (const file of changedFiles) {
|
|
3251
|
+
const glob = outGlobs.find((g) => globCoversPath(g, file));
|
|
3252
|
+
if (glob)
|
|
3253
|
+
violations.push({ file, glob });
|
|
3254
|
+
}
|
|
3255
|
+
return violations.length > 0 ? { ok: false, violations } : { ok: true };
|
|
3256
|
+
}
|
|
3018
3257
|
function isTaskIssueLabelSet(labels) {
|
|
3019
3258
|
return hasLabel("tranche", labels);
|
|
3020
3259
|
}
|
|
@@ -3079,6 +3318,8 @@ function checkProjectsRegistered(body, _labels, registeredNames) {
|
|
|
3079
3318
|
}
|
|
3080
3319
|
return errors.length > 0 ? { status: "fail", errors } : { status: "pass", errors: [] };
|
|
3081
3320
|
}
|
|
3321
|
+
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;
|
|
3322
|
+
var DOC_PATH_RE_GLOBAL = new RegExp(DOC_PATH_RE.source, "gi");
|
|
3082
3323
|
|
|
3083
3324
|
// ../../packages/aeg-core/src/coherence-checks.ts
|
|
3084
3325
|
var COHERENCE_ENFORCED_FROM = "2026-07-01";
|
|
@@ -3276,7 +3517,8 @@ function checkR1(issuesBySlug, grandfatheredIssues, registeredNames = []) {
|
|
|
3276
3517
|
continue;
|
|
3277
3518
|
const errors = [
|
|
3278
3519
|
...checkIssueRationale(issue.body).errors,
|
|
3279
|
-
...checkProjectsRegistered(issue.body, issue.labels, registeredNames).errors
|
|
3520
|
+
...checkProjectsRegistered(issue.body, issue.labels, registeredNames).errors,
|
|
3521
|
+
...checkIssueObjectives(issue.body, issue.number).errors
|
|
3280
3522
|
];
|
|
3281
3523
|
if (errors.length === 0)
|
|
3282
3524
|
continue;
|
|
@@ -3391,6 +3633,27 @@ function checkClosesN2(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
|
3391
3633
|
}
|
|
3392
3634
|
return { ok: true, expectedIssue };
|
|
3393
3635
|
}
|
|
3636
|
+
// ../../packages/aeg-core/src/archive-task.ts
|
|
3637
|
+
function closesRefs(text) {
|
|
3638
|
+
return [...new Set([...text.matchAll(/Closes #(\d+)/gi)].map((m) => Number(m[1])))];
|
|
3639
|
+
}
|
|
3640
|
+
function extractIssue(body) {
|
|
3641
|
+
const stripped = stripCode(body);
|
|
3642
|
+
const bodyNums = closesRefs(stripped);
|
|
3643
|
+
const anchored = anchoredRegion(stripped, "CLOSES");
|
|
3644
|
+
if (anchored !== null) {
|
|
3645
|
+
const anchorNums = closesRefs(anchored);
|
|
3646
|
+
if (anchorNums.length === 0)
|
|
3647
|
+
return { issue: null, extraIssues: bodyNums, outsideHeader: false };
|
|
3648
|
+
const issue2 = anchorNums[0];
|
|
3649
|
+
return { issue: issue2, extraIssues: bodyNums.filter((n) => n !== issue2), outsideHeader: false };
|
|
3650
|
+
}
|
|
3651
|
+
const headerNums = closesRefs(headerRegion(stripped));
|
|
3652
|
+
if (bodyNums.length === 0)
|
|
3653
|
+
return { issue: null, extraIssues: [], outsideHeader: false };
|
|
3654
|
+
const issue = headerNums.length > 0 ? headerNums[0] : bodyNums[0];
|
|
3655
|
+
return { issue, extraIssues: bodyNums.filter((n) => n !== issue), outsideHeader: headerNums.length === 0 };
|
|
3656
|
+
}
|
|
3394
3657
|
// ../../packages/aeg-core/src/review-gate.ts
|
|
3395
3658
|
function isBoundToHead(extraction, headSha) {
|
|
3396
3659
|
if (!extraction.headSha)
|
|
@@ -3409,6 +3672,11 @@ function isBoundByPatchIdentity(extraction, headSha, patchIdOf) {
|
|
|
3409
3672
|
function isBoundToPatch(extraction, headSha, patchIdOf) {
|
|
3410
3673
|
return isBoundToHead(extraction, headSha) || isBoundByPatchIdentity(extraction, headSha, patchIdOf);
|
|
3411
3674
|
}
|
|
3675
|
+
function isBoundToObjectives(extraction, currentVersion) {
|
|
3676
|
+
if (currentVersion === null)
|
|
3677
|
+
return true;
|
|
3678
|
+
return extraction.objectivesVersion === currentVersion;
|
|
3679
|
+
}
|
|
3412
3680
|
function checkReviewGate(input) {
|
|
3413
3681
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
3414
3682
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -3435,7 +3703,9 @@ function checkReviewGate(input) {
|
|
|
3435
3703
|
const securityClean = security.value === "PASS";
|
|
3436
3704
|
const codeReviewBound = isBoundToPatch(codeReview, input.headSha, input.patchIdOf);
|
|
3437
3705
|
const securityBound = isBoundToPatch(security, input.headSha, input.patchIdOf);
|
|
3438
|
-
|
|
3706
|
+
const codeReviewObjectivesBound = isBoundToObjectives(codeReview, input.objectivesVersion);
|
|
3707
|
+
const securityObjectivesBound = isBoundToObjectives(security, input.objectivesVersion);
|
|
3708
|
+
if (codeReviewClean && codeReviewBound && codeReviewObjectivesBound && securityClean && securityBound && securityObjectivesBound && mechanicalChecksClean) {
|
|
3439
3709
|
return {
|
|
3440
3710
|
verdict: "pass",
|
|
3441
3711
|
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.`,
|
|
@@ -3447,11 +3717,15 @@ function checkReviewGate(input) {
|
|
|
3447
3717
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
3448
3718
|
} else if (!codeReviewBound) {
|
|
3449
3719
|
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3720
|
+
} else if (!codeReviewObjectivesBound) {
|
|
3721
|
+
problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3450
3722
|
}
|
|
3451
3723
|
if (!securityClean) {
|
|
3452
3724
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
3453
3725
|
} else if (!securityBound) {
|
|
3454
3726
|
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3727
|
+
} else if (!securityObjectivesBound) {
|
|
3728
|
+
problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3455
3729
|
}
|
|
3456
3730
|
if (!mechanicalChecksClean) {
|
|
3457
3731
|
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(", ")}`);
|
|
@@ -3575,16 +3849,33 @@ function checkG1(rows, existsFn) {
|
|
|
3575
3849
|
return { check: "G1", status: findings.length > 0 ? "fail" : "pass", findings };
|
|
3576
3850
|
}
|
|
3577
3851
|
var SCAFFOLD_PLACEHOLDER = "[undocumented — fill in why]";
|
|
3852
|
+
var NON_GATE_BIN_NAMES = NON_GATE_BINS;
|
|
3853
|
+
var TWIN_CANDIDATE_EXCEPTIONS = {
|
|
3854
|
+
"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)',
|
|
3855
|
+
"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)`,
|
|
3856
|
+
"apps/cli/src/checks/bin/check-closes-n.ts": 'Ring 1 "Closes linkage" (implementation: verify-coherence.ts)'
|
|
3857
|
+
};
|
|
3858
|
+
var NON_GATE_HOOK_SCRIPTS = [".claude/hooks/track-transcript.sh"];
|
|
3578
3859
|
function checkG2(rows, candidateFiles) {
|
|
3579
3860
|
const implementations = new Set(rows.map((r) => r.implementation).filter((p) => p !== ""));
|
|
3861
|
+
const claimedNames = new Set(rows.flatMap((r) => claimedCheckNames(r.implementation)));
|
|
3580
3862
|
const findings = [];
|
|
3581
3863
|
for (const path of candidateFiles) {
|
|
3582
|
-
if (
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3864
|
+
if (implementations.has(path))
|
|
3865
|
+
continue;
|
|
3866
|
+
if (path.startsWith(AEG_CORE_BIN_PATH_PREFIX) && NON_GATE_BIN_NAMES.includes(basenameNoExt(path)))
|
|
3867
|
+
continue;
|
|
3868
|
+
if (NON_GATE_HOOK_SCRIPTS.includes(path))
|
|
3869
|
+
continue;
|
|
3870
|
+
if (path in TWIN_CANDIDATE_EXCEPTIONS)
|
|
3871
|
+
continue;
|
|
3872
|
+
const candidateNames = claimedCheckNames(path);
|
|
3873
|
+
if (candidateNames.length > 0 && candidateNames.some((n) => claimedNames.has(n)))
|
|
3874
|
+
continue;
|
|
3875
|
+
findings.push({
|
|
3876
|
+
path,
|
|
3877
|
+
reason: `"${path}" is not named as the implementation of any row in enforcement.md's ring tables`
|
|
3878
|
+
});
|
|
3588
3879
|
}
|
|
3589
3880
|
for (const row of rows) {
|
|
3590
3881
|
const carriesPlaceholder = [row.summary, row.description, row.spec].some((cell) => cell === SCAFFOLD_PLACEHOLDER);
|
|
@@ -3596,7 +3887,7 @@ function checkG2(rows, candidateFiles) {
|
|
|
3596
3887
|
});
|
|
3597
3888
|
}
|
|
3598
3889
|
}
|
|
3599
|
-
return { check: "G2", status: findings.length > 0 ? "
|
|
3890
|
+
return { check: "G2", status: findings.length > 0 ? "fail" : "pass", findings };
|
|
3600
3891
|
}
|
|
3601
3892
|
function checkG3(ring0Rows, crossingFiles) {
|
|
3602
3893
|
const ring0Implementations = new Set(ring0Rows.map((r) => r.implementation));
|
|
@@ -3687,55 +3978,20 @@ function checkG6(rows, registeredCheckNames) {
|
|
|
3687
3978
|
}
|
|
3688
3979
|
return { check: "G6", status: findings.length > 0 ? "fail" : "pass", findings };
|
|
3689
3980
|
}
|
|
3690
|
-
// ../../packages/aeg-core/src/
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
return {
|
|
3701
|
-
verdict: "refuse",
|
|
3702
|
-
reason: `Branch \`${branch}\` has a CLOSED PR (#${prNumber}) that was never merged — pushing here would resurrect abandoned/rejected work. Open a new task branch instead.`
|
|
3703
|
-
};
|
|
3704
|
-
}
|
|
3705
|
-
if (prState === "OPEN") {
|
|
3706
|
-
return {
|
|
3707
|
-
verdict: "allow",
|
|
3708
|
-
reason: `Branch \`${branch}\` has an OPEN PR (#${prNumber}) — pushing more commits to it is normal.`
|
|
3709
|
-
};
|
|
3710
|
-
}
|
|
3711
|
-
if (prState === "NONE") {
|
|
3712
|
-
return {
|
|
3713
|
-
verdict: "allow",
|
|
3714
|
-
reason: `Branch \`${branch}\` has no PR yet — a brand-new task branch legitimately has none.`
|
|
3715
|
-
};
|
|
3716
|
-
}
|
|
3717
|
-
return {
|
|
3718
|
-
verdict: "allow",
|
|
3719
|
-
reason: `Could not determine \`${branch}\`'s PR state (forge unreachable) — failing open rather than blocking the push on a transient issue.`
|
|
3720
|
-
};
|
|
3721
|
-
}
|
|
3722
|
-
// ../../packages/aeg-core/src/baseline-capture.ts
|
|
3723
|
-
function captureBaseline(counts, capturedAt) {
|
|
3724
|
-
return counts.map((c) => ({ tool: c.tool, findingCount: c.findingCount, capturedAt }));
|
|
3981
|
+
// ../../packages/aeg-core/src/reader-resolvable-prose.ts
|
|
3982
|
+
var PRODUCT_SLUG_SCOPE = [
|
|
3983
|
+
"apps/cli/src",
|
|
3984
|
+
".github/workflows",
|
|
3985
|
+
".vinaya",
|
|
3986
|
+
"apps/cli/README.md",
|
|
3987
|
+
"packages/sources/README.md"
|
|
3988
|
+
];
|
|
3989
|
+
function isUnderOrEqual(path, prefix) {
|
|
3990
|
+
return path === prefix || path.startsWith(`${prefix}/`);
|
|
3725
3991
|
}
|
|
3726
|
-
function
|
|
3727
|
-
|
|
3728
|
-
const baselineEntry = baseline.find((b) => b.tool === c.tool);
|
|
3729
|
-
const baselineCount = baselineEntry ? baselineEntry.findingCount : c.findingCount;
|
|
3730
|
-
return { tool: c.tool, baseline: baselineCount, current: c.findingCount, delta: c.findingCount - baselineCount };
|
|
3731
|
-
});
|
|
3732
|
-
return {
|
|
3733
|
-
withinBudget: perTool.every((t) => t.delta <= 0),
|
|
3734
|
-
delta: perTool.reduce((sum, t) => sum + t.delta, 0),
|
|
3735
|
-
perTool
|
|
3736
|
-
};
|
|
3992
|
+
function isProductScopeFile(path) {
|
|
3993
|
+
return PRODUCT_SLUG_SCOPE.some((prefix) => isUnderOrEqual(path, prefix));
|
|
3737
3994
|
}
|
|
3738
|
-
// ../../packages/aeg-core/src/reader-resolvable-prose.ts
|
|
3739
3995
|
var SHIPS_PREFIX = "aeg-root/";
|
|
3740
3996
|
function shipsArchivePrefix(shipsPrefix) {
|
|
3741
3997
|
return `${shipsPrefix}tranches/completed/`;
|
|
@@ -3755,6 +4011,8 @@ function classifyProseFile(path, readerFacingPrefix, readerFacingSuffix, shipsPr
|
|
|
3755
4011
|
}
|
|
3756
4012
|
if (isSpecFile2(path) || isClaudeMdFile(path))
|
|
3757
4013
|
return "internal";
|
|
4014
|
+
if (isProductScopeFile(path))
|
|
4015
|
+
return "product";
|
|
3758
4016
|
return null;
|
|
3759
4017
|
}
|
|
3760
4018
|
var SWEPT_CLASSES = new Set(["ships", "reader-facing"]);
|
|
@@ -3767,6 +4025,9 @@ function stripNonProse(path, content) {
|
|
|
3767
4025
|
}
|
|
3768
4026
|
return content;
|
|
3769
4027
|
}
|
|
4028
|
+
function stripNonProseForProduct(path, content) {
|
|
4029
|
+
return path.endsWith(".md") ? stripNonProse(path, content) : content;
|
|
4030
|
+
}
|
|
3770
4031
|
function lineAt(content, index) {
|
|
3771
4032
|
let line = 1;
|
|
3772
4033
|
for (let i = 0;i < index; i++) {
|
|
@@ -3791,12 +4052,19 @@ function checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuff
|
|
|
3791
4052
|
{ pattern: TRANCHE_SLUG_VN_PATTERN, what: "an internal tranche slug" },
|
|
3792
4053
|
...legacyPattern ? [{ pattern: legacyPattern, what: "an internal tranche slug", group: 2 }] : []
|
|
3793
4054
|
];
|
|
4055
|
+
const productPatterns = [
|
|
4056
|
+
{ pattern: TRANCHE_SLUG_VN_PATTERN, what: "an internal tranche slug in product code" }
|
|
4057
|
+
];
|
|
3794
4058
|
for (const file of files) {
|
|
3795
4059
|
const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix, shipsPrefix);
|
|
3796
|
-
if (!cls ||
|
|
4060
|
+
if (!cls || cls === "internal")
|
|
3797
4061
|
continue;
|
|
3798
|
-
|
|
3799
|
-
|
|
4062
|
+
if (cls !== "product" && !SWEPT_CLASSES.has(cls))
|
|
4063
|
+
continue;
|
|
4064
|
+
const blocking = cls === "product";
|
|
4065
|
+
const scrubbed = blocking ? stripNonProseForProduct(file.path, file.content) : stripNonProse(file.path, file.content);
|
|
4066
|
+
const patternsToRun = blocking ? productPatterns : patterns;
|
|
4067
|
+
for (const { pattern, what, group } of patternsToRun) {
|
|
3800
4068
|
pattern.lastIndex = 0;
|
|
3801
4069
|
let match = pattern.exec(scrubbed);
|
|
3802
4070
|
while (match !== null) {
|
|
@@ -3804,7 +4072,8 @@ function checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuff
|
|
|
3804
4072
|
findings.push({
|
|
3805
4073
|
file: file.path,
|
|
3806
4074
|
line: lineAt(scrubbed, match.index),
|
|
3807
|
-
message: `references ${what} ("${cited}") a reader outside this repo's tracker cannot resolve
|
|
4075
|
+
message: `references ${what} ("${cited}") a reader outside this repo's tracker cannot resolve`,
|
|
4076
|
+
blocking
|
|
3808
4077
|
});
|
|
3809
4078
|
match = pattern.exec(scrubbed);
|
|
3810
4079
|
}
|
|
@@ -3854,7 +4123,8 @@ function checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, read
|
|
|
3854
4123
|
findings.push({
|
|
3855
4124
|
file: file.path,
|
|
3856
4125
|
line,
|
|
3857
|
-
message: `uses coined term "${term}" without defining it inline or linking the glossary
|
|
4126
|
+
message: `uses coined term "${term}" without defining it inline or linking the glossary`,
|
|
4127
|
+
blocking: false
|
|
3858
4128
|
});
|
|
3859
4129
|
}
|
|
3860
4130
|
}
|
|
@@ -3877,6 +4147,7 @@ function parseGlossaryTerms(glossaryContent) {
|
|
|
3877
4147
|
}
|
|
3878
4148
|
return terms;
|
|
3879
4149
|
}
|
|
4150
|
+
|
|
3880
4151
|
// ../../packages/aeg-core/src/quoted-command.ts
|
|
3881
4152
|
var QUOTED_COMMAND_SHIPS_PREFIX = "aeg-root/";
|
|
3882
4153
|
var QUOTED_COMMAND_SWEPT_CLASSES = new Set(["ships", "reader-facing"]);
|
|
@@ -3971,6 +4242,54 @@ function evaluateCitedQuotes(citedQuotes, citedFileContents) {
|
|
|
3971
4242
|
}
|
|
3972
4243
|
return findings;
|
|
3973
4244
|
}
|
|
4245
|
+
// ../../packages/aeg-core/src/dead-branch-push-guard.ts
|
|
4246
|
+
function checkDeadBranchPush(input) {
|
|
4247
|
+
const { branch, prState, prNumber } = input;
|
|
4248
|
+
if (prState === "MERGED") {
|
|
4249
|
+
return {
|
|
4250
|
+
verdict: "refuse",
|
|
4251
|
+
reason: `Branch \`${branch}\` already has a MERGED PR (#${prNumber}) — this branch's work is done. Push a new task branch instead of pushing onto an already-resolved one.`
|
|
4252
|
+
};
|
|
4253
|
+
}
|
|
4254
|
+
if (prState === "CLOSED") {
|
|
4255
|
+
return {
|
|
4256
|
+
verdict: "refuse",
|
|
4257
|
+
reason: `Branch \`${branch}\` has a CLOSED PR (#${prNumber}) that was never merged — pushing here would resurrect abandoned/rejected work. Open a new task branch instead.`
|
|
4258
|
+
};
|
|
4259
|
+
}
|
|
4260
|
+
if (prState === "OPEN") {
|
|
4261
|
+
return {
|
|
4262
|
+
verdict: "allow",
|
|
4263
|
+
reason: `Branch \`${branch}\` has an OPEN PR (#${prNumber}) — pushing more commits to it is normal.`
|
|
4264
|
+
};
|
|
4265
|
+
}
|
|
4266
|
+
if (prState === "NONE") {
|
|
4267
|
+
return {
|
|
4268
|
+
verdict: "allow",
|
|
4269
|
+
reason: `Branch \`${branch}\` has no PR yet — a brand-new task branch legitimately has none.`
|
|
4270
|
+
};
|
|
4271
|
+
}
|
|
4272
|
+
return {
|
|
4273
|
+
verdict: "allow",
|
|
4274
|
+
reason: `Could not determine \`${branch}\`'s PR state (forge unreachable) — failing open rather than blocking the push on a transient issue.`
|
|
4275
|
+
};
|
|
4276
|
+
}
|
|
4277
|
+
// ../../packages/aeg-core/src/baseline-capture.ts
|
|
4278
|
+
function captureBaseline(counts, capturedAt) {
|
|
4279
|
+
return counts.map((c) => ({ tool: c.tool, findingCount: c.findingCount, capturedAt }));
|
|
4280
|
+
}
|
|
4281
|
+
function compareToBaseline(current, baseline) {
|
|
4282
|
+
const perTool = current.map((c) => {
|
|
4283
|
+
const baselineEntry = baseline.find((b) => b.tool === c.tool);
|
|
4284
|
+
const baselineCount = baselineEntry ? baselineEntry.findingCount : c.findingCount;
|
|
4285
|
+
return { tool: c.tool, baseline: baselineCount, current: c.findingCount, delta: c.findingCount - baselineCount };
|
|
4286
|
+
});
|
|
4287
|
+
return {
|
|
4288
|
+
withinBudget: perTool.every((t) => t.delta <= 0),
|
|
4289
|
+
delta: perTool.reduce((sum, t) => sum + t.delta, 0),
|
|
4290
|
+
perTool
|
|
4291
|
+
};
|
|
4292
|
+
}
|
|
3974
4293
|
// ../../packages/aeg-core/src/retired-vocabulary.ts
|
|
3975
4294
|
var RETIRED_PATTERNS = [
|
|
3976
4295
|
String.raw`\bD-[0-9]{3}\b`,
|
|
@@ -4440,6 +4759,223 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
|
|
|
4440
4759
|
}
|
|
4441
4760
|
return findings;
|
|
4442
4761
|
}
|
|
4762
|
+
// ../../packages/aeg-core/src/log/schema.ts
|
|
4763
|
+
import { z } from "zod";
|
|
4764
|
+
var ROLE_VALUES = [
|
|
4765
|
+
"planner",
|
|
4766
|
+
"developer",
|
|
4767
|
+
"code-reviewer",
|
|
4768
|
+
"security",
|
|
4769
|
+
"principal",
|
|
4770
|
+
"archivist",
|
|
4771
|
+
"architect"
|
|
4772
|
+
];
|
|
4773
|
+
var RoleSchema = z.enum(ROLE_VALUES);
|
|
4774
|
+
var HOST_VALUES = ["hook", "ci", "cli", "loop"];
|
|
4775
|
+
var HostSchema = z.enum(HOST_VALUES);
|
|
4776
|
+
var RUN_ID_PATTERN = /^[A-Za-z0-9_.-]{1,128}$/;
|
|
4777
|
+
var HeaderMetaSchema = z.object({
|
|
4778
|
+
schema: z.literal(1),
|
|
4779
|
+
ts: z.string(),
|
|
4780
|
+
run_id: z.string().regex(RUN_ID_PATTERN),
|
|
4781
|
+
seq: z.number().int().nonnegative(),
|
|
4782
|
+
repo: z.string().nullable(),
|
|
4783
|
+
vinaya: z.string(),
|
|
4784
|
+
doctrine: z.string(),
|
|
4785
|
+
host: HostSchema,
|
|
4786
|
+
machine: z.string()
|
|
4787
|
+
}).strict();
|
|
4788
|
+
var SubjectSchema = z.object({
|
|
4789
|
+
issue: z.number().int().nullable(),
|
|
4790
|
+
pr: z.number().int().optional(),
|
|
4791
|
+
sha: z.string().optional(),
|
|
4792
|
+
role: z.union([RoleSchema, z.literal("unattributed")]),
|
|
4793
|
+
round: z.number().int().optional(),
|
|
4794
|
+
objectives_version: z.string().optional()
|
|
4795
|
+
}).strict();
|
|
4796
|
+
var HeaderSchema = z.object({
|
|
4797
|
+
meta: HeaderMetaSchema,
|
|
4798
|
+
subject: SubjectSchema
|
|
4799
|
+
}).strict();
|
|
4800
|
+
var envelopeTail = {
|
|
4801
|
+
duration_ms: z.number().nonnegative().optional(),
|
|
4802
|
+
payload: z.object({}).strict()
|
|
4803
|
+
};
|
|
4804
|
+
var DispatchOutcomeSchema = z.discriminatedUnion("type", [
|
|
4805
|
+
z.object({ type: z.literal("pr_opened"), pr: z.number().int(), head: z.string() }).strict(),
|
|
4806
|
+
z.object({
|
|
4807
|
+
type: z.literal("round_pushed"),
|
|
4808
|
+
pr: z.number().int(),
|
|
4809
|
+
head: z.string(),
|
|
4810
|
+
comment_id: z.number().int()
|
|
4811
|
+
}).strict(),
|
|
4812
|
+
z.object({
|
|
4813
|
+
type: z.literal("verdict"),
|
|
4814
|
+
verdict: z.enum(["APPROVE", "REQUEST CHANGES", "PASS", "FAIL"]),
|
|
4815
|
+
head: z.string(),
|
|
4816
|
+
comment_id: z.number().int(),
|
|
4817
|
+
objectives: z.array(z.object({ id: z.string(), met: z.boolean() }).strict()),
|
|
4818
|
+
findings: z.array(z.object({ id: z.string(), severity: z.string(), state: z.string().optional() }).strict())
|
|
4819
|
+
}).strict(),
|
|
4820
|
+
z.object({
|
|
4821
|
+
type: z.literal("escalation"),
|
|
4822
|
+
class: z.enum(["authority", "strategy", "product"]),
|
|
4823
|
+
comment_id: z.number().int()
|
|
4824
|
+
}).strict(),
|
|
4825
|
+
z.object({ type: z.literal("brief"), comment_id: z.number().int(), hash: z.string() }).strict(),
|
|
4826
|
+
z.object({ type: z.literal("plan"), issues: z.array(z.number().int()) }).strict(),
|
|
4827
|
+
z.object({ type: z.literal("archive"), provenance_comment_id: z.number().int() }).strict()
|
|
4828
|
+
]);
|
|
4829
|
+
var dispatchShared = {
|
|
4830
|
+
meta: HeaderMetaSchema,
|
|
4831
|
+
subject: SubjectSchema,
|
|
4832
|
+
kind: z.literal("dispatch"),
|
|
4833
|
+
...envelopeTail,
|
|
4834
|
+
target_role: RoleSchema,
|
|
4835
|
+
model: z.string(),
|
|
4836
|
+
round: z.number().int().optional(),
|
|
4837
|
+
effect_id: z.string()
|
|
4838
|
+
};
|
|
4839
|
+
var dispatchUsageField = z.object({ input: z.number().nonnegative(), output: z.number().nonnegative() }).strict().nullable();
|
|
4840
|
+
var DispatchEventSchema = z.discriminatedUnion("event", [
|
|
4841
|
+
z.object({ ...dispatchShared, event: z.literal("dispatched"), prompt_hash: z.string() }).strict(),
|
|
4842
|
+
z.object({
|
|
4843
|
+
...dispatchShared,
|
|
4844
|
+
event: z.literal("outcome_received"),
|
|
4845
|
+
outcome: DispatchOutcomeSchema,
|
|
4846
|
+
usage: dispatchUsageField
|
|
4847
|
+
}).strict(),
|
|
4848
|
+
z.object({
|
|
4849
|
+
...dispatchShared,
|
|
4850
|
+
event: z.literal("dispatch_failed"),
|
|
4851
|
+
reason: z.enum(["timeout", "crash", "refused", "unattributed_write"]),
|
|
4852
|
+
usage: dispatchUsageField
|
|
4853
|
+
}).strict()
|
|
4854
|
+
]);
|
|
4855
|
+
var loopShared = {
|
|
4856
|
+
meta: HeaderMetaSchema,
|
|
4857
|
+
subject: SubjectSchema,
|
|
4858
|
+
kind: z.literal("dev_review_loop"),
|
|
4859
|
+
...envelopeTail,
|
|
4860
|
+
loop_id: z.string()
|
|
4861
|
+
};
|
|
4862
|
+
var DevReviewLoopEventSchema = z.discriminatedUnion("event", [
|
|
4863
|
+
z.object({
|
|
4864
|
+
...loopShared,
|
|
4865
|
+
event: z.literal("loop_started"),
|
|
4866
|
+
task: z.number().int(),
|
|
4867
|
+
policy: z.object({
|
|
4868
|
+
max_rounds: z.number().int().nonnegative(),
|
|
4869
|
+
reviewers: z.array(RoleSchema),
|
|
4870
|
+
models: z.record(RoleSchema, z.string())
|
|
4871
|
+
}).strict()
|
|
4872
|
+
}).strict(),
|
|
4873
|
+
z.object({ ...loopShared, event: z.literal("round_started"), round: z.number().int(), base_head: z.string() }).strict(),
|
|
4874
|
+
z.object({
|
|
4875
|
+
...loopShared,
|
|
4876
|
+
event: z.literal("gate_result_read"),
|
|
4877
|
+
round: z.number().int(),
|
|
4878
|
+
head: z.string(),
|
|
4879
|
+
green: z.boolean()
|
|
4880
|
+
}).strict(),
|
|
4881
|
+
z.object({
|
|
4882
|
+
...loopShared,
|
|
4883
|
+
event: z.literal("verdicts_read"),
|
|
4884
|
+
round: z.number().int(),
|
|
4885
|
+
head: z.string(),
|
|
4886
|
+
all_approve: z.boolean(),
|
|
4887
|
+
blockers: z.number().int().nonnegative()
|
|
4888
|
+
}).strict(),
|
|
4889
|
+
z.object({
|
|
4890
|
+
...loopShared,
|
|
4891
|
+
event: z.literal("findings_compared"),
|
|
4892
|
+
round: z.number().int(),
|
|
4893
|
+
open: z.array(z.string()),
|
|
4894
|
+
resolved: z.array(z.string()),
|
|
4895
|
+
new: z.array(z.string()),
|
|
4896
|
+
recurring: z.array(z.string())
|
|
4897
|
+
}).strict(),
|
|
4898
|
+
z.object({
|
|
4899
|
+
...loopShared,
|
|
4900
|
+
event: z.literal("stop_condition_met"),
|
|
4901
|
+
round: z.number().int(),
|
|
4902
|
+
condition: z.enum([
|
|
4903
|
+
"green",
|
|
4904
|
+
"max_rounds",
|
|
4905
|
+
"no_progress",
|
|
4906
|
+
"escalated",
|
|
4907
|
+
"principal_stop",
|
|
4908
|
+
"confidence",
|
|
4909
|
+
"reappearance"
|
|
4910
|
+
])
|
|
4911
|
+
}).strict(),
|
|
4912
|
+
z.object({
|
|
4913
|
+
...loopShared,
|
|
4914
|
+
event: z.literal("paused"),
|
|
4915
|
+
round: z.number().int(),
|
|
4916
|
+
reason: z.enum(["escalation", "principal_item", "refreeze_needed"])
|
|
4917
|
+
}).strict(),
|
|
4918
|
+
z.object({
|
|
4919
|
+
...loopShared,
|
|
4920
|
+
event: z.literal("resumed"),
|
|
4921
|
+
round: z.number().int(),
|
|
4922
|
+
by: z.literal("principal")
|
|
4923
|
+
}).strict(),
|
|
4924
|
+
z.object({
|
|
4925
|
+
...loopShared,
|
|
4926
|
+
event: z.literal("round_ended"),
|
|
4927
|
+
round: z.number().int(),
|
|
4928
|
+
base_head: z.string(),
|
|
4929
|
+
head: z.string(),
|
|
4930
|
+
files_changed: z.number().int().nonnegative(),
|
|
4931
|
+
insertions: z.number().int().nonnegative(),
|
|
4932
|
+
deletions: z.number().int().nonnegative(),
|
|
4933
|
+
wall_ms: z.number().nonnegative(),
|
|
4934
|
+
outcome: z.enum(["green", "changes_requested", "escalated"])
|
|
4935
|
+
}).strict(),
|
|
4936
|
+
z.object({
|
|
4937
|
+
...loopShared,
|
|
4938
|
+
event: z.literal("journal_finalized"),
|
|
4939
|
+
rounds: z.number().int().nonnegative(),
|
|
4940
|
+
total_wall_ms: z.number().nonnegative(),
|
|
4941
|
+
time_to_green_ms: z.number().nonnegative().nullable(),
|
|
4942
|
+
files_changed_total: z.number().int().nonnegative(),
|
|
4943
|
+
final_head: z.string(),
|
|
4944
|
+
result: z.enum(["merged_ready", "stopped"])
|
|
4945
|
+
}).strict()
|
|
4946
|
+
]);
|
|
4947
|
+
var ForgeOpSchema = z.enum([
|
|
4948
|
+
"pr.create",
|
|
4949
|
+
"pr.comment",
|
|
4950
|
+
"pr.body.replace",
|
|
4951
|
+
"pr.refreeze",
|
|
4952
|
+
"issue.create",
|
|
4953
|
+
"issue.edit",
|
|
4954
|
+
"issue.comment",
|
|
4955
|
+
"milestone.create",
|
|
4956
|
+
"milestone.edit",
|
|
4957
|
+
"milestone.close",
|
|
4958
|
+
"label.add",
|
|
4959
|
+
"label.remove"
|
|
4960
|
+
]);
|
|
4961
|
+
var ForgeWriteTargetSchema = z.object({
|
|
4962
|
+
issue: z.number().int().optional(),
|
|
4963
|
+
pr: z.number().int().optional()
|
|
4964
|
+
}).strict();
|
|
4965
|
+
var forgeWriteShared = {
|
|
4966
|
+
meta: HeaderMetaSchema,
|
|
4967
|
+
subject: SubjectSchema,
|
|
4968
|
+
kind: z.literal("forge_write"),
|
|
4969
|
+
...envelopeTail,
|
|
4970
|
+
op: ForgeOpSchema,
|
|
4971
|
+
target: ForgeWriteTargetSchema
|
|
4972
|
+
};
|
|
4973
|
+
var ForgeWriteEventSchema = z.discriminatedUnion("event", [
|
|
4974
|
+
z.object({ ...forgeWriteShared, event: z.literal("validated") }).strict(),
|
|
4975
|
+
z.object({ ...forgeWriteShared, event: z.literal("refused"), reason: z.string() }).strict(),
|
|
4976
|
+
z.object({ ...forgeWriteShared, event: z.literal("written"), comment_ids: z.array(z.string()) }).strict()
|
|
4977
|
+
]);
|
|
4978
|
+
var LogEventSchema = z.union([DispatchEventSchema, DevReviewLoopEventSchema, ForgeWriteEventSchema]);
|
|
4443
4979
|
// src/checks/contract.ts
|
|
4444
4980
|
var CHECK_SCHEMA_VERSION = 1;
|
|
4445
4981
|
function emitCheckError(error) {
|
|
@@ -4486,7 +5022,9 @@ var REGISTRY = [
|
|
|
4486
5022
|
env: {
|
|
4487
5023
|
PR_BODY: { optional: true },
|
|
4488
5024
|
BRANCH: { optional: true },
|
|
4489
|
-
PR_NUMBER: { optional: true }
|
|
5025
|
+
PR_NUMBER: { optional: true },
|
|
5026
|
+
GITHUB_TOKEN: { optional: true },
|
|
5027
|
+
GH_TOKEN: { optional: true }
|
|
4490
5028
|
}
|
|
4491
5029
|
},
|
|
4492
5030
|
0
|
|
@@ -4536,6 +5074,18 @@ var REGISTRY = [
|
|
|
4536
5074
|
},
|
|
4537
5075
|
0
|
|
4538
5076
|
],
|
|
5077
|
+
[
|
|
5078
|
+
{
|
|
5079
|
+
name: "pr-premise-reassert",
|
|
5080
|
+
run: bin("check-pr-premise-reassert"),
|
|
5081
|
+
scope: "diff",
|
|
5082
|
+
timeoutMs: 15000,
|
|
5083
|
+
env: {
|
|
5084
|
+
PR_BODY: { optional: true }
|
|
5085
|
+
}
|
|
5086
|
+
},
|
|
5087
|
+
0
|
|
5088
|
+
],
|
|
4539
5089
|
[
|
|
4540
5090
|
{
|
|
4541
5091
|
name: "dispatch-readiness",
|
|
@@ -4584,6 +5134,22 @@ var REGISTRY = [
|
|
|
4584
5134
|
},
|
|
4585
5135
|
0
|
|
4586
5136
|
],
|
|
5137
|
+
[
|
|
5138
|
+
{
|
|
5139
|
+
name: "surface-scope",
|
|
5140
|
+
run: bin("check-surface-scope"),
|
|
5141
|
+
scope: "diff",
|
|
5142
|
+
timeoutMs: 15000,
|
|
5143
|
+
env: {
|
|
5144
|
+
AEG_REPO: { optional: true },
|
|
5145
|
+
BASE_SHA: { optional: true },
|
|
5146
|
+
BRANCH: { optional: true },
|
|
5147
|
+
GITHUB_TOKEN: { optional: true },
|
|
5148
|
+
GH_TOKEN: { optional: true }
|
|
5149
|
+
}
|
|
5150
|
+
},
|
|
5151
|
+
0
|
|
5152
|
+
],
|
|
4587
5153
|
[
|
|
4588
5154
|
{
|
|
4589
5155
|
name: "test-plan",
|
|
@@ -4625,6 +5191,7 @@ var REGISTRY = [
|
|
|
4625
5191
|
requiresOpenPr: true,
|
|
4626
5192
|
env: {
|
|
4627
5193
|
PR_BODY: { optional: true },
|
|
5194
|
+
BRANCH: { optional: true },
|
|
4628
5195
|
CLAUDE_PROJECT_DIR: { optional: true },
|
|
4629
5196
|
CLAUDE_CODE_SESSION_ID: { optional: true }
|
|
4630
5197
|
}
|
|
@@ -4774,7 +5341,14 @@ var REGISTRY = [
|
|
|
4774
5341
|
scope: "full",
|
|
4775
5342
|
timeoutMs: 30000,
|
|
4776
5343
|
env: {},
|
|
4777
|
-
include: [
|
|
5344
|
+
include: [
|
|
5345
|
+
"aeg-root/**/*.md",
|
|
5346
|
+
"apps/cli/src/**",
|
|
5347
|
+
".github/workflows/**",
|
|
5348
|
+
".vinaya/**",
|
|
5349
|
+
"apps/cli/README.md",
|
|
5350
|
+
"packages/sources/README.md"
|
|
5351
|
+
]
|
|
4778
5352
|
},
|
|
4779
5353
|
0
|
|
4780
5354
|
],
|
|
@@ -5040,13 +5614,13 @@ async function main() {
|
|
|
5040
5614
|
const g3 = checkG3(ring0Rows, crossingFiles);
|
|
5041
5615
|
const g5 = checkG5(roles, contracts);
|
|
5042
5616
|
const g6 = checkG6(rows, new Set(coreCheckRegistry().map((s) => s.name)));
|
|
5043
|
-
emitResult(g2, false);
|
|
5044
5617
|
emitResult(g1, true);
|
|
5618
|
+
emitResult(g2, true);
|
|
5045
5619
|
emitResult(g3, true);
|
|
5046
5620
|
emitResult(g4Result, true);
|
|
5047
5621
|
emitResult(g5, true);
|
|
5048
5622
|
emitResult(g6, true);
|
|
5049
|
-
const blockingFailed = [g1, g3, g4Result, g5, g6].some((r) => r.status === "fail");
|
|
5623
|
+
const blockingFailed = [g1, g2, g3, g4Result, g5, g6].some((r) => r.status === "fail");
|
|
5050
5624
|
process.exit(blockingFailed ? 1 : 0);
|
|
5051
5625
|
}
|
|
5052
5626
|
main();
|