Skip to content

0.0.5

Choose a tag to compare

@github-actions github-actions released this 08 Sep 00:45
· 51 commits to main since this release
Immutable release. Only release title and notes can be modified.
v0.0.5
113c3e8

Added

  • The web template compatibility surface (#54): ferrochart-webtemplate reads
    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 links ferrochart-form and 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. min and max are read as the flattened integers they are, and
    template version detection reads templateId and the root archetype
    identifier, never semVer, 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-validate crate
    and ferrochart_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.sh compares the resolved serde_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's FORMAT_VERSION now 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
    CLUSTER are refused by name, because a fragment is not a document.
    docs/architecture.md sections 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-local at-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 the openehr-term crate. 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/$expand to fill a
    picker, CodeSystem/$lookup for the display text of an enumerated external
    code, and ValueSet/$validate-code to confirm a chosen one. Every request
    is formatted against the generated fhir-types operation 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, the OperationOutcome diagnostics
    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
    R4 snomedct.html section 4.3.1.0.9. The reason is in
    docs/architecture.md section 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, and sct-ecl would add a BUSL-1.1
    licence exception for a check the server repeats anyway.

  • scripts/test-term.sh runs 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.sh reads 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_bindings and
    all_constraint_bindings return every binding as a code and one target, and
    value_sets returns 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 FHIR CodeSystem and ValueSet
    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_id alike 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-Match is sent quoted and unweakened on every update,
    Prefer is 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-types crate (Apache-2.0,
    pinned at 0.1.85), which ferrochart-term now depends on (#95). It is
    generated from the HL7 FHIR packages, so ValueSet, CodeSystem,
    ConceptMap, Parameters and OperationOutcome are 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, $lookup and
    $validate-code as typed request and response shapes that convert to and
    from Parameters, 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.md section 7.3 records both decisions.

Fixed

  • The last hand-built archetype identifier in the three-part template_id
    form, in ferrochart-form's wire test. #127 corrected the .opt fixtures
    and its check covers those only, so this one sat in a hand-built key and in
    the committed wire snapshot, disagreeing with key.rs's own example.

  • The synthetic fixtures stated two shapes the Reference Model does not admit
    (#127), both invisible until the commit gate landed something that judges an
    instance. Ten .opt fixtures gave an archetype_id a three-part version,
    which is the template identifier's form: openEHR BASE Release-1.2.0
    base_types.html section 5.5 gives an archetype_id a single version
    number, and section 5.4.11 leaves TEMPLATE_ID with no lexical form at all.
    Twelve OBSERVATION-rooted fixtures put an ITEM_STRUCTURE, a CLUSTER or
    an ELEMENT straight under OBSERVATION.data, where RM Release-1.1.0
    ehr.html section 8.3.4 types that attribute HISTORY<ITEM_STRUCTURE>, so
    a HISTORY and an EVENT belong between; all 118 OBSERVATION.data
    attributes of the vendored CKM pack already state it that way. Anything
    those fixtures proved about an OBSERVATION was proved about a structure no
    CDR would accept. A new check over every committed .opt fixture holds all
    three shapes, so the next one fails at the fixture rather than at a CDR.

  • docs/architecture.md section 6.2 recorded a corpus split that does not
    sum: "87 single-purpose conformance templates plus 16 CKM clinical
    templates" for a total of 102. The total is consistent everywhere and is
    what every measurement rests on; the split is not re-derivable, because that
    corpus has since grown past 400 templates, so the document now says so
    rather than repeating an arithmetic that cannot be right. Section 16 gains
    the two upstream-report candidates found since it was written (#111, #129).

  • An operational template can declare two sibling constraints sharing the node
    id, the archetype id, the Reference Model type and the pinned name, which no
    instance can tell apart, and a form offering a field per member built a
    document its own template refuses (#122). AOM 1.4 permits the shape: it says
    a node id distinguishes siblings but states no invariant that requires it,
    and the ITS-XML schemas carry no identity constraint either. The compiler now
    folds such a group before it computes a key. Members that state the same
    constraint become one node, carrying the collective occurrences under a
    container attribute, which is lossless because the members are the same
    constraint. Members that differ under a container attribute are refused by
    name, because no instance could say which one it satisfies; that refuses no
    template of the committed pack. Folding before the key is what keeps the
    layout overlay stable when a template is republished with the duplicate
    gone. Over the pack, two more templates now build a document their own
    template accepts, and the gate reports no occurrences failure at all.

  • A composition wrapped around a template rooted below COMPOSITION named the
    entry's archetype as its own, so the document claimed a COMPOSITION was an
    OBSERVATION archetype root. That was 113 of the 123 committed templates.
    ehr.html section 5.4.1 makes Is_archetype_root an invariant of
    COMPOSITION and common.html section 3.2.2 requires a root's
    archetype_node_id to be the stringified archetype id, so the wrapper needs
    an archetype of its own. Configuration names it, the way it names the
    territory, and a build with none refuses rather than inventing one. Found by
    the commit gate of #24.

  • A COMPOSITION FerroCHART built put no ARCHETYPED on any node below its
    root, so every ENTRY it wrote violated Is_archetype_root
    (openEHR RM Release-1.1.0 ehr.html section 8.3.1, with
    LOCATABLE.Archetyped_valid in common.html section 3.1.2). Every
    archetype root below the document root now carries its own archetype
    identifier and the Reference Model release, and the template identifier
    stays at the document root alone, which is what common.html section 3.2.3
    says. The pre-post gate of #24 is what found it.

Changed

  • The hard rule about the non-canonical formats now names the two of them
    separately, because openEHR only specified one (#104). ITS-REST
    Release-1.1.0 publishes simplified_formats.html, "Simplified Formats for
    openEHR Data", in the STABLE state, so that document is the authority for
    the FLAT and structured formats: their media types (section 2.3), their
    field identifiers (section 4.2), level removal (section 4.6), the |other
    suffix (section 4.7), and the Reference Model mapping class by class
    (section 5). The web template stays a compatibility target, on the same
    document's word: section 2.2 puts "Web Template itself as a resource" under
    what the specification does not cover. Where the Reference Model and
    simplified_formats.html disagree, no specification settles it, so the two
    divide by subject and a real contradiction is filed upstream.
    CLAUDE.md, .claude/rules/spec-adherence.md, .claude/rules/testing.md,
    the spec-researcher agent, the /spec-lookup skill, the contributor book
    and the compat label all carry the corrected split.

Fixed

  • docs/VERSIONS.md said "Nothing is vendored yet" twelve lines above the
    table of what is vendored, and carried two headings for one subject (#112).
    It now has one section that says what is vendored, what is not, and that no
    specification text is, which is the fact a reader of a citation needs. The
    same file no longer claims there is no Cargo workspace, and
    .claude/rules/vendored-inputs.md no longer says nothing is vendored.
  • The datatype grid fixture carries the two field kinds it claimed to and did
    not (#114). ferro_datatype_grid.opt now states a DV_ORDINAL with three
    graded symbols and a DV_STATE with a two-state machine, so the derivation
    of both is snapshotted rather than asserted by a comment. DV_SCALE stays
    the exception the comment now names: OpenehrProfile.xsd declares no
    C_DV_SCALE, so only the ADL 2 reader reaches it.