Reported: the primary/secondary links on postcode pages and authority pages 404.
What is broken
PlaceView built every phase link as /schools/[slug]/[phase] — the shape that belongs to towns alone.
Page family
Link emitted
Result
Authority (87 of 151)
/schools/kent/primary
404
Authority (64 of 151)
/schools/barnet/primary
200, wrong schools — the town Barnet
Outcode (1,720)
/schools/sw11/primary
404
The 64 silent cases are the worse half: two namespaces exist because 67 town names collide with an authority name and neither set contains the other, and this link walked a reader straight from one to the other.
Verified on staging: /schools/authority/barnet/primary → 404, /schools/sw11/primary → 404, and a crawl of one page per family found the broken links on every authority and outcode page sampled, none on towns or localities.
Why nothing caught it
Both halves are a rule written twice and inherited by only one of the places that needed it.
Authorities. The spec lists /schools/authority/[la]/primary; the plan created the three bare routes and dropped the fourth. The sitemap is generated from the place registry, which was right about them all along — so 302 authority phase URLs have been submitted to Google and every one 404s. Adding the route makes the sitemap true and serves a query that is genuinely distinct: admissions are authority-run, so "primary schools in Kent" is how a parent searches before they have settled on a town.
Outcodes. The opposite: the registry computed phases for outcodes although the spec gives them no route, and the sitemap knew to skip them while the API did not. The registry now decides alone and the sitemap's copy of the rule is gone.
Also in here
An authority below the five-school threshold has no page, so the API now sends a null slug for it and the page names it without linking. Two English authorities are in that position (City of London, Isles of Scilly). Unreachable in today's data — checked across the EC and TR outcodes — but the thin-place redirect would have sent a reader to a 404 the year it isn't.
The missing guard
The new e2e journey walks every /schools link a page of each family emits and requires a 200. Each family built its own links, and nothing ever checked that a link pointed at a route.
Re-run sitemap_generate. The URL count moves very little — the 302 authority phase URLs were already being submitted and stay, and outcodes never had any — but those 302 stop being 404s.
Reported: the primary/secondary links on postcode pages and authority pages 404.
## What is broken
`PlaceView` built every phase link as `/schools/[slug]/[phase]` — the shape that belongs to towns alone.
| Page family | Link emitted | Result |
|---|---|---|
| Authority (87 of 151) | `/schools/kent/primary` | **404** |
| Authority (64 of 151) | `/schools/barnet/primary` | **200, wrong schools** — the *town* Barnet |
| Outcode (1,720) | `/schools/sw11/primary` | **404** |
The 64 silent cases are the worse half: two namespaces exist because 67 town names collide with an authority name and neither set contains the other, and this link walked a reader straight from one to the other.
Verified on staging: `/schools/authority/barnet/primary` → 404, `/schools/sw11/primary` → 404, and a crawl of one page per family found the broken links on every authority and outcode page sampled, none on towns or localities.
## Why nothing caught it
Both halves are a rule written twice and inherited by only one of the places that needed it.
**Authorities.** The spec lists `/schools/authority/[la]/primary`; the plan created the three bare routes and dropped the fourth. The sitemap is generated from the place registry, which was right about them all along — so **302 authority phase URLs have been submitted to Google and every one 404s**. Adding the route makes the sitemap true and serves a query that is genuinely distinct: admissions are authority-run, so "primary schools in Kent" is how a parent searches before they have settled on a town.
**Outcodes.** The opposite: the registry computed phases for outcodes although the spec gives them no route, and the sitemap knew to skip them while the API did not. The registry now decides alone and the sitemap's copy of the rule is gone.
## Also in here
An authority below the five-school threshold has no page, so the API now sends a null slug for it and the page names it without linking. Two English authorities are in that position (City of London, Isles of Scilly). Unreachable in today's data — checked across the EC and TR outcodes — but the thin-place redirect would have sent a reader to a 404 the year it isn't.
## The missing guard
The new e2e journey walks every `/schools` link a page of each family emits and requires a 200. Each family built its own links, and nothing ever checked that a link pointed at a route.
## Verification
- backend 122 passed · frontend 264 passed · `tsc --noEmit` clean · `next build` green, `/schools/authority/[la]/[phase]` registered
- 90 e2e journeys (was 87)
## After deploy
Re-run `sitemap_generate`. The URL count moves very little — the 302 authority phase URLs were already being submitted and stay, and outcodes never had any — but those 302 stop being 404s.
Every place page built its phase links as /schools/[slug]/[phase], the
shape that belongs to towns alone.
On an authority page that pointed into the town namespace. For 87 of the
151 authorities the target does not exist and the link 404s; for the
other 64 it resolves to the town of the same name — a different set of
schools, which is precisely the near-duplicate the two namespaces were
introduced to prevent. On an outcode page it 404s outright.
Two causes behind it, both a rule written twice and inherited by only
one of the places that needed it.
The authority phase route was in the spec and dropped by the plan, which
built the three bare routes and no fourth. The sitemap is generated from
the place registry, which was right about them all along, so 302
authority phase URLs have been submitted to Google and every one 404s.
Adding the route makes the sitemap true and serves a real query —
admissions are authority-run, so "primary schools in Kent" is how a
parent searches before they have settled on a town.
The outcode variants were the opposite: the registry computed phases for
outcodes although the spec gives them no route, and the sitemap knew to
skip them while the API did not. The registry now decides alone, and the
sitemap's duplicate of that rule is gone.
Also: an authority under the five-school threshold has no page, so the
API sends a null slug for it and the page names it without linking.
Two English authorities are in that position. It was unreachable in
today's data — verified across the EC and TR outcodes — but the thin
place redirect would have sent a reader to a 404 the year it isn't.
The e2e journey now walks every /schools link a page of each family
emits and requires a 200, which is the check that was missing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015mWQnpye9F299NVRCCSRvj
This PR fixes a real namespace bug where authority phase pages (/schools/authority/[la]/[phase]) were missing entirely, causing every authority page's phase links and 302 submitted sitemap URLs to 404 or silently resolve into the unrelated town namespace. It also correctly removes phantom phase links/sitemap entries from outcode pages and adds null-slug handling so authorities below the publish threshold (City of London, Isles of Scilly) are named but not linked. The change is well tested overall, but one edge case in the new null-slug handling reintroduces the exact 404 pattern the PR is meant to eliminate.
🟡 Minor
nextjs-app/app/schools/[place]/page.tsx: In the no-averages redirect, target = detail.place.authorities?.[0]?.slug ?? (parent_authority ? authoritySlug(parent_authority) : null). When the place's largest/parent authority is itself below the publish threshold (e.g. Isles of Scilly), the API correctly returns authorities[0].slug === null, but ?? falls through to re-slugifying parent_authority via authoritySlug(), producing a URL to a page that does not exist — the same 404 the surrounding comment explicitly says this code exists to avoid. It should treat an explicit null slug on authorities[0] as 'no page' and call notFound() directly instead of regenerating a slug client-side. None of the added tests cover this (the straddling-authority tests only put the null-slug authority second, not first/largest), so it would ship unnoticed.
## 🤖 AI Code Review (Claude Code)
This PR fixes a real namespace bug where authority phase pages (/schools/authority/[la]/[phase]) were missing entirely, causing every authority page's phase links and 302 submitted sitemap URLs to 404 or silently resolve into the unrelated town namespace. It also correctly removes phantom phase links/sitemap entries from outcode pages and adds null-slug handling so authorities below the publish threshold (City of London, Isles of Scilly) are named but not linked. The change is well tested overall, but one edge case in the new null-slug handling reintroduces the exact 404 pattern the PR is meant to eliminate.
### 🟡 Minor
- **nextjs-app/app/schools/[place]/page.tsx**: In the no-averages redirect, `target = detail.place.authorities?.[0]?.slug ?? (parent_authority ? authoritySlug(parent_authority) : null)`. When the place's largest/parent authority is itself below the publish threshold (e.g. Isles of Scilly), the API correctly returns `authorities[0].slug === null`, but `??` falls through to re-slugifying `parent_authority` via `authoritySlug()`, producing a URL to a page that does not exist — the same 404 the surrounding comment explicitly says this code exists to avoid. It should treat an explicit null slug on `authorities[0]` as 'no page' and call `notFound()` directly instead of regenerating a slug client-side. None of the added tests cover this (the straddling-authority tests only put the null-slug authority second, not first/largest), so it would ship unnoticed.
tudor
merged commit 43e0621728 into main2026-08-22 17:33:13 +00:00
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.
Reported: the primary/secondary links on postcode pages and authority pages 404.
What is broken
PlaceViewbuilt every phase link as/schools/[slug]/[phase]— the shape that belongs to towns alone./schools/kent/primary/schools/barnet/primary/schools/sw11/primaryThe 64 silent cases are the worse half: two namespaces exist because 67 town names collide with an authority name and neither set contains the other, and this link walked a reader straight from one to the other.
Verified on staging:
/schools/authority/barnet/primary→ 404,/schools/sw11/primary→ 404, and a crawl of one page per family found the broken links on every authority and outcode page sampled, none on towns or localities.Why nothing caught it
Both halves are a rule written twice and inherited by only one of the places that needed it.
Authorities. The spec lists
/schools/authority/[la]/primary; the plan created the three bare routes and dropped the fourth. The sitemap is generated from the place registry, which was right about them all along — so 302 authority phase URLs have been submitted to Google and every one 404s. Adding the route makes the sitemap true and serves a query that is genuinely distinct: admissions are authority-run, so "primary schools in Kent" is how a parent searches before they have settled on a town.Outcodes. The opposite: the registry computed phases for outcodes although the spec gives them no route, and the sitemap knew to skip them while the API did not. The registry now decides alone and the sitemap's copy of the rule is gone.
Also in here
An authority below the five-school threshold has no page, so the API now sends a null slug for it and the page names it without linking. Two English authorities are in that position (City of London, Isles of Scilly). Unreachable in today's data — checked across the EC and TR outcodes — but the thin-place redirect would have sent a reader to a 404 the year it isn't.
The missing guard
The new e2e journey walks every
/schoolslink a page of each family emits and requires a 200. Each family built its own links, and nothing ever checked that a link pointed at a route.Verification
tsc --noEmitclean ·next buildgreen,/schools/authority/[la]/[phase]registeredAfter deploy
Re-run
sitemap_generate. The URL count moves very little — the 302 authority phase URLs were already being submitted and stay, and outcodes never had any — but those 302 stop being 404s.🤖 AI Code Review (Claude Code)
This PR fixes a real namespace bug where authority phase pages (/schools/authority/[la]/[phase]) were missing entirely, causing every authority page's phase links and 302 submitted sitemap URLs to 404 or silently resolve into the unrelated town namespace. It also correctly removes phantom phase links/sitemap entries from outcode pages and adds null-slug handling so authorities below the publish threshold (City of London, Isles of Scilly) are named but not linked. The change is well tested overall, but one edge case in the new null-slug handling reintroduces the exact 404 pattern the PR is meant to eliminate.
🟡 Minor
target = detail.place.authorities?.[0]?.slug ?? (parent_authority ? authoritySlug(parent_authority) : null). When the place's largest/parent authority is itself below the publish threshold (e.g. Isles of Scilly), the API correctly returnsauthorities[0].slug === null, but??falls through to re-slugifyingparent_authorityviaauthoritySlug(), producing a URL to a page that does not exist — the same 404 the surrounding comment explicitly says this code exists to avoid. It should treat an explicit null slug onauthorities[0]as 'no page' and callnotFound()directly instead of regenerating a slug client-side. None of the added tests cover this (the straddling-authority tests only put the null-slug authority second, not first/largest), so it would ship unnoticed.