# Storage and export contract (schema 1)

`Documents/Sessions/<UUID>.rideform/` contains `manifest.json`, `motion.jsonl`, `events.jsonl`, optional `accelerometer.jsonl`/`gyroscope.jsonl`, and `calibration.json` with neutral and gesture inputs. JSONL contains one original Codable sample per line, with no interpolation or alignment across streams. Capability metadata distinguishes unavailable streams from available streams with no rows. Unknown schema versions fail explicitly.

`SessionWriter` owns a utility serial queue independent of capture. Enqueue calls copy values onto that queue. It appends batches of 64 records and checkpoints every second; explicit checkpoint is also available. Append failure retains the batch and truncates any partially written bytes to their prior offset before retry. Written counts advance only after synchronization. Accepted counts include explicitly rejected nonfinite samples; rejection diagnostics carry field path, stream, and sequence. Loss is accepted minus written. Counts do not include callbacks suppressed by the acquisition stop barrier. Manifest checkpoints and final replacement use atomic writes. Finalization drains every previously queued record, calculates observed sampling statistics from saved rows, and seals the writer. Completed or interrupted finalized masters cannot be reopened by a writer or changed through repository APIs.

Capture must stop before calling repository finalize. The final boundary, capabilities, accepted counts, clock mapping, and rotation convention come from `CaptureSummary`. A SaveError keeps the same writer and original final manifest for `retrySave`; it blocks starting another session until saving succeeds. Errors leave a discoverable incomplete bundle. Relaunch lists completed, interrupted, and incomplete bundles. Loading a truncated invalid final unterminated JSONL row returns preceding complete rows and an explicit diagnostic/incomplete status; corruption in earlier rows fails visibly. A recovered row-count mismatch also marks incomplete.

`recoverIncomplete` retains the source bytes and writes a new UUID bundle. Its diagnostic records source UUID, SHA256 of the canonical recovered input model, recovered stream/event row counts, and unknown missing-tail accepted count. The recovered bundle is interrupted and does not claim a complete original acquisition. No deletion API is provided.

`ShareExporter.prepare` makes a uniquely named temporary export directory and returns a standard ZIP master archive plus CSV/metadata URLs for the iOS share sheet. Cancellation has no master mutation path. ZIP method 0 preserves all regular bundle files byte-for-byte, with standard CRC32 and UTF-8 filenames; ZIP32 size limits fail explicitly. Temporary artifacts are retained. Share preparation should run off the UI thread; presenting the activity controller runs on the main actor. The caller supplies the iPad popover anchor if applicable.

CSV exports have separate motion, accelerometer, gyroscope, event, calibration summary, and calibration input files. Column names state seconds, radians, rad/s, g, microtesla, and device/reference frames. Captured attitude quaternion/matrix convention remains declared in manifest metadata. Optional measurements include status, reason, raw validity code, and preserved value; blank numeric cells mean absent. Calibration configuration is JSON in the summary cell and also supplied in calibration.json. The manifest accompanies CSV files. `String(Double)` and Foundation JSON use round-trip precision; there is no decimal rounding for appearance.

`DerivedSidecar<Payload>` attributes arbitrary Codable analysis outputs to session UUID, SHA256 of sorted-key canonical input JSON, complete AnalysisConfiguration, and its SHA256. `isCurrent` requires matching input contents and exact configuration including algorithm version. Store sidecars separately from immutable masters, use `SessionCodec.encode` to reject nonfinite payloads, and validate currentness before displaying/exporting derived values. U4 exports acquisition evidence; U6 can provide its typed derived CSV only after this check.
