Repository navigation
Added
-
The browser battery drives the published container image (#166).
scripts/ui-e2e.sh --image REFpulls it, starts it over the same two
committed templates and the same committed layout, and drives it with the
same browser, building nothing from the tree. An image that answers
/healthand serves no/uifails the run naming it, which is the one
thing the mode exists to catch. It runs only the journeys that hold for
every release, because the image is usually the last release and the rest
assert what this tree does. Thereleasedworkflow runs it weekly. -
scripts/checks/site.sh, which fails when the published site disagrees with
the tree it describes (#196). It checks that every version the landing page
states is the version this tree releases, and that every corpus figure the
site quotes is the figure the test suite asserts. The landing page said 2658
fields across 1789 groups against a suite asserting 2621 and 1775, and its
own meta description, which is what a search result shows, said "Nothing
renders a form to a clinician yet". Prose stays review-enforced: a sentence
saying a shipped capability is unbuilt reads exactly like a true one. -
A partial date is collected one component at a time (#197). Where the
template pins a precision the field keeps its native picker; where it admits
several, no native control collects that, and the fallback used to be one
text box whose placeholder was the ADL patternYYYY[-MM[-DD]]. It is now a
labelled box per component, on the precedent of the GOV.UK Design System's
date input. A number typed short is padded, so 9 for September is written
09, and a component to the right of an empty one is refused rather than
closed up: openEHR RM Release-1.1.0data_types.htmlsection 7.1.2.2 drops
components from the right and has no shape for a hole in the middle. -
A group lays its items on a two-column grid from the medium breakpoint up,
so a form of dates and counts reads across as well as down (#190). Most
kinds draw one input and share a line; an attachment, a parsable body, a
choice, an interval and an identifier keep the row, because each draws a
control inside a control or four boxes of its own. Measured from the
capture, the two committed corpus forms are 1568 and 2143 pixels tall,
against 4190 and 3934 when the issue was filed. -
The renderer reads the layout a person authored, which is half the product
and until now the browser read none of it (#201). A form is drawn in the
order they put its items in, under the names and the help text they wrote
over the archetype's, starting from the values they chose, and an item
behind a rule appears only while that rule holds. So a form grows as it is
answered instead of asking every question at once: the family history form
asks whether a family member has died, and the date and the age at death
arrive when the answer is yes. No specification governs any of that, and it
is what the overlay exists to carry.The server reads the layouts from
FERROCHART_OVERLAYS, a directory of at
most one overlay per template, and serves each asFormLayoutat
GET /api/templates/{template_id}/layout. A template nobody laid out
answers with a layout that decides nothing, so a renderer has no absent case
to tell apart from an unknown template. An overlay that will not read fails
the startup, as a template that will not compile does, and so do two
overlays over one template.e2e/overlays/carries the one authored layout
this repository ships, and the browser battery drives it. -
The browser battery opens a section the template says may be absent and
proves the fields inside it arrive (#202). A form now opens with those
sections closed, so a battery that stopped at the first screen would never
see what is in one, and the book would show a form nobody had touched. The
capture runs the same proof, which is why the picture shows an opened
section. -
scripts/checks/changelog.sh, which refuses a release section with two
headings of one change type (#184). Every pull request adds an entry under
[Unreleased], and the quick way to do that is to paste a fresh
### Changedabove the old one; three of those and one release has three
Changed sections and a reader can no longer find anything. It also checks
the headings are the six Keep a Changelog names, in the order that
specification lists them, and that none is empty. Every section in the file
is normalised to that shape, with no entry reworded. -
The round trip against a real CDR runs on every pull request (#28). The
live cases offerrochart-cdrandferrochart-servercommit a COMPOSITION
FerroCHART built and validated to a FerroEHR started from the release's own
compose.yaml, read it back, and compare. They existed, and ran only when
somebody rememberedscripts/test-cdr.sh, so the one lane that can falsify
the product's central promise was the one lane nothing enforced. A failure
now prints what the CDR logged, because the container is gone by the time
anybody looks. -
The browser battery drives and photographs every screen, not three of
eight (#192). The form screen with no template named, the three frames on
the rail whose content is still to come, and an address no route owns are
all covered, each with a proof of its own: the way out of a dead end, the
heading and the line under it, and the way back. The first run of the new
coverage found #191.
Changed
-
The Licensor and copyright holder of the project's own work is Vernum
Projecten B.V. (#214). EveryLicensor:, copyright and
SPDX-FileCopyrightTextline names the company. The licence terms are
unchanged, and maintainer credit stays a person. -
The repository moved to the FerroHEALTH organization
(https://github.com/FerroHEALTH/FerroCHART, #224); the old
rubentalstra/FerroCHARTURLs redirect. The image now publishes to
ghcr.io/ferrohealth/ferrochart, and the quickstartcompose.yamlpulls it
from there. Every tagged manifest up to v0.1.0 is copied there by digest and
stays available underghcr.io/rubentalstra/ferrochart, and its signatures
namerubentalstra/FerroCHARTas the repository that built it. The release
lanes spell the image owner as a literal, becausegithub.repository_owner
is nowFerroHEALTHand an OCI reference must be lowercase. -
The published site says what is built (#196). Every page in
SUMMARY.md
was read against the code: the landing page no longer says the product
renders nothing, the operations card no longer says the routes that do the
work have not arrived, the status list says the browser reads a layout, the
build order carries the current figures and names the milestones after it,
and the renderer page no longer says the browser honours no overlay. The
book gains the route table the "write your own renderer" page was already
pointing at, and names the layout as the fourth published document with a
version line of its own. -
The frame stops spending a fifth of the window on nothing (#193). A
320-pixel Inspector panel reading "Select a node to edit its layout" was
drawn on every screen, including four that have no node to select and
Layout itself, which says the authoring surface is not built. It arrives
with the selection that drives it (#27). A breadcrumb of one entry is no
longer drawn: it named the screen its own heading names, so Layout read
"Layout" twice. And the three frames say what their screen will do rather
than citing a tracker issue number at the reader. -
Everything the project says about itself says what is built (#196). It
was three releases behind and it understated the product to the people it is
for. The README said the round trip had never met a real CDR and to treat
the commit path as untested. The book's front page said the same, and its
renderer page said no published artefact serves the screens, when the
release binary embeds the bundle and the image copies that binary. The
landing page told a reader "the ITS-REST client, the composition builder,
and the FHIR terminology client are decided and scheduled for v0.0.5; they
are not written yet", when all three shipped in that release, and its
machine-readable version said0.0.3. The build order read as a plan for
work that has shipped, a health check example answered"version":"0.0.2",
and an attestation example named av0.0.5asset.None of it was a version bump: every claim was read against the code. What
the pages say is missing now is what is missing, which is the layout overlay
reaching the browser and a form that can commit. -
An open archetype slot reads as a place to add content, not a warning
(#180). openEHR AM Release-2.3.0AOM2.htmlsection 4.5.8 gives
ARCHETYPE_SLOTthe attributeis_closed, "closed to further filling
either in further specialisations or at runtime", and defaults it to false,
so a slot that survived into an operational template is one the template
meant to leave open. It was drawn as a yellow warning, and counted in a
banner reading "the template left content undetermined", on 116 of the 121
templates the committed pack compiles. It is a neutral notice now and the
banner counts only what is genuinely undetermined, which across the whole
pack is four nodes rather than 1194. -
The Reference Model's own container is not drawn as a group (#190). RM
Release-1.1.0data_structures.htmlsection 4.3 makesITEM_SINGLE,
ITEM_LISTandITEM_TREEthe structure an ENTRY attribute holds its
content in; they exist because the model needs a container, not because
anybody grouped anything, and the CKM archetypes head them "Tree" and
describe them "@ internal @". Their contents are drawn where they were, so
a form loses a nesting level and two meaningless headings. AnITEM_TABLE
keeps its card because a grid is a shape a person reads, aCLUSTERkeeps
its card because a cluster is the clinical grouping, and a container that
roots an archetype or repeats keeps its card because it carries something
of its own. No key, value or composition node changes. -
A timezone is chosen from the list the platform carries, and resolved to
the offset at the instant entered (#197). It was a free-text field with a
regular expression, so a person had to know their UTC offset and today's
daylight-saving state and type+02:00in a shape the pattern accepted.
The list is the IANA Time Zone Database, whose registry RFC 6557 puts with
IANA and which every current browser already carries: ECMA-402 publishes the
identifiers asIntl.supportedValuesOf('timeZone'). Nothing is vendored,
so the bundle carries no table. openEHR RM Release-1.1.0data_types.html
section 7.2.4 typesDV_DATE_TIMEonIso8601_date_time, so the value
carries an offset rather than a zone name, andEurope/Amsterdamis
+01:00in January and+02:00in July; the offset is resolved for the
instant the reader entered rather than for today. A platform that does not
publish the list still gets the offset field. -
A field that admits several precisions asked for
YYYY[-MM[-DD]], which is
ADL notation printed at a clinician (#197). It now asks for "A year, and a
month and day if you have them". The pattern still gates what the browser
accepts; only what a person reads changed. -
A duration is a number and a unit (#198). "Age at death" drew Years,
Months, Weeks, Days, Hours, Minutes and Seconds as seven number boxes plus a
checkbox, for a question a person answers with "82". The family history form
carried fourteen of those boxes, seven for the age at death and seven for
the age at onset. It is now one number and one unit, and where the template
admits a single unit there is no picker at all. A compound duration stays
reachable behind "and…", so the rare case works and the common case costs
nothing. The sign is offered only where a range could admit a negative
duration, so an age bounded at zero no longer asks whether it counts
backwards. -
Two parts of a duration naming one unit are refused.
assemblewrites the
first match per unit, so the second was being dropped without a word. -
A form is a list of questions with answers, and most of what it drew was
neither (#190). Four of the five lines a field drew were not the value.- A field carries a value or a reason there is none, never both. The
null-flavour select was drawn full width under every field that offered
one, which in the committed corpus is nearly every field, so a
fifteen-field form carried fifteen extra dropdowns reading "No value,
because: A value is entered". It showed a state openEHR RM Release-1.1.0
data_structures.htmlsection 5.2.3 forbids, sinceInv_is_null_valid
givesELEMENTno state carrying the two together. A quiet "No value"
at the end of the value row now replaces the control with the flavour and
a way back. - A field name is 14px solid ink where it was 12px grey, the same weight as
the help text under it. The name is what a person scans a form by. - The help text sits with the name it describes. Under the control it read
as a caption for the next field. - A value sits in one box, not three. The per-field fieldset keeps the
accessible name itslegendcarries and draws no border. - A date, a count and a unit stop at 28rem. Prose, an attachment and a
choice keep the width, because their answers use it. - A choice picker is labelled "Kind of value" where it said "Which of
these", and a repeat is headed "Entry 2" where it said "Occurrence 2".
Measured in the browser, over the two committed corpus forms: the family
history summary fell from 4190 to 3086 pixels and the alcohol consumption
summary from 3934 to 2828, both a little over a quarter shorter. The capture
prints the height of every screen it photographs, so the figure is read from
a run rather than estimated. - A field carries a value or a reason there is none, never both. The
-
The living style guide is no longer in a clinician's download, and the
renderer bundle fell from 410995 to 359486 gzipped, a saving of 51506 bytes
or 12.5% (#154). The design system draws every affordance the kit defines,
which instantiates every control a second time, and it is a surface for
whoever works on the kit rather than for a person filling a form. It is
behind thedesigncargo feature now, which is on by default:trunk serve
brings it up at/ui/designand the browser battery photographs it, while
the release bundle is built--no-default-features. That build answers
/ui/designthe way it answers any address it does not serve, and the rail
carries no entry for it. The feature is on by default so that an affordance
the kit defines and nothing draws is still a dead-code error (#186). -
The renderer says what a field collects instead of naming a Reference
Model class (#178). A choice between unlabelled alternatives offered
DV_COUNTandDV_QUANTITYas its options and now offers "a whole number"
and "a measurement"; an undetermined hole read "ITEM_TREE is left
undetermined." and now reads "The template leaves a group of fields here
undetermined."; the line under it says what the template does and does not
admit without spellingELEMENTorDV_INTERVAL. The class name is a fact
about the specification, and the audience came to build a form. The
refusal message built inferrochart-composeand the unlabelled-field
fallback are the rest of #178 and are not fixed here. -
A field the template never labelled says what it collects, and an
unlabelled archetype root says its concept (#178). The fallback was the
node identifier, so a form drew a heading readingat0004or
openEHR-EHR-OBSERVATION.blood_pressure.v2. An archetype identifier names a
concept (openEHR BASE Release-1.2.0base_types.htmlsection 5.4.10), so a
root is now called "Blood pressure"; below one the name leads with the value
and keeps the code, as "A whole number (at0004)", because the code is what
tells two siblings apart and dropping it would give a group several fields
with one name. A browser journey over every screen the battery drives fails
the build on an openEHR class name or a node code in the text a person
reads, and skips the monospaced surface where the path deliberately stays. -
A refusal on a link field read "The Reference Model class requires another
URI scheme." and now reads "The link has to start with the scheme this field
takes." (#178).
Fixed
-
The README's quick start runs as written (#213).
docker compose --profile demo upfailed before starting anything, because Compose resolves
FERROCHART_CDR_URLandFERROCHART_TERM_URLbefore it considers a profile;
the README and the compose header now set both to the demo services in the
same command. -
A timezone picked before the date was resolved against the wrong instant
(#197). A zone is not an offset, and the offset was resolved once, when the
zone was chosen. A reader who picked Europe/Amsterdam and then typed a
January date kept whatever offset was in force at the moment they picked. It
is re-resolved whenever the zone or the instant changes. A browser journey
pins both halves of the year. -
A date field spent half its width on a timezone it does not collect (#190).
The temporal control drew a two-column grid whatever the field admits, so a
date got one column of it and its placeholder read "Year, then month and day
if kn". The grid is one column where there is no zone to put in the second. -
A field the template fixed drew the sentence "The template fixed this
value" instead of the value, and one the template also said may be absent
could not be answered at all (#207). A template can narrow a field to one
value and still leave the element optional, which is how the family history
template asks whether a family member has died: the question is whether the
element is recorded, and yes is the only answer it admits. The value the
constraint names is now read in one place,FieldKind::only_admitted, which
the derivation and the renderer both use. A fixed field shows the value it
fixed; an optional one draws a checkbox that records it or leaves the
element out; a required one is written into the document, so a COMPOSITION
cannot go out missing a node the template requires. -
A repeating group inside an occurrence a reader added opened with one
entry, whatever its template said (#202). The count of occurrences a form is
showing is seeded from the definition, and a group inside a section nobody
had opened yet was never seeded, so the lookup answered with a hard-coded 1.
It answers with what the template requires now. The browser battery found
this the moment it started opening a section rather than photographing a
closed one. -
A section the template says may be absent opens with none of it (#202).
A repeatable group opened showingmax(1)occurrences, so a group whose
template says0..*opened one, expanded, with every field inside it drawn.
openEHR AM Release-2.3.0AOM1.4.htmlsection 4.3.6 makesoccurrencesthe
count a node may appear in the data, so a lower bound of zero is the
template saying none of that section is a complete answer, and the renderer
was discarding the only statement a template makes about how much of itself
applies. The family history form met a reader with a family member they had
not said existed and a clinical history for them. It now opens as a heading,
a description and "Add one". The floor on removing followed the same rule
and used to be one whatever the template said, so an optional section, once
added, could never be put back. -
A field the template fixed showed no value and offered "No value" beside it
(#190). "Deceased?" asked a question, answered "The template fixed this
value, so nothing is entered", and then invited the reader to say there was
no value. It shows what the template fixed it to now, and a fixed field
offers no null flavour, because a value the template gave is not one a
person can decline to give. -
An address no route owns lost the whole application (#193). It was drawn
outside the shell, so a reader who mistyped one had no rail, no theme
control and one link. It is a route inside the shell now, and it points at
the template library, which is somewhere a reader can act. -
The form screen asked the server for a template with no name (#191). Landing
on/ui/formsfrom the rail sentGET /api/templates//definitionand took a
404, because the guard sat on what the screen draws and not on the request.
The screen looked right, which is why it went unnoticed until the battery
started photographing it.
Security
rustlsmoves to 0.23.45 for RUSTSEC-2026-0285, TLS 1.3 handshake
messages incorrectly accepted across encryption level boundaries (#221).
The lockfile moves; no source changes.