@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
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/checks/bin/check-coherence.ts
4
4
  import { execFile as execFile4, execFileSync as execFileSync3 } from "node:child_process";
5
- import { existsSync, readFileSync } from "node:fs";
5
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
6
6
  import { join as join2 } from "node:path";
7
7
  import { promisify as promisify4 } from "node:util";
8
8
  // ../../packages/aeg-forge-state/src/strip-code.ts
@@ -276,6 +276,9 @@ function issueListByLabelArgs(owner, repo, label) {
276
276
  "200"
277
277
  ];
278
278
  }
279
+ function ghIssueListByLabel(owner, repo, label) {
280
+ return JSON.parse(run(issueListByLabelArgs(owner, repo, label)));
281
+ }
279
282
  async function ghIssueListByLabelAsync(owner, repo, label) {
280
283
  return JSON.parse(await runAsync(issueListByLabelArgs(owner, repo, label)));
281
284
  }
@@ -291,18 +294,6 @@ async function ghIssueListByAnyLabelAsync(owner, repo, labels) {
291
294
  return dedupeByNumber(perLabel.flat());
292
295
  }
293
296
 
294
- // ../../packages/aeg-forge-state/src/fetch-milestone.ts
295
- function findMilestoneForSlug(owner, repo, slug) {
296
- const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
297
- const match = milestones.find((m) => m.title === slug);
298
- if (!match)
299
- return null;
300
- return {
301
- goal: match.description ?? "",
302
- lifecycle: match.state === "closed" ? "complete" : "active"
303
- };
304
- }
305
-
306
297
  // ../../packages/aeg-forge-state/src/labels.ts
307
298
  var LABELS = [
308
299
  {
@@ -545,6 +536,66 @@ function resolveTaskIssueRef(title, labels) {
545
536
  return { trancheSlug, taskId };
546
537
  }
547
538
 
539
+ // ../../packages/aeg-forge-state/src/fetch-milestone.ts
540
+ var INTENTS_HEADING = /^#{1,6}\s*Tranche intents\s*$/im;
541
+ var NEXT_HEADING = /^#{1,6}\s+\S/m;
542
+ var INTENT_BULLET = /^-\s+([a-z0-9][a-z0-9-]*)\s*:\s*(.+)$/i;
543
+ function intentGoalForSlug(description, slug) {
544
+ const text = stripCode(description, { inlineSpans: "keep" });
545
+ const start = text.match(INTENTS_HEADING);
546
+ if (!start || start.index === undefined)
547
+ return "";
548
+ const rest = text.slice(start.index + start[0].length);
549
+ const next = rest.match(NEXT_HEADING);
550
+ const section = rest.slice(0, next && next.index !== undefined ? next.index : rest.length);
551
+ for (const line of section.split(`
552
+ `)) {
553
+ const trimmed = line.trim();
554
+ if (trimmed.length === 0)
555
+ continue;
556
+ const m = trimmed.match(INTENT_BULLET);
557
+ if (m && (m[1] ?? "").toLowerCase() === slug.toLowerCase())
558
+ return (m[2] ?? "").trim();
559
+ }
560
+ return "";
561
+ }
562
+ function matchesLegacyMilestone(milestones, slug) {
563
+ return milestones.find((m) => m.title === slug) ?? null;
564
+ }
565
+ function factsFromLegacyMilestone(milestone) {
566
+ return {
567
+ goal: milestone.description ?? "",
568
+ lifecycle: milestone.state === "closed" ? "complete" : "active"
569
+ };
570
+ }
571
+ function resolveLegacyFacts(milestone, labelIssues) {
572
+ if (milestone.state === "closed" && labelIssues.length > 0) {
573
+ return { goal: milestone.description ?? "", lifecycle: lifecycleFromIssues(labelIssues) };
574
+ }
575
+ return factsFromLegacyMilestone(milestone);
576
+ }
577
+ function lifecycleFromIssues(issues) {
578
+ if (issues.length === 0)
579
+ return "planned";
580
+ return issues.some((i) => i.state === "OPEN") ? "active" : "complete";
581
+ }
582
+ function goalFromMilestones(milestones, slug) {
583
+ for (const m of milestones) {
584
+ const goal = intentGoalForSlug(m.description ?? "", slug);
585
+ if (goal)
586
+ return goal;
587
+ }
588
+ return "";
589
+ }
590
+ function findMilestoneForSlug(owner, repo, slug) {
591
+ const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
592
+ const legacy = matchesLegacyMilestone(milestones, slug);
593
+ const issues = ghIssueListByLabel(owner, repo, trancheLabel(slug));
594
+ if (legacy)
595
+ return resolveLegacyFacts(legacy, issues);
596
+ return { goal: goalFromMilestones(milestones, slug), lifecycle: lifecycleFromIssues(issues) };
597
+ }
598
+
548
599
  // ../../packages/aeg-forge-state/src/derive-from-forge.ts
549
600
  function trancheFromIssues(slug, issues, known) {
550
601
  return {
@@ -2728,16 +2779,18 @@ function checkDeadBranchPush(input) {
2728
2779
  }
2729
2780
  // ../../packages/aeg-core/src/reader-resolvable-prose.ts
2730
2781
  var SHIPS_PREFIX = "aeg-root/";
2731
- var SHIPS_ARCHIVE_PREFIX = "aeg-root/tranches/completed/";
2782
+ function shipsArchivePrefix(shipsPrefix) {
2783
+ return `${shipsPrefix}tranches/completed/`;
2784
+ }
2732
2785
  function isSpecFile2(path) {
2733
2786
  return path.startsWith("apps/") && path.includes("/specs/") && path.endsWith(".md");
2734
2787
  }
2735
2788
  function isClaudeMdFile(path) {
2736
2789
  return path === "CLAUDE.md" || path.endsWith("/CLAUDE.md");
2737
2790
  }
2738
- function classifyProseFile(path, readerFacingPrefix, readerFacingSuffix) {
2739
- if (path.startsWith(SHIPS_PREFIX)) {
2740
- return path.startsWith(SHIPS_ARCHIVE_PREFIX) ? "internal" : "ships";
2791
+ function classifyProseFile(path, readerFacingPrefix, readerFacingSuffix, shipsPrefix = SHIPS_PREFIX) {
2792
+ if (path.startsWith(shipsPrefix)) {
2793
+ return path.startsWith(shipsArchivePrefix(shipsPrefix)) ? "internal" : "ships";
2741
2794
  }
2742
2795
  if (path.startsWith(readerFacingPrefix) && path.endsWith(readerFacingSuffix)) {
2743
2796
  return "reader-facing";
@@ -2772,7 +2825,7 @@ function legacySlugPattern(legacySlugs) {
2772
2825
  const alternation = legacySlugs.map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
2773
2826
  return new RegExp(`(^|[^a-zA-Z0-9\\n-])(${alternation})([^a-zA-Z0-9\\n-]|$)`, "g");
2774
2827
  }
2775
- function checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuffix, legacySlugs = []) {
2828
+ function checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuffix, legacySlugs = [], shipsPrefix = SHIPS_PREFIX) {
2776
2829
  const findings = [];
2777
2830
  const legacyPattern = legacySlugPattern(legacySlugs);
2778
2831
  const patterns = [
@@ -2781,7 +2834,7 @@ function checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuff
2781
2834
  ...legacyPattern ? [{ pattern: legacyPattern, what: "an internal tranche slug", group: 2 }] : []
2782
2835
  ];
2783
2836
  for (const file of files) {
2784
- const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix);
2837
+ const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix, shipsPrefix);
2785
2838
  if (!cls || !SWEPT_CLASSES.has(cls))
2786
2839
  continue;
2787
2840
  const scrubbed = stripNonProse(file.path, file.content);
@@ -2827,10 +2880,10 @@ function definesOrLinksGlossary(content, term) {
2827
2880
  return true;
2828
2881
  return /docs\/glossary|glossary\.md/i.test(content);
2829
2882
  }
2830
- function checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix) {
2883
+ function checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix, shipsPrefix = SHIPS_PREFIX) {
2831
2884
  const findings = [];
2832
2885
  for (const file of files) {
2833
- const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix);
2886
+ const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix, shipsPrefix);
2834
2887
  if (!cls || !SWEPT_CLASSES.has(cls))
2835
2888
  continue;
2836
2889
  const scrubbed = stripNonProse(file.path, file.content);
@@ -2849,10 +2902,10 @@ function checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, read
2849
2902
  }
2850
2903
  return findings;
2851
2904
  }
2852
- function checkReaderResolvableProse(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix, legacySlugs = []) {
2905
+ function checkReaderResolvableProse(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix, legacySlugs = [], shipsPrefix = SHIPS_PREFIX) {
2853
2906
  return [
2854
- ...checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuffix, legacySlugs),
2855
- ...checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix)
2907
+ ...checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuffix, legacySlugs, shipsPrefix),
2908
+ ...checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix, shipsPrefix)
2856
2909
  ];
2857
2910
  }
2858
2911
  function parseGlossaryTerms(glossaryContent) {
@@ -2866,6 +2919,101 @@ function parseGlossaryTerms(glossaryContent) {
2866
2919
  }
2867
2920
  return terms;
2868
2921
  }
2922
+ // ../../packages/aeg-core/src/retired-vocabulary.ts
2923
+ var RETIRED_PATTERNS = [
2924
+ String.raw`\bD-[0-9]{3}\b`,
2925
+ "D-(###|NNN|nnn|xxx)",
2926
+ "decision (entry|logged)",
2927
+ "decision-log entry",
2928
+ "decision log entry",
2929
+ "decision log(s|ged|ging)?([^a-z]|$)",
2930
+ "decision-log",
2931
+ "decision entr",
2932
+ "decisions? (are |is )?logged",
2933
+ "log entries",
2934
+ String.raw`decisions\.md`,
2935
+ "decisions-legacy",
2936
+ "CONTRADICTION",
2937
+ "assumes Tier 3",
2938
+ "Lock: ?YES",
2939
+ "Lock: ?NO",
2940
+ "lock approvals?",
2941
+ "approves locks",
2942
+ "Conforms to lock",
2943
+ "Challenges lock",
2944
+ "checkDecisionNumbersFresh",
2945
+ String.raw`roles/team-leader\.md`,
2946
+ String.raw`\(,`,
2947
+ String.raw`,\)`,
2948
+ String.raw`\(\.\)`
2949
+ ];
2950
+ var RETIRED_EXEMPT_SUBSTRINGS = [
2951
+ "docs/decisions-legacy.md",
2952
+ "apps/herald-ai/docs/",
2953
+ "apps/vada-ai/docs/",
2954
+ "aeg-root/tranches/completed/",
2955
+ "packages/aeg-core/src/docs/published-prose",
2956
+ "packages/aeg-core/src/retired-vocabulary.test.ts",
2957
+ "packages/aeg-core/src/retired-vocabulary.ts",
2958
+ "/fixtures/",
2959
+ "/node_modules/",
2960
+ "/.next/",
2961
+ "/.turbo/"
2962
+ ];
2963
+ var PATTERN_EXEMPT = {
2964
+ [String.raw`\bD-[0-9]{3}\b`]: ["./CLAUDE.md"],
2965
+ [String.raw`decisions\.md`]: [
2966
+ "docs-index.md",
2967
+ "apps/vada-ai/specs/legacy/README.md",
2968
+ "apps/vada-ai/specs/vada-product-spec.md",
2969
+ "apps/vada-ai/web/CLAUDE.md",
2970
+ "apps/vada-ai/CLAUDE.md"
2971
+ ],
2972
+ "decisions-legacy": [
2973
+ "packages/aeg-core/src/file-classify.ts",
2974
+ "packages/aeg-core/src/file-classify.test.ts",
2975
+ "packages/aeg-core/src/pr-tier.test.ts",
2976
+ "docs-index.md",
2977
+ ".claude/skills/database/SKILL.md",
2978
+ ".claude/skills/vada-architecture/SKILL.md",
2979
+ "apps/vada-ai/specs/vada-byok-principles.md",
2980
+ "apps/vada-ai/web/CLAUDE.md",
2981
+ "apps/vada-ai/CLAUDE.md"
2982
+ ],
2983
+ CONTRADICTION: ["apps/vada-ai/specs/vada-reviewers-spec.md"]
2984
+ };
2985
+ function lineNumbers(content, pattern) {
2986
+ const out = [];
2987
+ const lines = content.split(`
2988
+ `);
2989
+ for (let i = 0;i < lines.length; i++) {
2990
+ if (pattern.test(lines[i] ?? ""))
2991
+ out.push(i + 1);
2992
+ }
2993
+ return out;
2994
+ }
2995
+ function scanRetiredVocabulary(files) {
2996
+ const findings = [];
2997
+ for (const file of files) {
2998
+ if (RETIRED_EXEMPT_SUBSTRINGS.some((e) => file.path.includes(e)))
2999
+ continue;
3000
+ for (const pattern of RETIRED_PATTERNS) {
3001
+ const patternExempt = PATTERN_EXEMPT[pattern] ?? [];
3002
+ if (patternExempt.some((e) => file.path.includes(e)))
3003
+ continue;
3004
+ const re = new RegExp(pattern);
3005
+ for (const line of lineNumbers(file.content, re)) {
3006
+ findings.push({
3007
+ file: file.path,
3008
+ line,
3009
+ pattern,
3010
+ message: `claims a retired AEG mechanism is still live (matches /${pattern}/)`
3011
+ });
3012
+ }
3013
+ }
3014
+ }
3015
+ return findings;
3016
+ }
2869
3017
  // ../../packages/aeg-core/src/dispatch-gate.ts
2870
3018
  function isHandClosedByRecognizedPrincipal(dep, principalAllowlist) {
2871
3019
  return dep.issueState === "closed" && dep.stateReason === "completed" && isPrincipal(dep.closedByActor ?? null, principalAllowlist);
@@ -3142,9 +3290,15 @@ function emitCheckError(error) {
3142
3290
 
3143
3291
  // src/lib/config.ts
3144
3292
  import { execFileSync as execFileSync2 } from "node:child_process";
3293
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3145
3294
  import { homedir } from "node:os";
3146
3295
  import { dirname, join } from "node:path";
3147
3296
  import { z as z2 } from "zod";
3297
+
3298
+ // src/lib/agent-vendors.ts
3299
+ var AGENT_VENDORS = ["skills", "claude", "gemini"];
3300
+
3301
+ // src/lib/config.ts
3148
3302
  var EnvEntrySchema = z2.union([
3149
3303
  z2.literal(true),
3150
3304
  z2.object({ optional: z2.literal(true) }),
@@ -3183,6 +3337,11 @@ var CheckEntrySchema = z2.object({
3183
3337
  }
3184
3338
  }
3185
3339
  });
3340
+ var RoleEntrySchema = z2.object({
3341
+ contract: z2.string().refine((p) => p.includes("/"), {
3342
+ message: 'must be a path (contain at least one "/"), not a bare filename'
3343
+ })
3344
+ });
3186
3345
  var BRIEF_BUILTINS = [
3187
3346
  "tier",
3188
3347
  "testPlan",
@@ -3197,7 +3356,8 @@ var BRIEF_BUILTINS = [
3197
3356
  "for",
3198
3357
  "closesN",
3199
3358
  "premiseCoverage",
3200
- "issueRationale"
3359
+ "issueRationale",
3360
+ "milestoneShape"
3201
3361
  ];
3202
3362
  var BriefSectionSchema = z2.union([
3203
3363
  z2.object({ builtin: z2.enum(BRIEF_BUILTINS) }),
@@ -3208,6 +3368,7 @@ var BriefSectionSchema = z2.union([
3208
3368
  var BriefSchemaSchema = z2.object({
3209
3369
  pr: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3210
3370
  issue: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3371
+ milestone: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3211
3372
  ack: z2.array(z2.enum(BRIEF_BUILTINS)).optional()
3212
3373
  });
3213
3374
  function isSafeRepoRelPath(p) {
@@ -3229,7 +3390,8 @@ var ManagedManifestSchema = z2.object({
3229
3390
  version: z2.number().int().positive(),
3230
3391
  files: z2.array(SafeRepoRelPath),
3231
3392
  blocks: z2.array(ManagedBlockRecordSchema),
3232
- labels: z2.array(z2.string())
3393
+ labels: z2.array(z2.string()),
3394
+ agents: z2.array(z2.enum(AGENT_VENDORS)).optional()
3233
3395
  });
3234
3396
  var VinayaConfigSchema = z2.object({
3235
3397
  rings: z2.object({
@@ -3237,16 +3399,79 @@ var VinayaConfigSchema = z2.object({
3237
3399
  ring2_asyncAudits: z2.boolean()
3238
3400
  }).optional(),
3239
3401
  checks: z2.record(z2.string(), CheckEntrySchema).optional(),
3402
+ roles: z2.record(z2.string(), RoleEntrySchema).optional(),
3240
3403
  briefSchema: BriefSchemaSchema.optional(),
3241
3404
  managed: ManagedManifestSchema.optional(),
3242
3405
  principals: z2.array(z2.string()).min(1).optional(),
3243
3406
  releaseActor: z2.string().min(1).optional(),
3244
3407
  ci: z2.object({ setup: z2.string().min(1) }).optional(),
3245
- blastRadius: z2.object({ extraDomains: z2.array(z2.string()).optional() }).optional()
3408
+ blastRadius: z2.object({ extraDomains: z2.array(z2.string()).optional() }).optional(),
3409
+ proseGates: z2.object({
3410
+ doctrineRoot: z2.string().min(1).optional(),
3411
+ readerFacingPrefix: z2.string().min(1).optional(),
3412
+ readerFacingSuffix: z2.string().min(1).optional(),
3413
+ legacySlugDir: z2.string().min(1).optional()
3414
+ }).optional()
3246
3415
  });
3247
3416
  var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
3248
3417
  var GLOBAL_CONFIG_PATH = join(GLOBAL_VINAYA_HOME, "config.json");
3249
3418
  var LOCAL_CONFIG_FILENAME = "vinaya.config.json";
3419
+ function findLocalConfig() {
3420
+ let dir = process.cwd();
3421
+ while (true) {
3422
+ const candidate = join(dir, LOCAL_CONFIG_FILENAME);
3423
+ if (existsSync(candidate))
3424
+ return candidate;
3425
+ if (existsSync(join(dir, ".git")))
3426
+ return null;
3427
+ const parent = dirname(dir);
3428
+ if (parent === dir)
3429
+ return null;
3430
+ dir = parent;
3431
+ }
3432
+ }
3433
+ function configPath() {
3434
+ const local = findLocalConfig();
3435
+ if (local)
3436
+ return local;
3437
+ if (existsSync(GLOBAL_CONFIG_PATH))
3438
+ return GLOBAL_CONFIG_PATH;
3439
+ return null;
3440
+ }
3441
+ function globalChecksIgnoredWarning(path) {
3442
+ return `${path}: "checks" registration in the global config is ignored — checks may only be registered from a repo-local vinaya.config.json.`;
3443
+ }
3444
+ function globalPrincipalsIgnoredWarning(path) {
3445
+ return `${path}: "principals" in the global config is ignored — principals may only be declared from a repo-local vinaya.config.json.`;
3446
+ }
3447
+ function globalRolesIgnoredWarning(path) {
3448
+ return `${path}: "roles" registration in the global config is ignored — roles may only be registered from a repo-local vinaya.config.json.`;
3449
+ }
3450
+ function globalReleaseActorIgnoredWarning(path) {
3451
+ return `${path}: "releaseActor" in the global config is ignored — releaseActor may only be declared from a repo-local vinaya.config.json.`;
3452
+ }
3453
+ function stripGlobalOnlyKeys(config, path) {
3454
+ if (path !== GLOBAL_CONFIG_PATH)
3455
+ return config;
3456
+ let result = config;
3457
+ if (result.checks && Object.keys(result.checks).length > 0) {
3458
+ console.error(`⚠ ${globalChecksIgnoredWarning(path)}`);
3459
+ result = { ...result, checks: undefined };
3460
+ }
3461
+ if (result.roles && Object.keys(result.roles).length > 0) {
3462
+ console.error(`⚠ ${globalRolesIgnoredWarning(path)}`);
3463
+ result = { ...result, roles: undefined };
3464
+ }
3465
+ if (result.principals && result.principals.length > 0) {
3466
+ console.error(`⚠ ${globalPrincipalsIgnoredWarning(path)}`);
3467
+ result = { ...result, principals: undefined };
3468
+ }
3469
+ if (result.releaseActor) {
3470
+ console.error(`⚠ ${globalReleaseActorIgnoredWarning(path)}`);
3471
+ result = { ...result, releaseActor: undefined };
3472
+ }
3473
+ return result;
3474
+ }
3250
3475
  function resolvePrincipalAllowlist(config) {
3251
3476
  return config?.principals ?? PRINCIPAL_ALLOWLIST;
3252
3477
  }
@@ -3319,6 +3544,17 @@ function loadTrustAnchorConfig(fetcher = ghFetchTrustAnchorConfig) {
3319
3544
  return null;
3320
3545
  }
3321
3546
  }
3547
+ function loadConfig() {
3548
+ const path = configPath();
3549
+ if (!path)
3550
+ return null;
3551
+ try {
3552
+ const raw = JSON.parse(readFileSync(path, "utf-8"));
3553
+ return stripGlobalOnlyKeys(VinayaConfigSchema.parse(raw), path);
3554
+ } catch {
3555
+ return null;
3556
+ }
3557
+ }
3322
3558
 
3323
3559
  // src/checks/bin/check-coherence.ts
3324
3560
  var CHECK_NAME = "coherence";
@@ -3335,9 +3571,9 @@ function readRegisteredProjectNames() {
3335
3571
  if (!root)
3336
3572
  return [];
3337
3573
  const path = join2(root, ".vinaya/projects.md");
3338
- if (!existsSync(path))
3574
+ if (!existsSync2(path))
3339
3575
  return [];
3340
- return parseRegistry(readFileSync(path, "utf8")).map((p) => p.name);
3576
+ return parseRegistry(readFileSync2(path, "utf8")).map((p) => p.name);
3341
3577
  }
3342
3578
  function resolveRepo2() {
3343
3579
  const fromEnv = process.env.AEG_REPO;