The "vs LA avg" comparison on secondary search rows disappeared after the #175/#176 deploy. Neither PR touched it. The cause is older.
Root cause
HomeView fetched /api/la-averages with cache: 'force-cache'. That mode serves any stored response, however old, without asking the server. One failed request was stored and replayed on every later visit, and the .catch(() => {}) hid it, so laAverages stayed empty and no row showed a delta. Failures that can get stored this way include a deploy restart, when the proxy answers 502, and the July proxy outage.
Evidence from a Playwright browser profile on staging:
fetch mode
status
time
Date
force-cache (what the app did)
500
2 ms
Sun, 05 Jul 2026
no-store
200, 153 LAs
60 ms
today
default (this fix)
200, 153 LAs
82 ms, then 2 ms from cache
today
After the default-mode fetch replaced the stored entry, the same page showed "49.4 Attainment 8 +11.4 vs LA avg" for Burntwood. The delta code on staging was fine all along.
Fix
Use the default cache mode. The API already sends Cache-Control: public, max-age=300, stale-while-revalidate=604800 (and only on 200s), so a good answer is reused for five minutes and an error never is. Browsers that hold a stored failure recover on their next visit after this deploys, with nothing to clear.
Tests
Jest: HomeView.staleFetch.test.tsx checks LA averages are not fetched with force-cache. It fails before the fix. 70 suites, 597 tests pass; tsc --noEmit clean.
E2E: a mainstream secondary's search row shows "vs LA avg". Nothing checked this before, which is how it went missing unnoticed. Playwright disables the HTTP cache while intercepting requests, so a journey can't replay a stored failure. The unit test pins the cache mode instead. Passes against staging.
Merges cleanly with #177 (checked with git merge-tree).
The "vs LA avg" comparison on secondary search rows disappeared after the #175/#176 deploy. Neither PR touched it. The cause is older.
## Root cause
`HomeView` fetched `/api/la-averages` with `cache: 'force-cache'`. That mode serves **any stored response, however old, without asking the server**. One failed request was stored and replayed on every later visit, and the `.catch(() => {})` hid it, so `laAverages` stayed empty and no row showed a delta. Failures that can get stored this way include a deploy restart, when the proxy answers 502, and the July proxy outage.
Evidence from a Playwright browser profile on staging:
| fetch mode | status | time | `Date` |
|---|---|---|---|
| `force-cache` (what the app did) | **500** | 2 ms | **Sun, 05 Jul 2026** |
| `no-store` | 200, 153 LAs | 60 ms | today |
| default (this fix) | 200, 153 LAs | 82 ms, then 2 ms from cache | today |
After the default-mode fetch replaced the stored entry, the same page showed **"49.4 Attainment 8 +11.4 vs LA avg"** for Burntwood. The delta code on staging was fine all along.
## Fix
Use the default cache mode. The API already sends `Cache-Control: public, max-age=300, stale-while-revalidate=604800` (and only on 200s), so a good answer is reused for five minutes and an error never is. Browsers that hold a stored failure recover on their next visit after this deploys, with nothing to clear.
## Tests
- Jest: `HomeView.staleFetch.test.tsx` checks LA averages are not fetched with `force-cache`. It fails before the fix. 70 suites, 597 tests pass; `tsc --noEmit` clean.
- E2E: a mainstream secondary's search row shows "vs LA avg". Nothing checked this before, which is how it went missing unnoticed. Playwright disables the HTTP cache while intercepting requests, so a journey can't replay a stored failure. The unit test pins the cache mode instead. Passes against staging.
- Merges cleanly with #177 (checked with `git merge-tree`).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The search page fetched LA averages with cache: 'force-cache', which serves
any stored response, however old, without asking the server. One failed
request (a staging deploy restart; the July proxy outage) was stored and
replayed on every later visit, and the error was swallowed, so the
"vs LA avg" delta silently vanished from every secondary row in that
browser. A Playwright profile still held a 500 dated 5 July.
The default cache mode honours the API's Cache-Control (five minutes), so
a good answer is still reused and an error never is. Browsers holding a
stored failure recover on their next visit.
A journey now checks that a mainstream secondary's row shows the
comparison: nothing did, which is how it could go missing unnoticed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Removes the force-cache mode from the LA-averages fetch in HomeView so a single failed response is no longer replayed from the browser cache. Adds a unit test pinning the cache mode and an e2e journey checking that a secondary search row shows the 'vs LA avg' comparison. The change is small and correct, with only minor test-robustness concerns.
🟡 Minor
nextjs-app/components/HomeView.tsx: fetchLAaverages() is now called with no options. If the helper in lib/api reads an options argument without a default (e.g. destructures it), this would throw. The unit test reads options?.cache, which suggests it is optional, but this is worth confirming. The helper may also set its own cache mode internally, which the unit test would not catch.
nextjs-app/tests/components/HomeView.staleFetch.test.tsx: The test only asserts that the cache option is not 'force-cache'. It would pass if a different problematic mode such as 'only-if-cached' were used. It also assumes fetchLAaverages is already mocked in this file; if the mock is not set up, jest.mocked(...).mock.calls will fail.
e2e/tests/journeys.spec.ts: The e2e test depends on CSS-module class name fragments (__rowContent, __line3) and on live data (a secondary school matching the search 'school' that has an LA average). The fragments make it brittle against styling refactors. The test.skip fallback means the guard can silently become a no-op. The comment also says the test guards the comparison, but it cannot detect the force-cache regression, since Playwright disables the HTTP cache.
## 🤖 AI Code Review (Claude Code)
Removes the `force-cache` mode from the LA-averages fetch in HomeView so a single failed response is no longer replayed from the browser cache. Adds a unit test pinning the cache mode and an e2e journey checking that a secondary search row shows the 'vs LA avg' comparison. The change is small and correct, with only minor test-robustness concerns.
### 🟡 Minor
- **nextjs-app/components/HomeView.tsx**: fetchLAaverages() is now called with no options. If the helper in lib/api reads an options argument without a default (e.g. destructures it), this would throw. The unit test reads `options?.cache`, which suggests it is optional, but this is worth confirming. The helper may also set its own cache mode internally, which the unit test would not catch.
- **nextjs-app/__tests__/components/HomeView.staleFetch.test.tsx**: The test only asserts that the cache option is not 'force-cache'. It would pass if a different problematic mode such as 'only-if-cached' were used. It also assumes fetchLAaverages is already mocked in this file; if the mock is not set up, `jest.mocked(...).mock.calls` will fail.
- **e2e/tests/journeys.spec.ts**: The e2e test depends on CSS-module class name fragments (`__rowContent`, `__line3`) and on live data (a secondary school matching the search 'school' that has an LA average). The fragments make it brittle against styling refactors. The `test.skip` fallback means the guard can silently become a no-op. The comment also says the test guards the comparison, but it cannot detect the force-cache regression, since Playwright disables the HTTP cache.
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.
The "vs LA avg" comparison on secondary search rows disappeared after the #175/#176 deploy. Neither PR touched it. The cause is older.
Root cause
HomeViewfetched/api/la-averageswithcache: 'force-cache'. That mode serves any stored response, however old, without asking the server. One failed request was stored and replayed on every later visit, and the.catch(() => {})hid it, solaAveragesstayed empty and no row showed a delta. Failures that can get stored this way include a deploy restart, when the proxy answers 502, and the July proxy outage.Evidence from a Playwright browser profile on staging:
Dateforce-cache(what the app did)no-storeAfter the default-mode fetch replaced the stored entry, the same page showed "49.4 Attainment 8 +11.4 vs LA avg" for Burntwood. The delta code on staging was fine all along.
Fix
Use the default cache mode. The API already sends
Cache-Control: public, max-age=300, stale-while-revalidate=604800(and only on 200s), so a good answer is reused for five minutes and an error never is. Browsers that hold a stored failure recover on their next visit after this deploys, with nothing to clear.Tests
HomeView.staleFetch.test.tsxchecks LA averages are not fetched withforce-cache. It fails before the fix. 70 suites, 597 tests pass;tsc --noEmitclean.git merge-tree).🤖 Generated with Claude Code
🤖 AI Code Review (Claude Code)
Removes the
force-cachemode from the LA-averages fetch in HomeView so a single failed response is no longer replayed from the browser cache. Adds a unit test pinning the cache mode and an e2e journey checking that a secondary search row shows the 'vs LA avg' comparison. The change is small and correct, with only minor test-robustness concerns.🟡 Minor
options?.cache, which suggests it is optional, but this is worth confirming. The helper may also set its own cache mode internally, which the unit test would not catch.jest.mocked(...).mock.callswill fail.__rowContent,__line3) and on live data (a secondary school matching the search 'school' that has an LA average). The fragments make it brittle against styling refactors. Thetest.skipfallback means the guard can silently become a no-op. The comment also says the test guards the comparison, but it cannot detect the force-cache regression, since Playwright disables the HTTP cache.