The FHIR IG Publisher
On this page
This page is generated from docs/source/fhir-ig-publisher/ — each section below links to its own source.
The FHIR IG Publisher is HL7’s tool for turning an implementation guide’s sources into a published site. In this knowledge graph it is a mechanical actor — something a process hands work to, with declared inputs and outputs — rather than a command that happens to appear in a workflow file.
Writing it down matters because of how it is usually encountered here: as a
java -jar line in a skill, and as a Docker image name in somebody else’s
workflow. Neither says what it consumes, what it produces, what it resolves on
your behalf, or what it cannot be asked for. A reader who knows only the
invocation will reach for it to answer questions it has no way to answer, and
will credit it with work done by the steps around it.
That last mistake is the common one. In the WHO SMART Guidelines build the Publisher is one step between six pre-processing invocations and nine post-processing ones. Most of what a reader sees on a published SMART Guideline — the DAK API, the JSON Schemas, the JSON-LD vocabularies — is produced after the Publisher exits, by scripts, and is not the Publisher’s work at all.
What one run emits
Read from a published IG and from the build that produced it, rather than inferred from the rendered pages:
| it produces | notes |
|---|---|
output/ — the rendered site | HTML per resource and per page, from input/pagecontent/ and the pages: map in sushi-config.yaml |
{ResourceType}-{id}.json per resource | the structured surface everything downstream reads |
.xml and .ttl per resource | three serialisations of one resource; this platform’s render path takes the JSON only |
package.tgz → package/.index.json | filename, resourceType, id — index-version 2 |
canonicals.json, package.manifest.json | partial views of the same artefact set |
artifacts.html | the only source of an artefact’s category |
qa.json | the validation record |
| POT files | extracted translatable strings |
No count is given here on purpose, and the table is dated by its source rather than by this page. The emission list changes with the Publisher’s releases, and — see below — the version is not pinned. Any statement about what it emits is a measurement with a date and a version attached, or it is a claim with no provenance.
There is no artefact-index instance in that list, and its absence is the
single most consequential fact here. ValueSets.schema.json at a published
root is a schema describing an enumeration response, which happens to carry
an example holding the list. Reading it as an index is a documented trap.
An index over a published IG has to be reconstructed from four partial
views, none sufficient alone — and which view carries the weight differs per
IG, which is why provenance records all of them rather than naming a winner.
The three things nothing else can do
Three things, and they are the reason the transition to rendering pages elsewhere keeps the Publisher rather than removing it:
- Profile validation — every resource against its profiles and the declared FHIR version.
- Dependency-closure resolution with version pinning. For WHO’s
the DAK layer that is
hl7.terminology,hl7.fhir.uv.extensions.r4,hl7.fhir.uv.cql,hl7.fhir.uv.cpg,hl7.fhir.uv.crmiandhl7.fhir.uv.sdc. - Terminology expansion against a terminology server.
None is reproducible from its own output, which is what makes a cached build a cache rather than an alternative. An artefact rendered from cached output carries facts — indices, dependency edges, versions — that were true when the cache was written and are unverified now.
The third one fails quietly, and that is worth knowing before you read any
IG’s terminology. Expansion depends on the terminology server the build was
given. A build against a dead or restricted server produces fewer expansions,
and the steps that consume them warn and continue. So a thin vocabulary
output is not evidence of a thin ValueSet: check whether expansion happened
before concluding anything about the content.
What it cannot be asked for
The Publisher exports metadata about what it built, and those exports have a measured ceiling. Using them as a stand-in for a syntax tree, across two real WHO IGs:
| export | the trust IG | the immunizations IG |
|---|---|---|
valueset-ref-list.json ValueSet→CodeSystem edges | 17 over 14 | 431 over 252 |
codesystem-ref-list.json uses populated | 0 of 15 | 0 of 14 |
usage-stats.json extension→path | 6 | 35 (+5 profiles) |
Two findings, and the second is the one that constrains everything built on top:
uses is declared and never populated, in both IGs. A declared-and-empty
field is worse than an absent one, because a consumer cannot tell “no
dependencies” from “not computed”.
Nothing exports dependencies among Library, PlanDefinition or
Measure. In the immunizations IG that is 279 + 138 + 41 artefacts —
around 61 % of the IG, and precisely the CQL and decision-logic core —
with no dependency edges at all.
So the metadata reaches terminology dependencies and structurally cannot
reach the logic layer. Anything that needs to know what a decision depends
on has to get it from somewhere else. That is the case for an AST emitted by
the tool itself, and the reason a fork of the publisher alone would not be
enough: dependency loading happens in org.hl7.fhir.core.
Re-derive these numbers rather than quoting them. They are properties of two IGs at two versions, and a Publisher release can close either gap.
The version floats, and that is not a detail
In the WHO build, publisher.jar is downloaded from the latest release on
every run:
- name: Update the image to the latest publisher
uses: docker://hl7fhir/ig-publisher-base:latest
with:
args: curl -L https://github.com/HL7/fhir-ig-publisher/releases/latest/download/publisher.jar …
Two builds of an unchanged commit can therefore differ, and nothing in the output says which version produced it.
This is not a nit. It makes “the build changed” and “the content changed” indistinguishable, which is the same class of defect as an unrecorded workflow outcome: the artefact looks healthy and the question cannot be asked. It is also why a cached AST must carry a toolchain record — publisher version, core version, SUSHI version — as a required part of its shape rather than as an optional annotation.
Where the rules live
This page describes the tool. The rules for working with it are skills, and where the two disagree the skill wins and this page is wrong:
ig-publication— the run itself, the split between authorising a release and managing one, and why an agent never records the authorisation.ig-build-pipeline— the bare pipeline this platform runs, and the list of things that layer refuses to know about.ig-publisher-reduction— the phased transition to a Publisher invoked for an AST and a QA record only, with an exit criterion per phase.ig-publisher-fork— the brief for an experimental fork, and what an AST must carry.ig-artifact-ingestion— reading a published IG back as a knowledge graph, including the four partial views and the two traps.dak-preprocessinganddak-postprocessing— the WHO-specific phases around the run, step by step.