runtime/fork/lineage.rs
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4// Copyright (c) 2026 2youg1 and the sprawling contributors
5
6//! The `run_forked` line: written when a branch is frozen, and read back
7//! when a branch of that branch is rebuilt.
8
9use kernel::event::record::RunForked;
10use kernel::{
11 Address, AxCode, AxError, EventDraft, EventKind, EventRecord, Payload, RunId, Seq, TimeMs,
12};
13
14/// The `run_forked` draft for the city Ledger; the caller supplies the
15/// new run id, the room it lands in, and the clock reading.
16pub fn fork_draft(
17 origin: kernel::Origin,
18 new_run: RunId,
19 addr: Address,
20 t: TimeMs,
21 who: String,
22) -> Result<EventDraft, AxError> {
23 let data = Payload::of(&RunForked {
24 from: origin.run,
25 at_seq: origin.at_seq,
26 })?;
27 Ok(EventDraft {
28 run: new_run,
29 t,
30 who,
31 // The room is on the line because a fold that rebuilds what each
32 // room's session still owes reads it here: the new run's id says
33 // which run continues which, and the address says whose session
34 // has been served.
35 addr: Some(addr),
36 kind: EventKind::RunForked,
37 data,
38 ig: false,
39 })
40}
41
42/// Where `owner` was branched from, read off its `run_forked` line, when
43/// it was. The cut must lie before that line: a lineage that pointed at
44/// or past itself is a damaged history, refused rather than followed,
45/// which is also what makes the rebuild's recursion end.
46pub(super) fn forked_from(owner: RunId, records: &[EventRecord]) -> Result<Option<Seq>, AxError> {
47 let Some(line) = records
48 .iter()
49 .find(|record| record.kind() == EventKind::RunForked)
50 else {
51 return Ok(None);
52 };
53 let origin = line.data().read::<RunForked>()?.at_seq;
54 if origin >= line.seq() {
55 return Err(AxError::failure(
56 AxCode::InvalidArgs,
57 "fork",
58 format!(
59 "{owner} was branched at seq {}, which is not before its own run_forked at seq {}",
60 origin.value(),
61 line.seq().value()
62 ),
63 )
64 .with_recovery("branch from a run whose lineage this history holds in order"));
65 }
66 Ok(Some(origin))
67}