ADR-004: Deploy gating without branch protection
Status: accepted 7 September 2026; CI side implemented the same day. Amended by ADR-012 (optional local gate before launch).
Context
Section titled “Context”The repository is private on a GitHub plan without branch protection, so checks are informative only. One developer deploys through Laravel Forge, whose default Quick Deploy fires on every push regardless of CI.
Decision
Section titled “Decision”Do not seek branch protection. Instead, gate the deploy itself: Forge Quick Deploy is switched off, and a deploy job in the CI workflow runs only on pushes to main, only after quality, tests and audit pass, and calls the Forge deployment trigger URL held in the FORGE_DEPLOY_URL secret. A missing secret or a non-2xx answer fails the job.
Consequences
Section titled “Consequences”- A red push to
maindoes not deploy; a green one does. - The manual merge rule remains: do not merge a pull request with failing checks.
- Revisit branch protection if a second developer joins or the plan changes.
Amendment, 4 October 2026: a deploy ends when production runs the commit
Section titled “Amendment, 4 October 2026: a deploy ends when production runs the commit”A 2xx from the Forge trigger only means Forge accepted the request; the deploy can still fail on the server, or never run. After the trigger, both deploy jobs now poll the public /release endpoint until production reports the deployed commit, or a newer one that contains it, and fail after ten minutes otherwise (scripts/await-release).
The endpoint is public and returns only the commit SHA, read from the release’s git checkout when the configuration is cached at deploy. A token-protected variant was rejected: a commit hash of a private repository is low-risk information, and a token would add a secret to keep in step on both sides. Stage 3 follow-up S2-F06.
Amendment: storefront release (4 October 2026)
Section titled “Amendment: storefront release (4 October 2026)”The Astro storefront is a second process with its own build. /__storefront/release reports the commit it was built from. Once the repository variable STOREFRONT_RELEASE_URL points at it (any shop subdomain), the deploy jobs wait for it as they wait for Laravel’s /release, so a deploy that built Laravel but not the storefront fails instead of passing.