Release and recovery

TL;DR

A merge is a release. The deploy fast-forwards only, so a rollback is a revert commit rather than a reset, and a broken cron line aborts the deploy half applied.

There is no separate release step in CCC. Pushing to the acceptance branch deploys acceptance; pushing to the main branch deploys production. Everything below follows from that.

What a deploy does, in order

  1. Writes .env from the stored environment secret, overwriting what was there.
  2. Fetches and fast-forwards the checkout to the target branch.
  3. Copies the repository's cron file into the shared cron directory and installs the combined crontab for all applications on the host.
  4. Brings the infrastructure containers up, then the CCC container, waiting for it.
  5. Runs app/jobs/install/install.php inside the container.

The script runs with set -e, so the first failure stops everything after it.

So a merge also reinstalls the schedule and regenerates the permission matrix. Neither is a manual step on acceptance or production. A new page's permission row and a new job's cron line arrive with the deploy.

Symptom: the deploy landed half applied

A broken line in .cron/ccc.cron fails the crontab installation, and set -e aborts there: after the code was pulled, before the containers restarted and before the installer ran. The running application is on the old containers with the new code on disk, and the permission matrix has not been regenerated. Only the red workflow run says so.

Repair: fix the cron line and deploy again.

Verify: the workflow run is green, the containers restarted, and a page that needed the installer loads instead of returning 401.

Symptom: a new page returns 401 after a release

The installer did not run, or the page file was not on disk when it did. The permission matrix is generated by scanning app/http/ at install time. Re-run the deploy, or on test run the installer by hand.

Rolling back

The deploy pulls --ff-only, so a rewritten history will not deploy. Resetting a branch back to an earlier commit and force-pushing does not roll production back: the pull refuses and the deploy fails.

A rollback is a revert commit, merged forward the same way a change is. That also keeps acceptance and production on the same line of history, which is what makes the next promotion safe.

Promotion is wholesale

The acceptance branch promotes everything sitting on it, not only the change you meant to release. To hold something back, revert it on acceptance rather than cherry-picking around it.

What a release does not undo

An existing-table migration is not performed by the deploy. install.php describes a clean install and does not migrate, and CREATE TABLE on an existing table fails silently. Migrating an existing environment is hand-run SQL, before the code that depends on it is deployed. A release that assumes otherwise looks successful and writes nothing. See missing or stale data.

A paused cron stays paused. The installer never re-seeds the active column, by design, so a deploy cannot resume a step someone switched off.

Data already produced is not rebuilt. A code revert does not undo live rule edits in the database, and reverting a seed does not alter rules on an existing installation. Restore the intended live configuration deliberately, then rebuild affected dates and order totals. See recalculate margin.

Evidence

The deploy steps and their order from ccc/workflow.sh in the ocean1-infra repository, and the --ff-only pull in it. The half-applied failure mode, the permission matrix scan and the never-re-seeded active column from AGENTS.md, Deployment, Authorization and Jobs and the batches queue. The clean-install nature of install.php from AGENTS.md, Development flow step 8. Basis: code-checked against the current implementation.