Browse the docs

DocsCountries

Beyond the first countries

A plan. Phase 0, the country-agnostic core, is done, and so are the packs the site lists. The phases after it are intentions with a quarter attached, not software.

The plan for making the core usable in any country. The format of a country pack — the one taxonomy in this plan that will not get to be redone — was settled first, before any code was written, and is recorded in decision 0025. This document is the map; that decision is the first step on it.

The premise

A country is data, not code. A module of code per country breaks at every major version, and one product per market splits the users. Ekwo ships one core and one versioned pack per country, and a pack is something an accountant can read, a contributor can propose in a pull request, and a test can prove. Everything below follows from that.

Where the core stands today, phase 0 being done: 18 account types, the set most ledgers share; taxes, their postings and the boxes of a declaration as rows a pack fills; financial statements as rows too, with a country-less framework behind any chart that prescribes none; EN 16931 fields as columns; the French FEC; XBRL for the Belgian NBB; Factur-X; a REST API and an MCP server; row level security everywhere. The file formats — the French FEC, the Belgian CBSO taxonomy, Factur-X, Peppol UBL, the recapitulative statements added with ec_sales_list() and the readers of bank statements — are MIT packages under packages/formats/, organised by format and never by country.

No function of the core holds a country code, and a test enforces it. Every country is a directory under packs/ and a compiled seed, each carrying a year of books and the figures it produces; the table of packs is generated from them. The United Kingdom was the first that is not a Member State of the Union, the United States the first that levies no value added tax at all, and Senegal and Côte d'Ivoire the first two of seventeen States sharing one chart; each was written for that reason.

What an international core needs and does not have

The list this plan started from, with what phase 0 closed and what it did not.

Gap Where it stands Why it matters outside Belgium and France
No pack object Closed. packs/<cc>/ compiled into a committed seed, versioned, with country_packs and company_packs recording what an installation and a company hold UK, US or Canada would each add a third place where a country lives
Nothing on the invoice itself Closed. Numbering and its pattern, the legal payment term, the tax point, the e-invoicing profile, the bank formats and the legal mentions are pack data, each rule citing the article that imposes it Every country prescribes different sentences on an invoice, and a renderer that hard-codes them is a renderer per country
No year-end close, no opening balances Closed. opening_balance(), close_fiscal_year(), reopen_fiscal_year() and a closing_style the pack declares; shifted and 52/53-week years were always covered by fiscal_years UK years run April to March; US retail runs 52/53 weeks; every migration starts with an opening balance
Currencies without realised gains or revaluation Half closed. A matching that realises an exchange difference books it on the accounts the pack names; revaluation of open items is still out Mandatory the day a company invoices outside its functional currency
Accrual only Half closed. A tax can fall due on collection, which is what French services needed; cash accounting as a ledger is still out UK and US small businesses report on a cash basis; French VAT on services is due on collection; the UK has a cash accounting scheme
A tax engine that knows only EU VAT Closed except stacked taxes on one line. Kind, recoverability, jurisdiction, tax-inclusive prices, non-deductible VAT on the account of the line it taxes, rounding method per country GST with input credits (Canada, Australia, Singapore); stacked taxes on one line (GST + QST in Québec, phase 1); non-recoverable sales tax (US, Canadian PST); withholding (Spain, Italy, Portugal); tax-inclusive pricing (UK, Australia retail)
No cash-flow statement Open, and deliberately: statements.json already accepts cash_flow as a kind, and no pack here prescribes one Expected before tax compliance in the English-speaking world
Nothing proved a pack against figures Closed. A golden year of books per pack, and a legal source required on every tax and every box A pack that cannot be wrong in a way anyone notices is a pack nobody can review

Four phases

Phase 0 — a complete, country-agnostic core — done, 14 September 2026

The phase that decides everything. Nothing country-specific was added until it was done. The format of the pack was decided on 12 September 2026 and is written up in decision 0025; the twelve steps below were its execution order, and they recut the first list in three places: opening balances came first because they blocked adoption in Belgium and France; cash-basis VAT came before any new country because the French pack was wrong for services; the cash-flow statement, the revaluation of open items and several taxes on one line wait for the countries that need them.

All twelve are delivered and shipped in v0.2.0. What that buys is narrow and worth stating plainly: a country can now be described entirely in data, checked by a tool, replayed against a year of books, translated, versioned and upgraded in place — and a third country adds no place where a country lives. It does not buy a third country, which is phase 1.

  1. Pack format and compiler; Belgium and France extracted into packs/. Done.
  2. The pack migration: country_packs, company_packs, translated labels, seeds that upsert the template tables. Done.
  3. Declaration boxes as data, a generic vat_return(). Done — the boxes and their plus/minus formulas live in tax_report_templates and tax_report_box_templates, filled by the pack; the Belgian 71/72 and the French CA3 totals are pack data, and no function in the core holds a country code any more, which a test now enforces.
  4. Financial statements as data, a generic statement by account type. Donestatement_templates, statement_line_templates and statement_line_rules filled by the packs, financial_statement() and unmapped_accounts(); the NBB abbreviated schemes and the French liasse 2050-2053; a country-less packs/generic/ whose rules are all account types, which gives any chart a balance sheet that ties out. A country also gained several charts of accountschart_templates, chart_code on the template accounts and on company_packs, ekwo init --chart — with the Belgian association chart as the first second chart.
  5. The generalised tax engine: kind, recoverability, jurisdiction, tax-inclusive prices, non-deductible VAT, rounding rules. Done (12 September 2026): tax_kind, recoverable, jurisdiction, price_include, cash_basis on the taxes and their templates; rounding_method and cash_rounding_unit on the country model; the tax_on_base posting, which books non-deductible VAT on the account of the line it taxes. Belgian cars at 50 % and French fuel at 80 % are in the packs. The gross-to-net computation of a tax-inclusive price and the behaviour of cash_basis are not: the first waits for the country that sells that way, the second is phase 6.
  6. Cash-basis VAT and realised exchange differences. Done (12 September 2026): post_document books a cash-basis tax — and the base it is computed on — on the transition account the pack names and on no declaration box, and reconcile() moves the settled share, pro rata and cumulative, to the account and the box it is declared on. post_document and post_payment convert to the company's currency and write amount_currency, which nothing did before, and a matching between two lines in the same foreign currency books the realised difference on fx_gain_code / fx_loss_code of the country model. The French pack gains the six services taxes that fall due on collection; the option for the debits is the tax that was already there. Out of scope and staying out: revaluation of open items, and cash accounting as a ledger.
  7. Document rules, e-invoicing profiles and bank formats as data. Done: twenty columns on the country model — gapless numbering and the number pattern, the legal payment term and where its interest comes from, the tax point, the e-invoicing profile and the day it becomes obligatory, the ISO 6523 party and VAT schemes, the bank statement and payment formats, the usual opening of the financial year, and the article behind four of those rules with the register entry it is read at — plus legal_mention_templates, the sentences a country requires on an invoice with a closed vocabulary of nine conditions. document_legal_mentions decides which of them apply to one document from its country, its date and the treatments of the taxes on its lines; document_line_items gained the treatment and the exemption reason. Nothing executable: no function was added, and the numbering engine still builds its own number — the pattern is declared so that the engine which reads one changes nothing when it arrives.
  8. Opening balances and a parameterised year-end close. Doneopening_balance(), close_fiscal_year(), reopen_fiscal_year(), and closing_style with its four account roles in the pack.
  9. Pack versioning, ekwo pack upgrade, an append-only audit log. Donecountry_packs and company_packs carry the versions, ekwo pack status and ekwo pack upgrade diff by natural key and apply only an addition and a closed validity, and audit_log records every change to the configuration of a company and every act that changes a state. The ledger itself is not audited: a posted entry is immutable and is corrected by a reversal.
  10. One golden test per pack, a certification status. Done — each pack carries golden/scenario.json, a year of at least ten documents with the payments that settle some of them, and beside it the declaration, the statements and the trial balance the engine makes of it, to the cent. tests/golden.test.ts is one runner with no country in it: what it asks of a scenario, it asks of that scenario's own pack. A pack with no golden is refused unless its manifest says why. legal_reference became required on every tax and every box, and certification.statuscommunity, maintained, reviewed — is printed by ekwo init before a company is created. There is no status meaning "certified by Ekwo": writing a pack is not reviewing it. The first run reported two defects in the French pack rather than adjusting the golden to match them.
  11. End-to-end test, including an upgrade from the published version. Donetests/e2e/ installs the same release twice, once through the CLI's runner and once the way supabase db push and psql -f do, and compares every row of every table the seeds write; then it takes a company installed at the previous version, upgrades its pack, replays that pack's own golden scenario, files the declaration, prints both statements, closes the year, re-opens it and closes it again — comparing every figure to one it works out itself from sum(debit) - sum(credit). npm run e2e:supabase covers what PGlite cannot reach: the published binary over a pooler connection, PostgREST, GoTrue and a hosted project's extensions.
  12. Documentation: docs/packs.md, the contributor's guide. Donepacks.md is the format file by file, the compiler, every rule ekwo pack check applies, the certification policy and a walkthrough for adding a country in a day; CONTRIBUTING.md carries the invariants a country pack may not break; and the installation documentation names the four things an operator has to do on their own project, which ekwo init prints at the end of a successful run.

The original six-item list, for the record:

  1. The country pack format — chart of accounts with translations, taxes and boxes, financial-statement mappings per framework, document rules, e-invoicing profile, bank formats, defaults; versioned; installed by a generalised install_country_template; one golden test per pack: ten posted documents, every box and every statement line to the cent. Belgium and France become the first two packs, which purges the core of what was theirs.
  2. Year-end close and periods — result allocation, opening entries, monthly or 13 periods, shifted and 52/53-week years, opening balance import.
  3. Multi-currency, properly — functional currency per company, realised gains and losses at matching, periodic revaluation of open items and of foreign-currency bank accounts.
  4. Cash basis alongside accrual — reports derived from payments; VAT on collection (French services, the UK cash accounting scheme).
  5. Cash-flow statement, immutable audit log, translated labels.
  6. A generalised tax engine — a tax declares its kind (VAT, GST, sales tax, withholding, excise), whether it is recoverable, what it is computed on, whether prices include it, its jurisdiction, and its rounding rule. Reverse charge is already there.

Phase 1 — first wave (first quarter of 2027)

  • Estoniadone, 14 September 2026, and out of order: a small VAT system with a rate that moved twice in eighteen months, no legal chart of accounts, and a return that nests its boxes. It was picked to test the format against a country nobody designed it for, and the six gaps below are what it returned. A seventh — that a seed's file name was the country's alphabetical rank, so adding one renamed the seeds of every country after it — was fixed rather than recorded, because leaving it would have meant shipping the damage.
  • United Kingdomdone, 15 September 2026, and the first pack of a country outside the Union: no intra-Community tax on either side, retail prices quoted with the tax in them, an exemption the European code lists have no code for, and a nine-box return that prints one amount in two boxes at once. A British-style chart mapped onto the statutory small-company formats, the nine boxes of the VAT Return, FRS 102 for the fixed assets, and the gaps listed under "From the United Kingdom" are what it returned — the first two of them closed the same week. Northern Ireland, Making Tax Digital submission and the VAT schemes are out of its scope.
  • Irelanddone, 21 September 2026: a chart of accounts and form VAT3, described under "Ireland" below.
  • Canada and Québec — GST, HST and QST stacked per line, PST as a non-recoverable tax in British Columbia, Saskatchewan and Manitoba, two administrations (CRA and Revenu Québec), bilingual labels, a chart in the style Canadian bookkeepers use, shifted years. Rates live in the pack: fifteen or so stable combinations published by the CRA are data, not the thousands of monthly-changing American jurisdictions that belong to a feed. It comes before the United States: closer to the accounting model this core was built on, and a test of the tax model that the US does not offer. report_code on the postings and region on companies and contacts were built in phase 0 so that this pack migrates nothing twice.
  • Netherlands, Germany, Luxembourg — RGS, SKR03/04 with XRechnung, PCN. Luxembourg is done, as packs/lu/, and the Netherlands, as packs/nl/ on 21 September 2026; both are described below.
  • United Statesdone, 16 September 2026, and out of order for the same reason Estonia was: it is the country that tests what the format assumes about a tax. Every pack before it, the British one included, described a value added tax — levied nationally, reclaimed by the buyer, declared on one form. Here the tax is levied by the states and by thousands of districts under them, the buyer never gets a cent of it back, there is no national return and no legal chart of accounts. A chart written against Regulation S-X, sales and use taxes for three states chosen for what each demonstrates, California's thirty-nine-line return, and the gaps listed under "From the United States" are what it returned. Cash-basis reports, 1099 fields, the other forty-five states and every return but California's are out of its scope, and the rates of American jurisdictions remain what the decision below says they are: a feed, not a pack.
  • Formats — Peppol PINT and UBL 2.1 as the universal invoice; OFX, BAI2, MT940 and camt.053 bank parsers (camt.053 is read: @ekwo-ai/camt053). MIT packages under packages/formats/, one per format: camt.053 is no more European than UBL is universal, and neither is a country.

Phase 2 — GST countries and southern Europe (mid-2027)

Australia and New Zealand (BAS), Singapore; Spain, Italy and Portugal (withholding, FatturaPA, SII); consolidation across companies; iXBRL accounts for Companies House. Spain is done, as packs/es/ on 21 September 2026, ahead of the phase — see "From Spain" below.

Phase 3 — the community makes the countries

A contribution kit for a pack with its golden test, a status page per country, a reviewed-pack label. A localisation is data, contributable without touching the core.

Part of this arrived early, as a by-product of phase 0: the golden runner takes any pack, ekwo pack check tells a contributor what is wrong in their own terms, the three certification statuses exist and ekwo init prints the one it is installing, and packs.md walks through adding a country. The outside of it has started too: ekwo.ai publishes a page per pack, generated from the pack, with its status and every source it cites. What is missing is enough contributed packs for the question to be interesting.

The packs, country by country

Which packs this checkout carries, and the status of each, is the table of packs: it is generated from packs/, and so it is never a country behind. What each pack leaves out is written by whoever wrote it — below for the first six, in the table they landed with, and in its own section further down for every pack after them.

Country Pack Out of scope, and why
Belgium packs/be/
Estonia packs/ee/ KMD INF, the § 44 cash-accounting scheme, the fixed-asset rules, the XBRL fact keys of the annual report, and versions of form KMD before 1 July 2025
France packs/fr/
Luxembourg packs/lu/ the eCDF XML of the periodic return, the FAIA audit file, the annual VAT return, the special regimes, and corporate income tax
United Kingdom packs/gb/ Northern Ireland and the XI prefix, Making Tax Digital submission, the flat rate, cash accounting, annual accounting, margin and retail schemes, partial exemption, the Construction Industry Scheme return, corporation tax and capital allowances, the medium and large formats of S.I. 2008/410, and iXBRL for Companies House
United States packs/us/ forty-five other states with a sales tax and every return but California's, district rates by address, the local taxes Alaska, Colorado and Louisiana administer themselves, the Streamlined Sales and Use Tax Agreement, sales tax on services, marketplace facilitator rules, federal and state income tax, MACRS, and Inline XBRL for the Securities and Exchange Commission

Estonia

Written from the outside in, against a country nobody had designed the format for, and picked because it is small enough to finish and awkward enough to be interesting: a standard rate that moved twice in eighteen months, a reduced rate that went 9 %, 5 % and 9 % again, a return whose boxes nest three deep, and no legal chart of accounts at all.

It carries an original chart of 120 accounts, 30 taxes with the rate history back to 2009, form KMD as it stands since 1 July 2025, and the balance sheet and income statement scheme 1 of the annual report. Three things are worth knowing beyond the pack's own README:

  • The chart is written, not transcribed. The Accounting Act obliges every entity to draw up its own, so there is no text to copy. The pack follows the convention Estonian practice shares — four digits, four classes, equity inside class 2 — and blocks the codes so each range maps onto one line of the statutory schemes.
  • No tax provision is booked at the close, and that is the law. Estonia taxes distributed profit, not earned profit. closing_style is result_accounts because the statutory balance sheet keeps the year's result on a line of its own until the shareholders allocate it.
  • It is community. Nobody who files an Estonian return has read it, and the pack's README ends on the four points a reviewer should look at first.

Luxembourg

The third pack, and the first written from published sources alone rather than from a running installation. It carries the plan comptable normalisé of the règlement grand-ducal du 12 septembre 2019 whole — 1 026 accounts, 747 of them postable — the four VAT rates of article 39 with the temporary 2023 rates beside them, the 162 numbered fields of the eCDF periodic return, and the two abridged schemes of annual accounts keyed by their own eCDF field identifiers.

Three things about it are worth knowing beyond the pack's own README:

  • The State publishes the mapping. The annex that carries the chart carries the tableau de passage as well: which line of the abridged balance sheet or of the abridged profit and loss account each account reports in. The pack transcribes it account by account, so every postable account reaches exactly one line without anybody inferring a range.
  • It is community. Nothing here was read by a Luxembourg accountant, and the pack's README ends on the ten points a reviewer should look at first.
  • Out of scope, on purpose. The eCDF XML of the periodic return, the FAIA audit file, the annual VAT return (a different form with fields of its own), the franchise and VAT-group regimes, the full unabridged schemes and their notes, and the tax module. The first of those is a format library and not a pack; the rest wait for somebody who files them. The eCDF envelope itself is written by @ekwo-ai/ecdf since the recapitulative statement arrived, which is what the distinction looks like in practice: an envelope is a format, the boxes of a return are a pack.

France

The first pack, and the one the others were measured against — which is how it came to lag behind the form it transcribes. Compared line by line with the official form on 21 September 2026, it had no rate for the overseas departments and was missing some thirty lines of form 3310-CA3; the pack's own README lists every line of the form in force and says, for each one it does not carry, why.

  • The overseas departments are carried through the territories. Article 296 of the CGI taxes Guadeloupe, Martinique and La Réunion at 8,5 % and 2,1 %, and each code names its department in applies_when.supply_in. Since applies_when has no disjunction, it is one code per department and per rate: GP, MQ and RE are rows of territories already.
  • French Guiana and Mayotte are the case the format cannot yet state. Article 294, 1, puts them outside French VAT altogether. The pack offers no code for them, which is right, but nothing refuses a metropolitan code for an operation located there either: FR-S-20 names no territory, and a condition cannot say "not in GF". That is a property of the territory — its parent's tax does not reach it — and not of a French tax, so it belongs in the core, beside territories, and not in applies_when. Until then: the README of the pack says it, and the declarant does not pick a French code for an operation the law places in Guiana or Mayotte.
  • The frame A numbering predates the redesigned form. The lines the pack names 01, 03, 2A, 3A, 3C, 04, 05 and 06 are printed A1, B2, A3, A4, B4, E1, E2 and F2 today, and the continental 2,1 % sits on line 13 where the form now prints it on T6 — which the label of box 13 now says. A box identifier is immutable once published, so the correction is a new version of the form beside this one, once a pack can hold several.

United Kingdom

The fifth pack, and the first of a country that is not a Member State of the Union. It was written for that reason: every pack before it could lean on the VAT Directive, on the intra-Community mechanism and on the European code lists, and nobody knew how much of the format silently assumed them.

It carries an original chart of 190 accounts, 25 taxes with the standard rate back to the commencement of the Value Added Tax Act 1994, the nine boxes of the VAT Return as they stand since 1 January 2021, the balance sheet and the profit and loss account of the small companies regime, and the usual lives of a fixed asset under FRS 102. Four things are worth knowing beyond the pack's own README:

  • There is no legal chart of accounts, and no legal statement schemes either — there are legal formats. Companies Act 2006, s. 396 requires the accounts to comply with regulations as to their form and content, and those regulations prescribe the lines of the balance sheet and of the profit and loss account letter by letter. So the statements of this pack are transcribed, exactly as Luxembourg's are, while the chart underneath them is written: the codes are the four-digit convention a British nominal ledger uses, blocked so that each range reaches one item of Schedule 1 Format 1.
  • Nothing here is intra-Community, on either side. Since 1 January 2021 a supply from Great Britain to a Member State is an export and an arrival is an import, so four of the eleven treatments never occur. What replaced them is postponed VAT accounting, which the pack models as an import posting to boxes 1, 4 and 7 that nets to nothing in the ledger.
  • Northern Ireland is deliberately absent. The Windsor Framework keeps it inside the Union's rules for goods under one registration with Great Britain, and boxes 2, 8 and 9 of the return are about that trade alone. They are declared and empty; see the last gap below for why the pack could not carry them.
  • It is community. Nobody who files a British return has read it, and the pack's README ends on the points a reviewer should look at first. The first two of them were the exemption reason code this pack had to invent and the Union's export code it had to borrow; both are gone with pack version 0.1.1, which leaves no exemption_code on any British tax.

What a new country shows the core cannot say

From Luxembourg

A country pack is a test of the format as much as of the country. Four things the Luxembourg pack had to work around, with what would fix each. None was implemented for Luxembourg's sake — a gap the core has is a core issue, and patching the core for one country is what this format exists not to do. Three were closed on their own merits, a day later and for every country; the fourth waited until a third pack met it, and is closed too.

  • A company does not record which period it files on. Closed, 14 September 2026. companies.vat_period holds the answer, nullable and with no default; country_defaults.vat_period_default is where a pack proposes one, and every pack here leaves it null because Belgium, France and Luxembourg all make the cadence follow turnover. ekwo init asks when the form offers several, ekwo status prints it, and vat_return() does read it after all: it refuses a period the company does not file on, which is the one use of the answer that nothing around the return could have.
  • A form's period cannot say "month, quarter or year". Closed, 14 September 2026. tax_report_templates.periods is a list of declaration_period, and tax_report.json takes either the list or the single word it used to. month_or_quarter is read as the two cadences it always meant, and the column's default — which handed Belgium's cadence to every country that had not spoken — is gone: a form that names none is refused by ekwo pack check.
  • sequence on a declaration box means print order and evaluation order at once. Closed, 16 September 2026, and by both halves of the fix this entry proposed. The rule in ekwo pack check that refused a total naming a total at the same sequence or later is gone, replaced by the cycle check the statements already had, which names the boxes; evaluate_totals() had ordered by dependency since 12 September and the rule was the only thing saying otherwise. And print_sequence is now a field of a box, optional and meaning sequence where it is left out, so a form says where the administration prints a box and where the pack declares it, and the order they are worked out in is neither. vat_return() answers the resolved value. Raised by Luxembourg, met again by Estonia and by the United States, and closed by the pack that needed a box worked out from another box.
  • A statement line may be computed and carry a sign, and the sign is applied to the total. Closed, 14 September 2026. ekwo pack check refuses sign together with plus or minus, the way it already refuses a line that is both summed and computed. The evaluator is unchanged: applying the sign "once" has no meaning while the lines below carry their own, and the day a country wants a total presented against its components, the honest shape is a second line rather than a flag that reverses one. No pack combined the two, so no golden figure moved.

One restriction turned out to be worth keeping. A tax takes one base posting per kind of document, and the Luxembourg return reports the taxable amount of a sale twice — as turnover in section I, and in the rate breakdown of section II. The pack expresses the second as a total computed from the first, which is one definition instead of two and is the better shape. Documented, 15 September 2026, and not a change to the core: packs.md carries the rule where the postings are defined and again in the walkthrough, with box 472 as the worked example.

From Estonia

None of these blocked the pack. Each one made it say something less precise than the law does, and each is a change to the core rather than to a pack.

A fact key cannot be a plain element name, and a taxonomy version cannot be a date. ekwo pack check requires an xbrl key to be a metric plus at least one domain member, each part lower case, because the Belgian CBSO taxonomy is dimensional; and it requires taxonomy to read <name>:<dotted number>. The Estonian et-gaap taxonomy names the lines of its primary statements with plain, undimensioned concepts — et-gaap:CashAndCashEquivalents — and versions itself by date, et-gaap_2026-01-01. Fix: accept a single-part key, allow a hyphen in the prefix and mixed case in the local name, and widen the version to any sequence of letters, digits, dots and hyphens. Until then: the Estonian statements carry no fact keys at all, because a wrong key is worse than none, and no filing brick can read them.

A declaration form whose boxes nest cannot be expressed directly. Closed, 15 September 2026, by the first of the two fixes this note proposed: a base posting names several boxes. box takes a string or a list of strings in taxes.json, declaration_boxes text[] carries the list beside the declaration_box it starts at, and vat_return() sums a line into every box the posting behind it names. The second fix — a posting type that reports and books nothing — was not needed and would have been worse: it would have put a row on the ledger side of the format for something that is not a ledger fact. The Estonian pack now posts an intra-Community acquisition to boxes 1, 6 and 6.1 at once, and its six hidden boxes are gone, with every figure of the golden scenario unchanged to the cent. What is not closed is the rule that caused it: a tax still carries one base posting per kind of document, and a parent that is a sum is still a total. Only a parent that is not a sum is named by the posting.

There is no treatment for a service received from outside the Union. Closed, 15 September 2026, and not under the name this note proposed. The value is foreign_services_received, not import_services, because the rule it names — articles 44 and 196 of Directive 2006/112/EC — turns on whether the supplier is established in the buyer's country and not on whether the service crossed the Union's border; a name built on "import" would have been as wrong for it as import already was, and import in this vocabulary means goods declared to customs, which is a different mechanism behind a different document. The Estonian EE-P-VS-24 carries it and has dropped the sentence of its legal reference that apologised for saying import. On the invoice it resolves to the reverse-charge mention: the same mechanism as a domestic reverse charge, under a different article.

A country that keeps one account for both signs of the year's result has to name it twice. retained_earnings_loss may be null and falls back to retained_earnings; current_year_result_loss has no such fallback, and result_accounts closing requires both. The Estonian balance sheet has one line, Aruandeaasta kasum (kahjum), and Estonian practice one account. Fix: let current_year_result_loss fall back to current_year_result_profit, as its sibling already does. Until then: the manifest names 2980 twice.

An e-invoicing obligation that depends on the buyer cannot be said. einvoicing.mandatory_from is a date and nothing else, so a pack can say "from this day everyone is bound" or say nothing. Since 1 July 2025 an Estonian seller must issue an e-invoice when the buyer is registered in the commercial register as an e-invoice recipient and asks for one; there is no day on which everyone is bound. Fix: an obligation field beside the date, with a closed vocabulary — none, on_buyer_request, reception, emission. Until then: Estonia leaves mandatory_from null and puts the rule in the legal reference, so a reader asking whether e-invoicing is obligatory there is told nothing rather than told wrongly.

A box of a declaration is a monetary amount. Boxes 5.3 and 5.4 of form KMD each carry a number of cars beside the amount deducted. No fix is proposed here: a count comes from somewhere other than the ledger, and where that is belongs to a longer conversation than this list. Until then: the Estonian pack declares the two amounts and not the two counts, and says so.

One of Luxembourg's four turned up again, which is the answer to whether it was a Luxembourg problem: sequence on a declaration box means print order and evaluation order at once. Box 1 of form KMD is printed first and is a total of boxes printed after it. Estonia escapes because the rule only constrains a total that names another total, and box 1 names base boxes — but it escapes by luck, and the fix Luxembourg proposes is the fix. Closed, 16 September 2026, by print_sequence and by the removal of that rule.

From the United Kingdom

Seven things the first pack outside the Union could not say precisely. Four of them are about the same assumption — that a country's VAT is the Union's VAT — and the other three are about what a price, a rounding rule and a filing cadence belong to. None blocked the pack. None was patched for its sake: the first two were closed afterwards, on their own, with the pack already landed and its own README naming them as the things a reviewer should refuse first.

An eighth is of a different kind and is recorded at the end: ten assertions of the test suite that had never been contradicted, and that were fixed rather than worked around, because a test reading the pack is what this repository asks for in as many words.

A VAT exemption outside the Union has no reason code, and one is required. Closed, 15 September 2026. ekwo pack check demanded an exemption_code as soon as vat_category was E, and checked its shape against VATEX-EU-<article> or VATEX-<country>-<article>. The VATEX list is European: its own codes name articles of Directive 2006/112/EC, and its national codes — VATEX-FR-CGI261-1 and the rest — belong to Member States that publish them. A British exemption is Schedule 9 to the Value Added Tax Act 1994 and no published list carries a code for it, because no administration that would publish one has any reason to. The rule was right for a Member State and had no answer for a third country. The fix is the one proposed, generalised: the last column of the table under "What a tax says on the invoice" now applies where the Union's VAT does, and nowhere else. For a pack whose country the common system does not reach, exemption_code stays null, the article goes in legal_reference where it was going anyway, a VATEX-* code is refused by name, and the five intracom_* treatments are refused outright. The categories are untouched, because UNCL5305 is a UN/CEFACT list and E, G, O and AE mean there what they mean anywhere. Which side of the line a pack is on is read from territories — the eu_vat_scope of its country at the manifest's released_at — and nothing about it is written into the code: pack check has no database, so it parses supabase/seed/00_territories.sql, and tests/vat_codes.test.ts holds its answer against eu_vat_scope_of() for every territory on every date the table carries. Should a third country ever publish reason codes of its own, the column takes them where the pack's register declares that list with kind: standard; the field is provided for and the content is not, because nobody has published one. packs/gb/ 0.1.1 drops VATEX-GB-SCH9 and the three Union codes it had borrowed, and its README's first two review points are gone with them.

G and VATEX-EU-G say "export outside the EU", and a third country's export is not that. Closed, 15 September 2026, as the same change and by reading the standard more carefully. The claim that G describes the Union's border came from use case 4 of the Commission's technical guidance, which is written for a seller established in a Member State. UNCL5305 itself says of G free export item, VAT not charged: the goods leave the territory of whoever levies the tax, and a supply from Great Britain to a Member State is one. So the category was never wrong for a British export and the refusal message was — it now says what UNCL5305 says, with the guidance's use case named as the Member State's case of it, and docs/packs.md reads the same way. The code was the wrong half: VATEX-EU-G names article 146 of a Directive that does not bind the seller, and it is refused outside the Union along with the rest of the list. GB-S-EXPORT carries G and nothing else, with s. 30(6) of the Value Added Tax Act 1994 in its legal_reference.

One taxable amount, two printed boxes that are siblings and not nested. Closed, 15 September 2026, by the Estonian fix, which covered both shapes exactly as this note said it would. The value of a service received from a supplier established abroad is one base posting naming ["6", "7"], and boxes 6 and 7 are ordinary boxes summed from the ledger again rather than totals of a workaround. The three hidden boxes are gone from a nine-box form, and the golden scenario's figures did not move. Nothing here turned on the two boxes being siblings rather than nested: a posting names the boxes the form prints its amount in, and where they stand on the form is the form's business.

A price that includes the tax is declared and never computed. Closed, 15 September 2026. The engine takes the tax out of the gross of each tax group, rounds it once as BR-CO-14 requires, subtracts it to get the base — so base + tax is the price that was quoted, always — and shares that base back over the lines in proportion to their gross, the last line taking the remainder. The line keeps the gross it was quoted at and a snapshot of the flag, frozen when the document is posted. GB-S-20-INC is now booked in the golden scenario for what it is, a day of counter sales of 5 493,92 gross, and decision 0022 is where the arithmetic and the three refusals are written down. The shared invoice carries the two fields too, so a link says which price it is showing. Two narrower gaps came out of it and are below: the choice HMRC gives a retailer between two rounding units, and BT-146, the net unit price, which nothing publishes where the price was quoted gross.

A rounding unit is a choice a country may give a trader, and nothing can record it. VAT Notice 700, §§ 17.5 and 17.6, lets a retailer work the tax out line by line or invoice by invoice, and both are lawful. Ekwo does it invoice by invoice, per tax group, because that is what EN 16931 BR-CO-14 requires of a structured invoice and a line-by-line figure would fail validation. That is the right default and it is still a choice made for the trader rather than by them. Fix: whatever records the choice belongs beside the other one on this page — a nullable column on companies overriding the country, in the shape vat_period already has — and it is a different field from rounding_method, which is the arithmetic and not the unit it applies to. No new vocabulary was invented for it here, on purpose: a word in the pack format is a word every pack has to mean something by. Until then: a British retailer who works line by line files a figure Ekwo does not produce, and is within a penny or two of it on any invoice with more than one line.

BT-146 is the net unit price, and nothing publishes it where the price was quoted gross. document_lines.unit_price is the price as it was keyed, which on a retail line is the gross one, and document_line_items hands it on under that name. Nothing is ambiguous about it — the view and the shared payload both carry unit_price_includes_tax and amount_incl_tax, so a reader knows which price they have and what the gross was — but the net unit price itself is not published anywhere. Its honest definition is the base divided by the quantity, because BR-CO-10 wants quantity times BT-146 to be BT-131 and the group's remainder lands on a line. What stops it being a column today is the precision: it is the price column's six decimals and not the currency's, which round_amount does not express, and writing ::numeric(16, 6) would put a second place where decimals are decided — exactly what npm run check:rounding exists to refuse, and it refuses it. Fix: a way to say "at the precision this column has" that the rounding rule owns, then a unit_price_net beside the two fields that are already there. Until then: a renderer that needs BT-146 divides amount_untaxed by the quantity itself, which is the same arithmetic done one layer out, and every pack here but the British retail tax prices net and is unaffected.

A rounding rule belongs to a country, and HMRC gives one to each kind of trader. country_defaults.rounding_method is one value per country and there is no column beside it on companies. HMRC's concession, recorded in VATREC12010 and VATREC12020, lets an invoice trader round the VAT payable down to a whole penny, because the rounding is neutral between the supplier's output tax and the customer's input tax; and it says the same concession is not appropriate for a retailer, for whom rounding down reduces the tax accounted for without reducing the tax charged. Two lawful methods in one country, chosen by what the business is. Fix: a nullable rounding_method on companies that overrides the country's, which is the shape vat_period already has — the pack proposes, the company decides, and nothing falls back on another country. Until then: packs/gb/ declares half_up, which is the method a retailer must use and one an invoice trader may, and a company on the concession has nowhere to record it.

A tax cannot depend on the territory the parties are in, and one registration can cover two tax territories. Closed, 16 September 2026, and by the fix this note proposed, word for word: a tax names a territory, a party records the one it is in, and the engine compares them. applies_when on a tax takes seller_in, buyer_in and supply_in, each one territory of the reference table; companies.territory_code and contacts.territory_code say where a party is, documents.supply_territory_code says where a supply lands, and a condition is satisfied by the territory named and by every territory inside it, so a British tax that says nothing reaches a seller in Northern Ireland and one that says XI does not reach a seller in Manchester. post_document() raises tax_territory_mismatch where a document contradicts a condition, and ekwo pack check now judges a tax's VAT regime on the territory it applies in rather than on the pack's country — which is what makes eu_vat_scope = 'goods' mean something: a tax applying in XI is inside the common system for a supply or an acquisition of goods and outside it for everything else. tests/tax_territory.test.ts proves all three on a copy of packs/gb/ with one tax changed, because no pack of this repository declares an invented tax. packs/gb/ still carries no Northern Ireland tax: the case is expressible and writing the codes is a transcription of the Protocol that a British accountant should sign, not a thing to do in the pull request that made it possible. Boxes 2, 8 and 9 stay declared and empty until somebody does. The decision, and the three shapes weighed against it, are in decision 0024. What follows is the note as it was written.

A tax cannot depend on the territory the parties are in, and one registration can cover two tax territories. Half of what this note first proposed landed the same day, from the recapitulative statement's own list: territories is reference data of the framework, XI is in it with eu_vat_scope = 'goods' and the prefix VIES publishes for it, and GB carries the day it left the common system. What that buys is a reader — the statement asks the table and stops listing supplies to the United Kingdom after 2020. What it does not buy is a pack. Since 1 January 2021 one VAT registration covers Great Britain, where the Union's rules do not apply, and Northern Ireland, where they do for goods: a Northern Irish seller identifies under XI, makes intra-Community supplies of goods, and files boxes 2, 8 and 9 of the same nine-box return. A pack is keyed on a country and has no unit below it; companies.region and contacts.region exist and nothing reads them; and no tax may be conditioned on either. So packs/gb/ cannot carry the Northern Ireland taxes without claiming they apply to a company in Manchester. Fix: let a tax name a territory the way it already names a jurisdiction, and let a company record the territory it is established in, so that one country's pack can carry two sets of taxes and offer each where it applies — the table that says which territory is which already exists. Until then: packs/gb/ is Great Britain's return, boxes 2, 8 and 9 are declared and empty, and the pack's README sends a reader to territories for what XI is.

A pack may propose a filing cadence only where its form accepts exactly one, and the United Kingdom has a default its form does not show. Closed, 16 September 2026, and by the fix this note proposed: the proposal is judged against the law and not against the length of a list. It also moved where it belongs — onto the form, as tax_report.json's period_default — so that a pack carrying two declarations can propose a cadence for each. packs/gb now proposes quarter on a form filed on three cadences, citing reg. 25(1). Re-reading the other four against the same rule moved two of them: Belgium and France each make the monthly return the rule of the code and the quarterly one an authorisation granted on turnover, which is a default the law gives everybody, so both propose month; Luxembourg proposes nothing, because there the cadence follows turnover with no answer for everybody. What follows is the note as it was written.

A pack may propose a filing cadence only where its form accepts exactly one, and the United Kingdom has a default its form does not show. tests/tax_report.test.ts states the policy as an invariant over every pack: a form filed on one cadence has that cadence proposed in defaults.vat_period, a form filed on several proposes nothing. The reason given is sound for every pack written before this one — everywhere in Europe the cadence follows turnover, so the answer is a fact about the company and a pack proposing one would be choosing a filing deadline for somebody it knows nothing about. Regulation 25(1) of the Value Added Tax Regulations 1995 makes the prescribed accounting period three months for everybody, and a month or a year is something the Commissioners allow or direct on application. So the British form is filed on three cadences and the British law still gives one default, which is the case the rule cannot express: it reads the length of a list where the question is what the law says. Fix: judge the proposal against the law rather than against the form — keep the refusal of a proposed cadence the form is not filed on, which catches a real mistake, and drop the rule that a form with several may propose none. If the invariant is worth keeping mechanically, the manifest is where the distinction belongs: a cadence the law gives, and a cadence left to the company, are two different silences and today they are the same one. Until then: packs/gb/ proposes nothing, ekwo init asks a British company what it files on, and the pack's README says that a company which has asked HMRC for nothing files quarterly.

And ten assertions that were an unnamed country. These are not gaps in the format: the core supported everything the United Kingdom asked of it. They are places where tests/ had assumed something every pack until now happened to satisfy, and the invariant CONTRIBUTING states — a test may book in a country, it may not expect one — is what says they are defects. Unlike the seven above, they were fixed, in a commit of their own, and every fix removes an assumption rather than adding a country: npm run check:no-country-literals never saw any of them, because none of them spelled a country code.

What was assumed What the United Kingdom is What the test reads now
every pack declares at least one other language a pack written in English has none to declare the promise is checked for what is declared, and one assertion says the repository still has a multilingual pack
manifest.languages is an array it is optional and absent ?? []
every asset category has translated labels a pack with no i18n file has none the column's own empty object
a declaration form has more than twenty boxes the VAT Return has nine, and had twelve with the pack's hidden ones the form carries every box the pack's taxes post to, at least one of them, and at least one total
the fixed-assets module has exactly two country seeds, named it has one per pack that says something about fixed assets derived from allPacks and each manifest's seed_sequence
country_packs in slug order is allPacks order the first pack whose name does not sort where its slug does — United Kingdom after Luxembourg sorted the way the query asks, by name
a closing style is appropriation_accounts or result_accounts the first pack that closes straight into retained earnings, which the schema has always allowed and docs/packs.md names the United Kingdom for the enum of packs/schema/pack.1.json, beside the statuses and the cadences already read there
every pack names an account for the result of the year under retained_earnings there is no such account and the schema says the roles are null the roles, nullable
every pack's country is a Member State it is in territories with the day it left the country has to be known to the table, and a Member State exactly where the pack's own treatments are intra-Community

The last one arrived with territories on the same day, and is the sharpest of them: the table was written so that the core could say whether a country is in the common system, and the test that read it asked every pack's country to be a Member State. It now asks the pack. A pack whose taxes are intra-Community has to be inside the system and one whose taxes are not has to be outside it, which is a stronger claim than the one it replaces and the only one a pack of a third country can satisfy.

The eleventh is the cadence above. It was not fixed on the day it was found — it is a policy and not an assumption, so the pack worked around it — and it was fixed on 16 September 2026, by the fix it proposed.

One thing the United Kingdom confirmed rather than found. A legal mention cannot tell a domestic reverse charge from a foreign one — recorded from the EN 16931 code lists, where no pack needed the distinction. This one does: s. 55A of the Value Added Tax Act 1994 moves the liability on a construction service supplied inside the United Kingdom, and s. 8 does it on a service received from a supplier established abroad. Two articles, two mechanisms, one applies_when. The pack prints one sentence and names both articles in its legal reference, which is the argument for the tenth condition that note proposed.

From the United States

Fourteen things the first pack of a country with no value added tax could not say precisely, and they are not the same kind of thing as the seven the United Kingdom returned. Those were about a VAT that is not the Union's. These are about a tax that is not a VAT at all, levied by a government the pack format has no unit for, on a return that has no national version, against accounts no statute prescribes. None of them blocked the pack. None was patched for its sake — and two were then closed on their own merits, on 16 September 2026, for every country: the first on this list and the confirmation at the end of it, which turned out to be one change, because a box worked out from another box is what makes a print order and an evaluation order two different questions.

Two are recorded at the end and are of a different kind: one row of framework reference data a pack outside the Union cannot do without, and one assertion of the test suite that had never been contradicted.

Six of the fourteen were answered on 16 September 2026. Four of them are about vocabulary — a word for a tax the buyer assesses on themselves, a category asked where no invoice carries one, a register entry read as a code list it never claimed to be, and an exemption whose condition could not be written down. Three of those are struck through below, with what was done and what a reviewer still has to decide; the fourth is not, because the pack can now say what an exemption depends on and still cannot hold the evidence, so that note stands with the half that is closed marked and the half that is not written out. The fifth is the arithmetic of a form itself. The sixth is the largest of the list — a tax that could not name the territory its parties are in — and it was written from both ends, here and from the United Kingdom, before the same fix closed both. The rest stand, and what they are waiting on is no longer one another.

A box of a declaration form can be a rate applied to another box, and a total is only a list of boxes to add. Closed, 16 September 2026, by the fix this entry proposed and under the names it proposed. CDTFA-401-A states four of its lines as a multiplication in as many words: line 13 is line 12 times 0.06, line 14 is line 12 times 0.0025, line 15 is line 12 times 0.01, and Sections C and D are a base times 0.05 and times 0.039375. There is no expression language in this format and there is none now: a box may carry rate, a percentage, and rate_of, the one box it applies to, resolved the way a plus reference is — two named fields, nothing to parse, and a reviewer reads six per cent of line 12 in the diff. A box is a list or a rate and never both, only a computed box carries either, and evaluate_totals() works one out exactly as it works out a list: when the box it names has been worked out. Lines 13, 14 and 15 of packs/us are written that way and the four tax postings that used to fill them from the ledger have lost their boxes; not one figure of the golden year moved, in any of the three quarters, which is what the apportionment had promised and could not guarantee.

Two things the closure did not cover, and both are the pack's and not the format's. Line 16 is not a rate of line 12: a district tax is owed on the sales made in that district and not on the period's whole taxable total, and the form carries the figure over from CDTFA-531-A2 rather than multiplying, so that box stays summed from the ledger — correctly, and it is the one of the four that was never an apportionment of anything. Sections C and D are still not carried: their shape is sayable now, and what is missing is the eight deduction lines they hold and the taxes that would reach them, which nobody has transcribed. Line 20a, which they feed, is still declared and empty.

A pack declares one declaration form, and this country files fifty of the same one. The gap itself is not new — it is written above, under the recapitulative statement, where a pack that cannot declare a second form leaves ec_sales_list() with a guard nobody can arm. What the United States adds is the shape of the second form. There, the forms a pack is missing are other declarations of one country: a recapitulative statement, an annual return beside the periodic one, each with a kind of its own. Here they are the same declaration, fifty times over, filed to fifty administrations under fifty bodies of law, with different boxes, different cadences, different due dates and a different territory each. A company selling into three states files three sales tax returns and none of them is national. packs/us/ carries California's; the New York tax posts to the ledger and to no box at all, so an amount the company genuinely owes New York is invisible to every return the core can produce. Fix: the one proposed above — a list of forms per pack, with a kind on each — and one thing it has to carry that a European pack would not have asked for, which is the territory the form belongs to, because that is what says which of the fifty a given sale is declared on. Until then: one state, and a README that says which.

A tax cannot be conditioned on the territory of the parties, so a pack cannot offer a company only the taxes that apply to it. Closed, 16 September 2026, and by the fix both notes proposed, including the second half this one added: the condition is on the buyer and on the place of supply and not only on the seller. packs/us/ now says where each of its codes applies — US-CA-S-725 is a Californian seller and a Californian delivery, US-NY-S-8875 is a delivery to New York, US-OR-S-0 a delivery to Oregon — and post_document() refuses the Californian tax on a document delivered to New York, by name, before anything reaches the ledger. The golden year gained the territories of its eight parties and its one out-of-state shipment, and not one figure of the three expectation files moved: the codes, the rates and the postings are what they were, and all that is new is that the wrong one can no longer be booked. What the fix does not do is make the New York tax declarable — see the paragraph above on filing fifty of one form, which is where that belongs. The note as it was written follows.

A tax cannot be conditioned on the territory of the parties, so a pack cannot offer a company only the taxes that apply to it. The United Kingdom recorded this as the reason Northern Ireland is absent from packs/gb/, and it is worse here. packs/us/ offers a California company the New York and Oregon codes and an Oregon company the California ones, because jurisdiction is a label on the tax and nothing reads it; companies.region and contacts.region exist, carry a state code, and are read by nothing. The fix the British note proposes — let a tax name a territory, let a company record the one it is established in — is the same fix, and the American case adds the second half of it: the tax also follows the buyer's territory, because a sale is taxed where the goods are delivered. Until then: the codes are named for the state they belong to and a bookkeeper picks the right one.

How often a company files is one column, and it belongs to the return it is named after. Closed before this pack landed, by company_filing_periods — one row per declaration a company is subject to, keyed on the company and the code of the form. The United States was written against the column and rebased onto the table, and it has nothing to add to the fix except the reason it will matter more here than anywhere: an American company's cadences are not merely different lengths of the same obligation, they belong to different administrations, so the company's side of the answer is per form and per territory at once. The key is ready for the first half and the second waits on the paragraph above.

There is no treatment for a tax the buyer assesses on themselves that is not the reverse charge of the common system. Closed, 16 September 2026, by the fix this note proposed: self_assessed, a treatment of its own for a tax a buyer owes directly to an administration under that administration's own law and computes and declares themselves. California's use tax is imposed on the buyer by section 6201 and section 6202(a) makes them liable for it; mechanically that is the shape domestic_reverse_charge books, and it was wrong in every other respect — no exempt supply behind it, no supplier who was relieved of anything, no recapitulative statement, no article 196 of Directive 2006/112/EC, and nothing recovered at the other end, the same document carrying a tax_on_base posting because the tax is a cost. US-CA-P-USE-725 now says self_assessed, and its legal_reference states the mechanism instead of denying another one. The value carries no EN 16931 category and no reason code, for the reason import and foreign_services_received carry none: a buyer assessing a tax on themselves holds no invoice the standard governs, and there may be no supplier the levying State can reach at all. It is not refused inside the common system — a Member State levying a duty of its own that a buyer self-assesses would be describing this and not article 194 — and what tells the two apart is the law each tax cites, which every tax already carries. What a reviewer holding a CPA licence still has to decide is whether California's use tax is one operation or two: section 6202(a) says the buyer's liability is not extinguished until the tax has been paid to the State or to a retailer who collects it, and the pack splits those into US-CA-P-USE-725 and US-CA-P-725. Whether a California practitioner recognises that split, and whether the use tax accrual belongs on the same four accounts as the sales tax, are questions the vocabulary cannot settle. packs/us/README.md puts both on its reviewer's list.

And there is no treatment for a supply outside the taxing territory but inside the country. Section 6396 exempts a sale that the contract requires to be shipped, and that is shipped, outside California. It is not an export, which in this vocabulary means goods leaving the country; it is not not_subject, which is a supply the law places elsewhere; and it is not quite exempt either, because what the statute says is that the receipts are exempted from the computation of the tax. The pack writes exempt and explains itself. Fix: the same one the territory gap needs — once a tax can name a territory, a supply that leaves it has a word. Until then: three American codes carry exempt for three different reasons and the legal_reference is the only place the difference is recorded.

A tax that can reach a sale must name an EN 16931 category, in a country whose invoices are governed by no standard at all. Closed, 16 September 2026, by the fix this note proposed, and on the border ST38-1 had already drawn one field over. The British pack closed the reason code half: outside the common system exemption_code is null and the article goes in legal_reference. The category is now asked where a category is read — inside the common system, or where the pack declares an einvoicing.profile, every one the format names being built on the semantic model of EN 16931 and carrying BT-151 — and a pack that declares neither may leave the column null, the way a purchase-only tax already could. The United Kingdom is unaffected: it declares peppol-bis-3 and its sellers do issue invoices somebody reads BT-151 on. The United States declares no profile, no American administration publishes a category list, and nothing there reads the field.

The column is now free in such a pack and it is not unchecked: the category codes are UNCL5305, a UN/CEFACT list, so a value a pack does name is still held to its treatment, to its reason and to its rate, everywhere. packs/us/ therefore keeps what it wrote — S on a taxed sale, E on an exempt one, O on a sale into a state with no tax — because those are true statements about the operation and nothing in a pack is ever deleted. What changed is that they are the pack's choice rather than a European standard's demand.

A register entry of kind standard is read as the code list BT-121 comes from, whatever standard it actually is. Closed, 16 September 2026, by the first of the two fixes this note proposed: "reason_codes": true on the entry that publishes the list, rather than the first standard in declaration order. A kind of its own was the alternative and was not taken — a published list of codes is a technical norm, so a second kind would have made a pack choose between two true things, and a register may name several standards of which at most one is a list of exemption reasons. ekwo pack check refuses a second flagged entry and refuses the flag on an entry that is not a standard.

The trap it closes was live in two packs, not one. packs/us/ declared the FASB Accounting Standards Codification first and the Digital Business Networks Alliance second; packs/gb/ declares FRS 102 first, so a British reason code would have been authorised by a financial reporting standard. Neither pack carries an exemption_code anywhere, so nothing wrong was ever emitted. No pack of this repository declares reason_codes today and a test says so: the field is provided for and the content does not exist yet, because no country outside the Union publishes exemption reason codes and PINT is where one would surface.

An exemption that depends on a document the buyer signed cannot be recorded, and neither can one that depends on a threshold the seller crossed. Half closed, 16 September 2026, and the half that is not is written out below. A sale for resale is untaxed because the seller holds a resale certificate — section 6091 presumes every receipt taxable until they do, and Regulation 1668 says what the certificate contains. A seller must collect in a state where they have economic nexus, which since South Dakota v. Wayfair is a running total of sales into that state: 500,000 dollars for California. Both decide which tax code a line carries.

What is now recorded is what the question was. conditions is a closed vocabulary of five words on a tax — buyer_certificate, buyer_status, transport_evidence, seller_threshold, supply_nature — and there is no value beside any of them: no threshold amount, no certificate number, no operator and no expression. A field that could carry sales_into_territory > 500000 would be a pack that evaluates, and the next country would want a second operator; the figure a threshold is set at and the contents a certificate must have are in the article the tax already cites, which is where a reviewer reads them. Five American codes now say which question they are the answer to, and a reader of the chart no longer sees four zero-rated codes and a rate, each with one long sentence and nothing else. Nothing in the core reads the field, exactly as nothing reads treatment: it lets an application put the question to a human being instead of pretending to answer it. It compiles to tax_templates.conditions and stops there, the way source_key does — it is the pack's transcription of a country's rule, not a property of the tax a company went on to edit.

The evidence itself is still nowhere, and no fix is proposed for it here. There is no place on a contact for a certificate, for what it covers and for when it expires, and no place anywhere for a rolling total of sales per territory. The first is a document-management question — a file, a validity window, a renewal, and a rule about who may see it — and the second is a reporting one that needs what the third note of this section is about, a tax that can name a territory. Both are larger than the pack format and neither should be answered inside it: a conditions that grew a value would become the rule engine this format exists not to have. What the pack states is the code a bookkeeper reaches for once the answer is known, and what it now also states is that somebody has to know it.

fiscal_year_default offers four opening months where the law offers twelve and then offers a year that opens on no first of a month at all. The Internal Revenue Service publishes three kinds of tax year: the calendar year, a fiscal year ending on the last day of any month except December, and a 52-53-week year that varies between 52 and 53 weeks and need not end on the last day of a month. The enum is calendar, april, july, october — the four openings the first five packs needed. Fix: a month number, or nothing at all: the column exists so that fiscal_year_bounds() can open a first year without being told the dates, and a country where the answer is "whatever the company chose" should be able to say that and be asked. Until then: packs/us/ declares calendar, which is the ordinary American corporate answer, and its golden scenario runs a 52-week year from bounds it names itself, which fiscal_years has always accepted.

A declaration form filed in whole units, over a ledger kept in cents, cannot be said. CDTFA-401-A prints "please round cents to the nearest whole dollar" on its face. rounding_method is the arithmetic of the ledger and cash_rounding_unit is the smallest coin of a cash payment; neither is the unit a declaration is filed in, and there is no third field. Fix: a rounding_unit on the declaration form, beside its cadence, since it is a property of the form and not of the country — the same form is filed in dollars whoever files it. Until then: vat_return() gives cents, the golden records cents, and a filer rounds by hand. It is also, incidentally, why the first gap on this page costs less than it might: a cent of difference between line 17 and the ledger disappears on a form filed in dollars.

The close cannot send other comprehensive income anywhere but the result. close_fiscal_year() closes every income and expense account into the style the pack declares, which here is retained earnings. Generally accepted accounting principles put a foreign currency translation adjustment and an unrealised gain on an available-for-sale security in other comprehensive income, which closes to accumulated other comprehensive income — a separate line of equity, caption 30 of rule 5-02, that never passes through net income. Caption 21 of rule 5-03 is on the pack's income statement and two accounts reach it, and closing the year would put them in the wrong reserve. Fix: a fifth account role, or a flag on the account type, saying which income accounts close elsewhere. Until then: the pack's README says not to post to 8400 and 8410, and the golden year does not.

A fixed asset has one depreciation plan, and an American asset has two. The accounting charge is the Codification, topic 360: a life the entity estimates, no table, no method prescribed. The tax deduction is the Modified Accelerated Cost Recovery System of Publication 946: statutory recovery periods, a half-year or mid-quarter convention, a mid-month convention for real property, and a switch from double declining balance to the straight line. They are not variations of one thing — they are two computations over the same asset that an American company keeps side by side for its whole life, and the difference between them is a deferred tax. assets.json carries one plan per category. Fix: a second plan per category, or a category that names a purpose, so that one asset can carry an accounting schedule and a tax schedule. Until then: packs/us/assets.json is the accounting one, every category says so, and docs/international.md has always listed MACRS as deliberately out of scope — which was the right call for a module and is not an answer for a country.

defaults.region cannot say that a country has no default region. The field exists so a pack can propose the province a company sits in, and it was built for Canada. An American pack cannot use it: naming a state would give every American company California's, which is the country-default mistake one level down, and leaving it null is read as "this country taxes uniformly", which is the opposite of true. Fix: a value that means "required, and the company must answer" — the shape vat_period has, where a pack that proposes nothing makes ekwo init ask. Until then: packs/us/ leaves it out and nothing reads it anyway.

And one thing this pack confirmed rather than found. Closed, 16 September 2026. sequence on a declaration box meant print order and evaluation order at once, for the third time: CDTFA-401-A prints line 11 on page 1 and computes it from Sections A and B on page 3, so the pack ordered by dependency and the form's own order was lost. Luxembourg proposed the fix, Estonia escaped it by luck, and the United States was the second pack that could not print its form in the order the administration prints it. It is what the rate box forced: a box worked out from a box has a dependency the print order knows nothing about, so the two had to stop being one field. Every box of packs/us/tax_report.json now carries a print_sequence, and sequence is left where the pack declared it.

A second confirmation was on this list until the day the pack landed and is not a gap any more. Section 6452(a) of the California code makes the return quarterly for everybody and section 6455(a) lets the Department require another period — regulation 25(1) of the British VAT Regulations word for word in another language, and the case a pack could not express while a proposal was judged by the length of the form's list of cadences. period_default moved the proposal onto the form and made it a question about the law, so packs/us/ proposes quarter on a form filed on three cadences, exactly as packs/gb/ does. Two countries, two continents, the same rule, and the fix taken before either of them needed the workaround.

A pack of a third country needs a row in framework reference data, which is not one of the two places a pack is written. docs/packs.md says adding a country touches packs/<cc>/ and packs/<cc>/golden/ and nothing else. That is true of a Member State and false of a third country: vatRegime() reads territories to decide whether the VATEX list reaches the pack, and a country the table carries no row for is held to the Union's table — so an American exempt sale with no exemption_code would be refused, and the pack would be told to write a code naming an article of a Directive its seller is not bound by. tests/territories.test.ts refuses such a pack from the other side, for the same reason. So supabase/seed/00_territories.sql gains a US row, with eu_vat_scope none and no window, in a commit of its own. It is reference data of the framework and not a country literal in the core — the table exists precisely so that no function holds a list of countries — but it is a third place, and the walkthrough should say so: a pack for a country outside the common system of VAT adds one row to territories before anything else.

And one assertion of the test suite that was an unnamed country. tests/pack_install.test.ts groups every tax posting of every pack by country and by declaration form, with order by 1 — the country, and nothing else — and compares the result to a list it sorts itself by country and then by form code. Every pack until the sixth had one form and, at most, one group of postings that name no box, and the two happened to arrive in the order the comparator wanted. The United States is the first pack whose form code sorts after the word null under the comparator and before it in the database, so the query turned out never to have been asked for a total order. Fixed in a commit of its own, by sorting both sides with the same comparator: the fix removes the assumption that the database's order is the test's, and adds no country to anything. npm run check:no-country-literals never saw it, because it does not spell a country code.

From a box worked out from another box

Found while closing the first entry of the American list — rate and rate_of on a box, and print_sequence beside sequence. None of the four blocked that work, and none is patched here.

Nothing compares the rates a form prints with the rates the taxes charge. packs/us states line 13 as 6.00 per cent of line 12, line 14 as 0.25 and line 15 as 1.00, and its statewide sale code charges 7.25 — which is the three added up, and has to be. Nothing anywhere checks that. A pack whose form says 6.00 where the tax says 6.50 compiles, passes ekwo pack check, passes every unit test, and files a return that is wrong by half a point against a ledger that is right; only a golden scenario written by somebody who noticed would catch it, and a golden is the pack's own arithmetic either way. Fix: a check that, for each tax posting to a form, the rates of the boxes a taxable base reaches add to the tax's own rate — which needs the form to say which boxes are the tax lines of which base, and that is the honest reason it is not done here. Until then: packs/us/golden/expectations.json states the three rates by hand beside the arithmetic of every other computed box, and tests/tax_report.test.ts holds the pack to them.

A box worked out from a base no longer depends on the tax being posted at all. This is the same coin. While lines 13 to 16 were summed from the ledger, a sale booked with no tax code showed up as a missing tax; now the tax lines follow line 12, which follows line 1, so a base posted with the wrong code still produces a tax the company may not owe, or hides one it does. That is not a defect of the rate — it is how the form itself works, and it is why the United States files on the form's arithmetic and Belgium on the ledger's. No fix is proposed: what would help is a reconciliation between a declaration box and the account its tax is booked on, which is a report and not a pack field.

A rate cannot change inside a version of a form. tax_report_box_templates is keyed on (country, report_code, box, kind), so a box has exactly one row however many valid_from windows one might want. A tax that changes rate is a new tax code; a box whose rate changes on the first of January is a new form code, with all thirty-nine of its boxes copied. That is defensible — a rate on a form is the form's own text — and it is expensive, and nobody has met it yet. Fix: if it becomes real, a validity in the key, which is how the taxes already do it. Until then: a new form version.

A financial statement line still says sequence means evaluation order, and it has not for four days. The comment on statement_line_templates.plus_lines reads evaluated in sequence order, so a total may only name one computed before it, which 20260912100412 made untrue in the same file that wrote it: evaluate_totals() resolves by dependency for both callers, and the statement half of ekwo pack check has always tested for a cycle and never for an order. The declaration half is what carried the real rule, and it is the half that changed. Fix: a comment on column in a later migration, and a print_sequence on a statement line the day a scheme needs one — no pack has asked, because a statement's sequence was only ever used for printing. Until then: one stale sentence in docs/schema.md, which is generated from that comment.

From making a tax follow the territory

Five things found while closing the two notes above, on 16 September 2026. None of them blocked the work and none was patched for its sake.

A tax can name a place a party is in and not a place a party is not in. Closed for the case that asked, 21 September 2026, without a negation: applies_when.supply_vs_seller is a relation, same or other, between the place of supply and the seller's own territory, and US-CA-S-SHIPPED now says other — refused on a delivery inside California. What stays open is the second half of the paragraph: the treatment has no word for a supply outside the taxing territory but inside the country, and the code still says exempt. The note as it was written follows.

A tax can name a place a party is in and not a place a party is not in. applies_when is three keys, each an equality, and that is deliberate: the moment it grows an operator it is an expression language and the format stops being reviewable. The case it cannot reach is the one packs/us/ already had — section 6396 exempts a sale the contract requires to be shipped out of California, and "out of" is a negation. A fourth key, supply_outside, is the obvious shape and was not added, because nothing in this repository would read it and because the gap it belongs to is a different one: what is missing is the word for the operation, not the condition. The territory work gives that word its anchor — a tax names the territory that levies it and a document names the territory of the supply, so a treatment of "outside the taxing territory" finally has two things to be checked against — and the value itself belongs to whoever writes the treatments. Until then: US-CA-S-SHIPPED says seller_in and nothing about where the goods went, carries exempt, and explains itself in legal_reference.

The reference table now grows for a second reason, and nobody owns the growth. territories was the territories of the common system of VAT plus the third countries a pack books in. It is now also every territory a tax names and every territory a document delivers to, because all four columns are foreign keys — which is the whole point, since a code nobody can look up is what jurisdiction has been since the day it was added. The cost is real and is not paid here: an American company that ships to forty states needs forty rows of framework reference data, and framework reference data is shipped by a release. packs/us/ adds four rows and its golden year ships to one state it levies nothing in. No fix is proposed: the question is whether a release carries every ISO 3166-2 subdivision of every country a pack exists for, or whether a destination outside the table is allowed and simply satisfies no condition. What is certain is that the answer is a decision about reference data and not a column on a document.

A destination the table does not carry is refused by a constraint and not by a name. Everywhere else in this schema a value that cannot be resolved is refused by an exception that says which value is missing — no_cash_basis_account, no_territories, unknown_chart. A supply_territory_code the table has no row for is refused by the foreign key, with the message Postgres writes for a foreign key, naming the constraint rather than the question. It is the correct refusal and it is the wrong sentence. Fix: a trigger, or a check in whatever writes a document, that says "this release carries no territory X; here is where territories come from". Until then: the constraint's own message, which a reader can at least act on.

A document does not record the territory it was judged against. Closed, 21 September 2026, by the fix proposed: seller_territory_code, buyer_territory_code and supply_territory_resolved on documents, written by post_document() on every document it posts and frozen by the guard of a posted document. Null on what was posted before. The note as it was written follows.

A document does not record the territory it was judged against. document_territory() resolves the supply to the delivery address and then to the buyer, and post_document() compares the answer with the tax's conditions — and then nothing keeps the answer. Correct a contact's territory_code two years later and the entry that was booked is unchanged, which is right, but there is no column that says what the engine believed at the time. vat_category is snapshotted onto the document for exactly this reason and this is the same kind of fact. Fix: three columns on documents, written by post_document() the way entry_lines.posting_type now is. Until then: the ledger says which tax was booked, and the tax says which territories it required, so the answer is recoverable but not recorded.

Two columns now look like they answer the same question, and one of them is still read by nothing. companies.region and contacts.region are ISO 3166-2 without the prefix, added for a Canadian pack that will want to ask "which province" as a question with a short list of answers. territory_code is a key of territories and answers "which body of tax law is this party under". They are genuinely different questions — a Québec company is in CA-QC for the tax and picks QC from a list of thirteen — but a reader meeting both for the first time will not see that, and region has had no reader for four days longer than it should have. Fix: the Canadian pack decides, when it arrives, whether the province is derivable from the territory and region goes, or whether the two stay and the comments do the work. Until then: both exist, only one is read, and this paragraph is why.

From the EN 16931 code lists

Found while teaching ekwo pack check to compare a tax's treatment with its category and its exemption reason. Neither blocked that work; both are about the same two columns, and both are a change to the core rather than to a pack.

A VAT category comes back padded with a space. Closed, 15 September 2026. taxes.vat_category, tax_templates.vat_category and document_lines.vat_category were char(2), and every category of EN 16931 but AE is one character — so the database answered S , K , E , G , Z , O , and had done since the column was created. document_line_items and document_tax_summary published that as BT-151, which is not a code of UNCL5305: a renderer writing it straight into an invoice emits one that fails validation, a reader comparing it to 'S' finds nothing, and since shared_document() reads both views it reached whoever held the link to an invoice. Nothing here noticed, because the only test that compared the column compared two databases that pad identically and trimmed before it looked. The three columns are text, under a check constraint that accepts one or two capitals and nothing else, so the column now refuses what it used to manufacture; the two views were dropped and recreated unchanged but for existing, with their comments and their grants. No pack moved: a pack never wrote the space, the column added it — the golden files are identical and the seeds are untouched. tests/vat_category.test.ts follows one pack's category from the manifest to the payload an anonymous reader receives, and tests/packs.test.ts has dropped the expectation it used to pad on purpose.

A legal mention cannot tell a domestic reverse charge from a foreign one. applies_when is a closed vocabulary of nine conditions, and foreign_services_received had to join reverse_charge because that is the sentence all four packs print for it — the mechanism is the same and the wording is the same. A country whose law prescribes a different sentence for a service bought from a supplier established elsewhere cannot say so: it would have to choose between the two sentences for both cases. Fix: a tenth condition, foreign_reverse_charge, beside the one that exists. Until then: no pack here needs the distinction, and a pack that does will be the argument for adding it.

And it cannot name the simplification a triangular supply is relieved under. intracom_triangular joined the same condition on 15 September 2026, for the same reason — the mechanism is the customer owing the tax and the sentence is Reverse charge, which is what article 226(11a) requires. But article 226(11) also wants a reference to the provision that relieves the supply, and several Member States ask a triangular invoice to name article 141 itself. A pack that wants that sentence has today to fold it into its reverse-charge wording, where it would also print on a domestic reverse charge that has nothing to do with article 141. Fix: the same tenth condition generalised, or an eleventh — three articles behind one sentence is one too many. Until then: three treatments share reverse_charge, and the value of the treatment on the line is the only place the difference is recorded.

From a document read by its recipient

Publishing an invoice behind a link (20260915153000) put a reader in front of it who has no session, no preferences and no membership, and that reader found two things the core could not say. Both are closed by 20260915191200, and they turned out to be one thing: a chain that was re-walked on every read, from a starting point only a session could supply.

A document does not record the language it was written in. Closed, 15 September 2026. documents had no language column: the language was re-derived, every time, from the contact's, then the company's, then the one the country pack declares. So an invoice reprinted after the customer switched to another language came out in a language it was never sent in — wrong on a document whose legal mentions are part of what the law requires. documents.language is now a column of the table, filled from that same chain when the document is created and frozen the moment it is posted, which is what document_lines.vat_category and vat_rate already are and for the same reason. A draft keeps following the chain — it carries no number, no entry, and the customer on it may still change — and a posted document refuses the move by name, document_language_frozen. The documents that were already here were filled from the chain once, in the migration, which is the best that can be said about a document somebody has already sent.

preferred_languages() cannot serve a reader who is not signed in. Closed, 15 September 2026. It started at user_preferences for auth.uid(), which is null for anon, so the one published way of choosing a language was unavailable to the one reader who is outside the installation, and shared_document() resolved the chain itself — a second place a language was chosen. The chain was never about a user, only about where it starts: preferred_languages(language, company) takes the starting point explicitly, and preferred_languages(company) is now one line on top of it, supplying the signed-in reader's preference. document_legal_mentions walks the chain from documents.language, shared_document() reads the column and the view, and neither writes a chain of its own. anon gains nothing: the overload is granted to authenticated and service_role, and the public door is still one security definer function.

From the recapitulative statement

The statement of intra-Community supplies — ec_sales_list() and the four format bricks beside it — was the first thing written that is European rather than national: one engine, four files, no packs/eu/. Five things it could not say precisely, each a change to the core rather than to a pack.

A company records how often it files its return, and that is not how often it files anything else. Closed, 16 September 2026, and by the fix this note proposed: company_filing_periods, one row per declaration a company is subject to, keyed on the company and the code of the form. vat_return() reads the cadence of the form it was asked for rather than the one on the company, and ec_sales_list() gained the same guard with one condition more — the caller names the statement it is preparing, and a caller that names none is asking for figures and is refused nothing. Two decisions are worth knowing. companies.vat_period was kept, because it has been published since v0.3.0 and a reader outside this repository still reads it; it is now derived, a mirror of the row for the country's periodic return, held in step by two triggers that each write only when the other is out of date — the shape declaration_box and declaration_boxes already use. And the proposal moved with the fact: a pack proposes a cadence on the form now, in tax_report.json's period_default, because a country-level proposal can only ever be about one of the several declarations a company files. country_defaults.vat_period_default is deprecated and carries a copy written from the form.

A pack declares one declaration form, and a country files several. packs/<cc>/tax_report.json is a single object and pack.report a single form: the periodic return. Every other declaration a country files — the recapitulative statement, an annual return beside the periodic one, a form a second registration brings — has no report_code anywhere, which is why ec_sales_list() has to be told which statement it is preparing and why no pack of this repository can answer. The consequence is that the guard the fix above gave it is, today, a guard nobody in this repository can arm: a company can record a cadence only for a form the installation carries, and the only form it carries is the return. Fix: a list of forms per pack rather than one — tax_report.json becomes an array, or a tax_reports.json sits beside it — with a kind on each so the core can tell a periodic return from a recapitulative statement without naming a country, the way is_periodic_return already does for one of the two. It is a change to every pack, to ekwo pack check and to the compiler, which is why it is written here and not taken. Until then: ec_sales_list() accepts any report_code the installation carries and checks the period against it, ekwo init asks once per form a pack declares, and both are ready for the day a pack declares two.

One cadence per form is one too few where a country splits a statement by what is supplied. company_filing_periods is keyed on the company and the form, which is the shape of the fact in three of the four countries read while the statement was written. Luxembourg is the fourth: it lets a taxable person choose the cadence of the recapitulative statement separately for goods and for services, so one company files one form on two cadences at once. Nothing in the key can say that. Fix: the nature of the supply is already a column of what ec_sales_list() returns, so the cadence could be keyed on it too — a nullable third key column meaning whatever this form covers that no other row names. It is not taken here because no pack declares the statement as a form yet, so the row it would refine does not exist. Until then: such a company records the cadence of the statement it files more often, which is the safe half of the answer — a guard that refuses only what is certain will then refuse nothing on the other.

The core cannot say whether a country is a Member State. Closed, 15 September 2026, and by the fix this note proposed: territories, a reference table of the framework beside currencies, seeded by supabase/seed/00_territories.sql and read by is_eu_member(code, on), eu_vat_scope_of(code, on) and territory_of(code). It carries 49 rows — the 27 Member States with the day each became bound, the United Kingdom with the day it stopped being, Northern Ireland, and the territories articles 6 and 7 of Directive 2006/112/EC take out of the common system or put into it — each with the text it comes from. ec_sales_list() asks it as at the entry date of the line, so a statement for a period in 2020 still reports supplies to the United Kingdom and one for 2021 does not, and a supply to a territory the system did not reach comes back as vat_country_outside_the_union instead of being listed. Two decisions are worth knowing: the columns are named for VAT and not for membership — the United Kingdom left the Union on 31 January 2020 and the common system on 31 December 2020, and the reader of this table is VAT code — and Northern Ireland is a row of its own with a parent rather than a flag on the United Kingdom, with an eu_vat_scope of goods, because it is inside the system for supplies of goods and outside it for supplies of services. A statement of services to an XI customer therefore comes back as vat_country_outside_the_union_for_this_supply, which nothing could have said before.

A VAT identification prefix is not always the ISO country code. Closed, 15 September 2026, by the same table: vat_prefix_of(code) answers the two letters a territory's numbers carry, and territories.vat_prefix holds that answer only where it differs from the code — EL for Greece, FR for Monaco, GB for the Isle of Man — so the column is a difference and never a copy. Both paths into ec_sales_list() go through it, the prefix read off the number and the contact's ISO country alike, which means a number typed GR… is corrected as readily as one typed with none. It also fixed a defect nobody in Belgium or France could have seen: the company's own country was compared raw, so vat_country_is_the_company_country would never have fired for a Greek filer and a domestic supply would have been listed as an intra-Community one. A territory the table does not carry keeps its own two letters, so a third country is still readable on the statement that then refuses it.

A ledger line does not say which posting wrote it. Closed, 16 September 2026, and by the fix this note proposed: entry_lines.posting_type, the vocabulary of tax_postingsbase, tax, tax_on_base — written by post_document() from the posting it is already reading, and by settle_cash_basis_tax() on both legs of the transfer that makes a cash-basis tax due. A line no tax posting wrote is null, because null is what a column says about a question that does not apply; a none inside the vocabulary of what a posting is would then have to be refused on tax_postings itself. Null therefore carries two readings and tax_id separates them — with no tax it is not a posting, with one it is written before the column existed and not identified. The backfill wrote only what was certain: a line whose tax and declaration box name one posting type and no other. ec_sales_list() now says what it means, excluding a line known to be a tax_on_base one and leaving a null read exactly as it read before.

There is no treatment for a triangular operation. Closed, 15 September 2026, and it cost exactly what this note predicted: the value intracom_triangular, and nothing else. ec_sales_list() returns the nature triangular without a line of it changing, and the four bricks — published before the value existed — write T on the Belgian listing, the TVA_LICT form of the Luxembourg envelope and the kolmnurktehing column of the Estonian form VD, while the French DES says by name that a supply of goods belongs on another file. It is the middle supply of the arrangement — B's sale to C, relieved by article 141 of Directive 2006/112/EC and reverse-charged to C by article 197 — and not A's, which is an ordinary intra-Community supply. On the invoice it resolves to the reverse-charge mention and not to the intra-Community one, which is the sentence article 226(11a) requires: the supply is not exempt under article 138, it takes place where the goods arrive and the customer owes the tax. No pack of this repository declares a triangular tax, and a test insists on that — the path is proved on a fixture, a company's own tax with its treatment changed, because writing an invented tax into packs/<cc>/ is writing a rule nobody can review.

The same rule, next: the One-Stop Shop

Nothing is coded for it here, and this paragraph exists so that the next person does not rediscover the shape.

A recapitulative statement asks: who, in another Member State, did I supply, and how much. The One-Stop Shop asks: in which Member State did I have to charge the tax, at what rate, and how much. Both are answered from the same two facts — the treatment of the tax on the line, and the country of the customer — and from nothing else. The statement reads a treatment that says the supply is exempt in the seller's country and taxable in the buyer's, and groups by the buyer's VAT number; the One-Stop Shop reads a treatment that says the supply is taxable in the buyer's country, and groups by that country and by the rate applied. ec_sales_list() is the first of the two, written as a function of the core with a flat row shape and a brick per file, and the second is the same three pieces: a treatment the pack declares, an aggregation the core computes, a format package per administration.

Two things the second one will need that the first did not. One of them now exists. It needs to know which country a customer is in and whether that country is in the Union, because the scheme applies to consumers and not to identified businesses, so the VAT number is not the key — and that is territories, asked as at a date, which is what ec_sales_list() already does line by line. A consumer in a territory the system does not reach is not a One-Stop Shop supply at all, and the table says so for the Canary Islands and for Northern Ireland's services as readily as for Switzerland.

What it does not give is a rate per Member State of consumption, which is the genuinely new question and is unchanged by any of this. A French company selling into Germany charges German rates, and today the only place a German rate lives is the German pack that company does not hold. Adding rates to territories would be the wrong answer twice over: a rate has a validity and a category and an exemption reason, which is a tax and not a territory, and a tax is what a pack carries. So the choice stays the one this paragraph always named — either a company holds several packs, or the rates of the scheme are framework data of their own — and the table below it settles only where the system applies, not what it charges.

From the register of sources

Found while turning certification.sources into a register a reviewer can open — a key, a title, the publisher, an absolute link — and pointing every legal_reference at one of its keys. Neither blocked that work. Both were places where the format let a pack claim something and gave it nowhere to say where the claim came from, and both were closed the day after.

What a country puts on an invoice cites nothing. Closed, 15 September 2026. documents.references carries a legal_reference and a source per rule — numbering, payment_terms, tax_point — and not one for the section, because they are two or three different texts in every country the packs cover: Belgium numbers an invoice under a royal decree of 1992 and counts a payment term under a law of 2002, France numbers under an annex to the tax code and counts under the commercial code. A single citation would have had to name them all in one string, and then no rule would have had one. Six columns of country_defaults hold the three pairs, beside the rule each belongs to, and cgi-annexe-2 is now named by the rule it was read for. ekwo pack check refuses a declared rule that cites no article on a reviewed pack, warns about one on any other, and refuses a key the register does not carry.

One thing the reading turned up and this change did not act on. The Luxembourg pack declares tax_point: invoice_date, and the principle of the VAT law is art. 21. Closed, 16 September 2026, and it was true of three packs rather than one.

The vocabulary gained the two words it was missing — invoice_if_issued and earliest_of_delivery_or_payment — and the column gained a reader, because a vocabulary nothing reads is a comment. Each text was opened before the word was chosen, and one of the assumptions the work started from was wrong.

  • Luxembourg, art. 21: "Le fait générateur de la taxe intervient et la taxe devient exigible au moment où la livraison de biens ou la prestation de services est effectuée." Art. 24, par. 1er derogates "lorsqu'il y a obligation d'émettre une facture", to the issue of the invoice within the delay of art. 63, par. 5, or to the day that delay expires — and not for a supply with no invoicing obligation, nor for a service the customer is liable for under art. 61, par. 5. → invoice_if_issued.
  • Belgium was the same shape and nobody had looked. Art. 16, § 1er and art. 22, § 1er are the principle; art. 17, § 1er and art. 22bis, § 1er derogate to the invoice "peu importe que l'émission de cette facture ait lieu avant ou après le moment où la livraison est effectuée", with the fifteenth day of the following month where none was issued. → invoice_if_issued.
  • The United Kingdom likewise: VATA 1994 s. 6(2) and 6(3) give the basic tax point, s. 6(4) and 6(5) displace it. Its own citation already said the declared value was "a derogation the Act grants and not the principle". → invoice_if_issued.
  • Estonia was the assumption that turned out wrong. The work began from "KMS § 11 lg 1 keeps the first of three dates: supply, invoice, payment". The official English translation of lg 1 lists two acts for an ordinary supply — "1) the goods are dispatched or made available to the purchaser, or the services are provided; 2) full or partial payment is received" — and the invoice is not one of them: it belongs to lg 2, the separate rule for intra-Community supply. → earliest_of_delivery_or_payment, two branches and not three.
  • France was read and left alone. CGI art. 269, 1, a and 2, a put goods on the supply, art. 269, 2, c puts services on collection unless the taxpayer opts for the débits, and that half is taxes.cash_basis by a decision this repository already wrote down. delivery_date is France's principle and says so.

What the engine does with the word. tax_point_of() is its only reader; documents.tax_point_date (BT-7, which the table had been missing) and entry_lines.tax_point_date are where the answer is written; post_document() and settle_cash_basis_tax() write them and vat_return() reads them, so a figure lands in the period its tax fell due in rather than the period its entry was booked in. Null keeps the entry's date, which is what every reader did before, so no existing ledger and no golden moves.

Three things this left open, and none of them is a one-line follow-up:

  • A payment cannot pull a tax point forward unless the tax says cash_basis. The payment branch of earliest_of_delivery_or_payment — and of Belgian art. 17, § 1er, al. 3, and of VATA s. 6(4) — is a prepayment rule, and Ekwo has no prepayment document: matching is recorded against the entry post_document() is writing, so at posting time no payment of the document exists. post_document() passes null and the branch degenerates to the supply. Fix: a prepayment document of its own, or a tax point recomputed when a payment is matched, which is a design discussion and not a column.
  • The recapitulative statement still reads the entry date. ec_sales_list() was deliberately not moved onto the tax point, because the moment an intra-Community supply arises is a different article in both countries read here — KMS § 11 lg 2 in Estonia, art. 17, § 2 in Belgium, both of them the fifteenth of the following month or the invoice if earlier — and none of that is the rule tax_point_rule carries. Computing the statement from the general rule would make it wrong in a new way. Fix: a second rule on the country model for the intra-Community tax point, once a pack needs it.
  • BT-7 does not cross to a renderer. documents.tax_point_date is the field EN 16931 calls the value added tax point date, and document_header — the view an invoice is printed and an e-invoice emitted from — does not select it, nor documents.delivery_date beside it. The column is reachable by anything that reads documents and not by anything that reads the header, so a renderer would have to make a second query for a field that belongs on the letterhead. Fix: both columns in document_header and in the MCP's DOCUMENT_HEADER, when the view is next replaced — it is four hundred lines and replacing it for two columns alone was not worth the churn on the day this was found.
  • A purchase is dated by the buyer's country rule, which is the supplier's question. The tax point of a purchase invoice is fixed by the law the supplier is under, and tax_point_of() reads the company's own fiscal_country. It is exact for a domestic purchase and for the reverse charge, where the buyer is the person liable; it is an approximation for anything else. Fix: none proposed — the alternative is a country model per contact, and no pack has asked.

A legal reference the schema accepts and the compiler drops. Closed, 15 September 2026, for the half of it that was about e-invoicing. einvoicing.legal_reference had been read by pack.1.json and written by all four packs since the section existed — it is where the day an obligation starts is justified, and where France writes out an emission calendar that depends on the size of a company the core cannot yet hold — and the compiler dropped it on the floor. country_defaults.einvoice_legal_reference and einvoice_source_key now sit beside the profile and the date, written by the same update so that a rule and the text imposing it cannot reach the database by two routes and have one of them left behind.

What is left of the note is the other two: charts[].legal_reference compiles and the source beside it does not, and a statement line carries both in the pack and neither in the database. Closed, 16 September 2026. source_key on chart_templates and statement_line_templates, the way the register added it to tax_templates and tax_report_box_templates — and on statement_templates as well, which the note had not counted and which drops its source for exactly the same reason. Reading the compiler to write it corrected half the note: statement_line_templates.legal_reference was compiled all along, so a statement line carried the article and not the text it is in. Three nullable columns, filled by the compiled seed like the two before them, no backfill and no foreign key.

From recognising who paid

Found on 17 September 2026 while writing the motifs a counterparty is recognised by, and not blocking that work: the new table says counterparty_account where the rest of the schema says iban.

An account is named as if every country had an IBAN. bank_accounts.iban and .bic, contacts.iban and .bic, bank_transactions.counterpart_iban and documents.payee_iban are six columns that spell out ISO 13616 and leave no room for anything else. About half the world uses something else: the United States identifies an account by an ABA routing number and an account number, Canada by a transit and an institution number, Australia by a BSB, India by an IFSC; the United Kingdom has IBANs but pays domestically on a sort code and an account number. The sixth column is the one that gives the game away — documents.payee_iban carries BT-84 of EN 16931, which the standard calls Payment account identifier and which is not an IBAN: the schema narrowed the term it was citing.

The sixth pack did not catch it, and that is worth saying: packs/us/ models a tax and never records a payment, so nothing in it ever had to name an American bank account. The first American installation finds this on its first statement, which is the wrong moment.

Fix: the shape the same table already uses for another identifier — contacts.peppol_scheme beside contacts.peppol_identifier. An account identifier is a scheme and a value, the scheme from a closed list the database carries, iban being one entry of it, and the existing columns deprecated and mirrored the way companies.vat_period was when the cadence became a table: one source, two names, a trigger, and a release to remove the old one. The check digits follow the scheme — ISO 7064 mod-97-10 for an IBAN, the weighted sum of an ABA routing number — so a validator that assumed one becomes a validator that reads the scheme first.

Until then: contact_patterns stores what the statement wrote, under a kind that names neither a country nor a scheme, so the day the columns above learn to say their scheme, nothing in the matching has to be renamed.

From settling a statement line

Found on 17 September 2026 while matching a statement line against what it pays. Neither blocked the work; both are places where the core does something correct and cannot do the country-specific thing that would make it strong.

A structured communication is checkable arithmetically, and nothing checks it. bank_transactions.structured_reference is compared to a document's reference as a string, which is right and weak. A Belgian structured communication is twelve digits whose last two are the remainder of the first ten modulo 97 — with zero written as 97 — and an ISO 11649 creditor reference carries its own two check digits under mod-97-10, the same algorithm an IBAN uses. A communication whose key is wrong is not a communication, and it should never start a settlement: it is a typo, a truncation or somebody else's reference. Fix: the scheme belongs to the pack — bank.structured_reference, a closed vocabulary, one entry per scheme a country's banks actually use — and the check itself is arithmetic the core can do once the scheme is named. It also wants a second look at Estonia, whose reference numbers use the 7-3-1 weighting rather than mod 97, which would be the first non-97 scheme and the proof the field is worth having.

A payment in one currency cannot settle a document in another. suggest_matches() returns nothing when the statement line's currency is not the company's, deliberately rather than by omission: the ledger amount and the statement amount are then two numbers in two currencies, and the rate that reconciles them is what reconcile() decides at the moment of matching — it books the realised exchange difference — not something a search may assume beforehand. Fix: match on entry_lines.amount_currency where the currency agrees with the statement, and leave the rate to the matching, which already knows how to write the difference. Until then a company with a foreign-currency bank account matches those lines by hand.

From importing a statement

Found on 18 September 2026 while writing import_bank_statement(). None blocked the work; each is a place where the import refuses, by name, what a country could legitimately send.

A statement line holds two decimals. bank_transactions.amount and the balances of bank_statements are numeric(16, 2), from the first bank migration, while round_amount() has read the currency's decimals since the rounding did. A dinar has three. The reader returns them and the import refuses the line (unreadable_statement_line, "more than two decimals") rather than round a bank's figure. Fix: widen the columns to what currencies.decimal_places can ask for — the same change documents and entry_lines would need, and better made once.

An entry in another currency than its account is refused, not converted. The roadmap card asked for a conversion through currency_rates, traced — rate, date, source. It is not written: in a camt.053 the entry is in the account's currency and the foreign amount sits beside it (instructedAmount, kept in raw), so the case has not been met yet, and a conversion nobody has seen a file for would be a guess with an audit trail. Fix: when a format or a bank that books foreign entries is met, convert at import through currency_rates and keep the three facts on the line.

The account is looked up in a column called iban. The reader says whether the identifier is an IBAN or not; the core has one column for it and compares whatever the statement wrote. That works — the end-to-end test pays an invoice, in every pack, on an account identified by Othr/Id — and it is the wrong name, which is the gap written under From recognising who paid and fixed by the same change. A bank account that the file identifies one way and the company another can be named explicitly to the import.

Two readers of two formats do not recognise each other's lines — measured, now that there are three. The key of a line with a bank reference is that reference. tests/coda_cfonb120.test.ts imports the same invented day from a CODA and then from a camt.053, and asserts what happens as it is: when the bank writes the same reference in both, the lines are known and nothing is imported twice (the statement still is — the two formats name it differently); when it writes another, or none, the day is imported twice and nothing says so. Febelfin's standard calls the CODA reference "purely informative" and lets the bank change it without notice, so the first case is a courtesy and not a guarantee. A CFONB 120 has no bank reference at all: its month and the camt.053 of the same month are always imported twice. Fix: not a cleverer key — a fingerprint across formats compares a name cut at thirty-five characters with one that is not. What is comparable across formats is the statement: the same account, the same closing date, the same two balances, another source_format. An import that finds one should say so by name (statement_already_covered) and let the caller decide; that is a migration of its own, with the question of what a fortnight and its month look like to it.

From asking when a declaration is due

Written on 17 September 2026, with the deadline rule. Two of the six packs declared no date at all, and that was the finding rather than the omission. Both are answered since 21 September 2026 — see the next paragraph and "What the packs do not say yet".

A deadline can depend on the taxpayer rather than on the period, and the format cannot say that. packs/fr/ declares none: the French periodic return is due on a day the administration assigns from the taxpayer's identification number and legal form, staggered across the second half of the month, and no rule of the shape day N of the month that follows is true for more than a slice of filers. packs/lu/ declared none either, for a different reason — the date was not read, and a pack does not guess. It has been read since: art. 64, par. 6 of the VAT law, and the pack declares day 14. France now says what it could not compute: the rule depends_on_taxpayer carries the text that assigns the day — the CIBS, art. A. 161-28 and A. 161-29 since 2025 — and no day, so the country page prints an answer and not a gap. Fix for the date itself, when somebody writes it: a rule whose day comes from a value on the company, which is a fourth shape and a bigger change than it looks, because the values (place of filing, legal form, the first digits of the SIREN) are country data on a table the core owns. Until then: filing_deadline() answers null and upcoming_filings() lists the period with no date, which is honest and visibly incomplete.

An extension can depend on the scheme a company is in. The United Kingdom adds seven days to a return filed online and paid electronically, and takes them back from a business on annual accounting or on payments on account. The pack states the seven days and says so in the reference, because the usual case is the extension; what it cannot express is the exception, since nothing records which scheme a company is in. That is the same shape as the gap the cadence had before company_filing_periods, one field over.

And a date that falls on a day nobody works is still that date here. Several administrations move a deadline to the next working day, which needs a calendar of public holidays — national, sometimes regional, and revised by law every few years. No pack carries one and none should invent one, so a deadline this function produces may land on a Sunday. Saying so is better than a rule that is right in one country and wrong in the next.

Decisions taken with the plan

  • US sales tax is not in the core. Tens of thousands of jurisdictions and their updates; the core models the shape, a provider supplies the rates, in the commercial layer, as is usual for American sales tax. packs/us/ holds to it: one statewide rate per state and one worked district combination, documented as a worked example, and no rate that depends on a delivery address.
  • Order: Europe, then the UK and Ireland, then Canada and Québec, then the Commonwealth, then the United States. It is the order of proximity to the accounting model this core was built on, and the reverse of market size.
  • The pack comes before any new country. Adding the UK on the core as it stood would have added a third place where a country lives. Phase 0 was first a taxonomy decision, and a taxonomy is the one thing that does not get redone.

Deliberately out of scope

Inventory, payroll, advanced fixed-asset regimes (MACRS), point of sale and its certifications. A products table exists so that inventory can come later as its own schema, as decision 0044 describes.

What the packs do not say yet

Building a page per country made every silence in a pack visible at once, which is the one thing a folder of JSON does not do on its own. The list below is the one the pages printed on 19 September 2026, and what became of each item on 21 September. The rule for every line was the same: fill it from an official text in the pack's register, or make the pack say that the silence is the answer — and where neither was possible, leave it and say so here.

Pack What the page printed Now On what
ee tax balance: no account, either side filled2370 payable, 1211 receivable KMS § 27 (1), § 29 (1), § 34 (1); the Ministry of Finance's commentary of January 2026
gb tax balance: no receivable account filled1145, beside 2210 VATA 1994 s. 25(2)–(3)
us tax balance: no account, either side filled2208 payable, 1185 receivable, for the sales and use tax a return carries California R&TC § 6452(a) and § 6901
fr deadline: not declared stateddepends_on_taxpayer: the fifteenth to the twenty-fourth, by place of filing, legal form and SIREN CIBS, art. A. 161-28 and A. 161-29, to which CGI ann. IV art. 39 refers since 2025
lu deadline: not declared filled — day 14 of the month after the period VAT law art. 64, par. 6 ("avant le quinzième jour"), in the AED's coordinated text; the AED portal for the quarterly return
gb e-invoicing: no date of obligation statedobligation: none the consultation response of 26 November 2025: mandatory e-invoicing is announced for 2029 and not legislated
us e-invoicing: no profile declared statedobligation: none, with no profile to sit beside the pack's own legal reference: no statute, federal or of any state, obliges anybody; the network it names is voluntary
ee e-invoicing: no date of obligation statedobligation: on_request, not none RPS § 7¹ (7): since 1 July 2025 a buyer registered as an e-invoice recipient may require one; the Ministry of Finance confirms no general B2B obligation and no date
ee, gb, lu, us bank statements: bai2, camt.052, csv, mt940, ofx read by nothing remains — no reader was written ekwo pack check now warns once per format; the ledger of the debt is tests/bank_statement_formats.test.ts
every pack certification: no named reviewer remains — a person's act, and nothing here can stand in for it

Three findings came out of it, and each is worth more than the line it closed.

  • The premise was wrong once, and the vocabulary grew a word for it. The Estonian line was expected to close as "no e-invoicing obligation between companies". There is no general one — but since 1 July 2025 a seller must issue one when a registered buyer asks, which is an obligation and not a preference. Writing none would have been false; on_request is the word.
  • "Avant le quinzième jour" is the fourteenth. Luxembourg's law writes "au plus tard le quinzième jour" where it includes the day (art. 63, par. 5) and "avant" where it does not, and most secondary sources round it to the fifteenth. The pack follows the text, which is also the reading under which a return is never late; the pack's README asks a reviewer to confirm it.
  • A role has no citation of its own. defaults.roles names an account and cannot say which article makes a credit a claim on the administration, so the justification of every settlement account is in the pack's README and the texts are in its register. A reviewer reads both.

One silence is still the packs' and not the format's:

  • No pack is reviewed. Four are community and two are maintained, which means the maintainers keep them current and no named professional has read them against the law. Every country page says so in those words.

And one claim about coverage is still about the engine: the currency seed carries eleven currencies, "the handful a European ledger meets", at zero and two decimals. The engine reads a currency's decimals rather than assuming cents, so a third is a row and not a change — but until somebody adds the rows, the claim is about the engine and not about coverage.

Ireland

The seventh pack, packs/ie/, community, seed 16. It carries an original chart of 209 accounts mapped onto Schedule 3A of the Companies Act 2014, 39 taxes with the rate history since the Value-Added Tax Consolidation Act 2010 came into force on 1 November 2010 — the temporary 21 % of 2020–2021 and the three 9 % reliefs among them — the nine boxes of the VAT3 (T1 to T4, E1, E2, ES1, ES2, PA1), the balance sheet and the profit and loss account of Schedule 3A Format 1, and a register of thirty-six texts, all opened on 21 September 2026. The pack's own README says where each rule comes from and ends on what a reviewer should read first.

From Ireland

Five things the format could not say, none of them patched: each is contoured inside the pack and written down here.

A two-month taxable period. Section 2 of the 2010 Act defines the taxable period as two months beginning on 1 January, 1 March, 1 May, 1 July, 1 September or 1 November, and that is the cadence of every Irish VAT3 unless Revenue authorises another. tax_report.period knows month, quarter and year, so the pack declares the monthly and annual returns Revenue allows and proposes no period_default rather than a wrong one; the four-monthly and six-monthly periods Revenue authorises for small liabilities are not expressible either. vat_return() still computes a two-month period — the golden files on them — because a period that is not a whole cadence is never refused. Fix: a bimonth cadence (and four_month, half_year) in the schema, in company_filing_periods and in the guard of vat_return().

A second declaration with a period of its own. The Return of Trading Details is annual, due with the last VAT3 of the company's accounting year, and breaks sales and purchases down by rate; it is where the values of domestic supplies, exports and reverse-charge construction services are reported, since the VAT3 has no box for them. A pack carries one tax_report.json, so the RTD is absent and the domestic base postings name no box. Fix: several forms per pack, which report_code on a posting already anticipates.

A deadline that depends on how the company files, and an extension on a day of the month. The return is due on the 19th (s. 76(1)) and on the 23rd for a return filed and paid through ROS. The pack declares day: 19 and no plus_days: tests/filing_calendar.test.ts checks an extension against the last day of the following month, which is the British rule and not this one, so the four days would fail it. The deadline is therefore four days early for a ROS filer and right for a filer exempted from electronic filing. Fix: the test reading the rule it checks, and a condition on the filer for the extension, which is the British gap again.

A financial year the law leaves to the company. Companies Act 2014, s. 288 lets the directors fix the year end. The pack still declares fiscal_year_default: calendar, because bootstrap refuses a pack with none unless the first day is named, and the bootstrap test installs the first pack whose form proposes no cadence — alphabetically, Ireland — without naming one. It is a proposal an operator overrides, not a reading of the Act.

One sentence for every reverse charge a seller issues. The subcontractor's invoice under the construction reverse charge carries "VAT on this supply to be accounted for by the principal contractor", and the other domestic reverse charges of s. 16 — emission allowances, scrap metal, construction work between connected persons, gas and electricity to a dealer — would name the recipient instead. applies_when: reverse_charge is one condition, so the pack carries only the construction case on the sale side. Fix: a mention that can be tied to a tax rather than to a treatment.

The moneys received basis is a regime of the company. Section 80 authorises a company, not a tax, to account on receipts, and only for its sales. The pack carries it as two sale codes with cash_basis and conditions: ["seller_threshold"], as France carries services on collection; nothing stops an authorised company from picking the invoice-basis code, or an unauthorised one from picking this. The core records no such regime, which is the small_business gap in another form.

One practical note beside those: cro.ie answers every request without a browser with a Cloudflare challenge, so the Companies Registration Office is not in the register even though it is where Irish financial statements are filed; the Companies Act is cited instead. And camt.053 is the only statement format declared, because it is the only one Ekwo reads that an Irish bank sends; no payment format is declared because Ekwo writes none.

From Spain

Written on 21 September 2026 with packs/es/, the first pack of southern Europe. Status community; the pack's own README says what it carries and ends on the points a reviewer should read first. It carries a selection of 220 accounts of the Plan General de Contabilidad with the official codes, 26 taxes with the rate history back to 2010 and the temporary food rates of 2023–2024, the 63 boxes of form 303 that a company in the general regime fills, and the abridged balance sheet and profit and loss account transcribed from the account column the PGC itself prints. Seven things the format could not say, none of which was patched in the core:

  • A surcharge on the same line as the tax. The recargo de equivalencia (Ley 37/1992, arts. 154 to 163) is charged by a supplier to a retailer in the scheme on top of the VAT of the same line. That is the stacked tax group is reserved for, and the core refuses it. The boxes of the surcharge are declared and empty so that box 27 keeps the form's formula. Fix: the same one the Canadian GST and QST need.
  • A country minus one of its territories. Closed, 21 September 2026, by territories.outside_parent_tax: ES-CN, ES-CE and ES-ML carry it with art. 3 in their reference, the Spanish rates say supply_in: ES from version 0.2.0 of the pack, and post_document() refuses them on a supply that lands in any of the three — no negation written anywhere. See From measuring a supply against its seller at the end of this file. The note as it was written follows. A country minus one of its territories. Spanish VAT applies in Spain except in the Canary Islands, Ceuta and Melilla (art. 3). The territories exist, the export to ES-CN is in the golden, but applies_when has no negation, so the Spanish rates cannot refuse a supply that lands in ES-CN. The United States gap under What it cannot say is the same one.
  • A deadline with an exception for one period and another for one kind of filer. Form 303 is due on the 20th, except the last period of the year (30 January) and returns of SII filers (thirty days, end of February for January), all in Reglamento del IVA, art. 71.4. The pack declares the rule and writes the exceptions in its reference.
  • Invoice reporting that is not an invoice format. SII (records sent within four days, Reglamento del IVA, arts. 62.6 and 69 bis) and VERI*FACTU (Real Decreto 1007/2023) are obligations about transmitting invoice records, with their own dates and populations. einvoicing has room for a profile and a date only, so both are written in the README and in the e-invoicing reference.
  • An e-invoicing date that depends on a text not yet published. Real Decreto 238/2026 counts twelve or twenty-four months from a ministerial order that was not found in the BOE on the day of writing. mandatory_from is null, and the profile too: no brick writes a Spanish invoice, and declaring UBL or Facturae would make describe_pack say one does.
  • Norma 43. Spanish banks deliver statements in the AEB/CSB Norma 43 format, which is not in the list of statement formats; the pack names camt.053 and mt940.
  • A receivable that is not reconcilable passes ekwo pack check and fails the seed. The first build of this pack had three facturas pendientes accounts (4009, 4109, 4309) typed payable or receivable and not reconcilable. The check accepted them; the seed then failed on the check constraint account_templates_third_party_reconcilable. The check should refuse what the table refuses.

And one thing about the register rather than the format: the BOE answers 200 for a page that does not exist and says so only in its title. pack check --links reads status codes, so it will call a mistyped ELI of the BOE good. Every link of this register was checked by its title instead.

From the OHADA packs

Senegal, Côte d'Ivoire and Burkina Faso are the first three of the seventeen States that keep their books on the SYSCOHADA révisé. The chart, the journals, the roles and the two statements are the same for all of them and live once, in packs/ohada/; scripts/ohada-packs.mjs copies them into each member and the CI's hygiene job refuses a copy that has drifted. Every pack stays autonomous and nothing in the schema moved. What the three packs could not say, each a change to the core rather than to a pack:

A statement code is unique across every country. statement_templates is keyed on code alone, where an account, a tax and a journal are keyed on their country too, so seventeen packs carrying SYSCOHADA-BS would be one row, the last seed applied winning. The copy puts the country in front (SN-SYSCOHADA-BS); the same scheme is then seventeen rows. Fix: key a statement on (country, code) like the rest.

A frozen box holds two decimals. tax_filing_boxes.amount is numeric(16, 2): a return in XOF, which has none, freezes 1000000.00 where vat_return() answers 1000000, and a return in a currency with three would be cut. tests/filing_golden.test.ts now compares the two as values (trim_scale), which is what "figure for figure" meant. Fix: the column at the scale of the currency, like every other amount.

A pack carries one form, and a Senegalese company files three. The monthly return, the declaration of précompte (art. 372-2 b) and, by the DGID's calendar, the TVA pour compte; in Côte d'Ivoire the TVA pour compte de tiers of art. 442 is a declaration of its own on e-impots. The withheld tax waits on 4478 with no box; the TVA pour compte of Senegal has a box on the return, said in its reference to be a transcription. This is the gap the Canadian note above already names.

The seller's side of a withholding by the buyer. Under the Senegalese précompte the buyer pays the price before tax and the tax to the State, and the supplier's tax falls due when it is paid (art. 362-5 b) — by somebody else. A cash-basis tax would wait on its transition account for ever. No text says how the supplier's books clear it, and the pack says nothing.

A tax on a tax. The Ivorian AIRSI is 5 % of the invoice including the VAT, added to it for a buyer outside the régimes réels. That is a group, which the format reserves and the core does not carry.

A withholding on a payment. The Senegalese BRS (5 % of a service invoice, art. 200) and the Ivorian 2 % on the services of a micro-enterprise (art. 84 bis) are withheld when the invoice is paid, not charged on it. There is no posting of that kind. Burkina Faso's third pack adds a third shape of the same gap: art. 221 of its Code withholds 2 %, 5 % or 10 % of a payment depending on who is paid and for what — vacations d'enseignement and occasional manual work at 2 %, sums paid to public and quasi-public bodies at 5 %, occasional intellectual work at 10 % — always at payment, never at invoicing.

A tax the engine refuses to post. Burkina Faso's taxe de développement touristique (art. 336 to 341) is not a percentage: 200 to 1 000 F per person per night by a hotel's star rating, 2 000 or 3 000 F per air ticket by destination — amount_type: fixed, a value the schema has carried since phase 0. post_document() refuses it outright: unsupported_tax_amount_type: only percentage taxes can be posted (supabase/migrations/20260911120900_post_document.sql). A fixed tax is accepted by the schema and by nothing that turns a document into an entry, so the pack carries no code for it, however precisely the six tariffs are written into the law.

A credit carried into the next return. Line 5.2 of the Ivorian form is the credit of the month before. settle_filing() carries a credit to 4449 and the next vat_return() does not read it back, so the pack stops at the credit of the period (6.2) and says so.

An electronic invoice that is not EN 16931. The Ivorian FNE is a national API — codes TVA, TVAB, TVAC and TVAD, a QR code, a number the DGI's platform issues — compulsory since 1 December 2025 for every régime réel; Senegal's Code requires an electronic invoice since 2025 and no order has said what it is. einvoicing.profile names profiles of EN 16931, so both packs leave it empty, and neither the NCC nor the NINEA has an ISO 6523 code for party_scheme.

A condition on the seller. Senegal's 10 % is for approved tourist accommodation: a status of the seller, which the five words of conditions do not have. The pack says supply_nature, the nearest.

A deadline that depends on the taxpayer's office. The Ivorian return is due on the 10th, the 15th or the 20th depending on whether a company belongs to the large or medium taxpayers' office and on its sector — the gap the note on deadlines already carries.

A cash book. The Système minimal de trésorerie of AUDCIF art. 13, open to the smallest entities, is a book of receipts and payments per bank and per cash box with a year-end inventory taken outside it, and its statements name no account. A double-entry chart could only imitate it by inventing accounts, so the packs carry the Système normal and nothing else.

The currencies. XOF is added to the seed, at no decimal. XAF, KMF, GNF and CDF come with the first pack that needs each; the note above about the seed's eleven currencies is now twelve.

Niger, a third withholding on a payment, and a condition on how one was made. The taxe sur les paiements en numéraire (1 % of a cash payment over 100,000 F CFA, loi de finances 2026) and the retenue de conformité fiscale (10 % of the price, withheld by a public payer from a supplier not in order, loi de finances 2025) are collected or withheld when an amount is paid, not when it is invoiced — the same gap the Senegalese BRS and the Ivorian withholdings already name, and for the same reason neither is written into packs/ne/taxes.json. A further rule of the 2026 law denies deduction on a transaction over 2,000,000 F CFA settled other than through a banking channel: a condition of how the invoice was later paid, which no tax code — fixed at the moment a document is issued — can carry either. And a repealed law can still be the best available source: Niger's new Code général des impôts (in force since 1 January 2026) was not reachable during the research (the tax administration's site was down, its certificate expired), so the pack cites the 2012 Code for what the finance laws of 2024 to 2026 do not rewrite — the fait générateur, the filing calendar, the mentions of an invoice — and says, article by article, that the new Code's numbering was never confirmed.

The Netherlands

Written from published sources alone, on 21 September 2026, and community like every pack nobody who files the return has read. It carries a selection of 283 postable accounts of the Referentie GrootboekSchema 3.8 under their RGS reference codes, the rates of article 9 of the Wet op de omzetbelasting 1968 with the reduced rate's move from 6 % to 9 % on 1 January 2019, the rubrics of the 2026 aangifte omzetbelasting keyed to their elements in the Dutch Taxonomy, and models C and E of the Besluit modellen jaarrekening. The pack's own README ends on the ten points a reviewer should look at first; four things are worth knowing here.

  • A reference chart is not a legal chart, and the pack says which it is. The Netherlands prescribes no chart; the RGS is a standard of the Standard Business Reporting programme that Dutch software maps its ledgers onto. The pack uses the RGS reference codes as account codes, because they are the only identifiers in the published file that nest by prefix — the reference numbers lose their leading zero on some rows — and the statements read them by prefix.
  • The mapping to the statements is published, not inferred. The RGS publishes with the chart a dataset linking every code to the taxonomy concepts it reports in, the jenv-bw2 concepts of Book 2, title 9 of the Civil Code among them. The lines of models C and E follow it.
  • E-invoicing is an obligation to receive, and only for the State. The pack declares Peppol BIS 3.0, the KVK number (EAS 0106) and the Dutch VAT number (EAS 9944), and no mandatory_from, because no Dutch text obliges a business to send an electronic invoice to anybody.
  • Out of scope, on purpose. The ICP statement as a form of its own, the file of either declaration, the OSS return, the small-business scheme, cash accounting under article 26, the margin schemes, the suppletie, corporate income tax, and the XBRL fact keys of the annual accounts.

From the Netherlands

Four things the pack could not say, each a change to the core rather than to a pack, and none taken here.

The recapitulative statement is still not a form a pack can declare. The opgaaf intracommunautaire prestaties of article 37a is filed on its own cadence — monthly for goods, quarterly on option below a threshold, quarterly on option for services — and the Belastingdienst publishes it as an entrypoint of its own (bd-rpt-icp-opgaaf-2026). ec_sales_list() computes its lines from box 3b, which is enough to fill it by hand; what is missing is the form, the gap From the recapitulative statement already names. The Dutch statement adds one more voice to it and asks nothing new, except that the cadence of goods and the cadence of services differ, as in Luxembourg.

A statement cannot carry a fact key that is a plain element. The Dutch Taxonomy names jenv-bw2-i:TradeReceivables, an element with no dimension, and the format refuses a key that is not a metric plus a member — the gap Estonia raised. The lines carry xbrl: null and name the concept only in the pack's README. Fix: the one proposed under Estonia.

A heading of the RGS is postable in the RGS and a heading here. RGS levels 2 and 3 are aggregation codes and level 4 is where a ledger posts, which is how the pack reads them. But a Dutch ledger keyed on RGS may post at level 5 (mutaties) under a level-4 code, and the format has no way to say that a level-4 account is postable and has children. The pack stops at level 4. Fix: none needed until a user wants level 5; the day one does, a chart would need either a postable-heading flag or level 5 as its leaves.

The payment branch of the tax point. Article 13(2) makes the tax due, at the latest, when the consideration is received — so an advance payment moves the tax point forward. The pack declares invoice_if_issued for paragraph 1; paragraph 2 waits for the prepayment document the core does not have, the gap already written under the tax point in packs.md.

From measuring a supply against its seller

Written on 21 September 2026 with 20260921145425_a_supply_measured_against_its_seller.sql, which closes three notes above — the Canary Islands, California's section 6396 as a condition, and the territories a document was judged against. What the work found and left open:

A Spanish rate on a supply that the law places in Spain but that is delivered abroad now needs the place written down. Version 0.2.0 of packs/es/ puts supply_in: ES on its domestic sale taxes. The place of supply resolves to supply_territory_code, then delivery_country, then the buyer — so a service to a consumer in another Member State taxed in Spain under the general rule of art. 69, or a distance sale under the threshold of art. 68, is refused until the document says supply_territory_code: ES. That is the honest reading: the place of supply is Spain and the document did not say so. A customer with no country at all is refused with no_party_territory for the same reason. Fix: none proposed; a bookkeeper states the place, and the refusal names the column.

The flag is sourced for eight rows, not for all that might carry it. Mount Athos, French Guiana and Mayotte (French VAT is provisionally not applied there), and the Channel Islands stay false until somebody writes the national text into their row. No pack of this repository conditions a tax on Greece, France or the United Kingdom in a way that reaches them today.

No country with state taxes on both sides of a supply is seeded. The relation was designed for a pair of taxes levied inside a state and a third levied across states; the reference table carries no such country's subdivisions, so the proof runs on the one pack that already has states, with same shown on the company's copy of the tax. A pack that needs it adds its territories to supabase/seed/00_territories.sql first — ekwo pack check refuses the key on a country with no territory inside it.

Every pack seed gained one column. tax_templates.applies_supply_vs_seller is written by the compiler for every tax, null where a pack says nothing, so every supabase/seed/*_pack_*.sql changed by that column and by nothing else. A pack branch opened before this change runs ekwo pack build <cc> once.

Closed in the core: cadences and the unit of a frozen box (21 September 2026)

Three gaps the packs wrote down are closed by migrations 20260921145411 and 20260921145412. The paragraphs above that describe them stay as they were written, as the record of why; this is what the core says now.

  • A two-month taxable period. (Ireland) declaration_period gains bimonth, four_month and half_year, each a whole number of months anchored on 1 January, the anchoring section 2 of the 2010 Act states. declaration_period_of(), the guard of vat_return(), ec_sales_list(), upcoming_filings() and company_filing_periods read all six. packs/ie/ 0.2.0 declares ["month", "bimonth", "year"] and proposes bimonth; the four- and six-monthly periods stay undeclared until a text of its register allows them.
  • A frozen box holds two decimals. (Senegal, Côte d'Ivoire) tax_filing_boxes.amount is numeric with no scale, and a frozen figure is round_amount() at the currency: a return in XOF freezes 1000000. What was frozen before keeps its value; lifting the precision rewrites nothing. Neither pack changes.
  • A declaration form filed in whole units, over a ledger kept in cents, cannot be said. (United States) tax_report.json takes rounding, a power of ten with its text, compiled to tax_report_templates.rounding_unit. prepare_filing() freezes each box at that unit and filing_drift() compares at it; vat_return() still answers the cents. packs/us/ 0.7.0 declares unit: 1 from the face of CDTFA-401-A.

Still open, and deliberately outside this change: a ledger at three decimals (TND, KWD, BHD…), where every amount column of the ledger is numeric(16, 2); and a box rounded as the sum of rounded lines, which no form read so far asks for — each box is rounded from its own exact figure.

Germany

Written on 21 September 2026. packs/de/ is community: nobody who files a German return has read it, and its README ends on the points a reviewer should look at first. It carries a reference chart of 169 accounts, 22 taxes with the 2020 rates beside the current ones, the 51 boxes of form USt 1 A 2026, the balance sheet of § 266 HGB and the income statement of § 275 Abs. 2 HGB, and a register of seventeen texts, each opened on that day.

The chart is not SKR 03 or SKR 04, on purpose. Those are the standard charts of DATEV eG, published under DATEV's copyright and no open licence. Whether a chart of accounts can be protected at all is disputed, and a public repository under an open licence is not where to find out. So the pack copies neither their numbers nor their labels: it writes its own chart, four digits, flat, in which the first digit is the section of § 266 or § 275 and the next two are the numerals of the item, and every label is the statutory wording of the item it reaches. The consequence is that a German bookkeeper will not find the account numbers their software uses; a mapping from SKR 03 or SKR 04 to this chart would be a document of its own, written by somebody entitled to quote both.

What the pack could not say, each worked around inside the pack and none of it a change to the core:

  • ekwo pack check does not hold a chart to the constraint the seed is held to. An allowance account typed asset_receivable and not reconcilable passed the check and failed at psql on account_templates_third_party_reconcilable. Fix: the check reads the same rule — a receivable or payable account is reconcilable — before the seed is written. Until then: the allowance account 2219 is asset_current.
  • A bank account must not be reconcilable, and nothing says so. settle_from_statement() takes the third-party side of the payment it books as the first reconcilable line of the entry, with no order; a bank account marked reconcilable can be that line, and the settlement is then refused with reconcile_account_mismatch. No bank account of the six packs before this one is reconcilable, which is why nobody met it. Fix: the function picks the line on the counterpart account it resolved, or ekwo pack check refuses a reconcilable asset_cash. Until then: 2420, 2421 and 2440 are not reconcilable.
  • Import VAT has no document to live on. German import VAT is assessed by customs on a notice whose amount is the tax alone, and it is deducted in Kennzahl 62; nothing is declared on the Voranmeldung beside it. A tax posts on a line whose amount is its base, so a code for it would either owe the goods' value to customs or clear the customs debt in the VAT settlement, where it does not belong. Until then: Kennzahl 62 is declared and nothing posts to it.
  • The recapitulative statement has an engine and no German file. ec_sales_list() already lists every line whose treatment is intra-Community, so a German company gets its Zusammenfassende Meldung figures for free; what is missing is a brick writing the format the Bundeszentralamt für Steuern takes, and a second form in the pack to record its cadence — the gap written under "From the recapitulative statement".
  • A box the form asks for "in every case" is omitted when it is nil. Kennzahl 83 must be filled even at zero; vat_return() returns the boxes that came to something, so a nil month shows no line 50.
  • Two "of which" boxes the ledger cannot fill. Kennzahlen 50 and 37 report the part of a reduction that comes from an irrecoverable debt (§ 17 Abs. 2 Nr. 1 UStG). A credit note does not say why it was issued, so no posting can tell that part apart.
  • The deadline extension belongs to the company. The Dauerfristverlängerung of §§ 46 to 48 UStDV adds a month to every return of a company that asked for it, and the special advance payment it costs is deducted by hand in Kennzahl 39. It is the same shape as the United Kingdom's scheme-dependent extension, one field over.
  • Two elections are properties of the company, not of a tax. The small business scheme of § 19 UStG makes a company's supplies exempt and relieves it of the Voranmeldung, and there is no VATEX code for it, so it is a legal mention and not a tax. Cash accounting under § 20 UStG is granted on application to the whole company; cash_basis is per tax, so offering it would mean a second code for every sale rate.
  • E-invoicing has three dates and the manifest holds one. Reception is compulsory since 1 January 2025 and is mandatory_from; issuing becomes compulsory on 1 January 2027 above 800 000 euros of turnover and on 1 January 2028 for everybody (§ 27 Abs. 38 UStG), which is written in the legal reference until the core can hold a size.

Mexico

Written from published sources alone, on 21 September 2026, and community like every pack nobody who files the return has read. The first pack of Latin America: a chart of 280 codes of the SAT's grouping code (Anexo 24 of the RMF 2026) used as the catalogue itself, the value added tax on a cash basis with the 8 % of the border-region decrees until 31 December 2026, the two withholdings a persona moral applies, the fields of the monthly IVA personas morales declaration, a minimal balance sheet and income statement on the grouping code's rubros, and a golden year of 16 documents. The pack's own README says where each piece comes from; seven things it could not say are changes to the core, none taken here. The epic on compound taxes, withholding and the territory of the parties is where most of them belong.

From Mexico

Clearance is not an obligation to exchange. A CFDI is valid only once an authorised certification provider has validated it, given it the SAT's folio and sealed it (CFF art. 29, fr. IV–V), and every taxpayer who issues vouchers is bound by it. einvoicing cannot say so: obligation: mandatory requires a mandatory_from, which requires a profile, and a profile is an EN 16931 profile a brick of packages/formats/ writes. Declaring cfdi-4.0 anyway was tried and dropped: describePack() (boundaryOf, packages/cli/src/pack/describe.ts) then adds to the country page, for any declared profile, that "an invoice is written and validated as by a brick of packages/formats" and that sending needs "a certified access point" on Peppol — neither true of Mexico, and exactly the impression that a document out of Ekwo is a valid invoice. So the pack leaves profile, mandatory_from and obligation empty, like Spain and Côte d'Ivoire, says the obligation and that Ekwo neither generates, stamps nor transmits a CFDI in the legal reference, and prints on every document that it is not a CFDI. Fix: a clearance model the format can state (obligation: mandatory with a model: clearance and a certifying party, no EN 16931 profile required), and a boundary row drawn only from a profile a brick actually writes. Italy's SdI, India's IRN and most of Latin America are the same shape.

A withholding is due on payment, and a cash-basis tax takes one tax posting. A persona moral withholds two thirds of the VAT an individual charges for fees, rent or commissions, and 4 % of road freight (LIVA art. 1o.-A; RLIVA art. 3o.), at the moment it pays. The pack carries each as a purchase tax with a third posting to 216.10, booked on the invoice date, because ekwo pack check rightly refuses a second tax posting on a cash-basis tax. Fix: a posting that follows the payment — the gap the Senegalese BRS already names — so one tax can defer its creditable share and its withheld share together.

Two thirds cannot be written. factor_percent is numeric(7, 3): the pack writes -66.667, and the withholding drifts by a cent once the tax reaches 1 500 pesos. Fix: a factor as a fraction (numerator, denominator), or a scale wide enough that no real fraction rounds at the cent.

VAT and income tax withheld on one line. The same rent pays 16 % VAT, two thirds of it withheld, and 10 % ISR withheld (LISR arts. 106 and 116). A line carries one tax and group is reserved — the stacked-tax gap of Quebec and of the Spanish recargo, now with a withholding in the stack.

Zero-rated and exempt values fall due on collection too. LIVA declares the value of 0 % and exempt acts when collected, like the taxed ones. A cash-basis tax must carry a tax posting, and a 0 % line has no tax to post, so those codes declare their base on the invoice date. Fix: let a cash-basis tax defer a base with no tax, or let the transition carry the base alone.

A region that is a list of municipalities, and a condition on the seller. The 8 % applies in establishments of the northern and southern border regions — municipalities listed in each decree — held by a business registered in the SAT's list of beneficiaries. territories goes down to the state, and the five words of conditions have nothing for a status of the seller (the Senegalese approved hotel is the same gap). The bookkeeper picks the code.

Two more forms, and a party's CFDI data. The IVA retenciones declaration (filed with the same monthly payment) and the DIOT (a statement per supplier, LIVA art. 32, fr. VIII; RMF rule 4.5.1) are forms a pack cannot declare beside its periodic return — the Canadian and Senegalese gap. And a CFDI needs, for each party, the tax regime and the postal code of the tax domicile, and for the receiver the use of the CFDI (c_RegimenFiscal, c_UsoCFDI): the core has no column for any of them.

A deadline in working days, by a digit of the RFC. The 17th of LIVA art. 5o.-D moves by one to five working days by the sixth digit of the RFC (decree of 26 December 2013, art. 5.1), except for listed taxpayers. The pack says depends_on_taxpayer; the rule is fully written in law and a core that knew the RFC and the working-day calendar (CFF art. 12) could compute it.

MXN is added to 00_currencies.sql at two decimals, and MX to 00_territories.sql, outside the common system of VAT.

Australia

The first pack of Oceania, packs/au/, community, seed 40. Written from published sources alone on 21 September 2026: an original chart of 156 accounts blocked onto paragraph 35 of AASB 1060, 24 taxes — the one GST rate of s. 9-70 since 1 July 2000, GST-free and input-taxed supplies kept apart, the offshore reverse charge of Division 84 and the agreed one of s. 83-5, deferred GST on imports, cash accounting on both sides, and the 47 % withheld from a supplier who quotes no ABN — the business activity statement NAT 4189 with label 7A, the statement of financial position and the statement of profit or loss of AASB 1060, and a register of thirty-five texts. The pack's own README says where each rule comes from, ends on the nine points a reviewer should read first, and is written to be the model of the New Zealand pack.

From Australia

Seven things the format could not say, none of them patched: each is contoured inside the pack and written down here.

A deadline that depends on the cadence, and one period that has its own. A monthly statement is due on the 21st of the following month (GST Act, s. 31-10), a quarterly one on the 28th (s. 31-8), and the December quarter on 28 February, two months later. A form carries one deadline rule, so a form filed on three cadences cannot give each its day, and no rule names an exception for one period of the year. The pack declares the 21st, which is never later than the law and is seven days early for most quarterly filers. Fix: a deadline per cadence, and a period-specific exception — Spain's form 303 wants the second too.

A return reported on a method the filer chooses. The activity statement is completed either on the calculation worksheet, where every label includes GST and 1A and 1B are derived by dividing by eleven, or from the accounts, where G1 may exclude GST and 1A and 1B come from the records. The two fill the same labels with different figures, and a pack has one set of postings. The pack takes the accounts method with GST-exclusive amounts, which is the one a ledger produces, and leaves the worksheet labels out. Fix: none needed while a ledger is the source; the day a GST-inclusive G1 is wanted, a box that is the sum of a base and a tax of the same posting would do it — box_ref already qualifies a kind, and a total of G1:base and a hidden tax box would be the shape.

A statement that is more than one tax. The business activity statement reports GST, PAYG withholding, PAYG instalments, fringe benefits tax instalments, wine equalisation tax, luxury car tax and fuel tax credits on one form with one payable amount, label 9. The pack declares the whole form and leaves empty the labels no document fills, so 8A and 9 are right for a business whose only obligations are GST and no-ABN withholding and short for any other. Fix: a box fed from outside the ledger's taxes — a payroll figure, an instalment notice — which is an input to vat_return() rather than a posting.

A cash basis that is a regime of the business. Section 29-40 lets a small business account for all its GST on payment. The pack carries it as codes with cash_basis, as the Irish pack does for the moneys received basis, and a company can mix them with accruals codes. This is the small_business gap again, and Australia adds that the regime covers purchases as well as sales.

A reverse charge the ATO reports at 110 %. The ATO asks for the price of a reverse-charged offshore purchase multiplied by 1.1 at G1 and at G11, whatever method the filer uses. A base posting reports what the line carries, so the pack reports the price without GST, consistent with its choice at G1, and names the point first among those for a reviewer. Fix: a box_factor above 100 is already expressible and was deliberately not used, because it would contradict the GST-exclusive choice; the question is the ATO's, not the core's.

A mention that depends on the kind of document. A tax invoice must be clearly intended as one (s. 29-70(1)(d)) and an adjustment note is a different document; applies_when has no condition on the document type, so "Tax invoice" is left to the renderer's title rather than written as a mention.

A unit filed by truncation. Since rounding.unit a form may be filed in whole dollars, and the frozen box is round_amount() at the country's method, half up here. The activity statement says to round cents down — a label is truncated, not rounded — so declaring unit: 1 would freeze 1,170.60 as 1,171 where the ATO wants 1,170. The pack leaves rounding out and reports cents. Fix: a direction beside the unit (down), read by prepare_filing().

One more observation beside those. The tax point is the earlier of the invoice and the first payment (s. 29-5(1)), delivery playing no part; the vocabulary has earliest_of_delivery_or_payment and not its invoice counterpart, so the pack declares invoice_date, which is right whenever the invoice comes first.

Singapore

The first pack of Asia, packs/sg/, community, seed 50. Written from published sources alone on 21 September 2026: an original chart of 142 accounts blocked onto the statements of SFRS for Small Entities, 36 taxes — the 7 %, 8 % and 9 % of s. 16 of the Goods and Services Tax Act 1993 as three codes each, zero-rated exports and international services, the exempt financial and residential supplies of the Fourth Schedule, out-of-scope supplies, disallowed input tax, import GST paid to Singapore Customs and suspended under MES, the reverse charge of s. 14 in its claimable and non-claimable halves, customer accounting of s. 38A on both sides, and withholding on interest and royalties paid to non-residents — form GST F5 with boxes 1 to 17, and a register of twenty-four texts. The pack's own README says where each rule comes from, which text could not be read, and ends on the points a reviewer should read first. It is written to be the model of the next packs of the region.

From Singapore

Five things the format could not say, none of them patched: each is contoured inside the pack and written down here.

A transmission to the tax administration is not an exchange between businesses. The GST InvoiceNow Requirement obliges a GST-registered business to send the data of its sales and purchase invoices to IRAS through the Peppol network — for new voluntary registrants since 1 November 2025 and 1 April 2026, for everybody else in phases from 1 April 2028 to 1 April 2031 by amendments still to be enacted (IRAS e-Tax Guide, second edition, 9 March 2026). It binds the seller and the buyer alike, by registration status and turnover, and says nothing about what the customer receives. einvoicing.obligation has three words for whether businesses must exchange invoices between themselves, and none for a report to the administration that travels on the same network. The pack declares none and writes every date in the legal reference. Fix: a second field beside obligationclearance or reporting, with its own phased dates per class of taxpayer; Malaysia's MyInvois and several other Asian regimes will want the same.

A Peppol profile with its own category codes. PINT SG reports the GST category of a line as SR, ZR, ES33, ESN33, OS, NG, SRCA-S, SRCA-C, SRRC and a few more — not the UNCL5305 letters — and vat_category is a two-character UNCL5305 code. The pack declares the UNCL5305 letter its treatment requires and names the PINT SG code in each tax's legal reference, so an application cannot read it. Fix: a profile_category beside vat_category, free-text within the profile's own code list and checked for shape only, as reason_codes already is.

A box that is not a tax base. Box 13 of the F5 is the period's revenue from the profit and loss account, which IRAS accepts as an estimate. No tax posts to it, and a box can only be summed from tax postings or computed from other boxes. The pack declares it and leaves it empty. Fix: a box summed from accounts — the statement rules code_range and account_type are already the vocabulary — read by vat_return() over the period.

Quarters that do not start in January. Reg. 52(2) makes the quarter the standard period, and IRAS aligns the quarters on the month the financial year ends in: February to April, May to July and so on for a January year end, March to May for a February one. The six cadences of the core are anchored on 1 January, so a company whose quarters start in February files on a cadence the core calls none of them, and vat_return() lets the period through without the guard. Fix: an anchor month on company_filing_periods, defaulting to January.

A rule that zeroes a small net amount. Section 41(7) of the Act makes a net amount of tax, payable or repayable, zero when it is under $5. A total is a list of boxes with a floor at zero and nothing else, so box 8 reports the difference as it is. Fix: none needed in the return — the rule is IRAS's to apply when it assesses — but settle_filing() would book a sub-$5 balance that is never paid.

One more observation beside those. The SFRS for Small Entities text is served to Singapore IP addresses only, so the statements carry the paragraph numbers of the IFRS for SMEs Accounting Standard it is based on; the README puts this first among the points to review.

An order the user sees by code point. installedPacks(), which builds the country question of ekwo init, orders the packs by name in SQL, and the collation compares code points: Singapore comes before Sénégal, since é sorts above every unaccented letter. tests/cli/bootstrap.test.ts expected JavaScript's localeCompare and turned red with this pack, the first whose name falls between the two; the test now compares by code point, which is what the engine does. The socle is unchanged. Fix: order the list a person reads alphabetically in their language — localeCompare with the language of the installation, in installedPacks() — and the test with it.

Benin

The third OHADA member, written on 21 September 2026 alongside Senegal and Côte d'Ivoire above. packs/bj/ adds one thing to the family's list, and asks for nothing new beside it — the rest of what it could not say is the gaps already written under From the OHADA packs and Mexico, one country wider each:

A withholding that shares its base with the VAT on the very same line. The Beninese AIB (acompte sur impôt assis sur les bénéfices, CGI art. 130 to 134) is assessed on the identical net price as the VAT of the same purchase — "le prix toutes taxes comprises, à l'exception de la taxe sur la valeur ajoutée" (art. 132-1-b) — so a document line that already carries BJ-P-18 has nowhere to carry the AIB too: one line, one tax field. This is not a new shape of the gap Mexico named VAT and income tax withheld on one line — it is the same shape, a country earlier than the one that follows it. The pack carries no AIB at all, rather than a code nothing can invoice.

Two things Benin adds no code for either are gaps this file already carries a name for: the AIB withheld by the State, a local authority or a corporate taxpayer when they pay a supplier (art. 130-3) is A withholding on a payment, next to Senegal's BRS; and the MECeF/e-MECeF certified billing machine that alone makes a Beninese invoice valid (art. 481 to 483) is Clearance is not an obligation to exchange, next to Mexico's CFDI and Côte d'Ivoire's FNE.

Cameroon

Written from published sources alone, on 21 September 2026, and community like every pack nobody who files the return has read. The third of the seventeen OHADA members, after Senegal and Côte d'Ivoire: 17,5 % principal plus 10 % of it as centimes additionnels communaux (CAC), a displayed rate of 19,25 %, a reduced rate of 10 % created by the loi de finances 2026, and a golden year of twelve documents. The pack's own README says where each figure comes from; what it could not say either stays there (import VAT, non-VAT withholdings, the formulaire's own boxes) or is a change to the core, below.

From Cameroon

Not every "surtax on the tax" is the Ivorian gap above. The CAC (Livre de fiscalité locale art. C 82/C 83, 10 % of the principal VAT) is a fixed share of one tax's own amount, not a second tax computed on a base that already includes the first — unlike the AIRSI, which needs group. packs/cm/taxes.json posts it with a second factor/box_factor pair on the same tax entry (CM-S-1925, CM-S-1925-SRV), the mechanism CI-P-18-95 already uses to split one tax's amount between a recoverable account and a cost. Chad, the Congo and the Central African Republic have the same CAC and can reuse it.

Splitting a tax's postings by factor still has to sum to 100. The first draft declared rate: 17,5 (the principal alone) and gave the CAC a second posting at factor: 10 — 10 % of the principal, exactly what the law says. It rounds twice even so: document_tax_summary.tax_charged (supabase/migrations/20260918141605…) rounds the whole computation once, round(base × 19,25 %), and post_document()'s postings round tax_amount once and then share it out by factor, the last posting of a side taking the remainder (20260921145425…) — here round(round(base × 17,5 %) × 1,10), a different rounding path that can land a franc away from what the invoice charges. Declaring rate: 19,25 (the displayed rate) and splitting it 90,909 % / 9,091 % (the three decimals factor_percent allows) makes the two paths the same computation, because a side whose factors sum to exactly 100 rounds once either way. Caught on a golden document of base 1 003, an amount that does not round evenly, before it shipped; a pack that gives a compound rate to one posting and the remainder of the law to a second one, anywhere in the OHADA packs still to come, is worth the same check.

A withholding on a payment, two more shapes. Cameroon's VAT is withheld in full by a designated buyer (the State, a public or semi-public company, some non-profits and listed private companies) at settlement, against a DGI-issued attestation (art. 149 (2), 143) — the invoice itself is raised for the full amount, only the cash collected changes, which is the same gap the Senegalese BRS and the Ivorian micro-enterprise withholding already name. Cameroon adds income-tax withholdings printed on the same invoice without being VAT at all — a 2 %–10 % advance of IS/IRPP (art. 21), a précompte sur achats a seller collects from its own customers (art. 21 (3)), and 5 % on fees paid to a person domiciled in Cameroon (art. 92 bis) — none of which has a VAT return box to feed, so no core mechanism is missing for them so much as not aimed at them.

An electronic invoice condition on deduction, not just on the invoice. Cameroon's suivi électronique de la facturation (Livre des procédures fiscales art. L 8 bis, art. L 8 sexies since the loi de finances 2026) makes a VAT or IS deduction depend on the supplier's own invoice having passed through the DGI's system (art. 143 (1) b), 8 bis (2)) — a step further than the Ivorian FNE, which conditions deduction on holding a normalised invoice but not on a fact about the counterparty's compliance the core has no column for. No arrêté fixing a format or a rollout date is published, so einvoicing stays empty here too.

From Togo

Togo is the third OHADA member, after Senegal and Côte d'Ivoire, and the first with a single positive VAT rate: art. 195 of the CGI, read in the 361-page consolidated text and not just the research fiche, prints Abrogé where a reduced rate used to sit, and no other percentage appears in the whole VAT chapter. What Togo adds to the two gaps already named above:

A self-supply with no document type. The Togolese return has a line for the livraison à soi-même (CGI art. 190, 191-4°) — a delivery a business makes to itself, taxed at first use — with its own box on the form. A golden's four document types (sale_invoice, sale_credit_note, purchase_invoice, purchase_credit_note) all carry a contact, which a self-supply has none of: packs/tg/ declares the box and cites the article, and no tax posts to it.

A purchase-side cash basis. Togolese art. 199, last paragraph, opens the buyer's right to deduct a service or a works contract in the month it is paid, not the month the invoice is booked — the mirror of the seller's own cash_basis, which a sale tax can carry with a transition account (cash_basis_transition_account). A purchase tax has no such field: there is no way to defer a deduction to a payment, so the pack books it at the invoice and says so in legal_reference. Côte d'Ivoire's CI-P-18-SRV already takes the same shortcut for the same article (its own art. 361-2°), undocumented at the shared level until now.

A test that assumes every country has two rates. tests/golden.test.ts asks every pack's golden to exercise more than one positive rate, the one assertion of that file with no if (pack.taxes.some(...)) guard — every other line in it is conditional on what the pack itself declares. Senegal and Côte d'Ivoire, the two packs the assertion was written against, each have two; Togo has one, by law, in 2026. packs/tg/ leaves this one test red rather than dating its golden year before the reduced rate was abrogated, or inventing a rate the CGI no longer has. Fix: the assertion itself needs the same guard as its neighbours — skip it for a pack whose own taxes.json never declares a second positive rate.

From Gabon

packs/ga/ is the third OHADA member, after Senegal and Côte d'Ivoire. It adds nothing to the shared chart, journals or statements — see From the OHADA packs above — but its own tax, the contribution spéciale de solidarité (CSS), reopens two of that section's gaps and adds a third the other two packs did not show.

A tax withheld by any client, not a named one. Under the Senegalese précompte or the Ivorian TVA pour compte, a buyer of a listed status — the State, a large distributor, a public body — withholds the tax. Gabon's CSS (textes fiscaux non codifiés, art. 26, alinéa 3) is withheld by any VAT-registered client from any supplier's invoice and paid over for the supplier's account: the ordinary case of a sale between two companies, not an exception reserved to a few buyers. packs/ga/ posts GA-CSS-S and GA-CSS-P as though the CSS were paid and borne in cash on both sides instead, on the model of the précompte's unsolved seller's side, and says so in its own README.

A tax on a tax the golden year cannot show at all. The CSS sits beside the VAT on the very same invoice line, both read off the same base — the shape of the Ivorian AIRSI. AIRSI at least has no document to post against in a golden year; Gabon's CSS and VAT are two taxes a real invoice line carries together, and golden/scenario.json's lines[].tax is a single string. packs/ga/ defines GA-CSS-S and compiles it, but cannot exercise it beside a VAT code on one line of its own golden year.

A reduced rate under the directive's own floor. The CEMAC directive that frames Gabon's VAT allows a reduced rate between 5 % and 10 % (art. 22, 2°, c)); the 3 % Gabon created on 17 July 2026 for fer à béton made in Gabon is under that floor. Nothing in the schema checks a rate against the directive of the zone a pack names — packs/ga/ records the gap in prose, the way packs/ci/ records the AIRSI added on top of the VAT-inclusive price.

New Zealand

The second pack of Oceania, packs/nz/, community, seed 41, read beside packs/au/. Written mostly from Inland Revenue's own guide, IR375, after legislation.govt.nz refused every attempt to read the Goods and Services Tax Act 1985 directly this session, scripted and browser-driven alike: an original chart of 163 accounts, seventeen taxes at the one GST rate of 15 % since 1 October 2010 — zero-rating in four separate shapes, exempt supplies that leave the return altogether, the imported-services reverse charge of s. 8(4B), and the payments basis on both sides — form GST101A, the statement of financial position and profit or loss of a Tier 2 for-profit entity, and a register of nineteen texts. See packs/nz/README.md.

From New Zealand

A combined GST-inclusive box turned out not to need a format change. The Australian section above speculated that a GST-inclusive box like GST101A's Box 5 would need "a total of G1:base and a hidden tax box" — a new kind of box the core did not yet have. It does not: grossing the value of a standard-rated supply to 115 % on the base posting itself, with box_factor, writes the GST-inclusive figure straight into Box 5, and the GST itself posts as an ordinary tax posting straight into Box 8 — a real, printed box of the form, not an invented one. Box 8's own instruction on the form ("multiply Box 7 by three and divide by twenty-three") and the pack's direct posting agree to the cent whenever every rate is 0 % or 15 %, which is every rate a flat-rate GST carries. Nothing here needed a hidden box, and the first attempt at this pack used one — 5B/5T, 11B/11T — before tests/tax_report.test.ts, "the hidden boxes the packs still carry", refused it: a hidden box may only be an intermediate total the form itself works out and does not print, never a leaf a posting writes into, which is exactly the shape docs/packs.md retired when it said "a posting names every box it prints in". The Australian hint should be read as superseded by this pack's correction rather than as a second country confirming it.

A two-monthly period anchored to 1 January, not to the taxpayer's own balance date. Section 15 gives a two-monthly taxable period by default, and IR375 gives a March balance date's periods as "April-May, June-July, August-September, October-November, December-January, February-March" — six periods that start on the company's own balance month, not on January. The core's bimonth is a fixed calendar cadence, January-February, March-April and onward, anchored to 1 January for every company and every country. A New Zealand company whose balance date is not itself a bimonth boundary — the ordinary 31 March one included, since March is the third month of a Jan-anchored pair — cannot be filed on this pack's bimonth without a one- or two-month misalignment. This pack's own golden year sidesteps the gap by giving its company a 31 December balance date, which happens to fall exactly on a Jan-anchored boundary; a reviewer replaying a company with the ordinary March balance date would see it. Fix: an anchor month per company, read from the fiscal year rather than assumed to be January — the same gap Australia's own packs/au/README.md does not have to name only because its golden company's July balance date and its quarterly GST cadence both already start in a month the calendar quarter also starts in.

A deadline with two calendar exceptions and a weekend roll-forward, on one declared rule. IR375: the due date is the 28th of the month after the period, except a period ending 30 November is due 15 January and one ending 31 March is due 7 May, and any due date on a weekend or public holiday moves to the next working day. deadline carries one rule for the whole form, so this pack declares the 28th — correct for ten of the twelve months a period could end in, and early rather than late for the other two. Fix: the same one Australia's own deadline section asks for — a deadline rule that can name a dated exception for a specific period end, beside the general rule.

Taxable supply information graded by three value tiers, with no place in documents to say so structurely. Sections 19E to 19N ask for almost nothing below $200, the supplier's GST number above $200, and the buyer's own identity above $1,000 — a genuine three-tier regime, not the two-tier threshold documents already has a word for (a tax invoice required above a value, none below it). This pack states the tiers in documents.numbering's own legal_reference prose, because the schema has no field for a document rule that changes by the amount of the document itself. Fix: a structured field for a tiered particulars requirement would let ekwo pack check verify the tiers the way it verifies everything else about a document rule, rather than trusting the sentence.

Japan

The first pack of Asia, packs/jp/, community, seed 51. Written from published sources alone on 21 September 2026, in Japanese with a complete English file: an original chart of 125 accounts cut onto the items of the 会社計算規則, 33 taxes — the combined national and local consumption tax at 10 % and 8 % since 1 October 2019 and at 8 %, 5 % and 3 % before, exports and non-taxable supplies kept apart, the reverse charge on specified taxable purchases, imports, and the deduction for purchases from businesses that are not qualified invoice issuers on the schedule of the 2026 reform (80 %, 70 %, 50 %, 30 %) — the general-method return with the lines of 付表1-3 and 2-3 it is built from, the balance sheet and income statement of the Ordinance, and a register of twenty-four texts. The pack's own README says where each rule comes from and ends on the six points a 税理士 should read first.

From Japan

Eight things the format could not say, none of them patched: each is contoured inside the pack and written down here.

A rounding rule that is part of the law, and which the engine meets by coincidence. 消費税法施行令 art. 70-10 requires the tax on a qualified invoice to be rounded once per invoice and per rate, the direction being the issuer's choice. The engine rounds once per tax group, which is once per code: the rule holds as long as one rate is carried by one code on a document, and breaks for a document that mixes a tax-exclusive and a tax-inclusive code at the same rate, or a former-rate transaction and a current one. Fix: a tax group keyed on the rate (or on a rounding_group a pack declares), not on the code. Truncation is the pack's choice of rounding_method, the first down in the repository; the golden year runs on it without incident.

A box filled with a share of the tax is rounded on its own. The pack splits each invoice's tax 78/22 between the national tax and the local share. The ledger postings share the tax with the remainder on the last, but each posting's box_factor is applied to the whole tax and truncated separately, so an invoice's boxes can add up to a yen less than its tax (the golden year's local share is 26,927 in the box and 26,931 on the ledger). Fix: let the boxes of one side share out the tax the way the postings do, remainder on the last.

A category the core refuses. JP PINT gives the reduced rate the category AA ("lower rate") and has no Z; the check holds a taxed domestic supply to S. The reduced codes carry S, which a JP PINT validator would read as the standard rate. Fix: accept AA for a domestic supply above zero where the declared profile uses it; UNCL5305 defines it as a lower rate.

A return worked out from its own totals. The form multiplies: the 課税標準額 (truncated to the thousand yen) by 7.8 %, the local tax as ⑱ × 22/78 (truncated to the hundred). rate is a percentage with no truncation, 22/78 is not a finite decimal, and a unit on the form (rounding.unit) is one unit for every box when this form has three. The pack uses the accumulation method the law allows (sum of the invoices' tax × 78/100), which a ledger produces, and sums the local shares on three working boxes the form does not print (a hidden box may not be written into, so they are visible and marked (計算用)). Fix: a rate given as a fraction, and a unit per box with a direction (down), which the Australian pack also asks for.

An annual period that does not start in January. The taxable period of a company is its business year (art. 19(1)(ii)), which its articles fix — April to March in the golden year — and a cadence here is anchored on 1 January. year is declared and proposed, and a company on an April year files on a period the list cannot name. Fix: anchor year, half_year and quarter on the company's fiscal year for a form that says so.

A deadline of two months. The return is due within two months of the end of the period (art. 45(1)), three for a company that extends its corporation tax return and notifies it (art. 45-2), and on 31 March for an individual. The vocabulary has a day of the next month and the last day of the next month; the pack declares the last day of the next month plus 28 days, the 28th of the second month, never later than the law. Fix: months_after_period with a day or last, and the same per-cadence exception Australia asks for.

A deduction that depends on a ratio of the period. Where taxable sales exceed 500 million yen or the taxable sales ratio is below 95 %, the tax on purchases is apportioned (art. 30(2)); and the reverse charge applies only below 95 %. The ratio is computed on the return from boxes the pack fills (⑮ / ⑯), but a box cannot divide and a posting cannot depend on a period figure. The pack deducts in full and says so, and its golden company is kept above 95 % so that no figure is frozen that the law would not give; a company below 95 % is deducted too much, and the reverse-charge code, which only such a company uses, is left out of the golden year for that reason. Fix: a declared apportionment — a box that is a ratio of two boxes, and a deduction line that is a rate of another at that ratio — which Belgium, France and Spain (prorata) need as much as Japan.

Two regimes of the business, and a mark on a line. The simplified method (簡易課税) and the 20 % and 30 % special measures for businesses that became taxable by registering are properties of the business, not of a tax — the small_business gap again. And a qualified invoice must mark the reduced-rate items (art. 57-4(1)(iii)); applies_when has no condition on a rate, so the mark is left to the renderer, as is the issuer's registration number, which is the company's tax identifier.

JPY was already in 00_currencies.sql at no decimals. JP is added to 00_territories.sql, outside the common system of VAT. The food rate of 1 % the Cabinet proposed on 15 September 2026 for April 2027 to March 2029 is not in the pack: it is not law.

Hong Kong

packs/hk/, community, seed 52, HKD. Chosen on purpose, and not for its market: every pack this repository carried before it lives inside a country that charges a value added tax, a goods and services tax or a sales tax on what it sells, and Hong Kong Special Administrative Region charges none of the three — no turnover tax at all, at any level of government, and never has. It is the first pack that finds out whether "a country files a periodic return" was ever actually optional, the way docs/packs.md step 3 already said it was ("If the pack declares a periodic return, name tax_payable"), or whether it only looked optional because every pack so far had one.

The pack itself is unremarkable by the standard of the others: an original 104-account chart blocked to the statement of financial position and the income statement a company reporting under the SME-FRF & SME-FRS (HKICPA) presents, two tax codes (HK-S-NA, HK-P-NA) at 0 %, not_subject, posting their base and nothing else, a golden year of thirteen documents across two directions and a credit note on each side, and nine sources. What is unremarkable about it is the finding: vat_return() already has a clause for exactly this — a country with no row in tax_report_templates reads back "no pack for this country: the ledger boxes, and no total" — and it does, cleanly, returning zero rows for the one period the golden scenario declares, which tests/golden.test.ts's "files the declaration" comparison accepts without complaint. The core was already right. Two assertions of the test suite were not — found first here, fixed in a commit of their own once found, because a shared test file is not this pack's to change without saying why in the open.

From Hong Kong

Two things the core said once and the shared test suite said twice, plus one field the format has no honest answer for here.

tests/tax_report.test.ts, "accepts the packs of this repository as they are", asserted every pack has a report, unconditionally. The test loops over allPacks and opened with expect(pack.report, pack.slug).not.toBeNull() — which failed cleanly, hk: expected null not to be null, and never reached the lines below it that read pack.report!.boxes. Every pack this suite had ever carried files a return, so the assumption behind the assertion had never been exercised against one that does not. Fixed, in a commit of its own: the very first test of the same file already had the right shape — allPacks.filter((pack) => pack.report !== null) — so the report-specific assertions now skip a pack whose report is null, with a comment saying why, and nothing else in the test changed.

tests/golden.test.ts, "exercises both directions, more than one rate, and a credit note", asserted a scenario has more than one positive tax rate, unconditionally. expect(rates.size).toBeGreaterThan(1) was not gated by "where the pack has one" the way the reverse-charge and cash-basis assertions two it blocks below it are — it assumed every pack has a rate to begin with, let alone two. A Hong Kong scenario that invented a second rate to satisfy it would have been lying about Hong Kong law to satisfy a test, which is the one thing a pack must never do; this pack did not, and the assertion failed, honestly, on a scenario that was otherwise complete — both directions, a credit note, a matched payment and an unmatched one, a balanced ledger, a balance sheet that ties to the cent. Fixed, in the same commit: the assertion now runs only for a pack that declares more than one positive rate at all (pack.taxes.map((t) => t.rate).filter((r) => r > 0), more than one distinct value) — a pack with a single rate, or none, is not failing to exercise a second rate it never had.

documents.tax_point has no true answer for a country with no turnover tax. The field's own schema description names what it is for: "when the tax becomes chargeable under the country's general rule." Hong Kong has no turnover tax and so no such rule for a value to be read from — not a gap in research, an absence in the law. This pack declares invoice_date as the closest general commercial convention and says so in pack.json and in its README; a reviewer should read it as a placeholder for a question the format cannot ask of this country, not as a citation. Fix: none obvious that does not either special-case a no-tax country in the schema (which the same rule that refused patching the core for this pack refuses here too) or add a sixth enum value meaning "not applicable", which is a change to every pack's reader and not this one's to make.

Two further notes, neither a socle gap:

No revaluation reserve, and no statement of comprehensive income. SME-FRS keeps property, plant and equipment at cost, so this chart carries no revaluation account, and an entity reporting under it never has an item of other comprehensive income to put on a second statement — which is why statements.json carries an income statement and not a statement of comprehensive income, unlike every European pack in this repository.

elegislation.gov.hk could not be read in this environment. Three attempts — a plain script fetch, a JavaScript-rendering proxy, and the account's own browser automation — each returned the site's home page or a loading screen and never a section's text, for both Cap. 622 and Cap. 112. The two ordinances are cited at the level this pack could actually confirm against a source that did render: the Inland Revenue Department's own pages, which quote sections 14 and 51C of Cap. 112 directly, and Part 9 of Cap. 622 by name and subject rather than by subsection. packs/hk/README.md says so under "Sources" and lists the exact subsections a reviewer should confirm before the pack moves past community.

From Chad

packs/td/, community, seed 35, the third OHADA member after Senegal and Côte d'Ivoire. Its chart, journals, roles and two statements are the common part of packs/ohada/; what it adds is the value added tax of the Code général des impôts, read from the loi de finances pour 2024 (a native text PDF) and the loi de finances pour 2026 (an unlayered scan, read back by OCR and checked page by page against the images). Two things the format could not say, neither patched:

A rate the invoice shows once, a tax the return books in two places. Chad facturé at 19,25 % is 17,5 % (CGI art. 238-I-1°) plus 10 % of that same amount in centimes communaux et provinciaux (CGI art. 1017, loi de finances pour 2024, art. 16) — a surtax the CEMAC directive only allows because it is deductible exactly as the tax itself (directive n° 11/22, art. 22-1). Computing 17,5 % and then 10 % of the result as two independent taxes would round each on its own and could disagree with the 19,25 % the facture prints by a unit of the currency. The format already had what this needed: one tax at 19,25 %, two tax postings of factor 90,909 and 9,091 — summing to exactly 100,000 — to the account of the State's share and to 4422 « Impôts et taxes pour les collectivités publiques », and post_document()'s rule that the last posting of a side takes what the first one's rounding left over. No core change was needed to book it correctly; the pattern is written here because Cameroon, Centrafrique and Congo carry the same surtax and will want the same two postings rather than two taxes.

A third party's withholding, joined to a buyer's self-assessment. CGI art. 229-V (loi de finances pour 2026, art. 30) makes an assujetti who buys from a seller outside the Impôt Général Libératoire — or who has crossed its 50-million-FCFA threshold mid-operation — self-assess the tax the seller never charged, and "la verse... dans les conditions de droit commun relatives à la TVA retenue à la source": by the same remittance rule as CGI art. 245, where a buyer named on a DGI list withholds the tax owed by a supplier who is not on it and pays it to the Treasury in the supplier's place. The first is a two-party reverse charge, which treatment: domestic_reverse_charge and a due-and- deductible pair of postings already say (TD-P-AUTOLIQ, netting to zero as Senegal's SN-P-NR-18 already does for a foreign service). The second is a three-party withholding — the buyer remits a debt that stays the seller's — which is the exact gap docs/international.md already names for Senegal's précompte and Côte d'Ivoire's TVA pour compte de tiers: a third time, in a CEMAC country whose own law cross-references it from the mechanism the core can express. packs/td/ books the self-assessment and says nothing about the withholding.

One condition, contoured rather than patched: art. 229-V turns on the seller's regime and a threshold crossed during the operation, a fact about the seller that the document does not carry on its own. conditions already has seller_threshold for exactly this shape of fact — a running total the seller crossed, or has not — so TD-S-AUTOLIQ and TD-P-AUTOLIQ declare it and the pack says no more than that a human has to answer the question, as packs/sn/ already does with supply_nature for its own approved-status condition.

From the Central African Republic

packs/cf/, community, seed 25. Its chart, journals, roles and two statements are the common part of packs/ohada/; what it adds is the value added tax of the Code général des impôts, read from the DGID's own 2023 update of the 2017 edition — no loi de finances for 2025 or 2026 was found promulgated. Unlike the CEMAC neighbours already in this file, its 19 % is not a rate the invoice shows once and the return books in two places: no centimes additionnels or similar surtax on the VAT was found for the Central African Republic, so CF-S-19 and CF-S-19-SRV post the whole rate to one account, as any UEMOA pack does.

A closed list the CEMAC directive draws, a national list that ignores its edges. Directive n° 11/22 (art. 22-2 c) reserves an optional reduced rate to six tariff numbers. The Central African CGI's own 5 % list (art. 257) keeps two of them — milk 04.01, pesticides 38.08 — and adds four the directive does not cover: vegetables and tubers (07.01-07.14), non-school books, meat and poultry, and medico-surgical or dental furniture. A directive binds the member state to transpose it, not a taxpayer directly; what a Central African invoice owes is the CGI's own rate, transcribed here as written (CF-S-5), with the mismatch named rather than corrected to fit the directive's list or silenced. The same comparison, across the zone, already flags Gabon's third rate under the directive's 5 % floor and the Congo's disputed centimes for the same reason: a community text sets bounds a national law is free to miss on either side, and this pack records where, rather than silently aligning the two.

A withholding an article names, a base a summary alone cannot confirm. CGI art. 273 bis (loi de finances 2008) has client companies a ministerial order designates withhold 10 % of the price excluding tax — not 10 % of the tax itself, the shape every other VAT withholding in this file takes (Senegal's précompte, Chad's art. 245, Cameroon's art. 149 (2)). Whether that is a loosely worded VAT withholding or, despite its place in the VAT title, closer in effect to an income-tax advance was not settled by the research read for this pack, because neither the arrêté naming the companies nor the article's own text — only a secondary description of it — was found. Rather than guess which mechanism a base described only in summary actually is, packs/cf/ carries none of it, alongside the précompte of art. 166 bis (a three-party, retenue-au-paiement gap this file already names for Senegal and Chad) and the 15 % final withholding of art. 166 bis 1 on fees paid abroad, an income tax rather than a tax on an invoice line. All three wait on a local accountant or a fuller reading of the arrêté, not on a change to the core.

From DR Congo

packs/cd/, community, seed 34, CDF at two decimals — neither UEMOA nor CEMAC, the fourth OHADA member and the first outside either monetary union. Its chart, journals, roles and two statements are the common part of packs/ohada/; what it adds is the value added tax of the Ordonnance-loi n° 10/001 du 20 août 2010, at 16 % and, since 1 January 2023, at a reduced 8 % on a closed tariff list. Two things the format could not say, both already named for other members of the same family, and a third worth recording because it is a country's own administration saying so.

A third party's withholding, again — this time on the seller's own tax, not a buyer's self-assessment. OL 10/001, art. 53, al. 2 (modified by the loi de finances n° 22/071 du 28 décembre 2022, art. 32), has mining companies withhold the VAT due to a state-owned supplier when they pay that supplier's invoice, on the supplier's behalf, and has the Treasury do the same for suppliers of the State. There is no self-assessment here, unlike Chad's art. 229-V: the seller charged the tax, declares it, and a payer named by the law remits it in their place at the moment of payment — the exact three-party shape docs/international.md already names for Senegal's précompte (a buyer withholds a supplier's tax before paying the price) and for Chad's art. 245 (a buyer on a DGI list withholds a supplier's tax and pays it over). DR Congo's version withholds at payment rather than at purchase, which Senegal's does not, so even the buyer-side purchase posting packs/sn/ already found for its own précompte (SN-P-18-PC, on the invoice) does not reach it: nothing here happens when a document is issued, everything happens later, when it is paid. packs/cd/ books neither side of it.

The facture normalisée is a live clearance system, read the closest yet to one. A dispositif électronique fiscal — a physical Unité de Facturation and Module de Contrôle de Facturation, or their dematerialised equivalents e-UF and e-MCF — produces the invoice's own authentication code and QR code as it is issued, connected to the DGI's system at the moment of issuance, with a public verification site and a mobile app of its own. Côte d'Ivoire's FNE and Chad's FEN already showed this shape (a clearance platform, not an EN 16931 profile); DR Congo's decree of 3 March 2023 adds that even the device is administratively distinguished from the document — a company too small to run its own système de facturation d'entreprise (SFE) is handed the DGI's own e-UF application instead of a profile to implement. einvoicing.profile stays empty in the same spirit as the other two, for the same reason: naming EN 16931 or UBL here would misdescribe what the law actually requires.

An administration that says, itself, that its own reform is not ready. A federation of employers (FEC) reported on 17 September 2026 that the DGI and the DGDA, presenting the loi de finances rectificative n° 26/032 du 07 août 2026 at a briefing, said the two new rates it adds — 5 % on cement, 1 % on locally refined oil — were not yet configured in the DGI's own systems (e-DEF, e-MCF, i-impôts), over a month after the law's own effective date. No primary text of that law was found at any official URL tried. A pack that added the two rates from the FEC's account would be citing a federation of employers for a tax its own tax administration was not yet collecting through its own systems — the "jamais une taxe ajoutée pour faire passer un test" rule applies exactly as hard to a rate an administration itself has not switched on as to a rate a test invents. packs/cd/ carries neither rate, and names both in its own README against the day a primary text, or the DGI's own systems, catch up to the law.

From the Congo

packs/cg/, community, seed 27, the fourth OHADA member to carry the CEMAC surtax after Cameroon, Centrafrique and Chad. Its chart, journals, roles and two statements are the common part of packs/ohada/; what it adds is the value added tax of the loi n° 12-97 du 12 mai 1997, an uncodified law amended each year by a loi de finances, read from two unlayered scans (the 1997 law and the loi de finances rectificative pour 2012) checked page by page against the images, and from the loi de finances pour 2026 (a native text PDF). One thing the format already had, applied differently from its CEMAC neighbours; one thing it could not say, not patched.

The same surtax, a different account, because a different fact. Congo facturé at 18,9 % is 18 % (loi n° 12-97, art. 17) plus 5 % of that same amount in centimes additionnels (art. 37, confirmed by the loi de finances pour 2026 at a cumulated rate of 18,9 %) — the shape packs/td/ already predicted for Cameroon and Congo: one tax at 18,9 %, two tax postings of factor 95,238 and 4,762, the second absorbing the first's rounding so the two always sum to exactly what the invoice prints. Where Chad and Cameroon name their own surtax communal — centimes communaux et provinciaux, a Livre de fiscalité locale — and post it to 4422 « Impôts et taxes pour les collectivités publiques », no text read for the Congo names a beneficiary at all: the loi de finances pour 2026 folds the centimes additionnels into a précompte paid to the Trésor Public, never to a named collectivité. packs/cg/ posts them to 446 « État, autres taxes sur le chiffre d'affaires » instead — the same posting shape as its neighbours, a different account because the fact the account names is not established here. A text found later that calls the Congo's centimes communal would move this pack to 4422, as documented in packs/cg/README.md.

A directive that requires what a summary denies, and neither read consolidated. The CEMAC directive (art. 22-1) allows a surtax on VAT only if it is deductible exactly as the tax itself; packs/cg/ follows that principle and posts its purchase-side surtax fully deductible, in one amount, as packs/td/ and packs/cm/ already do. PwC's Worldwide Tax Summaries (a secondary source, last reviewed 07 August 2026) says the opposite — the Congolese surtax is "non-deductible, a definitive cost". No consolidated text of the loi n° 12-97 could be read to settle which is right; packs/cg/README.md carries the contradiction in full and flags it for a local accountant to resolve before the pack leaves community.

From Guinea

packs/gn/, community, seed 29, another of the seventeen OHADA States, outside both the UEMOA and the CEMAC: its VAT is entirely national, read from the Code général des impôts of 2022 and the loi de finances pour 2025 (a text-layerless scan, read back page by page against the images). One thing the format could not say, not patched, and one contrast worth keeping beside it.

A fourth instance of the seller's side of a withholding by the buyer. Article 373 Ter names a client — the State, a local authority, a public or mixed-economy body, a telecom company, an importer, distributor or miner — who withholds 50 % of the VAT a registered seller invoices and pays it to the Treasury directly; the seller still declares the tax in full and deducts the withheld half back under a box the Code itself names, "Déduction de la retenue 50% TVA". This is the exact shape docs/international.md already names for Senegal's précompte, Côte d'Ivoire's TVA pour compte de tiers and Chad's art. 245 and 229-V: a debt that stays the seller's while a buyer remits it, which no posting expresses on the side of the party who never touches the remittance. packs/gn/ carries no tax code for art. 373 Ter and says so in its README.

The same Code's other withholding is the opposite shape, and the format already has it. Articles 251 to 255 make certain of the same buyers withhold 10 % (or 5 % on some commissions) from a payment to a supplier who is not registered for VAT, and remit it as an advance of that supplier's own income tax. Nothing here is three-party: the entity whose books this pack keeps is the one doing the withholding, on its own purchase, exactly as Singapore's SG-P-WHT-INT-15 withholds tax on an interest payment it makes itself. GN-P-WHT-10 and GN-P-WHT-COM-5 post it, treatment: not_subject, to no box of the VAT return — because none of it is VAT. Two withholdings a few articles apart in the same chapter of the same Code, one a buyer can book because it is the buyer's own act, the other nobody can book because it happens to somebody else's tax.

From Equatorial Guinea

packs/gq/, community, seed 31, the fourth CEMAC member after Cameroon, Chad and Gabon. Its chart, journals, roles and two statements are the common part of packs/ohada/; what it adds is the value added tax of the Ley n° 1/2024, General Tributaria, read from a scanned, OCR'd facsimile of the Boletín Oficial del Estado — no site of the administration itself, boe.gob.gq excepted for an unrelated 2012 text, could be reached. Two things the format could not say, neither patched:

A zero rate that keeps the deduction, an exemption that does not. Article 155 gives three tipos — general (15 %), reduced (5 %) and zero — as three rates of one tax, while articles 140 to 142 exempt a separate list outright. Nothing in the read text says the zero rate carries the input-tax deduction an exemption forfeits, but that is what "a rate of the tax" means everywhere else this format has met it (Spain's own ES-S-00-ALIM, at the same zero rate, is vat_category: Z, never E). GQ-S-0-EXP (the export, category G, matching every other OHADA export code already written) and GQ-S-0 (six other zero-rated operations — antimalarials, medical masks and reagents, school books, chemotherapy, dialysis, agropastoral inputs, and a producer's own sale of subsoil products outside hydrocarbons and mining, category Z) sit apart from GQ-S-EXO (category E) on exactly that reading. No core change was needed — the three-way split between a taxed rate, a zero rate and an exemption was already how vat_category works — the pattern is written here because a reader moving from packs/ga/ or packs/td/, whose zero rate is only the export, could otherwise expect Z to be a synonym for E, which it never is.

A retention this pack could name and not model, at the third rate this format has met. Article 167-4 has the State, public bodies and designated private companies withhold VAT from a supplier they pay: 100 % from a supplier classed "at risk" (an individual, a non-resident, someone not registered), 0 % from a large-taxpayer-unit company, 40 % from anyone else — a rate that turns on a classification of the seller the document does not carry, and that no official list this pack's research could find fixes. This is the same three-party shape docs/international.md already names for Senegal's précompte (one rate, a closed list of buyer types) and Côte d'Ivoire's TVA pour compte de tiers, and the same gap Gabon's own CSS retention and précompte de l'État left unmodeled a member earlier: the core has no "retenue au paiement", the ledger effect of a tax a third party withholds and remits on the taxpayer's behalf. Equatorial Guinea adds a third rate tier to a mechanism the core already could not express at one — not a reason to guess at the missing list, so packs/gq/ says what article 167-4 does and books none of it.

From Taiwan

packs/tw/, community, seed 53, the first pack of this repository whose country runs its own government clearance platform underneath every invoice its businesses issue. Its business tax (加值型營業稅) is read from the 加值型及非加值型營業稅法, its balance sheet and income statement from 商業會計處理準則, and its chart of accounts — unlike packs/jp/, packs/hk/ or packs/sg/, each written from scratch because their own countries prescribe none — is transcribed from 商業會計項目表, an official table with a code and an English name for every item. Three things the format could not say, none patched for this pack's sake.

einvoicing was built for a bilateral exchange between a seller's and a buyer's own systems, and Taiwan's electronic uniform invoice (電子發票, eGUI) is not that. profile, obligation and mandatory_from ask which EN 16931 or Peppol semantic model a country's invoices are exchanged under, and whether a statute obliges two trading parties to use one between themselves. Taiwan's own system answers a different question: a seller submits an invoice to 財政部電子發票整合服務平台 (the Ministry of Finance's own Electronic Invoice Integration Service Platform), which is a third party to the transaction and not a format either side chose — the invoice's own number is drawn from a 字軌 (a two-letter "word-rail" prefix) and a range of eight-digit numbers the tax authority itself allocates to the business for each two-month filing period (統一發票使用辦法), the platform authenticates and timestamps the invoice, and every uniform invoice's number is entered into a nationwide lottery for consumers (統一發票) that pays out against the number the state itself assigned. None of profile's example values — peppol-bis-3, factur-x-en16931, xrechnung, a PINT — names a clearance model where the administration is inserted into the invoice's own issuance rather than merely receiving a copy of a document two parties already agreed on between themselves, because none of the packs this field was built against works that way: every VAT or GST jurisdiction in this repository so far either has no electronic-invoicing statute at all or has adopted a Peppol-family exchange profile. packs/tw/ declares obligation: "none" and profile: null, which is the honest answer to the question the field asks and not a statement that Taiwan's own system is any less real or any less mandatory than a country whose pack declares a profile — the opposite is closer to true. A pack for a clearance-model country — closer cousins are Mexico's CFDI and Brazil's NF-e than any Peppol profile — will meet the same gap, and the fix is a country field this repository does not have a second example of yet to design against with any confidence: a clearance shape distinct from profile, naming the government platform an invoice is submitted to rather than the semantic model two parties exchange it under. This pack proposes no schema change and states what it found instead.

A box printed as a rate of one box plus another box, which the schema's own vocabulary was already documented to refuse together. tax_report_box's rate/rate_of names a percentage of exactly one other box, and a plus/minus list adds and subtracts a list of them — the two are declared mutually exclusive by docs/packs.md itself ("A box is a list or a rate, never both, and only a computed box carries either"). Form 401's box 13 (得退稅限額合計, the refund a zero-rated exporter may claim) is printed ③×5%+⑩: a percentage of the zero-rated sales base and the previous total added on, on one line. This is not a new gap — packs/jp/ already met the same shape for its own local-tax share and answered it with a working box the return does not print, hidden: true, carrying the rate alone so the printed box can still be declared as a sum. packs/tw/'s own TWZR5 is the same device applied a second time, which is worth recording here precisely because it is the second time: a rate applied to a box that is itself then added to something else is not a one-off Japanese local-tax artefact, and a third pack meeting it should reach for the same working-box pattern rather than treat it as a japanese peculiarity or ask for a plus/minus-and-rate box the check already refuses by name.

No minimum-of-two-boxes operator, met for the first time. Form 401's box 14 (本期應退稅額) is printed "如12>13則為13,13>12則為12" — the lesser of box 12 and box 13 — and box 15 is computed from box 14 in turn. Every arithmetic tax_report_box can express today is additive: a sum, a difference, a percentage of one box, a floor at zero. None of the returns this repository has carried so far has asked for a comparison between two boxes rather than an addition of them, so the gap was never met before this pack. packs/tw/ leaves boxes 114 and 115 undeclared rather than approximate a minimum with a floor-at-zero difference (plus: ["112"], minus: ["113"] would compute a different figure whenever the refund cap binds, silently, which is exactly the kind of wrong-and-coherent figure docs/packs.md's introduction to the golden scenario warns a country pack against) — a reader compares 112 and 113 by hand. Whether the format should grow a min/max kind of box, on the same closed-vocabulary footing as rate/rate_of, is a question for whoever meets a third country that needs one; one instance is a data point and not yet a pattern.

Two further things, both already documented at the pack rather than novel here, and repeated in this section only because the brief for this pack asks for it: a return whose own printed note excludes a period that carries any exempt sale — form 401 is the return of a taxpayer whose sales are exclusively taxable and zero-rated; a period with an exempt sale is filed on form 403 instead, whose box layout this pack's research could not confirm against an official publication, so packs/tw/'s exempt tax codes carry no box of TW-401 at all — and a prior period's credit that this pack's own return cannot carry forward, the same gap packs/gq/README.md already names for its own box 027, met here on form 401's box 108. Neither is a finding about the core beyond what those two packs already established; both are named here so that a reader of this section does not have to reconstruct them from packs/tw/README.md alone.

From South Korea

packs/kr/, community, seed 53, KRW at zero decimals — the second pack of Asia after Japan, and the first whose VAT the seller does not merely invoice but reports to the tax administration almost as it happens.

전자세금계산서 is a same-day reporting duty to the NTS, and einvoicing cannot name it. Every corporation since 2011, and since 1 July 2024 an individual business above 80,000,000 원 of the prior year's supplies (부가가치세법 제32조, 시행령 제68조), must issue its tax invoice electronically and transmit its particulars to the National Tax Service by the day after issuance, on pain of a graduated penalty (0.3% late, 0.5% not at all). That is not a profile einvoicing.profile can hold: the field names an EN 16931 profile a brick of packages/formats/ actually writes and a Peppol access point actually carries, and South Korea has joined no such network — it runs no Peppol Authority (OpenPeppol's own list, consulted 22 September 2026, does not carry it), so there is no PINT-KR the way there is a JP PINT for Japan or a PINT SG for Singapore. The 전자세금계산서 itself is the NTS's own XML, filed through its own Hometax portal, and nothing in this repository produces it or talks to that portal. packs/kr/ therefore leaves profile, mandatory_from, party_scheme, vat_scheme and obligation all empty and states the duty in full in legal_reference instead — exactly the shape packs/mx/ already gave this problem for the CFDI, and the same one Côte d'Ivoire's FNE and Chad's FEN gave it for a live clearance platform rather than a document standard. Fix: the clearance model packs/mx/'s own README already asks for — obligation: mandatory with a model: clearance (or, here, model: real_time_report) and a certifying or receiving administration named without borrowing the vocabulary EN 16931 built for a four-corner exchange between two businesses.

A reporting duty most individual taxpayers never actually file. A corporation's quarterly return (제48조 예정신고, 제49조 확정신고) is an ordinary filing this pack's tax_report.json reads as period: quarter with no difficulty. Most individual general taxpayers, by contrast, do not file a preliminary return at all: 제48조제3항 has the NTS assess and collect half of the prior period's net tax by a notice of its own (예정고지), with no return behind it, unless the taxpayer's own books would show less. That is a payment on account raised by the administration itself rather than a cadence this pack's company declares — closer to Japan's own interim instalments of 消費税法 art. 42, which packs/jp/ likewise declares and leaves the boxes for empty, than to a second period this format could name. packs/kr/'s chart targets a company (법인사업자), which always files, so the golden year carries none of it; a pack extending this one to an individual taxpayer would meet the same gap Japan's interim returns already named.

From Italy

packs/it/, community, seed 37. A first Southern-European member with no official chart of accounts to transcribe — its accounts.csv is this pack's own numbering, built to read onto the captions of art. 2424 and art. 2425 of the Codice civile, which the README says plainly so a reviewer knows which half of the pack a citation can settle and which half cannot. Three things the format could not say, none patched, all left to the README and to this section instead.

Clearance, again, and confirmed. The Sistema di Interscambio has validated, sealed and delivered every business-to-business and business-to-consumer invoice in Italy since 1 January 2019 (legge 205/2017, art. 1, commi 909-910) — five years before Mexico's CFDI is that shape, in this same document's own words: "Italy's SdI, India's IRN and most of Latin America are the same shape." Nothing here changes that reading; packs/it/ is simply the second pack to meet it, with einvoicing.profile and mandatory_from both null and the obligation itself — fully in force, unlike Spain's not-yet-commenced one — spelled out only in einvoicing.legal_reference and in the README, because no brick of packages/formats/ writes, validates or transmits a FatturaPA envelope today and declaring a profile would claim otherwise. The fix asked for there — obligation: mandatory with a model: clearance and a certifying party, no EN 16931 profile required — is the same fix this pack would need.

A three-party withholding once more, on the buyer side of the state's own purchases. Article 17-ter of D.P.R. 633/1972 has a public administration (and certain other listed bodies) pay a supplier's invoice net of VAT and remit the tax itself, straight to the Treasury — scissione dei pagamenti, split payment. The supplier still shows the VAT on the invoice and still owes nothing to the buyer for it; the debt is the supplier's, the payment is the buyer's, and no posting of this core expresses that split for the party who never touches the remittance. This is the exact shape already named for Senegal's précompte, Côte d'Ivoire's TVA pour compte de tiers, Chad's art. 245 and 229-V, DR Congo's OL 10/001, Guinea's art. 373 Ter and Equatorial Guinea's art. 167-4 — six countries into the same gap before Italy is the seventh. packs/it/ carries no tax code for art. 17-ter and says so in its README, rather than guess at a two-party approximation that would post the VAT to the wrong side of the ledger.

A declaration due two months after the month the deadline rules already reach, and unsafe to approximate where Japan's was not. The annual VAT return is due between 1 February and 30 April of the following year (D.P.R. 322/1998, art. 8, comma 1) — the last day of the second month after the period ends, where deadline.rule only reaches a day, or the last day, of the first. Japan's own two-month gap (see "From Japan" above) is declared anyway, as the last day of the next month plus a fixed number of days, because every one of its months lands on or before the true deadline. The same fix cannot be trusted here: Italy's window spans February, so a constant plus_days computed to land exactly on 30 April in an ordinary year lands on 29 April — a day early, harmlessly — in a leap year only if the number of days added is chosen for the worse case; get the arithmetic one day wrong in the other direction and a leap year's 30 April becomes 1 May, past the law. Rather than ship a rule that is exactly right three years in four and silently late in the fourth, packs/it/tax_report.json declares no deadline at all and says so, in full, in its own legal_reference — which is the one place a missing deadline is not read as "nobody looked", because the text next to it proves someone did. Fix: months_after_period with a day or last, exactly what Japan's entry already asks for, generalised to more than one month and checked against a leap year before it is trusted.

From Portugal

packs/pt/, community, seed 43, inside the common system of VAT. Three things the format could not say, none patched, and one habit the register had to break for the first time.

A tax cannot name the one place it does not apply, and Portugal is the first pack where that costs something concrete. Article 18.º, n.º 3 of the Código do IVA lets the Azores and Madeira set VAT rates of their own — 4/9/16 % and 5-or-4/12/22 %, against the mainland's 6/13/23 % — under the same tax, filed on the same national declaration. applies_when.supply_in correctly restricts the Azorean and Madeiran codes to the territory rows this pack adds, PT-20 and PT-30 (full EU VAT scope, not outside_parent_tax: unlike the Canary Islands, Union VAT reaches both regions exactly as it reaches the mainland — only the rate differs). What it cannot do is restrict the mainland codes to "not the Azores, not Madeira": docs/packs.md's own words for applies_when are "a tax cannot name a place a party is not in", written for a shipment-destination case at the time; here the same limit means three overlapping rate schedules in one country, and post_document() refuses nothing if a document established in Ponta Delgada is posted at the mainland's 23 % instead of the Azores' 16 %. Spain's Canarias, Ceuta and Melilla never met this, because there the parent's tax simply stops, and outside_parent_tax is a refusal, not a choice between two rates that both apply. packs/pt/README.md carries the same sentence.

The deadline vocabulary reaches one month past the period, and this declaration is due in the second. Article 41.º of the Código do IVA, since Decreto-Lei n.º 49/2025 harmonised the monthly and the quarterly regime onto one date, sets the Declaração Periódica do IVA's deadline at the 20th of the second month after the period ends — a February return is due in April, not March. tax_report.json's deadline.rule: day_of_month_after_period names only "the day of the month that follows the period", one month, the shape every other pack that uses it needs (Belgium's and Estonia's twentieth, Luxembourg's fourteenth). There is no second rule for a form due a month later still, and last_day_of_month_after_period does not reach two months either. packs/pt/ declares no deadline at all rather than write a date the vocabulary cannot state and a reader would take for the wrong month; the true rule is in the pack's own legal_reference on tax_report.json's top-level object, for a filing brick to read the day this vocabulary grows a third shape.

Software, a code and a file are not an invoice, and this is the first pack where all three sit beside a none. Portugal has no statute requiring businesses to exchange a structured electronic invoice between themselves — einvoicing.obligation is correctly none, the same word the Spanish and the American packs already use for the same fact. What sits beside that word here, and what did not sit beside Spain's SII or VERI*FACTU with quite this much weight, is three separate obligations at once: a certified invoicing program (Decreto-Lei n.º 28/2019), a per-document ATCUD code and a QR code printed on it (Portaria n.º 195/2020), and a monthly SAF-T (PT) file delivered to the tax administration and never to the customer (Portaria n.º 302/2016). None of the three is einvoicing.profile, because none of them is a document format two businesses trade — they are how one business proves its books to the State, which packages/formats has no brick for at all, Portuguese or otherwise. A company keeping its books on this pack still needs software of its own for all three, and nothing here pretends the pack.json section that comes closest, einvoicing, was built to hold them.

From the United Arab Emirates

packs/ae/, community, seed 60, the first pack of the Gulf. Its value added tax is Federal Decree-Law No. 8 of 2017 and its Executive Regulation (Cabinet Decision No. 52 of 2017), both read consolidated and as amended as recently as September 2026; its electronic invoicing regime is read from the Ministry of Finance's own UAE Electronic Invoicing Guidelines, a 51-page primary document and not a summary. Two things the format could not say, neither patched, and one editorial choice worth naming so a future pack does not repeat the same gap silently.

A reporting model einvoicing was not built for. Every other pack's electronic invoicing is a 4-corner exchange — the seller's access point sends a document to the buyer's — where mandatory_from answers one question well: the day reception starts, "which binds everyone at once" even where issuance phases in later, as packs/de/ already uses it. The UAE's model is 5-corner: the seller's and the buyer's Accredited Service Providers each report the same transaction's Tax Data to the Federal Tax Authority independently, so there is no reception side that binds before an issuance side does — a Person either reports, on both sides of every transaction it is party to, from its own mandatory date, or it does not report at all yet. mandatory_from therefore carries the earliest date anyone is bound (1 January 2027, a Person with AED 50,000,000 or more in revenue), which is the closest available reading and not a description of when the obligation reaches a given company — a reader has to open pack.json's own einvoicing.legal_reference for the actual phased table (three thresholds, three dates, plus a 24-month grace period for transactions between members of one VAT group starting on the earliest date), because the field was not built to hold more than one date at all.

A designated zone is not a territory, and the territories table would be the wrong place for it anyway. Article 51 of the Executive Regulation treats a Cabinet-designated, fenced and Customs-controlled area as outside the State for goods (not services, except in the narrow cases Clauses 6 to 8 carve back in) — the same shape applies_when.supply_in already expresses for a sub-national EU territory such as the Canary Islands. What breaks the analogy is Clause 2 of the same article: a designated zone reverts to being treated as inside the State the moment it changes how it operates or breaches the conditions of Clause 1, by the Cabinet's own administrative finding, with no fixed date and no accession treaty to cite — territories is reference data precisely because Union membership is Union law that changes on a dated, citable event, and a status that can flip on an unannounced compliance finding is not that. packs/ae/ does not model a designated zone at all rather than force it through a table built for a different kind of boundary; the list of zones the Cabinet has actually designated was not verified against a citable decision in this research pass either, so there was nothing yet to model even if the table fit.

Where a clause of the law only asks for a value, and Article 48(4)(a) asks for its tax a paragraph later. Every other pack whose return boxes are read from an administrative form's own numbered boxes — Singapore's GST F5, France's CA3 — finds the tax figure already sitting in its own numbered box beside the value. The UAE has no such form this research pass could open (see packs/ae/README.md), so tax_report.json is built letter by letter on Article 64(5) of the Executive Regulation, which lists the value of a reverse-charge supply (paragraph g) as one of the minimum contents of a return, and says nothing in that same paragraph about the tax due on it — which paragraph (i) then needs, as part of "the total value of Due Tax", to balance at all. packs/ae/tax_report.json's box g2 is the pack's own addition to hold that figure, named as such in its own legal_reference rather than presented as a transcription of the article. This is not a gap in the format — tax_report_box already allows a box with no administrative number behind it — but the case is worth naming for the next pack built from a statute's minimum-content clause rather than from a form: a value without its tax is not a value a return can be filed on, and the missing box has to be written in, in the open, rather than left out because the law did not spell it out twice.

From Thailand

packs/th/, community, seed 53, the first pack of mainland Southeast Asia. Value added tax under the Revenue Code, Title IV Chapter 4 — sections 80, 80/1, 81, 81/1, 83 and 86/4 read directly this session from the Revenue Department's own English translation; the rest from its summary pages, which name what a form covers rather than its printed wording. Two things the format could not say, and one habit of the source material worth naming for whoever writes the next pack of the region.

A statute the tools here could not read at all: the Royal Decree that actually sets the rate. Section 80 fixes value added tax at 10 %; the 7 % every business in fact charges is a reduction a Royal Decree grants and a new decree keeps renewing, apparently without a gap, since 1997. Thailand publishes these decrees as scanned PDFs of the Royal Gazette with Thai text in embedded fonts, and both a WebFetch of the decree currently said to be in force (No. 799, B.E. 2568) and of the one said to take over on 1 October 2026 (No. 807) came back as unreadable binary streams — twice, from two independent attempts. TH-S-STD and TH-P-STD therefore state the 7 % rate and cite the chain of decrees by number and date as a secondary source reported it, with the reservation spelled out in their own legal_reference: neither the decree number, its date, nor the day the current one's own predecessor first set the rate running from could be read from a primary text this session opened itself. This is not the Légifrance 403 or the SSO script-only page this file already names for other countries — Thailand's site serves the file — it is a PDF whose text layer this session's tools could not extract. A reviewer with a PDF reader open on https://www.rd.go.th/fileadmin/user_upload/kormor/newlaw/dc799.pdf settles it in under a minute; nobody here could.

The 0.7 % local tax has no posting of its own, on purpose. The 7 % combined rate is, by the same secondary account, 6.3 % national value added tax plus a 0.7 % local tax the Revenue Department allocates to local government after collecting the whole amount from the taxpayer. Unlike Japan, where packs/jp/ posts the national and the local consumption tax as two shares of one rate because the return itself asks for both, nothing read here shows form VAT 30 asking a Thai company to split the two: the taxpayer pays and declares 7 % once. So TH-S-STD is one rate and one posting, and the split is named in its legal_reference rather than modelled — the opposite choice from Japan's, for what this session read as the opposite fact about the form.

A country pack cannot fall back to packs/generic/'s own lines, only to its shape. The docs read at the start of this pack's work say a chart that declares no statements "falls back to packs/generic/", and packs/be/'s asbl chart does exactly that. What is not written down, and cost real time to find by reading packages/cli/src/pack/read.ts, is that the coverage check run on a country's own chart (readPack, "every account... reaches a line of some statement of that chart") calls ruleCatches(), which switches on account_code, code_prefix and code_range and returns false for anything else — account_type included. packs/generic/'s own account_type rules are validated and consumed by a different function, readFrameworkPack(), written for a pack with no chart and no company ever installed on it. A country pack's own statements.json — the golden scenario's chart runs through readPack, not readFrameworkPack — has to use code_range, code_prefix or account_code like every sibling pack already does; account_type there is silently refused by exactly the check meant to catch an account reaching nothing, which is what packs/th/'s first attempt at reusing packs/generic/'s own lines ran into, seventy-seven times over, before the two functions were told apart. packs/th/statements.json groups this chart's own code ranges instead, and this paragraph is here so the next community contributor who reads "falls back to packs/generic/" and reaches for account_type finds the correction before losing the same hour.

What this pack does not carry, named rather than guessed at:

  • Section 82/5, the list of input tax the law excludes from credit (entertainment expenses and passenger cars, by reputation and not by a text read this session) — TH-P-STD assumes every standard-rated purchase is fully creditable.
  • The two-form timing of section 83/6. A service bought from abroad is self-assessed on form VAT 36 within seven days of payment and credited on form VAT 30 of that month or the next; TH-P-RC books the self-assessment and the credit on the same document, which is faster than the law allows.
  • Specific Business Tax, the Revenue Code's separate regime for banking, finance, life insurance, pawnbroking and commercial real estate (sections 91/2 and 91/6): a business subject to it is outside value added tax for that activity, files a different return (ภธ.40) at different rates, and none of it is in this pack.
  • The e-Tax Invoice & e-Receipt system. Whether it is mandatory for any class of taxpayer, and from what date, could not be read from etax.rd.go.th, which renders through client-side script the tools available here could not execute; einvoicing.obligation is none, which states only that no statute was found requiring it.
  • Sections 78 and 78/1, the time of supply, read only through secondary guidance after two direct attempts at the Revenue Code itself returned a server error; tax_point is the closest of five closed values to what that guidance says, and names the gap in its own legal_reference.
  • A Thai financial reporting standard's own line items. The Federation of Accounting Professions' standard for entities that are not publicly accountable could not be opened this session; TH-BS and TH-IS group this chart's own accounts by their code ranges instead of transcribing that standard's paragraphs, the way packs/sg/'s statements do transcribe SFRS for Small Entities.

Vietnam

packs/vn/, community, seed 53. Written from published sources alone on 22 September 2026: a 34-account chart cut onto the account classes of Thông tư 99/2025/TT-BTC (in force 1 January 2026, replacing Thông tư 200/2014/TT-BTC), ten taxes — value added tax at 0 %, 5 % and 10 %, the temporary 8 % rate of Nghị quyết 204/2025/QH15 running from 1 July 2025 to 31 December 2026, exports, Điều 5's twenty-eight non-taxable categories, and import VAT paid to customs — the deduction-method return Mẫu số 01/GTGT, the balance sheet and income statement of Thông tư 99/2025/TT-BTC, and a register of thirteen texts. The pack's own README says where each rule comes from and which points a Vietnamese accountant should read first — two of them because a text this pack needed could not be read in full even from a mirror, not because nobody looked.

From Vietnam

No word for "delivery or invoice, whichever is first." Luật Thuế giá trị gia tăng số 48/2024/QH15, Điều 8, khoản 1 fixes the tax point at the transfer of ownership (or the completion of a service) or the date the invoice is issued, whichever comes first — not whichever the invoice or a payment displaces, and not delivery against a payment. tax_point has invoice_date, delivery_date, payment_date, invoice_if_issued (a supply displaced by an invoice a country requires) and earliest_of_delivery_or_payment (Estonia's KMS § 11: delivery against payment), and none of the five is "delivery against the invoice's own date." packs/vn/ declares the closest of the five, earliest_of_delivery_or_payment, and says in its own legal_reference that the word it needs does not exist. Fix: a sixth value, earliest_of_delivery_or_invoice, which Vietnam is not likely to be the last pack to ask for — a business that invoices ahead of delivery is not a Vietnamese peculiarity.

A deadline that depends on the cadence, not only on the taxpayer. Luật Quản lý thuế số 38/2019/QH14, Điều 44, khoản 1 gives two different rules in the same paragraph: the 20th of the next month for a monthly filer (point a), the last day of the first month of the next quarter for a quarterly one (point b) — and which cadence a given company files on is itself a fact the core does not know in advance (Nghị định 126/2020/NĐ-CP ties it to the turnover of the previous year). tax_report.json carries one deadline block for a form filed on several cadences, which is right where one rule covers every cadence (Japan's "within two months," read literally, does) and wrong here, where the rule itself changes with the cadence. packs/vn/ declares the monthly rule and says so; a quarterly filer's return is due earlier, by the pack's own reading, than what filing_deadline() would answer. Fix: deadline keyed by cadence, the same shape period already takes as a list — Australia's BAS, due on different days for a monthly and a quarterly filer, would read it too.

A third-party withholding this pack chose not to guess at, not one the core cannot express. Thuế nhà thầu nước ngoài — a Vietnamese payer withholding and remitting VAT (and corporate income tax) owed by a foreign contractor with no permanent establishment — is the same shape already named here for Mexico's retención on a persona moral's payments and for DR Congo's mining-company withholding on a state-owned supplier: a purchase tax with a posting to a payable account, nothing the format lacks a word for. What stopped packs/vn/ was not the format: Thông tư 103/2014/TT-BTC, the text that has governed it since 2014, appears from several convergent professional sources to lose effect on 1 July 2026, and this pack's research found two different, unconfirmed circular numbers offered as its successor by secondary sources, neither read on an official page. A withholding coded against a citation that might already be wrong is worse than none, so packs/vn/ carries no foreign-contractor tax and says why in its own README rather than in a rule of this section.

VND is added to 00_currencies.sql at no decimals, the dong having no minor unit in use. VN is added to 00_territories.sql, outside the common system of VAT, so vat_category, exemption_code and the five intracom_* treatments are read the way every non-EU pack's are.

From Switzerland

packs/ch/, community, seed 37, the first pack of a country outside the common system of VAT whose own tax nonetheless looks the closest to it of any non-EU pack so far — three rates, an acquisition tax on services bought from abroad, an export exemption with full input deduction. Two things the format could not say, neither patched.

A deadline counted in days from the end of the period, where the closed vocabulary only counts a day of a following month. Art. 71 and Art. 86 MWSTG both give the same rule in the same words: the return is filed and the tax is paid "dans les 60 jours qui suivent l'expiration de la période de décompte" — sixty calendar days after the period ends, not the twentieth of the month that follows it the way Belgium's and Estonia's deadlines work, nor the last day of that month the way California's and the United Kingdom's do. tax_report.json's deadline object is one of exactly three shapes, and all three are anchored on "the month that follows the period" — day picks a day inside it, last_day_of_month_after_period is its last one, and plus_days only extends either from there. None of the three can add sixty days to a period end that is itself the last day of a quarter without silently changing which month the count lands in as the quarters' lengths differ (31, 30, 31, 31 days to the next quarter's start, before the sixty are even added). Declaring last_day_of_month_after_period would print a date up to several days wrong depending on the period; declaring nothing said only that nobody had read the text, which was no longer true. packs/ch/ declares no deadline and says why here rather than guess at a fourth shape this pack cannot itself introduce — the two articles are read in full in packs/ch/README.md, for whoever adds a day-count rule to the vocabulary next.

A structured payment reference the invoice itself carries, with no field of the document for it. Since 30 September 2022 (1 October in the SIX Group's own wording) every Swiss franc or euro payment slip issued in Switzerland is a "QR-facture": a Swiss QR Code encoding, among other things, a structured creditor reference the payer's bank matches the payment to automatically, replacing the red and orange payment slips (BVR/ESR) it succeeds. It is not a statute — no law compels it, SIX Group's own interbank standard does, by every Swiss bank and PostFinance having stopped accepting the old slips on that date — which is itself the reason it sits here rather than in a tax code: this pack has nothing to cite legal_reference against. What it needs from the core is a field the format does not have on documents: a structured reference distinct from the invoice number, printed on the document and read back off an incoming bank statement to reconcile a payment automatically. bank.payment_formats and bank.statement_formats already carry pain.001 and camt.053, which is the pipe the QR-bill's own data travels over end to end — the gap is a field on the document, not a new bank format, and no pack of this repository has needed one before. packs/ch/README.md names it for the same reason the deadline is named here: a country reading the pack should find the limitation before they find it themselves in production.

From Austria

packs/at/, community, seed 53. Two things the format could not say for this pack, and one that is missing for a reason worth telling apart from the other two — a source that was never opened rather than a shape the format cannot hold.

A declaration deadline of "the fifteenth day of the second month after" has no rule to be. UStG § 21 Abs. 1 sets the Umsatzsteuervoranmeldung due "bis zum 15. Tag des... zweitfolgenden Kalendermonats" — the fifteenth of the month that comes after the month immediately following the period, not of that immediate month itself. tax_report.json.deadline has three rules: day_of_month_after_period and last_day_of_month_after_period both reach one month past the period's end, and depends_on_taxpayer is for a schedule the administration assigns per filer, which this is not — every Austrian company faces the same fifteenth-of-the-second-month date. None of the three says "two months, then a day", so packs/at/ declares no deadline at all rather than write day_of_month_after_period, day: 15, which would file the return a month early on every single period and be a wrong answer with the shape of a right one. docs/packs.md gives a missing deadline exactly one meaning — nobody has read the text yet — which is not quite this case either; the gap is recorded here, in the pack's own README, and in this section together so a reader of any one of the three finds the other two.

A periodic return that reports a reverse charge's tax with no base at all. Every European pack this format has met so far gives a domestic or intra-Union reverse charge two Kennzahlen — a base posting on one box, a tax posting on the neighbouring one — because the form prints both. Form U30 does not, on the purchase side: Kennzahl 057 (§ 19 Abs. 1 zweiter Satz, a service from abroad) and Kennzahl 048 (§ 19 Abs. 1a, a construction service) each carry only the tax the buyer self-assesses, with no Kennzahl for the amount it was computed on anywhere in the form. The format already allows a base posting with no box — it is how a non-deductible tax's base stays on the line's own account without a figure to print — so AT-P-EUDL-20, AT-P-AUSL-20 and AT-P-BAU-20 use exactly that shape for a different reason: not because the base has nowhere recoverable to be, but because the form itself never asks for it. Nothing in the core changed; the pattern is named here because a reader moving from packs/de/, where the same reverse charges carry a base Kennzahl each, could otherwise read a missing box as an omission rather than as what the Austrian form actually does.

Two domestic reverse charges the form carries and this pack does not book, for the plainer reason that no source read while writing this pack gave a citable text for either: Sicherungseigentum, Vorbehaltseigentum and a sale in enforced auction of land (§ 19 Abs. 1b UStG, Kennzahlen 044 and 087), and scrap, waste metal, game consoles, laptops, tablets, and gas or electricity certificates (§ 19 Abs. 1d, Kennzahlen 032 and 089). Both are named in the U30 instructions this pack's certification.sources already cites, so tax_report.json declares their Kennzahlen; what is missing is the tax code that would post to them, which is a text nobody working on this pack read closely enough to write down rather than a shape the format cannot hold. packs/at/README.md says the same under "Before this pack is reviewed".

From Poland

packs/pl/, community, seed 53, inside the common system of VAT since 1 May 2004. Its VAT rates, its declaration and its balance sheet are read from the consolidated ustawa o podatku od towarów i usług and the ustawa o rachunkowości; two things the format could not say, and one decision worth keeping beside them.

A national e-invoicing system built on clearance, not on exchange between peers. einvoicing.profile and the rest of that section assume a structured invoice moves directly between the seller's and the buyer's own systems, in a format built on the semantic model of EN 16931 — Peppol, Factur-X, XRechnung, a PINT. Poland's Krajowy System e-Faktur (KSeF) is a different shape: a faktura ustrukturyzowana is deemed issued the moment it is sent to the administration's own system (art. 106na ust. 1 ustawy o VAT) and deemed received only once that system — not the seller — assigns it a KSeF number (art. 106na ust. 3). The seller's own numbering, which documents.number_format exists to describe, is not the number the buyer ever sees stamped on the exchange; a second, administration-issued identifier sits on top of it, and the format has no field for one. Four degraded modes the statute names (a KSeF outage, a total outage, the "offline24" procedure, and the system being unreachable) each carry their own consequence for whether a correction is later owed, which compounds the same gap rather than opening a new one. packs/pl/ names a profile (ksef-fa3) only so that obligation and mandatory_from can be read at all, and says in its own legal_reference that the value names no interoperable EN 16931 profile — it is the closest word available for "a national clearance schema," not a claim that KSeF is one of the network profiles this field was written for.

A mandatory means of payment that a per-line tax cannot trigger. The split payment mechanism (mechanizm podzielonej płatności, art. 108a ust. 1a) turns on a fact no single line of an invoice carries by itself: the invoice's total, gross of every line, exceeding 15 000 zł, and at least one line naming a good or service of załącznik nr 15 — a list of a hundred and fifty PKWiU positions from coal to scrap metal to processor chips. A tax's own applies_when and the closed vocabulary of a document mention both reason about one line or one invoice-wide category (reverse_charge, export, small_business...), never about a threshold computed over the whole document and a lookup into an external list of goods. packs/pl/ states the rule in its README and leaves the mandatory "mechanizm podzielonej płatności" wording unposted rather than approximating the condition on a tax that cannot see the invoice total or the annex.

A correction that is neither a credit note nor a company's choice. The Belgian, French and German packs already model VAT that a seller books and declares as usual; the ulga na złe długi of art. 89a-89b is a different mechanism again from the three-party withholdings docs/international.md already tracks above — here every party is the same two, but the correction runs on a clock the parties do not control. A seller unpaid ninety days past the invoice's due date must reduce output VAT already declared (art. 89a ust. 1), and the buyer must mirror that reduction on their own input VAT (art. 89b ust. 1) whether or not either party does anything about it; if the debt is later paid, both corrections reverse. Nothing in the core ties a declaration box to the mere passage of time since a due date, so packs/pl/ declares the four boxes the mechanism owns (P_46, P_47, seller-side P_68, P_69) and posts none of them.

From Saudi Arabia

packs/sa/, community, seed 71, the second pack of the Gulf after packs/ae/. Its value added tax is the Law of Royal Decree No. M/113 and its Implementing Regulations, read in ZATCA's English eighth edition with the Arabic tenth edition of April 2025 beside it; its electronic invoicing regime is FATOORA, read from the E-invoicing Regulation, the Governor's Decision No. (62738) and the Electronic Invoice XML Implementation Standard themselves. Sixteen texts in the register. What follows is the decision this pack was asked to take about a shared Gulf folder, and six things the core could not say, none of them patched.

No packs/gcc/, and the evidence for it

The six States of the Gulf Cooperation Council signed a Common VAT Agreement in November 2016 and four of them have enacted a VAT under it, so the question packs/ohada/ answers for seventeen West and Central African States — write the common part once, copy it into every member, let the CI refuse a copy that has drifted — is a fair one to ask here. The answer is no, and it is no for a structural reason and not a doubtful one: what packs/ohada/ copies is a chart of accounts and two financial statements, and the Gulf has neither.

scripts/ohada-packs.mjs writes four things into each member: accounts.csv (the 1 358 accounts of the SYSCOHADA révisé), statements.json (the balance sheet and income statement of the Système normal), the part of pack.json that belongs to the chart — charts, journals, defaults.roles, closing_style, rounding_method, fiscal_year_default — and the two register entries every member cites. A packs/gcc/ built on that model would write nothing into its members:

  • No chart. The Common VAT Agreement has seventy-eight articles and not one of them is about accounting; its only bookkeeping provision is Article 59, on how long records are kept. Saudi Arabia applies IFRS as endorsed by SOCPA and prescribes no chart; the United Arab Emirates prescribes none either, as packs/ae/ records. There is no Gulf chart to write once.
  • No statements. For the same reason: each State's entities present under IFRS, and packs/ae/ and packs/sa/ each build their own statement of financial position and income statement from the IFRS for SMEs line items. Two packs that reached the same shape independently are not two packs sharing a source.
  • No rate. Article 25(1) of the Agreement reads "Tax shall be applied at the standard rate of 5%", and the Kingdom applies 15 % under its own Article 2(2), Bahrain 10 %. Whatever the right reading of that divergence, it is not a figure a common folder could hold.
  • No invoice. Article 56(1) hands the contents of a tax invoice to each State: "Each Member State must determine the contents of the Tax Invoice and the period within which it must be issued". Article 55(4) says the same of its form, paper or electronic. The mentions cannot be shared because the Agreement says they are not shared.
  • And, decisively, no intra-GCC supply to model. The Agreement's reverse-charge mechanism between Member States (Article 9(1)), its clearing of revenue between them (Articles 13 and 64) and the Electronic Services System the Secretariat General was to run (Article 71) are not in force. Article 79(6) of the Kingdom's Implementing Regulations: "any Member State which has not introduced VAT, or which does not have an Electronic Services System in place with the Kingdom shall be considered a country outside the Council Territory ... A Person who is a resident in such Member State shall be treated as a resident of a third country outside Council Territory." Article 79(7) turns a supply to another Member State into an export and a receipt from one into an import; Article 79(8) leaves the starting day to an order of the Authority that this pack's research found no trace of. The Federal Tax Authority says the same from the other side, in Public Clarification VATP019: "currently, the UAE does not recognise any other state as an 'Implementing State' for the purposes of VAT." Six countries that treat each other as third countries have no common operation to share.

What the Agreement really does give the six is vocabulary and structure — the definitions of Article 1, the place-of-supply rules of Articles 10 to 21, the tax due date of Article 23, the registration thresholds of Articles 50 and 51, the obligatory zero rating of exports, international transport and investment metals, the exemption of financial services. That is a great deal, and every bit of it is already expressed in this repository's own vocabulary: treatment, tax_point, the boxes of a return. It is the shape of the data model, not data. A packs/gcc/ holding it would be a folder of one register entry — the Agreement itself — which every member can and does cite from its own certification.sources, as packs/sa/ does under the key vat-agreement.

How the four still to come will plug in, then: each writes its own packs/<cc>/, cites the Agreement in its own register beside its own law, and shares nothing mechanically. Bahrain (10 % since 1 January 2022) and Oman (5 % since 2021) have a VAT of their own to transcribe; Qatar and Kuwait had none on 22 September 2026, and a VAT pack for either would be a pack of a law that does not exist. A member joining packs/ae/ or packs/sa/ to a shared folder would also have to edit a pack it does not own, which is the thing packs/ohada/ was careful to make unnecessary by listing its seventeen members before any of them existed — and the Gulf cannot do that, because two of the six may never be members at all.

A clearance regime still has no word, and this is the fourth pack to say so

packs/mx/ (CFDI), packs/vn/ (hóa đơn có mã) and packs/kr/ (전자세금계산서) each leave einvoicing.profile, mandatory_from and obligation empty although electronic invoicing is obligatory in all three, because profile names an EN 16931 profile a brick of packages/formats/ actually writes and a national clearance format is not one. packs/sa/ is the fourth, and FATOORA is the strongest case yet: an invoice ZATCA has not stamped may not be given to the buyer at all (Governor's Decision No. 62738, Clause Second), and a simplified invoice must reach the Authority within 24 hours of being generated. The three fields stand or fall together — the schema refuses mandatory without mandatory_from, and mandatory_from without a profile — so a pack in this position cannot say the one true thing it knows: that the obligation exists, since when, and that Ekwo does not discharge it. Fix: either a value of obligation that does not drag a profile behind it, or a field beside profile for the exchange model — clearance, reporting, four_corner, five_corner — which packs/ae/'s 5-corner DCTCE would read too. Four packs and a fifth already named is no longer one country's oddity.

A partial translation, which the format allows and the tests refuse

docs/packs.md says of a translation file the manifest does not declare that it "may be partial, which is how a language is contributed one section at a time", and ekwo pack check accepts one: it holds a file to completeness only where languages declares it. The compiler then writes that file's labels into the seed like any other, and tests/languages.test.ts refuses a seed carrying a language the manifest does not declare — "a label under a language nobody declared is a label nobody maintains, and ekwo init would offer it." Each rule is right on its own and together they leave no room: a partial file fails the tests, and declaring the language demands every key.

Saudi Arabia is the first pack to stand in that gap, and it stands in it with a source in hand. ZATCA publishes the Arabic of every VAT category and of every VATEX-SA exemption reason beside its English, in the same table of the same standard; the Kingdom's law is Arabic and its English is explicitly the translation. So ten Saudi tax labels have an official Arabic wording that this repository cannot carry as data — while the 115 accounts of a chart no Saudi authority publishes, the fields of a return whose own labels could not be read as text, and two invoice mentions this pack wrote itself have none, and inventing them would be worse than the gap: Article 53(5) of the Implementing Regulations requires the details it lists to be printed in Arabic, so an invented Arabic mention would be this pack's translation of this pack's sentence, presented as the wording a Saudi invoice must carry. packs/sa/i18n/README.md therefore carries the ten sourced labels as a table and no ar.json. Fix: either the compiler skips a language the manifest does not declare, which is what ekwo pack check already assumes of it, or languages grows a way to declare a language as partial — the second is more honest, because a reader of ekwo pack describe would then be told that Arabic exists and how far it goes, instead of being told nothing at all.

The Kingdom files an export under Z, and the core knows only G

TREATMENT_CODES in packages/cli/src/pack/vat-codes.ts allows exactly one category on an export: G, free export item, VAT not charged, on the authority of UNCL5305 and of the European Commission's technical guidance. ZATCA publishes its own subset of that same UN/CEFACT list in the Electronic Invoice XML Implementation Standard, section 11.2.4, and files both the export of goods (VATEX-SA-32) and the export of services (VATEX-SA-33) under Z, zero rated goods — with G absent from the Saudi subset entirely. Both readings are defensible from UNCL5305; only one of them is what a Saudi invoice carries. packs/sa/ leaves vat_category null on its two export taxes rather than declare a category its own administration contradicts, which it may because the requirement is lifted outside the common system. Fix: the table is the Union's answer, stated as universal. Where a country's own tax administration publishes a UNCL5305 subset, that subset is the one its invoices are written against, and the check has no way to be told so.

VATEX-SA-* is refused by its prefix, by the very check built to allow it

ST38-1 provided for exactly this case: a pack outside the Union may carry a reason code from another published list, "on the condition that its register declares a published list — the entry of certification.sources carrying reason_codes". ZATCA publishes such a list, in the same standard and the same table, in Arabic and English: VATEX-SA-29 for financial services, VATEX-SA-30 for real estate, VATEX-SA-32 and -33 for exports, VATEX-SA-34-1 to -34-5 for transport, VATEX-SA-35, VATEX-SA-36, VATEX-SA-EDU, VATEX-SA-HEA, VATEX-SA-MLTRY, VATEX-SA-OOS. But reasonOutside() tests reason.startsWith('VATEX-') before it looks at the declared list, and refuses every one of them as "a code of the VATEX list, whose codes name articles of Directive 2006/112/EC". The Kingdom happens to have named its codes in the shape the Union uses for a Member State's national codes — VATEX-<country>-<article> — and the check reads the shape as provenance. packs/sa/ therefore carries no reason code at all and states each article in legal_reference, which is what the message asks for, and its register does not declare reason_codes since nothing would use it. Fix: read the declared list first; a register entry that says "these codes are mine" is a stronger signal than a prefix. The alternative — asking ZATCA to rename its codes — is not one.

A mention for "any line not at the basic rate"

Article 53(5)(k) of the Implementing Regulations requires, on every invoice where tax is not charged at the basic rate, "a narration explaining the Tax treatment applied to the supply" — one sentence covering zero-rated, exempt and out-of-scope lines alike. applies_when is a closed vocabulary of nine values resolved from the treatment of a line's tax, and it has exempt and export as separate values and nothing that means "not at the standard rate". packs/sa/ declares the mention on exempt, and a zero-rated domestic line — international transport, qualifying medicines, investment metals — carries no mention although the article asks for one. Fix: a tenth value, zero_rated, or one meaning "any relieved line", which more than one country outside the Union will want: the shape "say why, whenever it is not the normal rate" is common wherever the invoice, rather than a code list, carries the explanation.

A box with an adjustment column

Field 9 of the Saudi return reports the value of a reverse-charge import or service, and its tax column carries the tax net of the deduction of it: the portal treats the input tax as deductible on the supply, so a taxable person deducting in full shows nothing there. A partly-deducting one enters the non-deductible share of the value in a third column of the same field — the adjustment — and the form computes the tax on that share alone; ZATCA's guideline works the case through for a bank deducting 70 %. packs/sa/ reproduces the ordinary case exactly, with two tax postings that cancel in the box and stand in the ledger, and cannot reproduce the other one: a box has a base and a tax and no third figure, and proportional deduction (Articles 51 and 52) is not something this format holds either. The pack says so at the box. No fix is proposed here, because the missing piece is the deduction rule and not the column: a country's proportional deduction is a computation over a year of supplies, and tax_report.json describes a form.

One already-named gap this pack met again

The three-way tax point. Article 23(1) of the Common VAT Agreement, which the VAT Law applies directly, makes tax due on the earliest of the supply, the invoice and the payment. tax_point has no value for three triggers, as From Vietnam above records for Điều 8 of Luật số 48/2024/QH15; packs/sa/documents.tax_point declares the nearest of the five, earliest_of_delivery_or_payment, and says so. The Gulf makes the case broader than one country: the same sentence binds every State that enacts the Agreement, so four packs will declare the same approximation.

SAR and SA

SAR is added to 00_currencies.sql at two decimals. SA is added to 00_territories.sql outside the common system of VAT, so vat_category, exemption_code and the five intracom_* treatments are read the way every non-EU pack's are. vat_prefix is null: a Saudi VAT registration number is fifteen digits whose first and last are 3 (Electronic Invoice XML Implementation Standard, rules BR-KSA-39 and BR-KSA-40), and carries no country prefix of that table's kind. The Kingdom has no ISO 6523 identifier either — it is absent from the Peppol participant identifier scheme list v9.7, which carries AE and OM.

Rendered from docs/international.md, in the repository, when the site was built. Read or change it on GitHub