CI

Purpose and document map

This guide describes implemented workflows, job dependencies, artifacts, deployment-related jobs, and the limits of automated enforcement. Sections 1–13 define pipeline mechanics. Section 14 maps the Definition of Done to those mechanisms and manual review; it does not redefine completion policy.

Test-level selection belongs in test-strategy.md, local commands in development.md, container behavior in docker.md, and authoritative completion requirements in the Definition of Done.

Security-sensitive fixture configuration

The coverage and Docker validation jobs explicitly select local Mocking Bird mail with a synthetic fixture sender/recipient. Hosted staging migration jobs do not enable it or receive provider keys. No mail preview or capture is uploaded as a CI artifact.

Password-recovery browser tests use fixed per-project synthetic recipients and the local Mocking Bird preview only. APP_PUBLIC_URL is the host Vite origin in host E2E and the localhost gateway in Docker E2E. No real provider request, recovery preview, raw credential, trace, or successful-run Playwright report is persisted. Local success does not verify hosted sender/domain setup, TLS, provider acceptance, or inbox delivery.

The canonical Docker command explicitly enables the developer frontend build and local mock backend for the desktop/mobile console journey. Normal pnpm build retains the default disabled flag, and unit/route-variant tests separately prove the page declaration and backend endpoints are absent. Hosted migration configuration never receives the mock flags or synthetic recipients.

Authentication fixtures are synthetic and created only inside the guarded E2E lifecycle. CI does not contain a default production credential and does not provision users during backend startup or the normal database seed. Coverage and Docker jobs exercise the real PostgreSQL session/user path; the limiter remains intentionally in-process for this milestone. The only user provisioning in CI is the explicitly manual, non-gating staging-user-provisioning.yml dispatch described in §13.1.

1. Rules

CI is authoritative for automated gate outcomes. The Definition of Done combines those outcomes with acceptance and review evidence. Proportional local validation does not waive any CI gate. Never weaken, skip, or make checks non-blocking merely to obtain a green run.

2. Implemented workflows

Jobs select Node major 24 and pnpm 10.34.5, use checkout/frozen install, and invoke root package scripts. See §14 for exact enforcement and human-review limits. No application registry publishing or production deployment job exists.

3. Contract summary

Every validation job: checkout -> Node 24/pnpm 10.34.5 -> frozen install
Stage 1: quality | test | build | docs | supply-chain (parallel)
Stage 2: validate-docker | coverage (after all five stage-1 jobs pass)
Trusted main push only: staging-migrate (after all seven validation jobs pass)

The eight canonical checks include dependency installation, which is a step in each job rather than a separate status check. PRs require all seven validation jobs; trusted main runs additionally require migration success. Publishing is separate.

4. Canonical checks

Check Command Job
1. Install pnpm install --frozen-lockfile Every job
2. Static quality pnpm quality quality
3. Fast tests pnpm test test
4. Production build pnpm build build
5. Docker system pnpm validate:docker validate-docker
6. Coverage pnpm test:coverage coverage
7. Documentation pnpm docs:build docs
8. Supply chain pnpm security:dependencies + pnpm secrets:check + pnpm containers:lint + pnpm db:check supply-chain

4.1 Docker validation contents

validate:docker runs in its own foodtruck-order-validate Compose project on separate ports, so the containers/volume it removes are never the development ones; it then builds/starts with bounded readiness, runs backend real-database integration/API tests, then Docker-mode Chromium E2E. The exact root script/prerequisites are in Docker §6.8.

4.2 Test coverage mapping

test runs Node artifact-contract tests and frontend/backend fast suites, including OpenAPI route-surface tests. validate-docker exercises real repository/PostgreSQL, real HTTP integration, and the complete browser path through Compose. coverage reruns instrumented frontend and aggregate backend suites. build independently proves host production compilation; Docker separately compiles source into images.

4.3 Static checks

quality builds db to resolve backend/E2E type-aware lint imports, then runs formatting, ESLint, all-workspace typecheck, locale parity, tracked Markdown links, scoped spelling, architectural boundaries, tracked-file hygiene, mechanical specification traceability, and OpenAPI/TypeDoc documentation gates. All are offline; CI never reformats files. Scope/exclusions and 100-column Markdown wrapping are in development §13.

4.4 Complementary execution

Each ordinary test tier runs in its appropriate environment. Instrumented coverage intentionally repeats application suites to enforce the coverage policy. Dev-mode pnpm e2e, pnpm test:all, and extra per-workspace test invocations are not CI gates. Do not add redundant local reruns after canonical validation passes without a change or unresolved concern.

4.5 Strict coverage policy

The coverage job uses Vitest v8 and fails below 100% statements, lines, functions, or branches globally or per-file in configured frontend/backend sources. Backend coverage aggregates fast and real-database suites. Scope and justified exclusions are authoritative in testing §4.1. There is no db-package or E2E source-coverage threshold. Coverage does not replace acceptance tests or adequate assertions.

5. Dependencies and isolation

quality, test, build, docs, and supply-chain are independent after their own installation. coverage and validate-docker explicitly need all five jobs. This stage ordering is the actual workflow's fail-fast policy, not a technical dependency of Docker compilation on host outputs. staging-migrate needs all seven validation jobs.

Docker validation destroys its Compose project state and publishes ports; do not run two instances or coverage/database services sharing those ports on one runner. Actions jobs run on separate ubuntu-latest runners, so stage-2 jobs do not share Compose state or databases.

6. Environment

Every job selects the same toolchain and CI=true. CI also supplies the checked-out ${{ github.sha }} as the allowlisted public APP_BUILD_SHA, so host and Compose frontend builds display the same short revision. Only validate-docker creates a root .env from .env.example, installs Chromium with Linux dependencies, and starts the application through Compose. Its PostgreSQL is Compose-managed.

coverage uses a GitHub Actions postgres:17.5-alpine service and job-scoped local DATABASE_URL; it does not create a root .env or run application containers. The test helpers initialize dedicated databases. Static/test/build/docs jobs need no PostgreSQL. staging-migrate uses its separate Neon secret (§13).

Missing Docker, browser/system dependencies, unhealthy PostgreSQL, or inaccessible gateway/database fails the relevant job; it must not silently skip tests. The browser gateway preflight only checks reachability; assertions check the actual message workflow.

7. Reproducibility

Frozen installs reproduce lockfile resolutions. CI uses fresh checkouts and no configured artifact downloads or dependency/browser caches. Docker builds from source, excluding host outputs/configuration. Clean Compose volumes and dedicated test databases provide known data. No test retries are configured; application query retry is not a test retry. Playwright uses forbidOnly in CI and disables dev-server reuse; focused/skipped Vitest tests remain a review gap (§14).

Node is selected by major and base images by tags (node:24-alpine, nginx:1.29-alpine, postgres:17.5-alpine), not immutable digests/patches. Reproducible project inputs do not imply upstream tags or hosted runner images can never change.

Job timeouts are 10 minutes for quality/test/build/staging migration, 15 for docs/coverage, and 20 for Docker. Compose readiness is bounded at 300 seconds. Investigate failures rather than retrying until green; infrastructure reruns must still provide successful evidence for the delivered revision.

8. Failure and merge policy

Root commands propagate non-zero exits. Build preflight and upload failures also fail their producer jobs. Any failed validation job makes validation fail; missing/pending/cancelled required jobs are not successful. On trusted main pushes, staging migration must also succeed. An intentional PR migration skip is valid.

All seven validation jobs and their install steps are mandatory before merge. The recorded hosting constraint is private GitHub Free without private branch protection; there is no repository aggregate merge gate. Enforce the policy operationally by reviewing the revision/run and actual required conclusions. Hosted plan/settings were not independently re-audited by this documentation audit.

9. Artifact contract

9.1 Producing runs

Artifacts come from fresh checkouts, frozen installs, and canonical producing commands. Uploads persist outputs from that same run; artifact presence alone is not proof of overall CI success. Outputs are ignored locally and never committed.

9.2 Application output

pnpm build compiles db → frontend → backend. Before its first application upload, the build job requires these non-empty files:

packages/db/dist/index.js
packages/db/dist/index.d.ts
frontend/dist/index.html
backend/dist/server.js

It uploads separate db-dist, frontend-dist, and backend-dist artifacts from those workspace dist/ directories. Db includes compiled JS/declarations/source maps; frontend includes HTML/JS/CSS/assets; backend includes compiled server code. These are inspection/possible deployment inputs, not self-contained releases: backend runtime still needs production dependencies and the db package. E2E has no build output.

9.3 Docker images

Backend, frontend, database tooling, and gateway images are built and used by validate-docker and discarded with the runner. No image archive, registry push, release tag, or image deployment is performed. PostgreSQL is an upstream base image, not a project artifact.

9.4 Reports and documentation

No JUnit/Sonar report or coverage-percentage badge output is generated. Tests prove their assertions through exit codes; coverage additionally enforces thresholds.

9.5 Not deliverables

Dependencies/pnpm store, PostgreSQL volumes/test databases, local .env, browser binaries, TypeScript build caches, and Compose runtime logs are not successful build artifacts. Logs may be used as diagnostics without masking a failed check.

9.6 Out of scope

No application registry/package publishing, production environment provisioning, or application deployment exists in CI. Existing staging migration and Cloudflare documentation publishing are implemented exceptions, not future work. No OS/Node matrix or dev-mode E2E job exists.

9.7 Contract changes

The persisted set is the six successful outputs plus optional failure-only playwright-report. New persisted outputs require documenting purpose, producing command/path, naming, format, retention, failure behavior, and deployment versus inspection use. Production image publishing remains separately specified work.

9.8 Upload behavior

All uploads use actions/upload-artifact@v4 and seven-day retention. Successful build/docs/coverage uploads use if: success() and if-no-files-found: error. Docker diagnostics use if: failure() and if-no-files-found: warn: missing diagnostics do not add a failure, but upload-service errors still fail the step. Producer jobs have contents: read plus actions: write; other jobs keep read-only repository permissions. Uploads are blocking, not a replacement for their producing gates. Already uploaded files can remain available after a later step fails.

9.9 Verification and download

pnpm test:ci, included in pnpm test, checks artifact definitions/setup and runs the application preflight against complete/missing/empty fixtures. It does not contact the upload service. Hosted producer success establishes upload success; inspect changed artifact semantics as needed. Routine downloads are not required for every unchanged output.

In Actions → CI → run → Artifacts, the ZIP downloads contain each source directory's contents. Successful runs supply all six outputs until retention expires; failed Docker runs can supply diagnostics when files exist. Use the same revision/run's job conclusions and artifact evidence, not an older run or a workflow badge alone.

10. Workflow identifiers and badges

Required validation identifiers are quality, test, build, docs, coverage, and validate-docker; conditional release identifier is staging-migrate. README has native CI and documentation-publishing workflow badges only. There are no current per-job shields.io badges or percentage badges to maintain.

11. CI and Docker relationship

Docker validation is the full packaged-system check. Host install/quality/fast tests/ build/docs and coverage use native tooling; coverage's PostgreSQL is a service container. Tests run from the runner against the application, not inside runtime images. The Docker workflow leaves its stack running locally; ephemeral CI discards it with the runner. Sources and canonical commands are the same in both environments.

12. Documentation publishing

12.1 Gates

quality includes local Markdown links/spelling plus OpenAPI/TypeDoc checks; test includes OpenAPI route-surface and documentation-gate fixture tests; docs builds the complete site, runs pnpm docs:site:check, and only then uploads it for inspection.

12.2 Site generation

pnpm docs:build cleans docs-site/, generates Swagger UI and TypeDoc, and renders Markdown/raw sources. pnpm docs:site:check then requires the landing/project/API/reference/raw outputs and validates all generated local links/fragments. Tool choices and public API boundaries are authoritative in documentation.md. CI's docs artifact is not consumed by publishing.

12.3 Documentation coverage

TypeDoc validates missing comments, exports, and symbol links in configured public entry points (backend, frontend, and the shared db package; see typedoc.json). Validation warnings — the documentation-coverage gate — are treated as errors; warnings originating in third-party .d.ts comments reached through documented types (for example Redux Toolkit's) are advisory and do not fail the build. Redocly validates the OpenAPI document; its configured advisories are not equivalent to blocking errors (currently one accepted advisory: no 4xx on the fixed liveness reply GET /health, see redocly.yaml). Neither TypeDoc nor Redocly judges prose meaning or catches an untested response mismatch. Tracked Markdown and generated-site links are separate blocking checks.

Candidates to make mandatory (blocking) during the Phase 8 CI work, in rough order of value:

12.4 Route contract

The backend fast suite compares public Fastify methods/paths with OpenAPI and excludes implicit HEAD. CORS's runtime OPTIONS behavior is not a separately declared public operation. HTTP behavior/schema semantics require explicit API tests.

12.5 Cloudflare Pages

cloudflare-pages.yml runs quality → fast tests → complete docs build → wrangler Direct Upload to foodtruck-order-docs, with Node 24/pnpm 10.34.5/frozen install. Wrangler 4.131.1 runs via isolated pnpm dlx, not a workspace/global dependency. Only CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID repository secrets authenticate publishing. Workflow permissions are contents: read; no GitHub Environment is used. Main pushes and manual dispatch trigger it; production branch selection is explicitly --branch=main. Its Cloudflare project must have matching production-branch settings.

Cloudflare hosts generated static files and does not clone/build source. A concurrency group serializes uploads without cancelling active deployments. Earlier step failure prevents upload. Publishing is independent of the full CI validation workflow and does not explicitly wait for its Docker/coverage/build jobs; operational merge policy supplies prior validation. Manual dispatch must target a reviewed revision.

13. Neon staging migrations and Render deployment

The recorded external setup uses Render staging services linked to main with After CI Checks Pass, and a Neon staging database. This repository defines their migration gate, not provisioning or guaranteed hosted check-discovery behavior. Verify those external settings/results in the target systems before claiming them.

On a trusted push to refs/heads/main in adamrudolf-becube/foodtruck-order, staging-migrate waits for all seven validation jobs, installs the pinned tooling, and runs pnpm db:migrate. Only that step receives STAGING_DATABASE_URL as DATABASE_URL. PRs, feature pushes, and forks skip it and receive no staging secret. There is no pull_request_target, workflow_run, GitHub Environment, seed/reset, secret upload, or application deployment step.

Create/use a repository Actions secret, not an environment secret. Missing/invalid connection or migration failure fails the job. No build is needed for drizzle-kit. Staging seed is deliberately manual; migrated empty state can return message 404. Never bypass a failed migration with a manual application deployment.

The expected sequence is PR validation → trusted main validation → migration → all detected checks passing → Render deploys. Cloudflare's separately detected check can also affect Render readiness. Verify discovery/timing and served revision on a real run; local tests do not establish them. Manual Render deployment is outside this automatic contract.

Render service environment is also outside the GitHub workflow. Before deploying a revision that requires hosted sessions, configure the backend service with DEPLOYMENT_ENV=staging (or production), a stable provider-held SESSION_SECRET of at least 32 characters, and PUBLIC_BROWSER_ORIGIN set to the exact public frontend origin. Set CORS_ALLOWED_ORIGINS to that same origin when the frontend and backend origins differ. The repository's .env.example and the GitHub STAGING_DATABASE_URL secret do not populate Render environment variables. The complete runtime checklist, same-site domain requirement, and post-deploy cookie verification are in development §6.1.

Migration concurrency serializes the shared database without cancelling an active run; pending runs may be replaced and order is not FIFO. Keep migrations compatible with currently deployed code. CI neither serializes Render deployments nor rolls back a schema change if application deployment later fails. Target TLS, health/smoke, and upgrade compatibility follow development §6.1.

13.1 Manual staging user provisioning

staging-user-provisioning.yml provisions one permanent staging account on demand. It exists because the repository host cannot open a PostgreSQL connection to the hosted staging database (outbound TCP 5432 is blocked; only 443 and 22 are reachable), while GitHub-hosted runners have unrestricted egress. It is a transport for an existing repository command, not a new provisioning mechanism: the job runs the canonical pnpm user:create from development §"User provisioning", so validation, normalization, Argon2id hashing, and persistence stay exactly the same as a local run. tests/ci/staging-provisioning.test.mjs enforces its safety contract.

Trigger and behavior:

The repository operator supplies STAGING_USER_PASSWORD out of band. Rotate it after use, or prefer changing the account passphrase through /settings on the running application. Successful login still requires the hosted SESSION_SECRET, PUBLIC_BROWSER_ORIGIN, and APP_PUBLIC_URL values from §13; provisioning a row does not prove those.

14. Definition of Done validation mapping

This section is an enforcement map. The Definition of Done owns the requirements; each entry below identifies the command, workflow, artifact, or human review that supplies evidence and makes any automation gap explicit.

This maps the ten mandatory checklist items in the Definition of Done to the mechanisms present in the repository. It describes enforcement, not the outcome of a particular hosted run. The main workflow is .github/workflows/ci.yml; job identifiers below refer to that workflow.

Automatic means a configured command fails on a detected violation. Partial means automation checks only part of the requirement; review is still required. Human-only means the requirement needs judgment and has no acceptance gate in the repository. Local availability does not mean every command must be run locally for every change: follow the DoD's proportional validation rules. AI agents must still execute pnpm quality locally.

14.1 Every mandatory requirement

The item numbers identify the checklist's current order; descriptions identify what is being checked if the checklist is later reordered.

1. Implementation and applicable specification acceptance complete

2. Scope, architecture, technology stack, and significant decisions respected

3. Appropriate automated tests and bug regression coverage

4. Static quality, including local AI-agent execution

5. All required CI validation succeeds for the delivered revision

6. Required artifacts produced and persisted

7. Documentation/handover accurate; generated output excluded; migration files committed

8. Focused diff, no secrets/machine-specific prerequisites, reproducibility

9. No known unresolved blocking issues

10. Accurate completion report and explicit unverified checks

14.2 Concrete technical checks

These are the technical mechanisms behind the mandatory checklist, including checks that become relevant through its applicability guidance. Each command is available locally; only the documented affected subset needs local execution. All normal CI validation gates remain mandatory even for documentation changes.

Reproducible dependency installation

Formatting

Lint

Type checking

Unit/component/module and frontend integration tests

Real PostgreSQL integration tests

HTTP/API contract behavior

Browser E2E

Aggregate production code coverage

Production builds

Docker builds, startup, migrations/seed, and real communication

Dependency vulnerabilities

Secret material

Dockerfile quality

Migration consistency

OpenAPI and intentional public TypeScript documentation

Architecture boundaries and tracked repository hygiene

Mechanical specification traceability

Generated documentation site

Application/report artifact presence and uploads

Publishing and staging are event/scope-dependent additions, not extra local application test tiers. staging-migrate runs pnpm db:migrate only on trusted main pushes after validation; .github/workflows/cloudflare-pages.yml runs quality/fast tests/site generation and deployment on main/manual dispatch. Neither proves Render deployment timing, the served revision, or all target health/smoke criteria. Verify those externally when deployment is in scope (§13).

Periodic supply-chain diagnostics

The same weekly/manual workflow runs pnpm docs:links:external with bounded timeouts and one retry before inventory collection. HTTP 4xx targets are reported as broken; timeouts, rate limits, 5xx, DNS/TLS, and other provider/network failures are reported as unavailable. Either outcome fails that diagnostics execution visibly, but the command is absent from PR/offline quality.

14.3 Enforcement gaps and required manual evidence

The configured mechanical gates above have concrete commands/workflow steps. The following limitations remain; none is silently treated as automatically verified. A manual review/result in the DoD completion record is the current validation mechanism where automation is absent. This mapping adds no new gates and does not assert that the hosted repository settings were audited.

Architectural enforcement remains deliberately narrow

Hosted push-protection coverage is not assumed

Tracked generated-output policy is path-based

No automatic required-job merge enforcement in repository configuration

No semantic test-adequacy gate

Documentation semantic quality and external availability remain non-PR review

Clean-schema validation does not exercise every migration upgrade/runtime variant

No semantic documentation, handover, report, local-execution, or blocker-classification gate

For a review decision, record pass, fail, not applicable with reason, or not verified for the applicable checks and human reviews. Technical gaps remain visible even when manual evidence satisfies a particular change. Required missing verification cannot be converted to N/A. Once affected local checks have passed, obtain hosted results for the delivered revision rather than rerunning every available command manually.