diff --git a/docs/superpowers/plans/2026-04-14-phase-1i-deploy.md b/docs/superpowers/plans/2026-04-14-phase-1i-deploy.md new file mode 100644 index 00000000..6734d4d0 --- /dev/null +++ b/docs/superpowers/plans/2026-04-14-phase-1i-deploy.md @@ -0,0 +1,106 @@ +# Phase 1I — Deploy pipeline + runbook contracts + +**Parent plan:** `2026-04-14-phase-1-foundation-master.md`, section "1I — Deploy pipeline + runbook contracts" +**Date:** 2026-04-14 + +--- + +## Overview + +This sub-plan delivers the deploy pipeline contracts: Docker images for both build targets (standalone SSR and remote static), health endpoint, graceful shutdown handler, CI workflow template, and an operational runbook. + +## Constraints + +- Do NOT modify `modern.config.ts` (middleware registration is a future integration step) +- Health and shutdown modules export factory functions, not auto-registered middleware +- Coexist with the existing ASP.NET `Dockerfile` and `Dockerfile.local` (write `Dockerfile.react` and `Dockerfile.remote`) +- Do NOT touch `ClientApp/`, ASP.NET files, or `wwwroot/` + +--- + +## Tasks + +### Task 1 — `Dockerfile.react` (standalone SSR image) + +**File:** `Dockerfile.react` (repo root) + +Multi-stage build: +1. Stage 1 (`deps`): Node 24 base, enable corepack pnpm, copy `package.json` + `pnpm-lock.yaml`, run `pnpm install --frozen-lockfile` +2. Stage 2 (`build`): copy `src/`, config files, run `pnpm build:standalone` +3. Stage 3 (`runtime`): Node 24 slim, copy `dist/standalone/`, entrypoint `node dist/standalone/index.js` + +**Exit gate:** File exists, valid Dockerfile syntax. + +### Task 2 — `Dockerfile.remote` (nginx static image) + +**File:** `Dockerfile.remote` (repo root) + +Multi-stage build: +1. Stage 1 (`deps`): same as Task 1 +2. Stage 2 (`build`): copy source, run `pnpm build:remote` +3. Stage 3 (`runtime`): `nginx:alpine`, copy `dist/remote/` to `/usr/share/nginx/html`, expose port 80 + +**Exit gate:** File exists, valid Dockerfile syntax. + +### Task 3 — Health endpoint (`src/server/routes/health.ts`) + +**Files:** `src/server/routes/health.ts`, `src/server/routes/health.test.ts` + +Export `healthMiddleware(options)` factory function that returns an Express-style `(req, res, next)` handler. The middleware: +- Pings the upstream API client on each request (with configurable timeout, default 5000ms) +- Tracks last successful ping timestamp +- Returns 200 `{ status: "ok" }` if last success < 60s ago +- Returns 503 `{ status: "degraded", reason: "upstream_unreachable" }` otherwise + +**TDD:** Tests mock ApiClient, verify 200/503 responses, verify timeout behavior. + +**Exit gate:** `pnpm test` passes health tests. + +### Task 4 — Graceful shutdown (`src/server/shutdown.ts`) + +**Files:** `src/server/shutdown.ts`, `src/server/shutdown.test.ts` + +Export `registerGracefulShutdown(options)` factory function that: +- Registers a SIGTERM handler +- On SIGTERM: calls `server.close()`, waits up to `drainTimeoutMs` (default 30000), flushes logger transport, exits with code 0 +- If drain times out, force-exits with code 1 + +**TDD:** Tests mock `process.on`, `server.close`, `logger` flush; verify shutdown sequence. + +**Exit gate:** `pnpm test` passes shutdown tests. + +### Task 5 — CI deploy workflow (`.github/workflows/deploy.yml`) + +**File:** `.github/workflows/deploy.yml` + +Template workflow triggered on push to `main`: +- Checkout, setup Node 24, install pnpm, `pnpm install --frozen-lockfile` +- Build both targets (`pnpm build:both`) +- Build Docker images (`Dockerfile.react`, `Dockerfile.remote`) +- Push to registry (placeholder) +- Deploy to testing environment (placeholder) + +**Exit gate:** File exists, valid YAML syntax. + +### Task 6 — Operational runbook (`docs/superpowers/phase-1/runbook.md`) + +**File:** `docs/superpowers/phase-1/runbook.md` + +Covers: +1. Incident response decision tree +2. Canary rollout procedure +3. Rollback procedure (auto + manual) +4. Health-check interpretation +5. Log query cookbook +6. Known-failure playbooks (6 scenarios) + +**Exit gate:** File exists, covers all required sections. + +--- + +## Verification + +After all tasks: +```bash +pnpm typecheck && pnpm lint && pnpm test +```