# Mechanization, bundled `scripts/`, stdlib Node, all support `--dry`

`release-pack.mjs` walks the cycle in two phases per invariant 3, `--mode
cut` / `--mode from-scratch` promote `[Unreleased]` (BOTH modes now, a
peer's hand-authored section must be promoted whichever mode cut it, and a
loud guard blocks the bump if any roster package still carries non-empty
`[Unreleased]` content after promotion), run Step 4f (pre-tag coverage
`--fix` with the authoritative F-N1 matcher), and stop at the release commit
(PR → merge); `--mode handoff` tags/publishes/deploys from post-merge main.
**`--go` is the standard invocation for an operator-initiated release**, the
single authorization covers the cycle; evidence blocks print as a log;
granular `--yes`/`--push`/`--publish` remain for cautious manual runs
(`--push` still refuses to auto-fire past any F-N1 finding). `--mode batch`
is not implemented, it hard-rejects with a pointer to
[recovery-paths.md](recovery-paths.md) §Scenario 2 rather than silently
misbehaving; a real batch tags each version at its own release-merge SHA,
which this single-version orchestrator has no model for. Pre-flight (Step 3)
runs the full 30-gate roster from the single shared list in
`scripts/gate-roster.mjs`, the same list [cut-procedure.md](cut-procedure.md)
§3.1 transcribes, so a subset run requires editing that one file, not two.

The pieces are callable alone: `bump.mjs` (version fields + the factory
`.mcp.json` `@adia-ai/mcp` pin, invariant 8), `promote-unreleased.mjs` /
`insert-stub.mjs` (CHANGELOG blocks), `tag-lockstep.mjs` (umbrella + one tag
per lockstep roster package: the set is derived from `PACKAGE_ROSTER`,
never a number written here: it went stale at 14 while the roster said 17,
17 went stale in turn when gh#1240 folded two planned MCP rows into one
(now 16), and 16 goes stale again the day the ADR-0048 shims retire; run
`--dry` to see the live list, `--delete` to move), `dispatch-publish.mjs`
(publish workflows; `--verify-triggered` re-dispatches missing AND dead, cancelled/failed/timed-out, runs, each gated behind an npm-registry check
so an already-published version is never re-dispatched (gh#763); `--after`
enforces batch ordering, dispatch output states what WAS done, never a
to-do phrased as if it hadn't fired), and `pr-bridge.mjs` (the two-phase
bridge: pushes the release branch, opens the PR, waits out CI, and merges
ONLY on all-non-fail-soft-checks-green + zero unresolved threads + no
changes-requested; any other state stops with evidence, §5.7 mechanized,
`--dry` to preview the decision). All fail fast outside a monorepo root
(`assert-monorepo-root.mjs`); fork-configurable via `--host` / `--scope`.
All 6 CLI scripts (plus `scripts/release/check-release.mjs`) have a
`selftest` mode wired into CI's Plugin bin selftests step; the 3 helper
modules (`assert-monorepo-root`, `gate-roster`, `package-paths`) are
exercised through the CLIs' selftests, not their own.

## Site deploy

Site deploy (Step 10 of the handoff) dispatches `deploy-site.yml` (its own
pre-flight verify → snapshot → hardened rsync → post-deploy verify →
auto-rollback), never a raw local `rsync`, H1 (forge-campaign gh#268
audit): the script had drifted into bypassing that pipeline even though this
doc already taught the workflow-dispatch path. The dispatch rides the
cycle's single authorization (`--go`, or `--publish` on a manual run); the
workflow's own GitHub environment gate is the human stop for the deploy
itself.
