ADR 0019: Version the Compiled Artifact JSON Contract
Accepted architecture decision record: versioned compiled artifact JSON contract.
- Status: Accepted
- Date: 2026-06-16
Context
Ferrocat exposes Rust-native catalog types, but downstream host adapters, CI
jobs, caches, and editor tooling often need to move selected outputs across a
process boundary. serde support is useful for that, but not every Rust struct
should automatically become a durable host contract.
The highest-risk boundary is the locale-resolved compiled runtime artifact from
compile_catalog_artifact and compile_catalog_artifact_selected. Palamedes
and other host adapters can treat that payload as the handoff point between
catalog semantics and host-specific packaging.
Audit reports are also useful as JSON in CI, but their shape follows the Rust report API and does not need a separate schema-version field yet.
Decision
Ferrocat adds opt-in serde support for transport-oriented API outputs:
- low-level owned PO documents:
PoFile,PoItem,Header, andMsgStr - high-level catalog messages and message diagnostics
- catalog audit reports and audit diagnostics
- compiled catalogs, compiled ID reports, and compiled artifact diagnostics
CompiledCatalogArtifact
CompiledCatalogArtifact uses an explicit JSON wire contract instead of a
plain derived Rust struct representation. The current contract is schema
version 1 and serializes as:
{
"schema_version": 1,
"messages": {
"<compiled-key>": "<runtime ICU message>"
},
"missing": [
{
"key": "<compiled-key>",
"source_key": {
"msgid": "Checkout",
"msgctxt": null
},
"requested_locale": "de",
"resolved_locale": "en"
}
],
"diagnostics": [
{
"severity": "warning",
"code": "icu.syntax",
"message": "invalid ICU message",
"key": "<compiled-key>",
"msgid": "Checkout",
"msgctxt": null,
"locale": "de"
}
]
}schema_version is required when deserializing a compiled artifact. Ferrocat
rejects unknown compiled artifact schema versions instead of silently accepting
payloads with semantics it cannot prove it understands.
Diagnostic severities serialize as lowercase strings: info, warning, and
error.
Consequences
Positive:
- host adapters can share one versioned artifact payload without copying Ferrocat's internal Rust structs into JS/TS binding code
- CI can consume audit reports as stable JSON objects with machine-readable codes and lowercase severities
- parser-focused users can enable the
serdefeature for low-level PO data without opting into the full catalog feature profile
Negative:
- compiled artifact JSON changes now need compatibility review
- the artifact deserializer intentionally rejects unknown schema versions, so host adapters must handle version negotiation explicitly
- most derived serde shapes remain Rust API shapes, not independently versioned wire protocols
Compatibility Rules
For CompiledCatalogArtifact JSON:
- keep
schema_versionpresent and numeric - bump the schema version for removed fields, renamed fields, changed field meanings, or changed severity/code semantics
- prefer additive changes only when older consumers can safely ignore them; if they cannot, bump the schema version
- keep
messagessorted by compiled key through the underlying ordered map - keep diagnostic
codevalues machine-readable and stable once documented
For audit reports and other derived serde outputs:
- treat the JSON as the serde form of the Rust public API
- avoid changing field names or enum spelling without a semver-relevant Rust API decision
- add a separate schema version later if a host adapter needs to consume one of those outputs as a long-lived protocol independent of Rust releases
Alternatives Considered
Derive Serde For Everything
Rejected because it would make incidental Rust structure look like a durable host protocol. The compiled artifact is a cross-process contract and deserves a version field.
Add Schema Versions To Every Serializable Type
Rejected for now. PO parser output, catalog messages, and audit reports are useful through serde, but the strongest compatibility requirement is the runtime artifact handoff to host adapters.
Leave Versioning To Palamedes
Rejected because the artifact semantics are produced by Ferrocat. If Ferrocat owns the locale-resolution and fallback rules, it should also version the JSON payload that represents those rules.