Turbo caches @ekklesia/core:test across shell changes, so pnpm verify can pass locally while CI fails #105

Open
opened 2026-10-14 22:55:30 +00:00 by ekklesia-bot · 0 comments
Member

What I observed

pnpm verify passed locally on PR #104's first commit (af31724), but CI's verify job failed on the same commit (run 313, job "verify"):

FAIL tests/identity-verification-boundaries.test.ts > the browser shell reaches none of it > never puts a reset token in browser storage
AssertionError: …/surfaces/shell/src/browser/main.tsx: expected '…' not to contain 'localStorage'

Locally, Turbo reported @ekklesia/core:test as a cache hit, so the failing test never ran.

Cause

Some Core tests read source files from another workspace package at run time:

  • core/tests/identity-verification-boundaries.test.ts walks surfaces/shell/src (several assertions);
  • core/tests/mail-boundaries.test.ts also reads files outside core/.

Turbo's task hash for @ekklesia/core#test covers only core/'s own files plus dependsOn: ["^build"] outputs. turbo.json declares no inputs for it. @ekklesia/shell is not a dependency of @ekklesia/core (Core may not depend on a surface), so a change confined to surfaces/shell never invalidates Core's cached test result. Any shell-only PR that breaks one of these guards passes pnpm verify locally from a warm cache and fails only in CI.

How to reproduce

  1. On a clean main, run pnpm verify once to warm the cache.
  2. Add window.localStorage anywhere in surfaces/shell/src/browser/main.tsx (outside a comment).
  3. Run pnpm verify again. @ekklesia/core:test is a cache hit and passes.
  4. TURBO_FORCE=true pnpm verify fails in identity-verification-boundaries.test.ts.

Impact

  • Developers and agents get a false green from the documented local gate (pnpm verify), for exactly the cross-package security guards that matter most.
  • CI catches it, since it starts cold, but only after a push.

Suggested fixes

  1. In turbo.json, give @ekklesia/core#test explicit inputs covering what its tests read, e.g. ["$TURBO_DEFAULT$", "../surfaces/shell/src/**", …]. Simple, but the list has to be kept in step with the tests.
  2. Move the cross-package boundary tests to the root suite (//#test:root), whose hash already spans the repository, beside tests/deploy.test.ts. They are repository-shape assertions rather than Core unit tests, so this may be the more honest home.
  3. At minimum, document TURBO_FORCE=true pnpm verify in docs/development/testing.md for changes that touch files another package's tests read.

What I did not look at

Whether apps/api or other packages have tests that read files from packages outside their dependency graph (I only checked core/tests and apps/api/tests for reads of surfaces/), and whether CI's own Turbo cache could ever be warm (it appears to start cold).


Found by Claude while fixing the CI failure on PR #104 (Stage 1 Dashboard shell navigation).

## What I observed `pnpm verify` passed locally on PR #104's first commit (`af31724`), but CI's `verify` job failed on the same commit ([run 313, job "verify"](https://git.zayin.net/Zayin/Ekklesia/actions/runs/313)): ``` FAIL tests/identity-verification-boundaries.test.ts > the browser shell reaches none of it > never puts a reset token in browser storage AssertionError: …/surfaces/shell/src/browser/main.tsx: expected '…' not to contain 'localStorage' ``` Locally, Turbo reported `@ekklesia/core:test` as a **cache hit**, so the failing test never ran. ## Cause Some Core tests read source files from **another workspace package** at run time: - `core/tests/identity-verification-boundaries.test.ts` walks `surfaces/shell/src` (several assertions); - `core/tests/mail-boundaries.test.ts` also reads files outside `core/`. Turbo's task hash for `@ekklesia/core#test` covers only `core/`'s own files plus `dependsOn: ["^build"]` outputs. `turbo.json` declares no `inputs` for it. `@ekklesia/shell` is not a dependency of `@ekklesia/core` (Core may not depend on a surface), so a change confined to `surfaces/shell` never invalidates Core's cached test result. Any shell-only PR that breaks one of these guards passes `pnpm verify` locally from a warm cache and fails only in CI. ## How to reproduce 1. On a clean `main`, run `pnpm verify` once to warm the cache. 2. Add `window.localStorage` anywhere in `surfaces/shell/src/browser/main.tsx` (outside a comment). 3. Run `pnpm verify` again. `@ekklesia/core:test` is a cache hit and passes. 4. `TURBO_FORCE=true pnpm verify` fails in `identity-verification-boundaries.test.ts`. ## Impact - Developers and agents get a false green from the documented local gate (`pnpm verify`), for exactly the cross-package security guards that matter most. - CI catches it, since it starts cold, but only after a push. ## Suggested fixes 1. In `turbo.json`, give `@ekklesia/core#test` explicit `inputs` covering what its tests read, e.g. `["$TURBO_DEFAULT$", "../surfaces/shell/src/**", …]`. Simple, but the list has to be kept in step with the tests. 2. Move the cross-package boundary tests to the root suite (`//#test:root`), whose hash already spans the repository, beside `tests/deploy.test.ts`. They are repository-shape assertions rather than Core unit tests, so this may be the more honest home. 3. At minimum, document `TURBO_FORCE=true pnpm verify` in `docs/development/testing.md` for changes that touch files another package's tests read. ## What I did not look at Whether `apps/api` or other packages have tests that read files from packages outside their dependency graph (I only checked `core/tests` and `apps/api/tests` for reads of `surfaces/`), and whether CI's own Turbo cache could ever be warm (it appears to start cold). --- Found by Claude while fixing the CI failure on PR #104 (Stage 1 Dashboard shell navigation).
ekklesia-bot added the found-by-claude label 2026-10-14 22:55:30 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Zayin/Ekklesia#105