@attalabs/vinaya 0.24.0 → 0.25.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 +25 -2
- package/aeg-root/contracts/developer-reviewer.md +3 -0
- package/aeg-root/contracts/planner-brief.md +13 -0
- package/aeg-root/enforcement.md +11 -7
- package/aeg-root/process.md +4 -4
- package/aeg-root/roles/brief-author.md +4 -2
- package/aeg-root/roles/developer.md +20 -14
- package/aeg-root/roles/planner.md +14 -0
- package/aeg-root/roles/principal.md +3 -0
- package/aeg-root/roles/reviewer.md +23 -7
- package/aeg-root/roles/security.md +21 -6
- package/aeg-root/skills/brief-authoring/SKILL.md +2 -2
- package/aeg-root/state-machine.md +2 -2
- package/aeg-root/templates/brief-template.md +20 -4
- package/aeg-root/templates/issue-rationale-template.md +34 -1
- package/aeg-root/templates/pr-report-template.md +5 -13
- package/dist/checks/bin/check-body-bare-digits.js +572 -127
- package/dist/checks/bin/check-branch-topology.js +629 -73
- package/dist/checks/bin/check-brief-shape.js +1675 -70
- package/dist/checks/bin/check-changeset-coverage.js +584 -127
- package/dist/checks/bin/check-closes-n.js +629 -73
- package/dist/checks/bin/check-coherence.js +696 -134
- package/dist/checks/bin/check-dead-branch-push.js +505 -66
- package/dist/checks/bin/check-dispatch-readiness.js +696 -134
- package/dist/checks/bin/check-doc-coverage-push.js +572 -127
- package/dist/checks/bin/check-doc-coverage.js +572 -127
- package/dist/checks/bin/check-doctrine-no-procedures.js +572 -127
- package/dist/checks/bin/check-doctrine-portability.js +572 -127
- package/dist/checks/bin/check-evidence-fresh.js +572 -127
- package/dist/checks/bin/check-exec-bits.js +572 -127
- package/dist/checks/bin/check-first-push-dispatch.js +696 -134
- package/dist/checks/bin/check-issue-assignment.js +629 -73
- package/dist/checks/bin/check-main-branch-refusal.js +505 -66
- package/dist/checks/bin/check-no-disk-state.js +505 -66
- package/dist/checks/bin/check-pr-report-density.js +505 -66
- package/dist/checks/bin/check-quoted-command.js +572 -127
- package/dist/checks/bin/check-reader-resolvable-prose.js +610 -133
- package/dist/checks/bin/check-registry-gates.js +517 -69
- package/dist/checks/bin/check-retired-vocabulary.js +572 -127
- package/dist/checks/bin/check-review-gate.js +646 -130
- package/dist/checks/bin/check-single-plan-pr.js +505 -66
- package/dist/checks/bin/check-test-plan.js +505 -66
- package/dist/checks/bin/check-token-collection-wired.js +505 -66
- package/dist/checks/bin/check-token-report.js +505 -66
- package/dist/checks/bin/check-workspace-escape.js +582 -129
- package/dist/index.js +7740 -4283
- package/package.json +2 -2
- 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/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
|
@@ -1935,13 +1935,18 @@ function parseInlineFieldList(section) {
|
|
|
1935
1935
|
}
|
|
1936
1936
|
// ../../packages/aeg-core/src/verdict-extraction.ts
|
|
1937
1937
|
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
1938
|
-
|
|
1938
|
+
var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1939
|
+
function firstFiveLines(comment) {
|
|
1939
1940
|
return comment.split(`
|
|
1940
|
-
`).slice(0,
|
|
1941
|
+
`).slice(0, 5).join(`
|
|
1941
1942
|
`);
|
|
1942
1943
|
}
|
|
1943
1944
|
function extractHeadSha(comment) {
|
|
1944
|
-
const m =
|
|
1945
|
+
const m = firstFiveLines(comment).match(HEAD_SHA_PATTERN);
|
|
1946
|
+
return m ? m[1].toLowerCase() : null;
|
|
1947
|
+
}
|
|
1948
|
+
function extractObjectivesVersion(comment) {
|
|
1949
|
+
const m = firstFiveLines(comment).match(OBJECTIVES_VERSION_PATTERN);
|
|
1945
1950
|
return m ? m[1].toLowerCase() : null;
|
|
1946
1951
|
}
|
|
1947
1952
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
@@ -1950,21 +1955,24 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1950
1955
|
return {
|
|
1951
1956
|
value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
|
|
1952
1957
|
headSha: null,
|
|
1958
|
+
objectivesVersion: null,
|
|
1953
1959
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
1954
1960
|
};
|
|
1955
1961
|
}
|
|
1956
1962
|
const latest = candidates[candidates.length - 1];
|
|
1957
|
-
const m =
|
|
1963
|
+
const m = firstFiveLines(latest).match(valuePattern);
|
|
1958
1964
|
if (!m) {
|
|
1959
1965
|
return {
|
|
1960
|
-
value: `the most recent ${missingLabel} comment's VERDICT line is not within its first
|
|
1966
|
+
value: `the most recent ${missingLabel} comment's VERDICT line is not within its first five lines — DANGLING, see below`,
|
|
1961
1967
|
headSha: null,
|
|
1962
|
-
|
|
1968
|
+
objectivesVersion: null,
|
|
1969
|
+
danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
|
|
1963
1970
|
};
|
|
1964
1971
|
}
|
|
1965
1972
|
return {
|
|
1966
1973
|
value: m[1].toUpperCase().replace(/[_-]/g, " "),
|
|
1967
1974
|
headSha: extractHeadSha(latest),
|
|
1975
|
+
objectivesVersion: extractObjectivesVersion(latest),
|
|
1968
1976
|
danglingNote: null
|
|
1969
1977
|
};
|
|
1970
1978
|
}
|
|
@@ -2321,8 +2329,105 @@ function overrideActive(opts) {
|
|
|
2321
2329
|
return true;
|
|
2322
2330
|
return false;
|
|
2323
2331
|
}
|
|
2324
|
-
// ../../packages/aeg-core/src/
|
|
2332
|
+
// ../../packages/aeg-core/src/objectives.ts
|
|
2325
2333
|
import { createHash as createHash2 } from "node:crypto";
|
|
2334
|
+
var HEADING_RE = /^##[ \t]*Objectives[ \t]*$/im;
|
|
2335
|
+
var NEXT_HEADING_RE = /^##[ \t]/m;
|
|
2336
|
+
var OBJECTIVE_LINE_RE = /^O(\d+)\.[ \t]*(.*)$/;
|
|
2337
|
+
function hasBacktickedPath(text) {
|
|
2338
|
+
let i = 0;
|
|
2339
|
+
while (i < text.length) {
|
|
2340
|
+
const start = text.indexOf("`", i);
|
|
2341
|
+
if (start === -1)
|
|
2342
|
+
return false;
|
|
2343
|
+
const end = text.indexOf("`", start + 1);
|
|
2344
|
+
if (end === -1)
|
|
2345
|
+
return false;
|
|
2346
|
+
if (text.slice(start + 1, end).includes("/"))
|
|
2347
|
+
return true;
|
|
2348
|
+
i = end + 1;
|
|
2349
|
+
}
|
|
2350
|
+
return false;
|
|
2351
|
+
}
|
|
2352
|
+
var MIN_WORDS_OUTSIDE_BACKTICKS = 3;
|
|
2353
|
+
function stripObjectiveBackticks(text) {
|
|
2354
|
+
return text.replace(/`[^`\n]*`/g, " ");
|
|
2355
|
+
}
|
|
2356
|
+
function wordCount(text) {
|
|
2357
|
+
return text.split(/\s+/).filter((w) => /[a-z]/i.test(w)).length;
|
|
2358
|
+
}
|
|
2359
|
+
function objectivesSectionText(body) {
|
|
2360
|
+
const heading = HEADING_RE.exec(body);
|
|
2361
|
+
if (!heading)
|
|
2362
|
+
return null;
|
|
2363
|
+
const afterHeading = body.slice(heading.index + heading[0].length);
|
|
2364
|
+
const next = NEXT_HEADING_RE.exec(afterHeading);
|
|
2365
|
+
return next ? afterHeading.slice(0, next.index) : afterHeading;
|
|
2366
|
+
}
|
|
2367
|
+
function hasObjectivesHeading(body) {
|
|
2368
|
+
return HEADING_RE.test(body);
|
|
2369
|
+
}
|
|
2370
|
+
function objectivesOf(body) {
|
|
2371
|
+
const section = objectivesSectionText(body);
|
|
2372
|
+
if (section === null) {
|
|
2373
|
+
return { ok: false, errors: ["no `## Objectives` heading found in the body."] };
|
|
2374
|
+
}
|
|
2375
|
+
const lines = section.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
2376
|
+
if (lines.length === 0) {
|
|
2377
|
+
return { ok: false, errors: ["the `## Objectives` section has no `O<n>.` lines."] };
|
|
2378
|
+
}
|
|
2379
|
+
const objectives = [];
|
|
2380
|
+
const errors = [];
|
|
2381
|
+
for (const line of lines) {
|
|
2382
|
+
const m = OBJECTIVE_LINE_RE.exec(line);
|
|
2383
|
+
if (!m) {
|
|
2384
|
+
errors.push(`"${line}" is not a well-formed objective line — expected \`O<n>. <sentence>\`, e.g. \`O1. …\`.`);
|
|
2385
|
+
continue;
|
|
2386
|
+
}
|
|
2387
|
+
const n = m[1];
|
|
2388
|
+
const text = m[2].trim();
|
|
2389
|
+
if (text.length === 0) {
|
|
2390
|
+
errors.push(`O${n} has no sentence — every objective is one observable sentence.`);
|
|
2391
|
+
continue;
|
|
2392
|
+
}
|
|
2393
|
+
if (hasBacktickedPath(text) && wordCount(stripObjectiveBackticks(text)) < MIN_WORDS_OUTSIDE_BACKTICKS) {
|
|
2394
|
+
errors.push(`O${n} is little more than a file path — an objective states an observable outcome, never a bare path (the Brief Author maps it to files).`);
|
|
2395
|
+
continue;
|
|
2396
|
+
}
|
|
2397
|
+
objectives.push({ id: `O${n}`, text });
|
|
2398
|
+
}
|
|
2399
|
+
for (let i = 0;i < objectives.length; i++) {
|
|
2400
|
+
const expected = i + 1;
|
|
2401
|
+
const actual = Number.parseInt(objectives[i].id.slice(1), 10);
|
|
2402
|
+
if (actual !== expected) {
|
|
2403
|
+
errors.push(`numbering is not contiguous from O1 — expected O${expected}, found ${objectives[i].id}.`);
|
|
2404
|
+
break;
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
if (errors.length > 0)
|
|
2408
|
+
return { ok: false, errors };
|
|
2409
|
+
return { ok: true, objectives };
|
|
2410
|
+
}
|
|
2411
|
+
function normalizeText(text) {
|
|
2412
|
+
return text.trim().replace(/\s+/g, " ");
|
|
2413
|
+
}
|
|
2414
|
+
function objectivesVersion(objectives) {
|
|
2415
|
+
const normalized = objectives.map((o) => `${o.id} ${normalizeText(o.text)}`).join(`
|
|
2416
|
+
`);
|
|
2417
|
+
return createHash2("sha256").update(normalized).digest("hex");
|
|
2418
|
+
}
|
|
2419
|
+
function isIssueNotFoundError(err) {
|
|
2420
|
+
const stderr = err?.stderr;
|
|
2421
|
+
const haystack = [
|
|
2422
|
+
err?.message ?? "",
|
|
2423
|
+
typeof stderr === "string" ? stderr : stderr?.toString() ?? ""
|
|
2424
|
+
].join(`
|
|
2425
|
+
`);
|
|
2426
|
+
return /could not resolve to an (?:issue|pull request)|\b404\b|not found/i.test(haystack);
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
// ../../packages/aeg-core/src/premise-check.ts
|
|
2430
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
2326
2431
|
var ASSERTION_KINDS = new Set(["contains", "absent", "sha256"]);
|
|
2327
2432
|
function isPremiseHeader(line) {
|
|
2328
2433
|
const stripped = line.replace(/[*#]/g, "").trim();
|
|
@@ -2393,7 +2498,7 @@ function checkPremises(assertions, fileReader) {
|
|
|
2393
2498
|
} else if (a.kind === "absent" && content.includes(a.value)) {
|
|
2394
2499
|
failures.push(`premise-check: ${a.path} now contains "${a.value}", but the brief pinned it absent — the premise has moved; re-dig before proceeding.`);
|
|
2395
2500
|
} else if (a.kind === "sha256") {
|
|
2396
|
-
const actual =
|
|
2501
|
+
const actual = createHash3("sha256").update(content).digest("hex");
|
|
2397
2502
|
if (actual !== a.value.toLowerCase()) {
|
|
2398
2503
|
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.`);
|
|
2399
2504
|
}
|
|
@@ -2754,8 +2859,55 @@ function checkDefeatCases(prBody) {
|
|
|
2754
2859
|
]
|
|
2755
2860
|
};
|
|
2756
2861
|
}
|
|
2862
|
+
var PART_CITATION_RE = /Part\s+\d+\s*\(([^)]*)\)/gi;
|
|
2863
|
+
var OBJECTIVE_REF_RE = /O(\d+)/g;
|
|
2864
|
+
function citedObjectiveIds(section6) {
|
|
2865
|
+
const ids = new Set;
|
|
2866
|
+
for (const m of section6.matchAll(PART_CITATION_RE)) {
|
|
2867
|
+
for (const r of m[1].matchAll(OBJECTIVE_REF_RE))
|
|
2868
|
+
ids.add(Number.parseInt(r[1], 10));
|
|
2869
|
+
}
|
|
2870
|
+
return ids;
|
|
2871
|
+
}
|
|
2872
|
+
function checkObjectivesCopy(prBody, issueObjectives) {
|
|
2873
|
+
const parsed = objectivesOf(prBody);
|
|
2874
|
+
if (!parsed.ok) {
|
|
2875
|
+
return { status: "fail", errors: parsed.errors.map((e) => `brief-validation objectives copy: ${e}`) };
|
|
2876
|
+
}
|
|
2877
|
+
if (objectivesVersion(parsed.objectives) !== objectivesVersion(issueObjectives)) {
|
|
2878
|
+
return {
|
|
2879
|
+
status: "fail",
|
|
2880
|
+
errors: [
|
|
2881
|
+
"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)."
|
|
2882
|
+
]
|
|
2883
|
+
};
|
|
2884
|
+
}
|
|
2885
|
+
return { status: "pass", errors: [] };
|
|
2886
|
+
}
|
|
2887
|
+
function checkObjectivesCoverage(prBody) {
|
|
2888
|
+
const parsed = objectivesOf(prBody);
|
|
2889
|
+
if (!parsed.ok) {
|
|
2890
|
+
return { status: "fail", errors: parsed.errors.map((e) => `brief-validation objectives coverage: ${e}`) };
|
|
2891
|
+
}
|
|
2892
|
+
const declaredIds = new Set(parsed.objectives.map((o) => Number.parseInt(o.id.slice(1), 10)));
|
|
2893
|
+
const section6 = extractNumberedSection(prBody, 6) ?? "";
|
|
2894
|
+
const cited = citedObjectiveIds(section6);
|
|
2895
|
+
const maxDeclared = Math.max(...declaredIds, 0);
|
|
2896
|
+
const errors = [];
|
|
2897
|
+
for (const id of declaredIds) {
|
|
2898
|
+
if (!cited.has(id)) {
|
|
2899
|
+
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.`);
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2902
|
+
for (const id of cited) {
|
|
2903
|
+
if (!declaredIds.has(id)) {
|
|
2904
|
+
errors.push(`brief-validation objectives coverage: a Part in §6 cites O${id}, but the Objectives section ends at O${maxDeclared}.`);
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
return errors.length === 0 ? { status: "pass", errors: [] } : { status: "fail", errors };
|
|
2908
|
+
}
|
|
2757
2909
|
function checkBriefSections(prBody, readTier, options = {}) {
|
|
2758
|
-
const { requireClosesN = true, consumersOf = () => [] } = options;
|
|
2910
|
+
const { requireClosesN = true, consumersOf = () => [], issueObjectives } = options;
|
|
2759
2911
|
const results = [
|
|
2760
2912
|
checkTierField(prBody, readTier),
|
|
2761
2913
|
checkTestPlan(prBody),
|
|
@@ -2773,6 +2925,7 @@ function checkBriefSections(prBody, readTier, options = {}) {
|
|
|
2773
2925
|
checkCommandsCarryOutput(prBody),
|
|
2774
2926
|
checkConsumerTests(prBody, consumersOf),
|
|
2775
2927
|
checkDefeatCases(prBody),
|
|
2928
|
+
...issueObjectives !== undefined ? [checkObjectivesCopy(prBody, issueObjectives), checkObjectivesCoverage(prBody)] : [],
|
|
2776
2929
|
...requireClosesN ? [checkClosesN(prBody)] : []
|
|
2777
2930
|
];
|
|
2778
2931
|
return { errors: results.flatMap((r) => r.errors) };
|
|
@@ -2971,14 +3124,14 @@ function checkSectionDensity(prBody, heading) {
|
|
|
2971
3124
|
]
|
|
2972
3125
|
};
|
|
2973
3126
|
}
|
|
2974
|
-
function
|
|
2975
|
-
return checkSectionDensity(prBody, "
|
|
3127
|
+
function checkDecisionsDensity(prBody) {
|
|
3128
|
+
return checkSectionDensity(prBody, "Decisions");
|
|
2976
3129
|
}
|
|
2977
3130
|
function checkScopeDensity(prBody) {
|
|
2978
3131
|
return checkSectionDensity(prBody, "Scope");
|
|
2979
3132
|
}
|
|
2980
3133
|
function checkPrReportDensity(prBody) {
|
|
2981
|
-
const results = [
|
|
3134
|
+
const results = [checkDecisionsDensity(prBody), checkScopeDensity(prBody)];
|
|
2982
3135
|
return { errors: results.flatMap((r) => r.errors) };
|
|
2983
3136
|
}
|
|
2984
3137
|
// ../../packages/aeg-core/src/issue-validation.ts
|
|
@@ -3010,6 +3163,15 @@ function checkIssueRationale(body) {
|
|
|
3010
3163
|
}
|
|
3011
3164
|
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3012
3165
|
}
|
|
3166
|
+
var OBJECTIVES_SINCE_ISSUE = 404;
|
|
3167
|
+
function checkIssueObjectives(body, issueNumber) {
|
|
3168
|
+
if (issueNumber !== null && issueNumber < OBJECTIVES_SINCE_ISSUE)
|
|
3169
|
+
return { status: "pass", errors: [] };
|
|
3170
|
+
const result = objectivesOf(body);
|
|
3171
|
+
if (result.ok)
|
|
3172
|
+
return { status: "pass", errors: [] };
|
|
3173
|
+
return { status: "fail", errors: result.errors.map((e) => `issue-validation objectives: ${e}`) };
|
|
3174
|
+
}
|
|
3013
3175
|
function isTaskIssueLabelSet(labels) {
|
|
3014
3176
|
return hasLabel("tranche", labels);
|
|
3015
3177
|
}
|
|
@@ -3271,7 +3433,8 @@ function checkR1(issuesBySlug, grandfatheredIssues, registeredNames = []) {
|
|
|
3271
3433
|
continue;
|
|
3272
3434
|
const errors = [
|
|
3273
3435
|
...checkIssueRationale(issue.body).errors,
|
|
3274
|
-
...checkProjectsRegistered(issue.body, issue.labels, registeredNames).errors
|
|
3436
|
+
...checkProjectsRegistered(issue.body, issue.labels, registeredNames).errors,
|
|
3437
|
+
...checkIssueObjectives(issue.body, issue.number).errors
|
|
3275
3438
|
];
|
|
3276
3439
|
if (errors.length === 0)
|
|
3277
3440
|
continue;
|
|
@@ -3386,6 +3549,27 @@ function checkClosesN2(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
|
3386
3549
|
}
|
|
3387
3550
|
return { ok: true, expectedIssue };
|
|
3388
3551
|
}
|
|
3552
|
+
// ../../packages/aeg-core/src/archive-task.ts
|
|
3553
|
+
function closesRefs(text) {
|
|
3554
|
+
return [...new Set([...text.matchAll(/Closes #(\d+)/gi)].map((m) => Number(m[1])))];
|
|
3555
|
+
}
|
|
3556
|
+
function extractIssue(body) {
|
|
3557
|
+
const stripped = stripCode(body);
|
|
3558
|
+
const bodyNums = closesRefs(stripped);
|
|
3559
|
+
const anchored = anchoredRegion(stripped, "CLOSES");
|
|
3560
|
+
if (anchored !== null) {
|
|
3561
|
+
const anchorNums = closesRefs(anchored);
|
|
3562
|
+
if (anchorNums.length === 0)
|
|
3563
|
+
return { issue: null, extraIssues: bodyNums, outsideHeader: false };
|
|
3564
|
+
const issue2 = anchorNums[0];
|
|
3565
|
+
return { issue: issue2, extraIssues: bodyNums.filter((n) => n !== issue2), outsideHeader: false };
|
|
3566
|
+
}
|
|
3567
|
+
const headerNums = closesRefs(headerRegion(stripped));
|
|
3568
|
+
if (bodyNums.length === 0)
|
|
3569
|
+
return { issue: null, extraIssues: [], outsideHeader: false };
|
|
3570
|
+
const issue = headerNums.length > 0 ? headerNums[0] : bodyNums[0];
|
|
3571
|
+
return { issue, extraIssues: bodyNums.filter((n) => n !== issue), outsideHeader: headerNums.length === 0 };
|
|
3572
|
+
}
|
|
3389
3573
|
// ../../packages/aeg-core/src/review-gate.ts
|
|
3390
3574
|
function isBoundToHead(extraction, headSha) {
|
|
3391
3575
|
if (!extraction.headSha)
|
|
@@ -3404,6 +3588,11 @@ function isBoundByPatchIdentity(extraction, headSha, patchIdOf) {
|
|
|
3404
3588
|
function isBoundToPatch(extraction, headSha, patchIdOf) {
|
|
3405
3589
|
return isBoundToHead(extraction, headSha) || isBoundByPatchIdentity(extraction, headSha, patchIdOf);
|
|
3406
3590
|
}
|
|
3591
|
+
function isBoundToObjectives(extraction, currentVersion) {
|
|
3592
|
+
if (currentVersion === null)
|
|
3593
|
+
return true;
|
|
3594
|
+
return extraction.objectivesVersion === currentVersion;
|
|
3595
|
+
}
|
|
3407
3596
|
function checkReviewGate(input) {
|
|
3408
3597
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
3409
3598
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -3430,7 +3619,9 @@ function checkReviewGate(input) {
|
|
|
3430
3619
|
const securityClean = security.value === "PASS";
|
|
3431
3620
|
const codeReviewBound = isBoundToPatch(codeReview, input.headSha, input.patchIdOf);
|
|
3432
3621
|
const securityBound = isBoundToPatch(security, input.headSha, input.patchIdOf);
|
|
3433
|
-
|
|
3622
|
+
const codeReviewObjectivesBound = isBoundToObjectives(codeReview, input.objectivesVersion);
|
|
3623
|
+
const securityObjectivesBound = isBoundToObjectives(security, input.objectivesVersion);
|
|
3624
|
+
if (codeReviewClean && codeReviewBound && codeReviewObjectivesBound && securityClean && securityBound && securityObjectivesBound && mechanicalChecksClean) {
|
|
3434
3625
|
return {
|
|
3435
3626
|
verdict: "pass",
|
|
3436
3627
|
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.`,
|
|
@@ -3442,11 +3633,15 @@ function checkReviewGate(input) {
|
|
|
3442
3633
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
3443
3634
|
} else if (!codeReviewBound) {
|
|
3444
3635
|
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3636
|
+
} else if (!codeReviewObjectivesBound) {
|
|
3637
|
+
problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3445
3638
|
}
|
|
3446
3639
|
if (!securityClean) {
|
|
3447
3640
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
3448
3641
|
} else if (!securityBound) {
|
|
3449
3642
|
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3643
|
+
} else if (!securityObjectivesBound) {
|
|
3644
|
+
problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3450
3645
|
}
|
|
3451
3646
|
if (!mechanicalChecksClean) {
|
|
3452
3647
|
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(", ")}`);
|
|
@@ -3682,55 +3877,20 @@ function checkG6(rows, registeredCheckNames) {
|
|
|
3682
3877
|
}
|
|
3683
3878
|
return { check: "G6", status: findings.length > 0 ? "fail" : "pass", findings };
|
|
3684
3879
|
}
|
|
3685
|
-
// ../../packages/aeg-core/src/
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
return {
|
|
3696
|
-
verdict: "refuse",
|
|
3697
|
-
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.`
|
|
3698
|
-
};
|
|
3699
|
-
}
|
|
3700
|
-
if (prState === "OPEN") {
|
|
3701
|
-
return {
|
|
3702
|
-
verdict: "allow",
|
|
3703
|
-
reason: `Branch \`${branch}\` has an OPEN PR (#${prNumber}) — pushing more commits to it is normal.`
|
|
3704
|
-
};
|
|
3705
|
-
}
|
|
3706
|
-
if (prState === "NONE") {
|
|
3707
|
-
return {
|
|
3708
|
-
verdict: "allow",
|
|
3709
|
-
reason: `Branch \`${branch}\` has no PR yet — a brand-new task branch legitimately has none.`
|
|
3710
|
-
};
|
|
3711
|
-
}
|
|
3712
|
-
return {
|
|
3713
|
-
verdict: "allow",
|
|
3714
|
-
reason: `Could not determine \`${branch}\`'s PR state (forge unreachable) — failing open rather than blocking the push on a transient issue.`
|
|
3715
|
-
};
|
|
3716
|
-
}
|
|
3717
|
-
// ../../packages/aeg-core/src/baseline-capture.ts
|
|
3718
|
-
function captureBaseline(counts, capturedAt) {
|
|
3719
|
-
return counts.map((c) => ({ tool: c.tool, findingCount: c.findingCount, capturedAt }));
|
|
3880
|
+
// ../../packages/aeg-core/src/reader-resolvable-prose.ts
|
|
3881
|
+
var PRODUCT_SLUG_SCOPE = [
|
|
3882
|
+
"apps/cli/src",
|
|
3883
|
+
".github/workflows",
|
|
3884
|
+
".vinaya",
|
|
3885
|
+
"apps/cli/README.md",
|
|
3886
|
+
"packages/sources/README.md"
|
|
3887
|
+
];
|
|
3888
|
+
function isUnderOrEqual(path, prefix) {
|
|
3889
|
+
return path === prefix || path.startsWith(`${prefix}/`);
|
|
3720
3890
|
}
|
|
3721
|
-
function
|
|
3722
|
-
|
|
3723
|
-
const baselineEntry = baseline.find((b) => b.tool === c.tool);
|
|
3724
|
-
const baselineCount = baselineEntry ? baselineEntry.findingCount : c.findingCount;
|
|
3725
|
-
return { tool: c.tool, baseline: baselineCount, current: c.findingCount, delta: c.findingCount - baselineCount };
|
|
3726
|
-
});
|
|
3727
|
-
return {
|
|
3728
|
-
withinBudget: perTool.every((t) => t.delta <= 0),
|
|
3729
|
-
delta: perTool.reduce((sum, t) => sum + t.delta, 0),
|
|
3730
|
-
perTool
|
|
3731
|
-
};
|
|
3891
|
+
function isProductScopeFile(path) {
|
|
3892
|
+
return PRODUCT_SLUG_SCOPE.some((prefix) => isUnderOrEqual(path, prefix));
|
|
3732
3893
|
}
|
|
3733
|
-
// ../../packages/aeg-core/src/reader-resolvable-prose.ts
|
|
3734
3894
|
var SHIPS_PREFIX = "aeg-root/";
|
|
3735
3895
|
function shipsArchivePrefix(shipsPrefix) {
|
|
3736
3896
|
return `${shipsPrefix}tranches/completed/`;
|
|
@@ -3750,6 +3910,8 @@ function classifyProseFile(path, readerFacingPrefix, readerFacingSuffix, shipsPr
|
|
|
3750
3910
|
}
|
|
3751
3911
|
if (isSpecFile2(path) || isClaudeMdFile(path))
|
|
3752
3912
|
return "internal";
|
|
3913
|
+
if (isProductScopeFile(path))
|
|
3914
|
+
return "product";
|
|
3753
3915
|
return null;
|
|
3754
3916
|
}
|
|
3755
3917
|
var SWEPT_CLASSES = new Set(["ships", "reader-facing"]);
|
|
@@ -3762,6 +3924,9 @@ function stripNonProse(path, content) {
|
|
|
3762
3924
|
}
|
|
3763
3925
|
return content;
|
|
3764
3926
|
}
|
|
3927
|
+
function stripNonProseForProduct(path, content) {
|
|
3928
|
+
return path.endsWith(".md") ? stripNonProse(path, content) : content;
|
|
3929
|
+
}
|
|
3765
3930
|
function lineAt(content, index) {
|
|
3766
3931
|
let line = 1;
|
|
3767
3932
|
for (let i = 0;i < index; i++) {
|
|
@@ -3786,12 +3951,19 @@ function checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuff
|
|
|
3786
3951
|
{ pattern: TRANCHE_SLUG_VN_PATTERN, what: "an internal tranche slug" },
|
|
3787
3952
|
...legacyPattern ? [{ pattern: legacyPattern, what: "an internal tranche slug", group: 2 }] : []
|
|
3788
3953
|
];
|
|
3954
|
+
const productPatterns = [
|
|
3955
|
+
{ pattern: TRANCHE_SLUG_VN_PATTERN, what: "an internal tranche slug in product code" }
|
|
3956
|
+
];
|
|
3789
3957
|
for (const file of files) {
|
|
3790
3958
|
const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix, shipsPrefix);
|
|
3791
|
-
if (!cls ||
|
|
3959
|
+
if (!cls || cls === "internal")
|
|
3792
3960
|
continue;
|
|
3793
|
-
|
|
3794
|
-
|
|
3961
|
+
if (cls !== "product" && !SWEPT_CLASSES.has(cls))
|
|
3962
|
+
continue;
|
|
3963
|
+
const blocking = cls === "product";
|
|
3964
|
+
const scrubbed = blocking ? stripNonProseForProduct(file.path, file.content) : stripNonProse(file.path, file.content);
|
|
3965
|
+
const patternsToRun = blocking ? productPatterns : patterns;
|
|
3966
|
+
for (const { pattern, what, group } of patternsToRun) {
|
|
3795
3967
|
pattern.lastIndex = 0;
|
|
3796
3968
|
let match = pattern.exec(scrubbed);
|
|
3797
3969
|
while (match !== null) {
|
|
@@ -3799,7 +3971,8 @@ function checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuff
|
|
|
3799
3971
|
findings.push({
|
|
3800
3972
|
file: file.path,
|
|
3801
3973
|
line: lineAt(scrubbed, match.index),
|
|
3802
|
-
message: `references ${what} ("${cited}") a reader outside this repo's tracker cannot resolve
|
|
3974
|
+
message: `references ${what} ("${cited}") a reader outside this repo's tracker cannot resolve`,
|
|
3975
|
+
blocking
|
|
3803
3976
|
});
|
|
3804
3977
|
match = pattern.exec(scrubbed);
|
|
3805
3978
|
}
|
|
@@ -3849,7 +4022,8 @@ function checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, read
|
|
|
3849
4022
|
findings.push({
|
|
3850
4023
|
file: file.path,
|
|
3851
4024
|
line,
|
|
3852
|
-
message: `uses coined term "${term}" without defining it inline or linking the glossary
|
|
4025
|
+
message: `uses coined term "${term}" without defining it inline or linking the glossary`,
|
|
4026
|
+
blocking: false
|
|
3853
4027
|
});
|
|
3854
4028
|
}
|
|
3855
4029
|
}
|
|
@@ -3872,6 +4046,7 @@ function parseGlossaryTerms(glossaryContent) {
|
|
|
3872
4046
|
}
|
|
3873
4047
|
return terms;
|
|
3874
4048
|
}
|
|
4049
|
+
|
|
3875
4050
|
// ../../packages/aeg-core/src/quoted-command.ts
|
|
3876
4051
|
var QUOTED_COMMAND_SHIPS_PREFIX = "aeg-root/";
|
|
3877
4052
|
var QUOTED_COMMAND_SWEPT_CLASSES = new Set(["ships", "reader-facing"]);
|
|
@@ -3966,6 +4141,54 @@ function evaluateCitedQuotes(citedQuotes, citedFileContents) {
|
|
|
3966
4141
|
}
|
|
3967
4142
|
return findings;
|
|
3968
4143
|
}
|
|
4144
|
+
// ../../packages/aeg-core/src/dead-branch-push-guard.ts
|
|
4145
|
+
function checkDeadBranchPush(input) {
|
|
4146
|
+
const { branch, prState, prNumber } = input;
|
|
4147
|
+
if (prState === "MERGED") {
|
|
4148
|
+
return {
|
|
4149
|
+
verdict: "refuse",
|
|
4150
|
+
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.`
|
|
4151
|
+
};
|
|
4152
|
+
}
|
|
4153
|
+
if (prState === "CLOSED") {
|
|
4154
|
+
return {
|
|
4155
|
+
verdict: "refuse",
|
|
4156
|
+
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.`
|
|
4157
|
+
};
|
|
4158
|
+
}
|
|
4159
|
+
if (prState === "OPEN") {
|
|
4160
|
+
return {
|
|
4161
|
+
verdict: "allow",
|
|
4162
|
+
reason: `Branch \`${branch}\` has an OPEN PR (#${prNumber}) — pushing more commits to it is normal.`
|
|
4163
|
+
};
|
|
4164
|
+
}
|
|
4165
|
+
if (prState === "NONE") {
|
|
4166
|
+
return {
|
|
4167
|
+
verdict: "allow",
|
|
4168
|
+
reason: `Branch \`${branch}\` has no PR yet — a brand-new task branch legitimately has none.`
|
|
4169
|
+
};
|
|
4170
|
+
}
|
|
4171
|
+
return {
|
|
4172
|
+
verdict: "allow",
|
|
4173
|
+
reason: `Could not determine \`${branch}\`'s PR state (forge unreachable) — failing open rather than blocking the push on a transient issue.`
|
|
4174
|
+
};
|
|
4175
|
+
}
|
|
4176
|
+
// ../../packages/aeg-core/src/baseline-capture.ts
|
|
4177
|
+
function captureBaseline(counts, capturedAt) {
|
|
4178
|
+
return counts.map((c) => ({ tool: c.tool, findingCount: c.findingCount, capturedAt }));
|
|
4179
|
+
}
|
|
4180
|
+
function compareToBaseline(current, baseline) {
|
|
4181
|
+
const perTool = current.map((c) => {
|
|
4182
|
+
const baselineEntry = baseline.find((b) => b.tool === c.tool);
|
|
4183
|
+
const baselineCount = baselineEntry ? baselineEntry.findingCount : c.findingCount;
|
|
4184
|
+
return { tool: c.tool, baseline: baselineCount, current: c.findingCount, delta: c.findingCount - baselineCount };
|
|
4185
|
+
});
|
|
4186
|
+
return {
|
|
4187
|
+
withinBudget: perTool.every((t) => t.delta <= 0),
|
|
4188
|
+
delta: perTool.reduce((sum, t) => sum + t.delta, 0),
|
|
4189
|
+
perTool
|
|
4190
|
+
};
|
|
4191
|
+
}
|
|
3969
4192
|
// ../../packages/aeg-core/src/retired-vocabulary.ts
|
|
3970
4193
|
var RETIRED_PATTERNS = [
|
|
3971
4194
|
String.raw`\bD-[0-9]{3}\b`,
|
|
@@ -4435,6 +4658,222 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
|
|
|
4435
4658
|
}
|
|
4436
4659
|
return findings;
|
|
4437
4660
|
}
|
|
4661
|
+
// ../../packages/aeg-core/src/log/schema.ts
|
|
4662
|
+
import { z } from "zod";
|
|
4663
|
+
var ROLE_VALUES = [
|
|
4664
|
+
"planner",
|
|
4665
|
+
"brief-author",
|
|
4666
|
+
"developer",
|
|
4667
|
+
"code-reviewer",
|
|
4668
|
+
"security",
|
|
4669
|
+
"principal",
|
|
4670
|
+
"archivist",
|
|
4671
|
+
"architect"
|
|
4672
|
+
];
|
|
4673
|
+
var RoleSchema = z.enum(ROLE_VALUES);
|
|
4674
|
+
var HOST_VALUES = ["hook", "ci", "cli", "loop"];
|
|
4675
|
+
var HostSchema = z.enum(HOST_VALUES);
|
|
4676
|
+
var RUN_ID_PATTERN = /^[A-Za-z0-9_.-]{1,128}$/;
|
|
4677
|
+
var HeaderMetaSchema = z.object({
|
|
4678
|
+
schema: z.literal(1),
|
|
4679
|
+
ts: z.string(),
|
|
4680
|
+
run_id: z.string().regex(RUN_ID_PATTERN),
|
|
4681
|
+
seq: z.number().int().nonnegative(),
|
|
4682
|
+
repo: z.string().nullable(),
|
|
4683
|
+
vinaya: z.string(),
|
|
4684
|
+
doctrine: z.string(),
|
|
4685
|
+
host: HostSchema,
|
|
4686
|
+
machine: z.string()
|
|
4687
|
+
}).strict();
|
|
4688
|
+
var SubjectSchema = z.object({
|
|
4689
|
+
issue: z.number().int().nullable(),
|
|
4690
|
+
pr: z.number().int().optional(),
|
|
4691
|
+
sha: z.string().optional(),
|
|
4692
|
+
role: z.union([RoleSchema, z.literal("unattributed")]),
|
|
4693
|
+
round: z.number().int().optional(),
|
|
4694
|
+
objectives_version: z.string().optional()
|
|
4695
|
+
}).strict();
|
|
4696
|
+
var HeaderSchema = z.object({
|
|
4697
|
+
meta: HeaderMetaSchema,
|
|
4698
|
+
subject: SubjectSchema
|
|
4699
|
+
}).strict();
|
|
4700
|
+
var envelopeTail = {
|
|
4701
|
+
duration_ms: z.number().nonnegative().optional(),
|
|
4702
|
+
payload: z.object({}).strict()
|
|
4703
|
+
};
|
|
4704
|
+
var DispatchOutcomeSchema = z.discriminatedUnion("type", [
|
|
4705
|
+
z.object({ type: z.literal("pr_opened"), pr: z.number().int(), head: z.string() }).strict(),
|
|
4706
|
+
z.object({
|
|
4707
|
+
type: z.literal("round_pushed"),
|
|
4708
|
+
pr: z.number().int(),
|
|
4709
|
+
head: z.string(),
|
|
4710
|
+
comment_id: z.number().int()
|
|
4711
|
+
}).strict(),
|
|
4712
|
+
z.object({
|
|
4713
|
+
type: z.literal("verdict"),
|
|
4714
|
+
verdict: z.enum(["APPROVE", "REQUEST CHANGES", "PASS", "FAIL"]),
|
|
4715
|
+
head: z.string(),
|
|
4716
|
+
comment_id: z.number().int(),
|
|
4717
|
+
objectives: z.array(z.object({ id: z.string(), met: z.boolean() }).strict()),
|
|
4718
|
+
findings: z.array(z.object({ id: z.string(), severity: z.string(), state: z.string().optional() }).strict())
|
|
4719
|
+
}).strict(),
|
|
4720
|
+
z.object({
|
|
4721
|
+
type: z.literal("escalation"),
|
|
4722
|
+
class: z.enum(["authority", "strategy", "product"]),
|
|
4723
|
+
comment_id: z.number().int()
|
|
4724
|
+
}).strict(),
|
|
4725
|
+
z.object({ type: z.literal("brief"), comment_id: z.number().int(), hash: z.string() }).strict(),
|
|
4726
|
+
z.object({ type: z.literal("plan"), issues: z.array(z.number().int()) }).strict(),
|
|
4727
|
+
z.object({ type: z.literal("archive"), provenance_comment_id: z.number().int() }).strict()
|
|
4728
|
+
]);
|
|
4729
|
+
var dispatchShared = {
|
|
4730
|
+
meta: HeaderMetaSchema,
|
|
4731
|
+
subject: SubjectSchema,
|
|
4732
|
+
kind: z.literal("dispatch"),
|
|
4733
|
+
...envelopeTail,
|
|
4734
|
+
target_role: RoleSchema,
|
|
4735
|
+
model: z.string(),
|
|
4736
|
+
round: z.number().int().optional(),
|
|
4737
|
+
effect_id: z.string()
|
|
4738
|
+
};
|
|
4739
|
+
var DispatchEventSchema = z.discriminatedUnion("event", [
|
|
4740
|
+
z.object({ ...dispatchShared, event: z.literal("dispatched"), prompt_hash: z.string() }).strict(),
|
|
4741
|
+
z.object({
|
|
4742
|
+
...dispatchShared,
|
|
4743
|
+
event: z.literal("outcome_received"),
|
|
4744
|
+
outcome: DispatchOutcomeSchema,
|
|
4745
|
+
usage: z.object({ input: z.number().nonnegative(), output: z.number().nonnegative() }).strict().nullable()
|
|
4746
|
+
}).strict(),
|
|
4747
|
+
z.object({
|
|
4748
|
+
...dispatchShared,
|
|
4749
|
+
event: z.literal("dispatch_failed"),
|
|
4750
|
+
reason: z.enum(["timeout", "crash", "refused", "unattributed_write"])
|
|
4751
|
+
}).strict()
|
|
4752
|
+
]);
|
|
4753
|
+
var loopShared = {
|
|
4754
|
+
meta: HeaderMetaSchema,
|
|
4755
|
+
subject: SubjectSchema,
|
|
4756
|
+
kind: z.literal("dev_review_loop"),
|
|
4757
|
+
...envelopeTail,
|
|
4758
|
+
loop_id: z.string()
|
|
4759
|
+
};
|
|
4760
|
+
var DevReviewLoopEventSchema = z.discriminatedUnion("event", [
|
|
4761
|
+
z.object({
|
|
4762
|
+
...loopShared,
|
|
4763
|
+
event: z.literal("loop_started"),
|
|
4764
|
+
task: z.number().int(),
|
|
4765
|
+
policy: z.object({
|
|
4766
|
+
max_rounds: z.number().int().nonnegative(),
|
|
4767
|
+
reviewers: z.array(RoleSchema),
|
|
4768
|
+
models: z.record(RoleSchema, z.string())
|
|
4769
|
+
}).strict()
|
|
4770
|
+
}).strict(),
|
|
4771
|
+
z.object({ ...loopShared, event: z.literal("round_started"), round: z.number().int(), base_head: z.string() }).strict(),
|
|
4772
|
+
z.object({
|
|
4773
|
+
...loopShared,
|
|
4774
|
+
event: z.literal("gate_result_read"),
|
|
4775
|
+
round: z.number().int(),
|
|
4776
|
+
head: z.string(),
|
|
4777
|
+
green: z.boolean()
|
|
4778
|
+
}).strict(),
|
|
4779
|
+
z.object({
|
|
4780
|
+
...loopShared,
|
|
4781
|
+
event: z.literal("verdicts_read"),
|
|
4782
|
+
round: z.number().int(),
|
|
4783
|
+
head: z.string(),
|
|
4784
|
+
all_approve: z.boolean(),
|
|
4785
|
+
blockers: z.number().int().nonnegative()
|
|
4786
|
+
}).strict(),
|
|
4787
|
+
z.object({
|
|
4788
|
+
...loopShared,
|
|
4789
|
+
event: z.literal("findings_compared"),
|
|
4790
|
+
round: z.number().int(),
|
|
4791
|
+
open: z.array(z.string()),
|
|
4792
|
+
resolved: z.array(z.string()),
|
|
4793
|
+
new: z.array(z.string()),
|
|
4794
|
+
recurring: z.array(z.string())
|
|
4795
|
+
}).strict(),
|
|
4796
|
+
z.object({
|
|
4797
|
+
...loopShared,
|
|
4798
|
+
event: z.literal("stop_condition_met"),
|
|
4799
|
+
round: z.number().int(),
|
|
4800
|
+
condition: z.enum([
|
|
4801
|
+
"green",
|
|
4802
|
+
"max_rounds",
|
|
4803
|
+
"no_progress",
|
|
4804
|
+
"escalated",
|
|
4805
|
+
"principal_stop",
|
|
4806
|
+
"confidence",
|
|
4807
|
+
"reappearance"
|
|
4808
|
+
])
|
|
4809
|
+
}).strict(),
|
|
4810
|
+
z.object({
|
|
4811
|
+
...loopShared,
|
|
4812
|
+
event: z.literal("paused"),
|
|
4813
|
+
round: z.number().int(),
|
|
4814
|
+
reason: z.enum(["escalation", "principal_item", "refreeze_needed"])
|
|
4815
|
+
}).strict(),
|
|
4816
|
+
z.object({
|
|
4817
|
+
...loopShared,
|
|
4818
|
+
event: z.literal("resumed"),
|
|
4819
|
+
round: z.number().int(),
|
|
4820
|
+
by: z.literal("principal")
|
|
4821
|
+
}).strict(),
|
|
4822
|
+
z.object({
|
|
4823
|
+
...loopShared,
|
|
4824
|
+
event: z.literal("round_ended"),
|
|
4825
|
+
round: z.number().int(),
|
|
4826
|
+
base_head: z.string(),
|
|
4827
|
+
head: z.string(),
|
|
4828
|
+
files_changed: z.number().int().nonnegative(),
|
|
4829
|
+
insertions: z.number().int().nonnegative(),
|
|
4830
|
+
deletions: z.number().int().nonnegative(),
|
|
4831
|
+
wall_ms: z.number().nonnegative(),
|
|
4832
|
+
outcome: z.enum(["green", "changes_requested", "escalated"])
|
|
4833
|
+
}).strict(),
|
|
4834
|
+
z.object({
|
|
4835
|
+
...loopShared,
|
|
4836
|
+
event: z.literal("journal_finalized"),
|
|
4837
|
+
rounds: z.number().int().nonnegative(),
|
|
4838
|
+
total_wall_ms: z.number().nonnegative(),
|
|
4839
|
+
time_to_green_ms: z.number().nonnegative().nullable(),
|
|
4840
|
+
files_changed_total: z.number().int().nonnegative(),
|
|
4841
|
+
final_head: z.string(),
|
|
4842
|
+
result: z.enum(["merged_ready", "stopped"])
|
|
4843
|
+
}).strict()
|
|
4844
|
+
]);
|
|
4845
|
+
var ForgeOpSchema = z.enum([
|
|
4846
|
+
"pr.create",
|
|
4847
|
+
"pr.comment",
|
|
4848
|
+
"pr.body.replace",
|
|
4849
|
+
"pr.refreeze",
|
|
4850
|
+
"issue.create",
|
|
4851
|
+
"issue.edit",
|
|
4852
|
+
"issue.comment",
|
|
4853
|
+
"milestone.create",
|
|
4854
|
+
"milestone.edit",
|
|
4855
|
+
"milestone.close",
|
|
4856
|
+
"label.add",
|
|
4857
|
+
"label.remove"
|
|
4858
|
+
]);
|
|
4859
|
+
var ForgeWriteTargetSchema = z.object({
|
|
4860
|
+
issue: z.number().int().optional(),
|
|
4861
|
+
pr: z.number().int().optional()
|
|
4862
|
+
}).strict();
|
|
4863
|
+
var forgeWriteShared = {
|
|
4864
|
+
meta: HeaderMetaSchema,
|
|
4865
|
+
subject: SubjectSchema,
|
|
4866
|
+
kind: z.literal("forge_write"),
|
|
4867
|
+
...envelopeTail,
|
|
4868
|
+
op: ForgeOpSchema,
|
|
4869
|
+
target: ForgeWriteTargetSchema
|
|
4870
|
+
};
|
|
4871
|
+
var ForgeWriteEventSchema = z.discriminatedUnion("event", [
|
|
4872
|
+
z.object({ ...forgeWriteShared, event: z.literal("validated") }).strict(),
|
|
4873
|
+
z.object({ ...forgeWriteShared, event: z.literal("refused"), reason: z.string() }).strict(),
|
|
4874
|
+
z.object({ ...forgeWriteShared, event: z.literal("written"), comment_ids: z.array(z.string()) }).strict()
|
|
4875
|
+
]);
|
|
4876
|
+
var LogEventSchema = z.union([DispatchEventSchema, DevReviewLoopEventSchema, ForgeWriteEventSchema]);
|
|
4438
4877
|
// src/checks/contract.ts
|
|
4439
4878
|
var CHECK_SCHEMA_VERSION = 1;
|
|
4440
4879
|
function emitCheckError(error) {
|