fix(runtime-host): preserve durable event order - #4445
Conversation
Dismissing my own review state — the notes stand as comments, not as a merge block.
Astro-Han
left a comment
There was a problem hiding this comment.
(Reposting as a comment — I dismissed my own review state, none of this needs to block you.)
The reordering is real and worth fixing, but I don't think this is the owner, and one legacy path loses events.
P1 — backfilled runs disappear from the copy. runtime-read-model.ts:173-197: a terminal run whose durable ledger is empty gets its events reconstructed from stored messages and placed into view.events. Those events are not in runtimeEventStore. The new read only asks the store, so for such a session sourceEvents is empty for that run; conversation-copy.ts:623's fallback reads the store too and is also empty; assertConversationRuntimeLedgerCopySupported (:392) only checks continuation facts. The branch or revision copy is then written with runtimeEvents: [] for that run while copiedMessages still carries its messages — silently. Worth confirming what classifyTerminalRuntimeLedger does with an empty list; either way the copy shouldn't reach that state.
The ordering owner is one comparator. runtime-read-model.ts:245:
ordered.sort((a, b) =>
a.event.ts - b.event.ts || // primary
a.runIndex - b.runIndex || // durable order, tiebreak only
a.eventIndex - b.eventIndex || …)view.messages is projected from that same sorted array (:301), and so is the replay plan (:361). So after this change one function holds three orderings: sourceEvents durable, copiedMessages still timestamp, side_conversation still timestamp. Demoting ts in that comparator fixes all three together, and then the kind === 'side_conversation' branch and the extra full-session read both go away rather than being added.
If ts is primary on purpose, I'd like to know why — that's the fact that decides this.
P3. readSessionRuntimeEventEntries loads the whole session while source.events is still held, so a long session holds two full ledgers.
6274be7 to
5c4b757
Compare
Use RuntimeReadModel events for conversation copies so backfilled events are retained without a duplicate session-ledger read. Preserve immutable session order across timestamp conflicts and surface durable-order reader failures.
5c4b757 to
386afae
Compare
|
@Astro-Han Thanks for the review. Changed the approach to enforce durable event ordering in |
Summary
Branch and revision copies used timestamp-sorted RuntimeEvents when validating history-compaction checkpoints. If timestamps differed from durable append order, valid checkpoints could be rejected or dropped.
Read RuntimeEvents in durable ordinal order for branch and revision copies, while keeping the existing inline-run boundary and side-conversation behavior.
Fixes #4103
Verification
npm run lintnpm run format:checknpm --workspace @maka/runtime-host run typechecknpm --workspace @maka/runtime-host run test:dist: 1495 passed, 0 failed, 12 skippedapps/desktopandpackages/ui.cargois unavailable in the environment.AI use
Tool(s) and scope: OpenAI Codex — diagnosis, implementation, and tests.
Checklist
Does this PR entail a change in behavior?