Repository navigation
Releases: FerroHEALTH/FerroFORMS
Release list
0.1.1
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
keep...
0.1.0
Added
- "Write your own renderer" in the book, which is the page that makes the
published form definition a contract rather than an internal type (#26). It
describes the three documents and their version, the externally tagged
shape, why a key carries five parts, how an entered value is addressed
inside a repeating group, where a refusal is drawn and where it is not, and
which two changes are deliberately not a version bump.
Changed
- The published documents are externally tagged, and
FORMAT_VERSIONis 2
(#154). A variant is spelled{"quantity": {…}}where version 1 wrote
{"kind": "quantity", …}. The reason is measured rather than aesthetic: an
internally or adjacently tagged enum cannot be deserialised in one pass, so
serde buffers the input throughContentand monomorphises the whole subtree
twice. Over the renderer that cost 33269 gzipped bytes, 7.6% of the bundle
a reader downloads, confirmed by building both ways. The same buffer is
what #103 broke against, when a dependency enabling
serde_json/arbitrary_precisionstopped these documents deserialising with
no line changed in this tree. An externally tagged enum never enters that
code path, so the change removes a hazard class as well as the bytes.
Fixed
- A refusal from the composition builder is drawn on the repeat it came from
(#152). A validation failure carried no occurrence, so a form with a
repeating group showed "this value is out of range" under every repeat of
the field rather than the one that was wrong, and 67 of the 121 forms the
committed pack derives to carry a repeating group. The builder walks a form
with the occurrence of every repeating group above each field, so it knows
the address and now states it. - A refusal from the operational template still states no occurrence and is
still drawn on every repeat. Its Reference Model path carries positional
predicates into attribute arrays, and nothing establishes their
correspondence to a form's occurrence path, so an address there would
sometimes be invented. The type says which judgements know and which do not.
Fixed
- A COMPOSITION built around a template rooted below COMPOSITION states its
template identifier where the template is active, and a real CDR now accepts
it (#163). It was written at the wrapper COMPOSITION, a document FerroCHART
supplies from configuration and where no template is active, so FerroEHR
4.1.1 refused 66 of 66 such templates withexpected RM type conforming to OBSERVATION but found COMPOSITION. Every one had passed FerroCHART's own
gate first, which is the failure this project is least allowed to have.
openEHR RM Release-1.1.0common.htmlsection 3.2.3 makes the identifier
conditional: it is stated "if a template was active at this point in the
structure". - The read-back looks for the template identifier wherever it legitimately
sits, rather than at the document root alone, so the guard that refuses a
composition from another template keeps working for the 113 templates whose
identifier moved.
Changed
- The tied-sibling fold cites ADL 1.4 section 5.3.4.2 rule VCOC rather than
AOM 2's VSONCO (#142). Four places in this repository said VSONCO was "the
only definition of collective sibling occurrences openEHR publishes; AOM 1.4
defines none", and that is false: ADL 1.4 publishes VCOC, in the generation
these templates are written in, and it states the sum of upper bounds, which
is the bound that was in dispute. The arithmetic is unchanged, because it
was already what VCOC requires. The upstream-report register loses a
consequence it should never have carried.
Added
- The server serves the renderer at
/ui/, and/uiredirects onto it
(#166). The bundle Trunk builds rides inside theferrochartbinary as a
table of files the build script writes, so a release archive and the
container image behave the same and a request path never reaches the
filesystem. A path under/uithat names a file type the bundle does not
hold answers404, and any other path answersindex.html, which is how a
client-side route deep-links. Content-hashed assets are served
public, max-age=31536000, immutableandindex.htmlno-cache, each with
its own media type andX-Content-Type-Options: nosniff.FERROCHART_UI=off
drops the routes, and a binary built without the bundle serves no/ui
route. With the quickstartcompose.yamlthe address is
http://127.0.0.1:8080/ui/. - Every release carries a second CycloneDX document,
ferrochart-renderer-<tag>-<target>.cdx.json, attested against the same
archive (#166). The server's own document reaches none ofleptos,
wasm-bindgenorweb-sys, because the cargo feature that compiles the
bundle in is empty and there is no dependency edge to follow, so the
WebAssembly the binary serves to every reader was absent from the document
that claims to describe what shipped. The lane refuses to publish a renderer
document that lists none of those three. scripts/checks/palette-utilities.shrefuses a control that draws its own
focus ring or setsoutline-none(#157). The stylesheet's base layer holds
one:focus-visiblerule for the whole application, and that rule is what
guarantees every control has an indicator; a class that overrides it removes
one silently. The assertion existed in one kit module of eleven and now
covers every source file, including the ones nobody has written yet.
Added
- The book shows the renderer, and a test takes the pictures (#93).
scripts/ui-e2e.shstands up the form surface over two committed CKM
templates, serves the renderer bundle against it, and drives a pinned
headless Chromium through the template library, both forms and the design
system over WebDriver.--docs-shotswalks the same screens again on both
grounds and writes one PNG each into
website/book/src/operate/img/renderer, which the new
website/book/src/operate/renderer.mdpage and the book's introduction
embed. The journeys and the capture share one definition of what a screen
must have drawn (e2e/tests/it/screens.rs), so a screenshot cannot outlive
the journey that proves it, and an ordinary run rewrites no tracked file.
Aui-e2eCI job runs the battery on every pull request, uploads what a
failed journey was looking at, and fails when a run changed a tracked file.
scripts/checks/docs-shots.shrefuses an image that is not a PNG, is small
enough to be a blank page, carries a name the capture does not write, or is
committed with no page embedding it. - The form screen draws the controls (#26).
/ui/forms/{template_id}fetched
a definition and rendered its group tree as headings and labels, because the
request module and the controls were built in parallel and nothing joined
them. It now renderscontrol::group::FormBody, so a clinician sees the
form rather than its outline: inputs, the null-flavour affordance beside
each field, a repeatable group carrying its occurrence bounds, and a field
the template fixed saying so instead of offering an entry. trunk serveproxies/apias well as/health, so a local session runs
the whole surface rather than half of it.
Added
- The renderer's conversation with the server (#138). One module,
app/ferrochart-renderer/src/api, owns every request the browser makes over
gloo-net, and a test scans the crate's own sources and fails when any other
module names the transport. ItsApiErrorcarries the status the server
answered with and the bytes it answered with, reads the surface's error
document so the stable code and the message arrive as fields, and passes a
CDR's owncdr_statusandcdr_bodythrough. A request that never reached
an answer reports no status rather than an invented one. - The template library at
/ui/templateslists what the server holds, and
/ui/forms/{template_id}draws the group tree of one form definition as
headings with the field labels beneath. A read renders its failure inline,
with the status, the server's code, the CDR's answer and every link of the
cause chain, and never as a toast. app/ferrochart-renderer/src/placement.rs, the lookup a control calls to
find the validation failures that belong to it. A failure lands on every
control its node key names, and one about how many nodes there are is drawn
once rather than under every repeat. A failure that names no field of the
form stays reachable throughunplacedand is shown beside the form.- A control for every one of the eighteen
FieldKindvariants (#137), each
rendering from a pure admission function that refuses what the template
refuses: a boolean whose two flags are independent, a text field whose
closed list is the whole permitted set, a coded field that says plainly when
a value set needs a terminology server rather than inventing an expansion,
an ordinal that stores the symbol and shows the rubric, a quantity whose
magnitude range and decimal precision follow the chosen unit, a proportion
that holds theDV_PROPORTIONinvariants of openEHR RM Release-1.1.0
data_types.htmlsection 6.2.10, and a date, time and date-time at the
precision the template states and no finer. - The null-flavour affordance beside a field, and the add and remove pair a
repeatable field or group carries, disabled at the template's ceiling and
floor. Content the template left undetermined is drawn as a visible hole
carrying its reason and captures nothing. - Ten design-system affordances beside the buttons and surfaces already there:
the table shell, tab pills, the segmented control, badges and status pills,
the stat tile, the empty state, the loading skeleton, the confirm dialog,
the toast queue, and the inline notice family. Each has one definition, and
the living style guide at/ui/designdraws every on...
0.0.5
Added
-
The web template compatibility surface (#54):
ferrochart-webtemplatereads
a web template another tool produced into a form definition, and writes a
form definition back out as one. The web template is a compatibility target
and no openEHR specification defines it, so the crate sits beside the
compiler rather than under it and linksferrochart-formand nothing else
of this tree. A member the form definition does not model, a third party's
annotations among them, survives a round trip verbatim: a read keeps each
node's source object beside the definition, and a write starts from it, so
a node the definition still states as it was read goes back out untouched.
Over the whole vendored CKM pack, every document built from it reads and
writes back as the document it came from, with annotations, per-option
terminology bindings and members this crate has never seen put into it
first.minandmaxare read as the flattened integers they are, and
template version detection readstemplateIdand the root archetype
identifier, neversemVer, which not one document of the pack states. -
A COMPOSITION reads back into the form it was built from (#23), completing
the pair. The reader walks the data rather than the form, so a node the form
does not cover is reported instead of silently dropped, which is what an
editing round trip needs if it is not to delete a colleague's content. Over
the committed pack every value entered comes back against its own field, a
null flavour and its reason survive both directions, and a repeated field
reads back into the right occurrence. Where several form nodes share an
identity and the data cannot fill them all, the assignment is a guess the
reader reports as one rather than presenting as certain. -
The pre-post validation gate (#24), in the new
ferrochart-validatecrate
andferrochart_server::commit. A COMPOSITION FerroCHART built is judged
against its own operational template before any request reaches a CDR, and
every failure it finds is keyed onto the form definition, so a renderer puts
it on the field a clinician got wrong. The gate is the only path in this
workspace from entered values to a CDR write, and the ordering is tested by
construction: the client is pointed at a port nothing listens on, so a
refusal proves the gate ran first. A negative case per constraint kind
covers the value set, the numeric range, the string pattern, the data value
class, the occurrences, the cardinality, a mandatory node left out, and a
code outside its openEHR terminology group. A live case commits through the
gate to a real CDR and reads the document back. -
A validation report a renderer reads, in
ferrochart-form. It carries one
failure per problem, each with the node key it belongs to, the path it was
found at, its message and its category, so a renderer still links one crate
of this tree. A failure that names no field of the form keeps its path and
is reported unplaced rather than dropped. -
A best-effort reader for a CDR's own error body, marked vendor-specific.
openEHR ITS-REST Release-1.1.0 publishes no path on an error entry, so the
reader recognises one inside the prose where a CDR writes one and reports
the refusal verbatim where it does not. -
Three layers that guard the two documents FerroCHART publishes, after a
dependency's cargo feature took one of them off the wire with no change to
this tree (#107).serde_json/arbitrary_precision, enabled for the whole
workspace by one new dependency, sends a number to serde's content buffer as
a map, and the internally tagged enums of the form definition stopped
deserializing. Each layer catches something the others do not: the form
definition and the layout overlay each round-trip a document carrying every
number-bearing shape they publish, so a document that no longer reads back
fails; each also carries a golden snapshot of its exact bytes, so a document
that still reads back and no longer means the same thing to a consumer
written against the old bytes is a diff a person reads and accepts; and
scripts/checks/serde-json-features.shcompares the resolvedserde_json
feature set against the reviewed one, failing on a refused or an unreviewed
feature and naming what turned it on, which is the cause the two tests can
only report the effect of. The form definition'sFORMAT_VERSIONnow says
what the number covers, so a byte change is a decision rather than an
accident. -
The composition builder (part of #23), in
ferrochart-compose. It turns
what a clinician entered into a COMPOSITION, working from the form
definition alone and never from the template, which keeps that definition a
contract a third party can commit against as well as render against. 78 of
the 121 committed templates that read build a document; the 36 rooted at
CLUSTERare refused by name, because a fragment is not a document.
docs/architecture.mdsections 5.3 and 5.4 record where every Reference
Model field a form never shows comes from, which of them FerroCHART invents,
and what a round trip may assert. -
The terminology client (#25), in
ferrochart-term. It resolves the codes
behind a coded field, and the default path needs no network: an
archetype-localat-code list comes out of the template's own terminology
with its rubrics in the requested language, and openEHR's own terminology
groups come from theopenehr-termcrate. A test proves the local path
issues zero HTTP requests. Where the template names a target, the client
calls a FHIR R4 4.0.1 terminology server:ValueSet/$expandto fill a
picker,CodeSystem/$lookupfor the display text of an enumerated external
code, andValueSet/$validate-codeto confirm a chosen one. Every request
is formatted against the generatedfhir-typesoperation contracts, and no
FHIR resource is hand-written. A refused expansion, a value set the server
does not hold, a timeout and a binding no FHIR request can name are each a
typed error carrying the upstream status, theOperationOutcomediagnostics
where the body carries one, and the raw body regardless. None of them is an
empty picker. Expansions are cached per template, language and request, and
a recompile of one template drops that template's entries. -
FerroCHART does not parse the SNOMED CT expression constraint language, and
passes an expression through to the server as the implicit value set of FHIR
R4snomedct.htmlsection 4.3.1.0.9. The reason is in
docs/architecture.mdsection 7.3 and its decision register: executing an
expression needs a SNOMED CT release, a concept store and a closure index,
which is a terminology server's work, andsct-eclwould add a BUSL-1.1
licence exception for a check the server repeats anyway. -
scripts/test-term.shruns the terminology client's wire tests against a
real server, HL7's public R4 service by default and the compose demo profile
with--ferroterm. The cases are#[ignore]d, so a run without a server
reports them as ignored rather than as passes. -
Every chapter that asks for a table of contents is checked to have one
(#98).scripts/checks/book-toc.shreads the rendered pages, and the docs
lane runs it after the site is assembled, because mdBook reports a
preprocessor that ran and produced nothing as a warning at most and would
otherwise publish a chapter with a missing table of contents and a green
build. -
The terminology of an archetype can be enumerated, where it could only be
asked about a code the caller already held (#91).Terminology::definitions
returns the rubrics one language states,all_bindingsand
all_constraint_bindingsreturn every binding as a code and one target, and
value_setsreturns every enumerated value set with its members. All four
borrow the terminology and come back in code order, so a caller that wants
one code still pays for one lookup. The terminology client needs this to
emit an archetype's value sets as FHIRCodeSystemandValueSet
resources, which a lookup-only interface cannot do. -
The openEHR ITS-REST client (#22), in
ferrochart-cdr. It creates, reads
and updates a COMPOSITION, uploads and retrieves a template in both ADL
generations, and creates the EHR a first run needs. Every identifier the
wire carries is its own type, because the three composition operations spell
uid_based_idalike and accept different forms of it: the read takes either
a versioned object uid or a full version uid, and the update takes the
object uid only.If-Matchis sent quoted and unweakened on every update,
Preferis always explicit, and a 412 is a concurrent-edit outcome the form
surfaces rather than a retry. Every status openEHR ITS-REST Release-1.1.0
publishes has its own error variant, plus a catch-all, because the same
section permits statuses beyond that set. Nothing absorbs an upstream
failure into an empty value. -
The FHIR model comes from the published
fhir-typescrate (Apache-2.0,
pinned at 0.1.85), whichferrochart-termnow depends on (#95). It is
generated from the HL7 FHIR packages, soValueSet,CodeSystem,
ConceptMap,ParametersandOperationOutcomeare read from a generated
model the way the openEHR types already are, and no FHIR resource is
hand-written here. The crate also carries$expand,$lookupand
$validate-codeas typed request and response shapes that convert to and
fromParameters, which is why the terminology engine of the same release
line is left alone: FerroCHART is a client of a terminology server rather
than one.docs/architecture.mdsection 7.3 records both decisions.
Fixed
- The last hand-built archetype identifier in the three-part
template_id
form, inferrochart-form's wire test. #127 corrected the.optfixtures
and its check covers those only, so t...
0.0.4
Added
- Every icon the site should carry, from the one authority.
/favicon.icois
served at the site root for a client that asks for it directly, the landing
page links the brand palette instead of restating its hex values, and the
book's tab icons are staged fromassets/brand/at assembly time rather than
committed as a second copy.assets/brand/README.mdrecords where each file
is used, and why five of them are deliberately used by nothing (#94). - A field carries the reference bands its template states beside the value
(#52). openEHR RM Release-1.1.0data_types.htmlsection 6.2.1 gives every
DV_ORDEREDanormal_status, anormal_rangeand
other_reference_ranges, none of them entered by a clinician, and the
derivation used to drop all three. They now sit in
field::ReferenceRanges, reachable only from a field or a choice
alternative and never from a field's kind or a group's items, so a renderer
shows them without reading a Reference Model class name to tell metadata
from an entry field. A band reuses the value shape of the class it is stated
over: a status carries the value set of a coded field, and a range carries
the interval shape of the class the value collects. The three attributes are
no longer refused as unentered, and a band the derivation cannot represent
is refused by name rather than dropped. - What "equivalent" means for the two ADL readers is defined in one place,
cited, with the six differences accepted as legitimate and a count for each
allowance it spends, and the matched pair is compared as a whole value so a
field the internal constraint model grows cannot be left out of the
comparison (#59). The published dual-dialect archetype library cannot supply
the ADL 1.4 half of that comparison: the twins are ADL 1.4 text, the ADL 1.4
reader takes an ITS-XML operational template, nothing in the pinned crates
turns one into the other, and every one of the 321 ADL 2 halves declares
itselfgeneratedfrom its 1.4 twin. What the library does prove is
asserted over all 321 pairs: for the 300 whose ADL 2 half the reader reads,
the internal model names the same concept, in the same language, over the
same Reference Model class as the published ADL 1.4 twin. docs/architecture.mdsection 6.5 records the five things the replay's
outcome classes left underdetermined, decided while implementing #19 and
#20. The load-bearing one is that aretypedoutcome is unreachable from
the key alone, because a field's key step saysELEMENTwhile the field
collects aDV_*class. Section 11 states where the line between
ferrochart-formandferrochart-overlayfalls and why (#72).docs/architecture.mdsection 6.5 records why one document state is a
refusal at authoring time and an advisory afterwards, and section 11 stops
reading as thoughferrochart-formwere dependency-free. Two doc comments
that had gone stale with the type move are corrected, and the hint map's
ordering is asserted beside the type that has to keep it rather than in
another crate's test suite.- The book covers what the overlay carries, why its geometry is a column grid,
and what a replay reports, and says which releases are published and what
the compiler measurably does. - The citation rule says that
docs/architecture.mdis citable, because it is
the design of record and permanent, while a plan document is not. The rule
banned "an internal markdown file" while justifying the ban with plan
documents, which left every architecture citation in the overlay crate
arguably against the rules. Also fixes acodegen.mdreference to a
CLAUDE.mdheading that does not exist (#77) and a prose deferral where a
TODObelongs (#78). - The replay runs against the largest real difference the corpus can produce
(#21): an overlay authored on every node of one CKM entry, replayed against
a second entry for the same clinical concept. Nothing survives, and that is
the point: the two differ in the pinned name of nearly every node, so 0 of
171 entries match and the replay reports 7 moved, 164 disappeared and 181
appeared rather than rebinding layout onto nodes that merely look similar.
Every entry is still accounted for and every key still held. - A printed node key carries every part that decides whether it matches (#83):
the identifier, the Reference Model class, the pinned name and, where the
step is not the first of its tied siblings, the ordinal. The shape follows
the AQL node predicate. Before this, 40 distinct keys printed alike across
two real CKM entries while none of them matched, so a replay report could
show a person the same string twice and ask them to decide between them. A
test over the whole corpus asserts that two keys printing alike are the same
key. - The layout overlay and its store (#19), in
ferrochart-overlay. An overlay
carries what no specification governs: field order, authored sections,
labels, help text, defaults, conditional visibility and widget choice. It is
stored apart from the compiled definition and keyed by the node key of
docs/architecture.mdsection 6.2, whose every step carries the Reference
Model attribute, the node id, the archetype id, the Reference Model type and
the pinned name, with a sibling ordinal only where all five tie. The store
normalizes a key against the definition rather than trusting it: an entry
that decorates no node of the form is refused, and an entry whose key names
several nodes is reported as positionally keyed instead of being resolved
silently. It records which form the template identifier takes, because
openEHR ITS-REST Release-1.1.0definition.htmlresolves a partial
identifier to the latest major version, and none of the 123 committed CKM
templates pins a release in its own identifier. The serialisation is
byte-deterministic and round-trips. - The replay and the differential report (#20). A replay against a recompiled
definition classifies every entry as matched, disappeared, moved, ambiguous,
reordered or retyped, and names every node of the form that carries no
layout yet. Nothing is discarded and nothing is rebound: an unmatched entry
is retained against its old key so a later revision that restores the node
restores its layout, and a move is a suggestion a person accepts. The report
prints as text a form author can read, saying which entries survived, which
need a decision and what changed about each. Authoring a layout on every one
of the 4,447 nodes of the committed pack and replaying it comes back fully
matched, and the 102 nodes in 4 templates that no key can tell apart are
reported rather than guessed at. - The overlay's geometry (#69), a column grid that stores no coordinate. A
section declares a column count of 1 to 12, and an item carries a span, a
break-before flag and a width hint in character units. Nothing stores a row
or a column index: an item's place is the sibling order the overlay already
carries plus its span and break, so the visual order of a form equals its
source order by construction, which is what W3C WCAG 2.2 success criterion
1.3.2 asks for. A span wider than its section is refused naming both
numbers, a section wider than four columns is stored and advised against,
and a renderer clamps a span to a narrower grid rather than the overlay
dropping it, so one authored form serves a ward-round tablet and a desk. A
per-item hint map carries what the grid cannot express, uninterpreted and
outside the clean-replay guarantee. A replay reports what became of the
geometry beside what became of the key: a span that no longer fits the
section it lands in, and an entry whose section was dropped. The overlay
format version is 2, and version 1 is refused rather than read as a form
whose author chose one column. - A landing page at the site root, with the book under
/docs/(#92).
scripts/site/assemble.shputswebsite/landing/at/, the mark and the
favicons fromassets/brand/under/assets/brand/, and the built book
under/docs/; the Docs workflow uploads that assembled directory. The
book'ssite-urlis/docs/, so its asset, search and 404 links resolve
under the prefix, and its 404 page is promoted to the site root where
GitHub Pages serves it for a miss anywhere. The page draws only on the
"Rose & Iron" tokens and the contrast ratiosassets/brand/README.md
records, and it separates what is built from what is not: 121 of the 123
vendored CKM templates derive a form, and nothing yet renders a form to a
clinician, builds a COMPOSITION, or offers a surface for authoring layout.
Changed
- The overlay's layout types moved from
ferrochart-overlayto
ferrochart-form, wheredocs/architecture.mdsection 11 puts them (#72).
Layout,Section,Visibility,Condition,WidgetNameand the column
grid now live beside the definition they decorate, because a renderer applies
a layout and has no business with the store, the authoring session or the
replay, which read and write files and compare two definitions. The stored
document is unchanged, byte for byte.ferrochart-overlaykeeps the store,
the key normalization, the replay and the report, and re-exports nothing it
does not own, so a caller that needs a layout type namesferrochart-form.
scripts/checks/crate-closure.shis the new CI lane that holds the boundary:
it walks the resolved dependency closure and fails whenferrochart-form
links any other crate of this tree, or whenferrochart-rendererlinks
anything butferrochart-form.
Fixed
- The compiler refuses a quantity magnitude or precision it cannot represent,
naming the units and what it found, instead of reading part of the
constraint and leaving the field wider than the template (#86). - The compiler refuses a reference band stated on the end of an inte...
0.0.3
Added
- Snapshots of the derived form over the whole vendored corpus (#18). An
inventory carries one line per template with its group and field counts and
the kinds it derives, so a change shows as one changed line naming the
template rather than as a moved total; four whole documents are snapshotted
beside it, from a 4-field form to a 303-field one. A test also derives twenty
templates twice and compares the bytes, so the format's determinism claim is
checked rather than trusted. - The ADL 2 reader runs over the published archetype library (#49): 322
archetypes parse, 306 flatten into an operational template, and 301 read.
The 5 the reader refuses are mandatory unfilled slots, which is the
adjudication on #13; the 16 it never sees are refused by the upstream
flattener because a differential path in a specialised child does not
resolve in its parent, in a 2013 export that predates the AOM 2 validity
rules. The two families are counted apart, because they belong to different
owners. CI fetches the library and setsFERROCHART_REQUIRE_ADL2, so a
fetch that stops working is a red build rather than a skipped suite. - A fetch script for the ADL 2 archetype library (#49), pinned to a commit of
openEHR/adl-archetypes. It brings 322 ADL 2 archetypes and 330 ADL 1.4
twins, 321 of them the same archetype in both dialects, which is the first
real input for the property that both readers fill one internal constraint
model. Nothing from it is committed: one of the 652 files states a licence
and the rest state none, so the tree is fetched into a directory
.gitignorerefuses andPROVENANCE.mdrecords the omission. - The published form definition type (#17), in
ferrochart-form. A definition
is a tree of groups rooted at the template root; a group holds items, an item
is a nested group or a field, and a field carries one of eighteen field
kinds, one per thing a clinician can enter. Labels and help text are resolved
per language from the archetype terminology, occurrences carry repeatability
and optionality, anELEMENTthe template permits to be omitted carries the
four null flavours openEHR RM Release-1.1.0data_structures.html
section 4.1 names, and adefault_valueprefills the field. The crate
performs no I/O and depends on nothing else in the tree, so a third party can
write a renderer against it. Every map in the format is ordered and every
list keeps template order, so recompiling an unchanged template produces an
identical document. - The field derivation (#16), in
ferrochart-compile:docs/architecture.md
section 5 implemented against the internal constraint model, so it is written
once for both ADL generations. Each permitted unit of a quantity carries its
own magnitude bounds and decimal precision, so changing the unit changes
both; a coded field's value set is enumerated with its rubrics where the
archetype lists them and marked for expansion at render time where it only
names them; an ordinal keeps list order, scores by its integer and stores its
symbol's code; a date, time, date-and-time or duration pattern is honoured
component by component; an occurrences upper bound above one makes an item
repeatable; anassumed_valuenever reaches the data; and an open slot is
recorded as undetermined content rather than rendered. A constraint the
derivation does not understand is a typed error naming the node, never a
permissive field. Each of the twelve casesdocs/architecture.md
section 5.2 lists as ungoverned carries a decision in code labelled as
FerroCHART's own design. All 121 committed CKM templates the reader reads
derive a form.
0.0.2
Added
- A
./.github/actions/setup-rustcomposite action (#33). The six gating jobs
inci.ymland the coverage job insonar.ymlcall it instead of each
carrying its own pinned toolchain step. The release lane keeps its own,
because a publishing lane restores no cache, and now says so at the step. - The internal constraint model and the two template readers that fill it
(#13, #14), inferrochart-compile. Both ADL generations normalize into one
model, so the field derivation is written once and nothing above that point
can tell which reader produced a node: each node carries a node identity, a
Reference Model type, an occurrences interval, a constraint payload, the
terminology in scope, and the five parts an overlay key step needs
(docs/architecture.mdsections 3 and 6.2). The ADL 1.4 reader walks a
parsed.opt, resolvinguse_nodeinternal references the vendor flattener
left in place and reading the constrainers only the ITS-XML family declares
(C_DV_STATE,C_CODE_REFERENCE,T_COMPLEX_OBJECT). The ADL 2 reader
starts from source archetypes, flattens them and generates the operational
form itself, folding theC_PRIMITIVE_TUPLEshapes for quantity and ordinal
into the same payloads the ADL 1.4 domain types produce, and carrying
constraint_status, which ADL 1.4 cannot state. A node with
occurrences matches {0}is absent from the model, and a template that
requires content at a slot it never filled is refused with a typed error
naming the slot rather than rendered as a guess. 121 of the 123 committed CKM
templates read; the two refusals are required unfilled slots. - A crate-scoped
deny.tomllicence exception foropenehr-term, which
embeds the openEHR support terminology under CC-BY-SA-3.0 and reaches this
tree throughopenehr-itsandopenehr-adl. - The release supply chain (#39, #34): a
v*tag now publishes a container
image, SBOMs, checksums and Sigstore attestations beside the binaries, and a
quickstartcompose.yamla downloader can run without a clone. The binaries
and the image are built in reusable workflows called by jobs that carry no
steps of their own, which is what the SLSA Build Level 3 claim rests on;
cargo auditablewrites the dependency list into every shipped binary,
CycloneDX describes it beside the archive, and syft describes each image
platform from that embedded list. A consumer verifies with
gh attestation verify --signer-workflow, andfinalize-releaserefuses to
publish a draft missing any of the 27 assets a four-target release promises.
The compose file'sdemoprofile starts FerroEHR and FerroTERM alongside, and
its header says plainly that those are separately licensed products and that
the profile is for evaluation. - The server's runtime surface (#41): one
FERROCHART_environment namespace
read in one place, a loopback default bind that only the container image
widens, a refusal to start when either upstream endpoint is missing that
names the variable, an unauthenticatedGET /health, and orderly shutdown
on SIGTERM. The book gains an Operate section describing all of it. - The documentation lane (#4): a book under
website/bookrendered by the
pinned mdBook toolchain that.github/actions/docs-toolchaininstalls, and
docs.yml, which verifies it on every pull request and publishes it to
GitHub Pages frommain.docs/VERSIONS.mdcarries the three tool pins and
scripts/checks/versions.shstops skipping that check. - The vendored openEHR CKM template corpus, licence by licence (#12).
Fixed
- Status text that had gone stale as the repository gained code: the README no
longer says there is no code and no binary,docs/ci-cd.mdno longer calls
the Rust tier gated off,scripts/checks/versions.shand four.claude
rules no longer describe a design phase that closed, and a reference to
issue #20 that meant a different repository's issue now names #33.
Changed
.claude/rules/ci-cd.mdanddocs/ci-cd.mddescribe the pipeline that
exists rather than the one that was waiting for a workspace, and drop a
stale issue reference carried over from another repository.
0.0.1
Added
- The Rust CI tier, live now that the workspace exists (#15):
rustfmt,
clippywith-D warnings,cargo nextest, doctests,rustdoc, an MSRV
check throughcargo-hack, andcargo deny, all under the one required
conclusioncheck, with SonarQube importing Rust coverage from an
instrumented run. - The Cargo workspace (#11): the eight crates of
docs/architecture.md§11,
the workspace lint table from.claude/rules/reliability.mdwith
unsafe_codeforbidden, a release profile that keepspanic = "unwind"and
overflow-checks, and a separatewasm-releaseprofile for the renderer.
deny.toml's licence exceptions now name the real crates, including the
three pinned openEHR crates that are BUSL-1.1. - How a coded field gets its codes, decided against measured evidence (#6):
97.6% of the 843 coded fields in 102 operational templates take their
membership from the template, an enumerated external code carries no rubric
there, and one code path serves every binding kind by resolving locally and
asking a terminology server only for what is missing. Archetype value sets
are emitted as FHIRCodeSystem,ValueSetandConceptMapunder a minted
URL, because no openEHR specification defines one. - The overlay key, decided against measured evidence (#8): a step chain
carrying the RM attribute, thenode_id, the archetype id, the RM type and
the pinned name, with a sibling ordinal only where those tie. Walking 102
operational templates showed an id-only path collides in 14 of them, and
that the pinned name is the only discriminator for 41% of the 427 colliding
sibling groups, which reversed the earlier decision to keep the name out of
the key. - The design of record,
docs/architecture.md(#1): the version pins, both
template generations normalized into one internal constraint model, the
field derivation table from the Reference Model and its constraints, the
layout overlay with its key normalization and its replay report, the
terminology split between local and network resolution, the ITS-REST client
rules, the workspace layout, the build order, and a decision register.
Every decision carries a citation or an explicit note that no specification
governs it. - The specification and model-crate pins in
docs/VERSIONS.md, which now
carry values instead ofpending #1, andscripts/checks/versions.shalso
checksopenehr-amandopenehr-adl(#9). - The brand: the mark, the "Rose & Iron" palette, and the lockup, favicon and
social-card set (#3). Rose was chosen by measuring worst-case CIEDE2000
distance from the other three product hues under normal, deuteranope and
protanope vision, andassets/brand/README.mdrecords the ratio for every
token against both grounds. - The repository: the working discipline in
.claude/(rules, hooks, skills,
agents, memory), the community and governance documents, the pinned version
matrix indocs/VERSIONS.md, the committed guards underscripts/checks/,
the tracker helpers underscripts/gh/, and five workflows that work on a
repository with no code (CI, CodeQL, Scorecard, SonarQube Cloud, Release).
The configuration is FerroBRIDGE's, adapted from the FHIR and OMOP oracles to
the openEHR Reference Model, the Archetype Object Model, ITS-REST, and AQL.