@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
@@ -3,7 +3,7 @@ import { createRequire } from "node:module";
3
3
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
4
 
5
5
  // src/checks/bin/check-exec-bits.ts
6
- import { execFileSync as execFileSync5 } from "node:child_process";
6
+ import { execFileSync as execFileSync6 } from "node:child_process";
7
7
  import { readFileSync as readFileSync3 } from "node:fs";
8
8
 
9
9
  // src/checks/contract.ts
@@ -14,8 +14,8 @@ function emitCheckError(error) {
14
14
  }
15
15
 
16
16
  // src/lib/diff-evidence.ts
17
- import { execFileSync as execFileSync4 } from "node:child_process";
18
- import { join as join2, relative, resolve as resolve2 } from "node:path";
17
+ import { execFileSync as execFileSync5 } from "node:child_process";
18
+ import { join as join4, relative, resolve as resolve2 } from "node:path";
19
19
  // ../../packages/aeg-core/src/gate-audience.ts
20
20
  var GATE_AUDIENCE = {
21
21
  "check-branch-topology": { shippedAs: "branch-topology", ring: 0 },
@@ -1966,6 +1966,8 @@ function parseInlineFieldList(section) {
1966
1966
  var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
1967
1967
  var OBJECTIVES_VERSION_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Objectives version:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
1968
1968
  var RULING_ORDINAL_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Ruling ordinal:\s*(\d+)(?!\d)/im;
1969
+ var BRIEF_HASH_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Brief hash:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
1970
+ var POLICY_DIGEST_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Policy digest:\s*([0-9a-f]{64})(?![A-Za-z0-9])/im;
1969
1971
  function firstFiveLines(comment) {
1970
1972
  return comment.split(`
1971
1973
  `).slice(0, 5).join(`
@@ -1988,6 +1990,26 @@ function extractRulingOrdinal(comment) {
1988
1990
  const m = firstSevenLines(comment).match(RULING_ORDINAL_PATTERN);
1989
1991
  return m ? Number.parseInt(m[1], 10) : null;
1990
1992
  }
1993
+ function firstElevenLines(comment) {
1994
+ return comment.split(`
1995
+ `).slice(0, 11).join(`
1996
+ `);
1997
+ }
1998
+ function extractBriefHash(comment) {
1999
+ const m = firstElevenLines(comment).match(BRIEF_HASH_PATTERN);
2000
+ return m ? m[1].toLowerCase() : null;
2001
+ }
2002
+ function extractPolicyDigest(comment) {
2003
+ const m = firstElevenLines(comment).match(POLICY_DIGEST_PATTERN);
2004
+ return m ? m[1].toLowerCase() : null;
2005
+ }
2006
+ var FINDING_SEVERITY_LINE = /^\d+\.\s+\[([A-Z][A-Z]*)\]\s+(.+?)\s+—/gm;
2007
+ function extractFindingSeverities(comment) {
2008
+ return [...comment.matchAll(FINDING_SEVERITY_LINE)].map((m) => ({
2009
+ severity: m[1],
2010
+ location: m[2]
2011
+ }));
2012
+ }
1991
2013
  function extractVerdict(comments, valuePattern, missingLabel) {
1992
2014
  const candidates = comments.filter((c) => valuePattern.test(c));
1993
2015
  if (candidates.length === 0) {
@@ -1996,6 +2018,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
1996
2018
  headSha: null,
1997
2019
  objectivesVersion: null,
1998
2020
  rulingOrdinal: null,
2021
+ briefHash: null,
2022
+ policyDigest: null,
2023
+ findingSeverities: [],
1999
2024
  danglingNote: `no ${missingLabel} verdict comment found on this PR`
2000
2025
  };
2001
2026
  }
@@ -2007,6 +2032,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
2007
2032
  headSha: null,
2008
2033
  objectivesVersion: null,
2009
2034
  rulingOrdinal: null,
2035
+ briefHash: null,
2036
+ policyDigest: null,
2037
+ findingSeverities: [],
2010
2038
  danglingNote: `the most recent ${missingLabel} verdict comment carries a VERDICT-shaped line outside the first-five-line read window`
2011
2039
  };
2012
2040
  }
@@ -2015,6 +2043,9 @@ function extractVerdict(comments, valuePattern, missingLabel) {
2015
2043
  headSha: extractHeadSha(latest),
2016
2044
  objectivesVersion: extractObjectivesVersion(latest),
2017
2045
  rulingOrdinal: extractRulingOrdinal(latest),
2046
+ briefHash: extractBriefHash(latest),
2047
+ policyDigest: extractPolicyDigest(latest),
2048
+ findingSeverities: extractFindingSeverities(latest),
2018
2049
  danglingNote: null
2019
2050
  };
2020
2051
  }
@@ -2350,6 +2381,53 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive,
2350
2381
  }
2351
2382
  return out;
2352
2383
  }
2384
+ // ../../packages/aeg-core/src/review-policy.ts
2385
+ var CODE_REVIEW_SEVERITY_ORDER = ["BLOCKER", "MAJOR", "MINOR"];
2386
+ var SECURITY_SEVERITY_ORDER = ["CRITICAL", "HIGH", "MEDIUM", "LOW"];
2387
+ var DEFAULT_MAX_ROUNDS = 3;
2388
+ var DEFAULT_REVIEW_POLICY = {
2389
+ codeReviewThreshold: "BLOCKER",
2390
+ securityThreshold: "HIGH",
2391
+ maxRounds: DEFAULT_MAX_ROUNDS
2392
+ };
2393
+ var FILE_SHAPED_LOCATION = /\.[a-zA-Z0-9]{1,10}(:\d+)?\s*$/;
2394
+ var PROSE_LOCATION_PATTERNS = [/\bpr\s*body\b/i, /\bcomment\b/i];
2395
+ var ROLE_FILE_LOCATION = /(^|\/)aeg-root\/roles\//i;
2396
+ function isProseLocation(location) {
2397
+ if (ROLE_FILE_LOCATION.test(location))
2398
+ return true;
2399
+ if (FILE_SHAPED_LOCATION.test(location))
2400
+ return false;
2401
+ return PROSE_LOCATION_PATTERNS.some((pattern) => pattern.test(location));
2402
+ }
2403
+ var PROSE_CAP_SEVERITY = "MINOR";
2404
+ function blockingSeverities(scale, threshold) {
2405
+ const idx = scale.indexOf(threshold);
2406
+ if (idx === -1) {
2407
+ throw new Error(`blockingSeverities: threshold "${threshold}" is not one of ${scale.join(" > ")}`);
2408
+ }
2409
+ return scale.slice(0, idx + 1);
2410
+ }
2411
+ function evaluateReviewFindings(findings, scale, threshold) {
2412
+ const blocking = new Set(blockingSeverities(scale, threshold));
2413
+ const blockingFindings = findings.filter((f) => {
2414
+ if (!scale.includes(f.severity)) {
2415
+ throw new Error(`evaluateReviewFindings: severity "${f.severity}" is not one of ${scale.join(" > ")}`);
2416
+ }
2417
+ const effectiveSeverity = f.location !== undefined && isProseLocation(f.location) ? PROSE_CAP_SEVERITY : f.severity;
2418
+ return blocking.has(effectiveSeverity);
2419
+ });
2420
+ return { outcome: blockingFindings.length > 0 ? "blocked" : "clean", blockingFindings };
2421
+ }
2422
+ function evaluateCodeReview(findings, policy) {
2423
+ return evaluateReviewFindings(findings, CODE_REVIEW_SEVERITY_ORDER, policy.codeReviewThreshold);
2424
+ }
2425
+ function evaluateSecurityReview(findings, policy) {
2426
+ return evaluateReviewFindings(findings, SECURITY_SEVERITY_ORDER, policy.securityThreshold);
2427
+ }
2428
+ function isKnownSeverity(scale, value) {
2429
+ return scale.includes(value);
2430
+ }
2353
2431
  // ../../packages/aeg-core/src/pr-tier.ts
2354
2432
  var TIER_FIELD = /(\*\*)?\s*Tier\s*(\*\*)?\s*:\s*(\*\*)?\s*([013])\b/i;
2355
2433
  function readTierFromPrBody(prBody) {
@@ -2720,7 +2798,7 @@ function checkForField(prBody) {
2720
2798
  ]
2721
2799
  };
2722
2800
  }
2723
- function checkClosesN(prBody) {
2801
+ function checkClosesNPresence(prBody) {
2724
2802
  const closesPattern = /(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s{0,8}:?\s{0,8}#\d+/i;
2725
2803
  if (closesPattern.test(stripCode(prBody))) {
2726
2804
  return { status: "pass", errors: [] };
@@ -2752,10 +2830,22 @@ var COMMIT_TYPES = [
2752
2830
  "Test"
2753
2831
  ];
2754
2832
  var COMMIT_TYPE_STYLE = new RegExp(`^(${COMMIT_TYPES.join("|")})(\\([a-z0-9-]+\\))?: \\S`);
2833
+ function checkForgeTitle(title) {
2834
+ const taskStyle = /^\[[a-z0-9._-]+\] \S+ — \S/;
2835
+ if (COMMIT_TYPE_STYLE.test(title) || taskStyle.test(title))
2836
+ return { status: "pass", errors: [] };
2837
+ return {
2838
+ status: "fail",
2839
+ errors: [
2840
+ `brief-validation title: "${title}" matches neither title grammar — expected \`Type: description\` / \`Type(scope): description\` (commitlint types + Plan) or \`[tranche] id — description\` (task form).`
2841
+ ]
2842
+ };
2843
+ }
2755
2844
  var BRIEF_SHAPE_MARKERS = [checkSurfaceMap, checkDocUpdateList, checkStopConditions, checkAutonomyClause];
2756
2845
  var TASK_BRANCH_PATTERN = /^task\/[^/]+\/[^/]+$/;
2846
+ var TASK_ISSUE_BRANCH_PATTERN = /^task\/issue-\d+$/;
2757
2847
  function isTaskBranch(branch) {
2758
- return TASK_BRANCH_PATTERN.test(branch);
2848
+ return TASK_BRANCH_PATTERN.test(branch) || TASK_ISSUE_BRANCH_PATTERN.test(branch);
2759
2849
  }
2760
2850
  function isBriefShaped(prBody) {
2761
2851
  const stripped = stripCode(prBody);
@@ -2877,8 +2967,9 @@ function packagesNamedIn(text) {
2877
2967
  }
2878
2968
  function hasTestPathForConsumer(text, consumerDir) {
2879
2969
  const escaped = consumerDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2880
- const re = new RegExp(`${escaped}\\/[\\w./-]*\\.test\\.[A-Za-z0-9]+`);
2881
- return re.test(text);
2970
+ const filePathRe = new RegExp(`${escaped}\\/[\\w./-]*\\.test\\.[A-Za-z0-9]+`);
2971
+ const testDirRe = new RegExp(`${escaped}\\/(?:[\\w-]+\\/)*(?:tests|specs)(?:\\/|\\b)`);
2972
+ return filePathRe.test(text) || testDirRe.test(text);
2882
2973
  }
2883
2974
  function checkConsumerTests(prBody, consumersOf) {
2884
2975
  const section4 = extractNumberedSection(prBody, 4);
@@ -2982,7 +3073,7 @@ function checkBriefSections(prBody, readTier, options = {}) {
2982
3073
  checkConsumerTests(prBody, consumersOf),
2983
3074
  checkDefeatCases(prBody),
2984
3075
  ...issueObjectives !== undefined ? [checkObjectivesCopy(prBody, issueObjectives), checkObjectivesCoverage(prBody)] : [],
2985
- ...requireClosesN ? [checkClosesN(prBody)] : []
3076
+ ...requireClosesN ? [checkClosesNPresence(prBody)] : []
2986
3077
  ];
2987
3078
  return { errors: results.flatMap((r) => r.errors) };
2988
3079
  }
@@ -3272,6 +3363,28 @@ function topLevelSectionText(body, headingName) {
3272
3363
  const next = /^##[ \t]/m.exec(afterHeading);
3273
3364
  return next ? afterHeading.slice(0, next.index) : afterHeading;
3274
3365
  }
3366
+ function partHasBacktickedPath(text) {
3367
+ let i = 0;
3368
+ while (i < text.length) {
3369
+ const start = text.indexOf("`", i);
3370
+ if (start === -1)
3371
+ return false;
3372
+ const end = text.indexOf("`", start + 1);
3373
+ if (end === -1)
3374
+ return false;
3375
+ if (text.slice(start + 1, end).includes("/"))
3376
+ return true;
3377
+ i = end + 1;
3378
+ }
3379
+ return false;
3380
+ }
3381
+ var PART_MIN_WORDS_OUTSIDE_BACKTICKS = 3;
3382
+ function stripPartBackticks(text) {
3383
+ return text.replace(/`[^`\n]*`/g, " ");
3384
+ }
3385
+ function partWordCount(text) {
3386
+ return text.split(/\s+/).filter((w) => /[a-z]/i.test(w)).length;
3387
+ }
3275
3388
  function looksLikeFilePath(entry2) {
3276
3389
  const stripped = entry2.replace(/\/\*\*?$/, "");
3277
3390
  const lastSegment = stripped.split("/").pop() ?? stripped;
@@ -3312,6 +3425,18 @@ function globCoversPath(glob, path) {
3312
3425
  const p = path.replace(/\/+$/, "");
3313
3426
  return g === p || g.startsWith(`${p}/`) || p.startsWith(`${g}/`);
3314
3427
  }
3428
+ function checkSurfaceGlobsResolve(body, resolvesToFile) {
3429
+ const surface = parseIssueSurface(body);
3430
+ if (!surface.ok)
3431
+ return { status: "pass", errors: [] };
3432
+ const errors = [];
3433
+ for (const glob of surface.value.in) {
3434
+ if (!resolvesToFile(glob)) {
3435
+ errors.push(`issue-validation Surface: \`${glob}\` in \`## Surface\`'s \`in:\` list matches no tracked file — a Surface that cannot resolve cannot render a brief's file list.`);
3436
+ }
3437
+ }
3438
+ return { status: errors.length > 0 ? "fail" : "pass", errors };
3439
+ }
3315
3440
  function checkSurfaceScope(changedFiles, outGlobs) {
3316
3441
  if (outGlobs.length === 0)
3317
3442
  return { ok: true };
@@ -3323,9 +3448,77 @@ function checkSurfaceScope(changedFiles, outGlobs) {
3323
3448
  }
3324
3449
  return violations.length > 0 ? { ok: false, violations } : { ok: true };
3325
3450
  }
3451
+ var ISSUE_PART_LINE_RE = /^Part\s+(\d+)\s*\(([^)]*)\)\s*[-—–]\s*(.*)$/i;
3452
+ function parseIssueParts(body) {
3453
+ const section = topLevelSectionText(body, "Parts");
3454
+ if (section === null)
3455
+ return { ok: false, errors: ["no `## Parts` heading found in the body."] };
3456
+ const lines = section.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0);
3457
+ const parts = [];
3458
+ const errors = [];
3459
+ for (const line of lines) {
3460
+ const m = ISSUE_PART_LINE_RE.exec(line);
3461
+ if (!m) {
3462
+ errors.push(`"${line}" is not a well-formed Parts line — expected \`Part <n> (<refs>) — <outcome>\`.`);
3463
+ continue;
3464
+ }
3465
+ const n = Number.parseInt(m[1], 10);
3466
+ const refs = m[2];
3467
+ const text = m[3].trim();
3468
+ if (text.length === 0) {
3469
+ errors.push(`Part ${n} has no outcome text after the dash — every Part states one observable outcome.`);
3470
+ continue;
3471
+ }
3472
+ if (partHasBacktickedPath(text) && partWordCount(stripPartBackticks(text)) < PART_MIN_WORDS_OUTSIDE_BACKTICKS) {
3473
+ errors.push(`Part ${n} is little more than a file path — a Part names an outcome and symbols, never a bare path.`);
3474
+ continue;
3475
+ }
3476
+ const objectiveIds = [...refs.matchAll(/O(\d+)/g)].map((r) => Number.parseInt(r[1], 10));
3477
+ parts.push({ n, objectiveIds, text });
3478
+ }
3479
+ if (parts.length === 0) {
3480
+ errors.push("the `## Parts` section has no well-formed `Part <n> (<refs>) — <outcome>` lines.");
3481
+ }
3482
+ if (errors.length > 0)
3483
+ return { ok: false, errors };
3484
+ return { ok: true, value: parts };
3485
+ }
3486
+ function checkPartsCiteDefinedObjectives(body) {
3487
+ const parts = parseIssueParts(body);
3488
+ const objectives = objectivesOf(body);
3489
+ if (!parts.ok || !objectives.ok)
3490
+ return { status: "pass", errors: [] };
3491
+ const definedIds = new Set(objectives.objectives.map((o) => Number.parseInt(o.id.slice(1), 10)));
3492
+ const errors = [];
3493
+ for (const part of parts.value) {
3494
+ for (const objectiveId of part.objectiveIds) {
3495
+ if (!definedIds.has(objectiveId)) {
3496
+ errors.push(`issue-validation Parts: Part ${part.n} cites O${objectiveId}, which the Issue's own \`## Objectives\` section does not define.`);
3497
+ }
3498
+ }
3499
+ }
3500
+ return { status: errors.length > 0 ? "fail" : "pass", errors };
3501
+ }
3326
3502
  function isTaskIssueLabelSet(labels) {
3327
3503
  return hasLabel("tranche", labels);
3328
3504
  }
3505
+ function checkTrancheLabelPresence(_body, _labels) {
3506
+ return { status: "pass", errors: [] };
3507
+ }
3508
+ function checkMilestoneAttach(labels, currentMilestoneTitle, resolvedMilestoneTitle) {
3509
+ if (!isTaskIssueLabelSet(labels))
3510
+ return { status: "pass", errors: [] };
3511
+ if (resolvedMilestoneTitle === null)
3512
+ return { status: "pass", errors: [] };
3513
+ if (currentMilestoneTitle === resolvedMilestoneTitle)
3514
+ return { status: "pass", errors: [] };
3515
+ return {
3516
+ status: "fail",
3517
+ errors: [
3518
+ `issue-validation milestone attach: this task Issue's tranche label resolves to Milestone "${resolvedMilestoneTitle}", but its live Milestone is ${currentMilestoneTitle === null ? "unset" : `"${currentMilestoneTitle}"`} — attach it to "${resolvedMilestoneTitle}".`
3519
+ ]
3520
+ };
3521
+ }
3329
3522
  var TYPE_LABEL_IDS = LABELS.filter((l) => l.category === "type").map((l) => l.id);
3330
3523
  function isControlCodePoint(codePoint) {
3331
3524
  return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
@@ -3390,6 +3583,19 @@ function checkProjectsRegistered(body, _labels, registeredNames) {
3390
3583
  var DOC_PATH_RE = /(?:(?:aeg-root|apps|packages|specs|docs|tools|\.claude|\.github)\/[\w./@-]*\.(?:md|mdx)|\.claude\/(?:skills|rules)\/[\w./-]+|\b(?:docs-index|decisions|projects|state-machine|enforcement|process|README|CLAUDE)\.md\b|\b[\w-]+-(?:spec|decisions|backlog)\.md\b)/i;
3391
3584
  var DOC_PATH_RE_GLOBAL = new RegExp(DOC_PATH_RE.source, "gi");
3392
3585
 
3586
+ // ../../packages/aeg-core/src/task-branch-identity.ts
3587
+ var ISSUE_BRANCH_PATTERN = /^task\/issue-(\d+)$/;
3588
+ var TRANCHE_BRANCH_PATTERN = /^task\/([^/]+)\/([^/]+)$/;
3589
+ function parseTaskBranchIdentity(branch) {
3590
+ const issueMatch = ISSUE_BRANCH_PATTERN.exec(branch);
3591
+ if (issueMatch)
3592
+ return { kind: "issue", issueNumber: Number(issueMatch[1]) };
3593
+ const trancheMatch = TRANCHE_BRANCH_PATTERN.exec(branch);
3594
+ if (trancheMatch)
3595
+ return { kind: "tranche", tranche: trancheMatch[1], taskId: trancheMatch[2] };
3596
+ return null;
3597
+ }
3598
+
3393
3599
  // ../../packages/aeg-core/src/coherence-checks.ts
3394
3600
  var COHERENCE_ENFORCED_FROM = "2026-07-01";
3395
3601
  function isGrandfathered(isoDate) {
@@ -3410,6 +3616,7 @@ function checkA1(entries, principalAllowlist = PRINCIPAL_ALLOWLIST) {
3410
3616
  if (handClosed)
3411
3617
  continue;
3412
3618
  failures.push({
3619
+ code: "closed-without-merge",
3413
3620
  issue: e.task.issue,
3414
3621
  tranche: e.trancheSlug,
3415
3622
  task: e.task.id,
@@ -3434,6 +3641,7 @@ function checkA3(entries) {
3434
3641
  continue;
3435
3642
  if (e.facts.prState === "merged" && e.facts.issueState !== "closed") {
3436
3643
  failures.push({
3644
+ code: "auto-close-misfire",
3437
3645
  issue: e.task.issue,
3438
3646
  tranche: e.trancheSlug,
3439
3647
  task: e.task.id,
@@ -3458,6 +3666,7 @@ function checkT1(entries) {
3458
3666
  continue;
3459
3667
  if (e.facts === undefined) {
3460
3668
  failures.push({
3669
+ code: "phantom-issue-ref",
3461
3670
  issue: e.task.issue,
3462
3671
  tranche: e.trancheSlug,
3463
3672
  task: e.task.id,
@@ -3476,6 +3685,7 @@ function checkT2(openIssuesBySlug, topologyIssuesBySlug, ciTrancheSlug) {
3476
3685
  for (const num of openNums) {
3477
3686
  if (!topologySet.has(num)) {
3478
3687
  failures.push({
3688
+ code: "orphan-task",
3479
3689
  issue: num,
3480
3690
  tranche: slug,
3481
3691
  reason: `Issue #${num} is open and labeled ${trancheLabel(slug)} but does not appear in the topology file`
@@ -3510,6 +3720,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
3510
3720
  continue;
3511
3721
  if (forgeUnavailableSlugs?.has(e.trancheSlug)) {
3512
3722
  failures.push({
3723
+ code: "tbd-in-active-tranche",
3513
3724
  issue: null,
3514
3725
  tranche: e.trancheSlug,
3515
3726
  task: e.task.id,
@@ -3519,6 +3730,7 @@ function checkT3(entries, ciTrancheSlug, enrichedEntries, forgeUnavailableSlugs)
3519
3730
  continue;
3520
3731
  }
3521
3732
  failures.push({
3733
+ code: "tbd-in-active-tranche",
3522
3734
  issue: null,
3523
3735
  tranche: e.trancheSlug,
3524
3736
  task: e.task.id,
@@ -3551,6 +3763,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
3551
3763
  const sameIssue = depEntry.task.issue !== null && e.task.issue !== null && depEntry.task.issue === e.task.issue;
3552
3764
  if (sameTask || sameIssue) {
3553
3765
  failures.push({
3766
+ code: "d1-self-dependency",
3554
3767
  issue: e.task.issue,
3555
3768
  tranche: e.trancheSlug,
3556
3769
  task: e.task.id,
@@ -3562,6 +3775,7 @@ function checkD1(entries, issueToEntry, taskToEntry) {
3562
3775
  const depClosed = depFacts?.issueState === "closed";
3563
3776
  if (!depClosed) {
3564
3777
  failures.push({
3778
+ code: "dispatched-on-unmet-deps",
3565
3779
  issue: e.task.issue,
3566
3780
  tranche: e.trancheSlug,
3567
3781
  task: e.task.id,
@@ -3587,11 +3801,13 @@ function checkR1(issuesBySlug, grandfatheredIssues, registeredNames = []) {
3587
3801
  const errors = [
3588
3802
  ...checkIssueRationale(issue.body).errors,
3589
3803
  ...checkProjectsRegistered(issue.body, issue.labels, registeredNames).errors,
3590
- ...checkIssueObjectives(issue.body, issue.number).errors
3804
+ ...checkIssueObjectives(issue.body, issue.number).errors,
3805
+ ...checkPartsCiteDefinedObjectives(issue.body).errors
3591
3806
  ];
3592
3807
  if (errors.length === 0)
3593
3808
  continue;
3594
3809
  failures.push({
3810
+ code: "missing-rationale-field",
3595
3811
  issue: issue.number,
3596
3812
  tranche: slug,
3597
3813
  reason: `Issue #${issue.number} fails the rationale gate: ${errors.join(" | ")}`,
@@ -3620,6 +3836,7 @@ function checkL1(files, entriesBySlug) {
3620
3836
  const allClosed = withFacts.every((e) => e.facts?.issueState === "closed");
3621
3837
  if (allClosed) {
3622
3838
  failures.push({
3839
+ code: "archive-recommended",
3623
3840
  tranche: f.slug,
3624
3841
  reason: "Active tranche has no open task-Issues — consider archiving to completed/"
3625
3842
  });
@@ -3651,27 +3868,38 @@ function extractClosesReferences(prBody) {
3651
3868
  }
3652
3869
  return referenced;
3653
3870
  }
3654
- function checkClosesN2(branch, prBody, trancheFiles, taskIssueRefs) {
3871
+ function checkClosesNTopology(branch, prBody, trancheFiles, taskIssueRefs) {
3655
3872
  const referenced = extractClosesReferences(prBody);
3656
3873
  if (taskIssueRefs) {
3657
3874
  for (const n of referenced) {
3658
- const ref = taskIssueRefs.get(n);
3659
- if (!ref)
3875
+ const ref2 = taskIssueRefs.get(n);
3876
+ if (!ref2)
3660
3877
  continue;
3661
- const expectedBranch = `task/${ref.trancheSlug}/${ref.taskId}`;
3878
+ const expectedBranch = `task/${ref2.trancheSlug}/${ref2.taskId}`;
3662
3879
  if (branch !== expectedBranch) {
3663
3880
  return {
3664
3881
  ok: false,
3665
- 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.`
3882
+ message: `closes-n-reverse: branch "${branch}" closes #${n} (task ${ref2.taskId} of tranche "${ref2.trancheSlug}") but is not named "${expectedBranch}" — rename the branch and re-push, or if this work is intentionally outside AEG's dispatch flow, remove the Closes reference.`
3666
3883
  };
3667
3884
  }
3668
3885
  }
3669
3886
  }
3670
- const m = branch.match(/^task\/([^/]+)\/([^/]+)$/);
3671
- if (!m)
3887
+ const ref = parseTaskBranchIdentity(branch);
3888
+ if (!ref)
3672
3889
  return { ok: true };
3673
- const trancheSlug = m[1];
3674
- const taskId = m[2];
3890
+ if (ref.kind === "issue") {
3891
+ const expectedIssue2 = ref.issueNumber;
3892
+ if (!referenced.has(expectedIssue2)) {
3893
+ return {
3894
+ ok: false,
3895
+ expectedIssue: expectedIssue2,
3896
+ message: `closes-n: PR body does not contain \`Closes #${expectedIssue2}\` (required for branch "${branch}"). Add it to the PR body Summary section.`
3897
+ };
3898
+ }
3899
+ return { ok: true, expectedIssue: expectedIssue2 };
3900
+ }
3901
+ const trancheSlug = ref.tranche;
3902
+ const taskId = ref.taskId;
3675
3903
  const trancheFile = trancheFiles.find((f) => f.slug === trancheSlug);
3676
3904
  if (!trancheFile) {
3677
3905
  return {
@@ -3723,34 +3951,67 @@ function extractIssue(body) {
3723
3951
  const issue = headerNums.length > 0 ? headerNums[0] : bodyNums[0];
3724
3952
  return { issue, extraIssues: bodyNums.filter((n) => n !== issue), outsideHeader: headerNums.length === 0 };
3725
3953
  }
3726
- // ../../packages/aeg-core/src/review-gate.ts
3727
- function isBoundToHead(extraction, headSha) {
3728
- if (!extraction.headSha)
3954
+ // ../../packages/aeg-core/src/review-input-manifest.ts
3955
+ import { createHash as createHash4 } from "node:crypto";
3956
+ function briefHash(brief) {
3957
+ return createHash4("sha256").update(`${brief}
3958
+ `).digest("hex");
3959
+ }
3960
+ function policyDigest(policy) {
3961
+ return createHash4("sha256").update(JSON.stringify({ codeReviewThreshold: policy.codeReviewThreshold, securityThreshold: policy.securityThreshold })).digest("hex");
3962
+ }
3963
+ function isBoundToHead(echoed, headSha) {
3964
+ if (!echoed.headSha)
3729
3965
  return false;
3730
- return headSha.toLowerCase().startsWith(extraction.headSha.toLowerCase());
3966
+ return headSha.toLowerCase().startsWith(echoed.headSha.toLowerCase());
3731
3967
  }
3732
- function isBoundByPatchIdentity(extraction, headSha, patchIdOf) {
3733
- if (patchIdOf === undefined || !extraction.headSha)
3968
+ function isBoundByPatchIdentity(echoed, headSha, patchIdOf) {
3969
+ if (patchIdOf === undefined || !echoed.headSha)
3734
3970
  return false;
3735
- const judged = patchIdOf(extraction.headSha);
3971
+ const judged = patchIdOf(echoed.headSha);
3736
3972
  const current = patchIdOf(headSha);
3737
3973
  if (judged === null || current === null)
3738
3974
  return false;
3739
3975
  return judged === current;
3740
3976
  }
3741
- function isBoundToPatch(extraction, headSha, patchIdOf) {
3742
- return isBoundToHead(extraction, headSha) || isBoundByPatchIdentity(extraction, headSha, patchIdOf);
3977
+ function isBoundToPatch(echoed, headSha, patchIdOf) {
3978
+ return isBoundToHead(echoed, headSha) || isBoundByPatchIdentity(echoed, headSha, patchIdOf);
3743
3979
  }
3744
- function isBoundToObjectives(extraction, currentVersion) {
3980
+ function isBoundToObjectives(echoed, currentVersion) {
3745
3981
  if (currentVersion === null)
3746
3982
  return true;
3747
- return extraction.objectivesVersion === currentVersion;
3983
+ return echoed.objectivesVersion === currentVersion;
3748
3984
  }
3749
- function isBoundToRulings(extraction, currentOrdinal) {
3750
- if (extraction.rulingOrdinal === null)
3985
+ function isBoundToRulings(echoed, currentOrdinal) {
3986
+ if (echoed.rulingOrdinal === null)
3751
3987
  return currentOrdinal === 0;
3752
- return extraction.rulingOrdinal === currentOrdinal;
3988
+ return echoed.rulingOrdinal === currentOrdinal;
3753
3989
  }
3990
+ function isBoundToBriefHash(echoed, currentHash) {
3991
+ if (currentHash === null)
3992
+ return true;
3993
+ return echoed.briefHash === currentHash;
3994
+ }
3995
+ function isBoundToPolicy(echoed, currentDigest) {
3996
+ return echoed.policyDigest === currentDigest;
3997
+ }
3998
+ function compareManifest(echoed, current, patchIdOf) {
3999
+ const head = isBoundToPatch(echoed, current.headSha, patchIdOf);
4000
+ const briefHashBound = isBoundToBriefHash(echoed, current.briefHash);
4001
+ const objectivesVersion2 = isBoundToObjectives(echoed, current.objectivesVersion);
4002
+ const rulingOrdinal = isBoundToRulings(echoed, current.rulingOrdinal);
4003
+ const policyDigestBound = isBoundToPolicy(echoed, current.policyDigest);
4004
+ return {
4005
+ bound: head && briefHashBound && objectivesVersion2 && rulingOrdinal && policyDigestBound,
4006
+ head,
4007
+ briefHash: briefHashBound,
4008
+ objectivesVersion: objectivesVersion2,
4009
+ rulingOrdinal,
4010
+ policyDigest: policyDigestBound
4011
+ };
4012
+ }
4013
+
4014
+ // ../../packages/aeg-core/src/review-gate.ts
3754
4015
  function checkReviewGate(input) {
3755
4016
  const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
3756
4017
  const waived = isWaiverLabelActorVerified({
@@ -3773,15 +4034,55 @@ function checkReviewGate(input) {
3773
4034
  const verifiedBodies = verified.map((c) => c.body);
3774
4035
  const codeReview = extractCodeReviewVerdict(verifiedBodies);
3775
4036
  const security = extractSecurityReviewVerdict(verifiedBodies);
3776
- const codeReviewClean = codeReview.value === "APPROVE";
3777
- const securityClean = security.value === "PASS";
3778
- const codeReviewBound = isBoundToPatch(codeReview, input.headSha, input.patchIdOf);
3779
- const securityBound = isBoundToPatch(security, input.headSha, input.patchIdOf);
3780
- const codeReviewObjectivesBound = isBoundToObjectives(codeReview, input.objectivesVersion);
3781
- const securityObjectivesBound = isBoundToObjectives(security, input.objectivesVersion);
3782
- const codeReviewRulingsBound = isBoundToRulings(codeReview, input.rulingOrdinal);
3783
- const securityRulingsBound = isBoundToRulings(security, input.rulingOrdinal);
3784
- if (codeReviewClean && codeReviewBound && codeReviewObjectivesBound && codeReviewRulingsBound && securityClean && securityBound && securityObjectivesBound && securityRulingsBound && mechanicalChecksClean) {
4037
+ const policy = input.policy ?? DEFAULT_REVIEW_POLICY;
4038
+ let codeReviewPolicyEvaluation;
4039
+ let securityPolicyEvaluation;
4040
+ try {
4041
+ codeReviewPolicyEvaluation = evaluateCodeReview(codeReview.findingSeverities, policy);
4042
+ securityPolicyEvaluation = evaluateSecurityReview(security.findingSeverities, policy);
4043
+ } catch (err) {
4044
+ return {
4045
+ verdict: "fail",
4046
+ reason: `a verdict comment carries a finding severity this repository's policy does not recognize: ${err instanceof Error ? err.message : String(err)}`,
4047
+ waived: false
4048
+ };
4049
+ }
4050
+ const codeReviewTextClean = codeReview.value === "APPROVE";
4051
+ const securityTextClean = security.value === "PASS";
4052
+ const codeReviewPolicyClean = codeReviewPolicyEvaluation.outcome === "clean";
4053
+ const securityPolicyClean = securityPolicyEvaluation.outcome === "clean";
4054
+ const codeReviewClean = codeReviewTextClean && codeReviewPolicyClean;
4055
+ const securityClean = securityTextClean && securityPolicyClean;
4056
+ const currentManifest = {
4057
+ headSha: input.headSha,
4058
+ briefHash: input.briefHash ?? null,
4059
+ objectivesVersion: input.objectivesVersion,
4060
+ rulingOrdinal: input.rulingOrdinal,
4061
+ policyDigest: policyDigest(policy)
4062
+ };
4063
+ const codeReviewEchoed = {
4064
+ headSha: codeReview.headSha,
4065
+ briefHash: codeReview.briefHash,
4066
+ objectivesVersion: codeReview.objectivesVersion,
4067
+ rulingOrdinal: codeReview.rulingOrdinal,
4068
+ policyDigest: codeReview.policyDigest
4069
+ };
4070
+ const securityEchoed = {
4071
+ headSha: security.headSha,
4072
+ briefHash: security.briefHash,
4073
+ objectivesVersion: security.objectivesVersion,
4074
+ rulingOrdinal: security.rulingOrdinal,
4075
+ policyDigest: security.policyDigest
4076
+ };
4077
+ const codeReviewBinding = compareManifest(codeReviewEchoed, currentManifest, input.patchIdOf);
4078
+ const securityBinding = compareManifest(securityEchoed, currentManifest, input.patchIdOf);
4079
+ const codeReviewBound = codeReviewBinding.head;
4080
+ const securityBound = securityBinding.head;
4081
+ const codeReviewObjectivesBound = codeReviewBinding.objectivesVersion;
4082
+ const securityObjectivesBound = securityBinding.objectivesVersion;
4083
+ const codeReviewRulingsBound = codeReviewBinding.rulingOrdinal;
4084
+ const securityRulingsBound = securityBinding.rulingOrdinal;
4085
+ if (codeReviewClean && codeReviewBinding.bound && securityClean && securityBinding.bound && mechanicalChecksClean) {
3785
4086
  return {
3786
4087
  verdict: "pass",
3787
4088
  reason: `code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS, both covering head ${input.headSha}, and every reported mechanical check is green.`,
@@ -3789,23 +4090,35 @@ function checkReviewGate(input) {
3789
4090
  };
3790
4091
  }
3791
4092
  const problems = [];
3792
- if (!codeReviewClean) {
4093
+ if (!codeReviewTextClean) {
3793
4094
  problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
4095
+ } else if (!codeReviewPolicyClean) {
4096
+ problems.push(`code-reviewer verdict says APPROVE but carries a finding (${codeReviewPolicyEvaluation.blockingFindings.map((f) => f.severity).join(", ")}) at or above this repository's code-review policy threshold (${policy.codeReviewThreshold}) — a reviewer's own APPROVE never overrides policy`);
3794
4097
  } else if (!codeReviewBound) {
3795
4098
  problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
3796
4099
  } else if (!codeReviewObjectivesBound) {
3797
4100
  problems.push(`the newest code-review verdict was cast against objectives version ${codeReview.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
3798
4101
  } else if (!codeReviewRulingsBound) {
3799
4102
  problems.push(`the newest code-review verdict was cast against ruling ordinal ${codeReview.rulingOrdinal ?? "none"}, a newer ruling (ruling ${input.rulingOrdinal}) is now posted on this PR`);
4103
+ } else if (!codeReviewBinding.briefHash) {
4104
+ problems.push(`the newest code-review verdict was cast against brief hash ${codeReview.briefHash ?? "none"}, the frozen brief's current hash is ${currentManifest.briefHash ?? "none"}`);
4105
+ } else if (!codeReviewBinding.policyDigest) {
4106
+ problems.push(`the newest code-review verdict was cast against review policy digest ${codeReview.policyDigest ?? "none"}, this repository's current policy digest is ${currentManifest.policyDigest}`);
3800
4107
  }
3801
- if (!securityClean) {
4108
+ if (!securityTextClean) {
3802
4109
  problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
4110
+ } else if (!securityPolicyClean) {
4111
+ problems.push(`security-review verdict says PASS but carries a finding (${securityPolicyEvaluation.blockingFindings.map((f) => f.severity).join(", ")}) at or above this repository's security policy threshold (${policy.securityThreshold}) — a reviewer's own PASS never overrides policy`);
3803
4112
  } else if (!securityBound) {
3804
4113
  problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
3805
4114
  } else if (!securityObjectivesBound) {
3806
4115
  problems.push(`the newest security-review verdict was cast against objectives version ${security.objectivesVersion ?? "none"}, the Issue's list is now ${input.objectivesVersion}`);
3807
4116
  } else if (!securityRulingsBound) {
3808
4117
  problems.push(`the newest security-review verdict was cast against ruling ordinal ${security.rulingOrdinal ?? "none"}, a newer ruling (ruling ${input.rulingOrdinal}) is now posted on this PR`);
4118
+ } else if (!securityBinding.briefHash) {
4119
+ problems.push(`the newest security-review verdict was cast against brief hash ${security.briefHash ?? "none"}, the frozen brief's current hash is ${currentManifest.briefHash ?? "none"}`);
4120
+ } else if (!securityBinding.policyDigest) {
4121
+ problems.push(`the newest security-review verdict was cast against review policy digest ${security.policyDigest ?? "none"}, this repository's current policy digest is ${currentManifest.policyDigest}`);
3809
4122
  }
3810
4123
  if (!mechanicalChecksClean) {
3811
4124
  problems.push(reportedMechanicalChecks.length === 0 ? "no mechanical checks have reported for this head yet" : `mechanical check(s) not green: ${reportedMechanicalChecks.filter((c) => c.bucket !== "pass").map((c) => `${c.name} (${c.bucket})`).join(", ")}`);
@@ -4497,42 +4810,45 @@ function checkDispatchReadiness(input) {
4497
4810
  const { trancheSlug, task } = input;
4498
4811
  const taskLabel = `task ${task.id} (tranche ${trancheSlug})`;
4499
4812
  const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
4500
- const blockers = [];
4813
+ const blockerDetails = [];
4814
+ const push = (blockerClass, message) => {
4815
+ blockerDetails.push({ class: blockerClass, message });
4816
+ };
4501
4817
  if (task.issue === null) {
4502
- blockers.push(`dispatch-gate issue-existence: ${taskLabel} has no Issue (#TBD or blank) in the topology — not dispatchable until the Planner cuts the Issue.`);
4818
+ push("issue-existence", `dispatch-gate issue-existence: ${taskLabel} has no Issue (#TBD or blank) in the topology — not dispatchable until the Planner cuts the Issue.`);
4503
4819
  } else if (input.issue === null) {
4504
- blockers.push(`dispatch-gate issue-existence: ${taskLabel} names Issue #${task.issue}, but it does not resolve to a real GitHub Issue (phantom reference).`);
4820
+ push("issue-existence", `dispatch-gate issue-existence: ${taskLabel} names Issue #${task.issue}, but it does not resolve to a real GitHub Issue (phantom reference).`);
4505
4821
  }
4506
4822
  if (input.issue !== null && !input.issueRationalePass) {
4507
- 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.`);
4823
+ push("rationale", `dispatch-gate rationale: Issue #${input.issue.number} for ${taskLabel} fails the rationale gate (checkIssueRationale) — the Planner must complete the eight-field rationale before this task is dispatchable.`);
4508
4824
  }
4509
4825
  for (const dep of input.dependsOn) {
4510
4826
  if (isSelfDependency(dep, task, input.issue)) {
4511
4827
  const issueStr = input.issue !== null ? `#${input.issue.number}` : "?";
4512
- 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.`);
4828
+ push("internal-self-dependency", `dispatch-gate INTERNAL: parsed a self-dependency for task ${task.id} (${issueStr}) — this is a parser bug in parseRationaleDeps, not a real dependency. Please report it upstream. Re-run once the rationale is corrected or the fix ships.`);
4513
4829
  continue;
4514
4830
  }
4515
4831
  if (dep.resolved === false) {
4516
- 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.`);
4832
+ push("depends-on-unresolvable", `dispatch-gate depends-on: ${taskLabel} depends on "${dep.id}", which is UNRESOLVABLE — the resolver could not find a matching tranche/task/Issue for this edge (not a claim about merge status). Not dispatchable until the edge is corrected.`);
4517
4833
  continue;
4518
4834
  }
4519
4835
  if (!dep.merged && !isHandClosedByRecognizedPrincipal(dep, principalAllowlist)) {
4520
4836
  const issueStr = dep.issue !== null ? ` (#${dep.issue})` : "";
4521
- blockers.push(`dispatch-gate depends-on: ${taskLabel} depends on ${dep.id}${issueStr}, whose PR is not merged yet — not dispatchable, it serializes behind it.`);
4837
+ push("depends-on-not-merged", `dispatch-gate depends-on: ${taskLabel} depends on ${dep.id}${issueStr}, whose PR is not merged yet — not dispatchable, it serializes behind it.`);
4522
4838
  }
4523
4839
  }
4524
4840
  for (const c of input.conflictsWith) {
4525
4841
  if (c.openOrInFlight) {
4526
4842
  const issueStr = c.issue !== null ? ` (#${c.issue})` : "";
4527
- blockers.push(`dispatch-gate conflicts-with: ${taskLabel} conflicts with ${c.id}${issueStr}, whose PR is open or in-flight — not dispatchable until it merges.`);
4843
+ push("conflicts-with", `dispatch-gate conflicts-with: ${taskLabel} conflicts with ${c.id}${issueStr}, whose PR is open or in-flight — not dispatchable until it merges.`);
4528
4844
  }
4529
4845
  }
4530
4846
  for (const proj of input.priorTrancheArchival) {
4531
4847
  if (proj.priorTrancheSlug !== null && !proj.archived) {
4532
- 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.`);
4848
+ push("prior-tranche-archival", `dispatch-gate prior-tranche-archival: project \`${proj.project}\`'s previous tranche \`${proj.priorTrancheSlug}\` is not archived — the Tranche Archivist must run before new work on this product.`);
4533
4849
  }
4534
4850
  }
4535
- return { ready: blockers.length === 0, blockers };
4851
+ return { ready: blockerDetails.length === 0, blockers: blockerDetails.map((b) => b.message), blockerDetails };
4536
4852
  }
4537
4853
  // ../../packages/aeg-core/src/single-plan-pr.ts
4538
4854
  function trancheSlugFromTopologyPath(path) {
@@ -4634,6 +4950,91 @@ function checkBranchTopology(input) {
4634
4950
  reason: `Branch \`${branch}\` matches topology row \`${taskId}\` in ${topoPath}.`
4635
4951
  };
4636
4952
  }
4953
+ // ../../packages/aeg-core/src/task-tools.ts
4954
+ import { z } from "zod";
4955
+ var TASK_TOOL_ERROR_KINDS = [
4956
+ "validation",
4957
+ "authority",
4958
+ "precondition",
4959
+ "capability",
4960
+ "infrastructure",
4961
+ "cancellation",
4962
+ "timeout",
4963
+ "uncertain_effect"
4964
+ ];
4965
+ var TaskToolErrorSchema = z.object({
4966
+ kind: z.enum(TASK_TOOL_ERROR_KINDS),
4967
+ message: z.string().min(1),
4968
+ detail: z.string().optional()
4969
+ });
4970
+ var TaskToolRefSchema = z.union([
4971
+ z.object({ tranche: z.string().min(1), id: z.string().min(1) }),
4972
+ z.object({ issue: z.number().int().positive() })
4973
+ ]);
4974
+ var MAX_PAGE_LIMIT = 100;
4975
+ var PageRequestSchema = z.object({
4976
+ cursor: z.string().optional(),
4977
+ limit: z.number().int().positive().max(MAX_PAGE_LIMIT).optional()
4978
+ });
4979
+ var FreshnessSchema = z.enum(["fresh", "stale", "unknown"]);
4980
+ var ObservedSchema = z.object({
4981
+ observedAt: z.string(),
4982
+ freshness: FreshnessSchema
4983
+ });
4984
+ var TaskStatusInputSchema = z.object({
4985
+ task: TaskToolRefSchema.optional()
4986
+ }).merge(PageRequestSchema);
4987
+ var TaskStatusItemSchema = z.object({
4988
+ task: TaskToolRefSchema,
4989
+ issue: z.number().int().positive(),
4990
+ pr: z.number().int().positive().nullable(),
4991
+ state: z.string()
4992
+ }).merge(ObservedSchema);
4993
+ var TaskStatusResultSchema = z.object({
4994
+ items: z.array(TaskStatusItemSchema),
4995
+ nextCursor: z.string().nullable()
4996
+ });
4997
+ var RequestedAuthoritySchema = z.enum(["planner", "principal", "operator", "self"]);
4998
+ var EscalationInputsSchema = z.object({
4999
+ task: z.number().int().positive(),
5000
+ round: z.number().int().nonnegative(),
5001
+ head: z.string(),
5002
+ branch: z.string(),
5003
+ prNumber: z.number().int().positive()
5004
+ });
5005
+ var EscalationEvidenceSchema = z.object({
5006
+ round: z.number().int().nonnegative(),
5007
+ reviewer: z.string().nullable(),
5008
+ security: z.string().nullable()
5009
+ });
5010
+ var TaskEscalationReadInputSchema = z.object({
5011
+ task: TaskToolRefSchema
5012
+ }).merge(PageRequestSchema);
5013
+ var TaskEscalationPacketSchema = z.object({
5014
+ reason: z.string(),
5015
+ detail: z.string().nullable(),
5016
+ inputs: EscalationInputsSchema.nullable(),
5017
+ evidence: EscalationEvidenceSchema.nullable(),
5018
+ attemptedRecovery: z.string(),
5019
+ requestedAuthority: RequestedAuthoritySchema,
5020
+ permittedNextActions: z.array(z.string())
5021
+ }).merge(ObservedSchema);
5022
+ var TaskEscalationReadResultSchema = z.object({
5023
+ items: z.array(TaskEscalationPacketSchema),
5024
+ nextCursor: z.string().nullable()
5025
+ }).merge(ObservedSchema);
5026
+ var TaskStartInputSchema = z.object({
5027
+ tranche: z.string().min(1),
5028
+ id: z.string().min(1)
5029
+ });
5030
+ var TaskResumeInputSchema = z.object({
5031
+ task: TaskToolRefSchema
5032
+ });
5033
+ var TaskCancelInputSchema = z.object({
5034
+ task: TaskToolRefSchema,
5035
+ reason: z.string().min(1)
5036
+ });
5037
+ var NoResultSchema = z.never();
4637
5038
  // ../../packages/aeg-core/src/first-push-dispatch-gate.ts
4638
5039
  function parseTaskBranch(branch) {
4639
5040
  const m = /^task\/([^/]+)\/([^/]+)$/.exec(branch);
@@ -4717,7 +5118,6 @@ function decideIssueAssignment(input) {
4717
5118
  };
4718
5119
  }
4719
5120
  // ../../packages/aeg-core/src/test-plan-gate.ts
4720
- var TASK_BRANCH_PATTERN2 = /^task\/[^/]+\/[^/]+$/;
4721
5121
  function evaluateTestPlanGate(body, branch) {
4722
5122
  if (!body) {
4723
5123
  return {
@@ -4730,7 +5130,7 @@ function evaluateTestPlanGate(body, branch) {
4730
5130
  }
4731
5131
  const located = locateTestPlanSection(body);
4732
5132
  if (!located.found) {
4733
- if (TASK_BRANCH_PATTERN2.test(branch)) {
5133
+ if (parseTaskBranchIdentity(branch) !== null) {
4734
5134
  return {
4735
5135
  verdict: "fail",
4736
5136
  messages: [
@@ -4858,7 +5258,7 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
4858
5258
  return findings;
4859
5259
  }
4860
5260
  // ../../packages/aeg-core/src/log/schema.ts
4861
- import { z } from "zod";
5261
+ import { z as z2 } from "zod";
4862
5262
  var ROLE_VALUES = [
4863
5263
  "planner",
4864
5264
  "developer",
@@ -4868,136 +5268,172 @@ var ROLE_VALUES = [
4868
5268
  "archivist",
4869
5269
  "architect"
4870
5270
  ];
4871
- var RoleSchema = z.enum(ROLE_VALUES);
5271
+ var RoleSchema = z2.enum(ROLE_VALUES);
4872
5272
  var HOST_VALUES = ["hook", "ci", "cli", "loop"];
4873
- var HostSchema = z.enum(HOST_VALUES);
5273
+ var HostSchema = z2.enum(HOST_VALUES);
4874
5274
  var RUN_ID_PATTERN = /^[A-Za-z0-9_.-]{1,128}$/;
4875
- var HeaderMetaSchema = z.object({
4876
- schema: z.literal(1),
4877
- ts: z.string(),
4878
- run_id: z.string().regex(RUN_ID_PATTERN),
4879
- seq: z.number().int().nonnegative(),
4880
- repo: z.string().nullable(),
4881
- vinaya: z.string(),
4882
- doctrine: z.string(),
5275
+ var headerMetaCore = {
5276
+ ts: z2.string(),
5277
+ run_id: z2.string().regex(RUN_ID_PATTERN),
5278
+ seq: z2.number().int().nonnegative(),
5279
+ repo: z2.string().nullable(),
5280
+ vinaya: z2.string(),
5281
+ doctrine: z2.string(),
4883
5282
  host: HostSchema,
4884
- machine: z.string()
5283
+ machine: z2.string()
5284
+ };
5285
+ var HeaderMetaV1Schema = z2.object({
5286
+ schema: z2.literal(1),
5287
+ ...headerMetaCore
5288
+ }).strict();
5289
+ var LineageSchema = z2.object({
5290
+ run: z2.string().nullable(),
5291
+ attempt: z2.number().int().nullable(),
5292
+ parent: z2.string().nullable()
5293
+ }).strict();
5294
+ var InputVersionsSchema = z2.object({
5295
+ objectives_version: z2.string().nullable(),
5296
+ brief_hash: z2.string().nullable(),
5297
+ ruling_ordinal: z2.number().int().nullable(),
5298
+ policy_digest: z2.string().nullable()
5299
+ }).strict();
5300
+ var ProvenanceSchema = z2.enum(["parent_attributed", "env_correlated", "self_reported", "unavailable"]);
5301
+ var HeaderMetaV2Schema = z2.object({
5302
+ schema: z2.literal(2),
5303
+ ...headerMetaCore,
5304
+ event_id: z2.string().min(1),
5305
+ process_id: z2.string().min(1),
5306
+ actor_id: z2.string().nullable(),
5307
+ lineage: LineageSchema,
5308
+ input_versions: InputVersionsSchema,
5309
+ provenance: ProvenanceSchema
4885
5310
  }).strict();
4886
- var SubjectSchema = z.object({
4887
- issue: z.number().int().nullable(),
4888
- pr: z.number().int().optional(),
4889
- sha: z.string().optional(),
4890
- role: z.union([RoleSchema, z.literal("unattributed")]),
4891
- round: z.number().int().optional(),
4892
- objectives_version: z.string().optional()
5311
+ var HeaderMetaSchema = z2.discriminatedUnion("schema", [HeaderMetaV1Schema, HeaderMetaV2Schema]);
5312
+ var SubjectSchema = z2.object({
5313
+ issue: z2.number().int().nullable(),
5314
+ pr: z2.number().int().optional(),
5315
+ sha: z2.string().optional(),
5316
+ role: z2.union([RoleSchema, z2.literal("unattributed")]),
5317
+ round: z2.number().int().optional(),
5318
+ objectives_version: z2.string().optional()
4893
5319
  }).strict();
4894
- var HeaderSchema = z.object({
5320
+ var HeaderSchema = z2.object({
4895
5321
  meta: HeaderMetaSchema,
4896
5322
  subject: SubjectSchema
4897
5323
  }).strict();
4898
5324
  var envelopeTail = {
4899
- duration_ms: z.number().nonnegative().optional(),
4900
- payload: z.object({}).strict()
5325
+ duration_ms: z2.number().nonnegative().optional(),
5326
+ payload: z2.object({}).strict()
4901
5327
  };
4902
- var DispatchOutcomeSchema = z.discriminatedUnion("type", [
4903
- z.object({ type: z.literal("pr_opened"), pr: z.number().int(), head: z.string() }).strict(),
4904
- z.object({
4905
- type: z.literal("round_pushed"),
4906
- pr: z.number().int(),
4907
- head: z.string(),
4908
- comment_id: z.number().int()
5328
+ var ReviewFindingSchema = z2.object({
5329
+ id: z2.string(),
5330
+ severity: z2.string(),
5331
+ state: z2.string().optional(),
5332
+ severity_scale: z2.string().optional(),
5333
+ policy_treatment: z2.enum(["blocking", "non_blocking", "unavailable"]).optional(),
5334
+ confidence: z2.number().min(0).max(1).optional(),
5335
+ confidence_scale: z2.string().optional(),
5336
+ confidence_source: z2.string().optional()
5337
+ }).strict();
5338
+ var DispatchOutcomeSchema = z2.discriminatedUnion("type", [
5339
+ z2.object({ type: z2.literal("pr_opened"), pr: z2.number().int(), head: z2.string() }).strict(),
5340
+ z2.object({
5341
+ type: z2.literal("round_pushed"),
5342
+ pr: z2.number().int(),
5343
+ head: z2.string(),
5344
+ comment_id: z2.number().int()
4909
5345
  }).strict(),
4910
- z.object({
4911
- type: z.literal("verdict"),
4912
- verdict: z.enum(["APPROVE", "REQUEST CHANGES", "PASS", "FAIL"]),
4913
- head: z.string(),
4914
- comment_id: z.number().int(),
4915
- objectives: z.array(z.object({ id: z.string(), met: z.boolean() }).strict()),
4916
- findings: z.array(z.object({ id: z.string(), severity: z.string(), state: z.string().optional() }).strict())
5346
+ z2.object({
5347
+ type: z2.literal("verdict"),
5348
+ verdict: z2.enum(["APPROVE", "REQUEST CHANGES", "PASS", "FAIL"]),
5349
+ head: z2.string(),
5350
+ comment_id: z2.number().int(),
5351
+ objectives: z2.array(z2.object({ id: z2.string(), met: z2.boolean() }).strict()),
5352
+ findings: z2.array(ReviewFindingSchema)
4917
5353
  }).strict(),
4918
- z.object({
4919
- type: z.literal("escalation"),
4920
- class: z.enum(["authority", "strategy", "product"]),
4921
- comment_id: z.number().int()
5354
+ z2.object({
5355
+ type: z2.literal("escalation"),
5356
+ class: z2.enum(["authority", "strategy", "product"]),
5357
+ comment_id: z2.number().int()
4922
5358
  }).strict(),
4923
- z.object({ type: z.literal("brief"), comment_id: z.number().int(), hash: z.string() }).strict(),
4924
- z.object({ type: z.literal("plan"), issues: z.array(z.number().int()) }).strict(),
4925
- z.object({ type: z.literal("archive"), provenance_comment_id: z.number().int() }).strict()
5359
+ z2.object({ type: z2.literal("brief"), comment_id: z2.number().int(), hash: z2.string() }).strict(),
5360
+ z2.object({ type: z2.literal("plan"), issues: z2.array(z2.number().int()) }).strict(),
5361
+ z2.object({ type: z2.literal("archive"), provenance_comment_id: z2.number().int() }).strict()
4926
5362
  ]);
4927
5363
  var dispatchShared = {
4928
5364
  meta: HeaderMetaSchema,
4929
5365
  subject: SubjectSchema,
4930
- kind: z.literal("dispatch"),
5366
+ kind: z2.literal("dispatch"),
4931
5367
  ...envelopeTail,
4932
5368
  target_role: RoleSchema,
4933
- model: z.string(),
4934
- round: z.number().int().optional(),
4935
- effect_id: z.string()
5369
+ model: z2.string(),
5370
+ round: z2.number().int().optional(),
5371
+ effect_id: z2.string()
4936
5372
  };
4937
- var dispatchUsageField = z.object({ input: z.number().nonnegative(), output: z.number().nonnegative() }).strict().nullable();
4938
- var DispatchEventSchema = z.discriminatedUnion("event", [
4939
- z.object({ ...dispatchShared, event: z.literal("dispatched"), prompt_hash: z.string() }).strict(),
4940
- z.object({
5373
+ var dispatchUsageField = z2.object({ input: z2.number().nonnegative(), output: z2.number().nonnegative() }).strict().nullable();
5374
+ var DispatchEventSchema = z2.discriminatedUnion("event", [
5375
+ z2.object({ ...dispatchShared, event: z2.literal("dispatched"), prompt_hash: z2.string() }).strict(),
5376
+ z2.object({
4941
5377
  ...dispatchShared,
4942
- event: z.literal("outcome_received"),
5378
+ event: z2.literal("outcome_received"),
4943
5379
  outcome: DispatchOutcomeSchema,
4944
5380
  usage: dispatchUsageField
4945
5381
  }).strict(),
4946
- z.object({
5382
+ z2.object({
4947
5383
  ...dispatchShared,
4948
- event: z.literal("dispatch_failed"),
4949
- reason: z.enum(["timeout", "crash", "refused", "unattributed_write"]),
5384
+ event: z2.literal("dispatch_failed"),
5385
+ reason: z2.enum(["timeout", "crash", "refused", "unattributed_write"]),
4950
5386
  usage: dispatchUsageField
4951
5387
  }).strict()
4952
5388
  ]);
4953
5389
  var loopShared = {
4954
5390
  meta: HeaderMetaSchema,
4955
5391
  subject: SubjectSchema,
4956
- kind: z.literal("dev_review_loop"),
5392
+ kind: z2.literal("dev_review_loop"),
4957
5393
  ...envelopeTail,
4958
- loop_id: z.string()
5394
+ loop_id: z2.string()
4959
5395
  };
4960
- var DevReviewLoopEventSchema = z.discriminatedUnion("event", [
4961
- z.object({
5396
+ var DevReviewLoopEventSchema = z2.discriminatedUnion("event", [
5397
+ z2.object({
4962
5398
  ...loopShared,
4963
- event: z.literal("loop_started"),
4964
- task: z.number().int(),
4965
- policy: z.object({
4966
- max_rounds: z.number().int().nonnegative(),
4967
- reviewers: z.array(RoleSchema),
4968
- models: z.record(RoleSchema, z.string())
5399
+ event: z2.literal("loop_started"),
5400
+ task: z2.number().int(),
5401
+ policy: z2.object({
5402
+ max_rounds: z2.number().int().nonnegative(),
5403
+ reviewers: z2.array(RoleSchema),
5404
+ models: z2.record(RoleSchema, z2.string())
4969
5405
  }).strict()
4970
5406
  }).strict(),
4971
- z.object({ ...loopShared, event: z.literal("round_started"), round: z.number().int(), base_head: z.string() }).strict(),
4972
- z.object({
5407
+ z2.object({ ...loopShared, event: z2.literal("round_started"), round: z2.number().int(), base_head: z2.string() }).strict(),
5408
+ z2.object({
4973
5409
  ...loopShared,
4974
- event: z.literal("gate_result_read"),
4975
- round: z.number().int(),
4976
- head: z.string(),
4977
- green: z.boolean()
5410
+ event: z2.literal("gate_result_read"),
5411
+ round: z2.number().int(),
5412
+ head: z2.string(),
5413
+ green: z2.boolean()
4978
5414
  }).strict(),
4979
- z.object({
5415
+ z2.object({
4980
5416
  ...loopShared,
4981
- event: z.literal("verdicts_read"),
4982
- round: z.number().int(),
4983
- head: z.string(),
4984
- all_approve: z.boolean(),
4985
- blockers: z.number().int().nonnegative()
5417
+ event: z2.literal("verdicts_read"),
5418
+ round: z2.number().int(),
5419
+ head: z2.string(),
5420
+ all_approve: z2.boolean(),
5421
+ blockers: z2.number().int().nonnegative()
4986
5422
  }).strict(),
4987
- z.object({
5423
+ z2.object({
4988
5424
  ...loopShared,
4989
- event: z.literal("findings_compared"),
4990
- round: z.number().int(),
4991
- open: z.array(z.string()),
4992
- resolved: z.array(z.string()),
4993
- new: z.array(z.string()),
4994
- recurring: z.array(z.string())
5425
+ event: z2.literal("findings_compared"),
5426
+ round: z2.number().int(),
5427
+ open: z2.array(z2.string()),
5428
+ resolved: z2.array(z2.string()),
5429
+ new: z2.array(z2.string()),
5430
+ recurring: z2.array(z2.string())
4995
5431
  }).strict(),
4996
- z.object({
5432
+ z2.object({
4997
5433
  ...loopShared,
4998
- event: z.literal("stop_condition_met"),
4999
- round: z.number().int(),
5000
- condition: z.enum([
5434
+ event: z2.literal("stop_condition_met"),
5435
+ round: z2.number().int(),
5436
+ condition: z2.enum([
5001
5437
  "green",
5002
5438
  "max_rounds",
5003
5439
  "no_progress",
@@ -5007,42 +5443,49 @@ var DevReviewLoopEventSchema = z.discriminatedUnion("event", [
5007
5443
  "reappearance"
5008
5444
  ])
5009
5445
  }).strict(),
5010
- z.object({
5446
+ z2.object({
5447
+ ...loopShared,
5448
+ event: z2.literal("paused"),
5449
+ round: z2.number().int(),
5450
+ reason: z2.enum(["escalation", "principal_item", "refreeze_needed"])
5451
+ }).strict(),
5452
+ z2.object({
5011
5453
  ...loopShared,
5012
- event: z.literal("paused"),
5013
- round: z.number().int(),
5014
- reason: z.enum(["escalation", "principal_item", "refreeze_needed"])
5454
+ event: z2.literal("resumed"),
5455
+ round: z2.number().int(),
5456
+ by: z2.literal("principal")
5015
5457
  }).strict(),
5016
- z.object({
5458
+ z2.object({
5017
5459
  ...loopShared,
5018
- event: z.literal("resumed"),
5019
- round: z.number().int(),
5020
- by: z.literal("principal")
5460
+ event: z2.literal("unpushed_work_resume"),
5461
+ round: z2.number().int(),
5462
+ branch: z2.string(),
5463
+ detail: z2.string()
5021
5464
  }).strict(),
5022
- z.object({
5465
+ z2.object({
5023
5466
  ...loopShared,
5024
- event: z.literal("round_ended"),
5025
- round: z.number().int(),
5026
- base_head: z.string(),
5027
- head: z.string(),
5028
- files_changed: z.number().int().nonnegative(),
5029
- insertions: z.number().int().nonnegative(),
5030
- deletions: z.number().int().nonnegative(),
5031
- wall_ms: z.number().nonnegative(),
5032
- outcome: z.enum(["green", "changes_requested", "escalated"])
5467
+ event: z2.literal("round_ended"),
5468
+ round: z2.number().int(),
5469
+ base_head: z2.string(),
5470
+ head: z2.string(),
5471
+ files_changed: z2.number().int().nonnegative(),
5472
+ insertions: z2.number().int().nonnegative(),
5473
+ deletions: z2.number().int().nonnegative(),
5474
+ wall_ms: z2.number().nonnegative(),
5475
+ outcome: z2.enum(["green", "changes_requested", "escalated"])
5033
5476
  }).strict(),
5034
- z.object({
5477
+ z2.object({
5035
5478
  ...loopShared,
5036
- event: z.literal("journal_finalized"),
5037
- rounds: z.number().int().nonnegative(),
5038
- total_wall_ms: z.number().nonnegative(),
5039
- time_to_green_ms: z.number().nonnegative().nullable(),
5040
- files_changed_total: z.number().int().nonnegative(),
5041
- final_head: z.string(),
5042
- result: z.enum(["merged_ready", "stopped"])
5479
+ event: z2.literal("journal_finalized"),
5480
+ rounds: z2.number().int().nonnegative(),
5481
+ total_wall_ms: z2.number().nonnegative(),
5482
+ time_to_green_ms: z2.number().nonnegative().nullable(),
5483
+ files_changed_total: z2.number().int().nonnegative(),
5484
+ final_head: z2.string(),
5485
+ result: z2.enum(["merged_ready", "stopped"])
5043
5486
  }).strict()
5044
5487
  ]);
5045
- var ForgeOpSchema = z.enum([
5488
+ var ForgeOpSchema = z2.enum([
5046
5489
  "pr.create",
5047
5490
  "pr.comment",
5048
5491
  "pr.body.replace",
@@ -5056,50 +5499,238 @@ var ForgeOpSchema = z.enum([
5056
5499
  "label.add",
5057
5500
  "label.remove"
5058
5501
  ]);
5059
- var ForgeWriteTargetSchema = z.object({
5060
- issue: z.number().int().optional(),
5061
- pr: z.number().int().optional()
5502
+ var ForgeWriteTargetSchema = z2.object({
5503
+ issue: z2.number().int().optional(),
5504
+ pr: z2.number().int().optional()
5062
5505
  }).strict();
5063
5506
  var forgeWriteShared = {
5064
5507
  meta: HeaderMetaSchema,
5065
5508
  subject: SubjectSchema,
5066
- kind: z.literal("forge_write"),
5509
+ kind: z2.literal("forge_write"),
5067
5510
  ...envelopeTail,
5068
5511
  op: ForgeOpSchema,
5069
5512
  target: ForgeWriteTargetSchema
5070
5513
  };
5071
- var ForgeWriteEventSchema = z.discriminatedUnion("event", [
5072
- z.object({ ...forgeWriteShared, event: z.literal("validated") }).strict(),
5073
- z.object({ ...forgeWriteShared, event: z.literal("refused"), reason: z.string() }).strict(),
5074
- z.object({ ...forgeWriteShared, event: z.literal("written"), comment_ids: z.array(z.string()) }).strict()
5514
+ var ForgeWriteEventSchema = z2.discriminatedUnion("event", [
5515
+ z2.object({ ...forgeWriteShared, event: z2.literal("validated") }).strict(),
5516
+ z2.object({ ...forgeWriteShared, event: z2.literal("refused"), reason: z2.string() }).strict(),
5517
+ z2.object({ ...forgeWriteShared, event: z2.literal("written"), comment_ids: z2.array(z2.string()) }).strict()
5518
+ ]);
5519
+ var GateOutcomeSchema = z2.enum([
5520
+ "pass",
5521
+ "fail",
5522
+ "wait",
5523
+ "skip",
5524
+ "invalid_input",
5525
+ "unavailable_dependency",
5526
+ "timeout",
5527
+ "cancelled"
5528
+ ]);
5529
+ var gateShared = {
5530
+ meta: HeaderMetaSchema,
5531
+ subject: SubjectSchema,
5532
+ kind: z2.literal("gate"),
5533
+ ...envelopeTail,
5534
+ check: z2.string(),
5535
+ check_version: z2.string().nullable(),
5536
+ policy_version: z2.string().nullable(),
5537
+ input_fingerprint: z2.string().nullable()
5538
+ };
5539
+ var GateEventSchema = z2.discriminatedUnion("event", [
5540
+ z2.object({
5541
+ ...gateShared,
5542
+ event: z2.literal("checked"),
5543
+ outcome: GateOutcomeSchema,
5544
+ reason: z2.string().optional()
5545
+ }).strict()
5546
+ ]);
5547
+ var OperationResultSchema = z2.enum(["ok", "error", "refused", "timeout", "cancelled", "unavailable"]);
5548
+ var operationShared = {
5549
+ meta: HeaderMetaSchema,
5550
+ subject: SubjectSchema,
5551
+ kind: z2.literal("operation"),
5552
+ ...envelopeTail,
5553
+ operation: z2.string(),
5554
+ target: z2.string().nullable()
5555
+ };
5556
+ var OperationEventSchema = z2.discriminatedUnion("event", [
5557
+ z2.object({
5558
+ ...operationShared,
5559
+ event: z2.literal("completed"),
5560
+ result: OperationResultSchema,
5561
+ error_class: z2.string().nullable()
5562
+ }).strict()
5075
5563
  ]);
5076
- var LogEventSchema = z.union([DispatchEventSchema, DevReviewLoopEventSchema, ForgeWriteEventSchema]);
5564
+ var UsageUnitsSchema = z2.object({
5565
+ input: z2.number().nonnegative().nullable(),
5566
+ output: z2.number().nonnegative().nullable(),
5567
+ cache: z2.number().nonnegative().nullable()
5568
+ }).strict();
5569
+ var usageShared = {
5570
+ meta: HeaderMetaSchema,
5571
+ subject: SubjectSchema,
5572
+ kind: z2.literal("usage"),
5573
+ ...envelopeTail,
5574
+ model: z2.string().nullable(),
5575
+ source: z2.string(),
5576
+ semantics: z2.enum(["cumulative", "delta"])
5577
+ };
5578
+ var UsageEventSchema = z2.discriminatedUnion("event", [
5579
+ z2.object({
5580
+ ...usageShared,
5581
+ event: z2.literal("observed"),
5582
+ units: UsageUnitsSchema,
5583
+ unknown_reason: z2.string().nullable()
5584
+ }).strict()
5585
+ ]);
5586
+ var RoleAttemptOutcomeSchema = z2.enum([
5587
+ "completed",
5588
+ "incomplete",
5589
+ "infrastructure_failed",
5590
+ "cancelled",
5591
+ "timed_out",
5592
+ "capability_refused"
5593
+ ]);
5594
+ var roleAttemptShared = {
5595
+ meta: HeaderMetaSchema,
5596
+ subject: SubjectSchema,
5597
+ kind: z2.literal("role_attempt"),
5598
+ ...envelopeTail,
5599
+ actor: z2.string().nullable(),
5600
+ attempt: z2.number().int().nullable()
5601
+ };
5602
+ var RoleAttemptEventSchema = z2.discriminatedUnion("event", [
5603
+ z2.object({
5604
+ ...roleAttemptShared,
5605
+ event: z2.literal("attempted"),
5606
+ outcome: RoleAttemptOutcomeSchema,
5607
+ usage: dispatchUsageField
5608
+ }).strict()
5609
+ ]);
5610
+ var handoffShared = {
5611
+ meta: HeaderMetaSchema,
5612
+ subject: SubjectSchema,
5613
+ kind: z2.literal("handoff"),
5614
+ ...envelopeTail,
5615
+ class: z2.enum(["authority", "strategy", "product"]),
5616
+ reason: z2.string()
5617
+ };
5618
+ var HandoffEventSchema = z2.discriminatedUnion("event", [
5619
+ z2.object({
5620
+ ...handoffShared,
5621
+ event: z2.literal("raised"),
5622
+ requested_decision: z2.string().nullable()
5623
+ }).strict(),
5624
+ z2.object({
5625
+ ...handoffShared,
5626
+ event: z2.literal("resolved"),
5627
+ resolution: z2.string().nullable(),
5628
+ resolved_by: z2.string().nullable()
5629
+ }).strict()
5630
+ ]);
5631
+ var EffectTargetSchema = z2.object({
5632
+ kind: z2.string(),
5633
+ ref: z2.string()
5634
+ }).strict();
5635
+ var effectShared = {
5636
+ meta: HeaderMetaSchema,
5637
+ subject: SubjectSchema,
5638
+ kind: z2.literal("effect"),
5639
+ ...envelopeTail,
5640
+ effect_id: z2.string(),
5641
+ target: EffectTargetSchema
5642
+ };
5643
+ var EffectEventSchema = z2.discriminatedUnion("event", [
5644
+ z2.object({ ...effectShared, event: z2.literal("attempted") }).strict(),
5645
+ z2.object({ ...effectShared, event: z2.literal("observed"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict(),
5646
+ z2.object({ ...effectShared, event: z2.literal("verified"), outcome: z2.enum(["success", "failure", "uncertain"]) }).strict()
5647
+ ]);
5648
+ var LogEventSchema = z2.union([
5649
+ DispatchEventSchema,
5650
+ DevReviewLoopEventSchema,
5651
+ ForgeWriteEventSchema,
5652
+ GateEventSchema,
5653
+ OperationEventSchema,
5654
+ UsageEventSchema,
5655
+ RoleAttemptEventSchema,
5656
+ HandoffEventSchema,
5657
+ EffectEventSchema
5658
+ ]);
5659
+ // ../../packages/aeg-core/src/dev-review-loop/journal-reconstruction.ts
5660
+ var STOPPED_CONDITIONS = new Set(["confidence", "reappearance", "no_progress", "max_rounds"]);
5661
+ // ../../packages/aeg-core/src/control-store/records.ts
5662
+ import { z as z3 } from "zod";
5663
+ var isoTimestamp = z3.string().min(1);
5664
+ var taskId = z3.number().int().positive();
5665
+ var epochNumber = z3.number().int().nonnegative();
5666
+ var RunRecordSchema = z3.object({
5667
+ version: z3.literal(1),
5668
+ kind: z3.literal("run"),
5669
+ task: taskId,
5670
+ runId: z3.string().min(1),
5671
+ pid: z3.number().int().positive(),
5672
+ host: z3.string().min(1),
5673
+ startedAt: isoTimestamp
5674
+ }).strict();
5675
+ var InputRecordSchema = z3.object({
5676
+ version: z3.literal(1),
5677
+ kind: z3.literal("input"),
5678
+ task: taskId,
5679
+ runId: z3.string().min(1),
5680
+ source: z3.union([z3.literal("fresh"), z3.literal("resume")]),
5681
+ pr: z3.number().int().positive().nullable(),
5682
+ round: z3.number().int().nonnegative(),
5683
+ recordedAt: isoTimestamp
5684
+ }).strict();
5685
+ var OwnershipRecordSchema = z3.object({
5686
+ version: z3.literal(1),
5687
+ kind: z3.literal("ownership"),
5688
+ task: taskId,
5689
+ epoch: epochNumber,
5690
+ ownerId: z3.string().min(1),
5691
+ pid: z3.number().int().positive(),
5692
+ host: z3.string().min(1),
5693
+ acquiredAt: isoTimestamp
5694
+ }).strict();
5695
+ var TransitionRecordSchema = z3.object({
5696
+ version: z3.literal(1),
5697
+ kind: z3.literal("transition"),
5698
+ task: taskId,
5699
+ epoch: epochNumber,
5700
+ seq: z3.number().int().nonnegative(),
5701
+ from: z3.string().min(1),
5702
+ to: z3.string().min(1),
5703
+ detail: z3.string().optional(),
5704
+ at: isoTimestamp
5705
+ }).strict();
5077
5706
  // src/lib/config.ts
5078
5707
  import { execFileSync as execFileSync2 } from "node:child_process";
5079
5708
  import { existsSync, mkdirSync, readFileSync as readFileSync2, realpathSync, writeFileSync } from "node:fs";
5080
5709
  import { homedir } from "node:os";
5081
5710
  import { dirname, join, resolve } from "node:path";
5082
- import { z as z2 } from "zod";
5711
+ import { z as z4 } from "zod";
5083
5712
 
5084
5713
  // src/lib/agent-vendors.ts
5085
5714
  var AGENT_VENDORS = ["skills", "claude", "gemini"];
5086
5715
 
5087
5716
  // src/lib/config.ts
5088
- var EnvEntrySchema = z2.union([
5089
- z2.literal(true),
5090
- z2.object({ optional: z2.literal(true) }),
5091
- z2.object({ anyOf: z2.array(z2.string()).min(2) }),
5092
- z2.string()
5717
+ var EnvEntrySchema = z4.union([
5718
+ z4.literal(true),
5719
+ z4.object({ optional: z4.literal(true) }),
5720
+ z4.object({ anyOf: z4.array(z4.string()).min(2) }),
5721
+ z4.string()
5093
5722
  ]);
5094
- var CheckEntrySchema = z2.object({
5095
- run: z2.string(),
5096
- scope: z2.enum(["diff", "full"]),
5097
- include: z2.array(z2.string()).optional(),
5098
- args: z2.array(z2.string()).optional(),
5099
- timeoutMs: z2.number().optional(),
5100
- env: z2.record(z2.string(), EnvEntrySchema).optional(),
5101
- requiresOpenPr: z2.boolean().optional(),
5102
- ownWorkflow: z2.boolean().optional()
5723
+ var CheckEntrySchema = z4.object({
5724
+ run: z4.string(),
5725
+ scope: z4.enum(["diff", "full"]),
5726
+ include: z4.array(z4.string()).optional(),
5727
+ args: z4.array(z4.string()).optional(),
5728
+ timeoutMs: z4.number().optional(),
5729
+ env: z4.record(z4.string(), EnvEntrySchema).optional(),
5730
+ requiresOpenPr: z4.boolean().optional(),
5731
+ ownWorkflow: z4.boolean().optional(),
5732
+ principalOwed: z4.literal(true).optional(),
5733
+ validates: z4.enum(["body", "issue"]).optional()
5103
5734
  }).superRefine((entry2, ctx) => {
5104
5735
  if (!entry2.env)
5105
5736
  return;
@@ -5109,25 +5740,26 @@ var CheckEntrySchema = z2.object({
5109
5740
  const members = value.anyOf;
5110
5741
  if (new Set(members).size !== members.length) {
5111
5742
  ctx.addIssue({
5112
- code: z2.ZodIssueCode.custom,
5743
+ code: z4.ZodIssueCode.custom,
5113
5744
  message: `env.${key}.anyOf has duplicate members`,
5114
5745
  path: ["env", key, "anyOf"]
5115
5746
  });
5116
5747
  }
5117
5748
  if (!members.includes(key)) {
5118
5749
  ctx.addIssue({
5119
- code: z2.ZodIssueCode.custom,
5750
+ code: z4.ZodIssueCode.custom,
5120
5751
  message: `env.${key}.anyOf must include "${key}" itself as a member`,
5121
5752
  path: ["env", key, "anyOf"]
5122
5753
  });
5123
5754
  }
5124
5755
  }
5125
5756
  });
5126
- var RoleEntrySchema = z2.object({
5127
- contract: z2.string().refine((p) => p.includes("/"), {
5757
+ var RoleEntrySchema = z4.object({
5758
+ contract: z4.string().refine((p) => p.includes("/"), {
5128
5759
  message: 'must be a path (contain at least one "/"), not a bare filename'
5129
5760
  })
5130
5761
  });
5762
+ var MAX_REPORT_COMMAND_TIMEOUT_MS = 3600000;
5131
5763
  var BRIEF_BUILTINS = [
5132
5764
  "tier",
5133
5765
  "testPlan",
@@ -5147,17 +5779,17 @@ var BRIEF_BUILTINS = [
5147
5779
  "briefSections",
5148
5780
  "milestoneShape"
5149
5781
  ];
5150
- var BriefSectionSchema = z2.union([
5151
- z2.object({ builtin: z2.enum(BRIEF_BUILTINS) }),
5152
- z2.object({ heading: z2.string().min(1), name: z2.string().optional() }),
5153
- z2.object({ field: z2.string().min(1), name: z2.string().optional() }),
5154
- z2.object({ phrase: z2.string().min(1), name: z2.string().optional() })
5782
+ var BriefSectionSchema = z4.union([
5783
+ z4.object({ builtin: z4.enum(BRIEF_BUILTINS) }),
5784
+ z4.object({ heading: z4.string().min(1), name: z4.string().optional() }),
5785
+ z4.object({ field: z4.string().min(1), name: z4.string().optional() }),
5786
+ z4.object({ phrase: z4.string().min(1), name: z4.string().optional() })
5155
5787
  ]);
5156
- var BriefSchemaSchema = z2.object({
5157
- pr: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
5158
- issue: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
5159
- milestone: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
5160
- ack: z2.array(z2.enum(BRIEF_BUILTINS)).optional()
5788
+ var BriefSchemaSchema = z4.object({
5789
+ pr: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
5790
+ issue: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
5791
+ milestone: z4.object({ sections: z4.array(BriefSectionSchema) }).optional(),
5792
+ ack: z4.array(z4.enum(BRIEF_BUILTINS)).optional()
5161
5793
  });
5162
5794
  function isSafeRepoRelPath(p) {
5163
5795
  if (p.length === 0)
@@ -5166,7 +5798,7 @@ function isSafeRepoRelPath(p) {
5166
5798
  return false;
5167
5799
  return p.split(/[\\/]/).every((seg) => seg !== ".." && seg !== "");
5168
5800
  }
5169
- var SafeRepoRelPath = z2.string().refine(isSafeRepoRelPath, {
5801
+ var SafeRepoRelPath = z4.string().refine(isSafeRepoRelPath, {
5170
5802
  message: "must be a repo-root-relative path with no `..` segment or absolute root"
5171
5803
  });
5172
5804
  var CANONICAL_HOOK_BLOCK_PREFIXES = [".git/", ".husky/", ".vinaya/hooks/", ".claude/hooks/"];
@@ -5176,22 +5808,22 @@ function isCanonicalHookBlockPath(p) {
5176
5808
  var ManagedHookBlockPath = SafeRepoRelPath.refine(isCanonicalHookBlockPath, {
5177
5809
  message: `must start with one of ${CANONICAL_HOOK_BLOCK_PREFIXES.join(", ")} (byte-exact, case-sensitive)`
5178
5810
  });
5179
- var ManagedBlockRecordSchema = z2.object({
5811
+ var ManagedBlockRecordSchema = z4.object({
5180
5812
  path: ManagedHookBlockPath,
5181
- marker: z2.string(),
5182
- comment: z2.enum(["hash", "html"])
5813
+ marker: z4.string(),
5814
+ comment: z4.enum(["hash", "html"])
5183
5815
  });
5184
- var ManagedManifestSchema = z2.object({
5185
- version: z2.number().int().positive(),
5186
- files: z2.array(SafeRepoRelPath),
5187
- blocks: z2.array(ManagedBlockRecordSchema),
5188
- labels: z2.array(z2.string()),
5189
- agents: z2.array(z2.enum(AGENT_VENDORS)).optional()
5816
+ var ManagedManifestSchema = z4.object({
5817
+ version: z4.number().int().positive(),
5818
+ files: z4.array(SafeRepoRelPath),
5819
+ blocks: z4.array(ManagedBlockRecordSchema),
5820
+ labels: z4.array(z4.string()),
5821
+ agents: z4.array(z4.enum(AGENT_VENDORS)).optional()
5190
5822
  });
5191
- var ProjectEntrySchema = z2.object({
5192
- name: z2.string().min(1),
5193
- description: z2.string().min(1).optional(),
5194
- path: z2.string().min(1).optional()
5823
+ var ProjectEntrySchema = z4.object({
5824
+ name: z4.string().min(1),
5825
+ description: z4.string().min(1).optional(),
5826
+ path: z4.string().min(1).optional()
5195
5827
  });
5196
5828
  function parseTokensCollectDeclaration(value) {
5197
5829
  const m = value.trim().match(/^(\S+)\s+(\S+)$/);
@@ -5202,34 +5834,55 @@ function parseTokensCollectDeclaration(value) {
5202
5834
  return null;
5203
5835
  return { interpreter, script };
5204
5836
  }
5205
- var VinayaConfigSchema = z2.object({
5206
- rings: z2.object({
5207
- ring1_forgeWriteInterception: z2.boolean(),
5208
- ring2_asyncAudits: z2.boolean()
5837
+ var VinayaConfigSchema = z4.object({
5838
+ rings: z4.object({
5839
+ ring1_forgeWriteInterception: z4.boolean(),
5840
+ ring2_asyncAudits: z4.boolean()
5209
5841
  }).optional(),
5210
- checks: z2.record(z2.string(), CheckEntrySchema).optional(),
5211
- roles: z2.record(z2.string(), RoleEntrySchema).optional(),
5842
+ checks: z4.record(z4.string(), CheckEntrySchema).optional(),
5843
+ roles: z4.record(z4.string(), RoleEntrySchema).optional(),
5212
5844
  briefSchema: BriefSchemaSchema.optional(),
5213
5845
  managed: ManagedManifestSchema.optional(),
5214
- principals: z2.array(z2.string()).min(1).optional(),
5215
- releaseActor: z2.string().min(1).optional(),
5216
- ci: z2.object({ setup: z2.string().min(1) }).optional(),
5217
- tokens: z2.object({
5218
- collect: z2.string().min(1).refine((v) => parseTokensCollectDeclaration(v) !== null, {
5846
+ principals: z4.array(z4.string()).min(1).optional(),
5847
+ releaseActor: z4.string().min(1).optional(),
5848
+ ci: z4.object({ setup: z4.string().min(1) }).optional(),
5849
+ tokens: z4.object({
5850
+ collect: z4.string().min(1).refine((v) => parseTokensCollectDeclaration(v) !== null, {
5219
5851
  message: 'tokens.collect must be exactly "<interpreter> <repo-relative-script-path>" — two whitespace-separated tokens, no flags, no shell syntax, no quoting'
5220
5852
  })
5221
5853
  }).optional(),
5222
- blastRadius: z2.object({ extraDomains: z2.array(z2.string()).optional() }).optional(),
5223
- proseGates: z2.object({
5224
- doctrineRoot: z2.string().min(1).optional(),
5225
- readerFacingPrefix: z2.string().min(1).optional(),
5226
- readerFacingSuffix: z2.string().min(1).optional(),
5227
- legacySlugDir: z2.string().min(1).optional()
5854
+ blastRadius: z4.object({ extraDomains: z4.array(z4.string()).optional() }).optional(),
5855
+ proseGates: z4.object({
5856
+ doctrineRoot: z4.string().min(1).optional(),
5857
+ readerFacingPrefix: z4.string().min(1).optional(),
5858
+ readerFacingSuffix: z4.string().min(1).optional(),
5859
+ legacySlugDir: z4.string().min(1).optional()
5228
5860
  }).optional(),
5229
- projects: z2.array(ProjectEntrySchema).optional(),
5230
- dispatch: z2.object({
5231
- timeoutMs: z2.number().int().positive().optional(),
5232
- agent: z2.enum(["claude", "codex", "gemini"]).optional()
5861
+ projects: z4.array(ProjectEntrySchema).optional(),
5862
+ dispatch: z4.object({
5863
+ timeoutMs: z4.number().int().positive().optional(),
5864
+ killGraceMs: z4.number().int().positive().optional(),
5865
+ agent: z4.enum(["claude", "codex", "gemini"]).optional()
5866
+ }).optional(),
5867
+ reviewPolicy: z4.object({
5868
+ codeReviewThreshold: z4.string().min(1).optional(),
5869
+ securityThreshold: z4.string().min(1).optional(),
5870
+ maxRounds: z4.number().optional()
5871
+ }).optional(),
5872
+ prePush: z4.object({
5873
+ alwaysRun: z4.array(z4.string()).optional()
5874
+ }).optional(),
5875
+ report: z4.object({
5876
+ commandTimeoutMs: z4.number().int().positive().optional()
5877
+ }).superRefine((report, ctx) => {
5878
+ const commandTimeoutMs = report.commandTimeoutMs;
5879
+ if (commandTimeoutMs !== undefined && commandTimeoutMs > MAX_REPORT_COMMAND_TIMEOUT_MS) {
5880
+ ctx.addIssue({
5881
+ code: z4.ZodIssueCode.custom,
5882
+ path: ["commandTimeoutMs"],
5883
+ message: "must be at most 3600000 (1 hour)"
5884
+ });
5885
+ }
5233
5886
  }).optional()
5234
5887
  });
5235
5888
  var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
@@ -5304,6 +5957,28 @@ function resolvePrincipalAllowlist(config) {
5304
5957
  function resolveReleaseActor(config) {
5305
5958
  return config?.releaseActor ?? DEFAULT_RELEASE_ACTOR;
5306
5959
  }
5960
+ function resolveReviewPolicy(config) {
5961
+ const raw = config?.reviewPolicy;
5962
+ if (!raw)
5963
+ return DEFAULT_REVIEW_POLICY;
5964
+ const codeReviewThreshold = raw.codeReviewThreshold ?? DEFAULT_REVIEW_POLICY.codeReviewThreshold;
5965
+ if (!isKnownSeverity(CODE_REVIEW_SEVERITY_ORDER, codeReviewThreshold)) {
5966
+ throw new Error(`vinaya.config.json: reviewPolicy.codeReviewThreshold "${codeReviewThreshold}" is not one of ${CODE_REVIEW_SEVERITY_ORDER.join(" > ")} — fix the config, this never falls back to a default.`);
5967
+ }
5968
+ const securityThreshold = raw.securityThreshold ?? DEFAULT_REVIEW_POLICY.securityThreshold;
5969
+ if (!isKnownSeverity(SECURITY_SEVERITY_ORDER, securityThreshold)) {
5970
+ throw new Error(`vinaya.config.json: reviewPolicy.securityThreshold "${securityThreshold}" is not one of ${SECURITY_SEVERITY_ORDER.join(" > ")} — fix the config, this never falls back to a default.`);
5971
+ }
5972
+ const maxRounds = raw.maxRounds ?? DEFAULT_REVIEW_POLICY.maxRounds;
5973
+ if (!Number.isInteger(maxRounds) || maxRounds < 1) {
5974
+ throw new Error(`vinaya.config.json: reviewPolicy.maxRounds "${maxRounds}" is not a positive integer — fix the config, this never falls back to a default.`);
5975
+ }
5976
+ return {
5977
+ codeReviewThreshold,
5978
+ securityThreshold,
5979
+ maxRounds
5980
+ };
5981
+ }
5307
5982
  function trustAnchorRepo() {
5308
5983
  const wellFormed = (slug) => /^[^/\s]+\/[^/\s]+$/.test(slug) ? slug : null;
5309
5984
  const fromRunner = process.env.GITHUB_REPOSITORY?.trim();
@@ -5383,6 +6058,9 @@ function loadConfig() {
5383
6058
  }
5384
6059
  var TOKENS_COLLECT_TRUST_PATH = join(GLOBAL_VINAYA_HOME, "tokens-collect-trust.json");
5385
6060
 
6061
+ // src/lib/brief-assembly.ts
6062
+ import { execFileSync as execFileSync4 } from "node:child_process";
6063
+
5386
6064
  // ../../packages/sources/src/commands.ts
5387
6065
  var COMMANDS = [
5388
6066
  {
@@ -5541,6 +6219,17 @@ var COMMANDS = [
5541
6219
  ],
5542
6220
  status: "shipped"
5543
6221
  },
6222
+ {
6223
+ name: "task status",
6224
+ description: "Every open task with a frozen brief, its pull request, and whether its loop is running, paused, or published",
6225
+ flags: [{ flag: "--json", description: "Enveloped JSON output (schema: 1)" }],
6226
+ details: [
6227
+ "Read-only: one `gh issue list` for every open task Issue across every tranche (title/label resolved through the same `resolveTaskIssueRef` `list-tasks.ts` already uses), the open pull request per branch, and the driver pid record / pause record / publish effect markers under `<outboxRoot>/dev-review-loop/<task>/` — never a `ps` scan, never a re-parse of posted verdict comments to decide `published`.",
6228
+ "`running` names the driver's pid (`review-validity-v1` task 7's pid record); `paused` names the reason from the pause record; `published` means the newest round's reviewer and security verdict effect markers both read `posted`; `no driver` is the fallback when none of the above holds.",
6229
+ "`vinaya task status <tranche> <n>` narrows to one task and adds the last round's held or published verdict lines (`round-<n>-reviewer.md`/`round-<n>-security.md`, whichever their outbox carries) plus the exact `vinaya dev-review-loop --resume <pr>` command when paused."
6230
+ ],
6231
+ status: "shipped"
6232
+ },
5544
6233
  {
5545
6234
  name: "pr create",
5546
6235
  description: "Open a pull request after full brief-schema validation",
@@ -6050,10 +6739,10 @@ function createForgeSource(config) {
6050
6739
  };
6051
6740
  }
6052
6741
  // ../../packages/sources/src/select-source.ts
6053
- import { z as z3 } from "zod";
6054
- var StateSourceConfigSchema = z3.discriminatedUnion("kind", [
6055
- z3.object({ kind: z3.literal("forge"), owner: z3.string(), repo: z3.string() }),
6056
- z3.object({ kind: z3.literal("file"), root: z3.string().optional() })
6742
+ import { z as z5 } from "zod";
6743
+ var StateSourceConfigSchema = z5.discriminatedUnion("kind", [
6744
+ z5.object({ kind: z5.literal("forge"), owner: z5.string(), repo: z5.string() }),
6745
+ z5.object({ kind: z5.literal("file"), root: z5.string().optional() })
6057
6746
  ]);
6058
6747
  // src/checks/edge-resolve.ts
6059
6748
  import { execFileSync as execFileSync3 } from "node:child_process";
@@ -6173,6 +6862,589 @@ async function resolveEdge(id, taskById, factsByTaskId, repo, resolveSibling = d
6173
6862
  };
6174
6863
  }
6175
6864
 
6865
+ // src/lib/brief-assembly.ts
6866
+ function git(args) {
6867
+ try {
6868
+ return execFileSync4("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
6869
+ } catch {
6870
+ return "";
6871
+ }
6872
+ }
6873
+ function expandGlob(glob) {
6874
+ const out = git(["ls-files", "--", glob]);
6875
+ return out.split(`
6876
+ `).map((s) => s.trim()).filter(Boolean);
6877
+ }
6878
+ var DRAFT_ISSUE_SENTINEL = Number.MAX_SAFE_INTEGER;
6879
+
6880
+ // src/checks/registry.ts
6881
+ import { existsSync as existsSync3 } from "node:fs";
6882
+ import { join as join3 } from "node:path";
6883
+
6884
+ // src/lib/package-root.ts
6885
+ import { existsSync as existsSync2 } from "node:fs";
6886
+ import { dirname as dirname2, join as join2 } from "node:path";
6887
+ import { fileURLToPath } from "node:url";
6888
+ function packageRoot(moduleUrl) {
6889
+ let dir = dirname2(fileURLToPath(moduleUrl));
6890
+ while (!existsSync2(join2(dir, "package.json"))) {
6891
+ if (existsSync2(join2(dir, ".git")))
6892
+ break;
6893
+ const parent = dirname2(dir);
6894
+ if (parent === dir)
6895
+ break;
6896
+ dir = parent;
6897
+ }
6898
+ return dir;
6899
+ }
6900
+
6901
+ // src/checks/registry.ts
6902
+ var DIST_BIN_DIR = join3(packageRoot(import.meta.url), "dist", "checks", "bin");
6903
+ var SRC_BIN_DIR = join3(packageRoot(import.meta.url), "src", "checks", "bin");
6904
+ var BIN_DIR = existsSync3(DIST_BIN_DIR) ? DIST_BIN_DIR : SRC_BIN_DIR;
6905
+ var BIN_EXT = BIN_DIR === DIST_BIN_DIR ? ".js" : ".ts";
6906
+ function bin(name) {
6907
+ return join3(BIN_DIR, `${name}${BIN_EXT}`);
6908
+ }
6909
+ var REGISTRY = [
6910
+ [
6911
+ {
6912
+ name: "brief-shape",
6913
+ validates: "body",
6914
+ run: bin("check-brief-shape"),
6915
+ scope: "diff",
6916
+ timeoutMs: 15000,
6917
+ env: {
6918
+ PR_BODY: { optional: true },
6919
+ BRANCH: { optional: true },
6920
+ PR_NUMBER: { optional: true },
6921
+ GITHUB_TOKEN: { optional: true },
6922
+ GH_TOKEN: { optional: true }
6923
+ }
6924
+ },
6925
+ 0
6926
+ ],
6927
+ [
6928
+ {
6929
+ name: "pr-report-density",
6930
+ validates: "body",
6931
+ run: bin("check-pr-report-density"),
6932
+ scope: "diff",
6933
+ timeoutMs: 15000,
6934
+ env: {
6935
+ PR_BODY: { optional: true }
6936
+ }
6937
+ },
6938
+ 0
6939
+ ],
6940
+ [
6941
+ {
6942
+ name: "doc-coverage",
6943
+ validates: "body",
6944
+ run: bin("check-doc-coverage"),
6945
+ scope: "diff",
6946
+ timeoutMs: 15000,
6947
+ env: {
6948
+ BASE_SHA: { optional: true },
6949
+ PR_BODY: { optional: true },
6950
+ PR_BODY_FILE: { optional: true },
6951
+ PR_NUMBER: { optional: true },
6952
+ GITHUB_REPOSITORY: { optional: true },
6953
+ GITHUB_TOKEN: { optional: true },
6954
+ GH_TOKEN: { optional: true }
6955
+ }
6956
+ },
6957
+ 0
6958
+ ],
6959
+ [
6960
+ {
6961
+ name: "coherence",
6962
+ run: bin("check-coherence"),
6963
+ scope: "full",
6964
+ timeoutMs: 30000,
6965
+ env: {
6966
+ AEG_REPO: { optional: true },
6967
+ BRANCH: { optional: true },
6968
+ GITHUB_TOKEN: { optional: true },
6969
+ GH_TOKEN: { optional: true }
6970
+ }
6971
+ },
6972
+ 0
6973
+ ],
6974
+ [
6975
+ {
6976
+ name: "pr-premise-reassert",
6977
+ validates: "body",
6978
+ run: bin("check-pr-premise-reassert"),
6979
+ scope: "diff",
6980
+ timeoutMs: 15000,
6981
+ env: {
6982
+ PR_BODY: { optional: true }
6983
+ }
6984
+ },
6985
+ 0
6986
+ ],
6987
+ [
6988
+ {
6989
+ name: "dispatch-readiness",
6990
+ run: bin("check-dispatch-readiness"),
6991
+ scope: "full",
6992
+ timeoutMs: 30000,
6993
+ env: {
6994
+ AEG_REPO: { optional: true },
6995
+ BRANCH: { optional: true },
6996
+ GITHUB_TOKEN: { optional: true },
6997
+ GH_TOKEN: { optional: true },
6998
+ PREMISE_FILE: { optional: true }
6999
+ }
7000
+ },
7001
+ 0
7002
+ ],
7003
+ [
7004
+ {
7005
+ name: "closes-n",
7006
+ validates: "body",
7007
+ run: bin("check-closes-n"),
7008
+ scope: "diff",
7009
+ timeoutMs: 15000,
7010
+ requiresOpenPr: true,
7011
+ env: {
7012
+ BRANCH: { optional: true },
7013
+ PR_BODY: { optional: true },
7014
+ AEG_REPO: { optional: true },
7015
+ GITHUB_TOKEN: { optional: true },
7016
+ GH_TOKEN: { optional: true }
7017
+ }
7018
+ },
7019
+ 1
7020
+ ],
7021
+ [
7022
+ {
7023
+ name: "single-plan-pr",
7024
+ run: bin("check-single-plan-pr"),
7025
+ scope: "diff",
7026
+ timeoutMs: 15000,
7027
+ env: {
7028
+ BASE_SHA: { optional: true },
7029
+ PR_NUMBER: { optional: true },
7030
+ GITHUB_TOKEN: { optional: true },
7031
+ GH_TOKEN: { optional: true }
7032
+ }
7033
+ },
7034
+ 0
7035
+ ],
7036
+ [
7037
+ {
7038
+ name: "surface-scope",
7039
+ run: bin("check-surface-scope"),
7040
+ scope: "diff",
7041
+ timeoutMs: 15000,
7042
+ env: {
7043
+ AEG_REPO: { optional: true },
7044
+ BASE_SHA: { optional: true },
7045
+ BRANCH: { optional: true },
7046
+ GITHUB_TOKEN: { optional: true },
7047
+ GH_TOKEN: { optional: true }
7048
+ }
7049
+ },
7050
+ 0
7051
+ ],
7052
+ [
7053
+ {
7054
+ name: "test-plan",
7055
+ validates: "body",
7056
+ run: bin("check-test-plan"),
7057
+ scope: "diff",
7058
+ timeoutMs: 15000,
7059
+ requiresOpenPr: true,
7060
+ principalOwed: true,
7061
+ env: {
7062
+ PR_BODY: { optional: true },
7063
+ BRANCH: { optional: true }
7064
+ }
7065
+ },
7066
+ 1
7067
+ ],
7068
+ [
7069
+ {
7070
+ name: "body-bare-digits",
7071
+ validates: "body",
7072
+ run: bin("check-body-bare-digits"),
7073
+ scope: "diff",
7074
+ timeoutMs: 15000,
7075
+ requiresOpenPr: true,
7076
+ ownWorkflow: true,
7077
+ env: {
7078
+ PR_BODY: { optional: true },
7079
+ PR_NUMBER: { optional: true },
7080
+ GITHUB_REPOSITORY: { optional: true },
7081
+ GITHUB_TOKEN: { optional: true },
7082
+ GH_TOKEN: { optional: true }
7083
+ }
7084
+ },
7085
+ 1
7086
+ ],
7087
+ [
7088
+ {
7089
+ name: "token-report",
7090
+ validates: "body",
7091
+ run: bin("check-token-report"),
7092
+ scope: "diff",
7093
+ timeoutMs: 15000,
7094
+ requiresOpenPr: true,
7095
+ env: {
7096
+ PR_BODY: { optional: true },
7097
+ BRANCH: { optional: true },
7098
+ CLAUDE_PROJECT_DIR: { optional: true },
7099
+ CLAUDE_CODE_SESSION_ID: { optional: true }
7100
+ }
7101
+ },
7102
+ 1
7103
+ ],
7104
+ [
7105
+ {
7106
+ name: "no-disk-state",
7107
+ run: bin("check-no-disk-state"),
7108
+ scope: "diff",
7109
+ timeoutMs: 15000,
7110
+ env: { BASE_SHA: { optional: true } }
7111
+ },
7112
+ 0
7113
+ ],
7114
+ [
7115
+ {
7116
+ name: "registry-gates",
7117
+ run: bin("check-registry-gates"),
7118
+ scope: "full",
7119
+ timeoutMs: 30000,
7120
+ env: {
7121
+ AEG_REPO: { optional: true },
7122
+ GITHUB_TOKEN: { optional: true },
7123
+ GH_TOKEN: { optional: true }
7124
+ }
7125
+ },
7126
+ 0
7127
+ ],
7128
+ [
7129
+ {
7130
+ name: "review-gate",
7131
+ run: bin("check-review-gate"),
7132
+ scope: "full",
7133
+ timeoutMs: 30000,
7134
+ ownWorkflow: true,
7135
+ env: {
7136
+ PR_NUMBER: { optional: true },
7137
+ GITHUB_REPOSITORY: { optional: true },
7138
+ GITHUB_TOKEN: { optional: true },
7139
+ GH_TOKEN: { optional: true }
7140
+ }
7141
+ },
7142
+ 1
7143
+ ],
7144
+ [
7145
+ {
7146
+ name: "branch-topology",
7147
+ run: bin("check-branch-topology"),
7148
+ scope: "full",
7149
+ timeoutMs: 30000,
7150
+ env: {
7151
+ BRANCH: { optional: true },
7152
+ AEG_REPO: { optional: true },
7153
+ GITHUB_TOKEN: { optional: true },
7154
+ GH_TOKEN: { optional: true }
7155
+ }
7156
+ },
7157
+ 0
7158
+ ],
7159
+ [
7160
+ {
7161
+ name: "dead-branch-push",
7162
+ run: bin("check-dead-branch-push"),
7163
+ scope: "full",
7164
+ timeoutMs: 30000,
7165
+ env: {
7166
+ BRANCH: { optional: true },
7167
+ GITHUB_TOKEN: { optional: true },
7168
+ GH_TOKEN: { optional: true }
7169
+ }
7170
+ },
7171
+ 0
7172
+ ],
7173
+ [
7174
+ {
7175
+ name: "first-push-dispatch",
7176
+ run: bin("check-first-push-dispatch"),
7177
+ scope: "full",
7178
+ timeoutMs: 30000,
7179
+ env: {
7180
+ BRANCH: { optional: true },
7181
+ AEG_REPO: { optional: true },
7182
+ GITHUB_TOKEN: { optional: true },
7183
+ GH_TOKEN: { optional: true }
7184
+ }
7185
+ },
7186
+ 0
7187
+ ],
7188
+ [
7189
+ {
7190
+ name: "doc-coverage-push",
7191
+ validates: "body",
7192
+ run: bin("check-doc-coverage-push"),
7193
+ scope: "diff",
7194
+ timeoutMs: 15000,
7195
+ env: {
7196
+ OVERRIDE_DOCS: { optional: true },
7197
+ BASE_SHA: { optional: true },
7198
+ PR_BODY: { optional: true },
7199
+ PR_BODY_FILE: { optional: true },
7200
+ PR_LABELS: { optional: true },
7201
+ WAIVER_LABEL_ACTOR: { optional: true },
7202
+ GITHUB_REPOSITORY: { optional: true },
7203
+ GITHUB_TOKEN: { optional: true },
7204
+ GH_TOKEN: { optional: true }
7205
+ }
7206
+ },
7207
+ 0
7208
+ ],
7209
+ [
7210
+ {
7211
+ name: "issue-assignment",
7212
+ run: bin("check-issue-assignment"),
7213
+ scope: "full",
7214
+ timeoutMs: 30000,
7215
+ env: {
7216
+ AEG_REPO: { optional: true },
7217
+ BRANCH: { optional: true },
7218
+ GITHUB_TOKEN: { optional: true },
7219
+ GH_TOKEN: { optional: true }
7220
+ }
7221
+ },
7222
+ 0
7223
+ ],
7224
+ [
7225
+ {
7226
+ name: "evidence-fresh",
7227
+ validates: "body",
7228
+ run: bin("check-evidence-fresh"),
7229
+ scope: "diff",
7230
+ timeoutMs: 15000,
7231
+ requiresOpenPr: true,
7232
+ env: {
7233
+ PR_NUMBER: { optional: true },
7234
+ PR_BODY: { optional: true },
7235
+ BASE_SHA: { optional: true },
7236
+ GITHUB_TOKEN: { optional: true },
7237
+ GH_TOKEN: { optional: true }
7238
+ }
7239
+ },
7240
+ 1
7241
+ ],
7242
+ [
7243
+ {
7244
+ name: "reader-resolvable-prose",
7245
+ run: bin("check-reader-resolvable-prose"),
7246
+ scope: "full",
7247
+ timeoutMs: 30000,
7248
+ env: {},
7249
+ include: [
7250
+ "aeg-root/**/*.md",
7251
+ "apps/cli/src/**",
7252
+ ".github/workflows/**",
7253
+ ".vinaya/**",
7254
+ "apps/cli/README.md",
7255
+ "packages/sources/README.md"
7256
+ ]
7257
+ },
7258
+ 0
7259
+ ],
7260
+ [
7261
+ {
7262
+ name: "retired-vocabulary",
7263
+ run: bin("check-retired-vocabulary"),
7264
+ scope: "full",
7265
+ timeoutMs: 30000,
7266
+ env: {},
7267
+ include: ["aeg-root/**/*.md"]
7268
+ },
7269
+ 0
7270
+ ],
7271
+ [
7272
+ {
7273
+ name: "doctrine-portability",
7274
+ run: bin("check-doctrine-portability"),
7275
+ scope: "full",
7276
+ timeoutMs: 30000,
7277
+ env: { BASE_SHA: { optional: true } },
7278
+ include: ["aeg-root/**/*.md"]
7279
+ },
7280
+ 0
7281
+ ],
7282
+ [
7283
+ {
7284
+ name: "doctrine-no-procedures",
7285
+ run: bin("check-doctrine-no-procedures"),
7286
+ scope: "full",
7287
+ timeoutMs: 30000,
7288
+ env: {},
7289
+ include: ["aeg-root/**/*.md"]
7290
+ },
7291
+ 0
7292
+ ],
7293
+ [
7294
+ {
7295
+ name: "exec-bits",
7296
+ run: bin("check-exec-bits"),
7297
+ scope: "diff",
7298
+ timeoutMs: 30000,
7299
+ env: {}
7300
+ },
7301
+ 0
7302
+ ],
7303
+ [
7304
+ {
7305
+ name: "workspace-escape",
7306
+ run: bin("check-workspace-escape"),
7307
+ scope: "full",
7308
+ timeoutMs: 30000,
7309
+ env: {},
7310
+ include: ["aeg-root/**/*.md"]
7311
+ },
7312
+ 0
7313
+ ],
7314
+ [
7315
+ {
7316
+ name: "changeset-coverage",
7317
+ run: bin("check-changeset-coverage"),
7318
+ scope: "diff",
7319
+ timeoutMs: 15000,
7320
+ env: {}
7321
+ },
7322
+ 0
7323
+ ],
7324
+ [
7325
+ {
7326
+ name: "quoted-command",
7327
+ run: bin("check-quoted-command"),
7328
+ scope: "diff",
7329
+ timeoutMs: 15000,
7330
+ env: {}
7331
+ },
7332
+ 0
7333
+ ],
7334
+ [
7335
+ {
7336
+ name: "main-branch-refusal",
7337
+ run: bin("check-main-branch-refusal"),
7338
+ scope: "full",
7339
+ timeoutMs: 15000,
7340
+ env: { VINAYA_PUSH_REFS: { optional: true } }
7341
+ },
7342
+ 0
7343
+ ],
7344
+ [
7345
+ {
7346
+ name: "token-collection-wired",
7347
+ run: bin("check-token-collection-wired"),
7348
+ scope: "full",
7349
+ timeoutMs: 15000,
7350
+ env: {
7351
+ CLAUDE_PROJECT_DIR: { optional: true },
7352
+ CLAUDE_CODE_SESSION_ID: { optional: true }
7353
+ }
7354
+ },
7355
+ 0
7356
+ ],
7357
+ [
7358
+ {
7359
+ name: "issue-title-grammar",
7360
+ run: bin("check-issue-title-grammar"),
7361
+ validates: "issue",
7362
+ scope: "full",
7363
+ ownWorkflow: true,
7364
+ timeoutMs: 15000,
7365
+ env: { ISSUE_TITLE: { optional: true } }
7366
+ },
7367
+ 1
7368
+ ],
7369
+ [
7370
+ {
7371
+ name: "issue-objectives-numbering",
7372
+ run: bin("check-issue-objectives-numbering"),
7373
+ validates: "issue",
7374
+ scope: "full",
7375
+ ownWorkflow: true,
7376
+ timeoutMs: 15000,
7377
+ env: { ISSUE_BODY: { optional: true }, ISSUE_NUMBER: { optional: true } }
7378
+ },
7379
+ 1
7380
+ ],
7381
+ [
7382
+ {
7383
+ name: "issue-parts-coverage",
7384
+ run: bin("check-issue-parts-coverage"),
7385
+ validates: "issue",
7386
+ scope: "full",
7387
+ ownWorkflow: true,
7388
+ timeoutMs: 15000,
7389
+ env: { ISSUE_BODY: { optional: true } }
7390
+ },
7391
+ 1
7392
+ ],
7393
+ [
7394
+ {
7395
+ name: "issue-surface-globs",
7396
+ run: bin("check-issue-surface-globs"),
7397
+ validates: "issue",
7398
+ scope: "full",
7399
+ ownWorkflow: true,
7400
+ timeoutMs: 15000,
7401
+ env: { ISSUE_BODY: { optional: true } }
7402
+ },
7403
+ 1
7404
+ ],
7405
+ [
7406
+ {
7407
+ name: "issue-tranche-label",
7408
+ run: bin("check-issue-tranche-label"),
7409
+ validates: "issue",
7410
+ scope: "full",
7411
+ ownWorkflow: true,
7412
+ timeoutMs: 15000,
7413
+ env: { ISSUE_BODY: { optional: true }, ISSUE_LABELS: { optional: true } }
7414
+ },
7415
+ 1
7416
+ ],
7417
+ [
7418
+ {
7419
+ name: "issue-milestone-attach",
7420
+ run: bin("check-issue-milestone-attach"),
7421
+ validates: "issue",
7422
+ scope: "full",
7423
+ ownWorkflow: true,
7424
+ timeoutMs: 15000,
7425
+ env: {
7426
+ ISSUE_LABELS: { optional: true },
7427
+ CURRENT_MILESTONE_TITLE: { optional: true },
7428
+ RESOLVED_MILESTONE_TITLE: { optional: true }
7429
+ }
7430
+ },
7431
+ 1
7432
+ ]
7433
+ ];
7434
+ function coreCheckRegistry() {
7435
+ return REGISTRY.map(([spec]) => spec);
7436
+ }
7437
+ var CORE_CHECK_RING = Object.fromEntries(REGISTRY.map(([spec, ring]) => [spec.name, ring]));
7438
+
7439
+ // src/checks/runner.ts
7440
+ var activeKillers = new Set;
7441
+
7442
+ // src/lib/forge-write.ts
7443
+ var INFORMATIONAL_DISPATCH_BLOCKER_CLASSES = new Set([
7444
+ "depends-on-not-merged",
7445
+ "conflicts-with"
7446
+ ]);
7447
+
6176
7448
  // src/commands/review-post.ts
6177
7449
  function parseChangedLineRanges(diffOutput) {
6178
7450
  const result = {};
@@ -6204,7 +7476,7 @@ function parseChangedLineRanges(diffOutput) {
6204
7476
  function fileDiffAgainst(ref, path) {
6205
7477
  let out;
6206
7478
  try {
6207
- out = execFileSync4("git", ["diff", `${ref}...HEAD`, "--", path], {
7479
+ out = execFileSync5("git", ["diff", `${ref}...HEAD`, "--", path], {
6208
7480
  encoding: "utf8",
6209
7481
  stdio: ["ignore", "pipe", "pipe"]
6210
7482
  }).trim();
@@ -6215,7 +7487,7 @@ function fileDiffAgainst(ref, path) {
6215
7487
  }
6216
7488
  function revParse(ref) {
6217
7489
  try {
6218
- return execFileSync4("git", ["rev-parse", "--verify", ref], {
7490
+ return execFileSync5("git", ["rev-parse", "--verify", ref], {
6219
7491
  encoding: "utf8",
6220
7492
  stdio: ["ignore", "pipe", "pipe"]
6221
7493
  }).trim();
@@ -6225,7 +7497,7 @@ function revParse(ref) {
6225
7497
  }
6226
7498
  function repoRoot() {
6227
7499
  try {
6228
- return execFileSync4("git", ["rev-parse", "--show-toplevel"], {
7500
+ return execFileSync5("git", ["rev-parse", "--show-toplevel"], {
6229
7501
  encoding: "utf8",
6230
7502
  stdio: ["ignore", "pipe", "pipe"]
6231
7503
  }).trim();
@@ -6236,7 +7508,7 @@ function repoRoot() {
6236
7508
  function changedFiles(base) {
6237
7509
  let out;
6238
7510
  try {
6239
- out = execFileSync4("git", ["diff", "--name-only", `${base}...HEAD`], {
7511
+ out = execFileSync5("git", ["diff", "--name-only", `${base}...HEAD`], {
6240
7512
  encoding: "utf8",
6241
7513
  stdio: ["ignore", "pipe", "pipe"]
6242
7514
  }).trim();
@@ -6261,7 +7533,7 @@ function resolveDiff(base = process.env.BASE_SHA || "origin/main") {
6261
7533
  const relPaths = changedFiles(ref);
6262
7534
  if (relPaths === null)
6263
7535
  continue;
6264
- return { base: ref, root, files: relPaths.map((p) => join2(root, p)) };
7536
+ return { base: ref, root, files: relPaths.map((p) => join4(root, p)) };
6265
7537
  }
6266
7538
  return null;
6267
7539
  }
@@ -6295,15 +7567,15 @@ function findingsInThisDiff(findings) {
6295
7567
  // src/checks/bin/check-exec-bits.ts
6296
7568
  var CHECK_NAME = "exec-bits";
6297
7569
  var EXECUTABLE_MODE = "100755";
6298
- function git(args) {
7570
+ function git2(args) {
6299
7571
  try {
6300
- return execFileSync5("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
7572
+ return execFileSync6("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
6301
7573
  } catch {
6302
7574
  return null;
6303
7575
  }
6304
7576
  }
6305
7577
  function indexMode(path) {
6306
- const out = git(["ls-files", "-s", "--", path]);
7578
+ const out = git2(["ls-files", "-s", "--", path]);
6307
7579
  if (out === null || out === "")
6308
7580
  return null;
6309
7581
  const mode = out.split(/\s+/)[0] ?? "";