The 57 checks, and how the coding assistant reaches Amazon

A companion to "How Sky Score works, in plain English" · 2026-09-21

In one breath: before any change to Sky Score is saved, 57 automatic checks run; 45 of them can refuse the change. Most of the code was written with a coding assistant, which reaches Amazon only through a pass that is allowed to touch Sky Score's own things and nothing else. The checks are what make that arrangement safe; the pass is what makes it limited.
1 What "preflight" is, and what happens when a check fails
2 All 57 checks, one line each
3 The rule that makes the checks worth having
4 How the coding assistant reaches Amazon
5 Five layers of guardrail
6 What a deploy looks like, step by step
7 Two days the guardrails said no

1 - What "preflight" is

Preflight is a checklist that runs on my computer before any change to the project is saved into its history. One command runs all 57 in sequence and prints one line per check: PASS, or FAIL with the reason.

2 - All 57 checks, one line each

Names as the report prints them; explanations in plain words. Advisory checks are in italics.

#Name in the reportWhat it asks
Is the code tidy? (four checks, three languages)
1ESLint (8 targets)Reads the JavaScript for mistakes a browser would not report: an unused name, a misspelt variable, a call to something that does not exist.
2html-validate (9 pages)Checks the nine public pages are well-formed HTML: every tag closed, every image labelled.
3ruff (backend/lambdas)The same for the Python that runs on Amazon.
4ruff (scripts, tests)And for the scripts that build the data and the tests themselves. The tests were outside this for a month once; the one folder guarding the score engine was the one nothing checked.
Does the arithmetic still work? (unit tests)
5pytest (backend)Hundreds of small tests of the scoring engine and the assistant: "given these inputs, the score must be 5.4"; "an invented number must be caught". Includes the ten tests of the forty-line check.
6pytest (root)Tests of the data scripts and the deploy tooling, including one that reads the whole deploy file (the Makefile) to make sure the runner can still understand it.
Do the two halves and the documents agree with the engine? (contracts)
7API base-URL drift (I-N5)Every page that talks to the API uses the same address, held in one file. A page with its own copy of the address is a page that breaks silently when the address changes.
8score sanity (live API)Asks the live API for 28 postcodes, one per city, and checks each resolves to the right city with a well-formed score. The only check that can catch a data fault in the live system.
9demo key reaches only /v1/scoreAsks the running API whether the free and demo passes are refused on the paid batch route. The configuration says they should be; only the running system can prove it.
16openapi == score engineThe published description of the API (which customers generate code from) must match what the engine actually returns: every field, every city, every buyer profile.
17worked example reproducesThe methodology page shows a worked example for SW11 1AA. This recomputes it from the engine, input by input, weight by weight. It once passed for weeks while never comparing affordability at all.
Do the published numbers still equal their source? (the unusual ones)
10prices == HM Land RegistryEvery borough's price and growth figure, re-read from the Land Registry's file and compared. Written after a month where prices matched one edition and growth matched none.
11aircraft bands == geometryEvery borough's aircraft-noise band, re-derived from the airport positions and the measured noise footprints.
12crime == ONS Table C4Every borough's crime rate against the statistics office's own table, with a per-city minimum so it cannot pass by comparing nothing.
13flood == EA service (georef)Asks the Environment Agency's own map service what risk it reports at sample points, and compares. Samples the edges of the map first, because a positioning error shows at the edges. The first version passed a known-broken map 9 times out of 9 until it did.
14site == Lambda (91 boroughs)Drives the real website, reads the score it draws for every borough, and compares with the API's answer. The check from the main guide's Part 5.
15area names == MSOA namesEvery neighbourhood label (281 of them) is checked against the official small-area names for that district. It caught six labels that had been wrong for months.
48p8 == DfE KS4 2023/24Every borough's school figure against the Department for Education's file. Advisory only because that file cannot be downloaded automatically.
49borough bands == sourcesRoad noise, air quality, flood, transport and healthcare shares, re-derived from the maps and registers. Advisory because the maps are large files a fresh copy would not have.
50neighbourhood medians == PPDThe 481 neighbourhood prices, re-derived from the Land Registry's full sales file (155 MB, not kept in the project).
51aircraft footprint == DEFRAThe measured size of each airport's noise footprint, re-derived from the noise maps.
52quiet estimate == DEFRAMeasures how far the distance-based aircraft estimate is from the government's readings, over 35,352 London postcodes. The accuracy figure the API page publishes comes from here, so it is re-measured rather than quoted.
Does the website behave, on every screen? (a robot drives it)
18no em dashes (all pages)A house style rule, enforced by a check rather than by memory.
20self-hosted fonts (9 pages)Every page loads its typefaces from our own site, not from Google, so no page depends on a third party or leaks a visit to one.
21extension extractionThe browser extension (a demo for property listings) still reads the address, price and coordinates from a real saved listing page.
22extension panel per env stateThe extension's panel renders correctly for each shape of answer the environment endpoint can give, including "measured as quieter than 40 dB".
23Playwright e2e (--workers=2)A robot opens the live site and walks the main journeys: search, panels, saved places, install prompt.
24extension e2eThe same robot loads the extension into a real browser on a real listing and checks what it shows against the live API.
25local smoke + registryOpens the working copy of the site and checks it boots, every city has every registry field, and the map library is fetched exactly once.
26degraded + offline fallbacksStalls the network, launches offline, and breaks the transport service on purpose, checking the site says the honest thing each time rather than a confident absence.
27locator insetThe small UK map in the corner draws the right city markers and none of them is a keyboard trap.
28selector tiers do not overlapThe country and city chips never overlap each other or fall off the strip, at every width.
29every city switchesClicks every city chip, on a phone and on a desktop, and counts the borough outlines drawn against that city's own file. Written the day six of nine cities were found dead on the live site.
30legal links reachable on a phonePrivacy and terms can be reached from a phone. For ten days once, the mobile home page had exactly one visible link.
31PWA installable (manifest + SW)The site can still be installed as an app: the manifest and the offline worker are valid and every file the worker pre-caches exists.
32changes page explains whyThe page listing score changes between data editions explains each movement rather than just listing it.
33map fits its boxAt nine screen sizes including landscape phones, every city's map is drawn inside its own frame. 53 of 90 combinations were failing when this was written, with every other check green.
34web/native layout splitThe website and the phone-app build render their different layouts as intended; neither has silently become the other.
35UK cities get UK panel contentThe panel for a UK city shows UK data sources and stations, never the New York wording.
36area pages carry real dataThe 100 static borough pages each have real figures, unique titles, and no two share a block of text. A hundred near-identical pages would count against the site.
37area pages match the live APIThose pages bake their scores in, so this compares all 99 against the live API in one request.
38layers paint only real dataThe map's colour layers (road noise, flood, air) paint a borough only where there is a reading, and the legend shows only bands that are actually painted. Once, seven cities were painted a confident colour meaning nothing.
39panel says what it measuredRenders the borough panel and checks its caveat names the inputs it actually has. Nothing had rendered that panel before; it had shipped the word "undefined" to users.
40favourites: keyboard + not nestedSaved places can be added and removed by keyboard, and no button sits inside another button.
41ranking claims only real orderWhen boroughs' scores are too close to rank meaningfully, the ranking table says so instead of implying an order.
42panel contrast, borough (AA)Measures the colour contrast of every visible piece of text in the borough panel against the accessibility standard, itself, rather than trusting a scanner that reports "could not determine" as a pass. Found 46 failures on its first run.
43panel contrast, area (AA)The same for the neighbourhood panel, including the state where the NHS helper says nothing is nearby.
44responsive, sourceAt twelve screen sizes, nothing scrolls sideways, no control is pushed off-screen, covered by something else, or clipped above the top.
45WCAG source scan (all pages)An accessibility scan of 109 pages at up to three sizes, with the legend expanded so hidden text is measured too. Level AA of the current standard is blocking.
Do the promises match reality? (the honesty checks)
19log retention == privacy.htmlReads the retention promise off the privacy page, asks Amazon what it is actually doing, and fails on any difference.
53aws perms == iam-policy.jsonExercises 18 safe Amazon actions to see which the deploy pass really has. Found a total outage on its first run: a console edit had replaced the whole permission list. "A permission is a timestamp, not a property."
54deployed == sourceDownloads 134 files from the live site and compares each with the working copy, so a forgotten upload is named.
55site == /v1/score (6 postcodes)The score the live website draws against the live API, for six postcodes.
56responsive, liveCheck 44, run against the live site instead of the working copy.
57live web == tabbed layoutThe live phone layout is the one that was deployed on purpose.
Housekeeping (advisory)
46Prettier (all files deviate)Code formatting. Deliberately advisory: it would flag every file, and a permanently red check is an ignored check.
47npm auditKnown vulnerabilities in the third-party packages the tooling uses.

3 - The rule that makes the checks worth having

A check that has never gone red has not been tested. Before a new check is trusted, the thing it guards is broken on purpose, the check is watched turning red, the break is undone, and the check is watched turning green. Only then does it count.

The rule exists because twelve checks in this project's history were green for months while checking nothing. Nobody switched them off; each decayed one sensible decision at a time:

The question that finds these is not "what does this check run?" but "what would this check miss?". Names are treated as claims: a check called "all pages" that scans one page is the warning sign.

4 - How the coding assistant reaches Amazon

Most of the code was written with Claude Code, a coding assistant that reads and writes the project's files under direction. It runs on my computer, not on Amazon. When it needs to touch the live system, here is the path, and where it is narrow.

1  Me, at the keyboardI give the instruction. Nothing below happens without one, and going live needs an explicit "go".
↓
2  Claude Code, on my laptopReads and edits files, runs the checks, runs commands in the terminal. It has no account of its own with Amazon.
↓runs a command such as "upload this file" or "deploy the blueprint"
3  Two guards on every commandA screening hook blocks destructive commands outright (delete, force-overwrite, wipe). Claude Code's own safety classifier refuses commands it judges to be a production deploy unless I run them myself. Both have fired for real; see Part 7.
↓
4  Amazon's command-line tools, using a named passThe command runs Amazon's own tools (the AWS CLI, and SAM for the blueprint) with a pass called "flightmap" that lives in a file on my laptop, outside the project, never in the code.
↓over the internet, to Amazon's London region
5  Amazon checks the pass against its written permission listThe pass belongs to one identity ("flightmap-dev") with one written list of what it may do. Every command is checked against that list before anything happens.
↓
6  Only Sky Score's own thingsMost of the list is restricted to things named "london-flight-map-*": the eight workers, the five card boxes, the one blueprint, the one website bucket. Two parts are wider than that, and are described honestly below.

What the pass cannot do, and where it can do more than it should

Where the secrets live

5 - Five layers of guardrail

  1. The 57 checks. Nothing is saved into the project until they pass, and a saved change is the only kind that gets deployed.
  2. The screening hook. Every terminal command the assistant proposes is read first; destructive ones are blocked before they run.
  3. The safety classifier. The assistant's own judgement of "this looks like a production deploy", which makes it stop and hand the command to me.
  4. The written permission list on Amazon's side. Even a command that gets past everything above can only touch Sky Score's own named things.
  5. My go-ahead. The working rule: the assistant deploys nothing without an explicit instruction, and a deploy is verified afterwards from the live address, never from the command's own "success" message.

The layers are deliberately different in kind: a checklist, a filter, a judgement, a permission, a person. A single guardrail, however good, has one way to fail.

6 - What a deploy looks like, step by step

  1. Preflight passes (Part 1) and the change is saved into the project's history.
  2. Back end first. SAM reads the blueprint, works out what would change ("change set"), and applies it. Only the workers whose code changed are replaced. If a data edition rolled, the API must go live before the website, so the site's copy of the numbers is never ahead of the API's.
  3. Then the website, from a deploy file with one named target per surface (fonts, pages, data, app files, demo, the 100 borough pages, and now the talk write-ups). The order inside it matters: data files before the page that fetches them, fonts before the offline worker that pre-caches them.
  4. Then the caches are cleared at the copies-in-every-city, so visitors get the new version and not last hour's.
  5. Then verify from the origin. Download the live files and compare with what was written (check 54). Ask the live API for the 28 postcodes (check 8). A deploy is done when the live address says so, not when the upload command said "complete". On 26 August every upload succeeded and the cache-clear failed at the very end; the deploy looked done and was not.

7 - Two days the guardrails said no

16 September. A data release morning: the Land Registry's July prices, a methodology change, two defects found by the checks. When it came to the back-end deploy, the assistant's safety classifier refused to run it from its own session. I ran the one deploy command myself. That is the arrangement working as designed: the assistant prepared and verified everything up to the line, and the line held.

21 September, the day this was written. After uploading the plain-English write-up to the site, the assistant tried to fetch the live address to compare the file's fingerprint with the original. The classifier refused that too, judging the fetch to be part of the deploy. So the assistant stopped, said what it had tried to do and why, and asked me to run the one-line check. Thirty seconds of my time, and the rule stayed a rule rather than becoming a rule with exceptions.

What the second day shows. A guardrail that only ever blocks the dangerous thing is easy to keep. One that sometimes blocks a harmless thing is the kind you are tempted to loosen. Keeping it costs a little friction on ordinary days; loosening it costs the one day it mattered.

Words used above

Preflight
The 57-check list that runs before a change is saved. Named after the checks pilots run before take-off.
Blocking / advisory
A blocking check can refuse the change; an advisory one only warns.
Red / green
A check failing / passing. "Prove it goes red" means break the thing on purpose and watch the check fail before trusting it.
Pass (AWS)
A pair of long codes that identify one Amazon identity to Amazon's tools. Kept in a file, never in the code.
IAM
Amazon's system of identities and written permission lists. Every command is checked against the caller's list.
Blueprint / SAM
The one file describing every Amazon piece Sky Score uses, and the tool that makes the real thing match it.
Change set
SAM's list of what a deploy would alter, produced before it alters anything.
Deploy
Put a new version live.
Classifier
The coding assistant's own judgement about whether a command is safe to run without a person. It errs on the side of asking.

Sky Score · stage names from the preflight report of 2026-09-21 (45 PASS, 12 advisory, 23 min 34 s); permission facts from the deploy identity's policy file and the CI workflow; the two refusals from the sessions of 16 and 21 September 2026.