Skip to content

Serve addon definitions from the registry, not embedded data - #58

Merged
samlown merged 1 commit into
bump-gobl-v0.507.0from
fix-external-addon-data
Sep 17, 2026
Merged

samlown merged 1 commit into
bump-gobl-v0.507.0from
fix-external-addon-data

Conversation

@samlown

@samlown samlown commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Fixes GET /v0/addons/{key} returning 404 for every addon implemented in its own module — e.g. https://gobl.dev/v0/addons/mx-cfdi-v4.

The problem

handleAddon read addons/<key>.json from core GOBL's embedded data directory. Addons that moved out of core into their own modules (gobl.mx.cfdi, gobl.it.sdi, …) register themselves via init() when the bundle blank-imports them, but they ship no file in that directory — so only addons still compiled into core resolved.

The list endpoint reads the in-memory registry, so the two disagreed: /v0/addons advertised 25 addons, 12 of which could not be fetched.

before after
/v0/addons lists 25 25
/v0/addons/{key} resolves 13 25

404 before this change: mx-cfdi-v4, it-sdi-v1, pt-saft-v1, sa-zatca-v1, br-nfe-v4, br-nfse-v1, dk-oioubl-v2, fi-finvoice-v3, fr-ctc-flow2-v1, fr-ctc-flow6-v1, fr-ctc-flow10-v1, pl-favat-v3.

The fix

Render the definition from the registry via a new ops.AddonData helper shared by the API and the MCP server. It mirrors what core GOBL's addons/generate.go does to produce those files, so output for addons still in core is byte-for-byte identical — verified against all 13 embedded files. No change for existing consumers.

The same embedded read was duplicated in two more places, both fixed:

  • the MCP addon tool
  • the gobl://addons/{key} resource

and the gobl://addons resource listed the embedded directory rather than the registry, so it reported 13 keys where the addon_list tool reported 25. It now uses the registry too.

A side benefit: the handler no longer touches the filesystem at all, so the previous path.Join on user input is gone.

Tests

The tests assert the invariant that actually broke — everything the registry reports must be retrievable — so an addon moving out of core cannot regress this silently again. Coverage spans the API endpoint, both MCP paths, and the helper.

Crucially, the test packages now blank-import the bundle as the real binaries do. Without it no externally-implemented addon is registered in the test binary and the gap is invisible — which is exactly how this shipped: the existing tests only ever checked es-verifactu-v1, a core addon. Reverting the fix with the new tests in place fails on precisely the 12 affected addons.

One deliberate behaviour change

/v0/addons/pl-favat-v2 no longer resolves. It was a stale file left in core GOBL 0.505's data directory for an addon that is not registered, so it was never listed by /v0/addons and could not be used in a document's $addons. It is absent from core GOBL 0.507 entirely, so it 404s there regardless.

Notes

  • Stacked on Bump GOBL to v0.507.0 and refresh addon modules #57 so both can be released together; merge that one first. The bug itself predates the upgrade and the two touch disjoint files, so the order is a release convenience rather than a dependency. Worth noting Bump GOBL to v0.507.0 and refresh addon modules #57 makes pl-favat-v3 newly affected, since 0.507 drops its data file from core.
  • /v0/regimes/{code} is unaffected: all 32 registered regimes still live in core and resolve correctly. I checked.
  • The OpenAPI spec needs no change — it types key as a free-form string and only uses es-verifactu-v1 as an example, so behaviour now matches what the spec already promised.

🤖 Generated with Claude Code

@codecov-commenter

codecov-commenter commented Sep 16, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.95652% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.56%. Comparing base (64e2f44) to head (e2e16c7).

Files with missing lines Patch % Lines
internal/ops/addons.go 86.66% 1 Missing and 1 partial ⚠️
api/addons.go 80.00% 1 Missing ⚠️
Additional details and impacted files
@@                  Coverage Diff                   @@
##           bump-gobl-v0.507.0      #58      +/-   ##
======================================================
- Coverage               72.58%   72.56%   -0.02%     
======================================================
  Files                      47       48       +1     
  Lines                    1988     1994       +6     
======================================================
+ Hits                     1443     1447       +4     
- Misses                    465      467       +2     
  Partials                   80       80              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

GET /v0/addons/{key} returned 404 for every addon implemented in its own
module — mx-cfdi-v4, it-sdi-v1, pt-saft-v1, sa-zatca-v1, br-nfe-v4,
br-nfse-v1, dk-oioubl-v2, fi-finvoice-v3 and the three fr-ctc flows.

The handler read addons/<key>.json from core GOBL's embedded data
directory. Addons that moved out to their own modules register themselves
via init() when the bundle blank-imports them, but ship no file in that
directory, so only the addons still compiled into core resolved. The list
endpoint meanwhile reads the in-memory registry, so /v0/addons advertised
25 addons of which 12 could not be fetched.

Render the definition from the registry instead, via a new
ops.AddonData helper shared by the API and MCP server. This mirrors what
core GOBL's addons/generate.go does to produce those files, so output for
the addons still in core is byte-for-byte identical.

The same read was duplicated in the MCP addon tool and the
gobl://addons/{key} resource, and the gobl://addons resource listed the
embedded directory rather than the registry; all now go through the
helper.

Tests assert the invariant that broke — everything the registry reports
must be retrievable — so an addon moving out of core cannot regress this
again. They cover the API endpoint, both MCP paths, and the helper, and
the test packages now blank-import the bundle as the real binaries do;
without it no externally-implemented addon is registered and the gap is
invisible, which is how this shipped.

One deliberate behaviour change: /v0/addons/pl-favat-v2 no longer
resolves. It was a stale file left in core GOBL 0.505's data directory
for an addon that is not registered, so it was never listed by /v0/addons
and could not be used in a document's $addons. It is absent from core
GOBL 0.507 entirely.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@samlown
samlown force-pushed the fix-external-addon-data branch from b87d5ea to e2e16c7 Compare September 17, 2026 11:25
@samlown
samlown changed the base branch from main to bump-gobl-v0.507.0 September 17, 2026 11:25
@samlown
samlown merged commit fb59400 into bump-gobl-v0.507.0 Sep 17, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants