# `independent-package-release.md`, class-B cut (independently-versioned package)

> Load when releasing a package versioned **independently** of the lockstep set, > excluded from the lockstep gate, no umbrella tag, no demo-site deploy.
> **Class B's current member is `@adia-ai/adia-plugins`** (gh#1133/gh#1160,
> ADR-0045), the npm-sourced marketplace manifest package, marked
> `lockstep: false` on the roster (`scripts/package-paths.mjs`). It's the
> first member since the 2026-07-15 policy change (operator ruling) that
> emptied the class: the Claude Code plugins under `packages/plugins/*`
> joined the lockstep after class-B independence let npm drift a full
> harness-reset behind the repo (npm served forge 0.1.3/factory 0.2.4
> pre-reset content while the rewritten estate sat unpublished at a
> regressed 0.1.0). This procedure covers both onboarding a NEW class-B
> package and a routine cut of an existing one (adia-plugins). The
> lockstep cut is [`cut-procedure.md`](cut-procedure.md).

## §Two release classes in one monorepo

| | Class A, lockstep cut | Class B, independent package |
| --- | --- | --- |
| Versioning | whole set bumps together | own version line per package |
| Lockstep gate | enforced (`check:lockstep` 10/10) | **excluded**, a class-B package is not listed in `check-lockstep.mjs`'s `PACKAGES` (`adia-plugins`, the current member, is filtered out via `lockstep: false`, `scripts/package-paths.mjs`) |
| Umbrella tag | `vX.Y.Z` + per-package | **none**, per-package tags only (versions differ) |
| `dist-tag` ordering | cross-package publish order matters | N/A |
| Demo-site deploy | yes | **none** |
| Publish trigger | tag-triggered per-package workflow | same mechanism (`.github/workflows/publish-adia-ui-{factory,forge}.yml`) |

Class B keeps the single-authorization model and the verify-against-the-registry discipline; it drops every lockstep-specific invariant.

## §New-package onboarding, the three trip-wires a FIRST cut hits

Substrate fixes (release scripts / lockfile / CHANGELOG), landed once as a normal PR on `main` *before* tagging:

1. **Lockfile.** A new workspace package absent from the lockfile blocks the pre-commit hook and fails the workflow's `npm ci`. Fix: `npm install --package-lock-only && git add package-lock.json`.
2. **Release-trip-wire package registry.** F-N1 validates each pushed tag against `scripts/release/check-release.mjs`'s hardcoded `PACKAGES` array; an unregistered tag fails the pre-push hook with `unknown package`. Fix: add `{ tagPrefix: '<name>', path: 'packages/plugins/<name>' }`.
3. **CHANGELOG bracket form.** The trip-wire requires Keep-a-Changelog `## [<version>]` headings; bare `## <version>` fails with "no `## [<version>]` entry".

## §Procedure

1. **Re-baseline**, `git fetch`; confirm the checkout isn't on a peer's branch.
2. **Land the package(s) on `main`** via PR; resolve every review thread before an admin-merge (this repo's merge gate).
3. **Pre-publish gate.** `npm run verify:plugins` (package.json ↔ plugin-manifest versions synced). Plugin-specific structural gates: `npm run audit:plugin-coupling` + `npm run check:factory-bare-repo` (ADR-0040, the plugin must work in a bare consumer repo, no monorepo coupling). The lockstep gate still passes for the lockstep set. First publish of a name: `npm view @adia-ai/<pkg> version` → `E404` means the name is free.
4. **Tag at the published commit**, `git tag <pkg>-vX.Y.Z` per package; versions differ; no umbrella. Dry-run the trip-wire over the tags (`node scripts/release/check-release.mjs <tags>`) before pushing. If the shared working tree is on a peer's branch, push tags from a throwaway worktree on `origin/main`.
5. **Push the tags** → the per-package workflows run `npm ci → verify:plugins → npm publish --access public`.
6. **Verify against npm, not the workflow:** `npm view @adia-ai/<pkg> version` returns the new independent version for each package. Watch runs (`gh run watch <id> --exit-status`), but the registry is the source of truth.
7. **Marketplace cut-over**, per ADR-0045 the distribution channel is the npm-sourced manifest package `@adia-ai/adia-plugins` (no public git marketplace repo: that shape was considered and rejected). Bump/publish the manifest package so its `marketplace.json` entries reference the new plugin versions; consumers add the marketplace from npm, never from a git repo.

## §Verify target

The published package(s) on the **npm registry** at their independent versions, plus the npm-published `@adia-ai/adia-plugins` manifest referencing the new versions (ADR-0045). NOT "all 10 lockstep packages + the demo site", that's class A.

## §Gotchas (from the first independent cut, factory v0.2.2 + forge v0.1.2)

- Never add the package to the lockstep gate or tag it with the umbrella.
- Publish-workflow YAMLs are token-bearing, keep `persist-credentials: false` on the checkout step, matching the established workflows' action-pinning.
- A marketplace repo's org + visibility is an **operator call**, a public `gh repo create` is outward-facing; confirm first, and run a secret-scan of full history before pushing any repo public.
