@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-dispatch-readiness.ts
4
- import { execFile as execFile4, execFileSync as execFileSync3 } from "node:child_process";
4
+ import { execFile as execFile4, execFileSync as execFileSync4 } from "node:child_process";
5
5
  import { promisify as promisify4 } from "node:util";
6
6
  // ../../packages/aeg-forge-state/src/strip-code.ts
7
7
  function maskCode(body) {
@@ -274,6 +274,9 @@ function issueListByLabelArgs(owner, repo, label) {
274
274
  "200"
275
275
  ];
276
276
  }
277
+ function ghIssueListByLabel(owner, repo, label) {
278
+ return JSON.parse(run(issueListByLabelArgs(owner, repo, label)));
279
+ }
277
280
  async function ghIssueListByLabelAsync(owner, repo, label) {
278
281
  return JSON.parse(await runAsync(issueListByLabelArgs(owner, repo, label)));
279
282
  }
@@ -289,18 +292,6 @@ async function ghIssueListByAnyLabelAsync(owner, repo, labels) {
289
292
  return dedupeByNumber(perLabel.flat());
290
293
  }
291
294
 
292
- // ../../packages/aeg-forge-state/src/fetch-milestone.ts
293
- function findMilestoneForSlug(owner, repo, slug) {
294
- const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
295
- const match = milestones.find((m) => m.title === slug);
296
- if (!match)
297
- return null;
298
- return {
299
- goal: match.description ?? "",
300
- lifecycle: match.state === "closed" ? "complete" : "active"
301
- };
302
- }
303
-
304
295
  // ../../packages/aeg-forge-state/src/labels.ts
305
296
  var LABELS = [
306
297
  {
@@ -543,6 +534,66 @@ function resolveTaskIssueRef(title, labels) {
543
534
  return { trancheSlug, taskId };
544
535
  }
545
536
 
537
+ // ../../packages/aeg-forge-state/src/fetch-milestone.ts
538
+ var INTENTS_HEADING = /^#{1,6}\s*Tranche intents\s*$/im;
539
+ var NEXT_HEADING = /^#{1,6}\s+\S/m;
540
+ var INTENT_BULLET = /^-\s+([a-z0-9][a-z0-9-]*)\s*:\s*(.+)$/i;
541
+ function intentGoalForSlug(description, slug) {
542
+ const text = stripCode(description, { inlineSpans: "keep" });
543
+ const start = text.match(INTENTS_HEADING);
544
+ if (!start || start.index === undefined)
545
+ return "";
546
+ const rest = text.slice(start.index + start[0].length);
547
+ const next = rest.match(NEXT_HEADING);
548
+ const section = rest.slice(0, next && next.index !== undefined ? next.index : rest.length);
549
+ for (const line of section.split(`
550
+ `)) {
551
+ const trimmed = line.trim();
552
+ if (trimmed.length === 0)
553
+ continue;
554
+ const m = trimmed.match(INTENT_BULLET);
555
+ if (m && (m[1] ?? "").toLowerCase() === slug.toLowerCase())
556
+ return (m[2] ?? "").trim();
557
+ }
558
+ return "";
559
+ }
560
+ function matchesLegacyMilestone(milestones, slug) {
561
+ return milestones.find((m) => m.title === slug) ?? null;
562
+ }
563
+ function factsFromLegacyMilestone(milestone) {
564
+ return {
565
+ goal: milestone.description ?? "",
566
+ lifecycle: milestone.state === "closed" ? "complete" : "active"
567
+ };
568
+ }
569
+ function resolveLegacyFacts(milestone, labelIssues) {
570
+ if (milestone.state === "closed" && labelIssues.length > 0) {
571
+ return { goal: milestone.description ?? "", lifecycle: lifecycleFromIssues(labelIssues) };
572
+ }
573
+ return factsFromLegacyMilestone(milestone);
574
+ }
575
+ function lifecycleFromIssues(issues) {
576
+ if (issues.length === 0)
577
+ return "planned";
578
+ return issues.some((i) => i.state === "OPEN") ? "active" : "complete";
579
+ }
580
+ function goalFromMilestones(milestones, slug) {
581
+ for (const m of milestones) {
582
+ const goal = intentGoalForSlug(m.description ?? "", slug);
583
+ if (goal)
584
+ return goal;
585
+ }
586
+ return "";
587
+ }
588
+ function findMilestoneForSlug(owner, repo, slug) {
589
+ const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
590
+ const legacy = matchesLegacyMilestone(milestones, slug);
591
+ const issues = ghIssueListByLabel(owner, repo, trancheLabel(slug));
592
+ if (legacy)
593
+ return resolveLegacyFacts(legacy, issues);
594
+ return { goal: goalFromMilestones(milestones, slug), lifecycle: lifecycleFromIssues(issues) };
595
+ }
596
+
546
597
  // ../../packages/aeg-forge-state/src/derive-from-forge.ts
547
598
  function trancheFromIssues(slug, issues, known) {
548
599
  return {
@@ -2726,16 +2777,18 @@ function checkDeadBranchPush(input) {
2726
2777
  }
2727
2778
  // ../../packages/aeg-core/src/reader-resolvable-prose.ts
2728
2779
  var SHIPS_PREFIX = "aeg-root/";
2729
- var SHIPS_ARCHIVE_PREFIX = "aeg-root/tranches/completed/";
2780
+ function shipsArchivePrefix(shipsPrefix) {
2781
+ return `${shipsPrefix}tranches/completed/`;
2782
+ }
2730
2783
  function isSpecFile2(path) {
2731
2784
  return path.startsWith("apps/") && path.includes("/specs/") && path.endsWith(".md");
2732
2785
  }
2733
2786
  function isClaudeMdFile(path) {
2734
2787
  return path === "CLAUDE.md" || path.endsWith("/CLAUDE.md");
2735
2788
  }
2736
- function classifyProseFile(path, readerFacingPrefix, readerFacingSuffix) {
2737
- if (path.startsWith(SHIPS_PREFIX)) {
2738
- return path.startsWith(SHIPS_ARCHIVE_PREFIX) ? "internal" : "ships";
2789
+ function classifyProseFile(path, readerFacingPrefix, readerFacingSuffix, shipsPrefix = SHIPS_PREFIX) {
2790
+ if (path.startsWith(shipsPrefix)) {
2791
+ return path.startsWith(shipsArchivePrefix(shipsPrefix)) ? "internal" : "ships";
2739
2792
  }
2740
2793
  if (path.startsWith(readerFacingPrefix) && path.endsWith(readerFacingSuffix)) {
2741
2794
  return "reader-facing";
@@ -2770,7 +2823,7 @@ function legacySlugPattern(legacySlugs) {
2770
2823
  const alternation = legacySlugs.map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
2771
2824
  return new RegExp(`(^|[^a-zA-Z0-9\\n-])(${alternation})([^a-zA-Z0-9\\n-]|$)`, "g");
2772
2825
  }
2773
- function checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuffix, legacySlugs = []) {
2826
+ function checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuffix, legacySlugs = [], shipsPrefix = SHIPS_PREFIX) {
2774
2827
  const findings = [];
2775
2828
  const legacyPattern = legacySlugPattern(legacySlugs);
2776
2829
  const patterns = [
@@ -2779,7 +2832,7 @@ function checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuff
2779
2832
  ...legacyPattern ? [{ pattern: legacyPattern, what: "an internal tranche slug", group: 2 }] : []
2780
2833
  ];
2781
2834
  for (const file of files) {
2782
- const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix);
2835
+ const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix, shipsPrefix);
2783
2836
  if (!cls || !SWEPT_CLASSES.has(cls))
2784
2837
  continue;
2785
2838
  const scrubbed = stripNonProse(file.path, file.content);
@@ -2825,10 +2878,10 @@ function definesOrLinksGlossary(content, term) {
2825
2878
  return true;
2826
2879
  return /docs\/glossary|glossary\.md/i.test(content);
2827
2880
  }
2828
- function checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix) {
2881
+ function checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix, shipsPrefix = SHIPS_PREFIX) {
2829
2882
  const findings = [];
2830
2883
  for (const file of files) {
2831
- const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix);
2884
+ const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix, shipsPrefix);
2832
2885
  if (!cls || !SWEPT_CLASSES.has(cls))
2833
2886
  continue;
2834
2887
  const scrubbed = stripNonProse(file.path, file.content);
@@ -2847,10 +2900,10 @@ function checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, read
2847
2900
  }
2848
2901
  return findings;
2849
2902
  }
2850
- function checkReaderResolvableProse(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix, legacySlugs = []) {
2903
+ function checkReaderResolvableProse(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix, legacySlugs = [], shipsPrefix = SHIPS_PREFIX) {
2851
2904
  return [
2852
- ...checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuffix, legacySlugs),
2853
- ...checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix)
2905
+ ...checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuffix, legacySlugs, shipsPrefix),
2906
+ ...checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix, shipsPrefix)
2854
2907
  ];
2855
2908
  }
2856
2909
  function parseGlossaryTerms(glossaryContent) {
@@ -2864,6 +2917,101 @@ function parseGlossaryTerms(glossaryContent) {
2864
2917
  }
2865
2918
  return terms;
2866
2919
  }
2920
+ // ../../packages/aeg-core/src/retired-vocabulary.ts
2921
+ var RETIRED_PATTERNS = [
2922
+ String.raw`\bD-[0-9]{3}\b`,
2923
+ "D-(###|NNN|nnn|xxx)",
2924
+ "decision (entry|logged)",
2925
+ "decision-log entry",
2926
+ "decision log entry",
2927
+ "decision log(s|ged|ging)?([^a-z]|$)",
2928
+ "decision-log",
2929
+ "decision entr",
2930
+ "decisions? (are |is )?logged",
2931
+ "log entries",
2932
+ String.raw`decisions\.md`,
2933
+ "decisions-legacy",
2934
+ "CONTRADICTION",
2935
+ "assumes Tier 3",
2936
+ "Lock: ?YES",
2937
+ "Lock: ?NO",
2938
+ "lock approvals?",
2939
+ "approves locks",
2940
+ "Conforms to lock",
2941
+ "Challenges lock",
2942
+ "checkDecisionNumbersFresh",
2943
+ String.raw`roles/team-leader\.md`,
2944
+ String.raw`\(,`,
2945
+ String.raw`,\)`,
2946
+ String.raw`\(\.\)`
2947
+ ];
2948
+ var RETIRED_EXEMPT_SUBSTRINGS = [
2949
+ "docs/decisions-legacy.md",
2950
+ "apps/herald-ai/docs/",
2951
+ "apps/vada-ai/docs/",
2952
+ "aeg-root/tranches/completed/",
2953
+ "packages/aeg-core/src/docs/published-prose",
2954
+ "packages/aeg-core/src/retired-vocabulary.test.ts",
2955
+ "packages/aeg-core/src/retired-vocabulary.ts",
2956
+ "/fixtures/",
2957
+ "/node_modules/",
2958
+ "/.next/",
2959
+ "/.turbo/"
2960
+ ];
2961
+ var PATTERN_EXEMPT = {
2962
+ [String.raw`\bD-[0-9]{3}\b`]: ["./CLAUDE.md"],
2963
+ [String.raw`decisions\.md`]: [
2964
+ "docs-index.md",
2965
+ "apps/vada-ai/specs/legacy/README.md",
2966
+ "apps/vada-ai/specs/vada-product-spec.md",
2967
+ "apps/vada-ai/web/CLAUDE.md",
2968
+ "apps/vada-ai/CLAUDE.md"
2969
+ ],
2970
+ "decisions-legacy": [
2971
+ "packages/aeg-core/src/file-classify.ts",
2972
+ "packages/aeg-core/src/file-classify.test.ts",
2973
+ "packages/aeg-core/src/pr-tier.test.ts",
2974
+ "docs-index.md",
2975
+ ".claude/skills/database/SKILL.md",
2976
+ ".claude/skills/vada-architecture/SKILL.md",
2977
+ "apps/vada-ai/specs/vada-byok-principles.md",
2978
+ "apps/vada-ai/web/CLAUDE.md",
2979
+ "apps/vada-ai/CLAUDE.md"
2980
+ ],
2981
+ CONTRADICTION: ["apps/vada-ai/specs/vada-reviewers-spec.md"]
2982
+ };
2983
+ function lineNumbers(content, pattern) {
2984
+ const out = [];
2985
+ const lines = content.split(`
2986
+ `);
2987
+ for (let i = 0;i < lines.length; i++) {
2988
+ if (pattern.test(lines[i] ?? ""))
2989
+ out.push(i + 1);
2990
+ }
2991
+ return out;
2992
+ }
2993
+ function scanRetiredVocabulary(files) {
2994
+ const findings = [];
2995
+ for (const file of files) {
2996
+ if (RETIRED_EXEMPT_SUBSTRINGS.some((e) => file.path.includes(e)))
2997
+ continue;
2998
+ for (const pattern of RETIRED_PATTERNS) {
2999
+ const patternExempt = PATTERN_EXEMPT[pattern] ?? [];
3000
+ if (patternExempt.some((e) => file.path.includes(e)))
3001
+ continue;
3002
+ const re = new RegExp(pattern);
3003
+ for (const line of lineNumbers(file.content, re)) {
3004
+ findings.push({
3005
+ file: file.path,
3006
+ line,
3007
+ pattern,
3008
+ message: `claims a retired AEG mechanism is still live (matches /${pattern}/)`
3009
+ });
3010
+ }
3011
+ }
3012
+ }
3013
+ return findings;
3014
+ }
2867
3015
  // ../../packages/aeg-core/src/dispatch-gate.ts
2868
3016
  function isHandClosedByRecognizedPrincipal(dep, principalAllowlist) {
2869
3017
  return dep.issueState === "closed" && dep.stateReason === "completed" && isPrincipal(dep.closedByActor ?? null, principalAllowlist);
@@ -3140,9 +3288,15 @@ function emitCheckError(error) {
3140
3288
 
3141
3289
  // src/lib/config.ts
3142
3290
  import { execFileSync as execFileSync2 } from "node:child_process";
3291
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3143
3292
  import { homedir } from "node:os";
3144
3293
  import { dirname, join } from "node:path";
3145
3294
  import { z as z2 } from "zod";
3295
+
3296
+ // src/lib/agent-vendors.ts
3297
+ var AGENT_VENDORS = ["skills", "claude", "gemini"];
3298
+
3299
+ // src/lib/config.ts
3146
3300
  var EnvEntrySchema = z2.union([
3147
3301
  z2.literal(true),
3148
3302
  z2.object({ optional: z2.literal(true) }),
@@ -3181,6 +3335,11 @@ var CheckEntrySchema = z2.object({
3181
3335
  }
3182
3336
  }
3183
3337
  });
3338
+ var RoleEntrySchema = z2.object({
3339
+ contract: z2.string().refine((p) => p.includes("/"), {
3340
+ message: 'must be a path (contain at least one "/"), not a bare filename'
3341
+ })
3342
+ });
3184
3343
  var BRIEF_BUILTINS = [
3185
3344
  "tier",
3186
3345
  "testPlan",
@@ -3195,7 +3354,8 @@ var BRIEF_BUILTINS = [
3195
3354
  "for",
3196
3355
  "closesN",
3197
3356
  "premiseCoverage",
3198
- "issueRationale"
3357
+ "issueRationale",
3358
+ "milestoneShape"
3199
3359
  ];
3200
3360
  var BriefSectionSchema = z2.union([
3201
3361
  z2.object({ builtin: z2.enum(BRIEF_BUILTINS) }),
@@ -3206,6 +3366,7 @@ var BriefSectionSchema = z2.union([
3206
3366
  var BriefSchemaSchema = z2.object({
3207
3367
  pr: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3208
3368
  issue: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3369
+ milestone: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3209
3370
  ack: z2.array(z2.enum(BRIEF_BUILTINS)).optional()
3210
3371
  });
3211
3372
  function isSafeRepoRelPath(p) {
@@ -3227,7 +3388,8 @@ var ManagedManifestSchema = z2.object({
3227
3388
  version: z2.number().int().positive(),
3228
3389
  files: z2.array(SafeRepoRelPath),
3229
3390
  blocks: z2.array(ManagedBlockRecordSchema),
3230
- labels: z2.array(z2.string())
3391
+ labels: z2.array(z2.string()),
3392
+ agents: z2.array(z2.enum(AGENT_VENDORS)).optional()
3231
3393
  });
3232
3394
  var VinayaConfigSchema = z2.object({
3233
3395
  rings: z2.object({
@@ -3235,16 +3397,79 @@ var VinayaConfigSchema = z2.object({
3235
3397
  ring2_asyncAudits: z2.boolean()
3236
3398
  }).optional(),
3237
3399
  checks: z2.record(z2.string(), CheckEntrySchema).optional(),
3400
+ roles: z2.record(z2.string(), RoleEntrySchema).optional(),
3238
3401
  briefSchema: BriefSchemaSchema.optional(),
3239
3402
  managed: ManagedManifestSchema.optional(),
3240
3403
  principals: z2.array(z2.string()).min(1).optional(),
3241
3404
  releaseActor: z2.string().min(1).optional(),
3242
3405
  ci: z2.object({ setup: z2.string().min(1) }).optional(),
3243
- blastRadius: z2.object({ extraDomains: z2.array(z2.string()).optional() }).optional()
3406
+ blastRadius: z2.object({ extraDomains: z2.array(z2.string()).optional() }).optional(),
3407
+ proseGates: z2.object({
3408
+ doctrineRoot: z2.string().min(1).optional(),
3409
+ readerFacingPrefix: z2.string().min(1).optional(),
3410
+ readerFacingSuffix: z2.string().min(1).optional(),
3411
+ legacySlugDir: z2.string().min(1).optional()
3412
+ }).optional()
3244
3413
  });
3245
3414
  var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
3246
3415
  var GLOBAL_CONFIG_PATH = join(GLOBAL_VINAYA_HOME, "config.json");
3247
3416
  var LOCAL_CONFIG_FILENAME = "vinaya.config.json";
3417
+ function findLocalConfig() {
3418
+ let dir = process.cwd();
3419
+ while (true) {
3420
+ const candidate = join(dir, LOCAL_CONFIG_FILENAME);
3421
+ if (existsSync(candidate))
3422
+ return candidate;
3423
+ if (existsSync(join(dir, ".git")))
3424
+ return null;
3425
+ const parent = dirname(dir);
3426
+ if (parent === dir)
3427
+ return null;
3428
+ dir = parent;
3429
+ }
3430
+ }
3431
+ function configPath() {
3432
+ const local = findLocalConfig();
3433
+ if (local)
3434
+ return local;
3435
+ if (existsSync(GLOBAL_CONFIG_PATH))
3436
+ return GLOBAL_CONFIG_PATH;
3437
+ return null;
3438
+ }
3439
+ function globalChecksIgnoredWarning(path) {
3440
+ return `${path}: "checks" registration in the global config is ignored — checks may only be registered from a repo-local vinaya.config.json.`;
3441
+ }
3442
+ function globalPrincipalsIgnoredWarning(path) {
3443
+ return `${path}: "principals" in the global config is ignored — principals may only be declared from a repo-local vinaya.config.json.`;
3444
+ }
3445
+ function globalRolesIgnoredWarning(path) {
3446
+ return `${path}: "roles" registration in the global config is ignored — roles may only be registered from a repo-local vinaya.config.json.`;
3447
+ }
3448
+ function globalReleaseActorIgnoredWarning(path) {
3449
+ return `${path}: "releaseActor" in the global config is ignored — releaseActor may only be declared from a repo-local vinaya.config.json.`;
3450
+ }
3451
+ function stripGlobalOnlyKeys(config, path) {
3452
+ if (path !== GLOBAL_CONFIG_PATH)
3453
+ return config;
3454
+ let result = config;
3455
+ if (result.checks && Object.keys(result.checks).length > 0) {
3456
+ console.error(`⚠ ${globalChecksIgnoredWarning(path)}`);
3457
+ result = { ...result, checks: undefined };
3458
+ }
3459
+ if (result.roles && Object.keys(result.roles).length > 0) {
3460
+ console.error(`⚠ ${globalRolesIgnoredWarning(path)}`);
3461
+ result = { ...result, roles: undefined };
3462
+ }
3463
+ if (result.principals && result.principals.length > 0) {
3464
+ console.error(`⚠ ${globalPrincipalsIgnoredWarning(path)}`);
3465
+ result = { ...result, principals: undefined };
3466
+ }
3467
+ if (result.releaseActor) {
3468
+ console.error(`⚠ ${globalReleaseActorIgnoredWarning(path)}`);
3469
+ result = { ...result, releaseActor: undefined };
3470
+ }
3471
+ return result;
3472
+ }
3248
3473
  function resolvePrincipalAllowlist(config) {
3249
3474
  return config?.principals ?? PRINCIPAL_ALLOWLIST;
3250
3475
  }
@@ -3317,13 +3542,87 @@ function loadTrustAnchorConfig(fetcher = ghFetchTrustAnchorConfig) {
3317
3542
  return null;
3318
3543
  }
3319
3544
  }
3545
+ function loadConfig() {
3546
+ const path = configPath();
3547
+ if (!path)
3548
+ return null;
3549
+ try {
3550
+ const raw = JSON.parse(readFileSync(path, "utf-8"));
3551
+ return stripGlobalOnlyKeys(VinayaConfigSchema.parse(raw), path);
3552
+ } catch {
3553
+ return null;
3554
+ }
3555
+ }
3556
+
3557
+ // src/checks/edge-resolve.ts
3558
+ import { execFileSync as execFileSync3 } from "node:child_process";
3559
+ var DIRECT_ISSUE_REF = /#(\d+)/;
3560
+ var issueCache = new Map;
3561
+ function fetchIssueState(num, repo) {
3562
+ const key = `${repo.owner}/${repo.repo}#${num}`;
3563
+ if (issueCache.has(key))
3564
+ return issueCache.get(key) ?? null;
3565
+ let out = null;
3566
+ try {
3567
+ const raw = execFileSync3("gh", [
3568
+ "issue",
3569
+ "view",
3570
+ String(num),
3571
+ "-R",
3572
+ `${repo.owner}/${repo.repo}`,
3573
+ "--json",
3574
+ "state,stateReason,closedByPullRequestsReferences"
3575
+ ], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
3576
+ out = JSON.parse(raw);
3577
+ } catch {
3578
+ out = null;
3579
+ }
3580
+ issueCache.set(key, out);
3581
+ return out;
3582
+ }
3583
+ function edgeFromIssueJson(num, json) {
3584
+ if (json === null) {
3585
+ return { issue: num, merged: false, open: false, issueState: null, stateReason: null, closedByActor: null };
3586
+ }
3587
+ const state = json.state === "CLOSED" ? "closed" : json.state === "OPEN" ? "open" : null;
3588
+ const reason = json.stateReason === "COMPLETED" ? "completed" : json.stateReason === "NOT_PLANNED" ? "not_planned" : null;
3589
+ const closedByMergedPr = (json.closedByPullRequestsReferences ?? []).length > 0;
3590
+ return {
3591
+ issue: num,
3592
+ merged: state === "closed" && closedByMergedPr,
3593
+ open: false,
3594
+ issueState: state,
3595
+ stateReason: reason,
3596
+ closedByActor: null
3597
+ };
3598
+ }
3599
+ function resolveEdge(id, taskById, factsByTaskId, repo) {
3600
+ const target = taskById.get(id);
3601
+ if (target) {
3602
+ const facts = target.issue !== null ? factsByTaskId.get(target.id) : undefined;
3603
+ return {
3604
+ issue: target.issue,
3605
+ merged: facts?.prState === "merged",
3606
+ open: facts?.prState === "open",
3607
+ issueState: facts?.issueState ?? null,
3608
+ stateReason: facts?.stateReason ?? null,
3609
+ closedByActor: facts?.closedByActor ?? null
3610
+ };
3611
+ }
3612
+ const direct = id.match(DIRECT_ISSUE_REF);
3613
+ if (direct) {
3614
+ const num = Number(direct[1]);
3615
+ return edgeFromIssueJson(num, fetchIssueState(num, repo));
3616
+ }
3617
+ return { issue: null, merged: false, open: false, issueState: null, stateReason: null, closedByActor: null };
3618
+ }
3320
3619
 
3321
3620
  // src/checks/bin/check-dispatch-readiness.ts
3322
3621
  var CHECK_NAME = "dispatch-readiness";
3323
3622
  var execFileAsync4 = promisify4(execFile4);
3324
3623
  function git(args) {
3325
3624
  try {
3326
- return execFileSync3("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
3625
+ return execFileSync4("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
3327
3626
  } catch {
3328
3627
  return "";
3329
3628
  }
@@ -3370,29 +3669,6 @@ function fail(message, prompt) {
3370
3669
  });
3371
3670
  process.exit(1);
3372
3671
  }
3373
- function resolveEdge(id, taskById, factsByTaskId) {
3374
- const target = taskById.get(id);
3375
- if (target) {
3376
- const facts = target.issue !== null ? factsByTaskId.get(target.id) : undefined;
3377
- return {
3378
- issue: target.issue,
3379
- merged: facts?.prState === "merged",
3380
- open: facts?.prState === "open",
3381
- issueState: facts?.issueState ?? null,
3382
- stateReason: facts?.stateReason ?? null,
3383
- closedByActor: facts?.closedByActor ?? null
3384
- };
3385
- }
3386
- const direct = id.match(/^#(\d+)$/);
3387
- return {
3388
- issue: direct ? Number(direct[1]) : null,
3389
- merged: false,
3390
- open: false,
3391
- issueState: null,
3392
- stateReason: null,
3393
- closedByActor: null
3394
- };
3395
- }
3396
3672
  async function main() {
3397
3673
  const branch = currentBranch();
3398
3674
  const m = branch.match(/^task\/([^/]+)\/(.+)$/);
@@ -3433,7 +3709,7 @@ async function main() {
3433
3709
  const taskById = new Map(tranche.tasks.map((t) => [t.id, t]));
3434
3710
  const factsByTaskId = snapshot.facts;
3435
3711
  const dependsOn = task.dependsOn.map((dep) => {
3436
- const r = resolveEdge(dep, taskById, factsByTaskId);
3712
+ const r = resolveEdge(dep, taskById, factsByTaskId, repo);
3437
3713
  return {
3438
3714
  id: dep,
3439
3715
  issue: r.issue,
@@ -3444,7 +3720,7 @@ async function main() {
3444
3720
  };
3445
3721
  });
3446
3722
  const conflictsWith = task.conflictsWith.map((c) => {
3447
- const r = resolveEdge(c, taskById, factsByTaskId);
3723
+ const r = resolveEdge(c, taskById, factsByTaskId, repo);
3448
3724
  return { id: c, issue: r.issue, openOrInFlight: r.open };
3449
3725
  });
3450
3726
  const priorTrancheArchival = [];