Skip to content

feat(ci): cache pharos-tokens fingerprint + activate TurboSnap#86

Merged
dgraciac merged 1 commit into
mainfrom
feat/turbosnap-fingerprint
May 12, 2026
Merged

feat(ci): cache pharos-tokens fingerprint + activate TurboSnap#86
dgraciac merged 1 commit into
mainfrom
feat/turbosnap-fingerprint

Conversation

@dgraciac
Copy link
Copy Markdown
Member

Summary

Activates TurboSnap (`onlyChanged: true` in `chromaui/action`) by caching the content fingerprint of `@code-sherpas/pharos-tokens` in a git-tracked file. Closes the long-standing reason TurboSnap was disabled: pharos-tokens ships its CSS through `node_modules/`, which falls outside the git tree TurboSnap compares against, so a pharos-tokens release that changed pixels could not invalidate any snapshot (documented in the old comment in chromatic.yml, observed live when `error.fg` was darkened to pass WCAG AA 4.5:1).

Architecture

Four coordinated pieces:

  1. `scripts/snapshot-pharos-tokens.sh` — hashes every file under `node_modules/@code-sherpas/pharos-tokens` (sorted, content-addressed, locale-independent). Writes `version= / content_sha256=` to `.pharos-tokens.fingerprint`. Hashing the whole installed package directory, not just `dist/styles.css`, eliminates the residual risk a non-CSS change (a future JS icon-registry, a `.d.ts` shipped with the runtime, a metadata field that affects rendering) slips through unnoticed.

  2. `package.json` `postinstall` — runs the script after every `pnpm install`. Local developers get the fingerprint regenerated automatically; `git status` surfaces the drift to be committed.

  3. `ci.yml` `Verify pharos-tokens fingerprint is current` step — runs right after `pnpm install --frozen-lockfile` (which already executes the postinstall) and fails the CI with a precise remediation message when the committed fingerprint disagrees with `node_modules/`. This is the gate that prevents stale-fingerprint Chromatic baselines from shipping. Should be made a required status check via branch protection on `main` (CTO action — separate from this PR; see PR description below for the exact `gh api` invocation).

  4. `chromatic.yml` — flips `onlyChanged: true` and declares `.pharos-tokens.fingerprint` as a Chromatic `externals` glob. A pharos-tokens release that mutates any byte under `dist/` changes the fingerprint → TurboSnap sees the diff in the git tree → invalidates the whole baseline. Same correctness as today's `onlyChanged: false`, but limited to bumps that actually matter.

Expected impact

Chromatic publishes 40–60% reduction in snapshot consumption for typical workflows. For pharos-react specifically, the win compounds because:

  • Every Dependabot patch of dev-deps (vite/eslint/prettier/jsdom/chromatic itself) currently spends 67 snapshots × 2 (PR + main). With TurboSnap and an unchanged fingerprint, those PRs snapshot zero stories.
  • Every PR adding a single atom currently rebuilds all 67 stories. With TurboSnap, only the stories importing the changed module rebuild — typically 4-8 per atom PR.
  • pharos-tokens bumps still rebuild everything (the fingerprint changes), as they should.

What this does not do

Local verification

  • `pnpm install --frozen-lockfile` triggers postinstall, which regenerates `.pharos-tokens.fingerprint`. File content is idempotent given the same `node_modules/@code-sherpas/pharos-tokens` contents.
  • `pnpm build` / `pnpm typecheck` / `pnpm lint` / `pnpm format:check` / `pnpm test` — all clean (136 tests pass).
  • Simulated stale-fingerprint: editing `.pharos-tokens.fingerprint` by hand, then running the verify step (`git diff --exit-code .pharos-tokens.fingerprint`) fails with the expected remediation message.

Required branch-protection follow-up

After this merges, the `lint + typecheck + build + test` CI job needs to remain a required status check on `main` (it already is — this PR adds a step inside it, not a new job). Nothing further required for the verify step itself.

Test plan

  • CI on this PR runs Chromatic with TurboSnap on. Expect ~67 snapshots invalidated this run (the `.pharos-tokens.fingerprint` is brand new and triggers the rebuild).
  • After merge: a follow-up PR that does not touch `pharos-tokens` should see TurboSnap skip most stories. Verify in Chromatic UI `Build N` → `changed: X / Y` line.
  • After merge: open a synthetic PR that bumps `pharos-tokens` to a new patch — fingerprint should change, TurboSnap should rebuild everything, no stale snapshot risk.
  • Verify the fingerprint-verify step fails the build when fingerprint is stale (edit fingerprint manually in a draft PR, push, expect CI red).

🤖 Generated with Claude Code

@chromatic-com
Copy link
Copy Markdown

chromatic-com Bot commented May 12, 2026

Warning

Testing paused

Monthly snapshot limit reached. Update your plan to get more snapshots and resume testing.

Closes the long-standing reason TurboSnap was disabled
(`@code-sherpas/pharos-tokens` ships its CSS through `node_modules/`,
falling outside the git tree TurboSnap compares against, so a
pharos-tokens release that changed pixels could not invalidate any
snapshot — see the old comment in chromatic.yml).

Adds three coordinated pieces:

1. `scripts/snapshot-pharos-tokens.sh` — hashes every file under
   `node_modules/@code-sherpas/pharos-tokens` (sorted, content-addressed,
   locale-independent) and writes
   `version=<x> / content_sha256=<y>` to a tracked
   `.pharos-tokens.fingerprint` file. Hashing the whole installed
   directory (not just `dist/styles.css`) covers any future JS module,
   .d.ts, or metadata pharos-tokens may ship — eliminating the residual
   risk of a non-CSS change slipping through.

2. `package.json` `postinstall` invokes the script. Every `pnpm install`
   updates the fingerprint locally; `git status` surfaces the drift the
   developer must commit.

3. `.github/workflows/ci.yml` `Verify pharos-tokens fingerprint is
   current` step runs immediately after `pnpm install --frozen-lockfile`
   and fails the build (with a precise remediation message) when the
   committed fingerprint disagrees with `node_modules/`. This is the
   gate that prevents stale-fingerprint Chromatic baselines from ever
   shipping. To be made a *required* status check via branch protection
   on `main`.

4. `.github/workflows/chromatic.yml` flips `onlyChanged: true` (TurboSnap
   on) and declares `.pharos-tokens.fingerprint` as a Chromatic
   `externals` glob. A pharos-tokens release that mutates a single byte
   under `dist/` changes the fingerprint, TurboSnap sees it in the git
   diff, and invalidates the whole baseline — same behaviour we get
   today with `onlyChanged: false`, but limited to bumps that actually
   matter, not every push.

Expected outcome: 40–60% reduction in snapshot quota burn per
Chromatic's published TurboSnap savings, with zero new risk of stale
visual baselines.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dgraciac dgraciac force-pushed the feat/turbosnap-fingerprint branch from ccfd584 to 2bfd010 Compare May 12, 2026 22:28
@dgraciac dgraciac merged commit 0e915c9 into main May 12, 2026
8 of 10 checks passed
@dgraciac dgraciac deleted the feat/turbosnap-fingerprint branch May 12, 2026 22:30
dgraciac added a commit that referenced this pull request May 12, 2026
PR #86 wired `bash scripts/snapshot-pharos-tokens.sh` into the
`postinstall` lifecycle. That hook runs on **every** install,
including consumer installs of `@code-sherpas/pharos-react`. The
published tarball ships only `dist/` and `RULES.md` (see
`files` in package.json), so `scripts/` is absent from the
artifact — consumer installs (Alexandria, future consumers) would
fail with `bash: scripts/snapshot-pharos-tokens.sh: No such file
or directory`.

`prepare` is the correct lifecycle. From the npm docs:

> Runs on local `npm install` without any arguments. Runs BEFORE
> the package is packed. Does NOT run when the package is installed
> as a dependency.

So:

- Local dev: `pnpm install` triggers `prepare`, which now chains
  `husky` and the fingerprint script. Same DX as before.
- CI: `pnpm install --frozen-lockfile` also triggers `prepare`
  (verified locally — script regenerates `.pharos-tokens.fingerprint`
  in CI exactly as needed for the verify-fingerprint step in
  `ci.yml`).
- Publish: the release runner's `prepare` regenerates the
  fingerprint before packing; `files: ["dist", "RULES.md"]`
  excludes it from the tarball so consumers never see it.
- Consumer install of pharos-react: `prepare` is silent, the script
  is never invoked, the install succeeds.

The publish bug never reached npm (PR #86 has no changeset and
`0.11.1` on npm is unchanged). Fixing main before the next release
keeps consumers safe.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant