Manual release and merge-triggered cutover

Hosted GitHub Actions are currently unavailable because of the account's billing/spending limit. Use an explicit manual gate, not a fabricated successful Actions check. GitHub currently reports main as unprotected with no required status contexts; this is an observation, not permission to merge without review. Recheck enforcement if repository settings change.

Freeze and verify the candidate

  1. Pin the candidate commit/tree, lockfile and runtime. .nvmrc, the root engine declaration and the Actions workflow use Node22.23.2, matching the observed deployment runtime.
  2. Run npm run verify on a stable candidate. It covers common package checks, Main's Spell profile, browser/worker journeys and independent packed installations. Do not edit source while a proof consumes compiled packages from that source.
  3. Cover the other deployed combined-Main profiles with fresh profile-specific builds and compiled HTTP checks. Shared package/unit checks need not run three times just because the profile changes.
  4. Record commands, timestamps, results and any reused evidence in the PR. A narrow later correction requires affected checks again; never label an interrupted gate as one uninterrupted passing run.
  5. Obtain reviewer acceptance and hosted staging acceptance. Local fixtures do not certify Railway, real Auth0 callbacks, or actual provider credentials.

See verification and the review disposition.

Staging acceptance without operational effects

Use a separate staging Mongo server and volume instances, and a distinct deployment identity in the private access policy. Verify these boundaries before writes. Do not copy production process data or attachment contents into staging merely to test hosting; use controlled acceptance records/files.

For Main, set PROCESS_APP_CONFIG=applications/main/config/acceptance-no-effects.json in the staging build/runtime environment. This checked-in file contains public compilation aliases but no effect provisioning. Workflow HTTP, signing and derivation capabilities fail closed even if unrelated provider variables exist in the environment. Authentication can still call Auth0. The generic runtime is unchanged; this is explicit operational provisioning, not a test authentication bypass. Do not use this configuration for operational production workflows.

The ordinary staging worker must remain stopped while setup occurs. Test a controlled scheduler pass in a separate process only against the staging namespace. Never enable or replay operational effects as a side effect of a smoke test. Keep synthetic JWT fixtures local; hosted acceptance uses the real configured identity provider, not a publicly deployed synthetic signing authority.

Required evidence: hosted install/build/start, real login/callback at each selected origin, declared app/operator boundaries, anonymous/invalid-token denial, controlled file upload/read/denial, persistence after restart and controlled worker progression. Record anything that needs human login rather than guessing acceptance from a200 landing page.

Target service mapping

The current four-service mapping can be retained without creating a new platform service:

Railway serviceRole after cutoverBuild / start
process-platformMain operator UI + canonical authenticated API + file-volume ownernpm run build / npm start
Risk AssessmentIndependent Allocation Risk frontend, proxying canonical APInpm run build:packages && npm run build:app --workspace @processos/allocation-risk / npm run start --workspace @processos/allocation-risk
Spell RiskIndependent Spell frontend, proxying canonical APInpm run build:packages && npm run build:app --workspace @processos/spell-review / npm run start --workspace @processos/spell-review
Step RunnerExactly one worker on the canonical Mongo deploymentnpm run build:packages / npm run job:step

Frontend server proxies use PROCESS_BACKEND_ORIGIN; browsers continue using their own same-origin /api endpoints. Set each peer frontend's PROCESS_APPLICATION_ID to its declared application id (trusted list scope). Configure fixed PROCESS_AUTH_MOUNT_ID values on peer frontends and matching PROCESS_AUTH_MOUNTS on the backend. Auth0 must allow each frontend's callback/web/logout origin. Frontend public-base settings must be its actual origin, not localhost or another environment.

Only canonical backend/worker hosts need Mongo, deployment policy and provider capability bindings; peer frontends must not initialize an alternate backend or independent writable file store. Transfer reviewed referenced attachments to the canonical file volume at cutover. Preserve unresolved legacy bytes separately. Names and exact instance IDs belong in private operator evidence, not this handbook.

Private policy and keys

Put the reviewed access JSON in a private file on each relevant backend/worker host and set PROCESS_ACCESS_CONFIG to its path. Large policies should not be placed in a single oversized environment value. A persistent volume can hold the file under a separate configuration directory; keep it outside the attachment root and permission it0600. Verify its hash after transfer. Do not commit the real membership list or credentials. The policy is read lazily at runtime, not during build.

Provision two distinct persistent protected-effect keys where signing/derived handles are used. Services sharing protected handles need the same keyring. Supply secrets through protected runtime configuration, never CLI argument literals, documentation, logs or checked-in files. Verify presence and consistency without displaying values. The no-effects staging configuration does not establish production provider readiness.

Merge is the release trigger

Automatic GitHub deployment can stay configured if maintainers control the merge. Do not add a blanket disconnect/reconnect procedure merely because checks are unavailable. Instead, make merge the final release signal after the migration prerequisites are satisfied:

  1. Complete candidate review and staging acceptance; record the previous deploy/config IDs.
  2. Arrange a maintenance window and stop old web writers and worker. Keep the worker held/parked through the merge-triggered deployment until post-deploy checks pass; stopping an old worker alone is insufficient if a merge immediately starts a new worker deployment.
  3. Take final quiesced data and file backups. Convert/import the reviewed owned namespace and reconcile counts, definitions and attachment hashes while old writers remain stopped.
  4. Install reviewed policy, keys, service commands, API routing and file custody. Avoid accidental activation from configuration changes; configuration writes can themselves cause deployments.
  5. Merge the approved candidate only now. Observe each resulting deployment ID/status, not just command exit status. Staging and production auto-deploy independently; staging is not an automatic gate in front of production.
  6. Verify production login/read/write/file behavior and controlled progression while the ordinary worker remains held. Use only the explicitly approved test process or isolated acceptance namespace. Then release exactly one worker, explicitly accepting that eligible imported workflows can resume operational effects on its first tick; there is no synthetic-only grace period after release.

If these prerequisites are not complete, the PR may be code-review-ready but it is not yet time to click Merge. Holding auto-deploy is an alternative if maintainers later need to merge before the window, not a substitute for migration ordering. See cutover for create-only import and rollback. Code rollback cannot undo new database writes or external actions.