Files
josh 39563f6934 docs(01): plan phase 1 — 7 plans across 3 waves, verified after 1 revision
Wave 1: Plan 01 (scaffold + test infra)
Wave 2: Plans 02 (eslint firewall), 03 (save layer), 04 (content pipeline),
        05 (asset provenance — autonomous:false human-curate checkpoint),
        06 (doctrine docs)
Wave 3: Plan 07 (CI workflow)

All 16 Phase-1 REQ-IDs covered. Plan-checker found 4 blockers + 6 warnings
on first pass; revision iteration 1 landed all 10 fixes; iteration 2
returned VERIFICATION PASSED. Two orchestrator judgment calls during
revision: (1) implement CORE-04 localStorage fallback in Phase 1 (the
literal requirement and ROADMAP success criterion #2 both call for it),
(2) reclassify STRY-09 as vacuously satisfied in Phase 1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 23:09:08 -04:00

217 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 01
plan: 07
type: execute
wave: 3
depends_on: [01-01, 01-02, 01-03, 01-04, 01-05, 01-06]
files_modified:
- .github/workflows/ci.yml
autonomous: true
requirements: [PIPE-06]
must_haves:
truths:
- "A GitHub Actions workflow at `.github/workflows/ci.yml` runs `npm ci` (lockfile-strict install) then `npm run ci` (lint + test + validate-assets + build) on every push to main and every pull request"
- "The workflow uses Node 22.x (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)"
- "The workflow uses `actions/setup-node@v4` with `cache: 'npm'` (per RESEARCH § CI Pitfall A — never cache `node_modules/` directly)"
- "The workflow runs successfully on the post-Plan-06 codebase, verifying that PIPE-06's contract (Vitest covers all save migrations and runs on every CI build) is enforced as the CI job"
artifacts:
- path: .github/workflows/ci.yml
provides: "Minimum-viable CI workflow per RESEARCH Open Question #4 (~20-line) — single job, single matrix entry, npm ci + npm run ci"
contains: "npm run ci"
key_links:
- from: .github/workflows/ci.yml
to: package.json scripts.ci
via: "Workflow runs `npm run ci` which is `npm run lint && npm run test && npm run validate:assets && npm run build`"
pattern: "npm run ci"
---
<objective>
Ship the minimum-viable CI workflow per RESEARCH Open Question #4: a single GitHub Actions YAML at `.github/workflows/ci.yml` that on every push to `main` and every pull request runs `npm ci` (lockfile-strict install) followed by `npm run ci` (lint + test + validate-assets + build). This wires Plans 0206's automated checks into a single CI job. Per CONTEXT user pushback against ceremonial workflows: NO matrix builds across OSes, NO matrix builds across Node versions, NO test reporters, NO release automation, NO Codecov uploads — just one job that goes green or red.
PIPE-06's contract — "Project ships unit tests (Vitest) covering all save migrations and core economy formulas, run on every CI build" — is satisfied because Plan 03 authored the migration tests and `npm run ci` runs them. Phase 1 has no economy formulas yet (Phase 2 deliverable), so PIPE-06 in Phase 1 is "the CI runs Vitest on every PR/push." Phase 2+ will add economy tests that flow through this same workflow.
Purpose: This is the structural enforcement of PIPE-06 and the closing arc of Phase 1 — Plans 0106 all ship their own automated checks; this plan ensures those checks actually run on every commit going forward.
Output: One YAML file. Wave 3 by definition because it can only land after Plans 02 (lint), 03 (test), 04 (build), 05 (validate-assets), 06 (doc-lint) are all green — running before any of those would fail.
</objective>
<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/01-foundations-and-doctrine/01-CONTEXT.md
@.planning/phases/01-foundations-and-doctrine/01-RESEARCH.md
@.planning/phases/01-foundations-and-doctrine/01-01-SUMMARY.md
@.planning/phases/01-foundations-and-doctrine/01-02-SUMMARY.md
@.planning/phases/01-foundations-and-doctrine/01-03-SUMMARY.md
@.planning/phases/01-foundations-and-doctrine/01-04-SUMMARY.md
@.planning/phases/01-foundations-and-doctrine/01-05-SUMMARY.md
@.planning/phases/01-foundations-and-doctrine/01-06-SUMMARY.md
@CLAUDE.md
</context>
<tasks>
<task type="auto">
<name>Task 1: Author `.github/workflows/ci.yml` (~25 lines per RESEARCH Open Question #4)</name>
<files>
.github/workflows/ci.yml
</files>
<read_first>
- .planning/phases/01-foundations-and-doctrine/01-RESEARCH.md § "Open Questions #4" (20-line minimum-viable `.github/workflows/ci.yml` recommendation), § "Common Pitfalls (CI / Tooling Specific)" CI Pitfall A (use `actions/setup-node@v4` with `cache: 'npm'`, never cache `node_modules/`), CI Pitfall B (`--passWithNoTests=false` already on `npm run test` from Plan 01), CI Pitfall C (`--max-warnings 0` already on `npm run lint` from Plan 01)
- .planning/phases/01-foundations-and-doctrine/01-RESEARCH.md § "Environment Availability" (Node 22.x ideal)
- .planning/phases/01-foundations-and-doctrine/01-CONTEXT.md user pushback: solo dev, minimum-viable, no ceremonial workflows
- package.json (verify `npm run ci` script exists from Plan 01 and chains lint+test+validate:assets+build)
- All 6 prior SUMMARY files (verify each plan's artifacts are committed and tests are green BEFORE writing this workflow — this plan must NOT land if any prior plan's tests fail)
</read_first>
<action>
**Step 0 — Sanity check that all upstream plans landed green.** Run locally:
```bash
npm run ci
```
This MUST exit 0 before this plan's workflow lands. If it does not, the failing plan needs revision before this plan proceeds.
**Step 1 — Create the directory:**
```bash
mkdir -p .github/workflows
```
**Step 2 — Write `.github/workflows/ci.yml`** using the Write tool:
```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
```
**Concrete shape per RESEARCH Open Question #4:** ~25 lines (slightly over the 20-line target because the comment block is load-bearing context for future maintainers). This is single-job, single-matrix-entry, no third-party actions beyond `actions/checkout@v4` + `actions/setup-node@v4`.
**Step 3 — Validate the YAML locally** (without pushing):
```bash
# If `actionlint` is not installed: skip; the workflow will be validated by GitHub on push.
actionlint .github/workflows/ci.yml 2>/dev/null || echo "actionlint not installed; will validate on push"
```
**Step 4 — Local sanity check that `npm run ci` exits 0** (it should, since Plan 06 was green; this is the second time we run it just to be sure):
```bash
npm run ci
```
Confirm exit code 0 with output containing:
- `[lint]` or ESLint pass message
- `[test]` or Vitest pass message (some N tests passed)
- `[provenance] all <N> assets carry valid provenance.`
- `[build]` or Vite build success message
**Step 5 — Commit `ci(01-07): minimum-viable GitHub Actions workflow running npm run ci on push + PR (PIPE-06)`.**
**Step 6 (optional, only if the user has set up the GitHub remote):** push the branch and verify the workflow runs green on GitHub. Per CONTEXT user pushback, do NOT block this plan on the push — local `npm run ci` green is sufficient evidence that the workflow will run green when the user next pushes.
</action>
<verify>
<automated>test -f .github/workflows/ci.yml &amp;&amp; grep -q "npm run ci" .github/workflows/ci.yml &amp;&amp; grep -q "actions/setup-node@v4" .github/workflows/ci.yml &amp;&amp; grep -q "cache: 'npm'" .github/workflows/ci.yml &amp;&amp; npm run ci</automated>
</verify>
<acceptance_criteria>
- `.github/workflows/ci.yml` exists — verify with `test -f .github/workflows/ci.yml`.
- The workflow runs `npm run ci` — verify with `grep -q "npm run ci" .github/workflows/ci.yml`.
- The workflow uses `actions/setup-node@v4` with `cache: 'npm'` — verify with `grep -q "actions/setup-node@v4" .github/workflows/ci.yml && grep -q "cache: 'npm'" .github/workflows/ci.yml`.
- The workflow does NOT cache `node_modules/` directly (RESEARCH CI Pitfall A) — verify with `! grep -E "cache: 'node_modules" .github/workflows/ci.yml`.
- The workflow uses Node 22 — verify with `grep -E "node-version: '22'" .github/workflows/ci.yml`.
- The workflow runs `npm ci` (lockfile-strict) before `npm run ci` — verify with `grep -E "run: npm ci" .github/workflows/ci.yml` and that the `npm ci` step appears before the `npm run ci` step (`grep -n` line numbers ascending).
- The workflow triggers on `push` to `main` AND `pull_request` to `main` — verify with `grep -E "branches: \\[main\\]" .github/workflows/ci.yml | wc -l` returns 2 (one for push, one for pull_request).
- The workflow has a sensible `timeout-minutes` (10 is the recommended ceiling for Phase 1's ~30s runtime) — verify with `grep -q "timeout-minutes:" .github/workflows/ci.yml`.
- Locally `npm run ci` exits 0 — proves the workflow will be green on push.
- The workflow contains comments explaining what was deliberately omitted (no matrix, no codecov, no release automation) per CONTEXT user pushback — verify with `grep -qE "Deliberately omitted|matrix" .github/workflows/ci.yml`.
</acceptance_criteria>
<done>
`.github/workflows/ci.yml` authored as a minimum-viable single-job workflow running `npm ci` + `npm run ci` on push to main and PR; uses `actions/setup-node@v4` with npm caching per RESEARCH CI Pitfall A; Node 22; ~25 lines + load-bearing comments; locally `npm run ci` exits 0 proving the workflow will be green; commit landed.
</done>
</task>
</tasks>
<threat_model>
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|-----------|----------|-----------|-------------|-----------------|
| T-01-08 | Tampering | npm install supply-chain (a transitive dep gets compromised) | mitigate | `package-lock.json` is committed (Plan 01); `npm ci` in this workflow refuses if lockfile and `package.json` drift; per RESEARCH § Security Domain, this is the standard mitigation for solo-dev supply-chain risk in Phase 1. Phase 8 launch polish may add `npm audit` as a CI step if surface area grows. |
</threat_model>
<verification>
- Local `npm run ci` exits 0 (the workflow is a thin shell around the same script that runs locally).
- `.github/workflows/ci.yml` is well-formed YAML (validated by GitHub on push).
- All Phase-1 success criteria are now structurally enforced on every commit going forward:
- CORE-10 firewall via `npm run lint` (Plan 02)
- CORE-04 through CORE-09 via `npm test` (Plan 03 + Plan 06's doctrine.test.ts)
- PIPE-01 via `npm run build` + `npm test` (Plan 04 loader.test.ts)
- PIPE-03 + AEST-08 + AEST-09 via `npm run validate:assets` + `npm test` (Plan 05)
- PIPE-05 via `npm test` (Plan 06 doctrine.test.ts)
- PIPE-06 via this workflow (Vitest runs on every CI build)
- CORE-01 via `npm run build` (smoke; Phase 2 PIPE-07 adds Playwright load-time spec)
</verification>
<success_criteria>
- `.github/workflows/ci.yml` runs `npm ci && npm run ci` on push to main and PR.
- All Plan 0106 automated checks now run on every commit.
- PIPE-06's "Vitest tests run on every CI build" is structurally satisfied.
- Phase 2 inherits the same workflow without modification — adding economy tests just adds them to `npm test`.
</success_criteria>
<output>
After completion, create `.planning/phases/01-foundations-and-doctrine/01-07-SUMMARY.md` documenting:
- The exact workflow shape (~25 lines).
- Node version chosen (22).
- Confirmation that local `npm run ci` exits 0 immediately before this plan's commit.
- Note for 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 to run 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`.
- Note for Phase 8: visual regression testing (PIPE-04) will likely require a separate workflow file (matrix runs against multiple OSes) since it's a different cost profile from this single-job lint/test workflow.
Then update `.planning/phases/01-foundations-and-doctrine/01-VALIDATION.md` per-task table (the skeleton populated as plans were written): for each Plan 0107 task, fill in one row mapping Task ID → Plan → Wave → Requirement → Test Type → Automated Command → File Exists → Status. Set `nyquist_compliant: true` in the frontmatter and approval to "approved" once all rows are filled.
</output>