Deployment Workflows
Wavemap separates CI, app/API CD, docs publishing, and infrastructure mutation because they answer different questions.
- CI asks whether a commit is healthy enough to merge.
- Deployed-dev CD asks whether the selected ref, artifacts, credentials, runtime config, cloud state, and smoke target can
run in the shared
devenvironment. - Docs CD asks whether the static docs artifact can build, publish, and smoke independently from the app/API runtime.
- Infrastructure mutation asks whether a cloud resource graph transition is safe to apply.
The CI workflow is .github/workflows/ci.yml. It does not mutate deployed infrastructure.
Current CI jobs:
verify_tooling_and_codedocs_buildi18n_verificationfe_unit_testsfe_e2e_smokedocker_builds
The docs build job validates the Starlight site. The i18n job keeps localization failures visible as i18n contract failures rather than browser smoke surprises.
Deployed-Dev CD
Section titled “Deployed-Dev CD”The deployed-dev workflow is .github/workflows/deploy-dev.yml.
Command contracts for this lane live in the Deploy Dev command reference.
Pushes to develop run the conservative deploy-endpoint profile:
- Preflight.
- Read-only cloud deploy plan.
- Backend and frontend image build/push.
- Runtime deploy through the Pulumi-modeled SSM document.
- Read-only database migration status.
- Migration-only repair when the deployed database is behind the deployed API image.
- Endpoint smoke.
- Last-good runtime release receipt with app image identity and final migration state.
- Rollback attempt plus endpoint smoke if a push-triggered runtime deploy or endpoint smoke fails before a migration ran.
Reviewed operator procedures for workflow dispatch recipe selection, host stop/wake recovery, runtime config readiness, runtime deploy, last-good release receipts, rollback, database reset, and media discrepancy reporting live in Runbooks.
Manual dispatch exposes recipe-first profiles:
| Profile | Purpose |
|---|---|
preflight | Local/repo checks without cloud mutation. |
cloud-plan | Cloud-aware non-mutating validation and dry-run planning. |
deploy-endpoint | App/API deploy with migration status/repair and endpoint smoke. |
deploy-seeded-browser | Deploy plus reset, seeded smoke, and browser smoke. |
deploy-media | Deploy plus reset, seeded smoke, media smoke, and browser media smoke. |
deploy-lifecycle | Deploy plus lifecycle-oriented wake/cold-start proof. |
custom | Explicit one-off stage combinations. |
Raw stage toggles should be treated as add-ons or custom controls. Operators should choose a named profile first.
CD Responsibility Flow
Section titled “CD Responsibility Flow”CD is layered so each actor has a narrow job and a visible mutation boundary. When a deploy behavior changes, update the layer that owns the behavior instead of hiding it in GitHub YAML or duplicating it across notes.
| Layer | Owns | Does Not Own |
|---|---|---|
| Human operator | Profile selection, approval of destructive or privileged operations, and interpretation of failure evidence. | Silent infrastructure mutation, implicit database reset, or unreviewed permission expansion. |
| GitHub Actions workflow | Trigger policy, selected ref checkout, environment protection, concurrency, job DAG, credential setup, permissions, artifacts, and summaries. | Business logic hidden in YAML, decrypted runtime secrets, infrastructure graph changes during routine app CD, or command semantics. |
Public wavemap CLI | Stable operator command routes and compatibility-script delegation. | Live cloud execution details or typed file-contract internals. |
| Shell wrappers | Live external execution, provider CLIs, wait loops, dry-run/--execute gates, GitHub output files, and operator process control. | Public route registration, long-lived architecture decisions, or untyped parsing that belongs in operations helpers. |
@wavemap/operations | Typed planning, validation, summaries, projections, helper file contracts, and no-cloud checks. | Public CLI dependency, direct routine cloud mutation, or provider credential ownership. |
| Pulumi stack | Cloud resource graph, stable outputs, runtime SSM document, IAM/resource contracts, DNS, ingress, registry, runtime host, media storage, and control plane. | Routine app release orchestration, image identity, database reset, smoke checks, or app-runtime rollback. |
| GitHub deploy role | Narrow OIDC-authenticated cloud permissions required by selected deploy jobs. | Direct EC2 stop/start outside approved controls, Pulumi updates, runtime secret decryption, or broad inventory authority. |
| Runtime host and instance role | Pull selected images, read /wavemap/dev/runtime/*, render env files and Compose, start containers, access media storage, and register with SSM. | GitHub orchestration, Route53 control-plane writes, host shutdown authority, deploy profile selection, or workflow summaries. |
| Smoke jobs | Prove selected deployed behavior and publish diagnosable failure evidence. | Repairing the app, mutating infrastructure, or silently relaxing failed gates. |
The routine app/API CD handoff is:
- GitHub Actions selects the ref and profile, then resolves immutable source and image identity.
- Preflight runs repo-local checks before cloud authentication or mutation.
- The cloud plan reads the current deployment contract, validates the cloud target, checks live runtime-config metadata, and prints dry-run deploy plans.
- Backend and frontend image jobs build or reuse immutable
sha-<full-git-sha>ECR tags. - Runtime deploy sends the Pulumi-modeled SSM document with selected image URIs and deployment identity.
- The runtime host reads SSM runtime parameters, renders env files and Compose, pulls images, and starts the app stack.
- Database status compares the live Drizzle ledger against the deployed API image migration journal.
- A typed database gate converts that status into
pass,migrate, orblock. - If the gate returns
migrate, CD runs pending migrations only, then rechecks status. - Endpoint smoke proves the public app, backend health/readiness, and i18n manifest after the database gate passes.
- A smoke-passing endpoint deploy records the last-good runtime release receipt with app image identity and migration state.
- Push-triggered endpoint deploy failures attempt app-runtime rollback to that receipt only when the run did not apply migrations.
- Optional recipe stages add destructive reset, seeded smoke, browser smoke, media proof, discrepancy reporting, or lifecycle proof only when selected.
Deployment contract migration status: app/API CD now has the repo-side contract-read path. Cloud jobs read
deployment-contracts/<pulumiStack>/current.json from the private artifact store, unwrap contract.values into the
existing planning surface, and no longer require a routine Pulumi Cloud token. The live run remains gated on applying
the app deploy role’s contract-read grant and publishing a refreshed contract that includes runtimeDeployment.executor.
The full store and actor map lives in the
operator store and access model: routine CD reads
deployment contracts, infra operators own Pulumi state, runtime secrets stay in SSM, and GitHub environment values remain
bootstrap selectors rather than cloud state.
The deploy-time secret boundary is:
GitHub Actions -> reads the sanitized deployment contract, runtime parameter references, image tags, and deploy metadata -> sends SSM command payloads that reference parameter pathsRuntime host -> reads /wavemap/dev/runtime/* from SSM Parameter Store -> renders env files and docker-compose.yml locally -> pulls selected images and starts containersGitHub workflow logs, summaries, and artifacts must not contain decrypted SecureString values or rendered env files.
Docs CD is a separate static lane. It reads the current sanitized deployment contract, builds the docs app, publishes
apps/wavemap-docs/dist through the docs deploy command, invalidates the docs CloudFront distribution, and smokes
docs.wavemap.app. It does not use Pulumi stack output reads during routine deploy, the EC2 app runtime, backend API,
database, wake path, app media bucket, app runtime release receipt, or deployed-dev rollback path.
When responsibilities change, update the matching reference:
| Change Type | Documentation Home |
|---|---|
| Public command, flag, compatibility script, wrapper owner, or default mutation posture | Wavemap CLI Command Reference |
| Pipeline order, actor responsibilities, profile shape, smoke-lane promotion, or CD secret boundary | This page |
| Reviewed manual procedure, recovery path, destructive reset, rollback, or discrepancy report | Runbooks |
| Pulumi graph transition, IAM/trust boundary, resource lifecycle class, or infrastructure mutation policy | Infrastructure Change Policy |
| Cloud API action, resource scope, actor trust boundary, evidence, or explicit automation non-goal | Cloud Automation Permission Review |
| Runtime value ownership, cost posture, control plane, or lifecycle boundary | Deployed Dev Environment |
| Docs hosting target, docs publish permissions, or docs smoke behavior | Docs Hosting |
Smoke Lanes
Section titled “Smoke Lanes”Smoke lanes are promoted by cost, destructiveness, and signal quality.
Command contracts for deployed-dev and docs smoke checks live in the Smoke command reference.
| Lane | Default Posture | What It Proves |
|---|---|---|
| Endpoint smoke | Routine branch-CD gate. | Frontend readiness, app entrypoint, backend health/ready routes, and i18n manifest availability. |
| Wake smoke | Manual/add-on. | The wake route and endpoint smoke still work when the host is already awake or can be woken. |
| Browser routing smoke | Manual/add-on candidate for promotion. | Non-destructive public route rendering and unauthenticated redirect behavior. |
| Database status | Manual/read-only operator check. | Live Drizzle ledger state matches, is behind, or is ahead of the deployed API image. |
| Database migrate | Manual/schema mutation with --execute. | Pending migrations can be applied without reset or seed when the DB is safely behind the image. |
| Database reset | Manual/profile-scoped and destructive. | Disposable deployed-dev data can be reset intentionally. |
| Seeded smoke | Runs after opted-in reset. | Seeded API and page routes work against known data. |
| Browser smoke | Manual/profile-scoped. | Seeded browser interactions work with useful Playwright artifacts on failure. |
| Media smoke | Manual/profile-scoped. | Upload/delete storage semantics and public media URLs work against real S3/CloudFront. |
| Browser media smoke | Manual/profile-scoped. | Browser-rendered media works through the deployed path. |
| Cold-start browser smoke | Manual lifecycle proof. | A deliberately stopped host serves the cold-start page, wakes, and returns to the original route. |
Broader E2E remains release-confidence expansion. It should not become the default deployed-dev gate until auth/session setup, seed ownership, cleanup, artifacts, and runtime timing are boring.
Docs CD
Section titled “Docs CD”The docs workflow is .github/workflows/deploy-docs.yml.
Command contracts for publishing and smoke live in the Deploy Docs and Smoke command reference sections.
It is independent from the app/API deploy workflow:
- Check out the selected ref.
- Prepare docs deploy cloud context.
- Read and validate the current deployment contract from the private artifact store.
- Build with
pnpm build:docs. - Publish
apps/wavemap-docs/distwithpnpm wavemap -- deploy docs publish. - Smoke with
pnpm wavemap -- smoke docs. - Write a docs deploy summary.
The docs workflow supports manual dispatch and path-filtered develop pushes. Changes under
apps/wavemap-docs/working-notes/** do not trigger docs publish by themselves.
Command Conventions
Section titled “Command Conventions”Prefer public pnpm wavemap -- ... route names in durable docs and operator notes:
pnpm wavemap -- --helppnpm wavemap -- deploy dev preflightpnpm wavemap -- deploy dev contractspnpm wavemap -- deploy dev cloud-planpnpm wavemap -- deploy dev diagnostics endpointspnpm wavemap -- deploy dev runtime deploypnpm wavemap -- deploy dev runtime rollbackpnpm wavemap -- deploy dev database statuspnpm wavemap -- deploy dev database migratepnpm wavemap -- deploy dev database resetpnpm wavemap -- deploy dev media discrepancy-reportpnpm wavemap -- deploy docs publishpnpm wavemap -- smoke devpnpm wavemap -- smoke docspnpm wavemap -- infra topology capturepnpm wavemap -- infra topology project-inventorypnpm wavemap -- infra topology project-graphpnpm wavemap -- infra topology project-diagrampnpm wm -- ... is available as a local shortcut. Root scripts such as pnpm deploy:dev:runtime, pnpm smoke:docs,
and pnpm infra:topology:capture remain compatibility aliases for humans, CI, and older notes; document them beside the
public route rather than as a separate command surface. The canonical public surface lives in the
Wavemap CLI Command Reference.
Relevant command reference sections: Deploy Dev, Deploy Docs, Smoke, and Infra Topology.
The public CLI routes to shell wrappers. Shell wrappers may call @wavemap/operations for typed planning, validation,
projection, summaries, and helper files. Shell keeps live external command execution and explicit mutation gates.
Workflow Design Rules
Section titled “Workflow Design Rules”- Keep behavior inside repo-owned commands where practical; let GitHub Actions orchestrate checkout, setup, environment selection, concurrency, permissions, and summaries.
- Use immutable image identity:
deploymentVersion = imageTag = sha-<full-git-sha>. - Keep branch-triggered deploys conservative until heavier lanes prove stable and low-noise.
- Keep docs deployment static and separate from the app/API runtime.
- Keep Pulumi updates outside normal app-release CD. Before any workflow performs
pulumi up, it must publish the preview summary contract and wait for explicit approval. - Keep Pulumi state backend migration in the infra-operator runbook path. Routine app/API and docs CD should keep reading deployment contracts, not Pulumi state, regardless of whether the current Pulumi backend is Pulumi Cloud or S3.