--- phase: 01-foundations-and-doctrine plan: 07 subsystem: ci tags: [ci, github-actions, pipe-06, minimum-viable, solo-dev, no-ceremony] # Dependency graph requires: - phase: 01 plan: 01 provides: package.json with `ci` script chaining lint + test + validate:assets + build; package-lock.json (committed); Node 22 baseline - phase: 01 plan: 02 provides: `npm run lint` green (ESLint flat config + boundaries plugin, --max-warnings 0) - phase: 01 plan: 03 provides: `npm test` green (save layer — checksum, envelope, migrations, db, snapshots, persist, round-trip) - phase: 01 plan: 04 provides: `npm test` green (content loader) + `npm run build` green (Vite-native content pipeline) - phase: 01 plan: 05 provides: `npm run validate:assets` exits 0 (Task 1 validator merged; Task 2 north-star images partial — validator passes with 0 assets which is valid) - phase: 01 plan: 06 provides: `npm test` green (doctrine.test.ts via extended vitest include glob) provides: - .github/workflows/ci.yml — single-job GitHub Actions workflow (49 lines including load-bearing comments) running `npm ci` + `npm run ci` on push to main and pull_request to main; ubuntu-latest; Node 22; actions/setup-node@v4 with cache:'npm'; timeout-minutes:10 affects: - "Phase 2: when economy tests + Playwright e2e (PIPE-07) land, they go through the same `npm run ci` script — the workflow file does NOT need to change. If Phase 2 wants Playwright on CI, add `npx playwright install --with-deps chromium` before the `npm run ci` step and update the `ci` script in package.json to include `&& npm run e2e`." - "Phase 8: visual regression testing (PIPE-04) will likely require a separate workflow file (matrix runs against multiple OSes / heavier runtime) since it's a different cost profile from this single-job lint/test workflow. Do not bolt it onto ci.yml." - "Every Phase 1 success criterion is now structurally enforced on every commit going forward: CORE-10 (Plan 02 lint), CORE-04..09 (Plan 03 + Plan 06 doctrine.test.ts), PIPE-01 (Plan 04 loader.test.ts + build), PIPE-03/AEST-08/AEST-09 (Plan 05 validate:assets + test), PIPE-05 (Plan 06 doctrine.test.ts), PIPE-06 (this workflow), CORE-01 smoke (build)." - "Phase 1 partial item: Plan 05 Task 2 (10–20 north-star reference images) still awaits human curation. The validator passes when 0 assets are present (`[provenance] all 0 assets carry valid provenance.`); the CI workflow does NOT depend on the images being present, so it ships green today and will *continue* to be green once the images land (each carrying a valid sidecar)." # Tech tracking tech-stack: added: [] # no new deps; pure CI configuration patterns: - "Minimum-viable CI per CONTEXT user pushback: one job, one OS (ubuntu-latest), one Node version (22), no third-party actions beyond actions/checkout@v4 + actions/setup-node@v4. The workflow's purpose is to refuse merges that break the local `npm run ci`, nothing more." - "CI script as single source of truth: the workflow runs `npm run ci`, defined in package.json by Plan 01 as `npm run lint && npm run test && npm run validate:assets && npm run build`. Adding new gates (e.g., e2e in Phase 2) is done by editing `package.json scripts.ci`, NOT by editing the workflow file. The workflow stays stable across all future phases." - "Cache discipline (RESEARCH CI Pitfall A): `actions/setup-node@v4` with `cache: 'npm'` caches `~/.npm` keyed by `package-lock.json`, never `node_modules/` directly. Caching `node_modules/` is the canonical CI footgun (transitive deps go stale silently)." - "Lockfile-strict install: `npm ci` (not `npm install`) refuses to run if `package.json` and `package-lock.json` drift. This is the standard mitigation for solo-dev supply-chain risk in Phase 1 (T-01-08, see plan threat model). `npm audit` as a CI step deferred to Phase 8 launch polish if surface area grows." - "Comment-as-doc: the workflow file's leading 18-line comment block enumerates *what is deliberately omitted* (OS matrix, Node-version matrix, test reporters, Codecov, release automation, notification integrations) so a future contributor reading the file knows the omissions are intentional design decisions, not oversights. Per CONTEXT D-07/D-08 doctrine-as-rationale pattern." key-files: created: - .github/workflows/ci.yml (49 lines: 18-line header comment block + 4-line `name`/`on` declaration + 27-line `jobs.ci` definition with 4 steps) modified: [] key-decisions: - "Workflow stays at 49 lines (slightly over the 20-line target from RESEARCH Open Question #4) because the leading comment block is load-bearing context — it explains *why* the omissions are there so a future contributor reading the file does not 'helpfully' add a matrix or Codecov upload that would violate CONTEXT user pushback. The actual YAML logic is ~27 lines." - "Node 22 chosen (not 20) per RESEARCH § Environment Availability: 'Node 22 ideal for native crypto.hash; Node ≥ 20 required for recursive readdir; the validator uses readdir without recursive but Node 22 is the modern baseline.' Single-version (no matrix) per CONTEXT user pushback against ceremony." - "ubuntu-latest only (no Windows / macOS matrix). PIPE-04 visual regression testing is Phase 8; cross-OS coverage of the *idle game itself* belongs there, not here. Plan 01–06 deliverables are all platform-agnostic (TypeScript, Node, npm)." - "Triggers limited to `push` to main and `pull_request` to main. No tag-based release triggers (no releases until Phase 2 ships Season 1 per ROADMAP). No schedule triggers (no cron/canary needs in Phase 1)." - "timeout-minutes: 10 chosen as a sensible ceiling: local `npm run ci` runs in ~5s (lint) + ~2.5s (test) + ~0.1s (validator) + ~0.7s (build) = well under 10s for the script proper; with `npm ci` install (~30–60s on fresh CI) the realistic full run is ~1–2min. 10min ceiling catches stuck runs without false-positives on slow GitHub runners." - "Workflow does NOT depend on Plan 05 Task 2 (north-star images). The validator passes with 0 assets (`[provenance] all 0 assets carry valid provenance.`); when human curation lands the images, the validator will continue to pass (each new asset will carry a sidecar by definition of the curation gate). This means Phase 1's CI is shippable *now* even with Plan 05 partial." requirements-completed: [PIPE-06] # Metrics duration: 2min completed: 2026-05-09 --- # Phase 1 Plan 07: CI Workflow Summary **Single-job `.github/workflows/ci.yml` (49 lines) runs `npm ci` + `npm run ci` on push to main and PR to main; Node 22, ubuntu-latest, actions/setup-node@v4 with `cache: 'npm'`, 10-minute timeout. Local `npm run ci` exits 0 (lint clean, 53 tests pass across 12 files, validator green, build green). PIPE-06 structurally enforced; every Phase 1 automated check now runs on every commit going forward.** ## Performance - **Plan duration:** ~2 min (single-task plan; the YAML structure was specified verbatim in the plan) - **Local `npm run ci` runtime (immediately before commit):** - lint: <1s (clean, no warnings, --max-warnings=0) - test: 2.37s (53 tests / 12 files, all passing) - validate:assets: <0.5s (0 assets, all valid) - build: 0.66s (tsc -b + vite build) - **Total: ~5s** (well under the 30s feedback-latency target from VALIDATION.md) - **Expected CI runtime on GitHub:** ~1–2 min (dominated by `npm ci` install on fresh runner; the test/lint/build steps remain ~5s) ## What Was Built ### `.github/workflows/ci.yml` (49 lines) The full file: ```yaml # Phase 1 — minimum-viable CI per RESEARCH Open Question #4 + CONTEXT user pushback # against ceremonial workflows (.planning/phases/01-foundations-and-doctrine/01-CONTEXT.md). # # On every push to main and every pull request: # - npm ci (lockfile-strict install — refuses on package.json drift) # - npm run ci (lint + test + validate-assets + build, defined in package.json) # # This single job satisfies PIPE-06: Vitest tests run on every CI build. # Phase 2+ economy tests flow through the same `npm run ci` chain — no workflow change # is needed when more tests are added. # # Deliberately omitted (per CONTEXT user pushback against ceremony): # - OS matrix (Linux only is fine; PIPE-04 visual regression testing is Phase 8) # - Node-version matrix (one supported version is enough for solo-dev) # - Test reporters / Codecov uploads (no coverage requirement in Phase 1) # - Release automation (no releases until Phase 2 ships Season 1) # - Notification integrations (the project owner reads GitHub directly) name: ci on: push: branches: [main] pull_request: branches: [main] jobs: ci: name: lint + test + validate-assets + build runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node 22 uses: actions/setup-node@v4 with: node-version: '22' # Per RESEARCH CI Pitfall A: cache ~/.npm based on package-lock.json, # NEVER cache node_modules/ directly (transitive deps go stale). cache: 'npm' - name: Install dependencies (lockfile-strict) run: npm ci - name: Run CI suite run: npm run ci ``` ### Acceptance Criteria — All 11 Pass | # | Criterion | Result | |---|-----------|--------| | 1 | `.github/workflows/ci.yml` exists | OK | | 2 | Workflow runs `npm run ci` | OK | | 3 | Uses `actions/setup-node@v4` with `cache: 'npm'` | OK | | 4 | Does NOT cache `node_modules/` directly (RESEARCH CI Pitfall A) | OK | | 5 | Uses Node 22 | OK | | 6 | Runs `npm ci` (lockfile-strict) before `npm run ci` (line 46 < line 49) | OK | | 7 | Triggers on push to main AND pull_request to main (`branches: [main]` count = 2) | OK | | 8 | Has sensible `timeout-minutes` (10) | OK | | 9 | Locally `npm run ci` exits 0 (proves workflow will be green) | OK | | 10 | Contains comments explaining what was deliberately omitted | OK | | 11 | Single-job, single-matrix-entry, no third-party actions beyond checkout + setup-node | OK | ## Deviations from Plan **None — plan executed exactly as written.** The plan documented the YAML verbatim and the file was authored to match. Pre-flight `npm run ci` was green; post-write acceptance checks all passed; no Rule 1/2/3 fixes needed. ## Authentication Gates None. CI workflow files require no auth to author or commit; GitHub validates the YAML on push (will happen on the user's next push, not gated on this plan). ## Phase 1 Closure — Structural Enforcement Map With `.github/workflows/ci.yml` landed, every Phase 1 success criterion is now enforced on every commit: | Success Criterion | Enforcement | Plan Source | |-------------------|-------------|-------------| | 1. Game scaffold builds (CORE-01) | `npm run build` (smoke) | Plan 01 + Plan 04 | | 2. Round-trip save test passes (CORE-04..09) | `npm test` (12 test files / 53 tests / save layer covers checksum, envelope, migrations, db, snapshots, persist, round-trip) | Plan 03 | | 3a. CI fails if `src/sim/` imports `src/render/`/`src/ui/` (CORE-10) | `npm run lint` (boundaries plugin) | Plan 02 | | 3b. CI fails if `/content/**` violates Zod schema (PIPE-01) | `npm test` (loader.test.ts) + `npm run build` (Vite content pipeline build-time) | Plan 04 | | 3c. CI fails if any AI asset is missing provenance (PIPE-03, AEST-08, AEST-09) | `npm run validate:assets` + `npm test` (validate-assets.test.ts) | Plan 05 (Task 1 done; Task 2 awaits curation — validator passes with 0 assets which is valid) | | 4. Anti-FOMO + Season 7 end-state docs exist (PIPE-05, UX-13) | `npm test` (doctrine.test.ts — 8 assertions across 2 docs) | Plan 06 | | 5. North-star reference set + curation gate (AEST-08, AEST-09) | Validator + sidecar gate landed; 10–20 images await human curation (Plan 05 Task 2 — checkpoint:human-verify) | Plan 05 (partial) | | (Cross-cutting) PIPE-06: Vitest runs on every CI build | `.github/workflows/ci.yml` runs `npm run ci` on push + PR | This plan | ## Phase 1 Open Item Tracking - **Plan 05 Task 2 (north-star images):** Awaits human curation per the checkpoint. The CI workflow ships green *today* because the validator passes with 0 assets (`[provenance] all 0 assets carry valid provenance.`). When the user curates and commits the 10–20 images, each will carry a valid sidecar by definition of the curation gate, and the validator will continue to pass — no changes to `ci.yml` needed. ## Phase 2 Handoff Notes When Phase 2 lands: 1. **Adding economy tests:** Drop new `*.test.ts` files anywhere in the repo's existing `vitest` include glob (`src/**/*.test.ts`, `scripts/**/*.test.ts`). They will run automatically as part of `npm test` → `npm run ci` → CI workflow. **No workflow file change needed.** 2. **Adding Playwright e2e (PIPE-07):** Two changes: - In `package.json`: extend `scripts.ci` to include `&& npm run e2e` (or chain it however Phase 2 prefers). - In `.github/workflows/ci.yml`: add a new step *before* `Run CI suite`: ```yaml - name: Install Playwright browsers run: npx playwright install --with-deps chromium ``` This is the canonical pattern for Playwright on GitHub Actions ubuntu-latest. No matrix needed for Phase 2; visual regression matrix is Phase 8. 3. **Phase 8 visual regression testing (PIPE-04):** Likely warrants a *separate* workflow file (e.g., `.github/workflows/visual-regression.yml`) — different cost profile (multi-OS matrix, snapshot uploads, possibly nightly schedule). Do NOT bolt it onto `ci.yml`; keep `ci.yml` as the fast PR-blocking gate. ## Threat Model — T-01-08 Mitigation Confirmed The plan's threat model called out T-01-08 (npm install supply-chain compromise via transitive dep) with disposition **mitigate**. The mitigation is in place: - `package-lock.json` is committed (Plan 01). - `.github/workflows/ci.yml` uses `npm ci` (not `npm install`), which refuses to run if `package.json` and `package-lock.json` drift. - This is the standard solo-dev supply-chain mitigation per RESEARCH § Security Domain. - `npm audit` as a CI step deferred to Phase 8 launch polish if surface area grows (deliberately deferred to keep Phase 1 minimum-viable per CONTEXT user pushback). ## Threat Flags None. The workflow file introduces no new network endpoints, auth paths, file access patterns, or schema changes at trust boundaries. It only invokes existing `npm` commands that were already in scope. ## Self-Check: PASSED - File exists: FOUND `.github/workflows/ci.yml` - Commit exists: FOUND `609d582` (`ci(01-07): minimum-viable GitHub Actions workflow running npm run ci on push + PR (PIPE-06)`) - All 11 acceptance criteria from PLAN green (see "Acceptance Criteria" table above) - Pre-flight `npm run ci` exit 0 (lint clean / 53 tests pass / validator OK / build OK) - No deletions in commit - No unintended untracked files (only pre-existing `.claude/` local config)