refactor(ui): unify the CapabilityStatement implementation shared by HFS and HTS - #845
Merged
Merged
Conversation
… and HTS HFS and HTS each parsed and rendered the same `/metadata` document with their own code: two `CapabilityView` structs in two crates, two parsers, two templates. Every improvement had to be made twice and never was — version-correct specification links (#797) and the interaction colour coding landed on HFS only, and the HTS page rendered bare `<span class="tag">` chips and no links at all. The projection and the four common cards now live once, in `helios_ui_chrome::capability` (#543's crate, as #808 asked the PR to say): - `build_view` + the view model + `safe_canonical_href`, `system_interaction_href`, `resource_definition_href` and `interaction_tag_class`, as pure functions over `serde_json::Value`. - `templates/partials/capability-{summary,interactions,operations, resources}-card.html`, rendered through `CapabilityCards`. The crate stays a leaf: `serde_json` + `url`, no axum, no HTTP client, no FHIR schema pack. The two questions it cannot answer are supplied by the caller — `DocsVersion` (the release to link into, so this crate is not dragged into the R4/R4B/R5/R6 feature matrix) and `CoreResourceCatalog` (whether a type has an official page in that release; HFS answers from the validator's core packs, HTS from the three types a terminology server serves). What each page keeps, because the difference is real and not drift: - Fetching. HFS self-calls over loopback; HTS proxies two upstreams. - Degradation. HFS still degrades the whole page to one warning; HTS still degrades card by card, now by handing the shared cards the `hts-degraded-reason-*` sentence as a notice — so per-card degradation costs no duplicated card headings. - The raw fold. HFS's paginated JSON-fragment tree (#798) and HTS's byte-capped inline `<pre>` both keep a 400 KB+ statement off the page by different means; neither is the other's markup. - The Terminology capabilities card, which only HTS can have. Behaviour changes, all on the HTS page: - Resource types and operation definitions are now links into the release the binary was built for; interaction verbs carry the semantic tag palette. - "Base URL" now shows `implementation.url` rather than the statement's own `url` canonical, which is what the label promises. HTS emits no `implementation.url`, so it renders an em-dash; the canonical is still in the raw fold. - Absent summary fields render an em-dash on both pages. - `rest[]` is flattened across every server component instead of reading `rest[0]` (HFS) — client-mode components are excluded. Refs #808 Claude-Session: https://claude.ai/code/session_01Kos63w6bGcUoLtCxWGDaZY
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…lity-statement Claude-Session: https://claude.ai/code/session_01L43Eaf1uohC53Fqy71KNJS
Generalizes HFS's #798 bounded JSON-fragment engine (json_view + capability_json) from crates/ui into helios-ui-chrome behind a FragmentEndpoint, so HTS's raw fold now pages through the same htmx-lazy, syntax-highlighted tree HFS renders instead of a byte-capped <pre>. HTS gets its own /json-fragment route and ?raw=1 no-JS fallback to match. Also fixes two review findings on the original unification: - Terminology Capabilities now wraps its fields in card__body/kv-grid like Server Summary, instead of sitting flush against the card edge. - HTS's page was missing the capability-json.js/json-view.js script includes that drive the fold's toggle behavior, so opening it fell back to a "Load JSON" link instead of expanding immediately like HFS. Added regression tests in tests/chrome_parity.rs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ent' into feat/808-unify-capability-statement # Conflicts: # crates/hts-ui/tests/chrome_parity.rs
crates/ui/templates/** and crates/ui-chrome/templates/** were already pinned to `text eol=lf` (#671 — Askama output is asserted against LF string literals in tests) but crates/hts-ui/templates/** was never covered, so this Windows checkout had every HTS template on disk in CRLF. It went unnoticed until the newly-merged chevron-left.svg byte-comparison test exposed it. Add the missing gitattributes rule and re-checkout the tree so it takes effect locally. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…lity-statement # Conflicts: # crates/ui/src/lib.rs
eterceros
marked this pull request as ready for review
September 2, 2026 01:42
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
eterceros
self-requested a review
September 2, 2026 09:03
eterceros
approved these changes
Sep 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #808.
Where it landed
helios-ui-chrome— the crate #543 already created. #808 asked the PR to saywhich: this stacks on that extraction rather than creating a new minimal crate.
What is shared now
helios_ui_chrome::capabilityowns the projection and the four cards bothproducts stack:
build_viewand the view model (CapabilitySummary,SystemInteraction,OperationRow,InteractionTag,ResourceRow), plussafe_canonical_href,system_interaction_href,resource_definition_hrefandinteraction_tag_class.templates/partials/capability-{summary,interactions,operations,resources}-card.html,rendered through a
CapabilityCardsbuilder.The crate stays a leaf —
serde_json+url, no axum, no HTTP client, noFHIR schema pack — because the two questions it cannot answer are supplied by
the caller:
DocsVersion(which release to link into)matchonFhirVersionDocsVersion::from_code(state.fhir_version)CoreResourceCatalog(does this type have an official page in that release?)Keeping
FhirVersionout is deliberate: its variants are#[cfg]-gated, sonaming it here would drag a leaf crate into the R4/R4B/R5/R6 feature matrix of
both products for the sake of four documentation roots.
What each page keeps, because the difference is real
one source to fail. HTS still degrades card by card, now by handing the
shared cards the
hts-degraded-reason-*sentence as a notice, so per-carddegradation costs no duplicated card headings.
HTS's byte-capped inline
<pre>both keep a 400 KB+ statement off the page,by different means. Neither is the other's markup, so neither moved.
Unifying that is
json_view's problem, and hts-ui: "Raw request and response" fold has no expand affordance, no highlighting, and drops the payload on errors #803 has since become adifferent issue.
Behaviour changes
All on the HTS page, which is the one that had been missing the improvements:
binary was built for, and interaction verbs carry the semantic tag palette
(
tag--member/tag--config/tag--excluded/tag--muted).implementation.url, which is what the labelpromises, rather than the statement's own
urlcanonical — that identifiesthe document, not the endpoint. HTS emits no
implementation.url, so therow renders an em-dash; the canonical is still in the raw fold.
pages.
rest[]is flattened across every server component instead of readingrest[0](HFS's old reading).restis 0..*;mode: "client"componentsare excluded, since they describe what the server calls, not what it serves.
No new Fluent keys — the
cap-*catalog was already shared.Acceptance criteria
page, and states plainly when it truncates. (Unchanged on both sides:
HFS never inlines it, HTS caps it and says so. The HTS test that fires a
bulky statement at the page still passes.)
crates/ui/src/capability.rs'sprojection tests moved to
crates/ui-chrome/src/capability.rsand grewcard-rendering coverage; what stays in
crates/uiis what only HFS canassert (the
FhirVersion→DocsVersionmapping, the core-packcatalog's per-release answers,
CreateTargets).crates/hts-ui/tests/capability.rskeeps its page-level tests and itsCSS-class guard now scans the shared partials too.
Verification
All green. Two flakes surfaced in
crates/ui/tests/bulk_export_http.rs(
a_retry_that_loses_its_cas_retries_transient_cleanup_until_404, thena_poll_version_bump_does_not_discard_a_concurrent_second_start) — differenttest each run, both pass on rerun, and neither touches this code.
Not run: the Playwright e2e suite. The specs that pin this page's structure
(
design-system.spec.tsassertssection.card > .card__body > .kv-grid >seven
.detail__field;capability-statement.spec.tsasserts the filter'shidden version input, the resource links and the JSON fragment tree) all
target markup this PR preserves, and the shared crate has unit tests pinning
that shape — but they are worth a run before merge.
https://claude.ai/code/session_01Kos63w6bGcUoLtCxWGDaZY