@attalabs/vinaya 0.27.0 → 0.29.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 +5 -4
- package/aeg-root/contracts/planner-developer.md +4 -1
- package/aeg-root/contracts/planner-operator.md +86 -0
- package/aeg-root/contracts/principal-operator.md +88 -0
- package/aeg-root/enforcement.md +4 -4
- package/aeg-root/roles/developer.md +25 -18
- package/aeg-root/roles/operator.md +133 -0
- package/aeg-root/roles/planner.md +6 -0
- package/aeg-root/roles/principal.md +4 -0
- package/aeg-root/roles/reviewer.md +3 -3
- package/aeg-root/roles/security.md +3 -3
- package/aeg-root/skills/aeg-context-packets/SKILL.md +123 -0
- package/aeg-root/skills/aeg-context-packets/examples/operator-start.md +30 -0
- package/aeg-root/templates/brief-template.md +6 -2
- package/dist/checks/bin/check-body-bare-digits.js +1275 -240
- package/dist/checks/bin/check-branch-topology.js +1207 -180
- package/dist/checks/bin/check-brief-shape.js +1279 -243
- package/dist/checks/bin/check-changeset-coverage.js +2176 -264
- package/dist/checks/bin/check-closes-n.js +1211 -184
- package/dist/checks/bin/check-coherence.js +1378 -265
- package/dist/checks/bin/check-dead-branch-push.js +1158 -176
- package/dist/checks/bin/check-dispatch-readiness.js +1440 -276
- package/dist/checks/bin/check-doc-coverage-push.js +2171 -259
- package/dist/checks/bin/check-doc-coverage.js +2173 -261
- package/dist/checks/bin/check-doctrine-no-procedures.js +1275 -240
- package/dist/checks/bin/check-doctrine-portability.js +2171 -259
- package/dist/checks/bin/check-evidence-fresh.js +2613 -347
- package/dist/checks/bin/check-exec-bits.js +2169 -257
- package/dist/checks/bin/check-first-push-dispatch.js +1326 -246
- package/dist/checks/bin/check-issue-assignment.js +1209 -182
- package/dist/checks/bin/check-issue-milestone-attach.js +6087 -0
- package/dist/checks/bin/check-issue-objectives-numbering.js +6089 -0
- package/dist/checks/bin/check-issue-parts-coverage.js +6089 -0
- package/dist/checks/bin/check-issue-surface-globs.js +7304 -0
- package/dist/checks/bin/check-issue-title-grammar.js +6089 -0
- package/dist/checks/bin/check-issue-tranche-label.js +6089 -0
- package/dist/checks/bin/check-main-branch-refusal.js +1158 -176
- package/dist/checks/bin/check-no-disk-state.js +1158 -176
- package/dist/checks/bin/check-pr-premise-reassert.js +1275 -240
- package/dist/checks/bin/check-pr-report-density.js +1158 -176
- package/dist/checks/bin/check-quoted-command.js +2150 -257
- package/dist/checks/bin/check-reader-resolvable-prose.js +2153 -260
- package/dist/checks/bin/check-registry-gates.js +1245 -176
- package/dist/checks/bin/check-retired-vocabulary.js +2148 -255
- package/dist/checks/bin/check-review-gate.js +1375 -245
- package/dist/checks/bin/check-single-plan-pr.js +1158 -176
- package/dist/checks/bin/check-surface-scope.js +1217 -182
- package/dist/checks/bin/check-test-plan.js +1187 -189
- package/dist/checks/bin/check-token-collection-wired.js +1158 -176
- package/dist/checks/bin/check-token-report.js +1171 -179
- package/dist/checks/bin/check-workspace-escape.js +2167 -255
- package/dist/index.js +17322 -8709
- package/dist/lib/pre-push-changed-files.js +57 -0
- package/dist/lib/pre-push-select-tests.js +1616 -0
- package/package.json +4 -2
- 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/page.js +2 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error/page.js.nft.json +1 -1
- 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 +8 -7
- 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/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js +8 -7
- 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 +9 -8
- 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 +9 -8
- 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 +8 -7
- 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 +16 -4
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js +10 -8
- 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 +8 -7
- 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 +8 -7
- 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 +9 -8
- 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]__08f5pba._.js +3 -0
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__1o_0tjy._.js → [root-of-the-server]__0h9yhwx._.js} +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__0053k9k._.js → [root-of-the-server]__0ls23oh._.js} +3 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__0puovz5._.js → [root-of-the-server]__0nye8tb._.js} +3 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__18kqy4q._.js → [root-of-the-server]__0tfykr-._.js} +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__0o771t1._.js → [root-of-the-server]__0th3gyp._.js} +3 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1-zcufa._.js +51 -0
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__0p8q38b._.js → [root-of-the-server]__11svemm._.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__02c71kv._.js → [root-of-the-server]__140m1i_._.js} +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1bh2awk._.js +3 -0
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__0112h-k._.js → [root-of-the-server]__1gjqmhq._.js} +3 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1qmxez3._.js +4 -0
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1r47dr7._.js +28 -0
- 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/_01fv4fj._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{_0gvm3og._.js → _096f46i._.js} +3 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{_0lwxg63._.js → _0af2one._.js} +3 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{_1n0cnq-._.js → _0zf4d27._.js} +45 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{_03x_w6q._.js → _1-wo68i._.js} +3 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/apps_vinaya-studio_web_src_app_studio_projects_[name]_tranches_[slug]_05ufo8i._.js +1 -12
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_1wdusod._.js +3 -0
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{node_modules_1vo08dj._.js → node_modules_@clerk_nextjs_dist_esm_app-router_server-actions_1x7vfhn.js} +3 -4
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_next_1khj-jh._.js +3 -0
- 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 +17 -5
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{3_9xytdmiv1xi.js → 25f3-jtiv8p5n.js} +4 -4
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{0bn8c8v5q429o.js → 27-6ym7lp2v8o.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{3-6a3tinbdb-7.js → 3daxns3o--9me.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/tsconfig.json +12 -0
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0z6dav-._.js +0 -51
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__13rrmhr._.js +0 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__17h4sy8._.js +0 -45
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1hs0dcu._.js +0 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_next_17sz44y._.js +0 -3
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{DMofV2-9FrHE7100GfrpI → -UC6GS6zDHeiawEYgYbTN}/_buildManifest.js +0 -0
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{DMofV2-9FrHE7100GfrpI → -UC6GS6zDHeiawEYgYbTN}/_clientMiddlewareManifest.js +0 -0
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{DMofV2-9FrHE7100GfrpI → -UC6GS6zDHeiawEYgYbTN}/_ssgManifest.js +0 -0
|
@@ -1644,6 +1644,52 @@ var systemEnv3 = {
|
|
|
1644
1644
|
...process.env,
|
|
1645
1645
|
PATH: [process.env.PATH, "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"].filter(Boolean).join(":")
|
|
1646
1646
|
};
|
|
1647
|
+
var defaultExec = () => execFileAsync3("git", ["remote", "get-url", "origin"], { env: systemEnv3, timeout: 5000 });
|
|
1648
|
+
var cached;
|
|
1649
|
+
var inflight;
|
|
1650
|
+
async function resolveRepo(exec = defaultExec) {
|
|
1651
|
+
if (cached !== undefined)
|
|
1652
|
+
return cached;
|
|
1653
|
+
const fromEnv = process.env.AEG_REPO;
|
|
1654
|
+
if (fromEnv) {
|
|
1655
|
+
const parsed = parseOwnerRepo(fromEnv);
|
|
1656
|
+
if (parsed) {
|
|
1657
|
+
cached = parsed;
|
|
1658
|
+
return cached;
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
if (inflight)
|
|
1662
|
+
return inflight;
|
|
1663
|
+
inflight = (async () => {
|
|
1664
|
+
try {
|
|
1665
|
+
const { stdout } = await exec();
|
|
1666
|
+
cached = parseGitRemoteUrl(stdout.trim());
|
|
1667
|
+
return cached;
|
|
1668
|
+
} catch (err) {
|
|
1669
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1670
|
+
console.warn(`[aeg-forge-state] resolveRepo: git remote lookup failed (will retry): ${message}`);
|
|
1671
|
+
return null;
|
|
1672
|
+
} finally {
|
|
1673
|
+
inflight = undefined;
|
|
1674
|
+
}
|
|
1675
|
+
})();
|
|
1676
|
+
return inflight;
|
|
1677
|
+
}
|
|
1678
|
+
function parseGitRemoteUrl(url) {
|
|
1679
|
+
const ssh = url.match(/^git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/);
|
|
1680
|
+
if (ssh?.[1] && ssh[2])
|
|
1681
|
+
return { owner: ssh[1], repo: ssh[2] };
|
|
1682
|
+
const https = url.match(/^https?:\/\/(?:[^@]+@)?github\.com\/([^/]+)\/(.+?)(?:\.git)?\/?$/);
|
|
1683
|
+
if (https?.[1] && https[2])
|
|
1684
|
+
return { owner: https[1], repo: https[2] };
|
|
1685
|
+
return null;
|
|
1686
|
+
}
|
|
1687
|
+
function parseOwnerRepo(value) {
|
|
1688
|
+
const match = value.match(/^([^/]+)\/(.+)$/);
|
|
1689
|
+
if (!match?.[1] || !match[2])
|
|
1690
|
+
return null;
|
|
1691
|
+
return { owner: match[1], repo: match[2] };
|
|
1692
|
+
}
|
|
1647
1693
|
// ../../packages/aeg-core/src/anchored-region.ts
|
|
1648
1694
|
function anchoredRegionBounds(body, field) {
|
|
1649
1695
|
const masked = maskCode(body);
|
|
@@ -1954,6 +2000,9 @@ function parseInlineFieldList(section) {
|
|
|
1954
2000
|
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
1955
2001
|
var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1956
2002
|
var RULING_ORDINAL_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Ruling ordinal:\s*(\d+)(?!\d)/im;
|
|
2003
|
+
var BRIEF_HASH_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Brief hash:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
2004
|
+
var POLICY_DIGEST_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Policy digest:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
2005
|
+
var BASE_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged base:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
1957
2006
|
function firstFiveLines(comment) {
|
|
1958
2007
|
return comment.split(`
|
|
1959
2008
|
`).slice(0, 5).join(`
|
|
@@ -1976,6 +2025,35 @@ function extractRulingOrdinal(comment) {
|
|
|
1976
2025
|
const m = firstSevenLines(comment).match(RULING_ORDINAL_PATTERN);
|
|
1977
2026
|
return m ? Number.parseInt(m[1], 10) : null;
|
|
1978
2027
|
}
|
|
2028
|
+
function firstElevenLines(comment) {
|
|
2029
|
+
return comment.split(`
|
|
2030
|
+
`).slice(0, 11).join(`
|
|
2031
|
+
`);
|
|
2032
|
+
}
|
|
2033
|
+
function extractBriefHash(comment) {
|
|
2034
|
+
const m = firstElevenLines(comment).match(BRIEF_HASH_PATTERN);
|
|
2035
|
+
return m ? m[1].toLowerCase() : null;
|
|
2036
|
+
}
|
|
2037
|
+
function extractPolicyDigest(comment) {
|
|
2038
|
+
const m = firstElevenLines(comment).match(POLICY_DIGEST_PATTERN);
|
|
2039
|
+
return m ? m[1].toLowerCase() : null;
|
|
2040
|
+
}
|
|
2041
|
+
function firstThirteenLines(comment) {
|
|
2042
|
+
return comment.split(`
|
|
2043
|
+
`).slice(0, 13).join(`
|
|
2044
|
+
`);
|
|
2045
|
+
}
|
|
2046
|
+
function extractBaseSha(comment) {
|
|
2047
|
+
const m = firstThirteenLines(comment).match(BASE_SHA_PATTERN);
|
|
2048
|
+
return m ? m[1].toLowerCase() : null;
|
|
2049
|
+
}
|
|
2050
|
+
var FINDING_SEVERITY_LINE = /^\d+\.\s+\[([A-Z][A-Z]*)\]\s+(.+?)\s+—/gm;
|
|
2051
|
+
function extractFindingSeverities(comment) {
|
|
2052
|
+
return [...comment.matchAll(FINDING_SEVERITY_LINE)].map((m) => ({
|
|
2053
|
+
severity: m[1],
|
|
2054
|
+
location: m[2]
|
|
2055
|
+
}));
|
|
2056
|
+
}
|
|
1979
2057
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
1980
2058
|
const candidates = comments.filter((c) => valuePattern.test(c));
|
|
1981
2059
|
if (candidates.length === 0) {
|
|
@@ -1984,6 +2062,10 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1984
2062
|
headSha: null,
|
|
1985
2063
|
objectivesVersion: null,
|
|
1986
2064
|
rulingOrdinal: null,
|
|
2065
|
+
briefHash: null,
|
|
2066
|
+
policyDigest: null,
|
|
2067
|
+
baseSha: null,
|
|
2068
|
+
findingSeverities: [],
|
|
1987
2069
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
1988
2070
|
};
|
|
1989
2071
|
}
|
|
@@ -1995,6 +2077,10 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1995
2077
|
headSha: null,
|
|
1996
2078
|
objectivesVersion: null,
|
|
1997
2079
|
rulingOrdinal: null,
|
|
2080
|
+
briefHash: null,
|
|
2081
|
+
policyDigest: null,
|
|
2082
|
+
baseSha: null,
|
|
2083
|
+
findingSeverities: [],
|
|
1998
2084
|
danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
|
|
1999
2085
|
};
|
|
2000
2086
|
}
|
|
@@ -2003,6 +2089,10 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
2003
2089
|
headSha: extractHeadSha(latest),
|
|
2004
2090
|
objectivesVersion: extractObjectivesVersion(latest),
|
|
2005
2091
|
rulingOrdinal: extractRulingOrdinal(latest),
|
|
2092
|
+
briefHash: extractBriefHash(latest),
|
|
2093
|
+
policyDigest: extractPolicyDigest(latest),
|
|
2094
|
+
baseSha: extractBaseSha(latest),
|
|
2095
|
+
findingSeverities: extractFindingSeverities(latest),
|
|
2006
2096
|
danglingNote: null
|
|
2007
2097
|
};
|
|
2008
2098
|
}
|
|
@@ -2338,6 +2428,53 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive,
|
|
|
2338
2428
|
}
|
|
2339
2429
|
return out;
|
|
2340
2430
|
}
|
|
2431
|
+
// ../../packages/aeg-core/src/review-policy.ts
|
|
2432
|
+
var CODE_REVIEW_SEVERITY_ORDER = ["BLOCKER", "MAJOR", "MINOR"];
|
|
2433
|
+
var SECURITY_SEVERITY_ORDER = ["CRITICAL", "HIGH", "MEDIUM", "LOW"];
|
|
2434
|
+
var DEFAULT_MAX_ROUNDS = 3;
|
|
2435
|
+
var DEFAULT_REVIEW_POLICY = {
|
|
2436
|
+
codeReviewThreshold: "BLOCKER",
|
|
2437
|
+
securityThreshold: "HIGH",
|
|
2438
|
+
maxRounds: DEFAULT_MAX_ROUNDS
|
|
2439
|
+
};
|
|
2440
|
+
var FILE_SHAPED_LOCATION = /\.[a-zA-Z0-9]{1,10}(:\d+)?\s*$/;
|
|
2441
|
+
var PROSE_LOCATION_PATTERNS = [/\bpr\s*body\b/i, /\bcomment\b/i];
|
|
2442
|
+
var ROLE_FILE_LOCATION = /(^|\/)aeg-root\/roles\//i;
|
|
2443
|
+
function isProseLocation(location) {
|
|
2444
|
+
if (ROLE_FILE_LOCATION.test(location))
|
|
2445
|
+
return true;
|
|
2446
|
+
if (FILE_SHAPED_LOCATION.test(location))
|
|
2447
|
+
return false;
|
|
2448
|
+
return PROSE_LOCATION_PATTERNS.some((pattern) => pattern.test(location));
|
|
2449
|
+
}
|
|
2450
|
+
var PROSE_CAP_SEVERITY = "MINOR";
|
|
2451
|
+
function blockingSeverities(scale, threshold) {
|
|
2452
|
+
const idx = scale.indexOf(threshold);
|
|
2453
|
+
if (idx === -1) {
|
|
2454
|
+
throw new Error(`blockingSeverities: threshold "${threshold}" is not one of ${scale.join(" > ")}`);
|
|
2455
|
+
}
|
|
2456
|
+
return scale.slice(0, idx + 1);
|
|
2457
|
+
}
|
|
2458
|
+
function evaluateReviewFindings(findings, scale, threshold) {
|
|
2459
|
+
const blocking = new Set(blockingSeverities(scale, threshold));
|
|
2460
|
+
const blockingFindings = findings.filter((f) => {
|
|
2461
|
+
if (!scale.includes(f.severity)) {
|
|
2462
|
+
throw new Error(`evaluateReviewFindings: severity "${f.severity}" is not one of ${scale.join(" > ")}`);
|
|
2463
|
+
}
|
|
2464
|
+
const effectiveSeverity = f.location !== undefined && isProseLocation(f.location) ? PROSE_CAP_SEVERITY : f.severity;
|
|
2465
|
+
return blocking.has(effectiveSeverity);
|
|
2466
|
+
});
|
|
2467
|
+
return { outcome: blockingFindings.length > 0 ? "blocked" : "clean", blockingFindings };
|
|
2468
|
+
}
|
|
2469
|
+
function evaluateCodeReview(findings, policy) {
|
|
2470
|
+
return evaluateReviewFindings(findings, CODE_REVIEW_SEVERITY_ORDER, policy.codeReviewThreshold);
|
|
2471
|
+
}
|
|
2472
|
+
function evaluateSecurityReview(findings, policy) {
|
|
2473
|
+
return evaluateReviewFindings(findings, SECURITY_SEVERITY_ORDER, policy.securityThreshold);
|
|
2474
|
+
}
|
|
2475
|
+
function isKnownSeverity(scale, value) {
|
|
2476
|
+
return scale.includes(value);
|
|
2477
|
+
}
|
|
2341
2478
|
// ../../packages/aeg-core/src/pr-tier.ts
|
|
2342
2479
|
var TIER_FIELD = /(\*\*)?\s*Tier\s*(\*\*)?\s*:\s*(\*\*)?\s*([013])\b/i;
|
|
2343
2480
|
function readTierFromPrBody(prBody) {
|
|
@@ -2708,7 +2845,7 @@ function checkForField(prBody) {
|
|
|
2708
2845
|
]
|
|
2709
2846
|
};
|
|
2710
2847
|
}
|
|
2711
|
-
function
|
|
2848
|
+
function checkClosesNPresence(prBody) {
|
|
2712
2849
|
const closesPattern = /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s{0,8}:?\s{0,8}#\d+/i;
|
|
2713
2850
|
if (closesPattern.test(stripCode(prBody))) {
|
|
2714
2851
|
return { status: "pass", errors: [] };
|
|
@@ -2740,10 +2877,22 @@ var COMMIT_TYPES = [
|
|
|
2740
2877
|
"Test"
|
|
2741
2878
|
];
|
|
2742
2879
|
var COMMIT_TYPE_STYLE = new RegExp(`^(${COMMIT_TYPES.join("|")})(\\([a-z0-9-]+\\))?: \\S`);
|
|
2880
|
+
function checkForgeTitle(title) {
|
|
2881
|
+
const taskStyle = /^\[[a-z0-9._-]+\] \S+ — \S/;
|
|
2882
|
+
if (COMMIT_TYPE_STYLE.test(title) || taskStyle.test(title))
|
|
2883
|
+
return { status: "pass", errors: [] };
|
|
2884
|
+
return {
|
|
2885
|
+
status: "fail",
|
|
2886
|
+
errors: [
|
|
2887
|
+
`brief-validation title: "${title}" matches neither title grammar — expected \`Type: description\` / \`Type(scope): description\` (commitlint types + Plan) or \`[tranche] id — description\` (task form).`
|
|
2888
|
+
]
|
|
2889
|
+
};
|
|
2890
|
+
}
|
|
2743
2891
|
var BRIEF_SHAPE_MARKERS = [checkSurfaceMap, checkDocUpdateList, checkStopConditions, checkAutonomyClause];
|
|
2744
2892
|
var TASK_BRANCH_PATTERN = /^task\/[^/]+\/[^/]+$/;
|
|
2893
|
+
var TASK_ISSUE_BRANCH_PATTERN = /^task\/issue-\d+$/;
|
|
2745
2894
|
function isTaskBranch(branch) {
|
|
2746
|
-
return TASK_BRANCH_PATTERN.test(branch);
|
|
2895
|
+
return TASK_BRANCH_PATTERN.test(branch) || TASK_ISSUE_BRANCH_PATTERN.test(branch);
|
|
2747
2896
|
}
|
|
2748
2897
|
function isBriefShaped(prBody) {
|
|
2749
2898
|
const stripped = stripCode(prBody);
|
|
@@ -2865,8 +3014,9 @@ function packagesNamedIn(text) {
|
|
|
2865
3014
|
}
|
|
2866
3015
|
function hasTestPathForConsumer(text, consumerDir) {
|
|
2867
3016
|
const escaped = consumerDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2868
|
-
const
|
|
2869
|
-
|
|
3017
|
+
const filePathRe = new RegExp(`${escaped}\\/[\\w./-]*\\.test\\.[A-Za-z0-9]+`);
|
|
3018
|
+
const testDirRe = new RegExp(`${escaped}\\/(?:[\\w-]+\\/)*(?:tests|specs)(?:\\/|\\b)`);
|
|
3019
|
+
return filePathRe.test(text) || testDirRe.test(text);
|
|
2870
3020
|
}
|
|
2871
3021
|
function checkConsumerTests(prBody, consumersOf) {
|
|
2872
3022
|
const section4 = extractNumberedSection(prBody, 4);
|
|
@@ -2970,7 +3120,7 @@ function checkBriefSections(prBody, readTier, options = {}) {
|
|
|
2970
3120
|
checkConsumerTests(prBody, consumersOf),
|
|
2971
3121
|
checkDefeatCases(prBody),
|
|
2972
3122
|
...issueObjectives !== undefined ? [checkObjectivesCopy(prBody, issueObjectives), checkObjectivesCoverage(prBody)] : [],
|
|
2973
|
-
...requireClosesN ? [
|
|
3123
|
+
...requireClosesN ? [checkClosesNPresence(prBody)] : []
|
|
2974
3124
|
];
|
|
2975
3125
|
return { errors: results.flatMap((r) => r.errors) };
|
|
2976
3126
|
}
|
|
@@ -3260,6 +3410,28 @@ function topLevelSectionText(body, headingName) {
|
|
|
3260
3410
|
const next = /^##[ \t]/m.exec(afterHeading);
|
|
3261
3411
|
return next ? afterHeading.slice(0, next.index) : afterHeading;
|
|
3262
3412
|
}
|
|
3413
|
+
function partHasBacktickedPath(text) {
|
|
3414
|
+
let i = 0;
|
|
3415
|
+
while (i < text.length) {
|
|
3416
|
+
const start = text.indexOf("`", i);
|
|
3417
|
+
if (start === -1)
|
|
3418
|
+
return false;
|
|
3419
|
+
const end = text.indexOf("`", start + 1);
|
|
3420
|
+
if (end === -1)
|
|
3421
|
+
return false;
|
|
3422
|
+
if (text.slice(start + 1, end).includes("/"))
|
|
3423
|
+
return true;
|
|
3424
|
+
i = end + 1;
|
|
3425
|
+
}
|
|
3426
|
+
return false;
|
|
3427
|
+
}
|
|
3428
|
+
var PART_MIN_WORDS_OUTSIDE_BACKTICKS = 3;
|
|
3429
|
+
function stripPartBackticks(text) {
|
|
3430
|
+
return text.replace(/`[^`\n]*`/g, " ");
|
|
3431
|
+
}
|
|
3432
|
+
function partWordCount(text) {
|
|
3433
|
+
return text.split(/\s+/).filter((w) => /[a-z]/i.test(w)).length;
|
|
3434
|
+
}
|
|
3263
3435
|
function looksLikeFilePath(entry2) {
|
|
3264
3436
|
const stripped = entry2.replace(/\/\*\*?$/, "");
|
|
3265
3437
|
const lastSegment = stripped.split("/").pop() ?? stripped;
|
|
@@ -3300,6 +3472,18 @@ function globCoversPath(glob, path) {
|
|
|
3300
3472
|
const p = path.replace(/\/+$/, "");
|
|
3301
3473
|
return g === p || g.startsWith(`${p}/`) || p.startsWith(`${g}/`);
|
|
3302
3474
|
}
|
|
3475
|
+
function checkSurfaceGlobsResolve(body, resolvesToFile) {
|
|
3476
|
+
const surface = parseIssueSurface(body);
|
|
3477
|
+
if (!surface.ok)
|
|
3478
|
+
return { status: "pass", errors: [] };
|
|
3479
|
+
const errors = [];
|
|
3480
|
+
for (const glob of surface.value.in) {
|
|
3481
|
+
if (!resolvesToFile(glob)) {
|
|
3482
|
+
errors.push(`issue-validation Surface: \`${glob}\` in \`## Surface\`'s \`in:\` list matches no tracked file — a Surface that cannot resolve cannot render a brief's file list.`);
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3486
|
+
}
|
|
3303
3487
|
function checkSurfaceScope(changedFiles, outGlobs) {
|
|
3304
3488
|
if (outGlobs.length === 0)
|
|
3305
3489
|
return { ok: true };
|
|
@@ -3311,9 +3495,77 @@ function checkSurfaceScope(changedFiles, outGlobs) {
|
|
|
3311
3495
|
}
|
|
3312
3496
|
return violations.length > 0 ? { ok: false, violations } : { ok: true };
|
|
3313
3497
|
}
|
|
3498
|
+
var ISSUE_PART_LINE_RE = /^Part\s+(\d+)\s*\(([^)]*)\)\s*[-—–]\s*(.*)$/i;
|
|
3499
|
+
function parseIssueParts(body) {
|
|
3500
|
+
const section = topLevelSectionText(body, "Parts");
|
|
3501
|
+
if (section === null)
|
|
3502
|
+
return { ok: false, errors: ["no `## Parts` heading found in the body."] };
|
|
3503
|
+
const lines = section.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
3504
|
+
const parts = [];
|
|
3505
|
+
const errors = [];
|
|
3506
|
+
for (const line of lines) {
|
|
3507
|
+
const m = ISSUE_PART_LINE_RE.exec(line);
|
|
3508
|
+
if (!m) {
|
|
3509
|
+
errors.push(`"${line}" is not a well-formed Parts line — expected \`Part <n> (<refs>) — <outcome>\`.`);
|
|
3510
|
+
continue;
|
|
3511
|
+
}
|
|
3512
|
+
const n = Number.parseInt(m[1], 10);
|
|
3513
|
+
const refs = m[2];
|
|
3514
|
+
const text = m[3].trim();
|
|
3515
|
+
if (text.length === 0) {
|
|
3516
|
+
errors.push(`Part ${n} has no outcome text after the dash — every Part states one observable outcome.`);
|
|
3517
|
+
continue;
|
|
3518
|
+
}
|
|
3519
|
+
if (partHasBacktickedPath(text) && partWordCount(stripPartBackticks(text)) < PART_MIN_WORDS_OUTSIDE_BACKTICKS) {
|
|
3520
|
+
errors.push(`Part ${n} is little more than a file path — a Part names an outcome and symbols, never a bare path.`);
|
|
3521
|
+
continue;
|
|
3522
|
+
}
|
|
3523
|
+
const objectiveIds = [...refs.matchAll(/O(\d+)/g)].map((r) => Number.parseInt(r[1], 10));
|
|
3524
|
+
parts.push({ n, objectiveIds, text });
|
|
3525
|
+
}
|
|
3526
|
+
if (parts.length === 0) {
|
|
3527
|
+
errors.push("the `## Parts` section has no well-formed `Part <n> (<refs>) — <outcome>` lines.");
|
|
3528
|
+
}
|
|
3529
|
+
if (errors.length > 0)
|
|
3530
|
+
return { ok: false, errors };
|
|
3531
|
+
return { ok: true, value: parts };
|
|
3532
|
+
}
|
|
3533
|
+
function checkPartsCiteDefinedObjectives(body) {
|
|
3534
|
+
const parts = parseIssueParts(body);
|
|
3535
|
+
const objectives = objectivesOf(body);
|
|
3536
|
+
if (!parts.ok || !objectives.ok)
|
|
3537
|
+
return { status: "pass", errors: [] };
|
|
3538
|
+
const definedIds = new Set(objectives.objectives.map((o) => Number.parseInt(o.id.slice(1), 10)));
|
|
3539
|
+
const errors = [];
|
|
3540
|
+
for (const part of parts.value) {
|
|
3541
|
+
for (const objectiveId of part.objectiveIds) {
|
|
3542
|
+
if (!definedIds.has(objectiveId)) {
|
|
3543
|
+
errors.push(`issue-validation Parts: Part ${part.n} cites O${objectiveId}, which the Issue's own \`## Objectives\` section does not define.`);
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3547
|
+
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3548
|
+
}
|
|
3314
3549
|
function isTaskIssueLabelSet(labels) {
|
|
3315
3550
|
return hasLabel("tranche", labels);
|
|
3316
3551
|
}
|
|
3552
|
+
function checkTrancheLabelPresence(_body, _labels) {
|
|
3553
|
+
return { status: "pass", errors: [] };
|
|
3554
|
+
}
|
|
3555
|
+
function checkMilestoneAttach(labels, currentMilestoneTitle, resolvedMilestoneTitle) {
|
|
3556
|
+
if (!isTaskIssueLabelSet(labels))
|
|
3557
|
+
return { status: "pass", errors: [] };
|
|
3558
|
+
if (resolvedMilestoneTitle === null)
|
|
3559
|
+
return { status: "pass", errors: [] };
|
|
3560
|
+
if (currentMilestoneTitle === resolvedMilestoneTitle)
|
|
3561
|
+
return { status: "pass", errors: [] };
|
|
3562
|
+
return {
|
|
3563
|
+
status: "fail",
|
|
3564
|
+
errors: [
|
|
3565
|
+
`issue-validation milestone attach: this task Issue's tranche label resolves to Milestone "${resolvedMilestoneTitle}", but its live Milestone is ${currentMilestoneTitle === null ? "unset" : `"${currentMilestoneTitle}"`} — attach it to "${resolvedMilestoneTitle}".`
|
|
3566
|
+
]
|
|
3567
|
+
};
|
|
3568
|
+
}
|
|
3317
3569
|
var TYPE_LABEL_IDS = LABELS.filter((l) => l.category === "type").map((l) => l.id);
|
|
3318
3570
|
function isControlCodePoint(codePoint) {
|
|
3319
3571
|
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
|
|
@@ -3378,6 +3630,19 @@ function checkProjectsRegistered(body, _labels, registeredNames) {
|
|
|
3378
3630
|
var DOC_PATH_RE = /(?:(?:aeg-root|apps|packages|specs|docs|tools|\.claude|\.github)\/[\w./@-]*\.(?:md|mdx)|\.claude\/(?:skills|rules)\/[\w./-]+|\b(?:docs-index|decisions|projects|state-machine|enforcement|process|README|CLAUDE)\.md\b|\b[\w-]+-(?:spec|decisions|backlog)\.md\b)/i;
|
|
3379
3631
|
var DOC_PATH_RE_GLOBAL = new RegExp(DOC_PATH_RE.source, "gi");
|
|
3380
3632
|
|
|
3633
|
+
// ../../packages/aeg-core/src/task-branch-identity.ts
|
|
3634
|
+
var ISSUE_BRANCH_PATTERN = /^task\/issue-(\d+)$/;
|
|
3635
|
+
var TRANCHE_BRANCH_PATTERN = /^task\/([^/]+)\/([^/]+)$/;
|
|
3636
|
+
function parseTaskBranchIdentity(branch) {
|
|
3637
|
+
const issueMatch = ISSUE_BRANCH_PATTERN.exec(branch);
|
|
3638
|
+
if (issueMatch)
|
|
3639
|
+
return { kind: "issue", issueNumber: Number(issueMatch[1]) };
|
|
3640
|
+
const trancheMatch = TRANCHE_BRANCH_PATTERN.exec(branch);
|
|
3641
|
+
if (trancheMatch)
|
|
3642
|
+
return { kind: "tranche", tranche: trancheMatch[1], taskId: trancheMatch[2] };
|
|
3643
|
+
return null;
|
|
3644
|
+
}
|
|
3645
|
+
|
|
3381
3646
|
// ../../packages/aeg-core/src/coherence-checks.ts
|
|
3382
3647
|
var COHERENCE_ENFORCED_FROM = "2026-07-01";
|
|
3383
3648
|
function isGrandfathered(isoDate) {
|
|
@@ -3398,6 +3663,7 @@ function checkA1(entries, principalAllowlist = PRINCIPAL_ALLOWLIST) {
|
|
|
3398
3663
|
if (handClosed)
|
|
3399
3664
|
continue;
|
|
3400
3665
|
failures.push({
|
|
3666
|
+
code: "closed-without-merge",
|
|
3401
3667
|
issue: e.task.issue,
|
|
3402
3668
|
tranche: e.trancheSlug,
|
|
3403
3669
|
task: e.task.id,
|
|
@@ -3422,6 +3688,7 @@ function checkA3(entries) {
|
|
|
3422
3688
|
continue;
|
|
3423
3689
|
if (e.facts.prState === "merged" && e.facts.issueState !== "closed") {
|
|
3424
3690
|
failures.push({
|
|
3691
|
+
code: "auto-close-misfire",
|
|
3425
3692
|
issue: e.task.issue,
|
|
3426
3693
|
tranche: e.trancheSlug,
|
|
3427
3694
|
task: e.task.id,
|
|
@@ -3446,6 +3713,7 @@ function checkT1(entries) {
|
|
|
3446
3713
|
continue;
|
|
3447
3714
|
if (e.facts === undefined) {
|
|
3448
3715
|
failures.push({
|
|
3716
|
+
code: "phantom-issue-ref",
|
|
3449
3717
|
issue: e.task.issue,
|
|
3450
3718
|
tranche: e.trancheSlug,
|
|
3451
3719
|
task: e.task.id,
|
|
@@ -3464,6 +3732,7 @@ function checkT2(openIssuesBySlug, topologyIssuesBySlug, ciTrancheSlug) {
|
|
|
3464
3732
|
for (const num of openNums) {
|
|
3465
3733
|
if (!topologySet.has(num)) {
|
|
3466
3734
|
failures.push({
|
|
3735
|
+
code: "orphan-task",
|
|
3467
3736
|
issue: num,
|
|
3468
3737
|
tranche: slug,
|
|
3469
3738
|
reason: `Issue #${num} is open and labeled ${trancheLabel(slug)} but does not appear in the topology file`
|
|
@@ -3498,6 +3767,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
|
|
|
3498
3767
|
continue;
|
|
3499
3768
|
if (forgeUnavailableSlugs?.has(e.trancheSlug)) {
|
|
3500
3769
|
failures.push({
|
|
3770
|
+
code: "tbd-in-active-tranche",
|
|
3501
3771
|
issue: null,
|
|
3502
3772
|
tranche: e.trancheSlug,
|
|
3503
3773
|
task: e.task.id,
|
|
@@ -3507,6 +3777,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
|
|
|
3507
3777
|
continue;
|
|
3508
3778
|
}
|
|
3509
3779
|
failures.push({
|
|
3780
|
+
code: "tbd-in-active-tranche",
|
|
3510
3781
|
issue: null,
|
|
3511
3782
|
tranche: e.trancheSlug,
|
|
3512
3783
|
task: e.task.id,
|
|
@@ -3539,6 +3810,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
|
|
|
3539
3810
|
const sameIssue = depEntry.task.issue !== null && e.task.issue !== null && depEntry.task.issue === e.task.issue;
|
|
3540
3811
|
if (sameTask || sameIssue) {
|
|
3541
3812
|
failures.push({
|
|
3813
|
+
code: "d1-self-dependency",
|
|
3542
3814
|
issue: e.task.issue,
|
|
3543
3815
|
tranche: e.trancheSlug,
|
|
3544
3816
|
task: e.task.id,
|
|
@@ -3550,6 +3822,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
|
|
|
3550
3822
|
const depClosed = depFacts?.issueState === "closed";
|
|
3551
3823
|
if (!depClosed) {
|
|
3552
3824
|
failures.push({
|
|
3825
|
+
code: "dispatched-on-unmet-deps",
|
|
3553
3826
|
issue: e.task.issue,
|
|
3554
3827
|
tranche: e.trancheSlug,
|
|
3555
3828
|
task: e.task.id,
|
|
@@ -3575,11 +3848,13 @@ function checkR1(issuesBySlug, grandfatheredIssues, registeredNames = []) {
|
|
|
3575
3848
|
const errors = [
|
|
3576
3849
|
...checkIssueRationale(issue.body).errors,
|
|
3577
3850
|
...checkProjectsRegistered(issue.body, issue.labels, registeredNames).errors,
|
|
3578
|
-
...checkIssueObjectives(issue.body, issue.number).errors
|
|
3851
|
+
...checkIssueObjectives(issue.body, issue.number).errors,
|
|
3852
|
+
...checkPartsCiteDefinedObjectives(issue.body).errors
|
|
3579
3853
|
];
|
|
3580
3854
|
if (errors.length === 0)
|
|
3581
3855
|
continue;
|
|
3582
3856
|
failures.push({
|
|
3857
|
+
code: "missing-rationale-field",
|
|
3583
3858
|
issue: issue.number,
|
|
3584
3859
|
tranche: slug,
|
|
3585
3860
|
reason: `Issue #${issue.number} fails the rationale gate: ${errors.join(" | ")}`,
|
|
@@ -3608,6 +3883,7 @@ function checkL1(files, entriesBySlug) {
|
|
|
3608
3883
|
const allClosed = withFacts.every((e) => e.facts?.issueState === "closed");
|
|
3609
3884
|
if (allClosed) {
|
|
3610
3885
|
failures.push({
|
|
3886
|
+
code: "archive-recommended",
|
|
3611
3887
|
tranche: f.slug,
|
|
3612
3888
|
reason: "Active tranche has no open task-Issues — consider archiving to completed/"
|
|
3613
3889
|
});
|
|
@@ -3639,27 +3915,38 @@ function extractClosesReferences(prBody) {
|
|
|
3639
3915
|
}
|
|
3640
3916
|
return referenced;
|
|
3641
3917
|
}
|
|
3642
|
-
function
|
|
3918
|
+
function checkClosesNTopology(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
3643
3919
|
const referenced = extractClosesReferences(prBody);
|
|
3644
3920
|
if (taskIssueRefs) {
|
|
3645
3921
|
for (const n of referenced) {
|
|
3646
|
-
const
|
|
3647
|
-
if (!
|
|
3922
|
+
const ref2 = taskIssueRefs.get(n);
|
|
3923
|
+
if (!ref2)
|
|
3648
3924
|
continue;
|
|
3649
|
-
const expectedBranch = `task/${
|
|
3925
|
+
const expectedBranch = `task/${ref2.trancheSlug}/${ref2.taskId}`;
|
|
3650
3926
|
if (branch !== expectedBranch) {
|
|
3651
3927
|
return {
|
|
3652
3928
|
ok: false,
|
|
3653
|
-
message: `closes-n-reverse: branch "${branch}" closes #${n} (task ${
|
|
3929
|
+
message: `closes-n-reverse: branch "${branch}" closes #${n} (task ${ref2.taskId} of tranche "${ref2.trancheSlug}") but is not named "${expectedBranch}" — rename the branch and re-push, or if this work is intentionally outside AEG's dispatch flow, remove the Closes reference.`
|
|
3654
3930
|
};
|
|
3655
3931
|
}
|
|
3656
3932
|
}
|
|
3657
3933
|
}
|
|
3658
|
-
const
|
|
3659
|
-
if (!
|
|
3934
|
+
const ref = parseTaskBranchIdentity(branch);
|
|
3935
|
+
if (!ref)
|
|
3660
3936
|
return { ok: true };
|
|
3661
|
-
|
|
3662
|
-
|
|
3937
|
+
if (ref.kind === "issue") {
|
|
3938
|
+
const expectedIssue2 = ref.issueNumber;
|
|
3939
|
+
if (!referenced.has(expectedIssue2)) {
|
|
3940
|
+
return {
|
|
3941
|
+
ok: false,
|
|
3942
|
+
expectedIssue: expectedIssue2,
|
|
3943
|
+
message: `closes-n: PR body does not contain \`Closes #${expectedIssue2}\` (required for branch "${branch}"). Add it to the PR body Summary section.`
|
|
3944
|
+
};
|
|
3945
|
+
}
|
|
3946
|
+
return { ok: true, expectedIssue: expectedIssue2 };
|
|
3947
|
+
}
|
|
3948
|
+
const trancheSlug = ref.tranche;
|
|
3949
|
+
const taskId = ref.taskId;
|
|
3663
3950
|
const trancheFile = trancheFiles.find((f) => f.slug === trancheSlug);
|
|
3664
3951
|
if (!trancheFile) {
|
|
3665
3952
|
return {
|
|
@@ -3711,34 +3998,79 @@ function extractIssue(body) {
|
|
|
3711
3998
|
const issue = headerNums.length > 0 ? headerNums[0] : bodyNums[0];
|
|
3712
3999
|
return { issue, extraIssues: bodyNums.filter((n) => n !== issue), outsideHeader: headerNums.length === 0 };
|
|
3713
4000
|
}
|
|
3714
|
-
// ../../packages/aeg-core/src/review-
|
|
3715
|
-
|
|
3716
|
-
|
|
4001
|
+
// ../../packages/aeg-core/src/review-input-manifest.ts
|
|
4002
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
4003
|
+
function briefHash(brief) {
|
|
4004
|
+
return createHash4("sha256").update(`${brief}
|
|
4005
|
+
`).digest("hex");
|
|
4006
|
+
}
|
|
4007
|
+
function policyDigest(policy) {
|
|
4008
|
+
return createHash4("sha256").update(JSON.stringify({
|
|
4009
|
+
codeReviewThreshold: policy.codeReviewThreshold,
|
|
4010
|
+
securityThreshold: policy.securityThreshold,
|
|
4011
|
+
maxRounds: policy.maxRounds
|
|
4012
|
+
})).digest("hex");
|
|
4013
|
+
}
|
|
4014
|
+
function isBoundToHead(echoed, headSha) {
|
|
4015
|
+
if (!echoed.headSha)
|
|
3717
4016
|
return false;
|
|
3718
|
-
return headSha.toLowerCase().startsWith(
|
|
4017
|
+
return headSha.toLowerCase().startsWith(echoed.headSha.toLowerCase());
|
|
3719
4018
|
}
|
|
3720
|
-
function isBoundByPatchIdentity(
|
|
3721
|
-
if (patchIdOf === undefined || !
|
|
4019
|
+
function isBoundByPatchIdentity(echoed, headSha, patchIdOf) {
|
|
4020
|
+
if (patchIdOf === undefined || !echoed.headSha)
|
|
3722
4021
|
return false;
|
|
3723
|
-
const judged = patchIdOf(
|
|
4022
|
+
const judged = patchIdOf(echoed.headSha);
|
|
3724
4023
|
const current = patchIdOf(headSha);
|
|
3725
4024
|
if (judged === null || current === null)
|
|
3726
4025
|
return false;
|
|
3727
4026
|
return judged === current;
|
|
3728
4027
|
}
|
|
3729
|
-
function
|
|
3730
|
-
|
|
4028
|
+
function isBoundToBase(echoed, currentBase) {
|
|
4029
|
+
if (currentBase === null)
|
|
4030
|
+
return true;
|
|
4031
|
+
if (!echoed.baseSha)
|
|
4032
|
+
return false;
|
|
4033
|
+
return currentBase.toLowerCase().startsWith(echoed.baseSha.toLowerCase());
|
|
3731
4034
|
}
|
|
3732
|
-
function isBoundToObjectives(
|
|
4035
|
+
function isBoundToObjectives(echoed, currentVersion) {
|
|
3733
4036
|
if (currentVersion === null)
|
|
3734
4037
|
return true;
|
|
3735
|
-
return
|
|
4038
|
+
return echoed.objectivesVersion === currentVersion;
|
|
3736
4039
|
}
|
|
3737
|
-
function isBoundToRulings(
|
|
3738
|
-
if (
|
|
4040
|
+
function isBoundToRulings(echoed, currentOrdinal) {
|
|
4041
|
+
if (echoed.rulingOrdinal === null)
|
|
3739
4042
|
return currentOrdinal === 0;
|
|
3740
|
-
return
|
|
4043
|
+
return echoed.rulingOrdinal === currentOrdinal;
|
|
3741
4044
|
}
|
|
4045
|
+
function isBoundToBriefHash(echoed, currentHash) {
|
|
4046
|
+
if (currentHash === null)
|
|
4047
|
+
return true;
|
|
4048
|
+
return echoed.briefHash === currentHash;
|
|
4049
|
+
}
|
|
4050
|
+
function isBoundToPolicy(echoed, currentDigest) {
|
|
4051
|
+
return echoed.policyDigest === currentDigest;
|
|
4052
|
+
}
|
|
4053
|
+
function compareManifest(echoed, current, patchIdOf) {
|
|
4054
|
+
const exactHead = isBoundToHead(echoed, current.headSha);
|
|
4055
|
+
const patchHead = exactHead ? false : isBoundByPatchIdentity(echoed, current.headSha, patchIdOf);
|
|
4056
|
+
const head = exactHead || patchHead;
|
|
4057
|
+
const base = patchHead ? true : isBoundToBase(echoed, current.baseSha);
|
|
4058
|
+
const briefHashBound = isBoundToBriefHash(echoed, current.briefHash);
|
|
4059
|
+
const objectivesVersion2 = isBoundToObjectives(echoed, current.objectivesVersion);
|
|
4060
|
+
const rulingOrdinal = isBoundToRulings(echoed, current.rulingOrdinal);
|
|
4061
|
+
const policyDigestBound = isBoundToPolicy(echoed, current.policyDigest);
|
|
4062
|
+
return {
|
|
4063
|
+
bound: head && base && briefHashBound && objectivesVersion2 && rulingOrdinal && policyDigestBound,
|
|
4064
|
+
head,
|
|
4065
|
+
base,
|
|
4066
|
+
briefHash: briefHashBound,
|
|
4067
|
+
objectivesVersion: objectivesVersion2,
|
|
4068
|
+
rulingOrdinal,
|
|
4069
|
+
policyDigest: policyDigestBound
|
|
4070
|
+
};
|
|
4071
|
+
}
|
|
4072
|
+
|
|
4073
|
+
// ../../packages/aeg-core/src/review-gate.ts
|
|
3742
4074
|
function checkReviewGate(input) {
|
|
3743
4075
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
3744
4076
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -3761,15 +4093,58 @@ function checkReviewGate(input) {
|
|
|
3761
4093
|
const verifiedBodies = verified.map((c) => c.body);
|
|
3762
4094
|
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
3763
4095
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
3764
|
-
const
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
4096
|
+
const policy = input.policy ?? DEFAULT_REVIEW_POLICY;
|
|
4097
|
+
let codeReviewPolicyEvaluation;
|
|
4098
|
+
let securityPolicyEvaluation;
|
|
4099
|
+
try {
|
|
4100
|
+
codeReviewPolicyEvaluation = evaluateCodeReview(codeReview.findingSeverities, policy);
|
|
4101
|
+
securityPolicyEvaluation = evaluateSecurityReview(security.findingSeverities, policy);
|
|
4102
|
+
} catch (err) {
|
|
4103
|
+
return {
|
|
4104
|
+
verdict: "fail",
|
|
4105
|
+
reason: `a verdict comment carries a finding severity this repository's policy does not recognize: ${err instanceof Error ? err.message : String(err)}`,
|
|
4106
|
+
waived: false
|
|
4107
|
+
};
|
|
4108
|
+
}
|
|
4109
|
+
const codeReviewTextClean = codeReview.value === "APPROVE";
|
|
4110
|
+
const securityTextClean = security.value === "PASS";
|
|
4111
|
+
const codeReviewPolicyClean = codeReviewPolicyEvaluation.outcome === "clean";
|
|
4112
|
+
const securityPolicyClean = securityPolicyEvaluation.outcome === "clean";
|
|
4113
|
+
const codeReviewClean = codeReviewTextClean && codeReviewPolicyClean;
|
|
4114
|
+
const securityClean = securityTextClean && securityPolicyClean;
|
|
4115
|
+
const currentManifest = {
|
|
4116
|
+
headSha: input.headSha,
|
|
4117
|
+
baseSha: input.baseSha ?? null,
|
|
4118
|
+
briefHash: input.briefHash ?? null,
|
|
4119
|
+
objectivesVersion: input.objectivesVersion,
|
|
4120
|
+
rulingOrdinal: input.rulingOrdinal,
|
|
4121
|
+
policyDigest: policyDigest(policy)
|
|
4122
|
+
};
|
|
4123
|
+
const codeReviewEchoed = {
|
|
4124
|
+
headSha: codeReview.headSha,
|
|
4125
|
+
baseSha: codeReview.baseSha,
|
|
4126
|
+
briefHash: codeReview.briefHash,
|
|
4127
|
+
objectivesVersion: codeReview.objectivesVersion,
|
|
4128
|
+
rulingOrdinal: codeReview.rulingOrdinal,
|
|
4129
|
+
policyDigest: codeReview.policyDigest
|
|
4130
|
+
};
|
|
4131
|
+
const securityEchoed = {
|
|
4132
|
+
headSha: security.headSha,
|
|
4133
|
+
baseSha: security.baseSha,
|
|
4134
|
+
briefHash: security.briefHash,
|
|
4135
|
+
objectivesVersion: security.objectivesVersion,
|
|
4136
|
+
rulingOrdinal: security.rulingOrdinal,
|
|
4137
|
+
policyDigest: security.policyDigest
|
|
4138
|
+
};
|
|
4139
|
+
const codeReviewBinding = compareManifest(codeReviewEchoed, currentManifest, input.patchIdOf);
|
|
4140
|
+
const securityBinding = compareManifest(securityEchoed, currentManifest, input.patchIdOf);
|
|
4141
|
+
const codeReviewBound = codeReviewBinding.head;
|
|
4142
|
+
const securityBound = securityBinding.head;
|
|
4143
|
+
const codeReviewObjectivesBound = codeReviewBinding.objectivesVersion;
|
|
4144
|
+
const securityObjectivesBound = securityBinding.objectivesVersion;
|
|
4145
|
+
const codeReviewRulingsBound = codeReviewBinding.rulingOrdinal;
|
|
4146
|
+
const securityRulingsBound = securityBinding.rulingOrdinal;
|
|
4147
|
+
if (codeReviewClean && codeReviewBinding.bound && securityClean && securityBinding.bound && mechanicalChecksClean) {
|
|
3773
4148
|
return {
|
|
3774
4149
|
verdict: "pass",
|
|
3775
4150
|
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.`,
|
|
@@ -3777,23 +4152,39 @@ function checkReviewGate(input) {
|
|
|
3777
4152
|
};
|
|
3778
4153
|
}
|
|
3779
4154
|
const problems = [];
|
|
3780
|
-
if (!
|
|
4155
|
+
if (!codeReviewTextClean) {
|
|
3781
4156
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
4157
|
+
} else if (!codeReviewPolicyClean) {
|
|
4158
|
+
problems.push(`code-reviewer verdict says APPROVE but carries a finding (${codeReviewPolicyEvaluation.blockingFindings.map((f) => f.severity).join(", ")}) at or above this repository's code-review policy threshold (${policy.codeReviewThreshold}) — a reviewer's own APPROVE never overrides policy`);
|
|
3782
4159
|
} else if (!codeReviewBound) {
|
|
3783
4160
|
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
4161
|
+
} else if (!codeReviewBinding.base) {
|
|
4162
|
+
problems.push(`the newest code-review verdict was cast against base ${codeReview.baseSha ?? "no recorded base"}, the PR's base is now ${currentManifest.baseSha ?? "unresolved"} (a base-only change under an unchanged candidate — same patch text on a new base is not automatically equivalent)`);
|
|
3784
4163
|
} else if (!codeReviewObjectivesBound) {
|
|
3785
4164
|
problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3786
4165
|
} else if (!codeReviewRulingsBound) {
|
|
3787
4166
|
problems.push(`the newest code-review verdict was cast against ruling ordinal ${codeReview.rulingOrdinal ?? "none"}, a newer ruling (ruling ${input.rulingOrdinal}) is now posted on this PR`);
|
|
4167
|
+
} else if (!codeReviewBinding.briefHash) {
|
|
4168
|
+
problems.push(`the newest code-review verdict was cast against brief hash ${codeReview.briefHash ?? "none"}, the frozen brief's current hash is ${currentManifest.briefHash ?? "none"}`);
|
|
4169
|
+
} else if (!codeReviewBinding.policyDigest) {
|
|
4170
|
+
problems.push(`the newest code-review verdict was cast against review policy digest ${codeReview.policyDigest ?? "none"}, this repository's current policy digest is ${currentManifest.policyDigest}`);
|
|
3788
4171
|
}
|
|
3789
|
-
if (!
|
|
4172
|
+
if (!securityTextClean) {
|
|
3790
4173
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
4174
|
+
} else if (!securityPolicyClean) {
|
|
4175
|
+
problems.push(`security-review verdict says PASS but carries a finding (${securityPolicyEvaluation.blockingFindings.map((f) => f.severity).join(", ")}) at or above this repository's security policy threshold (${policy.securityThreshold}) — a reviewer's own PASS never overrides policy`);
|
|
3791
4176
|
} else if (!securityBound) {
|
|
3792
4177
|
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
4178
|
+
} else if (!securityBinding.base) {
|
|
4179
|
+
problems.push(`the newest security-review verdict was cast against base ${security.baseSha ?? "no recorded base"}, the PR's base is now ${currentManifest.baseSha ?? "unresolved"} (a base-only change under an unchanged candidate — same patch text on a new base is not automatically equivalent)`);
|
|
3793
4180
|
} else if (!securityObjectivesBound) {
|
|
3794
4181
|
problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3795
4182
|
} else if (!securityRulingsBound) {
|
|
3796
4183
|
problems.push(`the newest security-review verdict was cast against ruling ordinal ${security.rulingOrdinal ?? "none"}, a newer ruling (ruling ${input.rulingOrdinal}) is now posted on this PR`);
|
|
4184
|
+
} else if (!securityBinding.briefHash) {
|
|
4185
|
+
problems.push(`the newest security-review verdict was cast against brief hash ${security.briefHash ?? "none"}, the frozen brief's current hash is ${currentManifest.briefHash ?? "none"}`);
|
|
4186
|
+
} else if (!securityBinding.policyDigest) {
|
|
4187
|
+
problems.push(`the newest security-review verdict was cast against review policy digest ${security.policyDigest ?? "none"}, this repository's current policy digest is ${currentManifest.policyDigest}`);
|
|
3797
4188
|
}
|
|
3798
4189
|
if (!mechanicalChecksClean) {
|
|
3799
4190
|
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(", ")}`);
|
|
@@ -4485,42 +4876,45 @@ function checkDispatchReadiness(input) {
|
|
|
4485
4876
|
const { trancheSlug, task } = input;
|
|
4486
4877
|
const taskLabel = `task ${task.id} (tranche ${trancheSlug})`;
|
|
4487
4878
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
4488
|
-
const
|
|
4879
|
+
const blockerDetails = [];
|
|
4880
|
+
const push = (blockerClass, message) => {
|
|
4881
|
+
blockerDetails.push({ class: blockerClass, message });
|
|
4882
|
+
};
|
|
4489
4883
|
if (task.issue === null) {
|
|
4490
|
-
|
|
4884
|
+
push("issue-existence", `dispatch-gate issue-existence: ${taskLabel} has no Issue (#TBD or blank) in the topology — not dispatchable until the Planner cuts the Issue.`);
|
|
4491
4885
|
} else if (input.issue === null) {
|
|
4492
|
-
|
|
4886
|
+
push("issue-existence", `dispatch-gate issue-existence: ${taskLabel} names Issue #${task.issue}, but it does not resolve to a real GitHub Issue (phantom reference).`);
|
|
4493
4887
|
}
|
|
4494
4888
|
if (input.issue !== null && !input.issueRationalePass) {
|
|
4495
|
-
|
|
4889
|
+
push("rationale", `dispatch-gate rationale: Issue #${input.issue.number} for ${taskLabel} fails the rationale gate (checkIssueRationale) — the Planner must complete the eight-field rationale before this task is dispatchable.`);
|
|
4496
4890
|
}
|
|
4497
4891
|
for (const dep of input.dependsOn) {
|
|
4498
4892
|
if (isSelfDependency(dep, task, input.issue)) {
|
|
4499
4893
|
const issueStr = input.issue !== null ? `#${input.issue.number}` : "?";
|
|
4500
|
-
|
|
4894
|
+
push("internal-self-dependency", `dispatch-gate INTERNAL: parsed a self-dependency for task ${task.id} (${issueStr}) — this is a parser bug in parseRationaleDeps, not a real dependency. Please report it upstream. Re-run once the rationale is corrected or the fix ships.`);
|
|
4501
4895
|
continue;
|
|
4502
4896
|
}
|
|
4503
4897
|
if (dep.resolved === false) {
|
|
4504
|
-
|
|
4898
|
+
push("depends-on-unresolvable", `dispatch-gate depends-on: ${taskLabel} depends on "${dep.id}", which is UNRESOLVABLE — the resolver could not find a matching tranche/task/Issue for this edge (not a claim about merge status). Not dispatchable until the edge is corrected.`);
|
|
4505
4899
|
continue;
|
|
4506
4900
|
}
|
|
4507
4901
|
if (!dep.merged && !isHandClosedByRecognizedPrincipal(dep, principalAllowlist)) {
|
|
4508
4902
|
const issueStr = dep.issue !== null ? ` (#${dep.issue})` : "";
|
|
4509
|
-
|
|
4903
|
+
push("depends-on-not-merged", `dispatch-gate depends-on: ${taskLabel} depends on ${dep.id}${issueStr}, whose PR is not merged yet — not dispatchable, it serializes behind it.`);
|
|
4510
4904
|
}
|
|
4511
4905
|
}
|
|
4512
4906
|
for (const c of input.conflictsWith) {
|
|
4513
4907
|
if (c.openOrInFlight) {
|
|
4514
4908
|
const issueStr = c.issue !== null ? ` (#${c.issue})` : "";
|
|
4515
|
-
|
|
4909
|
+
push("conflicts-with", `dispatch-gate conflicts-with: ${taskLabel} conflicts with ${c.id}${issueStr}, whose PR is open or in-flight — not dispatchable until it merges.`);
|
|
4516
4910
|
}
|
|
4517
4911
|
}
|
|
4518
4912
|
for (const proj of input.priorTrancheArchival) {
|
|
4519
4913
|
if (proj.priorTrancheSlug !== null && !proj.archived) {
|
|
4520
|
-
|
|
4914
|
+
push("prior-tranche-archival", `dispatch-gate prior-tranche-archival: project \`${proj.project}\`'s previous tranche \`${proj.priorTrancheSlug}\` is not archived — the Tranche Archivist must run before new work on this product.`);
|
|
4521
4915
|
}
|
|
4522
4916
|
}
|
|
4523
|
-
return { ready:
|
|
4917
|
+
return { ready: blockerDetails.length === 0, blockers: blockerDetails.map((b) => b.message), blockerDetails };
|
|
4524
4918
|
}
|
|
4525
4919
|
// ../../packages/aeg-core/src/single-plan-pr.ts
|
|
4526
4920
|
function trancheSlugFromTopologyPath(path) {
|
|
@@ -4622,6 +5016,137 @@ function checkBranchTopology(input) {
|
|
|
4622
5016
|
reason: `Branch \`${branch}\` matches topology row \`${taskId}\` in ${topoPath}.`
|
|
4623
5017
|
};
|
|
4624
5018
|
}
|
|
5019
|
+
// ../../packages/aeg-core/src/task-tools.ts
|
|
5020
|
+
import { z } from "zod";
|
|
5021
|
+
var TASK_TOOL_ERROR_KINDS = [
|
|
5022
|
+
"validation",
|
|
5023
|
+
"authority",
|
|
5024
|
+
"precondition",
|
|
5025
|
+
"capability",
|
|
5026
|
+
"infrastructure",
|
|
5027
|
+
"cancellation",
|
|
5028
|
+
"timeout",
|
|
5029
|
+
"uncertain_effect"
|
|
5030
|
+
];
|
|
5031
|
+
var TaskToolErrorSchema = z.object({
|
|
5032
|
+
kind: z.enum(TASK_TOOL_ERROR_KINDS),
|
|
5033
|
+
message: z.string().min(1),
|
|
5034
|
+
detail: z.string().optional()
|
|
5035
|
+
});
|
|
5036
|
+
var TaskToolRefSchema = z.union([
|
|
5037
|
+
z.object({ tranche: z.string().min(1), id: z.string().min(1) }).strict(),
|
|
5038
|
+
z.object({ issue: z.number().int().positive() }).strict()
|
|
5039
|
+
]);
|
|
5040
|
+
var MAX_PAGE_LIMIT = 100;
|
|
5041
|
+
var PageRequestSchema = z.object({
|
|
5042
|
+
cursor: z.string().optional(),
|
|
5043
|
+
limit: z.number().int().positive().max(MAX_PAGE_LIMIT).optional()
|
|
5044
|
+
});
|
|
5045
|
+
var FreshnessSchema = z.enum(["fresh", "stale", "unknown"]);
|
|
5046
|
+
var ObservedSchema = z.object({
|
|
5047
|
+
observedAt: z.string(),
|
|
5048
|
+
freshness: FreshnessSchema
|
|
5049
|
+
});
|
|
5050
|
+
var TaskStatusInputSchema = z.object({
|
|
5051
|
+
task: TaskToolRefSchema.optional()
|
|
5052
|
+
}).merge(PageRequestSchema).strict();
|
|
5053
|
+
var TaskStatusItemSchema = z.object({
|
|
5054
|
+
task: TaskToolRefSchema,
|
|
5055
|
+
issue: z.number().int().positive(),
|
|
5056
|
+
pr: z.number().int().positive().nullable(),
|
|
5057
|
+
state: z.string()
|
|
5058
|
+
}).merge(ObservedSchema);
|
|
5059
|
+
var TaskStatusResultSchema = z.object({
|
|
5060
|
+
items: z.array(TaskStatusItemSchema),
|
|
5061
|
+
nextCursor: z.string().nullable()
|
|
5062
|
+
});
|
|
5063
|
+
var RequestedAuthoritySchema = z.enum(["planner", "principal", "operator", "self"]);
|
|
5064
|
+
var EscalationInputsSchema = z.object({
|
|
5065
|
+
task: z.number().int().positive(),
|
|
5066
|
+
round: z.number().int().nonnegative(),
|
|
5067
|
+
head: z.string(),
|
|
5068
|
+
branch: z.string(),
|
|
5069
|
+
prNumber: z.number().int().positive()
|
|
5070
|
+
});
|
|
5071
|
+
var EscalationEvidenceSchema = z.object({
|
|
5072
|
+
round: z.number().int().nonnegative(),
|
|
5073
|
+
reviewer: z.string().nullable(),
|
|
5074
|
+
security: z.string().nullable()
|
|
5075
|
+
});
|
|
5076
|
+
var EscalationRunIdentitySchema = z.object({
|
|
5077
|
+
runId: z.string(),
|
|
5078
|
+
pid: z.number().int().positive(),
|
|
5079
|
+
host: z.string()
|
|
5080
|
+
});
|
|
5081
|
+
var EscalationInputVersionsSchema = z.object({
|
|
5082
|
+
briefHash: z.string().nullable(),
|
|
5083
|
+
objectivesVersion: z.string().nullable(),
|
|
5084
|
+
rulingOrdinal: z.number().int().nonnegative(),
|
|
5085
|
+
policyDigest: z.string()
|
|
5086
|
+
});
|
|
5087
|
+
var TaskEscalationReadInputSchema = z.object({
|
|
5088
|
+
task: TaskToolRefSchema
|
|
5089
|
+
}).merge(PageRequestSchema).strict();
|
|
5090
|
+
var TaskEscalationPacketSchema = z.object({
|
|
5091
|
+
reason: z.string(),
|
|
5092
|
+
detail: z.string().nullable(),
|
|
5093
|
+
inputs: EscalationInputsSchema.nullable(),
|
|
5094
|
+
evidence: EscalationEvidenceSchema.nullable(),
|
|
5095
|
+
attemptedRecovery: z.string(),
|
|
5096
|
+
requestedAuthority: RequestedAuthoritySchema,
|
|
5097
|
+
permittedNextActions: z.array(z.string()),
|
|
5098
|
+
runIdentity: EscalationRunIdentitySchema.nullable(),
|
|
5099
|
+
inputVersions: EscalationInputVersionsSchema.nullable()
|
|
5100
|
+
}).merge(ObservedSchema);
|
|
5101
|
+
var TaskEscalationReadResultSchema = z.object({
|
|
5102
|
+
items: z.array(TaskEscalationPacketSchema),
|
|
5103
|
+
nextCursor: z.string().nullable()
|
|
5104
|
+
}).merge(ObservedSchema);
|
|
5105
|
+
var TaskStartInputSchema = z.object({
|
|
5106
|
+
tranche: z.string().min(1),
|
|
5107
|
+
id: z.string().min(1)
|
|
5108
|
+
}).strict();
|
|
5109
|
+
var TaskStartResultSchema = z.object({
|
|
5110
|
+
requestId: z.string().min(1),
|
|
5111
|
+
run: z.object({ tranche: z.string().min(1), id: z.string().min(1) }),
|
|
5112
|
+
started: z.boolean(),
|
|
5113
|
+
startedAt: z.string(),
|
|
5114
|
+
mode: z.literal("attended")
|
|
5115
|
+
});
|
|
5116
|
+
var TaskResumeInputSchema = z.object({
|
|
5117
|
+
task: TaskToolRefSchema
|
|
5118
|
+
}).strict();
|
|
5119
|
+
var TaskCancelInputSchema = z.object({
|
|
5120
|
+
task: TaskToolRefSchema,
|
|
5121
|
+
reason: z.string().min(1)
|
|
5122
|
+
}).strict();
|
|
5123
|
+
var TaskResumeResultSchema = z.object({
|
|
5124
|
+
task: z.number().int().positive(),
|
|
5125
|
+
pr: z.number().int().positive(),
|
|
5126
|
+
escalationId: z.string().min(1),
|
|
5127
|
+
outcome: z.enum(["started", "already_resumed"]),
|
|
5128
|
+
authenticatedBy: z.string().min(1),
|
|
5129
|
+
authenticatedFrom: z.string().min(1)
|
|
5130
|
+
});
|
|
5131
|
+
var TaskCancelOutcomeSchema = z.enum(["confirmed", "pending", "uncertain"]);
|
|
5132
|
+
var TaskCancelResultSchema = z.object({
|
|
5133
|
+
task: z.number().int().positive(),
|
|
5134
|
+
pr: z.number().int().positive(),
|
|
5135
|
+
escalationId: z.string().min(1),
|
|
5136
|
+
outcome: TaskCancelOutcomeSchema,
|
|
5137
|
+
authenticatedBy: z.string().min(1),
|
|
5138
|
+
authenticatedFrom: z.string().min(1),
|
|
5139
|
+
fencedEffectKeys: z.array(z.string())
|
|
5140
|
+
});
|
|
5141
|
+
var TASK_TOOL_NAMES = [
|
|
5142
|
+
"task_start",
|
|
5143
|
+
"task_status",
|
|
5144
|
+
"task_escalation_read",
|
|
5145
|
+
"task_resume",
|
|
5146
|
+
"task_cancel"
|
|
5147
|
+
];
|
|
5148
|
+
var OPERATOR_STATUS_FOLLOW = "task_status_follow";
|
|
5149
|
+
var OPERATOR_TOOL_GRANT = [...TASK_TOOL_NAMES, OPERATOR_STATUS_FOLLOW];
|
|
4625
5150
|
// ../../packages/aeg-core/src/first-push-dispatch-gate.ts
|
|
4626
5151
|
function parseTaskBranch(branch) {
|
|
4627
5152
|
const m = /^task\/([^/]+)\/([^/]+)$/.exec(branch);
|
|
@@ -4705,7 +5230,6 @@ function decideIssueAssignment(input) {
|
|
|
4705
5230
|
};
|
|
4706
5231
|
}
|
|
4707
5232
|
// ../../packages/aeg-core/src/test-plan-gate.ts
|
|
4708
|
-
var TASK_BRANCH_PATTERN2 = /^task\/[^/]+\/[^/]+$/;
|
|
4709
5233
|
function evaluateTestPlanGate(body, branch) {
|
|
4710
5234
|
if (!body) {
|
|
4711
5235
|
return {
|
|
@@ -4718,7 +5242,7 @@ function evaluateTestPlanGate(body, branch) {
|
|
|
4718
5242
|
}
|
|
4719
5243
|
const located = locateTestPlanSection(body);
|
|
4720
5244
|
if (!located.found) {
|
|
4721
|
-
if (
|
|
5245
|
+
if (parseTaskBranchIdentity(branch) !== null) {
|
|
4722
5246
|
return {
|
|
4723
5247
|
verdict: "fail",
|
|
4724
5248
|
messages: [
|
|
@@ -4845,8 +5369,11 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
|
|
|
4845
5369
|
}
|
|
4846
5370
|
return findings;
|
|
4847
5371
|
}
|
|
5372
|
+
// ../../packages/aeg-core/src/log/envelope.ts
|
|
5373
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
5374
|
+
|
|
4848
5375
|
// ../../packages/aeg-core/src/log/schema.ts
|
|
4849
|
-
import { z } from "zod";
|
|
5376
|
+
import { z as z2 } from "zod";
|
|
4850
5377
|
var ROLE_VALUES = [
|
|
4851
5378
|
"planner",
|
|
4852
5379
|
"developer",
|
|
@@ -4856,136 +5383,174 @@ var ROLE_VALUES = [
|
|
|
4856
5383
|
"archivist",
|
|
4857
5384
|
"architect"
|
|
4858
5385
|
];
|
|
4859
|
-
var RoleSchema =
|
|
5386
|
+
var RoleSchema = z2.enum(ROLE_VALUES);
|
|
4860
5387
|
var HOST_VALUES = ["hook", "ci", "cli", "loop"];
|
|
4861
|
-
var HostSchema =
|
|
5388
|
+
var HostSchema = z2.enum(HOST_VALUES);
|
|
4862
5389
|
var RUN_ID_PATTERN = /^[A-Za-z0-9_.-]{1,128}$/;
|
|
4863
|
-
var
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
doctrine: z.string(),
|
|
5390
|
+
var headerMetaCore = {
|
|
5391
|
+
ts: z2.string(),
|
|
5392
|
+
run_id: z2.string().regex(RUN_ID_PATTERN),
|
|
5393
|
+
seq: z2.number().int().nonnegative(),
|
|
5394
|
+
repo: z2.string().nullable(),
|
|
5395
|
+
vinaya: z2.string(),
|
|
5396
|
+
doctrine: z2.string(),
|
|
4871
5397
|
host: HostSchema,
|
|
4872
|
-
machine:
|
|
5398
|
+
machine: z2.string()
|
|
5399
|
+
};
|
|
5400
|
+
var HeaderMetaV1Schema = z2.object({
|
|
5401
|
+
schema: z2.literal(1),
|
|
5402
|
+
...headerMetaCore
|
|
4873
5403
|
}).strict();
|
|
4874
|
-
var
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
role: z.union([RoleSchema, z.literal("unattributed")]),
|
|
4879
|
-
round: z.number().int().optional(),
|
|
4880
|
-
objectives_version: z.string().optional()
|
|
5404
|
+
var LineageSchema = z2.object({
|
|
5405
|
+
run: z2.string().nullable(),
|
|
5406
|
+
attempt: z2.number().int().nullable(),
|
|
5407
|
+
parent: z2.string().nullable()
|
|
4881
5408
|
}).strict();
|
|
4882
|
-
var
|
|
5409
|
+
var InputVersionsSchema = z2.object({
|
|
5410
|
+
objectives_version: z2.string().nullable(),
|
|
5411
|
+
brief_hash: z2.string().nullable(),
|
|
5412
|
+
ruling_ordinal: z2.number().int().nullable(),
|
|
5413
|
+
policy_digest: z2.string().nullable()
|
|
5414
|
+
}).strict();
|
|
5415
|
+
var ProvenanceSchema = z2.enum(["parent_attributed", "env_correlated", "self_reported", "unavailable"]);
|
|
5416
|
+
var HeaderMetaV2Schema = z2.object({
|
|
5417
|
+
schema: z2.literal(2),
|
|
5418
|
+
...headerMetaCore,
|
|
5419
|
+
event_id: z2.string().min(1),
|
|
5420
|
+
process_id: z2.string().min(1),
|
|
5421
|
+
actor_id: z2.string().nullable(),
|
|
5422
|
+
lineage: LineageSchema,
|
|
5423
|
+
input_versions: InputVersionsSchema,
|
|
5424
|
+
provenance: ProvenanceSchema
|
|
5425
|
+
}).strict();
|
|
5426
|
+
var HeaderMetaSchema = z2.discriminatedUnion("schema", [HeaderMetaV1Schema, HeaderMetaV2Schema]);
|
|
5427
|
+
var SubjectSchema = z2.object({
|
|
5428
|
+
issue: z2.number().int().nullable(),
|
|
5429
|
+
pr: z2.number().int().optional(),
|
|
5430
|
+
sha: z2.string().optional(),
|
|
5431
|
+
role: z2.union([RoleSchema, z2.literal("unattributed")]),
|
|
5432
|
+
round: z2.number().int().optional(),
|
|
5433
|
+
objectives_version: z2.string().optional()
|
|
5434
|
+
}).strict();
|
|
5435
|
+
var HeaderSchema = z2.object({
|
|
4883
5436
|
meta: HeaderMetaSchema,
|
|
4884
5437
|
subject: SubjectSchema
|
|
4885
5438
|
}).strict();
|
|
4886
5439
|
var envelopeTail = {
|
|
4887
|
-
duration_ms:
|
|
4888
|
-
payload:
|
|
5440
|
+
duration_ms: z2.number().nonnegative().optional(),
|
|
5441
|
+
payload: z2.object({}).strict()
|
|
4889
5442
|
};
|
|
4890
|
-
var
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
5443
|
+
var ReviewFindingSchema = z2.object({
|
|
5444
|
+
id: z2.string(),
|
|
5445
|
+
severity: z2.string(),
|
|
5446
|
+
state: z2.string().optional(),
|
|
5447
|
+
severity_scale: z2.string().optional(),
|
|
5448
|
+
policy_treatment: z2.enum(["blocking", "non_blocking", "unavailable"]).optional(),
|
|
5449
|
+
confidence: z2.number().min(0).max(1).optional(),
|
|
5450
|
+
confidence_scale: z2.string().optional(),
|
|
5451
|
+
confidence_source: z2.string().optional()
|
|
5452
|
+
}).strict();
|
|
5453
|
+
var DispatchOutcomeSchema = z2.discriminatedUnion("type", [
|
|
5454
|
+
z2.object({ type: z2.literal("pr_opened"), pr: z2.number().int(), head: z2.string() }).strict(),
|
|
5455
|
+
z2.object({
|
|
5456
|
+
type: z2.literal("round_pushed"),
|
|
5457
|
+
pr: z2.number().int(),
|
|
5458
|
+
head: z2.string(),
|
|
5459
|
+
comment_id: z2.number().int()
|
|
4897
5460
|
}).strict(),
|
|
4898
|
-
|
|
4899
|
-
type:
|
|
4900
|
-
verdict:
|
|
4901
|
-
head:
|
|
4902
|
-
comment_id:
|
|
4903
|
-
objectives:
|
|
4904
|
-
findings:
|
|
5461
|
+
z2.object({
|
|
5462
|
+
type: z2.literal("verdict"),
|
|
5463
|
+
verdict: z2.enum(["APPROVE", "REQUEST CHANGES", "PASS", "FAIL"]),
|
|
5464
|
+
head: z2.string(),
|
|
5465
|
+
comment_id: z2.number().int(),
|
|
5466
|
+
objectives: z2.array(z2.object({ id: z2.string(), met: z2.boolean() }).strict()),
|
|
5467
|
+
findings: z2.array(ReviewFindingSchema)
|
|
4905
5468
|
}).strict(),
|
|
4906
|
-
|
|
4907
|
-
type:
|
|
4908
|
-
class:
|
|
4909
|
-
comment_id:
|
|
5469
|
+
z2.object({
|
|
5470
|
+
type: z2.literal("escalation"),
|
|
5471
|
+
class: z2.enum(["authority", "strategy", "product"]),
|
|
5472
|
+
comment_id: z2.number().int()
|
|
4910
5473
|
}).strict(),
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
5474
|
+
z2.object({ type: z2.literal("brief"), comment_id: z2.number().int(), hash: z2.string() }).strict(),
|
|
5475
|
+
z2.object({ type: z2.literal("plan"), issues: z2.array(z2.number().int()) }).strict(),
|
|
5476
|
+
z2.object({ type: z2.literal("archive"), provenance_comment_id: z2.number().int() }).strict(),
|
|
5477
|
+
z2.object({ type: z2.literal("completed") }).strict()
|
|
4914
5478
|
]);
|
|
4915
5479
|
var dispatchShared = {
|
|
4916
5480
|
meta: HeaderMetaSchema,
|
|
4917
5481
|
subject: SubjectSchema,
|
|
4918
|
-
kind:
|
|
5482
|
+
kind: z2.literal("dispatch"),
|
|
4919
5483
|
...envelopeTail,
|
|
4920
5484
|
target_role: RoleSchema,
|
|
4921
|
-
model:
|
|
4922
|
-
round:
|
|
4923
|
-
effect_id:
|
|
5485
|
+
model: z2.string(),
|
|
5486
|
+
round: z2.number().int().optional(),
|
|
5487
|
+
effect_id: z2.string()
|
|
4924
5488
|
};
|
|
4925
|
-
var dispatchUsageField =
|
|
4926
|
-
var DispatchEventSchema =
|
|
4927
|
-
|
|
4928
|
-
|
|
5489
|
+
var dispatchUsageField = z2.object({ input: z2.number().nonnegative(), output: z2.number().nonnegative() }).strict().nullable();
|
|
5490
|
+
var DispatchEventSchema = z2.discriminatedUnion("event", [
|
|
5491
|
+
z2.object({ ...dispatchShared, event: z2.literal("dispatched"), prompt_hash: z2.string() }).strict(),
|
|
5492
|
+
z2.object({
|
|
4929
5493
|
...dispatchShared,
|
|
4930
|
-
event:
|
|
5494
|
+
event: z2.literal("outcome_received"),
|
|
4931
5495
|
outcome: DispatchOutcomeSchema,
|
|
4932
5496
|
usage: dispatchUsageField
|
|
4933
5497
|
}).strict(),
|
|
4934
|
-
|
|
5498
|
+
z2.object({
|
|
4935
5499
|
...dispatchShared,
|
|
4936
|
-
event:
|
|
4937
|
-
reason:
|
|
5500
|
+
event: z2.literal("dispatch_failed"),
|
|
5501
|
+
reason: z2.enum(["timeout", "crash", "refused", "unattributed_write"]),
|
|
4938
5502
|
usage: dispatchUsageField
|
|
4939
5503
|
}).strict()
|
|
4940
5504
|
]);
|
|
4941
5505
|
var loopShared = {
|
|
4942
5506
|
meta: HeaderMetaSchema,
|
|
4943
5507
|
subject: SubjectSchema,
|
|
4944
|
-
kind:
|
|
5508
|
+
kind: z2.literal("dev_review_loop"),
|
|
4945
5509
|
...envelopeTail,
|
|
4946
|
-
loop_id:
|
|
5510
|
+
loop_id: z2.string()
|
|
4947
5511
|
};
|
|
4948
|
-
var DevReviewLoopEventSchema =
|
|
4949
|
-
|
|
5512
|
+
var DevReviewLoopEventSchema = z2.discriminatedUnion("event", [
|
|
5513
|
+
z2.object({
|
|
4950
5514
|
...loopShared,
|
|
4951
|
-
event:
|
|
4952
|
-
task:
|
|
4953
|
-
policy:
|
|
4954
|
-
max_rounds:
|
|
4955
|
-
reviewers:
|
|
4956
|
-
models:
|
|
5515
|
+
event: z2.literal("loop_started"),
|
|
5516
|
+
task: z2.number().int(),
|
|
5517
|
+
policy: z2.object({
|
|
5518
|
+
max_rounds: z2.number().int().nonnegative(),
|
|
5519
|
+
reviewers: z2.array(RoleSchema),
|
|
5520
|
+
models: z2.record(RoleSchema, z2.string())
|
|
4957
5521
|
}).strict()
|
|
4958
5522
|
}).strict(),
|
|
4959
|
-
|
|
4960
|
-
|
|
5523
|
+
z2.object({ ...loopShared, event: z2.literal("round_started"), round: z2.number().int(), base_head: z2.string() }).strict(),
|
|
5524
|
+
z2.object({
|
|
4961
5525
|
...loopShared,
|
|
4962
|
-
event:
|
|
4963
|
-
round:
|
|
4964
|
-
head:
|
|
4965
|
-
green:
|
|
5526
|
+
event: z2.literal("gate_result_read"),
|
|
5527
|
+
round: z2.number().int(),
|
|
5528
|
+
head: z2.string(),
|
|
5529
|
+
green: z2.boolean()
|
|
4966
5530
|
}).strict(),
|
|
4967
|
-
|
|
5531
|
+
z2.object({
|
|
4968
5532
|
...loopShared,
|
|
4969
|
-
event:
|
|
4970
|
-
round:
|
|
4971
|
-
head:
|
|
4972
|
-
all_approve:
|
|
4973
|
-
blockers:
|
|
5533
|
+
event: z2.literal("verdicts_read"),
|
|
5534
|
+
round: z2.number().int(),
|
|
5535
|
+
head: z2.string(),
|
|
5536
|
+
all_approve: z2.boolean(),
|
|
5537
|
+
blockers: z2.number().int().nonnegative(),
|
|
5538
|
+
findings: z2.array(ReviewFindingSchema).optional()
|
|
4974
5539
|
}).strict(),
|
|
4975
|
-
|
|
5540
|
+
z2.object({
|
|
4976
5541
|
...loopShared,
|
|
4977
|
-
event:
|
|
4978
|
-
round:
|
|
4979
|
-
open:
|
|
4980
|
-
resolved:
|
|
4981
|
-
new:
|
|
4982
|
-
recurring:
|
|
5542
|
+
event: z2.literal("findings_compared"),
|
|
5543
|
+
round: z2.number().int(),
|
|
5544
|
+
open: z2.array(z2.string()),
|
|
5545
|
+
resolved: z2.array(z2.string()),
|
|
5546
|
+
new: z2.array(z2.string()),
|
|
5547
|
+
recurring: z2.array(z2.string())
|
|
4983
5548
|
}).strict(),
|
|
4984
|
-
|
|
5549
|
+
z2.object({
|
|
4985
5550
|
...loopShared,
|
|
4986
|
-
event:
|
|
4987
|
-
round:
|
|
4988
|
-
condition:
|
|
5551
|
+
event: z2.literal("stop_condition_met"),
|
|
5552
|
+
round: z2.number().int(),
|
|
5553
|
+
condition: z2.enum([
|
|
4989
5554
|
"green",
|
|
4990
5555
|
"max_rounds",
|
|
4991
5556
|
"no_progress",
|
|
@@ -4995,42 +5560,49 @@ var DevReviewLoopEventSchema = z.discriminatedUnion("event", [
|
|
|
4995
5560
|
"reappearance"
|
|
4996
5561
|
])
|
|
4997
5562
|
}).strict(),
|
|
4998
|
-
|
|
5563
|
+
z2.object({
|
|
5564
|
+
...loopShared,
|
|
5565
|
+
event: z2.literal("paused"),
|
|
5566
|
+
round: z2.number().int(),
|
|
5567
|
+
reason: z2.enum(["escalation", "principal_item", "refreeze_needed"])
|
|
5568
|
+
}).strict(),
|
|
5569
|
+
z2.object({
|
|
4999
5570
|
...loopShared,
|
|
5000
|
-
event:
|
|
5001
|
-
round:
|
|
5002
|
-
|
|
5571
|
+
event: z2.literal("resumed"),
|
|
5572
|
+
round: z2.number().int(),
|
|
5573
|
+
by: z2.literal("principal")
|
|
5003
5574
|
}).strict(),
|
|
5004
|
-
|
|
5575
|
+
z2.object({
|
|
5005
5576
|
...loopShared,
|
|
5006
|
-
event:
|
|
5007
|
-
round:
|
|
5008
|
-
|
|
5577
|
+
event: z2.literal("unpushed_work_resume"),
|
|
5578
|
+
round: z2.number().int(),
|
|
5579
|
+
branch: z2.string(),
|
|
5580
|
+
detail: z2.string()
|
|
5009
5581
|
}).strict(),
|
|
5010
|
-
|
|
5582
|
+
z2.object({
|
|
5011
5583
|
...loopShared,
|
|
5012
|
-
event:
|
|
5013
|
-
round:
|
|
5014
|
-
base_head:
|
|
5015
|
-
head:
|
|
5016
|
-
files_changed:
|
|
5017
|
-
insertions:
|
|
5018
|
-
deletions:
|
|
5019
|
-
wall_ms:
|
|
5020
|
-
outcome:
|
|
5584
|
+
event: z2.literal("round_ended"),
|
|
5585
|
+
round: z2.number().int(),
|
|
5586
|
+
base_head: z2.string(),
|
|
5587
|
+
head: z2.string(),
|
|
5588
|
+
files_changed: z2.number().int().nonnegative(),
|
|
5589
|
+
insertions: z2.number().int().nonnegative(),
|
|
5590
|
+
deletions: z2.number().int().nonnegative(),
|
|
5591
|
+
wall_ms: z2.number().nonnegative(),
|
|
5592
|
+
outcome: z2.enum(["green", "changes_requested", "escalated"])
|
|
5021
5593
|
}).strict(),
|
|
5022
|
-
|
|
5594
|
+
z2.object({
|
|
5023
5595
|
...loopShared,
|
|
5024
|
-
event:
|
|
5025
|
-
rounds:
|
|
5026
|
-
total_wall_ms:
|
|
5027
|
-
time_to_green_ms:
|
|
5028
|
-
files_changed_total:
|
|
5029
|
-
final_head:
|
|
5030
|
-
result:
|
|
5596
|
+
event: z2.literal("journal_finalized"),
|
|
5597
|
+
rounds: z2.number().int().nonnegative(),
|
|
5598
|
+
total_wall_ms: z2.number().nonnegative(),
|
|
5599
|
+
time_to_green_ms: z2.number().nonnegative().nullable(),
|
|
5600
|
+
files_changed_total: z2.number().int().nonnegative(),
|
|
5601
|
+
final_head: z2.string(),
|
|
5602
|
+
result: z2.enum(["merged_ready", "stopped"])
|
|
5031
5603
|
}).strict()
|
|
5032
5604
|
]);
|
|
5033
|
-
var ForgeOpSchema =
|
|
5605
|
+
var ForgeOpSchema = z2.enum([
|
|
5034
5606
|
"pr.create",
|
|
5035
5607
|
"pr.comment",
|
|
5036
5608
|
"pr.body.replace",
|
|
@@ -5044,24 +5616,434 @@ var ForgeOpSchema = z.enum([
|
|
|
5044
5616
|
"label.add",
|
|
5045
5617
|
"label.remove"
|
|
5046
5618
|
]);
|
|
5047
|
-
var ForgeWriteTargetSchema =
|
|
5048
|
-
issue:
|
|
5049
|
-
pr:
|
|
5619
|
+
var ForgeWriteTargetSchema = z2.object({
|
|
5620
|
+
issue: z2.number().int().optional(),
|
|
5621
|
+
pr: z2.number().int().optional()
|
|
5050
5622
|
}).strict();
|
|
5051
5623
|
var forgeWriteShared = {
|
|
5052
5624
|
meta: HeaderMetaSchema,
|
|
5053
5625
|
subject: SubjectSchema,
|
|
5054
|
-
kind:
|
|
5626
|
+
kind: z2.literal("forge_write"),
|
|
5055
5627
|
...envelopeTail,
|
|
5056
5628
|
op: ForgeOpSchema,
|
|
5057
5629
|
target: ForgeWriteTargetSchema
|
|
5058
5630
|
};
|
|
5059
|
-
var ForgeWriteEventSchema =
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5631
|
+
var ForgeWriteEventSchema = z2.discriminatedUnion("event", [
|
|
5632
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("validated") }).strict(),
|
|
5633
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("refused"), reason: z2.string() }).strict(),
|
|
5634
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("written"), comment_ids: z2.array(z2.string()) }).strict()
|
|
5635
|
+
]);
|
|
5636
|
+
var GateOutcomeSchema = z2.enum([
|
|
5637
|
+
"pass",
|
|
5638
|
+
"fail",
|
|
5639
|
+
"wait",
|
|
5640
|
+
"skip",
|
|
5641
|
+
"invalid_input",
|
|
5642
|
+
"unavailable_dependency",
|
|
5643
|
+
"timeout",
|
|
5644
|
+
"cancelled"
|
|
5645
|
+
]);
|
|
5646
|
+
var gateShared = {
|
|
5647
|
+
meta: HeaderMetaSchema,
|
|
5648
|
+
subject: SubjectSchema,
|
|
5649
|
+
kind: z2.literal("gate"),
|
|
5650
|
+
...envelopeTail,
|
|
5651
|
+
check: z2.string(),
|
|
5652
|
+
check_version: z2.string().nullable(),
|
|
5653
|
+
policy_version: z2.string().nullable(),
|
|
5654
|
+
input_fingerprint: z2.string().nullable()
|
|
5655
|
+
};
|
|
5656
|
+
var GateEventSchema = z2.discriminatedUnion("event", [
|
|
5657
|
+
z2.object({
|
|
5658
|
+
...gateShared,
|
|
5659
|
+
event: z2.literal("checked"),
|
|
5660
|
+
outcome: GateOutcomeSchema,
|
|
5661
|
+
reason: z2.string().optional()
|
|
5662
|
+
}).strict()
|
|
5663
|
+
]);
|
|
5664
|
+
var OperationResultSchema = z2.enum(["ok", "error", "refused", "timeout", "cancelled", "unavailable"]);
|
|
5665
|
+
var operationShared = {
|
|
5666
|
+
meta: HeaderMetaSchema,
|
|
5667
|
+
subject: SubjectSchema,
|
|
5668
|
+
kind: z2.literal("operation"),
|
|
5669
|
+
...envelopeTail,
|
|
5670
|
+
operation: z2.string(),
|
|
5671
|
+
target: z2.string().nullable()
|
|
5672
|
+
};
|
|
5673
|
+
var OperationEventSchema = z2.discriminatedUnion("event", [
|
|
5674
|
+
z2.object({
|
|
5675
|
+
...operationShared,
|
|
5676
|
+
event: z2.literal("completed"),
|
|
5677
|
+
result: OperationResultSchema,
|
|
5678
|
+
error_class: z2.string().nullable()
|
|
5679
|
+
}).strict()
|
|
5680
|
+
]);
|
|
5681
|
+
var UsageUnitsSchema = z2.object({
|
|
5682
|
+
input: z2.number().nonnegative().nullable(),
|
|
5683
|
+
output: z2.number().nonnegative().nullable(),
|
|
5684
|
+
cache: z2.number().nonnegative().nullable()
|
|
5685
|
+
}).strict();
|
|
5686
|
+
var usageShared = {
|
|
5687
|
+
meta: HeaderMetaSchema,
|
|
5688
|
+
subject: SubjectSchema,
|
|
5689
|
+
kind: z2.literal("usage"),
|
|
5690
|
+
...envelopeTail,
|
|
5691
|
+
model: z2.string().nullable(),
|
|
5692
|
+
source: z2.string(),
|
|
5693
|
+
semantics: z2.enum(["cumulative", "delta"])
|
|
5694
|
+
};
|
|
5695
|
+
var UsageEventSchema = z2.discriminatedUnion("event", [
|
|
5696
|
+
z2.object({
|
|
5697
|
+
...usageShared,
|
|
5698
|
+
event: z2.literal("observed"),
|
|
5699
|
+
units: UsageUnitsSchema,
|
|
5700
|
+
unknown_reason: z2.string().nullable()
|
|
5701
|
+
}).strict()
|
|
5702
|
+
]);
|
|
5703
|
+
var RoleAttemptOutcomeSchema = z2.enum([
|
|
5704
|
+
"completed",
|
|
5705
|
+
"incomplete",
|
|
5706
|
+
"infrastructure_failed",
|
|
5707
|
+
"cancelled",
|
|
5708
|
+
"timed_out",
|
|
5709
|
+
"capability_refused"
|
|
5710
|
+
]);
|
|
5711
|
+
var roleAttemptShared = {
|
|
5712
|
+
meta: HeaderMetaSchema,
|
|
5713
|
+
subject: SubjectSchema,
|
|
5714
|
+
kind: z2.literal("role_attempt"),
|
|
5715
|
+
...envelopeTail,
|
|
5716
|
+
actor: z2.string().nullable(),
|
|
5717
|
+
attempt: z2.number().int().nullable(),
|
|
5718
|
+
effect_id: z2.string(),
|
|
5719
|
+
model: z2.string().nullable()
|
|
5720
|
+
};
|
|
5721
|
+
var RoleAttemptEventSchema = z2.discriminatedUnion("event", [
|
|
5722
|
+
z2.object({
|
|
5723
|
+
...roleAttemptShared,
|
|
5724
|
+
event: z2.literal("attempted"),
|
|
5725
|
+
outcome: RoleAttemptOutcomeSchema,
|
|
5726
|
+
usage: dispatchUsageField
|
|
5727
|
+
}).strict()
|
|
5728
|
+
]);
|
|
5729
|
+
var handoffShared = {
|
|
5730
|
+
meta: HeaderMetaSchema,
|
|
5731
|
+
subject: SubjectSchema,
|
|
5732
|
+
kind: z2.literal("handoff"),
|
|
5733
|
+
...envelopeTail,
|
|
5734
|
+
class: z2.enum(["authority", "strategy", "product"]),
|
|
5735
|
+
reason: z2.string()
|
|
5736
|
+
};
|
|
5737
|
+
var HandoffEventSchema = z2.discriminatedUnion("event", [
|
|
5738
|
+
z2.object({
|
|
5739
|
+
...handoffShared,
|
|
5740
|
+
event: z2.literal("raised"),
|
|
5741
|
+
requested_decision: z2.string().nullable()
|
|
5742
|
+
}).strict(),
|
|
5743
|
+
z2.object({
|
|
5744
|
+
...handoffShared,
|
|
5745
|
+
event: z2.literal("resolved"),
|
|
5746
|
+
resolution: z2.string().nullable(),
|
|
5747
|
+
resolved_by: z2.string().nullable()
|
|
5748
|
+
}).strict()
|
|
5749
|
+
]);
|
|
5750
|
+
var EffectTargetSchema = z2.object({
|
|
5751
|
+
kind: z2.string(),
|
|
5752
|
+
ref: z2.string()
|
|
5753
|
+
}).strict();
|
|
5754
|
+
var effectShared = {
|
|
5755
|
+
meta: HeaderMetaSchema,
|
|
5756
|
+
subject: SubjectSchema,
|
|
5757
|
+
kind: z2.literal("effect"),
|
|
5758
|
+
...envelopeTail,
|
|
5759
|
+
effect_id: z2.string(),
|
|
5760
|
+
target: EffectTargetSchema
|
|
5761
|
+
};
|
|
5762
|
+
var EffectEventSchema = z2.discriminatedUnion("event", [
|
|
5763
|
+
z2.object({ ...effectShared, event: z2.literal("attempted") }).strict(),
|
|
5764
|
+
z2.object({ ...effectShared, event: z2.literal("observed"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict(),
|
|
5765
|
+
z2.object({ ...effectShared, event: z2.literal("verified"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict()
|
|
5766
|
+
]);
|
|
5767
|
+
var LogEventSchema = z2.union([
|
|
5768
|
+
DispatchEventSchema,
|
|
5769
|
+
DevReviewLoopEventSchema,
|
|
5770
|
+
ForgeWriteEventSchema,
|
|
5771
|
+
GateEventSchema,
|
|
5772
|
+
OperationEventSchema,
|
|
5773
|
+
UsageEventSchema,
|
|
5774
|
+
RoleAttemptEventSchema,
|
|
5775
|
+
HandoffEventSchema,
|
|
5776
|
+
EffectEventSchema
|
|
5063
5777
|
]);
|
|
5064
|
-
|
|
5778
|
+
|
|
5779
|
+
// ../../packages/aeg-core/src/log/envelope.ts
|
|
5780
|
+
function isRole(value) {
|
|
5781
|
+
return value !== undefined && ROLE_VALUES.includes(value);
|
|
5782
|
+
}
|
|
5783
|
+
function actorIdFromEnv(role) {
|
|
5784
|
+
return role === undefined || role === "" ? null : role;
|
|
5785
|
+
}
|
|
5786
|
+
function lineageFieldFromEnv(value) {
|
|
5787
|
+
return value === undefined || value === "" ? null : value;
|
|
5788
|
+
}
|
|
5789
|
+
function attemptFromEnv(value) {
|
|
5790
|
+
if (value === undefined || value === "")
|
|
5791
|
+
return null;
|
|
5792
|
+
const n = Number(value);
|
|
5793
|
+
return Number.isInteger(n) ? n : null;
|
|
5794
|
+
}
|
|
5795
|
+
function provenanceFor(input) {
|
|
5796
|
+
if (input.provenance)
|
|
5797
|
+
return input.provenance;
|
|
5798
|
+
if (input.env.role || input.env.task)
|
|
5799
|
+
return "env_correlated";
|
|
5800
|
+
return "unavailable";
|
|
5801
|
+
}
|
|
5802
|
+
function issueFromTask(task) {
|
|
5803
|
+
if (task === undefined || task === "")
|
|
5804
|
+
return null;
|
|
5805
|
+
const n = Number(task);
|
|
5806
|
+
return Number.isInteger(n) ? n : null;
|
|
5807
|
+
}
|
|
5808
|
+
function roundFromEnv(round) {
|
|
5809
|
+
if (round === undefined || round === "")
|
|
5810
|
+
return;
|
|
5811
|
+
const n = Number(round);
|
|
5812
|
+
return Number.isInteger(n) ? n : undefined;
|
|
5813
|
+
}
|
|
5814
|
+
function buildHeader(input) {
|
|
5815
|
+
const role = isRole(input.env.role) ? input.env.role : "unattributed";
|
|
5816
|
+
const round = roundFromEnv(input.env.round);
|
|
5817
|
+
const subject = {
|
|
5818
|
+
issue: issueFromTask(input.env.task),
|
|
5819
|
+
role,
|
|
5820
|
+
...round !== undefined ? { round } : {},
|
|
5821
|
+
...input.subject ?? {}
|
|
5822
|
+
};
|
|
5823
|
+
return {
|
|
5824
|
+
meta: {
|
|
5825
|
+
schema: 2,
|
|
5826
|
+
ts: input.now.toISOString(),
|
|
5827
|
+
run_id: input.runId,
|
|
5828
|
+
seq: input.seq,
|
|
5829
|
+
repo: input.repo,
|
|
5830
|
+
vinaya: input.vinaya,
|
|
5831
|
+
doctrine: input.doctrine,
|
|
5832
|
+
host: input.host,
|
|
5833
|
+
machine: createHash5("sha256").update(input.hostname).digest("hex"),
|
|
5834
|
+
event_id: input.eventId,
|
|
5835
|
+
process_id: input.processId,
|
|
5836
|
+
actor_id: actorIdFromEnv(input.env.role),
|
|
5837
|
+
lineage: {
|
|
5838
|
+
run: lineageFieldFromEnv(input.env.run),
|
|
5839
|
+
attempt: attemptFromEnv(input.env.attempt),
|
|
5840
|
+
parent: lineageFieldFromEnv(input.env.parent)
|
|
5841
|
+
},
|
|
5842
|
+
input_versions: {
|
|
5843
|
+
objectives_version: input.inputVersions?.objectivesVersion ?? null,
|
|
5844
|
+
brief_hash: input.inputVersions?.briefHash ?? null,
|
|
5845
|
+
ruling_ordinal: input.inputVersions?.rulingOrdinal ?? null,
|
|
5846
|
+
policy_digest: input.inputVersions?.policyDigest ?? null
|
|
5847
|
+
},
|
|
5848
|
+
provenance: provenanceFor(input)
|
|
5849
|
+
},
|
|
5850
|
+
subject
|
|
5851
|
+
};
|
|
5852
|
+
}
|
|
5853
|
+
// ../../packages/aeg-core/src/log/redact.ts
|
|
5854
|
+
var GITHUB_TOKEN_PATTERN = /\b(?:gho_|ghp_|ghs_|ghu_|ghr_|github_pat_)[A-Za-z0-9_]+\b/g;
|
|
5855
|
+
var CLASSIC_GITHUB_PAT_PATTERN = /\b((?:token|GITHUB_TOKEN|PAT|api[_-]?key|api[_-]?token)\s*[:=]\s*)[a-f0-9]{40}\b/gi;
|
|
5856
|
+
var BEARER_TOKEN_PATTERN = /\bBearer\s+\S+/g;
|
|
5857
|
+
var AWS_ACCESS_KEY_PATTERN = /\bAKIA[0-9A-Z]{16}\b/g;
|
|
5858
|
+
var SLACK_TOKEN_PATTERN = /\bxox[baprs]-[0-9A-Za-z-]+\b/g;
|
|
5859
|
+
var STRIPE_KEY_PATTERN = /\b(?:sk|pk|rk)_(?:live|test)_[0-9A-Za-z]{10,}\b/g;
|
|
5860
|
+
var ANTHROPIC_KEY_PATTERN = /\bsk-ant-[A-Za-z0-9_-]{20,}\b/g;
|
|
5861
|
+
var OPENAI_KEY_PATTERN = /\bsk-(?!ant-)[A-Za-z0-9]{20,}\b/g;
|
|
5862
|
+
var NPM_TOKEN_PATTERN = /\bnpm_[A-Za-z0-9]{36}\b/g;
|
|
5863
|
+
var JWT_PATTERN = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g;
|
|
5864
|
+
var PEM_BLOCK_PATTERN = /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g;
|
|
5865
|
+
var BASIC_AUTH_URL_PATTERN = /(https?:\/\/)[^/\s:@]+:[^/\s@]+@/g;
|
|
5866
|
+
var GENERIC_SECRET_ASSIGNMENT_PATTERN = /\b((?:secret|password|passwd|api[_-]?key|access[_-]?key|client[_-]?secret|private[_-]?key)\s*[:=]\s*)[A-Za-z0-9+/_=-]{16,}\b/gi;
|
|
5867
|
+
function redactString(value, home) {
|
|
5868
|
+
let out = value.replace(GITHUB_TOKEN_PATTERN, "<redacted>").replace(CLASSIC_GITHUB_PAT_PATTERN, "$1<redacted>").replace(BEARER_TOKEN_PATTERN, "<redacted>").replace(AWS_ACCESS_KEY_PATTERN, "<redacted>").replace(SLACK_TOKEN_PATTERN, "<redacted>").replace(STRIPE_KEY_PATTERN, "<redacted>").replace(ANTHROPIC_KEY_PATTERN, "<redacted>").replace(OPENAI_KEY_PATTERN, "<redacted>").replace(NPM_TOKEN_PATTERN, "<redacted>").replace(JWT_PATTERN, "<redacted>").replace(PEM_BLOCK_PATTERN, "<redacted>").replace(BASIC_AUTH_URL_PATTERN, "$1<redacted>@").replace(GENERIC_SECRET_ASSIGNMENT_PATTERN, "$1<redacted>");
|
|
5869
|
+
if (home.length > 0 && out.startsWith(home)) {
|
|
5870
|
+
const rest = out.slice(home.length);
|
|
5871
|
+
if (rest === "" || rest.startsWith("/"))
|
|
5872
|
+
out = `~${rest}`;
|
|
5873
|
+
}
|
|
5874
|
+
return out;
|
|
5875
|
+
}
|
|
5876
|
+
function redact(value, home) {
|
|
5877
|
+
if (typeof value === "string")
|
|
5878
|
+
return redactString(value, home);
|
|
5879
|
+
if (Array.isArray(value))
|
|
5880
|
+
return value.map((v) => redact(v, home));
|
|
5881
|
+
if (value !== null && typeof value === "object") {
|
|
5882
|
+
const out = {};
|
|
5883
|
+
for (const [k, v] of Object.entries(value)) {
|
|
5884
|
+
out[k] = redact(v, home);
|
|
5885
|
+
}
|
|
5886
|
+
return out;
|
|
5887
|
+
}
|
|
5888
|
+
return value;
|
|
5889
|
+
}
|
|
5890
|
+
// ../../packages/aeg-core/src/log/store.ts
|
|
5891
|
+
function metaOf(obj) {
|
|
5892
|
+
if (obj === null || typeof obj !== "object")
|
|
5893
|
+
return null;
|
|
5894
|
+
const meta = obj.meta;
|
|
5895
|
+
if (meta === null || typeof meta !== "object")
|
|
5896
|
+
return null;
|
|
5897
|
+
return meta;
|
|
5898
|
+
}
|
|
5899
|
+
function recordIdentity(obj) {
|
|
5900
|
+
const meta = metaOf(obj);
|
|
5901
|
+
if (meta === null)
|
|
5902
|
+
return null;
|
|
5903
|
+
const eventId = meta.event_id;
|
|
5904
|
+
if (typeof eventId === "string" && eventId.length > 0)
|
|
5905
|
+
return eventId;
|
|
5906
|
+
const runId = meta.run_id;
|
|
5907
|
+
const seq = meta.seq;
|
|
5908
|
+
if (typeof runId === "string" && runId.length > 0 && typeof seq === "number" && Number.isInteger(seq)) {
|
|
5909
|
+
return `${runId}:${seq}`;
|
|
5910
|
+
}
|
|
5911
|
+
return null;
|
|
5912
|
+
}
|
|
5913
|
+
|
|
5914
|
+
// ../../packages/aeg-core/src/log/artifact.ts
|
|
5915
|
+
var TASK_LOG_ARTIFACT_MAX_BYTES = 8 * 1024 * 1024;
|
|
5916
|
+
// ../../packages/aeg-core/src/dev-review-loop/journal-reconstruction.ts
|
|
5917
|
+
var STOPPED_CONDITIONS = new Set(["confidence", "reappearance", "no_progress", "max_rounds"]);
|
|
5918
|
+
// ../../packages/aeg-core/src/control-store/records.ts
|
|
5919
|
+
import { z as z3 } from "zod";
|
|
5920
|
+
var isoTimestamp = z3.string().min(1);
|
|
5921
|
+
var taskId = z3.number().int().positive();
|
|
5922
|
+
var epochNumber = z3.number().int().nonnegative();
|
|
5923
|
+
var RunRecordSchema = z3.object({
|
|
5924
|
+
version: z3.literal(1),
|
|
5925
|
+
kind: z3.literal("run"),
|
|
5926
|
+
task: taskId,
|
|
5927
|
+
runId: z3.string().min(1),
|
|
5928
|
+
pid: z3.number().int().positive(),
|
|
5929
|
+
host: z3.string().min(1),
|
|
5930
|
+
startedAt: isoTimestamp,
|
|
5931
|
+
childStartedAt: z3.string().min(1).nullable().optional(),
|
|
5932
|
+
childCommand: z3.string().min(1).nullable().optional()
|
|
5933
|
+
}).strict();
|
|
5934
|
+
var InputRecordSchema = z3.object({
|
|
5935
|
+
version: z3.literal(1),
|
|
5936
|
+
kind: z3.literal("input"),
|
|
5937
|
+
task: taskId,
|
|
5938
|
+
runId: z3.string().min(1),
|
|
5939
|
+
source: z3.union([z3.literal("fresh"), z3.literal("resume")]),
|
|
5940
|
+
pr: z3.number().int().positive().nullable(),
|
|
5941
|
+
round: z3.number().int().nonnegative(),
|
|
5942
|
+
recordedAt: isoTimestamp
|
|
5943
|
+
}).strict();
|
|
5944
|
+
var OwnershipRecordSchema = z3.object({
|
|
5945
|
+
version: z3.literal(1),
|
|
5946
|
+
kind: z3.literal("ownership"),
|
|
5947
|
+
task: taskId,
|
|
5948
|
+
epoch: epochNumber,
|
|
5949
|
+
ownerId: z3.string().min(1),
|
|
5950
|
+
pid: z3.number().int().positive(),
|
|
5951
|
+
host: z3.string().min(1),
|
|
5952
|
+
acquiredAt: isoTimestamp
|
|
5953
|
+
}).strict();
|
|
5954
|
+
var TransitionRecordSchema = z3.object({
|
|
5955
|
+
version: z3.literal(1),
|
|
5956
|
+
kind: z3.literal("transition"),
|
|
5957
|
+
task: taskId,
|
|
5958
|
+
epoch: epochNumber,
|
|
5959
|
+
seq: z3.number().int().nonnegative(),
|
|
5960
|
+
from: z3.string().min(1),
|
|
5961
|
+
to: z3.string().min(1),
|
|
5962
|
+
detail: z3.string().optional(),
|
|
5963
|
+
at: isoTimestamp
|
|
5964
|
+
}).strict();
|
|
5965
|
+
var ManifestRecordSchema = z3.object({
|
|
5966
|
+
version: z3.literal(1),
|
|
5967
|
+
kind: z3.literal("manifest"),
|
|
5968
|
+
task: taskId,
|
|
5969
|
+
round: epochNumber,
|
|
5970
|
+
repository: z3.string().min(1),
|
|
5971
|
+
pr: z3.number().int().positive(),
|
|
5972
|
+
branch: z3.string().min(1),
|
|
5973
|
+
baseSha: z3.string().min(1).nullable(),
|
|
5974
|
+
headSha: z3.string().min(1),
|
|
5975
|
+
briefHash: z3.string().min(1).nullable(),
|
|
5976
|
+
objectivesVersion: z3.string().min(1).nullable(),
|
|
5977
|
+
rulingOrdinal: epochNumber,
|
|
5978
|
+
policyDigest: z3.string().min(1),
|
|
5979
|
+
recordedAt: isoTimestamp
|
|
5980
|
+
}).strict();
|
|
5981
|
+
var roundHeadIdentity = z3.object({ round: z3.number().int().positive(), head: z3.string().min(1) }).strict();
|
|
5982
|
+
var LoopBudgetsSchema = z3.object({
|
|
5983
|
+
mechanicalRetries: z3.number().int().nonnegative(),
|
|
5984
|
+
reviewRounds: z3.number().int().nonnegative(),
|
|
5985
|
+
infrastructureRetries: z3.number().int().nonnegative()
|
|
5986
|
+
}).strict();
|
|
5987
|
+
var LoopStateRecordSchema = z3.object({
|
|
5988
|
+
version: z3.literal(1),
|
|
5989
|
+
kind: z3.literal("loop_state"),
|
|
5990
|
+
task: taskId,
|
|
5991
|
+
round: z3.number().int().positive(),
|
|
5992
|
+
phase: z3.string().min(1),
|
|
5993
|
+
pauseReason: z3.string().min(1).nullable(),
|
|
5994
|
+
budgets: LoopBudgetsSchema,
|
|
5995
|
+
heldResult: roundHeadIdentity.nullable(),
|
|
5996
|
+
deliveredFindings: roundHeadIdentity.nullable(),
|
|
5997
|
+
recordedAt: isoTimestamp
|
|
5998
|
+
}).strict();
|
|
5999
|
+
var EffectRecordSchema = z3.object({
|
|
6000
|
+
version: z3.literal(1),
|
|
6001
|
+
kind: z3.literal("effect"),
|
|
6002
|
+
task: taskId,
|
|
6003
|
+
key: z3.string().min(1),
|
|
6004
|
+
operation: z3.string().min(1),
|
|
6005
|
+
target: z3.string().min(1),
|
|
6006
|
+
inputVersion: z3.number().int().nonnegative(),
|
|
6007
|
+
payloadDigest: z3.string().min(1),
|
|
6008
|
+
status: z3.union([z3.literal("started"), z3.literal("verified"), z3.literal("uncertain")]),
|
|
6009
|
+
url: z3.string().optional(),
|
|
6010
|
+
recordedAt: isoTimestamp
|
|
6011
|
+
}).strict();
|
|
6012
|
+
var EscalationRecordSchema = z3.object({
|
|
6013
|
+
version: z3.literal(1),
|
|
6014
|
+
kind: z3.literal("escalation"),
|
|
6015
|
+
task: taskId,
|
|
6016
|
+
escalationId: z3.string().min(1),
|
|
6017
|
+
round: epochNumber,
|
|
6018
|
+
head: z3.string().min(1),
|
|
6019
|
+
branch: z3.string().min(1),
|
|
6020
|
+
pr: z3.number().int().positive().nullable(),
|
|
6021
|
+
runId: z3.string().min(1),
|
|
6022
|
+
pid: z3.number().int().positive(),
|
|
6023
|
+
host: z3.string().min(1),
|
|
6024
|
+
agent: z3.string().min(1).optional(),
|
|
6025
|
+
reason: z3.string().min(1),
|
|
6026
|
+
detail: z3.string().optional(),
|
|
6027
|
+
evidence: z3.string().optional(),
|
|
6028
|
+
attemptedRecovery: z3.string().min(1),
|
|
6029
|
+
requestedDecision: z3.string().min(1),
|
|
6030
|
+
recipient: z3.string().min(1),
|
|
6031
|
+
briefHash: z3.string().min(1).nullable(),
|
|
6032
|
+
objectivesVersion: z3.string().min(1).nullable(),
|
|
6033
|
+
rulingOrdinal: epochNumber,
|
|
6034
|
+
policyDigest: z3.string().min(1),
|
|
6035
|
+
recordedAt: isoTimestamp
|
|
6036
|
+
}).strict();
|
|
6037
|
+
var ResolutionRecordSchema = z3.object({
|
|
6038
|
+
version: z3.literal(1),
|
|
6039
|
+
kind: z3.literal("resolution"),
|
|
6040
|
+
task: taskId,
|
|
6041
|
+
escalationId: z3.string().min(1),
|
|
6042
|
+
decision: z3.union([z3.literal("resume"), z3.literal("cancel")]),
|
|
6043
|
+
authenticatedBy: z3.string().min(1),
|
|
6044
|
+
authenticatedFrom: z3.string().min(1),
|
|
6045
|
+
consumedAt: isoTimestamp
|
|
6046
|
+
}).strict();
|
|
5065
6047
|
// src/checks/contract.ts
|
|
5066
6048
|
var CHECK_SCHEMA_VERSION = 1;
|
|
5067
6049
|
function emitCheckError(error) {
|
|
@@ -5074,27 +6056,29 @@ import { execFileSync as execFileSync2 } from "node:child_process";
|
|
|
5074
6056
|
import { existsSync, mkdirSync, readFileSync as readFileSync2, realpathSync, writeFileSync } from "node:fs";
|
|
5075
6057
|
import { homedir } from "node:os";
|
|
5076
6058
|
import { dirname, join, resolve } from "node:path";
|
|
5077
|
-
import { z as
|
|
6059
|
+
import { z as z4 } from "zod";
|
|
5078
6060
|
|
|
5079
6061
|
// src/lib/agent-vendors.ts
|
|
5080
6062
|
var AGENT_VENDORS = ["skills", "claude", "gemini"];
|
|
5081
6063
|
|
|
5082
6064
|
// src/lib/config.ts
|
|
5083
|
-
var EnvEntrySchema =
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
6065
|
+
var EnvEntrySchema = z4.union([
|
|
6066
|
+
z4.literal(true),
|
|
6067
|
+
z4.object({ optional: z4.literal(true) }),
|
|
6068
|
+
z4.object({ anyOf: z4.array(z4.string()).min(2) }),
|
|
6069
|
+
z4.string()
|
|
5088
6070
|
]);
|
|
5089
|
-
var CheckEntrySchema =
|
|
5090
|
-
run:
|
|
5091
|
-
scope:
|
|
5092
|
-
include:
|
|
5093
|
-
args:
|
|
5094
|
-
timeoutMs:
|
|
5095
|
-
env:
|
|
5096
|
-
requiresOpenPr:
|
|
5097
|
-
ownWorkflow:
|
|
6071
|
+
var CheckEntrySchema = z4.object({
|
|
6072
|
+
run: z4.string(),
|
|
6073
|
+
scope: z4.enum(["diff", "full"]),
|
|
6074
|
+
include: z4.array(z4.string()).optional(),
|
|
6075
|
+
args: z4.array(z4.string()).optional(),
|
|
6076
|
+
timeoutMs: z4.number().optional(),
|
|
6077
|
+
env: z4.record(z4.string(), EnvEntrySchema).optional(),
|
|
6078
|
+
requiresOpenPr: z4.boolean().optional(),
|
|
6079
|
+
ownWorkflow: z4.boolean().optional(),
|
|
6080
|
+
principalOwed: z4.literal(true).optional(),
|
|
6081
|
+
validates: z4.enum(["body", "issue"]).optional()
|
|
5098
6082
|
}).superRefine((entry2, ctx) => {
|
|
5099
6083
|
if (!entry2.env)
|
|
5100
6084
|
return;
|
|
@@ -5104,25 +6088,26 @@ var CheckEntrySchema = z2.object({
|
|
|
5104
6088
|
const members = value.anyOf;
|
|
5105
6089
|
if (new Set(members).size !== members.length) {
|
|
5106
6090
|
ctx.addIssue({
|
|
5107
|
-
code:
|
|
6091
|
+
code: z4.ZodIssueCode.custom,
|
|
5108
6092
|
message: `env.${key}.anyOf has duplicate members`,
|
|
5109
6093
|
path: ["env", key, "anyOf"]
|
|
5110
6094
|
});
|
|
5111
6095
|
}
|
|
5112
6096
|
if (!members.includes(key)) {
|
|
5113
6097
|
ctx.addIssue({
|
|
5114
|
-
code:
|
|
6098
|
+
code: z4.ZodIssueCode.custom,
|
|
5115
6099
|
message: `env.${key}.anyOf must include "${key}" itself as a member`,
|
|
5116
6100
|
path: ["env", key, "anyOf"]
|
|
5117
6101
|
});
|
|
5118
6102
|
}
|
|
5119
6103
|
}
|
|
5120
6104
|
});
|
|
5121
|
-
var RoleEntrySchema =
|
|
5122
|
-
contract:
|
|
6105
|
+
var RoleEntrySchema = z4.object({
|
|
6106
|
+
contract: z4.string().refine((p) => p.includes("/"), {
|
|
5123
6107
|
message: 'must be a path (contain at least one "/"), not a bare filename'
|
|
5124
6108
|
})
|
|
5125
6109
|
});
|
|
6110
|
+
var MAX_REPORT_COMMAND_TIMEOUT_MS = 3600000;
|
|
5126
6111
|
var BRIEF_BUILTINS = [
|
|
5127
6112
|
"tier",
|
|
5128
6113
|
"testPlan",
|
|
@@ -5142,17 +6127,17 @@ var BRIEF_BUILTINS = [
|
|
|
5142
6127
|
"briefSections",
|
|
5143
6128
|
"milestoneShape"
|
|
5144
6129
|
];
|
|
5145
|
-
var BriefSectionSchema =
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
6130
|
+
var BriefSectionSchema = z4.union([
|
|
6131
|
+
z4.object({ builtin: z4.enum(BRIEF_BUILTINS) }),
|
|
6132
|
+
z4.object({ heading: z4.string().min(1), name: z4.string().optional() }),
|
|
6133
|
+
z4.object({ field: z4.string().min(1), name: z4.string().optional() }),
|
|
6134
|
+
z4.object({ phrase: z4.string().min(1), name: z4.string().optional() })
|
|
5150
6135
|
]);
|
|
5151
|
-
var BriefSchemaSchema =
|
|
5152
|
-
pr:
|
|
5153
|
-
issue:
|
|
5154
|
-
milestone:
|
|
5155
|
-
ack:
|
|
6136
|
+
var BriefSchemaSchema = z4.object({
|
|
6137
|
+
pr: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
|
|
6138
|
+
issue: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
|
|
6139
|
+
milestone: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
|
|
6140
|
+
ack: z4.array(z4.enum(BRIEF_BUILTINS)).optional()
|
|
5156
6141
|
});
|
|
5157
6142
|
function isSafeRepoRelPath(p) {
|
|
5158
6143
|
if (p.length === 0)
|
|
@@ -5161,7 +6146,7 @@ function isSafeRepoRelPath(p) {
|
|
|
5161
6146
|
return false;
|
|
5162
6147
|
return p.split(/[\\/]/).every((seg) => seg !== ".." && seg !== "");
|
|
5163
6148
|
}
|
|
5164
|
-
var SafeRepoRelPath =
|
|
6149
|
+
var SafeRepoRelPath = z4.string().refine(isSafeRepoRelPath, {
|
|
5165
6150
|
message: "must be a repo-root-relative path with no `..` segment or absolute root"
|
|
5166
6151
|
});
|
|
5167
6152
|
var CANONICAL_HOOK_BLOCK_PREFIXES = [".git/", ".husky/", ".vinaya/hooks/", ".claude/hooks/"];
|
|
@@ -5171,22 +6156,22 @@ function isCanonicalHookBlockPath(p) {
|
|
|
5171
6156
|
var ManagedHookBlockPath = SafeRepoRelPath.refine(isCanonicalHookBlockPath, {
|
|
5172
6157
|
message: `must start with one of ${CANONICAL_HOOK_BLOCK_PREFIXES.join(", ")} (byte-exact, case-sensitive)`
|
|
5173
6158
|
});
|
|
5174
|
-
var ManagedBlockRecordSchema =
|
|
6159
|
+
var ManagedBlockRecordSchema = z4.object({
|
|
5175
6160
|
path: ManagedHookBlockPath,
|
|
5176
|
-
marker:
|
|
5177
|
-
comment:
|
|
6161
|
+
marker: z4.string(),
|
|
6162
|
+
comment: z4.enum(["hash", "html"])
|
|
5178
6163
|
});
|
|
5179
|
-
var ManagedManifestSchema =
|
|
5180
|
-
version:
|
|
5181
|
-
files:
|
|
5182
|
-
blocks:
|
|
5183
|
-
labels:
|
|
5184
|
-
agents:
|
|
6164
|
+
var ManagedManifestSchema = z4.object({
|
|
6165
|
+
version: z4.number().int().positive(),
|
|
6166
|
+
files: z4.array(SafeRepoRelPath),
|
|
6167
|
+
blocks: z4.array(ManagedBlockRecordSchema),
|
|
6168
|
+
labels: z4.array(z4.string()),
|
|
6169
|
+
agents: z4.array(z4.enum(AGENT_VENDORS)).optional()
|
|
5185
6170
|
});
|
|
5186
|
-
var ProjectEntrySchema =
|
|
5187
|
-
name:
|
|
5188
|
-
description:
|
|
5189
|
-
path:
|
|
6171
|
+
var ProjectEntrySchema = z4.object({
|
|
6172
|
+
name: z4.string().min(1),
|
|
6173
|
+
description: z4.string().min(1).optional(),
|
|
6174
|
+
path: z4.string().min(1).optional()
|
|
5190
6175
|
});
|
|
5191
6176
|
function parseTokensCollectDeclaration(value) {
|
|
5192
6177
|
const m = value.trim().match(/^(\S+)\s+(\S+)$/);
|
|
@@ -5197,34 +6182,62 @@ function parseTokensCollectDeclaration(value) {
|
|
|
5197
6182
|
return null;
|
|
5198
6183
|
return { interpreter, script };
|
|
5199
6184
|
}
|
|
5200
|
-
var VinayaConfigSchema =
|
|
5201
|
-
rings:
|
|
5202
|
-
ring1_forgeWriteInterception:
|
|
5203
|
-
ring2_asyncAudits:
|
|
6185
|
+
var VinayaConfigSchema = z4.object({
|
|
6186
|
+
rings: z4.object({
|
|
6187
|
+
ring1_forgeWriteInterception: z4.boolean(),
|
|
6188
|
+
ring2_asyncAudits: z4.boolean()
|
|
5204
6189
|
}).optional(),
|
|
5205
|
-
checks:
|
|
5206
|
-
roles:
|
|
6190
|
+
checks: z4.record(z4.string(), CheckEntrySchema).optional(),
|
|
6191
|
+
roles: z4.record(z4.string(), RoleEntrySchema).optional(),
|
|
5207
6192
|
briefSchema: BriefSchemaSchema.optional(),
|
|
5208
6193
|
managed: ManagedManifestSchema.optional(),
|
|
5209
|
-
principals:
|
|
5210
|
-
releaseActor:
|
|
5211
|
-
ci:
|
|
5212
|
-
tokens:
|
|
5213
|
-
collect:
|
|
6194
|
+
principals: z4.array(z4.string()).min(1).optional(),
|
|
6195
|
+
releaseActor: z4.string().min(1).optional(),
|
|
6196
|
+
ci: z4.object({ setup: z4.string().min(1) }).optional(),
|
|
6197
|
+
tokens: z4.object({
|
|
6198
|
+
collect: z4.string().min(1).refine((v) => parseTokensCollectDeclaration(v) !== null, {
|
|
5214
6199
|
message: 'tokens.collect must be exactly "<interpreter> <repo-relative-script-path>" — two whitespace-separated tokens, no flags, no shell syntax, no quoting'
|
|
5215
6200
|
})
|
|
5216
6201
|
}).optional(),
|
|
5217
|
-
blastRadius:
|
|
5218
|
-
proseGates:
|
|
5219
|
-
doctrineRoot:
|
|
5220
|
-
readerFacingPrefix:
|
|
5221
|
-
readerFacingSuffix:
|
|
5222
|
-
legacySlugDir:
|
|
6202
|
+
blastRadius: z4.object({ extraDomains: z4.array(z4.string()).optional() }).optional(),
|
|
6203
|
+
proseGates: z4.object({
|
|
6204
|
+
doctrineRoot: z4.string().min(1).optional(),
|
|
6205
|
+
readerFacingPrefix: z4.string().min(1).optional(),
|
|
6206
|
+
readerFacingSuffix: z4.string().min(1).optional(),
|
|
6207
|
+
legacySlugDir: z4.string().min(1).optional()
|
|
5223
6208
|
}).optional(),
|
|
5224
|
-
projects:
|
|
5225
|
-
dispatch:
|
|
5226
|
-
timeoutMs:
|
|
5227
|
-
|
|
6209
|
+
projects: z4.array(ProjectEntrySchema).optional(),
|
|
6210
|
+
dispatch: z4.object({
|
|
6211
|
+
timeoutMs: z4.number().int().positive().optional(),
|
|
6212
|
+
killGraceMs: z4.number().int().positive().optional(),
|
|
6213
|
+
agent: z4.enum(["claude", "codex", "gemini"]).optional()
|
|
6214
|
+
}).optional(),
|
|
6215
|
+
reviewPolicy: z4.object({
|
|
6216
|
+
codeReviewThreshold: z4.string().min(1).optional(),
|
|
6217
|
+
securityThreshold: z4.string().min(1).optional(),
|
|
6218
|
+
maxRounds: z4.number().optional()
|
|
6219
|
+
}).optional(),
|
|
6220
|
+
prePush: z4.object({
|
|
6221
|
+
alwaysRun: z4.array(z4.string()).optional()
|
|
6222
|
+
}).optional(),
|
|
6223
|
+
report: z4.object({
|
|
6224
|
+
commandTimeoutMs: z4.number().int().positive().optional()
|
|
6225
|
+
}).superRefine((report, ctx) => {
|
|
6226
|
+
const commandTimeoutMs = report.commandTimeoutMs;
|
|
6227
|
+
if (commandTimeoutMs !== undefined && commandTimeoutMs > MAX_REPORT_COMMAND_TIMEOUT_MS) {
|
|
6228
|
+
ctx.addIssue({
|
|
6229
|
+
code: z4.ZodIssueCode.custom,
|
|
6230
|
+
path: ["commandTimeoutMs"],
|
|
6231
|
+
message: "must be at most 3600000 (1 hour)"
|
|
6232
|
+
});
|
|
6233
|
+
}
|
|
6234
|
+
}).optional(),
|
|
6235
|
+
logPublish: z4.object({
|
|
6236
|
+
issue: z4.number().int().positive().optional(),
|
|
6237
|
+
pr: z4.number().int().positive().optional(),
|
|
6238
|
+
maxChunksPerFlush: z4.number().int().positive().optional()
|
|
6239
|
+
}).refine((v) => !(v.issue !== undefined && v.pr !== undefined), {
|
|
6240
|
+
message: "logPublish: set at most one of issue/pr, not both"
|
|
5228
6241
|
}).optional()
|
|
5229
6242
|
});
|
|
5230
6243
|
var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
|
|
@@ -5299,6 +6312,28 @@ function resolvePrincipalAllowlist(config) {
|
|
|
5299
6312
|
function resolveReleaseActor(config) {
|
|
5300
6313
|
return config?.releaseActor ?? DEFAULT_RELEASE_ACTOR;
|
|
5301
6314
|
}
|
|
6315
|
+
function resolveReviewPolicy(config) {
|
|
6316
|
+
const raw = config?.reviewPolicy;
|
|
6317
|
+
if (!raw)
|
|
6318
|
+
return DEFAULT_REVIEW_POLICY;
|
|
6319
|
+
const codeReviewThreshold = raw.codeReviewThreshold ?? DEFAULT_REVIEW_POLICY.codeReviewThreshold;
|
|
6320
|
+
if (!isKnownSeverity(CODE_REVIEW_SEVERITY_ORDER, codeReviewThreshold)) {
|
|
6321
|
+
throw new Error(`vinaya.config.json: reviewPolicy.codeReviewThreshold "${codeReviewThreshold}" is not one of ${CODE_REVIEW_SEVERITY_ORDER.join(" > ")} — fix the config, this never falls back to a default.`);
|
|
6322
|
+
}
|
|
6323
|
+
const securityThreshold = raw.securityThreshold ?? DEFAULT_REVIEW_POLICY.securityThreshold;
|
|
6324
|
+
if (!isKnownSeverity(SECURITY_SEVERITY_ORDER, securityThreshold)) {
|
|
6325
|
+
throw new Error(`vinaya.config.json: reviewPolicy.securityThreshold "${securityThreshold}" is not one of ${SECURITY_SEVERITY_ORDER.join(" > ")} — fix the config, this never falls back to a default.`);
|
|
6326
|
+
}
|
|
6327
|
+
const maxRounds = raw.maxRounds ?? DEFAULT_REVIEW_POLICY.maxRounds;
|
|
6328
|
+
if (!Number.isInteger(maxRounds) || maxRounds < 1) {
|
|
6329
|
+
throw new Error(`vinaya.config.json: reviewPolicy.maxRounds "${maxRounds}" is not a positive integer — fix the config, this never falls back to a default.`);
|
|
6330
|
+
}
|
|
6331
|
+
return {
|
|
6332
|
+
codeReviewThreshold,
|
|
6333
|
+
securityThreshold,
|
|
6334
|
+
maxRounds
|
|
6335
|
+
};
|
|
6336
|
+
}
|
|
5302
6337
|
function trustAnchorRepo() {
|
|
5303
6338
|
const wellFormed = (slug) => /^[^/\s]+\/[^/\s]+$/.test(slug) ? slug : null;
|
|
5304
6339
|
const fromRunner = process.env.GITHUB_REPOSITORY?.trim();
|
|
@@ -5426,6 +6461,44 @@ function fetchIssueBodyForObjectives(issueNumber) {
|
|
|
5426
6461
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5427
6462
|
});
|
|
5428
6463
|
}
|
|
6464
|
+
function fetchIssueCommentsForBrief(issueNumber) {
|
|
6465
|
+
const out = execFileSync4("gh", ["issue", "view", String(issueNumber), "--json", "comments"], {
|
|
6466
|
+
encoding: "utf8",
|
|
6467
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
6468
|
+
});
|
|
6469
|
+
const parsed = JSON.parse(out);
|
|
6470
|
+
return parsed.comments.map((c) => ({ body: c.body, author: c.author?.login ?? null }));
|
|
6471
|
+
}
|
|
6472
|
+
function resolveBriefHash(pr, principalAllowlist) {
|
|
6473
|
+
const { issue } = extractIssue(pr.body);
|
|
6474
|
+
if (issue === null)
|
|
6475
|
+
return null;
|
|
6476
|
+
let comments;
|
|
6477
|
+
try {
|
|
6478
|
+
comments = fetchIssueCommentsForBrief(issue);
|
|
6479
|
+
} catch (err) {
|
|
6480
|
+
if (isIssueNotFoundError(err)) {
|
|
6481
|
+
emitCheckError({
|
|
6482
|
+
schema: CHECK_SCHEMA_VERSION,
|
|
6483
|
+
check: CHECK_NAME,
|
|
6484
|
+
severity: "error",
|
|
6485
|
+
message: `review-gate severity:infra — Issue #${issue} does not resolve via \`gh issue view\` — cannot verify the brief-hash binding for a PR whose linked Issue no longer exists.`,
|
|
6486
|
+
agent_recovery_prompt: `Restore Issue #${issue}, fix \`Closes #N\` to name a real Issue, or have a principal apply the \`vinaya/waiver:review\` label, then re-run \`vinaya check review-gate\`.`
|
|
6487
|
+
});
|
|
6488
|
+
process.exit(1);
|
|
6489
|
+
}
|
|
6490
|
+
emitCheckError({
|
|
6491
|
+
schema: CHECK_SCHEMA_VERSION,
|
|
6492
|
+
check: CHECK_NAME,
|
|
6493
|
+
severity: "error",
|
|
6494
|
+
message: `review-gate severity:infra — could not fetch Issue #${issue}'s comments via \`gh issue view\` to resolve its frozen-brief hash: ${err.message}`,
|
|
6495
|
+
agent_recovery_prompt: "Confirm `gh auth status` passes and the Issue number is correct, then re-run `vinaya check review-gate`."
|
|
6496
|
+
});
|
|
6497
|
+
process.exit(1);
|
|
6498
|
+
}
|
|
6499
|
+
const frozen = resolveNewestFrozenBrief(comments, principalAllowlist);
|
|
6500
|
+
return frozen ? briefHash(frozen.content) : null;
|
|
6501
|
+
}
|
|
5429
6502
|
function resolveObjectivesVersion(pr) {
|
|
5430
6503
|
const { issue } = extractIssue(pr.body);
|
|
5431
6504
|
const source = resolveObjectivesSource(pr.body, issue, OBJECTIVES_SINCE_ISSUE);
|
|
@@ -5481,6 +6554,15 @@ function resolveObjectivesVersion(pr) {
|
|
|
5481
6554
|
}
|
|
5482
6555
|
return objectivesVersion(own.objectives);
|
|
5483
6556
|
}
|
|
6557
|
+
function uncheckedPrincipalReason(body, branch) {
|
|
6558
|
+
const result = evaluateTestPlanGate(body, branch);
|
|
6559
|
+
if (result.verdict !== "fail")
|
|
6560
|
+
return null;
|
|
6561
|
+
const uncheckedLines = result.messages.filter((m) => /^\s*[-*]\s+\[\s\]/.test(m)).map((m) => m.trim());
|
|
6562
|
+
if (uncheckedLines.length === 0)
|
|
6563
|
+
return null;
|
|
6564
|
+
return `unticked [principal] Test Plan item(s) — ${uncheckedLines.join("; ")}`;
|
|
6565
|
+
}
|
|
5484
6566
|
function shaFromLsRemote(branch) {
|
|
5485
6567
|
try {
|
|
5486
6568
|
const out = execFileSync4("git", ["ls-remote", "origin", `refs/heads/${branch}`], {
|
|
@@ -5504,6 +6586,9 @@ function shaFromGhApi(branch) {
|
|
|
5504
6586
|
return null;
|
|
5505
6587
|
}
|
|
5506
6588
|
}
|
|
6589
|
+
function resolveBaseSha(pr) {
|
|
6590
|
+
return shaFromLsRemote(pr.baseRefName) ?? shaFromGhApi(pr.baseRefName);
|
|
6591
|
+
}
|
|
5507
6592
|
function resolveTrueHeadSha(pr) {
|
|
5508
6593
|
const trueSha = shaFromLsRemote(pr.headRefName) ?? shaFromGhApi(pr.headRefName);
|
|
5509
6594
|
if (trueSha && pr.headRefOid && trueSha !== pr.headRefOid) {
|
|
@@ -5604,7 +6689,32 @@ function main() {
|
|
|
5604
6689
|
});
|
|
5605
6690
|
process.exit(1);
|
|
5606
6691
|
}
|
|
5607
|
-
const
|
|
6692
|
+
const baseSha = resolveBaseSha(pr);
|
|
6693
|
+
if (!baseSha) {
|
|
6694
|
+
emitCheckError({
|
|
6695
|
+
schema: CHECK_SCHEMA_VERSION,
|
|
6696
|
+
check: CHECK_NAME,
|
|
6697
|
+
severity: "error",
|
|
6698
|
+
message: `review-gate severity:infra — could not resolve PR #${prNumber}'s base branch \`${pr.baseRefName}\`'s tip via \`git ls-remote\` or the forge's \`git/ref/heads\` API.`,
|
|
6699
|
+
agent_recovery_prompt: "Confirm the base branch still exists on origin and `gh auth status` passes, then re-run `vinaya check review-gate`."
|
|
6700
|
+
});
|
|
6701
|
+
process.exit(1);
|
|
6702
|
+
}
|
|
6703
|
+
const trustAnchorConfig = loadTrustAnchorConfig();
|
|
6704
|
+
const principalAllowlist = resolvePrincipalAllowlist(trustAnchorConfig);
|
|
6705
|
+
let reviewPolicy;
|
|
6706
|
+
try {
|
|
6707
|
+
reviewPolicy = resolveReviewPolicy(trustAnchorConfig);
|
|
6708
|
+
} catch (err) {
|
|
6709
|
+
emitCheckError({
|
|
6710
|
+
schema: CHECK_SCHEMA_VERSION,
|
|
6711
|
+
check: CHECK_NAME,
|
|
6712
|
+
severity: "error",
|
|
6713
|
+
message: `review-gate severity:infra — ${err instanceof Error ? err.message : String(err)}`,
|
|
6714
|
+
agent_recovery_prompt: "Fix `reviewPolicy` in the default branch's `vinaya.config.json` to a known severity on each role's own scale, then re-run `vinaya check review-gate`."
|
|
6715
|
+
});
|
|
6716
|
+
process.exit(1);
|
|
6717
|
+
}
|
|
5608
6718
|
const waived = isWaiverLabelActorVerified({
|
|
5609
6719
|
label: WAIVER_LABEL_REVIEW,
|
|
5610
6720
|
labels,
|
|
@@ -5619,9 +6729,13 @@ function main() {
|
|
|
5619
6729
|
mechanicalChecks,
|
|
5620
6730
|
headSha,
|
|
5621
6731
|
patchIdOf: (sha) => patchIdAt(pr.baseRefName, sha),
|
|
6732
|
+
baseSha,
|
|
5622
6733
|
objectivesVersion: waived ? null : resolveObjectivesVersion(pr),
|
|
5623
|
-
rulingOrdinal: newestPrincipalRulingOrdinal(pr.comments.map((c) => ({ body: c.body, author: c.author?.login ?? null })), principalAllowlist)
|
|
6734
|
+
rulingOrdinal: newestPrincipalRulingOrdinal(pr.comments.map((c) => ({ body: c.body, author: c.author?.login ?? null })), principalAllowlist),
|
|
6735
|
+
policy: reviewPolicy,
|
|
6736
|
+
briefHash: waived ? null : resolveBriefHash(pr, principalAllowlist)
|
|
5624
6737
|
});
|
|
6738
|
+
let failed = false;
|
|
5625
6739
|
if (result.verdict === "fail") {
|
|
5626
6740
|
emitCheckError({
|
|
5627
6741
|
schema: CHECK_SCHEMA_VERSION,
|
|
@@ -5630,8 +6744,24 @@ function main() {
|
|
|
5630
6744
|
message: `review-gate (PR #${prNumber}): ${result.reason}`,
|
|
5631
6745
|
agent_recovery_prompt: "Wait for a code-reviewer APPROVE and a security-review PASS on this PR (or ask a principal to apply the `vinaya/waiver:review` label), then re-run `vinaya check review-gate`."
|
|
5632
6746
|
});
|
|
5633
|
-
|
|
6747
|
+
failed = true;
|
|
5634
6748
|
}
|
|
5635
|
-
|
|
6749
|
+
const principalReason = uncheckedPrincipalReason(pr.body, pr.headRefName);
|
|
6750
|
+
if (principalReason !== null) {
|
|
6751
|
+
emitCheckError({
|
|
6752
|
+
schema: CHECK_SCHEMA_VERSION,
|
|
6753
|
+
check: CHECK_NAME,
|
|
6754
|
+
severity: "error",
|
|
6755
|
+
message: `review-gate (PR #${prNumber}): ${principalReason}`,
|
|
6756
|
+
agent_recovery_prompt: "Nothing for the Developer to fix here — wait for the Principal to verify in a real signed-in browser and tick each `[principal]` Test Plan box, then re-run `vinaya check review-gate`."
|
|
6757
|
+
});
|
|
6758
|
+
failed = true;
|
|
6759
|
+
}
|
|
6760
|
+
process.exit(failed ? 1 : 0);
|
|
5636
6761
|
}
|
|
5637
|
-
main
|
|
6762
|
+
if (__require.main == __require.module) {
|
|
6763
|
+
main();
|
|
6764
|
+
}
|
|
6765
|
+
export {
|
|
6766
|
+
uncheckedPrincipalReason
|
|
6767
|
+
};
|