mirror of
https://github.com/stablyai/orca.git
synced 2026-10-06 08:02:28 +00:00
* refactor(mobile): put the branch-compare leg on the lifecycle owner (step 5) The compare kept three hand-rolled guards for one reply, combined in an `isCurrentLoad()` the four exit points each had to remember to call: `branchCompareGenerationRef` (latest-wins), `currentBranchCompareIdentityRef` (the route identity, written in render) and `mountedRef`. The owner replaces the first two. An attempt now `reset()`s and then `load`s, so the newest attempt is the only one holding a live lease, and the reply is published only through `commit(lease, value)`. What retires a compare is named at the call site: this host, this route identity, this workspace. A compare is a refresh, so neither of the owner's other two mechanisms applies here and the `reset()` before each `load` is what says so: nothing it holds is reusable, and no attempt may share its predecessor's reply. Dropping that line makes the second attempt join the first's request and publish a base ref the user already navigated away from. The identity retire moves into the render-phase adjust-on-prop-change block, where the identity ref was written. Leaving it to the next load's scope is not the same thing: that load only starts once the fresh `git.status` returns, and an in-flight compare would publish the old worktree's commits first. `mountedRef` stays. A detached route has no screen to publish to, which is a fact about the view, not about which reply is current. The three decision points that used to write state mid-flight — no base ref, a refused capability, an unreadable reply — are a returned `BranchCompareOutcome` now, so the loader body writes nothing and the screen is written in one place. That also puts this file under the loader-write source fence. No golden moves: the recording suites reproduce byte for byte. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): correct the compare scope comment to the one call that reads it The pilot's wording named two scope consumers; the compare leg has only `load`. What the scope still adds over the render-phase retire is the structural half: a scope the owner has not seen retires on its own. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): give the lifecycle owner's loader a currency probe A loader that spans two round trips had no way to ask whether its scope had moved, so a superseded attempt sent its second request and was only refused at commit. The probe answers exactly the question commit asks and carries nothing to publish with, so the owner's publish fence is unchanged: a loader that stops on it returns null, which the owner already reads as no value. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): keep a superseded branch compare off the wire Restores request-count parity with main for the one path the migration changed: an attempt superseded while it resolved its base ref used to stop before sending git.branchCompare, and under the owner it sent one and was refused at commit. It now stops on the owner's currency probe between the two legs, so the screen is unchanged and so is the request count. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): say what the probe's missing generation actually is Stripping the directive gives TS2339, a member that does not exist, not a privacy error: the probe has no generation to keep private. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): pin that a detached route sends no compare The detach reset() was the only thing retiring an attempt after the route went away, and deleting it left the suite green. This schedule detaches mid base-ref lookup and asserts nothing reaches git.branchCompare; without the reset() it fails with one request sent. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): drop the scope member the identity key already carries statusIdentityKey is `${hostId}\0${worktreeId}`, so listing worktreeId beside it read as a third fence when it fences nothing new. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): split the compare protocol out of the loaders hook The outcome union, the attempt and the screen mapping are the compare leg's own protocol, not the hook's: nothing in them reaches React. Moved verbatim to mobile-branch-compare-outcome.ts with a unit pin for the mapping, which only the hook's schedules covered before. The hook drops from 283 to 230 lines against a 300 limit. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): correct the joiner comment and narrow the compare sender A joiner never receives the probe: its fn is never invoked, it awaits the originating request's promise, and retire() clears inFlight so none can join across a generation bump. The compare attempt takes the operation sender the convention names rather than a whole RpcClient, which it only ever used as that. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb