Deploy Model

A multi-repo deploy is manifest-driven and orchestrated centrally. A service repo holds almost no deploy logic — it just calls into the ops repo, which owns the pipeline.

The flow

service-repo push
  → thin caller workflow (in the service repo)
    → ops-repo reusable deploy.yml  (workflow_call)
      → orchestrator (orchestrator/deploy.py)
        → reads the ops repo's platform.json, dispatches on deploy.strategy
          → smart IaC: tofu plan → apply ONLY on change
          → build
          → publish (e.g. S3 sync + CloudFront invalidate for static)

Who owns what

PieceLives inRole
Platform shapeops-repo platform.jsonDeclares services, strategies, IaC targets
Thin callerEach service repoMinimal workflow that calls the ops repo
Reusable workflow + orchestratorThe ops repoCheckout, credentials, and the deploy engine
Infrastructurethe infrastructure repo + per-repo ci/iacShared control plane + app-edge stacks

Smart IaC

The orchestrator plans the service's IaC stack with tofu plan -detailed-exitcode and only applies when there is an actual change (exit code 2); a no-change plan (exit 0) skips the apply. This keeps every deploy cheap and idempotent. Details in Orchestrator & Strategies.

Two deploy models

Monorepo (in-repo): foundry generate emits a self-contained pipeline + ci/scripts/ that run inside the same repo as the manifest. This is what a single-repo platform uses.

Multi-repo (orchestrated): the flow above, owned by the ops repo. Thin callers are generated from the central manifest by foundry generate callers; the reusable workflow and orchestrator engine are hand-maintained in the ops repo.