Background
PR #40 merged a new blog post (the Rocketship Flywheel article) that added source files under src/content/blog/ and SVG assets under public/blog/rocketship-flywheel/. However, the corresponding docs/ build output was never regenerated before merge.
Because GitHub Pages serves from main's docs/ folder, the two SVG diagrams in the new post 404'd on the deployed site. PR #41 fixes the immediate issue by committing the rebuilt docs/, but the same failure mode can happen again on any future content change.
This issue tracks adding CI checks to prevent the class of problem.
Root cause
docs/ is a checked-in build artifact that can silently drift from source. The repo convention is:
- Change source under
src/ or public/.
- Run
npm run build locally.
- Commit both the source changes and the regenerated
docs/ in the same PR.
Nothing enforces step 2. If a contributor forgets, the PR still merges cleanly and the deploy breaks.
Proposed scope
Add a GitHub Actions workflow (e.g. .github/workflows/ci.yml) that runs on every pull request against main and performs three checks. If any fails, the PR cannot merge (once branch protection is configured).
1. docs/ matches source (primary fix)
Run npm ci && npm run build, then fail if git status --porcelain docs/ is non-empty. This forces contributors to commit an up-to-date docs/ as part of their PR and catches the exact failure mode from PR #40.
Note: vite.config.ts currently sets emptyOutDir: false, which is why stale assets have accumulated in docs/assets/. The CI check only verifies that rebuilding produces no new diffs — it does not clean up existing stale files. Separate hygiene issue, not in scope here.
2. Lint
Run npm run lint (already exists). Surfaces ESLint violations that currently only fail locally.
3. Blog asset link check
Custom script that:
- Parses each
src/content/blog/*.md file.
- Extracts all local asset references (images like
/blog/... or /upstyling.png, etc.).
- Verifies that each referenced file exists under
public/.
- Fails the CI run if any reference is broken.
Would have caught the specific Rocketship Flywheel issue at PR-time (in addition to check #1), and prevents typos in asset paths in future posts.
Out of scope
- Migrating away from checked-in
docs/ to an auto-deploy workflow (actions/deploy-pages). That is a larger restructure; considered and deferred. Revisit if the verify-matches approach proves too annoying in practice.
- Cleaning up stale entries in
docs/assets/. Separate hygiene issue.
- Running tests (
npm run test:run) in CI. Can be added in a follow-up if useful.
Acceptance criteria
Context
Background
PR #40 merged a new blog post (the Rocketship Flywheel article) that added source files under
src/content/blog/and SVG assets underpublic/blog/rocketship-flywheel/. However, the correspondingdocs/build output was never regenerated before merge.Because GitHub Pages serves from
main'sdocs/folder, the two SVG diagrams in the new post 404'd on the deployed site. PR #41 fixes the immediate issue by committing the rebuiltdocs/, but the same failure mode can happen again on any future content change.This issue tracks adding CI checks to prevent the class of problem.
Root cause
docs/is a checked-in build artifact that can silently drift from source. The repo convention is:src/orpublic/.npm run buildlocally.docs/in the same PR.Nothing enforces step 2. If a contributor forgets, the PR still merges cleanly and the deploy breaks.
Proposed scope
Add a GitHub Actions workflow (e.g.
.github/workflows/ci.yml) that runs on every pull request againstmainand performs three checks. If any fails, the PR cannot merge (once branch protection is configured).1.
docs/matches source (primary fix)Run
npm ci && npm run build, then fail ifgit status --porcelain docs/is non-empty. This forces contributors to commit an up-to-datedocs/as part of their PR and catches the exact failure mode from PR #40.Note:
vite.config.tscurrently setsemptyOutDir: false, which is why stale assets have accumulated indocs/assets/. The CI check only verifies that rebuilding produces no new diffs — it does not clean up existing stale files. Separate hygiene issue, not in scope here.2. Lint
Run
npm run lint(already exists). Surfaces ESLint violations that currently only fail locally.3. Blog asset link check
Custom script that:
src/content/blog/*.mdfile./blog/...or/upstyling.png, etc.).public/.Would have caught the specific Rocketship Flywheel issue at PR-time (in addition to check #1), and prevents typos in asset paths in future posts.
Out of scope
docs/to an auto-deploy workflow (actions/deploy-pages). That is a larger restructure; considered and deferred. Revisit if the verify-matches approach proves too annoying in practice.docs/assets/. Separate hygiene issue.npm run test:run) in CI. Can be added in a follow-up if useful.Acceptance criteria
.github/workflows/ci.ymlexists and runs on pull_request events targetingmain.src/orpublic/without rebuildingdocs/.AGENTS.md(or equivalent) describing the expected workflow.mainthat requires these checks to pass before merge (repo setting, not a file change).Context
docs/build: fix: ship rocketship flywheel SVGs and trim docs #41