@attalabs/vinaya 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/README.md +1 -0
  2. package/dist/checks/bin/check-body-bare-digits.js +7 -1
  3. package/dist/checks/bin/check-branch-topology.js +27 -1
  4. package/dist/checks/bin/check-brief-shape.js +7 -1
  5. package/dist/checks/bin/check-changeset-coverage.js +7 -1
  6. package/dist/checks/bin/check-closes-n.js +27 -1
  7. package/dist/checks/bin/check-coherence.js +27 -1
  8. package/dist/checks/bin/check-dead-branch-push.js +7 -1
  9. package/dist/checks/bin/check-dispatch-readiness.js +27 -1
  10. package/dist/checks/bin/check-doc-coverage-push.js +7 -1
  11. package/dist/checks/bin/check-doc-coverage.js +7 -1
  12. package/dist/checks/bin/check-doctrine-no-procedures.js +7 -1
  13. package/dist/checks/bin/check-doctrine-portability.js +7 -1
  14. package/dist/checks/bin/check-evidence-fresh.js +7 -1
  15. package/dist/checks/bin/check-exec-bits.js +7 -1
  16. package/dist/checks/bin/check-first-push-dispatch.js +27 -1
  17. package/dist/checks/bin/check-issue-assignment.js +27 -1
  18. package/dist/checks/bin/check-main-branch-refusal.js +13 -2
  19. package/dist/checks/bin/check-no-disk-state.js +7 -1
  20. package/dist/checks/bin/check-pr-report-density.js +7 -1
  21. package/dist/checks/bin/check-quoted-command.js +38 -12
  22. package/dist/checks/bin/check-reader-resolvable-prose.js +38 -12
  23. package/dist/checks/bin/check-registry-gates.js +7 -1
  24. package/dist/checks/bin/check-retired-vocabulary.js +38 -12
  25. package/dist/checks/bin/check-review-gate.js +7 -1
  26. package/dist/checks/bin/check-single-plan-pr.js +7 -1
  27. package/dist/checks/bin/check-test-plan.js +7 -1
  28. package/dist/checks/bin/check-token-collection-wired.js +7 -1
  29. package/dist/checks/bin/check-token-report.js +7 -1
  30. package/dist/checks/bin/check-workspace-escape.js +7 -1
  31. package/dist/index.js +362 -87
  32. package/package.json +1 -1
  33. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/anchored-region.ts +9 -4
  34. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/blast-radius-domains.ts +7 -1
  35. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/brief-render.ts +462 -0
  36. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/brief-validation.ts +405 -4
  37. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/coherence-checks.ts +19 -0
  38. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/consumer-enumeration.ts +75 -0
  39. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/dispatch-gate.ts +44 -0
  40. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/doctrine-no-procedures.ts +94 -0
  41. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/doctrine-portability.ts +128 -5
  42. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/gate-audience.ts +3 -0
  43. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/index.ts +25 -3
  44. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/parse-token-report.ts +17 -5
  45. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/pr-report-density.ts +116 -0
  46. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/premise-check.ts +35 -0
  47. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/quoted-command.ts +3 -3
  48. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/review-gate.ts +116 -7
  49. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/review-status.ts +200 -0
  50. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/test-plan-gate.ts +25 -12
  51. package/studio-standalone/_node_modules/@attalabs/aeg-core/src/verdict-extraction.ts +70 -11
  52. package/studio-standalone/apps/vinaya-studio/web/.next/BUILD_ID +1 -1
  53. package/studio-standalone/apps/vinaya-studio/web/.next/build-manifest.json +3 -3
  54. package/studio-standalone/apps/vinaya-studio/web/.next/prerender-manifest.json +3 -3
  55. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.html +1 -1
  56. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.rsc +1 -1
  57. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  58. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  59. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  60. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  61. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  62. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page/server-reference-manifest.json +2 -2
  63. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  64. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  65. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/api/coherence/route.js.nft.json +1 -1
  66. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page/server-reference-manifest.json +2 -2
  67. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js.nft.json +1 -1
  68. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page_client-reference-manifest.js +1 -1
  69. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page/server-reference-manifest.json +2 -2
  70. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js.nft.json +1 -1
  71. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page_client-reference-manifest.js +1 -1
  72. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page/server-reference-manifest.json +2 -2
  73. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js.nft.json +1 -1
  74. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page_client-reference-manifest.js +1 -1
  75. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page/server-reference-manifest.json +2 -2
  76. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js.nft.json +1 -1
  77. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page_client-reference-manifest.js +1 -1
  78. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page/server-reference-manifest.json +2 -2
  79. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js.nft.json +1 -1
  80. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page_client-reference-manifest.js +1 -1
  81. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page/server-reference-manifest.json +2 -2
  82. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js.nft.json +1 -1
  83. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page_client-reference-manifest.js +1 -1
  84. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page/server-reference-manifest.json +2 -2
  85. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js.nft.json +1 -1
  86. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page_client-reference-manifest.js +1 -1
  87. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page/server-reference-manifest.json +2 -2
  88. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js.nft.json +1 -1
  89. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page_client-reference-manifest.js +1 -1
  90. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/[root-of-the-server]__02a-3g8._.js +1 -1
  91. 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
  92. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0053k9k._.js +1 -1
  93. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0112h-k._.js +1 -1
  94. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0o771t1._.js +1 -1
  95. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0puovz5._.js +1 -1
  96. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1hs0dcu._.js +1 -1
  97. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__1mcitst._.js → [root-of-the-server]__1pndh3_._.js} +1 -1
  98. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1wc4-ip._.js +1 -1
  99. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_03x_w6q._.js +1 -1
  100. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0gvm3og._.js +1 -1
  101. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0lwxg63._.js +1 -1
  102. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1fqw88f._.js +2 -2
  103. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1n0cnq-._.js +1 -1
  104. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/apps_vinaya-studio_web_src_app_studio_projects_[name]_tranches_[slug]_05ufo8i._.js +3 -3
  105. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_1vo08dj._.js +2 -2
  106. package/studio-standalone/apps/vinaya-studio/web/.next/server/middleware-build-manifest.js +3 -3
  107. package/studio-standalone/apps/vinaya-studio/web/.next/server/pages/500.html +1 -1
  108. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.js +1 -1
  109. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.json +3 -3
  110. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{0jer_drkl1e4r.js → 0zebsmmk1bvnb.js} +4 -4
  111. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{20x-t0a0v129z.js → 1uogpj5w2n5ju.js} +1 -1
  112. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{2ou_81ahqw31e.js → 3m1kgax7j2vgs.js} +1 -1
  113. package/studio-standalone/apps/vinaya-studio/web/package.json +2 -2
  114. /package/studio-standalone/_node_modules/@attalabs/vinaya/studio-standalone/_node_modules/@attalabs/vinaya/studio-standalone/_node_modules/@attalabs/{aeg-core → vinaya/studio-standalone/_node_modules/@attalabs/aeg-core}/bin/verify-coherence.ts +0 -0
  115. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{5xy_GA7G5KRuE3HDumIiw → QY2GhiI47-765D9DHGy1I}/_buildManifest.js +0 -0
  116. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{5xy_GA7G5KRuE3HDumIiw → QY2GhiI47-765D9DHGy1I}/_clientMiddlewareManifest.js +0 -0
  117. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{5xy_GA7G5KRuE3HDumIiw → QY2GhiI47-765D9DHGy1I}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -31,6 +31,7 @@ npx @attalabs/vinaya init # or: pnpm dlx / yarn dlx / bunx
31
31
  | `vinaya milestone edit <n> --body-file <path>` | Edit an existing Milestone's description after the same `checkMilestoneShape` gate `create` runs — the gated replacement for a raw `gh api PATCH` against a Milestone. Only the description changes; the title is untouched. `--validate-only` runs every check without writing; `--json` for the enveloped machine form. |
32
32
  | `vinaya milestone close --slug <slug>` | Close a tranche's Milestone — the gated replacement for the raw `gh api .../milestones/<n> -X PATCH -f state=closed` recipe the Tranche Archivist used to run on faith. Resolves the target Milestone the same legacy-or-intent-declared way Issue create auto-attach does, then refuses to close on any mismatch between the label's Issues and the Milestone's natively attached Issues — naming each unattached or foreign Issue and its repair path (`gh issue edit <n> --milestone <title>`, or `vinaya milestone adopt`) — before the PATCH ever reaches the forge. `--validate-only` verifies attachment without closing; `--json` for the enveloped machine form. |
33
33
  | `vinaya quickstart [--yes] [--dry-run]` | Guided wizard that runs `init` → optional doc-owners bind → optional project registration → commit → `demo break` → `doctor` → `push` in sequence, prompting between steps. `--yes` forwards straight through to `init` and answers every one of quickstart's own prompts with its documented default (skip the two optional steps, run the refusal-then-fix proof, push) — no prompt is opened at all, so the command completes with no human at the keyboard rather than merely with stdin closed. `--dry-run` also forwards to `init` and stops immediately after its preview — nothing is installed, so no later step runs against an uninstalled repo. |
34
+ | `vinaya release [--dry-run] [--allow-any-commit]` | Run this repo's own publish sequence in one command (`apps/cli/specs/self-hosting.md`, "How the published version is produced"). Refuses unless HEAD is the default branch, the tree is clean, HEAD equals `origin/<default>` (after `git fetch origin`), HEAD's commit subject starts with `Chore(release): Version packages` (unless `--allow-any-commit`), and `npm whoami` exits `0` — each its own refusal naming the fix. Then streams `bun install --frozen-lockfile`, `bun run build`, `bun run changeset:publish`, and a real `git push origin --tags`, so the repo's own generated pre-push hook sees the push exactly as any other push would. Afterward prints `npm view <pkg> version` for every tag now on HEAD, noting registry lag on `@attalabs/vinaya` (observed ~20 minutes) when it still shows the previous version. `--dry-run` stops after the preconditions and prints the plan; publishes nothing. |
34
35
 
35
36
  ## Config
36
37
 
@@ -4157,8 +4157,12 @@ function isNewDiskStateFile(path, status) {
4157
4157
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4158
4158
  }
4159
4159
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4160
+ function pushesAnyBranchRef(pushRefs) {
4161
+ return pushRefs.split(`
4162
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4163
+ }
4160
4164
  function checkMainBranchRefusal(facts) {
4161
- const { currentSymbolicBranch, defaultBranch } = facts;
4165
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4162
4166
  if (currentSymbolicBranch === null)
4163
4167
  return null;
4164
4168
  if (defaultBranch === null) {
@@ -4170,6 +4174,8 @@ function checkMainBranchRefusal(facts) {
4170
4174
  };
4171
4175
  }
4172
4176
  if (currentSymbolicBranch === defaultBranch) {
4177
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4178
+ return null;
4173
4179
  return {
4174
4180
  reason: "on-default-branch",
4175
4181
  severity: "error",
@@ -4157,8 +4157,12 @@ function isNewDiskStateFile(path, status) {
4157
4157
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4158
4158
  }
4159
4159
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4160
+ function pushesAnyBranchRef(pushRefs) {
4161
+ return pushRefs.split(`
4162
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4163
+ }
4160
4164
  function checkMainBranchRefusal(facts) {
4161
- const { currentSymbolicBranch, defaultBranch } = facts;
4165
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4162
4166
  if (currentSymbolicBranch === null)
4163
4167
  return null;
4164
4168
  if (defaultBranch === null) {
@@ -4170,6 +4174,8 @@ function checkMainBranchRefusal(facts) {
4170
4174
  };
4171
4175
  }
4172
4176
  if (currentSymbolicBranch === defaultBranch) {
4177
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4178
+ return null;
4173
4179
  return {
4174
4180
  reason: "on-default-branch",
4175
4181
  severity: "error",
@@ -4915,6 +4921,26 @@ var COMMANDS = [
4915
4921
  "Never reimplements or edits `init`/`init product`/`demo break`/`doctor` — it only calls their existing, unmodified entry points in sequence."
4916
4922
  ],
4917
4923
  status: "shipped"
4924
+ },
4925
+ {
4926
+ name: "release",
4927
+ description: "Run this repo's own publish sequence in one command, refusing to start unless every precondition holds",
4928
+ flags: [
4929
+ {
4930
+ flag: "--dry-run",
4931
+ description: "Run the preconditions only and print the plan; publishes nothing"
4932
+ },
4933
+ {
4934
+ flag: "--allow-any-commit",
4935
+ description: "Skip the check that HEAD's commit is a Version Packages commit"
4936
+ }
4937
+ ],
4938
+ details: [
4939
+ "Refuses unless HEAD is the default branch, the working tree is clean, HEAD equals `origin/<default>` (after `git fetch origin`), HEAD's commit subject starts with `Chore(release): Version packages` (unless `--allow-any-commit`), and `npm whoami` exits `0` — each its own refusal naming the fix.",
4940
+ "Then streams `bun install --frozen-lockfile`, `bun run build`, `bun run changeset:publish`, and `git push origin --tags` — a real push, so the repo's own generated pre-push hook sees it exactly as any other push would. After it, prints `npm view <pkg> version` for every tag now on HEAD, noting registry lag on `@attalabs/vinaya` (observed ~20 minutes) when it still shows the previous version.",
4941
+ 'This is the one procedure named in `apps/cli/specs/self-hosting.md`, "How the published version is produced" — publishing itself stays manual and human-triggered; this command only removes the hand-typed four-step recipe.'
4942
+ ],
4943
+ status: "shipped"
4918
4944
  }
4919
4945
  ];
4920
4946
  // ../../packages/sources/src/forge-adapter.ts
@@ -4157,8 +4157,12 @@ function isNewDiskStateFile(path, status) {
4157
4157
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4158
4158
  }
4159
4159
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4160
+ function pushesAnyBranchRef(pushRefs) {
4161
+ return pushRefs.split(`
4162
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4163
+ }
4160
4164
  function checkMainBranchRefusal(facts) {
4161
- const { currentSymbolicBranch, defaultBranch } = facts;
4165
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4162
4166
  if (currentSymbolicBranch === null)
4163
4167
  return null;
4164
4168
  if (defaultBranch === null) {
@@ -4170,6 +4174,8 @@ function checkMainBranchRefusal(facts) {
4170
4174
  };
4171
4175
  }
4172
4176
  if (currentSymbolicBranch === defaultBranch) {
4177
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4178
+ return null;
4173
4179
  return {
4174
4180
  reason: "on-default-branch",
4175
4181
  severity: "error",
@@ -4159,8 +4159,12 @@ function isNewDiskStateFile(path, status) {
4159
4159
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4160
4160
  }
4161
4161
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4162
+ function pushesAnyBranchRef(pushRefs) {
4163
+ return pushRefs.split(`
4164
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4165
+ }
4162
4166
  function checkMainBranchRefusal(facts) {
4163
- const { currentSymbolicBranch, defaultBranch } = facts;
4167
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4164
4168
  if (currentSymbolicBranch === null)
4165
4169
  return null;
4166
4170
  if (defaultBranch === null) {
@@ -4172,6 +4176,8 @@ function checkMainBranchRefusal(facts) {
4172
4176
  };
4173
4177
  }
4174
4178
  if (currentSymbolicBranch === defaultBranch) {
4179
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4180
+ return null;
4175
4181
  return {
4176
4182
  reason: "on-default-branch",
4177
4183
  severity: "error",
@@ -4157,8 +4157,12 @@ function isNewDiskStateFile(path, status) {
4157
4157
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4158
4158
  }
4159
4159
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4160
+ function pushesAnyBranchRef(pushRefs) {
4161
+ return pushRefs.split(`
4162
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4163
+ }
4160
4164
  function checkMainBranchRefusal(facts) {
4161
- const { currentSymbolicBranch, defaultBranch } = facts;
4165
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4162
4166
  if (currentSymbolicBranch === null)
4163
4167
  return null;
4164
4168
  if (defaultBranch === null) {
@@ -4170,6 +4174,8 @@ function checkMainBranchRefusal(facts) {
4170
4174
  };
4171
4175
  }
4172
4176
  if (currentSymbolicBranch === defaultBranch) {
4177
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4178
+ return null;
4173
4179
  return {
4174
4180
  reason: "on-default-branch",
4175
4181
  severity: "error",
@@ -4915,6 +4921,26 @@ var COMMANDS = [
4915
4921
  "Never reimplements or edits `init`/`init product`/`demo break`/`doctor` — it only calls their existing, unmodified entry points in sequence."
4916
4922
  ],
4917
4923
  status: "shipped"
4924
+ },
4925
+ {
4926
+ name: "release",
4927
+ description: "Run this repo's own publish sequence in one command, refusing to start unless every precondition holds",
4928
+ flags: [
4929
+ {
4930
+ flag: "--dry-run",
4931
+ description: "Run the preconditions only and print the plan; publishes nothing"
4932
+ },
4933
+ {
4934
+ flag: "--allow-any-commit",
4935
+ description: "Skip the check that HEAD's commit is a Version Packages commit"
4936
+ }
4937
+ ],
4938
+ details: [
4939
+ "Refuses unless HEAD is the default branch, the working tree is clean, HEAD equals `origin/<default>` (after `git fetch origin`), HEAD's commit subject starts with `Chore(release): Version packages` (unless `--allow-any-commit`), and `npm whoami` exits `0` — each its own refusal naming the fix.",
4940
+ "Then streams `bun install --frozen-lockfile`, `bun run build`, `bun run changeset:publish`, and `git push origin --tags` — a real push, so the repo's own generated pre-push hook sees it exactly as any other push would. After it, prints `npm view <pkg> version` for every tag now on HEAD, noting registry lag on `@attalabs/vinaya` (observed ~20 minutes) when it still shows the previous version.",
4941
+ 'This is the one procedure named in `apps/cli/specs/self-hosting.md`, "How the published version is produced" — publishing itself stays manual and human-triggered; this command only removes the hand-typed four-step recipe.'
4942
+ ],
4943
+ status: "shipped"
4918
4944
  }
4919
4945
  ];
4920
4946
  // ../../packages/sources/src/forge-adapter.ts
@@ -4160,8 +4160,12 @@ function isNewDiskStateFile(path, status) {
4160
4160
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4161
4161
  }
4162
4162
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4163
+ function pushesAnyBranchRef(pushRefs) {
4164
+ return pushRefs.split(`
4165
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4166
+ }
4163
4167
  function checkMainBranchRefusal(facts) {
4164
- const { currentSymbolicBranch, defaultBranch } = facts;
4168
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4165
4169
  if (currentSymbolicBranch === null)
4166
4170
  return null;
4167
4171
  if (defaultBranch === null) {
@@ -4173,6 +4177,8 @@ function checkMainBranchRefusal(facts) {
4173
4177
  };
4174
4178
  }
4175
4179
  if (currentSymbolicBranch === defaultBranch) {
4180
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4181
+ return null;
4176
4182
  return {
4177
4183
  reason: "on-default-branch",
4178
4184
  severity: "error",
@@ -4918,6 +4924,26 @@ var COMMANDS = [
4918
4924
  "Never reimplements or edits `init`/`init product`/`demo break`/`doctor` — it only calls their existing, unmodified entry points in sequence."
4919
4925
  ],
4920
4926
  status: "shipped"
4927
+ },
4928
+ {
4929
+ name: "release",
4930
+ description: "Run this repo's own publish sequence in one command, refusing to start unless every precondition holds",
4931
+ flags: [
4932
+ {
4933
+ flag: "--dry-run",
4934
+ description: "Run the preconditions only and print the plan; publishes nothing"
4935
+ },
4936
+ {
4937
+ flag: "--allow-any-commit",
4938
+ description: "Skip the check that HEAD's commit is a Version Packages commit"
4939
+ }
4940
+ ],
4941
+ details: [
4942
+ "Refuses unless HEAD is the default branch, the working tree is clean, HEAD equals `origin/<default>` (after `git fetch origin`), HEAD's commit subject starts with `Chore(release): Version packages` (unless `--allow-any-commit`), and `npm whoami` exits `0` — each its own refusal naming the fix.",
4943
+ "Then streams `bun install --frozen-lockfile`, `bun run build`, `bun run changeset:publish`, and `git push origin --tags` — a real push, so the repo's own generated pre-push hook sees it exactly as any other push would. After it, prints `npm view <pkg> version` for every tag now on HEAD, noting registry lag on `@attalabs/vinaya` (observed ~20 minutes) when it still shows the previous version.",
4944
+ 'This is the one procedure named in `apps/cli/specs/self-hosting.md`, "How the published version is produced" — publishing itself stays manual and human-triggered; this command only removes the hand-typed four-step recipe.'
4945
+ ],
4946
+ status: "shipped"
4921
4947
  }
4922
4948
  ];
4923
4949
  // ../../packages/sources/src/forge-adapter.ts
@@ -4157,8 +4157,12 @@ function isNewDiskStateFile(path, status) {
4157
4157
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4158
4158
  }
4159
4159
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4160
+ function pushesAnyBranchRef(pushRefs) {
4161
+ return pushRefs.split(`
4162
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4163
+ }
4160
4164
  function checkMainBranchRefusal(facts) {
4161
- const { currentSymbolicBranch, defaultBranch } = facts;
4165
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4162
4166
  if (currentSymbolicBranch === null)
4163
4167
  return null;
4164
4168
  if (defaultBranch === null) {
@@ -4170,6 +4174,8 @@ function checkMainBranchRefusal(facts) {
4170
4174
  };
4171
4175
  }
4172
4176
  if (currentSymbolicBranch === defaultBranch) {
4177
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4178
+ return null;
4173
4179
  return {
4174
4180
  reason: "on-default-branch",
4175
4181
  severity: "error",
@@ -4160,8 +4160,12 @@ function isNewDiskStateFile(path, status) {
4160
4160
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4161
4161
  }
4162
4162
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4163
+ function pushesAnyBranchRef(pushRefs) {
4164
+ return pushRefs.split(`
4165
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4166
+ }
4163
4167
  function checkMainBranchRefusal(facts) {
4164
- const { currentSymbolicBranch, defaultBranch } = facts;
4168
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4165
4169
  if (currentSymbolicBranch === null)
4166
4170
  return null;
4167
4171
  if (defaultBranch === null) {
@@ -4173,6 +4177,8 @@ function checkMainBranchRefusal(facts) {
4173
4177
  };
4174
4178
  }
4175
4179
  if (currentSymbolicBranch === defaultBranch) {
4180
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4181
+ return null;
4176
4182
  return {
4177
4183
  reason: "on-default-branch",
4178
4184
  severity: "error",
@@ -4918,6 +4924,26 @@ var COMMANDS = [
4918
4924
  "Never reimplements or edits `init`/`init product`/`demo break`/`doctor` — it only calls their existing, unmodified entry points in sequence."
4919
4925
  ],
4920
4926
  status: "shipped"
4927
+ },
4928
+ {
4929
+ name: "release",
4930
+ description: "Run this repo's own publish sequence in one command, refusing to start unless every precondition holds",
4931
+ flags: [
4932
+ {
4933
+ flag: "--dry-run",
4934
+ description: "Run the preconditions only and print the plan; publishes nothing"
4935
+ },
4936
+ {
4937
+ flag: "--allow-any-commit",
4938
+ description: "Skip the check that HEAD's commit is a Version Packages commit"
4939
+ }
4940
+ ],
4941
+ details: [
4942
+ "Refuses unless HEAD is the default branch, the working tree is clean, HEAD equals `origin/<default>` (after `git fetch origin`), HEAD's commit subject starts with `Chore(release): Version packages` (unless `--allow-any-commit`), and `npm whoami` exits `0` — each its own refusal naming the fix.",
4943
+ "Then streams `bun install --frozen-lockfile`, `bun run build`, `bun run changeset:publish`, and `git push origin --tags` — a real push, so the repo's own generated pre-push hook sees it exactly as any other push would. After it, prints `npm view <pkg> version` for every tag now on HEAD, noting registry lag on `@attalabs/vinaya` (observed ~20 minutes) when it still shows the previous version.",
4944
+ 'This is the one procedure named in `apps/cli/specs/self-hosting.md`, "How the published version is produced" — publishing itself stays manual and human-triggered; this command only removes the hand-typed four-step recipe.'
4945
+ ],
4946
+ status: "shipped"
4921
4947
  }
4922
4948
  ];
4923
4949
  // ../../packages/sources/src/forge-adapter.ts
@@ -4158,8 +4158,12 @@ function isNewDiskStateFile(path, status) {
4158
4158
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4159
4159
  }
4160
4160
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4161
+ function pushesAnyBranchRef(pushRefs) {
4162
+ return pushRefs.split(`
4163
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4164
+ }
4161
4165
  function checkMainBranchRefusal(facts) {
4162
- const { currentSymbolicBranch, defaultBranch } = facts;
4166
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4163
4167
  if (currentSymbolicBranch === null)
4164
4168
  return null;
4165
4169
  if (defaultBranch === null) {
@@ -4171,6 +4175,8 @@ function checkMainBranchRefusal(facts) {
4171
4175
  };
4172
4176
  }
4173
4177
  if (currentSymbolicBranch === defaultBranch) {
4178
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4179
+ return null;
4174
4180
  return {
4175
4181
  reason: "on-default-branch",
4176
4182
  severity: "error",
@@ -4158,8 +4158,12 @@ function isNewDiskStateFile(path, status) {
4158
4158
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4159
4159
  }
4160
4160
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4161
+ function pushesAnyBranchRef(pushRefs) {
4162
+ return pushRefs.split(`
4163
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4164
+ }
4161
4165
  function checkMainBranchRefusal(facts) {
4162
- const { currentSymbolicBranch, defaultBranch } = facts;
4166
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4163
4167
  if (currentSymbolicBranch === null)
4164
4168
  return null;
4165
4169
  if (defaultBranch === null) {
@@ -4171,6 +4175,8 @@ function checkMainBranchRefusal(facts) {
4171
4175
  };
4172
4176
  }
4173
4177
  if (currentSymbolicBranch === defaultBranch) {
4178
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4179
+ return null;
4174
4180
  return {
4175
4181
  reason: "on-default-branch",
4176
4182
  severity: "error",
@@ -4158,8 +4158,12 @@ function isNewDiskStateFile(path, status) {
4158
4158
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4159
4159
  }
4160
4160
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4161
+ function pushesAnyBranchRef(pushRefs) {
4162
+ return pushRefs.split(`
4163
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4164
+ }
4161
4165
  function checkMainBranchRefusal(facts) {
4162
- const { currentSymbolicBranch, defaultBranch } = facts;
4166
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4163
4167
  if (currentSymbolicBranch === null)
4164
4168
  return null;
4165
4169
  if (defaultBranch === null) {
@@ -4171,6 +4175,8 @@ function checkMainBranchRefusal(facts) {
4171
4175
  };
4172
4176
  }
4173
4177
  if (currentSymbolicBranch === defaultBranch) {
4178
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4179
+ return null;
4174
4180
  return {
4175
4181
  reason: "on-default-branch",
4176
4182
  severity: "error",
@@ -4159,8 +4159,12 @@ function isNewDiskStateFile(path, status) {
4159
4159
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4160
4160
  }
4161
4161
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4162
+ function pushesAnyBranchRef(pushRefs) {
4163
+ return pushRefs.split(`
4164
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4165
+ }
4162
4166
  function checkMainBranchRefusal(facts) {
4163
- const { currentSymbolicBranch, defaultBranch } = facts;
4167
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4164
4168
  if (currentSymbolicBranch === null)
4165
4169
  return null;
4166
4170
  if (defaultBranch === null) {
@@ -4172,6 +4176,8 @@ function checkMainBranchRefusal(facts) {
4172
4176
  };
4173
4177
  }
4174
4178
  if (currentSymbolicBranch === defaultBranch) {
4179
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4180
+ return null;
4175
4181
  return {
4176
4182
  reason: "on-default-branch",
4177
4183
  severity: "error",
@@ -4157,8 +4157,12 @@ function isNewDiskStateFile(path, status) {
4157
4157
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4158
4158
  }
4159
4159
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4160
+ function pushesAnyBranchRef(pushRefs) {
4161
+ return pushRefs.split(`
4162
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4163
+ }
4160
4164
  function checkMainBranchRefusal(facts) {
4161
- const { currentSymbolicBranch, defaultBranch } = facts;
4165
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4162
4166
  if (currentSymbolicBranch === null)
4163
4167
  return null;
4164
4168
  if (defaultBranch === null) {
@@ -4170,6 +4174,8 @@ function checkMainBranchRefusal(facts) {
4170
4174
  };
4171
4175
  }
4172
4176
  if (currentSymbolicBranch === defaultBranch) {
4177
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4178
+ return null;
4173
4179
  return {
4174
4180
  reason: "on-default-branch",
4175
4181
  severity: "error",
@@ -4169,8 +4169,12 @@ function isNewDiskStateFile(path, status) {
4169
4169
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4170
4170
  }
4171
4171
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4172
+ function pushesAnyBranchRef(pushRefs) {
4173
+ return pushRefs.split(`
4174
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4175
+ }
4172
4176
  function checkMainBranchRefusal(facts) {
4173
- const { currentSymbolicBranch, defaultBranch } = facts;
4177
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4174
4178
  if (currentSymbolicBranch === null)
4175
4179
  return null;
4176
4180
  if (defaultBranch === null) {
@@ -4182,6 +4186,8 @@ function checkMainBranchRefusal(facts) {
4182
4186
  };
4183
4187
  }
4184
4188
  if (currentSymbolicBranch === defaultBranch) {
4189
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4190
+ return null;
4185
4191
  return {
4186
4192
  reason: "on-default-branch",
4187
4193
  severity: "error",
@@ -4157,8 +4157,12 @@ function isNewDiskStateFile(path, status) {
4157
4157
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4158
4158
  }
4159
4159
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4160
+ function pushesAnyBranchRef(pushRefs) {
4161
+ return pushRefs.split(`
4162
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4163
+ }
4160
4164
  function checkMainBranchRefusal(facts) {
4161
- const { currentSymbolicBranch, defaultBranch } = facts;
4165
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4162
4166
  if (currentSymbolicBranch === null)
4163
4167
  return null;
4164
4168
  if (defaultBranch === null) {
@@ -4170,6 +4174,8 @@ function checkMainBranchRefusal(facts) {
4170
4174
  };
4171
4175
  }
4172
4176
  if (currentSymbolicBranch === defaultBranch) {
4177
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4178
+ return null;
4173
4179
  return {
4174
4180
  reason: "on-default-branch",
4175
4181
  severity: "error",
@@ -4915,6 +4921,26 @@ var COMMANDS = [
4915
4921
  "Never reimplements or edits `init`/`init product`/`demo break`/`doctor` — it only calls their existing, unmodified entry points in sequence."
4916
4922
  ],
4917
4923
  status: "shipped"
4924
+ },
4925
+ {
4926
+ name: "release",
4927
+ description: "Run this repo's own publish sequence in one command, refusing to start unless every precondition holds",
4928
+ flags: [
4929
+ {
4930
+ flag: "--dry-run",
4931
+ description: "Run the preconditions only and print the plan; publishes nothing"
4932
+ },
4933
+ {
4934
+ flag: "--allow-any-commit",
4935
+ description: "Skip the check that HEAD's commit is a Version Packages commit"
4936
+ }
4937
+ ],
4938
+ details: [
4939
+ "Refuses unless HEAD is the default branch, the working tree is clean, HEAD equals `origin/<default>` (after `git fetch origin`), HEAD's commit subject starts with `Chore(release): Version packages` (unless `--allow-any-commit`), and `npm whoami` exits `0` — each its own refusal naming the fix.",
4940
+ "Then streams `bun install --frozen-lockfile`, `bun run build`, `bun run changeset:publish`, and `git push origin --tags` — a real push, so the repo's own generated pre-push hook sees it exactly as any other push would. After it, prints `npm view <pkg> version` for every tag now on HEAD, noting registry lag on `@attalabs/vinaya` (observed ~20 minutes) when it still shows the previous version.",
4941
+ 'This is the one procedure named in `apps/cli/specs/self-hosting.md`, "How the published version is produced" — publishing itself stays manual and human-triggered; this command only removes the hand-typed four-step recipe.'
4942
+ ],
4943
+ status: "shipped"
4918
4944
  }
4919
4945
  ];
4920
4946
  // ../../packages/sources/src/forge-adapter.ts
@@ -4157,8 +4157,12 @@ function isNewDiskStateFile(path, status) {
4157
4157
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4158
4158
  }
4159
4159
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4160
+ function pushesAnyBranchRef(pushRefs) {
4161
+ return pushRefs.split(`
4162
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4163
+ }
4160
4164
  function checkMainBranchRefusal(facts) {
4161
- const { currentSymbolicBranch, defaultBranch } = facts;
4165
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4162
4166
  if (currentSymbolicBranch === null)
4163
4167
  return null;
4164
4168
  if (defaultBranch === null) {
@@ -4170,6 +4174,8 @@ function checkMainBranchRefusal(facts) {
4170
4174
  };
4171
4175
  }
4172
4176
  if (currentSymbolicBranch === defaultBranch) {
4177
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4178
+ return null;
4173
4179
  return {
4174
4180
  reason: "on-default-branch",
4175
4181
  severity: "error",
@@ -4915,6 +4921,26 @@ var COMMANDS = [
4915
4921
  "Never reimplements or edits `init`/`init product`/`demo break`/`doctor` — it only calls their existing, unmodified entry points in sequence."
4916
4922
  ],
4917
4923
  status: "shipped"
4924
+ },
4925
+ {
4926
+ name: "release",
4927
+ description: "Run this repo's own publish sequence in one command, refusing to start unless every precondition holds",
4928
+ flags: [
4929
+ {
4930
+ flag: "--dry-run",
4931
+ description: "Run the preconditions only and print the plan; publishes nothing"
4932
+ },
4933
+ {
4934
+ flag: "--allow-any-commit",
4935
+ description: "Skip the check that HEAD's commit is a Version Packages commit"
4936
+ }
4937
+ ],
4938
+ details: [
4939
+ "Refuses unless HEAD is the default branch, the working tree is clean, HEAD equals `origin/<default>` (after `git fetch origin`), HEAD's commit subject starts with `Chore(release): Version packages` (unless `--allow-any-commit`), and `npm whoami` exits `0` — each its own refusal naming the fix.",
4940
+ "Then streams `bun install --frozen-lockfile`, `bun run build`, `bun run changeset:publish`, and `git push origin --tags` — a real push, so the repo's own generated pre-push hook sees it exactly as any other push would. After it, prints `npm view <pkg> version` for every tag now on HEAD, noting registry lag on `@attalabs/vinaya` (observed ~20 minutes) when it still shows the previous version.",
4941
+ 'This is the one procedure named in `apps/cli/specs/self-hosting.md`, "How the published version is produced" — publishing itself stays manual and human-triggered; this command only removes the hand-typed four-step recipe.'
4942
+ ],
4943
+ status: "shipped"
4918
4944
  }
4919
4945
  ];
4920
4946
  // ../../packages/sources/src/forge-adapter.ts
@@ -4157,8 +4157,12 @@ function isNewDiskStateFile(path, status) {
4157
4157
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4158
4158
  }
4159
4159
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4160
+ function pushesAnyBranchRef(pushRefs) {
4161
+ return pushRefs.split(`
4162
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4163
+ }
4160
4164
  function checkMainBranchRefusal(facts) {
4161
- const { currentSymbolicBranch, defaultBranch } = facts;
4165
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4162
4166
  if (currentSymbolicBranch === null)
4163
4167
  return null;
4164
4168
  if (defaultBranch === null) {
@@ -4170,6 +4174,8 @@ function checkMainBranchRefusal(facts) {
4170
4174
  };
4171
4175
  }
4172
4176
  if (currentSymbolicBranch === defaultBranch) {
4177
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4178
+ return null;
4173
4179
  return {
4174
4180
  reason: "on-default-branch",
4175
4181
  severity: "error",
@@ -4462,12 +4468,17 @@ function defaultBranch() {
4462
4468
  function main() {
4463
4469
  const currentSymbolicBranchName = currentSymbolicBranch();
4464
4470
  const defaultBranchName = defaultBranch();
4471
+ const pushRefs = process.env.VINAYA_PUSH_REFS ?? null;
4465
4472
  const finding = checkMainBranchRefusal({
4466
4473
  currentSymbolicBranch: currentSymbolicBranchName,
4467
- defaultBranch: defaultBranchName
4474
+ defaultBranch: defaultBranchName,
4475
+ pushRefs
4468
4476
  });
4469
4477
  if (finding === null) {
4470
4478
  console.log(`${CHECK_NAME}: pass (branch: ${currentSymbolicBranchName ?? "(detached)"})`);
4479
+ if (pushRefs !== null && currentSymbolicBranchName !== null && currentSymbolicBranchName === defaultBranchName) {
4480
+ console.log(`${CHECK_NAME}: tag-only push — no refs/heads/* ref in this push, allowing it from the default branch`);
4481
+ }
4471
4482
  process.exit(0);
4472
4483
  }
4473
4484
  if (finding.reason === "on-default-branch") {
@@ -4157,8 +4157,12 @@ function isNewDiskStateFile(path, status) {
4157
4157
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4158
4158
  }
4159
4159
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4160
+ function pushesAnyBranchRef(pushRefs) {
4161
+ return pushRefs.split(`
4162
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4163
+ }
4160
4164
  function checkMainBranchRefusal(facts) {
4161
- const { currentSymbolicBranch, defaultBranch } = facts;
4165
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4162
4166
  if (currentSymbolicBranch === null)
4163
4167
  return null;
4164
4168
  if (defaultBranch === null) {
@@ -4170,6 +4174,8 @@ function checkMainBranchRefusal(facts) {
4170
4174
  };
4171
4175
  }
4172
4176
  if (currentSymbolicBranch === defaultBranch) {
4177
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4178
+ return null;
4173
4179
  return {
4174
4180
  reason: "on-default-branch",
4175
4181
  severity: "error",
@@ -4154,8 +4154,12 @@ function isNewDiskStateFile(path, status) {
4154
4154
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
4155
4155
  }
4156
4156
  // ../../packages/aeg-core/src/main-branch-refusal.ts
4157
+ function pushesAnyBranchRef(pushRefs) {
4158
+ return pushRefs.split(`
4159
+ `).map((line) => line.trim()).filter((line) => line.length > 0).some((line) => line.split(/\s+/)[2]?.startsWith("refs/heads/") === true);
4160
+ }
4157
4161
  function checkMainBranchRefusal(facts) {
4158
- const { currentSymbolicBranch, defaultBranch } = facts;
4162
+ const { currentSymbolicBranch, defaultBranch, pushRefs = null } = facts;
4159
4163
  if (currentSymbolicBranch === null)
4160
4164
  return null;
4161
4165
  if (defaultBranch === null) {
@@ -4167,6 +4171,8 @@ function checkMainBranchRefusal(facts) {
4167
4171
  };
4168
4172
  }
4169
4173
  if (currentSymbolicBranch === defaultBranch) {
4174
+ if (pushRefs !== null && !pushesAnyBranchRef(pushRefs))
4175
+ return null;
4170
4176
  return {
4171
4177
  reason: "on-default-branch",
4172
4178
  severity: "error",