The FHIR IG Publisher

On this page
  1. What one run emits
  2. The three things nothing else can do
  3. What it cannot be asked for
  4. The version floats, and that is not a detail
  5. Where the rules live

This page is generated from docs/source/fhir-ig-publisher/ — each section below links to its own source.

✎ Edit

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

✎ Edit

Read from a published IG and from the build that produced it, rather than inferred from the rendered pages:

it producesnotes
output/ — the rendered siteHTML per resource and per page, from input/pagecontent/ and the pages: map in sushi-config.yaml
{ResourceType}-{id}.json per resourcethe structured surface everything downstream reads
.xml and .ttl per resourcethree serialisations of one resource; this platform’s render path takes the JSON only
package.tgz → package/.index.jsonfilename, resourceType, id — index-version 2
canonicals.json, package.manifest.jsonpartial views of the same artefact set
artifacts.htmlthe only source of an artefact’s category
qa.jsonthe validation record
POT filesextracted 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

✎ Edit

Three things, and they are the reason the transition to rendering pages elsewhere keeps the Publisher rather than removing it:

  1. Profile validation — every resource against its profiles and the declared FHIR version.
  2. 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.crmi and hl7.fhir.uv.sdc.
  3. 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

✎ Edit

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:

exportthe trust IGthe immunizations IG
valueset-ref-list.json ValueSet→CodeSystem edges17 over 14431 over 252
codesystem-ref-list.json uses populated0 of 150 of 14
usage-stats.json extension→path635 (+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

✎ Edit

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

✎ Edit

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-preprocessing and dak-postprocessing — the WHO-specific phases around the run, step by step.

folio-assistant — code under the Apache License 2.0, documentation under CC BY 3.0. Generated schema and API references are rebuilt from source on every push to main.

deployed dev