@attalabs/vinaya 0.18.0 → 0.19.1

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 (91) hide show
  1. package/README.md +37 -1
  2. package/aeg-root/contracts/architect-planner.md +72 -0
  3. package/aeg-root/enforcement.md +3 -2
  4. package/aeg-root/roles/architect.md +91 -0
  5. package/aeg-root/roles/planner.md +14 -15
  6. package/aeg-root/tranche-model.md +9 -1
  7. package/dist/checks/bin/check-body-bare-digits.js +262 -26
  8. package/dist/checks/bin/check-branch-topology.js +171 -23
  9. package/dist/checks/bin/check-brief-shape.js +171 -23
  10. package/dist/checks/bin/check-closes-n.js +171 -23
  11. package/dist/checks/bin/check-coherence.js +265 -29
  12. package/dist/checks/bin/check-dead-branch-push.js +171 -23
  13. package/dist/checks/bin/check-dispatch-readiness.js +329 -53
  14. package/dist/checks/bin/check-doc-coverage-push.js +266 -30
  15. package/dist/checks/bin/check-doc-coverage.js +266 -30
  16. package/dist/checks/bin/check-evidence-fresh.js +171 -23
  17. package/dist/checks/bin/check-first-push-dispatch.js +330 -54
  18. package/dist/checks/bin/check-issue-assignment.js +171 -23
  19. package/dist/checks/bin/check-no-disk-state.js +171 -23
  20. package/dist/checks/bin/check-reader-resolvable-prose.js +465 -46
  21. package/dist/checks/bin/check-registry-gates.js +171 -23
  22. package/dist/checks/bin/check-retired-vocabulary.js +3595 -0
  23. package/dist/checks/bin/check-review-gate.js +262 -26
  24. package/dist/checks/bin/check-single-plan-pr.js +171 -23
  25. package/dist/checks/bin/check-test-plan.js +171 -23
  26. package/dist/index.js +2959 -1581
  27. package/package.json +1 -1
  28. package/studio-standalone/apps/vinaya-studio/web/.next/BUILD_ID +1 -1
  29. package/studio-standalone/apps/vinaya-studio/web/.next/build-manifest.json +3 -3
  30. package/studio-standalone/apps/vinaya-studio/web/.next/prerender-manifest.json +3 -3
  31. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.html +1 -1
  32. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.rsc +1 -1
  33. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  34. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  35. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  36. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page/server-reference-manifest.json +2 -2
  37. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  38. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  39. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/api/coherence/route.js.nft.json +1 -1
  40. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page/server-reference-manifest.json +2 -2
  41. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js.nft.json +1 -1
  42. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page_client-reference-manifest.js +1 -1
  43. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page/server-reference-manifest.json +2 -2
  44. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js.nft.json +1 -1
  45. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page_client-reference-manifest.js +1 -1
  46. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page/server-reference-manifest.json +2 -2
  47. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js.nft.json +1 -1
  48. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page_client-reference-manifest.js +1 -1
  49. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page/server-reference-manifest.json +2 -2
  50. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js.nft.json +1 -1
  51. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page_client-reference-manifest.js +1 -1
  52. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page/server-reference-manifest.json +2 -2
  53. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js.nft.json +1 -1
  54. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page_client-reference-manifest.js +1 -1
  55. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page/server-reference-manifest.json +2 -2
  56. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js.nft.json +1 -1
  57. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page_client-reference-manifest.js +1 -1
  58. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page/server-reference-manifest.json +2 -2
  59. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js.nft.json +1 -1
  60. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page_client-reference-manifest.js +1 -1
  61. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page/server-reference-manifest.json +2 -2
  62. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js.nft.json +1 -1
  63. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page_client-reference-manifest.js +1 -1
  64. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/[root-of-the-server]__0e95dv5._.js +1 -1
  65. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/1q96_modules_@clerk_nextjs_dist_esm_app-router_client_keyless-creator-reader_0lom2js.js +1 -1
  66. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__029-1ch._.js +1 -1
  67. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0q56ohl._.js +1 -1
  68. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__0z47hfl._.js → [root-of-the-server]__0xdkvod._.js} +1 -1
  69. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1_ojzp1._.js +1 -1
  70. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1d_8nm4._.js +1 -1
  71. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1zkwtx8._.js +1 -1
  72. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__210ehg9._.js +1 -1
  73. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_00yjcv1._.js +1 -1
  74. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0rix02i._.js +1 -1
  75. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_18-d2be._.js +1 -1
  76. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1fqw88f._.js +5 -5
  77. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1gw6vc9._.js +1 -1
  78. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_1vo08dj._.js +2 -2
  79. package/studio-standalone/apps/vinaya-studio/web/.next/server/middleware-build-manifest.js +3 -3
  80. package/studio-standalone/apps/vinaya-studio/web/.next/server/pages/500.html +1 -1
  81. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.js +1 -1
  82. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.json +3 -3
  83. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{2myi4phnshkfw.js → 1zaotsjjq5o6o.js} +1 -1
  84. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{415k4k1rf3swg.js → 295mri9sjante.js} +1 -1
  85. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{3z3a3gb6he23i.js → 2opd70pfbc5yx.js} +1 -1
  86. package/studio-standalone/apps/vinaya-studio/web/package.json +2 -2
  87. package/templates/noop-check.template.ts +18 -0
  88. package/templates/role.template.md +20 -0
  89. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{8tXGbArNoU7_MriIVYvN0 → SG6gXRhit12wl-8HLX9d3}/_buildManifest.js +0 -0
  90. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{8tXGbArNoU7_MriIVYvN0 → SG6gXRhit12wl-8HLX9d3}/_clientMiddlewareManifest.js +0 -0
  91. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{8tXGbArNoU7_MriIVYvN0 → SG6gXRhit12wl-8HLX9d3}/_ssgManifest.js +0 -0
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/checks/bin/check-first-push-dispatch.ts
4
- import { execFileSync as execFileSync3 } from "node:child_process";
4
+ import { execFileSync as execFileSync4 } from "node:child_process";
5
5
  // ../../packages/aeg-forge-state/src/strip-code.ts
6
6
  function maskCode(body) {
7
7
  const fill = (line) => " ".repeat(line.length);
@@ -273,6 +273,9 @@ function issueListByLabelArgs(owner, repo, label) {
273
273
  "200"
274
274
  ];
275
275
  }
276
+ function ghIssueListByLabel(owner, repo, label) {
277
+ return JSON.parse(run(issueListByLabelArgs(owner, repo, label)));
278
+ }
276
279
  async function ghIssueListByLabelAsync(owner, repo, label) {
277
280
  return JSON.parse(await runAsync(issueListByLabelArgs(owner, repo, label)));
278
281
  }
@@ -288,18 +291,6 @@ async function ghIssueListByAnyLabelAsync(owner, repo, labels) {
288
291
  return dedupeByNumber(perLabel.flat());
289
292
  }
290
293
 
291
- // ../../packages/aeg-forge-state/src/fetch-milestone.ts
292
- function findMilestoneForSlug(owner, repo, slug) {
293
- const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
294
- const match = milestones.find((m) => m.title === slug);
295
- if (!match)
296
- return null;
297
- return {
298
- goal: match.description ?? "",
299
- lifecycle: match.state === "closed" ? "complete" : "active"
300
- };
301
- }
302
-
303
294
  // ../../packages/aeg-forge-state/src/labels.ts
304
295
  var LABELS = [
305
296
  {
@@ -542,6 +533,66 @@ function resolveTaskIssueRef(title, labels) {
542
533
  return { trancheSlug, taskId };
543
534
  }
544
535
 
536
+ // ../../packages/aeg-forge-state/src/fetch-milestone.ts
537
+ var INTENTS_HEADING = /^#{1,6}\s*Tranche intents\s*$/im;
538
+ var NEXT_HEADING = /^#{1,6}\s+\S/m;
539
+ var INTENT_BULLET = /^-\s+([a-z0-9][a-z0-9-]*)\s*:\s*(.+)$/i;
540
+ function intentGoalForSlug(description, slug) {
541
+ const text = stripCode(description, { inlineSpans: "keep" });
542
+ const start = text.match(INTENTS_HEADING);
543
+ if (!start || start.index === undefined)
544
+ return "";
545
+ const rest = text.slice(start.index + start[0].length);
546
+ const next = rest.match(NEXT_HEADING);
547
+ const section = rest.slice(0, next && next.index !== undefined ? next.index : rest.length);
548
+ for (const line of section.split(`
549
+ `)) {
550
+ const trimmed = line.trim();
551
+ if (trimmed.length === 0)
552
+ continue;
553
+ const m = trimmed.match(INTENT_BULLET);
554
+ if (m && (m[1] ?? "").toLowerCase() === slug.toLowerCase())
555
+ return (m[2] ?? "").trim();
556
+ }
557
+ return "";
558
+ }
559
+ function matchesLegacyMilestone(milestones, slug) {
560
+ return milestones.find((m) => m.title === slug) ?? null;
561
+ }
562
+ function factsFromLegacyMilestone(milestone) {
563
+ return {
564
+ goal: milestone.description ?? "",
565
+ lifecycle: milestone.state === "closed" ? "complete" : "active"
566
+ };
567
+ }
568
+ function resolveLegacyFacts(milestone, labelIssues) {
569
+ if (milestone.state === "closed" && labelIssues.length > 0) {
570
+ return { goal: milestone.description ?? "", lifecycle: lifecycleFromIssues(labelIssues) };
571
+ }
572
+ return factsFromLegacyMilestone(milestone);
573
+ }
574
+ function lifecycleFromIssues(issues) {
575
+ if (issues.length === 0)
576
+ return "planned";
577
+ return issues.some((i) => i.state === "OPEN") ? "active" : "complete";
578
+ }
579
+ function goalFromMilestones(milestones, slug) {
580
+ for (const m of milestones) {
581
+ const goal = intentGoalForSlug(m.description ?? "", slug);
582
+ if (goal)
583
+ return goal;
584
+ }
585
+ return "";
586
+ }
587
+ function findMilestoneForSlug(owner, repo, slug) {
588
+ const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
589
+ const legacy = matchesLegacyMilestone(milestones, slug);
590
+ const issues = ghIssueListByLabel(owner, repo, trancheLabel(slug));
591
+ if (legacy)
592
+ return resolveLegacyFacts(legacy, issues);
593
+ return { goal: goalFromMilestones(milestones, slug), lifecycle: lifecycleFromIssues(issues) };
594
+ }
595
+
545
596
  // ../../packages/aeg-forge-state/src/derive-from-forge.ts
546
597
  function trancheFromIssues(slug, issues, known) {
547
598
  return {
@@ -2725,16 +2776,18 @@ function checkDeadBranchPush(input) {
2725
2776
  }
2726
2777
  // ../../packages/aeg-core/src/reader-resolvable-prose.ts
2727
2778
  var SHIPS_PREFIX = "aeg-root/";
2728
- var SHIPS_ARCHIVE_PREFIX = "aeg-root/tranches/completed/";
2779
+ function shipsArchivePrefix(shipsPrefix) {
2780
+ return `${shipsPrefix}tranches/completed/`;
2781
+ }
2729
2782
  function isSpecFile2(path) {
2730
2783
  return path.startsWith("apps/") && path.includes("/specs/") && path.endsWith(".md");
2731
2784
  }
2732
2785
  function isClaudeMdFile(path) {
2733
2786
  return path === "CLAUDE.md" || path.endsWith("/CLAUDE.md");
2734
2787
  }
2735
- function classifyProseFile(path, readerFacingPrefix, readerFacingSuffix) {
2736
- if (path.startsWith(SHIPS_PREFIX)) {
2737
- return path.startsWith(SHIPS_ARCHIVE_PREFIX) ? "internal" : "ships";
2788
+ function classifyProseFile(path, readerFacingPrefix, readerFacingSuffix, shipsPrefix = SHIPS_PREFIX) {
2789
+ if (path.startsWith(shipsPrefix)) {
2790
+ return path.startsWith(shipsArchivePrefix(shipsPrefix)) ? "internal" : "ships";
2738
2791
  }
2739
2792
  if (path.startsWith(readerFacingPrefix) && path.endsWith(readerFacingSuffix)) {
2740
2793
  return "reader-facing";
@@ -2769,7 +2822,7 @@ function legacySlugPattern(legacySlugs) {
2769
2822
  const alternation = legacySlugs.map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
2770
2823
  return new RegExp(`(^|[^a-zA-Z0-9\\n-])(${alternation})([^a-zA-Z0-9\\n-]|$)`, "g");
2771
2824
  }
2772
- function checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuffix, legacySlugs = []) {
2825
+ function checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuffix, legacySlugs = [], shipsPrefix = SHIPS_PREFIX) {
2773
2826
  const findings = [];
2774
2827
  const legacyPattern = legacySlugPattern(legacySlugs);
2775
2828
  const patterns = [
@@ -2778,7 +2831,7 @@ function checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuff
2778
2831
  ...legacyPattern ? [{ pattern: legacyPattern, what: "an internal tranche slug", group: 2 }] : []
2779
2832
  ];
2780
2833
  for (const file of files) {
2781
- const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix);
2834
+ const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix, shipsPrefix);
2782
2835
  if (!cls || !SWEPT_CLASSES.has(cls))
2783
2836
  continue;
2784
2837
  const scrubbed = stripNonProse(file.path, file.content);
@@ -2824,10 +2877,10 @@ function definesOrLinksGlossary(content, term) {
2824
2877
  return true;
2825
2878
  return /docs\/glossary|glossary\.md/i.test(content);
2826
2879
  }
2827
- function checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix) {
2880
+ function checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix, shipsPrefix = SHIPS_PREFIX) {
2828
2881
  const findings = [];
2829
2882
  for (const file of files) {
2830
- const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix);
2883
+ const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix, shipsPrefix);
2831
2884
  if (!cls || !SWEPT_CLASSES.has(cls))
2832
2885
  continue;
2833
2886
  const scrubbed = stripNonProse(file.path, file.content);
@@ -2846,10 +2899,10 @@ function checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, read
2846
2899
  }
2847
2900
  return findings;
2848
2901
  }
2849
- function checkReaderResolvableProse(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix, legacySlugs = []) {
2902
+ function checkReaderResolvableProse(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix, legacySlugs = [], shipsPrefix = SHIPS_PREFIX) {
2850
2903
  return [
2851
- ...checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuffix, legacySlugs),
2852
- ...checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix)
2904
+ ...checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuffix, legacySlugs, shipsPrefix),
2905
+ ...checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix, shipsPrefix)
2853
2906
  ];
2854
2907
  }
2855
2908
  function parseGlossaryTerms(glossaryContent) {
@@ -2863,6 +2916,101 @@ function parseGlossaryTerms(glossaryContent) {
2863
2916
  }
2864
2917
  return terms;
2865
2918
  }
2919
+ // ../../packages/aeg-core/src/retired-vocabulary.ts
2920
+ var RETIRED_PATTERNS = [
2921
+ String.raw`\bD-[0-9]{3}\b`,
2922
+ "D-(###|NNN|nnn|xxx)",
2923
+ "decision (entry|logged)",
2924
+ "decision-log entry",
2925
+ "decision log entry",
2926
+ "decision log(s|ged|ging)?([^a-z]|$)",
2927
+ "decision-log",
2928
+ "decision entr",
2929
+ "decisions? (are |is )?logged",
2930
+ "log entries",
2931
+ String.raw`decisions\.md`,
2932
+ "decisions-legacy",
2933
+ "CONTRADICTION",
2934
+ "assumes Tier 3",
2935
+ "Lock: ?YES",
2936
+ "Lock: ?NO",
2937
+ "lock approvals?",
2938
+ "approves locks",
2939
+ "Conforms to lock",
2940
+ "Challenges lock",
2941
+ "checkDecisionNumbersFresh",
2942
+ String.raw`roles/team-leader\.md`,
2943
+ String.raw`\(,`,
2944
+ String.raw`,\)`,
2945
+ String.raw`\(\.\)`
2946
+ ];
2947
+ var RETIRED_EXEMPT_SUBSTRINGS = [
2948
+ "docs/decisions-legacy.md",
2949
+ "apps/herald-ai/docs/",
2950
+ "apps/vada-ai/docs/",
2951
+ "aeg-root/tranches/completed/",
2952
+ "packages/aeg-core/src/docs/published-prose",
2953
+ "packages/aeg-core/src/retired-vocabulary.test.ts",
2954
+ "packages/aeg-core/src/retired-vocabulary.ts",
2955
+ "/fixtures/",
2956
+ "/node_modules/",
2957
+ "/.next/",
2958
+ "/.turbo/"
2959
+ ];
2960
+ var PATTERN_EXEMPT = {
2961
+ [String.raw`\bD-[0-9]{3}\b`]: ["./CLAUDE.md"],
2962
+ [String.raw`decisions\.md`]: [
2963
+ "docs-index.md",
2964
+ "apps/vada-ai/specs/legacy/README.md",
2965
+ "apps/vada-ai/specs/vada-product-spec.md",
2966
+ "apps/vada-ai/web/CLAUDE.md",
2967
+ "apps/vada-ai/CLAUDE.md"
2968
+ ],
2969
+ "decisions-legacy": [
2970
+ "packages/aeg-core/src/file-classify.ts",
2971
+ "packages/aeg-core/src/file-classify.test.ts",
2972
+ "packages/aeg-core/src/pr-tier.test.ts",
2973
+ "docs-index.md",
2974
+ ".claude/skills/database/SKILL.md",
2975
+ ".claude/skills/vada-architecture/SKILL.md",
2976
+ "apps/vada-ai/specs/vada-byok-principles.md",
2977
+ "apps/vada-ai/web/CLAUDE.md",
2978
+ "apps/vada-ai/CLAUDE.md"
2979
+ ],
2980
+ CONTRADICTION: ["apps/vada-ai/specs/vada-reviewers-spec.md"]
2981
+ };
2982
+ function lineNumbers(content, pattern) {
2983
+ const out = [];
2984
+ const lines = content.split(`
2985
+ `);
2986
+ for (let i = 0;i < lines.length; i++) {
2987
+ if (pattern.test(lines[i] ?? ""))
2988
+ out.push(i + 1);
2989
+ }
2990
+ return out;
2991
+ }
2992
+ function scanRetiredVocabulary(files) {
2993
+ const findings = [];
2994
+ for (const file of files) {
2995
+ if (RETIRED_EXEMPT_SUBSTRINGS.some((e) => file.path.includes(e)))
2996
+ continue;
2997
+ for (const pattern of RETIRED_PATTERNS) {
2998
+ const patternExempt = PATTERN_EXEMPT[pattern] ?? [];
2999
+ if (patternExempt.some((e) => file.path.includes(e)))
3000
+ continue;
3001
+ const re = new RegExp(pattern);
3002
+ for (const line of lineNumbers(file.content, re)) {
3003
+ findings.push({
3004
+ file: file.path,
3005
+ line,
3006
+ pattern,
3007
+ message: `claims a retired AEG mechanism is still live (matches /${pattern}/)`
3008
+ });
3009
+ }
3010
+ }
3011
+ }
3012
+ return findings;
3013
+ }
2866
3014
  // ../../packages/aeg-core/src/dispatch-gate.ts
2867
3015
  function isHandClosedByRecognizedPrincipal(dep, principalAllowlist) {
2868
3016
  return dep.issueState === "closed" && dep.stateReason === "completed" && isPrincipal(dep.closedByActor ?? null, principalAllowlist);
@@ -3139,9 +3287,15 @@ function emitCheckError(error) {
3139
3287
 
3140
3288
  // src/lib/config.ts
3141
3289
  import { execFileSync as execFileSync2 } from "node:child_process";
3290
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3142
3291
  import { homedir } from "node:os";
3143
3292
  import { dirname, join } from "node:path";
3144
3293
  import { z as z2 } from "zod";
3294
+
3295
+ // src/lib/agent-vendors.ts
3296
+ var AGENT_VENDORS = ["skills", "claude", "gemini"];
3297
+
3298
+ // src/lib/config.ts
3145
3299
  var EnvEntrySchema = z2.union([
3146
3300
  z2.literal(true),
3147
3301
  z2.object({ optional: z2.literal(true) }),
@@ -3180,6 +3334,11 @@ var CheckEntrySchema = z2.object({
3180
3334
  }
3181
3335
  }
3182
3336
  });
3337
+ var RoleEntrySchema = z2.object({
3338
+ contract: z2.string().refine((p) => p.includes("/"), {
3339
+ message: 'must be a path (contain at least one "/"), not a bare filename'
3340
+ })
3341
+ });
3183
3342
  var BRIEF_BUILTINS = [
3184
3343
  "tier",
3185
3344
  "testPlan",
@@ -3194,7 +3353,8 @@ var BRIEF_BUILTINS = [
3194
3353
  "for",
3195
3354
  "closesN",
3196
3355
  "premiseCoverage",
3197
- "issueRationale"
3356
+ "issueRationale",
3357
+ "milestoneShape"
3198
3358
  ];
3199
3359
  var BriefSectionSchema = z2.union([
3200
3360
  z2.object({ builtin: z2.enum(BRIEF_BUILTINS) }),
@@ -3205,6 +3365,7 @@ var BriefSectionSchema = z2.union([
3205
3365
  var BriefSchemaSchema = z2.object({
3206
3366
  pr: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3207
3367
  issue: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3368
+ milestone: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3208
3369
  ack: z2.array(z2.enum(BRIEF_BUILTINS)).optional()
3209
3370
  });
3210
3371
  function isSafeRepoRelPath(p) {
@@ -3226,7 +3387,8 @@ var ManagedManifestSchema = z2.object({
3226
3387
  version: z2.number().int().positive(),
3227
3388
  files: z2.array(SafeRepoRelPath),
3228
3389
  blocks: z2.array(ManagedBlockRecordSchema),
3229
- labels: z2.array(z2.string())
3390
+ labels: z2.array(z2.string()),
3391
+ agents: z2.array(z2.enum(AGENT_VENDORS)).optional()
3230
3392
  });
3231
3393
  var VinayaConfigSchema = z2.object({
3232
3394
  rings: z2.object({
@@ -3234,16 +3396,79 @@ var VinayaConfigSchema = z2.object({
3234
3396
  ring2_asyncAudits: z2.boolean()
3235
3397
  }).optional(),
3236
3398
  checks: z2.record(z2.string(), CheckEntrySchema).optional(),
3399
+ roles: z2.record(z2.string(), RoleEntrySchema).optional(),
3237
3400
  briefSchema: BriefSchemaSchema.optional(),
3238
3401
  managed: ManagedManifestSchema.optional(),
3239
3402
  principals: z2.array(z2.string()).min(1).optional(),
3240
3403
  releaseActor: z2.string().min(1).optional(),
3241
3404
  ci: z2.object({ setup: z2.string().min(1) }).optional(),
3242
- blastRadius: z2.object({ extraDomains: z2.array(z2.string()).optional() }).optional()
3405
+ blastRadius: z2.object({ extraDomains: z2.array(z2.string()).optional() }).optional(),
3406
+ proseGates: z2.object({
3407
+ doctrineRoot: z2.string().min(1).optional(),
3408
+ readerFacingPrefix: z2.string().min(1).optional(),
3409
+ readerFacingSuffix: z2.string().min(1).optional(),
3410
+ legacySlugDir: z2.string().min(1).optional()
3411
+ }).optional()
3243
3412
  });
3244
3413
  var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
3245
3414
  var GLOBAL_CONFIG_PATH = join(GLOBAL_VINAYA_HOME, "config.json");
3246
3415
  var LOCAL_CONFIG_FILENAME = "vinaya.config.json";
3416
+ function findLocalConfig() {
3417
+ let dir = process.cwd();
3418
+ while (true) {
3419
+ const candidate = join(dir, LOCAL_CONFIG_FILENAME);
3420
+ if (existsSync(candidate))
3421
+ return candidate;
3422
+ if (existsSync(join(dir, ".git")))
3423
+ return null;
3424
+ const parent = dirname(dir);
3425
+ if (parent === dir)
3426
+ return null;
3427
+ dir = parent;
3428
+ }
3429
+ }
3430
+ function configPath() {
3431
+ const local = findLocalConfig();
3432
+ if (local)
3433
+ return local;
3434
+ if (existsSync(GLOBAL_CONFIG_PATH))
3435
+ return GLOBAL_CONFIG_PATH;
3436
+ return null;
3437
+ }
3438
+ function globalChecksIgnoredWarning(path) {
3439
+ return `${path}: "checks" registration in the global config is ignored — checks may only be registered from a repo-local vinaya.config.json.`;
3440
+ }
3441
+ function globalPrincipalsIgnoredWarning(path) {
3442
+ return `${path}: "principals" in the global config is ignored — principals may only be declared from a repo-local vinaya.config.json.`;
3443
+ }
3444
+ function globalRolesIgnoredWarning(path) {
3445
+ return `${path}: "roles" registration in the global config is ignored — roles may only be registered from a repo-local vinaya.config.json.`;
3446
+ }
3447
+ function globalReleaseActorIgnoredWarning(path) {
3448
+ return `${path}: "releaseActor" in the global config is ignored — releaseActor may only be declared from a repo-local vinaya.config.json.`;
3449
+ }
3450
+ function stripGlobalOnlyKeys(config, path) {
3451
+ if (path !== GLOBAL_CONFIG_PATH)
3452
+ return config;
3453
+ let result = config;
3454
+ if (result.checks && Object.keys(result.checks).length > 0) {
3455
+ console.error(`⚠ ${globalChecksIgnoredWarning(path)}`);
3456
+ result = { ...result, checks: undefined };
3457
+ }
3458
+ if (result.roles && Object.keys(result.roles).length > 0) {
3459
+ console.error(`⚠ ${globalRolesIgnoredWarning(path)}`);
3460
+ result = { ...result, roles: undefined };
3461
+ }
3462
+ if (result.principals && result.principals.length > 0) {
3463
+ console.error(`⚠ ${globalPrincipalsIgnoredWarning(path)}`);
3464
+ result = { ...result, principals: undefined };
3465
+ }
3466
+ if (result.releaseActor) {
3467
+ console.error(`⚠ ${globalReleaseActorIgnoredWarning(path)}`);
3468
+ result = { ...result, releaseActor: undefined };
3469
+ }
3470
+ return result;
3471
+ }
3247
3472
  function resolvePrincipalAllowlist(config) {
3248
3473
  return config?.principals ?? PRINCIPAL_ALLOWLIST;
3249
3474
  }
@@ -3316,12 +3541,86 @@ function loadTrustAnchorConfig(fetcher = ghFetchTrustAnchorConfig) {
3316
3541
  return null;
3317
3542
  }
3318
3543
  }
3544
+ function loadConfig() {
3545
+ const path = configPath();
3546
+ if (!path)
3547
+ return null;
3548
+ try {
3549
+ const raw = JSON.parse(readFileSync(path, "utf-8"));
3550
+ return stripGlobalOnlyKeys(VinayaConfigSchema.parse(raw), path);
3551
+ } catch {
3552
+ return null;
3553
+ }
3554
+ }
3555
+
3556
+ // src/checks/edge-resolve.ts
3557
+ import { execFileSync as execFileSync3 } from "node:child_process";
3558
+ var DIRECT_ISSUE_REF = /#(\d+)/;
3559
+ var issueCache = new Map;
3560
+ function fetchIssueState(num, repo) {
3561
+ const key = `${repo.owner}/${repo.repo}#${num}`;
3562
+ if (issueCache.has(key))
3563
+ return issueCache.get(key) ?? null;
3564
+ let out = null;
3565
+ try {
3566
+ const raw = execFileSync3("gh", [
3567
+ "issue",
3568
+ "view",
3569
+ String(num),
3570
+ "-R",
3571
+ `${repo.owner}/${repo.repo}`,
3572
+ "--json",
3573
+ "state,stateReason,closedByPullRequestsReferences"
3574
+ ], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
3575
+ out = JSON.parse(raw);
3576
+ } catch {
3577
+ out = null;
3578
+ }
3579
+ issueCache.set(key, out);
3580
+ return out;
3581
+ }
3582
+ function edgeFromIssueJson(num, json) {
3583
+ if (json === null) {
3584
+ return { issue: num, merged: false, open: false, issueState: null, stateReason: null, closedByActor: null };
3585
+ }
3586
+ const state = json.state === "CLOSED" ? "closed" : json.state === "OPEN" ? "open" : null;
3587
+ const reason = json.stateReason === "COMPLETED" ? "completed" : json.stateReason === "NOT_PLANNED" ? "not_planned" : null;
3588
+ const closedByMergedPr = (json.closedByPullRequestsReferences ?? []).length > 0;
3589
+ return {
3590
+ issue: num,
3591
+ merged: state === "closed" && closedByMergedPr,
3592
+ open: false,
3593
+ issueState: state,
3594
+ stateReason: reason,
3595
+ closedByActor: null
3596
+ };
3597
+ }
3598
+ function resolveEdge(id, taskById, factsByTaskId, repo) {
3599
+ const target = taskById.get(id);
3600
+ if (target) {
3601
+ const facts = target.issue !== null ? factsByTaskId.get(target.id) : undefined;
3602
+ return {
3603
+ issue: target.issue,
3604
+ merged: facts?.prState === "merged",
3605
+ open: facts?.prState === "open",
3606
+ issueState: facts?.issueState ?? null,
3607
+ stateReason: facts?.stateReason ?? null,
3608
+ closedByActor: facts?.closedByActor ?? null
3609
+ };
3610
+ }
3611
+ const direct = id.match(DIRECT_ISSUE_REF);
3612
+ if (direct) {
3613
+ const num = Number(direct[1]);
3614
+ return edgeFromIssueJson(num, fetchIssueState(num, repo));
3615
+ }
3616
+ return { issue: null, merged: false, open: false, issueState: null, stateReason: null, closedByActor: null };
3617
+ }
3319
3618
 
3320
3619
  // src/checks/bin/check-first-push-dispatch.ts
3321
3620
  var CHECK_NAME = "first-push-dispatch";
3322
3621
  function git(args) {
3323
3622
  try {
3324
- return execFileSync3("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
3623
+ return execFileSync4("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
3325
3624
  } catch {
3326
3625
  return "";
3327
3626
  }
@@ -3344,7 +3643,7 @@ function resolveRepo2() {
3344
3643
  }
3345
3644
  function prExistsFor(branch) {
3346
3645
  try {
3347
- const out = execFileSync3("gh", ["pr", "list", "--head", branch, "--state", "all", "--json", "number", "--limit", "1"], {
3646
+ const out = execFileSync4("gh", ["pr", "list", "--head", branch, "--state", "all", "--json", "number", "--limit", "1"], {
3348
3647
  encoding: "utf8",
3349
3648
  stdio: ["ignore", "pipe", "ignore"]
3350
3649
  });
@@ -3354,29 +3653,6 @@ function prExistsFor(branch) {
3354
3653
  return false;
3355
3654
  }
3356
3655
  }
3357
- function resolveEdge(id, taskById, factsByTaskId) {
3358
- const target = taskById.get(id);
3359
- if (target) {
3360
- const facts = target.issue !== null ? factsByTaskId.get(target.id) : undefined;
3361
- return {
3362
- issue: target.issue,
3363
- merged: facts?.prState === "merged",
3364
- open: facts?.prState === "open",
3365
- issueState: facts?.issueState ?? null,
3366
- stateReason: facts?.stateReason ?? null,
3367
- closedByActor: facts?.closedByActor ?? null
3368
- };
3369
- }
3370
- const direct = id.match(/^#(\d+)$/);
3371
- return {
3372
- issue: direct ? Number(direct[1]) : null,
3373
- merged: false,
3374
- open: false,
3375
- issueState: null,
3376
- stateReason: null,
3377
- closedByActor: null
3378
- };
3379
- }
3380
3656
  async function classifyReadiness(trancheSlug, taskId) {
3381
3657
  const repo = resolveRepo2();
3382
3658
  if (!repo)
@@ -3404,7 +3680,7 @@ async function classifyReadiness(trancheSlug, taskId) {
3404
3680
  const taskById = new Map(tranche.tasks.map((t) => [t.id, t]));
3405
3681
  const factsByTaskId = snapshot.facts;
3406
3682
  const dependsOn = task.dependsOn.map((dep) => {
3407
- const r = resolveEdge(dep, taskById, factsByTaskId);
3683
+ const r = resolveEdge(dep, taskById, factsByTaskId, repo);
3408
3684
  return {
3409
3685
  id: dep,
3410
3686
  issue: r.issue,
@@ -3415,7 +3691,7 @@ async function classifyReadiness(trancheSlug, taskId) {
3415
3691
  };
3416
3692
  });
3417
3693
  const conflictsWith = task.conflictsWith.map((c) => {
3418
- const r = resolveEdge(c, taskById, factsByTaskId);
3694
+ const r = resolveEdge(c, taskById, factsByTaskId, repo);
3419
3695
  return { id: c, issue: r.issue, openOrInFlight: r.open };
3420
3696
  });
3421
3697
  const input = {