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/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
On a clean main, run pnpm verify once to warm the cache.
Add window.localStorage anywhere in surfaces/shell/src/browser/main.tsx (outside a comment).
Run pnpm verify again. @ekklesia/core:test is a cache hit and passes.
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
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.
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.
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).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What I observed
pnpm verifypassed locally on PR #104's first commit (af31724), but CI'sverifyjob failed on the same commit (run 313, job "verify"):Locally, Turbo reported
@ekklesia/core:testas 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.tswalkssurfaces/shell/src(several assertions);core/tests/mail-boundaries.test.tsalso reads files outsidecore/.Turbo's task hash for
@ekklesia/core#testcovers onlycore/'s own files plusdependsOn: ["^build"]outputs.turbo.jsondeclares noinputsfor it.@ekklesia/shellis not a dependency of@ekklesia/core(Core may not depend on a surface), so a change confined tosurfaces/shellnever invalidates Core's cached test result. Any shell-only PR that breaks one of these guards passespnpm verifylocally from a warm cache and fails only in CI.How to reproduce
main, runpnpm verifyonce to warm the cache.window.localStorageanywhere insurfaces/shell/src/browser/main.tsx(outside a comment).pnpm verifyagain.@ekklesia/core:testis a cache hit and passes.TURBO_FORCE=true pnpm verifyfails inidentity-verification-boundaries.test.ts.Impact
pnpm verify), for exactly the cross-package security guards that matter most.Suggested fixes
turbo.json, give@ekklesia/core#testexplicitinputscovering what its tests read, e.g.["$TURBO_DEFAULT$", "../surfaces/shell/src/**", …]. Simple, but the list has to be kept in step with the tests.//#test:root), whose hash already spans the repository, besidetests/deploy.test.ts. They are repository-shape assertions rather than Core unit tests, so this may be the more honest home.TURBO_FORCE=true pnpm verifyindocs/development/testing.mdfor changes that touch files another package's tests read.What I did not look at
Whether
apps/apior other packages have tests that read files from packages outside their dependency graph (I only checkedcore/testsandapps/api/testsfor reads ofsurfaces/), 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).