Vibecode AirHelp
track this build5 phases, 15 steps, beginner friendly0%A useful personal organizer is buildable, but AirHelp is more than the visible intake form. A local app can assess EC261 and UK261 eligibility, organize evidence, draft letters, and track deadlines. It cannot responsibly reproduce AirHelp's managed airline correspondence, cross-jurisdictional legal judgment, funded court action, and professional accountability in one sitting. This is a credible personal substitute for straightforward self-service claims, not a full replacement.
You are building a lean indie version of AirHelp. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # AirHelp · indie build A local case file per disrupted flight: journey facts, a distance and delay calculation producing a compensation band, an evidence checklist, generated letters citing the right regulation, and deadline tracking that knows the limitation period differs by jurisdiction. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Node 22 with SQLite, running locally | a claim contains passenger names, booking references and correspondence, and there is no reason for it to leave your machine | | Rules | A dated rules table, versioned, separate from the code | amounts and thresholds change and a claim must be assessable against the rules that applied on the flight date | | Distance | Great circle distance between airport coordinates | the regulation's bands are distance based and the calculation must be reproducible | | Documents | Markdown letters rendered to PDF | one source, readable output, and a record of exactly what you sent | | Evidence | Files stored alongside the case with a manifest | a claim is won on evidence and the manifest is what tells you what is missing | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the case file, calculations and document generation. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **A read of the regulation text for your jurisdictions** · free, and it is the first task rather than a formality - Why: Phase 1 builds a table of legal figures, and secondary summaries simplify in ways that change outcomes. Reading the text is the difference between a tool that helps and one that misleads you about your own claim. - Get it: Read the regulation on the official legal database for your jurisdiction, and the enforcement body's guidance. Record what you checked and when. - Verify: The rules table has been corrected against the regulation text and the corrections are recorded - [ ] **An airport coordinate dataset you may use** · free with an open dataset - Why: Distance drives the compensation band, so the coordinates must come from a source you can cite and are licensed to use. - Get it: Use an openly licensed airport dataset and record its licence. Verify a few known city pairs against published distances before trusting it. - Verify: Three known city pair distances match published figures - [ ] **A real disrupted flight, or a detailed hypothetical** · free - Why: Every phase is meaningless against invented data, and real cases have the awkward details: separate bookings, connections, and a vague airline reason. - Get it: Use your own disruption if you have one, including the actual airline correspondence. Otherwise construct one in detail from a friend's. - Verify: A complete journey with real times and a real airline reason is entered - [ ] **A decision about how this is presented** · free - Why: A tool that produces a compensation figure will be read as an answer. If anyone but you uses it, the framing matters more than the calculation. - Get it: Decide whether this stays private. If it does not, write the disclaimer before the first screen, not after someone relies on it. - Verify: The non-advice statement appears on every screen that produces an output ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Legal advice or a judgement on a contested case. - Someone to argue with the airline for you. - No-win no-fee risk sharing. - Case law interpretation of extraordinary circumstances. - AirHelp staff handling airline correspondence and negotiation - case-specific legal judgment across jurisdictions - lawyer access and funded legal action under a no-win, no-fee model - commercial flight, weather, and disruption data used to validate claims - professional support and accountability when a claim becomes contested If one of those is essential to you, that is the reason to keep paying for AirHelp, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · AirHelp The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a private case file for a flight compensation claim: eligibility, evidence, letters and deadlines to replace AirHelp. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero This organises facts and drafts letters. It is not legal advice, it cannot judge a contested case, and it must say so on every screen that produces an output. What the service you are replacing actually sells is a person who argues with the airline for you, and a no-win no-fee arrangement that carries the risk. Neither is code. Every legal figure in this build is a value in a dated table, never a constant in the logic, because these change. The figures below were checked in September 2026 and the prompt requires you to re-check them against the regulation before relying on any output. ### Stack (fixed, do not substitute) - Runtime: Node 22 with SQLite, running locally · a claim contains passenger names, booking references and correspondence, and there is no reason for it to leave your machine - Rules: A dated rules table, versioned, separate from the code · amounts and thresholds change and a claim must be assessable against the rules that applied on the flight date - Distance: Great circle distance between airport coordinates · the regulation's bands are distance based and the calculation must be reproducible - Documents: Markdown letters rendered to PDF · one source, readable output, and a record of exactly what you sent - Evidence: Files stored alongside the case with a manifest · a claim is won on evidence and the manifest is what tells you what is missing ### Data model (create this before Phase 1) - cases(id, created_at, state, jurisdiction, regulation, rules_version) · state is drafting, submitted, escalated, settled or refused - flights(case_id, ordinal, carrier, number, date, origin_iata, destination_iata, scheduled_departure, actual_arrival, distance_km) - passengers(case_id, name, booking_reference, is_claimant) - disruption(case_id, kind, delay_minutes_at_destination, airline_reason, reason_category) · kind is delay, cancellation or denied_boarding - evidence(case_id, kind, path, added_at, notes) · boarding pass, booking confirmation, airline correspondence, receipts - correspondence(case_id, direction, at, channel, subject, body_path, deadline_at) - rules(version, effective_from, jurisdiction, band_km_min, band_km_max, amount, currency, notes, source_url, checked_on) - The rules table carries a source URL and a check date on every row. A figure without both is not usable. ### Phase 1 · The rules table, before any logic Build: Build the rules table first. Enter the compensation bands for each jurisdiction you support with their currency, the delay threshold, the reduction rule for long flights, and the limitation period. Record a source URL and a check date on every row. Then read the regulation itself and correct whatever you got wrong. Trap: A compensation amount written as a constant in the code is a figure nobody will re-check, and these change. Reform proposals were under discussion in 2026 that would replace the current patchwork of national limitation periods with a single deadline, potentially from around the second half of 2027. Keep every figure in dated data so a change is a row rather than a rewrite. Done when: Every band, threshold and limitation period is a row with a source URL and a check date. No monetary amount or duration appears anywhere in the code. A case created for a flight date resolves the rules version that was effective then rather than the newest one. Do not build yet: No eligibility logic, no evidence, no letters. ### Phase 2 · Journey facts and distance Build: Enter the flights of a journey with times and airports. Compute great circle distance between the first departure and the final destination, not the sum of the legs. Compute delay at the final destination, not at the connection where it started. Trap: Two errors here change the answer and both are easy to make. Distance is measured from the first departure to the final destination on a single booking, so a connecting journey is one distance rather than the sum of its legs. Delay is measured at the final destination, so a two hour delay on the first leg that causes a six hour arrival delay is a six hour case. Done when: A connecting journey computes one distance from first origin to final destination rather than summing legs. Delay is computed at the final destination. A journey booked as two separate reservations is flagged as likely two cases rather than treated as one. Distance for a known city pair matches a published figure within a small tolerance. Do not build yet: No eligibility assessment yet. ### Phase 3 · Eligibility, with the uncertainty visible Build: Apply the resolved rules to the journey and produce a band and an amount. Then present the conditions the application cannot assess: whether the carrier and route are within scope, and whether the airline's stated reason is genuinely extraordinary. Show these as open questions rather than resolving them. Trap: Extraordinary circumstances is the airline's defence and the burden of proving it rests with them, but whether a given reason qualifies is a legal judgement that has been litigated extensively and is not reducible to a lookup table. Present the airline's stated reason, note that the burden is theirs, and never output a confident yes or no on it. Done when: A journey with a known distance and delay produces the correct band from the rules table. The output states which conditions were assessed and which are open. An airline reason is recorded and categorised without the application declaring whether it qualifies. Changing the flight date to one before a rules version's effective date changes the amount. Do not build yet: No evidence tracking or letters yet. ### Phase 4 · Evidence, and what is missing Build: An evidence checklist per case kind: booking confirmation, boarding pass, the airline's notification, and receipts for expenses. Store files with the case and show what is missing. Warn where evidence is time sensitive and likely to disappear. Trap: Airline apps and booking sites remove access to a booking within months of travel, and a boarding pass in an email is easy to lose in a search. Capture the evidence at the time of the disruption rather than when you get round to claiming, and make the checklist say which items become unobtainable. Done when: A case shows a checklist with present and missing items. Adding a file records it against the case with a note. Items known to expire are marked with why. The evidence manifest exports as a list you can attach to correspondence. Do not build yet: No letters or deadlines yet. ### Phase 5 · Letters and the deadline patchwork Build: Generate a first letter and an escalation from templates, citing the regulation and the figures from the resolved rules version. Track the limitation period, which differs by jurisdiction: the United Kingdom applies six years from the flight date, while claims in European Union member states run to national limitation law and can range from about a year to several. Warn well before expiry. Trap: Applying one limitation period everywhere will either lose a valid claim or waste effort on an expired one. The period depends on where the claim is brought and is set by national law rather than by the regulation itself, so keep it per jurisdiction in the rules table with its own source, and show the basis alongside the date. Done when: A generated letter cites the regulation, the band and the amount from the resolved rules version. Changing the jurisdiction changes the stated limitation date and shows its basis. A case approaching its limitation date produces a warning with enough notice to act. Every letter sent is stored with its date. Do not build yet: Escalation records and export come last. ### Phase 6 · Escalation, records and knowing when to stop Build: Record the airline's response and the escalation route for the jurisdiction, which is typically a national enforcement body or an approved dispute scheme. Export the complete case as a folder anyone can read. Then write down honestly when handing the claim to a service or a solicitor is the better choice. Trap: A refused claim is where the service you are replacing earns its fee, because contesting a refusal means arguing about evidence and law rather than filling in a form. Record the escalation route, and be honest in your own documentation that a contested case is where a no-win no-fee arrangement is often worth the percentage. Done when: The escalation route for the case's jurisdiction is recorded with a source. A complete case exports as a folder containing the facts, evidence, correspondence and the assessment, readable without this application. The documentation states plainly when handing over is the better option. Do not build yet: Nothing after this. ### Out of scope (and why) - Legal advice of any kind. This organises facts and produces drafts, and every screen that outputs something must say so. - A person who argues with the airline on your behalf, which is the actual service and the reason the fee exists. - No-win no-fee risk sharing. If your claim fails you have spent your own time; a service absorbs that risk for a percentage. - Case law judgement on whether a specific reason is extraordinary, which has been litigated extensively and is not a lookup. - Coverage of every jurisdiction. Support the ones you actually fly and say which those are. ### README must contain - That this is not legal advice and produces drafts rather than decisions - That every legal figure lives in a dated rules table with a source, and must be re-checked before use - That distance is measured end to end and delay at the final destination - That limitation periods differ by jurisdiction, with the United Kingdom at six years and European Union claims following national law - That extraordinary circumstances is the airline's defence with the burden on them, and that this application does not judge it ===== AGENTS.md ===== # Agent instructions · AirHelp indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Node 22 with SQLite, running locally, A dated rules table, versioned, separate from the code, Great circle distance between airport coordinates, Markdown letters rendered to PDF, Files stored alongside the case with a manifest. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - A compensation amount written as a constant in the code is a figure nobody will re-check, and these change. Reform proposals were under discussion in 2026 that would replace the current patchwork of national limitation periods with a single deadline, potentially from around the second half of 2027. Keep every figure in dated data so a change is a row rather than a rewrite. - Two errors here change the answer and both are easy to make. Distance is measured from the first departure to the final destination on a single booking, so a connecting journey is one distance rather than the sum of its legs. Delay is measured at the final destination, so a two hour delay on the first leg that causes a six hour arrival delay is a six hour case. - Extraordinary circumstances is the airline's defence and the burden of proving it rests with them, but whether a given reason qualifies is a legal judgement that has been litigated extensively and is not reducible to a lookup table. Present the airline's stated reason, note that the burden is theirs, and never output a confident yes or no on it. - Airline apps and booking sites remove access to a booking within months of travel, and a boarding pass in an email is easy to lose in a search. Capture the evidence at the time of the disruption rather than when you get round to claiming, and make the checklist say which items become unobtainable. - Applying one limitation period everywhere will either lose a valid claim or waste effort on an expired one. The period depends on where the claim is brought and is set by national law rather than by the regulation itself, so keep it per jurisdiction in the rules table with its own source, and show the basis alongside the date. ===== BUILD_PLAN.md ===== # Build plan · AirHelp A local case file per disrupted flight: journey facts, a distance and delay calculation producing a compensation band, an evidence checklist, generated letters citing the right regulation, and deadline tracking that knows the limitation period differs by jurisdiction. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · The rules table, before any logic Every legal figure lives in dated data with a source, and nothing is hard coded. ### Steps 1. Enter the bands per jurisdiction As of September 2026 the EU bands are 250 euro under 1,500 km, 400 euro from 1,500 to 3,500 km and 600 euro above 3,500 km, and the UK equivalents are 220, 350 and 520 pounds. Enter them with sources and check them yourself. Files: `data/rules.json` 2. Record thresholds and reductions The three hour arrival threshold, and the rule allowing a reduction for a long flight arriving between three and four hours late. Both as data. Files: `data/rules.json` 3. Read the regulation and correct the table Secondary sources summarise and simplify. Read the text itself for the rows you entered, and record what you changed. Files: `docs/rules-review.md` ### Done when - [ ] Every rule row has a source URL and a check date - [ ] No monetary amount appears in code - [ ] A case resolves the rules version effective on its flight date - [ ] The table was corrected against the regulation text, and the changes are recorded ### Watch out - A compensation amount written as a constant in the code is a figure nobody will re-check, and these change. Reform proposals were under discussion in 2026 that would replace the current patchwork of national limitation periods with a single deadline, potentially from around the second half of 2027. Keep every figure in dated data so a change is a row rather than a rewrite. ## Phase 2 · Journey facts and distance A case records the journey accurately, and the distance is computed the way the regulation means it. ### Steps 1. Store flights as an ordered journey With scheduled and actual times per leg, and a flag for whether they share one booking reference. Files: `src/journey.js` 2. Compute great circle distance end to end First origin to final destination. Verify against a published distance for a city pair you know. Files: `src/distance.js` 3. Compute delay at the destination Actual arrival against scheduled arrival at the final destination. This is the number the regulation uses. Files: `src/delay.js` ### Done when - [ ] A connecting journey yields one end-to-end distance - [ ] Delay is computed at the final destination - [ ] Separate bookings are flagged as likely separate cases - [ ] A known city pair distance matches a published figure ### Watch out - Two errors here change the answer and both are easy to make. Distance is measured from the first departure to the final destination on a single booking, so a connecting journey is one distance rather than the sum of its legs. Delay is measured at the final destination, so a two hour delay on the first leg that causes a six hour arrival delay is a six hour case. ## Phase 3 · Eligibility, with the uncertainty visible The application says which band applies and, just as clearly, what it cannot decide. ### Steps 1. Apply the resolved rules Distance and delay to a band and amount, using the version effective on the flight date. Files: `src/assess.js` 2. Separate assessed from open Two lists in the output. Anything requiring judgement goes in the second list with a note on where the burden lies. Files: `src/assess.js` 3. Record the airline's reason without judging it Categorised for your own tracking, with the burden of proof noted, and no verdict. Files: `src/reasons.js` ### Done when - [ ] A known journey produces the correct band - [ ] Open questions are listed separately from assessed conditions - [ ] The airline's reason is recorded without a verdict - [ ] An earlier flight date resolves an earlier rules version ### Watch out - Extraordinary circumstances is the airline's defence and the burden of proving it rests with them, but whether a given reason qualifies is a legal judgement that has been litigated extensively and is not reducible to a lookup table. Present the airline's stated reason, note that the burden is theirs, and never output a confident yes or no on it. ## Phase 4 · Evidence, and what is missing The case knows what it has and what it still needs. ### Steps 1. Define checklists per disruption kind Delay, cancellation and denied boarding need different evidence. Keep the lists as data. Files: `data/checklists.json` 2. Store files with a manifest In a case folder with recorded kinds and notes. The manifest is what you attach. Files: `src/evidence.js` 3. Flag time-sensitive items Booking access and app records disappear. Say which and how soon. Files: `data/checklists.json` ### Done when - [ ] The checklist shows present and missing items - [ ] Adding a file records it with its kind - [ ] Time-sensitive items are flagged with a reason - [ ] The manifest exports as an attachable list ### Watch out - Airline apps and booking sites remove access to a booking within months of travel, and a boarding pass in an email is easy to lose in a search. Capture the evidence at the time of the disruption rather than when you get round to claiming, and make the checklist say which items become unobtainable. ## Phase 5 · Letters and the deadline patchwork You can send a correctly framed letter and you know when the claim expires. ### Steps 1. Template the letters First claim and escalation, both drawing figures from the rules version rather than restating them. Files: `templates/` 2. Compute limitation per jurisdiction From the rules table, with the basis shown next to the date. Never one global period. Files: `src/deadlines.js` 3. Warn early and store what was sent A warning with real notice, and a copy of every letter with its date. Files: `src/correspondence.js` ### Done when - [ ] A letter cites the resolved band and amount - [ ] Changing jurisdiction changes the limitation date and shows its basis - [ ] An approaching deadline produces an early warning - [ ] Every sent letter is stored with its date ### Watch out - Applying one limitation period everywhere will either lose a valid claim or waste effort on an expired one. The period depends on where the claim is brought and is set by national law rather than by the regulation itself, so keep it per jurisdiction in the rules table with its own source, and show the basis alongside the date. ## Not in this build - Legal advice or a judgement on a contested case. - Someone to argue with the airline for you. - No-win no-fee risk sharing. - Case law interpretation of extraordinary circumstances. ## After v1, if you want it - A capture flow for the moment of disruption, since evidence is easiest to collect at the airport - Support for a second jurisdiction, which is a rules table addition rather than code - A reminder that checks stale rules rows against their sources on a schedule ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Case files, containing passenger names and booking references. Local, and worth encrypting the backup. DB_PATH=./data/cases.db # Required. Where evidence files are stored, one folder per case. EVIDENCE_DIR=./data/evidence # Required. Which rules set to resolve by default. Changing it changes amounts, currency and limitation period. DEFAULT_JURISDICTION=UK # Required. How far ahead of the limitation date to warn. Long enough to actually act. DEADLINE_WARN_DAYS=90 # Required. The dated rules table. Every figure here carries a source and a check date. RULES_PATH=./data/rules.json
You are building a lean indie version of AirHelp. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # AirHelp · indie build A local case file per disrupted flight: journey facts, a distance and delay calculation producing a compensation band, an evidence checklist, generated letters citing the right regulation, and deadline tracking that knows the limitation period differs by jurisdiction. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Node 22 with SQLite, running locally | a claim contains passenger names, booking references and correspondence, and there is no reason for it to leave your machine | | Rules | A dated rules table, versioned, separate from the code | amounts and thresholds change and a claim must be assessable against the rules that applied on the flight date | | Distance | Great circle distance between airport coordinates | the regulation's bands are distance based and the calculation must be reproducible | | Documents | Markdown letters rendered to PDF | one source, readable output, and a record of exactly what you sent | | Evidence | Files stored alongside the case with a manifest | a claim is won on evidence and the manifest is what tells you what is missing | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the case file, calculations and document generation. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **A read of the regulation text for your jurisdictions** · free, and it is the first task rather than a formality - Why: Phase 1 builds a table of legal figures, and secondary summaries simplify in ways that change outcomes. Reading the text is the difference between a tool that helps and one that misleads you about your own claim. - Get it: Read the regulation on the official legal database for your jurisdiction, and the enforcement body's guidance. Record what you checked and when. - Verify: The rules table has been corrected against the regulation text and the corrections are recorded - [ ] **An airport coordinate dataset you may use** · free with an open dataset - Why: Distance drives the compensation band, so the coordinates must come from a source you can cite and are licensed to use. - Get it: Use an openly licensed airport dataset and record its licence. Verify a few known city pairs against published distances before trusting it. - Verify: Three known city pair distances match published figures - [ ] **A real disrupted flight, or a detailed hypothetical** · free - Why: Every phase is meaningless against invented data, and real cases have the awkward details: separate bookings, connections, and a vague airline reason. - Get it: Use your own disruption if you have one, including the actual airline correspondence. Otherwise construct one in detail from a friend's. - Verify: A complete journey with real times and a real airline reason is entered - [ ] **A decision about how this is presented** · free - Why: A tool that produces a compensation figure will be read as an answer. If anyone but you uses it, the framing matters more than the calculation. - Get it: Decide whether this stays private. If it does not, write the disclaimer before the first screen, not after someone relies on it. - Verify: The non-advice statement appears on every screen that produces an output ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Legal advice or a judgement on a contested case. - Someone to argue with the airline for you. - No-win no-fee risk sharing. - Case law interpretation of extraordinary circumstances. - AirHelp staff handling airline correspondence and negotiation - case-specific legal judgment across jurisdictions - lawyer access and funded legal action under a no-win, no-fee model - commercial flight, weather, and disruption data used to validate claims - professional support and accountability when a claim becomes contested If one of those is essential to you, that is the reason to keep paying for AirHelp, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · AirHelp The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a private case file for a flight compensation claim: eligibility, evidence, letters and deadlines to replace AirHelp. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero This organises facts and drafts letters. It is not legal advice, it cannot judge a contested case, and it must say so on every screen that produces an output. What the service you are replacing actually sells is a person who argues with the airline for you, and a no-win no-fee arrangement that carries the risk. Neither is code. Every legal figure in this build is a value in a dated table, never a constant in the logic, because these change. The figures below were checked in September 2026 and the prompt requires you to re-check them against the regulation before relying on any output. ### Stack (fixed, do not substitute) - Runtime: Node 22 with SQLite, running locally · a claim contains passenger names, booking references and correspondence, and there is no reason for it to leave your machine - Rules: A dated rules table, versioned, separate from the code · amounts and thresholds change and a claim must be assessable against the rules that applied on the flight date - Distance: Great circle distance between airport coordinates · the regulation's bands are distance based and the calculation must be reproducible - Documents: Markdown letters rendered to PDF · one source, readable output, and a record of exactly what you sent - Evidence: Files stored alongside the case with a manifest · a claim is won on evidence and the manifest is what tells you what is missing ### Data model (create this before Phase 1) - cases(id, created_at, state, jurisdiction, regulation, rules_version) · state is drafting, submitted, escalated, settled or refused - flights(case_id, ordinal, carrier, number, date, origin_iata, destination_iata, scheduled_departure, actual_arrival, distance_km) - passengers(case_id, name, booking_reference, is_claimant) - disruption(case_id, kind, delay_minutes_at_destination, airline_reason, reason_category) · kind is delay, cancellation or denied_boarding - evidence(case_id, kind, path, added_at, notes) · boarding pass, booking confirmation, airline correspondence, receipts - correspondence(case_id, direction, at, channel, subject, body_path, deadline_at) - rules(version, effective_from, jurisdiction, band_km_min, band_km_max, amount, currency, notes, source_url, checked_on) - The rules table carries a source URL and a check date on every row. A figure without both is not usable. ### Phase 1 · The rules table, before any logic Build: Build the rules table first. Enter the compensation bands for each jurisdiction you support with their currency, the delay threshold, the reduction rule for long flights, and the limitation period. Record a source URL and a check date on every row. Then read the regulation itself and correct whatever you got wrong. Trap: A compensation amount written as a constant in the code is a figure nobody will re-check, and these change. Reform proposals were under discussion in 2026 that would replace the current patchwork of national limitation periods with a single deadline, potentially from around the second half of 2027. Keep every figure in dated data so a change is a row rather than a rewrite. Done when: Every band, threshold and limitation period is a row with a source URL and a check date. No monetary amount or duration appears anywhere in the code. A case created for a flight date resolves the rules version that was effective then rather than the newest one. Do not build yet: No eligibility logic, no evidence, no letters. ### Phase 2 · Journey facts and distance Build: Enter the flights of a journey with times and airports. Compute great circle distance between the first departure and the final destination, not the sum of the legs. Compute delay at the final destination, not at the connection where it started. Trap: Two errors here change the answer and both are easy to make. Distance is measured from the first departure to the final destination on a single booking, so a connecting journey is one distance rather than the sum of its legs. Delay is measured at the final destination, so a two hour delay on the first leg that causes a six hour arrival delay is a six hour case. Done when: A connecting journey computes one distance from first origin to final destination rather than summing legs. Delay is computed at the final destination. A journey booked as two separate reservations is flagged as likely two cases rather than treated as one. Distance for a known city pair matches a published figure within a small tolerance. Do not build yet: No eligibility assessment yet. ### Phase 3 · Eligibility, with the uncertainty visible Build: Apply the resolved rules to the journey and produce a band and an amount. Then present the conditions the application cannot assess: whether the carrier and route are within scope, and whether the airline's stated reason is genuinely extraordinary. Show these as open questions rather than resolving them. Trap: Extraordinary circumstances is the airline's defence and the burden of proving it rests with them, but whether a given reason qualifies is a legal judgement that has been litigated extensively and is not reducible to a lookup table. Present the airline's stated reason, note that the burden is theirs, and never output a confident yes or no on it. Done when: A journey with a known distance and delay produces the correct band from the rules table. The output states which conditions were assessed and which are open. An airline reason is recorded and categorised without the application declaring whether it qualifies. Changing the flight date to one before a rules version's effective date changes the amount. Do not build yet: No evidence tracking or letters yet. ### Phase 4 · Evidence, and what is missing Build: An evidence checklist per case kind: booking confirmation, boarding pass, the airline's notification, and receipts for expenses. Store files with the case and show what is missing. Warn where evidence is time sensitive and likely to disappear. Trap: Airline apps and booking sites remove access to a booking within months of travel, and a boarding pass in an email is easy to lose in a search. Capture the evidence at the time of the disruption rather than when you get round to claiming, and make the checklist say which items become unobtainable. Done when: A case shows a checklist with present and missing items. Adding a file records it against the case with a note. Items known to expire are marked with why. The evidence manifest exports as a list you can attach to correspondence. Do not build yet: No letters or deadlines yet. ### Phase 5 · Letters and the deadline patchwork Build: Generate a first letter and an escalation from templates, citing the regulation and the figures from the resolved rules version. Track the limitation period, which differs by jurisdiction: the United Kingdom applies six years from the flight date, while claims in European Union member states run to national limitation law and can range from about a year to several. Warn well before expiry. Trap: Applying one limitation period everywhere will either lose a valid claim or waste effort on an expired one. The period depends on where the claim is brought and is set by national law rather than by the regulation itself, so keep it per jurisdiction in the rules table with its own source, and show the basis alongside the date. Done when: A generated letter cites the regulation, the band and the amount from the resolved rules version. Changing the jurisdiction changes the stated limitation date and shows its basis. A case approaching its limitation date produces a warning with enough notice to act. Every letter sent is stored with its date. Do not build yet: Escalation records and export come last. ### Phase 6 · Escalation, records and knowing when to stop Build: Record the airline's response and the escalation route for the jurisdiction, which is typically a national enforcement body or an approved dispute scheme. Export the complete case as a folder anyone can read. Then write down honestly when handing the claim to a service or a solicitor is the better choice. Trap: A refused claim is where the service you are replacing earns its fee, because contesting a refusal means arguing about evidence and law rather than filling in a form. Record the escalation route, and be honest in your own documentation that a contested case is where a no-win no-fee arrangement is often worth the percentage. Done when: The escalation route for the case's jurisdiction is recorded with a source. A complete case exports as a folder containing the facts, evidence, correspondence and the assessment, readable without this application. The documentation states plainly when handing over is the better option. Do not build yet: Nothing after this. ### Out of scope (and why) - Legal advice of any kind. This organises facts and produces drafts, and every screen that outputs something must say so. - A person who argues with the airline on your behalf, which is the actual service and the reason the fee exists. - No-win no-fee risk sharing. If your claim fails you have spent your own time; a service absorbs that risk for a percentage. - Case law judgement on whether a specific reason is extraordinary, which has been litigated extensively and is not a lookup. - Coverage of every jurisdiction. Support the ones you actually fly and say which those are. ### README must contain - That this is not legal advice and produces drafts rather than decisions - That every legal figure lives in a dated rules table with a source, and must be re-checked before use - That distance is measured end to end and delay at the final destination - That limitation periods differ by jurisdiction, with the United Kingdom at six years and European Union claims following national law - That extraordinary circumstances is the airline's defence with the burden on them, and that this application does not judge it ===== AGENTS.md ===== # Agent instructions · AirHelp indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Node 22 with SQLite, running locally, A dated rules table, versioned, separate from the code, Great circle distance between airport coordinates, Markdown letters rendered to PDF, Files stored alongside the case with a manifest. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - A compensation amount written as a constant in the code is a figure nobody will re-check, and these change. Reform proposals were under discussion in 2026 that would replace the current patchwork of national limitation periods with a single deadline, potentially from around the second half of 2027. Keep every figure in dated data so a change is a row rather than a rewrite. - Two errors here change the answer and both are easy to make. Distance is measured from the first departure to the final destination on a single booking, so a connecting journey is one distance rather than the sum of its legs. Delay is measured at the final destination, so a two hour delay on the first leg that causes a six hour arrival delay is a six hour case. - Extraordinary circumstances is the airline's defence and the burden of proving it rests with them, but whether a given reason qualifies is a legal judgement that has been litigated extensively and is not reducible to a lookup table. Present the airline's stated reason, note that the burden is theirs, and never output a confident yes or no on it. - Airline apps and booking sites remove access to a booking within months of travel, and a boarding pass in an email is easy to lose in a search. Capture the evidence at the time of the disruption rather than when you get round to claiming, and make the checklist say which items become unobtainable. - Applying one limitation period everywhere will either lose a valid claim or waste effort on an expired one. The period depends on where the claim is brought and is set by national law rather than by the regulation itself, so keep it per jurisdiction in the rules table with its own source, and show the basis alongside the date. ===== BUILD_PLAN.md ===== # Build plan · AirHelp A local case file per disrupted flight: journey facts, a distance and delay calculation producing a compensation band, an evidence checklist, generated letters citing the right regulation, and deadline tracking that knows the limitation period differs by jurisdiction. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · The rules table, before any logic Every legal figure lives in dated data with a source, and nothing is hard coded. ### Steps 1. Enter the bands per jurisdiction As of September 2026 the EU bands are 250 euro under 1,500 km, 400 euro from 1,500 to 3,500 km and 600 euro above 3,500 km, and the UK equivalents are 220, 350 and 520 pounds. Enter them with sources and check them yourself. Files: `data/rules.json` 2. Record thresholds and reductions The three hour arrival threshold, and the rule allowing a reduction for a long flight arriving between three and four hours late. Both as data. Files: `data/rules.json` 3. Read the regulation and correct the table Secondary sources summarise and simplify. Read the text itself for the rows you entered, and record what you changed. Files: `docs/rules-review.md` ### Done when - [ ] Every rule row has a source URL and a check date - [ ] No monetary amount appears in code - [ ] A case resolves the rules version effective on its flight date - [ ] The table was corrected against the regulation text, and the changes are recorded ### Watch out - A compensation amount written as a constant in the code is a figure nobody will re-check, and these change. Reform proposals were under discussion in 2026 that would replace the current patchwork of national limitation periods with a single deadline, potentially from around the second half of 2027. Keep every figure in dated data so a change is a row rather than a rewrite. ## Phase 2 · Journey facts and distance A case records the journey accurately, and the distance is computed the way the regulation means it. ### Steps 1. Store flights as an ordered journey With scheduled and actual times per leg, and a flag for whether they share one booking reference. Files: `src/journey.js` 2. Compute great circle distance end to end First origin to final destination. Verify against a published distance for a city pair you know. Files: `src/distance.js` 3. Compute delay at the destination Actual arrival against scheduled arrival at the final destination. This is the number the regulation uses. Files: `src/delay.js` ### Done when - [ ] A connecting journey yields one end-to-end distance - [ ] Delay is computed at the final destination - [ ] Separate bookings are flagged as likely separate cases - [ ] A known city pair distance matches a published figure ### Watch out - Two errors here change the answer and both are easy to make. Distance is measured from the first departure to the final destination on a single booking, so a connecting journey is one distance rather than the sum of its legs. Delay is measured at the final destination, so a two hour delay on the first leg that causes a six hour arrival delay is a six hour case. ## Phase 3 · Eligibility, with the uncertainty visible The application says which band applies and, just as clearly, what it cannot decide. ### Steps 1. Apply the resolved rules Distance and delay to a band and amount, using the version effective on the flight date. Files: `src/assess.js` 2. Separate assessed from open Two lists in the output. Anything requiring judgement goes in the second list with a note on where the burden lies. Files: `src/assess.js` 3. Record the airline's reason without judging it Categorised for your own tracking, with the burden of proof noted, and no verdict. Files: `src/reasons.js` ### Done when - [ ] A known journey produces the correct band - [ ] Open questions are listed separately from assessed conditions - [ ] The airline's reason is recorded without a verdict - [ ] An earlier flight date resolves an earlier rules version ### Watch out - Extraordinary circumstances is the airline's defence and the burden of proving it rests with them, but whether a given reason qualifies is a legal judgement that has been litigated extensively and is not reducible to a lookup table. Present the airline's stated reason, note that the burden is theirs, and never output a confident yes or no on it. ## Phase 4 · Evidence, and what is missing The case knows what it has and what it still needs. ### Steps 1. Define checklists per disruption kind Delay, cancellation and denied boarding need different evidence. Keep the lists as data. Files: `data/checklists.json` 2. Store files with a manifest In a case folder with recorded kinds and notes. The manifest is what you attach. Files: `src/evidence.js` 3. Flag time-sensitive items Booking access and app records disappear. Say which and how soon. Files: `data/checklists.json` ### Done when - [ ] The checklist shows present and missing items - [ ] Adding a file records it with its kind - [ ] Time-sensitive items are flagged with a reason - [ ] The manifest exports as an attachable list ### Watch out - Airline apps and booking sites remove access to a booking within months of travel, and a boarding pass in an email is easy to lose in a search. Capture the evidence at the time of the disruption rather than when you get round to claiming, and make the checklist say which items become unobtainable. ## Phase 5 · Letters and the deadline patchwork You can send a correctly framed letter and you know when the claim expires. ### Steps 1. Template the letters First claim and escalation, both drawing figures from the rules version rather than restating them. Files: `templates/` 2. Compute limitation per jurisdiction From the rules table, with the basis shown next to the date. Never one global period. Files: `src/deadlines.js` 3. Warn early and store what was sent A warning with real notice, and a copy of every letter with its date. Files: `src/correspondence.js` ### Done when - [ ] A letter cites the resolved band and amount - [ ] Changing jurisdiction changes the limitation date and shows its basis - [ ] An approaching deadline produces an early warning - [ ] Every sent letter is stored with its date ### Watch out - Applying one limitation period everywhere will either lose a valid claim or waste effort on an expired one. The period depends on where the claim is brought and is set by national law rather than by the regulation itself, so keep it per jurisdiction in the rules table with its own source, and show the basis alongside the date. ## Not in this build - Legal advice or a judgement on a contested case. - Someone to argue with the airline for you. - No-win no-fee risk sharing. - Case law interpretation of extraordinary circumstances. ## After v1, if you want it - A capture flow for the moment of disruption, since evidence is easiest to collect at the airport - Support for a second jurisdiction, which is a rules table addition rather than code - A reminder that checks stale rules rows against their sources on a schedule ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Case files, containing passenger names and booking references. Local, and worth encrypting the backup. DB_PATH=./data/cases.db # Required. Where evidence files are stored, one folder per case. EVIDENCE_DIR=./data/evidence # Required. Which rules set to resolve by default. Changing it changes amounts, currency and limitation period. DEFAULT_JURISDICTION=UK # Required. How far ahead of the limitation date to warn. Long enough to actually act. DEADLINE_WARN_DAYS=90 # Required. The dated rules table. Every figure here carries a source and a check date. RULES_PATH=./data/rules.json
You are building a production product version of AirHelp. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # AirHelp · product brief ## Problem A useful personal organizer is buildable, but AirHelp is more than the visible intake form. A local app can assess EC261 and UK261 eligibility, organize evidence, draft letters, and track deadlines. It cannot responsibly reproduce AirHelp's managed airline correspondence, cross-jurisdictional legal judgment, funded court action, and professional accountability in one sitting. This is a credible personal substitute for straightforward self-service claims, not a full replacement. ## Product outcome A case file that computes a compensation band from dated rules rather than hard coded figures, keeps evidence before it disappears, tracks a jurisdiction-correct deadline, produces sendable letters, and says plainly what it cannot decide. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - Node.js 22 - modern browser - SQLite - user-entered flight, passenger, and evidence data - current official passenger-rights sources ## Explicit non-goals for v1 - Legal advice or a judgement on a contested case. - Someone to argue with the airline for you. - No-win no-fee risk sharing. - Case law interpretation of extraordinary circumstances. - AirHelp staff handling airline correspondence and negotiation - case-specific legal judgment across jurisdictions - lawyer access and funded legal action under a no-win, no-fee model - commercial flight, weather, and disruption data used to validate claims - professional support and accountability when a claim becomes contested ## Success criteria - Every rules row has a source and a check date within your staleness window - Distance and delay are verified against a real case - The non-advice statement appears on every output screen - The limitation date changes correctly with jurisdiction - A case exports as a folder readable without this application ===== BRIEF.md ===== # Build brief · AirHelp The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a private case file for a flight compensation claim: eligibility, evidence, letters and deadlines to replace AirHelp. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero This organises facts and drafts letters. It is not legal advice, it cannot judge a contested case, and it must say so on every screen that produces an output. What the service you are replacing actually sells is a person who argues with the airline for you, and a no-win no-fee arrangement that carries the risk. Neither is code. Every legal figure in this build is a value in a dated table, never a constant in the logic, because these change. The figures below were checked in September 2026 and the prompt requires you to re-check them against the regulation before relying on any output. ### Stack (fixed, do not substitute) - Runtime: Node 22 with SQLite, running locally · a claim contains passenger names, booking references and correspondence, and there is no reason for it to leave your machine - Rules: A dated rules table, versioned, separate from the code · amounts and thresholds change and a claim must be assessable against the rules that applied on the flight date - Distance: Great circle distance between airport coordinates · the regulation's bands are distance based and the calculation must be reproducible - Documents: Markdown letters rendered to PDF · one source, readable output, and a record of exactly what you sent - Evidence: Files stored alongside the case with a manifest · a claim is won on evidence and the manifest is what tells you what is missing ### Data model (create this before Phase 1) - cases(id, created_at, state, jurisdiction, regulation, rules_version) · state is drafting, submitted, escalated, settled or refused - flights(case_id, ordinal, carrier, number, date, origin_iata, destination_iata, scheduled_departure, actual_arrival, distance_km) - passengers(case_id, name, booking_reference, is_claimant) - disruption(case_id, kind, delay_minutes_at_destination, airline_reason, reason_category) · kind is delay, cancellation or denied_boarding - evidence(case_id, kind, path, added_at, notes) · boarding pass, booking confirmation, airline correspondence, receipts - correspondence(case_id, direction, at, channel, subject, body_path, deadline_at) - rules(version, effective_from, jurisdiction, band_km_min, band_km_max, amount, currency, notes, source_url, checked_on) - The rules table carries a source URL and a check date on every row. A figure without both is not usable. ### Phase 1 · The rules table, before any logic Build: Build the rules table first. Enter the compensation bands for each jurisdiction you support with their currency, the delay threshold, the reduction rule for long flights, and the limitation period. Record a source URL and a check date on every row. Then read the regulation itself and correct whatever you got wrong. Trap: A compensation amount written as a constant in the code is a figure nobody will re-check, and these change. Reform proposals were under discussion in 2026 that would replace the current patchwork of national limitation periods with a single deadline, potentially from around the second half of 2027. Keep every figure in dated data so a change is a row rather than a rewrite. Done when: Every band, threshold and limitation period is a row with a source URL and a check date. No monetary amount or duration appears anywhere in the code. A case created for a flight date resolves the rules version that was effective then rather than the newest one. Do not build yet: No eligibility logic, no evidence, no letters. ### Phase 2 · Journey facts and distance Build: Enter the flights of a journey with times and airports. Compute great circle distance between the first departure and the final destination, not the sum of the legs. Compute delay at the final destination, not at the connection where it started. Trap: Two errors here change the answer and both are easy to make. Distance is measured from the first departure to the final destination on a single booking, so a connecting journey is one distance rather than the sum of its legs. Delay is measured at the final destination, so a two hour delay on the first leg that causes a six hour arrival delay is a six hour case. Done when: A connecting journey computes one distance from first origin to final destination rather than summing legs. Delay is computed at the final destination. A journey booked as two separate reservations is flagged as likely two cases rather than treated as one. Distance for a known city pair matches a published figure within a small tolerance. Do not build yet: No eligibility assessment yet. ### Phase 3 · Eligibility, with the uncertainty visible Build: Apply the resolved rules to the journey and produce a band and an amount. Then present the conditions the application cannot assess: whether the carrier and route are within scope, and whether the airline's stated reason is genuinely extraordinary. Show these as open questions rather than resolving them. Trap: Extraordinary circumstances is the airline's defence and the burden of proving it rests with them, but whether a given reason qualifies is a legal judgement that has been litigated extensively and is not reducible to a lookup table. Present the airline's stated reason, note that the burden is theirs, and never output a confident yes or no on it. Done when: A journey with a known distance and delay produces the correct band from the rules table. The output states which conditions were assessed and which are open. An airline reason is recorded and categorised without the application declaring whether it qualifies. Changing the flight date to one before a rules version's effective date changes the amount. Do not build yet: No evidence tracking or letters yet. ### Phase 4 · Evidence, and what is missing Build: An evidence checklist per case kind: booking confirmation, boarding pass, the airline's notification, and receipts for expenses. Store files with the case and show what is missing. Warn where evidence is time sensitive and likely to disappear. Trap: Airline apps and booking sites remove access to a booking within months of travel, and a boarding pass in an email is easy to lose in a search. Capture the evidence at the time of the disruption rather than when you get round to claiming, and make the checklist say which items become unobtainable. Done when: A case shows a checklist with present and missing items. Adding a file records it against the case with a note. Items known to expire are marked with why. The evidence manifest exports as a list you can attach to correspondence. Do not build yet: No letters or deadlines yet. ### Phase 5 · Letters and the deadline patchwork Build: Generate a first letter and an escalation from templates, citing the regulation and the figures from the resolved rules version. Track the limitation period, which differs by jurisdiction: the United Kingdom applies six years from the flight date, while claims in European Union member states run to national limitation law and can range from about a year to several. Warn well before expiry. Trap: Applying one limitation period everywhere will either lose a valid claim or waste effort on an expired one. The period depends on where the claim is brought and is set by national law rather than by the regulation itself, so keep it per jurisdiction in the rules table with its own source, and show the basis alongside the date. Done when: A generated letter cites the regulation, the band and the amount from the resolved rules version. Changing the jurisdiction changes the stated limitation date and shows its basis. A case approaching its limitation date produces a warning with enough notice to act. Every letter sent is stored with its date. Do not build yet: Escalation records and export come last. ### Phase 6 · Escalation, records and knowing when to stop Build: Record the airline's response and the escalation route for the jurisdiction, which is typically a national enforcement body or an approved dispute scheme. Export the complete case as a folder anyone can read. Then write down honestly when handing the claim to a service or a solicitor is the better choice. Trap: A refused claim is where the service you are replacing earns its fee, because contesting a refusal means arguing about evidence and law rather than filling in a form. Record the escalation route, and be honest in your own documentation that a contested case is where a no-win no-fee arrangement is often worth the percentage. Done when: The escalation route for the case's jurisdiction is recorded with a source. A complete case exports as a folder containing the facts, evidence, correspondence and the assessment, readable without this application. The documentation states plainly when handing over is the better option. Do not build yet: Nothing after this. ### Out of scope (and why) - Legal advice of any kind. This organises facts and produces drafts, and every screen that outputs something must say so. - A person who argues with the airline on your behalf, which is the actual service and the reason the fee exists. - No-win no-fee risk sharing. If your claim fails you have spent your own time; a service absorbs that risk for a percentage. - Case law judgement on whether a specific reason is extraordinary, which has been litigated extensively and is not a lookup. - Coverage of every jurisdiction. Support the ones you actually fly and say which those are. ### README must contain - That this is not legal advice and produces drafts rather than decisions - That every legal figure lives in a dated rules table with a source, and must be re-checked before use - That distance is measured end to end and delay at the final destination - That limitation periods differ by jurisdiction, with the United Kingdom at six years and European Union claims following national law - That extraordinary circumstances is the airline's defence with the burden on them, and that this application does not judge it ===== ARCHITECTURE.md ===== # Architecture · AirHelp ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Node 22 with SQLite, running locally | a claim contains passenger names, booking references and correspondence, and there is no reason for it to leave your machine | | Rules | A dated rules table, versioned, separate from the code | amounts and thresholds change and a claim must be assessable against the rules that applied on the flight date | | Distance | Great circle distance between airport coordinates | the regulation's bands are distance based and the calculation must be reproducible | | Documents | Markdown letters rendered to PDF | one source, readable output, and a record of exactly what you sent | | Evidence | Files stored alongside the case with a manifest | a claim is won on evidence and the manifest is what tells you what is missing | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Rules | Every legal figure, dated and sourced. | This is the module that makes the tool maintainable. If a figure ever moves into code, the tool has started rotting. | | Journey | Flights, distance and delay. | Pure computation, testable against published distances and known cases. | | Assess | Applying rules and separating open questions. | Any implementation, provided it never resolves a question that requires legal judgement. | | Evidence | Files and the checklist. | Straightforward. The value is the checklist saying what expires. | | Correspondence | Letters and deadlines. | Templates are data. The per-jurisdiction limitation period is the part that must not be simplified. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DB_PATH` · required · Case files, containing passenger names and booking references. Local, and worth encrypting the backup. - `EVIDENCE_DIR` · required · Where evidence files are stored, one folder per case. - `DEFAULT_JURISDICTION` · required · Which rules set to resolve by default. Changing it changes amounts, currency and limitation period. - `DEADLINE_WARN_DAYS` · required · How far ahead of the limitation date to warn. Long enough to actually act. - `RULES_PATH` · required · The dated rules table. Every figure here carries a source and a check date. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · AirHelp product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Node 22 with SQLite, running locally, A dated rules table, versioned, separate from the code, Great circle distance between airport coordinates, Markdown letters rendered to PDF, Files stored alongside the case with a manifest. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - A compensation amount written as a constant in the code is a figure nobody will re-check, and these change. Reform proposals were under discussion in 2026 that would replace the current patchwork of national limitation periods with a single deadline, potentially from around the second half of 2027. Keep every figure in dated data so a change is a row rather than a rewrite. - Two errors here change the answer and both are easy to make. Distance is measured from the first departure to the final destination on a single booking, so a connecting journey is one distance rather than the sum of its legs. Delay is measured at the final destination, so a two hour delay on the first leg that causes a six hour arrival delay is a six hour case. - Extraordinary circumstances is the airline's defence and the burden of proving it rests with them, but whether a given reason qualifies is a legal judgement that has been litigated extensively and is not reducible to a lookup table. Present the airline's stated reason, note that the burden is theirs, and never output a confident yes or no on it. - Airline apps and booking sites remove access to a booking within months of travel, and a boarding pass in an email is easy to lose in a search. Capture the evidence at the time of the disruption rather than when you get round to claiming, and make the checklist say which items become unobtainable. - Applying one limitation period everywhere will either lose a valid claim or waste effort on an expired one. The period depends on where the claim is brought and is set by national law rather than by the regulation itself, so keep it per jurisdiction in the rules table with its own source, and show the basis alongside the date. - A refused claim is where the service you are replacing earns its fee, because contesting a refusal means arguing about evidence and law rather than filling in a form. Record the escalation route, and be honest in your own documentation that a contested case is where a no-win no-fee arrangement is often worth the percentage. ===== MILESTONES.md ===== # Delivery milestones · AirHelp Estimated effort: **weekend** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · The rules table, before any logic Every legal figure lives in dated data with a source, and nothing is hard coded. ### Steps 1. Enter the bands per jurisdiction As of September 2026 the EU bands are 250 euro under 1,500 km, 400 euro from 1,500 to 3,500 km and 600 euro above 3,500 km, and the UK equivalents are 220, 350 and 520 pounds. Enter them with sources and check them yourself. Files: `data/rules.json` 2. Record thresholds and reductions The three hour arrival threshold, and the rule allowing a reduction for a long flight arriving between three and four hours late. Both as data. Files: `data/rules.json` 3. Read the regulation and correct the table Secondary sources summarise and simplify. Read the text itself for the rows you entered, and record what you changed. Files: `docs/rules-review.md` ### Done when - [ ] Every rule row has a source URL and a check date - [ ] No monetary amount appears in code - [ ] A case resolves the rules version effective on its flight date - [ ] The table was corrected against the regulation text, and the changes are recorded ### Watch out - A compensation amount written as a constant in the code is a figure nobody will re-check, and these change. Reform proposals were under discussion in 2026 that would replace the current patchwork of national limitation periods with a single deadline, potentially from around the second half of 2027. Keep every figure in dated data so a change is a row rather than a rewrite. ## M2 · Journey facts and distance A case records the journey accurately, and the distance is computed the way the regulation means it. ### Steps 1. Store flights as an ordered journey With scheduled and actual times per leg, and a flag for whether they share one booking reference. Files: `src/journey.js` 2. Compute great circle distance end to end First origin to final destination. Verify against a published distance for a city pair you know. Files: `src/distance.js` 3. Compute delay at the destination Actual arrival against scheduled arrival at the final destination. This is the number the regulation uses. Files: `src/delay.js` ### Done when - [ ] A connecting journey yields one end-to-end distance - [ ] Delay is computed at the final destination - [ ] Separate bookings are flagged as likely separate cases - [ ] A known city pair distance matches a published figure ### Watch out - Two errors here change the answer and both are easy to make. Distance is measured from the first departure to the final destination on a single booking, so a connecting journey is one distance rather than the sum of its legs. Delay is measured at the final destination, so a two hour delay on the first leg that causes a six hour arrival delay is a six hour case. ## M3 · Eligibility, with the uncertainty visible The application says which band applies and, just as clearly, what it cannot decide. ### Steps 1. Apply the resolved rules Distance and delay to a band and amount, using the version effective on the flight date. Files: `src/assess.js` 2. Separate assessed from open Two lists in the output. Anything requiring judgement goes in the second list with a note on where the burden lies. Files: `src/assess.js` 3. Record the airline's reason without judging it Categorised for your own tracking, with the burden of proof noted, and no verdict. Files: `src/reasons.js` ### Done when - [ ] A known journey produces the correct band - [ ] Open questions are listed separately from assessed conditions - [ ] The airline's reason is recorded without a verdict - [ ] An earlier flight date resolves an earlier rules version ### Watch out - Extraordinary circumstances is the airline's defence and the burden of proving it rests with them, but whether a given reason qualifies is a legal judgement that has been litigated extensively and is not reducible to a lookup table. Present the airline's stated reason, note that the burden is theirs, and never output a confident yes or no on it. ## M4 · Evidence, and what is missing The case knows what it has and what it still needs. ### Steps 1. Define checklists per disruption kind Delay, cancellation and denied boarding need different evidence. Keep the lists as data. Files: `data/checklists.json` 2. Store files with a manifest In a case folder with recorded kinds and notes. The manifest is what you attach. Files: `src/evidence.js` 3. Flag time-sensitive items Booking access and app records disappear. Say which and how soon. Files: `data/checklists.json` ### Done when - [ ] The checklist shows present and missing items - [ ] Adding a file records it with its kind - [ ] Time-sensitive items are flagged with a reason - [ ] The manifest exports as an attachable list ### Watch out - Airline apps and booking sites remove access to a booking within months of travel, and a boarding pass in an email is easy to lose in a search. Capture the evidence at the time of the disruption rather than when you get round to claiming, and make the checklist say which items become unobtainable. ## M5 · Letters and the deadline patchwork You can send a correctly framed letter and you know when the claim expires. ### Steps 1. Template the letters First claim and escalation, both drawing figures from the rules version rather than restating them. Files: `templates/` 2. Compute limitation per jurisdiction From the rules table, with the basis shown next to the date. Never one global period. Files: `src/deadlines.js` 3. Warn early and store what was sent A warning with real notice, and a copy of every letter with its date. Files: `src/correspondence.js` ### Done when - [ ] A letter cites the resolved band and amount - [ ] Changing jurisdiction changes the limitation date and shows its basis - [ ] An approaching deadline produces an early warning - [ ] Every sent letter is stored with its date ### Watch out - Applying one limitation period everywhere will either lose a valid claim or waste effort on an expired one. The period depends on where the claim is brought and is set by national law rather than by the regulation itself, so keep it per jurisdiction in the rules table with its own source, and show the basis alongside the date. ## M6 · Escalation, records and knowing when to stop (production only) You know what happens after a refusal, and you can hand the whole case to someone else. ### Steps 1. Record escalation routes as data Per jurisdiction, with a source and a check date, exactly like the rules table. Files: `data/escalation.json` 2. Export a complete case folder Facts, evidence, letters and the assessment. Readable by a solicitor without this application. Files: `src/export.js` 3. Say when to hand over In the documentation and in the interface at the point of refusal. This is the honest part. Files: `README.md` ### Done when - [ ] Escalation routes are recorded with sources and check dates - [ ] A case exports as a readable folder - [ ] The hand-over guidance appears at the point of refusal - [ ] The export contains every letter and evidence file ### Watch out - A refused claim is where the service you are replacing earns its fee, because contesting a refusal means arguing about evidence and law rather than filling in a form. Record the escalation route, and be honest in your own documentation that a contested case is where a no-win no-fee arrangement is often worth the percentage. ===== OPERATIONS.md ===== # Operations · AirHelp ## Backup Case files contain passenger names, booking references and correspondence. Back them up encrypted, and delete cases once settled and the money has arrived, since there is no reason to keep them. ## Restore Restore the database and the evidence folder together. A case without its evidence is a record of a claim rather than a claim. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring The only thing worth watching is the rules table's check dates. A figure last verified two years ago should be surfaced as stale rather than used silently. ## Incident checklist The realistic failure is relying on an out-of-date figure and sending a letter claiming the wrong amount, which weakens the claim. Surface stale rules before generating a letter rather than after. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] Every rules row has a source and a check date within your staleness window - [ ] Distance and delay are verified against a real case - [ ] The non-advice statement appears on every output screen - [ ] The limitation date changes correctly with jurisdiction - [ ] A case exports as a folder readable without this application ## Launch constraint Do not market omitted AirHelp capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. Case files, containing passenger names and booking references. Local, and worth encrypting the backup. DB_PATH=./data/cases.db # Required. Where evidence files are stored, one folder per case. EVIDENCE_DIR=./data/evidence # Required. Which rules set to resolve by default. Changing it changes amounts, currency and limitation period. DEFAULT_JURISDICTION=UK # Required. How far ahead of the limitation date to warn. Long enough to actually act. DEADLINE_WARN_DAYS=90 # Required. The dated rules table. Every figure here carries a source and a check date. RULES_PATH=./data/rules.json
# AirHelp · indie build A local case file per disrupted flight: journey facts, a distance and delay calculation producing a compensation band, an evidence checklist, generated letters citing the right regulation, and deadline tracking that knows the limitation period differs by jurisdiction. Estimated effort: **weekend**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Node 22 with SQLite, running locally | a claim contains passenger names, booking references and correspondence, and there is no reason for it to leave your machine | | Rules | A dated rules table, versioned, separate from the code | amounts and thresholds change and a claim must be assessable against the rules that applied on the flight date | | Distance | Great circle distance between airport coordinates | the regulation's bands are distance based and the calculation must be reproducible | | Documents | Markdown letters rendered to PDF | one source, readable output, and a record of exactly what you sent | | Evidence | Files stored alongside the case with a manifest | a claim is won on evidence and the manifest is what tells you what is missing | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **Node.js 22 or newer** · free - Why: Runs the case file, calculations and document generation. - Get it: nodejs.org, or nvm install 22. - Verify: node --version prints v22 or higher - [ ] **A read of the regulation text for your jurisdictions** · free, and it is the first task rather than a formality - Why: Phase 1 builds a table of legal figures, and secondary summaries simplify in ways that change outcomes. Reading the text is the difference between a tool that helps and one that misleads you about your own claim. - Get it: Read the regulation on the official legal database for your jurisdiction, and the enforcement body's guidance. Record what you checked and when. - Verify: The rules table has been corrected against the regulation text and the corrections are recorded - [ ] **An airport coordinate dataset you may use** · free with an open dataset - Why: Distance drives the compensation band, so the coordinates must come from a source you can cite and are licensed to use. - Get it: Use an openly licensed airport dataset and record its licence. Verify a few known city pairs against published distances before trusting it. - Verify: Three known city pair distances match published figures - [ ] **A real disrupted flight, or a detailed hypothetical** · free - Why: Every phase is meaningless against invented data, and real cases have the awkward details: separate bookings, connections, and a vague airline reason. - Get it: Use your own disruption if you have one, including the actual airline correspondence. Otherwise construct one in detail from a friend's. - Verify: A complete journey with real times and a real airline reason is entered - [ ] **A decision about how this is presented** · free - Why: A tool that produces a compensation figure will be read as an answer. If anyone but you uses it, the framing matters more than the calculation. - Get it: Decide whether this stays private. If it does not, write the disclaimer before the first screen, not after someone relies on it. - Verify: The non-advice statement appears on every screen that produces an output ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Legal advice or a judgement on a contested case. - Someone to argue with the airline for you. - No-win no-fee risk sharing. - Case law interpretation of extraordinary circumstances. - AirHelp staff handling airline correspondence and negotiation - case-specific legal judgment across jurisdictions - lawyer access and funded legal action under a no-win, no-fee model - commercial flight, weather, and disruption data used to validate claims - professional support and accountability when a claim becomes contested If one of those is essential to you, that is the reason to keep paying for AirHelp, and the README should say so rather than pretend.
# Build brief · AirHelp The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a private case file for a flight compensation claim: eligibility, evidence, letters and deadlines to replace AirHelp. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero This organises facts and drafts letters. It is not legal advice, it cannot judge a contested case, and it must say so on every screen that produces an output. What the service you are replacing actually sells is a person who argues with the airline for you, and a no-win no-fee arrangement that carries the risk. Neither is code. Every legal figure in this build is a value in a dated table, never a constant in the logic, because these change. The figures below were checked in September 2026 and the prompt requires you to re-check them against the regulation before relying on any output. ### Stack (fixed, do not substitute) - Runtime: Node 22 with SQLite, running locally · a claim contains passenger names, booking references and correspondence, and there is no reason for it to leave your machine - Rules: A dated rules table, versioned, separate from the code · amounts and thresholds change and a claim must be assessable against the rules that applied on the flight date - Distance: Great circle distance between airport coordinates · the regulation's bands are distance based and the calculation must be reproducible - Documents: Markdown letters rendered to PDF · one source, readable output, and a record of exactly what you sent - Evidence: Files stored alongside the case with a manifest · a claim is won on evidence and the manifest is what tells you what is missing ### Data model (create this before Phase 1) - cases(id, created_at, state, jurisdiction, regulation, rules_version) · state is drafting, submitted, escalated, settled or refused - flights(case_id, ordinal, carrier, number, date, origin_iata, destination_iata, scheduled_departure, actual_arrival, distance_km) - passengers(case_id, name, booking_reference, is_claimant) - disruption(case_id, kind, delay_minutes_at_destination, airline_reason, reason_category) · kind is delay, cancellation or denied_boarding - evidence(case_id, kind, path, added_at, notes) · boarding pass, booking confirmation, airline correspondence, receipts - correspondence(case_id, direction, at, channel, subject, body_path, deadline_at) - rules(version, effective_from, jurisdiction, band_km_min, band_km_max, amount, currency, notes, source_url, checked_on) - The rules table carries a source URL and a check date on every row. A figure without both is not usable. ### Phase 1 · The rules table, before any logic Build: Build the rules table first. Enter the compensation bands for each jurisdiction you support with their currency, the delay threshold, the reduction rule for long flights, and the limitation period. Record a source URL and a check date on every row. Then read the regulation itself and correct whatever you got wrong. Trap: A compensation amount written as a constant in the code is a figure nobody will re-check, and these change. Reform proposals were under discussion in 2026 that would replace the current patchwork of national limitation periods with a single deadline, potentially from around the second half of 2027. Keep every figure in dated data so a change is a row rather than a rewrite. Done when: Every band, threshold and limitation period is a row with a source URL and a check date. No monetary amount or duration appears anywhere in the code. A case created for a flight date resolves the rules version that was effective then rather than the newest one. Do not build yet: No eligibility logic, no evidence, no letters. ### Phase 2 · Journey facts and distance Build: Enter the flights of a journey with times and airports. Compute great circle distance between the first departure and the final destination, not the sum of the legs. Compute delay at the final destination, not at the connection where it started. Trap: Two errors here change the answer and both are easy to make. Distance is measured from the first departure to the final destination on a single booking, so a connecting journey is one distance rather than the sum of its legs. Delay is measured at the final destination, so a two hour delay on the first leg that causes a six hour arrival delay is a six hour case. Done when: A connecting journey computes one distance from first origin to final destination rather than summing legs. Delay is computed at the final destination. A journey booked as two separate reservations is flagged as likely two cases rather than treated as one. Distance for a known city pair matches a published figure within a small tolerance. Do not build yet: No eligibility assessment yet. ### Phase 3 · Eligibility, with the uncertainty visible Build: Apply the resolved rules to the journey and produce a band and an amount. Then present the conditions the application cannot assess: whether the carrier and route are within scope, and whether the airline's stated reason is genuinely extraordinary. Show these as open questions rather than resolving them. Trap: Extraordinary circumstances is the airline's defence and the burden of proving it rests with them, but whether a given reason qualifies is a legal judgement that has been litigated extensively and is not reducible to a lookup table. Present the airline's stated reason, note that the burden is theirs, and never output a confident yes or no on it. Done when: A journey with a known distance and delay produces the correct band from the rules table. The output states which conditions were assessed and which are open. An airline reason is recorded and categorised without the application declaring whether it qualifies. Changing the flight date to one before a rules version's effective date changes the amount. Do not build yet: No evidence tracking or letters yet. ### Phase 4 · Evidence, and what is missing Build: An evidence checklist per case kind: booking confirmation, boarding pass, the airline's notification, and receipts for expenses. Store files with the case and show what is missing. Warn where evidence is time sensitive and likely to disappear. Trap: Airline apps and booking sites remove access to a booking within months of travel, and a boarding pass in an email is easy to lose in a search. Capture the evidence at the time of the disruption rather than when you get round to claiming, and make the checklist say which items become unobtainable. Done when: A case shows a checklist with present and missing items. Adding a file records it against the case with a note. Items known to expire are marked with why. The evidence manifest exports as a list you can attach to correspondence. Do not build yet: No letters or deadlines yet. ### Phase 5 · Letters and the deadline patchwork Build: Generate a first letter and an escalation from templates, citing the regulation and the figures from the resolved rules version. Track the limitation period, which differs by jurisdiction: the United Kingdom applies six years from the flight date, while claims in European Union member states run to national limitation law and can range from about a year to several. Warn well before expiry. Trap: Applying one limitation period everywhere will either lose a valid claim or waste effort on an expired one. The period depends on where the claim is brought and is set by national law rather than by the regulation itself, so keep it per jurisdiction in the rules table with its own source, and show the basis alongside the date. Done when: A generated letter cites the regulation, the band and the amount from the resolved rules version. Changing the jurisdiction changes the stated limitation date and shows its basis. A case approaching its limitation date produces a warning with enough notice to act. Every letter sent is stored with its date. Do not build yet: Escalation records and export come last. ### Phase 6 · Escalation, records and knowing when to stop Build: Record the airline's response and the escalation route for the jurisdiction, which is typically a national enforcement body or an approved dispute scheme. Export the complete case as a folder anyone can read. Then write down honestly when handing the claim to a service or a solicitor is the better choice. Trap: A refused claim is where the service you are replacing earns its fee, because contesting a refusal means arguing about evidence and law rather than filling in a form. Record the escalation route, and be honest in your own documentation that a contested case is where a no-win no-fee arrangement is often worth the percentage. Done when: The escalation route for the case's jurisdiction is recorded with a source. A complete case exports as a folder containing the facts, evidence, correspondence and the assessment, readable without this application. The documentation states plainly when handing over is the better option. Do not build yet: Nothing after this. ### Out of scope (and why) - Legal advice of any kind. This organises facts and produces drafts, and every screen that outputs something must say so. - A person who argues with the airline on your behalf, which is the actual service and the reason the fee exists. - No-win no-fee risk sharing. If your claim fails you have spent your own time; a service absorbs that risk for a percentage. - Case law judgement on whether a specific reason is extraordinary, which has been litigated extensively and is not a lookup. - Coverage of every jurisdiction. Support the ones you actually fly and say which those are. ### README must contain - That this is not legal advice and produces drafts rather than decisions - That every legal figure lives in a dated rules table with a source, and must be re-checked before use - That distance is measured end to end and delay at the final destination - That limitation periods differ by jurisdiction, with the United Kingdom at six years and European Union claims following national law - That extraordinary circumstances is the airline's defence with the burden on them, and that this application does not judge it
# Agent instructions · AirHelp indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Node 22 with SQLite, running locally, A dated rules table, versioned, separate from the code, Great circle distance between airport coordinates, Markdown letters rendered to PDF, Files stored alongside the case with a manifest. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - A compensation amount written as a constant in the code is a figure nobody will re-check, and these change. Reform proposals were under discussion in 2026 that would replace the current patchwork of national limitation periods with a single deadline, potentially from around the second half of 2027. Keep every figure in dated data so a change is a row rather than a rewrite. - Two errors here change the answer and both are easy to make. Distance is measured from the first departure to the final destination on a single booking, so a connecting journey is one distance rather than the sum of its legs. Delay is measured at the final destination, so a two hour delay on the first leg that causes a six hour arrival delay is a six hour case. - Extraordinary circumstances is the airline's defence and the burden of proving it rests with them, but whether a given reason qualifies is a legal judgement that has been litigated extensively and is not reducible to a lookup table. Present the airline's stated reason, note that the burden is theirs, and never output a confident yes or no on it. - Airline apps and booking sites remove access to a booking within months of travel, and a boarding pass in an email is easy to lose in a search. Capture the evidence at the time of the disruption rather than when you get round to claiming, and make the checklist say which items become unobtainable. - Applying one limitation period everywhere will either lose a valid claim or waste effort on an expired one. The period depends on where the claim is brought and is set by national law rather than by the regulation itself, so keep it per jurisdiction in the rules table with its own source, and show the basis alongside the date.
# Build plan · AirHelp A local case file per disrupted flight: journey facts, a distance and delay calculation producing a compensation band, an evidence checklist, generated letters citing the right regulation, and deadline tracking that knows the limitation period differs by jurisdiction. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · The rules table, before any logic Every legal figure lives in dated data with a source, and nothing is hard coded. ### Steps 1. Enter the bands per jurisdiction As of September 2026 the EU bands are 250 euro under 1,500 km, 400 euro from 1,500 to 3,500 km and 600 euro above 3,500 km, and the UK equivalents are 220, 350 and 520 pounds. Enter them with sources and check them yourself. Files: `data/rules.json` 2. Record thresholds and reductions The three hour arrival threshold, and the rule allowing a reduction for a long flight arriving between three and four hours late. Both as data. Files: `data/rules.json` 3. Read the regulation and correct the table Secondary sources summarise and simplify. Read the text itself for the rows you entered, and record what you changed. Files: `docs/rules-review.md` ### Done when - [ ] Every rule row has a source URL and a check date - [ ] No monetary amount appears in code - [ ] A case resolves the rules version effective on its flight date - [ ] The table was corrected against the regulation text, and the changes are recorded ### Watch out - A compensation amount written as a constant in the code is a figure nobody will re-check, and these change. Reform proposals were under discussion in 2026 that would replace the current patchwork of national limitation periods with a single deadline, potentially from around the second half of 2027. Keep every figure in dated data so a change is a row rather than a rewrite. ## Phase 2 · Journey facts and distance A case records the journey accurately, and the distance is computed the way the regulation means it. ### Steps 1. Store flights as an ordered journey With scheduled and actual times per leg, and a flag for whether they share one booking reference. Files: `src/journey.js` 2. Compute great circle distance end to end First origin to final destination. Verify against a published distance for a city pair you know. Files: `src/distance.js` 3. Compute delay at the destination Actual arrival against scheduled arrival at the final destination. This is the number the regulation uses. Files: `src/delay.js` ### Done when - [ ] A connecting journey yields one end-to-end distance - [ ] Delay is computed at the final destination - [ ] Separate bookings are flagged as likely separate cases - [ ] A known city pair distance matches a published figure ### Watch out - Two errors here change the answer and both are easy to make. Distance is measured from the first departure to the final destination on a single booking, so a connecting journey is one distance rather than the sum of its legs. Delay is measured at the final destination, so a two hour delay on the first leg that causes a six hour arrival delay is a six hour case. ## Phase 3 · Eligibility, with the uncertainty visible The application says which band applies and, just as clearly, what it cannot decide. ### Steps 1. Apply the resolved rules Distance and delay to a band and amount, using the version effective on the flight date. Files: `src/assess.js` 2. Separate assessed from open Two lists in the output. Anything requiring judgement goes in the second list with a note on where the burden lies. Files: `src/assess.js` 3. Record the airline's reason without judging it Categorised for your own tracking, with the burden of proof noted, and no verdict. Files: `src/reasons.js` ### Done when - [ ] A known journey produces the correct band - [ ] Open questions are listed separately from assessed conditions - [ ] The airline's reason is recorded without a verdict - [ ] An earlier flight date resolves an earlier rules version ### Watch out - Extraordinary circumstances is the airline's defence and the burden of proving it rests with them, but whether a given reason qualifies is a legal judgement that has been litigated extensively and is not reducible to a lookup table. Present the airline's stated reason, note that the burden is theirs, and never output a confident yes or no on it. ## Phase 4 · Evidence, and what is missing The case knows what it has and what it still needs. ### Steps 1. Define checklists per disruption kind Delay, cancellation and denied boarding need different evidence. Keep the lists as data. Files: `data/checklists.json` 2. Store files with a manifest In a case folder with recorded kinds and notes. The manifest is what you attach. Files: `src/evidence.js` 3. Flag time-sensitive items Booking access and app records disappear. Say which and how soon. Files: `data/checklists.json` ### Done when - [ ] The checklist shows present and missing items - [ ] Adding a file records it with its kind - [ ] Time-sensitive items are flagged with a reason - [ ] The manifest exports as an attachable list ### Watch out - Airline apps and booking sites remove access to a booking within months of travel, and a boarding pass in an email is easy to lose in a search. Capture the evidence at the time of the disruption rather than when you get round to claiming, and make the checklist say which items become unobtainable. ## Phase 5 · Letters and the deadline patchwork You can send a correctly framed letter and you know when the claim expires. ### Steps 1. Template the letters First claim and escalation, both drawing figures from the rules version rather than restating them. Files: `templates/` 2. Compute limitation per jurisdiction From the rules table, with the basis shown next to the date. Never one global period. Files: `src/deadlines.js` 3. Warn early and store what was sent A warning with real notice, and a copy of every letter with its date. Files: `src/correspondence.js` ### Done when - [ ] A letter cites the resolved band and amount - [ ] Changing jurisdiction changes the limitation date and shows its basis - [ ] An approaching deadline produces an early warning - [ ] Every sent letter is stored with its date ### Watch out - Applying one limitation period everywhere will either lose a valid claim or waste effort on an expired one. The period depends on where the claim is brought and is set by national law rather than by the regulation itself, so keep it per jurisdiction in the rules table with its own source, and show the basis alongside the date. ## Not in this build - Legal advice or a judgement on a contested case. - Someone to argue with the airline for you. - No-win no-fee risk sharing. - Case law interpretation of extraordinary circumstances. ## After v1, if you want it - A capture flow for the moment of disruption, since evidence is easiest to collect at the airport - Support for a second jurisdiction, which is a rules table addition rather than code - A reminder that checks stale rules rows against their sources on a schedule
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Case files, containing passenger names and booking references. Local, and worth encrypting the backup. DB_PATH=./data/cases.db # Required. Where evidence files are stored, one folder per case. EVIDENCE_DIR=./data/evidence # Required. Which rules set to resolve by default. Changing it changes amounts, currency and limitation period. DEFAULT_JURISDICTION=UK # Required. How far ahead of the limitation date to warn. Long enough to actually act. DEADLINE_WARN_DAYS=90 # Required. The dated rules table. Every figure here carries a source and a check date. RULES_PATH=./data/rules.json
# AirHelp · product brief ## Problem A useful personal organizer is buildable, but AirHelp is more than the visible intake form. A local app can assess EC261 and UK261 eligibility, organize evidence, draft letters, and track deadlines. It cannot responsibly reproduce AirHelp's managed airline correspondence, cross-jurisdictional legal judgment, funded court action, and professional accountability in one sitting. This is a credible personal substitute for straightforward self-service claims, not a full replacement. ## Product outcome A case file that computes a compensation band from dated rules rather than hard coded figures, keeps evidence before it disappears, tracks a jurisdiction-correct deadline, produces sendable letters, and says plainly what it cannot decide. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - Node.js 22 - modern browser - SQLite - user-entered flight, passenger, and evidence data - current official passenger-rights sources ## Explicit non-goals for v1 - Legal advice or a judgement on a contested case. - Someone to argue with the airline for you. - No-win no-fee risk sharing. - Case law interpretation of extraordinary circumstances. - AirHelp staff handling airline correspondence and negotiation - case-specific legal judgment across jurisdictions - lawyer access and funded legal action under a no-win, no-fee model - commercial flight, weather, and disruption data used to validate claims - professional support and accountability when a claim becomes contested ## Success criteria - Every rules row has a source and a check date within your staleness window - Distance and delay are verified against a real case - The non-advice statement appears on every output screen - The limitation date changes correctly with jurisdiction - A case exports as a folder readable without this application
# Build brief · AirHelp The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a private case file for a flight compensation claim: eligibility, evidence, letters and deadlines to replace AirHelp. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Rule zero This organises facts and drafts letters. It is not legal advice, it cannot judge a contested case, and it must say so on every screen that produces an output. What the service you are replacing actually sells is a person who argues with the airline for you, and a no-win no-fee arrangement that carries the risk. Neither is code. Every legal figure in this build is a value in a dated table, never a constant in the logic, because these change. The figures below were checked in September 2026 and the prompt requires you to re-check them against the regulation before relying on any output. ### Stack (fixed, do not substitute) - Runtime: Node 22 with SQLite, running locally · a claim contains passenger names, booking references and correspondence, and there is no reason for it to leave your machine - Rules: A dated rules table, versioned, separate from the code · amounts and thresholds change and a claim must be assessable against the rules that applied on the flight date - Distance: Great circle distance between airport coordinates · the regulation's bands are distance based and the calculation must be reproducible - Documents: Markdown letters rendered to PDF · one source, readable output, and a record of exactly what you sent - Evidence: Files stored alongside the case with a manifest · a claim is won on evidence and the manifest is what tells you what is missing ### Data model (create this before Phase 1) - cases(id, created_at, state, jurisdiction, regulation, rules_version) · state is drafting, submitted, escalated, settled or refused - flights(case_id, ordinal, carrier, number, date, origin_iata, destination_iata, scheduled_departure, actual_arrival, distance_km) - passengers(case_id, name, booking_reference, is_claimant) - disruption(case_id, kind, delay_minutes_at_destination, airline_reason, reason_category) · kind is delay, cancellation or denied_boarding - evidence(case_id, kind, path, added_at, notes) · boarding pass, booking confirmation, airline correspondence, receipts - correspondence(case_id, direction, at, channel, subject, body_path, deadline_at) - rules(version, effective_from, jurisdiction, band_km_min, band_km_max, amount, currency, notes, source_url, checked_on) - The rules table carries a source URL and a check date on every row. A figure without both is not usable. ### Phase 1 · The rules table, before any logic Build: Build the rules table first. Enter the compensation bands for each jurisdiction you support with their currency, the delay threshold, the reduction rule for long flights, and the limitation period. Record a source URL and a check date on every row. Then read the regulation itself and correct whatever you got wrong. Trap: A compensation amount written as a constant in the code is a figure nobody will re-check, and these change. Reform proposals were under discussion in 2026 that would replace the current patchwork of national limitation periods with a single deadline, potentially from around the second half of 2027. Keep every figure in dated data so a change is a row rather than a rewrite. Done when: Every band, threshold and limitation period is a row with a source URL and a check date. No monetary amount or duration appears anywhere in the code. A case created for a flight date resolves the rules version that was effective then rather than the newest one. Do not build yet: No eligibility logic, no evidence, no letters. ### Phase 2 · Journey facts and distance Build: Enter the flights of a journey with times and airports. Compute great circle distance between the first departure and the final destination, not the sum of the legs. Compute delay at the final destination, not at the connection where it started. Trap: Two errors here change the answer and both are easy to make. Distance is measured from the first departure to the final destination on a single booking, so a connecting journey is one distance rather than the sum of its legs. Delay is measured at the final destination, so a two hour delay on the first leg that causes a six hour arrival delay is a six hour case. Done when: A connecting journey computes one distance from first origin to final destination rather than summing legs. Delay is computed at the final destination. A journey booked as two separate reservations is flagged as likely two cases rather than treated as one. Distance for a known city pair matches a published figure within a small tolerance. Do not build yet: No eligibility assessment yet. ### Phase 3 · Eligibility, with the uncertainty visible Build: Apply the resolved rules to the journey and produce a band and an amount. Then present the conditions the application cannot assess: whether the carrier and route are within scope, and whether the airline's stated reason is genuinely extraordinary. Show these as open questions rather than resolving them. Trap: Extraordinary circumstances is the airline's defence and the burden of proving it rests with them, but whether a given reason qualifies is a legal judgement that has been litigated extensively and is not reducible to a lookup table. Present the airline's stated reason, note that the burden is theirs, and never output a confident yes or no on it. Done when: A journey with a known distance and delay produces the correct band from the rules table. The output states which conditions were assessed and which are open. An airline reason is recorded and categorised without the application declaring whether it qualifies. Changing the flight date to one before a rules version's effective date changes the amount. Do not build yet: No evidence tracking or letters yet. ### Phase 4 · Evidence, and what is missing Build: An evidence checklist per case kind: booking confirmation, boarding pass, the airline's notification, and receipts for expenses. Store files with the case and show what is missing. Warn where evidence is time sensitive and likely to disappear. Trap: Airline apps and booking sites remove access to a booking within months of travel, and a boarding pass in an email is easy to lose in a search. Capture the evidence at the time of the disruption rather than when you get round to claiming, and make the checklist say which items become unobtainable. Done when: A case shows a checklist with present and missing items. Adding a file records it against the case with a note. Items known to expire are marked with why. The evidence manifest exports as a list you can attach to correspondence. Do not build yet: No letters or deadlines yet. ### Phase 5 · Letters and the deadline patchwork Build: Generate a first letter and an escalation from templates, citing the regulation and the figures from the resolved rules version. Track the limitation period, which differs by jurisdiction: the United Kingdom applies six years from the flight date, while claims in European Union member states run to national limitation law and can range from about a year to several. Warn well before expiry. Trap: Applying one limitation period everywhere will either lose a valid claim or waste effort on an expired one. The period depends on where the claim is brought and is set by national law rather than by the regulation itself, so keep it per jurisdiction in the rules table with its own source, and show the basis alongside the date. Done when: A generated letter cites the regulation, the band and the amount from the resolved rules version. Changing the jurisdiction changes the stated limitation date and shows its basis. A case approaching its limitation date produces a warning with enough notice to act. Every letter sent is stored with its date. Do not build yet: Escalation records and export come last. ### Phase 6 · Escalation, records and knowing when to stop Build: Record the airline's response and the escalation route for the jurisdiction, which is typically a national enforcement body or an approved dispute scheme. Export the complete case as a folder anyone can read. Then write down honestly when handing the claim to a service or a solicitor is the better choice. Trap: A refused claim is where the service you are replacing earns its fee, because contesting a refusal means arguing about evidence and law rather than filling in a form. Record the escalation route, and be honest in your own documentation that a contested case is where a no-win no-fee arrangement is often worth the percentage. Done when: The escalation route for the case's jurisdiction is recorded with a source. A complete case exports as a folder containing the facts, evidence, correspondence and the assessment, readable without this application. The documentation states plainly when handing over is the better option. Do not build yet: Nothing after this. ### Out of scope (and why) - Legal advice of any kind. This organises facts and produces drafts, and every screen that outputs something must say so. - A person who argues with the airline on your behalf, which is the actual service and the reason the fee exists. - No-win no-fee risk sharing. If your claim fails you have spent your own time; a service absorbs that risk for a percentage. - Case law judgement on whether a specific reason is extraordinary, which has been litigated extensively and is not a lookup. - Coverage of every jurisdiction. Support the ones you actually fly and say which those are. ### README must contain - That this is not legal advice and produces drafts rather than decisions - That every legal figure lives in a dated rules table with a source, and must be re-checked before use - That distance is measured end to end and delay at the final destination - That limitation periods differ by jurisdiction, with the United Kingdom at six years and European Union claims following national law - That extraordinary circumstances is the airline's defence with the burden on them, and that this application does not judge it
# Architecture · AirHelp ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Node 22 with SQLite, running locally | a claim contains passenger names, booking references and correspondence, and there is no reason for it to leave your machine | | Rules | A dated rules table, versioned, separate from the code | amounts and thresholds change and a claim must be assessable against the rules that applied on the flight date | | Distance | Great circle distance between airport coordinates | the regulation's bands are distance based and the calculation must be reproducible | | Documents | Markdown letters rendered to PDF | one source, readable output, and a record of exactly what you sent | | Evidence | Files stored alongside the case with a manifest | a claim is won on evidence and the manifest is what tells you what is missing | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Rules | Every legal figure, dated and sourced. | This is the module that makes the tool maintainable. If a figure ever moves into code, the tool has started rotting. | | Journey | Flights, distance and delay. | Pure computation, testable against published distances and known cases. | | Assess | Applying rules and separating open questions. | Any implementation, provided it never resolves a question that requires legal judgement. | | Evidence | Files and the checklist. | Straightforward. The value is the checklist saying what expires. | | Correspondence | Letters and deadlines. | Templates are data. The per-jurisdiction limitation period is the part that must not be simplified. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `DB_PATH` · required · Case files, containing passenger names and booking references. Local, and worth encrypting the backup. - `EVIDENCE_DIR` · required · Where evidence files are stored, one folder per case. - `DEFAULT_JURISDICTION` · required · Which rules set to resolve by default. Changing it changes amounts, currency and limitation period. - `DEADLINE_WARN_DAYS` · required · How far ahead of the limitation date to warn. Long enough to actually act. - `RULES_PATH` · required · The dated rules table. Every figure here carries a source and a check date. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · AirHelp product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Node 22 with SQLite, running locally, A dated rules table, versioned, separate from the code, Great circle distance between airport coordinates, Markdown letters rendered to PDF, Files stored alongside the case with a manifest. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - A compensation amount written as a constant in the code is a figure nobody will re-check, and these change. Reform proposals were under discussion in 2026 that would replace the current patchwork of national limitation periods with a single deadline, potentially from around the second half of 2027. Keep every figure in dated data so a change is a row rather than a rewrite. - Two errors here change the answer and both are easy to make. Distance is measured from the first departure to the final destination on a single booking, so a connecting journey is one distance rather than the sum of its legs. Delay is measured at the final destination, so a two hour delay on the first leg that causes a six hour arrival delay is a six hour case. - Extraordinary circumstances is the airline's defence and the burden of proving it rests with them, but whether a given reason qualifies is a legal judgement that has been litigated extensively and is not reducible to a lookup table. Present the airline's stated reason, note that the burden is theirs, and never output a confident yes or no on it. - Airline apps and booking sites remove access to a booking within months of travel, and a boarding pass in an email is easy to lose in a search. Capture the evidence at the time of the disruption rather than when you get round to claiming, and make the checklist say which items become unobtainable. - Applying one limitation period everywhere will either lose a valid claim or waste effort on an expired one. The period depends on where the claim is brought and is set by national law rather than by the regulation itself, so keep it per jurisdiction in the rules table with its own source, and show the basis alongside the date. - A refused claim is where the service you are replacing earns its fee, because contesting a refusal means arguing about evidence and law rather than filling in a form. Record the escalation route, and be honest in your own documentation that a contested case is where a no-win no-fee arrangement is often worth the percentage.
# Delivery milestones · AirHelp Estimated effort: **weekend** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · The rules table, before any logic Every legal figure lives in dated data with a source, and nothing is hard coded. ### Steps 1. Enter the bands per jurisdiction As of September 2026 the EU bands are 250 euro under 1,500 km, 400 euro from 1,500 to 3,500 km and 600 euro above 3,500 km, and the UK equivalents are 220, 350 and 520 pounds. Enter them with sources and check them yourself. Files: `data/rules.json` 2. Record thresholds and reductions The three hour arrival threshold, and the rule allowing a reduction for a long flight arriving between three and four hours late. Both as data. Files: `data/rules.json` 3. Read the regulation and correct the table Secondary sources summarise and simplify. Read the text itself for the rows you entered, and record what you changed. Files: `docs/rules-review.md` ### Done when - [ ] Every rule row has a source URL and a check date - [ ] No monetary amount appears in code - [ ] A case resolves the rules version effective on its flight date - [ ] The table was corrected against the regulation text, and the changes are recorded ### Watch out - A compensation amount written as a constant in the code is a figure nobody will re-check, and these change. Reform proposals were under discussion in 2026 that would replace the current patchwork of national limitation periods with a single deadline, potentially from around the second half of 2027. Keep every figure in dated data so a change is a row rather than a rewrite. ## M2 · Journey facts and distance A case records the journey accurately, and the distance is computed the way the regulation means it. ### Steps 1. Store flights as an ordered journey With scheduled and actual times per leg, and a flag for whether they share one booking reference. Files: `src/journey.js` 2. Compute great circle distance end to end First origin to final destination. Verify against a published distance for a city pair you know. Files: `src/distance.js` 3. Compute delay at the destination Actual arrival against scheduled arrival at the final destination. This is the number the regulation uses. Files: `src/delay.js` ### Done when - [ ] A connecting journey yields one end-to-end distance - [ ] Delay is computed at the final destination - [ ] Separate bookings are flagged as likely separate cases - [ ] A known city pair distance matches a published figure ### Watch out - Two errors here change the answer and both are easy to make. Distance is measured from the first departure to the final destination on a single booking, so a connecting journey is one distance rather than the sum of its legs. Delay is measured at the final destination, so a two hour delay on the first leg that causes a six hour arrival delay is a six hour case. ## M3 · Eligibility, with the uncertainty visible The application says which band applies and, just as clearly, what it cannot decide. ### Steps 1. Apply the resolved rules Distance and delay to a band and amount, using the version effective on the flight date. Files: `src/assess.js` 2. Separate assessed from open Two lists in the output. Anything requiring judgement goes in the second list with a note on where the burden lies. Files: `src/assess.js` 3. Record the airline's reason without judging it Categorised for your own tracking, with the burden of proof noted, and no verdict. Files: `src/reasons.js` ### Done when - [ ] A known journey produces the correct band - [ ] Open questions are listed separately from assessed conditions - [ ] The airline's reason is recorded without a verdict - [ ] An earlier flight date resolves an earlier rules version ### Watch out - Extraordinary circumstances is the airline's defence and the burden of proving it rests with them, but whether a given reason qualifies is a legal judgement that has been litigated extensively and is not reducible to a lookup table. Present the airline's stated reason, note that the burden is theirs, and never output a confident yes or no on it. ## M4 · Evidence, and what is missing The case knows what it has and what it still needs. ### Steps 1. Define checklists per disruption kind Delay, cancellation and denied boarding need different evidence. Keep the lists as data. Files: `data/checklists.json` 2. Store files with a manifest In a case folder with recorded kinds and notes. The manifest is what you attach. Files: `src/evidence.js` 3. Flag time-sensitive items Booking access and app records disappear. Say which and how soon. Files: `data/checklists.json` ### Done when - [ ] The checklist shows present and missing items - [ ] Adding a file records it with its kind - [ ] Time-sensitive items are flagged with a reason - [ ] The manifest exports as an attachable list ### Watch out - Airline apps and booking sites remove access to a booking within months of travel, and a boarding pass in an email is easy to lose in a search. Capture the evidence at the time of the disruption rather than when you get round to claiming, and make the checklist say which items become unobtainable. ## M5 · Letters and the deadline patchwork You can send a correctly framed letter and you know when the claim expires. ### Steps 1. Template the letters First claim and escalation, both drawing figures from the rules version rather than restating them. Files: `templates/` 2. Compute limitation per jurisdiction From the rules table, with the basis shown next to the date. Never one global period. Files: `src/deadlines.js` 3. Warn early and store what was sent A warning with real notice, and a copy of every letter with its date. Files: `src/correspondence.js` ### Done when - [ ] A letter cites the resolved band and amount - [ ] Changing jurisdiction changes the limitation date and shows its basis - [ ] An approaching deadline produces an early warning - [ ] Every sent letter is stored with its date ### Watch out - Applying one limitation period everywhere will either lose a valid claim or waste effort on an expired one. The period depends on where the claim is brought and is set by national law rather than by the regulation itself, so keep it per jurisdiction in the rules table with its own source, and show the basis alongside the date. ## M6 · Escalation, records and knowing when to stop (production only) You know what happens after a refusal, and you can hand the whole case to someone else. ### Steps 1. Record escalation routes as data Per jurisdiction, with a source and a check date, exactly like the rules table. Files: `data/escalation.json` 2. Export a complete case folder Facts, evidence, letters and the assessment. Readable by a solicitor without this application. Files: `src/export.js` 3. Say when to hand over In the documentation and in the interface at the point of refusal. This is the honest part. Files: `README.md` ### Done when - [ ] Escalation routes are recorded with sources and check dates - [ ] A case exports as a readable folder - [ ] The hand-over guidance appears at the point of refusal - [ ] The export contains every letter and evidence file ### Watch out - A refused claim is where the service you are replacing earns its fee, because contesting a refusal means arguing about evidence and law rather than filling in a form. Record the escalation route, and be honest in your own documentation that a contested case is where a no-win no-fee arrangement is often worth the percentage.
# Operations · AirHelp ## Backup Case files contain passenger names, booking references and correspondence. Back them up encrypted, and delete cases once settled and the money has arrived, since there is no reason to keep them. ## Restore Restore the database and the evidence folder together. A case without its evidence is a record of a claim rather than a claim. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring The only thing worth watching is the rules table's check dates. A figure last verified two years ago should be surfaced as stale rather than used silently. ## Incident checklist The realistic failure is relying on an out-of-date figure and sending a letter claiming the wrong amount, which weakens the claim. Surface stale rules before generating a letter rather than after. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] Every rules row has a source and a check date within your staleness window - [ ] Distance and delay are verified against a real case - [ ] The non-advice statement appears on every output screen - [ ] The limitation date changes correctly with jurisdiction - [ ] A case exports as a folder readable without this application ## Launch constraint Do not market omitted AirHelp capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. Case files, containing passenger names and booking references. Local, and worth encrypting the backup. DB_PATH=./data/cases.db # Required. Where evidence files are stored, one folder per case. EVIDENCE_DIR=./data/evidence # Required. Which rules set to resolve by default. Changing it changes amounts, currency and limitation period. DEFAULT_JURISDICTION=UK # Required. How far ahead of the limitation date to warn. Long enough to actually act. DEADLINE_WARN_DAYS=90 # Required. The dated rules table. Every figure here carries a source and a check date. RULES_PATH=./data/rules.json
$ choose a build depth, inspect the files, then open the complete pack in your agent
People pay AirHelp to transfer the work, uncertainty, follow-up, and legal escalation to a specialist on a no-win, no-fee basis. A local tool can organize a straightforward self-service claim, but it does not negotiate with the airline, fund litigation, or take professional responsibility for a contested case.
xAirHelp staff handling airline correspondence and negotiation
xcase-specific legal judgment across jurisdictions
xlawyer access and funded legal action under a no-win, no-fee model
xcommercial flight, weather, and disruption data used to validate claims
xprofessional support and accountability when a claim becomes contested
AirHelp pricing
| plan | monthly | annual (per mo) | what you get |
|---|---|---|---|
| no win, no fee | custom | — | $0 upfront; AirHelp keeps 35% of successfully recovered flight compensation |
| airhelp+ single trip protection | custom | — | 1 trip, including outbound, return, and any connecting flights |
| airhelp+ lite | — | $2.88/user | 1 year of membership; the official fee list does not publish a numeric trip allowance |
| airhelp+ smart / protect 3 trips | — | $3.84/user | 3 registered trips/year; €100 disruption payout, €200 missed-connection payout, and €100 baggage payout |
| airhelp+ pro / protect 9 trips | — | $9.61/user | 9 registered trips/year; €100 disruption payout, €200 missed-connection payout, and €100 baggage payout |
| airhelp+ usa comfort | — | $15/user | 6 registered trips/year; $200 fast disruption payout plus eligible compensation claims |
| airhelp+ usa pro | — | $20.83/user | 9 registered trips/year; $200 fast disruption payout plus eligible compensation claims |
free tierEligibility and information checks cost $0; an unsuccessful compensation claim costs $0; there is no free AirHelp+ membership.
billingsuccess-fee claim service, one-time single-trip protection, or annual AirHelp+ membership; no monthly AirHelp+ plan
hidden costsSuccessful claims lose 35%; legal action adds another 15% (up to 50% total, used in about 3% of cases). Goodwill claims cost $29.99 unless recovery is below $30 or 3,000 loyalty points. Membership benefits may require registration at least 48 hours before departure; bank/FX fees can reduce payouts.
verified 2026-08-14 · source ↗
Vibecode AirHelp
Kinda. The core of AirHelp is buildable in a weekend with the prompt on this page, but there are real gaps: AirHelp staff handling airline correspondence and negotiation, case-specific legal judgment across jurisdictions. Read the honest list above before committing.
How much does AirHelp cost?
AirHelp's pricing is usage-based or varies by plan · AirHelp deducts a 35% service fee from recovered compensation. If legal action is required, it deducts an additional 15%..
What do I lose by replacing AirHelp?
Honestly: AirHelp staff handling airline correspondence and negotiation; case-specific legal judgment across jurisdictions; lawyer access and funded legal action under a no-win, no-fee model; commercial flight, weather, and disruption data used to validate claims; professional support and accountability when a claim becomes contested. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to AirHelp?
Yes: ClaimSolo (Open-source, local-first personal workspace for assessing, documenting, and tracking EC261 and UK261 flight claims.). Using prior art is also vibecoding; the prompt is for when you want it exactly your way.