rows { options { physical_type: PHYSICAL_STREAM_TYPE_QUADS max_name_table_size: 128 max_prefix_table_size: 16 max_datatype_table_size: 16 logical_type: LOGICAL_STREAM_TYPE_DATASETS version: 2 } } rows { prefix { value: "https://w3id.org/sciencelive/np/" } } rows { name { value: "RATn8QuiQng-s2Y4B8tYjLK1BDpRGzo3sXOZ2qXlgC1Sg" } } rows { namespace { name: "this" value { prefix_id: 1 } } } rows { prefix { value: "https://w3id.org/sciencelive/np/RATn8QuiQng-s2Y4B8tYjLK1BDpRGzo3sXOZ2qXlgC1Sg/" } } rows { name { } } rows { namespace { name: "sub" value { prefix_id: 2 } } } rows { prefix { value: "http://www.nanopub.org/nschema#" } } rows { namespace { name: "np" value { prefix_id: 3 name_id: 2 } } } rows { prefix { value: "http://www.w3.org/1999/02/22-rdf-syntax-ns#" } } rows { namespace { name: "rdf" value { prefix_id: 4 name_id: 2 } } } rows { prefix { value: "http://www.w3.org/ns/prov#" } } rows { namespace { name: "prov" value { prefix_id: 5 name_id: 2 } } } rows { prefix { value: "http://purl.org/nanopub/x/" } } rows { namespace { name: "npx" value { prefix_id: 6 name_id: 2 } } } rows { prefix { value: "http://purl.org/dc/terms/" } } rows { namespace { name: "dc" value { prefix_id: 7 name_id: 2 } } } rows { prefix { value: "http://www.w3.org/2001/XMLSchema#" } } rows { namespace { name: "xsd" value { prefix_id: 8 name_id: 2 } } } rows { name { value: "hasAssertion" } } rows { name { value: "assertion" } } rows { name { value: "Head" } } rows { quad { s_iri { prefix_id: 1 name_id: 1 } p_iri { prefix_id: 3 name_id: 3 } o_iri { prefix_id: 2 } g_iri { } } } rows { name { value: "hasProvenance" } } rows { name { value: "provenance" } } rows { quad { p_iri { prefix_id: 3 } o_iri { prefix_id: 2 } } } rows { name { value: "hasPublicationInfo" } } rows { name { value: "pubinfo" } } rows { quad { p_iri { prefix_id: 3 } o_iri { prefix_id: 2 } } } rows { name { value: "type" } } rows { name { value: "Nanopublication" } } rows { quad { p_iri { prefix_id: 4 } o_iri { prefix_id: 3 } } } rows { name { value: "icechunk-atomicity-outcome-2026" } } rows { prefix { value: "http://schema.org/" } } rows { name { value: "endDate" } } rows { datatype { value: "http://www.w3.org/2001/XMLSchema#date" } } rows { quad { s_iri { prefix_id: 2 } p_iri { prefix_id: 9 } o_literal { lex: "2026-06-06" datatype: 1 } g_iri { prefix_id: 2 name_id: 4 } } } rows { prefix { value: "https://w3id.org/sciencelive/o/terms/" } } rows { name { value: "FORRT-Replication-Outcome" } } rows { quad { p_iri { prefix_id: 4 name_id: 10 } o_iri { prefix_id: 10 name_id: 14 } } } rows { prefix { value: "http://www.w3.org/2000/01/rdf-schema#" } } rows { name { value: "label" } } rows { quad { p_iri { prefix_id: 11 } o_literal { lex: "Icechunk atomic commit: zero inconsistencies across F1-F4, including a positive control for conditional-write rejection (F4), on local filesystem and on a real S3-compatible object store (NIRD)" } } } rows { name { value: "hasConclusionDescription" } } rows { quad { p_iri { prefix_id: 10 } o_literal { lex: "Icechunk\'s session commit-or-abandon model and snapshot isolation produce zero observable metadata\342\200\223data inconsistencies across F1 (crash after data, before metadata \342\200\224 abandoned session), F2 (metadata-ahead-of-data state, measured directly \342\200\224 not applicable as a fault *injection* since Icechunk co-commits both in one session, but now empirically checked rather than asserted), and F3 (concurrent reader during an in-progress write), on both backends tested: 0/1000 trials on the local filesystem, and 0/100 trials on a real S3-compatible object store (NIRD/Sigma2, `s3.nird.sigma2.no`). A naive disconnected STAC index (B0) produces inconsistencies in every trial across all scenarios on the local filesystem. STAC B1 (best-effort: write-ordering + reconciliation sweeper) eliminates the F2 scenario by construction and closes the F1 window after the sweeper runs (1000 \342\206\222 0), but cannot prevent the F3 concurrent-read window \342\200\224 any reader arriving between the zarr write and the STAC update observes an inconsistency, identical to B0.\n\nBeyond the session-model scenarios, a fourth scenario \342\200\224 F4, concurrent racing writers \342\200\224 tests the mechanism the claim attributes *specifically* to object stores: conditional writes (compare-and-swap on the branch tip), the property that lets a store reject a commit whose session is based on a stale tip. Two sessions branch from the same committed snapshot, write OVERLAPPING data to the same array region (forcing a genuine conflict \342\200\224 Icechunk can silently rebase non-overlapping edits, which would not test rejection), and both attempt to commit. F4 was run on both backends \342\200\224 local filesystem (1000 trials) and NIRD/Sigma2 (100 trials, with live `MINIO_*` credentials) \342\200\224 and on **both**, every single trial showed: the first commit succeeds and moves the branch tip; the second session, based on a now-stale tip, has its commit rejected with `icechunk.ConflictError` (the exact exception, confirmed against the installed icechunk==2.0.6 API/docstring); and the store\nends in a single coherent state regardless of which writer won.\n\n F4 \342\200\224 conflict_rejected == True / inconsistent == False:\n Local filesystem: 1000/1000 / 1000/1000\n NIRD/Sigma2 object store: 100/100 / 100/100\n\nThis is the positive control for the conditional-write guarantee, green, on the actual target backend \342\200\224 measured with real network round-trips against a production S3-compatible object store, not asserted from code or inferred from a local unit test.\n\nTaken together, every sub-claim in the headline claim is now supported by measured evidence in its target environment: commit-or-abandon atomicity (F1), snapshot isolation (F3), the absence of a metadata-ahead-of-data state (F2, measured), and \342\200\224 the part that is *specific* to object stores and the reason this replication went to NIRD \342\200\224 conditional writes correctly enforcing atomicity under a genuine write race (F4). The claim is Validated." } } } rows { name { value: "hasConfidenceLevel" } } rows { name { value: "HighConfidence" } } rows { quad { p_iri { } o_iri { } } } rows { name { value: "hasEvidenceDescription" } } rows { quad { p_iri { } o_literal { lex: "Fault-injection harness, run on two backends. icechunk 2.0.6, zarr 3.2.1, Python 3.12. Results in data/results/results.parquet (10400 rows total). All fault scenarios are deterministic (fault always injected at the same point, or \342\200\224 for F4 \342\200\224 the same race is forced deliberately every trial); counts reflect worst-case presence of inconsistency, not empirical hit probability.\n\n Backend 1 \342\200\224 local filesystem: 1000 trials per scenario per system for F1/F2/F3 (icechunk, stac_b0, stac_b1) and 1000 trials of F4 (icechunk only), seed=42.\n Backend 2 \342\200\224 NIRD/Sigma2 S3-compatible object store (`s3.nird.sigma2.no`, a private project bucket, prefix `icechunk-atomicity-test//`): 100 trials per\n scenario for F1/F2/F3/F4, Icechunk only, seed=43. (The STAC baseline is not re-run on the object store: its inconsistency is a structural property of the disconnected\n two-step write, not of the storage layer \342\200\224 see `harness/run_matrix.py` for the rationale. F4 is Icechunk-specific by construction \342\200\224 STAC has no commit/conflict model to race against.)\n\n=== Local filesystem backend (1000 trials) ===\n\nF1 \342\200\224 crash after data write, before metadata update:\n Icechunk: 0/1000 inconsistencies (0 percentage points)\n [abandoned session; last committed snapshot unchanged]\n STAC B0: 1000/1000 (100 percentage points)\n [zarr updated, STAC JSON not \342\200\224 stale metadata persists]\n STAC B1: 1000/1000 pre-sweep \342\206\222 0/1000 post-sweep\n [write-ordering does not prevent F1; sweeper detects and corrects\n the mismatch by recomputing sha256 from zarr and overwriting STAC]\n\nF2 \342\200\224 crash after metadata update, before data write:\n Icechunk: 0/1000 (measured, not asserted: `icechunk_is_metadata_ahead_of_data`\n checks the committed snapshot directly for a metadata-ahead-of-data\n state after the only crash path Icechunk has \342\200\224 an abandoned session.\n There is no metadata-before-data write order to inject a fault into,\n since both are written in the same session and co-committed atomically;\n this measurement makes that structural property falsifiable rather than\n assumed \342\200\224 a future change that split the commit would flip it to True)\n STAC B0: 1000/1000 (100 percentage points)\n [STAC updated with new sha256, zarr still holds old data]\n STAC B1: 0/1000 (by design \342\200\224 write-ordering enforces data-before-STAC, making\n the F2 fault point unreachable in B1\'s code path; not empirically\n measured \342\200\224 asserted by inspection of the implementation)\n\nF3 \342\200\224 concurrent reader during in-progress write:\n Icechunk: 0/1000 (readonly_session reads last committed snapshot;\n in-progress writer changes are invisible until commit)\n STAC B0: 1000/1000 (100 percentage points)\n STAC B1: 1000/1000 (100 percentage points)\n [write-ordering does not close the F3 window; the sweeper was not\n invoked during F3 trials because post-hoc reconciliation cannot\n retroactively prevent a reader that already observed the window]\n\nNote: F3 counts represent worst-case exposure (simulated reader always arrives between the two writes). In a real workload, hit probability depends on window duration and reader polling frequency \342\200\224 not measured here.\n\n=== NIRD/Sigma2 S3-compatible object store (100 trials, Icechunk only) ===\n\nF1 \342\200\224 crash after data write, before metadata update:\n Icechunk: 0/100 inconsistencies (0 percentage points)\n [identical mechanism to local FS: abandoned session, last committed\n snapshot unchanged \342\200\224 but here the \"commit\" is a conditional write\n (compare-and-swap) against the object store, not a POSIX rename]\n\nF2 \342\200\224 crash after metadata update, before data write:\n Icechunk: 0/100 (measured via `icechunk_is_metadata_ahead_of_data`, identical\n method and reasoning to local FS \342\200\224 see above)\n\nF3 \342\200\224 concurrent reader during in-progress write:\n Icechunk: 0/100 (readonly_session reads the last committed snapshot;\n in-progress writer changes are invisible until the conditional-write\n commit succeeds)\n\nThe F1/F2/F3 result is identical in kind and in count (zero) to the local-filesystem result. Note what these three scenarios actually exercise on this backend: the abandoned-session path (F1), the committed-snapshot state directly (F2), and a single-writer commit (F3). None of these issues a conditional write that contests an already-moved branch tip \342\200\224 that is what F4, below, tests, and it is the scenario that determines whether this backend\'s CAS implementation is what the claim needs it to be.\n\n=== F4 \342\200\224 concurrent racing writers (the conditional-write / CAS test) ===\n\nTwo sessions branch from the same committed snapshot, write OVERLAPPING data to the same array region (forcing a real conflict \342\200\224 Icechunk can silently rebase non-overlapping edits, which would not test rejection), and both attempt to commit. Expected (CAS enforced): the first commit succeeds and moves the branch tip; the second session is now based on a stale tip and `Session.commit()` must raise `icechunk.ConflictError` (the exact exception name, confirmed against the installed icechunk==2.0.6 API \342\200\224 the `commit()` docstring states: \"If the session is out of date, this will raise a ConflictError exception depicting the conflict that occurred\").\n\n Local filesystem (1000 trials, seed=42):\n inconsistent: 0/1000 (store always ends in a single coherent state)\n conflict_rejected: 1000/1000 (icechunk.ConflictError raised on every stale commit)\n \342\206\222 POSIX rename also enforces a form of compare-and-swap at the filesystem level, so\n this result was expected; it confirms the F4 harness code itself works and measures\n what it claims to measure (an assertion-style check would fail loudly if\n `ConflictError` were not raised, or if the wrong exception name had been pinned \342\200\224\n this was first verified with a 20-trial unit test before the full 1000-trial run).\n\n NIRD/Sigma2 S3-compatible object store (100 trials, seed=43, run with live `MINIO_*`\n credentials \342\200\224 the run that determines whether the conditional-write guarantee, the\n actual subject of the claim on object stores, holds on NIRD):\n inconsistent: 0/100 (store always ends in a single coherent state)\n conflict_rejected: 100/100 (icechunk.ConflictError raised on every stale commit,\n over a real network round-trip against a production\n S3-compatible endpoint, not a local POSIX filesystem)\n\n This is the positive control, green, on the real target backend: every stale-tip commit was rejected, and the store was never observed in a mixed/blended state. Had `conflict_rejected` been False on any NIRD trial \342\200\224 the object store accepting a commit from a stale tip \342\200\224 that would itself have been the headline finding (a Contradicted or qualified result), not a footnote. It was not observed; the conditional-write guarantee holds on NIRD/Sigma2, identically in kind to the local-filesystem result and confirmed independently over a real network against a production object store." } } } rows { name { value: "hasLimitationsDescription" } } rows { quad { p_iri { } o_literal { lex: "Object-store trial count is smaller than the local-FS sweep: 100 trials per scenario (including F4, the scenario this Outcome\'s Validated status now rests on most heavily) on NIRD/Sigma2 versus 1000 on local filesystem (see `harness/run_matrix.py` \342\200\224 each object-store trial creates a remote repo over a real network round-trip, so the count is deliberately reduced; the harness\'s own rationale is that 100 trials \"is sufficient to establish the pattern; expand if needed\"). Zero inconsistencies (and zero unrejected conflicts) in 100 trials is meaningfully different from zero in 1000 \342\200\224 a rare bug with per-trial probability between roughly 1% and 0.1% could be present but unobserved at this sample size. This is the main reason Confidence is HighConfidence rather than VeryHighConfidence: the conclusion is unambiguous at the sample size tested, but the sample is an order of magnitude smaller than the local-FS sweep. Expanding the object-store run to 1000 trials (matching local FS) would close this residual gap and is the natural follow-on, should stronger confidence be wanted later.\n\nSingle object-store provider tested: only NIRD/Sigma2 S3-compatible storage was exercised. Different S3-compatible implementations (AWS S3 itself, other MinIO\ndeployments, other national e-infrastructure providers) may differ in their conditional-write semantics or consistency guarantees; this result does not generalise automatically to \"all object stores,\" only to \"the conditional-write path that NIRD\'s implementation provides, which behaved correctly here.\"\n\nDeterministic fault injection: all fault scenarios place the fault or the simulated reader at a fixed point in the write sequence. The 1000/1000 counts for STAC B0/B1 F3 reflect \"the inconsistency window always exists\" \342\200\224 they do not measure how often a real concurrent reader would hit that window in practice. Probabilistic fault injection (randomised fault timing relative to write progress) is deferred to a follow-on run.\n\nF2 B1 = 0 by design, not measurement: B1\'s write-ordering makes the F2 fault point unreachable in the implementation. The zero is an assertion from code inspection, not an empirical result from running the scenario.\n\nSynthetic data only: 256 float32 values per array. Real L2 EO granule sizes may produce wider F3 windows (more data to write = longer gap between zarr write and STAC update), which would increase real-world hit probability but would not change the binary presence/absence result.\n\nF3 staleness window duration not measured \342\200\224 only presence or absence of inconsistency recorded. Quantifying the window requires a concurrent-thread reader design.\n\nF5\342\200\223F6 (partial batch failure, other concurrency patterns) remain out of scope for this vertical slice. F4 (concurrent competing writers) is now in scope, implemented, and run to completion on both backends \342\200\224 see above.\n\nIcechunk 2.0.6 warns that the local filesystem store is \"not safe for concurrent commits.\" Our scenarios involve a single writer at a time (F1/F2/F3), so this does not\naffect the reported results on either backend \342\200\224 but it is a further reason the object-store result, not the local-FS result, should be treated as the primary evidence for the claim: local filesystem is explicitly documented by Icechunk as a development/testing convenience, not the production target.\n\nTest repos created on NIRD during this run (under `icechunk-atomicity-test//` in the project\'s private bucket) are left in place \342\200\224 no automatic cleanup is performed by the harness (see `harness/run_matrix.py`)." } } } rows { name { value: "hasOutcomeRepository" } } rows { prefix { value: "https://github.com/j34ni/" } } rows { name { value: "forrt-replication-zarr-consistency" } } rows { quad { p_iri { } o_iri { prefix_id: 12 } } } rows { name { value: "hasValidationStatus" } } rows { name { value: "Validated" } } rows { quad { p_iri { prefix_id: 10 } o_iri { } } } rows { name { value: "isOutcomeOf" } } rows { prefix { value: "https://w3id.org/sciencelive/np/RAZ0vOtKCde72HBMNvLh99XPyurv0h2IYiXGpChRWNTKE/" } } rows { name { value: "icechunk-atomicity-study-2026" } } rows { quad { p_iri { } o_iri { prefix_id: 13 } } } rows { name { value: "wasAttributedTo" } } rows { prefix { value: "https://orcid.org/" } } rows { name { value: "0000-0002-8763-1643" } } rows { quad { s_iri { prefix_id: 2 name_id: 4 } p_iri { prefix_id: 5 name_id: 27 } o_iri { prefix_id: 14 } g_iri { prefix_id: 2 name_id: 7 } } } rows { prefix { value: "http://xmlns.com/foaf/0.1/" } } rows { name { value: "name" } } rows { quad { s_iri { prefix_id: 14 name_id: 28 } p_iri { prefix_id: 15 } o_literal { lex: "Jean Iaquinta" } g_iri { prefix_id: 2 name_id: 9 } } } rows { name { value: "created" } } rows { datatype { value: "http://www.w3.org/2001/XMLSchema#dateTime" } } rows { quad { s_iri { prefix_id: 1 name_id: 1 } p_iri { prefix_id: 7 name_id: 30 } o_literal { lex: "2026-06-08T12:36:19.353Z" datatype: 2 } } } rows { name { value: "creator" } } rows { quad { p_iri { } o_iri { prefix_id: 14 name_id: 28 } } } rows { name { value: "license" } } rows { prefix { value: "https://creativecommons.org/licenses/by/4.0/" } } rows { quad { p_iri { prefix_id: 7 name_id: 32 } o_iri { prefix_id: 16 name_id: 2 } } } rows { name { value: "introduces" } } rows { quad { p_iri { prefix_id: 6 name_id: 33 } o_iri { prefix_id: 2 name_id: 12 } } } rows { name { value: "wasCreatedAt" } } rows { prefix { id: 8 value: "https://" } } rows { name { value: "platform.sciencelive4all.org" } } rows { quad { p_iri { prefix_id: 6 name_id: 34 } o_iri { prefix_id: 8 } } } rows { quad { p_iri { prefix_id: 11 name_id: 15 } o_literal { lex: "Icechunk atomic commit: zero inconsistencies across F1-F4, including a positive control for conditional-write rejection (F4), on local filesystem and on a real S3-compatible object store (NIRD)" } } } rows { prefix { id: 3 value: "https://w3id.org/np/o/ntemplate/" } } rows { name { value: "wasCreatedFromTemplate" } } rows { prefix { id: 9 value: "https://w3id.org/np/" } } rows { name { value: "RA2zljn0Nw9SadppOyxZoh-_Rxosslrq-vYG-p9SttnJE" } } rows { quad { p_iri { prefix_id: 3 name_id: 36 } o_iri { prefix_id: 9 } } } rows { name { value: "sig" } } rows { name { value: "hasAlgorithm" } } rows { quad { s_iri { prefix_id: 2 } p_iri { prefix_id: 6 } o_literal { lex: "RSA" } } } rows { name { value: "hasPublicKey" } } rows { quad { p_iri { } o_literal { lex: "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzHC00ZumAokw0N4gvAIpWZTTOilFCcmYB4ZhKAcVdxLnRsy0YYniEpexCCD0WEcNc/x9bDUTsrFlz2mkxzvCBpUaFdGKl5E+xZ5zHsuzAZlmBM48+jEbB/bn9kTXnQHJL0KDus2wTdW0QJhaBa7fNZjar7AkoSQSyFzg7+BPDS726I7imAbkQwgAMPop4CkazqiU0ynyUQOLwZR9TCkaR/HlzSVdKwbNXiLnpXMPIiEFUNa/S7c5Iu+PeMYY2ApA/7vvyDqSIG2eFHf7uSaVV8W+5vYzwaTnTiWJ9AXMa28WU5N1EtRNkJhifHUFgh5Y/ch0WgGL1c8ASBxO1HpGrqQkbMTSG3krLspkRS+ms2AFsV0zv7RtG4jw0BnVyjG3L3ZBmYClfSGvLJ05YLFf9Z0CZ+yvei3MudrinJTF9XmzwiyVp4DR/QKCjb3qInO3H9c0GHdZqgQ3OM3fxRpla89PSWVyY0ukq9Rw0KH0G/NtXEr5n1CVavut0UrQmRJweQTvRTAiQdKIoy1Mbr60RwQXoIN8RhbfAd4jtqb+CWm637gU8sX+iv5J0XqEw4C2OJc2mKI0ClvO9NuGMfZG9QV7qZGXhmlYSqifOLKnnPDb54laFg6h+g1X4MyU5yrXe3tYhrNX9NFJrb74YOtKv6CnjU81uPpi0yJOTsfCTS0CAwEAAQ==" } } } rows { name { value: "hasSignature" } } rows { quad { p_iri { } o_literal { lex: "NBTDm4DkmhK6oGua4GfbDCYVcTfSmYmI/Iz+INqDGkHOjn2qDqPvMSBRQIio22EcUpAJ6NKw1nuKP5SDMQSBQEM0iwtG/z4SYyW7zdVseDkwzufWd2L6CCZbwc24P2rmkq3fCoXk3pVaDJPGS4MB/BrTafnYOUJB6y2YCN/B0QQgZB4TT6hdO6NTuIX4BbAMquToH3moPAExJNzJVwu2RNh5Y8io1JkZdqfJYBh5wIdIHa/dW9HBu5qTepJujCBU4KFWAVy77G/r3NWD3o4ud6yNcCdDb/EY/prFJeDHf+bdbJRfTco+i7nu0NJpLKohx7ELCPOgPydElo6ha0XyN+Go0J+YB5qebqXsYuy8p++MaB7MrOteXXgJgsggbRC/2KWmmVLg26oRwykLuoG/Uo7l7vTQyyk/Ep7uEodB/l/0w69pMx2LN71LmhwJLSKlSi0G6E6kMJ1lT+gefwI2DBNWIHTKVnYDbBgEIpzso+UVDYz6Ybl1h3qTmR4UF13wuJW4LIDRhWbnsJLd2XC4pgxrPlwcaaXRFjwiiHZNti5TdMu7Dj2DTR609+EL7P7W/xJk/BkMPalSMRPqCuJFAi1TOqZxYNbVYGTrX7r40EOxhxXO1mK2k09V0I2eXMyeCsQLWeubrK7gF1X0B88NwCH0c3c6/WvYd0MDX/r6khE=" } } } rows { name { value: "hasSignatureTarget" } } rows { quad { p_iri { } o_iri { prefix_id: 1 name_id: 1 } } } rows { name { value: "signedBy" } } rows { quad { p_iri { prefix_id: 6 name_id: 43 } o_iri { prefix_id: 14 name_id: 28 } } }