fhir-community-search
jmandel/fhir-community-searchSKILL.md
Skill: FHIR Community Research
Research HL7 FHIR specification history by triangulating local Jira issues, Zulip community discussions, Confluence WG minutes/pages, and FHIR spec/source artifacts. Use this skill when the user asks why or when a FHIR core or IG change happened, who discussed it, which issue/PR/commit caused it, what a work group decided, or how wording changed across releases.
Setup
# Download repository zip via GitHub API (no git required)
curl -L "https://api.github.com/repos/jmandel/fhir-community-search/zipball" -o fhir-community-search.zip
unzip fhir-community-search.zip
cd jmandel-fhir-community-search-*
# Install Bun if needed, then install dependencies
# https://bun.sh/docs/installation
bun install
# Download pre-indexed community databases.
# Sizes vary by stream; use moving "latest" tags maintained per data stream.
curl -L "https://github.com/jmandel/fhir-community-search/releases/download/jira-latest/jira-data.db.gz" | gzip -dc > jira/data.db
curl -L "https://github.com/jmandel/fhir-community-search/releases/download/zulip-latest/zulip-data.db.gz" | gzip -dc > zulip/data.db
curl -L "https://github.com/jmandel/fhir-community-search/releases/download/confluence-latest/confluence-data.db.gz" | gzip -dc > confluence/data.db
# Build a lightweight spec catalog.
# Defaults include all QAS/package-list metadata and a bounded recent published-feed scan.
bun run spec:sync catalog
Data Sources
| Source | Content |
|---|---|
| Jira | 50k+ indexed HL7 Jira issues from jira.hl7.org. Default scope includes FHIR specification issues (FHIR-*), historical HL7 Terminology Authority issues (HTA-*), and current UTG change proposals (UP-*). |
| Zulip | Public-channel messages from chat.fhir.org community discussions |
| Confluence | HL7 WG pages, minutes, agendas, and publication requests from confluence.hl7.org |
| Spec | FHIR core and IG package/source locator based on package-list, package feeds, QAS, and git |
Usage
Read README.md, jira/README.md, zulip/README.md, confluence/README.md, and spec/README.md in the repository for detailed command behavior and caveats.
For any FHIR research question, search iteratively across all relevant sources:
Jira Search Task
Research question: [YOUR QUESTION]
Before searching:
1. Read jira/README.md for schema, CLI commands, and search strategies
2. Note the "Search → Snapshot → Explore" methodology
Search iteratively with multiple phrasings. Snapshot promising issues to read full context including comments. Report findings relevant to the research question.
Zulip Search Task
Research question: [YOUR QUESTION]
Before searching:
1. Read zulip/README.md for schema, CLI commands, and search strategies
2. Note key streams and expert users to watch for
Search iteratively with multiple phrasings. Snapshot promising threads for full conversations. Report findings relevant to the research question.
Spec/Source Search Task
Research question: [YOUR QUESTION]
Before searching:
1. Read spec/README.md for package catalog sync, source resolution, package fallback, leases, and GC
2. Use spec:resolve/spec:list to understand version names before materializing anything
3. Use spec:locate without mode flags to obtain a stable local source path, then inspect with rg, git grep, git log, or git blame
Search current and relevant published versions. Distinguish:
- source commit date
- PR merge date
- Jira resolution/vote date
- publication date/version where wording first appears
- package fallback vs date-matched source, if locate cannot provide source
Confluence Search Task
Research question: [YOUR QUESTION]
Before searching:
1. Read confluence/README.md for scope, page kinds, references, CLI commands, and caveats
2. Prioritize minutes, agendas, and publication requests when looking for work-group decisions
Search iteratively with full-text and refs queries. Snapshot promising pages to read the actual minutes, agenda, or publication request. Report findings relevant to the research question.
Key Commands
# Full-text search
bun run jira:search fts "your query"
bun run zulip:search fts "your query"
bun run confluence:search fts "your query"
# Get full context
bun run jira:search snapshot FHIR-XXXXX
bun run jira:search snapshot UP-806
bun run zulip:search snapshot stream "topic name"
bun run confluence:search snapshot PAGE_ID
bun run confluence:search refs jira FHIR-XXXXX
# Spec catalog and locating files
bun run spec:sync catalog
bun run spec:list
bun run spec:list hl7.fhir.core
bun run spec:resolve core r4
bun run spec:locate core r4
# Inspect located spec files
rg "Bundle.entry.resource" <path-from-spec-locate>
git -C <source-path> log -S "exact phrase" -- source/bundle/structuredefinition-Bundle.xml
git -C <source-path> blame -- source/bundle/structuredefinition-Bundle.xml
# If you need multiple snapshots, run them in a single shell command
# to avoid extra back-and-forth:
# bun run jira:search snapshot FHIR-12345; bun run jira:search snapshot FHIR-67890
# bun run zulip:search snapshot "stream" "topic A" && bun run zulip:search snapshot "stream" "topic B"
# bun run confluence:search snapshot 453905263 && bun run confluence:search refs jira FHIR-54993
# Help
bun run jira:search --help
bun run zulip:search --help
bun run confluence:search --help
bun run spec:locate --help
FHIR History Workflow
Use this workflow for questions like "when was this language added?", "why did this profile change?", "which tracker caused this?", or "what discussion led to this rule?"
-
Identify the exact artifact and text.
- Use
spec:resolveto pin the requested version. - Use plain
spec:locatefirst. For FHIR core and most HL7 IGs this should give a source checkout or date-matched source tree; that is the default evidence for history questions. - Inspect source files with
rg,git grep,git log, andgit blame. - Use generated package artifacts only when source cannot be located, when you are checking a published conformance JSON surface, or when source and publication need to be compared explicitly.
- Use
-
Compare versions before explaining causality.
- Check the last version without the wording and the first version with it.
- Record whether the change first appears in source, CI build, ballot, or final published release.
- For FHIR core, remember that source history, package history, and rendered publication dates are different clocks.
-
Trace source history.
- Search exact phrases with
git log -Sorgit log -G. - Use
git show,git blame, PR descriptions, and linked commits to identify the implementation event. - If source was date-matched rather than exact-tagged, say so.
- Search exact phrases with
-
Search Jira for intent and formal disposition.
- Search by artifact, element path, resource name, exact phrase, and suspected issue keys.
- For terminology/THO/UTG questions, search both
FHIR-*issues and currentUP-*UTG change proposals; older terminology authority work may appear asHTA-*. UP-*snapshots include UTG proposal fields such as proposal type/description, sponsor approval date, published examples, changed source-of-truth objects, and OSG voting fields. These fields are full-text indexed, so searches likebun run jira:search fts "\"Reading Modes\""can find proposal text even when the Jira description is empty.- Snapshot issues, especially ones with resolutions, vote dates, related URLs, applied versions, comments, PR links, or commit links.
- If you know the key, use
snapshotdirectly. In FTS queries, quote hyphenated issue keys and exact element paths because FTS5 can parse hyphens as operators.
-
Search Zulip for informal rationale.
- Search exact issue keys, element paths, commit/PR terms, and likely topic names.
- Snapshot whole threads. Important rationale often appears before Jira is filed or after a proposed resolution is challenged.
- Follow links from Jira comments into Zulip threads and from Zulip bot notifications back to Jira.
- The default Zulip DB omits high-volume machine-generated notification/crawler streams; use a full
--include-noisy-streamsrebuild only when bot announcements are central evidence.
-
Search Confluence for work-group minutes and publication requests.
- Search exact issue keys, feature names, canonical URLs, and likely agenda terms.
- Use
refs jira FHIR-XXXXXwhen you know the tracker key. - Snapshot pages that look like minutes, agendas, or publication requests; these often record what was actually discussed or approved.
- Treat Confluence as meeting evidence, distinct from Jira formal disposition and Zulip informal discussion.
-
Triangulate, then report.
- Present a timeline with separate dates for discussion, Jira creation/resolution/vote, source commit, PR merge, and publication.
- Explain what is firmly evidenced and what is inferred.
- Mention disagreements, later reversals, and whether the current build still matches the historical rule.
Search Iteration Strategy
FTS is keyword-based, not semantic. Do not search only the user's natural-language question. Build a small set of searches at different levels of specificity, then adjust based on the first results.
Start from precise anchors:
- exact spec phrase copied from current text
- element path, profile id, extension URL, resource name, operation name, or code system URL
- Jira key, PR number, commit SHA, ballot id, package id, canonical URL, or Zulip topic title
Then vary the wording:
- FHIR spelling variants:
Bundle.entry.resource,Bundle entry resource,entry.resource - release/package variants:
R4,R4B,R5,6.0.0-ballot3,hl7.fhir.us.core - terminology variants:
Parameters,parameter,operation parameters,MessageHeader.focus - normative words:
SHALL,must,required,prohibited,allowed,invariant,constraint - implementation words:
validation,validator,example,snapshot,differential,publisher - historical words:
change,proposal,resolution,vote,ballot,applied,published
Use a search ladder:
# Very specific: good when you already have exact wording.
bun run jira:search fts "\"Parameters resource if and only if\""
rg "Parameters resource if and only if" <spec-path>
# Element/path level: good when wording may have changed.
bun run jira:search fts "\"Bundle.entry.resource\" Parameters"
bun run zulip:search fts "\"Bundle.entry.resource\" Parameters"
# Resource/concept level: good when exact paths were not used in discussion.
bun run jira:search fts "Bundle Parameters"
bun run zulip:search fts "Bundle Parameters"
# Related artifact level: good when the cause is elsewhere.
bun run jira:search fts "\"MessageHeader.focus\" Parameters"
bun run zulip:search fts "Parameters messaging"
If results are too sparse:
- remove one term at a time instead of inventing a new broad query
- drop punctuation-heavy paths and search individual parts
- try synonyms from spec prose, examples, and Jira summaries
- search Zulip by people or stream if one expert or workgroup is likely involved
- search source with
rg, then use nearby filenames and comments as new Jira/Zulip terms - use SQL date windows around publication, Jira resolution, or PR merge dates
If results are too noisy:
- add artifact names, exact phrases, stream/workgroup filters, version filters, or date windows
- group results by issue key, Zulip stream/topic, sender, or year before reading everything
- prefer snapshots for threads/issues that connect multiple sources, such as a Jira issue mentioned in Zulip or a Jira comment linking a PR
- move from broad FTS snippets to full snapshots once a candidate looks relevant
Explore several levels of detail before concluding:
- Current wording: what does the current package/source/rendered page say?
- Release boundary: which published versions have or lack the wording?
- Source event: which commit or PR changed it?
- Formal rationale: which Jira issue records disposition, vote, and applied version?
- Informal rationale: which Zulip thread explains the discussion or objections?
- Later reassessment: did later Jira/Zulip/spec history reverse or reinterpret the change?
Record failed searches when they shaped the conclusion. A useful report can say, for example, that exact phrase search found nothing in R4/R4B, path-level search found a 2026 reversal issue, and broader messaging/Parameters searches led back to the 2020 rationale.
When Spec Locate Cannot Find It
If the question is about a spec or IG that spec:resolve/spec:locate cannot find, do not stop at Jira/Zulip. Use web discovery to find the published package, rendered pages, or source repository, then inspect those artifacts locally where possible.
First make sure the local catalog is not just too narrow:
bun run spec:sync catalog --qas-limit all --package-list-limit all --published-limit all --feed-limit all
bun run spec:resolve <package-id-or-alias> <version>
bun run spec:locate <package-id-or-alias> <version>
If that still fails, search the web for:
- the exact IG title or canonical URL
- the package id, if known
<canonical>/package-list.jsonsite:build.fhir.org/ig <package-id-or-title>site:github.com/HL7 <package-id-or-title>
Then try structured package discovery before relying on rendered HTML:
# If a canonical or published IG root is known:
curl -fsSL "<published-root>/package-list.json" | jq .
curl -fL "<published-version-path>/package.tgz" -o /tmp/spec-package.tgz
# If only package id and version are known:
curl -fL "https://packages2.fhir.org/packages/<package-id>/<version>" -o /tmp/spec-package.tgz
# Some feeds expose a named .tgz instead of package.tgz; use the exact link if package-list/feed metadata gives one.
mkdir -p /tmp/spec-package
tar -xzf /tmp/spec-package.tgz -C /tmp/spec-package
rg "your phrase" /tmp/spec-package
jq -r '.url, .version, .name, .title' /tmp/spec-package/package/package.json
Use QAS to look for likely GitHub source claims, but treat them as claims rather than proof:
curl -fsSL https://build.fhir.org/ig/qas.json \
| jq -r '.[] | select(."package-id" == "<package-id>") | {package: ."package-id", version: ."ig-ver", dateISO8601, repo, url, errs, warnings}'
Prefer source history when you can find a plausible GitHub repository:
git clone https://github.com/<org>/<repo>.git /tmp/spec-source
git -C /tmp/spec-source log --all -S "your phrase"
git -C /tmp/spec-source log --all --since "<publication-date-minus-window>" --until "<publication-date-plus-window>" --oneline
rg "your phrase" /tmp/spec-source
If source cannot be established, use the package artifact for structured resources and rendered HTML for narrative pages:
curl -fsSL "<published-page-url>" -o /tmp/spec-page.html
rg -n "your phrase|nearby terms" /tmp/spec-page.html
When reporting from these fallbacks, be explicit: say whether evidence came from a package archive, package-list metadata, QAS source claim, git source, or rendered HTML. If the source repo could not be located, say that directly and avoid implying commit-level provenance.
Scripted and Advanced Searches
Most questions should start with direct CLI searches and snapshots. Write a small script when the investigation needs breadth, repeatability, or cross-source validation.
Good cases for a script:
- Checking the same phrase across many FHIR releases, IG package versions, or source branches.
- Running several Jira/Zulip phrasings and intersecting the hits by issue key, topic, date, person, or linked URL.
- Building a release matrix such as "first version where this element definition contains this phrase."
- Cross-checking a suspected Jira key against Zulip bot notifications, human discussion threads, PR references, and spec commits.
- Searching many files after
spec:locate, especially when the relevant text might live in generated JSON, XML source, examples, or narrative pages. - Repeating date-window searches around a publication date, Jira resolution date, PR merge date, or commit date.
Keep these scripts disposable unless they become generally useful. A good scratch script prints candidate evidence and commands to snapshot next; it should not replace reading the full Jira issue, Zulip thread, or commit.
Advanced SQLite Queries
Use raw SQL when the built-in CLI filters are too narrow: date windows, grouping by thread, combining FTS with structured Jira fields, searching Jira comments, or checking whether a set of issue keys appears in Zulip.
Schema cheat sheet:
- Jira:
issues(key TEXT PRIMARY KEY, data JSON)plus contentless FTS tableissues_fts(key, summary, description, specification, work_group, related_artifacts, resolution_description, labels, comments_text). - Zulip:
messages(id, stream_id, stream_name, topic, sender_name, content, timestamp, created_at, ...),streams(id, name, ...), plus FTS tablemessages_fts(stream_name, topic, sender_name, content). - Jira JSON fields commonly worth extracting:
summary,description,status,status_category,resolution,issue_type,created_at,updated_at,resolved_at,reporter.name,work_group,related_artifacts,selected_ballot,raised_in_version,applied_for_version,resolution_description,related_url,related_pages,links,comments. - Important Jira JSON gotcha: fields such as
work_group,related_artifacts,raised_in_version, andapplied_for_versionare arrays. For exact filters, joinjson_each(...)instead of comparingjson_extract(...)to a scalar string. - Important Jira status gotcha: use
status_categoryfor Jira's open/done distinction.resolved_at IS NOT NULLis broader than "done";Resolved - change requiredhas a resolution date but remainsstatus_category = 'indeterminate'until applied or published. - Important Jira links note: use the normalized
linksarray for traversal. It includes direct custom links such asduplicate_of/related_issues, standard Jira issue links if exposed by the API, and derived backlinks such asis duplicated by. - Important Jira FTS gotcha:
issues_ftsis contentless. Its columns are searchable but read back asNULL, so joinissues_ftstoissuesbyrowid, not bykey. - Important Zulip FTS note:
messages.idis anINTEGER PRIMARY KEY, so it aliases SQLitemessages.rowid; usemessages.rowid = messages_fts.rowidin docs/examples to match the Jira join pattern. - Important attached-database FTS gotcha: when using
sqlite3 :memory:withATTACH, keep the FTS table name unaliased in theMATCHexpression, for exampleWHERE issues_fts MATCH 'Parameters'.
First inspect an example row before writing a complex query:
bun run jira:search sql \
"SELECT key,
json_extract(data, '$.summary') AS summary,
json_extract(data, '$.status') AS status,
json_extract(data, '$.created_at') AS created_at
FROM issues
WHERE key = 'FHIR-26390'"
bun run zulip:search sql \
"SELECT id, stream_name, topic, sender_name,
datetime(timestamp, 'unixepoch') AS sent_at,
substr(content, 1, 100) AS excerpt
FROM messages
WHERE id = 189223319"
Combine Jira FTS with JSON filters:
bun run jira:search sql \
"SELECT i.key,
json_extract(i.data, '$.summary') AS summary,
json_extract(i.data, '$.created_at') AS created_at,
json_extract(i.data, '$.status') AS status
FROM issues_fts fts
JOIN issues i ON i.rowid = fts.rowid
WHERE issues_fts MATCH 'Parameters Bundle entry'
ORDER BY fts.rank
LIMIT 20"
Filter exact Jira array fields, such as responsible work group:
bun run jira:search sql \
"SELECT i.key,
json_extract(i.data, '$.summary') AS summary,
json_extract(i.data, '$.status') AS status
FROM issues i,
json_each(i.data, '$.work_group') wg
WHERE lower(wg.value) = 'fhir-i'
ORDER BY json_extract(i.data, '$.updated_at') DESC
LIMIT 20"
Count unresolved application work for a work group:
bun run jira:search sql \
"SELECT json_extract(i.data, '$.resolution') AS resolution,
COUNT(*) AS n
FROM issues i,
json_each(i.data, '$.work_group') wg
WHERE lower(wg.value) = 'fhir-i'
AND json_extract(i.data, '$.status') = 'Resolved - change required'
GROUP BY resolution
ORDER BY n DESC"
Follow normalized Jira links, including derived backlinks:
bun run jira:search sql \
"SELECT i.key,
json_extract(i.data, '$.summary') AS summary,
json_extract(l.value, '$.label') AS link_label
FROM issues i,
json_each(i.data, '$.links') l
WHERE json_extract(l.value, '$.key') = 'FHIR-57372'
ORDER BY json_extract(i.data, '$.updated_at') DESC"
Search Jira comments and extract implementation links:
bun run jira:search sql \
"SELECT i.key,
json_extract(i.data, '$.summary') AS summary,
json_extract(c.value, '$.created_at') AS comment_created,
substr(json_extract(c.value, '$.body'), 1, 180) AS comment
FROM issues i,
json_each(i.data, '$.comments') c
WHERE json_extract(c.value, '$.body') LIKE '%github.com/HL7/fhir/pull%'
ORDER BY json_extract(i.data, '$.updated_at') DESC
LIMIT 20"
Group Zulip FTS hits by thread to decide what to snapshot:
bun run zulip:search sql \
"SELECT m.stream_name,
m.topic,
COUNT(*) AS hits,
datetime(MIN(m.timestamp), 'unixepoch') AS first_seen,
datetime(MAX(m.timestamp), 'unixepoch') AS last_seen
FROM messages_fts fts
JOIN messages m ON m.rowid = fts.rowid
WHERE messages_fts MATCH 'Parameters'
AND m.content LIKE '%Bundle.entry.resource%'
GROUP BY m.stream_name, m.topic
ORDER BY hits DESC, last_seen DESC
LIMIT 20"
Use date windows around Jira resolution, PR merge, ballot publication, or known discussion dates:
bun run zulip:search sql \
"SELECT id, stream_name, topic, sender_name,
datetime(timestamp, 'unixepoch') AS sent_at,
substr(content, 1, 160) AS excerpt
FROM messages
WHERE timestamp BETWEEN strftime('%s', '2020-02-01')
AND strftime('%s', '2020-03-15')
AND content LIKE '%FHIR-26390%'
ORDER BY timestamp"
For cross-source checks, attach both databases directly with sqlite3:
sqlite3 :memory: "
ATTACH 'jira/data.db' AS jira;
ATTACH 'zulip/data.db' AS zulip;
WITH keys(key) AS (
VALUES ('FHIR-26390'), ('FHIR-26368'), ('FHIR-57354')
)
SELECT k.key,
m.stream_name,
m.topic,
COUNT(*) AS mentions,
datetime(MIN(m.timestamp), 'unixepoch') AS first_seen,
datetime(MAX(m.timestamp), 'unixepoch') AS last_seen
FROM keys k
JOIN zulip.messages m ON m.content LIKE '%' || k.key || '%'
GROUP BY k.key, m.stream_name, m.topic
ORDER BY k.key, mentions DESC;
"
SQL is best for narrowing and prioritizing. After it finds likely issues or threads, still run jira:search snapshot and zulip:search snapshot before drawing conclusions.
Bun Cross-Check Pattern
The CLIs support --json. For clean JSON inside scripts, call the TypeScript entrypoints directly:
#!/usr/bin/env bun
async function runJson(args: string[]) {
const proc = Bun.spawn(args, { stdout: "pipe", stderr: "inherit" });
const text = await new Response(proc.stdout).text();
const code = await proc.exited;
if (code !== 0) throw new Error(`${args.join(" ")} exited ${code}`);
return JSON.parse(text);
}
const queries = [
"Parameters Bundle entry",
"\"Bundle.entry.resource\" Parameters",
"\"if and only if\" Parameters",
];
for (const query of queries) {
const [jira, zulip] = await Promise.all([
runJson(["bun", "jira/search.ts", "fts", query, "--limit", "20", "--json"]),
runJson(["bun", "zulip/search.ts", "fts", query, "--limit", "20", "--json"]),
]);
console.log(`\n## ${query}`);
console.log("Jira:", jira.map((hit: any) => hit.key ?? hit.summary).slice(0, 8));
console.log(
"Zulip:",
zulip.map((hit: any) => `${hit.stream_name} > ${hit.topic}`).slice(0, 8),
);
}
Use the output to choose snapshots:
bun run jira:search snapshot FHIR-26390
bun run zulip:search snapshot implementers "modeling specific details in FHIR Messaging"
Multi-Version Spec Scan
When the question is "when did this wording appear?", script the release boundary instead of eyeballing one version at a time. Start with plain spec:locate so the path is source-backed when available:
#!/usr/bin/env bun
const versions = ["r4", "4.3.0", "r5", "6.0.0-ballot3"];
const phrase = "Parameters resource if and only if";
async function run(args: string[]) {
const proc = Bun.spawn(args, { stdout: "pipe", stderr: "inherit" });
const text = (await new Response(proc.stdout).text()).trim();
const code = await proc.exited;
if (code !== 0) throw new Error(`${args.join(" ")} exited ${code}`);
return text;
}
for (const version of versions) {
const path = await run([
"bun",
"spec/locate.ts",
"hl7.fhir.core",
version,
]);
const grep = Bun.spawn(["rg", "-l", phrase, path], { stdout: "pipe" });
const matches = (await new Response(grep.stdout).text()).trim().split("\n").filter(Boolean);
await grep.exited;
console.log(`${version}: ${matches.length ? "present" : "absent"}`);
}
If locate falls back to a package and the package looks incomplete, use canonical published URLs or source history and say which source was used. Do not force package mode unless you specifically need generated package JSON.
Advanced Git History
Use regular rg for current files, then switch to Git when the question is historical:
# Pickaxe: first commit that added or removed an exact phrase.
git -C <source-path> log --all --reverse -S "Parameters resource if and only if" -- source/bundle/structuredefinition-Bundle.xml
# Regex history when the wording changed but the concept stayed similar.
git -C <source-path> log --all --reverse -G "Parameters.*Bundle|Bundle.*Parameters" -- source/bundle/structuredefinition-Bundle.xml
# Show the implementation commit with surrounding patch.
git -C <source-path> show --stat --patch ce61932 -- source/bundle/structuredefinition-Bundle.xml
# Follow a renamed/moved source file where that matters.
git -C <source-path> log --follow --all -- source/bundle/structuredefinition-Bundle.xml
# Restrict a search to the publication/resolution window you are testing.
git -C <source-path> log --all --since "2020-02-01" --until "2022-09-01" -S "Parameters resource" -- source/bundle/structuredefinition-Bundle.xml
# See which branches/tags contain a commit, but treat tags as hints rather than authoritative publication data.
git -C <source-path> branch --all --contains ce61932
git -C <source-path> tag --contains ce61932
When tags or branch names are unreliable, use package-feed publication dates to create date windows, then verify the wording in source first and in published packages or canonical release URLs only when publication-surface evidence is required.
Calibrated Example: Bundle.entry.resource Parameters Rule
Question: when did Bundle.entry.resource limit Parameters to only when another Bundle resource references it, and why?
This is a useful pattern because the answer is not in one place. The final story came from a current spec phrase, a Jira resolution, a PR/commit, and two Zulip threads.
Step 1: Find the Current Wording
Start with source locate. This gives a git worktree when source is known, which keeps history and package-surface checks from getting mixed up:
bun run spec:locate hl7.fhir.core 6.0.0-ballot3
rg -n "Parameters resource if and only if" <path-from-spec-locate>
This found the exact sentence in source or date-matched source:
The Resource for the entry. The purpose/meaning of the resource is determined by the Bundle.type.
This is allowed to be a Parameters resource if and only if it is referenced by something else within the Bundle that provides context/meaning.
Step 2: Check Release Boundaries
Compare adjacent major releases with source locate first:
bun run spec:locate core r4
bun run spec:locate core 4.3.0
bun run spec:locate core r5
curl -fsSL https://hl7.org/fhir/R4/bundle.profile.json \
| jq -r '.snapshot.element[] | select(.id=="Bundle.entry.resource") | .definition'
curl -fsSL https://hl7.org/fhir/R4B/bundle.profile.json \
| jq -r '.snapshot.element[] | select(.id=="Bundle.entry.resource") | .definition'
curl -fsSL https://hl7.org/fhir/R5/bundle.profile.json \
| jq -r '.snapshot.element[] | select(.id=="Bundle.entry.resource") | .definition'
Result:
- R4: no Parameters sentence.
- R4B: no Parameters sentence.
- R5: sentence present.
- R6 ballot package: sentence present.
False path: the R4 package from packages2.fhir.org was only a tiny metadata package in this cache, so checking hl7.org/fhir/R4/bundle.profile.json was the better release comparison source for R4.
Step 3: Search Jira Broadly, Then Snapshot
The most productive first query was:
bun run jira:search fts "Parameters Bundle entry" --limit 20
This immediately surfaced:
FHIR-57354 Remove restriction on Parameters in Bundle.entry.resource requiring reference from another entry
FHIR-26368 Allow Parameters to be used in Messaging
Snapshot known-good issues:
bun run jira:search snapshot FHIR-57354
bun run jira:search snapshot FHIR-26368
FHIR-57354 described the later 2026 removal request and explicitly named the background: FHIR-26390 alongside the broader FHIR-26368 work. FHIR-26368 showed the original February 2020 issue: Parameters said it had "no other use" outside operations, which conflicted with messaging examples/use cases.
Then snapshot the linked issue:
bun run jira:search snapshot FHIR-26390
FHIR-26390 supplied the core rationale: the broad FHIR-26368 relaxation could be read as changing an implicit invariant from "any resource except Parameters" to "any resource including Parameters." Its resolution allowed Parameters only where specific elements explicitly said so, including Bundle.entry.resource, with the "must be referenced by something else in the Bundle" note.
False path: searching for issue keys inside FTS like this can fail:
bun run jira:search fts "FHIR-57354 Bundle.entry.resource Parameters"
SQLite FTS may parse the hyphen as an operator. If you know the key, use snapshot FHIR-57354 directly, or quote/escape carefully.
Step 4: Search Zulip for Rationale and Later Reassessment
The direct query:
bun run zulip:search fts "Parameters Bundle entry" --limit 20
found the 2026 thread:
#fhir/infrastructure-wg > Bundle.entry.resource Parameters rules?
Snapshot it:
bun run zulip:search snapshot fhir/infrastructure-wg "Bundle.entry.resource Parameters rules?"
This thread explained the later reassessment. Josh Mandel asked whether the prose rule should be reverted or made a constraint; Grahame Grieve noted there were contexts with Bundles containing all Parameters; the thread reconstructed the 2020 rationale; and the conclusion was that the sentence no longer had clear value. This led to FHIR-57354.
The FHIR-26368 Jira comment linked the origin Zulip threads. Snapshot the most relevant one:
bun run zulip:search snapshot implementers "modeling specific details in FHIR Messaging"
This February 2020 discussion showed the original motivation. Josh Mandel asked how to carry named values in a message; Lloyd McKenzie said MessageHeader.focus might be Parameters; Vassil Peytchev then objected that this broadened Parameters beyond operations and should have clear boundaries.
Another linked thread:
bun run zulip:search snapshot fhir/infrastructure-wg "Message forbids entry.request and entry.response"
was a related messaging semantics discussion. It helped explain the broader messaging context, but it was not the direct source of the Bundle/Parameters sentence.
Step 5: Confirm the Source Change
FHIR-26390 included PR/commit links:
PR: https://github.com/HL7/fhir/pull/1957
Commit: ce61932
Inspecting that commit showed the exact line added:
Bundle.entry.resource can be Parameters if referenced FHIR-26390
The commit ce61932c90c34eb60476534a308e9ec7ece36dca changed source/bundle/structuredefinition-Bundle.xml on May 13, 2022. PR HL7/fhir#1957 merged August 3, 2022 and included the broader set of Parameters limited-use changes.
Final Answer Pattern
When reporting, separate the clocks:
- Initial problem: February 2020,
FHIR-26368and Zulip messaging discussion. - Formal rationale:
FHIR-26390, created February 27, 2020, resolved July 9, 2020. - Source implementation: commit
ce61932c90c34eb60476534a308e9ec7ece36dca, May 13, 2022. - PR merge:
HL7/fhir#1957, August 3, 2022. - Publication boundary: absent from R4/R4B, present in R5
5.0.0published March 26, 2023. - Later reversal request:
FHIR-57354, created/resolved May 19, 2026 after Zulip discussion.
The causal story: the Bundle restriction was a guardrail added as part of the FHIR-26390 response to FHIR-26368. The workgroup wanted to allow Parameters in specific messaging/audit/test/documentation contexts without opening the door to arbitrary Parameters anywhere Resource or Reference(Any) appeared. The Bundle-specific "referenced by something else" wording was meant to ensure a Parameters entry had context/meaning supplied by another Bundle resource. By 2026, participants recognized legitimate standalone Bundle Parameters use cases, such as subscription notifications and batch/transaction responses, and decided the prose-only guardrail should be removed rather than converted into an invariant.
Tips
- Try multiple phrasings - FTS is keyword-based, not semantic
- Snapshot liberally - full context reveals insights that snippets miss
- Follow references - issues, threads, and pages often link to related discussions
- Synthesize cleanly - present findings as substance, not search mechanics
- Separate clocks - source commit, PR merge, Jira resolution, CI build, and publication can all differ
- Prefer exact text for git history -
git log -Sworks best with stable phrases copied from the spec - Use plain spec:locate first - default source/git resolution avoids mixing historical evidence with generated package artifacts. Use package artifacts only as an explicit publication-surface check or fallback.