atagen

macltok

public
Star0
Watch0
c9f0fd0ee1cccdd0048f121ba1c8ab1f12482f5d
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git

Blame: docs/segmented-history.md

c9f0fd0ee1ccatagen2026-10-25 06:16 UTC1
# Segmented history implementation
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC2
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC3
The durable implementation is in the macltok `segmented-history` feature
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC4
worktree. Its SDK dependency has been published at
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC5
`49a9cd1aff19df1de7e46710cb6c984a1bddc30e` on the SDK `segmented-history`
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC6
bookmark. The macltok manifest and lockfile are being updated to that revision.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC7
The macltok feature bookmark will also be named `segmented-history`. Malice has
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC8
not yet been pinned to the macltok revision.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC9
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC10
## Current behavior
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC11
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC12
Opening a room loads retained history and its gap rows, then fetches a bounded
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC13
recent window from a fresh server position. The recent fetch is capped at four
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC14
pages of 50 raw events. It preserves retained sections and their older cursors.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC15
If it cannot establish continuity, it installs a durable gap between the
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC16
sections. It stops when continuity is established or accessible history ends.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC17
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC18
`HistoryRepairCommand::FillGap` takes a room ID and stable gap transaction ID.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC19
It fetches up to four backward pages of 50 events and advances only that gap's
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC20
saved cursor. Fill progress and events commit atomically. Concurrent history
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC21
operations for the room return `RoomBusy`. An empty page with an advancing
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC22
cursor keeps the gap. A repeated cursor pauses it. Accessible history ending
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC23
before the retained boundary marks the interval unavailable. Only reaching the
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC24
retained boundary joins sections. Already joined gaps are harmless to stale
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC25
callers.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC26
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC27
The cache stores gap identity, boundary evidence, cursor, and status alongside
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC28
the encrypted linked event chunks. SQLite supports legacy token data and the
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC29
new metadata. IndexedDB stores the optional encrypted metadata field. Room
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC30
close and restart retain sections and fill cursors. Timeline snapshots and
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC31
incremental updates project gaps as `TimelineItem::HistoryGap`. Gap rows retain
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC32
their identity while status changes. Joined markers disappear and unavailable
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC33
intervals remain visible.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC34
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC35
The recent fetch can complete while live sync continues. Cache installation
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC36
uses a candidate state and commits events, gap metadata, and cursor together.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC37
Sync publication is serialized with gap installation. Existing loaded timelines
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC38
receive incremental insertions that preserve retained item identities and
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC39
pending sends. Loading history outside the current timeline window still needs
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC40
a reset.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC41
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC42
Each SDK room cache owns a coordinator task. Recent callers share an in-flight
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC43
request. Cancelling one caller preserves the operation for other subscribers,
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC44
and cancelling all callers stops fetching before installation. Started commits
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC45
finish before the coordinator accepts conflicting work. No mutex guard crosses
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC46
the network request. The idle task holds only a weak cache reference, so it does
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC47
not keep a room cache alive. The SDK UI calls `wait_for_history` as a fence,
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC48
then takes an atomic event and gap snapshot while replacing its cache
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC49
subscription. The macltok watcher is stopped before its timeline reset is sent,
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC50
so old diffs cannot arrive after the reset. A mirror publication acknowledgement
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC51
completes before the command response is released. The room actor starts recent
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC52
loading in the background when the room opens, so opening does not wait for the
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC53
network request.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC54
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC55
Limited sync results persist explicit gap markers in the event cache. Later sync
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC56
updates preserve those markers until the pagination chain reaches the retained
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC57
boundary. The integration uses the room's live timeline flush and watcher reset
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC58
path. It does not reseed the cache. Thread and pinned timelines remain open
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC59
through this update path.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC60
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC61
## Public and wire shapes
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC62
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC63
`HistoryRepairCommand::FillGap { room_id, gap_id }` is the supported targeted
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC64
fill operation, sent through `ClientHandle::execute` as
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC65
`Command::HistoryRepair`. It returns `CommandOutput::Done` on success. The older
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC66
policy and staged repair command variants remain available for serde
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC67
compatibility, but room opening no longer consults the saved policy. `Continue`
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC68
is invalid because continuation must identify its gap. `GetPolicy` and
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC69
`SetPolicy` remain compatibility operations and do not control opening.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC70
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC71
Targeted fill progress is published as a `HistoryRepairReport` with `gap_id`
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC72
set and `phase` set to `Bridging`. This report supplies the loading state while
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC73
the fill runs. Recent loading has its own room keyed `RecentHistoryState` with
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC74
`Idle`, `Loading`, `Loaded`, or `Failed` values. The mirror exposes that state in
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC75
`recent_history`, separately from gap continuity and repair progress.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC76
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC77
`TimelineItem::HistoryGap` carries a `HistoryGap` with `id`,
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC78
`older_boundary`, `newer_boundary`, `lifecycle`, `continuity`, and `failure`.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC79
Lifecycle values are `Idle`, `Fetching`, `Paused`, `Failed`, and `Unavailable`.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC80
Continuity values are `Unresolved`, `Verified`, and `AccessibleStart`. The gap
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC81
ID is an owned transaction ID. Boundary values are optional owned event IDs.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC82
Failure is an optional displayable string. `TimelineState.items` contains these
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC83
items in order with event rows. Mirror snapshots serialize the same item shape,
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC84
and incremental timeline changes carry it through the normal reset or changed
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC85
diff.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC86
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC87
Serde enums use externally tagged variants. A command payload is shaped as
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC88
follows, with the outer `Command` adding its `HistoryRepair` tag.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC89
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC90
```json
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC91
{
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC92
  "FillGap": {
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC93
    "room_id": "!room:example.org",
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC94
    "gap_id": "opaque-gap-transaction-id"
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC95
  }
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC96
}
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC97
```
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC98
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC99
The final history repair publication acknowledgement is internal, skipped by
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC100
serde, and not part of application wire data. The mirror applies the report
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC101
before acknowledging it, so command completion follows publication.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC102
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC103
## Remaining publication and Malice work
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC104
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC105
Publish the SDK revision on the user's fork, then publish the macltok revision
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC106
that pins it. Update the Malice macltok dependency in both native and worker
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC107
manifests and regenerate the applicable lockfiles against that same revision.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC108
The current worktree has not recorded a final publishable commit or revision
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC109
for these changes.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC110
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC111
Malice should remove the saved policy and threshold controls from room opening.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC112
Render each inline gap between its neighboring event sections. A fill should
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC113
preserve the visible event anchor, composer draft, reply or edit state, pending
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC114
sends, and encryption identity. Keep event-context windows independent from
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC115
live room history.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC116
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC117
## Lost interval investigation
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC118
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC119
The reported failure showed retained events followed by newly sent messages,
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC120
with an omitted interval of roughly eight to ten hours presented as continuous.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC121
Older pagination continued before the retained buffer, and a page refresh did
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC122
not recover the missing range. A refresh can reconnect to the existing
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC123
SharedWorker, so it does not prove that the engine restarted.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC124
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC125
The earlier SDK cache path could discard a limited-sync boundary token when a
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC126
timeline was reported unlimited despite cached events. It could also discard a
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC127
token when all incoming events were duplicates. Both cases can hide a real
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC128
discontinuity from older pagination. Initial pagination could also finish
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC129
against retained history before fresh sync arrived. These are concrete failure
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC130
paths, but none has been confirmed as the production cause. The durable gaps
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC131
preserve discontinuities when observed. Startup, subscription, and sync
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC132
diagnostics are still needed to identify where this particular interval was
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC133
lost.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC134
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC135
## Validation commands
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC136
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC137
Run native checks from `../macltok-history-repair` with the repository's Nix
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC138
toolchain.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC139
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC140
```sh
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC141
nix develop --command cargo test --all-targets
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC142
nix develop --command cargo test --all-targets --features sqlite
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC143
nix develop --command cargo clippy --all-targets -- -D warnings
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC144
nix develop --command cargo clippy --all-targets --features sqlite -- -D warnings
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC145
```
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC146
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC147
For the browser worker runtime, use the web devshell. The harness starts a local
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC148
homeserver and runs both worker targets in Firefox.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC149
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC150
```sh
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC151
nix develop .#web -c nu harness/web/run.nu
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC152
```
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC153
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC154
The flake checks include native and WebAssembly compilation with IndexedDB and
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC155
the web worker runtime. `nix flake check` is the aggregate gate and remains
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC156
pending.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC157
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC158
Seven focused SDK recent-history tests and seven segmented-history tests pass.
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC159
The SDK WebAssembly compile passes with IndexedDB enabled. The SDK UI timeline
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC160
checks pass with 123 unit tests, seven room unit tests, and 14 timed integration
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC161
tests. Two headless Firefox tests pass for the IndexedDB shared gap storage
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC162
contract with plaintext and encrypted stores. The full flake gate remains
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC163
pending. Complete worker restart and encrypted room recovery have not been
c9f0fd0ee1ccatagen2026-10-25 06:16 UTC164
tested.