Proof
Every rule, and the tests that guard it.
Each rule Ninety applies is checked three ways: an engine unit or property test, a scenario that the European Commission's own calculator also runs, and a TestSprite end-to-end test in a cloud browser on every pull request.
All 54 TestSprite tests passed on their latest cloud run (github-pr-70).
Generated from src/data/rules.json and TestSprite's latest results (npm run proof:matrix). 12 rules · 40/40 of the TestSprite tests tied to a rule passed on their latest cloud run · whole suite: 54/54 passed (github-pr-70). History of every run: LOOP.md.
As of PR #70, merged 2026-10-25 as d787b01. Every number links to where it comes from.
- 54/54TestSprite end-to-end tests passed on the PR preview, a required check
- 30/30TestSprite API and MCP tests passed, a required check
- 7required checks before anything merges into main, admins included, one a fail-closed TestSprite N/N gate
- 14pull requests that went red on TestSprite, then green (6 app defects, 1 UX, 7 test or CI)
- 14/14scenarios match the European Commission calculator
- 399/400stored inputs match the Commission script on every printed field (the one difference is documented)
- 5,000random histories against the Commission script on every PR (20,000 nightly)
- 1,200cases against an independent reference of the rule on every PR (20,000 nightly)
- 94.47%engine mutation score, combined: a full run (87.44%), then a targeted rerun
- 408Vitest unit, property and fuzz tests passing on every PR (3 more run only against the live Commission page)
TestSprite N/N gate
A required check reads the real count from the TestSprite V3 status and the backend run on the same commit. It fails unless both are N/N with N > 0, and fails closed when a result is missing or unreadable. How it works and what we found.
Defect ledger
All 14 pull requests that went red on TestSprite, then green, with the red and green status for each commit: 6 with app defects (7 real bugs), 1 UX, 7 test plan or CI. docs/DEFECTS.md.
Deep parity 399/400: the one difference
Two overlapping trips, 1 Jan–5 Apr and 25 Mar–10 Apr 2024, checked on 10 Apr. The Commission script prints one overstay per row: 31 Mar–5 Apr (6 days) and 31 Mar–10 Apr (11 days). The 6 days sit inside the 11. Ninety lists the 11 days once, and the script itself prints one 11-day overstay when the same days are entered as one stay. Details.
| Rule | Engine tests | Commission parity | TestSprite |
|---|---|---|---|
| R1 At most 90 days in any rolling 180-day period Regulation (EU) 2016/399 (Schengen Borders Code), Art. 6(1)
| TS01TS03 | ||
| R2 The entry day and the exit day both count Commission user manual for the short-stay calculator
| TS03TC010 | ||
| R3 A same-day exit and re-entry counts that day once Commission calculator behaviour | TS26 | ||
| R4 The 180-day window rolls: old days drop out Regulation (EU) 2016/399 (Schengen Borders Code), Art. 6(1)
| TS34TS42TS60 | ||
| R5 An overstay is named with its exact days and window Commission calculator, control mode | TS27TS46TS52 | ||
| R6 A stay still in progress counts through today, with a latest legal exit Commission calculator, planning mode | Not a calculator feature | TS28TS09 | |
| R7 A planned trip fits only up to its latest legal exit Commission calculator, planning mode | TS06TS10TS35TS45 | ||
| R8 Dates are YYYY-MM-DD; reversed or missing dates are refused Input rule | Not a calculator feature | TS04TC009TS29TS21TS54TS61 | |
| R9 EES: a gap is explained (missing exit, missing entry, exit dates disagree) Regulation (EU) 2017/2226 (EES)
| Not a calculator feature | TS07TS13TS14TS47TS48 | |
| R10 EES: stays that began before 10 April 2026 are not compared End of the EES progressive start, 10 April 2026
| Not a calculator feature | TS30TS31TS38 | |
| R11 The correction request cites Article 52(1)–(5) and its deadlines Regulation (EU) 2017/2226, Art. 52
| Not a calculator feature | TS08TS15TS17TS18TS49TS50 | |
| R12 The count agrees with the European Commission's own calculator Commission short-stay calculator | TS02TS51TS57TS65 |
The other 14 TestSprite tests
Not tied to one rule: they check the interface, navigation and keyboard behaviour. Same latest cloud run as above.
| Test | Checks | Latest |
|---|---|---|
| TS05 | Deleting a trip can be undone and the same trip comes back | passed |
| TC011 | Trips persist after reloading the page | passed |
| TS16 | A demo scenario carries to the EES check with the same numbers | passed |
| TS36 | /proof lists every rule with its tests | passed |
| TS37 | Scenario gallery cards open the history they describe | passed |
| TS40 | Unknown address: 404 page leads back to every page | passed |
| TS41 | Trip sheet takes focus, makes the page inert, and gives it back | passed |
| TS53 | The developers page documents the API and its OpenAPI document lists the three endpoints | passed |
| TS55 | Cancelling the trip sheet saves nothing | passed |
| TS56 | Deleting every example trip returns to the empty state, 0 used and 90 left | passed |
| TS58 | The skip link is the first Tab stop and moves focus to the main content | passed |
| TS59 | Footer links open /proof and /developers | passed |
| TS63 | The landing band switches from the example to the saved trips after a reload | passed |
| TS64 | Trips saved on the calculator are the ones the EES check compares: 10 days, a 4-day gap to an EES figure of 14 | passed |