ADR-0004: GitOps everything; promotion is a platform-raised PR¶
Status: Accepted — product brief (2026-06); confirmed 001-walking-skeleton Principles served: VI Source: docs/design.md D3; specs/001-walking-skeleton/research.md D-3; .specify/memory/constitution.md Principle VI
Context¶
Promotion from sandbox to production needs an approval step whose audit trail is provable, performed by users who never touch git. The platform itself — including its policy baseline — also needs a delivery path that is versioned and reviewable. Introducing a bespoke approval workflow would create a second audit system beside git.
Decision¶
Argo CD reconciles everything from git: application code, sandbox and production environments, and platform and policy-baseline updates. A promotion request is a pull request from the sandbox track to the production track in the app's repo, raised by the platform when the user clicks promote, with the evidence record attached by automation. Approval is the merge by the responsible approver, captured in git history and the attestation chain. Merge = deploy. No separate workflow engine is introduced in v1.
Alternatives considered¶
- A custom approval/workflow service — rejected: reinvents git review and breaks the git-native audit trail.
- Argo Rollouts gating — rejected: heavier than needed.
Consequences¶
The approval audit trail is git-native and one delivery path serves both
apps and the platform itself. Promotion requests route to the
platform-approver group in Keycloak (mapped to the Gitea PR reviewer
group); the PR reflects the snapshot taken at promote time, so iteration
continues while a promotion is pending. Two recorded tensions follow from
this decision: cost-cap suspension deliberately deviates from the
git-commit path (ADR-0012), and dedicated promotion tooling (Kargo) stays
deferred until real multi-environment needs appear (ADR-0015). The loop is self-hosting from a
fresh cluster: hack/seed-gitea.sh installs Gitea directly, pushes the
platform repo into it, and registers the Argo CD credential once, after which
the app-of-apps adopts those same manifests and reconciles everything. The
bootstrap gap recorded when this ADR was written is closed.