Releases: rubentalstra/Veredictum
Release list
0.1.0-alpha.6
Fixed
- The release pipeline locates the dependency SBOM instead of assuming its
depth (#109 aftermath). cargo-cyclonedx writes the instrument's SBOM
beside its crate manifest, except when the workspace member's version
matches a published crates.io version, when it writes to the workspace
root — both observed first-hand. The binaries lane searched a fixed depth,
found nothing at the alpha.5 cut, and both binary legs failed; it now
searches both locations and excludes the console crate's SBOMs by path.
0.1.0-alpha.4
Added
- A committed example results document,
examples/results.example.json,
generated by the crate's own machinery (cargo run --example make_example_results, deterministic): schema-valid, invariant-checked,
with a real embedded HDR V2 histogram and a verdict computed against the
catalogue's POC case. It doubles as reader documentation for the results
schema and as a real seed for theparty_documentandhdr_v2fuzz
targets, which previously started from mutations. - A deliberate library API,
veredictum::pipeline, so the engine is
consumable by something other than the command line. It carries one seam per
whole operation —cataloguevalidates an artifact tree,conformance
drives it against a running system under test,judgementcomputes the
verdicts and renders the submission set,assetsrenders the published
visuals and the schema set, andmeasuredruns the class window, the stress
ladder and the AQL probe. Every seam returns typed values: a validation
carries its findings and the tree it loaded, a run carries the results
record and its outcome tally, a judgement carries the verdict report and its
documents as named bodies, and the measured window reports its progress as
typed events. Nothing returns console text, so a consumer renders its own
views over the same facts the command line prints. Theveredictumbinary
is now a clap front end over exactly those seams; its behaviour, its output
and its exit codes are unchanged. - A documentation website at https://veredictum.eu, built from
website/and
deployed to GitHub Pages by a newDocsworkflow. The root serves a
hand-written landing page in the project's own brand palette, and/docs/
serves an mdBook with five chapters: an introduction, installation, running
the instrument, a command reference covering every subcommand with its real
flags, the conformance method (the attribution law, positive and negative
testing, the ambiguity-register lifecycle), and catalogue authoring. The site
loads nothing from an external host, renders in both light and dark, and takes
its palette from the brand tokens.scripts/site/build.shassembles the same
tree locally that the workflow deploys, theCNAMEfor the custom domain
included. A pull request touching the site builds, lints and link-checks it
without deploying. - The vendored CKM ADL 1.4 archetype pack is exercised in this repository, by
tests/it/corpus_packs.rson everycargo nextest run. All 944 ADL 1.4
exports are decoded as UTF-8 and required to open with anarchetype (…)
header declaringadl_version=1.4and to declare the archetype id their file
name carries; all 944 AM 1.4 XML twins are read to end of input and required
to root atarchetypeinhttp://schemas.openehr.org/v1with that same
identity; both counts are pinned against the pack's own inventory record. The
pack had no exerciser here — its only one was an ADL-engine parse gate in the
repository this instrument was split out of, and this repository ships no ADL
parser. The pack stays as reserve material for wire batteries the catalogue
has not authored yet, and the exercise is at the byte level, which is what the
instrument can perform first-hand. - The ADL 2 pair pack and the CKM Operational Template breadth pack gain the
same byte-level exercisers, so every vendored corpus tree in this repository
now has one. The pair pack's 654 files are all read and refused when empty,
its 322 ADL 2 sources are checked foradl_version=2.0.6and its 330 ADL 1.4
twins foradl_version=1.4, each against the archetype id written inside it,
and the 321 archetypes upstream published in both dialects are proven to pair
with a twin in the same directory. The files that do not pair are pinned as
what they are: one ADL 2 template, which the archetypes-only 1.4 half has
nothing to hold, and nine ADL 1.4 archetypes this snapshot never converted.
The template pack's 305 exports are each parsed to end of input and checked
to root attemplateinhttp://schemas.openehr.org/v1carrying a template
id, and its file list is compared against the record's own vendored table
rather than against its count alone. - A fuzzing lane over the readers that parse text or bytes the instrument did
not write, in its own nightlyfuzz/workspace: six libFuzzer targets
covering the${…}reference and identifier grammars, the decision-table
literal grammar, the citation reader, a case core end to end through YAML and
the published schema into the typed model, the IXIT, statement and results
documents a party publishes, and the HDR histogram V2 decode path a measured
verdict is re-derived from. Seeds come from the catalogue and the party
declarations already committed here; recorded findings live in
fuzz/regressions/and are re-checked by every run. The harnesses compile on
the pull-request path as a gating CI job, and a weekly campaign fuzzes each
target with its corpus kept between runs.fuzz/README.mdcarries the threat
model and the commands,.claude/rules/fuzzing.mdthe discipline and the
crash-to-regression-test procedure. veredictum::load::yaml_str_to_valueparses artifact YAML from a string under
the same budget and duplicate-key refusal the file reader uses, and
veredictum::validateexposescitation_clauses,expand_bracesand
section_candidates, so a consumer can read a citation the way the validator
does.- A published VEX record under
security/vex/, in OpenVEX format: the
distroless base's adjudicated OpenSSL finding as a hand-authored statement
beside its.trivyignore.yamltwin, and the Rust advisoriesdeny.toml
accepts as a GENERATED document whose id set cannot drift from the gate —
scripts/security/vex-generate.shrefuses on any disagreement and the CI
guard tier regenerates and diffs on every pull request. The scheduled
published-image scan applies the documents, and
scripts/security/scan-images.shreruns that exact scan locally.
Changed
- The container image is the web console now.
ghcr.io/rubentalstra/veredictum
ships the newveredictum-consoleLeptos server (app/veredictum-console,
a second workspace package that never publishes to crates.io) instead of the
CLI, per the ruling recorded indocker/Dockerfilewhen the image first
shipped: the CLI payload was a placeholder, and its no-toolchain paths are
cargo install veredictumand the attested release binaries. Start the
console withdocker run --rm -p 127.0.0.1:3000:3000 -v "$PWD:/work" ghcr.io/rubentalstra/veredictum:<tag>; it binds loopback through the
publish flag because the console has no login. The server answers
/healthz, the image bakes aHEALTHCHECKthat probes it (the binary is
its own probe, because distroless carries no curl), and the binary drains
in-flight requests on SIGTERM, sodocker stopends it gracefully. The
image build properties are unchanged: pushed by digest, smoke-driven and
scanned before any tag applies, SLSA provenance and an SBOM attested on
the digest,:latestmoving only on a release tag. - The CKM template breadth pack is re-vendored. CKM published new asset
versions ofips-problem-listandips-allergies-and-intoleranceson
2026-08-19, so those two exports carry different bytes. The library is still
305 vendored templates beside the one private-incubator template that answers
404 without an account.
Fixed
- Three ways a document the instrument was JUDGING could stop the instrument,
all found by the new fuzzing lane on its first local campaign. A
decision-table cell nesting 4000 lists deep, or chaining 4000 ordinal tuples,
ranLiteral::from_textoff the stack; a Rust stack overflow aborts rather
than unwinding, so a validator run died instead of reporting a finding. And a
113-byte citation carrying 22{a,b}groups in one path hint asked citation
resolution for four million strings, hanging the run: the 32-variant ceiling
was applied across a clause's tokens but not within one. Literal nesting is
now bounded atliteral::MAX_NESTINGand brace expansion at
validate::MAX_CITATION_VARIANTS, both refusing with a typed finding. The
grammars' own forms are unaffected — a literal reaches three levels and an
authored shorthand names two or three sibling documents. - The README quoted 1107 spec-cited cases, which was the file count under
artifacts/schedule/. The instrument reports 1103, because the four
schedule/performance/journey definitions load as measured-workload
definitions and are not case cores. The page now carries the number
validateprints and says where that number comes from. - Re-running
scripts/vendor/ckm-archetypes.shwould have regressed two facts
in the pack'sPROVENANCE.md: the corrected mixed-licence count, and now the
exerciser. The script emits both, so the record survives a refresh. - The SonarQube lane no longer runs on a Dependabot pull request.
SONAR_TOKEN
is an Actions secret and a Dependabot run reads a separate store, so every
such run failed on the missing secret. The lane is advisory and gates no
merge, so skipping it costs nothing.
0.1.0-alpha.3
Fixed
-
The image vulnerability gate refused to tag the
0.1.0-alpha.2image, so that
release published its binaries and its crate but no pullable image tag. The
finding was real:libssl3t64in the distroless base, CVE-2026-14456, HIGH,
with a Debian fix the base image has not been rebuilt against — the current
:nonrootdigest still carries the vulnerable version, so a base bump does not
resolve it and a distroless image has no package manager to upgrade it in a
layer of our own.It is adjudicated as unreachable rather than suppressed, on the shipped
binary's own ELF header: its dynamic dependencies arelibgcc_s,libmand
libconly. TLS is rustls and the JOSE signing is aws-lc-rs, so nothing this
project builds links OpenSSL, and the image is distroless — no shell, no
package manager, no second executable that could load the library. The entry
lives in a new.trivyignore.yaml, scoped to that one package by PURL, with
the evidence and a three-month expiry, so it has to be re-argued rather than
quietly becoming permanent.
Added
scripts/checks/image-labels.sh, in the ungated guard tier. The base image
digest is declared in three places — the runtimeFROM, the Dockerfile's
base.digestlabel, and the release pipeline'slabels:input, which is the
copy the published image actually carries because it overrides the Dockerfile's
— and an automated base bump edits only the first. Without the guard, merging
one publishes an image whosebase.digestnames a parent it was not built on.
It also checks that every shared OCI key agrees between the two declaration
sites, and refuses to pass vacuously if the publishing lane it expects is
absent.ARCHITECTURE.mdat the repository root: the instrument's design record,
moved here from the FerroEHR mono-repo where it was written. It is the design
authority for the machinery — the artifact set and the case-core field
definitions, the operation bindings, the outcome taxonomy and the ambiguity
register, the assertion vocabulary, verdict computation — and it carries the
population-anchored performance-class model with its journey decomposition,
plus the evidence base and the ISO/IEC 9646 and CASCO grounding the scheme is
built in. Names and paths were adapted to this tree; the substance is
unchanged.- The GHCR image-pulls badge in the README, now that the package exists.
- A Dependabot
ignoreforrandmajor bumps. The dev-dependency exists to hand
pgp's signing call an RNG, andpgp 0.20is onrand_core 0.6, so a major
bump does not compile. Patch and minor bumps within the pin are still proposed,
and advisory-driven updates are unaffected.
Changed
- The container image states in its own header that its current payload is a
placeholder: it ships the CLI today and becomes the web UI's image when that
lands (#6). The CLI's own distribution channels arecargo install veredictum
and the prebuilt binaries on each release. - The distroless base moves to the current
:nonrootdigest
(sha256:a77defd6…). This is not a security fix — the new digest carries
the samelibssl3t64version, verified by scanning it — it is base currency,
so the image is not built on a two-month-old parent.
0.1.0-alpha.2
Added
-
The release pipeline. A
v*tag now publishes a release:release.yml
verifies every release fact against the tagged commit before anything is
built, creates the GitHub release as a draft, builds per-architecture Linux
binaries (x86_64 and aarch64) each with a checksum, a CycloneDX dependency
SBOM and Sigstore provenance and SBOM bundles, attaches a repository-wide
SPDX SBOM, and publishes the release only once every expected asset is
attached. The binary and image builds each live in a reusable workflow, which
is GitHub's documented construction for SLSA Build Level 3, so a consumer can
pin the signer withgh attestation verify --signer-workflow. -
The multi-architecture container image on GHCR, pushed BY DIGEST, smoke-run and
Trivy-scanned on both architectures before any tag names it, with provenance
and an SPDX SBOM attested on the digest.:latestmoves on a release tag and
never on a pre-release. -
docker runneeds no Rust toolchain.docker/Dockerfilebuilds a
distroless image that runs as uid 65532 and carries nothing but the runner:
mount the repository at/workand every subcommand works, because the
entrypoint is the instrument itself.docker run --rm -v "$PWD:/work" ghcr.io/rubentalstra/veredictum:<tag> \ validate --root /work/artifacts --specs /work/specs/openehr
The catalogue and the vendored specification oracle are deliberately NOT baked
in — 347 MB, read as run-time paths, and a party may want to point at their
own — so the image stays 55 MB and the data comes from the mount. -
A
Dockerfile lintjob in CI, gated on a change to the image tier, running
hadolint at its warning threshold against a configuration where any
deliberately violated rule is named with its reason. -
The changelog guard now requires an entry, not just a valid file shape. A
change touching a user-visible surface with no entry under the Unreleased
heading fails, and the path set that decides "user-visible" is declared in
scripts/checks/changelog-entry.shbeside the reason for each path rather
than inferred from a pattern in a workflow. Theno-changeloglabel waives it
and says so in the run, so a waived guard is auditable afterwards. -
Two scheduled lanes, both of which report a finding by filing or updating one
tracking issue and keep the run green — the run goes red only when the probe
itself cannot answer, because a red scheduled run is invisible to anyone not
watching the Actions tab:image-scan.yml, Mondays, Trivy over the PUBLISHED image on both
architectures, so a CVE disclosed after a release is still found. Before the
first release it reports that nothing is published and exits green, so
"nothing found" and "nothing looked at" are never the same line.latest-deps.yml, Mondays,cargo updatethencargo check --all-targets,
the Cargo book's named mitigation for a committed lockfile: a breaking
in-range upstream release is found on a schedule instead of during an
unrelated pull request.
-
Dependabot covers the
dockerecosystem now that a Dockerfile exists, with a
fourteen-day cooldown — the longest of the three, because a base-image bump
changes the bytes every user of the published image runs. -
The crate publish joins the same tag, as the last leg of the pipeline and after
the release is otherwise complete, so thecrates-ioenvironment's reviewer
approval blocks nothing else.publish-crates.ymlstays as the out-of-band
dry-run and recovery lane, and both lanes call one implementation,
scripts/release/publish-crate.sh. -
The release procedure is written into
CLAUDE.mdand driven by this file: a
missing or empty section for the tagged version fails the pipeline'splanjob
before anything is published. -
The crates.io version, crate-downloads and docs.rs badges in the README.
-
Published on crates.io as
veredictum, both a binary and a library:
cargo install veredictum --version 0.1.0-alpha.2puts the command on your
PATH, and the library target lets an integrator consume the typed artifact
model and the published JSON Schemas rather than reimplementing the format.
The package carries the code and the legal set; the catalogue and the vendored
specification oracle are 347 MB of data no registry accepts, and every root is
a path passed at run time, so both come from the repository. -
publish-crates.yml: the release lane for the crate, authenticating through
crates.io Trusted Publishing so no long-lived registry token exists in this
repository. Manual dispatch, dry run by default, the upload built from the
checkout with no cache restored, and the registry read back before the lane
reports success. -
The instrument itself builds and runs from this repository: the runner,
the catalogue with its 1107 case cores and 247 operation bindings, the
corpora, the ambiguity register, the party declarations, and the vendored
openEHR specification text that is its oracle. -
The command is
veredictum. The package, the binary and the library carry the
product's name, and so does the debug switch, now
VEREDICTUM_DEBUG_EXCHANGES. Every subcommand keeps its name and its flags:
validate,run,verdicts,perf,stress,aql-probe,
stress-compare,perf-assets,conformance-assets,emit-schemas. Two
paths move with the tree — an artifact root is nowartifactsand a spec root
is nowspecs/openehr. -
The standalone workspace: one package at the root, its own SemVer line from
0.1.0-alpha.1, edition 2024, Apache-2.0, with the deny-tier lint tables,
rust-toolchain.toml,rustfmt.toml,clippy.toml,deny.tomland
.config/nextest.tomlcarried over and adapted to what this tree actually
contains.Cargo.lockis committed, because this repository ships a binary. -
Three released machine-readable bundles beside the specification text, so a
citation that can only resolve against a schema resolves here rather than
nowhere:specs/its-xml-schemas/(the two XSD lineages),
specs/its-json-schemas/(the ITS-JSON validation oracle) and
specs/rest-oas/(the 21 released ITS-REST OpenAPI bundles). -
The corpus vendoring scripts, so every vendored tree can still be refreshed
the only sanctioned way, by re-running its script:
scripts/vendor/ckm-templates.sh,scripts/vendor/ckm-archetypes.sh,
scripts/vendor/adl2-archetypes.shandscripts/generate-ckm-examples.sh. -
The Rust CI tier, gated on whether a change touches anything it reads:
rustfmt,clippy --all-targetsat-D warnings, build plusnextestplus
the instrument's ownvalidateself-check, the rustdoc gate, the declared
MSRV verified withcargo hack check --rust-version,cargo deny check, and
cargo machetefor dependencies nothing imports. All seven join the single
requiredconclusioncheck. -
CodeQL analyzes
rustbesideactions, and the SonarQube scope coverssrc/
andtests/with the vendored trees excluded. -
Continuous integration.
ci.ymlruns on every pull request, every push to
mainand every merge-queue entry: a guard tier (comment style, changelog
structure, the no-attribution scan over the pushed commits, REUSE 3.3
licensing), a workflow audit (zizmor for the security posture, actionlint with
bundled shellcheck for correctness, and a check that every job actually gates
the merge), and a single requiredconclusioncheck. -
scorecard.yml: the weekly OpenSSF Scorecard analysis, publishing its score
to the OpenSSF API and its findings into code scanning. -
sonar.ymlandsonar-project.properties: SonarQube Cloud analysis on every
pull request and every push tomain, advisory under
.claude/rules/ai-code-review.md, with the New Code window anchored to the
package version so "new code" means "since the last release". -
Test coverage, measured and published. The Sonar lane runs the suite under
cargo-llvm-covand imports the merged lcov; the denominator excludes the
test tree, the CLI entry point and the two asset renderers, each with its
reason recorded, because a coverage percentage is only useful if every file
counted could in principle be covered by a test. The README carries the
coverage and quality-gate badges beside the CI, CodeQL, reliability, security,
maintainability and duplication readings. -
Dependabot covers the
cargoecosystem, with a seven-day cooldown against
the actions entry's three: a crate compiles into the published binary, so a
compromised release reaches every downstream run rather than one CI job. -
The OpenSSF Best Practices and OpenSSF Scorecard badges in the README, both
reading live scores rather than asserting a posture. -
Two ported guard scripts:
scripts/checks/changelog-structure.sh(Keep a
Changelog structure) andscripts/checks/ci-conclusion-complete.sh(no CI job
runs without gating the merge). -
The tracker machinery.
scripts/gh/rel.shis the one sanctioned write path
for GitHub's four native issue edges — sub-issue, blocked-by and their
inverses — resolving an issue number to the database id the write endpoints
actually want and failing loud on a bad one, with
.claude/rules/issue-relationships.mdas its policy. The label taxonomy is
complete against the schemeCLAUDE.mddefines:blocked-upstream,
on-hold,no-changelog, and the eightspec:component labels join the
type and priority sets. Two milestones open the release spine,v0.0.1and
v0.1.0./phase-status,/next-taskand/phase-doneare ported and
trimmed to the machinery that exists, each naming what it deliberately does
not check.
Removed
- The
accessibilitylabel. Nothing referenced it and it is not part of the
taxonomyCLAUDE.mddefines.
Fixed
- The SonarQube Cloud lane, which had failed on every run since the code
migration, the push tomainincl...
0.0.1-alpha.1
surface, the published artifact schemas, verdict semantics, the container
image, or anything a party's published conformance record depends on.
The runner itself still lives in FerroEHR tools/cnf-runner until the
migration (FerroEHR#2789)
completes; releases before that carry the repository skeleton and identity.