Measuring a FHIR Implementation Guide
Measures a FHIR IG objectively and reports what it counted. Strictly read-only: it never
builds, changes, or publishes anything, and it does not forecast.
Preconditions
Discover the context; do not assume it, and never create it.
The tool. python3 must be runnable. The analyser is standard-library only — nothing to
install. If python3 is absent, say so and stop; do not fetch an interpreter.
The subject. One or more IGs, each given as a local path, a git URL, or a package
.tgz. run resolves each itself (shallow clone for a URL, download for a package). Nothing
needs to be discovered inside the repository beyond what the tool reads.
- A local path with neither
sushi-config.yaml nor input/ is not an IG project. Report that
and stop.
- A git URL requires network access; if cloning fails, report the failure rather than reporting
an empty measurement.
Static by default, and know what that costs. Without a build, the analysis is static:
sushi-config/package metadata, FSH counts, narrative, directives, dependencies, linguistics,
duplication, hygiene. Build-derived metrics (qa.json: errors, warnings, broken links,
validation) stay null and are marked Build in the catalog. A package .tgz yields a further
reduced analysis — generated resources only.
State which mode produced the numbers whenever you report them. A null is not a zero.
Procedure
The generated reports are in German; this skill's instructions are in English. That is
deliberate and does not follow from either fact on its own — the report prose was inherited and has
not been translated. Do not "fix" it silently, and do not translate the numbers.
Resolve the script path first. The commands below name the tool relative to this skill's
own directory, not to your working directory — which is the project you are operating on. Set
SKILL_DIR to the directory containing this SKILL.md (you just read it, so you know where it
is) and use it in every invocation:
SKILL_DIR=<the directory containing this SKILL.md> # e.g. .claude/skills/fhir-ig-analysis
Running a bare scripts/... from the project root does not merely fail — if the project happens
to have its own scripts/ directory with a same-named file, it silently runs that instead.
Measure. One IG or several, from the IG's root or anywhere:
python3 "$SKILL_DIR/scripts/ig-stats.py" run <input…> [-o OUTDIR] [--label a,b]
With two or more inputs this writes one report per IG plus compare-report.md
automatically. --label sets the display label (report heading, compare columns), one
per input; output filenames always derive from the IG id.
Two consequences of how inputs resolve, so the numbers are read correctly: a git URL is
shallow-cloned, so the git-history metrics (bus factor, maintenance cadence) are reported
as not derivable (git.history_complete: false, values null) — measure a full local
clone when you need them. And the tool reports counted maturity components without an
aggregated score: a 0–100 "readiness" number would be a forecast, which this skill
deliberately does not produce.
Power-user entry points, when you want the stages separately:
python3 "$SKILL_DIR/scripts/ig-stats.py" analyze <ig-dir> [-o stats.json]
python3 "$SKILL_DIR/scripts/ig-stats.py" report <stats.json> [-o report.md]
python3 "$SKILL_DIR/scripts/ig-stats.py" compare <stats.json…> [-o compare.md]
Read the mandatory-page finding carefully. The page set the tool checks against lives in
references/report-content.json under mandatory_pages and is
hand-editable on purpose. It is seeded with the MII KDS module template's actual page names.
If a measured IG legitimately uses a different page set, correct the list rather than reporting
its pages as missing — and say in your report which list you used. A page-completeness metric is
only as good as the set it compares against.
Report both artefacts. The JSON is the durable one: it has a fixed schema, so a series of
runs diffs cleanly and shows how a module grows between releases. The Markdown is for people.
Compare only on normalised metrics. Absolute counts across IGs of different size say almost
nothing; the comparison report aggregates a Σ total for scope but the fair columns are the
normalised ones.
Same-module verification is automatic. When every compared input carries the same
packageId, the inputs are states of ONE module (typically: the platform source vs its
migrated copy) and the report switches to Same-Module-Verifikation: identity-field
equality, published-artifact-set equality (internal FSH constructs reported separately —
template adoption adds scaffold rulesets legitimately), canonical-URL equality (read from
fsh-generated when both inputs have it), and narrative per language level
(default / translations / retained platform pages) with word-coverage indicators. The Σ
column is dropped (meaningless for one module). Every verdict is counted, none forecast.
Order matters: the FIRST input is the reference.
Pre-flight aspects (migration Gate 0)
From v0.17.0 every measurement carries a preflight section with
migration-decisive aspects, measured OFFLINE from the tree alone:
- Licence evidence, tiered — every place a licence is declared
(sushi-config/package.json, LICENSE file, narrative copyright statements),
with absence and contradiction as first-class findings.
- Canonical-space census — resources whose
url lies outside the IG
canonical, plus id↔url mismatches inside it: together the
special_url_prediction, the publisher's special-url list a migration
will need; example.org canonicals are called out separately. A
sushi-config-vs-package.json canonical disagreement is reported as
config_contradiction (measured live: PROs' package.json carries a
modul-dok canonical).
- Dependency-pin health — old-style/virtual packages, whether
THO/Extensions-Pack are pinned DIRECTLY (absent → the IG Publisher injects
the latest release at build time), and external parent canonicals. The
parser reads BOTH SUSHI dependency forms (flat and nested id/version
sub-keys);
dependency_block_unparsed: true flags a block that exists but
yielded no entries — a parser finding, never a dependency-free source.
- Package layout — for distributed packages:
.index.json presence
(absent → index-based oracles run dry) and nested resource directories
(flat globs lose them; the census here is recursive).
- Narrative sources — which trees carry prose (
pagecontent,
intro-notes, implementation-guides/); dual_source: true plus each
tree's last commit date makes the authoritative-copy decision a measured
one instead of a rank default.
- QA baseline — a rendered
qa.json in the tree, if any. None means:
obtain it (build the unmigrated source, or fetch its rendered qa) — never
assume it.
Three census honesty rules sit behind these aspects: instances of canonical
base types (ConceptMap, Measure, ObservationDefinition, …) are counted in an
open artifacts.other bucket instead of being absorbed as "examples"; where
FSH declarations AND generated resources both exist, their counts are
cross-checked and every mismatch is reported (generated_crosscheck — the
generated resourceType census is the authoritative one for page decisions);
and a repository whose SUSHI project nests below the root (strukturdaten's
Resources/) is re-rooted with a page-one warning instead of yielding an
empty identity without a finding.
None values name what this instrument cannot measure from the tree — the
consumer obtains it elsewhere, never assumes it (the "a null is not a zero"
rule). The mii-ig-migration skill runs this measurement on the unmigrated
source as its REQUIRED Gate-0 pre-flight.
Verification
python3 "$SKILL_DIR/scripts/ig-stats.py" run <ig-dir> -o /tmp/igstats
python3 -c "import json;d=json.load(open('/tmp/igstats/<name>-stats.json'));print(d['mode'],d['schemaVersion'])"
- The run exits 0 and writes one
*-stats.json and one *-report.md per input.
- With ≥2 inputs,
compare-report.md exists.
mode in the JSON matches what you claim (static / reduced), and schemaVersion is present.
- Every metric you quote is either a counted number or
null — never an estimate. If a field is
null, report it as not derivable statically rather than as zero.
- The mandatory-page result is plausible against the IG you measured. All-missing usually means the
wrong page set, not a broken IG.
- On a same-module comparison, the Befund block states IDENTISCH/DIVERGIERT per aspect — a
migration is only faithful when identity, published artifact set and canonical URLs are all
IDENTISCH; the migration skill's Definition of Done requires exactly that.
Scope and delimitation
Covers measurement and comparison: what is in an IG, how clean it is, how it compares, how it
changes over time.
Deliberately not covered:
- Effort forecasting. The tool reports what it counted. It does not estimate person-days, cost
or a readiness score, and an earlier version of this skill that framed the numbers as migration
scoping was narrowed on purpose. Do not reintroduce it: a measurement dressed as a forecast is
the least trustworthy thing this skill could produce.
- Migration onto the MII KDS module template — see
mii-ig-migration. Verifying a migrated
guide against its source belongs there too, in its scripts/verify-migration.py: artefact
reachability, menu structure, content placement, rendered header metadata and toolchain
provenance are pass/fail gates that read the migration's page map and run log — inputs that are
not properties of an IG and would break this skill's "a path, a URL or a .tgz" contract. The
same-module verification below is the measurement that gate consumes, not a substitute for it:
it proves an artefact is PRESENT, never that anything links to it.
- Translation — see
fhir-ig-translation.
- Convention and naming checks against the MII meta wiki or the metadata contract. Those are a
different check, and the module template ships its own skill for them.
- Building or publishing anything. Read-only is a guardrail, not a default.
If a skill of this name is provided both by this catalog and locally, the local one wins.
Guardrails
- Read-only. Never modify the analysed IG; never force a build.
- Measurement, not forecasting. See above.
- Fair comparison only via normalised metrics.
- No invention. A missing input yields
null, never a guess. Heuristic metrics are marked as
heuristics in the catalog and must be reported as such.
References
references/metrics-catalog.md — the parameter catalog
(groups A–N), each metric with its source and its use. Hand-extensible; this is the SSOT for what
is measured.
references/ig-stats-schema.json — the schema of
ig-stats.json.
references/report-content.json — plain-language texts,
glossary, directive patterns, metric explanations, and the mandatory_pages list. Hand-editable.
references/triggers.md — the Gate 3 prompt set.
scripts/ig-stats.py — the analyser (run / analyze / report /
compare).
Provenance
Derived from skills/ig-analyze in
medizininformatik-initiative/mii-kds-module-template at commit
b5beedb17a66a4397d597429668c7b6d54202c62, which in turn adapted the ig-analyze skill of
forschungsgruppe-digital-health/mii-kds-sample-ig-inoffiziell (CC-BY-4.0). Both steps of that
lineage are recorded deliberately.
Reworked on 2026-07-31 for this catalog. Beyond the catalog contract, three substantive changes:
- The analyser now ships with the skill. It previously lived in the source repository's
scripts/ and was referenced by parent traversal, so the skill pointed at a file it could not
reach once installed elsewhere.
scripts/ig-stats.py locates references/report-content.json relative to its own path. It
previously built that path from a computed repository root plus a hard-coded
skills/ig-analyze/references/…, which broke twice here: there is no repository root to compute,
and the skill was renamed.
- The mandatory-page list moved out of the code into
references/report-content.json and was
corrected. The inherited list named eleven pages of which six do not exist in the template
modules are built from (use-cases, data-sets, uml, context, references,
security-privacy), so every measurement reported six false missing pages.
Revised on 2026-08-01 after the skill's first real-task exercise (a dry run against
kerndatensatz-dokument + kerndatensatz-basis). Four output defects were fixed, all in the
direction of "measurement, not forecasting": the aggregated Reifegrad/"Freigabe-Indikator"
score was removed (components are now reported unaggregated — schemaVersion 1.4); the
"Empfehlungen" table's migration-scoping prose became neutral "Befunde & Einordnung"
measurement explanations and its metric-less "Arbeitsweise" row was dropped; git-history
metrics on shallow clones (including every clone the tool makes itself) now report
history_complete: false and null instead of a fictive "100 % top author"; and
references/ig-stats-schema.json was regenerated from the real output after drifting
(mandatory_missing was declared but never emitted). The dry-run findings live in the
mii-kds-dokument-ig-inoffiziell sandbox under docs/reports/dry-run-2026-07-31/.
Original licence: CC-BY-4.0, as declared by both source repositories. scripts/ is Apache-2.0,
matching this repository's code licence.
Promoted to stable on 2026-08-05: repeated real-task use: module measurement/comparison across four KDS modules, and the same-module verification gate of two completed migrations. The trigger set in
references/triggers.md was exercised by those runs.
1---2name: fhir-ig-analysis3description: Measures one or more FHIR Implementation Guides read-only and reports the numbers as JSON and Markdown — scope, complexity, content hygiene, duplication, linguistics, maturity and risk — and compares several IGs side by side using normalised metrics. It builds, changes and publishes nothing. Use this skill when reviewing or QA-ing an MII KDS module IG before a release, when comparing a module against kerndatensatz-basis or another Implementierungsleitfaden, when tracking how a module grows between releases, or when someone asks how large, how clean or how mature a guide actually is. Do not use for migrating a guide onto the module template or for translating one; see mii-ig-migration and fhir-ig-translation.4license: CC-BY-4.05---67# Measuring a FHIR Implementation Guide89Measures a FHIR IG objectively and reports what it counted. **Strictly read-only:** it never10builds, changes, or publishes anything, and it does not forecast.1112## Preconditions1314Discover the context; do not assume it, and never create it.15161. **The tool.** `python3` must be runnable. The analyser is standard-library only — nothing to17 install. If `python3` is absent, say so and stop; do not fetch an interpreter.18192. **The subject.** One or more IGs, each given as a **local path**, a **git URL**, or a **package20 `.tgz`**. `run` resolves each itself (shallow clone for a URL, download for a package). Nothing21 needs to be discovered inside the repository beyond what the tool reads.22 - A local path with neither `sushi-config.yaml` nor `input/` is not an IG project. Report that23 and stop.24 - A git URL requires network access; if cloning fails, report the failure rather than reporting25 an empty measurement.26273. **Static by default, and know what that costs.** Without a build, the analysis is **static**:28 sushi-config/package metadata, FSH counts, narrative, directives, dependencies, linguistics,29 duplication, hygiene. Build-derived metrics (`qa.json`: errors, warnings, broken links,30 validation) stay `null` and are marked *Build* in the catalog. A package `.tgz` yields a further31 **reduced** analysis — generated resources only.3233 State which mode produced the numbers whenever you report them. A `null` is not a zero.3435## Procedure3637**The generated reports are in German**; this skill's instructions are in English. That is38deliberate and does not follow from either fact on its own — the report prose was inherited and has39not been translated. Do not "fix" it silently, and do not translate the numbers.404142> **Resolve the script path first.** The commands below name the tool relative to **this skill's43> own directory**, not to your working directory — which is the project you are operating on. Set44> `SKILL_DIR` to the directory containing this `SKILL.md` (you just read it, so you know where it45> is) and use it in every invocation:46>47> ```bash48> SKILL_DIR=<the directory containing this SKILL.md> # e.g. .claude/skills/fhir-ig-analysis49> ```50>51> Running a bare `scripts/...` from the project root does not merely fail — if the project happens52> to have its own `scripts/` directory with a same-named file, it silently runs **that** instead.53541. **Measure.** One IG or several, from the IG's root or anywhere:5556 ```bash57 python3 "$SKILL_DIR/scripts/ig-stats.py" run <input…> [-o OUTDIR] [--label a,b]58 ```5960 With two or more inputs this writes one report per IG **plus** `compare-report.md`61 automatically. `--label` sets the **display label** (report heading, compare columns), one62 per input; output **filenames** always derive from the IG id.6364 Two consequences of how inputs resolve, so the numbers are read correctly: a **git URL is65 shallow-cloned**, so the git-history metrics (bus factor, maintenance cadence) are reported66 as *not derivable* (`git.history_complete: false`, values `null`) — measure a full local67 clone when you need them. And the tool reports **counted maturity components without an68 aggregated score**: a 0–100 "readiness" number would be a forecast, which this skill69 deliberately does not produce.70712. **Power-user entry points**, when you want the stages separately:7273 ```bash74 python3 "$SKILL_DIR/scripts/ig-stats.py" analyze <ig-dir> [-o stats.json]75 python3 "$SKILL_DIR/scripts/ig-stats.py" report <stats.json> [-o report.md]76 python3 "$SKILL_DIR/scripts/ig-stats.py" compare <stats.json…> [-o compare.md]77 ```78793. **Read the mandatory-page finding carefully.** The page set the tool checks against lives in80 [`references/report-content.json`](references/report-content.json) under `mandatory_pages` and is81 **hand-editable on purpose**. It is seeded with the MII KDS module template's actual page names.8283 If a measured IG legitimately uses a different page set, correct the list rather than reporting84 its pages as missing — and say in your report which list you used. A page-completeness metric is85 only as good as the set it compares against.86874. **Report both artefacts.** The JSON is the durable one: it has a fixed schema, so a series of88 runs diffs cleanly and shows how a module grows between releases. The Markdown is for people.89905. **Compare only on normalised metrics.** Absolute counts across IGs of different size say almost91 nothing; the comparison report aggregates a Σ total for scope but the fair columns are the92 normalised ones.93946. **Same-module verification is automatic.** When every compared input carries the same95 `packageId`, the inputs are states of ONE module (typically: the platform source vs its96 migrated copy) and the report switches to **Same-Module-Verifikation**: identity-field97 equality, published-artifact-set equality (internal FSH constructs reported separately —98 template adoption adds scaffold rulesets legitimately), canonical-URL equality (read from99 `fsh-generated` when both inputs have it), and narrative per language level100 (default / translations / retained platform pages) with word-coverage indicators. The Σ101 column is dropped (meaningless for one module). Every verdict is counted, none forecast.102 Order matters: the FIRST input is the reference.103104## Pre-flight aspects (migration Gate 0)105106From v0.17.0 every measurement carries a `preflight` section with107migration-decisive aspects, measured OFFLINE from the tree alone:108109- **Licence evidence, tiered** — every place a licence is declared110 (sushi-config/package.json, LICENSE file, narrative copyright statements),111 with absence and contradiction as first-class findings.112- **Canonical-space census** — resources whose `url` lies outside the IG113 canonical, plus id↔url mismatches inside it: together the114 `special_url_prediction`, the publisher's `special-url` list a migration115 will need; `example.org` canonicals are called out separately. A116 sushi-config-vs-package.json canonical disagreement is reported as117 `config_contradiction` (measured live: PROs' package.json carries a118 modul-dok canonical).119- **Dependency-pin health** — old-style/virtual packages, whether120 THO/Extensions-Pack are pinned DIRECTLY (absent → the IG Publisher injects121 the latest release at build time), and external parent canonicals. The122 parser reads BOTH SUSHI dependency forms (flat and nested id/version123 sub-keys); `dependency_block_unparsed: true` flags a block that exists but124 yielded no entries — a parser finding, never a dependency-free source.125- **Package layout** — for distributed packages: `.index.json` presence126 (absent → index-based oracles run dry) and nested resource directories127 (flat globs lose them; the census here is recursive).128- **Narrative sources** — which trees carry prose (`pagecontent`,129 `intro-notes`, `implementation-guides/`); `dual_source: true` plus each130 tree's last commit date makes the authoritative-copy decision a measured131 one instead of a rank default.132- **QA baseline** — a rendered `qa.json` in the tree, if any. `None` means:133 obtain it (build the unmigrated source, or fetch its rendered qa) — never134 assume it.135136Three census honesty rules sit behind these aspects: instances of canonical137base types (ConceptMap, Measure, ObservationDefinition, …) are counted in an138open `artifacts.other` bucket instead of being absorbed as "examples"; where139FSH declarations AND generated resources both exist, their counts are140cross-checked and every mismatch is reported (`generated_crosscheck` — the141generated resourceType census is the authoritative one for page decisions);142and a repository whose SUSHI project nests below the root (strukturdaten's143`Resources/`) is re-rooted with a page-one warning instead of yielding an144empty identity without a finding.145146`None` values name what this instrument cannot measure from the tree — the147consumer obtains it elsewhere, never assumes it (the "a null is not a zero"148rule). The `mii-ig-migration` skill runs this measurement on the unmigrated149source as its REQUIRED Gate-0 pre-flight.150151## Verification152153```bash154python3 "$SKILL_DIR/scripts/ig-stats.py" run <ig-dir> -o /tmp/igstats155python3 -c "import json;d=json.load(open('/tmp/igstats/<name>-stats.json'));print(d['mode'],d['schemaVersion'])"156```157158- The run exits 0 and writes one `*-stats.json` and one `*-report.md` per input.159- With ≥2 inputs, `compare-report.md` exists.160- `mode` in the JSON matches what you claim (`static` / `reduced`), and `schemaVersion` is present.161- **Every metric you quote is either a counted number or `null`** — never an estimate. If a field is162 `null`, report it as not derivable statically rather than as zero.163- The mandatory-page result is plausible against the IG you measured. All-missing usually means the164 wrong page set, not a broken IG.165- On a same-module comparison, the Befund block states IDENTISCH/DIVERGIERT per aspect — a166 migration is only faithful when identity, published artifact set and canonical URLs are all167 IDENTISCH; the migration skill's Definition of Done requires exactly that.168169## Scope and delimitation170171Covers **measurement and comparison**: what is in an IG, how clean it is, how it compares, how it172changes over time.173174Deliberately not covered:175176- **Effort forecasting.** The tool reports what it counted. It does not estimate person-days, cost177 or a readiness score, and an earlier version of this skill that framed the numbers as migration178 scoping was narrowed on purpose. Do not reintroduce it: a measurement dressed as a forecast is179 the least trustworthy thing this skill could produce.180- **Migration** onto the MII KDS module template — see `mii-ig-migration`. **Verifying a migrated181 guide against its source belongs there too**, in its `scripts/verify-migration.py`: artefact182 *reachability*, menu structure, content placement, rendered header metadata and toolchain183 provenance are pass/fail gates that read the migration's page map and run log — inputs that are184 not properties of an IG and would break this skill's "a path, a URL or a `.tgz`" contract. The185 same-module verification below is the *measurement* that gate consumes, not a substitute for it:186 it proves an artefact is PRESENT, never that anything links to it.187- **Translation** — see `fhir-ig-translation`.188- **Convention and naming checks** against the MII meta wiki or the metadata contract. Those are a189 different check, and the module template ships its own skill for them.190- **Building or publishing anything.** Read-only is a guardrail, not a default.191192If a skill of this name is provided both by this catalog and locally, the local one wins.193194## Guardrails195196- **Read-only.** Never modify the analysed IG; never force a build.197- **Measurement, not forecasting.** See above.198- **Fair comparison only via normalised metrics.**199- **No invention.** A missing input yields `null`, never a guess. Heuristic metrics are marked as200 heuristics in the catalog and must be reported as such.201202## References203204- [`references/metrics-catalog.md`](references/metrics-catalog.md) — the parameter catalog205 (groups A–N), each metric with its source and its use. Hand-extensible; this is the SSOT for what206 is measured.207- [`references/ig-stats-schema.json`](references/ig-stats-schema.json) — the schema of208 `ig-stats.json`.209- [`references/report-content.json`](references/report-content.json) — plain-language texts,210 glossary, directive patterns, metric explanations, and the `mandatory_pages` list. Hand-editable.211- [`references/triggers.md`](references/triggers.md) — the Gate 3 prompt set.212- [`scripts/ig-stats.py`](scripts/ig-stats.py) — the analyser (`run` / `analyze` / `report` /213 `compare`).214215## Provenance216217Derived from `skills/ig-analyze` in218`medizininformatik-initiative/mii-kds-module-template` at commit219`b5beedb17a66a4397d597429668c7b6d54202c62`, which in turn adapted the `ig-analyze` skill of220`forschungsgruppe-digital-health/mii-kds-sample-ig-inoffiziell` (CC-BY-4.0). Both steps of that221lineage are recorded deliberately.222223Reworked on 2026-07-31 for this catalog. Beyond the catalog contract, three substantive changes:224225- The analyser now ships **with** the skill. It previously lived in the source repository's226 `scripts/` and was referenced by parent traversal, so the skill pointed at a file it could not227 reach once installed elsewhere.228- `scripts/ig-stats.py` locates `references/report-content.json` relative to **its own path**. It229 previously built that path from a computed repository root plus a hard-coded230 `skills/ig-analyze/references/…`, which broke twice here: there is no repository root to compute,231 and the skill was renamed.232- The mandatory-page list moved out of the code into `references/report-content.json` and was233 corrected. The inherited list named eleven pages of which **six do not exist** in the template234 modules are built from (`use-cases`, `data-sets`, `uml`, `context`, `references`,235 `security-privacy`), so every measurement reported six false missing pages.236237Revised on 2026-08-01 after the skill's first real-task exercise (a dry run against238`kerndatensatz-dokument` + `kerndatensatz-basis`). Four output defects were fixed, all in the239direction of "measurement, not forecasting": the aggregated `Reifegrad`/"Freigabe-Indikator"240score was removed (components are now reported unaggregated — schemaVersion 1.4); the241"Empfehlungen" table's migration-scoping prose became neutral "Befunde & Einordnung"242measurement explanations and its metric-less "Arbeitsweise" row was dropped; git-history243metrics on shallow clones (including every clone the tool makes itself) now report244`history_complete: false` and `null` instead of a fictive "100 % top author"; and245`references/ig-stats-schema.json` was regenerated from the real output after drifting246(`mandatory_missing` was declared but never emitted). The dry-run findings live in the247`mii-kds-dokument-ig-inoffiziell` sandbox under `docs/reports/dry-run-2026-07-31/`.248249Original licence: CC-BY-4.0, as declared by both source repositories. `scripts/` is Apache-2.0,250matching this repository's code licence.251252Promoted to `stable` on 2026-08-05: repeated real-task use: module measurement/comparison across four KDS modules, and the same-module verification gate of two completed migrations. The trigger set in253[references/triggers.md](references/triggers.md) was exercised by those runs.