@attalabs/vinaya 0.27.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.
Files changed (48) hide show
  1. package/README.md +4 -3
  2. package/aeg-root/enforcement.md +4 -4
  3. package/aeg-root/roles/developer.md +23 -17
  4. package/aeg-root/roles/planner.md +2 -0
  5. package/aeg-root/roles/principal.md +4 -0
  6. package/aeg-root/roles/reviewer.md +3 -3
  7. package/aeg-root/roles/security.md +3 -3
  8. package/dist/checks/bin/check-body-bare-digits.js +915 -240
  9. package/dist/checks/bin/check-branch-topology.js +820 -180
  10. package/dist/checks/bin/check-brief-shape.js +915 -240
  11. package/dist/checks/bin/check-changeset-coverage.js +1534 -262
  12. package/dist/checks/bin/check-closes-n.js +824 -184
  13. package/dist/checks/bin/check-coherence.js +984 -265
  14. package/dist/checks/bin/check-dead-branch-push.js +805 -176
  15. package/dist/checks/bin/check-dispatch-readiness.js +1046 -276
  16. package/dist/checks/bin/check-doc-coverage-push.js +1530 -258
  17. package/dist/checks/bin/check-doc-coverage.js +1532 -260
  18. package/dist/checks/bin/check-doctrine-no-procedures.js +915 -240
  19. package/dist/checks/bin/check-doctrine-portability.js +1529 -257
  20. package/dist/checks/bin/check-evidence-fresh.js +1875 -258
  21. package/dist/checks/bin/check-exec-bits.js +1527 -255
  22. package/dist/checks/bin/check-first-push-dispatch.js +932 -246
  23. package/dist/checks/bin/check-issue-assignment.js +822 -182
  24. package/dist/checks/bin/check-issue-milestone-attach.js +5734 -0
  25. package/dist/checks/bin/check-issue-objectives-numbering.js +5736 -0
  26. package/dist/checks/bin/check-issue-parts-coverage.js +5736 -0
  27. package/dist/checks/bin/check-issue-surface-globs.js +6910 -0
  28. package/dist/checks/bin/check-issue-title-grammar.js +5736 -0
  29. package/dist/checks/bin/check-issue-tranche-label.js +5736 -0
  30. package/dist/checks/bin/check-main-branch-refusal.js +805 -176
  31. package/dist/checks/bin/check-no-disk-state.js +805 -176
  32. package/dist/checks/bin/check-pr-premise-reassert.js +915 -240
  33. package/dist/checks/bin/check-pr-report-density.js +805 -176
  34. package/dist/checks/bin/check-quoted-command.js +1508 -255
  35. package/dist/checks/bin/check-reader-resolvable-prose.js +1512 -259
  36. package/dist/checks/bin/check-registry-gates.js +892 -176
  37. package/dist/checks/bin/check-retired-vocabulary.js +1506 -253
  38. package/dist/checks/bin/check-review-gate.js +1000 -245
  39. package/dist/checks/bin/check-single-plan-pr.js +805 -176
  40. package/dist/checks/bin/check-surface-scope.js +830 -182
  41. package/dist/checks/bin/check-test-plan.js +834 -189
  42. package/dist/checks/bin/check-token-collection-wired.js +805 -176
  43. package/dist/checks/bin/check-token-report.js +805 -176
  44. package/dist/checks/bin/check-workspace-escape.js +1525 -253
  45. package/dist/index.js +9835 -5597
  46. package/dist/lib/pre-push-changed-files.js +57 -0
  47. package/dist/lib/pre-push-select-tests.js +1473 -0
  48. package/package.json +4 -2
@@ -1951,6 +1951,8 @@ function parseInlineFieldList(section) {
1951
1951
  var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
1952
1952
  var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
1953
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;
1954
1956
  function firstFiveLines(comment) {
1955
1957
  return comment.split(`
1956
1958
  `).slice(0, 5).join(`
@@ -1973,6 +1975,26 @@ function extractRulingOrdinal(comment) {
1973
1975
  const m = firstSevenLines(comment).match(RULING_ORDINAL_PATTERN);
1974
1976
  return m ? Number.parseInt(m[1], 10) : null;
1975
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
+ }
1976
1998
  function extractVerdict(comments, valuePattern, missingLabel) {
1977
1999
  const candidates = comments.filter((c) => valuePattern.test(c));
1978
2000
  if (candidates.length === 0) {
@@ -1981,6 +2003,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
1981
2003
  headSha: null,
1982
2004
  objectivesVersion: null,
1983
2005
  rulingOrdinal: null,
2006
+ briefHash: null,
2007
+ policyDigest: null,
2008
+ findingSeverities: [],
1984
2009
  danglingNote: `no ${missingLabel} verdict comment found on this PR`
1985
2010
  };
1986
2011
  }
@@ -1992,6 +2017,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
1992
2017
  headSha: null,
1993
2018
  objectivesVersion: null,
1994
2019
  rulingOrdinal: null,
2020
+ briefHash: null,
2021
+ policyDigest: null,
2022
+ findingSeverities: [],
1995
2023
  danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
1996
2024
  };
1997
2025
  }
@@ -2000,6 +2028,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
2000
2028
  headSha: extractHeadSha(latest),
2001
2029
  objectivesVersion: extractObjectivesVersion(latest),
2002
2030
  rulingOrdinal: extractRulingOrdinal(latest),
2031
+ briefHash: extractBriefHash(latest),
2032
+ policyDigest: extractPolicyDigest(latest),
2033
+ findingSeverities: extractFindingSeverities(latest),
2003
2034
  danglingNote: null
2004
2035
  };
2005
2036
  }
@@ -2335,6 +2366,53 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive,
2335
2366
  }
2336
2367
  return out;
2337
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
+ }
2338
2416
  // ../../packages/aeg-core/src/pr-tier.ts
2339
2417
  var TIER_FIELD = /(\*\*)?\s*Tier\s*(\*\*)?\s*:\s*(\*\*)?\s*([013])\b/i;
2340
2418
  function readTierFromPrBody(prBody) {
@@ -2705,7 +2783,7 @@ function checkForField(prBody) {
2705
2783
  ]
2706
2784
  };
2707
2785
  }
2708
- function checkClosesN(prBody) {
2786
+ function checkClosesNPresence(prBody) {
2709
2787
  const closesPattern = /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s{0,8}:?\s{0,8}#\d+/i;
2710
2788
  if (closesPattern.test(stripCode(prBody))) {
2711
2789
  return { status: "pass", errors: [] };
@@ -2737,10 +2815,22 @@ var COMMIT_TYPES = [
2737
2815
  "Test"
2738
2816
  ];
2739
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
+ }
2740
2829
  var BRIEF_SHAPE_MARKERS = [checkSurfaceMap, checkDocUpdateList, checkStopConditions, checkAutonomyClause];
2741
2830
  var TASK_BRANCH_PATTERN = /^task\/[^/]+\/[^/]+$/;
2831
+ var TASK_ISSUE_BRANCH_PATTERN = /^task\/issue-\d+$/;
2742
2832
  function isTaskBranch(branch) {
2743
- return TASK_BRANCH_PATTERN.test(branch);
2833
+ return TASK_BRANCH_PATTERN.test(branch) || TASK_ISSUE_BRANCH_PATTERN.test(branch);
2744
2834
  }
2745
2835
  function isBriefShaped(prBody) {
2746
2836
  const stripped = stripCode(prBody);
@@ -2862,8 +2952,9 @@ function packagesNamedIn(text) {
2862
2952
  }
2863
2953
  function hasTestPathForConsumer(text, consumerDir) {
2864
2954
  const escaped = consumerDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2865
- const re = new RegExp(`${escaped}\\/[\\w./-]*\\.test\\.[A-Za-z0-9]+`);
2866
- return re.test(text);
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);
2867
2958
  }
2868
2959
  function checkConsumerTests(prBody, consumersOf) {
2869
2960
  const section4 = extractNumberedSection(prBody, 4);
@@ -2967,7 +3058,7 @@ function checkBriefSections(prBody, readTier, options = {}) {
2967
3058
  checkConsumerTests(prBody, consumersOf),
2968
3059
  checkDefeatCases(prBody),
2969
3060
  ...issueObjectives !== undefined ? [checkObjectivesCopy(prBody, issueObjectives), checkObjectivesCoverage(prBody)] : [],
2970
- ...requireClosesN ? [checkClosesN(prBody)] : []
3061
+ ...requireClosesN ? [checkClosesNPresence(prBody)] : []
2971
3062
  ];
2972
3063
  return { errors: results.flatMap((r) => r.errors) };
2973
3064
  }
@@ -3257,6 +3348,28 @@ function topLevelSectionText(body, headingName) {
3257
3348
  const next = /^##[ \t]/m.exec(afterHeading);
3258
3349
  return next ? afterHeading.slice(0, next.index) : afterHeading;
3259
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
+ }
3260
3373
  function looksLikeFilePath(entry2) {
3261
3374
  const stripped = entry2.replace(/\/\*\*?$/, "");
3262
3375
  const lastSegment = stripped.split("/").pop() ?? stripped;
@@ -3297,6 +3410,18 @@ function globCoversPath(glob, path) {
3297
3410
  const p = path.replace(/\/+$/, "");
3298
3411
  return g === p || g.startsWith(`${p}/`) || p.startsWith(`${g}/`);
3299
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
+ }
3300
3425
  function checkSurfaceScope(changedFiles, outGlobs) {
3301
3426
  if (outGlobs.length === 0)
3302
3427
  return { ok: true };
@@ -3308,9 +3433,77 @@ function checkSurfaceScope(changedFiles, outGlobs) {
3308
3433
  }
3309
3434
  return violations.length > 0 ? { ok: false, violations } : { ok: true };
3310
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
+ }
3311
3487
  function isTaskIssueLabelSet(labels) {
3312
3488
  return hasLabel("tranche", labels);
3313
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
+ }
3314
3507
  var TYPE_LABEL_IDS = LABELS.filter((l) => l.category === "type").map((l) => l.id);
3315
3508
  function isControlCodePoint(codePoint) {
3316
3509
  return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
@@ -3375,6 +3568,19 @@ function checkProjectsRegistered(body, _labels, registeredNames) {
3375
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;
3376
3569
  var DOC_PATH_RE_GLOBAL = new RegExp(DOC_PATH_RE.source, "gi");
3377
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
+
3378
3584
  // ../../packages/aeg-core/src/coherence-checks.ts
3379
3585
  var COHERENCE_ENFORCED_FROM = "2026-07-01";
3380
3586
  function isGrandfathered(isoDate) {
@@ -3395,6 +3601,7 @@ function checkA1(entries, principalAllowlist = PRINCIPAL_ALLOWLIST) {
3395
3601
  if (handClosed)
3396
3602
  continue;
3397
3603
  failures.push({
3604
+ code: "closed-without-merge",
3398
3605
  issue: e.task.issue,
3399
3606
  tranche: e.trancheSlug,
3400
3607
  task: e.task.id,
@@ -3419,6 +3626,7 @@ function checkA3(entries) {
3419
3626
  continue;
3420
3627
  if (e.facts.prState === "merged" && e.facts.issueState !== "closed") {
3421
3628
  failures.push({
3629
+ code: "auto-close-misfire",
3422
3630
  issue: e.task.issue,
3423
3631
  tranche: e.trancheSlug,
3424
3632
  task: e.task.id,
@@ -3443,6 +3651,7 @@ function checkT1(entries) {
3443
3651
  continue;
3444
3652
  if (e.facts === undefined) {
3445
3653
  failures.push({
3654
+ code: "phantom-issue-ref",
3446
3655
  issue: e.task.issue,
3447
3656
  tranche: e.trancheSlug,
3448
3657
  task: e.task.id,
@@ -3461,6 +3670,7 @@ function checkT2(openIssuesBySlug, topologyIssuesBySlug, ciTrancheSlug) {
3461
3670
  for (const num of openNums) {
3462
3671
  if (!topologySet.has(num)) {
3463
3672
  failures.push({
3673
+ code: "orphan-task",
3464
3674
  issue: num,
3465
3675
  tranche: slug,
3466
3676
  reason: `Issue #${num} is open and labeled ${trancheLabel(slug)} but does not appear in the topology file`
@@ -3495,6 +3705,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
3495
3705
  continue;
3496
3706
  if (forgeUnavailableSlugs?.has(e.trancheSlug)) {
3497
3707
  failures.push({
3708
+ code: "tbd-in-active-tranche",
3498
3709
  issue: null,
3499
3710
  tranche: e.trancheSlug,
3500
3711
  task: e.task.id,
@@ -3504,6 +3715,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
3504
3715
  continue;
3505
3716
  }
3506
3717
  failures.push({
3718
+ code: "tbd-in-active-tranche",
3507
3719
  issue: null,
3508
3720
  tranche: e.trancheSlug,
3509
3721
  task: e.task.id,
@@ -3536,6 +3748,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
3536
3748
  const sameIssue = depEntry.task.issue !== null && e.task.issue !== null && depEntry.task.issue === e.task.issue;
3537
3749
  if (sameTask || sameIssue) {
3538
3750
  failures.push({
3751
+ code: "d1-self-dependency",
3539
3752
  issue: e.task.issue,
3540
3753
  tranche: e.trancheSlug,
3541
3754
  task: e.task.id,
@@ -3547,6 +3760,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
3547
3760
  const depClosed = depFacts?.issueState === "closed";
3548
3761
  if (!depClosed) {
3549
3762
  failures.push({
3763
+ code: "dispatched-on-unmet-deps",
3550
3764
  issue: e.task.issue,
3551
3765
  tranche: e.trancheSlug,
3552
3766
  task: e.task.id,
@@ -3572,11 +3786,13 @@ function checkR1(issuesBySlug, grandfatheredIssues, registeredNames = []) {
3572
3786
  const errors = [
3573
3787
  ...checkIssueRationale(issue.body).errors,
3574
3788
  ...checkProjectsRegistered(issue.body, issue.labels, registeredNames).errors,
3575
- ...checkIssueObjectives(issue.body, issue.number).errors
3789
+ ...checkIssueObjectives(issue.body, issue.number).errors,
3790
+ ...checkPartsCiteDefinedObjectives(issue.body).errors
3576
3791
  ];
3577
3792
  if (errors.length === 0)
3578
3793
  continue;
3579
3794
  failures.push({
3795
+ code: "missing-rationale-field",
3580
3796
  issue: issue.number,
3581
3797
  tranche: slug,
3582
3798
  reason: `Issue #${issue.number} fails the rationale gate: ${errors.join(" | ")}`,
@@ -3605,6 +3821,7 @@ function checkL1(files, entriesBySlug) {
3605
3821
  const allClosed = withFacts.every((e) => e.facts?.issueState === "closed");
3606
3822
  if (allClosed) {
3607
3823
  failures.push({
3824
+ code: "archive-recommended",
3608
3825
  tranche: f.slug,
3609
3826
  reason: "Active tranche has no open task-Issues — consider archiving to completed/"
3610
3827
  });
@@ -3636,27 +3853,38 @@ function extractClosesReferences(prBody) {
3636
3853
  }
3637
3854
  return referenced;
3638
3855
  }
3639
- function checkClosesN2(branch, prBody, trancheFiles, taskIssueRefs) {
3856
+ function checkClosesNTopology(branch, prBody, trancheFiles, taskIssueRefs) {
3640
3857
  const referenced = extractClosesReferences(prBody);
3641
3858
  if (taskIssueRefs) {
3642
3859
  for (const n of referenced) {
3643
- const ref = taskIssueRefs.get(n);
3644
- if (!ref)
3860
+ const ref2 = taskIssueRefs.get(n);
3861
+ if (!ref2)
3645
3862
  continue;
3646
- const expectedBranch = `task/${ref.trancheSlug}/${ref.taskId}`;
3863
+ const expectedBranch = `task/${ref2.trancheSlug}/${ref2.taskId}`;
3647
3864
  if (branch !== expectedBranch) {
3648
3865
  return {
3649
3866
  ok: false,
3650
- message: `closes-n-reverse: branch "${branch}" closes #${n} (task ${ref.taskId} of tranche "${ref.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.`
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.`
3651
3868
  };
3652
3869
  }
3653
3870
  }
3654
3871
  }
3655
- const m = branch.match(/^task\/([^/]+)\/([^/]+)$/);
3656
- if (!m)
3872
+ const ref = parseTaskBranchIdentity(branch);
3873
+ if (!ref)
3657
3874
  return { ok: true };
3658
- const trancheSlug = m[1];
3659
- const taskId = m[2];
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;
3660
3888
  const trancheFile = trancheFiles.find((f) => f.slug === trancheSlug);
3661
3889
  if (!trancheFile) {
3662
3890
  return {
@@ -3708,34 +3936,67 @@ function extractIssue(body) {
3708
3936
  const issue = headerNums.length > 0 ? headerNums[0] : bodyNums[0];
3709
3937
  return { issue, extraIssues: bodyNums.filter((n) => n !== issue), outsideHeader: headerNums.length === 0 };
3710
3938
  }
3711
- // ../../packages/aeg-core/src/review-gate.ts
3712
- function isBoundToHead(extraction, headSha) {
3713
- if (!extraction.headSha)
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)
3714
3950
  return false;
3715
- return headSha.toLowerCase().startsWith(extraction.headSha.toLowerCase());
3951
+ return headSha.toLowerCase().startsWith(echoed.headSha.toLowerCase());
3716
3952
  }
3717
- function isBoundByPatchIdentity(extraction, headSha, patchIdOf) {
3718
- if (patchIdOf === undefined || !extraction.headSha)
3953
+ function isBoundByPatchIdentity(echoed, headSha, patchIdOf) {
3954
+ if (patchIdOf === undefined || !echoed.headSha)
3719
3955
  return false;
3720
- const judged = patchIdOf(extraction.headSha);
3956
+ const judged = patchIdOf(echoed.headSha);
3721
3957
  const current = patchIdOf(headSha);
3722
3958
  if (judged === null || current === null)
3723
3959
  return false;
3724
3960
  return judged === current;
3725
3961
  }
3726
- function isBoundToPatch(extraction, headSha, patchIdOf) {
3727
- return isBoundToHead(extraction, headSha) || isBoundByPatchIdentity(extraction, headSha, patchIdOf);
3962
+ function isBoundToPatch(echoed, headSha, patchIdOf) {
3963
+ return isBoundToHead(echoed, headSha) || isBoundByPatchIdentity(echoed, headSha, patchIdOf);
3728
3964
  }
3729
- function isBoundToObjectives(extraction, currentVersion) {
3965
+ function isBoundToObjectives(echoed, currentVersion) {
3730
3966
  if (currentVersion === null)
3731
3967
  return true;
3732
- return extraction.objectivesVersion === currentVersion;
3968
+ return echoed.objectivesVersion === currentVersion;
3733
3969
  }
3734
- function isBoundToRulings(extraction, currentOrdinal) {
3735
- if (extraction.rulingOrdinal === null)
3970
+ function isBoundToRulings(echoed, currentOrdinal) {
3971
+ if (echoed.rulingOrdinal === null)
3736
3972
  return currentOrdinal === 0;
3737
- return extraction.rulingOrdinal === currentOrdinal;
3973
+ return echoed.rulingOrdinal === currentOrdinal;
3974
+ }
3975
+ function isBoundToBriefHash(echoed, currentHash) {
3976
+ if (currentHash === null)
3977
+ return true;
3978
+ return echoed.briefHash === currentHash;
3738
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
3739
4000
  function checkReviewGate(input) {
3740
4001
  const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
3741
4002
  const waived = isWaiverLabelActorVerified({
@@ -3758,15 +4019,55 @@ function checkReviewGate(input) {
3758
4019
  const verifiedBodies = verified.map((c) => c.body);
3759
4020
  const codeReview = extractCodeReviewVerdict(verifiedBodies);
3760
4021
  const security = extractSecurityReviewVerdict(verifiedBodies);
3761
- const codeReviewClean = codeReview.value === "APPROVE";
3762
- const securityClean = security.value === "PASS";
3763
- const codeReviewBound = isBoundToPatch(codeReview, input.headSha, input.patchIdOf);
3764
- const securityBound = isBoundToPatch(security, input.headSha, input.patchIdOf);
3765
- const codeReviewObjectivesBound = isBoundToObjectives(codeReview, input.objectivesVersion);
3766
- const securityObjectivesBound = isBoundToObjectives(security, input.objectivesVersion);
3767
- const codeReviewRulingsBound = isBoundToRulings(codeReview, input.rulingOrdinal);
3768
- const securityRulingsBound = isBoundToRulings(security, input.rulingOrdinal);
3769
- if (codeReviewClean && codeReviewBound && codeReviewObjectivesBound && codeReviewRulingsBound && securityClean && securityBound && securityObjectivesBound && securityRulingsBound && mechanicalChecksClean) {
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) {
3770
4071
  return {
3771
4072
  verdict: "pass",
3772
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.`,
@@ -3774,23 +4075,35 @@ function checkReviewGate(input) {
3774
4075
  };
3775
4076
  }
3776
4077
  const problems = [];
3777
- if (!codeReviewClean) {
4078
+ if (!codeReviewTextClean) {
3778
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`);
3779
4082
  } else if (!codeReviewBound) {
3780
4083
  problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
3781
4084
  } else if (!codeReviewObjectivesBound) {
3782
4085
  problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
3783
4086
  } else if (!codeReviewRulingsBound) {
3784
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}`);
3785
4092
  }
3786
- if (!securityClean) {
4093
+ if (!securityTextClean) {
3787
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`);
3788
4097
  } else if (!securityBound) {
3789
4098
  problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
3790
4099
  } else if (!securityObjectivesBound) {
3791
4100
  problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
3792
4101
  } else if (!securityRulingsBound) {
3793
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}`);
3794
4107
  }
3795
4108
  if (!mechanicalChecksClean) {
3796
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(", ")}`);
@@ -4482,42 +4795,45 @@ function checkDispatchReadiness(input) {
4482
4795
  const { trancheSlug, task } = input;
4483
4796
  const taskLabel = `task ${task.id} (tranche ${trancheSlug})`;
4484
4797
  const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
4485
- const blockers = [];
4798
+ const blockerDetails = [];
4799
+ const push = (blockerClass, message) => {
4800
+ blockerDetails.push({ class: blockerClass, message });
4801
+ };
4486
4802
  if (task.issue === null) {
4487
- blockers.push(`dispatch-gate issue-existence: ${taskLabel} has no Issue (#TBD or blank) in the topology — not dispatchable until the Planner cuts the Issue.`);
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.`);
4488
4804
  } else if (input.issue === null) {
4489
- blockers.push(`dispatch-gate issue-existence: ${taskLabel} names Issue #${task.issue}, but it does not resolve to a real GitHub Issue (phantom reference).`);
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).`);
4490
4806
  }
4491
4807
  if (input.issue !== null && !input.issueRationalePass) {
4492
- blockers.push(`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.`);
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.`);
4493
4809
  }
4494
4810
  for (const dep of input.dependsOn) {
4495
4811
  if (isSelfDependency(dep, task, input.issue)) {
4496
4812
  const issueStr = input.issue !== null ? `#${input.issue.number}` : "?";
4497
- blockers.push(`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.`);
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.`);
4498
4814
  continue;
4499
4815
  }
4500
4816
  if (dep.resolved === false) {
4501
- blockers.push(`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.`);
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.`);
4502
4818
  continue;
4503
4819
  }
4504
4820
  if (!dep.merged && !isHandClosedByRecognizedPrincipal(dep, principalAllowlist)) {
4505
4821
  const issueStr = dep.issue !== null ? ` (#${dep.issue})` : "";
4506
- blockers.push(`dispatch-gate depends-on: ${taskLabel} depends on ${dep.id}${issueStr}, whose PR is not merged yet — not dispatchable, it serializes behind it.`);
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.`);
4507
4823
  }
4508
4824
  }
4509
4825
  for (const c of input.conflictsWith) {
4510
4826
  if (c.openOrInFlight) {
4511
4827
  const issueStr = c.issue !== null ? ` (#${c.issue})` : "";
4512
- blockers.push(`dispatch-gate conflicts-with: ${taskLabel} conflicts with ${c.id}${issueStr}, whose PR is open or in-flight — not dispatchable until it merges.`);
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.`);
4513
4829
  }
4514
4830
  }
4515
4831
  for (const proj of input.priorTrancheArchival) {
4516
4832
  if (proj.priorTrancheSlug !== null && !proj.archived) {
4517
- blockers.push(`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.`);
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.`);
4518
4834
  }
4519
4835
  }
4520
- return { ready: blockers.length === 0, blockers };
4836
+ return { ready: blockerDetails.length === 0, blockers: blockerDetails.map((b) => b.message), blockerDetails };
4521
4837
  }
4522
4838
  // ../../packages/aeg-core/src/single-plan-pr.ts
4523
4839
  function trancheSlugFromTopologyPath(path) {
@@ -4619,6 +4935,91 @@ function checkBranchTopology(input) {
4619
4935
  reason: `Branch \`${branch}\` matches topology row \`${taskId}\` in ${topoPath}.`
4620
4936
  };
4621
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();
4622
5023
  // ../../packages/aeg-core/src/first-push-dispatch-gate.ts
4623
5024
  function parseTaskBranch(branch) {
4624
5025
  const m = /^task\/([^/]+)\/([^/]+)$/.exec(branch);
@@ -4702,7 +5103,6 @@ function decideIssueAssignment(input) {
4702
5103
  };
4703
5104
  }
4704
5105
  // ../../packages/aeg-core/src/test-plan-gate.ts
4705
- var TASK_BRANCH_PATTERN2 = /^task\/[^/]+\/[^/]+$/;
4706
5106
  function evaluateTestPlanGate(body, branch) {
4707
5107
  if (!body) {
4708
5108
  return {
@@ -4715,7 +5115,7 @@ function evaluateTestPlanGate(body, branch) {
4715
5115
  }
4716
5116
  const located = locateTestPlanSection(body);
4717
5117
  if (!located.found) {
4718
- if (TASK_BRANCH_PATTERN2.test(branch)) {
5118
+ if (parseTaskBranchIdentity(branch) !== null) {
4719
5119
  return {
4720
5120
  verdict: "fail",
4721
5121
  messages: [
@@ -4843,7 +5243,7 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
4843
5243
  return findings;
4844
5244
  }
4845
5245
  // ../../packages/aeg-core/src/log/schema.ts
4846
- import { z } from "zod";
5246
+ import { z as z2 } from "zod";
4847
5247
  var ROLE_VALUES = [
4848
5248
  "planner",
4849
5249
  "developer",
@@ -4853,136 +5253,172 @@ var ROLE_VALUES = [
4853
5253
  "archivist",
4854
5254
  "architect"
4855
5255
  ];
4856
- var RoleSchema = z.enum(ROLE_VALUES);
5256
+ var RoleSchema = z2.enum(ROLE_VALUES);
4857
5257
  var HOST_VALUES = ["hook", "ci", "cli", "loop"];
4858
- var HostSchema = z.enum(HOST_VALUES);
5258
+ var HostSchema = z2.enum(HOST_VALUES);
4859
5259
  var RUN_ID_PATTERN = /^[A-Za-z0-9_.-]{1,128}$/;
4860
- var HeaderMetaSchema = z.object({
4861
- schema: z.literal(1),
4862
- ts: z.string(),
4863
- run_id: z.string().regex(RUN_ID_PATTERN),
4864
- seq: z.number().int().nonnegative(),
4865
- repo: z.string().nullable(),
4866
- vinaya: z.string(),
4867
- 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(),
4868
5267
  host: HostSchema,
4869
- machine: z.string()
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()
4870
5284
  }).strict();
4871
- var SubjectSchema = z.object({
4872
- issue: z.number().int().nullable(),
4873
- pr: z.number().int().optional(),
4874
- sha: z.string().optional(),
4875
- role: z.union([RoleSchema, z.literal("unattributed")]),
4876
- round: z.number().int().optional(),
4877
- objectives_version: z.string().optional()
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
4878
5295
  }).strict();
4879
- var HeaderSchema = z.object({
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()
5304
+ }).strict();
5305
+ var HeaderSchema = z2.object({
4880
5306
  meta: HeaderMetaSchema,
4881
5307
  subject: SubjectSchema
4882
5308
  }).strict();
4883
5309
  var envelopeTail = {
4884
- duration_ms: z.number().nonnegative().optional(),
4885
- payload: z.object({}).strict()
5310
+ duration_ms: z2.number().nonnegative().optional(),
5311
+ payload: z2.object({}).strict()
4886
5312
  };
4887
- var DispatchOutcomeSchema = z.discriminatedUnion("type", [
4888
- z.object({ type: z.literal("pr_opened"), pr: z.number().int(), head: z.string() }).strict(),
4889
- z.object({
4890
- type: z.literal("round_pushed"),
4891
- pr: z.number().int(),
4892
- head: z.string(),
4893
- comment_id: z.number().int()
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()
4894
5330
  }).strict(),
4895
- z.object({
4896
- type: z.literal("verdict"),
4897
- verdict: z.enum(["APPROVE", "REQUEST CHANGES", "PASS", "FAIL"]),
4898
- head: z.string(),
4899
- comment_id: z.number().int(),
4900
- objectives: z.array(z.object({ id: z.string(), met: z.boolean() }).strict()),
4901
- findings: z.array(z.object({ id: z.string(), severity: z.string(), state: z.string().optional() }).strict())
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)
4902
5338
  }).strict(),
4903
- z.object({
4904
- type: z.literal("escalation"),
4905
- class: z.enum(["authority", "strategy", "product"]),
4906
- comment_id: z.number().int()
5339
+ z2.object({
5340
+ type: z2.literal("escalation"),
5341
+ class: z2.enum(["authority", "strategy", "product"]),
5342
+ comment_id: z2.number().int()
4907
5343
  }).strict(),
4908
- z.object({ type: z.literal("brief"), comment_id: z.number().int(), hash: z.string() }).strict(),
4909
- z.object({ type: z.literal("plan"), issues: z.array(z.number().int()) }).strict(),
4910
- z.object({ type: z.literal("archive"), provenance_comment_id: z.number().int() }).strict()
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()
4911
5347
  ]);
4912
5348
  var dispatchShared = {
4913
5349
  meta: HeaderMetaSchema,
4914
5350
  subject: SubjectSchema,
4915
- kind: z.literal("dispatch"),
5351
+ kind: z2.literal("dispatch"),
4916
5352
  ...envelopeTail,
4917
5353
  target_role: RoleSchema,
4918
- model: z.string(),
4919
- round: z.number().int().optional(),
4920
- effect_id: z.string()
5354
+ model: z2.string(),
5355
+ round: z2.number().int().optional(),
5356
+ effect_id: z2.string()
4921
5357
  };
4922
- var dispatchUsageField = z.object({ input: z.number().nonnegative(), output: z.number().nonnegative() }).strict().nullable();
4923
- var DispatchEventSchema = z.discriminatedUnion("event", [
4924
- z.object({ ...dispatchShared, event: z.literal("dispatched"), prompt_hash: z.string() }).strict(),
4925
- z.object({
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({
4926
5362
  ...dispatchShared,
4927
- event: z.literal("outcome_received"),
5363
+ event: z2.literal("outcome_received"),
4928
5364
  outcome: DispatchOutcomeSchema,
4929
5365
  usage: dispatchUsageField
4930
5366
  }).strict(),
4931
- z.object({
5367
+ z2.object({
4932
5368
  ...dispatchShared,
4933
- event: z.literal("dispatch_failed"),
4934
- reason: z.enum(["timeout", "crash", "refused", "unattributed_write"]),
5369
+ event: z2.literal("dispatch_failed"),
5370
+ reason: z2.enum(["timeout", "crash", "refused", "unattributed_write"]),
4935
5371
  usage: dispatchUsageField
4936
5372
  }).strict()
4937
5373
  ]);
4938
5374
  var loopShared = {
4939
5375
  meta: HeaderMetaSchema,
4940
5376
  subject: SubjectSchema,
4941
- kind: z.literal("dev_review_loop"),
5377
+ kind: z2.literal("dev_review_loop"),
4942
5378
  ...envelopeTail,
4943
- loop_id: z.string()
5379
+ loop_id: z2.string()
4944
5380
  };
4945
- var DevReviewLoopEventSchema = z.discriminatedUnion("event", [
4946
- z.object({
5381
+ var DevReviewLoopEventSchema = z2.discriminatedUnion("event", [
5382
+ z2.object({
4947
5383
  ...loopShared,
4948
- event: z.literal("loop_started"),
4949
- task: z.number().int(),
4950
- policy: z.object({
4951
- max_rounds: z.number().int().nonnegative(),
4952
- reviewers: z.array(RoleSchema),
4953
- models: z.record(RoleSchema, z.string())
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())
4954
5390
  }).strict()
4955
5391
  }).strict(),
4956
- z.object({ ...loopShared, event: z.literal("round_started"), round: z.number().int(), base_head: z.string() }).strict(),
4957
- z.object({
5392
+ z2.object({ ...loopShared, event: z2.literal("round_started"), round: z2.number().int(), base_head: z2.string() }).strict(),
5393
+ z2.object({
4958
5394
  ...loopShared,
4959
- event: z.literal("gate_result_read"),
4960
- round: z.number().int(),
4961
- head: z.string(),
4962
- green: z.boolean()
5395
+ event: z2.literal("gate_result_read"),
5396
+ round: z2.number().int(),
5397
+ head: z2.string(),
5398
+ green: z2.boolean()
4963
5399
  }).strict(),
4964
- z.object({
5400
+ z2.object({
4965
5401
  ...loopShared,
4966
- event: z.literal("verdicts_read"),
4967
- round: z.number().int(),
4968
- head: z.string(),
4969
- all_approve: z.boolean(),
4970
- blockers: z.number().int().nonnegative()
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()
4971
5407
  }).strict(),
4972
- z.object({
5408
+ z2.object({
4973
5409
  ...loopShared,
4974
- event: z.literal("findings_compared"),
4975
- round: z.number().int(),
4976
- open: z.array(z.string()),
4977
- resolved: z.array(z.string()),
4978
- new: z.array(z.string()),
4979
- recurring: z.array(z.string())
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())
4980
5416
  }).strict(),
4981
- z.object({
5417
+ z2.object({
4982
5418
  ...loopShared,
4983
- event: z.literal("stop_condition_met"),
4984
- round: z.number().int(),
4985
- condition: z.enum([
5419
+ event: z2.literal("stop_condition_met"),
5420
+ round: z2.number().int(),
5421
+ condition: z2.enum([
4986
5422
  "green",
4987
5423
  "max_rounds",
4988
5424
  "no_progress",
@@ -4992,42 +5428,49 @@ var DevReviewLoopEventSchema = z.discriminatedUnion("event", [
4992
5428
  "reappearance"
4993
5429
  ])
4994
5430
  }).strict(),
4995
- z.object({
5431
+ z2.object({
4996
5432
  ...loopShared,
4997
- event: z.literal("paused"),
4998
- round: z.number().int(),
4999
- reason: z.enum(["escalation", "principal_item", "refreeze_needed"])
5433
+ event: z2.literal("paused"),
5434
+ round: z2.number().int(),
5435
+ reason: z2.enum(["escalation", "principal_item", "refreeze_needed"])
5000
5436
  }).strict(),
5001
- z.object({
5437
+ z2.object({
5002
5438
  ...loopShared,
5003
- event: z.literal("resumed"),
5004
- round: z.number().int(),
5005
- by: z.literal("principal")
5439
+ event: z2.literal("resumed"),
5440
+ round: z2.number().int(),
5441
+ by: z2.literal("principal")
5006
5442
  }).strict(),
5007
- z.object({
5443
+ z2.object({
5008
5444
  ...loopShared,
5009
- event: z.literal("round_ended"),
5010
- round: z.number().int(),
5011
- base_head: z.string(),
5012
- head: z.string(),
5013
- files_changed: z.number().int().nonnegative(),
5014
- insertions: z.number().int().nonnegative(),
5015
- deletions: z.number().int().nonnegative(),
5016
- wall_ms: z.number().nonnegative(),
5017
- outcome: z.enum(["green", "changes_requested", "escalated"])
5445
+ event: z2.literal("unpushed_work_resume"),
5446
+ round: z2.number().int(),
5447
+ branch: z2.string(),
5448
+ detail: z2.string()
5018
5449
  }).strict(),
5019
- z.object({
5450
+ z2.object({
5020
5451
  ...loopShared,
5021
- event: z.literal("journal_finalized"),
5022
- rounds: z.number().int().nonnegative(),
5023
- total_wall_ms: z.number().nonnegative(),
5024
- time_to_green_ms: z.number().nonnegative().nullable(),
5025
- files_changed_total: z.number().int().nonnegative(),
5026
- final_head: z.string(),
5027
- result: z.enum(["merged_ready", "stopped"])
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"])
5461
+ }).strict(),
5462
+ z2.object({
5463
+ ...loopShared,
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"])
5028
5471
  }).strict()
5029
5472
  ]);
5030
- var ForgeOpSchema = z.enum([
5473
+ var ForgeOpSchema = z2.enum([
5031
5474
  "pr.create",
5032
5475
  "pr.comment",
5033
5476
  "pr.body.replace",
@@ -5041,24 +5484,210 @@ var ForgeOpSchema = z.enum([
5041
5484
  "label.add",
5042
5485
  "label.remove"
5043
5486
  ]);
5044
- var ForgeWriteTargetSchema = z.object({
5045
- issue: z.number().int().optional(),
5046
- pr: z.number().int().optional()
5487
+ var ForgeWriteTargetSchema = z2.object({
5488
+ issue: z2.number().int().optional(),
5489
+ pr: z2.number().int().optional()
5047
5490
  }).strict();
5048
5491
  var forgeWriteShared = {
5049
5492
  meta: HeaderMetaSchema,
5050
5493
  subject: SubjectSchema,
5051
- kind: z.literal("forge_write"),
5494
+ kind: z2.literal("forge_write"),
5052
5495
  ...envelopeTail,
5053
5496
  op: ForgeOpSchema,
5054
5497
  target: ForgeWriteTargetSchema
5055
5498
  };
5056
- var ForgeWriteEventSchema = z.discriminatedUnion("event", [
5057
- z.object({ ...forgeWriteShared, event: z.literal("validated") }).strict(),
5058
- z.object({ ...forgeWriteShared, event: z.literal("refused"), reason: z.string() }).strict(),
5059
- z.object({ ...forgeWriteShared, event: z.literal("written"), comment_ids: z.array(z.string()) }).strict()
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()
5503
+ ]);
5504
+ var GateOutcomeSchema = z2.enum([
5505
+ "pass",
5506
+ "fail",
5507
+ "wait",
5508
+ "skip",
5509
+ "invalid_input",
5510
+ "unavailable_dependency",
5511
+ "timeout",
5512
+ "cancelled"
5060
5513
  ]);
5061
- var LogEventSchema = z.union([DispatchEventSchema, DevReviewLoopEventSchema, ForgeWriteEventSchema]);
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();
5062
5691
  // src/checks/contract.ts
5063
5692
  var CHECK_SCHEMA_VERSION = 1;
5064
5693
  function emitCheckError(error) {