@attalabs/vinaya 0.26.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/aeg-root/enforcement.md +5 -5
- package/aeg-root/milestone-model.md +2 -0
- package/aeg-root/process.md +1 -1
- package/aeg-root/roles/developer.md +23 -17
- package/aeg-root/roles/planner.md +4 -0
- package/aeg-root/roles/principal.md +8 -2
- package/aeg-root/roles/reviewer.md +9 -5
- package/aeg-root/roles/security.md +9 -5
- package/dist/checks/bin/check-body-bare-digits.js +1038 -257
- package/dist/checks/bin/check-branch-topology.js +963 -199
- package/dist/checks/bin/check-brief-shape.js +1276 -203
- package/dist/checks/bin/check-changeset-coverage.js +1677 -281
- package/dist/checks/bin/check-closes-n.js +967 -203
- package/dist/checks/bin/check-coherence.js +1127 -284
- package/dist/checks/bin/check-dead-branch-push.js +910 -193
- package/dist/checks/bin/check-dispatch-readiness.js +1189 -295
- package/dist/checks/bin/check-doc-coverage-push.js +1673 -277
- package/dist/checks/bin/check-doc-coverage.js +1675 -279
- package/dist/checks/bin/check-doctrine-no-procedures.js +1020 -257
- package/dist/checks/bin/check-doctrine-portability.js +1672 -276
- package/dist/checks/bin/check-evidence-fresh.js +2526 -322
- package/dist/checks/bin/check-exec-bits.js +1670 -274
- package/dist/checks/bin/check-first-push-dispatch.js +1075 -265
- package/dist/checks/bin/check-issue-assignment.js +965 -201
- package/dist/checks/bin/check-issue-milestone-attach.js +5734 -0
- package/dist/checks/bin/check-issue-objectives-numbering.js +5736 -0
- package/dist/checks/bin/check-issue-parts-coverage.js +5736 -0
- package/dist/checks/bin/check-issue-surface-globs.js +6910 -0
- package/dist/checks/bin/check-issue-title-grammar.js +5736 -0
- package/dist/checks/bin/check-issue-tranche-label.js +5736 -0
- package/dist/checks/bin/check-main-branch-refusal.js +910 -193
- package/dist/checks/bin/check-no-disk-state.js +910 -193
- package/dist/checks/bin/check-pr-premise-reassert.js +1020 -257
- package/dist/checks/bin/check-pr-report-density.js +910 -193
- package/dist/checks/bin/check-quoted-command.js +1651 -274
- package/dist/checks/bin/check-reader-resolvable-prose.js +1655 -278
- package/dist/checks/bin/check-registry-gates.js +997 -193
- package/dist/checks/bin/check-retired-vocabulary.js +1649 -272
- package/dist/checks/bin/check-review-gate.js +1163 -316
- package/dist/checks/bin/check-single-plan-pr.js +910 -193
- package/dist/checks/bin/check-surface-scope.js +973 -201
- package/dist/checks/bin/check-test-plan.js +939 -206
- package/dist/checks/bin/check-token-collection-wired.js +910 -193
- package/dist/checks/bin/check-token-report.js +910 -193
- package/dist/checks/bin/check-workspace-escape.js +1668 -272
- package/dist/index.js +10602 -5352
- package/dist/lib/pre-push-changed-files.js +57 -0
- package/dist/lib/pre-push-select-tests.js +1473 -0
- package/package.json +4 -2
|
@@ -3,7 +3,7 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
4
4
|
|
|
5
5
|
// src/checks/bin/check-exec-bits.ts
|
|
6
|
-
import { execFileSync as
|
|
6
|
+
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
7
7
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
8
8
|
|
|
9
9
|
// src/checks/contract.ts
|
|
@@ -14,8 +14,8 @@ function emitCheckError(error) {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
// src/lib/diff-evidence.ts
|
|
17
|
-
import { execFileSync as
|
|
18
|
-
import { join as
|
|
17
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
18
|
+
import { join as join4, relative, resolve as resolve2 } from "node:path";
|
|
19
19
|
// ../../packages/aeg-core/src/gate-audience.ts
|
|
20
20
|
var GATE_AUDIENCE = {
|
|
21
21
|
"check-branch-topology": { shippedAs: "branch-topology", ring: 0 },
|
|
@@ -315,7 +315,7 @@ function issueListByLabelArgs(owner, repo, label) {
|
|
|
315
315
|
"--state",
|
|
316
316
|
"all",
|
|
317
317
|
"--json",
|
|
318
|
-
"number,title,body,state,labels,milestone",
|
|
318
|
+
"number,title,body,state,labels,milestone,stateReason",
|
|
319
319
|
"--limit",
|
|
320
320
|
"200"
|
|
321
321
|
];
|
|
@@ -661,14 +661,19 @@ function resolveTaskIssueRef(title, labels) {
|
|
|
661
661
|
var INTENTS_HEADING = /^#{1,6}\s*Tranche intents\s*$/im;
|
|
662
662
|
var NEXT_HEADING = /^#{1,6}\s+\S/m;
|
|
663
663
|
var INTENT_BULLET = /^-\s+([a-z0-9][a-z0-9-]*)\s*:\s*(.+)$/i;
|
|
664
|
-
function
|
|
665
|
-
const text = stripCode(description, { inlineSpans: "keep" });
|
|
664
|
+
function intentsSection(text) {
|
|
666
665
|
const start = text.match(INTENTS_HEADING);
|
|
667
666
|
if (!start || start.index === undefined)
|
|
668
|
-
return
|
|
667
|
+
return null;
|
|
669
668
|
const rest = text.slice(start.index + start[0].length);
|
|
670
669
|
const next = rest.match(NEXT_HEADING);
|
|
671
|
-
|
|
670
|
+
return rest.slice(0, next && next.index !== undefined ? next.index : rest.length);
|
|
671
|
+
}
|
|
672
|
+
function intentGoalForSlug(description, slug) {
|
|
673
|
+
const text = stripCode(description, { inlineSpans: "keep" });
|
|
674
|
+
const section = intentsSection(text);
|
|
675
|
+
if (section === null)
|
|
676
|
+
return "";
|
|
672
677
|
for (const line of section.split(`
|
|
673
678
|
`)) {
|
|
674
679
|
const trimmed = line.trim();
|
|
@@ -1960,6 +1965,9 @@ function parseInlineFieldList(section) {
|
|
|
1960
1965
|
// ../../packages/aeg-core/src/verdict-extraction.ts
|
|
1961
1966
|
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
1962
1967
|
var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1968
|
+
var RULING_ORDINAL_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Ruling ordinal:\s*(\d+)(?!\d)/im;
|
|
1969
|
+
var BRIEF_HASH_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Brief hash:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1970
|
+
var POLICY_DIGEST_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Policy digest:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1963
1971
|
function firstFiveLines(comment) {
|
|
1964
1972
|
return comment.split(`
|
|
1965
1973
|
`).slice(0, 5).join(`
|
|
@@ -1973,6 +1981,35 @@ function extractObjectivesVersion(comment) {
|
|
|
1973
1981
|
const m = firstFiveLines(comment).match(OBJECTIVES_VERSION_PATTERN);
|
|
1974
1982
|
return m ? m[1].toLowerCase() : null;
|
|
1975
1983
|
}
|
|
1984
|
+
function firstSevenLines(comment) {
|
|
1985
|
+
return comment.split(`
|
|
1986
|
+
`).slice(0, 7).join(`
|
|
1987
|
+
`);
|
|
1988
|
+
}
|
|
1989
|
+
function extractRulingOrdinal(comment) {
|
|
1990
|
+
const m = firstSevenLines(comment).match(RULING_ORDINAL_PATTERN);
|
|
1991
|
+
return m ? Number.parseInt(m[1], 10) : null;
|
|
1992
|
+
}
|
|
1993
|
+
function firstElevenLines(comment) {
|
|
1994
|
+
return comment.split(`
|
|
1995
|
+
`).slice(0, 11).join(`
|
|
1996
|
+
`);
|
|
1997
|
+
}
|
|
1998
|
+
function extractBriefHash(comment) {
|
|
1999
|
+
const m = firstElevenLines(comment).match(BRIEF_HASH_PATTERN);
|
|
2000
|
+
return m ? m[1].toLowerCase() : null;
|
|
2001
|
+
}
|
|
2002
|
+
function extractPolicyDigest(comment) {
|
|
2003
|
+
const m = firstElevenLines(comment).match(POLICY_DIGEST_PATTERN);
|
|
2004
|
+
return m ? m[1].toLowerCase() : null;
|
|
2005
|
+
}
|
|
2006
|
+
var FINDING_SEVERITY_LINE = /^\d+\.\s+\[([A-Z][A-Z]*)\]\s+(.+?)\s+—/gm;
|
|
2007
|
+
function extractFindingSeverities(comment) {
|
|
2008
|
+
return [...comment.matchAll(FINDING_SEVERITY_LINE)].map((m) => ({
|
|
2009
|
+
severity: m[1],
|
|
2010
|
+
location: m[2]
|
|
2011
|
+
}));
|
|
2012
|
+
}
|
|
1976
2013
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
1977
2014
|
const candidates = comments.filter((c) => valuePattern.test(c));
|
|
1978
2015
|
if (candidates.length === 0) {
|
|
@@ -1980,6 +2017,10 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1980
2017
|
value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
|
|
1981
2018
|
headSha: null,
|
|
1982
2019
|
objectivesVersion: null,
|
|
2020
|
+
rulingOrdinal: null,
|
|
2021
|
+
briefHash: null,
|
|
2022
|
+
policyDigest: null,
|
|
2023
|
+
findingSeverities: [],
|
|
1983
2024
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
1984
2025
|
};
|
|
1985
2026
|
}
|
|
@@ -1990,6 +2031,10 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1990
2031
|
value: `the most recent ${missingLabel} comment's VERDICT line is not within its first five lines — DANGLING, see below`,
|
|
1991
2032
|
headSha: null,
|
|
1992
2033
|
objectivesVersion: null,
|
|
2034
|
+
rulingOrdinal: null,
|
|
2035
|
+
briefHash: null,
|
|
2036
|
+
policyDigest: null,
|
|
2037
|
+
findingSeverities: [],
|
|
1993
2038
|
danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
|
|
1994
2039
|
};
|
|
1995
2040
|
}
|
|
@@ -1997,6 +2042,10 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1997
2042
|
value: m[1].toUpperCase().replace(/[_-]/g, " "),
|
|
1998
2043
|
headSha: extractHeadSha(latest),
|
|
1999
2044
|
objectivesVersion: extractObjectivesVersion(latest),
|
|
2045
|
+
rulingOrdinal: extractRulingOrdinal(latest),
|
|
2046
|
+
briefHash: extractBriefHash(latest),
|
|
2047
|
+
policyDigest: extractPolicyDigest(latest),
|
|
2048
|
+
findingSeverities: extractFindingSeverities(latest),
|
|
2000
2049
|
danglingNote: null
|
|
2001
2050
|
};
|
|
2002
2051
|
}
|
|
@@ -2332,6 +2381,53 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive,
|
|
|
2332
2381
|
}
|
|
2333
2382
|
return out;
|
|
2334
2383
|
}
|
|
2384
|
+
// ../../packages/aeg-core/src/review-policy.ts
|
|
2385
|
+
var CODE_REVIEW_SEVERITY_ORDER = ["BLOCKER", "MAJOR", "MINOR"];
|
|
2386
|
+
var SECURITY_SEVERITY_ORDER = ["CRITICAL", "HIGH", "MEDIUM", "LOW"];
|
|
2387
|
+
var DEFAULT_MAX_ROUNDS = 3;
|
|
2388
|
+
var DEFAULT_REVIEW_POLICY = {
|
|
2389
|
+
codeReviewThreshold: "BLOCKER",
|
|
2390
|
+
securityThreshold: "HIGH",
|
|
2391
|
+
maxRounds: DEFAULT_MAX_ROUNDS
|
|
2392
|
+
};
|
|
2393
|
+
var FILE_SHAPED_LOCATION = /\.[a-zA-Z0-9]{1,10}(:\d+)?\s*$/;
|
|
2394
|
+
var PROSE_LOCATION_PATTERNS = [/\bpr\s*body\b/i, /\bcomment\b/i];
|
|
2395
|
+
var ROLE_FILE_LOCATION = /(^|\/)aeg-root\/roles\//i;
|
|
2396
|
+
function isProseLocation(location) {
|
|
2397
|
+
if (ROLE_FILE_LOCATION.test(location))
|
|
2398
|
+
return true;
|
|
2399
|
+
if (FILE_SHAPED_LOCATION.test(location))
|
|
2400
|
+
return false;
|
|
2401
|
+
return PROSE_LOCATION_PATTERNS.some((pattern) => pattern.test(location));
|
|
2402
|
+
}
|
|
2403
|
+
var PROSE_CAP_SEVERITY = "MINOR";
|
|
2404
|
+
function blockingSeverities(scale, threshold) {
|
|
2405
|
+
const idx = scale.indexOf(threshold);
|
|
2406
|
+
if (idx === -1) {
|
|
2407
|
+
throw new Error(`blockingSeverities: threshold "${threshold}" is not one of ${scale.join(" > ")}`);
|
|
2408
|
+
}
|
|
2409
|
+
return scale.slice(0, idx + 1);
|
|
2410
|
+
}
|
|
2411
|
+
function evaluateReviewFindings(findings, scale, threshold) {
|
|
2412
|
+
const blocking = new Set(blockingSeverities(scale, threshold));
|
|
2413
|
+
const blockingFindings = findings.filter((f) => {
|
|
2414
|
+
if (!scale.includes(f.severity)) {
|
|
2415
|
+
throw new Error(`evaluateReviewFindings: severity "${f.severity}" is not one of ${scale.join(" > ")}`);
|
|
2416
|
+
}
|
|
2417
|
+
const effectiveSeverity = f.location !== undefined && isProseLocation(f.location) ? PROSE_CAP_SEVERITY : f.severity;
|
|
2418
|
+
return blocking.has(effectiveSeverity);
|
|
2419
|
+
});
|
|
2420
|
+
return { outcome: blockingFindings.length > 0 ? "blocked" : "clean", blockingFindings };
|
|
2421
|
+
}
|
|
2422
|
+
function evaluateCodeReview(findings, policy) {
|
|
2423
|
+
return evaluateReviewFindings(findings, CODE_REVIEW_SEVERITY_ORDER, policy.codeReviewThreshold);
|
|
2424
|
+
}
|
|
2425
|
+
function evaluateSecurityReview(findings, policy) {
|
|
2426
|
+
return evaluateReviewFindings(findings, SECURITY_SEVERITY_ORDER, policy.securityThreshold);
|
|
2427
|
+
}
|
|
2428
|
+
function isKnownSeverity(scale, value) {
|
|
2429
|
+
return scale.includes(value);
|
|
2430
|
+
}
|
|
2335
2431
|
// ../../packages/aeg-core/src/pr-tier.ts
|
|
2336
2432
|
var TIER_FIELD = /(\*\*)?\s*Tier\s*(\*\*)?\s*:\s*(\*\*)?\s*([013])\b/i;
|
|
2337
2433
|
function readTierFromPrBody(prBody) {
|
|
@@ -2358,6 +2454,9 @@ import { createHash as createHash2 } from "node:crypto";
|
|
|
2358
2454
|
var HEADING_RE = /^##[ \t]*Objectives[ \t]*$/im;
|
|
2359
2455
|
var NEXT_HEADING_RE = /^##[ \t]/m;
|
|
2360
2456
|
var OBJECTIVE_LINE_RE = /^O(\d+)\.[ \t]*(.*)$/;
|
|
2457
|
+
function maskedForHeadingSearch(body) {
|
|
2458
|
+
return maskDetailsBlocks(maskCode(body));
|
|
2459
|
+
}
|
|
2361
2460
|
function hasBacktickedPath(text) {
|
|
2362
2461
|
let i = 0;
|
|
2363
2462
|
while (i < text.length) {
|
|
@@ -2380,16 +2479,22 @@ function stripObjectiveBackticks(text) {
|
|
|
2380
2479
|
function wordCount(text) {
|
|
2381
2480
|
return text.split(/\s+/).filter((w) => /[a-z]/i.test(w)).length;
|
|
2382
2481
|
}
|
|
2383
|
-
function
|
|
2384
|
-
const
|
|
2482
|
+
function objectivesSectionBounds(body) {
|
|
2483
|
+
const masked = maskedForHeadingSearch(body);
|
|
2484
|
+
const heading = HEADING_RE.exec(masked);
|
|
2385
2485
|
if (!heading)
|
|
2386
2486
|
return null;
|
|
2387
|
-
const
|
|
2487
|
+
const start = heading.index + heading[0].length;
|
|
2488
|
+
const afterHeading = masked.slice(start);
|
|
2388
2489
|
const next = NEXT_HEADING_RE.exec(afterHeading);
|
|
2389
|
-
return next ?
|
|
2490
|
+
return { start, end: next ? start + next.index : body.length };
|
|
2491
|
+
}
|
|
2492
|
+
function objectivesSectionText(body) {
|
|
2493
|
+
const bounds = objectivesSectionBounds(body);
|
|
2494
|
+
return bounds === null ? null : body.slice(bounds.start, bounds.end);
|
|
2390
2495
|
}
|
|
2391
2496
|
function hasObjectivesHeading(body) {
|
|
2392
|
-
return HEADING_RE.test(body);
|
|
2497
|
+
return HEADING_RE.test(maskedForHeadingSearch(body));
|
|
2393
2498
|
}
|
|
2394
2499
|
function objectivesOf(body) {
|
|
2395
2500
|
const section = objectivesSectionText(body);
|
|
@@ -2449,6 +2554,15 @@ function isIssueNotFoundError(err) {
|
|
|
2449
2554
|
`);
|
|
2450
2555
|
return /could not resolve to an (?:issue|pull request)|\b404\b|not found/i.test(haystack);
|
|
2451
2556
|
}
|
|
2557
|
+
function resolveObjectivesSource(prBody, issue, cutoverIssue) {
|
|
2558
|
+
if (issue !== null && issue < cutoverIssue)
|
|
2559
|
+
return { kind: "none" };
|
|
2560
|
+
if (issue !== null)
|
|
2561
|
+
return { kind: "issue", issue };
|
|
2562
|
+
if (hasObjectivesHeading(prBody))
|
|
2563
|
+
return { kind: "body" };
|
|
2564
|
+
return { kind: "none" };
|
|
2565
|
+
}
|
|
2452
2566
|
|
|
2453
2567
|
// ../../packages/aeg-core/src/premise-check.ts
|
|
2454
2568
|
import { createHash as createHash3 } from "node:crypto";
|
|
@@ -2684,7 +2798,7 @@ function checkForField(prBody) {
|
|
|
2684
2798
|
]
|
|
2685
2799
|
};
|
|
2686
2800
|
}
|
|
2687
|
-
function
|
|
2801
|
+
function checkClosesNPresence(prBody) {
|
|
2688
2802
|
const closesPattern = /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s{0,8}:?\s{0,8}#\d+/i;
|
|
2689
2803
|
if (closesPattern.test(stripCode(prBody))) {
|
|
2690
2804
|
return { status: "pass", errors: [] };
|
|
@@ -2716,10 +2830,22 @@ var COMMIT_TYPES = [
|
|
|
2716
2830
|
"Test"
|
|
2717
2831
|
];
|
|
2718
2832
|
var COMMIT_TYPE_STYLE = new RegExp(`^(${COMMIT_TYPES.join("|")})(\\([a-z0-9-]+\\))?: \\S`);
|
|
2833
|
+
function checkForgeTitle(title) {
|
|
2834
|
+
const taskStyle = /^\[[a-z0-9._-]+\] \S+ — \S/;
|
|
2835
|
+
if (COMMIT_TYPE_STYLE.test(title) || taskStyle.test(title))
|
|
2836
|
+
return { status: "pass", errors: [] };
|
|
2837
|
+
return {
|
|
2838
|
+
status: "fail",
|
|
2839
|
+
errors: [
|
|
2840
|
+
`brief-validation title: "${title}" matches neither title grammar — expected \`Type: description\` / \`Type(scope): description\` (commitlint types + Plan) or \`[tranche] id — description\` (task form).`
|
|
2841
|
+
]
|
|
2842
|
+
};
|
|
2843
|
+
}
|
|
2719
2844
|
var BRIEF_SHAPE_MARKERS = [checkSurfaceMap, checkDocUpdateList, checkStopConditions, checkAutonomyClause];
|
|
2720
2845
|
var TASK_BRANCH_PATTERN = /^task\/[^/]+\/[^/]+$/;
|
|
2846
|
+
var TASK_ISSUE_BRANCH_PATTERN = /^task\/issue-\d+$/;
|
|
2721
2847
|
function isTaskBranch(branch) {
|
|
2722
|
-
return TASK_BRANCH_PATTERN.test(branch);
|
|
2848
|
+
return TASK_BRANCH_PATTERN.test(branch) || TASK_ISSUE_BRANCH_PATTERN.test(branch);
|
|
2723
2849
|
}
|
|
2724
2850
|
function isBriefShaped(prBody) {
|
|
2725
2851
|
const stripped = stripCode(prBody);
|
|
@@ -2841,8 +2967,9 @@ function packagesNamedIn(text) {
|
|
|
2841
2967
|
}
|
|
2842
2968
|
function hasTestPathForConsumer(text, consumerDir) {
|
|
2843
2969
|
const escaped = consumerDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2844
|
-
const
|
|
2845
|
-
|
|
2970
|
+
const filePathRe = new RegExp(`${escaped}\\/[\\w./-]*\\.test\\.[A-Za-z0-9]+`);
|
|
2971
|
+
const testDirRe = new RegExp(`${escaped}\\/(?:[\\w-]+\\/)*(?:tests|specs)(?:\\/|\\b)`);
|
|
2972
|
+
return filePathRe.test(text) || testDirRe.test(text);
|
|
2846
2973
|
}
|
|
2847
2974
|
function checkConsumerTests(prBody, consumersOf) {
|
|
2848
2975
|
const section4 = extractNumberedSection(prBody, 4);
|
|
@@ -2946,21 +3073,44 @@ function checkBriefSections(prBody, readTier, options = {}) {
|
|
|
2946
3073
|
checkConsumerTests(prBody, consumersOf),
|
|
2947
3074
|
checkDefeatCases(prBody),
|
|
2948
3075
|
...issueObjectives !== undefined ? [checkObjectivesCopy(prBody, issueObjectives), checkObjectivesCoverage(prBody)] : [],
|
|
2949
|
-
...requireClosesN ? [
|
|
3076
|
+
...requireClosesN ? [checkClosesNPresence(prBody)] : []
|
|
2950
3077
|
];
|
|
2951
3078
|
return { errors: results.flatMap((r) => r.errors) };
|
|
2952
3079
|
}
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
`
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
3080
|
+
function contentAfterNLines(body, n) {
|
|
3081
|
+
let idx = -1;
|
|
3082
|
+
for (let i = 0;i < n; i++) {
|
|
3083
|
+
idx = body.indexOf(`
|
|
3084
|
+
`, idx + 1);
|
|
3085
|
+
if (idx === -1)
|
|
3086
|
+
return "";
|
|
3087
|
+
}
|
|
3088
|
+
return body.slice(idx + 1);
|
|
3089
|
+
}
|
|
3090
|
+
var BRIEF_MARKER_LINE_RE = /^<!-- aeg:brief:v(\d+) -->$/;
|
|
3091
|
+
function parseBriefMarkerVersion(firstLine) {
|
|
3092
|
+
const m = BRIEF_MARKER_LINE_RE.exec(firstLine.trim());
|
|
3093
|
+
if (!m)
|
|
3094
|
+
return null;
|
|
3095
|
+
const version = Number.parseInt(m[1], 10);
|
|
3096
|
+
return Number.isInteger(version) && version >= 1 ? version : null;
|
|
3097
|
+
}
|
|
3098
|
+
function frozenBriefContent(body, version) {
|
|
3099
|
+
return contentAfterNLines(body, version === 1 ? 2 : 3);
|
|
3100
|
+
}
|
|
3101
|
+
function resolveNewestFrozenBrief(comments, allowlist) {
|
|
3102
|
+
let best = null;
|
|
3103
|
+
for (const c of comments) {
|
|
3104
|
+
if (!isPrincipal(c.author, allowlist))
|
|
3105
|
+
continue;
|
|
3106
|
+
const version = parseBriefMarkerVersion(c.body.split(`
|
|
3107
|
+
`)[0] ?? "");
|
|
3108
|
+
if (version === null)
|
|
3109
|
+
continue;
|
|
3110
|
+
if (best === null || version > best.version)
|
|
3111
|
+
best = { ...c, version };
|
|
3112
|
+
}
|
|
3113
|
+
return best === null ? null : { ...best, content: frozenBriefContent(best.body, best.version) };
|
|
2964
3114
|
}
|
|
2965
3115
|
// ../../packages/aeg-core/src/doctrine-portability.ts
|
|
2966
3116
|
var DEFAULT_SHIPS_PREFIX = "aeg-root/";
|
|
@@ -3213,6 +3363,28 @@ function topLevelSectionText(body, headingName) {
|
|
|
3213
3363
|
const next = /^##[ \t]/m.exec(afterHeading);
|
|
3214
3364
|
return next ? afterHeading.slice(0, next.index) : afterHeading;
|
|
3215
3365
|
}
|
|
3366
|
+
function partHasBacktickedPath(text) {
|
|
3367
|
+
let i = 0;
|
|
3368
|
+
while (i < text.length) {
|
|
3369
|
+
const start = text.indexOf("`", i);
|
|
3370
|
+
if (start === -1)
|
|
3371
|
+
return false;
|
|
3372
|
+
const end = text.indexOf("`", start + 1);
|
|
3373
|
+
if (end === -1)
|
|
3374
|
+
return false;
|
|
3375
|
+
if (text.slice(start + 1, end).includes("/"))
|
|
3376
|
+
return true;
|
|
3377
|
+
i = end + 1;
|
|
3378
|
+
}
|
|
3379
|
+
return false;
|
|
3380
|
+
}
|
|
3381
|
+
var PART_MIN_WORDS_OUTSIDE_BACKTICKS = 3;
|
|
3382
|
+
function stripPartBackticks(text) {
|
|
3383
|
+
return text.replace(/`[^`\n]*`/g, " ");
|
|
3384
|
+
}
|
|
3385
|
+
function partWordCount(text) {
|
|
3386
|
+
return text.split(/\s+/).filter((w) => /[a-z]/i.test(w)).length;
|
|
3387
|
+
}
|
|
3216
3388
|
function looksLikeFilePath(entry2) {
|
|
3217
3389
|
const stripped = entry2.replace(/\/\*\*?$/, "");
|
|
3218
3390
|
const lastSegment = stripped.split("/").pop() ?? stripped;
|
|
@@ -3253,6 +3425,18 @@ function globCoversPath(glob, path) {
|
|
|
3253
3425
|
const p = path.replace(/\/+$/, "");
|
|
3254
3426
|
return g === p || g.startsWith(`${p}/`) || p.startsWith(`${g}/`);
|
|
3255
3427
|
}
|
|
3428
|
+
function checkSurfaceGlobsResolve(body, resolvesToFile) {
|
|
3429
|
+
const surface = parseIssueSurface(body);
|
|
3430
|
+
if (!surface.ok)
|
|
3431
|
+
return { status: "pass", errors: [] };
|
|
3432
|
+
const errors = [];
|
|
3433
|
+
for (const glob of surface.value.in) {
|
|
3434
|
+
if (!resolvesToFile(glob)) {
|
|
3435
|
+
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.`);
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3439
|
+
}
|
|
3256
3440
|
function checkSurfaceScope(changedFiles, outGlobs) {
|
|
3257
3441
|
if (outGlobs.length === 0)
|
|
3258
3442
|
return { ok: true };
|
|
@@ -3264,9 +3448,77 @@ function checkSurfaceScope(changedFiles, outGlobs) {
|
|
|
3264
3448
|
}
|
|
3265
3449
|
return violations.length > 0 ? { ok: false, violations } : { ok: true };
|
|
3266
3450
|
}
|
|
3451
|
+
var ISSUE_PART_LINE_RE = /^Part\s+(\d+)\s*\(([^)]*)\)\s*[-—–]\s*(.*)$/i;
|
|
3452
|
+
function parseIssueParts(body) {
|
|
3453
|
+
const section = topLevelSectionText(body, "Parts");
|
|
3454
|
+
if (section === null)
|
|
3455
|
+
return { ok: false, errors: ["no `## Parts` heading found in the body."] };
|
|
3456
|
+
const lines = section.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
3457
|
+
const parts = [];
|
|
3458
|
+
const errors = [];
|
|
3459
|
+
for (const line of lines) {
|
|
3460
|
+
const m = ISSUE_PART_LINE_RE.exec(line);
|
|
3461
|
+
if (!m) {
|
|
3462
|
+
errors.push(`"${line}" is not a well-formed Parts line — expected \`Part <n> (<refs>) — <outcome>\`.`);
|
|
3463
|
+
continue;
|
|
3464
|
+
}
|
|
3465
|
+
const n = Number.parseInt(m[1], 10);
|
|
3466
|
+
const refs = m[2];
|
|
3467
|
+
const text = m[3].trim();
|
|
3468
|
+
if (text.length === 0) {
|
|
3469
|
+
errors.push(`Part ${n} has no outcome text after the dash — every Part states one observable outcome.`);
|
|
3470
|
+
continue;
|
|
3471
|
+
}
|
|
3472
|
+
if (partHasBacktickedPath(text) && partWordCount(stripPartBackticks(text)) < PART_MIN_WORDS_OUTSIDE_BACKTICKS) {
|
|
3473
|
+
errors.push(`Part ${n} is little more than a file path — a Part names an outcome and symbols, never a bare path.`);
|
|
3474
|
+
continue;
|
|
3475
|
+
}
|
|
3476
|
+
const objectiveIds = [...refs.matchAll(/O(\d+)/g)].map((r) => Number.parseInt(r[1], 10));
|
|
3477
|
+
parts.push({ n, objectiveIds, text });
|
|
3478
|
+
}
|
|
3479
|
+
if (parts.length === 0) {
|
|
3480
|
+
errors.push("the `## Parts` section has no well-formed `Part <n> (<refs>) — <outcome>` lines.");
|
|
3481
|
+
}
|
|
3482
|
+
if (errors.length > 0)
|
|
3483
|
+
return { ok: false, errors };
|
|
3484
|
+
return { ok: true, value: parts };
|
|
3485
|
+
}
|
|
3486
|
+
function checkPartsCiteDefinedObjectives(body) {
|
|
3487
|
+
const parts = parseIssueParts(body);
|
|
3488
|
+
const objectives = objectivesOf(body);
|
|
3489
|
+
if (!parts.ok || !objectives.ok)
|
|
3490
|
+
return { status: "pass", errors: [] };
|
|
3491
|
+
const definedIds = new Set(objectives.objectives.map((o) => Number.parseInt(o.id.slice(1), 10)));
|
|
3492
|
+
const errors = [];
|
|
3493
|
+
for (const part of parts.value) {
|
|
3494
|
+
for (const objectiveId of part.objectiveIds) {
|
|
3495
|
+
if (!definedIds.has(objectiveId)) {
|
|
3496
|
+
errors.push(`issue-validation Parts: Part ${part.n} cites O${objectiveId}, which the Issue's own \`## Objectives\` section does not define.`);
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
}
|
|
3500
|
+
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3501
|
+
}
|
|
3267
3502
|
function isTaskIssueLabelSet(labels) {
|
|
3268
3503
|
return hasLabel("tranche", labels);
|
|
3269
3504
|
}
|
|
3505
|
+
function checkTrancheLabelPresence(_body, _labels) {
|
|
3506
|
+
return { status: "pass", errors: [] };
|
|
3507
|
+
}
|
|
3508
|
+
function checkMilestoneAttach(labels, currentMilestoneTitle, resolvedMilestoneTitle) {
|
|
3509
|
+
if (!isTaskIssueLabelSet(labels))
|
|
3510
|
+
return { status: "pass", errors: [] };
|
|
3511
|
+
if (resolvedMilestoneTitle === null)
|
|
3512
|
+
return { status: "pass", errors: [] };
|
|
3513
|
+
if (currentMilestoneTitle === resolvedMilestoneTitle)
|
|
3514
|
+
return { status: "pass", errors: [] };
|
|
3515
|
+
return {
|
|
3516
|
+
status: "fail",
|
|
3517
|
+
errors: [
|
|
3518
|
+
`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}".`
|
|
3519
|
+
]
|
|
3520
|
+
};
|
|
3521
|
+
}
|
|
3270
3522
|
var TYPE_LABEL_IDS = LABELS.filter((l) => l.category === "type").map((l) => l.id);
|
|
3271
3523
|
function isControlCodePoint(codePoint) {
|
|
3272
3524
|
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
|
|
@@ -3331,6 +3583,19 @@ function checkProjectsRegistered(body, _labels, registeredNames) {
|
|
|
3331
3583
|
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;
|
|
3332
3584
|
var DOC_PATH_RE_GLOBAL = new RegExp(DOC_PATH_RE.source, "gi");
|
|
3333
3585
|
|
|
3586
|
+
// ../../packages/aeg-core/src/task-branch-identity.ts
|
|
3587
|
+
var ISSUE_BRANCH_PATTERN = /^task\/issue-(\d+)$/;
|
|
3588
|
+
var TRANCHE_BRANCH_PATTERN = /^task\/([^/]+)\/([^/]+)$/;
|
|
3589
|
+
function parseTaskBranchIdentity(branch) {
|
|
3590
|
+
const issueMatch = ISSUE_BRANCH_PATTERN.exec(branch);
|
|
3591
|
+
if (issueMatch)
|
|
3592
|
+
return { kind: "issue", issueNumber: Number(issueMatch[1]) };
|
|
3593
|
+
const trancheMatch = TRANCHE_BRANCH_PATTERN.exec(branch);
|
|
3594
|
+
if (trancheMatch)
|
|
3595
|
+
return { kind: "tranche", tranche: trancheMatch[1], taskId: trancheMatch[2] };
|
|
3596
|
+
return null;
|
|
3597
|
+
}
|
|
3598
|
+
|
|
3334
3599
|
// ../../packages/aeg-core/src/coherence-checks.ts
|
|
3335
3600
|
var COHERENCE_ENFORCED_FROM = "2026-07-01";
|
|
3336
3601
|
function isGrandfathered(isoDate) {
|
|
@@ -3351,6 +3616,7 @@ function checkA1(entries, principalAllowlist = PRINCIPAL_ALLOWLIST) {
|
|
|
3351
3616
|
if (handClosed)
|
|
3352
3617
|
continue;
|
|
3353
3618
|
failures.push({
|
|
3619
|
+
code: "closed-without-merge",
|
|
3354
3620
|
issue: e.task.issue,
|
|
3355
3621
|
tranche: e.trancheSlug,
|
|
3356
3622
|
task: e.task.id,
|
|
@@ -3375,6 +3641,7 @@ function checkA3(entries) {
|
|
|
3375
3641
|
continue;
|
|
3376
3642
|
if (e.facts.prState === "merged" && e.facts.issueState !== "closed") {
|
|
3377
3643
|
failures.push({
|
|
3644
|
+
code: "auto-close-misfire",
|
|
3378
3645
|
issue: e.task.issue,
|
|
3379
3646
|
tranche: e.trancheSlug,
|
|
3380
3647
|
task: e.task.id,
|
|
@@ -3399,6 +3666,7 @@ function checkT1(entries) {
|
|
|
3399
3666
|
continue;
|
|
3400
3667
|
if (e.facts === undefined) {
|
|
3401
3668
|
failures.push({
|
|
3669
|
+
code: "phantom-issue-ref",
|
|
3402
3670
|
issue: e.task.issue,
|
|
3403
3671
|
tranche: e.trancheSlug,
|
|
3404
3672
|
task: e.task.id,
|
|
@@ -3417,6 +3685,7 @@ function checkT2(openIssuesBySlug, topologyIssuesBySlug, ciTrancheSlug) {
|
|
|
3417
3685
|
for (const num of openNums) {
|
|
3418
3686
|
if (!topologySet.has(num)) {
|
|
3419
3687
|
failures.push({
|
|
3688
|
+
code: "orphan-task",
|
|
3420
3689
|
issue: num,
|
|
3421
3690
|
tranche: slug,
|
|
3422
3691
|
reason: `Issue #${num} is open and labeled ${trancheLabel(slug)} but does not appear in the topology file`
|
|
@@ -3451,6 +3720,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
|
|
|
3451
3720
|
continue;
|
|
3452
3721
|
if (forgeUnavailableSlugs?.has(e.trancheSlug)) {
|
|
3453
3722
|
failures.push({
|
|
3723
|
+
code: "tbd-in-active-tranche",
|
|
3454
3724
|
issue: null,
|
|
3455
3725
|
tranche: e.trancheSlug,
|
|
3456
3726
|
task: e.task.id,
|
|
@@ -3460,6 +3730,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
|
|
|
3460
3730
|
continue;
|
|
3461
3731
|
}
|
|
3462
3732
|
failures.push({
|
|
3733
|
+
code: "tbd-in-active-tranche",
|
|
3463
3734
|
issue: null,
|
|
3464
3735
|
tranche: e.trancheSlug,
|
|
3465
3736
|
task: e.task.id,
|
|
@@ -3492,6 +3763,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
|
|
|
3492
3763
|
const sameIssue = depEntry.task.issue !== null && e.task.issue !== null && depEntry.task.issue === e.task.issue;
|
|
3493
3764
|
if (sameTask || sameIssue) {
|
|
3494
3765
|
failures.push({
|
|
3766
|
+
code: "d1-self-dependency",
|
|
3495
3767
|
issue: e.task.issue,
|
|
3496
3768
|
tranche: e.trancheSlug,
|
|
3497
3769
|
task: e.task.id,
|
|
@@ -3503,6 +3775,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
|
|
|
3503
3775
|
const depClosed = depFacts?.issueState === "closed";
|
|
3504
3776
|
if (!depClosed) {
|
|
3505
3777
|
failures.push({
|
|
3778
|
+
code: "dispatched-on-unmet-deps",
|
|
3506
3779
|
issue: e.task.issue,
|
|
3507
3780
|
tranche: e.trancheSlug,
|
|
3508
3781
|
task: e.task.id,
|
|
@@ -3528,11 +3801,13 @@ function checkR1(issuesBySlug, grandfatheredIssues, registeredNames = []) {
|
|
|
3528
3801
|
const errors = [
|
|
3529
3802
|
...checkIssueRationale(issue.body).errors,
|
|
3530
3803
|
...checkProjectsRegistered(issue.body, issue.labels, registeredNames).errors,
|
|
3531
|
-
...checkIssueObjectives(issue.body, issue.number).errors
|
|
3804
|
+
...checkIssueObjectives(issue.body, issue.number).errors,
|
|
3805
|
+
...checkPartsCiteDefinedObjectives(issue.body).errors
|
|
3532
3806
|
];
|
|
3533
3807
|
if (errors.length === 0)
|
|
3534
3808
|
continue;
|
|
3535
3809
|
failures.push({
|
|
3810
|
+
code: "missing-rationale-field",
|
|
3536
3811
|
issue: issue.number,
|
|
3537
3812
|
tranche: slug,
|
|
3538
3813
|
reason: `Issue #${issue.number} fails the rationale gate: ${errors.join(" | ")}`,
|
|
@@ -3561,6 +3836,7 @@ function checkL1(files, entriesBySlug) {
|
|
|
3561
3836
|
const allClosed = withFacts.every((e) => e.facts?.issueState === "closed");
|
|
3562
3837
|
if (allClosed) {
|
|
3563
3838
|
failures.push({
|
|
3839
|
+
code: "archive-recommended",
|
|
3564
3840
|
tranche: f.slug,
|
|
3565
3841
|
reason: "Active tranche has no open task-Issues — consider archiving to completed/"
|
|
3566
3842
|
});
|
|
@@ -3592,27 +3868,38 @@ function extractClosesReferences(prBody) {
|
|
|
3592
3868
|
}
|
|
3593
3869
|
return referenced;
|
|
3594
3870
|
}
|
|
3595
|
-
function
|
|
3871
|
+
function checkClosesNTopology(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
3596
3872
|
const referenced = extractClosesReferences(prBody);
|
|
3597
3873
|
if (taskIssueRefs) {
|
|
3598
3874
|
for (const n of referenced) {
|
|
3599
|
-
const
|
|
3600
|
-
if (!
|
|
3875
|
+
const ref2 = taskIssueRefs.get(n);
|
|
3876
|
+
if (!ref2)
|
|
3601
3877
|
continue;
|
|
3602
|
-
const expectedBranch = `task/${
|
|
3878
|
+
const expectedBranch = `task/${ref2.trancheSlug}/${ref2.taskId}`;
|
|
3603
3879
|
if (branch !== expectedBranch) {
|
|
3604
3880
|
return {
|
|
3605
3881
|
ok: false,
|
|
3606
|
-
message: `closes-n-reverse: branch "${branch}" closes #${n} (task ${
|
|
3882
|
+
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.`
|
|
3607
3883
|
};
|
|
3608
3884
|
}
|
|
3609
3885
|
}
|
|
3610
3886
|
}
|
|
3611
|
-
const
|
|
3612
|
-
if (!
|
|
3887
|
+
const ref = parseTaskBranchIdentity(branch);
|
|
3888
|
+
if (!ref)
|
|
3613
3889
|
return { ok: true };
|
|
3614
|
-
|
|
3615
|
-
|
|
3890
|
+
if (ref.kind === "issue") {
|
|
3891
|
+
const expectedIssue2 = ref.issueNumber;
|
|
3892
|
+
if (!referenced.has(expectedIssue2)) {
|
|
3893
|
+
return {
|
|
3894
|
+
ok: false,
|
|
3895
|
+
expectedIssue: expectedIssue2,
|
|
3896
|
+
message: `closes-n: PR body does not contain \`Closes #${expectedIssue2}\` (required for branch "${branch}"). Add it to the PR body Summary section.`
|
|
3897
|
+
};
|
|
3898
|
+
}
|
|
3899
|
+
return { ok: true, expectedIssue: expectedIssue2 };
|
|
3900
|
+
}
|
|
3901
|
+
const trancheSlug = ref.tranche;
|
|
3902
|
+
const taskId = ref.taskId;
|
|
3616
3903
|
const trancheFile = trancheFiles.find((f) => f.slug === trancheSlug);
|
|
3617
3904
|
if (!trancheFile) {
|
|
3618
3905
|
return {
|
|
@@ -3664,29 +3951,67 @@ function extractIssue(body) {
|
|
|
3664
3951
|
const issue = headerNums.length > 0 ? headerNums[0] : bodyNums[0];
|
|
3665
3952
|
return { issue, extraIssues: bodyNums.filter((n) => n !== issue), outsideHeader: headerNums.length === 0 };
|
|
3666
3953
|
}
|
|
3667
|
-
// ../../packages/aeg-core/src/review-
|
|
3668
|
-
|
|
3669
|
-
|
|
3954
|
+
// ../../packages/aeg-core/src/review-input-manifest.ts
|
|
3955
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3956
|
+
function briefHash(brief) {
|
|
3957
|
+
return createHash4("sha256").update(`${brief}
|
|
3958
|
+
`).digest("hex");
|
|
3959
|
+
}
|
|
3960
|
+
function policyDigest(policy) {
|
|
3961
|
+
return createHash4("sha256").update(JSON.stringify({ codeReviewThreshold: policy.codeReviewThreshold, securityThreshold: policy.securityThreshold })).digest("hex");
|
|
3962
|
+
}
|
|
3963
|
+
function isBoundToHead(echoed, headSha) {
|
|
3964
|
+
if (!echoed.headSha)
|
|
3670
3965
|
return false;
|
|
3671
|
-
return headSha.toLowerCase().startsWith(
|
|
3966
|
+
return headSha.toLowerCase().startsWith(echoed.headSha.toLowerCase());
|
|
3672
3967
|
}
|
|
3673
|
-
function isBoundByPatchIdentity(
|
|
3674
|
-
if (patchIdOf === undefined || !
|
|
3968
|
+
function isBoundByPatchIdentity(echoed, headSha, patchIdOf) {
|
|
3969
|
+
if (patchIdOf === undefined || !echoed.headSha)
|
|
3675
3970
|
return false;
|
|
3676
|
-
const judged = patchIdOf(
|
|
3971
|
+
const judged = patchIdOf(echoed.headSha);
|
|
3677
3972
|
const current = patchIdOf(headSha);
|
|
3678
3973
|
if (judged === null || current === null)
|
|
3679
3974
|
return false;
|
|
3680
3975
|
return judged === current;
|
|
3681
3976
|
}
|
|
3682
|
-
function isBoundToPatch(
|
|
3683
|
-
return isBoundToHead(
|
|
3977
|
+
function isBoundToPatch(echoed, headSha, patchIdOf) {
|
|
3978
|
+
return isBoundToHead(echoed, headSha) || isBoundByPatchIdentity(echoed, headSha, patchIdOf);
|
|
3684
3979
|
}
|
|
3685
|
-
function isBoundToObjectives(
|
|
3980
|
+
function isBoundToObjectives(echoed, currentVersion) {
|
|
3686
3981
|
if (currentVersion === null)
|
|
3687
3982
|
return true;
|
|
3688
|
-
return
|
|
3983
|
+
return echoed.objectivesVersion === currentVersion;
|
|
3984
|
+
}
|
|
3985
|
+
function isBoundToRulings(echoed, currentOrdinal) {
|
|
3986
|
+
if (echoed.rulingOrdinal === null)
|
|
3987
|
+
return currentOrdinal === 0;
|
|
3988
|
+
return echoed.rulingOrdinal === currentOrdinal;
|
|
3989
|
+
}
|
|
3990
|
+
function isBoundToBriefHash(echoed, currentHash) {
|
|
3991
|
+
if (currentHash === null)
|
|
3992
|
+
return true;
|
|
3993
|
+
return echoed.briefHash === currentHash;
|
|
3994
|
+
}
|
|
3995
|
+
function isBoundToPolicy(echoed, currentDigest) {
|
|
3996
|
+
return echoed.policyDigest === currentDigest;
|
|
3689
3997
|
}
|
|
3998
|
+
function compareManifest(echoed, current, patchIdOf) {
|
|
3999
|
+
const head = isBoundToPatch(echoed, current.headSha, patchIdOf);
|
|
4000
|
+
const briefHashBound = isBoundToBriefHash(echoed, current.briefHash);
|
|
4001
|
+
const objectivesVersion2 = isBoundToObjectives(echoed, current.objectivesVersion);
|
|
4002
|
+
const rulingOrdinal = isBoundToRulings(echoed, current.rulingOrdinal);
|
|
4003
|
+
const policyDigestBound = isBoundToPolicy(echoed, current.policyDigest);
|
|
4004
|
+
return {
|
|
4005
|
+
bound: head && briefHashBound && objectivesVersion2 && rulingOrdinal && policyDigestBound,
|
|
4006
|
+
head,
|
|
4007
|
+
briefHash: briefHashBound,
|
|
4008
|
+
objectivesVersion: objectivesVersion2,
|
|
4009
|
+
rulingOrdinal,
|
|
4010
|
+
policyDigest: policyDigestBound
|
|
4011
|
+
};
|
|
4012
|
+
}
|
|
4013
|
+
|
|
4014
|
+
// ../../packages/aeg-core/src/review-gate.ts
|
|
3690
4015
|
function checkReviewGate(input) {
|
|
3691
4016
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
3692
4017
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -3709,13 +4034,55 @@ function checkReviewGate(input) {
|
|
|
3709
4034
|
const verifiedBodies = verified.map((c) => c.body);
|
|
3710
4035
|
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
3711
4036
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
3712
|
-
const
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
4037
|
+
const policy = input.policy ?? DEFAULT_REVIEW_POLICY;
|
|
4038
|
+
let codeReviewPolicyEvaluation;
|
|
4039
|
+
let securityPolicyEvaluation;
|
|
4040
|
+
try {
|
|
4041
|
+
codeReviewPolicyEvaluation = evaluateCodeReview(codeReview.findingSeverities, policy);
|
|
4042
|
+
securityPolicyEvaluation = evaluateSecurityReview(security.findingSeverities, policy);
|
|
4043
|
+
} catch (err) {
|
|
4044
|
+
return {
|
|
4045
|
+
verdict: "fail",
|
|
4046
|
+
reason: `a verdict comment carries a finding severity this repository's policy does not recognize: ${err instanceof Error ? err.message : String(err)}`,
|
|
4047
|
+
waived: false
|
|
4048
|
+
};
|
|
4049
|
+
}
|
|
4050
|
+
const codeReviewTextClean = codeReview.value === "APPROVE";
|
|
4051
|
+
const securityTextClean = security.value === "PASS";
|
|
4052
|
+
const codeReviewPolicyClean = codeReviewPolicyEvaluation.outcome === "clean";
|
|
4053
|
+
const securityPolicyClean = securityPolicyEvaluation.outcome === "clean";
|
|
4054
|
+
const codeReviewClean = codeReviewTextClean && codeReviewPolicyClean;
|
|
4055
|
+
const securityClean = securityTextClean && securityPolicyClean;
|
|
4056
|
+
const currentManifest = {
|
|
4057
|
+
headSha: input.headSha,
|
|
4058
|
+
briefHash: input.briefHash ?? null,
|
|
4059
|
+
objectivesVersion: input.objectivesVersion,
|
|
4060
|
+
rulingOrdinal: input.rulingOrdinal,
|
|
4061
|
+
policyDigest: policyDigest(policy)
|
|
4062
|
+
};
|
|
4063
|
+
const codeReviewEchoed = {
|
|
4064
|
+
headSha: codeReview.headSha,
|
|
4065
|
+
briefHash: codeReview.briefHash,
|
|
4066
|
+
objectivesVersion: codeReview.objectivesVersion,
|
|
4067
|
+
rulingOrdinal: codeReview.rulingOrdinal,
|
|
4068
|
+
policyDigest: codeReview.policyDigest
|
|
4069
|
+
};
|
|
4070
|
+
const securityEchoed = {
|
|
4071
|
+
headSha: security.headSha,
|
|
4072
|
+
briefHash: security.briefHash,
|
|
4073
|
+
objectivesVersion: security.objectivesVersion,
|
|
4074
|
+
rulingOrdinal: security.rulingOrdinal,
|
|
4075
|
+
policyDigest: security.policyDigest
|
|
4076
|
+
};
|
|
4077
|
+
const codeReviewBinding = compareManifest(codeReviewEchoed, currentManifest, input.patchIdOf);
|
|
4078
|
+
const securityBinding = compareManifest(securityEchoed, currentManifest, input.patchIdOf);
|
|
4079
|
+
const codeReviewBound = codeReviewBinding.head;
|
|
4080
|
+
const securityBound = securityBinding.head;
|
|
4081
|
+
const codeReviewObjectivesBound = codeReviewBinding.objectivesVersion;
|
|
4082
|
+
const securityObjectivesBound = securityBinding.objectivesVersion;
|
|
4083
|
+
const codeReviewRulingsBound = codeReviewBinding.rulingOrdinal;
|
|
4084
|
+
const securityRulingsBound = securityBinding.rulingOrdinal;
|
|
4085
|
+
if (codeReviewClean && codeReviewBinding.bound && securityClean && securityBinding.bound && mechanicalChecksClean) {
|
|
3719
4086
|
return {
|
|
3720
4087
|
verdict: "pass",
|
|
3721
4088
|
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.`,
|
|
@@ -3723,19 +4090,35 @@ function checkReviewGate(input) {
|
|
|
3723
4090
|
};
|
|
3724
4091
|
}
|
|
3725
4092
|
const problems = [];
|
|
3726
|
-
if (!
|
|
4093
|
+
if (!codeReviewTextClean) {
|
|
3727
4094
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
4095
|
+
} else if (!codeReviewPolicyClean) {
|
|
4096
|
+
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`);
|
|
3728
4097
|
} else if (!codeReviewBound) {
|
|
3729
4098
|
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3730
4099
|
} else if (!codeReviewObjectivesBound) {
|
|
3731
4100
|
problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3732
|
-
}
|
|
3733
|
-
|
|
4101
|
+
} else if (!codeReviewRulingsBound) {
|
|
4102
|
+
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`);
|
|
4103
|
+
} else if (!codeReviewBinding.briefHash) {
|
|
4104
|
+
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"}`);
|
|
4105
|
+
} else if (!codeReviewBinding.policyDigest) {
|
|
4106
|
+
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}`);
|
|
4107
|
+
}
|
|
4108
|
+
if (!securityTextClean) {
|
|
3734
4109
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
4110
|
+
} else if (!securityPolicyClean) {
|
|
4111
|
+
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`);
|
|
3735
4112
|
} else if (!securityBound) {
|
|
3736
4113
|
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3737
4114
|
} else if (!securityObjectivesBound) {
|
|
3738
4115
|
problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
4116
|
+
} else if (!securityRulingsBound) {
|
|
4117
|
+
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`);
|
|
4118
|
+
} else if (!securityBinding.briefHash) {
|
|
4119
|
+
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"}`);
|
|
4120
|
+
} else if (!securityBinding.policyDigest) {
|
|
4121
|
+
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}`);
|
|
3739
4122
|
}
|
|
3740
4123
|
if (!mechanicalChecksClean) {
|
|
3741
4124
|
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(", ")}`);
|
|
@@ -3752,6 +4135,24 @@ var DEFAULT_RELEASE_ACTOR = "github-actions[bot]";
|
|
|
3752
4135
|
function isChangesetsReleasePr(branch, author, expectedAuthor) {
|
|
3753
4136
|
return branch === CHANGESET_RELEASE_BRANCH && author === expectedAuthor;
|
|
3754
4137
|
}
|
|
4138
|
+
// ../../packages/aeg-core/src/ruling-ordinal.ts
|
|
4139
|
+
var RULING_MARKER_ORDINAL = /^<!-- aeg:principal:ruling:\d+-(\d+) -->$/;
|
|
4140
|
+
function newestPrincipalRulingOrdinal(comments, allowlist) {
|
|
4141
|
+
let best = 0;
|
|
4142
|
+
for (const c of comments) {
|
|
4143
|
+
if (!isPrincipal(c.author, allowlist))
|
|
4144
|
+
continue;
|
|
4145
|
+
const firstLine = (c.body.split(`
|
|
4146
|
+
`)[0] ?? "").trim();
|
|
4147
|
+
const m = RULING_MARKER_ORDINAL.exec(firstLine);
|
|
4148
|
+
if (!m)
|
|
4149
|
+
continue;
|
|
4150
|
+
const k = Number.parseInt(m[1], 10);
|
|
4151
|
+
if (k > best)
|
|
4152
|
+
best = k;
|
|
4153
|
+
}
|
|
4154
|
+
return best;
|
|
4155
|
+
}
|
|
3755
4156
|
// ../../packages/aeg-core/src/markdown-table.ts
|
|
3756
4157
|
function splitRow(line) {
|
|
3757
4158
|
const trimmed = line.trim().replace(/^\|/, "").replace(/\|$/, "");
|
|
@@ -4409,42 +4810,45 @@ function checkDispatchReadiness(input) {
|
|
|
4409
4810
|
const { trancheSlug, task } = input;
|
|
4410
4811
|
const taskLabel = `task ${task.id} (tranche ${trancheSlug})`;
|
|
4411
4812
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
4412
|
-
const
|
|
4813
|
+
const blockerDetails = [];
|
|
4814
|
+
const push = (blockerClass, message) => {
|
|
4815
|
+
blockerDetails.push({ class: blockerClass, message });
|
|
4816
|
+
};
|
|
4413
4817
|
if (task.issue === null) {
|
|
4414
|
-
|
|
4818
|
+
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.`);
|
|
4415
4819
|
} else if (input.issue === null) {
|
|
4416
|
-
|
|
4820
|
+
push("issue-existence", `dispatch-gate issue-existence: ${taskLabel} names Issue #${task.issue}, but it does not resolve to a real GitHub Issue (phantom reference).`);
|
|
4417
4821
|
}
|
|
4418
4822
|
if (input.issue !== null && !input.issueRationalePass) {
|
|
4419
|
-
|
|
4823
|
+
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.`);
|
|
4420
4824
|
}
|
|
4421
4825
|
for (const dep of input.dependsOn) {
|
|
4422
4826
|
if (isSelfDependency(dep, task, input.issue)) {
|
|
4423
4827
|
const issueStr = input.issue !== null ? `#${input.issue.number}` : "?";
|
|
4424
|
-
|
|
4828
|
+
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.`);
|
|
4425
4829
|
continue;
|
|
4426
4830
|
}
|
|
4427
4831
|
if (dep.resolved === false) {
|
|
4428
|
-
|
|
4832
|
+
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.`);
|
|
4429
4833
|
continue;
|
|
4430
4834
|
}
|
|
4431
4835
|
if (!dep.merged && !isHandClosedByRecognizedPrincipal(dep, principalAllowlist)) {
|
|
4432
4836
|
const issueStr = dep.issue !== null ? ` (#${dep.issue})` : "";
|
|
4433
|
-
|
|
4837
|
+
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.`);
|
|
4434
4838
|
}
|
|
4435
4839
|
}
|
|
4436
4840
|
for (const c of input.conflictsWith) {
|
|
4437
4841
|
if (c.openOrInFlight) {
|
|
4438
4842
|
const issueStr = c.issue !== null ? ` (#${c.issue})` : "";
|
|
4439
|
-
|
|
4843
|
+
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.`);
|
|
4440
4844
|
}
|
|
4441
4845
|
}
|
|
4442
4846
|
for (const proj of input.priorTrancheArchival) {
|
|
4443
4847
|
if (proj.priorTrancheSlug !== null && !proj.archived) {
|
|
4444
|
-
|
|
4848
|
+
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.`);
|
|
4445
4849
|
}
|
|
4446
4850
|
}
|
|
4447
|
-
return { ready:
|
|
4851
|
+
return { ready: blockerDetails.length === 0, blockers: blockerDetails.map((b) => b.message), blockerDetails };
|
|
4448
4852
|
}
|
|
4449
4853
|
// ../../packages/aeg-core/src/single-plan-pr.ts
|
|
4450
4854
|
function trancheSlugFromTopologyPath(path) {
|
|
@@ -4546,6 +4950,91 @@ function checkBranchTopology(input) {
|
|
|
4546
4950
|
reason: `Branch \`${branch}\` matches topology row \`${taskId}\` in ${topoPath}.`
|
|
4547
4951
|
};
|
|
4548
4952
|
}
|
|
4953
|
+
// ../../packages/aeg-core/src/task-tools.ts
|
|
4954
|
+
import { z } from "zod";
|
|
4955
|
+
var TASK_TOOL_ERROR_KINDS = [
|
|
4956
|
+
"validation",
|
|
4957
|
+
"authority",
|
|
4958
|
+
"precondition",
|
|
4959
|
+
"capability",
|
|
4960
|
+
"infrastructure",
|
|
4961
|
+
"cancellation",
|
|
4962
|
+
"timeout",
|
|
4963
|
+
"uncertain_effect"
|
|
4964
|
+
];
|
|
4965
|
+
var TaskToolErrorSchema = z.object({
|
|
4966
|
+
kind: z.enum(TASK_TOOL_ERROR_KINDS),
|
|
4967
|
+
message: z.string().min(1),
|
|
4968
|
+
detail: z.string().optional()
|
|
4969
|
+
});
|
|
4970
|
+
var TaskToolRefSchema = z.union([
|
|
4971
|
+
z.object({ tranche: z.string().min(1), id: z.string().min(1) }),
|
|
4972
|
+
z.object({ issue: z.number().int().positive() })
|
|
4973
|
+
]);
|
|
4974
|
+
var MAX_PAGE_LIMIT = 100;
|
|
4975
|
+
var PageRequestSchema = z.object({
|
|
4976
|
+
cursor: z.string().optional(),
|
|
4977
|
+
limit: z.number().int().positive().max(MAX_PAGE_LIMIT).optional()
|
|
4978
|
+
});
|
|
4979
|
+
var FreshnessSchema = z.enum(["fresh", "stale", "unknown"]);
|
|
4980
|
+
var ObservedSchema = z.object({
|
|
4981
|
+
observedAt: z.string(),
|
|
4982
|
+
freshness: FreshnessSchema
|
|
4983
|
+
});
|
|
4984
|
+
var TaskStatusInputSchema = z.object({
|
|
4985
|
+
task: TaskToolRefSchema.optional()
|
|
4986
|
+
}).merge(PageRequestSchema);
|
|
4987
|
+
var TaskStatusItemSchema = z.object({
|
|
4988
|
+
task: TaskToolRefSchema,
|
|
4989
|
+
issue: z.number().int().positive(),
|
|
4990
|
+
pr: z.number().int().positive().nullable(),
|
|
4991
|
+
state: z.string()
|
|
4992
|
+
}).merge(ObservedSchema);
|
|
4993
|
+
var TaskStatusResultSchema = z.object({
|
|
4994
|
+
items: z.array(TaskStatusItemSchema),
|
|
4995
|
+
nextCursor: z.string().nullable()
|
|
4996
|
+
});
|
|
4997
|
+
var RequestedAuthoritySchema = z.enum(["planner", "principal", "operator", "self"]);
|
|
4998
|
+
var EscalationInputsSchema = z.object({
|
|
4999
|
+
task: z.number().int().positive(),
|
|
5000
|
+
round: z.number().int().nonnegative(),
|
|
5001
|
+
head: z.string(),
|
|
5002
|
+
branch: z.string(),
|
|
5003
|
+
prNumber: z.number().int().positive()
|
|
5004
|
+
});
|
|
5005
|
+
var EscalationEvidenceSchema = z.object({
|
|
5006
|
+
round: z.number().int().nonnegative(),
|
|
5007
|
+
reviewer: z.string().nullable(),
|
|
5008
|
+
security: z.string().nullable()
|
|
5009
|
+
});
|
|
5010
|
+
var TaskEscalationReadInputSchema = z.object({
|
|
5011
|
+
task: TaskToolRefSchema
|
|
5012
|
+
}).merge(PageRequestSchema);
|
|
5013
|
+
var TaskEscalationPacketSchema = z.object({
|
|
5014
|
+
reason: z.string(),
|
|
5015
|
+
detail: z.string().nullable(),
|
|
5016
|
+
inputs: EscalationInputsSchema.nullable(),
|
|
5017
|
+
evidence: EscalationEvidenceSchema.nullable(),
|
|
5018
|
+
attemptedRecovery: z.string(),
|
|
5019
|
+
requestedAuthority: RequestedAuthoritySchema,
|
|
5020
|
+
permittedNextActions: z.array(z.string())
|
|
5021
|
+
}).merge(ObservedSchema);
|
|
5022
|
+
var TaskEscalationReadResultSchema = z.object({
|
|
5023
|
+
items: z.array(TaskEscalationPacketSchema),
|
|
5024
|
+
nextCursor: z.string().nullable()
|
|
5025
|
+
}).merge(ObservedSchema);
|
|
5026
|
+
var TaskStartInputSchema = z.object({
|
|
5027
|
+
tranche: z.string().min(1),
|
|
5028
|
+
id: z.string().min(1)
|
|
5029
|
+
});
|
|
5030
|
+
var TaskResumeInputSchema = z.object({
|
|
5031
|
+
task: TaskToolRefSchema
|
|
5032
|
+
});
|
|
5033
|
+
var TaskCancelInputSchema = z.object({
|
|
5034
|
+
task: TaskToolRefSchema,
|
|
5035
|
+
reason: z.string().min(1)
|
|
5036
|
+
});
|
|
5037
|
+
var NoResultSchema = z.never();
|
|
4549
5038
|
// ../../packages/aeg-core/src/first-push-dispatch-gate.ts
|
|
4550
5039
|
function parseTaskBranch(branch) {
|
|
4551
5040
|
const m = /^task\/([^/]+)\/([^/]+)$/.exec(branch);
|
|
@@ -4629,7 +5118,6 @@ function decideIssueAssignment(input) {
|
|
|
4629
5118
|
};
|
|
4630
5119
|
}
|
|
4631
5120
|
// ../../packages/aeg-core/src/test-plan-gate.ts
|
|
4632
|
-
var TASK_BRANCH_PATTERN2 = /^task\/[^/]+\/[^/]+$/;
|
|
4633
5121
|
function evaluateTestPlanGate(body, branch) {
|
|
4634
5122
|
if (!body) {
|
|
4635
5123
|
return {
|
|
@@ -4642,7 +5130,7 @@ function evaluateTestPlanGate(body, branch) {
|
|
|
4642
5130
|
}
|
|
4643
5131
|
const located = locateTestPlanSection(body);
|
|
4644
5132
|
if (!located.found) {
|
|
4645
|
-
if (
|
|
5133
|
+
if (parseTaskBranchIdentity(branch) !== null) {
|
|
4646
5134
|
return {
|
|
4647
5135
|
verdict: "fail",
|
|
4648
5136
|
messages: [
|
|
@@ -4770,7 +5258,7 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
|
|
|
4770
5258
|
return findings;
|
|
4771
5259
|
}
|
|
4772
5260
|
// ../../packages/aeg-core/src/log/schema.ts
|
|
4773
|
-
import { z } from "zod";
|
|
5261
|
+
import { z as z2 } from "zod";
|
|
4774
5262
|
var ROLE_VALUES = [
|
|
4775
5263
|
"planner",
|
|
4776
5264
|
"developer",
|
|
@@ -4780,136 +5268,172 @@ var ROLE_VALUES = [
|
|
|
4780
5268
|
"archivist",
|
|
4781
5269
|
"architect"
|
|
4782
5270
|
];
|
|
4783
|
-
var RoleSchema =
|
|
5271
|
+
var RoleSchema = z2.enum(ROLE_VALUES);
|
|
4784
5272
|
var HOST_VALUES = ["hook", "ci", "cli", "loop"];
|
|
4785
|
-
var HostSchema =
|
|
5273
|
+
var HostSchema = z2.enum(HOST_VALUES);
|
|
4786
5274
|
var RUN_ID_PATTERN = /^[A-Za-z0-9_.-]{1,128}$/;
|
|
4787
|
-
var
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
doctrine: z.string(),
|
|
5275
|
+
var headerMetaCore = {
|
|
5276
|
+
ts: z2.string(),
|
|
5277
|
+
run_id: z2.string().regex(RUN_ID_PATTERN),
|
|
5278
|
+
seq: z2.number().int().nonnegative(),
|
|
5279
|
+
repo: z2.string().nullable(),
|
|
5280
|
+
vinaya: z2.string(),
|
|
5281
|
+
doctrine: z2.string(),
|
|
4795
5282
|
host: HostSchema,
|
|
4796
|
-
machine:
|
|
5283
|
+
machine: z2.string()
|
|
5284
|
+
};
|
|
5285
|
+
var HeaderMetaV1Schema = z2.object({
|
|
5286
|
+
schema: z2.literal(1),
|
|
5287
|
+
...headerMetaCore
|
|
5288
|
+
}).strict();
|
|
5289
|
+
var LineageSchema = z2.object({
|
|
5290
|
+
run: z2.string().nullable(),
|
|
5291
|
+
attempt: z2.number().int().nullable(),
|
|
5292
|
+
parent: z2.string().nullable()
|
|
5293
|
+
}).strict();
|
|
5294
|
+
var InputVersionsSchema = z2.object({
|
|
5295
|
+
objectives_version: z2.string().nullable(),
|
|
5296
|
+
brief_hash: z2.string().nullable(),
|
|
5297
|
+
ruling_ordinal: z2.number().int().nullable(),
|
|
5298
|
+
policy_digest: z2.string().nullable()
|
|
5299
|
+
}).strict();
|
|
5300
|
+
var ProvenanceSchema = z2.enum(["parent_attributed", "env_correlated", "self_reported", "unavailable"]);
|
|
5301
|
+
var HeaderMetaV2Schema = z2.object({
|
|
5302
|
+
schema: z2.literal(2),
|
|
5303
|
+
...headerMetaCore,
|
|
5304
|
+
event_id: z2.string().min(1),
|
|
5305
|
+
process_id: z2.string().min(1),
|
|
5306
|
+
actor_id: z2.string().nullable(),
|
|
5307
|
+
lineage: LineageSchema,
|
|
5308
|
+
input_versions: InputVersionsSchema,
|
|
5309
|
+
provenance: ProvenanceSchema
|
|
4797
5310
|
}).strict();
|
|
4798
|
-
var
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
5311
|
+
var HeaderMetaSchema = z2.discriminatedUnion("schema", [HeaderMetaV1Schema, HeaderMetaV2Schema]);
|
|
5312
|
+
var SubjectSchema = z2.object({
|
|
5313
|
+
issue: z2.number().int().nullable(),
|
|
5314
|
+
pr: z2.number().int().optional(),
|
|
5315
|
+
sha: z2.string().optional(),
|
|
5316
|
+
role: z2.union([RoleSchema, z2.literal("unattributed")]),
|
|
5317
|
+
round: z2.number().int().optional(),
|
|
5318
|
+
objectives_version: z2.string().optional()
|
|
4805
5319
|
}).strict();
|
|
4806
|
-
var HeaderSchema =
|
|
5320
|
+
var HeaderSchema = z2.object({
|
|
4807
5321
|
meta: HeaderMetaSchema,
|
|
4808
5322
|
subject: SubjectSchema
|
|
4809
5323
|
}).strict();
|
|
4810
5324
|
var envelopeTail = {
|
|
4811
|
-
duration_ms:
|
|
4812
|
-
payload:
|
|
5325
|
+
duration_ms: z2.number().nonnegative().optional(),
|
|
5326
|
+
payload: z2.object({}).strict()
|
|
4813
5327
|
};
|
|
4814
|
-
var
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
5328
|
+
var ReviewFindingSchema = z2.object({
|
|
5329
|
+
id: z2.string(),
|
|
5330
|
+
severity: z2.string(),
|
|
5331
|
+
state: z2.string().optional(),
|
|
5332
|
+
severity_scale: z2.string().optional(),
|
|
5333
|
+
policy_treatment: z2.enum(["blocking", "non_blocking", "unavailable"]).optional(),
|
|
5334
|
+
confidence: z2.number().min(0).max(1).optional(),
|
|
5335
|
+
confidence_scale: z2.string().optional(),
|
|
5336
|
+
confidence_source: z2.string().optional()
|
|
5337
|
+
}).strict();
|
|
5338
|
+
var DispatchOutcomeSchema = z2.discriminatedUnion("type", [
|
|
5339
|
+
z2.object({ type: z2.literal("pr_opened"), pr: z2.number().int(), head: z2.string() }).strict(),
|
|
5340
|
+
z2.object({
|
|
5341
|
+
type: z2.literal("round_pushed"),
|
|
5342
|
+
pr: z2.number().int(),
|
|
5343
|
+
head: z2.string(),
|
|
5344
|
+
comment_id: z2.number().int()
|
|
4821
5345
|
}).strict(),
|
|
4822
|
-
|
|
4823
|
-
type:
|
|
4824
|
-
verdict:
|
|
4825
|
-
head:
|
|
4826
|
-
comment_id:
|
|
4827
|
-
objectives:
|
|
4828
|
-
findings:
|
|
5346
|
+
z2.object({
|
|
5347
|
+
type: z2.literal("verdict"),
|
|
5348
|
+
verdict: z2.enum(["APPROVE", "REQUEST CHANGES", "PASS", "FAIL"]),
|
|
5349
|
+
head: z2.string(),
|
|
5350
|
+
comment_id: z2.number().int(),
|
|
5351
|
+
objectives: z2.array(z2.object({ id: z2.string(), met: z2.boolean() }).strict()),
|
|
5352
|
+
findings: z2.array(ReviewFindingSchema)
|
|
4829
5353
|
}).strict(),
|
|
4830
|
-
|
|
4831
|
-
type:
|
|
4832
|
-
class:
|
|
4833
|
-
comment_id:
|
|
5354
|
+
z2.object({
|
|
5355
|
+
type: z2.literal("escalation"),
|
|
5356
|
+
class: z2.enum(["authority", "strategy", "product"]),
|
|
5357
|
+
comment_id: z2.number().int()
|
|
4834
5358
|
}).strict(),
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
5359
|
+
z2.object({ type: z2.literal("brief"), comment_id: z2.number().int(), hash: z2.string() }).strict(),
|
|
5360
|
+
z2.object({ type: z2.literal("plan"), issues: z2.array(z2.number().int()) }).strict(),
|
|
5361
|
+
z2.object({ type: z2.literal("archive"), provenance_comment_id: z2.number().int() }).strict()
|
|
4838
5362
|
]);
|
|
4839
5363
|
var dispatchShared = {
|
|
4840
5364
|
meta: HeaderMetaSchema,
|
|
4841
5365
|
subject: SubjectSchema,
|
|
4842
|
-
kind:
|
|
5366
|
+
kind: z2.literal("dispatch"),
|
|
4843
5367
|
...envelopeTail,
|
|
4844
5368
|
target_role: RoleSchema,
|
|
4845
|
-
model:
|
|
4846
|
-
round:
|
|
4847
|
-
effect_id:
|
|
5369
|
+
model: z2.string(),
|
|
5370
|
+
round: z2.number().int().optional(),
|
|
5371
|
+
effect_id: z2.string()
|
|
4848
5372
|
};
|
|
4849
|
-
var dispatchUsageField =
|
|
4850
|
-
var DispatchEventSchema =
|
|
4851
|
-
|
|
4852
|
-
|
|
5373
|
+
var dispatchUsageField = z2.object({ input: z2.number().nonnegative(), output: z2.number().nonnegative() }).strict().nullable();
|
|
5374
|
+
var DispatchEventSchema = z2.discriminatedUnion("event", [
|
|
5375
|
+
z2.object({ ...dispatchShared, event: z2.literal("dispatched"), prompt_hash: z2.string() }).strict(),
|
|
5376
|
+
z2.object({
|
|
4853
5377
|
...dispatchShared,
|
|
4854
|
-
event:
|
|
5378
|
+
event: z2.literal("outcome_received"),
|
|
4855
5379
|
outcome: DispatchOutcomeSchema,
|
|
4856
5380
|
usage: dispatchUsageField
|
|
4857
5381
|
}).strict(),
|
|
4858
|
-
|
|
5382
|
+
z2.object({
|
|
4859
5383
|
...dispatchShared,
|
|
4860
|
-
event:
|
|
4861
|
-
reason:
|
|
5384
|
+
event: z2.literal("dispatch_failed"),
|
|
5385
|
+
reason: z2.enum(["timeout", "crash", "refused", "unattributed_write"]),
|
|
4862
5386
|
usage: dispatchUsageField
|
|
4863
5387
|
}).strict()
|
|
4864
5388
|
]);
|
|
4865
5389
|
var loopShared = {
|
|
4866
5390
|
meta: HeaderMetaSchema,
|
|
4867
5391
|
subject: SubjectSchema,
|
|
4868
|
-
kind:
|
|
5392
|
+
kind: z2.literal("dev_review_loop"),
|
|
4869
5393
|
...envelopeTail,
|
|
4870
|
-
loop_id:
|
|
5394
|
+
loop_id: z2.string()
|
|
4871
5395
|
};
|
|
4872
|
-
var DevReviewLoopEventSchema =
|
|
4873
|
-
|
|
5396
|
+
var DevReviewLoopEventSchema = z2.discriminatedUnion("event", [
|
|
5397
|
+
z2.object({
|
|
4874
5398
|
...loopShared,
|
|
4875
|
-
event:
|
|
4876
|
-
task:
|
|
4877
|
-
policy:
|
|
4878
|
-
max_rounds:
|
|
4879
|
-
reviewers:
|
|
4880
|
-
models:
|
|
5399
|
+
event: z2.literal("loop_started"),
|
|
5400
|
+
task: z2.number().int(),
|
|
5401
|
+
policy: z2.object({
|
|
5402
|
+
max_rounds: z2.number().int().nonnegative(),
|
|
5403
|
+
reviewers: z2.array(RoleSchema),
|
|
5404
|
+
models: z2.record(RoleSchema, z2.string())
|
|
4881
5405
|
}).strict()
|
|
4882
5406
|
}).strict(),
|
|
4883
|
-
|
|
4884
|
-
|
|
5407
|
+
z2.object({ ...loopShared, event: z2.literal("round_started"), round: z2.number().int(), base_head: z2.string() }).strict(),
|
|
5408
|
+
z2.object({
|
|
4885
5409
|
...loopShared,
|
|
4886
|
-
event:
|
|
4887
|
-
round:
|
|
4888
|
-
head:
|
|
4889
|
-
green:
|
|
5410
|
+
event: z2.literal("gate_result_read"),
|
|
5411
|
+
round: z2.number().int(),
|
|
5412
|
+
head: z2.string(),
|
|
5413
|
+
green: z2.boolean()
|
|
4890
5414
|
}).strict(),
|
|
4891
|
-
|
|
5415
|
+
z2.object({
|
|
4892
5416
|
...loopShared,
|
|
4893
|
-
event:
|
|
4894
|
-
round:
|
|
4895
|
-
head:
|
|
4896
|
-
all_approve:
|
|
4897
|
-
blockers:
|
|
5417
|
+
event: z2.literal("verdicts_read"),
|
|
5418
|
+
round: z2.number().int(),
|
|
5419
|
+
head: z2.string(),
|
|
5420
|
+
all_approve: z2.boolean(),
|
|
5421
|
+
blockers: z2.number().int().nonnegative()
|
|
4898
5422
|
}).strict(),
|
|
4899
|
-
|
|
5423
|
+
z2.object({
|
|
4900
5424
|
...loopShared,
|
|
4901
|
-
event:
|
|
4902
|
-
round:
|
|
4903
|
-
open:
|
|
4904
|
-
resolved:
|
|
4905
|
-
new:
|
|
4906
|
-
recurring:
|
|
5425
|
+
event: z2.literal("findings_compared"),
|
|
5426
|
+
round: z2.number().int(),
|
|
5427
|
+
open: z2.array(z2.string()),
|
|
5428
|
+
resolved: z2.array(z2.string()),
|
|
5429
|
+
new: z2.array(z2.string()),
|
|
5430
|
+
recurring: z2.array(z2.string())
|
|
4907
5431
|
}).strict(),
|
|
4908
|
-
|
|
5432
|
+
z2.object({
|
|
4909
5433
|
...loopShared,
|
|
4910
|
-
event:
|
|
4911
|
-
round:
|
|
4912
|
-
condition:
|
|
5434
|
+
event: z2.literal("stop_condition_met"),
|
|
5435
|
+
round: z2.number().int(),
|
|
5436
|
+
condition: z2.enum([
|
|
4913
5437
|
"green",
|
|
4914
5438
|
"max_rounds",
|
|
4915
5439
|
"no_progress",
|
|
@@ -4919,42 +5443,49 @@ var DevReviewLoopEventSchema = z.discriminatedUnion("event", [
|
|
|
4919
5443
|
"reappearance"
|
|
4920
5444
|
])
|
|
4921
5445
|
}).strict(),
|
|
4922
|
-
|
|
5446
|
+
z2.object({
|
|
5447
|
+
...loopShared,
|
|
5448
|
+
event: z2.literal("paused"),
|
|
5449
|
+
round: z2.number().int(),
|
|
5450
|
+
reason: z2.enum(["escalation", "principal_item", "refreeze_needed"])
|
|
5451
|
+
}).strict(),
|
|
5452
|
+
z2.object({
|
|
4923
5453
|
...loopShared,
|
|
4924
|
-
event:
|
|
4925
|
-
round:
|
|
4926
|
-
|
|
5454
|
+
event: z2.literal("resumed"),
|
|
5455
|
+
round: z2.number().int(),
|
|
5456
|
+
by: z2.literal("principal")
|
|
4927
5457
|
}).strict(),
|
|
4928
|
-
|
|
5458
|
+
z2.object({
|
|
4929
5459
|
...loopShared,
|
|
4930
|
-
event:
|
|
4931
|
-
round:
|
|
4932
|
-
|
|
5460
|
+
event: z2.literal("unpushed_work_resume"),
|
|
5461
|
+
round: z2.number().int(),
|
|
5462
|
+
branch: z2.string(),
|
|
5463
|
+
detail: z2.string()
|
|
4933
5464
|
}).strict(),
|
|
4934
|
-
|
|
5465
|
+
z2.object({
|
|
4935
5466
|
...loopShared,
|
|
4936
|
-
event:
|
|
4937
|
-
round:
|
|
4938
|
-
base_head:
|
|
4939
|
-
head:
|
|
4940
|
-
files_changed:
|
|
4941
|
-
insertions:
|
|
4942
|
-
deletions:
|
|
4943
|
-
wall_ms:
|
|
4944
|
-
outcome:
|
|
5467
|
+
event: z2.literal("round_ended"),
|
|
5468
|
+
round: z2.number().int(),
|
|
5469
|
+
base_head: z2.string(),
|
|
5470
|
+
head: z2.string(),
|
|
5471
|
+
files_changed: z2.number().int().nonnegative(),
|
|
5472
|
+
insertions: z2.number().int().nonnegative(),
|
|
5473
|
+
deletions: z2.number().int().nonnegative(),
|
|
5474
|
+
wall_ms: z2.number().nonnegative(),
|
|
5475
|
+
outcome: z2.enum(["green", "changes_requested", "escalated"])
|
|
4945
5476
|
}).strict(),
|
|
4946
|
-
|
|
5477
|
+
z2.object({
|
|
4947
5478
|
...loopShared,
|
|
4948
|
-
event:
|
|
4949
|
-
rounds:
|
|
4950
|
-
total_wall_ms:
|
|
4951
|
-
time_to_green_ms:
|
|
4952
|
-
files_changed_total:
|
|
4953
|
-
final_head:
|
|
4954
|
-
result:
|
|
5479
|
+
event: z2.literal("journal_finalized"),
|
|
5480
|
+
rounds: z2.number().int().nonnegative(),
|
|
5481
|
+
total_wall_ms: z2.number().nonnegative(),
|
|
5482
|
+
time_to_green_ms: z2.number().nonnegative().nullable(),
|
|
5483
|
+
files_changed_total: z2.number().int().nonnegative(),
|
|
5484
|
+
final_head: z2.string(),
|
|
5485
|
+
result: z2.enum(["merged_ready", "stopped"])
|
|
4955
5486
|
}).strict()
|
|
4956
5487
|
]);
|
|
4957
|
-
var ForgeOpSchema =
|
|
5488
|
+
var ForgeOpSchema = z2.enum([
|
|
4958
5489
|
"pr.create",
|
|
4959
5490
|
"pr.comment",
|
|
4960
5491
|
"pr.body.replace",
|
|
@@ -4968,50 +5499,238 @@ var ForgeOpSchema = z.enum([
|
|
|
4968
5499
|
"label.add",
|
|
4969
5500
|
"label.remove"
|
|
4970
5501
|
]);
|
|
4971
|
-
var ForgeWriteTargetSchema =
|
|
4972
|
-
issue:
|
|
4973
|
-
pr:
|
|
5502
|
+
var ForgeWriteTargetSchema = z2.object({
|
|
5503
|
+
issue: z2.number().int().optional(),
|
|
5504
|
+
pr: z2.number().int().optional()
|
|
4974
5505
|
}).strict();
|
|
4975
5506
|
var forgeWriteShared = {
|
|
4976
5507
|
meta: HeaderMetaSchema,
|
|
4977
5508
|
subject: SubjectSchema,
|
|
4978
|
-
kind:
|
|
5509
|
+
kind: z2.literal("forge_write"),
|
|
4979
5510
|
...envelopeTail,
|
|
4980
5511
|
op: ForgeOpSchema,
|
|
4981
5512
|
target: ForgeWriteTargetSchema
|
|
4982
5513
|
};
|
|
4983
|
-
var ForgeWriteEventSchema =
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
5514
|
+
var ForgeWriteEventSchema = z2.discriminatedUnion("event", [
|
|
5515
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("validated") }).strict(),
|
|
5516
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("refused"), reason: z2.string() }).strict(),
|
|
5517
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("written"), comment_ids: z2.array(z2.string()) }).strict()
|
|
5518
|
+
]);
|
|
5519
|
+
var GateOutcomeSchema = z2.enum([
|
|
5520
|
+
"pass",
|
|
5521
|
+
"fail",
|
|
5522
|
+
"wait",
|
|
5523
|
+
"skip",
|
|
5524
|
+
"invalid_input",
|
|
5525
|
+
"unavailable_dependency",
|
|
5526
|
+
"timeout",
|
|
5527
|
+
"cancelled"
|
|
5528
|
+
]);
|
|
5529
|
+
var gateShared = {
|
|
5530
|
+
meta: HeaderMetaSchema,
|
|
5531
|
+
subject: SubjectSchema,
|
|
5532
|
+
kind: z2.literal("gate"),
|
|
5533
|
+
...envelopeTail,
|
|
5534
|
+
check: z2.string(),
|
|
5535
|
+
check_version: z2.string().nullable(),
|
|
5536
|
+
policy_version: z2.string().nullable(),
|
|
5537
|
+
input_fingerprint: z2.string().nullable()
|
|
5538
|
+
};
|
|
5539
|
+
var GateEventSchema = z2.discriminatedUnion("event", [
|
|
5540
|
+
z2.object({
|
|
5541
|
+
...gateShared,
|
|
5542
|
+
event: z2.literal("checked"),
|
|
5543
|
+
outcome: GateOutcomeSchema,
|
|
5544
|
+
reason: z2.string().optional()
|
|
5545
|
+
}).strict()
|
|
5546
|
+
]);
|
|
5547
|
+
var OperationResultSchema = z2.enum(["ok", "error", "refused", "timeout", "cancelled", "unavailable"]);
|
|
5548
|
+
var operationShared = {
|
|
5549
|
+
meta: HeaderMetaSchema,
|
|
5550
|
+
subject: SubjectSchema,
|
|
5551
|
+
kind: z2.literal("operation"),
|
|
5552
|
+
...envelopeTail,
|
|
5553
|
+
operation: z2.string(),
|
|
5554
|
+
target: z2.string().nullable()
|
|
5555
|
+
};
|
|
5556
|
+
var OperationEventSchema = z2.discriminatedUnion("event", [
|
|
5557
|
+
z2.object({
|
|
5558
|
+
...operationShared,
|
|
5559
|
+
event: z2.literal("completed"),
|
|
5560
|
+
result: OperationResultSchema,
|
|
5561
|
+
error_class: z2.string().nullable()
|
|
5562
|
+
}).strict()
|
|
5563
|
+
]);
|
|
5564
|
+
var UsageUnitsSchema = z2.object({
|
|
5565
|
+
input: z2.number().nonnegative().nullable(),
|
|
5566
|
+
output: z2.number().nonnegative().nullable(),
|
|
5567
|
+
cache: z2.number().nonnegative().nullable()
|
|
5568
|
+
}).strict();
|
|
5569
|
+
var usageShared = {
|
|
5570
|
+
meta: HeaderMetaSchema,
|
|
5571
|
+
subject: SubjectSchema,
|
|
5572
|
+
kind: z2.literal("usage"),
|
|
5573
|
+
...envelopeTail,
|
|
5574
|
+
model: z2.string().nullable(),
|
|
5575
|
+
source: z2.string(),
|
|
5576
|
+
semantics: z2.enum(["cumulative", "delta"])
|
|
5577
|
+
};
|
|
5578
|
+
var UsageEventSchema = z2.discriminatedUnion("event", [
|
|
5579
|
+
z2.object({
|
|
5580
|
+
...usageShared,
|
|
5581
|
+
event: z2.literal("observed"),
|
|
5582
|
+
units: UsageUnitsSchema,
|
|
5583
|
+
unknown_reason: z2.string().nullable()
|
|
5584
|
+
}).strict()
|
|
5585
|
+
]);
|
|
5586
|
+
var RoleAttemptOutcomeSchema = z2.enum([
|
|
5587
|
+
"completed",
|
|
5588
|
+
"incomplete",
|
|
5589
|
+
"infrastructure_failed",
|
|
5590
|
+
"cancelled",
|
|
5591
|
+
"timed_out",
|
|
5592
|
+
"capability_refused"
|
|
5593
|
+
]);
|
|
5594
|
+
var roleAttemptShared = {
|
|
5595
|
+
meta: HeaderMetaSchema,
|
|
5596
|
+
subject: SubjectSchema,
|
|
5597
|
+
kind: z2.literal("role_attempt"),
|
|
5598
|
+
...envelopeTail,
|
|
5599
|
+
actor: z2.string().nullable(),
|
|
5600
|
+
attempt: z2.number().int().nullable()
|
|
5601
|
+
};
|
|
5602
|
+
var RoleAttemptEventSchema = z2.discriminatedUnion("event", [
|
|
5603
|
+
z2.object({
|
|
5604
|
+
...roleAttemptShared,
|
|
5605
|
+
event: z2.literal("attempted"),
|
|
5606
|
+
outcome: RoleAttemptOutcomeSchema,
|
|
5607
|
+
usage: dispatchUsageField
|
|
5608
|
+
}).strict()
|
|
5609
|
+
]);
|
|
5610
|
+
var handoffShared = {
|
|
5611
|
+
meta: HeaderMetaSchema,
|
|
5612
|
+
subject: SubjectSchema,
|
|
5613
|
+
kind: z2.literal("handoff"),
|
|
5614
|
+
...envelopeTail,
|
|
5615
|
+
class: z2.enum(["authority", "strategy", "product"]),
|
|
5616
|
+
reason: z2.string()
|
|
5617
|
+
};
|
|
5618
|
+
var HandoffEventSchema = z2.discriminatedUnion("event", [
|
|
5619
|
+
z2.object({
|
|
5620
|
+
...handoffShared,
|
|
5621
|
+
event: z2.literal("raised"),
|
|
5622
|
+
requested_decision: z2.string().nullable()
|
|
5623
|
+
}).strict(),
|
|
5624
|
+
z2.object({
|
|
5625
|
+
...handoffShared,
|
|
5626
|
+
event: z2.literal("resolved"),
|
|
5627
|
+
resolution: z2.string().nullable(),
|
|
5628
|
+
resolved_by: z2.string().nullable()
|
|
5629
|
+
}).strict()
|
|
5630
|
+
]);
|
|
5631
|
+
var EffectTargetSchema = z2.object({
|
|
5632
|
+
kind: z2.string(),
|
|
5633
|
+
ref: z2.string()
|
|
5634
|
+
}).strict();
|
|
5635
|
+
var effectShared = {
|
|
5636
|
+
meta: HeaderMetaSchema,
|
|
5637
|
+
subject: SubjectSchema,
|
|
5638
|
+
kind: z2.literal("effect"),
|
|
5639
|
+
...envelopeTail,
|
|
5640
|
+
effect_id: z2.string(),
|
|
5641
|
+
target: EffectTargetSchema
|
|
5642
|
+
};
|
|
5643
|
+
var EffectEventSchema = z2.discriminatedUnion("event", [
|
|
5644
|
+
z2.object({ ...effectShared, event: z2.literal("attempted") }).strict(),
|
|
5645
|
+
z2.object({ ...effectShared, event: z2.literal("observed"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict(),
|
|
5646
|
+
z2.object({ ...effectShared, event: z2.literal("verified"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict()
|
|
4987
5647
|
]);
|
|
4988
|
-
var LogEventSchema =
|
|
5648
|
+
var LogEventSchema = z2.union([
|
|
5649
|
+
DispatchEventSchema,
|
|
5650
|
+
DevReviewLoopEventSchema,
|
|
5651
|
+
ForgeWriteEventSchema,
|
|
5652
|
+
GateEventSchema,
|
|
5653
|
+
OperationEventSchema,
|
|
5654
|
+
UsageEventSchema,
|
|
5655
|
+
RoleAttemptEventSchema,
|
|
5656
|
+
HandoffEventSchema,
|
|
5657
|
+
EffectEventSchema
|
|
5658
|
+
]);
|
|
5659
|
+
// ../../packages/aeg-core/src/dev-review-loop/journal-reconstruction.ts
|
|
5660
|
+
var STOPPED_CONDITIONS = new Set(["confidence", "reappearance", "no_progress", "max_rounds"]);
|
|
5661
|
+
// ../../packages/aeg-core/src/control-store/records.ts
|
|
5662
|
+
import { z as z3 } from "zod";
|
|
5663
|
+
var isoTimestamp = z3.string().min(1);
|
|
5664
|
+
var taskId = z3.number().int().positive();
|
|
5665
|
+
var epochNumber = z3.number().int().nonnegative();
|
|
5666
|
+
var RunRecordSchema = z3.object({
|
|
5667
|
+
version: z3.literal(1),
|
|
5668
|
+
kind: z3.literal("run"),
|
|
5669
|
+
task: taskId,
|
|
5670
|
+
runId: z3.string().min(1),
|
|
5671
|
+
pid: z3.number().int().positive(),
|
|
5672
|
+
host: z3.string().min(1),
|
|
5673
|
+
startedAt: isoTimestamp
|
|
5674
|
+
}).strict();
|
|
5675
|
+
var InputRecordSchema = z3.object({
|
|
5676
|
+
version: z3.literal(1),
|
|
5677
|
+
kind: z3.literal("input"),
|
|
5678
|
+
task: taskId,
|
|
5679
|
+
runId: z3.string().min(1),
|
|
5680
|
+
source: z3.union([z3.literal("fresh"), z3.literal("resume")]),
|
|
5681
|
+
pr: z3.number().int().positive().nullable(),
|
|
5682
|
+
round: z3.number().int().nonnegative(),
|
|
5683
|
+
recordedAt: isoTimestamp
|
|
5684
|
+
}).strict();
|
|
5685
|
+
var OwnershipRecordSchema = z3.object({
|
|
5686
|
+
version: z3.literal(1),
|
|
5687
|
+
kind: z3.literal("ownership"),
|
|
5688
|
+
task: taskId,
|
|
5689
|
+
epoch: epochNumber,
|
|
5690
|
+
ownerId: z3.string().min(1),
|
|
5691
|
+
pid: z3.number().int().positive(),
|
|
5692
|
+
host: z3.string().min(1),
|
|
5693
|
+
acquiredAt: isoTimestamp
|
|
5694
|
+
}).strict();
|
|
5695
|
+
var TransitionRecordSchema = z3.object({
|
|
5696
|
+
version: z3.literal(1),
|
|
5697
|
+
kind: z3.literal("transition"),
|
|
5698
|
+
task: taskId,
|
|
5699
|
+
epoch: epochNumber,
|
|
5700
|
+
seq: z3.number().int().nonnegative(),
|
|
5701
|
+
from: z3.string().min(1),
|
|
5702
|
+
to: z3.string().min(1),
|
|
5703
|
+
detail: z3.string().optional(),
|
|
5704
|
+
at: isoTimestamp
|
|
5705
|
+
}).strict();
|
|
4989
5706
|
// src/lib/config.ts
|
|
4990
5707
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
4991
5708
|
import { existsSync, mkdirSync, readFileSync as readFileSync2, realpathSync, writeFileSync } from "node:fs";
|
|
4992
5709
|
import { homedir } from "node:os";
|
|
4993
5710
|
import { dirname, join, resolve } from "node:path";
|
|
4994
|
-
import { z as
|
|
5711
|
+
import { z as z4 } from "zod";
|
|
4995
5712
|
|
|
4996
5713
|
// src/lib/agent-vendors.ts
|
|
4997
5714
|
var AGENT_VENDORS = ["skills", "claude", "gemini"];
|
|
4998
5715
|
|
|
4999
5716
|
// src/lib/config.ts
|
|
5000
|
-
var EnvEntrySchema =
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5717
|
+
var EnvEntrySchema = z4.union([
|
|
5718
|
+
z4.literal(true),
|
|
5719
|
+
z4.object({ optional: z4.literal(true) }),
|
|
5720
|
+
z4.object({ anyOf: z4.array(z4.string()).min(2) }),
|
|
5721
|
+
z4.string()
|
|
5005
5722
|
]);
|
|
5006
|
-
var CheckEntrySchema =
|
|
5007
|
-
run:
|
|
5008
|
-
scope:
|
|
5009
|
-
include:
|
|
5010
|
-
args:
|
|
5011
|
-
timeoutMs:
|
|
5012
|
-
env:
|
|
5013
|
-
requiresOpenPr:
|
|
5014
|
-
ownWorkflow:
|
|
5723
|
+
var CheckEntrySchema = z4.object({
|
|
5724
|
+
run: z4.string(),
|
|
5725
|
+
scope: z4.enum(["diff", "full"]),
|
|
5726
|
+
include: z4.array(z4.string()).optional(),
|
|
5727
|
+
args: z4.array(z4.string()).optional(),
|
|
5728
|
+
timeoutMs: z4.number().optional(),
|
|
5729
|
+
env: z4.record(z4.string(), EnvEntrySchema).optional(),
|
|
5730
|
+
requiresOpenPr: z4.boolean().optional(),
|
|
5731
|
+
ownWorkflow: z4.boolean().optional(),
|
|
5732
|
+
principalOwed: z4.literal(true).optional(),
|
|
5733
|
+
validates: z4.enum(["body", "issue"]).optional()
|
|
5015
5734
|
}).superRefine((entry2, ctx) => {
|
|
5016
5735
|
if (!entry2.env)
|
|
5017
5736
|
return;
|
|
@@ -5021,25 +5740,26 @@ var CheckEntrySchema = z2.object({
|
|
|
5021
5740
|
const members = value.anyOf;
|
|
5022
5741
|
if (new Set(members).size !== members.length) {
|
|
5023
5742
|
ctx.addIssue({
|
|
5024
|
-
code:
|
|
5743
|
+
code: z4.ZodIssueCode.custom,
|
|
5025
5744
|
message: `env.${key}.anyOf has duplicate members`,
|
|
5026
5745
|
path: ["env", key, "anyOf"]
|
|
5027
5746
|
});
|
|
5028
5747
|
}
|
|
5029
5748
|
if (!members.includes(key)) {
|
|
5030
5749
|
ctx.addIssue({
|
|
5031
|
-
code:
|
|
5750
|
+
code: z4.ZodIssueCode.custom,
|
|
5032
5751
|
message: `env.${key}.anyOf must include "${key}" itself as a member`,
|
|
5033
5752
|
path: ["env", key, "anyOf"]
|
|
5034
5753
|
});
|
|
5035
5754
|
}
|
|
5036
5755
|
}
|
|
5037
5756
|
});
|
|
5038
|
-
var RoleEntrySchema =
|
|
5039
|
-
contract:
|
|
5757
|
+
var RoleEntrySchema = z4.object({
|
|
5758
|
+
contract: z4.string().refine((p) => p.includes("/"), {
|
|
5040
5759
|
message: 'must be a path (contain at least one "/"), not a bare filename'
|
|
5041
5760
|
})
|
|
5042
5761
|
});
|
|
5762
|
+
var MAX_REPORT_COMMAND_TIMEOUT_MS = 3600000;
|
|
5043
5763
|
var BRIEF_BUILTINS = [
|
|
5044
5764
|
"tier",
|
|
5045
5765
|
"testPlan",
|
|
@@ -5059,17 +5779,17 @@ var BRIEF_BUILTINS = [
|
|
|
5059
5779
|
"briefSections",
|
|
5060
5780
|
"milestoneShape"
|
|
5061
5781
|
];
|
|
5062
|
-
var BriefSectionSchema =
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5782
|
+
var BriefSectionSchema = z4.union([
|
|
5783
|
+
z4.object({ builtin: z4.enum(BRIEF_BUILTINS) }),
|
|
5784
|
+
z4.object({ heading: z4.string().min(1), name: z4.string().optional() }),
|
|
5785
|
+
z4.object({ field: z4.string().min(1), name: z4.string().optional() }),
|
|
5786
|
+
z4.object({ phrase: z4.string().min(1), name: z4.string().optional() })
|
|
5067
5787
|
]);
|
|
5068
|
-
var BriefSchemaSchema =
|
|
5069
|
-
pr:
|
|
5070
|
-
issue:
|
|
5071
|
-
milestone:
|
|
5072
|
-
ack:
|
|
5788
|
+
var BriefSchemaSchema = z4.object({
|
|
5789
|
+
pr: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
|
|
5790
|
+
issue: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
|
|
5791
|
+
milestone: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
|
|
5792
|
+
ack: z4.array(z4.enum(BRIEF_BUILTINS)).optional()
|
|
5073
5793
|
});
|
|
5074
5794
|
function isSafeRepoRelPath(p) {
|
|
5075
5795
|
if (p.length === 0)
|
|
@@ -5078,7 +5798,7 @@ function isSafeRepoRelPath(p) {
|
|
|
5078
5798
|
return false;
|
|
5079
5799
|
return p.split(/[\\/]/).every((seg) => seg !== ".." && seg !== "");
|
|
5080
5800
|
}
|
|
5081
|
-
var SafeRepoRelPath =
|
|
5801
|
+
var SafeRepoRelPath = z4.string().refine(isSafeRepoRelPath, {
|
|
5082
5802
|
message: "must be a repo-root-relative path with no `..` segment or absolute root"
|
|
5083
5803
|
});
|
|
5084
5804
|
var CANONICAL_HOOK_BLOCK_PREFIXES = [".git/", ".husky/", ".vinaya/hooks/", ".claude/hooks/"];
|
|
@@ -5088,22 +5808,22 @@ function isCanonicalHookBlockPath(p) {
|
|
|
5088
5808
|
var ManagedHookBlockPath = SafeRepoRelPath.refine(isCanonicalHookBlockPath, {
|
|
5089
5809
|
message: `must start with one of ${CANONICAL_HOOK_BLOCK_PREFIXES.join(", ")} (byte-exact, case-sensitive)`
|
|
5090
5810
|
});
|
|
5091
|
-
var ManagedBlockRecordSchema =
|
|
5811
|
+
var ManagedBlockRecordSchema = z4.object({
|
|
5092
5812
|
path: ManagedHookBlockPath,
|
|
5093
|
-
marker:
|
|
5094
|
-
comment:
|
|
5813
|
+
marker: z4.string(),
|
|
5814
|
+
comment: z4.enum(["hash", "html"])
|
|
5095
5815
|
});
|
|
5096
|
-
var ManagedManifestSchema =
|
|
5097
|
-
version:
|
|
5098
|
-
files:
|
|
5099
|
-
blocks:
|
|
5100
|
-
labels:
|
|
5101
|
-
agents:
|
|
5816
|
+
var ManagedManifestSchema = z4.object({
|
|
5817
|
+
version: z4.number().int().positive(),
|
|
5818
|
+
files: z4.array(SafeRepoRelPath),
|
|
5819
|
+
blocks: z4.array(ManagedBlockRecordSchema),
|
|
5820
|
+
labels: z4.array(z4.string()),
|
|
5821
|
+
agents: z4.array(z4.enum(AGENT_VENDORS)).optional()
|
|
5102
5822
|
});
|
|
5103
|
-
var ProjectEntrySchema =
|
|
5104
|
-
name:
|
|
5105
|
-
description:
|
|
5106
|
-
path:
|
|
5823
|
+
var ProjectEntrySchema = z4.object({
|
|
5824
|
+
name: z4.string().min(1),
|
|
5825
|
+
description: z4.string().min(1).optional(),
|
|
5826
|
+
path: z4.string().min(1).optional()
|
|
5107
5827
|
});
|
|
5108
5828
|
function parseTokensCollectDeclaration(value) {
|
|
5109
5829
|
const m = value.trim().match(/^(\S+)\s+(\S+)$/);
|
|
@@ -5114,34 +5834,55 @@ function parseTokensCollectDeclaration(value) {
|
|
|
5114
5834
|
return null;
|
|
5115
5835
|
return { interpreter, script };
|
|
5116
5836
|
}
|
|
5117
|
-
var VinayaConfigSchema =
|
|
5118
|
-
rings:
|
|
5119
|
-
ring1_forgeWriteInterception:
|
|
5120
|
-
ring2_asyncAudits:
|
|
5837
|
+
var VinayaConfigSchema = z4.object({
|
|
5838
|
+
rings: z4.object({
|
|
5839
|
+
ring1_forgeWriteInterception: z4.boolean(),
|
|
5840
|
+
ring2_asyncAudits: z4.boolean()
|
|
5121
5841
|
}).optional(),
|
|
5122
|
-
checks:
|
|
5123
|
-
roles:
|
|
5842
|
+
checks: z4.record(z4.string(), CheckEntrySchema).optional(),
|
|
5843
|
+
roles: z4.record(z4.string(), RoleEntrySchema).optional(),
|
|
5124
5844
|
briefSchema: BriefSchemaSchema.optional(),
|
|
5125
5845
|
managed: ManagedManifestSchema.optional(),
|
|
5126
|
-
principals:
|
|
5127
|
-
releaseActor:
|
|
5128
|
-
ci:
|
|
5129
|
-
tokens:
|
|
5130
|
-
collect:
|
|
5846
|
+
principals: z4.array(z4.string()).min(1).optional(),
|
|
5847
|
+
releaseActor: z4.string().min(1).optional(),
|
|
5848
|
+
ci: z4.object({ setup: z4.string().min(1) }).optional(),
|
|
5849
|
+
tokens: z4.object({
|
|
5850
|
+
collect: z4.string().min(1).refine((v) => parseTokensCollectDeclaration(v) !== null, {
|
|
5131
5851
|
message: 'tokens.collect must be exactly "<interpreter> <repo-relative-script-path>" — two whitespace-separated tokens, no flags, no shell syntax, no quoting'
|
|
5132
5852
|
})
|
|
5133
5853
|
}).optional(),
|
|
5134
|
-
blastRadius:
|
|
5135
|
-
proseGates:
|
|
5136
|
-
doctrineRoot:
|
|
5137
|
-
readerFacingPrefix:
|
|
5138
|
-
readerFacingSuffix:
|
|
5139
|
-
legacySlugDir:
|
|
5854
|
+
blastRadius: z4.object({ extraDomains: z4.array(z4.string()).optional() }).optional(),
|
|
5855
|
+
proseGates: z4.object({
|
|
5856
|
+
doctrineRoot: z4.string().min(1).optional(),
|
|
5857
|
+
readerFacingPrefix: z4.string().min(1).optional(),
|
|
5858
|
+
readerFacingSuffix: z4.string().min(1).optional(),
|
|
5859
|
+
legacySlugDir: z4.string().min(1).optional()
|
|
5860
|
+
}).optional(),
|
|
5861
|
+
projects: z4.array(ProjectEntrySchema).optional(),
|
|
5862
|
+
dispatch: z4.object({
|
|
5863
|
+
timeoutMs: z4.number().int().positive().optional(),
|
|
5864
|
+
killGraceMs: z4.number().int().positive().optional(),
|
|
5865
|
+
agent: z4.enum(["claude", "codex", "gemini"]).optional()
|
|
5866
|
+
}).optional(),
|
|
5867
|
+
reviewPolicy: z4.object({
|
|
5868
|
+
codeReviewThreshold: z4.string().min(1).optional(),
|
|
5869
|
+
securityThreshold: z4.string().min(1).optional(),
|
|
5870
|
+
maxRounds: z4.number().optional()
|
|
5140
5871
|
}).optional(),
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5872
|
+
prePush: z4.object({
|
|
5873
|
+
alwaysRun: z4.array(z4.string()).optional()
|
|
5874
|
+
}).optional(),
|
|
5875
|
+
report: z4.object({
|
|
5876
|
+
commandTimeoutMs: z4.number().int().positive().optional()
|
|
5877
|
+
}).superRefine((report, ctx) => {
|
|
5878
|
+
const commandTimeoutMs = report.commandTimeoutMs;
|
|
5879
|
+
if (commandTimeoutMs !== undefined && commandTimeoutMs > MAX_REPORT_COMMAND_TIMEOUT_MS) {
|
|
5880
|
+
ctx.addIssue({
|
|
5881
|
+
code: z4.ZodIssueCode.custom,
|
|
5882
|
+
path: ["commandTimeoutMs"],
|
|
5883
|
+
message: "must be at most 3600000 (1 hour)"
|
|
5884
|
+
});
|
|
5885
|
+
}
|
|
5145
5886
|
}).optional()
|
|
5146
5887
|
});
|
|
5147
5888
|
var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
|
|
@@ -5216,6 +5957,28 @@ function resolvePrincipalAllowlist(config) {
|
|
|
5216
5957
|
function resolveReleaseActor(config) {
|
|
5217
5958
|
return config?.releaseActor ?? DEFAULT_RELEASE_ACTOR;
|
|
5218
5959
|
}
|
|
5960
|
+
function resolveReviewPolicy(config) {
|
|
5961
|
+
const raw = config?.reviewPolicy;
|
|
5962
|
+
if (!raw)
|
|
5963
|
+
return DEFAULT_REVIEW_POLICY;
|
|
5964
|
+
const codeReviewThreshold = raw.codeReviewThreshold ?? DEFAULT_REVIEW_POLICY.codeReviewThreshold;
|
|
5965
|
+
if (!isKnownSeverity(CODE_REVIEW_SEVERITY_ORDER, codeReviewThreshold)) {
|
|
5966
|
+
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.`);
|
|
5967
|
+
}
|
|
5968
|
+
const securityThreshold = raw.securityThreshold ?? DEFAULT_REVIEW_POLICY.securityThreshold;
|
|
5969
|
+
if (!isKnownSeverity(SECURITY_SEVERITY_ORDER, securityThreshold)) {
|
|
5970
|
+
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.`);
|
|
5971
|
+
}
|
|
5972
|
+
const maxRounds = raw.maxRounds ?? DEFAULT_REVIEW_POLICY.maxRounds;
|
|
5973
|
+
if (!Number.isInteger(maxRounds) || maxRounds < 1) {
|
|
5974
|
+
throw new Error(`vinaya.config.json: reviewPolicy.maxRounds "${maxRounds}" is not a positive integer — fix the config, this never falls back to a default.`);
|
|
5975
|
+
}
|
|
5976
|
+
return {
|
|
5977
|
+
codeReviewThreshold,
|
|
5978
|
+
securityThreshold,
|
|
5979
|
+
maxRounds
|
|
5980
|
+
};
|
|
5981
|
+
}
|
|
5219
5982
|
function trustAnchorRepo() {
|
|
5220
5983
|
const wellFormed = (slug) => /^[^/\s]+\/[^/\s]+$/.test(slug) ? slug : null;
|
|
5221
5984
|
const fromRunner = process.env.GITHUB_REPOSITORY?.trim();
|
|
@@ -5295,6 +6058,9 @@ function loadConfig() {
|
|
|
5295
6058
|
}
|
|
5296
6059
|
var TOKENS_COLLECT_TRUST_PATH = join(GLOBAL_VINAYA_HOME, "tokens-collect-trust.json");
|
|
5297
6060
|
|
|
6061
|
+
// src/lib/brief-assembly.ts
|
|
6062
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
6063
|
+
|
|
5298
6064
|
// ../../packages/sources/src/commands.ts
|
|
5299
6065
|
var COMMANDS = [
|
|
5300
6066
|
{
|
|
@@ -5415,14 +6181,52 @@ var COMMANDS = [
|
|
|
5415
6181
|
},
|
|
5416
6182
|
{
|
|
5417
6183
|
name: "task dispatch",
|
|
5418
|
-
description: "
|
|
6184
|
+
description: "Deprecated — render, pin, and post the brief on the Issue as the frozen original; start the developer",
|
|
5419
6185
|
flags: [
|
|
5420
6186
|
{ flag: "--agent <claude|codex|gemini>", description: "Start the developer through dispatchRole once posted" }
|
|
5421
6187
|
],
|
|
5422
6188
|
details: [
|
|
5423
6189
|
"Renders the brief from the Issue and the tree (the same assembly `brief render` uses) and posts it once as an Issue comment whose first line is `<!-- aeg:brief:v1 -->` and whose second line is `Brief hash: <sha256>` — the hash covers only the body below those two lines, so any reader recomputes it. Refuses outright, naming the existing comment's URL, when a `v1` comment already exists on the Issue — the brief is frozen by design, never overwritten or silently reissued.",
|
|
5424
6190
|
"With `--agent`, starts the Developer through `dispatchRole` when `apps/cli/src/lib/dispatch.ts` exports it; otherwise prints the rendered brief and the manual dispatch instruction and exits `0` — a soft dependency, never a hard block.",
|
|
5425
|
-
"Principal-only, with or without `--agent`: refuses before any render, forge read, or post when the authenticated `gh` identity is not on the Principal allowlist (or cannot be resolved at all) — dispatching is the `todo → in-flight` transition, not a general-purpose comment poster."
|
|
6191
|
+
"Principal-only, with or without `--agent`: refuses before any render, forge read, or post when the authenticated `gh` identity is not on the Principal allowlist (or cannot be resolved at all) — dispatching is the `todo → in-flight` transition, not a general-purpose comment poster.",
|
|
6192
|
+
"Deprecated in favor of `task brief` (preparation only) and `task run` (the full unattended loop) — kept for a documented compatibility window while callers migrate."
|
|
6193
|
+
],
|
|
6194
|
+
status: "shipped"
|
|
6195
|
+
},
|
|
6196
|
+
{
|
|
6197
|
+
name: "task brief",
|
|
6198
|
+
description: "Render and freeze the brief as the Issue's original comment — preparation only, starts nobody",
|
|
6199
|
+
details: [
|
|
6200
|
+
"The preparation half of `task dispatch`, extracted so it is callable on its own: resolves the task's Issue, renders the brief, refuses on any gap, refuses when a frozen brief already exists, and posts it once as the same `aeg:brief:v1` Issue comment `task dispatch` posts. Starts no agent under any circumstances — there is no `--agent` flag here at all.",
|
|
6201
|
+
"Successor to `task dispatch` for the preparation step; the full unattended run (preparation, then the developer, then the review loop) is `task run`."
|
|
6202
|
+
],
|
|
6203
|
+
status: "shipped"
|
|
6204
|
+
},
|
|
6205
|
+
{
|
|
6206
|
+
name: "task run",
|
|
6207
|
+
description: "One command from a planned Issue to a reviewed pull request — exactly one developer started",
|
|
6208
|
+
flags: [
|
|
6209
|
+
{
|
|
6210
|
+
flag: "--agent <claude|codex|gemini>",
|
|
6211
|
+
description: "Vendor for the developer and both reviewers this run dispatches"
|
|
6212
|
+
}
|
|
6213
|
+
],
|
|
6214
|
+
details: [
|
|
6215
|
+
"Composes `task brief`'s own preparation (`prepareTask`) with `dev-review-loop` (`devReviewLoop`) — nothing else. Preparation starts no agent; the loop's own round 1 reads the frozen brief off the Issue and is the only place a developer is ever dispatched from a fresh task, so exactly one developer is started by construction.",
|
|
6216
|
+
"A brief already frozen on the Issue is reused, never re-posted — the second `task dispatch`/`task brief` call this composes around does not fail the whole run, it just skips straight to running the loop. A task whose Issue refuses preparation (a missing brief section, an unmet dispatch gate) is refused before any agent starts, with nothing posted.",
|
|
6217
|
+
"Refuses when the frozen brief's developer branch already has an open pull request — the old `task dispatch` followed by `task run` cannot start two developers this way.",
|
|
6218
|
+
"Exit and printed summary distinguish a published, reviewed pull request (exit `0`, the PR URL) from a pause (exit `1`, with the exact `vinaya dev-review-loop --resume <pr>` command to continue), a usage/argv error (exit `2`), and any other failure (exit `3`) — never sharing `1` with a pause, so an unattended host tells them apart from the exit code alone. A run that pauses is resumed with the loop's own existing `--resume <pr>` flag, never a flag on this command."
|
|
6219
|
+
],
|
|
6220
|
+
status: "shipped"
|
|
6221
|
+
},
|
|
6222
|
+
{
|
|
6223
|
+
name: "task status",
|
|
6224
|
+
description: "Every open task with a frozen brief, its pull request, and whether its loop is running, paused, or published",
|
|
6225
|
+
flags: [{ flag: "--json", description: "Enveloped JSON output (schema: 1)" }],
|
|
6226
|
+
details: [
|
|
6227
|
+
"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`.",
|
|
6228
|
+
"`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.",
|
|
6229
|
+
"`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."
|
|
5426
6230
|
],
|
|
5427
6231
|
status: "shipped"
|
|
5428
6232
|
},
|
|
@@ -5630,6 +6434,15 @@ var COMMANDS = [
|
|
|
5630
6434
|
],
|
|
5631
6435
|
status: "shipped"
|
|
5632
6436
|
},
|
|
6437
|
+
{
|
|
6438
|
+
name: "milestone status",
|
|
6439
|
+
description: "Print each of a Milestone's declared tranche intents with its derived lifecycle and issue counts",
|
|
6440
|
+
flags: [{ flag: "--json", description: "Enveloped JSON output (schema: 1)" }],
|
|
6441
|
+
details: [
|
|
6442
|
+
"Read-only — writes nothing. For each `- <slug>: …` line in the Milestone's `### Tranche intents` section, prints the tranche's lifecycle (`planned`/`active`/`complete`) and its labeled Issues' counts (merged, open, not planned), all derived from the forge. Refuses if `<n>` isn't a real Milestone in this repo, or if the forge is unreachable."
|
|
6443
|
+
],
|
|
6444
|
+
status: "shipped"
|
|
6445
|
+
},
|
|
5633
6446
|
{
|
|
5634
6447
|
name: "review status",
|
|
5635
6448
|
description: "Print the review loop's own state for a PR, and its branch's distance from the base",
|
|
@@ -5926,10 +6739,10 @@ function createForgeSource(config) {
|
|
|
5926
6739
|
};
|
|
5927
6740
|
}
|
|
5928
6741
|
// ../../packages/sources/src/select-source.ts
|
|
5929
|
-
import { z as
|
|
5930
|
-
var StateSourceConfigSchema =
|
|
5931
|
-
|
|
5932
|
-
|
|
6742
|
+
import { z as z5 } from "zod";
|
|
6743
|
+
var StateSourceConfigSchema = z5.discriminatedUnion("kind", [
|
|
6744
|
+
z5.object({ kind: z5.literal("forge"), owner: z5.string(), repo: z5.string() }),
|
|
6745
|
+
z5.object({ kind: z5.literal("file"), root: z5.string().optional() })
|
|
5933
6746
|
]);
|
|
5934
6747
|
// src/checks/edge-resolve.ts
|
|
5935
6748
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
@@ -6049,6 +6862,589 @@ async function resolveEdge(id, taskById, factsByTaskId, repo, resolveSibling = d
|
|
|
6049
6862
|
};
|
|
6050
6863
|
}
|
|
6051
6864
|
|
|
6865
|
+
// src/lib/brief-assembly.ts
|
|
6866
|
+
function git(args) {
|
|
6867
|
+
try {
|
|
6868
|
+
return execFileSync4("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
6869
|
+
} catch {
|
|
6870
|
+
return "";
|
|
6871
|
+
}
|
|
6872
|
+
}
|
|
6873
|
+
function expandGlob(glob) {
|
|
6874
|
+
const out = git(["ls-files", "--", glob]);
|
|
6875
|
+
return out.split(`
|
|
6876
|
+
`).map((s) => s.trim()).filter(Boolean);
|
|
6877
|
+
}
|
|
6878
|
+
var DRAFT_ISSUE_SENTINEL = Number.MAX_SAFE_INTEGER;
|
|
6879
|
+
|
|
6880
|
+
// src/checks/registry.ts
|
|
6881
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
6882
|
+
import { join as join3 } from "node:path";
|
|
6883
|
+
|
|
6884
|
+
// src/lib/package-root.ts
|
|
6885
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
6886
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
6887
|
+
import { fileURLToPath } from "node:url";
|
|
6888
|
+
function packageRoot(moduleUrl) {
|
|
6889
|
+
let dir = dirname2(fileURLToPath(moduleUrl));
|
|
6890
|
+
while (!existsSync2(join2(dir, "package.json"))) {
|
|
6891
|
+
if (existsSync2(join2(dir, ".git")))
|
|
6892
|
+
break;
|
|
6893
|
+
const parent = dirname2(dir);
|
|
6894
|
+
if (parent === dir)
|
|
6895
|
+
break;
|
|
6896
|
+
dir = parent;
|
|
6897
|
+
}
|
|
6898
|
+
return dir;
|
|
6899
|
+
}
|
|
6900
|
+
|
|
6901
|
+
// src/checks/registry.ts
|
|
6902
|
+
var DIST_BIN_DIR = join3(packageRoot(import.meta.url), "dist", "checks", "bin");
|
|
6903
|
+
var SRC_BIN_DIR = join3(packageRoot(import.meta.url), "src", "checks", "bin");
|
|
6904
|
+
var BIN_DIR = existsSync3(DIST_BIN_DIR) ? DIST_BIN_DIR : SRC_BIN_DIR;
|
|
6905
|
+
var BIN_EXT = BIN_DIR === DIST_BIN_DIR ? ".js" : ".ts";
|
|
6906
|
+
function bin(name) {
|
|
6907
|
+
return join3(BIN_DIR, `${name}${BIN_EXT}`);
|
|
6908
|
+
}
|
|
6909
|
+
var REGISTRY = [
|
|
6910
|
+
[
|
|
6911
|
+
{
|
|
6912
|
+
name: "brief-shape",
|
|
6913
|
+
validates: "body",
|
|
6914
|
+
run: bin("check-brief-shape"),
|
|
6915
|
+
scope: "diff",
|
|
6916
|
+
timeoutMs: 15000,
|
|
6917
|
+
env: {
|
|
6918
|
+
PR_BODY: { optional: true },
|
|
6919
|
+
BRANCH: { optional: true },
|
|
6920
|
+
PR_NUMBER: { optional: true },
|
|
6921
|
+
GITHUB_TOKEN: { optional: true },
|
|
6922
|
+
GH_TOKEN: { optional: true }
|
|
6923
|
+
}
|
|
6924
|
+
},
|
|
6925
|
+
0
|
|
6926
|
+
],
|
|
6927
|
+
[
|
|
6928
|
+
{
|
|
6929
|
+
name: "pr-report-density",
|
|
6930
|
+
validates: "body",
|
|
6931
|
+
run: bin("check-pr-report-density"),
|
|
6932
|
+
scope: "diff",
|
|
6933
|
+
timeoutMs: 15000,
|
|
6934
|
+
env: {
|
|
6935
|
+
PR_BODY: { optional: true }
|
|
6936
|
+
}
|
|
6937
|
+
},
|
|
6938
|
+
0
|
|
6939
|
+
],
|
|
6940
|
+
[
|
|
6941
|
+
{
|
|
6942
|
+
name: "doc-coverage",
|
|
6943
|
+
validates: "body",
|
|
6944
|
+
run: bin("check-doc-coverage"),
|
|
6945
|
+
scope: "diff",
|
|
6946
|
+
timeoutMs: 15000,
|
|
6947
|
+
env: {
|
|
6948
|
+
BASE_SHA: { optional: true },
|
|
6949
|
+
PR_BODY: { optional: true },
|
|
6950
|
+
PR_BODY_FILE: { optional: true },
|
|
6951
|
+
PR_NUMBER: { optional: true },
|
|
6952
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
6953
|
+
GITHUB_TOKEN: { optional: true },
|
|
6954
|
+
GH_TOKEN: { optional: true }
|
|
6955
|
+
}
|
|
6956
|
+
},
|
|
6957
|
+
0
|
|
6958
|
+
],
|
|
6959
|
+
[
|
|
6960
|
+
{
|
|
6961
|
+
name: "coherence",
|
|
6962
|
+
run: bin("check-coherence"),
|
|
6963
|
+
scope: "full",
|
|
6964
|
+
timeoutMs: 30000,
|
|
6965
|
+
env: {
|
|
6966
|
+
AEG_REPO: { optional: true },
|
|
6967
|
+
BRANCH: { optional: true },
|
|
6968
|
+
GITHUB_TOKEN: { optional: true },
|
|
6969
|
+
GH_TOKEN: { optional: true }
|
|
6970
|
+
}
|
|
6971
|
+
},
|
|
6972
|
+
0
|
|
6973
|
+
],
|
|
6974
|
+
[
|
|
6975
|
+
{
|
|
6976
|
+
name: "pr-premise-reassert",
|
|
6977
|
+
validates: "body",
|
|
6978
|
+
run: bin("check-pr-premise-reassert"),
|
|
6979
|
+
scope: "diff",
|
|
6980
|
+
timeoutMs: 15000,
|
|
6981
|
+
env: {
|
|
6982
|
+
PR_BODY: { optional: true }
|
|
6983
|
+
}
|
|
6984
|
+
},
|
|
6985
|
+
0
|
|
6986
|
+
],
|
|
6987
|
+
[
|
|
6988
|
+
{
|
|
6989
|
+
name: "dispatch-readiness",
|
|
6990
|
+
run: bin("check-dispatch-readiness"),
|
|
6991
|
+
scope: "full",
|
|
6992
|
+
timeoutMs: 30000,
|
|
6993
|
+
env: {
|
|
6994
|
+
AEG_REPO: { optional: true },
|
|
6995
|
+
BRANCH: { optional: true },
|
|
6996
|
+
GITHUB_TOKEN: { optional: true },
|
|
6997
|
+
GH_TOKEN: { optional: true },
|
|
6998
|
+
PREMISE_FILE: { optional: true }
|
|
6999
|
+
}
|
|
7000
|
+
},
|
|
7001
|
+
0
|
|
7002
|
+
],
|
|
7003
|
+
[
|
|
7004
|
+
{
|
|
7005
|
+
name: "closes-n",
|
|
7006
|
+
validates: "body",
|
|
7007
|
+
run: bin("check-closes-n"),
|
|
7008
|
+
scope: "diff",
|
|
7009
|
+
timeoutMs: 15000,
|
|
7010
|
+
requiresOpenPr: true,
|
|
7011
|
+
env: {
|
|
7012
|
+
BRANCH: { optional: true },
|
|
7013
|
+
PR_BODY: { optional: true },
|
|
7014
|
+
AEG_REPO: { optional: true },
|
|
7015
|
+
GITHUB_TOKEN: { optional: true },
|
|
7016
|
+
GH_TOKEN: { optional: true }
|
|
7017
|
+
}
|
|
7018
|
+
},
|
|
7019
|
+
1
|
|
7020
|
+
],
|
|
7021
|
+
[
|
|
7022
|
+
{
|
|
7023
|
+
name: "single-plan-pr",
|
|
7024
|
+
run: bin("check-single-plan-pr"),
|
|
7025
|
+
scope: "diff",
|
|
7026
|
+
timeoutMs: 15000,
|
|
7027
|
+
env: {
|
|
7028
|
+
BASE_SHA: { optional: true },
|
|
7029
|
+
PR_NUMBER: { optional: true },
|
|
7030
|
+
GITHUB_TOKEN: { optional: true },
|
|
7031
|
+
GH_TOKEN: { optional: true }
|
|
7032
|
+
}
|
|
7033
|
+
},
|
|
7034
|
+
0
|
|
7035
|
+
],
|
|
7036
|
+
[
|
|
7037
|
+
{
|
|
7038
|
+
name: "surface-scope",
|
|
7039
|
+
run: bin("check-surface-scope"),
|
|
7040
|
+
scope: "diff",
|
|
7041
|
+
timeoutMs: 15000,
|
|
7042
|
+
env: {
|
|
7043
|
+
AEG_REPO: { optional: true },
|
|
7044
|
+
BASE_SHA: { optional: true },
|
|
7045
|
+
BRANCH: { optional: true },
|
|
7046
|
+
GITHUB_TOKEN: { optional: true },
|
|
7047
|
+
GH_TOKEN: { optional: true }
|
|
7048
|
+
}
|
|
7049
|
+
},
|
|
7050
|
+
0
|
|
7051
|
+
],
|
|
7052
|
+
[
|
|
7053
|
+
{
|
|
7054
|
+
name: "test-plan",
|
|
7055
|
+
validates: "body",
|
|
7056
|
+
run: bin("check-test-plan"),
|
|
7057
|
+
scope: "diff",
|
|
7058
|
+
timeoutMs: 15000,
|
|
7059
|
+
requiresOpenPr: true,
|
|
7060
|
+
principalOwed: true,
|
|
7061
|
+
env: {
|
|
7062
|
+
PR_BODY: { optional: true },
|
|
7063
|
+
BRANCH: { optional: true }
|
|
7064
|
+
}
|
|
7065
|
+
},
|
|
7066
|
+
1
|
|
7067
|
+
],
|
|
7068
|
+
[
|
|
7069
|
+
{
|
|
7070
|
+
name: "body-bare-digits",
|
|
7071
|
+
validates: "body",
|
|
7072
|
+
run: bin("check-body-bare-digits"),
|
|
7073
|
+
scope: "diff",
|
|
7074
|
+
timeoutMs: 15000,
|
|
7075
|
+
requiresOpenPr: true,
|
|
7076
|
+
ownWorkflow: true,
|
|
7077
|
+
env: {
|
|
7078
|
+
PR_BODY: { optional: true },
|
|
7079
|
+
PR_NUMBER: { optional: true },
|
|
7080
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
7081
|
+
GITHUB_TOKEN: { optional: true },
|
|
7082
|
+
GH_TOKEN: { optional: true }
|
|
7083
|
+
}
|
|
7084
|
+
},
|
|
7085
|
+
1
|
|
7086
|
+
],
|
|
7087
|
+
[
|
|
7088
|
+
{
|
|
7089
|
+
name: "token-report",
|
|
7090
|
+
validates: "body",
|
|
7091
|
+
run: bin("check-token-report"),
|
|
7092
|
+
scope: "diff",
|
|
7093
|
+
timeoutMs: 15000,
|
|
7094
|
+
requiresOpenPr: true,
|
|
7095
|
+
env: {
|
|
7096
|
+
PR_BODY: { optional: true },
|
|
7097
|
+
BRANCH: { optional: true },
|
|
7098
|
+
CLAUDE_PROJECT_DIR: { optional: true },
|
|
7099
|
+
CLAUDE_CODE_SESSION_ID: { optional: true }
|
|
7100
|
+
}
|
|
7101
|
+
},
|
|
7102
|
+
1
|
|
7103
|
+
],
|
|
7104
|
+
[
|
|
7105
|
+
{
|
|
7106
|
+
name: "no-disk-state",
|
|
7107
|
+
run: bin("check-no-disk-state"),
|
|
7108
|
+
scope: "diff",
|
|
7109
|
+
timeoutMs: 15000,
|
|
7110
|
+
env: { BASE_SHA: { optional: true } }
|
|
7111
|
+
},
|
|
7112
|
+
0
|
|
7113
|
+
],
|
|
7114
|
+
[
|
|
7115
|
+
{
|
|
7116
|
+
name: "registry-gates",
|
|
7117
|
+
run: bin("check-registry-gates"),
|
|
7118
|
+
scope: "full",
|
|
7119
|
+
timeoutMs: 30000,
|
|
7120
|
+
env: {
|
|
7121
|
+
AEG_REPO: { optional: true },
|
|
7122
|
+
GITHUB_TOKEN: { optional: true },
|
|
7123
|
+
GH_TOKEN: { optional: true }
|
|
7124
|
+
}
|
|
7125
|
+
},
|
|
7126
|
+
0
|
|
7127
|
+
],
|
|
7128
|
+
[
|
|
7129
|
+
{
|
|
7130
|
+
name: "review-gate",
|
|
7131
|
+
run: bin("check-review-gate"),
|
|
7132
|
+
scope: "full",
|
|
7133
|
+
timeoutMs: 30000,
|
|
7134
|
+
ownWorkflow: true,
|
|
7135
|
+
env: {
|
|
7136
|
+
PR_NUMBER: { optional: true },
|
|
7137
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
7138
|
+
GITHUB_TOKEN: { optional: true },
|
|
7139
|
+
GH_TOKEN: { optional: true }
|
|
7140
|
+
}
|
|
7141
|
+
},
|
|
7142
|
+
1
|
|
7143
|
+
],
|
|
7144
|
+
[
|
|
7145
|
+
{
|
|
7146
|
+
name: "branch-topology",
|
|
7147
|
+
run: bin("check-branch-topology"),
|
|
7148
|
+
scope: "full",
|
|
7149
|
+
timeoutMs: 30000,
|
|
7150
|
+
env: {
|
|
7151
|
+
BRANCH: { optional: true },
|
|
7152
|
+
AEG_REPO: { optional: true },
|
|
7153
|
+
GITHUB_TOKEN: { optional: true },
|
|
7154
|
+
GH_TOKEN: { optional: true }
|
|
7155
|
+
}
|
|
7156
|
+
},
|
|
7157
|
+
0
|
|
7158
|
+
],
|
|
7159
|
+
[
|
|
7160
|
+
{
|
|
7161
|
+
name: "dead-branch-push",
|
|
7162
|
+
run: bin("check-dead-branch-push"),
|
|
7163
|
+
scope: "full",
|
|
7164
|
+
timeoutMs: 30000,
|
|
7165
|
+
env: {
|
|
7166
|
+
BRANCH: { optional: true },
|
|
7167
|
+
GITHUB_TOKEN: { optional: true },
|
|
7168
|
+
GH_TOKEN: { optional: true }
|
|
7169
|
+
}
|
|
7170
|
+
},
|
|
7171
|
+
0
|
|
7172
|
+
],
|
|
7173
|
+
[
|
|
7174
|
+
{
|
|
7175
|
+
name: "first-push-dispatch",
|
|
7176
|
+
run: bin("check-first-push-dispatch"),
|
|
7177
|
+
scope: "full",
|
|
7178
|
+
timeoutMs: 30000,
|
|
7179
|
+
env: {
|
|
7180
|
+
BRANCH: { optional: true },
|
|
7181
|
+
AEG_REPO: { optional: true },
|
|
7182
|
+
GITHUB_TOKEN: { optional: true },
|
|
7183
|
+
GH_TOKEN: { optional: true }
|
|
7184
|
+
}
|
|
7185
|
+
},
|
|
7186
|
+
0
|
|
7187
|
+
],
|
|
7188
|
+
[
|
|
7189
|
+
{
|
|
7190
|
+
name: "doc-coverage-push",
|
|
7191
|
+
validates: "body",
|
|
7192
|
+
run: bin("check-doc-coverage-push"),
|
|
7193
|
+
scope: "diff",
|
|
7194
|
+
timeoutMs: 15000,
|
|
7195
|
+
env: {
|
|
7196
|
+
OVERRIDE_DOCS: { optional: true },
|
|
7197
|
+
BASE_SHA: { optional: true },
|
|
7198
|
+
PR_BODY: { optional: true },
|
|
7199
|
+
PR_BODY_FILE: { optional: true },
|
|
7200
|
+
PR_LABELS: { optional: true },
|
|
7201
|
+
WAIVER_LABEL_ACTOR: { optional: true },
|
|
7202
|
+
GITHUB_REPOSITORY: { optional: true },
|
|
7203
|
+
GITHUB_TOKEN: { optional: true },
|
|
7204
|
+
GH_TOKEN: { optional: true }
|
|
7205
|
+
}
|
|
7206
|
+
},
|
|
7207
|
+
0
|
|
7208
|
+
],
|
|
7209
|
+
[
|
|
7210
|
+
{
|
|
7211
|
+
name: "issue-assignment",
|
|
7212
|
+
run: bin("check-issue-assignment"),
|
|
7213
|
+
scope: "full",
|
|
7214
|
+
timeoutMs: 30000,
|
|
7215
|
+
env: {
|
|
7216
|
+
AEG_REPO: { optional: true },
|
|
7217
|
+
BRANCH: { optional: true },
|
|
7218
|
+
GITHUB_TOKEN: { optional: true },
|
|
7219
|
+
GH_TOKEN: { optional: true }
|
|
7220
|
+
}
|
|
7221
|
+
},
|
|
7222
|
+
0
|
|
7223
|
+
],
|
|
7224
|
+
[
|
|
7225
|
+
{
|
|
7226
|
+
name: "evidence-fresh",
|
|
7227
|
+
validates: "body",
|
|
7228
|
+
run: bin("check-evidence-fresh"),
|
|
7229
|
+
scope: "diff",
|
|
7230
|
+
timeoutMs: 15000,
|
|
7231
|
+
requiresOpenPr: true,
|
|
7232
|
+
env: {
|
|
7233
|
+
PR_NUMBER: { optional: true },
|
|
7234
|
+
PR_BODY: { optional: true },
|
|
7235
|
+
BASE_SHA: { optional: true },
|
|
7236
|
+
GITHUB_TOKEN: { optional: true },
|
|
7237
|
+
GH_TOKEN: { optional: true }
|
|
7238
|
+
}
|
|
7239
|
+
},
|
|
7240
|
+
1
|
|
7241
|
+
],
|
|
7242
|
+
[
|
|
7243
|
+
{
|
|
7244
|
+
name: "reader-resolvable-prose",
|
|
7245
|
+
run: bin("check-reader-resolvable-prose"),
|
|
7246
|
+
scope: "full",
|
|
7247
|
+
timeoutMs: 30000,
|
|
7248
|
+
env: {},
|
|
7249
|
+
include: [
|
|
7250
|
+
"aeg-root/**/*.md",
|
|
7251
|
+
"apps/cli/src/**",
|
|
7252
|
+
".github/workflows/**",
|
|
7253
|
+
".vinaya/**",
|
|
7254
|
+
"apps/cli/README.md",
|
|
7255
|
+
"packages/sources/README.md"
|
|
7256
|
+
]
|
|
7257
|
+
},
|
|
7258
|
+
0
|
|
7259
|
+
],
|
|
7260
|
+
[
|
|
7261
|
+
{
|
|
7262
|
+
name: "retired-vocabulary",
|
|
7263
|
+
run: bin("check-retired-vocabulary"),
|
|
7264
|
+
scope: "full",
|
|
7265
|
+
timeoutMs: 30000,
|
|
7266
|
+
env: {},
|
|
7267
|
+
include: ["aeg-root/**/*.md"]
|
|
7268
|
+
},
|
|
7269
|
+
0
|
|
7270
|
+
],
|
|
7271
|
+
[
|
|
7272
|
+
{
|
|
7273
|
+
name: "doctrine-portability",
|
|
7274
|
+
run: bin("check-doctrine-portability"),
|
|
7275
|
+
scope: "full",
|
|
7276
|
+
timeoutMs: 30000,
|
|
7277
|
+
env: { BASE_SHA: { optional: true } },
|
|
7278
|
+
include: ["aeg-root/**/*.md"]
|
|
7279
|
+
},
|
|
7280
|
+
0
|
|
7281
|
+
],
|
|
7282
|
+
[
|
|
7283
|
+
{
|
|
7284
|
+
name: "doctrine-no-procedures",
|
|
7285
|
+
run: bin("check-doctrine-no-procedures"),
|
|
7286
|
+
scope: "full",
|
|
7287
|
+
timeoutMs: 30000,
|
|
7288
|
+
env: {},
|
|
7289
|
+
include: ["aeg-root/**/*.md"]
|
|
7290
|
+
},
|
|
7291
|
+
0
|
|
7292
|
+
],
|
|
7293
|
+
[
|
|
7294
|
+
{
|
|
7295
|
+
name: "exec-bits",
|
|
7296
|
+
run: bin("check-exec-bits"),
|
|
7297
|
+
scope: "diff",
|
|
7298
|
+
timeoutMs: 30000,
|
|
7299
|
+
env: {}
|
|
7300
|
+
},
|
|
7301
|
+
0
|
|
7302
|
+
],
|
|
7303
|
+
[
|
|
7304
|
+
{
|
|
7305
|
+
name: "workspace-escape",
|
|
7306
|
+
run: bin("check-workspace-escape"),
|
|
7307
|
+
scope: "full",
|
|
7308
|
+
timeoutMs: 30000,
|
|
7309
|
+
env: {},
|
|
7310
|
+
include: ["aeg-root/**/*.md"]
|
|
7311
|
+
},
|
|
7312
|
+
0
|
|
7313
|
+
],
|
|
7314
|
+
[
|
|
7315
|
+
{
|
|
7316
|
+
name: "changeset-coverage",
|
|
7317
|
+
run: bin("check-changeset-coverage"),
|
|
7318
|
+
scope: "diff",
|
|
7319
|
+
timeoutMs: 15000,
|
|
7320
|
+
env: {}
|
|
7321
|
+
},
|
|
7322
|
+
0
|
|
7323
|
+
],
|
|
7324
|
+
[
|
|
7325
|
+
{
|
|
7326
|
+
name: "quoted-command",
|
|
7327
|
+
run: bin("check-quoted-command"),
|
|
7328
|
+
scope: "diff",
|
|
7329
|
+
timeoutMs: 15000,
|
|
7330
|
+
env: {}
|
|
7331
|
+
},
|
|
7332
|
+
0
|
|
7333
|
+
],
|
|
7334
|
+
[
|
|
7335
|
+
{
|
|
7336
|
+
name: "main-branch-refusal",
|
|
7337
|
+
run: bin("check-main-branch-refusal"),
|
|
7338
|
+
scope: "full",
|
|
7339
|
+
timeoutMs: 15000,
|
|
7340
|
+
env: { VINAYA_PUSH_REFS: { optional: true } }
|
|
7341
|
+
},
|
|
7342
|
+
0
|
|
7343
|
+
],
|
|
7344
|
+
[
|
|
7345
|
+
{
|
|
7346
|
+
name: "token-collection-wired",
|
|
7347
|
+
run: bin("check-token-collection-wired"),
|
|
7348
|
+
scope: "full",
|
|
7349
|
+
timeoutMs: 15000,
|
|
7350
|
+
env: {
|
|
7351
|
+
CLAUDE_PROJECT_DIR: { optional: true },
|
|
7352
|
+
CLAUDE_CODE_SESSION_ID: { optional: true }
|
|
7353
|
+
}
|
|
7354
|
+
},
|
|
7355
|
+
0
|
|
7356
|
+
],
|
|
7357
|
+
[
|
|
7358
|
+
{
|
|
7359
|
+
name: "issue-title-grammar",
|
|
7360
|
+
run: bin("check-issue-title-grammar"),
|
|
7361
|
+
validates: "issue",
|
|
7362
|
+
scope: "full",
|
|
7363
|
+
ownWorkflow: true,
|
|
7364
|
+
timeoutMs: 15000,
|
|
7365
|
+
env: { ISSUE_TITLE: { optional: true } }
|
|
7366
|
+
},
|
|
7367
|
+
1
|
|
7368
|
+
],
|
|
7369
|
+
[
|
|
7370
|
+
{
|
|
7371
|
+
name: "issue-objectives-numbering",
|
|
7372
|
+
run: bin("check-issue-objectives-numbering"),
|
|
7373
|
+
validates: "issue",
|
|
7374
|
+
scope: "full",
|
|
7375
|
+
ownWorkflow: true,
|
|
7376
|
+
timeoutMs: 15000,
|
|
7377
|
+
env: { ISSUE_BODY: { optional: true }, ISSUE_NUMBER: { optional: true } }
|
|
7378
|
+
},
|
|
7379
|
+
1
|
|
7380
|
+
],
|
|
7381
|
+
[
|
|
7382
|
+
{
|
|
7383
|
+
name: "issue-parts-coverage",
|
|
7384
|
+
run: bin("check-issue-parts-coverage"),
|
|
7385
|
+
validates: "issue",
|
|
7386
|
+
scope: "full",
|
|
7387
|
+
ownWorkflow: true,
|
|
7388
|
+
timeoutMs: 15000,
|
|
7389
|
+
env: { ISSUE_BODY: { optional: true } }
|
|
7390
|
+
},
|
|
7391
|
+
1
|
|
7392
|
+
],
|
|
7393
|
+
[
|
|
7394
|
+
{
|
|
7395
|
+
name: "issue-surface-globs",
|
|
7396
|
+
run: bin("check-issue-surface-globs"),
|
|
7397
|
+
validates: "issue",
|
|
7398
|
+
scope: "full",
|
|
7399
|
+
ownWorkflow: true,
|
|
7400
|
+
timeoutMs: 15000,
|
|
7401
|
+
env: { ISSUE_BODY: { optional: true } }
|
|
7402
|
+
},
|
|
7403
|
+
1
|
|
7404
|
+
],
|
|
7405
|
+
[
|
|
7406
|
+
{
|
|
7407
|
+
name: "issue-tranche-label",
|
|
7408
|
+
run: bin("check-issue-tranche-label"),
|
|
7409
|
+
validates: "issue",
|
|
7410
|
+
scope: "full",
|
|
7411
|
+
ownWorkflow: true,
|
|
7412
|
+
timeoutMs: 15000,
|
|
7413
|
+
env: { ISSUE_BODY: { optional: true }, ISSUE_LABELS: { optional: true } }
|
|
7414
|
+
},
|
|
7415
|
+
1
|
|
7416
|
+
],
|
|
7417
|
+
[
|
|
7418
|
+
{
|
|
7419
|
+
name: "issue-milestone-attach",
|
|
7420
|
+
run: bin("check-issue-milestone-attach"),
|
|
7421
|
+
validates: "issue",
|
|
7422
|
+
scope: "full",
|
|
7423
|
+
ownWorkflow: true,
|
|
7424
|
+
timeoutMs: 15000,
|
|
7425
|
+
env: {
|
|
7426
|
+
ISSUE_LABELS: { optional: true },
|
|
7427
|
+
CURRENT_MILESTONE_TITLE: { optional: true },
|
|
7428
|
+
RESOLVED_MILESTONE_TITLE: { optional: true }
|
|
7429
|
+
}
|
|
7430
|
+
},
|
|
7431
|
+
1
|
|
7432
|
+
]
|
|
7433
|
+
];
|
|
7434
|
+
function coreCheckRegistry() {
|
|
7435
|
+
return REGISTRY.map(([spec]) => spec);
|
|
7436
|
+
}
|
|
7437
|
+
var CORE_CHECK_RING = Object.fromEntries(REGISTRY.map(([spec, ring]) => [spec.name, ring]));
|
|
7438
|
+
|
|
7439
|
+
// src/checks/runner.ts
|
|
7440
|
+
var activeKillers = new Set;
|
|
7441
|
+
|
|
7442
|
+
// src/lib/forge-write.ts
|
|
7443
|
+
var INFORMATIONAL_DISPATCH_BLOCKER_CLASSES = new Set([
|
|
7444
|
+
"depends-on-not-merged",
|
|
7445
|
+
"conflicts-with"
|
|
7446
|
+
]);
|
|
7447
|
+
|
|
6052
7448
|
// src/commands/review-post.ts
|
|
6053
7449
|
function parseChangedLineRanges(diffOutput) {
|
|
6054
7450
|
const result = {};
|
|
@@ -6080,7 +7476,7 @@ function parseChangedLineRanges(diffOutput) {
|
|
|
6080
7476
|
function fileDiffAgainst(ref, path) {
|
|
6081
7477
|
let out;
|
|
6082
7478
|
try {
|
|
6083
|
-
out =
|
|
7479
|
+
out = execFileSync5("git", ["diff", `${ref}...HEAD`, "--", path], {
|
|
6084
7480
|
encoding: "utf8",
|
|
6085
7481
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6086
7482
|
}).trim();
|
|
@@ -6091,7 +7487,7 @@ function fileDiffAgainst(ref, path) {
|
|
|
6091
7487
|
}
|
|
6092
7488
|
function revParse(ref) {
|
|
6093
7489
|
try {
|
|
6094
|
-
return
|
|
7490
|
+
return execFileSync5("git", ["rev-parse", "--verify", ref], {
|
|
6095
7491
|
encoding: "utf8",
|
|
6096
7492
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6097
7493
|
}).trim();
|
|
@@ -6101,7 +7497,7 @@ function revParse(ref) {
|
|
|
6101
7497
|
}
|
|
6102
7498
|
function repoRoot() {
|
|
6103
7499
|
try {
|
|
6104
|
-
return
|
|
7500
|
+
return execFileSync5("git", ["rev-parse", "--show-toplevel"], {
|
|
6105
7501
|
encoding: "utf8",
|
|
6106
7502
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6107
7503
|
}).trim();
|
|
@@ -6112,7 +7508,7 @@ function repoRoot() {
|
|
|
6112
7508
|
function changedFiles(base) {
|
|
6113
7509
|
let out;
|
|
6114
7510
|
try {
|
|
6115
|
-
out =
|
|
7511
|
+
out = execFileSync5("git", ["diff", "--name-only", `${base}...HEAD`], {
|
|
6116
7512
|
encoding: "utf8",
|
|
6117
7513
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6118
7514
|
}).trim();
|
|
@@ -6137,7 +7533,7 @@ function resolveDiff(base = process.env.BASE_SHA || "origin/main") {
|
|
|
6137
7533
|
const relPaths = changedFiles(ref);
|
|
6138
7534
|
if (relPaths === null)
|
|
6139
7535
|
continue;
|
|
6140
|
-
return { base: ref, root, files: relPaths.map((p) =>
|
|
7536
|
+
return { base: ref, root, files: relPaths.map((p) => join4(root, p)) };
|
|
6141
7537
|
}
|
|
6142
7538
|
return null;
|
|
6143
7539
|
}
|
|
@@ -6171,15 +7567,15 @@ function findingsInThisDiff(findings) {
|
|
|
6171
7567
|
// src/checks/bin/check-exec-bits.ts
|
|
6172
7568
|
var CHECK_NAME = "exec-bits";
|
|
6173
7569
|
var EXECUTABLE_MODE = "100755";
|
|
6174
|
-
function
|
|
7570
|
+
function git2(args) {
|
|
6175
7571
|
try {
|
|
6176
|
-
return
|
|
7572
|
+
return execFileSync6("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
6177
7573
|
} catch {
|
|
6178
7574
|
return null;
|
|
6179
7575
|
}
|
|
6180
7576
|
}
|
|
6181
7577
|
function indexMode(path) {
|
|
6182
|
-
const out =
|
|
7578
|
+
const out = git2(["ls-files", "-s", "--", path]);
|
|
6183
7579
|
if (out === null || out === "")
|
|
6184
7580
|
return null;
|
|
6185
7581
|
const mode = out.split(/\s+/)[0] ?? "";
|