@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
@@ -275,6 +275,9 @@ function issueListByLabelArgs(owner, repo, label) {
275
275
  "200"
276
276
  ];
277
277
  }
278
+ function ghIssueListByLabel(owner, repo, label) {
279
+ return JSON.parse(run(issueListByLabelArgs(owner, repo, label)));
280
+ }
278
281
  async function ghIssueListByLabelAsync(owner, repo, label) {
279
282
  return JSON.parse(await runAsync(issueListByLabelArgs(owner, repo, label)));
280
283
  }
@@ -290,18 +293,6 @@ async function ghIssueListByAnyLabelAsync(owner, repo, labels) {
290
293
  return dedupeByNumber(perLabel.flat());
291
294
  }
292
295
 
293
- // ../../packages/aeg-forge-state/src/fetch-milestone.ts
294
- function findMilestoneForSlug(owner, repo, slug) {
295
- const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
296
- const match = milestones.find((m) => m.title === slug);
297
- if (!match)
298
- return null;
299
- return {
300
- goal: match.description ?? "",
301
- lifecycle: match.state === "closed" ? "complete" : "active"
302
- };
303
- }
304
-
305
296
  // ../../packages/aeg-forge-state/src/labels.ts
306
297
  var LABELS = [
307
298
  {
@@ -544,6 +535,66 @@ function resolveTaskIssueRef(title, labels) {
544
535
  return { trancheSlug, taskId };
545
536
  }
546
537
 
538
+ // ../../packages/aeg-forge-state/src/fetch-milestone.ts
539
+ var INTENTS_HEADING = /^#{1,6}\s*Tranche intents\s*$/im;
540
+ var NEXT_HEADING = /^#{1,6}\s+\S/m;
541
+ var INTENT_BULLET = /^-\s+([a-z0-9][a-z0-9-]*)\s*:\s*(.+)$/i;
542
+ function intentGoalForSlug(description, slug) {
543
+ const text = stripCode(description, { inlineSpans: "keep" });
544
+ const start = text.match(INTENTS_HEADING);
545
+ if (!start || start.index === undefined)
546
+ return "";
547
+ const rest = text.slice(start.index + start[0].length);
548
+ const next = rest.match(NEXT_HEADING);
549
+ const section = rest.slice(0, next && next.index !== undefined ? next.index : rest.length);
550
+ for (const line of section.split(`
551
+ `)) {
552
+ const trimmed = line.trim();
553
+ if (trimmed.length === 0)
554
+ continue;
555
+ const m = trimmed.match(INTENT_BULLET);
556
+ if (m && (m[1] ?? "").toLowerCase() === slug.toLowerCase())
557
+ return (m[2] ?? "").trim();
558
+ }
559
+ return "";
560
+ }
561
+ function matchesLegacyMilestone(milestones, slug) {
562
+ return milestones.find((m) => m.title === slug) ?? null;
563
+ }
564
+ function factsFromLegacyMilestone(milestone) {
565
+ return {
566
+ goal: milestone.description ?? "",
567
+ lifecycle: milestone.state === "closed" ? "complete" : "active"
568
+ };
569
+ }
570
+ function resolveLegacyFacts(milestone, labelIssues) {
571
+ if (milestone.state === "closed" && labelIssues.length > 0) {
572
+ return { goal: milestone.description ?? "", lifecycle: lifecycleFromIssues(labelIssues) };
573
+ }
574
+ return factsFromLegacyMilestone(milestone);
575
+ }
576
+ function lifecycleFromIssues(issues) {
577
+ if (issues.length === 0)
578
+ return "planned";
579
+ return issues.some((i) => i.state === "OPEN") ? "active" : "complete";
580
+ }
581
+ function goalFromMilestones(milestones, slug) {
582
+ for (const m of milestones) {
583
+ const goal = intentGoalForSlug(m.description ?? "", slug);
584
+ if (goal)
585
+ return goal;
586
+ }
587
+ return "";
588
+ }
589
+ function findMilestoneForSlug(owner, repo, slug) {
590
+ const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
591
+ const legacy = matchesLegacyMilestone(milestones, slug);
592
+ const issues = ghIssueListByLabel(owner, repo, trancheLabel(slug));
593
+ if (legacy)
594
+ return resolveLegacyFacts(legacy, issues);
595
+ return { goal: goalFromMilestones(milestones, slug), lifecycle: lifecycleFromIssues(issues) };
596
+ }
597
+
547
598
  // ../../packages/aeg-forge-state/src/derive-from-forge.ts
548
599
  function trancheFromIssues(slug, issues, known) {
549
600
  return {
@@ -2727,16 +2778,18 @@ function checkDeadBranchPush(input) {
2727
2778
  }
2728
2779
  // ../../packages/aeg-core/src/reader-resolvable-prose.ts
2729
2780
  var SHIPS_PREFIX = "aeg-root/";
2730
- var SHIPS_ARCHIVE_PREFIX = "aeg-root/tranches/completed/";
2781
+ function shipsArchivePrefix(shipsPrefix) {
2782
+ return `${shipsPrefix}tranches/completed/`;
2783
+ }
2731
2784
  function isSpecFile2(path) {
2732
2785
  return path.startsWith("apps/") && path.includes("/specs/") && path.endsWith(".md");
2733
2786
  }
2734
2787
  function isClaudeMdFile(path) {
2735
2788
  return path === "CLAUDE.md" || path.endsWith("/CLAUDE.md");
2736
2789
  }
2737
- function classifyProseFile(path, readerFacingPrefix, readerFacingSuffix) {
2738
- if (path.startsWith(SHIPS_PREFIX)) {
2739
- return path.startsWith(SHIPS_ARCHIVE_PREFIX) ? "internal" : "ships";
2790
+ function classifyProseFile(path, readerFacingPrefix, readerFacingSuffix, shipsPrefix = SHIPS_PREFIX) {
2791
+ if (path.startsWith(shipsPrefix)) {
2792
+ return path.startsWith(shipsArchivePrefix(shipsPrefix)) ? "internal" : "ships";
2740
2793
  }
2741
2794
  if (path.startsWith(readerFacingPrefix) && path.endsWith(readerFacingSuffix)) {
2742
2795
  return "reader-facing";
@@ -2771,7 +2824,7 @@ function legacySlugPattern(legacySlugs) {
2771
2824
  const alternation = legacySlugs.map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
2772
2825
  return new RegExp(`(^|[^a-zA-Z0-9\\n-])(${alternation})([^a-zA-Z0-9\\n-]|$)`, "g");
2773
2826
  }
2774
- function checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuffix, legacySlugs = []) {
2827
+ function checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuffix, legacySlugs = [], shipsPrefix = SHIPS_PREFIX) {
2775
2828
  const findings = [];
2776
2829
  const legacyPattern = legacySlugPattern(legacySlugs);
2777
2830
  const patterns = [
@@ -2780,7 +2833,7 @@ function checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuff
2780
2833
  ...legacyPattern ? [{ pattern: legacyPattern, what: "an internal tranche slug", group: 2 }] : []
2781
2834
  ];
2782
2835
  for (const file of files) {
2783
- const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix);
2836
+ const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix, shipsPrefix);
2784
2837
  if (!cls || !SWEPT_CLASSES.has(cls))
2785
2838
  continue;
2786
2839
  const scrubbed = stripNonProse(file.path, file.content);
@@ -2826,10 +2879,10 @@ function definesOrLinksGlossary(content, term) {
2826
2879
  return true;
2827
2880
  return /docs\/glossary|glossary\.md/i.test(content);
2828
2881
  }
2829
- function checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix) {
2882
+ function checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix, shipsPrefix = SHIPS_PREFIX) {
2830
2883
  const findings = [];
2831
2884
  for (const file of files) {
2832
- const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix);
2885
+ const cls = classifyProseFile(file.path, readerFacingPrefix, readerFacingSuffix, shipsPrefix);
2833
2886
  if (!cls || !SWEPT_CLASSES.has(cls))
2834
2887
  continue;
2835
2888
  const scrubbed = stripNonProse(file.path, file.content);
@@ -2848,10 +2901,10 @@ function checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, read
2848
2901
  }
2849
2902
  return findings;
2850
2903
  }
2851
- function checkReaderResolvableProse(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix, legacySlugs = []) {
2904
+ function checkReaderResolvableProse(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix, legacySlugs = [], shipsPrefix = SHIPS_PREFIX) {
2852
2905
  return [
2853
- ...checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuffix, legacySlugs),
2854
- ...checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix)
2906
+ ...checkUnresolvableReferences(files, readerFacingPrefix, readerFacingSuffix, legacySlugs, shipsPrefix),
2907
+ ...checkUndefinedVocabulary(files, glossaryTerms, readerFacingPrefix, readerFacingSuffix, shipsPrefix)
2855
2908
  ];
2856
2909
  }
2857
2910
  function parseGlossaryTerms(glossaryContent) {
@@ -2865,6 +2918,101 @@ function parseGlossaryTerms(glossaryContent) {
2865
2918
  }
2866
2919
  return terms;
2867
2920
  }
2921
+ // ../../packages/aeg-core/src/retired-vocabulary.ts
2922
+ var RETIRED_PATTERNS = [
2923
+ String.raw`\bD-[0-9]{3}\b`,
2924
+ "D-(###|NNN|nnn|xxx)",
2925
+ "decision (entry|logged)",
2926
+ "decision-log entry",
2927
+ "decision log entry",
2928
+ "decision log(s|ged|ging)?([^a-z]|$)",
2929
+ "decision-log",
2930
+ "decision entr",
2931
+ "decisions? (are |is )?logged",
2932
+ "log entries",
2933
+ String.raw`decisions\.md`,
2934
+ "decisions-legacy",
2935
+ "CONTRADICTION",
2936
+ "assumes Tier 3",
2937
+ "Lock: ?YES",
2938
+ "Lock: ?NO",
2939
+ "lock approvals?",
2940
+ "approves locks",
2941
+ "Conforms to lock",
2942
+ "Challenges lock",
2943
+ "checkDecisionNumbersFresh",
2944
+ String.raw`roles/team-leader\.md`,
2945
+ String.raw`\(,`,
2946
+ String.raw`,\)`,
2947
+ String.raw`\(\.\)`
2948
+ ];
2949
+ var RETIRED_EXEMPT_SUBSTRINGS = [
2950
+ "docs/decisions-legacy.md",
2951
+ "apps/herald-ai/docs/",
2952
+ "apps/vada-ai/docs/",
2953
+ "aeg-root/tranches/completed/",
2954
+ "packages/aeg-core/src/docs/published-prose",
2955
+ "packages/aeg-core/src/retired-vocabulary.test.ts",
2956
+ "packages/aeg-core/src/retired-vocabulary.ts",
2957
+ "/fixtures/",
2958
+ "/node_modules/",
2959
+ "/.next/",
2960
+ "/.turbo/"
2961
+ ];
2962
+ var PATTERN_EXEMPT = {
2963
+ [String.raw`\bD-[0-9]{3}\b`]: ["./CLAUDE.md"],
2964
+ [String.raw`decisions\.md`]: [
2965
+ "docs-index.md",
2966
+ "apps/vada-ai/specs/legacy/README.md",
2967
+ "apps/vada-ai/specs/vada-product-spec.md",
2968
+ "apps/vada-ai/web/CLAUDE.md",
2969
+ "apps/vada-ai/CLAUDE.md"
2970
+ ],
2971
+ "decisions-legacy": [
2972
+ "packages/aeg-core/src/file-classify.ts",
2973
+ "packages/aeg-core/src/file-classify.test.ts",
2974
+ "packages/aeg-core/src/pr-tier.test.ts",
2975
+ "docs-index.md",
2976
+ ".claude/skills/database/SKILL.md",
2977
+ ".claude/skills/vada-architecture/SKILL.md",
2978
+ "apps/vada-ai/specs/vada-byok-principles.md",
2979
+ "apps/vada-ai/web/CLAUDE.md",
2980
+ "apps/vada-ai/CLAUDE.md"
2981
+ ],
2982
+ CONTRADICTION: ["apps/vada-ai/specs/vada-reviewers-spec.md"]
2983
+ };
2984
+ function lineNumbers(content, pattern) {
2985
+ const out = [];
2986
+ const lines = content.split(`
2987
+ `);
2988
+ for (let i = 0;i < lines.length; i++) {
2989
+ if (pattern.test(lines[i] ?? ""))
2990
+ out.push(i + 1);
2991
+ }
2992
+ return out;
2993
+ }
2994
+ function scanRetiredVocabulary(files) {
2995
+ const findings = [];
2996
+ for (const file of files) {
2997
+ if (RETIRED_EXEMPT_SUBSTRINGS.some((e) => file.path.includes(e)))
2998
+ continue;
2999
+ for (const pattern of RETIRED_PATTERNS) {
3000
+ const patternExempt = PATTERN_EXEMPT[pattern] ?? [];
3001
+ if (patternExempt.some((e) => file.path.includes(e)))
3002
+ continue;
3003
+ const re = new RegExp(pattern);
3004
+ for (const line of lineNumbers(file.content, re)) {
3005
+ findings.push({
3006
+ file: file.path,
3007
+ line,
3008
+ pattern,
3009
+ message: `claims a retired AEG mechanism is still live (matches /${pattern}/)`
3010
+ });
3011
+ }
3012
+ }
3013
+ }
3014
+ return findings;
3015
+ }
2868
3016
  // ../../packages/aeg-core/src/dispatch-gate.ts
2869
3017
  function isHandClosedByRecognizedPrincipal(dep, principalAllowlist) {
2870
3018
  return dep.issueState === "closed" && dep.stateReason === "completed" && isPrincipal(dep.closedByActor ?? null, principalAllowlist);