Status: DRAFT — awaiting Matthew's review (2026-10-01).
Scope, at his instruction (2026-10-01): the IMPORT ONLY — "we'll just do the import first, nothing
else."
Built, and the IMPORT VERIFIED LIVE AND ACCEPTED by me (2026-10-01). OpenCode imported the register — it is
live in the platform store:mhra_wdais the only release,wda-2026-09-30-f33878ba, published and live,
sha256f33878ba…, 149,703 bytes, 1,757 licence rows / 1,754 distinct numbers / 2,902 site rows, the
register's ownTotal Recordsline and its typos carried verbatim, and a second run of the same file proved a
no-op with the store unchanged. Themhra_wdasource row exists (publisher MHRA, routeCMS export (xlsx),
consent_required=False, the CMS Terms as its licence, with the attribution line). At his direct request in the
same session the supplier-screen lookup was built too — its service layer I have verified live (live release,
search by holder / name / postcode / licence number, the 25-hit cap,too_short, warn-never-block), while the
screen and the save path await one decision (below). The daily check is specified separately
(the Daily Check) and is
unbuilt.
The register (cms.mhra.gov.uk/mhra/wda) is a licensing list: which companies hold a wholesale
dealer's authorisation, and at which premises. A Supplier record in this app is a trading
relationship — account number, order-file layout, minimum order value, portal. The register knows
none of that. So the register becomes app data in its own right, and the supplier list stays what it
is.
Decided with Matthew, 2026-10-01: the register is a platform-level registry (one copy, the
same home as the dm+d store, not a tenant's supplier list); suppliers will later be created one
per licensed site, labelled with the site's trading name; a wholesaler who is not on the register
will warn and never block; the right to publish the register's facts is his ruling — publish for
the purpose the register serves, labelled with its source, no consent request, closed (§7); and the
scheduled update is parked (§6, §7). Only the import is in this build.
https://cms.mhra.gov.uk/mhra/wda (human medicines: every authorisation number isUK WDA(H) …).https://cms.mhra.gov.uk/mhra/wda?_format=xlsx. xlsx only —_format=csv answers HTTP 406 with an empty body (measured 2026-10-01).sha256 f33878ba9d0f7dc9aaa835c4f3ae6a31e9a86a9f19dffc340a0c82fe080ff7e0, one worksheet namedWDA, dimension A1:D1764. Matthew's own download of the same day matches it row for row.MHRA-GMDP / (Public Site), row 2 Search WDA, row 3 blank, row 4Total Records: 1757, Wed Sep 30 21:35:41 BST 2026, row 5 blank, row 6 the four column names.A Authorisation Number (UK WDA(H) 62089), B Authorisation Holder,C Site Details, D Authorisation Date (dd/mm/yyyy on all 1,757).UNITED KINGDOM, entries separated by a newline and a bullet●).Measured over the whole file — these are the acceptance numbers:
| Measure | Value |
|---|---|
| Records (rows) | 1,757 |
| Distinct licence numbers | 1,754 |
| Site entries (column C, all rows) | 2,902 |
| Licences with one site / two sites | 1,409 / 219 |
| Largest site lists | Booker 136, Bestway 59, BOC 37 |
| Sites whose name differs from the holder's | 583 |
Sites ending UNITED KINGDOM |
2,902 of 2,902 |
Quirks that must survive the import untouched (the register's own text; no silent correction):
52485,50426, 52484 — so the licence number alone is not a unique key.O where a zero belongs: S014 5JP, NR18 OSW, and HD7 FJN on bothGENUS PHARMACEUTICALS… rows (THORNTON & ROSS LIMITED's own row reads HD7 5JN). Postcodes areC/O THORNTON & ROSS LIMITED …).How the file arrives. A person obtains it and hands it to the import — no fetch, no crawler,
no request out to the register from our systems. The file above is already held, so no retrieval is
needed for this build.
The platform's existing reference-data machinery is used as it stands, so the register is a source
like any other:
ReferenceSource row — the register as a licensed national dataset: publisher, distributionCMS export), the source's own licence terms and the attribution line (§7), cadence andReferenceRelease per file imported — the register's own Total Records: … line, the fileReferenceWdaLicence — licence number, holder, authorisation date, the row it came from.ReferenceWdaSite — the licence it belongs to, its position in the cell, site name, addressNo foreign key of any kind is created into any tenant database; nothing is written to a tenant, and no
Supplier row is touched — see §6.
One management command in the platform repo, named in the app's existing style beside
import_dmd_release — e.g. import_wda_register — taking the path to the file:
Total Records line;No new dependency. The platform's parser stack is the standard library (dm+d is XML), and the
xlsx is a zip of XML — a zipfile + ElementTree reader is enough and was written and exercised
against this very file (see §8 for where that reader lives, and the code is available to the build).
If OpenCode would rather add openpyxl, that is its call to make and record — but it is not required.
Verified 2026-10-01 — the import, live and accepted. Run by me through the platform app against the central
store, read-only, and then the no-op re-run in situ. Read back: mhra_wda holds one release,
wda-2026-09-30-f33878ba (published, is_live, publication date 2026-09-30), sha256 f33878ba…, 149,703
bytes, row_counts {licences: 1757, sites: 2902}, the register's own Total Records: 1757, Wed Sep 30 21:35…
line on the manifest; 1,757 licence rows / 1,754 distinct numbers / 2,902 site rows; the repeated numbers
exactly 50426, 52484, 52485; the register's typos verbatim (S014 5JP, NR18 OSW, HD7 FJN, HD7 5JN);
Great Bear's four sites with their postcodes and per-entry hashes. The re-run printed already held as Published
(live) — no-op and the store was unchanged afterwards (one release, same counts). Footprint: no tenant row, no
Supplier row, no network fetch; the files copied for the test were deleted from host and container.
Verified 2026-10-01 — the supplier-screen lookup, live and accepted. Driven over HTTPS as the Business Manager
through a real OIDC login, read-only until the final save. The panel renders on /stock/suppliers/new/ with the
source line "MHRA Wholesale Dealer Authorisations register — cms.mhra.gov.uk/mhra/wda — release
wda-2026-09-30-f33878ba, published 30/09/2026"; a register search returns licensed sites; "Use these details"
prefills all four fields (display name, licence UK WDA(H) 42582, trading address, postcode G52 4RU); and the
save stored the licence number, trading address and postcode, which read back on the supplier's own page
(/stock/suppliers/17/). Service-side, the client app reads the live release and searches by holder, site name,
postcode and licence number, with the 25-hit cap, the too_short guard and warn-never-block. The supplier created
for the check — id 17, Stirling Anglian Pharmaceuticals Limited, taken from the register — is left ACTIVE on
Matthew's instruction, so the supplier screen and the register panel can be used for guide figures (which means any
figure showing the demo tenant's supplier list, e.g. C7, wants re-capturing when pictures are next wanted). One
minor nit: supplier_warning('') returns the warning text with empty quotes instead of an empty string —
unreachable through the form, since a supplier name is required, so it is a guard to add when that file is next
touched.
The plan below is the one the import was held to; it stays as the record.
Nothing on report. Run the import against the file in §2 and read the result back: 1,757 records /
1,754 distinct licence numbers / 2,902 sites, the three duplicated licence numbers and the three
letter-O typos carried through verbatim, the six-row header block stepped over, and the
Total Records: 1757 line recorded on the release. Then: a second run of the same file changes
nothing (same release, same counts, no duplicates); a deliberately edited copy (rows added, changed
and removed) records a new release and leaves the first intact; the raw cell text of a multi-site
holder (Great Bear, four sites) is stored whole and its four entries are all present; and nothing
anywhere wrote to a tenant database, a Supplier row, or fetched anything from the network.
Supplier field, no migration on a tenantSupplier licence/address/postcode fields wereMy first draft of this page said the register is "Crown copyright, reusable under the Open Government
Licence". That was wrong, and the correction stands: the WDA page links to the CMS's own Terms and
Conditions (https://cms.mhra.gov.uk/terms-and-conditions, read 2026-10-01), which address automated
access (3.1.2), copying the data for display elsewhere (3.1.3), Crown copyright in the data
(3.1.9), and reproduction or distribution (3.2, requiring MHRA's written consent unless permitted
by those conditions). They are recorded here as the source's own terms and nothing more.
Matthew's ruling, 2026-10-01, and it is closed: the register is published for the purpose it
serves, labelled with its source; in his words the register "serves no purpose" otherwise and
he will "challenge any authority on this basis". No consent request will be made, and the question
is not to be raised again unless he raises it. The source row therefore carries the register's own
terms and the attribution line; the design proceeds under his decision, taken with the clauses in
front of him.
Disclosed once, for the record: the file in §2 was obtained by me with a scripted HTTP GET of the
export link the page itself offers — one request, no crawl, nothing followed. It sits only in this
host's scratch directory, no further retrieval has been made or will be made, and it is deleted on
Matthew's word.
The brief to OpenCode follows Matthew's review, and goes to
agents/handoff/from-hermes. It sits behind the already
authorised satellite work (outbound stages 3–4 and inbound I1–I3) unless the queue is reordered.
The stdlib xlsx reader written against this file — which resolves the shared-string table, walks the
first worksheet, and prints rows — is kept with the working notes for this thread and handed to the
build; the parse rules in §2 are the contract it implements.