Unit ID: iri-patterns-post-3866b21-remediation
Status: ✅ Complete. Every review finding is dispositioned, and the compiler templates are aligned. Follow-on work lives in other units (see “Is this unit complete?”)
Last updated: 2026-09-23
Review: iri-patterns-post-3866b21-review.md
Documents changed: rdf-sparql-patterns-guide.md (summary in its Appendix D.3), iri-identity-patterns.md, iri-policy.md, ontology/persistence/spec/persistence.ttl (comments), ontology/persistence/shapes/ (StoreLocalEpochWarningShape message)
Yes. All 47 findings of the review (A1–A7, B1–B14, C1–C9, D1–D7, E ×10) are fixed in the documents, and the compiler templates generate the corrected shapes (471 tests at the end of this unit, 526 after the follow-on Slice 2 of persistence-compiler-iri-sync).
What the corrected documents specify but this unit did not build, and where it is tracked:
| Follow-on | Tracked in |
|---|---|
| Retention job (low-water marks, pinned-head copies, prefix-only drops), epoch-bump quiesce, erasure-register replay on restore | Housekeeping first cut, rdf-sparql-patterns-phase-plan.md Slice 3; erasure bindings also in persistence-compiler-iri-sync Slice 4 |
dal:firstWrite dal:PreCreatedRow in the compiler |
✅ Done in persistence-compiler-iri-sync Slice 2 |
| Identity, privacy and claim-scheme profiles in the compiler | persistence-compiler-iri-sync: identity ✅ Slice 3; privacy and claim schemes Slices 4–5 |
Request-time execution: $requestDigest, confirmation reads, outcome classification |
The store SPI and the ingestion gateway (epic P2.3.1, P2.3.6). The caller contract is documented in tools/persistence/README.md, “Using the generated SPARQL directly” |
| TCK tests T-12 to T-17, S-suite, R-suite | Written into guide Chapter 27 as specifications. Executable tests belong to the store SPI TCK (epic P0.5.6) |
Ratification of iri-identity-patterns.md and ADR-A82 |
phase-0-status.md, P0.1.3 |
pat:Revision alignment (prov:Activity or fnd:Evidence) |
Guide Appendix E item 3 |
dal: terms for the YAML-only declaration keys |
Guide Appendix E item 10 |
The review record iri-patterns-post-3866b21-review.md is closed against this status.
These resolve the review’s findings where more than one fix was possible. Each is recorded once, here, and applied in the documents.
| Decision | Chosen | Rejected alternative, and why |
|---|---|---|
| Epoch semantics after a bump (A1) | Guard on the dataset epoch only. Rebase the row’s pat:epoch on its next write, continue pat:seq, let the receipt chain cross the epoch boundary |
Bulk rewrite of every row at bump time: a long transaction over every row, not portable. Restarting seq per epoch: breaks the chain and S3 |
| Idempotency binding (A5) | pat:requestDigest, SHA-256 over enc([operationKind, target, expected, *sorted canonical N-Triples]) |
Hashing the SPARQL text: changes with template versions and parameter rendering |
| Dormant live heads under retention (A7) | Copy into urn:g:txlog/pinned before a drop (recommended), or pin the bucket |
Relaxing prefix-only retention: breaks the as-of floor |
| Retention low-water mark (B7) | Lowest seq guaranteed retained, default 1, advanced before the drop. S3 driven from version rows |
Receipts-only check: misses suffix and total loss |
| Canonical text pipeline (B10) | trim(NFKC_Casefold(x)) via ICU. The non-ICU fallback is declared as its own pipeline |
A hand-maintained ignorable list: incomplete (soft hyphen, variation selectors) |
| Store-local epoch (D1) | Unsafe, warned not refused, with the stated acceptance conditions | Prohibited: contradicts the SHACL warning shape and ADR-A82’s warn-not-refuse stance |
iri-policy.md (D7) |
Body removed, defects listed, git pointer to the historical text | Fencing the body: still read as guidance |
urn:g:people in Part II (C5) |
Kept as a flagged simplification, with the production design in guide §24.5 | Rewriting every Part II example to per-subject graphs: a large diff for no pedagogical gain |
| Finding | Disposition | Where |
|---|---|---|
| A1 epoch bump wedges rows | Fixed | guide §10.1, §19.1, §24.1, §24.4, T-12 |
| A2 external sequencers not dense | Fixed | guide S8, §21.2, §25.4, §26.1, §26.2 |
| A3 unbounded global HLC read | Fixed | guide §21.3, dal:LagWindowRead comment |
| A4 txn-claim TTL unsafe for append | Fixed | guide §10.1, §15.2, §24.2 (receipt-side audits), T-15 |
| A5 idempotency key not bound to content | Fixed | guide §15.2, §19.1–§19.4, §30.2, Appendices A and B, T-14 |
| A6 Appendix B contradicts body | Fixed | guide §19.7, Appendix B (pat:TxnCardinalityShape, pat:VersionRow, pat:RetentionShape) |
| A7 live head not always newest bucket | Fixed | guide §24.2, T-16 |
| B1 weak ETags | Fixed | guide F3, F4, §15.4, §30.2, Glossary |
| B2 firstWrite / optional head | Fixed | guide §14.2, §19.3, §24.1 |
| B3 create path unguarded | Fixed | guide §14.2, §19.3, T-13 |
| B4 deadlock contradiction | Fixed | guide §25.7 |
| B5 target IRIs | Fixed | guide S2, S4 |
| B6 store tables disagree | Fixed | guide §21.2, §26.1, §26.2 |
| B7 low-water mark semantics | Fixed | guide §2.3, S3, §24.2, Appendix A |
| B8 outcome classification | Fixed | guide §15.2, §19.4, §19.5, §25.1, §25.5, §25.7 |
| B9 HLC never merged | Fixed | guide S7, §19.4 |
| B10 normalization specified four ways | Fixed | guide §8.1, §25.3, §29.3, QP4, K-6; iri-identity §7.3; dal:NfkcTrim* comments |
| B11 delimiter concatenation | Fixed | guide Chapter 5, §6.1, §7.4; iri-identity §7.4 |
| B12 placeholder example values | Fixed, recomputed by running the code | guide §2.1, §2.3, Chapters 5–7, D.1 |
| B13 fence datatypes | Fixed | guide §7.4, §16.2, bootstraps in §10.1, §14.2, §19.3 |
| B14 unbound graph, unpadded epochs, mixed widths | Fixed, every example IRI regenerated at 19 digits | guide §2.1, §10.1, S5, Chapter 13, §19.4 |
| C1 arbitrary graph writes | Fixed | guide §25.1, S-2 |
| C2 destructive TCK at registration | Fixed | guide §25.2, Chapter 27 intro |
| C3 TCK over-claimed | Fixed | guide §27.5 (R-suite), §27.6, D.2 |
| C4 quiesce writers for a bump | Fixed | guide §24.4 step 1, iri-identity §10.3 |
| C5 erasure conflicts | Fixed | guide §2.2, §6.1, §6.2, §24.0, §24.1, §24.4, new §24.5 |
| C6 recreate path | Fixed | guide §24.1, T-17 |
| C7 keys-graph access control | Fixed | guide §6.1, §25.2, S-1 |
| C8 unregistered URN namespaces | Fixed | guide §2.1 |
C9 YAML versus dal: |
Fixed | guide §29.3, Appendix E item 10 |
| D1 store-local epoch | Fixed | iri-identity §10.3, dal:StoreLocalEpoch comment, warning shape message, guide §24.4 |
| D2 key rotation dual-write | Fixed | iri-identity §6.4 |
| D3 revision class alignment | Fixed, left as the one open decision | iri-identity §10.1, guide Appendix E item 3 |
| D4 status contradiction | Fixed | iri-identity §1 |
| D5 missing ADR-A82 | Fixed | iri-identity header |
| D6 rendering defect | Fixed | iri-identity §17 |
| D7 iri-policy.md | Fixed, body removed | iri-policy.md |
| E (all ten) | Fixed | guide §1.3, §2.3, §6.2, §10.2, S6, F5, §24.2, §24.3, Appendix A, Appendix B, §25.2, S-3 |
Run in autonomous mode, 2026-09-23. mise run check:persistence: 471 passed (290 before this unit. The rise is new templates multiplying the parametrized injection corpus, plus tests/test_template_alignment.py).
| Behaviour now generated | Templates | Review finding |
|---|---|---|
Dataset-guard variants guard on the dataset epoch only and rebase the row’s pat:epoch on write |
cas-replace-named-graph-dataset-guard, cas-replace-composite-property-dataset-guard, tombstone-delete-named-graph-dataset-guard, new append-event-dataset-guard |
A1 |
pat:requestDigest $requestDigest on every txn claim |
every row-writing template | A5 |
pat:head read in OPTIONAL |
all CAS and tombstone templates | B2 |
a pat:VersionRow on row creation, dataset guard on create |
create-if-absent-named-graph, new create-if-absent-named-graph-dataset-guard, new bootstrap-version-row and its dataset-guard variant |
A6, B3 |
Append maintains pat:head/pat:prevRev, uses STRDT typing, pads seq to 19 digits, writes events to urn:g:events/{family}/{month} |
append-event, append-event-dataset-guard |
B14, C1 |
| Fork audit counts txn claims per revision | fork-detection-audit |
A6 |
| Receipt-side duplicate audits | new revision-multi-txn-audit, txn-multi-revision-audit |
A4 |
| Row-driven, cross-epoch gap scan with low-water mark and pinned heads | gap-scan-audit |
B7, A7 |
Registry-listed log buckets via a request-time slot (originally #LOG_GRAPHS#, now the Mustache slot } after persistence-compiler-iri-sync Slice 2), never STRSTARTS |
all audits | S3, F6 |
pat:txn written as a string, not the claim IRI |
all receipt-writing templates | Appendix B datatype |
A pre-existing defect was fixed on the way: key-claim-write and key-claim-retire wrote claims into the txn graph (urn:g:txn). They now write urn:g:keys.
Tests added (tools/persistence/tests/test_template_alignment.py): every template parses with a complete context (the “markers unsubstituted” test was later replaced by a fail-closed test when the markers became Mustache slots); dataset-guard row writers rebase rather than guard the row epoch; heads are optional; every txn claim carries the digest; row creation is typed; key claims use the keys graph; no template scans by prefix; the gap scan is row-driven; append typing and padding; template selection for the new fixture ontology/persistence/examples/append-stream-dataset-guard.ttl. The injection corpus context in test_terms.py now binds every slot.
Adversarial probe: reverting cas-replace-named-graph-dataset-guard to guard the row epoch and require pat:head fails test_dataset_guard_rebases_row_epoch_instead_of_guarding_it and test_head_is_read_optionally. The template was restored and the full suite re-run green.
Not in scope for this unit: see “Is this unit complete?” above. pat:hlc on receipts and configurable infrastructure graph IRIs are recorded in tools/persistence/README.md “Known limitations”.
mise run check:persistence