@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
|
@@ -300,7 +300,7 @@ function issueListByLabelArgs(owner, repo, label) {
|
|
|
300
300
|
"--state",
|
|
301
301
|
"all",
|
|
302
302
|
"--json",
|
|
303
|
-
"number,title,body,state,labels,milestone",
|
|
303
|
+
"number,title,body,state,labels,milestone,stateReason",
|
|
304
304
|
"--limit",
|
|
305
305
|
"200"
|
|
306
306
|
];
|
|
@@ -646,14 +646,19 @@ function resolveTaskIssueRef(title, labels) {
|
|
|
646
646
|
var INTENTS_HEADING = /^#{1,6}\s*Tranche intents\s*$/im;
|
|
647
647
|
var NEXT_HEADING = /^#{1,6}\s+\S/m;
|
|
648
648
|
var INTENT_BULLET = /^-\s+([a-z0-9][a-z0-9-]*)\s*:\s*(.+)$/i;
|
|
649
|
-
function
|
|
650
|
-
const text = stripCode(description, { inlineSpans: "keep" });
|
|
649
|
+
function intentsSection(text) {
|
|
651
650
|
const start = text.match(INTENTS_HEADING);
|
|
652
651
|
if (!start || start.index === undefined)
|
|
653
|
-
return
|
|
652
|
+
return null;
|
|
654
653
|
const rest = text.slice(start.index + start[0].length);
|
|
655
654
|
const next = rest.match(NEXT_HEADING);
|
|
656
|
-
|
|
655
|
+
return rest.slice(0, next && next.index !== undefined ? next.index : rest.length);
|
|
656
|
+
}
|
|
657
|
+
function intentGoalForSlug(description, slug) {
|
|
658
|
+
const text = stripCode(description, { inlineSpans: "keep" });
|
|
659
|
+
const section = intentsSection(text);
|
|
660
|
+
if (section === null)
|
|
661
|
+
return "";
|
|
657
662
|
for (const line of section.split(`
|
|
658
663
|
`)) {
|
|
659
664
|
const trimmed = line.trim();
|
|
@@ -1945,6 +1950,9 @@ function parseInlineFieldList(section) {
|
|
|
1945
1950
|
// ../../packages/aeg-core/src/verdict-extraction.ts
|
|
1946
1951
|
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
1947
1952
|
var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1953
|
+
var RULING_ORDINAL_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Ruling ordinal:\s*(\d+)(?!\d)/im;
|
|
1954
|
+
var BRIEF_HASH_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Brief hash:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1955
|
+
var POLICY_DIGEST_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Policy digest:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
|
|
1948
1956
|
function firstFiveLines(comment) {
|
|
1949
1957
|
return comment.split(`
|
|
1950
1958
|
`).slice(0, 5).join(`
|
|
@@ -1958,6 +1966,35 @@ function extractObjectivesVersion(comment) {
|
|
|
1958
1966
|
const m = firstFiveLines(comment).match(OBJECTIVES_VERSION_PATTERN);
|
|
1959
1967
|
return m ? m[1].toLowerCase() : null;
|
|
1960
1968
|
}
|
|
1969
|
+
function firstSevenLines(comment) {
|
|
1970
|
+
return comment.split(`
|
|
1971
|
+
`).slice(0, 7).join(`
|
|
1972
|
+
`);
|
|
1973
|
+
}
|
|
1974
|
+
function extractRulingOrdinal(comment) {
|
|
1975
|
+
const m = firstSevenLines(comment).match(RULING_ORDINAL_PATTERN);
|
|
1976
|
+
return m ? Number.parseInt(m[1], 10) : null;
|
|
1977
|
+
}
|
|
1978
|
+
function firstElevenLines(comment) {
|
|
1979
|
+
return comment.split(`
|
|
1980
|
+
`).slice(0, 11).join(`
|
|
1981
|
+
`);
|
|
1982
|
+
}
|
|
1983
|
+
function extractBriefHash(comment) {
|
|
1984
|
+
const m = firstElevenLines(comment).match(BRIEF_HASH_PATTERN);
|
|
1985
|
+
return m ? m[1].toLowerCase() : null;
|
|
1986
|
+
}
|
|
1987
|
+
function extractPolicyDigest(comment) {
|
|
1988
|
+
const m = firstElevenLines(comment).match(POLICY_DIGEST_PATTERN);
|
|
1989
|
+
return m ? m[1].toLowerCase() : null;
|
|
1990
|
+
}
|
|
1991
|
+
var FINDING_SEVERITY_LINE = /^\d+\.\s+\[([A-Z][A-Z]*)\]\s+(.+?)\s+—/gm;
|
|
1992
|
+
function extractFindingSeverities(comment) {
|
|
1993
|
+
return [...comment.matchAll(FINDING_SEVERITY_LINE)].map((m) => ({
|
|
1994
|
+
severity: m[1],
|
|
1995
|
+
location: m[2]
|
|
1996
|
+
}));
|
|
1997
|
+
}
|
|
1961
1998
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
1962
1999
|
const candidates = comments.filter((c) => valuePattern.test(c));
|
|
1963
2000
|
if (candidates.length === 0) {
|
|
@@ -1965,6 +2002,10 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1965
2002
|
value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
|
|
1966
2003
|
headSha: null,
|
|
1967
2004
|
objectivesVersion: null,
|
|
2005
|
+
rulingOrdinal: null,
|
|
2006
|
+
briefHash: null,
|
|
2007
|
+
policyDigest: null,
|
|
2008
|
+
findingSeverities: [],
|
|
1968
2009
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
1969
2010
|
};
|
|
1970
2011
|
}
|
|
@@ -1975,6 +2016,10 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1975
2016
|
value: `the most recent ${missingLabel} comment's VERDICT line is not within its first five lines — DANGLING, see below`,
|
|
1976
2017
|
headSha: null,
|
|
1977
2018
|
objectivesVersion: null,
|
|
2019
|
+
rulingOrdinal: null,
|
|
2020
|
+
briefHash: null,
|
|
2021
|
+
policyDigest: null,
|
|
2022
|
+
findingSeverities: [],
|
|
1978
2023
|
danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
|
|
1979
2024
|
};
|
|
1980
2025
|
}
|
|
@@ -1982,6 +2027,10 @@ function extractVerdict(comments, valuePattern, missingLabel) {
|
|
|
1982
2027
|
value: m[1].toUpperCase().replace(/[_-]/g, " "),
|
|
1983
2028
|
headSha: extractHeadSha(latest),
|
|
1984
2029
|
objectivesVersion: extractObjectivesVersion(latest),
|
|
2030
|
+
rulingOrdinal: extractRulingOrdinal(latest),
|
|
2031
|
+
briefHash: extractBriefHash(latest),
|
|
2032
|
+
policyDigest: extractPolicyDigest(latest),
|
|
2033
|
+
findingSeverities: extractFindingSeverities(latest),
|
|
1985
2034
|
danglingNote: null
|
|
1986
2035
|
};
|
|
1987
2036
|
}
|
|
@@ -2317,6 +2366,53 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive,
|
|
|
2317
2366
|
}
|
|
2318
2367
|
return out;
|
|
2319
2368
|
}
|
|
2369
|
+
// ../../packages/aeg-core/src/review-policy.ts
|
|
2370
|
+
var CODE_REVIEW_SEVERITY_ORDER = ["BLOCKER", "MAJOR", "MINOR"];
|
|
2371
|
+
var SECURITY_SEVERITY_ORDER = ["CRITICAL", "HIGH", "MEDIUM", "LOW"];
|
|
2372
|
+
var DEFAULT_MAX_ROUNDS = 3;
|
|
2373
|
+
var DEFAULT_REVIEW_POLICY = {
|
|
2374
|
+
codeReviewThreshold: "BLOCKER",
|
|
2375
|
+
securityThreshold: "HIGH",
|
|
2376
|
+
maxRounds: DEFAULT_MAX_ROUNDS
|
|
2377
|
+
};
|
|
2378
|
+
var FILE_SHAPED_LOCATION = /\.[a-zA-Z0-9]{1,10}(:\d+)?\s*$/;
|
|
2379
|
+
var PROSE_LOCATION_PATTERNS = [/\bpr\s*body\b/i, /\bcomment\b/i];
|
|
2380
|
+
var ROLE_FILE_LOCATION = /(^|\/)aeg-root\/roles\//i;
|
|
2381
|
+
function isProseLocation(location) {
|
|
2382
|
+
if (ROLE_FILE_LOCATION.test(location))
|
|
2383
|
+
return true;
|
|
2384
|
+
if (FILE_SHAPED_LOCATION.test(location))
|
|
2385
|
+
return false;
|
|
2386
|
+
return PROSE_LOCATION_PATTERNS.some((pattern) => pattern.test(location));
|
|
2387
|
+
}
|
|
2388
|
+
var PROSE_CAP_SEVERITY = "MINOR";
|
|
2389
|
+
function blockingSeverities(scale, threshold) {
|
|
2390
|
+
const idx = scale.indexOf(threshold);
|
|
2391
|
+
if (idx === -1) {
|
|
2392
|
+
throw new Error(`blockingSeverities: threshold "${threshold}" is not one of ${scale.join(" > ")}`);
|
|
2393
|
+
}
|
|
2394
|
+
return scale.slice(0, idx + 1);
|
|
2395
|
+
}
|
|
2396
|
+
function evaluateReviewFindings(findings, scale, threshold) {
|
|
2397
|
+
const blocking = new Set(blockingSeverities(scale, threshold));
|
|
2398
|
+
const blockingFindings = findings.filter((f) => {
|
|
2399
|
+
if (!scale.includes(f.severity)) {
|
|
2400
|
+
throw new Error(`evaluateReviewFindings: severity "${f.severity}" is not one of ${scale.join(" > ")}`);
|
|
2401
|
+
}
|
|
2402
|
+
const effectiveSeverity = f.location !== undefined && isProseLocation(f.location) ? PROSE_CAP_SEVERITY : f.severity;
|
|
2403
|
+
return blocking.has(effectiveSeverity);
|
|
2404
|
+
});
|
|
2405
|
+
return { outcome: blockingFindings.length > 0 ? "blocked" : "clean", blockingFindings };
|
|
2406
|
+
}
|
|
2407
|
+
function evaluateCodeReview(findings, policy) {
|
|
2408
|
+
return evaluateReviewFindings(findings, CODE_REVIEW_SEVERITY_ORDER, policy.codeReviewThreshold);
|
|
2409
|
+
}
|
|
2410
|
+
function evaluateSecurityReview(findings, policy) {
|
|
2411
|
+
return evaluateReviewFindings(findings, SECURITY_SEVERITY_ORDER, policy.securityThreshold);
|
|
2412
|
+
}
|
|
2413
|
+
function isKnownSeverity(scale, value) {
|
|
2414
|
+
return scale.includes(value);
|
|
2415
|
+
}
|
|
2320
2416
|
// ../../packages/aeg-core/src/pr-tier.ts
|
|
2321
2417
|
var TIER_FIELD = /(\*\*)?\s*Tier\s*(\*\*)?\s*:\s*(\*\*)?\s*([013])\b/i;
|
|
2322
2418
|
function readTierFromPrBody(prBody) {
|
|
@@ -2343,6 +2439,9 @@ import { createHash as createHash2 } from "node:crypto";
|
|
|
2343
2439
|
var HEADING_RE = /^##[ \t]*Objectives[ \t]*$/im;
|
|
2344
2440
|
var NEXT_HEADING_RE = /^##[ \t]/m;
|
|
2345
2441
|
var OBJECTIVE_LINE_RE = /^O(\d+)\.[ \t]*(.*)$/;
|
|
2442
|
+
function maskedForHeadingSearch(body) {
|
|
2443
|
+
return maskDetailsBlocks(maskCode(body));
|
|
2444
|
+
}
|
|
2346
2445
|
function hasBacktickedPath(text) {
|
|
2347
2446
|
let i = 0;
|
|
2348
2447
|
while (i < text.length) {
|
|
@@ -2365,16 +2464,22 @@ function stripObjectiveBackticks(text) {
|
|
|
2365
2464
|
function wordCount(text) {
|
|
2366
2465
|
return text.split(/\s+/).filter((w) => /[a-z]/i.test(w)).length;
|
|
2367
2466
|
}
|
|
2368
|
-
function
|
|
2369
|
-
const
|
|
2467
|
+
function objectivesSectionBounds(body) {
|
|
2468
|
+
const masked = maskedForHeadingSearch(body);
|
|
2469
|
+
const heading = HEADING_RE.exec(masked);
|
|
2370
2470
|
if (!heading)
|
|
2371
2471
|
return null;
|
|
2372
|
-
const
|
|
2472
|
+
const start = heading.index + heading[0].length;
|
|
2473
|
+
const afterHeading = masked.slice(start);
|
|
2373
2474
|
const next = NEXT_HEADING_RE.exec(afterHeading);
|
|
2374
|
-
return next ?
|
|
2475
|
+
return { start, end: next ? start + next.index : body.length };
|
|
2476
|
+
}
|
|
2477
|
+
function objectivesSectionText(body) {
|
|
2478
|
+
const bounds = objectivesSectionBounds(body);
|
|
2479
|
+
return bounds === null ? null : body.slice(bounds.start, bounds.end);
|
|
2375
2480
|
}
|
|
2376
2481
|
function hasObjectivesHeading(body) {
|
|
2377
|
-
return HEADING_RE.test(body);
|
|
2482
|
+
return HEADING_RE.test(maskedForHeadingSearch(body));
|
|
2378
2483
|
}
|
|
2379
2484
|
function objectivesOf(body) {
|
|
2380
2485
|
const section = objectivesSectionText(body);
|
|
@@ -2434,6 +2539,15 @@ function isIssueNotFoundError(err) {
|
|
|
2434
2539
|
`);
|
|
2435
2540
|
return /could not resolve to an (?:issue|pull request)|\b404\b|not found/i.test(haystack);
|
|
2436
2541
|
}
|
|
2542
|
+
function resolveObjectivesSource(prBody, issue, cutoverIssue) {
|
|
2543
|
+
if (issue !== null && issue < cutoverIssue)
|
|
2544
|
+
return { kind: "none" };
|
|
2545
|
+
if (issue !== null)
|
|
2546
|
+
return { kind: "issue", issue };
|
|
2547
|
+
if (hasObjectivesHeading(prBody))
|
|
2548
|
+
return { kind: "body" };
|
|
2549
|
+
return { kind: "none" };
|
|
2550
|
+
}
|
|
2437
2551
|
|
|
2438
2552
|
// ../../packages/aeg-core/src/premise-check.ts
|
|
2439
2553
|
import { createHash as createHash3 } from "node:crypto";
|
|
@@ -2669,7 +2783,7 @@ function checkForField(prBody) {
|
|
|
2669
2783
|
]
|
|
2670
2784
|
};
|
|
2671
2785
|
}
|
|
2672
|
-
function
|
|
2786
|
+
function checkClosesNPresence(prBody) {
|
|
2673
2787
|
const closesPattern = /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s{0,8}:?\s{0,8}#\d+/i;
|
|
2674
2788
|
if (closesPattern.test(stripCode(prBody))) {
|
|
2675
2789
|
return { status: "pass", errors: [] };
|
|
@@ -2701,10 +2815,22 @@ var COMMIT_TYPES = [
|
|
|
2701
2815
|
"Test"
|
|
2702
2816
|
];
|
|
2703
2817
|
var COMMIT_TYPE_STYLE = new RegExp(`^(${COMMIT_TYPES.join("|")})(\\([a-z0-9-]+\\))?: \\S`);
|
|
2818
|
+
function checkForgeTitle(title) {
|
|
2819
|
+
const taskStyle = /^\[[a-z0-9._-]+\] \S+ — \S/;
|
|
2820
|
+
if (COMMIT_TYPE_STYLE.test(title) || taskStyle.test(title))
|
|
2821
|
+
return { status: "pass", errors: [] };
|
|
2822
|
+
return {
|
|
2823
|
+
status: "fail",
|
|
2824
|
+
errors: [
|
|
2825
|
+
`brief-validation title: "${title}" matches neither title grammar — expected \`Type: description\` / \`Type(scope): description\` (commitlint types + Plan) or \`[tranche] id — description\` (task form).`
|
|
2826
|
+
]
|
|
2827
|
+
};
|
|
2828
|
+
}
|
|
2704
2829
|
var BRIEF_SHAPE_MARKERS = [checkSurfaceMap, checkDocUpdateList, checkStopConditions, checkAutonomyClause];
|
|
2705
2830
|
var TASK_BRANCH_PATTERN = /^task\/[^/]+\/[^/]+$/;
|
|
2831
|
+
var TASK_ISSUE_BRANCH_PATTERN = /^task\/issue-\d+$/;
|
|
2706
2832
|
function isTaskBranch(branch) {
|
|
2707
|
-
return TASK_BRANCH_PATTERN.test(branch);
|
|
2833
|
+
return TASK_BRANCH_PATTERN.test(branch) || TASK_ISSUE_BRANCH_PATTERN.test(branch);
|
|
2708
2834
|
}
|
|
2709
2835
|
function isBriefShaped(prBody) {
|
|
2710
2836
|
const stripped = stripCode(prBody);
|
|
@@ -2826,8 +2952,9 @@ function packagesNamedIn(text) {
|
|
|
2826
2952
|
}
|
|
2827
2953
|
function hasTestPathForConsumer(text, consumerDir) {
|
|
2828
2954
|
const escaped = consumerDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2829
|
-
const
|
|
2830
|
-
|
|
2955
|
+
const filePathRe = new RegExp(`${escaped}\\/[\\w./-]*\\.test\\.[A-Za-z0-9]+`);
|
|
2956
|
+
const testDirRe = new RegExp(`${escaped}\\/(?:[\\w-]+\\/)*(?:tests|specs)(?:\\/|\\b)`);
|
|
2957
|
+
return filePathRe.test(text) || testDirRe.test(text);
|
|
2831
2958
|
}
|
|
2832
2959
|
function checkConsumerTests(prBody, consumersOf) {
|
|
2833
2960
|
const section4 = extractNumberedSection(prBody, 4);
|
|
@@ -2931,21 +3058,44 @@ function checkBriefSections(prBody, readTier, options = {}) {
|
|
|
2931
3058
|
checkConsumerTests(prBody, consumersOf),
|
|
2932
3059
|
checkDefeatCases(prBody),
|
|
2933
3060
|
...issueObjectives !== undefined ? [checkObjectivesCopy(prBody, issueObjectives), checkObjectivesCoverage(prBody)] : [],
|
|
2934
|
-
...requireClosesN ? [
|
|
3061
|
+
...requireClosesN ? [checkClosesNPresence(prBody)] : []
|
|
2935
3062
|
];
|
|
2936
3063
|
return { errors: results.flatMap((r) => r.errors) };
|
|
2937
3064
|
}
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
`
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
3065
|
+
function contentAfterNLines(body, n) {
|
|
3066
|
+
let idx = -1;
|
|
3067
|
+
for (let i = 0;i < n; i++) {
|
|
3068
|
+
idx = body.indexOf(`
|
|
3069
|
+
`, idx + 1);
|
|
3070
|
+
if (idx === -1)
|
|
3071
|
+
return "";
|
|
3072
|
+
}
|
|
3073
|
+
return body.slice(idx + 1);
|
|
3074
|
+
}
|
|
3075
|
+
var BRIEF_MARKER_LINE_RE = /^<!-- aeg:brief:v(\d+) -->$/;
|
|
3076
|
+
function parseBriefMarkerVersion(firstLine) {
|
|
3077
|
+
const m = BRIEF_MARKER_LINE_RE.exec(firstLine.trim());
|
|
3078
|
+
if (!m)
|
|
3079
|
+
return null;
|
|
3080
|
+
const version = Number.parseInt(m[1], 10);
|
|
3081
|
+
return Number.isInteger(version) && version >= 1 ? version : null;
|
|
3082
|
+
}
|
|
3083
|
+
function frozenBriefContent(body, version) {
|
|
3084
|
+
return contentAfterNLines(body, version === 1 ? 2 : 3);
|
|
3085
|
+
}
|
|
3086
|
+
function resolveNewestFrozenBrief(comments, allowlist) {
|
|
3087
|
+
let best = null;
|
|
3088
|
+
for (const c of comments) {
|
|
3089
|
+
if (!isPrincipal(c.author, allowlist))
|
|
3090
|
+
continue;
|
|
3091
|
+
const version = parseBriefMarkerVersion(c.body.split(`
|
|
3092
|
+
`)[0] ?? "");
|
|
3093
|
+
if (version === null)
|
|
3094
|
+
continue;
|
|
3095
|
+
if (best === null || version > best.version)
|
|
3096
|
+
best = { ...c, version };
|
|
3097
|
+
}
|
|
3098
|
+
return best === null ? null : { ...best, content: frozenBriefContent(best.body, best.version) };
|
|
2949
3099
|
}
|
|
2950
3100
|
// ../../packages/aeg-core/src/doctrine-portability.ts
|
|
2951
3101
|
var DEFAULT_SHIPS_PREFIX = "aeg-root/";
|
|
@@ -3198,6 +3348,28 @@ function topLevelSectionText(body, headingName) {
|
|
|
3198
3348
|
const next = /^##[ \t]/m.exec(afterHeading);
|
|
3199
3349
|
return next ? afterHeading.slice(0, next.index) : afterHeading;
|
|
3200
3350
|
}
|
|
3351
|
+
function partHasBacktickedPath(text) {
|
|
3352
|
+
let i = 0;
|
|
3353
|
+
while (i < text.length) {
|
|
3354
|
+
const start = text.indexOf("`", i);
|
|
3355
|
+
if (start === -1)
|
|
3356
|
+
return false;
|
|
3357
|
+
const end = text.indexOf("`", start + 1);
|
|
3358
|
+
if (end === -1)
|
|
3359
|
+
return false;
|
|
3360
|
+
if (text.slice(start + 1, end).includes("/"))
|
|
3361
|
+
return true;
|
|
3362
|
+
i = end + 1;
|
|
3363
|
+
}
|
|
3364
|
+
return false;
|
|
3365
|
+
}
|
|
3366
|
+
var PART_MIN_WORDS_OUTSIDE_BACKTICKS = 3;
|
|
3367
|
+
function stripPartBackticks(text) {
|
|
3368
|
+
return text.replace(/`[^`\n]*`/g, " ");
|
|
3369
|
+
}
|
|
3370
|
+
function partWordCount(text) {
|
|
3371
|
+
return text.split(/\s+/).filter((w) => /[a-z]/i.test(w)).length;
|
|
3372
|
+
}
|
|
3201
3373
|
function looksLikeFilePath(entry2) {
|
|
3202
3374
|
const stripped = entry2.replace(/\/\*\*?$/, "");
|
|
3203
3375
|
const lastSegment = stripped.split("/").pop() ?? stripped;
|
|
@@ -3238,6 +3410,18 @@ function globCoversPath(glob, path) {
|
|
|
3238
3410
|
const p = path.replace(/\/+$/, "");
|
|
3239
3411
|
return g === p || g.startsWith(`${p}/`) || p.startsWith(`${g}/`);
|
|
3240
3412
|
}
|
|
3413
|
+
function checkSurfaceGlobsResolve(body, resolvesToFile) {
|
|
3414
|
+
const surface = parseIssueSurface(body);
|
|
3415
|
+
if (!surface.ok)
|
|
3416
|
+
return { status: "pass", errors: [] };
|
|
3417
|
+
const errors = [];
|
|
3418
|
+
for (const glob of surface.value.in) {
|
|
3419
|
+
if (!resolvesToFile(glob)) {
|
|
3420
|
+
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.`);
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3424
|
+
}
|
|
3241
3425
|
function checkSurfaceScope(changedFiles, outGlobs) {
|
|
3242
3426
|
if (outGlobs.length === 0)
|
|
3243
3427
|
return { ok: true };
|
|
@@ -3249,9 +3433,77 @@ function checkSurfaceScope(changedFiles, outGlobs) {
|
|
|
3249
3433
|
}
|
|
3250
3434
|
return violations.length > 0 ? { ok: false, violations } : { ok: true };
|
|
3251
3435
|
}
|
|
3436
|
+
var ISSUE_PART_LINE_RE = /^Part\s+(\d+)\s*\(([^)]*)\)\s*[-—–]\s*(.*)$/i;
|
|
3437
|
+
function parseIssueParts(body) {
|
|
3438
|
+
const section = topLevelSectionText(body, "Parts");
|
|
3439
|
+
if (section === null)
|
|
3440
|
+
return { ok: false, errors: ["no `## Parts` heading found in the body."] };
|
|
3441
|
+
const lines = section.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
|
|
3442
|
+
const parts = [];
|
|
3443
|
+
const errors = [];
|
|
3444
|
+
for (const line of lines) {
|
|
3445
|
+
const m = ISSUE_PART_LINE_RE.exec(line);
|
|
3446
|
+
if (!m) {
|
|
3447
|
+
errors.push(`"${line}" is not a well-formed Parts line — expected \`Part <n> (<refs>) — <outcome>\`.`);
|
|
3448
|
+
continue;
|
|
3449
|
+
}
|
|
3450
|
+
const n = Number.parseInt(m[1], 10);
|
|
3451
|
+
const refs = m[2];
|
|
3452
|
+
const text = m[3].trim();
|
|
3453
|
+
if (text.length === 0) {
|
|
3454
|
+
errors.push(`Part ${n} has no outcome text after the dash — every Part states one observable outcome.`);
|
|
3455
|
+
continue;
|
|
3456
|
+
}
|
|
3457
|
+
if (partHasBacktickedPath(text) && partWordCount(stripPartBackticks(text)) < PART_MIN_WORDS_OUTSIDE_BACKTICKS) {
|
|
3458
|
+
errors.push(`Part ${n} is little more than a file path — a Part names an outcome and symbols, never a bare path.`);
|
|
3459
|
+
continue;
|
|
3460
|
+
}
|
|
3461
|
+
const objectiveIds = [...refs.matchAll(/O(\d+)/g)].map((r) => Number.parseInt(r[1], 10));
|
|
3462
|
+
parts.push({ n, objectiveIds, text });
|
|
3463
|
+
}
|
|
3464
|
+
if (parts.length === 0) {
|
|
3465
|
+
errors.push("the `## Parts` section has no well-formed `Part <n> (<refs>) — <outcome>` lines.");
|
|
3466
|
+
}
|
|
3467
|
+
if (errors.length > 0)
|
|
3468
|
+
return { ok: false, errors };
|
|
3469
|
+
return { ok: true, value: parts };
|
|
3470
|
+
}
|
|
3471
|
+
function checkPartsCiteDefinedObjectives(body) {
|
|
3472
|
+
const parts = parseIssueParts(body);
|
|
3473
|
+
const objectives = objectivesOf(body);
|
|
3474
|
+
if (!parts.ok || !objectives.ok)
|
|
3475
|
+
return { status: "pass", errors: [] };
|
|
3476
|
+
const definedIds = new Set(objectives.objectives.map((o) => Number.parseInt(o.id.slice(1), 10)));
|
|
3477
|
+
const errors = [];
|
|
3478
|
+
for (const part of parts.value) {
|
|
3479
|
+
for (const objectiveId of part.objectiveIds) {
|
|
3480
|
+
if (!definedIds.has(objectiveId)) {
|
|
3481
|
+
errors.push(`issue-validation Parts: Part ${part.n} cites O${objectiveId}, which the Issue's own \`## Objectives\` section does not define.`);
|
|
3482
|
+
}
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
return { status: errors.length > 0 ? "fail" : "pass", errors };
|
|
3486
|
+
}
|
|
3252
3487
|
function isTaskIssueLabelSet(labels) {
|
|
3253
3488
|
return hasLabel("tranche", labels);
|
|
3254
3489
|
}
|
|
3490
|
+
function checkTrancheLabelPresence(_body, _labels) {
|
|
3491
|
+
return { status: "pass", errors: [] };
|
|
3492
|
+
}
|
|
3493
|
+
function checkMilestoneAttach(labels, currentMilestoneTitle, resolvedMilestoneTitle) {
|
|
3494
|
+
if (!isTaskIssueLabelSet(labels))
|
|
3495
|
+
return { status: "pass", errors: [] };
|
|
3496
|
+
if (resolvedMilestoneTitle === null)
|
|
3497
|
+
return { status: "pass", errors: [] };
|
|
3498
|
+
if (currentMilestoneTitle === resolvedMilestoneTitle)
|
|
3499
|
+
return { status: "pass", errors: [] };
|
|
3500
|
+
return {
|
|
3501
|
+
status: "fail",
|
|
3502
|
+
errors: [
|
|
3503
|
+
`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}".`
|
|
3504
|
+
]
|
|
3505
|
+
};
|
|
3506
|
+
}
|
|
3255
3507
|
var TYPE_LABEL_IDS = LABELS.filter((l) => l.category === "type").map((l) => l.id);
|
|
3256
3508
|
function isControlCodePoint(codePoint) {
|
|
3257
3509
|
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
|
|
@@ -3316,6 +3568,19 @@ function checkProjectsRegistered(body, _labels, registeredNames) {
|
|
|
3316
3568
|
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;
|
|
3317
3569
|
var DOC_PATH_RE_GLOBAL = new RegExp(DOC_PATH_RE.source, "gi");
|
|
3318
3570
|
|
|
3571
|
+
// ../../packages/aeg-core/src/task-branch-identity.ts
|
|
3572
|
+
var ISSUE_BRANCH_PATTERN = /^task\/issue-(\d+)$/;
|
|
3573
|
+
var TRANCHE_BRANCH_PATTERN = /^task\/([^/]+)\/([^/]+)$/;
|
|
3574
|
+
function parseTaskBranchIdentity(branch) {
|
|
3575
|
+
const issueMatch = ISSUE_BRANCH_PATTERN.exec(branch);
|
|
3576
|
+
if (issueMatch)
|
|
3577
|
+
return { kind: "issue", issueNumber: Number(issueMatch[1]) };
|
|
3578
|
+
const trancheMatch = TRANCHE_BRANCH_PATTERN.exec(branch);
|
|
3579
|
+
if (trancheMatch)
|
|
3580
|
+
return { kind: "tranche", tranche: trancheMatch[1], taskId: trancheMatch[2] };
|
|
3581
|
+
return null;
|
|
3582
|
+
}
|
|
3583
|
+
|
|
3319
3584
|
// ../../packages/aeg-core/src/coherence-checks.ts
|
|
3320
3585
|
var COHERENCE_ENFORCED_FROM = "2026-07-01";
|
|
3321
3586
|
function isGrandfathered(isoDate) {
|
|
@@ -3336,6 +3601,7 @@ function checkA1(entries, principalAllowlist = PRINCIPAL_ALLOWLIST) {
|
|
|
3336
3601
|
if (handClosed)
|
|
3337
3602
|
continue;
|
|
3338
3603
|
failures.push({
|
|
3604
|
+
code: "closed-without-merge",
|
|
3339
3605
|
issue: e.task.issue,
|
|
3340
3606
|
tranche: e.trancheSlug,
|
|
3341
3607
|
task: e.task.id,
|
|
@@ -3360,6 +3626,7 @@ function checkA3(entries) {
|
|
|
3360
3626
|
continue;
|
|
3361
3627
|
if (e.facts.prState === "merged" && e.facts.issueState !== "closed") {
|
|
3362
3628
|
failures.push({
|
|
3629
|
+
code: "auto-close-misfire",
|
|
3363
3630
|
issue: e.task.issue,
|
|
3364
3631
|
tranche: e.trancheSlug,
|
|
3365
3632
|
task: e.task.id,
|
|
@@ -3384,6 +3651,7 @@ function checkT1(entries) {
|
|
|
3384
3651
|
continue;
|
|
3385
3652
|
if (e.facts === undefined) {
|
|
3386
3653
|
failures.push({
|
|
3654
|
+
code: "phantom-issue-ref",
|
|
3387
3655
|
issue: e.task.issue,
|
|
3388
3656
|
tranche: e.trancheSlug,
|
|
3389
3657
|
task: e.task.id,
|
|
@@ -3402,6 +3670,7 @@ function checkT2(openIssuesBySlug, topologyIssuesBySlug, ciTrancheSlug) {
|
|
|
3402
3670
|
for (const num of openNums) {
|
|
3403
3671
|
if (!topologySet.has(num)) {
|
|
3404
3672
|
failures.push({
|
|
3673
|
+
code: "orphan-task",
|
|
3405
3674
|
issue: num,
|
|
3406
3675
|
tranche: slug,
|
|
3407
3676
|
reason: `Issue #${num} is open and labeled ${trancheLabel(slug)} but does not appear in the topology file`
|
|
@@ -3436,6 +3705,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
|
|
|
3436
3705
|
continue;
|
|
3437
3706
|
if (forgeUnavailableSlugs?.has(e.trancheSlug)) {
|
|
3438
3707
|
failures.push({
|
|
3708
|
+
code: "tbd-in-active-tranche",
|
|
3439
3709
|
issue: null,
|
|
3440
3710
|
tranche: e.trancheSlug,
|
|
3441
3711
|
task: e.task.id,
|
|
@@ -3445,6 +3715,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
|
|
|
3445
3715
|
continue;
|
|
3446
3716
|
}
|
|
3447
3717
|
failures.push({
|
|
3718
|
+
code: "tbd-in-active-tranche",
|
|
3448
3719
|
issue: null,
|
|
3449
3720
|
tranche: e.trancheSlug,
|
|
3450
3721
|
task: e.task.id,
|
|
@@ -3477,6 +3748,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
|
|
|
3477
3748
|
const sameIssue = depEntry.task.issue !== null && e.task.issue !== null && depEntry.task.issue === e.task.issue;
|
|
3478
3749
|
if (sameTask || sameIssue) {
|
|
3479
3750
|
failures.push({
|
|
3751
|
+
code: "d1-self-dependency",
|
|
3480
3752
|
issue: e.task.issue,
|
|
3481
3753
|
tranche: e.trancheSlug,
|
|
3482
3754
|
task: e.task.id,
|
|
@@ -3488,6 +3760,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
|
|
|
3488
3760
|
const depClosed = depFacts?.issueState === "closed";
|
|
3489
3761
|
if (!depClosed) {
|
|
3490
3762
|
failures.push({
|
|
3763
|
+
code: "dispatched-on-unmet-deps",
|
|
3491
3764
|
issue: e.task.issue,
|
|
3492
3765
|
tranche: e.trancheSlug,
|
|
3493
3766
|
task: e.task.id,
|
|
@@ -3513,11 +3786,13 @@ function checkR1(issuesBySlug, grandfatheredIssues, registeredNames = []) {
|
|
|
3513
3786
|
const errors = [
|
|
3514
3787
|
...checkIssueRationale(issue.body).errors,
|
|
3515
3788
|
...checkProjectsRegistered(issue.body, issue.labels, registeredNames).errors,
|
|
3516
|
-
...checkIssueObjectives(issue.body, issue.number).errors
|
|
3789
|
+
...checkIssueObjectives(issue.body, issue.number).errors,
|
|
3790
|
+
...checkPartsCiteDefinedObjectives(issue.body).errors
|
|
3517
3791
|
];
|
|
3518
3792
|
if (errors.length === 0)
|
|
3519
3793
|
continue;
|
|
3520
3794
|
failures.push({
|
|
3795
|
+
code: "missing-rationale-field",
|
|
3521
3796
|
issue: issue.number,
|
|
3522
3797
|
tranche: slug,
|
|
3523
3798
|
reason: `Issue #${issue.number} fails the rationale gate: ${errors.join(" | ")}`,
|
|
@@ -3546,6 +3821,7 @@ function checkL1(files, entriesBySlug) {
|
|
|
3546
3821
|
const allClosed = withFacts.every((e) => e.facts?.issueState === "closed");
|
|
3547
3822
|
if (allClosed) {
|
|
3548
3823
|
failures.push({
|
|
3824
|
+
code: "archive-recommended",
|
|
3549
3825
|
tranche: f.slug,
|
|
3550
3826
|
reason: "Active tranche has no open task-Issues — consider archiving to completed/"
|
|
3551
3827
|
});
|
|
@@ -3577,27 +3853,38 @@ function extractClosesReferences(prBody) {
|
|
|
3577
3853
|
}
|
|
3578
3854
|
return referenced;
|
|
3579
3855
|
}
|
|
3580
|
-
function
|
|
3856
|
+
function checkClosesNTopology(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
3581
3857
|
const referenced = extractClosesReferences(prBody);
|
|
3582
3858
|
if (taskIssueRefs) {
|
|
3583
3859
|
for (const n of referenced) {
|
|
3584
|
-
const
|
|
3585
|
-
if (!
|
|
3860
|
+
const ref2 = taskIssueRefs.get(n);
|
|
3861
|
+
if (!ref2)
|
|
3586
3862
|
continue;
|
|
3587
|
-
const expectedBranch = `task/${
|
|
3863
|
+
const expectedBranch = `task/${ref2.trancheSlug}/${ref2.taskId}`;
|
|
3588
3864
|
if (branch !== expectedBranch) {
|
|
3589
3865
|
return {
|
|
3590
3866
|
ok: false,
|
|
3591
|
-
message: `closes-n-reverse: branch "${branch}" closes #${n} (task ${
|
|
3867
|
+
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.`
|
|
3592
3868
|
};
|
|
3593
3869
|
}
|
|
3594
3870
|
}
|
|
3595
3871
|
}
|
|
3596
|
-
const
|
|
3597
|
-
if (!
|
|
3872
|
+
const ref = parseTaskBranchIdentity(branch);
|
|
3873
|
+
if (!ref)
|
|
3598
3874
|
return { ok: true };
|
|
3599
|
-
|
|
3600
|
-
|
|
3875
|
+
if (ref.kind === "issue") {
|
|
3876
|
+
const expectedIssue2 = ref.issueNumber;
|
|
3877
|
+
if (!referenced.has(expectedIssue2)) {
|
|
3878
|
+
return {
|
|
3879
|
+
ok: false,
|
|
3880
|
+
expectedIssue: expectedIssue2,
|
|
3881
|
+
message: `closes-n: PR body does not contain \`Closes #${expectedIssue2}\` (required for branch "${branch}"). Add it to the PR body Summary section.`
|
|
3882
|
+
};
|
|
3883
|
+
}
|
|
3884
|
+
return { ok: true, expectedIssue: expectedIssue2 };
|
|
3885
|
+
}
|
|
3886
|
+
const trancheSlug = ref.tranche;
|
|
3887
|
+
const taskId = ref.taskId;
|
|
3601
3888
|
const trancheFile = trancheFiles.find((f) => f.slug === trancheSlug);
|
|
3602
3889
|
if (!trancheFile) {
|
|
3603
3890
|
return {
|
|
@@ -3649,29 +3936,67 @@ function extractIssue(body) {
|
|
|
3649
3936
|
const issue = headerNums.length > 0 ? headerNums[0] : bodyNums[0];
|
|
3650
3937
|
return { issue, extraIssues: bodyNums.filter((n) => n !== issue), outsideHeader: headerNums.length === 0 };
|
|
3651
3938
|
}
|
|
3652
|
-
// ../../packages/aeg-core/src/review-
|
|
3653
|
-
|
|
3654
|
-
|
|
3939
|
+
// ../../packages/aeg-core/src/review-input-manifest.ts
|
|
3940
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3941
|
+
function briefHash(brief) {
|
|
3942
|
+
return createHash4("sha256").update(`${brief}
|
|
3943
|
+
`).digest("hex");
|
|
3944
|
+
}
|
|
3945
|
+
function policyDigest(policy) {
|
|
3946
|
+
return createHash4("sha256").update(JSON.stringify({ codeReviewThreshold: policy.codeReviewThreshold, securityThreshold: policy.securityThreshold })).digest("hex");
|
|
3947
|
+
}
|
|
3948
|
+
function isBoundToHead(echoed, headSha) {
|
|
3949
|
+
if (!echoed.headSha)
|
|
3655
3950
|
return false;
|
|
3656
|
-
return headSha.toLowerCase().startsWith(
|
|
3951
|
+
return headSha.toLowerCase().startsWith(echoed.headSha.toLowerCase());
|
|
3657
3952
|
}
|
|
3658
|
-
function isBoundByPatchIdentity(
|
|
3659
|
-
if (patchIdOf === undefined || !
|
|
3953
|
+
function isBoundByPatchIdentity(echoed, headSha, patchIdOf) {
|
|
3954
|
+
if (patchIdOf === undefined || !echoed.headSha)
|
|
3660
3955
|
return false;
|
|
3661
|
-
const judged = patchIdOf(
|
|
3956
|
+
const judged = patchIdOf(echoed.headSha);
|
|
3662
3957
|
const current = patchIdOf(headSha);
|
|
3663
3958
|
if (judged === null || current === null)
|
|
3664
3959
|
return false;
|
|
3665
3960
|
return judged === current;
|
|
3666
3961
|
}
|
|
3667
|
-
function isBoundToPatch(
|
|
3668
|
-
return isBoundToHead(
|
|
3962
|
+
function isBoundToPatch(echoed, headSha, patchIdOf) {
|
|
3963
|
+
return isBoundToHead(echoed, headSha) || isBoundByPatchIdentity(echoed, headSha, patchIdOf);
|
|
3669
3964
|
}
|
|
3670
|
-
function isBoundToObjectives(
|
|
3965
|
+
function isBoundToObjectives(echoed, currentVersion) {
|
|
3671
3966
|
if (currentVersion === null)
|
|
3672
3967
|
return true;
|
|
3673
|
-
return
|
|
3968
|
+
return echoed.objectivesVersion === currentVersion;
|
|
3969
|
+
}
|
|
3970
|
+
function isBoundToRulings(echoed, currentOrdinal) {
|
|
3971
|
+
if (echoed.rulingOrdinal === null)
|
|
3972
|
+
return currentOrdinal === 0;
|
|
3973
|
+
return echoed.rulingOrdinal === currentOrdinal;
|
|
3674
3974
|
}
|
|
3975
|
+
function isBoundToBriefHash(echoed, currentHash) {
|
|
3976
|
+
if (currentHash === null)
|
|
3977
|
+
return true;
|
|
3978
|
+
return echoed.briefHash === currentHash;
|
|
3979
|
+
}
|
|
3980
|
+
function isBoundToPolicy(echoed, currentDigest) {
|
|
3981
|
+
return echoed.policyDigest === currentDigest;
|
|
3982
|
+
}
|
|
3983
|
+
function compareManifest(echoed, current, patchIdOf) {
|
|
3984
|
+
const head = isBoundToPatch(echoed, current.headSha, patchIdOf);
|
|
3985
|
+
const briefHashBound = isBoundToBriefHash(echoed, current.briefHash);
|
|
3986
|
+
const objectivesVersion2 = isBoundToObjectives(echoed, current.objectivesVersion);
|
|
3987
|
+
const rulingOrdinal = isBoundToRulings(echoed, current.rulingOrdinal);
|
|
3988
|
+
const policyDigestBound = isBoundToPolicy(echoed, current.policyDigest);
|
|
3989
|
+
return {
|
|
3990
|
+
bound: head && briefHashBound && objectivesVersion2 && rulingOrdinal && policyDigestBound,
|
|
3991
|
+
head,
|
|
3992
|
+
briefHash: briefHashBound,
|
|
3993
|
+
objectivesVersion: objectivesVersion2,
|
|
3994
|
+
rulingOrdinal,
|
|
3995
|
+
policyDigest: policyDigestBound
|
|
3996
|
+
};
|
|
3997
|
+
}
|
|
3998
|
+
|
|
3999
|
+
// ../../packages/aeg-core/src/review-gate.ts
|
|
3675
4000
|
function checkReviewGate(input) {
|
|
3676
4001
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
3677
4002
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -3694,13 +4019,55 @@ function checkReviewGate(input) {
|
|
|
3694
4019
|
const verifiedBodies = verified.map((c) => c.body);
|
|
3695
4020
|
const codeReview = extractCodeReviewVerdict(verifiedBodies);
|
|
3696
4021
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
3697
|
-
const
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
4022
|
+
const policy = input.policy ?? DEFAULT_REVIEW_POLICY;
|
|
4023
|
+
let codeReviewPolicyEvaluation;
|
|
4024
|
+
let securityPolicyEvaluation;
|
|
4025
|
+
try {
|
|
4026
|
+
codeReviewPolicyEvaluation = evaluateCodeReview(codeReview.findingSeverities, policy);
|
|
4027
|
+
securityPolicyEvaluation = evaluateSecurityReview(security.findingSeverities, policy);
|
|
4028
|
+
} catch (err) {
|
|
4029
|
+
return {
|
|
4030
|
+
verdict: "fail",
|
|
4031
|
+
reason: `a verdict comment carries a finding severity this repository's policy does not recognize: ${err instanceof Error ? err.message : String(err)}`,
|
|
4032
|
+
waived: false
|
|
4033
|
+
};
|
|
4034
|
+
}
|
|
4035
|
+
const codeReviewTextClean = codeReview.value === "APPROVE";
|
|
4036
|
+
const securityTextClean = security.value === "PASS";
|
|
4037
|
+
const codeReviewPolicyClean = codeReviewPolicyEvaluation.outcome === "clean";
|
|
4038
|
+
const securityPolicyClean = securityPolicyEvaluation.outcome === "clean";
|
|
4039
|
+
const codeReviewClean = codeReviewTextClean && codeReviewPolicyClean;
|
|
4040
|
+
const securityClean = securityTextClean && securityPolicyClean;
|
|
4041
|
+
const currentManifest = {
|
|
4042
|
+
headSha: input.headSha,
|
|
4043
|
+
briefHash: input.briefHash ?? null,
|
|
4044
|
+
objectivesVersion: input.objectivesVersion,
|
|
4045
|
+
rulingOrdinal: input.rulingOrdinal,
|
|
4046
|
+
policyDigest: policyDigest(policy)
|
|
4047
|
+
};
|
|
4048
|
+
const codeReviewEchoed = {
|
|
4049
|
+
headSha: codeReview.headSha,
|
|
4050
|
+
briefHash: codeReview.briefHash,
|
|
4051
|
+
objectivesVersion: codeReview.objectivesVersion,
|
|
4052
|
+
rulingOrdinal: codeReview.rulingOrdinal,
|
|
4053
|
+
policyDigest: codeReview.policyDigest
|
|
4054
|
+
};
|
|
4055
|
+
const securityEchoed = {
|
|
4056
|
+
headSha: security.headSha,
|
|
4057
|
+
briefHash: security.briefHash,
|
|
4058
|
+
objectivesVersion: security.objectivesVersion,
|
|
4059
|
+
rulingOrdinal: security.rulingOrdinal,
|
|
4060
|
+
policyDigest: security.policyDigest
|
|
4061
|
+
};
|
|
4062
|
+
const codeReviewBinding = compareManifest(codeReviewEchoed, currentManifest, input.patchIdOf);
|
|
4063
|
+
const securityBinding = compareManifest(securityEchoed, currentManifest, input.patchIdOf);
|
|
4064
|
+
const codeReviewBound = codeReviewBinding.head;
|
|
4065
|
+
const securityBound = securityBinding.head;
|
|
4066
|
+
const codeReviewObjectivesBound = codeReviewBinding.objectivesVersion;
|
|
4067
|
+
const securityObjectivesBound = securityBinding.objectivesVersion;
|
|
4068
|
+
const codeReviewRulingsBound = codeReviewBinding.rulingOrdinal;
|
|
4069
|
+
const securityRulingsBound = securityBinding.rulingOrdinal;
|
|
4070
|
+
if (codeReviewClean && codeReviewBinding.bound && securityClean && securityBinding.bound && mechanicalChecksClean) {
|
|
3704
4071
|
return {
|
|
3705
4072
|
verdict: "pass",
|
|
3706
4073
|
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.`,
|
|
@@ -3708,19 +4075,35 @@ function checkReviewGate(input) {
|
|
|
3708
4075
|
};
|
|
3709
4076
|
}
|
|
3710
4077
|
const problems = [];
|
|
3711
|
-
if (!
|
|
4078
|
+
if (!codeReviewTextClean) {
|
|
3712
4079
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
4080
|
+
} else if (!codeReviewPolicyClean) {
|
|
4081
|
+
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`);
|
|
3713
4082
|
} else if (!codeReviewBound) {
|
|
3714
4083
|
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3715
4084
|
} else if (!codeReviewObjectivesBound) {
|
|
3716
4085
|
problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
3717
|
-
}
|
|
3718
|
-
|
|
4086
|
+
} else if (!codeReviewRulingsBound) {
|
|
4087
|
+
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`);
|
|
4088
|
+
} else if (!codeReviewBinding.briefHash) {
|
|
4089
|
+
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"}`);
|
|
4090
|
+
} else if (!codeReviewBinding.policyDigest) {
|
|
4091
|
+
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}`);
|
|
4092
|
+
}
|
|
4093
|
+
if (!securityTextClean) {
|
|
3719
4094
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
4095
|
+
} else if (!securityPolicyClean) {
|
|
4096
|
+
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`);
|
|
3720
4097
|
} else if (!securityBound) {
|
|
3721
4098
|
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
3722
4099
|
} else if (!securityObjectivesBound) {
|
|
3723
4100
|
problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
|
|
4101
|
+
} else if (!securityRulingsBound) {
|
|
4102
|
+
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`);
|
|
4103
|
+
} else if (!securityBinding.briefHash) {
|
|
4104
|
+
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"}`);
|
|
4105
|
+
} else if (!securityBinding.policyDigest) {
|
|
4106
|
+
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}`);
|
|
3724
4107
|
}
|
|
3725
4108
|
if (!mechanicalChecksClean) {
|
|
3726
4109
|
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(", ")}`);
|
|
@@ -3737,6 +4120,24 @@ var DEFAULT_RELEASE_ACTOR = "github-actions[bot]";
|
|
|
3737
4120
|
function isChangesetsReleasePr(branch, author, expectedAuthor) {
|
|
3738
4121
|
return branch === CHANGESET_RELEASE_BRANCH && author === expectedAuthor;
|
|
3739
4122
|
}
|
|
4123
|
+
// ../../packages/aeg-core/src/ruling-ordinal.ts
|
|
4124
|
+
var RULING_MARKER_ORDINAL = /^<!-- aeg:principal:ruling:\d+-(\d+) -->$/;
|
|
4125
|
+
function newestPrincipalRulingOrdinal(comments, allowlist) {
|
|
4126
|
+
let best = 0;
|
|
4127
|
+
for (const c of comments) {
|
|
4128
|
+
if (!isPrincipal(c.author, allowlist))
|
|
4129
|
+
continue;
|
|
4130
|
+
const firstLine = (c.body.split(`
|
|
4131
|
+
`)[0] ?? "").trim();
|
|
4132
|
+
const m = RULING_MARKER_ORDINAL.exec(firstLine);
|
|
4133
|
+
if (!m)
|
|
4134
|
+
continue;
|
|
4135
|
+
const k = Number.parseInt(m[1], 10);
|
|
4136
|
+
if (k > best)
|
|
4137
|
+
best = k;
|
|
4138
|
+
}
|
|
4139
|
+
return best;
|
|
4140
|
+
}
|
|
3740
4141
|
// ../../packages/aeg-core/src/markdown-table.ts
|
|
3741
4142
|
function splitRow(line) {
|
|
3742
4143
|
const trimmed = line.trim().replace(/^\|/, "").replace(/\|$/, "");
|
|
@@ -4394,42 +4795,45 @@ function checkDispatchReadiness(input) {
|
|
|
4394
4795
|
const { trancheSlug, task } = input;
|
|
4395
4796
|
const taskLabel = `task ${task.id} (tranche ${trancheSlug})`;
|
|
4396
4797
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
4397
|
-
const
|
|
4798
|
+
const blockerDetails = [];
|
|
4799
|
+
const push = (blockerClass, message) => {
|
|
4800
|
+
blockerDetails.push({ class: blockerClass, message });
|
|
4801
|
+
};
|
|
4398
4802
|
if (task.issue === null) {
|
|
4399
|
-
|
|
4803
|
+
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.`);
|
|
4400
4804
|
} else if (input.issue === null) {
|
|
4401
|
-
|
|
4805
|
+
push("issue-existence", `dispatch-gate issue-existence: ${taskLabel} names Issue #${task.issue}, but it does not resolve to a real GitHub Issue (phantom reference).`);
|
|
4402
4806
|
}
|
|
4403
4807
|
if (input.issue !== null && !input.issueRationalePass) {
|
|
4404
|
-
|
|
4808
|
+
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.`);
|
|
4405
4809
|
}
|
|
4406
4810
|
for (const dep of input.dependsOn) {
|
|
4407
4811
|
if (isSelfDependency(dep, task, input.issue)) {
|
|
4408
4812
|
const issueStr = input.issue !== null ? `#${input.issue.number}` : "?";
|
|
4409
|
-
|
|
4813
|
+
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.`);
|
|
4410
4814
|
continue;
|
|
4411
4815
|
}
|
|
4412
4816
|
if (dep.resolved === false) {
|
|
4413
|
-
|
|
4817
|
+
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.`);
|
|
4414
4818
|
continue;
|
|
4415
4819
|
}
|
|
4416
4820
|
if (!dep.merged && !isHandClosedByRecognizedPrincipal(dep, principalAllowlist)) {
|
|
4417
4821
|
const issueStr = dep.issue !== null ? ` (#${dep.issue})` : "";
|
|
4418
|
-
|
|
4822
|
+
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.`);
|
|
4419
4823
|
}
|
|
4420
4824
|
}
|
|
4421
4825
|
for (const c of input.conflictsWith) {
|
|
4422
4826
|
if (c.openOrInFlight) {
|
|
4423
4827
|
const issueStr = c.issue !== null ? ` (#${c.issue})` : "";
|
|
4424
|
-
|
|
4828
|
+
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.`);
|
|
4425
4829
|
}
|
|
4426
4830
|
}
|
|
4427
4831
|
for (const proj of input.priorTrancheArchival) {
|
|
4428
4832
|
if (proj.priorTrancheSlug !== null && !proj.archived) {
|
|
4429
|
-
|
|
4833
|
+
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.`);
|
|
4430
4834
|
}
|
|
4431
4835
|
}
|
|
4432
|
-
return { ready:
|
|
4836
|
+
return { ready: blockerDetails.length === 0, blockers: blockerDetails.map((b) => b.message), blockerDetails };
|
|
4433
4837
|
}
|
|
4434
4838
|
// ../../packages/aeg-core/src/single-plan-pr.ts
|
|
4435
4839
|
function trancheSlugFromTopologyPath(path) {
|
|
@@ -4531,6 +4935,91 @@ function checkBranchTopology(input) {
|
|
|
4531
4935
|
reason: `Branch \`${branch}\` matches topology row \`${taskId}\` in ${topoPath}.`
|
|
4532
4936
|
};
|
|
4533
4937
|
}
|
|
4938
|
+
// ../../packages/aeg-core/src/task-tools.ts
|
|
4939
|
+
import { z } from "zod";
|
|
4940
|
+
var TASK_TOOL_ERROR_KINDS = [
|
|
4941
|
+
"validation",
|
|
4942
|
+
"authority",
|
|
4943
|
+
"precondition",
|
|
4944
|
+
"capability",
|
|
4945
|
+
"infrastructure",
|
|
4946
|
+
"cancellation",
|
|
4947
|
+
"timeout",
|
|
4948
|
+
"uncertain_effect"
|
|
4949
|
+
];
|
|
4950
|
+
var TaskToolErrorSchema = z.object({
|
|
4951
|
+
kind: z.enum(TASK_TOOL_ERROR_KINDS),
|
|
4952
|
+
message: z.string().min(1),
|
|
4953
|
+
detail: z.string().optional()
|
|
4954
|
+
});
|
|
4955
|
+
var TaskToolRefSchema = z.union([
|
|
4956
|
+
z.object({ tranche: z.string().min(1), id: z.string().min(1) }),
|
|
4957
|
+
z.object({ issue: z.number().int().positive() })
|
|
4958
|
+
]);
|
|
4959
|
+
var MAX_PAGE_LIMIT = 100;
|
|
4960
|
+
var PageRequestSchema = z.object({
|
|
4961
|
+
cursor: z.string().optional(),
|
|
4962
|
+
limit: z.number().int().positive().max(MAX_PAGE_LIMIT).optional()
|
|
4963
|
+
});
|
|
4964
|
+
var FreshnessSchema = z.enum(["fresh", "stale", "unknown"]);
|
|
4965
|
+
var ObservedSchema = z.object({
|
|
4966
|
+
observedAt: z.string(),
|
|
4967
|
+
freshness: FreshnessSchema
|
|
4968
|
+
});
|
|
4969
|
+
var TaskStatusInputSchema = z.object({
|
|
4970
|
+
task: TaskToolRefSchema.optional()
|
|
4971
|
+
}).merge(PageRequestSchema);
|
|
4972
|
+
var TaskStatusItemSchema = z.object({
|
|
4973
|
+
task: TaskToolRefSchema,
|
|
4974
|
+
issue: z.number().int().positive(),
|
|
4975
|
+
pr: z.number().int().positive().nullable(),
|
|
4976
|
+
state: z.string()
|
|
4977
|
+
}).merge(ObservedSchema);
|
|
4978
|
+
var TaskStatusResultSchema = z.object({
|
|
4979
|
+
items: z.array(TaskStatusItemSchema),
|
|
4980
|
+
nextCursor: z.string().nullable()
|
|
4981
|
+
});
|
|
4982
|
+
var RequestedAuthoritySchema = z.enum(["planner", "principal", "operator", "self"]);
|
|
4983
|
+
var EscalationInputsSchema = z.object({
|
|
4984
|
+
task: z.number().int().positive(),
|
|
4985
|
+
round: z.number().int().nonnegative(),
|
|
4986
|
+
head: z.string(),
|
|
4987
|
+
branch: z.string(),
|
|
4988
|
+
prNumber: z.number().int().positive()
|
|
4989
|
+
});
|
|
4990
|
+
var EscalationEvidenceSchema = z.object({
|
|
4991
|
+
round: z.number().int().nonnegative(),
|
|
4992
|
+
reviewer: z.string().nullable(),
|
|
4993
|
+
security: z.string().nullable()
|
|
4994
|
+
});
|
|
4995
|
+
var TaskEscalationReadInputSchema = z.object({
|
|
4996
|
+
task: TaskToolRefSchema
|
|
4997
|
+
}).merge(PageRequestSchema);
|
|
4998
|
+
var TaskEscalationPacketSchema = z.object({
|
|
4999
|
+
reason: z.string(),
|
|
5000
|
+
detail: z.string().nullable(),
|
|
5001
|
+
inputs: EscalationInputsSchema.nullable(),
|
|
5002
|
+
evidence: EscalationEvidenceSchema.nullable(),
|
|
5003
|
+
attemptedRecovery: z.string(),
|
|
5004
|
+
requestedAuthority: RequestedAuthoritySchema,
|
|
5005
|
+
permittedNextActions: z.array(z.string())
|
|
5006
|
+
}).merge(ObservedSchema);
|
|
5007
|
+
var TaskEscalationReadResultSchema = z.object({
|
|
5008
|
+
items: z.array(TaskEscalationPacketSchema),
|
|
5009
|
+
nextCursor: z.string().nullable()
|
|
5010
|
+
}).merge(ObservedSchema);
|
|
5011
|
+
var TaskStartInputSchema = z.object({
|
|
5012
|
+
tranche: z.string().min(1),
|
|
5013
|
+
id: z.string().min(1)
|
|
5014
|
+
});
|
|
5015
|
+
var TaskResumeInputSchema = z.object({
|
|
5016
|
+
task: TaskToolRefSchema
|
|
5017
|
+
});
|
|
5018
|
+
var TaskCancelInputSchema = z.object({
|
|
5019
|
+
task: TaskToolRefSchema,
|
|
5020
|
+
reason: z.string().min(1)
|
|
5021
|
+
});
|
|
5022
|
+
var NoResultSchema = z.never();
|
|
4534
5023
|
// ../../packages/aeg-core/src/first-push-dispatch-gate.ts
|
|
4535
5024
|
function parseTaskBranch(branch) {
|
|
4536
5025
|
const m = /^task\/([^/]+)\/([^/]+)$/.exec(branch);
|
|
@@ -4614,7 +5103,6 @@ function decideIssueAssignment(input) {
|
|
|
4614
5103
|
};
|
|
4615
5104
|
}
|
|
4616
5105
|
// ../../packages/aeg-core/src/test-plan-gate.ts
|
|
4617
|
-
var TASK_BRANCH_PATTERN2 = /^task\/[^/]+\/[^/]+$/;
|
|
4618
5106
|
function evaluateTestPlanGate(body, branch) {
|
|
4619
5107
|
if (!body) {
|
|
4620
5108
|
return {
|
|
@@ -4627,7 +5115,7 @@ function evaluateTestPlanGate(body, branch) {
|
|
|
4627
5115
|
}
|
|
4628
5116
|
const located = locateTestPlanSection(body);
|
|
4629
5117
|
if (!located.found) {
|
|
4630
|
-
if (
|
|
5118
|
+
if (parseTaskBranchIdentity(branch) !== null) {
|
|
4631
5119
|
return {
|
|
4632
5120
|
verdict: "fail",
|
|
4633
5121
|
messages: [
|
|
@@ -4755,7 +5243,7 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
|
|
|
4755
5243
|
return findings;
|
|
4756
5244
|
}
|
|
4757
5245
|
// ../../packages/aeg-core/src/log/schema.ts
|
|
4758
|
-
import { z } from "zod";
|
|
5246
|
+
import { z as z2 } from "zod";
|
|
4759
5247
|
var ROLE_VALUES = [
|
|
4760
5248
|
"planner",
|
|
4761
5249
|
"developer",
|
|
@@ -4765,136 +5253,172 @@ var ROLE_VALUES = [
|
|
|
4765
5253
|
"archivist",
|
|
4766
5254
|
"architect"
|
|
4767
5255
|
];
|
|
4768
|
-
var RoleSchema =
|
|
5256
|
+
var RoleSchema = z2.enum(ROLE_VALUES);
|
|
4769
5257
|
var HOST_VALUES = ["hook", "ci", "cli", "loop"];
|
|
4770
|
-
var HostSchema =
|
|
5258
|
+
var HostSchema = z2.enum(HOST_VALUES);
|
|
4771
5259
|
var RUN_ID_PATTERN = /^[A-Za-z0-9_.-]{1,128}$/;
|
|
4772
|
-
var
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
doctrine: z.string(),
|
|
5260
|
+
var headerMetaCore = {
|
|
5261
|
+
ts: z2.string(),
|
|
5262
|
+
run_id: z2.string().regex(RUN_ID_PATTERN),
|
|
5263
|
+
seq: z2.number().int().nonnegative(),
|
|
5264
|
+
repo: z2.string().nullable(),
|
|
5265
|
+
vinaya: z2.string(),
|
|
5266
|
+
doctrine: z2.string(),
|
|
4780
5267
|
host: HostSchema,
|
|
4781
|
-
machine:
|
|
5268
|
+
machine: z2.string()
|
|
5269
|
+
};
|
|
5270
|
+
var HeaderMetaV1Schema = z2.object({
|
|
5271
|
+
schema: z2.literal(1),
|
|
5272
|
+
...headerMetaCore
|
|
5273
|
+
}).strict();
|
|
5274
|
+
var LineageSchema = z2.object({
|
|
5275
|
+
run: z2.string().nullable(),
|
|
5276
|
+
attempt: z2.number().int().nullable(),
|
|
5277
|
+
parent: z2.string().nullable()
|
|
5278
|
+
}).strict();
|
|
5279
|
+
var InputVersionsSchema = z2.object({
|
|
5280
|
+
objectives_version: z2.string().nullable(),
|
|
5281
|
+
brief_hash: z2.string().nullable(),
|
|
5282
|
+
ruling_ordinal: z2.number().int().nullable(),
|
|
5283
|
+
policy_digest: z2.string().nullable()
|
|
5284
|
+
}).strict();
|
|
5285
|
+
var ProvenanceSchema = z2.enum(["parent_attributed", "env_correlated", "self_reported", "unavailable"]);
|
|
5286
|
+
var HeaderMetaV2Schema = z2.object({
|
|
5287
|
+
schema: z2.literal(2),
|
|
5288
|
+
...headerMetaCore,
|
|
5289
|
+
event_id: z2.string().min(1),
|
|
5290
|
+
process_id: z2.string().min(1),
|
|
5291
|
+
actor_id: z2.string().nullable(),
|
|
5292
|
+
lineage: LineageSchema,
|
|
5293
|
+
input_versions: InputVersionsSchema,
|
|
5294
|
+
provenance: ProvenanceSchema
|
|
4782
5295
|
}).strict();
|
|
4783
|
-
var
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
5296
|
+
var HeaderMetaSchema = z2.discriminatedUnion("schema", [HeaderMetaV1Schema, HeaderMetaV2Schema]);
|
|
5297
|
+
var SubjectSchema = z2.object({
|
|
5298
|
+
issue: z2.number().int().nullable(),
|
|
5299
|
+
pr: z2.number().int().optional(),
|
|
5300
|
+
sha: z2.string().optional(),
|
|
5301
|
+
role: z2.union([RoleSchema, z2.literal("unattributed")]),
|
|
5302
|
+
round: z2.number().int().optional(),
|
|
5303
|
+
objectives_version: z2.string().optional()
|
|
4790
5304
|
}).strict();
|
|
4791
|
-
var HeaderSchema =
|
|
5305
|
+
var HeaderSchema = z2.object({
|
|
4792
5306
|
meta: HeaderMetaSchema,
|
|
4793
5307
|
subject: SubjectSchema
|
|
4794
5308
|
}).strict();
|
|
4795
5309
|
var envelopeTail = {
|
|
4796
|
-
duration_ms:
|
|
4797
|
-
payload:
|
|
5310
|
+
duration_ms: z2.number().nonnegative().optional(),
|
|
5311
|
+
payload: z2.object({}).strict()
|
|
4798
5312
|
};
|
|
4799
|
-
var
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
5313
|
+
var ReviewFindingSchema = z2.object({
|
|
5314
|
+
id: z2.string(),
|
|
5315
|
+
severity: z2.string(),
|
|
5316
|
+
state: z2.string().optional(),
|
|
5317
|
+
severity_scale: z2.string().optional(),
|
|
5318
|
+
policy_treatment: z2.enum(["blocking", "non_blocking", "unavailable"]).optional(),
|
|
5319
|
+
confidence: z2.number().min(0).max(1).optional(),
|
|
5320
|
+
confidence_scale: z2.string().optional(),
|
|
5321
|
+
confidence_source: z2.string().optional()
|
|
5322
|
+
}).strict();
|
|
5323
|
+
var DispatchOutcomeSchema = z2.discriminatedUnion("type", [
|
|
5324
|
+
z2.object({ type: z2.literal("pr_opened"), pr: z2.number().int(), head: z2.string() }).strict(),
|
|
5325
|
+
z2.object({
|
|
5326
|
+
type: z2.literal("round_pushed"),
|
|
5327
|
+
pr: z2.number().int(),
|
|
5328
|
+
head: z2.string(),
|
|
5329
|
+
comment_id: z2.number().int()
|
|
4806
5330
|
}).strict(),
|
|
4807
|
-
|
|
4808
|
-
type:
|
|
4809
|
-
verdict:
|
|
4810
|
-
head:
|
|
4811
|
-
comment_id:
|
|
4812
|
-
objectives:
|
|
4813
|
-
findings:
|
|
5331
|
+
z2.object({
|
|
5332
|
+
type: z2.literal("verdict"),
|
|
5333
|
+
verdict: z2.enum(["APPROVE", "REQUEST CHANGES", "PASS", "FAIL"]),
|
|
5334
|
+
head: z2.string(),
|
|
5335
|
+
comment_id: z2.number().int(),
|
|
5336
|
+
objectives: z2.array(z2.object({ id: z2.string(), met: z2.boolean() }).strict()),
|
|
5337
|
+
findings: z2.array(ReviewFindingSchema)
|
|
4814
5338
|
}).strict(),
|
|
4815
|
-
|
|
4816
|
-
type:
|
|
4817
|
-
class:
|
|
4818
|
-
comment_id:
|
|
5339
|
+
z2.object({
|
|
5340
|
+
type: z2.literal("escalation"),
|
|
5341
|
+
class: z2.enum(["authority", "strategy", "product"]),
|
|
5342
|
+
comment_id: z2.number().int()
|
|
4819
5343
|
}).strict(),
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
5344
|
+
z2.object({ type: z2.literal("brief"), comment_id: z2.number().int(), hash: z2.string() }).strict(),
|
|
5345
|
+
z2.object({ type: z2.literal("plan"), issues: z2.array(z2.number().int()) }).strict(),
|
|
5346
|
+
z2.object({ type: z2.literal("archive"), provenance_comment_id: z2.number().int() }).strict()
|
|
4823
5347
|
]);
|
|
4824
5348
|
var dispatchShared = {
|
|
4825
5349
|
meta: HeaderMetaSchema,
|
|
4826
5350
|
subject: SubjectSchema,
|
|
4827
|
-
kind:
|
|
5351
|
+
kind: z2.literal("dispatch"),
|
|
4828
5352
|
...envelopeTail,
|
|
4829
5353
|
target_role: RoleSchema,
|
|
4830
|
-
model:
|
|
4831
|
-
round:
|
|
4832
|
-
effect_id:
|
|
5354
|
+
model: z2.string(),
|
|
5355
|
+
round: z2.number().int().optional(),
|
|
5356
|
+
effect_id: z2.string()
|
|
4833
5357
|
};
|
|
4834
|
-
var dispatchUsageField =
|
|
4835
|
-
var DispatchEventSchema =
|
|
4836
|
-
|
|
4837
|
-
|
|
5358
|
+
var dispatchUsageField = z2.object({ input: z2.number().nonnegative(), output: z2.number().nonnegative() }).strict().nullable();
|
|
5359
|
+
var DispatchEventSchema = z2.discriminatedUnion("event", [
|
|
5360
|
+
z2.object({ ...dispatchShared, event: z2.literal("dispatched"), prompt_hash: z2.string() }).strict(),
|
|
5361
|
+
z2.object({
|
|
4838
5362
|
...dispatchShared,
|
|
4839
|
-
event:
|
|
5363
|
+
event: z2.literal("outcome_received"),
|
|
4840
5364
|
outcome: DispatchOutcomeSchema,
|
|
4841
5365
|
usage: dispatchUsageField
|
|
4842
5366
|
}).strict(),
|
|
4843
|
-
|
|
5367
|
+
z2.object({
|
|
4844
5368
|
...dispatchShared,
|
|
4845
|
-
event:
|
|
4846
|
-
reason:
|
|
5369
|
+
event: z2.literal("dispatch_failed"),
|
|
5370
|
+
reason: z2.enum(["timeout", "crash", "refused", "unattributed_write"]),
|
|
4847
5371
|
usage: dispatchUsageField
|
|
4848
5372
|
}).strict()
|
|
4849
5373
|
]);
|
|
4850
5374
|
var loopShared = {
|
|
4851
5375
|
meta: HeaderMetaSchema,
|
|
4852
5376
|
subject: SubjectSchema,
|
|
4853
|
-
kind:
|
|
5377
|
+
kind: z2.literal("dev_review_loop"),
|
|
4854
5378
|
...envelopeTail,
|
|
4855
|
-
loop_id:
|
|
5379
|
+
loop_id: z2.string()
|
|
4856
5380
|
};
|
|
4857
|
-
var DevReviewLoopEventSchema =
|
|
4858
|
-
|
|
5381
|
+
var DevReviewLoopEventSchema = z2.discriminatedUnion("event", [
|
|
5382
|
+
z2.object({
|
|
4859
5383
|
...loopShared,
|
|
4860
|
-
event:
|
|
4861
|
-
task:
|
|
4862
|
-
policy:
|
|
4863
|
-
max_rounds:
|
|
4864
|
-
reviewers:
|
|
4865
|
-
models:
|
|
5384
|
+
event: z2.literal("loop_started"),
|
|
5385
|
+
task: z2.number().int(),
|
|
5386
|
+
policy: z2.object({
|
|
5387
|
+
max_rounds: z2.number().int().nonnegative(),
|
|
5388
|
+
reviewers: z2.array(RoleSchema),
|
|
5389
|
+
models: z2.record(RoleSchema, z2.string())
|
|
4866
5390
|
}).strict()
|
|
4867
5391
|
}).strict(),
|
|
4868
|
-
|
|
4869
|
-
|
|
5392
|
+
z2.object({ ...loopShared, event: z2.literal("round_started"), round: z2.number().int(), base_head: z2.string() }).strict(),
|
|
5393
|
+
z2.object({
|
|
4870
5394
|
...loopShared,
|
|
4871
|
-
event:
|
|
4872
|
-
round:
|
|
4873
|
-
head:
|
|
4874
|
-
green:
|
|
5395
|
+
event: z2.literal("gate_result_read"),
|
|
5396
|
+
round: z2.number().int(),
|
|
5397
|
+
head: z2.string(),
|
|
5398
|
+
green: z2.boolean()
|
|
4875
5399
|
}).strict(),
|
|
4876
|
-
|
|
5400
|
+
z2.object({
|
|
4877
5401
|
...loopShared,
|
|
4878
|
-
event:
|
|
4879
|
-
round:
|
|
4880
|
-
head:
|
|
4881
|
-
all_approve:
|
|
4882
|
-
blockers:
|
|
5402
|
+
event: z2.literal("verdicts_read"),
|
|
5403
|
+
round: z2.number().int(),
|
|
5404
|
+
head: z2.string(),
|
|
5405
|
+
all_approve: z2.boolean(),
|
|
5406
|
+
blockers: z2.number().int().nonnegative()
|
|
4883
5407
|
}).strict(),
|
|
4884
|
-
|
|
5408
|
+
z2.object({
|
|
4885
5409
|
...loopShared,
|
|
4886
|
-
event:
|
|
4887
|
-
round:
|
|
4888
|
-
open:
|
|
4889
|
-
resolved:
|
|
4890
|
-
new:
|
|
4891
|
-
recurring:
|
|
5410
|
+
event: z2.literal("findings_compared"),
|
|
5411
|
+
round: z2.number().int(),
|
|
5412
|
+
open: z2.array(z2.string()),
|
|
5413
|
+
resolved: z2.array(z2.string()),
|
|
5414
|
+
new: z2.array(z2.string()),
|
|
5415
|
+
recurring: z2.array(z2.string())
|
|
4892
5416
|
}).strict(),
|
|
4893
|
-
|
|
5417
|
+
z2.object({
|
|
4894
5418
|
...loopShared,
|
|
4895
|
-
event:
|
|
4896
|
-
round:
|
|
4897
|
-
condition:
|
|
5419
|
+
event: z2.literal("stop_condition_met"),
|
|
5420
|
+
round: z2.number().int(),
|
|
5421
|
+
condition: z2.enum([
|
|
4898
5422
|
"green",
|
|
4899
5423
|
"max_rounds",
|
|
4900
5424
|
"no_progress",
|
|
@@ -4904,42 +5428,49 @@ var DevReviewLoopEventSchema = z.discriminatedUnion("event", [
|
|
|
4904
5428
|
"reappearance"
|
|
4905
5429
|
])
|
|
4906
5430
|
}).strict(),
|
|
4907
|
-
|
|
5431
|
+
z2.object({
|
|
5432
|
+
...loopShared,
|
|
5433
|
+
event: z2.literal("paused"),
|
|
5434
|
+
round: z2.number().int(),
|
|
5435
|
+
reason: z2.enum(["escalation", "principal_item", "refreeze_needed"])
|
|
5436
|
+
}).strict(),
|
|
5437
|
+
z2.object({
|
|
4908
5438
|
...loopShared,
|
|
4909
|
-
event:
|
|
4910
|
-
round:
|
|
4911
|
-
|
|
5439
|
+
event: z2.literal("resumed"),
|
|
5440
|
+
round: z2.number().int(),
|
|
5441
|
+
by: z2.literal("principal")
|
|
4912
5442
|
}).strict(),
|
|
4913
|
-
|
|
5443
|
+
z2.object({
|
|
4914
5444
|
...loopShared,
|
|
4915
|
-
event:
|
|
4916
|
-
round:
|
|
4917
|
-
|
|
5445
|
+
event: z2.literal("unpushed_work_resume"),
|
|
5446
|
+
round: z2.number().int(),
|
|
5447
|
+
branch: z2.string(),
|
|
5448
|
+
detail: z2.string()
|
|
4918
5449
|
}).strict(),
|
|
4919
|
-
|
|
5450
|
+
z2.object({
|
|
4920
5451
|
...loopShared,
|
|
4921
|
-
event:
|
|
4922
|
-
round:
|
|
4923
|
-
base_head:
|
|
4924
|
-
head:
|
|
4925
|
-
files_changed:
|
|
4926
|
-
insertions:
|
|
4927
|
-
deletions:
|
|
4928
|
-
wall_ms:
|
|
4929
|
-
outcome:
|
|
5452
|
+
event: z2.literal("round_ended"),
|
|
5453
|
+
round: z2.number().int(),
|
|
5454
|
+
base_head: z2.string(),
|
|
5455
|
+
head: z2.string(),
|
|
5456
|
+
files_changed: z2.number().int().nonnegative(),
|
|
5457
|
+
insertions: z2.number().int().nonnegative(),
|
|
5458
|
+
deletions: z2.number().int().nonnegative(),
|
|
5459
|
+
wall_ms: z2.number().nonnegative(),
|
|
5460
|
+
outcome: z2.enum(["green", "changes_requested", "escalated"])
|
|
4930
5461
|
}).strict(),
|
|
4931
|
-
|
|
5462
|
+
z2.object({
|
|
4932
5463
|
...loopShared,
|
|
4933
|
-
event:
|
|
4934
|
-
rounds:
|
|
4935
|
-
total_wall_ms:
|
|
4936
|
-
time_to_green_ms:
|
|
4937
|
-
files_changed_total:
|
|
4938
|
-
final_head:
|
|
4939
|
-
result:
|
|
5464
|
+
event: z2.literal("journal_finalized"),
|
|
5465
|
+
rounds: z2.number().int().nonnegative(),
|
|
5466
|
+
total_wall_ms: z2.number().nonnegative(),
|
|
5467
|
+
time_to_green_ms: z2.number().nonnegative().nullable(),
|
|
5468
|
+
files_changed_total: z2.number().int().nonnegative(),
|
|
5469
|
+
final_head: z2.string(),
|
|
5470
|
+
result: z2.enum(["merged_ready", "stopped"])
|
|
4940
5471
|
}).strict()
|
|
4941
5472
|
]);
|
|
4942
|
-
var ForgeOpSchema =
|
|
5473
|
+
var ForgeOpSchema = z2.enum([
|
|
4943
5474
|
"pr.create",
|
|
4944
5475
|
"pr.comment",
|
|
4945
5476
|
"pr.body.replace",
|
|
@@ -4953,24 +5484,210 @@ var ForgeOpSchema = z.enum([
|
|
|
4953
5484
|
"label.add",
|
|
4954
5485
|
"label.remove"
|
|
4955
5486
|
]);
|
|
4956
|
-
var ForgeWriteTargetSchema =
|
|
4957
|
-
issue:
|
|
4958
|
-
pr:
|
|
5487
|
+
var ForgeWriteTargetSchema = z2.object({
|
|
5488
|
+
issue: z2.number().int().optional(),
|
|
5489
|
+
pr: z2.number().int().optional()
|
|
4959
5490
|
}).strict();
|
|
4960
5491
|
var forgeWriteShared = {
|
|
4961
5492
|
meta: HeaderMetaSchema,
|
|
4962
5493
|
subject: SubjectSchema,
|
|
4963
|
-
kind:
|
|
5494
|
+
kind: z2.literal("forge_write"),
|
|
4964
5495
|
...envelopeTail,
|
|
4965
5496
|
op: ForgeOpSchema,
|
|
4966
5497
|
target: ForgeWriteTargetSchema
|
|
4967
5498
|
};
|
|
4968
|
-
var ForgeWriteEventSchema =
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
5499
|
+
var ForgeWriteEventSchema = z2.discriminatedUnion("event", [
|
|
5500
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("validated") }).strict(),
|
|
5501
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("refused"), reason: z2.string() }).strict(),
|
|
5502
|
+
z2.object({ ...forgeWriteShared, event: z2.literal("written"), comment_ids: z2.array(z2.string()) }).strict()
|
|
4972
5503
|
]);
|
|
4973
|
-
var
|
|
5504
|
+
var GateOutcomeSchema = z2.enum([
|
|
5505
|
+
"pass",
|
|
5506
|
+
"fail",
|
|
5507
|
+
"wait",
|
|
5508
|
+
"skip",
|
|
5509
|
+
"invalid_input",
|
|
5510
|
+
"unavailable_dependency",
|
|
5511
|
+
"timeout",
|
|
5512
|
+
"cancelled"
|
|
5513
|
+
]);
|
|
5514
|
+
var gateShared = {
|
|
5515
|
+
meta: HeaderMetaSchema,
|
|
5516
|
+
subject: SubjectSchema,
|
|
5517
|
+
kind: z2.literal("gate"),
|
|
5518
|
+
...envelopeTail,
|
|
5519
|
+
check: z2.string(),
|
|
5520
|
+
check_version: z2.string().nullable(),
|
|
5521
|
+
policy_version: z2.string().nullable(),
|
|
5522
|
+
input_fingerprint: z2.string().nullable()
|
|
5523
|
+
};
|
|
5524
|
+
var GateEventSchema = z2.discriminatedUnion("event", [
|
|
5525
|
+
z2.object({
|
|
5526
|
+
...gateShared,
|
|
5527
|
+
event: z2.literal("checked"),
|
|
5528
|
+
outcome: GateOutcomeSchema,
|
|
5529
|
+
reason: z2.string().optional()
|
|
5530
|
+
}).strict()
|
|
5531
|
+
]);
|
|
5532
|
+
var OperationResultSchema = z2.enum(["ok", "error", "refused", "timeout", "cancelled", "unavailable"]);
|
|
5533
|
+
var operationShared = {
|
|
5534
|
+
meta: HeaderMetaSchema,
|
|
5535
|
+
subject: SubjectSchema,
|
|
5536
|
+
kind: z2.literal("operation"),
|
|
5537
|
+
...envelopeTail,
|
|
5538
|
+
operation: z2.string(),
|
|
5539
|
+
target: z2.string().nullable()
|
|
5540
|
+
};
|
|
5541
|
+
var OperationEventSchema = z2.discriminatedUnion("event", [
|
|
5542
|
+
z2.object({
|
|
5543
|
+
...operationShared,
|
|
5544
|
+
event: z2.literal("completed"),
|
|
5545
|
+
result: OperationResultSchema,
|
|
5546
|
+
error_class: z2.string().nullable()
|
|
5547
|
+
}).strict()
|
|
5548
|
+
]);
|
|
5549
|
+
var UsageUnitsSchema = z2.object({
|
|
5550
|
+
input: z2.number().nonnegative().nullable(),
|
|
5551
|
+
output: z2.number().nonnegative().nullable(),
|
|
5552
|
+
cache: z2.number().nonnegative().nullable()
|
|
5553
|
+
}).strict();
|
|
5554
|
+
var usageShared = {
|
|
5555
|
+
meta: HeaderMetaSchema,
|
|
5556
|
+
subject: SubjectSchema,
|
|
5557
|
+
kind: z2.literal("usage"),
|
|
5558
|
+
...envelopeTail,
|
|
5559
|
+
model: z2.string().nullable(),
|
|
5560
|
+
source: z2.string(),
|
|
5561
|
+
semantics: z2.enum(["cumulative", "delta"])
|
|
5562
|
+
};
|
|
5563
|
+
var UsageEventSchema = z2.discriminatedUnion("event", [
|
|
5564
|
+
z2.object({
|
|
5565
|
+
...usageShared,
|
|
5566
|
+
event: z2.literal("observed"),
|
|
5567
|
+
units: UsageUnitsSchema,
|
|
5568
|
+
unknown_reason: z2.string().nullable()
|
|
5569
|
+
}).strict()
|
|
5570
|
+
]);
|
|
5571
|
+
var RoleAttemptOutcomeSchema = z2.enum([
|
|
5572
|
+
"completed",
|
|
5573
|
+
"incomplete",
|
|
5574
|
+
"infrastructure_failed",
|
|
5575
|
+
"cancelled",
|
|
5576
|
+
"timed_out",
|
|
5577
|
+
"capability_refused"
|
|
5578
|
+
]);
|
|
5579
|
+
var roleAttemptShared = {
|
|
5580
|
+
meta: HeaderMetaSchema,
|
|
5581
|
+
subject: SubjectSchema,
|
|
5582
|
+
kind: z2.literal("role_attempt"),
|
|
5583
|
+
...envelopeTail,
|
|
5584
|
+
actor: z2.string().nullable(),
|
|
5585
|
+
attempt: z2.number().int().nullable()
|
|
5586
|
+
};
|
|
5587
|
+
var RoleAttemptEventSchema = z2.discriminatedUnion("event", [
|
|
5588
|
+
z2.object({
|
|
5589
|
+
...roleAttemptShared,
|
|
5590
|
+
event: z2.literal("attempted"),
|
|
5591
|
+
outcome: RoleAttemptOutcomeSchema,
|
|
5592
|
+
usage: dispatchUsageField
|
|
5593
|
+
}).strict()
|
|
5594
|
+
]);
|
|
5595
|
+
var handoffShared = {
|
|
5596
|
+
meta: HeaderMetaSchema,
|
|
5597
|
+
subject: SubjectSchema,
|
|
5598
|
+
kind: z2.literal("handoff"),
|
|
5599
|
+
...envelopeTail,
|
|
5600
|
+
class: z2.enum(["authority", "strategy", "product"]),
|
|
5601
|
+
reason: z2.string()
|
|
5602
|
+
};
|
|
5603
|
+
var HandoffEventSchema = z2.discriminatedUnion("event", [
|
|
5604
|
+
z2.object({
|
|
5605
|
+
...handoffShared,
|
|
5606
|
+
event: z2.literal("raised"),
|
|
5607
|
+
requested_decision: z2.string().nullable()
|
|
5608
|
+
}).strict(),
|
|
5609
|
+
z2.object({
|
|
5610
|
+
...handoffShared,
|
|
5611
|
+
event: z2.literal("resolved"),
|
|
5612
|
+
resolution: z2.string().nullable(),
|
|
5613
|
+
resolved_by: z2.string().nullable()
|
|
5614
|
+
}).strict()
|
|
5615
|
+
]);
|
|
5616
|
+
var EffectTargetSchema = z2.object({
|
|
5617
|
+
kind: z2.string(),
|
|
5618
|
+
ref: z2.string()
|
|
5619
|
+
}).strict();
|
|
5620
|
+
var effectShared = {
|
|
5621
|
+
meta: HeaderMetaSchema,
|
|
5622
|
+
subject: SubjectSchema,
|
|
5623
|
+
kind: z2.literal("effect"),
|
|
5624
|
+
...envelopeTail,
|
|
5625
|
+
effect_id: z2.string(),
|
|
5626
|
+
target: EffectTargetSchema
|
|
5627
|
+
};
|
|
5628
|
+
var EffectEventSchema = z2.discriminatedUnion("event", [
|
|
5629
|
+
z2.object({ ...effectShared, event: z2.literal("attempted") }).strict(),
|
|
5630
|
+
z2.object({ ...effectShared, event: z2.literal("observed"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict(),
|
|
5631
|
+
z2.object({ ...effectShared, event: z2.literal("verified"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict()
|
|
5632
|
+
]);
|
|
5633
|
+
var LogEventSchema = z2.union([
|
|
5634
|
+
DispatchEventSchema,
|
|
5635
|
+
DevReviewLoopEventSchema,
|
|
5636
|
+
ForgeWriteEventSchema,
|
|
5637
|
+
GateEventSchema,
|
|
5638
|
+
OperationEventSchema,
|
|
5639
|
+
UsageEventSchema,
|
|
5640
|
+
RoleAttemptEventSchema,
|
|
5641
|
+
HandoffEventSchema,
|
|
5642
|
+
EffectEventSchema
|
|
5643
|
+
]);
|
|
5644
|
+
// ../../packages/aeg-core/src/dev-review-loop/journal-reconstruction.ts
|
|
5645
|
+
var STOPPED_CONDITIONS = new Set(["confidence", "reappearance", "no_progress", "max_rounds"]);
|
|
5646
|
+
// ../../packages/aeg-core/src/control-store/records.ts
|
|
5647
|
+
import { z as z3 } from "zod";
|
|
5648
|
+
var isoTimestamp = z3.string().min(1);
|
|
5649
|
+
var taskId = z3.number().int().positive();
|
|
5650
|
+
var epochNumber = z3.number().int().nonnegative();
|
|
5651
|
+
var RunRecordSchema = z3.object({
|
|
5652
|
+
version: z3.literal(1),
|
|
5653
|
+
kind: z3.literal("run"),
|
|
5654
|
+
task: taskId,
|
|
5655
|
+
runId: z3.string().min(1),
|
|
5656
|
+
pid: z3.number().int().positive(),
|
|
5657
|
+
host: z3.string().min(1),
|
|
5658
|
+
startedAt: isoTimestamp
|
|
5659
|
+
}).strict();
|
|
5660
|
+
var InputRecordSchema = z3.object({
|
|
5661
|
+
version: z3.literal(1),
|
|
5662
|
+
kind: z3.literal("input"),
|
|
5663
|
+
task: taskId,
|
|
5664
|
+
runId: z3.string().min(1),
|
|
5665
|
+
source: z3.union([z3.literal("fresh"), z3.literal("resume")]),
|
|
5666
|
+
pr: z3.number().int().positive().nullable(),
|
|
5667
|
+
round: z3.number().int().nonnegative(),
|
|
5668
|
+
recordedAt: isoTimestamp
|
|
5669
|
+
}).strict();
|
|
5670
|
+
var OwnershipRecordSchema = z3.object({
|
|
5671
|
+
version: z3.literal(1),
|
|
5672
|
+
kind: z3.literal("ownership"),
|
|
5673
|
+
task: taskId,
|
|
5674
|
+
epoch: epochNumber,
|
|
5675
|
+
ownerId: z3.string().min(1),
|
|
5676
|
+
pid: z3.number().int().positive(),
|
|
5677
|
+
host: z3.string().min(1),
|
|
5678
|
+
acquiredAt: isoTimestamp
|
|
5679
|
+
}).strict();
|
|
5680
|
+
var TransitionRecordSchema = z3.object({
|
|
5681
|
+
version: z3.literal(1),
|
|
5682
|
+
kind: z3.literal("transition"),
|
|
5683
|
+
task: taskId,
|
|
5684
|
+
epoch: epochNumber,
|
|
5685
|
+
seq: z3.number().int().nonnegative(),
|
|
5686
|
+
from: z3.string().min(1),
|
|
5687
|
+
to: z3.string().min(1),
|
|
5688
|
+
detail: z3.string().optional(),
|
|
5689
|
+
at: isoTimestamp
|
|
5690
|
+
}).strict();
|
|
4974
5691
|
// src/checks/contract.ts
|
|
4975
5692
|
var CHECK_SCHEMA_VERSION = 1;
|
|
4976
5693
|
function emitCheckError(error) {
|
|
@@ -4980,24 +5697,40 @@ function emitCheckError(error) {
|
|
|
4980
5697
|
|
|
4981
5698
|
// src/checks/bin/check-test-plan.ts
|
|
4982
5699
|
var CHECK_NAME = "test-plan";
|
|
5700
|
+
function buildTestPlanCheckErrors(result) {
|
|
5701
|
+
if (result.verdict !== "fail")
|
|
5702
|
+
return [];
|
|
5703
|
+
const principalPending = result.messages.some((m) => m.startsWith("FAIL — the following [principal] Test Plan items are unticked:"));
|
|
5704
|
+
const errors = [];
|
|
5705
|
+
for (const message of result.messages) {
|
|
5706
|
+
if (!message)
|
|
5707
|
+
continue;
|
|
5708
|
+
errors.push({
|
|
5709
|
+
schema: CHECK_SCHEMA_VERSION,
|
|
5710
|
+
check: CHECK_NAME,
|
|
5711
|
+
severity: "error",
|
|
5712
|
+
message,
|
|
5713
|
+
agent_recovery_prompt: principalPending ? "Nothing for the Developer to fix here — wait for the Principal to verify in a real signed-in browser and tick each `[principal]` Test Plan box, then re-run `vinaya check test-plan`." : "Add a `## Test Plan` section to the PR body (or the `Test Plan: unit-tests-only` sentinel for a pure-logic brief with no runtime surface), then re-run `vinaya check test-plan`.",
|
|
5714
|
+
...principalPending ? { pending: true } : {}
|
|
5715
|
+
});
|
|
5716
|
+
}
|
|
5717
|
+
return errors;
|
|
5718
|
+
}
|
|
4983
5719
|
function main() {
|
|
4984
5720
|
const body = process.env.PR_BODY ?? "";
|
|
4985
5721
|
const branch = process.env.BRANCH ?? "";
|
|
4986
5722
|
const result = evaluateTestPlanGate(body, branch);
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
emitCheckError({
|
|
4992
|
-
schema: CHECK_SCHEMA_VERSION,
|
|
4993
|
-
check: CHECK_NAME,
|
|
4994
|
-
severity: "error",
|
|
4995
|
-
message,
|
|
4996
|
-
agent_recovery_prompt: "Run each `[agent]` Test Plan command from the PR head and let `vinaya pr report` write its output into the AEG:EVIDENCE block; leave `[principal]` boxes for the Principal to tick after verifying in a real signed-in browser. Re-run `vinaya check test-plan` afterwards."
|
|
4997
|
-
});
|
|
4998
|
-
}
|
|
5723
|
+
const errors = buildTestPlanCheckErrors(result);
|
|
5724
|
+
if (errors.length > 0) {
|
|
5725
|
+
for (const error of errors)
|
|
5726
|
+
emitCheckError(error);
|
|
4999
5727
|
process.exit(1);
|
|
5000
5728
|
}
|
|
5001
5729
|
process.exit(0);
|
|
5002
5730
|
}
|
|
5003
|
-
main
|
|
5731
|
+
if (__require.main == __require.module) {
|
|
5732
|
+
main();
|
|
5733
|
+
}
|
|
5734
|
+
export {
|
|
5735
|
+
buildTestPlanCheckErrors
|
|
5736
|
+
};
|