fix(admissions): state distance in miles throughout, never mixed
PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 1m3s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 10s
PR Checks / Build Frontend (no push) (pull_request) Successful in 45s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 11s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 58s

The postcode check read "69 m away — inside the September 2026 cut-off of
0.17 miles". Both numbers are right and the sentence is still useless: the
reader has to convert one of them to check a comparison we had already made
for them.

The cause was a readability rule of mine in formatCutoffDistance, which swapped
to metres below 100m on the grounds that "0.04 miles" carries less than "69 m".
Taken one figure at a time that holds. Taken in a sentence containing two
figures it guarantees a mismatch whenever they fall either side of the
threshold — and a 270m cut-off with a nearby home does exactly that.

Miles now lead everywhere. It is the unit UK school admissions runs on:
councils publish cut-offs in miles (90% of the collected source rows), and it
is what a parent has already been quoted in their booklet and offer letter.
The metric figure survives only as support beside the miles figure on the
Admissions tile, where it converts the same value rather than presenting a
second one to compare.

Below 0.01 miles the decimal places run out rather than the unit being wrong,
so a very short distance is described — "under 0.01 miles" — instead of
rounding to a flat "0.00 miles", which would read as no distance at all.

Both figures in the verdict now go through one formatter with no fallback that
could reach for another unit. The old `?? "N m"` fallbacks on that line were a
second route to the same defect and are gone.

Covered by a sweep over sixty home/cut-off combinations spanning the old
switch point, asserting no verdict contains a metric reading and that exactly
two miles figures appear; plus the reported case pinned verbatim, and an e2e
guard on the rendered verdict.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WDvkyqqHABm4bmth2kjAxE
This commit is contained in:
TudorandClaude Opus 5 committed 2026-08-20 20:35:05 +01:00
1 parent ffe7e04951
commit ea5249a2ea
4 files changed
+94 -23

No files matched your search

+31 -17
View File
@@ -505,32 +505,46 @@ export function getPhaseStyle(phase?: string | null): { key: string; label: stri
const METRES_PER_MILE = 1609.344;
/**
* Format an admission cut-off distance.
* A distance in miles, the unit UK school admissions is conducted in.
*
* Miles lead because that is the unit councils publish in — 90% of the source
* rows are miles — and because it is the unit a parent has already been quoted
* if they have read their council's booklet. The metric figure follows as
* support rather than replacing it.
* Councils publish cut-offs in miles — 90% of the collected source rows — and
* it is the unit a parent has already been quoted in their council's booklet
* and offer letter. Everything a reader is asked to compare therefore renders
* through this one function, so two figures in the same sentence can never
* arrive in different units.
*
* Below 100 m the miles figure stops carrying information ("0.03 miles"), so
* the two swap round and metres lead instead.
* That was not previously true. This formatter used to swap to metres below
* 100m, on the reasoning that "0.04 miles" carries less for a reader than
* "69 m". Taken one figure at a time that holds; taken in a sentence it
* produced "69 m away — inside the September 2026 cut-off of 0.17 miles",
* which asks the reader to convert between units to understand a comparison we
* had already made for them. Legibility of a single number lost to coherence
* of the pair.
*
* Below 0.01 miles the two decimal places run out rather than the unit being
* wrong, so the figure is described instead of rounded to a flat "0.00 miles".
*/
export function formatMiles(metres: number): string {
const miles = metres / METRES_PER_MILE;
if (miles < 0.01) return 'under 0.01 miles';
return `${miles.toFixed(2)} miles`;
}
/**
* Format an admission cut-off distance: the headline figure in miles, and a
* metric equivalent to support it.
*
* The metric figure is support, not an alternative — it appears beside the
* miles figure, never instead of it, so nothing a reader compares is ever in
* two units at once.
*/
export function formatCutoffDistance(
metres: number | null | undefined
): { primary: string; secondary: string } | null {
if (metres == null || !Number.isFinite(metres) || metres <= 0) return null;
const miles = metres / METRES_PER_MILE;
if (metres < 100) {
return {
primary: `${Math.round(metres)} m`,
secondary: `${miles.toFixed(2)} miles`,
};
}
return {
primary: `${miles.toFixed(2)} miles`,
primary: formatMiles(metres),
secondary: metres < 1000
? `${Math.round(metres / 10) * 10} m`
: `${(metres / 1000).toFixed(1)} km`,