@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
|
@@ -1647,6 +1647,52 @@ var systemEnv3 = {
|
|
|
1647
1647
|
...process.env,
|
|
1648
1648
|
PATH: [process.env.PATH, "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"].filter(Boolean).join(":")
|
|
1649
1649
|
};
|
|
1650
|
+
var defaultExec = () => execFileAsync3("git", ["remote", "get-url", "origin"], { env: systemEnv3, timeout: 5000 });
|
|
1651
|
+
var cached;
|
|
1652
|
+
var inflight;
|
|
1653
|
+
async function resolveRepo(exec = defaultExec) {
|
|
1654
|
+
if (cached !== undefined)
|
|
1655
|
+
return cached;
|
|
1656
|
+
const fromEnv = process.env.AEG_REPO;
|
|
1657
|
+
if (fromEnv) {
|
|
1658
|
+
const parsed = parseOwnerRepo(fromEnv);
|
|
1659
|
+
if (parsed) {
|
|
1660
|
+
cached = parsed;
|
|
1661
|
+
return cached;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
if (inflight)
|
|
1665
|
+
return inflight;
|
|
1666
|
+
inflight = (async () => {
|
|
1667
|
+
try {
|
|
1668
|
+
const { stdout } = await exec();
|
|
1669
|
+
cached = parseGitRemoteUrl(stdout.trim());
|
|
1670
|
+
return cached;
|
|
1671
|
+
} catch (err) {
|
|
1672
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1673
|
+
console.warn(`[aeg-forge-state] resolveRepo: git remote lookup failed (will retry): ${message}`);
|
|
1674
|
+
return null;
|
|
1675
|
+
} finally {
|
|
1676
|
+
inflight = undefined;
|
|
1677
|
+
}
|
|
1678
|
+
})();
|
|
1679
|
+
return inflight;
|
|
1680
|
+
}
|
|
1681
|
+
function parseGitRemoteUrl(url) {
|
|
1682
|
+
const ssh = url.match(/^git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/);
|
|
1683
|
+
if (ssh?.[1] && ssh[2])
|
|
1684
|
+
return { owner: ssh[1], repo: ssh[2] };
|
|
1685
|
+
const https = url.match(/^https?:\/\/(?:[^@]+@)?github\.com\/([^/]+)\/(.+?)(?:\.git)?\/?$/);
|
|
1686
|
+
if (https?.[1] && https[2])
|
|
1687
|
+
return { owner: https[1], repo: https[2] };
|
|
1688
|
+
return null;
|
|
1689
|
+
}
|
|
1690
|
+
function parseOwnerRepo(value) {
|
|
1691
|
+
const match = value.match(/^([^/]+)\/(.+)$/);
|
|
1692
|
+
if (!match?.[1] || !match[2])
|
|
1693
|
+
return null;
|
|
1694
|
+
return { owner: match[1], repo: match[2] };
|
|
1695
|
+
}
|
|
1650
1696
|
// ../../packages/aeg-core/src/anchored-region.ts
|
|
1651
1697
|
function anchoredRegionBounds(body, field) {
|
|
1652
1698
|
const masked = maskCode(body);
|
|
@@ -1957,6 +2003,9 @@ function parseInlineFieldList(section) {
|
|
|
1957
2003
|
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
1958
2004
|
var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1959
2005
|
var RULING_ORDINAL_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Ruling ordinal:\s*(\d+)(?!\d)/im;
|
|
2006
|
+
var BRIEF_HASH_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Brief hash:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
2007
|
+
var POLICY_DIGEST_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Policy digest:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
2008
|
+
var BASE_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged base:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
1960
2009
|
function firstFiveLines(comment) {
|
|
1961
2010
|
return comment.split(`
|
|
1962
2011
|
`).slice(0, 5).join(`
|
|
@@ -1979,6 +2028,35 @@ function extractRulingOrdinal(comment) {
|
|
|
1979
2028
|
const m = firstSevenLines(comment).match(RULING_ORDINAL_PATTERN);
|
|
1980
2029
|
return m ? Number.parseInt(m[1], 10) : null;
|
|
1981
2030
|
}
|
|
2031
|
+
function firstElevenLines(comment) {
|
|
2032
|
+
return comment.split(`
|
|
2033
|
+
`).slice(0, 11).join(`
|
|
2034
|
+
`);
|
|
2035
|
+
}
|
|
2036
|
+
function extractBriefHash(comment) {
|
|
2037
|
+
const m = firstElevenLines(comment).match(BRIEF_HASH_PATTERN);
|
|
2038
|
+
return m ? m[1].toLowerCase() : null;
|
|
2039
|
+
}
|
|
2040
|
+
function extractPolicyDigest(comment) {
|
|
2041
|
+
const m = firstElevenLines(comment).match(POLICY_DIGEST_PATTERN);
|
|
2042
|
+
return m ? m[1].toLowerCase() : null;
|
|
2043
|
+
}
|
|
2044
|
+
function firstThirteenLines(comment) {
|
|
2045
|
+
return comment.split(`
|
|
2046
|
+
`).slice(0, 13).join(`
|
|
2047
|
+
`);
|
|
2048
|
+
}
|
|
2049
|
+
function extractBaseSha(comment) {
|
|
2050
|
+
const m = firstThirteenLines(comment).match(BASE_SHA_PATTERN);
|
|
2051
|
+
return m ? m[1].toLowerCase() : null;
|
|
2052
|
+
}
|
|
2053
|
+
var FINDING_SEVERITY_LINE = /^\d+\.\s+\[([A-Z][A-Z]*)\]\s+(.+?)\s+—/gm;
|
|
2054
|
+
function extractFindingSeverities(comment) {
|
|
2055
|
+
return [...comment.matchAll(FINDING_SEVERITY_LINE)].map((m) => ({
|
|
2056
|
+
severity: m[1],
|
|
2057
|
+
location: m[2]
|
|
2058
|
+
}));
|
|
2059
|
+
}
|
|
1982
2060
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
1983
2061
|
const candidates = comments.filter((c) => valuePattern.test(c));
|
|
1984
2062
|
if (candidates.length === 0) {
|
|
@@ -1987,6 +2065,10 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1987
2065
|
headSha: null,
|
|
1988
2066
|
objectivesVersion: null,
|
|
1989
2067
|
rulingOrdinal: null,
|
|
2068
|
+
briefHash: null,
|
|
2069
|
+
policyDigest: null,
|
|
2070
|
+
baseSha: null,
|
|
2071
|
+
findingSeverities: [],
|
|
1990
2072
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
1991
2073
|
};
|
|
1992
2074
|
}
|
|
@@ -1998,6 +2080,10 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1998
2080
|
headSha: null,
|
|
1999
2081
|
objectivesVersion: null,
|
|
2000
2082
|
rulingOrdinal: null,
|
|
2083
|
+
briefHash: null,
|
|
2084
|
+
policyDigest: null,
|
|
2085
|
+
baseSha: null,
|
|
2086
|
+
findingSeverities: [],
|
|
2001
2087
|
danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
|
|
2002
2088
|
};
|
|
2003
2089
|
}
|
|
@@ -2006,6 +2092,10 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
2006
2092
|
headSha: extractHeadSha(latest),
|
|
2007
2093
|
objectivesVersion: extractObjectivesVersion(latest),
|
|
2008
2094
|
rulingOrdinal: extractRulingOrdinal(latest),
|
|
2095
|
+
briefHash: extractBriefHash(latest),
|
|
2096
|
+
policyDigest: extractPolicyDigest(latest),
|
|
2097
|
+
baseSha: extractBaseSha(latest),
|
|
2098
|
+
findingSeverities: extractFindingSeverities(latest),
|
|
2009
2099
|
danglingNote: null
|
|
2010
2100
|
};
|
|
2011
2101
|
}
|
|
@@ -2341,6 +2431,53 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive,
|
|
|
2341
2431
|
}
|
|
2342
2432
|
return out;
|
|
2343
2433
|
}
|
|
2434
|
+
// ../../packages/aeg-core/src/review-policy.ts
|
|
2435
|
+
var CODE_REVIEW_SEVERITY_ORDER = ["BLOCKER", "MAJOR", "MINOR"];
|
|
2436
|
+
var SECURITY_SEVERITY_ORDER = ["CRITICAL", "HIGH", "MEDIUM", "LOW"];
|
|
2437
|
+
var DEFAULT_MAX_ROUNDS = 3;
|
|
2438
|
+
var DEFAULT_REVIEW_POLICY = {
|
|
2439
|
+
codeReviewThreshold: "BLOCKER",
|
|
2440
|
+
securityThreshold: "HIGH",
|
|
2441
|
+
maxRounds: DEFAULT_MAX_ROUNDS
|
|
2442
|
+
};
|
|
2443
|
+
var FILE_SHAPED_LOCATION = /\.[a-zA-Z0-9]{1,10}(:\d+)?\s*$/;
|
|
2444
|
+
var PROSE_LOCATION_PATTERNS = [/\bpr\s*body\b/i, /\bcomment\b/i];
|
|
2445
|
+
var ROLE_FILE_LOCATION = /(^|\/)aeg-root\/roles\//i;
|
|
2446
|
+
function isProseLocation(location) {
|
|
2447
|
+
if (ROLE_FILE_LOCATION.test(location))
|
|
2448
|
+
return true;
|
|
2449
|
+
if (FILE_SHAPED_LOCATION.test(location))
|
|
2450
|
+
return false;
|
|
2451
|
+
return PROSE_LOCATION_PATTERNS.some((pattern) => pattern.test(location));
|
|
2452
|
+
}
|
|
2453
|
+
var PROSE_CAP_SEVERITY = "MINOR";
|
|
2454
|
+
function blockingSeverities(scale, threshold) {
|
|
2455
|
+
const idx = scale.indexOf(threshold);
|
|
2456
|
+
if (idx === -1) {
|
|
2457
|
+
throw new Error(`blockingSeverities: threshold "${threshold}" is not one of ${scale.join(" > ")}`);
|
|
2458
|
+
}
|
|
2459
|
+
return scale.slice(0, idx + 1);
|
|
2460
|
+
}
|
|
2461
|
+
function evaluateReviewFindings(findings, scale, threshold) {
|
|
2462
|
+
const blocking = new Set(blockingSeverities(scale, threshold));
|
|
2463
|
+
const blockingFindings = findings.filter((f) => {
|
|
2464
|
+
if (!scale.includes(f.severity)) {
|
|
2465
|
+
throw new Error(`evaluateReviewFindings: severity "${f.severity}" is not one of ${scale.join(" > ")}`);
|
|
2466
|
+
}
|
|
2467
|
+
const effectiveSeverity = f.location !== undefined && isProseLocation(f.location) ? PROSE_CAP_SEVERITY : f.severity;
|
|
2468
|
+
return blocking.has(effectiveSeverity);
|
|
2469
|
+
});
|
|
2470
|
+
return { outcome: blockingFindings.length > 0 ? "blocked" : "clean", blockingFindings };
|
|
2471
|
+
}
|
|
2472
|
+
function evaluateCodeReview(findings, policy) {
|
|
2473
|
+
return evaluateReviewFindings(findings, CODE_REVIEW_SEVERITY_ORDER, policy.codeReviewThreshold);
|
|
2474
|
+
}
|
|
2475
|
+
function evaluateSecurityReview(findings, policy) {
|
|
2476
|
+
return evaluateReviewFindings(findings, SECURITY_SEVERITY_ORDER, policy.securityThreshold);
|
|
2477
|
+
}
|
|
2478
|
+
function isKnownSeverity(scale, value) {
|
|
2479
|
+
return scale.includes(value);
|
|
2480
|
+
}
|
|
2344
2481
|
// ../../packages/aeg-core/src/pr-tier.ts
|
|
2345
2482
|
var TIER_FIELD = /(\*\*)?\s*Tier\s*(\*\*)?\s*:\s*(\*\*)?\s*([013])\b/i;
|
|
2346
2483
|
function readTierFromPrBody(prBody) {
|
|
@@ -2711,7 +2848,7 @@ function checkForField(prBody) {
|
|
|
2711
2848
|
]
|
|
2712
2849
|
};
|
|
2713
2850
|
}
|
|
2714
|
-
function
|
|
2851
|
+
function checkClosesNPresence(prBody) {
|
|
2715
2852
|
const closesPattern = /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s{0,8}:?\s{0,8}#\d+/i;
|
|
2716
2853
|
if (closesPattern.test(stripCode(prBody))) {
|
|
2717
2854
|
return { status: "pass", errors: [] };
|
|
@@ -2743,10 +2880,22 @@ var COMMIT_TYPES = [
|
|
|
2743
2880
|
"Test"
|
|
2744
2881
|
];
|
|
2745
2882
|
var COMMIT_TYPE_STYLE = new RegExp(`^(${COMMIT_TYPES.join("|")})(\\([a-z0-9-]+\\))?: \\S`);
|
|
2883
|
+
function checkForgeTitle(title) {
|
|
2884
|
+
const taskStyle = /^\[[a-z0-9._-]+\] \S+ — \S/;
|
|
2885
|
+
if (COMMIT_TYPE_STYLE.test(title) || taskStyle.test(title))
|
|
2886
|
+
return { status: "pass", errors: [] };
|
|
2887
|
+
return {
|
|
2888
|
+
status: "fail",
|
|
2889
|
+
errors: [
|
|
2890
|
+
`brief-validation title: "${title}" matches neither title grammar — expected \`Type: description\` / \`Type(scope): description\` (commitlint types + Plan) or \`[tranche] id — description\` (task form).`
|
|
2891
|
+
]
|
|
2892
|
+
};
|
|
2893
|
+
}
|
|
2746
2894
|
var BRIEF_SHAPE_MARKERS = [checkSurfaceMap, checkDocUpdateList, checkStopConditions, checkAutonomyClause];
|
|
2747
2895
|
var TASK_BRANCH_PATTERN = /^task\/[^/]+\/[^/]+$/;
|
|
2896
|
+
var TASK_ISSUE_BRANCH_PATTERN = /^task\/issue-\d+$/;
|
|
2748
2897
|
function isTaskBranch(branch) {
|
|
2749
|
-
return TASK_BRANCH_PATTERN.test(branch);
|
|
2898
|
+
return TASK_BRANCH_PATTERN.test(branch) || TASK_ISSUE_BRANCH_PATTERN.test(branch);
|
|
2750
2899
|
}
|
|
2751
2900
|
function isBriefShaped(prBody) {
|
|
2752
2901
|
const stripped = stripCode(prBody);
|
|
@@ -2868,8 +3017,9 @@ function packagesNamedIn(text) {
|
|
|
2868
3017
|
}
|
|
2869
3018
|
function hasTestPathForConsumer(text, consumerDir) {
|
|
2870
3019
|
const escaped = consumerDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2871
|
-
const
|
|
2872
|
-
|
|
3020
|
+
const filePathRe = new RegExp(`${escaped}\\/[\\w./-]*\\.test\\.[A-Za-z0-9]+`);
|
|
3021
|
+
const testDirRe = new RegExp(`${escaped}\\/(?:[\\w-]+\\/)*(?:tests|specs)(?:\\/|\\b)`);
|
|
3022
|
+
return filePathRe.test(text) || testDirRe.test(text);
|
|
2873
3023
|
}
|
|
2874
3024
|
function checkConsumerTests(prBody, consumersOf) {
|
|
2875
3025
|
const section4 = extractNumberedSection(prBody, 4);
|
|
@@ -2973,7 +3123,7 @@ function checkBriefSections(prBody, readTier, options = {}) {
|
|
|
2973
3123
|
checkConsumerTests(prBody, consumersOf),
|
|
2974
3124
|
checkDefeatCases(prBody),
|
|
2975
3125
|
...issueObjectives !== undefined ? [checkObjectivesCopy(prBody, issueObjectives), checkObjectivesCoverage(prBody)] : [],
|
|
2976
|
-
...requireClosesN ? [
|
|
3126
|
+
...requireClosesN ? [checkClosesNPresence(prBody)] : []
|
|
2977
3127
|
];
|
|
2978
3128
|
return { errors: results.flatMap((r) => r.errors) };
|
|
2979
3129
|
}
|
|
@@ -3263,6 +3413,28 @@ function topLevelSectionText(body, headingName) {
|
|
|
3263
3413
|
const next = /^##[ \t]/m.exec(afterHeading);
|
|
3264
3414
|
return next ? afterHeading.slice(0, next.index) : afterHeading;
|
|
3265
3415
|
}
|
|
3416
|
+
function partHasBacktickedPath(text) {
|
|
3417
|
+
let i = 0;
|
|
3418
|
+
while (i < text.length) {
|
|
3419
|
+
const start = text.indexOf("`", i);
|
|
3420
|
+
if (start === -1)
|
|
3421
|
+
return false;
|
|
3422
|
+
const end = text.indexOf("`", start + 1);
|
|
3423
|
+
if (end === -1)
|
|
3424
|
+
return false;
|
|
3425
|
+
if (text.slice(start + 1, end).includes("/"))
|
|
3426
|
+
return true;
|
|
3427
|
+
i = end + 1;
|
|
3428
|
+
}
|
|
3429
|
+
return false;
|
|
3430
|
+
}
|
|
3431
|
+
var PART_MIN_WORDS_OUTSIDE_BACKTICKS = 3;
|
|
3432
|
+
function stripPartBackticks(text) {
|
|
3433
|
+
return text.replace(/`[^`\n]*`/g, " ");
|
|
3434
|
+
}
|
|
3435
|
+
function partWordCount(text) {
|
|
3436
|
+
return text.split(/\s+/).filter((w) => /[a-z]/i.test(w)).length;
|
|
3437
|
+
}
|
|
3266
3438
|
function looksLikeFilePath(entry2) {
|
|
3267
3439
|
const stripped = entry2.replace(/\/\*\*?$/, "");
|
|
3268
3440
|
const lastSegment = stripped.split("/").pop() ?? stripped;
|
|
@@ -3303,6 +3475,18 @@ function globCoversPath(glob, path) {
|
|
|
3303
3475
|
const p = path.replace(/\/+$/, "");
|
|
3304
3476
|
return g === p || g.startsWith(`${p}/`) || p.startsWith(`${g}/`);
|
|
3305
3477
|
}
|
|
3478
|
+
function checkSurfaceGlobsResolve(body, resolvesToFile) {
|
|
3479
|
+
const surface = parseIssueSurface(body);
|
|
3480
|
+
if (!surface.ok)
|
|
3481
|
+
return { status: "pass", errors: [] };
|
|
3482
|
+
const errors = [];
|
|
3483
|
+
for (const glob of surface.value.in) {
|
|
3484
|
+
if (!resolvesToFile(glob)) {
|
|
3485
|
+
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.`);
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3489
|
+
}
|
|
3306
3490
|
function checkSurfaceScope(changedFiles, outGlobs) {
|
|
3307
3491
|
if (outGlobs.length === 0)
|
|
3308
3492
|
return { ok: true };
|
|
@@ -3314,9 +3498,77 @@ function checkSurfaceScope(changedFiles, outGlobs) {
|
|
|
3314
3498
|
}
|
|
3315
3499
|
return violations.length > 0 ? { ok: false, violations } : { ok: true };
|
|
3316
3500
|
}
|
|
3501
|
+
var ISSUE_PART_LINE_RE = /^Part\s+(\d+)\s*\(([^)]*)\)\s*[-—–]\s*(.*)$/i;
|
|
3502
|
+
function parseIssueParts(body) {
|
|
3503
|
+
const section = topLevelSectionText(body, "Parts");
|
|
3504
|
+
if (section === null)
|
|
3505
|
+
return { ok: false, errors: ["no `## Parts` heading found in the body."] };
|
|
3506
|
+
const lines = section.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
3507
|
+
const parts = [];
|
|
3508
|
+
const errors = [];
|
|
3509
|
+
for (const line of lines) {
|
|
3510
|
+
const m = ISSUE_PART_LINE_RE.exec(line);
|
|
3511
|
+
if (!m) {
|
|
3512
|
+
errors.push(`"${line}" is not a well-formed Parts line — expected \`Part <n> (<refs>) — <outcome>\`.`);
|
|
3513
|
+
continue;
|
|
3514
|
+
}
|
|
3515
|
+
const n = Number.parseInt(m[1], 10);
|
|
3516
|
+
const refs = m[2];
|
|
3517
|
+
const text = m[3].trim();
|
|
3518
|
+
if (text.length === 0) {
|
|
3519
|
+
errors.push(`Part ${n} has no outcome text after the dash — every Part states one observable outcome.`);
|
|
3520
|
+
continue;
|
|
3521
|
+
}
|
|
3522
|
+
if (partHasBacktickedPath(text) && partWordCount(stripPartBackticks(text)) < PART_MIN_WORDS_OUTSIDE_BACKTICKS) {
|
|
3523
|
+
errors.push(`Part ${n} is little more than a file path — a Part names an outcome and symbols, never a bare path.`);
|
|
3524
|
+
continue;
|
|
3525
|
+
}
|
|
3526
|
+
const objectiveIds = [...refs.matchAll(/O(\d+)/g)].map((r) => Number.parseInt(r[1], 10));
|
|
3527
|
+
parts.push({ n, objectiveIds, text });
|
|
3528
|
+
}
|
|
3529
|
+
if (parts.length === 0) {
|
|
3530
|
+
errors.push("the `## Parts` section has no well-formed `Part <n> (<refs>) — <outcome>` lines.");
|
|
3531
|
+
}
|
|
3532
|
+
if (errors.length > 0)
|
|
3533
|
+
return { ok: false, errors };
|
|
3534
|
+
return { ok: true, value: parts };
|
|
3535
|
+
}
|
|
3536
|
+
function checkPartsCiteDefinedObjectives(body) {
|
|
3537
|
+
const parts = parseIssueParts(body);
|
|
3538
|
+
const objectives = objectivesOf(body);
|
|
3539
|
+
if (!parts.ok || !objectives.ok)
|
|
3540
|
+
return { status: "pass", errors: [] };
|
|
3541
|
+
const definedIds = new Set(objectives.objectives.map((o) => Number.parseInt(o.id.slice(1), 10)));
|
|
3542
|
+
const errors = [];
|
|
3543
|
+
for (const part of parts.value) {
|
|
3544
|
+
for (const objectiveId of part.objectiveIds) {
|
|
3545
|
+
if (!definedIds.has(objectiveId)) {
|
|
3546
|
+
errors.push(`issue-validation Parts: Part ${part.n} cites O${objectiveId}, which the Issue's own \`## Objectives\` section does not define.`);
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3551
|
+
}
|
|
3317
3552
|
function isTaskIssueLabelSet(labels) {
|
|
3318
3553
|
return hasLabel("tranche", labels);
|
|
3319
3554
|
}
|
|
3555
|
+
function checkTrancheLabelPresence(_body, _labels) {
|
|
3556
|
+
return { status: "pass", errors: [] };
|
|
3557
|
+
}
|
|
3558
|
+
function checkMilestoneAttach(labels, currentMilestoneTitle, resolvedMilestoneTitle) {
|
|
3559
|
+
if (!isTaskIssueLabelSet(labels))
|
|
3560
|
+
return { status: "pass", errors: [] };
|
|
3561
|
+
if (resolvedMilestoneTitle === null)
|
|
3562
|
+
return { status: "pass", errors: [] };
|
|
3563
|
+
if (currentMilestoneTitle === resolvedMilestoneTitle)
|
|
3564
|
+
return { status: "pass", errors: [] };
|
|
3565
|
+
return {
|
|
3566
|
+
status: "fail",
|
|
3567
|
+
errors: [
|
|
3568
|
+
`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}".`
|
|
3569
|
+
]
|
|
3570
|
+
};
|
|
3571
|
+
}
|
|
3320
3572
|
var TYPE_LABEL_IDS = LABELS.filter((l) => l.category === "type").map((l) => l.id);
|
|
3321
3573
|
function isControlCodePoint(codePoint) {
|
|
3322
3574
|
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
|
|
@@ -3381,6 +3633,19 @@ function checkProjectsRegistered(body, _labels, registeredNames) {
|
|
|
3381
3633
|
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;
|
|
3382
3634
|
var DOC_PATH_RE_GLOBAL = new RegExp(DOC_PATH_RE.source, "gi");
|
|
3383
3635
|
|
|
3636
|
+
// ../../packages/aeg-core/src/task-branch-identity.ts
|
|
3637
|
+
var ISSUE_BRANCH_PATTERN = /^task\/issue-(\d+)$/;
|
|
3638
|
+
var TRANCHE_BRANCH_PATTERN = /^task\/([^/]+)\/([^/]+)$/;
|
|
3639
|
+
function parseTaskBranchIdentity(branch) {
|
|
3640
|
+
const issueMatch = ISSUE_BRANCH_PATTERN.exec(branch);
|
|
3641
|
+
if (issueMatch)
|
|
3642
|
+
return { kind: "issue", issueNumber: Number(issueMatch[1]) };
|
|
3643
|
+
const trancheMatch = TRANCHE_BRANCH_PATTERN.exec(branch);
|
|
3644
|
+
if (trancheMatch)
|
|
3645
|
+
return { kind: "tranche", tranche: trancheMatch[1], taskId: trancheMatch[2] };
|
|
3646
|
+
return null;
|
|
3647
|
+
}
|
|
3648
|
+
|
|
3384
3649
|
// ../../packages/aeg-core/src/coherence-checks.ts
|
|
3385
3650
|
var COHERENCE_ENFORCED_FROM = "2026-07-01";
|
|
3386
3651
|
function isGrandfathered(isoDate) {
|
|
@@ -3401,6 +3666,7 @@ function checkA1(entries, principalAllowlist = PRINCIPAL_ALLOWLIST) {
|
|
|
3401
3666
|
if (handClosed)
|
|
3402
3667
|
continue;
|
|
3403
3668
|
failures.push({
|
|
3669
|
+
code: "closed-without-merge",
|
|
3404
3670
|
issue: e.task.issue,
|
|
3405
3671
|
tranche: e.trancheSlug,
|
|
3406
3672
|
task: e.task.id,
|
|
@@ -3425,6 +3691,7 @@ function checkA3(entries) {
|
|
|
3425
3691
|
continue;
|
|
3426
3692
|
if (e.facts.prState === "merged" && e.facts.issueState !== "closed") {
|
|
3427
3693
|
failures.push({
|
|
3694
|
+
code: "auto-close-misfire",
|
|
3428
3695
|
issue: e.task.issue,
|
|
3429
3696
|
tranche: e.trancheSlug,
|
|
3430
3697
|
task: e.task.id,
|
|
@@ -3449,6 +3716,7 @@ function checkT1(entries) {
|
|
|
3449
3716
|
continue;
|
|
3450
3717
|
if (e.facts === undefined) {
|
|
3451
3718
|
failures.push({
|
|
3719
|
+
code: "phantom-issue-ref",
|
|
3452
3720
|
issue: e.task.issue,
|
|
3453
3721
|
tranche: e.trancheSlug,
|
|
3454
3722
|
task: e.task.id,
|
|
@@ -3467,6 +3735,7 @@ function checkT2(openIssuesBySlug, topologyIssuesBySlug, ciTrancheSlug) {
|
|
|
3467
3735
|
for (const num of openNums) {
|
|
3468
3736
|
if (!topologySet.has(num)) {
|
|
3469
3737
|
failures.push({
|
|
3738
|
+
code: "orphan-task",
|
|
3470
3739
|
issue: num,
|
|
3471
3740
|
tranche: slug,
|
|
3472
3741
|
reason: `Issue #${num} is open and labeled ${trancheLabel(slug)} but does not appear in the topology file`
|
|
@@ -3501,6 +3770,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
|
|
|
3501
3770
|
continue;
|
|
3502
3771
|
if (forgeUnavailableSlugs?.has(e.trancheSlug)) {
|
|
3503
3772
|
failures.push({
|
|
3773
|
+
code: "tbd-in-active-tranche",
|
|
3504
3774
|
issue: null,
|
|
3505
3775
|
tranche: e.trancheSlug,
|
|
3506
3776
|
task: e.task.id,
|
|
@@ -3510,6 +3780,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
|
|
|
3510
3780
|
continue;
|
|
3511
3781
|
}
|
|
3512
3782
|
failures.push({
|
|
3783
|
+
code: "tbd-in-active-tranche",
|
|
3513
3784
|
issue: null,
|
|
3514
3785
|
tranche: e.trancheSlug,
|
|
3515
3786
|
task: e.task.id,
|
|
@@ -3542,6 +3813,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
|
|
|
3542
3813
|
const sameIssue = depEntry.task.issue !== null && e.task.issue !== null && depEntry.task.issue === e.task.issue;
|
|
3543
3814
|
if (sameTask || sameIssue) {
|
|
3544
3815
|
failures.push({
|
|
3816
|
+
code: "d1-self-dependency",
|
|
3545
3817
|
issue: e.task.issue,
|
|
3546
3818
|
tranche: e.trancheSlug,
|
|
3547
3819
|
task: e.task.id,
|
|
@@ -3553,6 +3825,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
|
|
|
3553
3825
|
const depClosed = depFacts?.issueState === "closed";
|
|
3554
3826
|
if (!depClosed) {
|
|
3555
3827
|
failures.push({
|
|
3828
|
+
code: "dispatched-on-unmet-deps",
|
|
3556
3829
|
issue: e.task.issue,
|
|
3557
3830
|
tranche: e.trancheSlug,
|
|
3558
3831
|
task: e.task.id,
|
|
@@ -3578,11 +3851,13 @@ function checkR1(issuesBySlug, grandfatheredIssues, registeredNames = []) {
|
|
|
3578
3851
|
const errors = [
|
|
3579
3852
|
...checkIssueRationale(issue.body).errors,
|
|
3580
3853
|
...checkProjectsRegistered(issue.body, issue.labels, registeredNames).errors,
|
|
3581
|
-
...checkIssueObjectives(issue.body, issue.number).errors
|
|
3854
|
+
...checkIssueObjectives(issue.body, issue.number).errors,
|
|
3855
|
+
...checkPartsCiteDefinedObjectives(issue.body).errors
|
|
3582
3856
|
];
|
|
3583
3857
|
if (errors.length === 0)
|
|
3584
3858
|
continue;
|
|
3585
3859
|
failures.push({
|
|
3860
|
+
code: "missing-rationale-field",
|
|
3586
3861
|
issue: issue.number,
|
|
3587
3862
|
tranche: slug,
|
|
3588
3863
|
reason: `Issue #${issue.number} fails the rationale gate: ${errors.join(" | ")}`,
|
|
@@ -3611,6 +3886,7 @@ function checkL1(files, entriesBySlug) {
|
|
|
3611
3886
|
const allClosed = withFacts.every((e) => e.facts?.issueState === "closed");
|
|
3612
3887
|
if (allClosed) {
|
|
3613
3888
|
failures.push({
|
|
3889
|
+
code: "archive-recommended",
|
|
3614
3890
|
tranche: f.slug,
|
|
3615
3891
|
reason: "Active tranche has no open task-Issues — consider archiving to completed/"
|
|
3616
3892
|
});
|
|
@@ -3642,27 +3918,38 @@ function extractClosesReferences(prBody) {
|
|
|
3642
3918
|
}
|
|
3643
3919
|
return referenced;
|
|
3644
3920
|
}
|
|
3645
|
-
function
|
|
3921
|
+
function checkClosesNTopology(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
3646
3922
|
const referenced = extractClosesReferences(prBody);
|
|
3647
3923
|
if (taskIssueRefs) {
|
|
3648
3924
|
for (const n of referenced) {
|
|
3649
|
-
const
|
|
3650
|
-
if (!
|
|
3925
|
+
const ref2 = taskIssueRefs.get(n);
|
|
3926
|
+
if (!ref2)
|
|
3651
3927
|
continue;
|
|
3652
|
-
const expectedBranch = `task/${
|
|
3928
|
+
const expectedBranch = `task/${ref2.trancheSlug}/${ref2.taskId}`;
|
|
3653
3929
|
if (branch !== expectedBranch) {
|
|
3654
3930
|
return {
|
|
3655
3931
|
ok: false,
|
|
3656
|
-
message: `closes-n-reverse: branch "${branch}" closes #${n} (task ${
|
|
3932
|
+
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.`
|
|
3657
3933
|
};
|
|
3658
3934
|
}
|
|
3659
3935
|
}
|
|
3660
3936
|
}
|
|
3661
|
-
const
|
|
3662
|
-
if (!
|
|
3937
|
+
const ref = parseTaskBranchIdentity(branch);
|
|
3938
|
+
if (!ref)
|
|
3663
3939
|
return { ok: true };
|
|
3664
|
-
|
|
3665
|
-
|
|
3940
|
+
if (ref.kind === "issue") {
|
|
3941
|
+
const expectedIssue2 = ref.issueNumber;
|
|
3942
|
+
if (!referenced.has(expectedIssue2)) {
|
|
3943
|
+
return {
|
|
3944
|
+
ok: false,
|
|
3945
|
+
expectedIssue: expectedIssue2,
|
|
3946
|
+
message: `closes-n: PR body does not contain \`Closes #${expectedIssue2}\` (required for branch "${branch}"). Add it to the PR body Summary section.`
|
|
3947
|
+
};
|
|
3948
|
+
}
|
|
3949
|
+
return { ok: true, expectedIssue: expectedIssue2 };
|
|
3950
|
+
}
|
|
3951
|
+
const trancheSlug = ref.tranche;
|
|
3952
|
+
const taskId = ref.taskId;
|
|
3666
3953
|
const trancheFile = trancheFiles.find((f) => f.slug === trancheSlug);
|
|
3667
3954
|
if (!trancheFile) {
|
|
3668
3955
|
return {
|
|
@@ -3714,34 +4001,79 @@ function extractIssue(body) {
|
|
|
3714
4001
|
const issue = headerNums.length > 0 ? headerNums[0] : bodyNums[0];
|
|
3715
4002
|
return { issue, extraIssues: bodyNums.filter((n) => n !== issue), outsideHeader: headerNums.length === 0 };
|
|
3716
4003
|
}
|
|
3717
|
-
// ../../packages/aeg-core/src/review-
|
|
3718
|
-
|
|
3719
|
-
|
|
4004
|
+
// ../../packages/aeg-core/src/review-input-manifest.ts
|
|
4005
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
4006
|
+
function briefHash(brief) {
|
|
4007
|
+
return createHash4("sha256").update(`${brief}
|
|
4008
|
+
`).digest("hex");
|
|
4009
|
+
}
|
|
4010
|
+
function policyDigest(policy) {
|
|
4011
|
+
return createHash4("sha256").update(JSON.stringify({
|
|
4012
|
+
codeReviewThreshold: policy.codeReviewThreshold,
|
|
4013
|
+
securityThreshold: policy.securityThreshold,
|
|
4014
|
+
maxRounds: policy.maxRounds
|
|
4015
|
+
})).digest("hex");
|
|
4016
|
+
}
|
|
4017
|
+
function isBoundToHead(echoed, headSha) {
|
|
4018
|
+
if (!echoed.headSha)
|
|
3720
4019
|
return false;
|
|
3721
|
-
return headSha.toLowerCase().startsWith(
|
|
4020
|
+
return headSha.toLowerCase().startsWith(echoed.headSha.toLowerCase());
|
|
3722
4021
|
}
|
|
3723
|
-
function isBoundByPatchIdentity(
|
|
3724
|
-
if (patchIdOf === undefined || !
|
|
4022
|
+
function isBoundByPatchIdentity(echoed, headSha, patchIdOf) {
|
|
4023
|
+
if (patchIdOf === undefined || !echoed.headSha)
|
|
3725
4024
|
return false;
|
|
3726
|
-
const judged = patchIdOf(
|
|
4025
|
+
const judged = patchIdOf(echoed.headSha);
|
|
3727
4026
|
const current = patchIdOf(headSha);
|
|
3728
4027
|
if (judged === null || current === null)
|
|
3729
4028
|
return false;
|
|
3730
4029
|
return judged === current;
|
|
3731
4030
|
}
|
|
3732
|
-
function
|
|
3733
|
-
|
|
4031
|
+
function isBoundToBase(echoed, currentBase) {
|
|
4032
|
+
if (currentBase === null)
|
|
4033
|
+
return true;
|
|
4034
|
+
if (!echoed.baseSha)
|
|
4035
|
+
return false;
|
|
4036
|
+
return currentBase.toLowerCase().startsWith(echoed.baseSha.toLowerCase());
|
|
3734
4037
|
}
|
|
3735
|
-
function isBoundToObjectives(
|
|
4038
|
+
function isBoundToObjectives(echoed, currentVersion) {
|
|
3736
4039
|
if (currentVersion === null)
|
|
3737
4040
|
return true;
|
|
3738
|
-
return
|
|
4041
|
+
return echoed.objectivesVersion === currentVersion;
|
|
3739
4042
|
}
|
|
3740
|
-
function isBoundToRulings(
|
|
3741
|
-
if (
|
|
4043
|
+
function isBoundToRulings(echoed, currentOrdinal) {
|
|
4044
|
+
if (echoed.rulingOrdinal === null)
|
|
3742
4045
|
return currentOrdinal === 0;
|
|
3743
|
-
return
|
|
4046
|
+
return echoed.rulingOrdinal === currentOrdinal;
|
|
4047
|
+
}
|
|
4048
|
+
function isBoundToBriefHash(echoed, currentHash) {
|
|
4049
|
+
if (currentHash === null)
|
|
4050
|
+
return true;
|
|
4051
|
+
return echoed.briefHash === currentHash;
|
|
4052
|
+
}
|
|
4053
|
+
function isBoundToPolicy(echoed, currentDigest) {
|
|
4054
|
+
return echoed.policyDigest === currentDigest;
|
|
4055
|
+
}
|
|
4056
|
+
function compareManifest(echoed, current, patchIdOf) {
|
|
4057
|
+
const exactHead = isBoundToHead(echoed, current.headSha);
|
|
4058
|
+
const patchHead = exactHead ? false : isBoundByPatchIdentity(echoed, current.headSha, patchIdOf);
|
|
4059
|
+
const head = exactHead || patchHead;
|
|
4060
|
+
const base = patchHead ? true : isBoundToBase(echoed, current.baseSha);
|
|
4061
|
+
const briefHashBound = isBoundToBriefHash(echoed, current.briefHash);
|
|
4062
|
+
const objectivesVersion2 = isBoundToObjectives(echoed, current.objectivesVersion);
|
|
4063
|
+
const rulingOrdinal = isBoundToRulings(echoed, current.rulingOrdinal);
|
|
4064
|
+
const policyDigestBound = isBoundToPolicy(echoed, current.policyDigest);
|
|
4065
|
+
return {
|
|
4066
|
+
bound: head && base && briefHashBound && objectivesVersion2 && rulingOrdinal && policyDigestBound,
|
|
4067
|
+
head,
|
|
4068
|
+
base,
|
|
4069
|
+
briefHash: briefHashBound,
|
|
4070
|
+
objectivesVersion: objectivesVersion2,
|
|
4071
|
+
rulingOrdinal,
|
|
4072
|
+
policyDigest: policyDigestBound
|
|
4073
|
+
};
|
|
3744
4074
|
}
|
|
4075
|
+
|
|
4076
|
+
// ../../packages/aeg-core/src/review-gate.ts
|
|
3745
4077
|
function checkReviewGate(input) {
|
|
3746
4078
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
3747
4079
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -3764,15 +4096,58 @@ function checkReviewGate(input) {
|
|
|
3764
4096
|
const verifiedBodies = verified.map((c) => c.body);
|
|
3765
4097
|
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
3766
4098
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
3767
|
-
const
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
4099
|
+
const policy = input.policy ?? DEFAULT_REVIEW_POLICY;
|
|
4100
|
+
let codeReviewPolicyEvaluation;
|
|
4101
|
+
let securityPolicyEvaluation;
|
|
4102
|
+
try {
|
|
4103
|
+
codeReviewPolicyEvaluation = evaluateCodeReview(codeReview.findingSeverities, policy);
|
|
4104
|
+
securityPolicyEvaluation = evaluateSecurityReview(security.findingSeverities, policy);
|
|
4105
|
+
} catch (err) {
|
|
4106
|
+
return {
|
|
4107
|
+
verdict: "fail",
|
|
4108
|
+
reason: `a verdict comment carries a finding severity this repository's policy does not recognize: ${err instanceof Error ? err.message : String(err)}`,
|
|
4109
|
+
waived: false
|
|
4110
|
+
};
|
|
4111
|
+
}
|
|
4112
|
+
const codeReviewTextClean = codeReview.value === "APPROVE";
|
|
4113
|
+
const securityTextClean = security.value === "PASS";
|
|
4114
|
+
const codeReviewPolicyClean = codeReviewPolicyEvaluation.outcome === "clean";
|
|
4115
|
+
const securityPolicyClean = securityPolicyEvaluation.outcome === "clean";
|
|
4116
|
+
const codeReviewClean = codeReviewTextClean && codeReviewPolicyClean;
|
|
4117
|
+
const securityClean = securityTextClean && securityPolicyClean;
|
|
4118
|
+
const currentManifest = {
|
|
4119
|
+
headSha: input.headSha,
|
|
4120
|
+
baseSha: input.baseSha ?? null,
|
|
4121
|
+
briefHash: input.briefHash ?? null,
|
|
4122
|
+
objectivesVersion: input.objectivesVersion,
|
|
4123
|
+
rulingOrdinal: input.rulingOrdinal,
|
|
4124
|
+
policyDigest: policyDigest(policy)
|
|
4125
|
+
};
|
|
4126
|
+
const codeReviewEchoed = {
|
|
4127
|
+
headSha: codeReview.headSha,
|
|
4128
|
+
baseSha: codeReview.baseSha,
|
|
4129
|
+
briefHash: codeReview.briefHash,
|
|
4130
|
+
objectivesVersion: codeReview.objectivesVersion,
|
|
4131
|
+
rulingOrdinal: codeReview.rulingOrdinal,
|
|
4132
|
+
policyDigest: codeReview.policyDigest
|
|
4133
|
+
};
|
|
4134
|
+
const securityEchoed = {
|
|
4135
|
+
headSha: security.headSha,
|
|
4136
|
+
baseSha: security.baseSha,
|
|
4137
|
+
briefHash: security.briefHash,
|
|
4138
|
+
objectivesVersion: security.objectivesVersion,
|
|
4139
|
+
rulingOrdinal: security.rulingOrdinal,
|
|
4140
|
+
policyDigest: security.policyDigest
|
|
4141
|
+
};
|
|
4142
|
+
const codeReviewBinding = compareManifest(codeReviewEchoed, currentManifest, input.patchIdOf);
|
|
4143
|
+
const securityBinding = compareManifest(securityEchoed, currentManifest, input.patchIdOf);
|
|
4144
|
+
const codeReviewBound = codeReviewBinding.head;
|
|
4145
|
+
const securityBound = securityBinding.head;
|
|
4146
|
+
const codeReviewObjectivesBound = codeReviewBinding.objectivesVersion;
|
|
4147
|
+
const securityObjectivesBound = securityBinding.objectivesVersion;
|
|
4148
|
+
const codeReviewRulingsBound = codeReviewBinding.rulingOrdinal;
|
|
4149
|
+
const securityRulingsBound = securityBinding.rulingOrdinal;
|
|
4150
|
+
if (codeReviewClean && codeReviewBinding.bound && securityClean && securityBinding.bound && mechanicalChecksClean) {
|
|
3776
4151
|
return {
|
|
3777
4152
|
verdict: "pass",
|
|
3778
4153
|
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.`,
|
|
@@ -3780,23 +4155,39 @@ function checkReviewGate(input) {
|
|
|
3780
4155
|
};
|
|
3781
4156
|
}
|
|
3782
4157
|
const problems = [];
|
|
3783
|
-
if (!
|
|
4158
|
+
if (!codeReviewTextClean) {
|
|
3784
4159
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
4160
|
+
} else if (!codeReviewPolicyClean) {
|
|
4161
|
+
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`);
|
|
3785
4162
|
} else if (!codeReviewBound) {
|
|
3786
4163
|
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
4164
|
+
} else if (!codeReviewBinding.base) {
|
|
4165
|
+
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)`);
|
|
3787
4166
|
} else if (!codeReviewObjectivesBound) {
|
|
3788
4167
|
problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3789
4168
|
} else if (!codeReviewRulingsBound) {
|
|
3790
4169
|
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`);
|
|
4170
|
+
} else if (!codeReviewBinding.briefHash) {
|
|
4171
|
+
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"}`);
|
|
4172
|
+
} else if (!codeReviewBinding.policyDigest) {
|
|
4173
|
+
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}`);
|
|
3791
4174
|
}
|
|
3792
|
-
if (!
|
|
4175
|
+
if (!securityTextClean) {
|
|
3793
4176
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
4177
|
+
} else if (!securityPolicyClean) {
|
|
4178
|
+
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`);
|
|
3794
4179
|
} else if (!securityBound) {
|
|
3795
4180
|
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
4181
|
+
} else if (!securityBinding.base) {
|
|
4182
|
+
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)`);
|
|
3796
4183
|
} else if (!securityObjectivesBound) {
|
|
3797
4184
|
problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3798
4185
|
} else if (!securityRulingsBound) {
|
|
3799
4186
|
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`);
|
|
4187
|
+
} else if (!securityBinding.briefHash) {
|
|
4188
|
+
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"}`);
|
|
4189
|
+
} else if (!securityBinding.policyDigest) {
|
|
4190
|
+
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}`);
|
|
3800
4191
|
}
|
|
3801
4192
|
if (!mechanicalChecksClean) {
|
|
3802
4193
|
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(", ")}`);
|
|
@@ -4488,42 +4879,45 @@ function checkDispatchReadiness(input) {
|
|
|
4488
4879
|
const { trancheSlug, task } = input;
|
|
4489
4880
|
const taskLabel = `task ${task.id} (tranche ${trancheSlug})`;
|
|
4490
4881
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
4491
|
-
const
|
|
4882
|
+
const blockerDetails = [];
|
|
4883
|
+
const push = (blockerClass, message) => {
|
|
4884
|
+
blockerDetails.push({ class: blockerClass, message });
|
|
4885
|
+
};
|
|
4492
4886
|
if (task.issue === null) {
|
|
4493
|
-
|
|
4887
|
+
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.`);
|
|
4494
4888
|
} else if (input.issue === null) {
|
|
4495
|
-
|
|
4889
|
+
push("issue-existence", `dispatch-gate issue-existence: ${taskLabel} names Issue #${task.issue}, but it does not resolve to a real GitHub Issue (phantom reference).`);
|
|
4496
4890
|
}
|
|
4497
4891
|
if (input.issue !== null && !input.issueRationalePass) {
|
|
4498
|
-
|
|
4892
|
+
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.`);
|
|
4499
4893
|
}
|
|
4500
4894
|
for (const dep of input.dependsOn) {
|
|
4501
4895
|
if (isSelfDependency(dep, task, input.issue)) {
|
|
4502
4896
|
const issueStr = input.issue !== null ? `#${input.issue.number}` : "?";
|
|
4503
|
-
|
|
4897
|
+
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.`);
|
|
4504
4898
|
continue;
|
|
4505
4899
|
}
|
|
4506
4900
|
if (dep.resolved === false) {
|
|
4507
|
-
|
|
4901
|
+
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.`);
|
|
4508
4902
|
continue;
|
|
4509
4903
|
}
|
|
4510
4904
|
if (!dep.merged && !isHandClosedByRecognizedPrincipal(dep, principalAllowlist)) {
|
|
4511
4905
|
const issueStr = dep.issue !== null ? ` (#${dep.issue})` : "";
|
|
4512
|
-
|
|
4906
|
+
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.`);
|
|
4513
4907
|
}
|
|
4514
4908
|
}
|
|
4515
4909
|
for (const c of input.conflictsWith) {
|
|
4516
4910
|
if (c.openOrInFlight) {
|
|
4517
4911
|
const issueStr = c.issue !== null ? ` (#${c.issue})` : "";
|
|
4518
|
-
|
|
4912
|
+
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.`);
|
|
4519
4913
|
}
|
|
4520
4914
|
}
|
|
4521
4915
|
for (const proj of input.priorTrancheArchival) {
|
|
4522
4916
|
if (proj.priorTrancheSlug !== null && !proj.archived) {
|
|
4523
|
-
|
|
4917
|
+
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.`);
|
|
4524
4918
|
}
|
|
4525
4919
|
}
|
|
4526
|
-
return { ready:
|
|
4920
|
+
return { ready: blockerDetails.length === 0, blockers: blockerDetails.map((b) => b.message), blockerDetails };
|
|
4527
4921
|
}
|
|
4528
4922
|
// ../../packages/aeg-core/src/single-plan-pr.ts
|
|
4529
4923
|
function trancheSlugFromTopologyPath(path) {
|
|
@@ -4625,6 +5019,137 @@ function checkBranchTopology(input) {
|
|
|
4625
5019
|
reason: `Branch \`${branch}\` matches topology row \`${taskId}\` in ${topoPath}.`
|
|
4626
5020
|
};
|
|
4627
5021
|
}
|
|
5022
|
+
// ../../packages/aeg-core/src/task-tools.ts
|
|
5023
|
+
import { z } from "zod";
|
|
5024
|
+
var TASK_TOOL_ERROR_KINDS = [
|
|
5025
|
+
"validation",
|
|
5026
|
+
"authority",
|
|
5027
|
+
"precondition",
|
|
5028
|
+
"capability",
|
|
5029
|
+
"infrastructure",
|
|
5030
|
+
"cancellation",
|
|
5031
|
+
"timeout",
|
|
5032
|
+
"uncertain_effect"
|
|
5033
|
+
];
|
|
5034
|
+
var TaskToolErrorSchema = z.object({
|
|
5035
|
+
kind: z.enum(TASK_TOOL_ERROR_KINDS),
|
|
5036
|
+
message: z.string().min(1),
|
|
5037
|
+
detail: z.string().optional()
|
|
5038
|
+
});
|
|
5039
|
+
var TaskToolRefSchema = z.union([
|
|
5040
|
+
z.object({ tranche: z.string().min(1), id: z.string().min(1) }).strict(),
|
|
5041
|
+
z.object({ issue: z.number().int().positive() }).strict()
|
|
5042
|
+
]);
|
|
5043
|
+
var MAX_PAGE_LIMIT = 100;
|
|
5044
|
+
var PageRequestSchema = z.object({
|
|
5045
|
+
cursor: z.string().optional(),
|
|
5046
|
+
limit: z.number().int().positive().max(MAX_PAGE_LIMIT).optional()
|
|
5047
|
+
});
|
|
5048
|
+
var FreshnessSchema = z.enum(["fresh", "stale", "unknown"]);
|
|
5049
|
+
var ObservedSchema = z.object({
|
|
5050
|
+
observedAt: z.string(),
|
|
5051
|
+
freshness: FreshnessSchema
|
|
5052
|
+
});
|
|
5053
|
+
var TaskStatusInputSchema = z.object({
|
|
5054
|
+
task: TaskToolRefSchema.optional()
|
|
5055
|
+
}).merge(PageRequestSchema).strict();
|
|
5056
|
+
var TaskStatusItemSchema = z.object({
|
|
5057
|
+
task: TaskToolRefSchema,
|
|
5058
|
+
issue: z.number().int().positive(),
|
|
5059
|
+
pr: z.number().int().positive().nullable(),
|
|
5060
|
+
state: z.string()
|
|
5061
|
+
}).merge(ObservedSchema);
|
|
5062
|
+
var TaskStatusResultSchema = z.object({
|
|
5063
|
+
items: z.array(TaskStatusItemSchema),
|
|
5064
|
+
nextCursor: z.string().nullable()
|
|
5065
|
+
});
|
|
5066
|
+
var RequestedAuthoritySchema = z.enum(["planner", "principal", "operator", "self"]);
|
|
5067
|
+
var EscalationInputsSchema = z.object({
|
|
5068
|
+
task: z.number().int().positive(),
|
|
5069
|
+
round: z.number().int().nonnegative(),
|
|
5070
|
+
head: z.string(),
|
|
5071
|
+
branch: z.string(),
|
|
5072
|
+
prNumber: z.number().int().positive()
|
|
5073
|
+
});
|
|
5074
|
+
var EscalationEvidenceSchema = z.object({
|
|
5075
|
+
round: z.number().int().nonnegative(),
|
|
5076
|
+
reviewer: z.string().nullable(),
|
|
5077
|
+
security: z.string().nullable()
|
|
5078
|
+
});
|
|
5079
|
+
var EscalationRunIdentitySchema = z.object({
|
|
5080
|
+
runId: z.string(),
|
|
5081
|
+
pid: z.number().int().positive(),
|
|
5082
|
+
host: z.string()
|
|
5083
|
+
});
|
|
5084
|
+
var EscalationInputVersionsSchema = z.object({
|
|
5085
|
+
briefHash: z.string().nullable(),
|
|
5086
|
+
objectivesVersion: z.string().nullable(),
|
|
5087
|
+
rulingOrdinal: z.number().int().nonnegative(),
|
|
5088
|
+
policyDigest: z.string()
|
|
5089
|
+
});
|
|
5090
|
+
var TaskEscalationReadInputSchema = z.object({
|
|
5091
|
+
task: TaskToolRefSchema
|
|
5092
|
+
}).merge(PageRequestSchema).strict();
|
|
5093
|
+
var TaskEscalationPacketSchema = z.object({
|
|
5094
|
+
reason: z.string(),
|
|
5095
|
+
detail: z.string().nullable(),
|
|
5096
|
+
inputs: EscalationInputsSchema.nullable(),
|
|
5097
|
+
evidence: EscalationEvidenceSchema.nullable(),
|
|
5098
|
+
attemptedRecovery: z.string(),
|
|
5099
|
+
requestedAuthority: RequestedAuthoritySchema,
|
|
5100
|
+
permittedNextActions: z.array(z.string()),
|
|
5101
|
+
runIdentity: EscalationRunIdentitySchema.nullable(),
|
|
5102
|
+
inputVersions: EscalationInputVersionsSchema.nullable()
|
|
5103
|
+
}).merge(ObservedSchema);
|
|
5104
|
+
var TaskEscalationReadResultSchema = z.object({
|
|
5105
|
+
items: z.array(TaskEscalationPacketSchema),
|
|
5106
|
+
nextCursor: z.string().nullable()
|
|
5107
|
+
}).merge(ObservedSchema);
|
|
5108
|
+
var TaskStartInputSchema = z.object({
|
|
5109
|
+
tranche: z.string().min(1),
|
|
5110
|
+
id: z.string().min(1)
|
|
5111
|
+
}).strict();
|
|
5112
|
+
var TaskStartResultSchema = z.object({
|
|
5113
|
+
requestId: z.string().min(1),
|
|
5114
|
+
run: z.object({ tranche: z.string().min(1), id: z.string().min(1) }),
|
|
5115
|
+
started: z.boolean(),
|
|
5116
|
+
startedAt: z.string(),
|
|
5117
|
+
mode: z.literal("attended")
|
|
5118
|
+
});
|
|
5119
|
+
var TaskResumeInputSchema = z.object({
|
|
5120
|
+
task: TaskToolRefSchema
|
|
5121
|
+
}).strict();
|
|
5122
|
+
var TaskCancelInputSchema = z.object({
|
|
5123
|
+
task: TaskToolRefSchema,
|
|
5124
|
+
reason: z.string().min(1)
|
|
5125
|
+
}).strict();
|
|
5126
|
+
var TaskResumeResultSchema = z.object({
|
|
5127
|
+
task: z.number().int().positive(),
|
|
5128
|
+
pr: z.number().int().positive(),
|
|
5129
|
+
escalationId: z.string().min(1),
|
|
5130
|
+
outcome: z.enum(["started", "already_resumed"]),
|
|
5131
|
+
authenticatedBy: z.string().min(1),
|
|
5132
|
+
authenticatedFrom: z.string().min(1)
|
|
5133
|
+
});
|
|
5134
|
+
var TaskCancelOutcomeSchema = z.enum(["confirmed", "pending", "uncertain"]);
|
|
5135
|
+
var TaskCancelResultSchema = z.object({
|
|
5136
|
+
task: z.number().int().positive(),
|
|
5137
|
+
pr: z.number().int().positive(),
|
|
5138
|
+
escalationId: z.string().min(1),
|
|
5139
|
+
outcome: TaskCancelOutcomeSchema,
|
|
5140
|
+
authenticatedBy: z.string().min(1),
|
|
5141
|
+
authenticatedFrom: z.string().min(1),
|
|
5142
|
+
fencedEffectKeys: z.array(z.string())
|
|
5143
|
+
});
|
|
5144
|
+
var TASK_TOOL_NAMES = [
|
|
5145
|
+
"task_start",
|
|
5146
|
+
"task_status",
|
|
5147
|
+
"task_escalation_read",
|
|
5148
|
+
"task_resume",
|
|
5149
|
+
"task_cancel"
|
|
5150
|
+
];
|
|
5151
|
+
var OPERATOR_STATUS_FOLLOW = "task_status_follow";
|
|
5152
|
+
var OPERATOR_TOOL_GRANT = [...TASK_TOOL_NAMES, OPERATOR_STATUS_FOLLOW];
|
|
4628
5153
|
// ../../packages/aeg-core/src/first-push-dispatch-gate.ts
|
|
4629
5154
|
function parseTaskBranch(branch) {
|
|
4630
5155
|
const m = /^task\/([^/]+)\/([^/]+)$/.exec(branch);
|
|
@@ -4708,7 +5233,6 @@ function decideIssueAssignment(input) {
|
|
|
4708
5233
|
};
|
|
4709
5234
|
}
|
|
4710
5235
|
// ../../packages/aeg-core/src/test-plan-gate.ts
|
|
4711
|
-
var TASK_BRANCH_PATTERN2 = /^task\/[^/]+\/[^/]+$/;
|
|
4712
5236
|
function evaluateTestPlanGate(body, branch) {
|
|
4713
5237
|
if (!body) {
|
|
4714
5238
|
return {
|
|
@@ -4721,7 +5245,7 @@ function evaluateTestPlanGate(body, branch) {
|
|
|
4721
5245
|
}
|
|
4722
5246
|
const located = locateTestPlanSection(body);
|
|
4723
5247
|
if (!located.found) {
|
|
4724
|
-
if (
|
|
5248
|
+
if (parseTaskBranchIdentity(branch) !== null) {
|
|
4725
5249
|
return {
|
|
4726
5250
|
verdict: "fail",
|
|
4727
5251
|
messages: [
|
|
@@ -4848,8 +5372,11 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
|
|
|
4848
5372
|
}
|
|
4849
5373
|
return findings;
|
|
4850
5374
|
}
|
|
5375
|
+
// ../../packages/aeg-core/src/log/envelope.ts
|
|
5376
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
5377
|
+
|
|
4851
5378
|
// ../../packages/aeg-core/src/log/schema.ts
|
|
4852
|
-
import { z } from "zod";
|
|
5379
|
+
import { z as z2 } from "zod";
|
|
4853
5380
|
var ROLE_VALUES = [
|
|
4854
5381
|
"planner",
|
|
4855
5382
|
"developer",
|
|
@@ -4859,136 +5386,174 @@ var ROLE_VALUES = [
|
|
|
4859
5386
|
"archivist",
|
|
4860
5387
|
"architect"
|
|
4861
5388
|
];
|
|
4862
|
-
var RoleSchema =
|
|
5389
|
+
var RoleSchema = z2.enum(ROLE_VALUES);
|
|
4863
5390
|
var HOST_VALUES = ["hook", "ci", "cli", "loop"];
|
|
4864
|
-
var HostSchema =
|
|
5391
|
+
var HostSchema = z2.enum(HOST_VALUES);
|
|
4865
5392
|
var RUN_ID_PATTERN = /^[A-Za-z0-9_.-]{1,128}$/;
|
|
4866
|
-
var
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
doctrine: z.string(),
|
|
5393
|
+
var headerMetaCore = {
|
|
5394
|
+
ts: z2.string(),
|
|
5395
|
+
run_id: z2.string().regex(RUN_ID_PATTERN),
|
|
5396
|
+
seq: z2.number().int().nonnegative(),
|
|
5397
|
+
repo: z2.string().nullable(),
|
|
5398
|
+
vinaya: z2.string(),
|
|
5399
|
+
doctrine: z2.string(),
|
|
4874
5400
|
host: HostSchema,
|
|
4875
|
-
machine:
|
|
5401
|
+
machine: z2.string()
|
|
5402
|
+
};
|
|
5403
|
+
var HeaderMetaV1Schema = z2.object({
|
|
5404
|
+
schema: z2.literal(1),
|
|
5405
|
+
...headerMetaCore
|
|
5406
|
+
}).strict();
|
|
5407
|
+
var LineageSchema = z2.object({
|
|
5408
|
+
run: z2.string().nullable(),
|
|
5409
|
+
attempt: z2.number().int().nullable(),
|
|
5410
|
+
parent: z2.string().nullable()
|
|
5411
|
+
}).strict();
|
|
5412
|
+
var InputVersionsSchema = z2.object({
|
|
5413
|
+
objectives_version: z2.string().nullable(),
|
|
5414
|
+
brief_hash: z2.string().nullable(),
|
|
5415
|
+
ruling_ordinal: z2.number().int().nullable(),
|
|
5416
|
+
policy_digest: z2.string().nullable()
|
|
4876
5417
|
}).strict();
|
|
4877
|
-
var
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
5418
|
+
var ProvenanceSchema = z2.enum(["parent_attributed", "env_correlated", "self_reported", "unavailable"]);
|
|
5419
|
+
var HeaderMetaV2Schema = z2.object({
|
|
5420
|
+
schema: z2.literal(2),
|
|
5421
|
+
...headerMetaCore,
|
|
5422
|
+
event_id: z2.string().min(1),
|
|
5423
|
+
process_id: z2.string().min(1),
|
|
5424
|
+
actor_id: z2.string().nullable(),
|
|
5425
|
+
lineage: LineageSchema,
|
|
5426
|
+
input_versions: InputVersionsSchema,
|
|
5427
|
+
provenance: ProvenanceSchema
|
|
4884
5428
|
}).strict();
|
|
4885
|
-
var
|
|
5429
|
+
var HeaderMetaSchema = z2.discriminatedUnion("schema", [HeaderMetaV1Schema, HeaderMetaV2Schema]);
|
|
5430
|
+
var SubjectSchema = z2.object({
|
|
5431
|
+
issue: z2.number().int().nullable(),
|
|
5432
|
+
pr: z2.number().int().optional(),
|
|
5433
|
+
sha: z2.string().optional(),
|
|
5434
|
+
role: z2.union([RoleSchema, z2.literal("unattributed")]),
|
|
5435
|
+
round: z2.number().int().optional(),
|
|
5436
|
+
objectives_version: z2.string().optional()
|
|
5437
|
+
}).strict();
|
|
5438
|
+
var HeaderSchema = z2.object({
|
|
4886
5439
|
meta: HeaderMetaSchema,
|
|
4887
5440
|
subject: SubjectSchema
|
|
4888
5441
|
}).strict();
|
|
4889
5442
|
var envelopeTail = {
|
|
4890
|
-
duration_ms:
|
|
4891
|
-
payload:
|
|
5443
|
+
duration_ms: z2.number().nonnegative().optional(),
|
|
5444
|
+
payload: z2.object({}).strict()
|
|
4892
5445
|
};
|
|
4893
|
-
var
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
5446
|
+
var ReviewFindingSchema = z2.object({
|
|
5447
|
+
id: z2.string(),
|
|
5448
|
+
severity: z2.string(),
|
|
5449
|
+
state: z2.string().optional(),
|
|
5450
|
+
severity_scale: z2.string().optional(),
|
|
5451
|
+
policy_treatment: z2.enum(["blocking", "non_blocking", "unavailable"]).optional(),
|
|
5452
|
+
confidence: z2.number().min(0).max(1).optional(),
|
|
5453
|
+
confidence_scale: z2.string().optional(),
|
|
5454
|
+
confidence_source: z2.string().optional()
|
|
5455
|
+
}).strict();
|
|
5456
|
+
var DispatchOutcomeSchema = z2.discriminatedUnion("type", [
|
|
5457
|
+
z2.object({ type: z2.literal("pr_opened"), pr: z2.number().int(), head: z2.string() }).strict(),
|
|
5458
|
+
z2.object({
|
|
5459
|
+
type: z2.literal("round_pushed"),
|
|
5460
|
+
pr: z2.number().int(),
|
|
5461
|
+
head: z2.string(),
|
|
5462
|
+
comment_id: z2.number().int()
|
|
4900
5463
|
}).strict(),
|
|
4901
|
-
|
|
4902
|
-
type:
|
|
4903
|
-
verdict:
|
|
4904
|
-
head:
|
|
4905
|
-
comment_id:
|
|
4906
|
-
objectives:
|
|
4907
|
-
findings:
|
|
5464
|
+
z2.object({
|
|
5465
|
+
type: z2.literal("verdict"),
|
|
5466
|
+
verdict: z2.enum(["APPROVE", "REQUEST CHANGES", "PASS", "FAIL"]),
|
|
5467
|
+
head: z2.string(),
|
|
5468
|
+
comment_id: z2.number().int(),
|
|
5469
|
+
objectives: z2.array(z2.object({ id: z2.string(), met: z2.boolean() }).strict()),
|
|
5470
|
+
findings: z2.array(ReviewFindingSchema)
|
|
4908
5471
|
}).strict(),
|
|
4909
|
-
|
|
4910
|
-
type:
|
|
4911
|
-
class:
|
|
4912
|
-
comment_id:
|
|
5472
|
+
z2.object({
|
|
5473
|
+
type: z2.literal("escalation"),
|
|
5474
|
+
class: z2.enum(["authority", "strategy", "product"]),
|
|
5475
|
+
comment_id: z2.number().int()
|
|
4913
5476
|
}).strict(),
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
5477
|
+
z2.object({ type: z2.literal("brief"), comment_id: z2.number().int(), hash: z2.string() }).strict(),
|
|
5478
|
+
z2.object({ type: z2.literal("plan"), issues: z2.array(z2.number().int()) }).strict(),
|
|
5479
|
+
z2.object({ type: z2.literal("archive"), provenance_comment_id: z2.number().int() }).strict(),
|
|
5480
|
+
z2.object({ type: z2.literal("completed") }).strict()
|
|
4917
5481
|
]);
|
|
4918
5482
|
var dispatchShared = {
|
|
4919
5483
|
meta: HeaderMetaSchema,
|
|
4920
5484
|
subject: SubjectSchema,
|
|
4921
|
-
kind:
|
|
5485
|
+
kind: z2.literal("dispatch"),
|
|
4922
5486
|
...envelopeTail,
|
|
4923
5487
|
target_role: RoleSchema,
|
|
4924
|
-
model:
|
|
4925
|
-
round:
|
|
4926
|
-
effect_id:
|
|
5488
|
+
model: z2.string(),
|
|
5489
|
+
round: z2.number().int().optional(),
|
|
5490
|
+
effect_id: z2.string()
|
|
4927
5491
|
};
|
|
4928
|
-
var dispatchUsageField =
|
|
4929
|
-
var DispatchEventSchema =
|
|
4930
|
-
|
|
4931
|
-
|
|
5492
|
+
var dispatchUsageField = z2.object({ input: z2.number().nonnegative(), output: z2.number().nonnegative() }).strict().nullable();
|
|
5493
|
+
var DispatchEventSchema = z2.discriminatedUnion("event", [
|
|
5494
|
+
z2.object({ ...dispatchShared, event: z2.literal("dispatched"), prompt_hash: z2.string() }).strict(),
|
|
5495
|
+
z2.object({
|
|
4932
5496
|
...dispatchShared,
|
|
4933
|
-
event:
|
|
5497
|
+
event: z2.literal("outcome_received"),
|
|
4934
5498
|
outcome: DispatchOutcomeSchema,
|
|
4935
5499
|
usage: dispatchUsageField
|
|
4936
5500
|
}).strict(),
|
|
4937
|
-
|
|
5501
|
+
z2.object({
|
|
4938
5502
|
...dispatchShared,
|
|
4939
|
-
event:
|
|
4940
|
-
reason:
|
|
5503
|
+
event: z2.literal("dispatch_failed"),
|
|
5504
|
+
reason: z2.enum(["timeout", "crash", "refused", "unattributed_write"]),
|
|
4941
5505
|
usage: dispatchUsageField
|
|
4942
5506
|
}).strict()
|
|
4943
5507
|
]);
|
|
4944
5508
|
var loopShared = {
|
|
4945
5509
|
meta: HeaderMetaSchema,
|
|
4946
5510
|
subject: SubjectSchema,
|
|
4947
|
-
kind:
|
|
5511
|
+
kind: z2.literal("dev_review_loop"),
|
|
4948
5512
|
...envelopeTail,
|
|
4949
|
-
loop_id:
|
|
5513
|
+
loop_id: z2.string()
|
|
4950
5514
|
};
|
|
4951
|
-
var DevReviewLoopEventSchema =
|
|
4952
|
-
|
|
5515
|
+
var DevReviewLoopEventSchema = z2.discriminatedUnion("event", [
|
|
5516
|
+
z2.object({
|
|
4953
5517
|
...loopShared,
|
|
4954
|
-
event:
|
|
4955
|
-
task:
|
|
4956
|
-
policy:
|
|
4957
|
-
max_rounds:
|
|
4958
|
-
reviewers:
|
|
4959
|
-
models:
|
|
5518
|
+
event: z2.literal("loop_started"),
|
|
5519
|
+
task: z2.number().int(),
|
|
5520
|
+
policy: z2.object({
|
|
5521
|
+
max_rounds: z2.number().int().nonnegative(),
|
|
5522
|
+
reviewers: z2.array(RoleSchema),
|
|
5523
|
+
models: z2.record(RoleSchema, z2.string())
|
|
4960
5524
|
}).strict()
|
|
4961
5525
|
}).strict(),
|
|
4962
|
-
|
|
4963
|
-
|
|
5526
|
+
z2.object({ ...loopShared, event: z2.literal("round_started"), round: z2.number().int(), base_head: z2.string() }).strict(),
|
|
5527
|
+
z2.object({
|
|
4964
5528
|
...loopShared,
|
|
4965
|
-
event:
|
|
4966
|
-
round:
|
|
4967
|
-
head:
|
|
4968
|
-
green:
|
|
5529
|
+
event: z2.literal("gate_result_read"),
|
|
5530
|
+
round: z2.number().int(),
|
|
5531
|
+
head: z2.string(),
|
|
5532
|
+
green: z2.boolean()
|
|
4969
5533
|
}).strict(),
|
|
4970
|
-
|
|
5534
|
+
z2.object({
|
|
4971
5535
|
...loopShared,
|
|
4972
|
-
event:
|
|
4973
|
-
round:
|
|
4974
|
-
head:
|
|
4975
|
-
all_approve:
|
|
4976
|
-
blockers:
|
|
5536
|
+
event: z2.literal("verdicts_read"),
|
|
5537
|
+
round: z2.number().int(),
|
|
5538
|
+
head: z2.string(),
|
|
5539
|
+
all_approve: z2.boolean(),
|
|
5540
|
+
blockers: z2.number().int().nonnegative(),
|
|
5541
|
+
findings: z2.array(ReviewFindingSchema).optional()
|
|
4977
5542
|
}).strict(),
|
|
4978
|
-
|
|
5543
|
+
z2.object({
|
|
4979
5544
|
...loopShared,
|
|
4980
|
-
event:
|
|
4981
|
-
round:
|
|
4982
|
-
open:
|
|
4983
|
-
resolved:
|
|
4984
|
-
new:
|
|
4985
|
-
recurring:
|
|
5545
|
+
event: z2.literal("findings_compared"),
|
|
5546
|
+
round: z2.number().int(),
|
|
5547
|
+
open: z2.array(z2.string()),
|
|
5548
|
+
resolved: z2.array(z2.string()),
|
|
5549
|
+
new: z2.array(z2.string()),
|
|
5550
|
+
recurring: z2.array(z2.string())
|
|
4986
5551
|
}).strict(),
|
|
4987
|
-
|
|
5552
|
+
z2.object({
|
|
4988
5553
|
...loopShared,
|
|
4989
|
-
event:
|
|
4990
|
-
round:
|
|
4991
|
-
condition:
|
|
5554
|
+
event: z2.literal("stop_condition_met"),
|
|
5555
|
+
round: z2.number().int(),
|
|
5556
|
+
condition: z2.enum([
|
|
4992
5557
|
"green",
|
|
4993
5558
|
"max_rounds",
|
|
4994
5559
|
"no_progress",
|
|
@@ -4998,42 +5563,49 @@ var DevReviewLoopEventSchema = z.discriminatedUnion("event", [
|
|
|
4998
5563
|
"reappearance"
|
|
4999
5564
|
])
|
|
5000
5565
|
}).strict(),
|
|
5001
|
-
|
|
5566
|
+
z2.object({
|
|
5002
5567
|
...loopShared,
|
|
5003
|
-
event:
|
|
5004
|
-
round:
|
|
5005
|
-
reason:
|
|
5568
|
+
event: z2.literal("paused"),
|
|
5569
|
+
round: z2.number().int(),
|
|
5570
|
+
reason: z2.enum(["escalation", "principal_item", "refreeze_needed"])
|
|
5006
5571
|
}).strict(),
|
|
5007
|
-
|
|
5572
|
+
z2.object({
|
|
5008
5573
|
...loopShared,
|
|
5009
|
-
event:
|
|
5010
|
-
round:
|
|
5011
|
-
by:
|
|
5574
|
+
event: z2.literal("resumed"),
|
|
5575
|
+
round: z2.number().int(),
|
|
5576
|
+
by: z2.literal("principal")
|
|
5012
5577
|
}).strict(),
|
|
5013
|
-
|
|
5578
|
+
z2.object({
|
|
5014
5579
|
...loopShared,
|
|
5015
|
-
event:
|
|
5016
|
-
round:
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
files_changed: z.number().int().nonnegative(),
|
|
5020
|
-
insertions: z.number().int().nonnegative(),
|
|
5021
|
-
deletions: z.number().int().nonnegative(),
|
|
5022
|
-
wall_ms: z.number().nonnegative(),
|
|
5023
|
-
outcome: z.enum(["green", "changes_requested", "escalated"])
|
|
5580
|
+
event: z2.literal("unpushed_work_resume"),
|
|
5581
|
+
round: z2.number().int(),
|
|
5582
|
+
branch: z2.string(),
|
|
5583
|
+
detail: z2.string()
|
|
5024
5584
|
}).strict(),
|
|
5025
|
-
|
|
5585
|
+
z2.object({
|
|
5026
5586
|
...loopShared,
|
|
5027
|
-
event:
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5587
|
+
event: z2.literal("round_ended"),
|
|
5588
|
+
round: z2.number().int(),
|
|
5589
|
+
base_head: z2.string(),
|
|
5590
|
+
head: z2.string(),
|
|
5591
|
+
files_changed: z2.number().int().nonnegative(),
|
|
5592
|
+
insertions: z2.number().int().nonnegative(),
|
|
5593
|
+
deletions: z2.number().int().nonnegative(),
|
|
5594
|
+
wall_ms: z2.number().nonnegative(),
|
|
5595
|
+
outcome: z2.enum(["green", "changes_requested", "escalated"])
|
|
5596
|
+
}).strict(),
|
|
5597
|
+
z2.object({
|
|
5598
|
+
...loopShared,
|
|
5599
|
+
event: z2.literal("journal_finalized"),
|
|
5600
|
+
rounds: z2.number().int().nonnegative(),
|
|
5601
|
+
total_wall_ms: z2.number().nonnegative(),
|
|
5602
|
+
time_to_green_ms: z2.number().nonnegative().nullable(),
|
|
5603
|
+
files_changed_total: z2.number().int().nonnegative(),
|
|
5604
|
+
final_head: z2.string(),
|
|
5605
|
+
result: z2.enum(["merged_ready", "stopped"])
|
|
5034
5606
|
}).strict()
|
|
5035
5607
|
]);
|
|
5036
|
-
var ForgeOpSchema =
|
|
5608
|
+
var ForgeOpSchema = z2.enum([
|
|
5037
5609
|
"pr.create",
|
|
5038
5610
|
"pr.comment",
|
|
5039
5611
|
"pr.body.replace",
|
|
@@ -5047,24 +5619,434 @@ var ForgeOpSchema = z.enum([
|
|
|
5047
5619
|
"label.add",
|
|
5048
5620
|
"label.remove"
|
|
5049
5621
|
]);
|
|
5050
|
-
var ForgeWriteTargetSchema =
|
|
5051
|
-
issue:
|
|
5052
|
-
pr:
|
|
5622
|
+
var ForgeWriteTargetSchema = z2.object({
|
|
5623
|
+
issue: z2.number().int().optional(),
|
|
5624
|
+
pr: z2.number().int().optional()
|
|
5053
5625
|
}).strict();
|
|
5054
5626
|
var forgeWriteShared = {
|
|
5055
5627
|
meta: HeaderMetaSchema,
|
|
5056
5628
|
subject: SubjectSchema,
|
|
5057
|
-
kind:
|
|
5629
|
+
kind: z2.literal("forge_write"),
|
|
5058
5630
|
...envelopeTail,
|
|
5059
5631
|
op: ForgeOpSchema,
|
|
5060
5632
|
target: ForgeWriteTargetSchema
|
|
5061
5633
|
};
|
|
5062
|
-
var ForgeWriteEventSchema =
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5634
|
+
var ForgeWriteEventSchema = z2.discriminatedUnion("event", [
|
|
5635
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("validated") }).strict(),
|
|
5636
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("refused"), reason: z2.string() }).strict(),
|
|
5637
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("written"), comment_ids: z2.array(z2.string()) }).strict()
|
|
5638
|
+
]);
|
|
5639
|
+
var GateOutcomeSchema = z2.enum([
|
|
5640
|
+
"pass",
|
|
5641
|
+
"fail",
|
|
5642
|
+
"wait",
|
|
5643
|
+
"skip",
|
|
5644
|
+
"invalid_input",
|
|
5645
|
+
"unavailable_dependency",
|
|
5646
|
+
"timeout",
|
|
5647
|
+
"cancelled"
|
|
5648
|
+
]);
|
|
5649
|
+
var gateShared = {
|
|
5650
|
+
meta: HeaderMetaSchema,
|
|
5651
|
+
subject: SubjectSchema,
|
|
5652
|
+
kind: z2.literal("gate"),
|
|
5653
|
+
...envelopeTail,
|
|
5654
|
+
check: z2.string(),
|
|
5655
|
+
check_version: z2.string().nullable(),
|
|
5656
|
+
policy_version: z2.string().nullable(),
|
|
5657
|
+
input_fingerprint: z2.string().nullable()
|
|
5658
|
+
};
|
|
5659
|
+
var GateEventSchema = z2.discriminatedUnion("event", [
|
|
5660
|
+
z2.object({
|
|
5661
|
+
...gateShared,
|
|
5662
|
+
event: z2.literal("checked"),
|
|
5663
|
+
outcome: GateOutcomeSchema,
|
|
5664
|
+
reason: z2.string().optional()
|
|
5665
|
+
}).strict()
|
|
5066
5666
|
]);
|
|
5067
|
-
var
|
|
5667
|
+
var OperationResultSchema = z2.enum(["ok", "error", "refused", "timeout", "cancelled", "unavailable"]);
|
|
5668
|
+
var operationShared = {
|
|
5669
|
+
meta: HeaderMetaSchema,
|
|
5670
|
+
subject: SubjectSchema,
|
|
5671
|
+
kind: z2.literal("operation"),
|
|
5672
|
+
...envelopeTail,
|
|
5673
|
+
operation: z2.string(),
|
|
5674
|
+
target: z2.string().nullable()
|
|
5675
|
+
};
|
|
5676
|
+
var OperationEventSchema = z2.discriminatedUnion("event", [
|
|
5677
|
+
z2.object({
|
|
5678
|
+
...operationShared,
|
|
5679
|
+
event: z2.literal("completed"),
|
|
5680
|
+
result: OperationResultSchema,
|
|
5681
|
+
error_class: z2.string().nullable()
|
|
5682
|
+
}).strict()
|
|
5683
|
+
]);
|
|
5684
|
+
var UsageUnitsSchema = z2.object({
|
|
5685
|
+
input: z2.number().nonnegative().nullable(),
|
|
5686
|
+
output: z2.number().nonnegative().nullable(),
|
|
5687
|
+
cache: z2.number().nonnegative().nullable()
|
|
5688
|
+
}).strict();
|
|
5689
|
+
var usageShared = {
|
|
5690
|
+
meta: HeaderMetaSchema,
|
|
5691
|
+
subject: SubjectSchema,
|
|
5692
|
+
kind: z2.literal("usage"),
|
|
5693
|
+
...envelopeTail,
|
|
5694
|
+
model: z2.string().nullable(),
|
|
5695
|
+
source: z2.string(),
|
|
5696
|
+
semantics: z2.enum(["cumulative", "delta"])
|
|
5697
|
+
};
|
|
5698
|
+
var UsageEventSchema = z2.discriminatedUnion("event", [
|
|
5699
|
+
z2.object({
|
|
5700
|
+
...usageShared,
|
|
5701
|
+
event: z2.literal("observed"),
|
|
5702
|
+
units: UsageUnitsSchema,
|
|
5703
|
+
unknown_reason: z2.string().nullable()
|
|
5704
|
+
}).strict()
|
|
5705
|
+
]);
|
|
5706
|
+
var RoleAttemptOutcomeSchema = z2.enum([
|
|
5707
|
+
"completed",
|
|
5708
|
+
"incomplete",
|
|
5709
|
+
"infrastructure_failed",
|
|
5710
|
+
"cancelled",
|
|
5711
|
+
"timed_out",
|
|
5712
|
+
"capability_refused"
|
|
5713
|
+
]);
|
|
5714
|
+
var roleAttemptShared = {
|
|
5715
|
+
meta: HeaderMetaSchema,
|
|
5716
|
+
subject: SubjectSchema,
|
|
5717
|
+
kind: z2.literal("role_attempt"),
|
|
5718
|
+
...envelopeTail,
|
|
5719
|
+
actor: z2.string().nullable(),
|
|
5720
|
+
attempt: z2.number().int().nullable(),
|
|
5721
|
+
effect_id: z2.string(),
|
|
5722
|
+
model: z2.string().nullable()
|
|
5723
|
+
};
|
|
5724
|
+
var RoleAttemptEventSchema = z2.discriminatedUnion("event", [
|
|
5725
|
+
z2.object({
|
|
5726
|
+
...roleAttemptShared,
|
|
5727
|
+
event: z2.literal("attempted"),
|
|
5728
|
+
outcome: RoleAttemptOutcomeSchema,
|
|
5729
|
+
usage: dispatchUsageField
|
|
5730
|
+
}).strict()
|
|
5731
|
+
]);
|
|
5732
|
+
var handoffShared = {
|
|
5733
|
+
meta: HeaderMetaSchema,
|
|
5734
|
+
subject: SubjectSchema,
|
|
5735
|
+
kind: z2.literal("handoff"),
|
|
5736
|
+
...envelopeTail,
|
|
5737
|
+
class: z2.enum(["authority", "strategy", "product"]),
|
|
5738
|
+
reason: z2.string()
|
|
5739
|
+
};
|
|
5740
|
+
var HandoffEventSchema = z2.discriminatedUnion("event", [
|
|
5741
|
+
z2.object({
|
|
5742
|
+
...handoffShared,
|
|
5743
|
+
event: z2.literal("raised"),
|
|
5744
|
+
requested_decision: z2.string().nullable()
|
|
5745
|
+
}).strict(),
|
|
5746
|
+
z2.object({
|
|
5747
|
+
...handoffShared,
|
|
5748
|
+
event: z2.literal("resolved"),
|
|
5749
|
+
resolution: z2.string().nullable(),
|
|
5750
|
+
resolved_by: z2.string().nullable()
|
|
5751
|
+
}).strict()
|
|
5752
|
+
]);
|
|
5753
|
+
var EffectTargetSchema = z2.object({
|
|
5754
|
+
kind: z2.string(),
|
|
5755
|
+
ref: z2.string()
|
|
5756
|
+
}).strict();
|
|
5757
|
+
var effectShared = {
|
|
5758
|
+
meta: HeaderMetaSchema,
|
|
5759
|
+
subject: SubjectSchema,
|
|
5760
|
+
kind: z2.literal("effect"),
|
|
5761
|
+
...envelopeTail,
|
|
5762
|
+
effect_id: z2.string(),
|
|
5763
|
+
target: EffectTargetSchema
|
|
5764
|
+
};
|
|
5765
|
+
var EffectEventSchema = z2.discriminatedUnion("event", [
|
|
5766
|
+
z2.object({ ...effectShared, event: z2.literal("attempted") }).strict(),
|
|
5767
|
+
z2.object({ ...effectShared, event: z2.literal("observed"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict(),
|
|
5768
|
+
z2.object({ ...effectShared, event: z2.literal("verified"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict()
|
|
5769
|
+
]);
|
|
5770
|
+
var LogEventSchema = z2.union([
|
|
5771
|
+
DispatchEventSchema,
|
|
5772
|
+
DevReviewLoopEventSchema,
|
|
5773
|
+
ForgeWriteEventSchema,
|
|
5774
|
+
GateEventSchema,
|
|
5775
|
+
OperationEventSchema,
|
|
5776
|
+
UsageEventSchema,
|
|
5777
|
+
RoleAttemptEventSchema,
|
|
5778
|
+
HandoffEventSchema,
|
|
5779
|
+
EffectEventSchema
|
|
5780
|
+
]);
|
|
5781
|
+
|
|
5782
|
+
// ../../packages/aeg-core/src/log/envelope.ts
|
|
5783
|
+
function isRole(value) {
|
|
5784
|
+
return value !== undefined && ROLE_VALUES.includes(value);
|
|
5785
|
+
}
|
|
5786
|
+
function actorIdFromEnv(role) {
|
|
5787
|
+
return role === undefined || role === "" ? null : role;
|
|
5788
|
+
}
|
|
5789
|
+
function lineageFieldFromEnv(value) {
|
|
5790
|
+
return value === undefined || value === "" ? null : value;
|
|
5791
|
+
}
|
|
5792
|
+
function attemptFromEnv(value) {
|
|
5793
|
+
if (value === undefined || value === "")
|
|
5794
|
+
return null;
|
|
5795
|
+
const n = Number(value);
|
|
5796
|
+
return Number.isInteger(n) ? n : null;
|
|
5797
|
+
}
|
|
5798
|
+
function provenanceFor(input) {
|
|
5799
|
+
if (input.provenance)
|
|
5800
|
+
return input.provenance;
|
|
5801
|
+
if (input.env.role || input.env.task)
|
|
5802
|
+
return "env_correlated";
|
|
5803
|
+
return "unavailable";
|
|
5804
|
+
}
|
|
5805
|
+
function issueFromTask(task) {
|
|
5806
|
+
if (task === undefined || task === "")
|
|
5807
|
+
return null;
|
|
5808
|
+
const n = Number(task);
|
|
5809
|
+
return Number.isInteger(n) ? n : null;
|
|
5810
|
+
}
|
|
5811
|
+
function roundFromEnv(round) {
|
|
5812
|
+
if (round === undefined || round === "")
|
|
5813
|
+
return;
|
|
5814
|
+
const n = Number(round);
|
|
5815
|
+
return Number.isInteger(n) ? n : undefined;
|
|
5816
|
+
}
|
|
5817
|
+
function buildHeader(input) {
|
|
5818
|
+
const role = isRole(input.env.role) ? input.env.role : "unattributed";
|
|
5819
|
+
const round = roundFromEnv(input.env.round);
|
|
5820
|
+
const subject = {
|
|
5821
|
+
issue: issueFromTask(input.env.task),
|
|
5822
|
+
role,
|
|
5823
|
+
...round !== undefined ? { round } : {},
|
|
5824
|
+
...input.subject ?? {}
|
|
5825
|
+
};
|
|
5826
|
+
return {
|
|
5827
|
+
meta: {
|
|
5828
|
+
schema: 2,
|
|
5829
|
+
ts: input.now.toISOString(),
|
|
5830
|
+
run_id: input.runId,
|
|
5831
|
+
seq: input.seq,
|
|
5832
|
+
repo: input.repo,
|
|
5833
|
+
vinaya: input.vinaya,
|
|
5834
|
+
doctrine: input.doctrine,
|
|
5835
|
+
host: input.host,
|
|
5836
|
+
machine: createHash5("sha256").update(input.hostname).digest("hex"),
|
|
5837
|
+
event_id: input.eventId,
|
|
5838
|
+
process_id: input.processId,
|
|
5839
|
+
actor_id: actorIdFromEnv(input.env.role),
|
|
5840
|
+
lineage: {
|
|
5841
|
+
run: lineageFieldFromEnv(input.env.run),
|
|
5842
|
+
attempt: attemptFromEnv(input.env.attempt),
|
|
5843
|
+
parent: lineageFieldFromEnv(input.env.parent)
|
|
5844
|
+
},
|
|
5845
|
+
input_versions: {
|
|
5846
|
+
objectives_version: input.inputVersions?.objectivesVersion ?? null,
|
|
5847
|
+
brief_hash: input.inputVersions?.briefHash ?? null,
|
|
5848
|
+
ruling_ordinal: input.inputVersions?.rulingOrdinal ?? null,
|
|
5849
|
+
policy_digest: input.inputVersions?.policyDigest ?? null
|
|
5850
|
+
},
|
|
5851
|
+
provenance: provenanceFor(input)
|
|
5852
|
+
},
|
|
5853
|
+
subject
|
|
5854
|
+
};
|
|
5855
|
+
}
|
|
5856
|
+
// ../../packages/aeg-core/src/log/redact.ts
|
|
5857
|
+
var GITHUB_TOKEN_PATTERN = /\b(?:gho_|ghp_|ghs_|ghu_|ghr_|github_pat_)[A-Za-z0-9_]+\b/g;
|
|
5858
|
+
var CLASSIC_GITHUB_PAT_PATTERN = /\b((?:token|GITHUB_TOKEN|PAT|api[_-]?key|api[_-]?token)\s*[:=]\s*)[a-f0-9]{40}\b/gi;
|
|
5859
|
+
var BEARER_TOKEN_PATTERN = /\bBearer\s+\S+/g;
|
|
5860
|
+
var AWS_ACCESS_KEY_PATTERN = /\bAKIA[0-9A-Z]{16}\b/g;
|
|
5861
|
+
var SLACK_TOKEN_PATTERN = /\bxox[baprs]-[0-9A-Za-z-]+\b/g;
|
|
5862
|
+
var STRIPE_KEY_PATTERN = /\b(?:sk|pk|rk)_(?:live|test)_[0-9A-Za-z]{10,}\b/g;
|
|
5863
|
+
var ANTHROPIC_KEY_PATTERN = /\bsk-ant-[A-Za-z0-9_-]{20,}\b/g;
|
|
5864
|
+
var OPENAI_KEY_PATTERN = /\bsk-(?!ant-)[A-Za-z0-9]{20,}\b/g;
|
|
5865
|
+
var NPM_TOKEN_PATTERN = /\bnpm_[A-Za-z0-9]{36}\b/g;
|
|
5866
|
+
var JWT_PATTERN = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g;
|
|
5867
|
+
var PEM_BLOCK_PATTERN = /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g;
|
|
5868
|
+
var BASIC_AUTH_URL_PATTERN = /(https?:\/\/)[^/\s:@]+:[^/\s@]+@/g;
|
|
5869
|
+
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;
|
|
5870
|
+
function redactString(value, home) {
|
|
5871
|
+
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>");
|
|
5872
|
+
if (home.length > 0 && out.startsWith(home)) {
|
|
5873
|
+
const rest = out.slice(home.length);
|
|
5874
|
+
if (rest === "" || rest.startsWith("/"))
|
|
5875
|
+
out = `~${rest}`;
|
|
5876
|
+
}
|
|
5877
|
+
return out;
|
|
5878
|
+
}
|
|
5879
|
+
function redact(value, home) {
|
|
5880
|
+
if (typeof value === "string")
|
|
5881
|
+
return redactString(value, home);
|
|
5882
|
+
if (Array.isArray(value))
|
|
5883
|
+
return value.map((v) => redact(v, home));
|
|
5884
|
+
if (value !== null && typeof value === "object") {
|
|
5885
|
+
const out = {};
|
|
5886
|
+
for (const [k, v] of Object.entries(value)) {
|
|
5887
|
+
out[k] = redact(v, home);
|
|
5888
|
+
}
|
|
5889
|
+
return out;
|
|
5890
|
+
}
|
|
5891
|
+
return value;
|
|
5892
|
+
}
|
|
5893
|
+
// ../../packages/aeg-core/src/log/store.ts
|
|
5894
|
+
function metaOf(obj) {
|
|
5895
|
+
if (obj === null || typeof obj !== "object")
|
|
5896
|
+
return null;
|
|
5897
|
+
const meta = obj.meta;
|
|
5898
|
+
if (meta === null || typeof meta !== "object")
|
|
5899
|
+
return null;
|
|
5900
|
+
return meta;
|
|
5901
|
+
}
|
|
5902
|
+
function recordIdentity(obj) {
|
|
5903
|
+
const meta = metaOf(obj);
|
|
5904
|
+
if (meta === null)
|
|
5905
|
+
return null;
|
|
5906
|
+
const eventId = meta.event_id;
|
|
5907
|
+
if (typeof eventId === "string" && eventId.length > 0)
|
|
5908
|
+
return eventId;
|
|
5909
|
+
const runId = meta.run_id;
|
|
5910
|
+
const seq = meta.seq;
|
|
5911
|
+
if (typeof runId === "string" && runId.length > 0 && typeof seq === "number" && Number.isInteger(seq)) {
|
|
5912
|
+
return `${runId}:${seq}`;
|
|
5913
|
+
}
|
|
5914
|
+
return null;
|
|
5915
|
+
}
|
|
5916
|
+
|
|
5917
|
+
// ../../packages/aeg-core/src/log/artifact.ts
|
|
5918
|
+
var TASK_LOG_ARTIFACT_MAX_BYTES = 8 * 1024 * 1024;
|
|
5919
|
+
// ../../packages/aeg-core/src/dev-review-loop/journal-reconstruction.ts
|
|
5920
|
+
var STOPPED_CONDITIONS = new Set(["confidence", "reappearance", "no_progress", "max_rounds"]);
|
|
5921
|
+
// ../../packages/aeg-core/src/control-store/records.ts
|
|
5922
|
+
import { z as z3 } from "zod";
|
|
5923
|
+
var isoTimestamp = z3.string().min(1);
|
|
5924
|
+
var taskId = z3.number().int().positive();
|
|
5925
|
+
var epochNumber = z3.number().int().nonnegative();
|
|
5926
|
+
var RunRecordSchema = z3.object({
|
|
5927
|
+
version: z3.literal(1),
|
|
5928
|
+
kind: z3.literal("run"),
|
|
5929
|
+
task: taskId,
|
|
5930
|
+
runId: z3.string().min(1),
|
|
5931
|
+
pid: z3.number().int().positive(),
|
|
5932
|
+
host: z3.string().min(1),
|
|
5933
|
+
startedAt: isoTimestamp,
|
|
5934
|
+
childStartedAt: z3.string().min(1).nullable().optional(),
|
|
5935
|
+
childCommand: z3.string().min(1).nullable().optional()
|
|
5936
|
+
}).strict();
|
|
5937
|
+
var InputRecordSchema = z3.object({
|
|
5938
|
+
version: z3.literal(1),
|
|
5939
|
+
kind: z3.literal("input"),
|
|
5940
|
+
task: taskId,
|
|
5941
|
+
runId: z3.string().min(1),
|
|
5942
|
+
source: z3.union([z3.literal("fresh"), z3.literal("resume")]),
|
|
5943
|
+
pr: z3.number().int().positive().nullable(),
|
|
5944
|
+
round: z3.number().int().nonnegative(),
|
|
5945
|
+
recordedAt: isoTimestamp
|
|
5946
|
+
}).strict();
|
|
5947
|
+
var OwnershipRecordSchema = z3.object({
|
|
5948
|
+
version: z3.literal(1),
|
|
5949
|
+
kind: z3.literal("ownership"),
|
|
5950
|
+
task: taskId,
|
|
5951
|
+
epoch: epochNumber,
|
|
5952
|
+
ownerId: z3.string().min(1),
|
|
5953
|
+
pid: z3.number().int().positive(),
|
|
5954
|
+
host: z3.string().min(1),
|
|
5955
|
+
acquiredAt: isoTimestamp
|
|
5956
|
+
}).strict();
|
|
5957
|
+
var TransitionRecordSchema = z3.object({
|
|
5958
|
+
version: z3.literal(1),
|
|
5959
|
+
kind: z3.literal("transition"),
|
|
5960
|
+
task: taskId,
|
|
5961
|
+
epoch: epochNumber,
|
|
5962
|
+
seq: z3.number().int().nonnegative(),
|
|
5963
|
+
from: z3.string().min(1),
|
|
5964
|
+
to: z3.string().min(1),
|
|
5965
|
+
detail: z3.string().optional(),
|
|
5966
|
+
at: isoTimestamp
|
|
5967
|
+
}).strict();
|
|
5968
|
+
var ManifestRecordSchema = z3.object({
|
|
5969
|
+
version: z3.literal(1),
|
|
5970
|
+
kind: z3.literal("manifest"),
|
|
5971
|
+
task: taskId,
|
|
5972
|
+
round: epochNumber,
|
|
5973
|
+
repository: z3.string().min(1),
|
|
5974
|
+
pr: z3.number().int().positive(),
|
|
5975
|
+
branch: z3.string().min(1),
|
|
5976
|
+
baseSha: z3.string().min(1).nullable(),
|
|
5977
|
+
headSha: z3.string().min(1),
|
|
5978
|
+
briefHash: z3.string().min(1).nullable(),
|
|
5979
|
+
objectivesVersion: z3.string().min(1).nullable(),
|
|
5980
|
+
rulingOrdinal: epochNumber,
|
|
5981
|
+
policyDigest: z3.string().min(1),
|
|
5982
|
+
recordedAt: isoTimestamp
|
|
5983
|
+
}).strict();
|
|
5984
|
+
var roundHeadIdentity = z3.object({ round: z3.number().int().positive(), head: z3.string().min(1) }).strict();
|
|
5985
|
+
var LoopBudgetsSchema = z3.object({
|
|
5986
|
+
mechanicalRetries: z3.number().int().nonnegative(),
|
|
5987
|
+
reviewRounds: z3.number().int().nonnegative(),
|
|
5988
|
+
infrastructureRetries: z3.number().int().nonnegative()
|
|
5989
|
+
}).strict();
|
|
5990
|
+
var LoopStateRecordSchema = z3.object({
|
|
5991
|
+
version: z3.literal(1),
|
|
5992
|
+
kind: z3.literal("loop_state"),
|
|
5993
|
+
task: taskId,
|
|
5994
|
+
round: z3.number().int().positive(),
|
|
5995
|
+
phase: z3.string().min(1),
|
|
5996
|
+
pauseReason: z3.string().min(1).nullable(),
|
|
5997
|
+
budgets: LoopBudgetsSchema,
|
|
5998
|
+
heldResult: roundHeadIdentity.nullable(),
|
|
5999
|
+
deliveredFindings: roundHeadIdentity.nullable(),
|
|
6000
|
+
recordedAt: isoTimestamp
|
|
6001
|
+
}).strict();
|
|
6002
|
+
var EffectRecordSchema = z3.object({
|
|
6003
|
+
version: z3.literal(1),
|
|
6004
|
+
kind: z3.literal("effect"),
|
|
6005
|
+
task: taskId,
|
|
6006
|
+
key: z3.string().min(1),
|
|
6007
|
+
operation: z3.string().min(1),
|
|
6008
|
+
target: z3.string().min(1),
|
|
6009
|
+
inputVersion: z3.number().int().nonnegative(),
|
|
6010
|
+
payloadDigest: z3.string().min(1),
|
|
6011
|
+
status: z3.union([z3.literal("started"), z3.literal("verified"), z3.literal("uncertain")]),
|
|
6012
|
+
url: z3.string().optional(),
|
|
6013
|
+
recordedAt: isoTimestamp
|
|
6014
|
+
}).strict();
|
|
6015
|
+
var EscalationRecordSchema = z3.object({
|
|
6016
|
+
version: z3.literal(1),
|
|
6017
|
+
kind: z3.literal("escalation"),
|
|
6018
|
+
task: taskId,
|
|
6019
|
+
escalationId: z3.string().min(1),
|
|
6020
|
+
round: epochNumber,
|
|
6021
|
+
head: z3.string().min(1),
|
|
6022
|
+
branch: z3.string().min(1),
|
|
6023
|
+
pr: z3.number().int().positive().nullable(),
|
|
6024
|
+
runId: z3.string().min(1),
|
|
6025
|
+
pid: z3.number().int().positive(),
|
|
6026
|
+
host: z3.string().min(1),
|
|
6027
|
+
agent: z3.string().min(1).optional(),
|
|
6028
|
+
reason: z3.string().min(1),
|
|
6029
|
+
detail: z3.string().optional(),
|
|
6030
|
+
evidence: z3.string().optional(),
|
|
6031
|
+
attemptedRecovery: z3.string().min(1),
|
|
6032
|
+
requestedDecision: z3.string().min(1),
|
|
6033
|
+
recipient: z3.string().min(1),
|
|
6034
|
+
briefHash: z3.string().min(1).nullable(),
|
|
6035
|
+
objectivesVersion: z3.string().min(1).nullable(),
|
|
6036
|
+
rulingOrdinal: epochNumber,
|
|
6037
|
+
policyDigest: z3.string().min(1),
|
|
6038
|
+
recordedAt: isoTimestamp
|
|
6039
|
+
}).strict();
|
|
6040
|
+
var ResolutionRecordSchema = z3.object({
|
|
6041
|
+
version: z3.literal(1),
|
|
6042
|
+
kind: z3.literal("resolution"),
|
|
6043
|
+
task: taskId,
|
|
6044
|
+
escalationId: z3.string().min(1),
|
|
6045
|
+
decision: z3.union([z3.literal("resume"), z3.literal("cancel")]),
|
|
6046
|
+
authenticatedBy: z3.string().min(1),
|
|
6047
|
+
authenticatedFrom: z3.string().min(1),
|
|
6048
|
+
consumedAt: isoTimestamp
|
|
6049
|
+
}).strict();
|
|
5068
6050
|
// ../../packages/sources/src/commands.ts
|
|
5069
6051
|
var COMMANDS = [
|
|
5070
6052
|
{
|
|
@@ -5223,6 +6205,27 @@ var COMMANDS = [
|
|
|
5223
6205
|
],
|
|
5224
6206
|
status: "shipped"
|
|
5225
6207
|
},
|
|
6208
|
+
{
|
|
6209
|
+
name: "task status",
|
|
6210
|
+
description: "Every open task with a frozen brief, its pull request, and whether its loop is running, paused, or published",
|
|
6211
|
+
flags: [{ flag: "--json", description: "Enveloped JSON output (schema: 1)" }],
|
|
6212
|
+
details: [
|
|
6213
|
+
"Read-only: one `gh issue list` for every open task Issue across every tranche (title/label resolved through the same `resolveTaskIssueRef` `list-tasks.ts` already uses), the open pull request per branch, and the driver pid record / pause record / publish effect markers under `<outboxRoot>/dev-review-loop/<task>/` — never a `ps` scan, never a re-parse of posted verdict comments to decide `published`.",
|
|
6214
|
+
"`running` names the driver's pid (`review-validity-v1` task 7's pid record); `paused` names the reason from the pause record; `published` means the newest round's reviewer and security verdict effect markers both read `posted`; `no driver` is the fallback when none of the above holds.",
|
|
6215
|
+
"`vinaya task status <tranche> <n>` narrows to one task and adds the last round's held or published verdict lines (`round-<n>-reviewer.md`/`round-<n>-security.md`, whichever their outbox carries) plus the exact `vinaya dev-review-loop --resume <pr>` command when paused."
|
|
6216
|
+
],
|
|
6217
|
+
status: "shipped"
|
|
6218
|
+
},
|
|
6219
|
+
{
|
|
6220
|
+
name: "task-tools serve",
|
|
6221
|
+
description: "Run the task-operator MCP tool server over stdio — the command both runtime adapters register",
|
|
6222
|
+
details: [
|
|
6223
|
+
"Exposes the task-operator tool catalog (`packages/aeg-core/src/task-tools.ts`) to an agent runtime as one MCP server speaking newline-delimited JSON-RPC 2.0 (`initialize`/`tools/list`/`tools/call`). Claude registers it through the generated `.mcp.json`; Codex through its documented `~/.codex/config.toml` `[mcp_servers]` table — both point at this same command.",
|
|
6224
|
+
"stdout carries JSON-RPC and nothing else: any stray write from the CLI (a forge-read warning) is redirected to stderr, so the protocol stream is never corrupted.",
|
|
6225
|
+
"`task_status`/`task_escalation_read` read; `task_start` starts a run in attended mode only, requiring an authenticated caller from the invocation context (`VINAYA_MCP_CALLER`) and refusing without one — MCP is a transport, not authorization. `task_resume`/`task_cancel` require the same authenticated caller, plus a current Principal ruling read fresh from the run's own PR — never a caller-supplied approval; `task_resume` triggers the existing `dev-review-loop --resume` continuation exactly once per escalation, and `task_cancel` reports a truthful `confirmed`/`pending`/`uncertain` outcome, idempotently, across repeated calls. No unattended start until the worker-isolation boundary and a capability flag land."
|
|
6226
|
+
],
|
|
6227
|
+
status: "shipped"
|
|
6228
|
+
},
|
|
5226
6229
|
{
|
|
5227
6230
|
name: "pr create",
|
|
5228
6231
|
description: "Open a pull request after full brief-schema validation",
|
|
@@ -5367,6 +6370,30 @@ var COMMANDS = [
|
|
|
5367
6370
|
],
|
|
5368
6371
|
status: "shipped"
|
|
5369
6372
|
},
|
|
6373
|
+
{
|
|
6374
|
+
name: "log export-artifact",
|
|
6375
|
+
description: "Copy this run's own local outbox into a file, for the generated task-path workflow's own upload step",
|
|
6376
|
+
flags: [],
|
|
6377
|
+
details: [
|
|
6378
|
+
"Concatenates every `*.ndjson` file under `~/.vinaya/outbox/` into `<dest>`, verbatim — no filtering, no second redaction pass (lines are already redacted at `log()`'s own write boundary). Writes nothing when the outbox is empty, and exits 0 either way: no gate events this run is a legitimate outcome, never a failure.",
|
|
6379
|
+
"Runs inside the generated `vinaya-checks.yml` job, which holds no forge-write credential — this command never calls the forge. Both this step and the `actions/upload-artifact` step after it run `if: always()`, so a cancelled or failed job still exports whatever partial outbox it has."
|
|
6380
|
+
],
|
|
6381
|
+
status: "shipped"
|
|
6382
|
+
},
|
|
6383
|
+
{
|
|
6384
|
+
name: "log collect-artifact",
|
|
6385
|
+
description: "Validate a downloaded task-log artifact and publish the valid records through the existing flush path",
|
|
6386
|
+
flags: [
|
|
6387
|
+
{ flag: "--pr", description: "The pull request this artifact belongs to" },
|
|
6388
|
+
{ flag: "--repo", description: "Expected `owner/repo` — a provenance cross-check against each record" }
|
|
6389
|
+
],
|
|
6390
|
+
details: [
|
|
6391
|
+
"Runs inside the generated `vinaya-task-log-collector.yml` workflow — a `workflow_run`-triggered job on the default branch, holding the write credential the task-path job that produced the artifact never gets. `<path>` is already downloaded by that workflow, scoped to a specific, API-verified run id — a provenance guarantee this command never re-derives from the artifact bytes.",
|
|
6392
|
+
"Never executes, imports, or evaluates the artifact's content: every line is read as text and validated (schema, an 8 MiB whole-artifact size cap, redaction, and a repo cross-check) before anything is trusted. A rejected line becomes a named gap — printed, never a silent drop — and the artifact is never partially trusted past that check into executing anything.",
|
|
6393
|
+
"The valid, re-redacted survivors are appended into the same local outbox `vinaya log flush` would read for that PR's Issue, then published through the existing `flushOutbox` unmodified — reusing its chunking, its `<!-- aeg:log:… -->` marker, and its idempotent-retry read, rather than a second publisher. A retried collector run (a fresh runner, the same forge state) therefore dedupes for free: the marker already on the forge is acknowledged, never re-posted."
|
|
6394
|
+
],
|
|
6395
|
+
status: "shipped"
|
|
6396
|
+
},
|
|
5370
6397
|
{
|
|
5371
6398
|
name: "milestone create",
|
|
5372
6399
|
description: "Create a GitHub Milestone from a validated body",
|
|
@@ -5732,10 +6759,10 @@ function createForgeSource(config) {
|
|
|
5732
6759
|
};
|
|
5733
6760
|
}
|
|
5734
6761
|
// ../../packages/sources/src/select-source.ts
|
|
5735
|
-
import { z as
|
|
5736
|
-
var StateSourceConfigSchema =
|
|
5737
|
-
|
|
5738
|
-
|
|
6762
|
+
import { z as z4 } from "zod";
|
|
6763
|
+
var StateSourceConfigSchema = z4.discriminatedUnion("kind", [
|
|
6764
|
+
z4.object({ kind: z4.literal("forge"), owner: z4.string(), repo: z4.string() }),
|
|
6765
|
+
z4.object({ kind: z4.literal("file"), root: z4.string().optional() })
|
|
5739
6766
|
]);
|
|
5740
6767
|
// src/checks/contract.ts
|
|
5741
6768
|
var CHECK_SCHEMA_VERSION = 1;
|
|
@@ -5749,27 +6776,29 @@ import { execFileSync as execFileSync2 } from "node:child_process";
|
|
|
5749
6776
|
import { existsSync, mkdirSync, readFileSync as readFileSync2, realpathSync, writeFileSync } from "node:fs";
|
|
5750
6777
|
import { homedir } from "node:os";
|
|
5751
6778
|
import { dirname, join, resolve } from "node:path";
|
|
5752
|
-
import { z as
|
|
6779
|
+
import { z as z5 } from "zod";
|
|
5753
6780
|
|
|
5754
6781
|
// src/lib/agent-vendors.ts
|
|
5755
6782
|
var AGENT_VENDORS = ["skills", "claude", "gemini"];
|
|
5756
6783
|
|
|
5757
6784
|
// src/lib/config.ts
|
|
5758
|
-
var EnvEntrySchema =
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
6785
|
+
var EnvEntrySchema = z5.union([
|
|
6786
|
+
z5.literal(true),
|
|
6787
|
+
z5.object({ optional: z5.literal(true) }),
|
|
6788
|
+
z5.object({ anyOf: z5.array(z5.string()).min(2) }),
|
|
6789
|
+
z5.string()
|
|
5763
6790
|
]);
|
|
5764
|
-
var CheckEntrySchema =
|
|
5765
|
-
run:
|
|
5766
|
-
scope:
|
|
5767
|
-
include:
|
|
5768
|
-
args:
|
|
5769
|
-
timeoutMs:
|
|
5770
|
-
env:
|
|
5771
|
-
requiresOpenPr:
|
|
5772
|
-
ownWorkflow:
|
|
6791
|
+
var CheckEntrySchema = z5.object({
|
|
6792
|
+
run: z5.string(),
|
|
6793
|
+
scope: z5.enum(["diff", "full"]),
|
|
6794
|
+
include: z5.array(z5.string()).optional(),
|
|
6795
|
+
args: z5.array(z5.string()).optional(),
|
|
6796
|
+
timeoutMs: z5.number().optional(),
|
|
6797
|
+
env: z5.record(z5.string(), EnvEntrySchema).optional(),
|
|
6798
|
+
requiresOpenPr: z5.boolean().optional(),
|
|
6799
|
+
ownWorkflow: z5.boolean().optional(),
|
|
6800
|
+
principalOwed: z5.literal(true).optional(),
|
|
6801
|
+
validates: z5.enum(["body", "issue"]).optional()
|
|
5773
6802
|
}).superRefine((entry2, ctx) => {
|
|
5774
6803
|
if (!entry2.env)
|
|
5775
6804
|
return;
|
|
@@ -5779,25 +6808,26 @@ var CheckEntrySchema = z3.object({
|
|
|
5779
6808
|
const members = value.anyOf;
|
|
5780
6809
|
if (new Set(members).size !== members.length) {
|
|
5781
6810
|
ctx.addIssue({
|
|
5782
|
-
code:
|
|
6811
|
+
code: z5.ZodIssueCode.custom,
|
|
5783
6812
|
message: `env.${key}.anyOf has duplicate members`,
|
|
5784
6813
|
path: ["env", key, "anyOf"]
|
|
5785
6814
|
});
|
|
5786
6815
|
}
|
|
5787
6816
|
if (!members.includes(key)) {
|
|
5788
6817
|
ctx.addIssue({
|
|
5789
|
-
code:
|
|
6818
|
+
code: z5.ZodIssueCode.custom,
|
|
5790
6819
|
message: `env.${key}.anyOf must include "${key}" itself as a member`,
|
|
5791
6820
|
path: ["env", key, "anyOf"]
|
|
5792
6821
|
});
|
|
5793
6822
|
}
|
|
5794
6823
|
}
|
|
5795
6824
|
});
|
|
5796
|
-
var RoleEntrySchema =
|
|
5797
|
-
contract:
|
|
6825
|
+
var RoleEntrySchema = z5.object({
|
|
6826
|
+
contract: z5.string().refine((p) => p.includes("/"), {
|
|
5798
6827
|
message: 'must be a path (contain at least one "/"), not a bare filename'
|
|
5799
6828
|
})
|
|
5800
6829
|
});
|
|
6830
|
+
var MAX_REPORT_COMMAND_TIMEOUT_MS = 3600000;
|
|
5801
6831
|
var BRIEF_BUILTINS = [
|
|
5802
6832
|
"tier",
|
|
5803
6833
|
"testPlan",
|
|
@@ -5817,17 +6847,17 @@ var BRIEF_BUILTINS = [
|
|
|
5817
6847
|
"briefSections",
|
|
5818
6848
|
"milestoneShape"
|
|
5819
6849
|
];
|
|
5820
|
-
var BriefSectionSchema =
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
6850
|
+
var BriefSectionSchema = z5.union([
|
|
6851
|
+
z5.object({ builtin: z5.enum(BRIEF_BUILTINS) }),
|
|
6852
|
+
z5.object({ heading: z5.string().min(1), name: z5.string().optional() }),
|
|
6853
|
+
z5.object({ field: z5.string().min(1), name: z5.string().optional() }),
|
|
6854
|
+
z5.object({ phrase: z5.string().min(1), name: z5.string().optional() })
|
|
5825
6855
|
]);
|
|
5826
|
-
var BriefSchemaSchema =
|
|
5827
|
-
pr:
|
|
5828
|
-
issue:
|
|
5829
|
-
milestone:
|
|
5830
|
-
ack:
|
|
6856
|
+
var BriefSchemaSchema = z5.object({
|
|
6857
|
+
pr: z5.object({ sections: z5.array(BriefSectionSchema) }).optional(),
|
|
6858
|
+
issue: z5.object({ sections: z5.array(BriefSectionSchema) }).optional(),
|
|
6859
|
+
milestone: z5.object({ sections: z5.array(BriefSectionSchema) }).optional(),
|
|
6860
|
+
ack: z5.array(z5.enum(BRIEF_BUILTINS)).optional()
|
|
5831
6861
|
});
|
|
5832
6862
|
function isSafeRepoRelPath(p) {
|
|
5833
6863
|
if (p.length === 0)
|
|
@@ -5836,7 +6866,7 @@ function isSafeRepoRelPath(p) {
|
|
|
5836
6866
|
return false;
|
|
5837
6867
|
return p.split(/[\\/]/).every((seg) => seg !== ".." && seg !== "");
|
|
5838
6868
|
}
|
|
5839
|
-
var SafeRepoRelPath =
|
|
6869
|
+
var SafeRepoRelPath = z5.string().refine(isSafeRepoRelPath, {
|
|
5840
6870
|
message: "must be a repo-root-relative path with no `..` segment or absolute root"
|
|
5841
6871
|
});
|
|
5842
6872
|
var CANONICAL_HOOK_BLOCK_PREFIXES = [".git/", ".husky/", ".vinaya/hooks/", ".claude/hooks/"];
|
|
@@ -5846,22 +6876,22 @@ function isCanonicalHookBlockPath(p) {
|
|
|
5846
6876
|
var ManagedHookBlockPath = SafeRepoRelPath.refine(isCanonicalHookBlockPath, {
|
|
5847
6877
|
message: `must start with one of ${CANONICAL_HOOK_BLOCK_PREFIXES.join(", ")} (byte-exact, case-sensitive)`
|
|
5848
6878
|
});
|
|
5849
|
-
var ManagedBlockRecordSchema =
|
|
6879
|
+
var ManagedBlockRecordSchema = z5.object({
|
|
5850
6880
|
path: ManagedHookBlockPath,
|
|
5851
|
-
marker:
|
|
5852
|
-
comment:
|
|
6881
|
+
marker: z5.string(),
|
|
6882
|
+
comment: z5.enum(["hash", "html"])
|
|
5853
6883
|
});
|
|
5854
|
-
var ManagedManifestSchema =
|
|
5855
|
-
version:
|
|
5856
|
-
files:
|
|
5857
|
-
blocks:
|
|
5858
|
-
labels:
|
|
5859
|
-
agents:
|
|
6884
|
+
var ManagedManifestSchema = z5.object({
|
|
6885
|
+
version: z5.number().int().positive(),
|
|
6886
|
+
files: z5.array(SafeRepoRelPath),
|
|
6887
|
+
blocks: z5.array(ManagedBlockRecordSchema),
|
|
6888
|
+
labels: z5.array(z5.string()),
|
|
6889
|
+
agents: z5.array(z5.enum(AGENT_VENDORS)).optional()
|
|
5860
6890
|
});
|
|
5861
|
-
var ProjectEntrySchema =
|
|
5862
|
-
name:
|
|
5863
|
-
description:
|
|
5864
|
-
path:
|
|
6891
|
+
var ProjectEntrySchema = z5.object({
|
|
6892
|
+
name: z5.string().min(1),
|
|
6893
|
+
description: z5.string().min(1).optional(),
|
|
6894
|
+
path: z5.string().min(1).optional()
|
|
5865
6895
|
});
|
|
5866
6896
|
function parseTokensCollectDeclaration(value) {
|
|
5867
6897
|
const m = value.trim().match(/^(\S+)\s+(\S+)$/);
|
|
@@ -5872,34 +6902,62 @@ function parseTokensCollectDeclaration(value) {
|
|
|
5872
6902
|
return null;
|
|
5873
6903
|
return { interpreter, script };
|
|
5874
6904
|
}
|
|
5875
|
-
var VinayaConfigSchema =
|
|
5876
|
-
rings:
|
|
5877
|
-
ring1_forgeWriteInterception:
|
|
5878
|
-
ring2_asyncAudits:
|
|
6905
|
+
var VinayaConfigSchema = z5.object({
|
|
6906
|
+
rings: z5.object({
|
|
6907
|
+
ring1_forgeWriteInterception: z5.boolean(),
|
|
6908
|
+
ring2_asyncAudits: z5.boolean()
|
|
5879
6909
|
}).optional(),
|
|
5880
|
-
checks:
|
|
5881
|
-
roles:
|
|
6910
|
+
checks: z5.record(z5.string(), CheckEntrySchema).optional(),
|
|
6911
|
+
roles: z5.record(z5.string(), RoleEntrySchema).optional(),
|
|
5882
6912
|
briefSchema: BriefSchemaSchema.optional(),
|
|
5883
6913
|
managed: ManagedManifestSchema.optional(),
|
|
5884
|
-
principals:
|
|
5885
|
-
releaseActor:
|
|
5886
|
-
ci:
|
|
5887
|
-
tokens:
|
|
5888
|
-
collect:
|
|
6914
|
+
principals: z5.array(z5.string()).min(1).optional(),
|
|
6915
|
+
releaseActor: z5.string().min(1).optional(),
|
|
6916
|
+
ci: z5.object({ setup: z5.string().min(1) }).optional(),
|
|
6917
|
+
tokens: z5.object({
|
|
6918
|
+
collect: z5.string().min(1).refine((v) => parseTokensCollectDeclaration(v) !== null, {
|
|
5889
6919
|
message: 'tokens.collect must be exactly "<interpreter> <repo-relative-script-path>" — two whitespace-separated tokens, no flags, no shell syntax, no quoting'
|
|
5890
6920
|
})
|
|
5891
6921
|
}).optional(),
|
|
5892
|
-
blastRadius:
|
|
5893
|
-
proseGates:
|
|
5894
|
-
doctrineRoot:
|
|
5895
|
-
readerFacingPrefix:
|
|
5896
|
-
readerFacingSuffix:
|
|
5897
|
-
legacySlugDir:
|
|
6922
|
+
blastRadius: z5.object({ extraDomains: z5.array(z5.string()).optional() }).optional(),
|
|
6923
|
+
proseGates: z5.object({
|
|
6924
|
+
doctrineRoot: z5.string().min(1).optional(),
|
|
6925
|
+
readerFacingPrefix: z5.string().min(1).optional(),
|
|
6926
|
+
readerFacingSuffix: z5.string().min(1).optional(),
|
|
6927
|
+
legacySlugDir: z5.string().min(1).optional()
|
|
6928
|
+
}).optional(),
|
|
6929
|
+
projects: z5.array(ProjectEntrySchema).optional(),
|
|
6930
|
+
dispatch: z5.object({
|
|
6931
|
+
timeoutMs: z5.number().int().positive().optional(),
|
|
6932
|
+
killGraceMs: z5.number().int().positive().optional(),
|
|
6933
|
+
agent: z5.enum(["claude", "codex", "gemini"]).optional()
|
|
5898
6934
|
}).optional(),
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
6935
|
+
reviewPolicy: z5.object({
|
|
6936
|
+
codeReviewThreshold: z5.string().min(1).optional(),
|
|
6937
|
+
securityThreshold: z5.string().min(1).optional(),
|
|
6938
|
+
maxRounds: z5.number().optional()
|
|
6939
|
+
}).optional(),
|
|
6940
|
+
prePush: z5.object({
|
|
6941
|
+
alwaysRun: z5.array(z5.string()).optional()
|
|
6942
|
+
}).optional(),
|
|
6943
|
+
report: z5.object({
|
|
6944
|
+
commandTimeoutMs: z5.number().int().positive().optional()
|
|
6945
|
+
}).superRefine((report, ctx) => {
|
|
6946
|
+
const commandTimeoutMs = report.commandTimeoutMs;
|
|
6947
|
+
if (commandTimeoutMs !== undefined && commandTimeoutMs > MAX_REPORT_COMMAND_TIMEOUT_MS) {
|
|
6948
|
+
ctx.addIssue({
|
|
6949
|
+
code: z5.ZodIssueCode.custom,
|
|
6950
|
+
path: ["commandTimeoutMs"],
|
|
6951
|
+
message: "must be at most 3600000 (1 hour)"
|
|
6952
|
+
});
|
|
6953
|
+
}
|
|
6954
|
+
}).optional(),
|
|
6955
|
+
logPublish: z5.object({
|
|
6956
|
+
issue: z5.number().int().positive().optional(),
|
|
6957
|
+
pr: z5.number().int().positive().optional(),
|
|
6958
|
+
maxChunksPerFlush: z5.number().int().positive().optional()
|
|
6959
|
+
}).refine((v) => !(v.issue !== undefined && v.pr !== undefined), {
|
|
6960
|
+
message: "logPublish: set at most one of issue/pr, not both"
|
|
5903
6961
|
}).optional()
|
|
5904
6962
|
});
|
|
5905
6963
|
var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
|
|
@@ -5974,6 +7032,28 @@ function resolvePrincipalAllowlist(config) {
|
|
|
5974
7032
|
function resolveReleaseActor(config) {
|
|
5975
7033
|
return config?.releaseActor ?? DEFAULT_RELEASE_ACTOR;
|
|
5976
7034
|
}
|
|
7035
|
+
function resolveReviewPolicy(config) {
|
|
7036
|
+
const raw = config?.reviewPolicy;
|
|
7037
|
+
if (!raw)
|
|
7038
|
+
return DEFAULT_REVIEW_POLICY;
|
|
7039
|
+
const codeReviewThreshold = raw.codeReviewThreshold ?? DEFAULT_REVIEW_POLICY.codeReviewThreshold;
|
|
7040
|
+
if (!isKnownSeverity(CODE_REVIEW_SEVERITY_ORDER, codeReviewThreshold)) {
|
|
7041
|
+
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.`);
|
|
7042
|
+
}
|
|
7043
|
+
const securityThreshold = raw.securityThreshold ?? DEFAULT_REVIEW_POLICY.securityThreshold;
|
|
7044
|
+
if (!isKnownSeverity(SECURITY_SEVERITY_ORDER, securityThreshold)) {
|
|
7045
|
+
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.`);
|
|
7046
|
+
}
|
|
7047
|
+
const maxRounds = raw.maxRounds ?? DEFAULT_REVIEW_POLICY.maxRounds;
|
|
7048
|
+
if (!Number.isInteger(maxRounds) || maxRounds < 1) {
|
|
7049
|
+
throw new Error(`vinaya.config.json: reviewPolicy.maxRounds "${maxRounds}" is not a positive integer — fix the config, this never falls back to a default.`);
|
|
7050
|
+
}
|
|
7051
|
+
return {
|
|
7052
|
+
codeReviewThreshold,
|
|
7053
|
+
securityThreshold,
|
|
7054
|
+
maxRounds
|
|
7055
|
+
};
|
|
7056
|
+
}
|
|
5977
7057
|
function trustAnchorRepo() {
|
|
5978
7058
|
const wellFormed = (slug) => /^[^/\s]+\/[^/\s]+$/.test(slug) ? slug : null;
|
|
5979
7059
|
const fromRunner = process.env.GITHUB_REPOSITORY?.trim();
|
|
@@ -6311,14 +7391,99 @@ function containedRealPath(root, p) {
|
|
|
6311
7391
|
return null;
|
|
6312
7392
|
}
|
|
6313
7393
|
}
|
|
7394
|
+
function fetchIssueJson(issueNumber) {
|
|
7395
|
+
try {
|
|
7396
|
+
const out = execFileSync4("gh", ["issue", "view", String(issueNumber), "--json", "number,state,body,labels"], {
|
|
7397
|
+
encoding: "utf8",
|
|
7398
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
7399
|
+
});
|
|
7400
|
+
return JSON.parse(out);
|
|
7401
|
+
} catch {
|
|
7402
|
+
return null;
|
|
7403
|
+
}
|
|
7404
|
+
}
|
|
7405
|
+
async function runIssueMode(issueNumber) {
|
|
7406
|
+
const issueJson = fetchIssueJson(issueNumber);
|
|
7407
|
+
if (!issueJson) {
|
|
7408
|
+
fail(`dispatch-gate issue-existence: Issue #${issueNumber} does not resolve on the forge.`, "Confirm the Issue number is correct and `gh auth status` passes, then re-run `vinaya check dispatch-readiness`.");
|
|
7409
|
+
}
|
|
7410
|
+
if (issueJson.labels.some((l) => l.name.startsWith("vinaya/tranche:"))) {
|
|
7411
|
+
fail(`dispatch-gate: Issue #${issueNumber} carries a vinaya/tranche:* label — it belongs to a tranche, not the tranche-less backlog path.`, "This Issue has a real tranche home — run this check on its tranche-shaped branch instead.");
|
|
7412
|
+
}
|
|
7413
|
+
const repo = resolveRepo2();
|
|
7414
|
+
if (!repo) {
|
|
7415
|
+
fail("dispatch-gate severity:infra — could not resolve owner/repo.", "Set AEG_REPO=owner/repo, or confirm `git remote get-url origin` resolves to a GitHub URL, then re-run `vinaya check dispatch-readiness`.");
|
|
7416
|
+
}
|
|
7417
|
+
const issueRationalePass = checkIssueRationale(issueJson.body).status !== "fail";
|
|
7418
|
+
const { dependsOn: dependsOnIds, conflictsWith: conflictsWithIds } = parseRationaleDeps(issueJson.body);
|
|
7419
|
+
const taskById = new Map;
|
|
7420
|
+
const factsByTaskId = new Map;
|
|
7421
|
+
const dependsOn = await Promise.all(dependsOnIds.map(async (dep) => {
|
|
7422
|
+
const r = await resolveEdge(dep, taskById, factsByTaskId, repo);
|
|
7423
|
+
return {
|
|
7424
|
+
id: dep,
|
|
7425
|
+
issue: r.issue,
|
|
7426
|
+
merged: r.merged,
|
|
7427
|
+
resolved: r.resolved,
|
|
7428
|
+
issueState: r.issueState,
|
|
7429
|
+
stateReason: r.stateReason,
|
|
7430
|
+
closedByActor: r.closedByActor
|
|
7431
|
+
};
|
|
7432
|
+
}));
|
|
7433
|
+
const conflictsWith = await Promise.all(conflictsWithIds.map(async (c) => {
|
|
7434
|
+
const r = await resolveEdge(c, taskById, factsByTaskId, repo);
|
|
7435
|
+
return { id: c, issue: r.issue, openOrInFlight: r.open };
|
|
7436
|
+
}));
|
|
7437
|
+
const task = {
|
|
7438
|
+
id: String(issueNumber),
|
|
7439
|
+
title: "",
|
|
7440
|
+
issue: issueNumber,
|
|
7441
|
+
projects: [],
|
|
7442
|
+
dependsOn: dependsOnIds,
|
|
7443
|
+
conflictsWith: conflictsWithIds,
|
|
7444
|
+
rationaleMarkdown: ""
|
|
7445
|
+
};
|
|
7446
|
+
const input = {
|
|
7447
|
+
trancheSlug: `issue-${issueNumber}`,
|
|
7448
|
+
task,
|
|
7449
|
+
issue: { number: issueNumber, state: issueJson.state === "OPEN" ? "open" : "closed" },
|
|
7450
|
+
issueRationalePass,
|
|
7451
|
+
dependsOn,
|
|
7452
|
+
conflictsWith,
|
|
7453
|
+
priorTask: null,
|
|
7454
|
+
priorTrancheArchival: [],
|
|
7455
|
+
principalAllowlist: resolvePrincipalAllowlist(loadTrustAnchorConfig())
|
|
7456
|
+
};
|
|
7457
|
+
const result = checkDispatchReadiness(input);
|
|
7458
|
+
let ready = true;
|
|
7459
|
+
if (!result.ready) {
|
|
7460
|
+
for (const detail of result.blockerDetails) {
|
|
7461
|
+
emitCheckError({
|
|
7462
|
+
schema: CHECK_SCHEMA_VERSION,
|
|
7463
|
+
check: CHECK_NAME,
|
|
7464
|
+
severity: "error",
|
|
7465
|
+
message: detail.message,
|
|
7466
|
+
agent_recovery_prompt: recoveryPromptFor(detail.class)
|
|
7467
|
+
});
|
|
7468
|
+
}
|
|
7469
|
+
ready = false;
|
|
7470
|
+
}
|
|
7471
|
+
if (!checkPremiseReassertion())
|
|
7472
|
+
ready = false;
|
|
7473
|
+
process.exit(ready ? 0 : 1);
|
|
7474
|
+
}
|
|
6314
7475
|
async function main() {
|
|
6315
7476
|
const branch = currentBranch();
|
|
6316
|
-
const
|
|
6317
|
-
if (!
|
|
7477
|
+
const ref = parseTaskBranchIdentity(branch);
|
|
7478
|
+
if (!ref) {
|
|
6318
7479
|
process.exit(0);
|
|
6319
7480
|
}
|
|
6320
|
-
|
|
6321
|
-
|
|
7481
|
+
if (ref.kind === "issue") {
|
|
7482
|
+
await runIssueMode(ref.issueNumber);
|
|
7483
|
+
return;
|
|
7484
|
+
}
|
|
7485
|
+
const trancheSlug = ref.tranche;
|
|
7486
|
+
const taskId2 = ref.taskId;
|
|
6322
7487
|
const repo = resolveRepo2();
|
|
6323
7488
|
if (!repo) {
|
|
6324
7489
|
fail("dispatch-gate severity:infra — could not resolve owner/repo.", "Set AEG_REPO=owner/repo, or confirm `git remote get-url origin` resolves to a GitHub URL, then re-run `vinaya check dispatch-readiness`.");
|
|
@@ -6330,9 +7495,9 @@ async function main() {
|
|
|
6330
7495
|
} catch (err) {
|
|
6331
7496
|
fail(`dispatch-gate severity:infra — could not derive tranche "${trancheSlug}" from the forge: ${err.message}`, "Confirm `gh auth status` passes and the tranche has a Milestone + labeled Issues on the forge, then re-run this check.");
|
|
6332
7497
|
}
|
|
6333
|
-
const task = tranche.tasks.find((t) => t.id ===
|
|
7498
|
+
const task = tranche.tasks.find((t) => t.id === taskId2);
|
|
6334
7499
|
if (!task) {
|
|
6335
|
-
fail(`dispatch-gate row-existence: task "${
|
|
7500
|
+
fail(`dispatch-gate row-existence: task "${taskId2}" is not present in tranche "${trancheSlug}"'s forge-derived task list.`, "Confirm the branch name matches a real, forge-registered task id, or wait for the Planner to open the task Issue before re-running.");
|
|
6336
7501
|
}
|
|
6337
7502
|
const taskRefs = tranche.tasks.map((t) => ({ id: t.id, issue: t.issue }));
|
|
6338
7503
|
const snapshot = await fetchForgeFacts({
|
|
@@ -6381,13 +7546,13 @@ async function main() {
|
|
|
6381
7546
|
const result = checkDispatchReadiness(input);
|
|
6382
7547
|
let ready = true;
|
|
6383
7548
|
if (!result.ready) {
|
|
6384
|
-
for (const
|
|
7549
|
+
for (const detail of result.blockerDetails) {
|
|
6385
7550
|
emitCheckError({
|
|
6386
7551
|
schema: CHECK_SCHEMA_VERSION,
|
|
6387
7552
|
check: CHECK_NAME,
|
|
6388
7553
|
severity: "error",
|
|
6389
|
-
message:
|
|
6390
|
-
agent_recovery_prompt: recoveryPromptFor(
|
|
7554
|
+
message: detail.message,
|
|
7555
|
+
agent_recovery_prompt: recoveryPromptFor(detail.class)
|
|
6391
7556
|
});
|
|
6392
7557
|
}
|
|
6393
7558
|
ready = false;
|
|
@@ -6396,29 +7561,28 @@ async function main() {
|
|
|
6396
7561
|
ready = false;
|
|
6397
7562
|
process.exit(ready ? 0 : 1);
|
|
6398
7563
|
}
|
|
6399
|
-
function recoveryPromptFor(
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
}
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
}
|
|
6421
|
-
return "Resolve the named dispatch blocker before continuing work on this task, then re-run `vinaya check dispatch-readiness`.";
|
|
7564
|
+
function recoveryPromptFor(blockerClass) {
|
|
7565
|
+
switch (blockerClass) {
|
|
7566
|
+
case "internal-self-dependency":
|
|
7567
|
+
return "This is an INTERNAL parser-bug report, not a real dispatch gate — a task cannot depend on itself, so there is nothing here to wait for or resolve. Do NOT work around it and do NOT skip the hook. Report it upstream, and re-run once the rationale text is corrected or the parser fix ships.";
|
|
7568
|
+
case "issue-existence":
|
|
7569
|
+
return "This task has no resolvable Issue yet. Wait for the Planner to cut the Issue (or fix the phantom reference in the topology), then re-run `vinaya check dispatch-readiness`.";
|
|
7570
|
+
case "rationale":
|
|
7571
|
+
return "The task's Issue fails the rationale gate. Ask the Planner to complete the eight-field rationale on the Issue body, then re-run `vinaya check dispatch-readiness`.";
|
|
7572
|
+
case "depends-on-unresolvable":
|
|
7573
|
+
return "A declared dependency edge could not be resolved to any tranche/task/Issue — check the tranche slug and task id in the edge text, then re-run `vinaya check dispatch-readiness`.";
|
|
7574
|
+
case "depends-on-not-merged":
|
|
7575
|
+
return "A declared dependency is not merged yet. Do not start this task — wait for the named dependency PR to merge, then re-run `vinaya check dispatch-readiness`.";
|
|
7576
|
+
case "conflicts-with":
|
|
7577
|
+
return "A declared conflicting task has an open or in-flight PR. Wait for it to merge before continuing, then re-run `vinaya check dispatch-readiness`.";
|
|
7578
|
+
case "prior-tranche-archival":
|
|
7579
|
+
return "This project's previous tranche is not archived. Ask the Tranche Archivist to run first, then re-run `vinaya check dispatch-readiness`.";
|
|
7580
|
+
default:
|
|
7581
|
+
return assertNeverBlockerClass(blockerClass);
|
|
7582
|
+
}
|
|
7583
|
+
}
|
|
7584
|
+
function assertNeverBlockerClass(x) {
|
|
7585
|
+
throw new Error(`Unhandled DispatchBlockerClass: ${JSON.stringify(x)}`);
|
|
6422
7586
|
}
|
|
6423
7587
|
if (__require.main == __require.module) {
|
|
6424
7588
|
main();
|