@attalabs/vinaya 0.23.0 → 0.24.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.
- package/README.md +1 -0
- package/aeg-root/enforcement.md +1 -1
- package/aeg-root/roles/developer.md +1 -1
- package/dist/checks/bin/check-body-bare-digits.js +7 -1
- package/dist/checks/bin/check-branch-topology.js +27 -1
- package/dist/checks/bin/check-brief-shape.js +7 -1
- package/dist/checks/bin/check-changeset-coverage.js +19 -1
- package/dist/checks/bin/check-closes-n.js +27 -1
- package/dist/checks/bin/check-coherence.js +27 -1
- package/dist/checks/bin/check-dead-branch-push.js +7 -1
- package/dist/checks/bin/check-dispatch-readiness.js +27 -1
- package/dist/checks/bin/check-doc-coverage-push.js +7 -1
- package/dist/checks/bin/check-doc-coverage.js +7 -1
- package/dist/checks/bin/check-doctrine-no-procedures.js +7 -1
- package/dist/checks/bin/check-doctrine-portability.js +7 -1
- package/dist/checks/bin/check-evidence-fresh.js +7 -1
- package/dist/checks/bin/check-exec-bits.js +7 -1
- package/dist/checks/bin/check-first-push-dispatch.js +27 -1
- package/dist/checks/bin/check-issue-assignment.js +27 -1
- package/dist/checks/bin/check-main-branch-refusal.js +13 -2
- package/dist/checks/bin/check-no-disk-state.js +7 -1
- package/dist/checks/bin/check-pr-report-density.js +7 -1
- package/dist/checks/bin/check-quoted-command.js +38 -12
- package/dist/checks/bin/check-reader-resolvable-prose.js +38 -12
- package/dist/checks/bin/check-registry-gates.js +8 -2
- package/dist/checks/bin/check-retired-vocabulary.js +38 -12
- package/dist/checks/bin/check-review-gate.js +7 -1
- package/dist/checks/bin/check-single-plan-pr.js +7 -1
- package/dist/checks/bin/check-test-plan.js +7 -1
- package/dist/checks/bin/check-token-collection-wired.js +7 -1
- package/dist/checks/bin/check-token-report.js +7 -1
- package/dist/checks/bin/check-workspace-escape.js +17 -3
- package/dist/index.js +405 -98
- package/package.json +2 -2
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/anchored-region.ts +9 -4
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/blast-radius-domains.ts +7 -1
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/brief-render.ts +462 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/brief-validation.ts +405 -4
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/coherence-checks.ts +19 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/consumer-enumeration.ts +75 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/dispatch-gate.ts +44 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/doctrine-no-procedures.ts +94 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/doctrine-portability.ts +128 -5
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/gate-audience.ts +3 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/index.ts +25 -3
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/parse-token-report.ts +17 -5
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/pr-report-density.ts +116 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/premise-check.ts +35 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/quoted-command.ts +3 -3
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/review-gate.ts +116 -7
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/review-status.ts +200 -0
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/test-plan-gate.ts +25 -12
- package/studio-standalone/_node_modules/@attalabs/aeg-core/src/verdict-extraction.ts +70 -11
- package/studio-standalone/apps/vinaya-studio/web/.next/BUILD_ID +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/build-manifest.json +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/prerender-manifest.json +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.html +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/api/coherence/route.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/[root-of-the-server]__02a-3g8._.js +1 -1
- 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
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0053k9k._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0112h-k._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0o771t1._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0puovz5._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1hs0dcu._.js +1 -1
- 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
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1wc4-ip._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_03x_w6q._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0gvm3og._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0lwxg63._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1fqw88f._.js +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1n0cnq-._.js +1 -1
- 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
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_1vo08dj._.js +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/middleware-build-manifest.js +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/server/pages/500.html +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.json +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{0jer_drkl1e4r.js → 0zebsmmk1bvnb.js} +4 -4
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{20x-t0a0v129z.js → 1uogpj5w2n5ju.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{2ou_81ahqw31e.js → 3m1kgax7j2vgs.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/package.json +2 -2
- /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
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{5xy_GA7G5KRuE3HDumIiw → QY2GhiI47-765D9DHGy1I}/_buildManifest.js +0 -0
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{5xy_GA7G5KRuE3HDumIiw → QY2GhiI47-765D9DHGy1I}/_clientMiddlewareManifest.js +0 -0
- /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
|
|
package/aeg-root/enforcement.md
CHANGED
|
@@ -144,7 +144,7 @@ Every pull request, on open and on every push, re-runs the same checks in CI:
|
|
|
144
144
|
| doctrine-portability | Ever shipped doctrine that named a file only you could see? | ci | Re-checks that shipped doctrine doesn't cite a path that exists only in the authoring repository. | A cited path's top segment judged against an allow-list: the caller-supplied ships prefix itself (`<doctrineRoot>/`, `aeg-root/` by default — doctrine citing its own tree, wherever that tree actually lives for the caller, checked dynamically so a non-default `doctrineRoot` never falsely flags an adopter's own self-citations), plus a fixed set of static portable prefixes (`roles/`, `contracts/`, `skills/`, and adopter-owned `.github/`, `.vinaya/`, `.claude/`, `.git/`, `.husky/`) — an unclassified prefix is a finding, never a silent pass, so a never-seen author-repo directory (e.g. a build artifact under `apps/*/dist/`) cannot slip through the way it would under a deny-list. Zero I/O in `@attalabs/aeg-core`'s `checkDoctrinePortability`; the bin supplies the only I/O, reading `<doctrineRoot>/**` twice — once from the working tree, once from `BASE_SHA` (`origin/main` by default) via `git show`/`git ls-tree` — and reports only citations present in the former and absent from the latter, so day-one install of an unfixed backlog never turns every adopter's CI red. Registered in `coreCheckRegistry()`, report-only like `reader-resolvable-prose`/`retired-vocabulary`. **Line-scoped under `--diff-only`** (this task): a finding prints only when its own line falls inside a changed hunk of that file's diff against the base. A full sweep is unchanged and still reports every finding in the tree. The hunk parser lives in exactly one place — the shared diff-evidence helper both this and every sibling sweep call — never a second regex per check. Without this, a diff that touched one line of a long doctrine page reported that page's whole standing backlog as if this PR had caused it, and a real new finding was indistinguishable from inherited noise. Registered with `include: ['aeg-root/**/*.md']` (task 12) — pinning/documentation only today, since `runner.ts`'s `shouldSkip` returns before consulting `include` for a `scope: 'full'` entry (out of that task's surface); a future `runner.ts` change extending `--diff-only` skipping to `scope: 'full'` finds this already declared. | product | `apps/cli/src/checks/bin/check-doctrine-portability.ts` |
|
|
145
145
|
| doctrine-no-procedures | Ever pasted a copy-paste runbook into doctrine, and it rotted the moment the real command changed? | ci | Re-checks that doctrine (`roles/*.md`, `contracts/*.md`, …) describes commands rather than scripting them (task 9/task 10's rule). | A fenced code block anywhere under `<doctrineRoot>/**/*.md`, tagged with no language or an explicit `sh`/`bash`/`shell`/`console`, containing two or more lines that each start with a shell command word (`export`/`bun`/`gh`/`git`/`grep`/`sed`/`cat`/`diff`/`vinaya`) is a finding — a runbook, not an illustration. A block tagged with any other language (` ```ts `, ` ```json `, …) is never a candidate at all, however many command-word-shaped lines it contains — a two-line ` ```ts ` excerpt whose lines both start with `export` is TypeScript, not shell. Two further exemptions: a block sitting inside the existing `AEG:VENDOR-EXAMPLE` anchor pair (`tranche-model.md` §12's one sanctioned fenced home for a real command sequence), and any file under a `templates/` directory. Zero I/O in `@attalabs/aeg-core`'s `checkDoctrineNoProcedures`; the bin supplies the only I/O, walking `<doctrineRoot>` once. Unlike its sibling `doctrine-portability` above, this is **blocking, not baseline** (`severity: error`, exit 1 on any finding) — a fresh rule with an expected-zero corpus after this task's own sweep-and-fix, not a 200+-finding backlog needing a report-only rollout. Registered in `coreCheckRegistry()`. Registered with `include: ['aeg-root/**/*.md']` (task 12) — pinning/documentation only today, since `runner.ts`'s `shouldSkip` returns before consulting `include` for a `scope: 'full'` entry (out of that task's surface); a future `runner.ts` change extending `--diff-only` skipping to `scope: 'full'` finds this already declared. | product | `apps/cli/src/checks/bin/check-doctrine-no-procedures.ts` |
|
|
146
146
|
| No new on-disk state | Ever had a file and the forge disagree about the same fact? | ci | Blocks a diff that creates a new on-disk state file duplicating what the forge already derives. | No live per-tranche topology file appears under `aeg-root/tranches/` (a top-level file there fails on add or edit; under `completed/**` only genuinely NEW files are refused, so the legacy archives stay editable), and no new `*.tokens.md` appears anywhere — token figures live in the PR body, and the ledger row is appended post-merge. Status-aware (`git diff --name-status`), never fired by a deletion; a rename counts as an addition at its new path. The registered `no-disk-state` check runs the same `isNewDiskStateFile` predicate at ring 0 (the managed hooks) and here; this row names the standalone CLI shim of that gate. | product | `packages/aeg-core/bin/check-no-disk-state.ts` |
|
|
147
|
-
| workspace-escape | Ever deleted a package only to break a sibling's tests through a path nobody's dependency graph saw? | ci | Re-checks that no source file's constructed filesystem reference reaches outside its own workspace package, or points at a path that does not exist. | Every `readFileSync`/`readFile` call with a literal relative-path argument, and every `new URL('…', import.meta.url)`, across every `.ts`/`.tsx` file under `apps/*`/`packages/*` — resolved against the citing file's own directory and judged against its own two-segment workspace-package prefix (`apps/<name>`, `packages/<name>`), never against `import`/`require` module specifiers (the dependency graph already owns those). Zero I/O in `@attalabs/aeg-core`'s `findWorkspaceEscapes`; the bin supplies the only I/O, walking the working tree once for source content and once for the existence-check universe (files AND directories, so a reference to a directory itself resolves as existing). Registered in `coreCheckRegistry()` (task 17), report-only like `reader-resolvable-prose`/`retired-vocabulary`/`doctrine-portability` — this repo's own corpus
|
|
147
|
+
| workspace-escape | Ever deleted a package only to break a sibling's tests through a path nobody's dependency graph saw? | ci | Re-checks that no source file's constructed filesystem reference reaches outside its own workspace package, or points at a path that does not exist. | Every `readFileSync`/`readFile` call with a literal relative-path argument, and every `new URL('…', import.meta.url)`, across every `.ts`/`.tsx` file under `apps/*`/`packages/*` — resolved against the citing file's own directory and judged against its own two-segment workspace-package prefix (`apps/<name>`, `packages/<name>`), never against `import`/`require` module specifiers (the dependency graph already owns those). Zero I/O in `@attalabs/aeg-core`'s `findWorkspaceEscapes`; the bin supplies the only I/O, walking the working tree once for source content and once for the existence-check universe (files AND directories, so a reference to a directory itself resolves as existing). Registered in `coreCheckRegistry()` (task 17), report-only like `reader-resolvable-prose`/`retired-vocabulary`/`doctrine-portability`. `*.test.ts`/`*.test.tsx` files are excluded from the swept surface entirely (O2, found live 2026-09-04) — this repo's own corpus carried a genuine escape at `packages/sources/src/commands-router-coverage.test.ts` reaching into `apps/cli/src/index.ts`, structurally the same incident this check exists to catch, until that exclusion silenced it alongside `workspace-escape.test.ts`'s own fixture-string false positive; report-only severity stays regardless, since an ordinary (non-test) source file's constructed reference can still exist with nobody having triaged it yet. **Line-scoped under `--diff-only`** (this task): a finding prints only when its own line falls inside a changed hunk of that file's diff against the base. A full sweep is unchanged and still reports every finding in the tree. The hunk parser lives in exactly one place — the shared diff-evidence helper both this and every sibling sweep call — never a second regex per check. Without this, a diff that touched one line of a long doctrine page reported that page's whole standing backlog as if this PR had caused it, and a real new finding was indistinguishable from inherited noise. Registered with `include: ['aeg-root/**/*.md']` (task 12) — pinning/documentation only today, since `runner.ts`'s `shouldSkip` returns before consulting `include` for a `scope: 'full'` entry (out of that task's surface); a future `runner.ts` change extending `--diff-only` skipping to `scope: 'full'` finds this already declared. (this sweep's real domain is `apps`/`packages`, not `aeg-root` — named per the Issue's own list; harmless either way while `include` has no runtime effect on a `scope: 'full'` entry.) | product | `apps/cli/src/checks/bin/check-workspace-escape.ts` |
|
|
148
148
|
| changeset-coverage | Ever shipped a change to a published package and the release train never picked it up? | ci | Re-checks that a diff touching a published package's own shipped files also carries a changeset in the same diff. | For each member of `.changeset/config.json`'s `fixed` group, a changed path counts as SHIPPED iff it falls under that member's own `package.json` `files` allowlist, read live from every workspace member's own manifest — never a hardcoded path list, never a directory heuristic. A diff that hits at least one shipped path with no `.changeset/*.md` entry (excluding `README.md`) in the same diff is a finding, naming the shipped paths hit. The Changesets-release branch itself is exempt by construction (its diff IS the changesets being consumed) — the exemption reuses `@attalabs/aeg-core`'s own release-branch constant, the same one `body-bare-digits`'s Changesets-release exemption is keyed on, rather than inventing a second branch-name special-case; unlike that check it never live-fetches a PR author, since a report-only, always-exit-`0` check has no gate for a spoofed branch name to bypass. Written after four separate published-package pull requests, over the course of one day, each merged with no changeset and caught only by a human after the fact — the obligation existed in no enforceable place before this check: not in `roles/developer.md`'s deliverable conventions (now amended alongside this row), not in any registered check. Zero I/O in the pure predicate (`changeset-coverage-logic.ts`); the bin supplies the only I/O — reading the config, every fixed-group member's own manifest, and the diff. Registered in `coreCheckRegistry()`, report-only like `reader-resolvable-prose`/`retired-vocabulary`/`doctrine-portability`/`workspace-escape` above — graduating to a blocking check, and any waiver-label escape that would need, is a later, separately-dispatched decision once the false-positive rate is observed; v1 carries no such escape. | product | `apps/cli/src/checks/bin/check-changeset-coverage.ts` |
|
|
149
149
|
| quoted-command | Ever read a doc that quoted a command as "what runs today", and the command changed underneath it? | ci | Re-checks that a doc's explicitly marked quote of a command or config line still matches, verbatim, the file it names as its source. | Marker-based only, by explicit Principal decision, not inferred from command-looking spans: a doc opts a span in with `<!-- AEG:QUOTES-FILE:START:<path> -->…<!-- AEG:QUOTES-FILE:END -->` (the same invisible-on-render, code-blind HTML-comment idiom `anchored-region.ts` uses for its own seven PR/Issue-body fields, reusing that file's masking primitive rather than a second parser — but a distinct marker kind, since the cited path is free text, not one of that file's seven closed field names). An adopter-facing command in a README with no marker is instruction, not a claim, and is never flagged — inference over command-looking spans was explicitly rejected as the false-positive shape that gets a gate disabled. `findCitedQuotes` sweeps the identical `ships`/`reader-facing` corpus `reader-resolvable-prose` sweeps (never a second notion of "governed doc"); `evaluateCitedQuotes` then checks each marked span against its cited file's live content, wherever that file lives in the repo, naming both sides in a finding — what the doc claims, and which file no longer contains it verbatim. Zero I/O in `@attalabs/aeg-core`'s pure predicate (`quoted-command.ts`), corpus-tested against the real `aeg-root/**` tree; the bin supplies the only I/O. Ships with one real, live annotation (this very paragraph's own sibling callout above, quoting the CI invocation this page names) rather than only synthetic fixtures. Written after a measured incident: a CI invocation was pinned to a version in one pull request; this page quoted the pre-pin form verbatim as current fact; every registered check passed, `doctor` reported healthy, security passed, and a code-review agent found the drift only after independent re-derivation. Registered in `coreCheckRegistry()`, report-only like `reader-resolvable-prose`/`retired-vocabulary`/`doctrine-portability`/`workspace-escape`/`changeset-coverage` above — graduating to a blocking check is a later, separately-dispatched decision once the false-positive rate is observed against real adopter corpora; v1 carries no waiver escape. | product | `apps/cli/src/checks/bin/check-quoted-command.ts` |
|
|
150
150
|
| token-report | Ever seen a cost figure nobody could trace to a source? | ci | Re-checks that a PR's "Token report" section actually carries real numeric Tokens in/out figures, on any host this repo can prove is metering-capable. | The presence and shape of the `## Token report` block `roles/developer.md` requires every self-metering role to paste at turn-end — never whether the reported figures are TRUE, which CI structurally cannot recompute (the same bounded honesty this page's `evidence-fresh` row states for its own Group B). Runs `resolveMeteringCapability` (task 1's probe, `@attalabs/aeg-core`) fresh in THIS process; an incapable verdict (any reason) passes silently — the sanctioned operator-metered case, same discipline `token-collection-wired` above already applies. **Ring derived mechanically as `1`, not chosen** (a 2026-08-29 amendment correcting the original brief rationale's now-retracted ring-0/1 instruction): `requiresOpenPr: true` makes this check CI-only by `CoreCheckRing`'s own derivation rule, since the PR body it reads does not exist before a PR does. A probe that itself fails to run (an unexpected `exists`/`readFile` throw the probe does not catch) is never read as a clean incapable verdict: the bin lets it propagate uncaught, which the runner surfaces as `status: 'error'`, distinct from the `status: 'pass'` a real incapable verdict produces. | product | `apps/cli/src/checks/bin/check-token-report.ts` |
|
|
@@ -103,7 +103,7 @@ Items 3, 5, and 7 read live forge state. Item 6 checks the brief's own Step 0 te
|
|
|
103
103
|
|
|
104
104
|
**Worktree discipline.** Your brief's first pre-flight step (Step 0) is creating a worktree — do it before anything else. If dispatched by an automation layer, you work in the worktree it created at `.worktrees/task/<tranche>/<n>/`. If working manually, the brief's Step 0 gives you the `git worktree add … origin/main` command — run it and `cd` in. Never branch from a local checkout that may be behind.
|
|
105
105
|
|
|
106
|
-
**Commit per Part, push once.** Commit after each numbered Part in the brief — small, one logical change per commit, so the history reads as a narrative of how you approached the problem. Push exactly once, immediately before `pr create` — not after every commit.
|
|
106
|
+
**Commit per Part, push once.** Commit after each numbered Part in the brief — small, one logical change per commit, so the history reads as a narrative of how you approached the problem. Push exactly once, immediately before `pr create` — not after every commit. The pre-push hook itself runs the affected test suite (`bunx turbo test --affected`) on that one push and refuses it on failure; you do not additionally run it yourself per Part. A regeneration or evidence run (`vinaya pr report --write`/`--push`) passes `--force` to its own test invocation. A verdict binds to the head it judged; a push landing after the newest verdict's judged head voids it and is named as such: `vinaya review status` prints `push after verdict — re-review required`, and merge waits on a fresh review round.
|
|
107
107
|
|
|
108
108
|
**Opening the PR with a complete description.** The PR description must (1) **carry the report only** — `pr create` posts the brief as its own comment marked `aeg:brief`, once; that comment is the brief's permanent, durable home, and the Reviewer and Archivist read it there; (2) follow the canonical form in [§ PR body — canonical form](#pr-body--canonical-form) below — that section holds the verbatim copy-pasteable template, including the **exact `Tier:` field syntax** the `verify-docs` gate requires; (3) reference the task's Issue (`Closes #N`) so the merge auto-closes it. The description is not optional — the reviews depend on it. Opening the PR is itself the `in-flight → in-review` transition; you write no status field. **The body is authored once, at open.** After the PR is open, you never hand-edit it again — not to append a response to a review round, not to record a decision, not for any reason. Two writes are sanctioned after open, both machine-regenerated, never typed: the Evidence block, and one appended row in the Token report for a re-entry turn (see [§ Evidence is emitted, never typed](#evidence-is-emitted-never-typed)). Everything else a review round produces — your response to findings, re-run `[agent]` evidence, any disclosure the brief didn't anticipate — is a PR comment.
|
|
109
109
|
|
|
@@ -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",
|
|
@@ -4942,6 +4948,13 @@ function git(args) {
|
|
|
4942
4948
|
return "";
|
|
4943
4949
|
}
|
|
4944
4950
|
}
|
|
4951
|
+
function currentBranch() {
|
|
4952
|
+
return git(["symbolic-ref", "--quiet", "--short", "HEAD"]);
|
|
4953
|
+
}
|
|
4954
|
+
function defaultBranch() {
|
|
4955
|
+
const ref = git(["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"]);
|
|
4956
|
+
return ref.startsWith("origin/") ? ref.slice("origin/".length) : "";
|
|
4957
|
+
}
|
|
4945
4958
|
function toPosix(p) {
|
|
4946
4959
|
return p.split("\\").join("/");
|
|
4947
4960
|
}
|
|
@@ -4998,6 +5011,11 @@ function fixedGroupNames(root) {
|
|
|
4998
5011
|
}
|
|
4999
5012
|
function main() {
|
|
5000
5013
|
const root = repoRoot() ?? process.cwd();
|
|
5014
|
+
const current = currentBranch();
|
|
5015
|
+
const base = defaultBranch();
|
|
5016
|
+
if (current !== "" && current === base) {
|
|
5017
|
+
process.exit(0);
|
|
5018
|
+
}
|
|
5001
5019
|
const changedAbs = resolveChangedFiles();
|
|
5002
5020
|
if (changedAbs === null) {
|
|
5003
5021
|
emitCheckError({
|
|
@@ -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
|