fix(cloud): incrementally replay imported histories - #692
Conversation
Review follow-ups for the bounded imported-history replay: - Fix an infinite synchronous loop in trimMerkleFrontier: a zero-frozen frontier ([].at(-1) == null with pop() a no-op) froze the renderer the first time a merkle plan carried no frozen events. - Validate the cursor's frozen chain in either hash mode instead of gating intactness on mode equality. Pre-checkpoint flat-v1 cursors now migrate to the merkle checkpoint through the ordinary delta append, and a transiently failed turn-id probe downgrades the same way — neither path re-uploads an intact history through an epoch rewrite anymore. - Adopt an upgraded checkpoint locally on the unchanged-session early-out so the next delta takes the bounded path without a network write. - Length-delimit hashStringList via stableStringify; provider-native turn ids are free-form strings and a newline join could collide across element boundaries. - Compute the shrink-dance observation count without forcing the plan, so a first shrink observation no longer hashes the full transcript just to skip the pass. - Error on unparseable Codex cloud turn ids like the Claude reader does instead of returning a silently empty window. - Extract the Merkle frontier helpers into org2CloudMerkleFrontier.ts with direct unit tests (build/append equivalence, JSON round-trip stability, frontier structure validation) plus engine tests for flat-cursor migration, transient probe failure, and checkpoint persistence. Pre-commit hook ran. Total eslint: 20, total circular: 0
|
Review + follow-up fixes landed in 2b9a608 ( Findings fixed:
Merkle helpers are extracted to Verification: Org2Cloud suite 113 files / 1085 tests green; typecheck green; |
Fresh-state runs sample only the post-change state space, so bugs living in the version TRANSITION stay invisible: PR #692's costliest defect (legacy flat cursors forced O(total) epoch rewrites) escaped every run that built its anchors with the new binary, and PR #693's lineage stamp was erased by the very next rescan the new build itself performed. The protocol now demands one cell where the OLD build writes the durable state and the NEW build must ride the ordinary incremental path over it, plus a second-order cycle proving state the new build stamps survives its own next scan. Fault-injection guidance also gains "inject the fault point the change ADDS", since the rotation list only encodes yesterday's failure modes. Pre-commit hook ran. Total eslint: 18, total circular: 0
createZodJsonStorage answers a failed whole-store parse with the initial value, so ONE malformed cursor entry (disk corruption, or a future checkpoint version rolled back onto this build) reset EVERY push cursor at load — and a full reset re-anchors every previously pushed session through an epoch rewrite on its next pass, an epoch-churn storm in the #608 shape whose individual rewrites all look legitimate. The imported replay checkpoint made this failure surface strictly larger (literal version, bounded frontier, integer constraints), so the cursors record now parses per entry and sheds only invalid ones: losing one cursor is the designed recovery — that session alone re-anchors through the server OCC check. Pre-commit hook ran. Total eslint: 18, total circular: 0
|
Follow-up in 88aadae: an escape-class sweep (boundary / cross-version / degraded / delayed-manifestation lenses) over this PR's surface found one more real hazard. |
Problem
Cloud replay network writes were already cursor/segment based, but each dirty background pass for an imported history still loaded, normalized, and hashed the complete local transcript before slicing the network delta. Long-running Claude Code, Codex App, and Cursor IDE sessions therefore paid O(total history) CPU, RAM, and I/O for every new turn, which made background upload appear stalled on large histories.
Solution
Add provider-native, bounded replay checkpoints for Claude Code, Codex App, and Cursor IDE. The first upload remains a complete authoritative anchor. Later uploads validate ordered provider turn IDs plus a compact Merkle frontier, then reread only the previously mutable final turn and newly appended turns while preserving the existing epoch/segment protocol.
Incremental preparation is capped at 50 turns and 16 segments. Invalid checkpoints, reordered or duplicate turn IDs, shrinks, oversized windows, unsupported providers, reader errors, and OCC conflicts all fall back to a complete authoritative reread; a bounded suffix is never used as a rewrite body. The checkpoint is optional and local-only, so existing cursors keep their current behavior. No server schema migration or public cloud wire migration is included.
Potential risks
A historical provider body mutation that preserves every provider-native turn ID outside the reread overlap cannot be detected solely from the compact checkpoint until another invariant fails or a future full re-anchor occurs. Codex catalogs beyond the bounded 4,096-turn cache intentionally lose incremental eligibility as their retained ID prefix shifts and use the full path. Large deltas beyond the turn or segment caps also trade performance for the existing authoritative path.
Rollback is a client revert. Existing persisted cursors remain readable because importedReplay is optional; reverting only removes the bounded preparation optimization. The server continues to store the same epochs, segments, tails, and total counts.
Architecture audit
All 10 architecture-audit layers were covered. Compilation and clippy are green; the production chain was traced from the Cloud sync pass through the source registry, Tauri handlers, and provider readers; no dead parallel entry point was added. Provider-native turnId is explicitly distinguished from normalized event IDs. Unsupported/default cases use the full authoritative loader. Provider parsing remains inside orgtrack-core, the two IPC commands are registered through the canonical handler list, and all three capable providers expose the same ordered-ID plus bounded-window pair. Real serialized segment payloads were exercised against managed Cloud; there is no new initialization path or asymmetric multi-field resolver.
Performance audit
Performance verdict: pass
Verification
No screenshot is attached because this PR has no user-visible UI change. Synthetic Cloud rows were soft-deleted and the isolated CC, Codex, and Cursor fixtures and caches were removed after verification.