Assembles a claimed session's shard ledger into recording.wav + peaks.json and finishes the claim.
Flow: read the session's terminal counts and the stored rows' positions (both bounded before any
shard bytes are loaded) → without ForcePartial, hand the claim back (Deferred) when the positions
alone show the recording cannot be Complete → resolve where the recording is stored → load and parse
the shards (a shard that fails to parse, or whose header disagrees with its ledger row, is logged and
treated as missing) → decode (AssembleOpusRecordingPcm; a shard with an undecodable packet is
logged and silenced) → when the session already has its own recording.wav and shards are missing,
read it and fill those ranges from it (FillGapsFromPreviousRecording: a Complete assembly deleted
the rows its audio came from, so a re-opened session must not overwrite that audio with silence; the
early position-only Deferred is skipped for such a session for the same reason). Then:
incomplete and not ForcePartial: the claim is handed back as Pending with no file, keeping the
gaps already stored for the session's existing recording → Deferred;
otherwise the gaps are stamped (fenced: a lost claim is Superseded, nothing written) and the WAV
is stored: a first assembly uploads recording.wav
into the session folder; a re-assembly overwrites the session's existing WAV object in place, keeping
its MJ: Files id. peaks.json is rewritten next to it. The finish is fenced on Version; only then
is a new file linked to the session and, for Complete, the assembled shard rows deleted.
The session is never saved through BaseEntity (that rewrites every column and would clobber the
concurrent proc writes to the recording state); the finish proc stamps RecordingFileID.
Never throws. Failures split by kind:
a RecordingAssemblyError (deterministic: bounds exceeded, overlapping or duplicate shards,
no storage account, agent not loadable) is logged, the claim is finished as Failed (when the fence
still holds), and the outcome is Failed;
any other error (SQL, storage, decoder load: likely transient) is logged with the session id and
version, and the claim is LEFT in Assembling with no finish call; the outcome is Retry. The
janitor's stale-Assembling re-claim (with its backoff and give-up cap) retries a closed session,
and a later terminal or shard re-opens a live one.
A lost fence is Superseded and leaves the shards.
Assembles a claimed session's shard ledger into
recording.wav+peaks.jsonand finishes the claim.Flow: read the session's terminal counts and the stored rows' positions (both bounded before any shard bytes are loaded) → without
ForcePartial, hand the claim back (Deferred) when the positions alone show the recording cannot be Complete → resolve where the recording is stored → load and parse the shards (a shard that fails to parse, or whose header disagrees with its ledger row, is logged and treated as missing) → decode (AssembleOpusRecordingPcm; a shard with an undecodable packet is logged and silenced) → when the session already has its ownrecording.wavand shards are missing, read it and fill those ranges from it (FillGapsFromPreviousRecording: a Complete assembly deleted the rows its audio came from, so a re-opened session must not overwrite that audio with silence; the early position-only Deferred is skipped for such a session for the same reason). Then:ForcePartial: the claim is handed back asPendingwith no file, keeping the gaps already stored for the session's existing recording →Deferred;Superseded, nothing written) and the WAV is stored: a first assembly uploadsrecording.wavinto the session folder; a re-assembly overwrites the session's existing WAV object in place, keeping itsMJ: Filesid.peaks.jsonis rewritten next to it. The finish is fenced onVersion; only then is a new file linked to the session and, forComplete, the assembled shard rows deleted.The session is never saved through
BaseEntity(that rewrites every column and would clobber the concurrent proc writes to the recording state); the finish proc stampsRecordingFileID.Never throws. Failures split by kind:
Failed(when the fence still holds), and the outcome isFailed;Assemblingwith no finish call; the outcome isRetry. The janitor's stale-Assemblingre-claim (with its backoff and give-up cap) retries a closed session, and a later terminal or shard re-opens a live one. A lost fence isSupersededand leaves the shards.