# Release, provenance, and rollback

Lycato releases are built only by [the protected release workflow](https://github.com/axysar/lycato/blob/main/.github/workflows/release.yml).
It runs the repository's canonical `ci:release` plan once, seals the compiled packages and public
Hosting tree as a commit-addressed artifact, and allows publication or deployment only after that job
passes. Local commands are useful rehearsals, not an alternate production release path.

## One-time environment setup

Create two protected GitHub environments with required reviewers and deployment-branch/tag rules:

- `npm-production` for package publication. Store `NPM_TOKEN` there until every package uses npm
  trusted publishing; the job also grants only the OIDC `id-token: write` permission needed for npm
  provenance.
- `firebase-production` for Firebase. Configure `GCP_WORKLOAD_IDENTITY_PROVIDER`
  and `GCP_FIREBASE_SERVICE_ACCOUNT` for a Google service account restricted to
  Auth-provider deployment, Firestore/Storage rule publication, and the
  `lycatoprod` Hosting target. The environment URL is `https://lycato.app`; use
  `docs/deployment/PRODUCTION_DOMAIN.md` for Firebase/Cloudflare validation. Do
  not store a service-account JSON key or Firebase refresh token.

Both workflows default to `contents: read`, use commit-pinned actions, disable checkout credential
persistence, pin pnpm 11.1.1, bound every job with a timeout, and serialize releases by Git ref.

## Cut a release

1. Add a changeset for every publishable package whose behavior changed.
2. On a release branch, run `pnpm exec changeset version`, then `pnpm install --lockfile-only` so
   package versions, internal dependency ranges, changelogs, and the lockfile move together.
3. Run `pnpm run ci:release`. Review the package manifests and generated changelogs, then merge the
   version commit to `main`.
4. Create and push a new signed `v*` tag at that exact commit. Never move or reuse a release tag.
5. Approve the `npm-production` and `firebase-production` jobs after the verification job is green.

The npm job downloads the compiled outputs from the verified job and runs Changesets with
`NPM_CONFIG_PROVENANCE=true`. The Firebase job downloads the same verified artifact, reruns the public
tree allowlist/link check, authenticates with short-lived Workload Identity Federation credentials,
and deploys Auth providers, Firestore/Storage rules, and Hosting without rebuilding.
`firebase.json` intentionally has no `predeploy` build hook: a build at deployment time would replace
the artifact that passed the release gate.

After publication, confirm the npm registry shows provenance for each new version and that package
metadata links to `https://github.com/axysar/lycato`. Confirm the Firebase release points at the
expected Git commit and run the public Hosting smoke check against production before announcing it.

## Roll back safely

Published npm versions are immutable. Do not unpublish a normal bad release and never overwrite its
tag. Instead:

1. deprecate each affected version with a precise warning;
2. move the `latest` dist-tag back to the last known-good version if new installs are unsafe;
3. fix forward, add changesets, and publish a new patch version through the protected workflow.

For Hosting, select the last known-good release in Firebase Hosting's release history and use its
rollback control, then rerun the production smoke check. Keep the failed GitHub artifact and CI
summary for diagnosis. Correct the source and deploy a newly verified artifact; do not rebuild or
edit the rolled-back artifact in place.

If package and Hosting behavior must remain coupled, roll back Hosting first (the reversible edge),
restore safe npm dist-tags second, and then ship a coordinated forward fix. Record the affected
versions, artifact SHA, operator, timestamps, and verification result in the incident or release log.
