Add a platform
A backend platform (WooCommerce, Magento, …) is one self-contained module, registered by one entry in platform.All (internal/platform/platform.go). The module is internal/platform/<name> plus its…
Generated from docs/HOWTO-add-platform.md in the Sarvex repository — edit the source there, not this page.
A backend platform (WooCommerce, Magento, …) is one self-contained module, registered by one entry in platform.All (internal/platform/platform.go). The module is internal/platform/<name> plus its merchant-side plugin in plugin/<name>. No established platform, spec version or plane changes (CLAUDE.md §8, docs/plans/version-platform-modularity.md).
V1’s scope is WooCommerce and Magento (CLAUDE.md §0, §6). A connector beyond those is a stop-and-report condition, so get scope agreed before you start.
1. Scaffold it
make new-platform P=shopware D=Shopware
P is the registry name: lowercase letters only. It must not contain, or be contained in, another platform’s name, frozen ones included. The boundary check finds platform code by that name. D is the display name; it defaults to P capitalised.
This copies the registry’s default platform into internal/platform/<name>: the connector and merchant config, validator/ and sim/. It rewrites every spelling of the old name: lowercase becomes P, uppercase becomes P in uppercase, anything else becomes D. It also adds the module to platform.All and creates plugin/<name>/README.md.
The copy builds and passes the boundary check, but it still speaks the template’s store API.
2. Make it talk to the platform
| Path | What it is |
|---|---|
internal/platform/<name> |
The connector: client.go (the store API), mapper.go/types.go (store shapes ↔ the canonical model, internal/pkg/commerce/model), config.go (merchant config + its JSON schema), factory.go (PlatformName, Factory). Platform-only features, such as a platform’s own payment handler, live here too. |
validator/ |
Checks a merchant’s credentials against the store. |
sim/ |
The simulated store: the platform’s REST surface over internal/pkg/simstore, served by the dev/test simulator under /stores/{store_id}/. |
plugin/<name> |
The merchant-side plugin. It has two roles only, signalling and the /.well-known/ucp doorway, and no business logic (CLAUDE.md §4). |
Rules:
- Connectors produce the canonical model and never a spec version’s types. Version differences belong in the version codecs.
- Nothing outside the module names the platform. Composition roots call
platform.Register*, the control plane checksplatform.Supported, and tools default toplatform.Default(). Add noswitch platformto shared code. - Every capability works on every platform (CLAUDE.md §8). If the platform cannot support one, stop and report. Do not ship it advertised.
3. The contract and the console
These tests fail until each item is done:
sarvex-v1-control-plane.yaml— add<name>to theplatformenum, and astore_credentials_<name>schema beside the others (plus itsoneOfentry where the store credentials are accepted). Checked byTestControlPlaneContractListsEveryPlatform. The contract is authoritative (CLAUDE.md §3).- The console —
cd web/console && npm run gen:api, then add the platform toSTORE_PLATFORMSinsrc/platforms.tsand its label tosettings.storeinsrc/locales/en.ts. Checked bynpm run typecheck. - The plugin —
plugin/<name>must exist (the scaffold creates it). Checked byTestPluginsMatchRegisteredPlatforms. - The simulated store — its routes must not overlap another platform’s. Checked by
TestSimSurfacesDoNotOverlap, which fails on a fresh scaffold untilsim/(and the connector’s client) speak the new platform’s API.
4. Goldens and tests
UPDATE_GOLDENS=1 go test ./internal/planes/...
git status --short '**/testdata/golden'
Only new files may appear (the new platform’s directory under internal/planes/sync/sim/testdata/golden). A modified existing golden means the new platform changed another platform’s output, so do not accept it.
Then:
make test-unit
make test-scenarios
cd web/console && npm run typecheck && npm run test:run
These checks cover the new platform automatically:
TestCapabilityMatrixruns it against every servable generation.TestCapabilityMatrixCoversEverySupportedPlatformandTestEveryPlatformIsCompletecheck that its module is whole.TestCapabilityMatrix_CanariesChangeNothingproves that registering a platform changes nothing else.
5. The PR
Label the PR platform-change/<name>. golden-guard requires it, because the PR adds goldens.