Doctrine. zic-rs treats every serious failure mode as a claim-boundary problem first: the project must either prove the claim with typed evidence, receipts, hashes, and tests, or refuse the claim explicitly. The dangerous failures here are not "Rust memory bugs" first — they are claim-boundary bugs: wrong claim · wrong authority · wrong oracle · wrong source release · wrong vendor generalization · wrong semantic conclusion · wrong install-durability assumption. Those are the ones that propagate into serious infrastructure.
The thing that makes timezone tooling dangerous is not a crash — it is a structurally-valid, plausible-but-wrong artifact that loads fine and is wrong at a civil-time boundary. A loud failure is safe; a silent wrong answer is not. This register exists so every such mode is named and bound to a guard or an explicit non-claim, never left to folklore.
This is a standing, append-only document (drafted early — like
differences-from-reference-zic.mdandpanic-policy.md— because it summarises already-shipped receipts; its formal owners are T17 reliability + T20 security). Rows are never deleted or softened; status fields move forward only. Status vocabulary:guarded(a real, tested guard exists) ·partial(guarded in part; named residual) ·doctrine(the rule + non-claim are recorded; the enforcing build is a tracked future milestone) ·deferred(named, owned by a later milestone, not yet built). Per the project's doc-evidentiary-density rule, this is dense on purpose: a serious reviewer should be able to trace each risk to its evidence, not skim a conclusion.
- Failure mode. zic-rs is taken to define civil time, rather than to compile admitted IANA tzdb source. Time is a legal/political fact set by governments; the tzdb is a maintained public process (RFC 6557 / BCP 175) coordinated around IANA — zic-rs is strictly downstream of that authority and of the data itself.
- Consequence. cron at the wrong local time · billing windows close wrong · logs sort into the wrong hour · audit trails disagree · compliance/court deadlines miscompute · distributed systems compare wrong local timestamps · calendars drift across a DST transition.
- Where it enters. marketing language · a reviewer assuming "compiles tz ⇒ owns tz" · using a future projection (POSIX footer) as a civil-time guarantee.
- Evidence surfaces.
docs/tzdb-governance.md(the IANA/CLDR authority boundary) · release-admission matrix (only 2026b admitted;ReferenceLocatorKind/SignatureTrustModel) ·release-diff+ semantic-report (changes are measured against the admitted release, not asserted). - Tests / receipts / hashes. the admitted-2026b archive (
reports/t12_5a2-reference-admission.md, signature + sha256-pinned) ·declared_scope_hash(the claim envelope is a hash, not a slogan). - Status.
guarded. - Non-claim.
does_not_claim_future_civil_time_authority·does_not_curate_time_or_define_display_names. - Hardening owner. T19
TRUST.md(front-door restatement) · T20 (legal/compliance persona). - Boundary. zic-rs compiles admitted tzdb source; it does not determine civil-time truth.
- Failure mode. a compiled TZif parses cleanly and loads in every reader yet diverges semantically from reference output. Format validity (RFC 9636) is not behavioural parity.
- Consequence. readers accept the file · operators trust it · the drift stays hidden until a transition boundary, then every consumer is silently wrong at once.
- Where it enters. conflating the structural validator's "conformant" verdict with "correct" · byte differences assumed semantic, or semantic differences assumed structural.
- Evidence surfaces. CORE.1 (341/341 behaviour-match over 1900..2040, the binding claim) · T15.4
tzif-validate— five separate verdicts, never onevalid:true·semantic-report(zdump-backed) kept a separate surface ·release-diffkeeps the structural axis and behavioural axis strictly separate ·VALID_DISAMBIGUATION(7 distinct senses of "valid", public inconformance_status). - Tests / receipts / hashes. the CORE.1 sweep (
bash /tmp/t9sweep.sh→ 341/0/0) ·tests/tzif_*/tests/semantic_*/ therfc9636validator tests (incl. the type-index-bounds violation) · standards-currency check (RFC9636-ERRATA.1): RFC 9636 has 0 published errata (authoritativeerrata.json, shacc92d64d…, 2026-06-05) — the validator's normative target is uncorrected; the predecessor RFC 8536's 4 errata are all example/appendix-only (reports/rfc9636-errata/). Re-check each release; a new RFC 9636 erratum is a signal to re-assess the validator. - Status.
guarded. - Non-claim.
does_not_claim_arbitrary_tzif_roundtrip· structural validity is explicitly not semantic behaviour (in-report note). - Hardening owner. T15.4 enrichment (dual-block consistency, after-last-transition witness) — tracked.
- Boundary. TZif structural validity is not semantic parity.
- Failure mode. leap handling is rare, specialized, and easy to accidentally flatten or mis-expire. RFC 9636 gives leap-table expiration + post-expiry interpretation real semantics; treating leap as a normal timestamp edge is the trap.
- Consequence.
right/zones wrong · post-expiry timestamps interpreted inconsistently · far-future timestamps look authoritative when they are not ·time_t/range × leap interactions go wrong. - Where it enters. inferring leap behaviour from output shape · applying
-rtruncation with a rolling leap · assuming smear. - Evidence surfaces. leap is opt-in (
-L, T11), never the default;LeapSourceModetyped (T17.2) · T15.4LeapExpiryVerdictseparate ·release-diffleap_onlychange-kind exists · rolling-leap +-ris a hard error (compile/leap.rs). - Tests / receipts / hashes.
fixtures/leap/reference/*.tzif(stationary/rolling/v4-expires, byte-pinned) ·tests/leap.rs· theright/UTC27-leap reproduction · T23.reader-compat.2 (reports/reader-compat/RECEIPT-2026-06-03-appendix-a.md) which found a real divergence below. - FOUND + FIXED (T23.reader-compat.2, 2026-06-03). The reader gauntlet found that zic-rs's
right/profile leaped the table but left a zone's transition times at POSIX values, soright/output for zones with transitions drifted behind reference by the accumulated leap count (zdumponright/America/New_York);right/Etc/UTCmatched only because it has no transitions — exactly why T11'sright/UTCwitness missed it. Fixed:apply_leapsnow shifts each transition by the cumulative leap correction effective there (right/only — POSIX never calls it). Verifiedright/{America/New_York, Europe/London,Etc/UTC}zdump-match reference · POSIX New_York unchanged · CORE.1 341/0/0 · +1 regression test (right_profile_shifts_transitions_by_cumulative_leap_correction). - Status.
guardedfor the implemented surface (stationary/rolling/expiry, opt-in), includingright/transition-bearing zones for the tested fixtures; POSIX/default unaffected. The-r×leap-expiry interaction still has no semantic witness → non-claim. - Non-claim.
does_not_claim_leap_smear_semantics·does_not_claim_range_truncation_leap_expiry_interaction_parity_without_witness·does_not_claim_universal_leap_profile_parity_beyond_tested_fixtures(verified for the testedright/zones, not a universal leap-profile theorem). - Hardening owner. T15.4 (
LeapExpiryVerdictmatches-ref/mismatch needs the oracle) · T20. - Boundary. Leap records are explicit evidence; zic-rs does not infer smear or future-leap truth.
- Failure mode. one vendor's measured result is taken to generalize to another. The vendor lab proved the world is fractured (tzcode-zic vs glibc-zic, old forks, fat/slim defaults, version-stratified diagnostics, package-selected lineages, build-host vs runtime-target split).
- Consequence. packagers assume the wrong bloat default · operators assume glibc version implies
tzdata behaviour ·
doctoroutput masks old-fork-like diagnostics · vendor-specific compat gaps missed. - Where it enters. collapsing the six-axis matrix · reading "glibc present" as "glibc-zic selected" · reading "RPM" as "glibc-zic".
- Evidence surfaces.
vendor-oracle-receipt-v1receipts are immutable, per-vendor, admitted by rule, never inferred ·known_divergencesexplicit per receipt ·receipt_production_moderecorded (RECEIPT-MATRIX.md) · the six axes {lineage · tzcode/glibc version · behaviour-tier · bloat_default · packaging_model · lineage_selection_source} are kept independent · the "do not collapse" map. - Tests / receipts / hashes. 19 receipts / 17 ecology rows (
../zic-rs-vendor-oracle-lab/, each with binary sha256 + package ownership +IMAGE-PROVENANCE.md) ·tests/vendor_oracle_receipt.rs. - Status.
guarded. - Non-claim.
does_not_claim_unadmitted_vendor_parity·does_not_ship_or_operate_vendor_qemu_labs_in_core_repo. - Hardening owner. T16.6.x (matrix renderer derives only what receipt fields support; honest unknowns).
- Boundary. A vendor receipt admits one measured vendor ecology, not a family-wide theorem.
- Failure mode. a vendor (OpenBSD/DragonFly/Ubuntu-2.39/Alma-2.34) safely rejects a malformed input (exit ≠ 0) but classifies it differently than modern zic / zic-rs — and a naïve test reads "rejected" as "diagnostic parity."
- Consequence. a test suite says "passes" on exit code alone · diagnostic tooling silently loses class fidelity · operators believe a typed diagnostic contract matches when only the rejection does.
- Where it enters. comparing exit status instead of class+location · a vendor receipt admitted on rejection without recording the class divergence.
- Evidence surfaces. per-receipt
class_location_verdicts(a real class+location compare, not exit-code only) ·known_divergences(e.g.continuation_without_zone,nul_byte→"line too long") · the append-onlyZIC001–ZIC026contract · the standing "safe rejection ≠ diagnostic-class parity" doctrine. - Tests / receipts / hashes.
tests/diagnostic_parity.rs(class→location→wording-last vszic -v) · the per-vendor 3–4/5 core-5 verdicts in the lab. - Status.
guarded. - Non-claim.
does_not_claim_byte_exact_stderr_wording_parity. - Hardening owner. T13 contract (closed); vendor rows extend it on demand.
- Boundary. Rejection parity is not diagnostic parity.
- Failure mode.
zdumpreads the host's installed zoneinfo instead of the generated file, or a report uses a PATH tool that is not the admitted reference — so the "match" is against the wrong bytes. - Consequence. a semantic report validates the host's installed zone, not zic-rs output ·
release-diffuses the wrongzdump· an operator believes behaviour matched when the oracle read different bytes. - Where it enters. invoking
zdump <name>(zone-DB lookup) instead of an explicit path · trusting a PATH binary without recording its identity. - Evidence surfaces.
zoneinfo_resolution = explicit_tzif_path_argument(the oracle reads our bytes, T15.5) ·oracle_identity(binary sha256 · argv · envTZ/LC_ALL) ·doctorToolVersionStatus+HashReadStatus(path and hash of the resolved tool, T17.3) ·OracleFailureScope(T17.3) · the live-vs-sealed reference split (T16.3). - Tests / receipts / hashes.
tests/reference_admission.rs· the doctor present-tool test (path + read hash) ·zdumpalways invoked on an absolute path to a freshly-written file. - Status.
guarded. - Non-claim. an oracle result is admitted only when the oracle identity and input path are known.
- Hardening owner. T17.3 (shipped) · T20 (supply-chain persona).
- Boundary. An oracle result is only admitted when the oracle identity and input path are known.
- Failure mode.
release-difflacks azdumporacle (or it fails) and the behaviour axis is reported as "same" instead of "not assessed." This is a silent killer: a release looks safer than it is. - Consequence. release changes look safer than they are · operators skip review · silent semantic drift survives into deployment.
- Where it enters. absent oracle defaulting to "unchanged" · one failed identifier poisoning the whole run or being read as "no diff."
- Evidence surfaces.
oracle_mode: unavailable(absence is visible) · thebehaviour_unassessedchange-kind (an explicit "we did not check") ·OracleFailureScope{global_tool_unavailablevsrow_or_identifier_failure} (T17.3 — a per-row failure is recorded on that row'sbehaviour_error, never poisoning the rest) · the report'snon_claim. - Tests / receipts / hashes.
tests/release_diff.rs:changed_zone_without_oracle_is_behaviour_unassessed·unresolvable_zdump_is_global_unavailable_up_front· the split-seam test. - Status.
guarded. - Non-claim. absence of the oracle is not evidence of no behaviour change.
- Hardening owner. T16.6/T17.3 (shipped).
- Boundary. Unassessed behaviour is not unchanged behaviour.
- Failure mode. a compiler that materializes an output tree writes outside
--out, clobbers host files, follows a symlink, or confuses copy vs symlink materialization. - Consequence. write outside the output dir · clobber host files · unsafe symlink following · wrong localtime/posix/right layout.
- Where it enters. a hostile zone/link name used as a path · a pre-planted file/symlink/dir at the
output leaf ·
--forcefollowing a symlink. - Where it enters (cont.) ·
--forcereplacing a symlink via a remove-then-create gap. - Evidence surfaces.
ZIC008path-traversal reject (absolute/..////trailing-//leading--/NUL) · the operational/materialization diagnostic layer ·OutputTree+LinkModetyped (T17.2) · T9.3 compile-all-to-memory-then-write (no partial install after a fatal) · T14.6 hostile-output-tree (pre-planted leaf fails closed, never written through; regular-file--force=rename, replaces not follows) · T17.4: every leaf publish is now check-then-act-free — regular files viahard_link(exclusive create) /rename(--force); symlinks viasymlink_exclusive(thesymlink(2)EEXISTis the exclusive create, noexists()race) and, under--force, a temp-symlink +rename(atomic, operates on the link itself — replaces, never follows; no remove-then-create gap). - Tests / receipts / hashes.
tests/output_safety.rs·tests/hostile_output_tree.rs·tests/zone_name_path_policy.rs· T17.4fs::output_tree::tests::{symlink_exclusive_create_rejects_preexisting_without_following, symlink_force_overwrite_replaces_atomically}. - Status.
partial— every leaf operation is now race-free / fail-closed (guarded, incl. the symlink overwrite, T17.4); the residual is narrower and named precisely: a concurrent parent-component symlink swap during path resolution (create_dir_all/open resolve parent symlinks at syscall time) remains not claimed — closing it needs fd-relativeopenat/O_NOFOLLOW, which std does not expose withoutunsafe/a new dep (both forbidden), so it staysRequiresOpenatStyleHardening. - Non-claim.
does_not_claim_full_toctou_resistance(now scoped specifically to the parent-component swap race, not the leaf). - Hardening owner. T17.4 closed the leaf races; the parent-component residual → T20 (an
openat-style materialization would need the unsafe/dep decision revisited). - Boundary. Compilation correctness and installation safety under hostile concurrency are separate claims; T17.4 closed the leaf-level races, the parent-component swap race is explicitly still open.
- Failure mode. the install is believed crash-durable, but syncing the temp file is not the same as a parent-directory fsync + rename durability contract.
- Consequence. an operator believes the install is crash-durable · power loss leaves a missing or stale file · a deployment pipeline trusts a stronger guarantee than is implemented.
- Where it enters. reading "atomic publish" as "crash-durable" · assuming
renamealone survives power loss without a directory fsync. - Evidence surfaces. the atomic temp-file + no-clobber publish (real) · T17.4 implemented the full
three-layer durable publish for the install path: (1) content
sync_all()on the temp file before publish, (2) atomic publish (hard_link/rename), (3) parent-directory fsync (fsync_dir, Unix) after the publish — so each written file's directory entry is crash-durable, not just its content. Ephemeral scratch writers (thecompareoracle tree, the release-diff zdump tree) passdurable=falseto skip the pointless fsync on soon-deleted files. - Tests / receipts / hashes.
fs::output_tree::tests::durable_write_succeeds_and_round_trips(the install path incl. dir-fsync must succeed on the test FS) + the existing atomic-publish/cleanup tests; the three-layer contract is documented insrc/fs/atomic_write.rs's module header. - Status.
partial→ improved: per-file crash-durable publish is now guarded (content fsync + atomic publish + parent-dir fsync, Unix). What remains explicitly not claimed: whole-tree crash-atomicity — a crash mid-run can leave some files durably published and others not (there is no tree-level transaction), and directory-entry fsync is a Unix guarantee (a documented no-op elsewhere). - Non-claim.
does_not_claim_whole_tree_crash_atomic_install(a crash mid-run may leave a partial tree; per-file publish is durable, the set is not transactional) · durability is Unix-scoped. - Hardening owner. T17.4 (shipped the per-file durable publish); whole-tree transactional install (if ever needed) → a future milestone, named not faked.
- Boundary. Each file is durably published (content + entry); the whole output tree is not a single crash-atomic transaction.
- Failure mode. even memory-safe, unbounded parsing of a malicious/huge source set becomes an availability problem.
- Consequence. CI-runner exhaustion · packager build failure · hostile input triggers huge memory/time · DoS against any service wrapper exposing zic-rs.
- Where it enters. a
.ziwith millions of zones/rules/links/leaps · a pathological link chain · a giant single file. - Evidence surfaces.
limits::ResourceLimits(T17.1b): per-file source bytes · zone / rule / link / leap counts · link-chain depth · continuation-eras/zone · the pre-existing line-length cap (MAX_LINE_LEN,ZIC017) and per-zone transition cap (MAX_TRANSITIONS,ZIC009) ·overflow-checks = trueas a last-resort backstop ·docs/panic-policy.md. - Tests / receipts / hashes.
src/limits.rstests (each dimension breached with a tinyResourceLimits; the 400-link acyclic chain hits the depth cap; the production defaults pass a 500-zone DB). - Status.
guarded(caps are generous — they bound the pathological tail, not a tight quota). - Non-claim. not total DoS resistance; a cap breach is an operational safety refusal
(
Error::config), not aZIC###grammar diagnostic. - Hardening owner. T17.1b (shipped) · CLI-configurable caps → T17.2+.
- Boundary. A resource-cap breach is an operational safety refusal, not a grammar diagnostic.
- Failure mode. TZif header count fields and transition arrays are classic overflow / multiplication-overflow / out-of-bounds-index sites — counts from input are hostile until checked.
- Consequence. panic · misparsed TZif · truncated arrays · wrong transition/type mapping · a false structural verdict.
- Where it enters.
block_lencount arithmetic on untrusted u32s ·Vec::with_capacity(count)pre-allocating from a declared count · a transitiontype_indexpasttypecntindexingtypes[]. - Evidence surfaces (T17.5
CountArithmeticVerdict,reports/t17-count-arithmetic-verdict.md). the bounds-checkedCursor(Err on truncation, never panics) ·checked_block_len— everycount × element-sizeterm viachecked_mul/checked_add→ typedErron overflow (no 32-bit wrap, nooverflow-checkspanic) · the pre-allocation bound:checked_block_len ≤ cursor.remaining()checked before anywith_capacity, so a header claimingtimecnt = 1e9with a tiny body is rejected before a multi-GB allocation (the headline DoS fix) ·Cursor::skip(checked_block_len)for the v1-block skip · the T17.1atype_index < typecntguard ·read_cstr'sdesigidxbound ·overflow-checks = trueas a backstop only. - Tests / receipts / hashes.
tzif::validate::tests::{implausibly_large_declared_count_is_rejected_not_ooming, count_block_len_is_checked_arithmetic, out_of_range_transition_type_index_is_rejected_not_panic}· therfc9636type-index-bounds violation test ·reports/t17-count-arithmetic-verdict.md(the per-surface table). - Status.
guarded(T17.5) — count/size/offset arithmetic is checked before allocation, slicing, indexing, iteration, and cursor advance; hostile counts reject with a typedErr, not panic/wrap/OOM. (The structural indicator-count consistency / ascending invariants remain the separate T15.4 verdict axis — count-arithmetic safety ≠ RFC-structural conformance.) - Non-claim. structural-valid ≠ resource-safe ≠ count-arithmetic-safe (three separate axes); T17.5 guards the arithmetic, not the semantics.
- Hardening owner. T17.5 (shipped); structural count-relations stay with T15.4.
- Boundary. Counts from input are hostile until range-checked — and now they are, before use.
- Failure mode. a JSON report looks official and is treated as a signed certification.
- Consequence. someone treats a local report as signed certification · a report is copied without its build context · a stale report is used as proof.
- Where it enters. a
support-reportJSON shared as "the proof" · a report without its compiler / scope context. - Evidence surfaces.
ReportProvenance = unsigned_local_report(a report is not an attestation) ·CompilerIdentity(rustc/git honestlynull— nobuild.rs) ·declared_scope_hash(the claim envelope is a deterministic hash) ·ConformanceLevelbounded (release_admitted_compile_coverage, nevercompatible/conformant:true). - Tests / receipts / hashes.
tests/conformance_golden.rs(the golden pins provenance =unsigned_local_report, no unbounded verdict) · the declared-scope-hash determinism test. - Status.
guarded. - Non-claim.
does_not_claim_report_authenticity_without_signature_or_reproducible_context. - Hardening owner. T19 (signing only when real) · T20.
- Boundary. A local unsigned report is evidence, not attestation.
- Failure mode. T18 turns into scraping/mirroring rather than provenance, or treats archive links as authority, or weak sources as primary.
- Consequence. copyright violation · mirrored proprietary content · stale links · archive links treated as authority · forum posts treated as primary evidence.
- Where it enters. the T18 knowledge-index build step (network) · promoting an archive URL to primary · admitting a source without recording rights posture.
- Evidence surfaces. the T18 provenance doctrine (plan T18): typed
SourceLedgerEntry,AuthorityKindevidence tiers,canonical_url = authority/archive_url = preservation witness, Wayback + archive.today capture where lawful, copyright boundary (copyrighted_source_not_republished/link_only_or_existing_archive_only). - Tests / receipts / hashes. none yet — T18 is not built; the doctrine is recorded so the build is born compliant.
- Status.
doctrine(recorded; T18 not yet implemented). - Non-claim. T18 preserves claim provenance, not copyrighted bodies of text.
- Hardening owner. T18.
- Boundary. Provenance is preserved; copyrighted bodies of text are not republished; an archive URL is a witness, not the authority.
- Failure mode. old-OS
zicarchaeology (the parkedT16.HISTcampaign) leaks into the current vendor matrix, so historical behaviour is mistaken for current vendor behaviour. - Consequence. historical
zicbehaviour mistaken for current · unsupported archive images weaken the provenance chain · old bugs become current claims. - Where it enters. mixing historical receipts into the current matrix · admitting an image with weak checksum authority.
- Evidence surfaces.
T16.HISTis parked and separated from the current matrix · a typedreceipt_epoch{current/historical/archival} is reserved ·IMAGE-PROVENANCE.mdrecords checksum authority (published-vs-computed) per image. - Tests / receipts / hashes. the current 19-receipt matrix is the only admitted set; no historical rows exist yet.
- Status.
doctrine(separation recorded;T16.HISTnot run). - Non-claim. historical rows explain lineage; they do not establish current vendor parity.
- Hardening owner. T16.HIST (if/when run).
- Boundary. Historical rows explain lineage; they do not establish current vendor parity.
- Failure mode. zic-rs is used as a primary system-
zicreplacement before its install ecology is admitted, or a consumer assumes full reference parity / CLDR-ICU API / a measured resource profile it never had. - Consequence. a primary
zicreplacement before install ecology is admitted · a packager assumes full reference parity · a runtime assumes CLDR/ICU API compatibility · an embedded consumer assumes a resource profile that was never measured. - Where it enters. skipping the staged adoption path · reading CORE.1 (behaviour parity over a horizon) as full operational parity.
- Evidence surfaces. the staged adoption model (T19: verification-only → side-by-side → optional
package mode → replacement candidate → default) ·
negative_capabilities(typed, guard-enforced) · the four-bucketdifferences-from-reference-zic.mdmap · the precise live claim (CORE.1 over 1900..2040, not full replacement). - Tests / receipts / hashes. the per-milestone receipts each bound their own claim;
negative_capabilitieshas a test asserting each entry maps to an enforced guard. - Status.
partial/doctrine— the staging + non-claims are recorded; the persona-specific "may conclude / may not conclude" packets are T20; the install-ecology admission is bounded (compile_output_tree_only). - Non-claim.
does_not_claim_reference_install_directory_layout_without_a_REDO_layout_witness· not-a-runtime-localtime-library · not-a-tzselect/CLDR replacement. - Hardening owner. T19 (adoption staging +
TRUST.md) · T20 (per-persona conclusions) · T21 (embedded resource profile). - Boundary. zic-rs is replacement-grade only for the surfaces it has admitted.
The dangerous failures are claim-boundary bugs, not (first) memory bugs. For each, zic-rs either proves the claim with typed evidence + receipts + hashes + tests, or refuses it with an explicit non-claim:
| Risk | One-line boundary | Status |
|---|---|---|
| RISK.TIME.1 | compiles admitted tzdb source; does not determine civil-time truth | guarded |
| RISK.TZIF.1 | structural validity ≠ semantic parity | guarded |
| RISK.LEAP.1 | leap records are explicit evidence; no smear / no future-leap truth | guarded (opt-in) |
| RISK.VENDOR.1 | a receipt admits one ecology, not a family theorem | guarded |
| RISK.DIAG.1 | rejection parity ≠ diagnostic-class parity | guarded |
| RISK.ORACLE.1 | an oracle result needs known identity + input path | guarded |
| RISK.DIFF.1 | unassessed behaviour ≠ unchanged behaviour | guarded |
| RISK.PATH.1 | compile correctness ≠ hostile-concurrency install safety | partial — leaf races closed (T17.4); parent-component swap still open |
| RISK.INSTALL.1 | each file durably published; the whole tree is not one crash-atomic transaction | partial — per-file durable publish guarded (T17.4); whole-tree atomicity not claimed |
| RISK.RESOURCE.1 | a cap breach is an operational refusal, not a grammar diagnostic | guarded |
| RISK.COUNT.1 | input counts are hostile until range-checked — and now are, before use | guarded (T17.5) |
| RISK.REPORT.1 | a local unsigned report is evidence, not attestation | guarded |
| RISK.SOURCE.1 | provenance preserved; copyrighted text not republished | doctrine (T18) |
| RISK.HIST.1 | historical rows explain lineage, not current parity | doctrine |
| RISK.ADOPT.1 | replacement-grade only for admitted surfaces | partial / doctrine |
The doctrine sentence, restated: zic-rs treats every serious failure mode as a claim-boundary problem first — prove the claim with typed evidence, receipts, hashes, and tests, or refuse it explicitly. This register is where each refusal or proof is recorded; it grows (never shrinks) as milestones land.