# Changelog of SiteVerdict — UK261/EU261 Flight Compensation Checker (`siteverdict/siteverdict-flight-compensation`) Actor

- **URL**: https://apify.com/siteverdict/siteverdict-flight-compensation/changelog.md
- **Full Actor documentation**: https://apify.com/siteverdict/siteverdict-flight-compensation.md

## Changelog

### 1.1.0 — 2026-10-24 (offline build; not yet pushed or published)

#### Review fixes R5 (owner decision 24 Oct 2026: references are never stored; see FIXES-1.1.0-R5.md)

- **Behaviour change: the `reference` you type is never stored or echoed.** Every dataset row, CLAIMS.csv row and claim-pack entry
  is labelled `claim-<n>`, n = the claim's position in `claims` (1-based). No copy, hash or other derivative of the reference is
  written to the dataset, OUTPUT, CLAIM-PACK.md, CLAIMS.csv, logs, errors or the status message. 1.0.4 echoed your label (e.g.
  `trip-1`); keep your own list if you need to map `claim-<n>` back. Dataset rows arrive in completion order: sort or join by
  `reference`, not by row position (CLAIM-PACK.md and CLAIMS.csv are in input order).
- The `reference` input field is unchanged and still accepted. A reference that looks like a booking reference, passport number,
  e-mail address or labelled ticket number still makes the claim an uncharged error row (`referenceRefused: true`), as before.
- Unknown fields in a claim (e.g. a passenger name, PNR or ticket number under another key) are ignored and never stored.
- `.actor/actor.json` description shortened to 289 characters (Apify's push limit is 300); input-schema prefill no longer
  carries example references.

#### Review fixes R4 (R3 review 24 Oct 2026, N1–N3; see FIXES-1.1.0-R4.md)

- Text claim fields have length limits, checked before any other check (64 for `reference`, `flightNumber`, `marketingCarrier`,
  `airlineReason`, `arrivalTimeZone` and the choice fields; 32 for airport codes, `operatingCarrier`, `date`; 40 for times). A longer
  value makes the claim an uncharged error row ("… is too long … The value is not shown."). The personal-data check is now linear
  in the input length and runs once per claim (N2: a 100,000-character reference took 20 s before, now well under 200 ms).
- Ranges (N3): `rerouteArrivalDelayMinutes` −20,160 to 43,200 (a re-routing offered days earlier is a `yes` again, as before R3);
  `rerouteDepartedMinutesEarly` −43,200 to 20,160 (re-routing rules apply only with under 14 days' notice, so up to 14 days early); `arrivalDelayMinutes`/`finalArrivalDelayMinutes` −1,440 to 43,200 (30 days).
  The same −1,440 to 43,200 range now applies to a delay computed from `scheduledArrival`/`actualArrival` (before, 8 days typed
  as times was accepted while 8 days typed in minutes was refused).
- A fractional `cancellationNoticeDays` is shown truncated to 1 decimal in reasons and letters (N1: `3.07700900123` → "3");
  the verdict still uses the exact value, so a displayed figure never crosses the 7- or 14-day threshold.

#### Review fixes R3 (R2 review 24 Oct 2026, leaks L1–L4; see FIXES-1.1.0-R3.md)

- Numeric claim fields must be JSON numbers, finite and in range: `arrivalDelayMinutes`, `finalArrivalDelayMinutes`,
  `rerouteArrivalDelayMinutes` −1,440 to 10,080 minutes; `rerouteDepartedMinutesEarly` −10,080 to 10,080; `cancellationNoticeDays`
  0 to 365; `passengers` whole number 1 to 50. Boolean claim fields (`rerouteOffered`, `deniedBoardingVoluntary`, `checkedInOnTime`,
  `freeOrNonPublicFare`, `alreadyCompensatedElsewhere`, `singleBooking`) must be JSON `true`/`false`. Anything else (text, `"true"`,
  numbers out of range, arrays, objects) makes the claim an **uncharged error row**; the value is never shown. Fixes a text
  `cancellationNoticeDays` giving a charged `yes` verdict (L1), a text `singleBooking` echoed in `applicability.journey` (L2) and
  9-digit numbers stored as delays (L3). A claim that is not a JSON object is an error row too.
- Engine and letter: a non-number notice period is treated as "not given" (never `yes`); `applicability.journey.singleBooking` is only
  ever `true`/`false`/`null`.
- Top level (defence in depth; the platform validates these first): unknown `provider`/`aerodataboxGateway` and a non-text `apiKey`
  fail with "value not shown" (L4); a non-number `maxClaims`/`concurrency` uses the default and a non-boolean `claimPack` writes no
  pack, each with a warning that does not quote the value. Valid inputs give identical output to R2 (golden test; HTTP counters and letter date normalised).

#### Review fixes R2 (final review 24 Oct 2026, conditions P1–P3; see FIXES-1.1.0-R2.md)

- Every user-typed claim field is validated (airport and airline codes, flight number, date, times, IANA zone, enum fields). A value
  that fails is **never echoed**: messages name the field and the reason only ("The value is not shown."). Only validated values are
  stored or quoted. `flightNumber` is now screened by the personal-data guard too (refused → uncharged error row,
  `flightNumberRefused: true`); a flight number that is not in flight-number shape is ignored with a warning and not stored.
- PNR rule: a token with a lowercase letter is a label (`claim1`); a 6-character upper-case letters+digits token is accepted only as a
  flight number (2-character IATA designator, e.g. `BA2490`, `U28123`, or a known 3-letter ICAO carrier, e.g. `EZY123`, `BAW117`);
  otherwise it is refused (`ABC123`, `ZZZ999`, `XK9P2L`). This **replaces** the R1 acceptance of `ABC123`. A 6-letter upper-case
  token with no vowel (`XKPQLM`) is refused; one with a vowel (`LONDON`) is kept with a warning. The labelled form now allows up to 3
  filler words and more phrases ("my pnr is …", "confirmation code …", "reservation number …", "e-ticket …").

#### Review fixes R1 (independent review 24 Oct 2026; see FIXES-1.1.0-R1.md)

- `.actor/actor.json` version **1.1**, build tag **`test-1-1-0`** (never `latest`); live 1.0.4 stays `latest`.
- A `reference` refused by the personal-data guard is now **replaced by `claim-<n>`** everywhere (dataset, claim pack); the row carries
  `referenceRefused: true` and a note. The refused value is never written.
- Personal-data guard no longer refuses ordinary labels and flight numbers (`claim1`, `trip01`, `row123`, `BA2490`, `U28123`,
  `claim123`). Random 6-character locators (`XK9P2L`, `AB12CD`), labelled PNRs/ticket numbers, passport-style numbers and e-mails are
  still refused. A bare letters-then-digits code such as `ABC123` is now accepted (it cannot be told apart from a label).
- Claim pack: letters are written only for claims estimated `yes`/`maybe` under UK261 or EU261; others are listed under
  "Claims with no letter" with the reason, and each row gets `claimPackLetter {written, reason}`. `assessment.nextSteps` for a `no`
  estimate no longer suggests a claim letter or ADR escalation.
- Limitation: UK261 **Art. 3(9)** (not 3(8)) names Limitation Act 1980 s.9. Scotland's structured `confidence` is now
  `indicative-verify` (was `statutory`). Northern Ireland now cites Limitation (NI) Order 1989 **art. 4(d)** (was art. 6).
- Provider keys shorter than 8 characters are rejected with a clear message; the top-level error handler redacts the key.
- Dataset view renamed "Estimates" / column "Estimate"; README notes the České aerolinie (C-502/18) limitation.

Legal wording, applicability, delay measurement and UK routing improvements. Every 1.0.x input still works unchanged;
all new inputs are optional and all new output fields are additive. Pricing events and prices are unchanged.

#### Legal re-verification (primary sources read 24 Oct 2026)

- Limitation citation corrected: England & Wales claims run under **Limitation Act 1980 s.9** (sums recoverable by statute), which
  UK261 Art. 3(9) now names expressly (SI 2023/1370). 1.0.x cited s.5. Deadline arithmetic unchanged (6 years).
- Added the **Aviation (Consumers) (Amendment) Regulations 2023 (SI 2023/1370**, in force 14 Dec 2023): statutory definitions of
  "arrival time" (when passengers may leave the aircraft), "extraordinary circumstances" and "scheduled arrival time" (Art. 2(n)–(p));
  the single-booking rule in Art. 3(1A); Art. 3(7)–(9) on Montreal and limitation; and the codified three-hour delay rule in
  Art. 6(3)–(4). UK261 delay reasons now cite Art. 6(3) alongside Sturgeon/Nelson.
- Added **Chelluri v Air India \[2021] EWCA Civ 1953**: a single booking on non-UK/EU carriers that starts outside the UK/EU is not
  covered "wherever it lands along the way". The engine now explains this when a UK/EU leg of such a booking is delayed and prompts
  for `journeyOrigin`/`finalDestination` when a non-UK/EU carrier's single leg departs the UK/EU.
- Scotland: 5-year prescription now cites Sch. 1 para. 1(h) of the 1973 Act (payments under an enactment, inserted 28 Feb 2025) and is
  labelled an estimate pending Scottish authority.
- Case-law URLs moved from BAILII to caselaw.nationalarchives.gov.uk (BAILII returns 403 to automated reads).

#### New inputs (all optional)

- `claimPack` (run level, default false): also writes `CLAIM-PACK.md` (one letter template per claim) and `CLAIMS.csv` to the key-value store.
- Per claim: `singleBooking` (true = one booking; false = self-transfer, which ignores `journeyOrigin`/`finalDestination`),
  `marketingCarrier` (informational; warns when it differs from the operating carrier), `arrivalTimeZone` (IANA zone, makes manual
  times clock-change safe), `actualArrivalBasis` (`doors_open` | `gate` | `runway`), `adrBody` (override the UK ADR routing).

#### New outputs (additive)

- `assessment` — stable structured schema **1.0**: `regime, eligible (true|false|"unknown"), band, bandLabel, amountGBP, amountEUR,
  amountIfAirlineReduces, passengers, totalGBP, totalEUR, reasons[], certainty, certaintyBasis, unknowns[], limitationDeadline{},
  adrRoute{body,url,startUrl,court{}}, nextSteps[], evidence[], disclaimer`.
- `certainty` (`likely` | `uncertain` | `unlikely` | `not_applicable`) with an explicit list of what flight data cannot establish
  (cause, reasonable measures, Huzar-type technical faults, knock-on events, door-opening time).
- `applicability` — journey origin/destination zones, leg vs journey, single-booking flag, operating vs marketing carrier, effective
  licence, UK261/EU261 scope bases, post-Brexit flag.
- `bandExplanation[]` — plain-English distance band, GBP and EUR amounts side by side (never converted), 50% conditions.
- UK routing: CAA-approved ADR body by operating airline (AviationADR/CDRL or CEDR, lists read 24 Oct 2026, conflicts flagged),
  PACT for airlines without one, and the court route for England & Wales (Money Claim Online fees), Scotland (Simple Procedure) and
  Northern Ireland (small claims).
- Evidence checklist per disruption type.

#### Wording

- Headlines, reasons, README and the disclaimer now say **estimate**; "likely eligible", "owed", "verdict" and "entitled" removed.
  The disclaimer states that nothing in the output says money is owed and that only an ADR body or court can decide a dispute.

#### Security

- Provider errors are fixed strings: AeroDataBox/AeroAPI 400 responses and Aviationstack error messages are no longer echoed.
  Non-JSON upstream bodies are never carried into errors or outputs.
- Every dataset item, the OUTPUT record and the claim pack pass through `scrubKey` (raw and URL-encoded key).
- Free-text `reference` is refused if it looks like a PNR, passport number, e-mail or labelled identifier (UK GDPR minimisation).
- CSV cells are escaped and formula-injection prefixes neutralised.
- Pipeline moved to `src/run.ts` so the full run (including provider failures that echo the key) is covered by tests.

#### Build

- `npm test` glob fixed for Node 22 (`node --test dist/test/*.test.js`); `.actorignore` excludes `*.bak*`.
- Tests: 75 → 138 (all passing offline); R1 fixes add regression tests (see FIXES-1.1.0-R1.md).

### 1.0.0 — 2026-10-11

- First release.
- Deterministic UK261/EU261 rules engine covering:
  - scope (post-Brexit UK and EU carrier tests, the EEA/Swiss extension, pre-2021 flights, special territories)
  - great-circle distance bands from OurAirports
  - the 3h delay threshold, the door-opening rule and the 50% reductions
  - cancellation notice and re-routing windows
  - denied boarding
  - extraordinary-circumstances case law
  - right to care
  - Montreal Convention notes (2024 limits)
  - claim time limits (England & Wales, Scotland, Northern Ireland; EU indicative)
  - verified CAA, ADR and airline claim links
  - a citation for every reason
- Flight-status adapters that use the end user's own key: AeroDataBox (RapidAPI, API.Market, direct), FlightAware AeroAPI v4 (/flights and /history/flights), Aviationstack, and OAG (experimental). Manual-times mode needs no key. A synthetic `sample` mode is free.
- Flags flights on or after 23 Oct 2027 for EU Regulation 2026/2202.
- Pay-per-event pricing: `flight-checked` and `claim-assessed`.
