# Session schema v1

`RideFormCore` has no UIKit or Core Motion dependency. It supports iOS 17, watchOS 10, and macOS 13. `MotionSession` is the portable in-memory fixture/reopened-session model. The same Codable records are intended for the master bundle: `manifest.json` (`SessionManifest`), `motion.jsonl` (`MotionSample`), optional `accelerometer.jsonl`/`gyroscope.jsonl`, `events.jsonl` (`EventMarker`), and calibration inputs (`Calibration`). The streaming writer and bundle layout implementation belong to U4.

Use `SessionCodec.encode` for any manifest or row. Foundation encodes finite Double values with round-trip precision. A nonfinite Double throws `SessionSchemaError.nonFinite` containing a separately serializable `DiagnosticRecord` with field path and reason; acquisition/writer code must retain that diagnostic, never replace the measurement with zero or a numeric string. `decode` checks the full session header first; `decodeManifest` checks a separately stored manifest. Both explicitly reject unknown schema versions with `unsupportedVersion`. The package does not silently migrate masters.

Each stream is independent and carries its own capability and original sample order. Samples carry sequence IDs, sensor timestamps, and callback receipt uptime. Optional stream capability `.available` with zero rows means available but empty; `.unavailable(reason:)` means unsupported or deliberately unavailable. Optional fields carry `.available`, `.invalid` (retaining a finite raw value if provided), or `.unavailable`; raw validity codes remain separate from human-readable reasons. Magnetic-field calibration accuracy can be saved as its raw validity code. Invalid heading in an arbitrary reference frame is distinct from an SDK-unavailable heading accuracy.

Fused records preserve attitude roll/pitch/yaw in `attitudeRadians` x/y/z, quaternion x/y/z/w, full 3×3 row/column-labelled rotation matrix, angular rate in rad/s, gravity and user acceleration in Core Motion g, magnetic field in µT, and optional heading/accuracy in radians. Raw acceleration is in g; raw angular rate is in rad/s. All timestamps and intervals are seconds. Raw streams never inherit fused times or align by row number. Streams declare units, reference frame, requested interval, counts, and observed statistics in `StreamMetadata`. Defaults represent an unconfigured empty fixture, not hardware evidence.

Sensor time and callback host time are separate clock domains until `ClockMapping` records and validates a mapping. Persist clock names, units, offset and checks. The default host name is a placeholder for configuration; the adapter must declare the actual host clock. Receipt time minus mapped sensor time is delivery age, not physical latency. `ClockAnchor` pairs wall time and host uptime at start/end/interruption. Events retain host time, optional cue schedule time, and actual audio-dispatch time; audible onset is unmeasured.

The canonical core rotation convention is active device-to-reference. Captured Core Motion quaternions and matrices remain unchanged. `RotationConvention` records whether conversion requires identity or inverse after adapter verification; its default is explicitly unverified. Right-handed rider axes are +X forward, +Y left, +Z up. Left tilt is negative rotation about +X (displayed as positive leftward), forward tilt positive about +Y, and axial left turn positive about +Z. `Calibration` retains neutral/left/forward sample sets, reference quaternion, fixed device-to-rider basis, numeric configuration, duration, dispersion, method and verification state. Calibration fitting and sign checks belong to U3.

`SessionManifest` records identity/platform/device/OS/app/build, boundaries/completeness, clocks, rotation conventions, per-stream counts/rates, event count, calibration identity, diagnostics and analysis configuration. `AnalysisConfiguration` stores algorithm/version, gap rule, derivative window, filter/resampling parameters, transition thresholds and formula descriptions. Future analysis lives in separate derived outputs, preserving captured values and completed masters. iPhone/Watch differences are metadata, not separate analysis input models.

The checked-in project uses a shared `RideForm` scheme, local package reference, and synchronized source groups for `App`, `Tests/RideFormAppTests`, and `Tests/RideFormUITests`. It has app unit/UI test targets for later units. The shell itself has no acquisition, lifecycle, calibration or persistence behavior yet.
