Repository navigation
Session robustness: per-message timers, no stranded Working, cutover races closed - #35
Merged
Merged
Conversation
Two user-visible bugs since the ACP/session-DO rework, one shared root: - 'Timer opens at 30:00 on send': started_at survived a session settling, so the next turn's Working (steer into a parked run passes fresh_start=false, and the UI's pending-send overlay reads the row directly) displayed the PREVIOUS turn's elapsed. set_status now restamps started_at on any entry into Working from a settled state, drops it entirely on Idle/Errored, and the steer-boundary split restamps too (the timer is per user message, not per child process). The status strip additionally clamps to the in-flight send's own timestamp during the send->ack window. - 'Completed session never marked completed, timer keeps counting': the ACP adapter could emit Steered AFTER the turn's Done when the _session/steering response raced the prompt response (unbiased select). The engine un-parks on Steered and waits for a Done that never comes -- Working forever, heartbeat keeping it fresh. The adapter now settles an in-flight steering call at the turn boundary, BEFORE closing the turn: a landed injection emits its Steered ahead of the Done, a rejected/unsettled call redelivers as the next turn. Also hoists the unknown-id InputRequested drop ahead of the parked gate (un-parking on a dropped event disarmed the idle reaper with no turn running - leaked child, no reap). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… plan chip Fixes from a two-agent adversarial audit of the ACP + session-DO rework, plus what the real-edge smoke run surfaced: - chat2 seed (cutover) was doubly broken: v0.1.32's self-pin (the seed's own doc Arc tripped the pinned re-check -- every seed aborted) and, once un-blocked, a data-loss race: seeding at open() rebuilt from the host's LOCAL doc before the s2 room backfill landed, forking other devices' rows (a queued command, a phone steer) into the retired lineage -- the e2e smoke's cross-device run never executed. Seeds now defer until the room has joined and the doc frontier has been quiet for 2s, and a frontier seal aborts the flip if ANY doc movement happened since the rebuild. - Routed-steer at-least-once ledger: a steer accepted into a dying run's mailbox (idle-reaper race, parked child death, mid-turn error discarding queued boundary steers) is re-dispatched as a fresh turn at run exit -- a message that shows as sent can never silently not run. steer() also gained the liveness re-check + Working-before-note causal ordering the dispatch route already had. - Input-request stranding: resolvers drain at turn end (a late answer un-parked the session into Working with no turn -- timer forever, reaper disarmed); a question arriving while parked is auto-declined instead of un-parking into a dead AwaitingInput. - Live plan/todo chip: the stale-echo filter ate the stable 'acp-plan' id after the first park/steer boundary -- plans never rendered again for the rest of the run. Shared LIVE_PLAN_TOOL_ID const, exempted. - UI standing watches (Sessions/Chats/Devices/Spaces) resubscribe on stream end like the transcript watch -- a daemon restart froze them for the app's life (remote Idle/Completed transitions never arrived: 'the session never completes'). - Reader devices now retire a cached s2 handle when the registry says the chat cut over to chat2 (was: frozen fat lineage forever). - Adopt-path requeued commands commit AFTER the local-update subscription (were invisible to the room -- host never saw them). - Registry batches chunk at 400 ops (delete_space on a big space minted a >cap batch the server rejects forever, wedging ALL later writes from that device, session-status rows included). - EngineChatSink holds a Weak doc ref (a strong one made every chat2 handle perma-pinned -- LRU eviction was dead, warm docs leaked). - chat2: host reacts to ServerReset/PushRejected with a compensating checkpoint; rooms are included in user-signal liveness probes; checkpoint Range-resume restarts when the blob changed mid-download. - ACP: mid-turn Steered drains queued pre-injection updates first (text landed on the wrong side of the split); interrupt escalation timer aborts BEFORE the child is reaped (pid-reuse signal hazard). - e2e-smoke.sh: split cargo build invocations (--example filtered out the comet bin -- the smoke never ran). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- DocHost watches registry chat rows: an s2-mode handle whose chat flipped to roomGen 2 is retired ON the flip (not on the next lucky reopen) and dropped unless a live run holds the doc -- watch streams end cleanly and clients resubscribe onto the chat2 adopt path. Verified live: a viewer streaming a run over s2 (36 delta frames through the real DO) has its stream end at the flip and its resubscribe deliver the full converged transcript. - e2e_driver: wait_stream resubscribes on stream end (the cutover contract every client follows) and parses WatchDocMessages' framed reset/upsert protocol -- the old array-shaped predicate could never match, so the smoke silently never verified 5a. - Full two-device smoke now PASSES end to end against a real wrangler edge: doc-queued cross-device run, transcript sync, working->idle status round-trip, chat2 seed+flip+adopt underneath. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
guilhermexp
pushed a commit
to guilhermexp/comet
that referenced
this pull request
Aug 30, 2026
…mer-fixes Session robustness: per-message timers, no stranded Working, cutover races closed
hoangvu12
referenced
this pull request
in hoangvu12/roboco
Oct 13, 2026
… edge-to-edge, process lifecycle hooks (ui-parity #35) - shell/RootKind, RootCrossfade (CATransition composite: old-on-top fade, Motion.rootSwap 350 ms, no input during the swap, key() identity), RootPresentations (+RegisterRootPresentation), AppRoot + AppRootState + CoreUnavailableScreen (replaces #13's PlaceholderScreen), RootHosts (ContextMenu > Alert > Toast > ActionSheet > GlassMenu > Sheet, all registered for instant dismissal before a swap), SystemBarsEffect, FirstFrameHold (splash hold, 2 s cap), RobocoAppContent - core/AppLifecycleHooks: ProcessLifecycleOwner ON_START/ON_STOP -> onForeground/onBackground, one registerDefaultNetworkCallback -> setNetworkOnline (NetworkOnlineTracker dedupe), single roboco-lifecycle thread, install-once guard, clientReady() re-send - RobocoApplication: routerHooks, appearance, lifecycleHooks install before coreStartup.start(), rootInput from 71's state+engines - MainActivity: applyRobocoEdgeToEdge before super, RobocoAppContent, holdFirstFrame, GalleryLaunch kept; manifest configChanges; accent color + android:colorAccent; gallery 'App root' section - tests (written, not run): RootKindTest, RootPresentationsTest, NetworkOnlineTrackerTest (plain JVM); RootCrossfadeTest, AppRootTest, AppLifecycleHooksTest, MainActivityWindowTest, AppRootScreenshotTest (Robolectric/Roborazzi)
hoangvu12
referenced
this pull request
in hoangvu12/roboco
Oct 13, 2026
…FF), gallery fake-sheet registry - CoreUnavailableScreen had the ink branches swapped (black-on-black in dark); the placeholder's inks are restored verbatim - the gallery's fake sheet registered on the default LocalRootPresentations while AppRoot dismissed the section's own registry — provide the same registry around the frame so 'Paired' toggling removes the panel instantly before the cross-fade, as the ticket's step 12 requires
hoangvu12
referenced
this pull request
in hoangvu12/roboco
Oct 13, 2026
… redeclaration) - SelectTextSheetScreenshotTest and SelectTextSheetTest both declared a private top-level LinearDensity class in package sh.roboco.android.sheets (a JVM redeclaration); the screenshot test's copy is ShotLinearDensity. - SelectTextSheetTest, DetailWidgetTest: assertExists/assertDoesNotExist are SemanticsNodeInteraction members, not importable top-level functions. - RootCrossfadeTest: missing androidx.compose.ui.test.onRoot import.
hoangvu12
referenced
this pull request
in hoangvu12/roboco
Oct 13, 2026
… sheets, transcript tests Production files read clean (FolderRoute, FolderScreen, SessionList, AboutGallery, SelectTextSheet, TextSheetController, DetailWidget, RowWidget, the debug galleries). Test fixes: - FolderRouteTest: missing uniffi.roboco_core.WorkspaceSnapshot import. - FolderScreenTest: the loading case shows a live StatusGlyph spinner, which never lets Compose idle; wrap in ProvideGlyphClock(frozenAtNanos = 0L). - SelectTextSheetTest: setContent once per test (configurations swap through keyed state); frozen-clock showDetail/selectText writes are delivered with Snapshot.sendApplyNotifications(). - DetailWidgetTest: config[Text] throws when absent - use getOrNull; one setContent (the widget toggles through state); the scroll-from-widget drag went down at offset 0, which cannot scroll - it now drags up the same 240 px from the same start.
hoangvu12
referenced
this pull request
in hoangvu12/roboco
Oct 13, 2026
Main and debug sources read clean (enableEdgeToEdge/SystemBarStyle, LocalActivity, ProcessLifecycleOwner, LifecycleRegistry checked with javap; the @JvmName on TabletShellState's setter is a real clash fix). Tests: - AppLifecycleHooksTest: lifecycle 2.11's LifecycleOwner is a Kotlin interface with `val lifecycle`; `override fun getLifecycle()` overrides nothing (compile error). Now `override val lifecycle`. - AppRootTest: Robolectric's default window is 320 dp wide, so the 900/840 dp split cases were clamped; @config qualifiers w1280dp-h900dp. - RootCrossfadeTest, AppRootScreenshotTest#crossfadeMidLight: frozen-clock swaps are delivered (Snapshot.sendApplyNotifications) and given their frame before asserting; post-click SideEffect reads get one frame. - RootPresentationsTest#dismissingTwiceIsSafe: the registry is persistent by design (ticket 35), so the test's handler is now idempotent like a real presenter's dismiss; the expected count stays 1.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the two reported bugs plus a batch of robustness defects found by an adversarial audit of the ACP + session-DO rework, all verified against a real wrangler edge (real DOs) and the real claude harness on haiku.
The two reported bugs
Timer opens at ~30:00 on send.
started_atsurvived a session settling: a steer into a parked run kept the previous turn's base, and the UI's pending-send overlay read the stale row directly. Nowset_statusrestamps on any entry into Working from a settled state, clears the base on Idle/Errored, the steer boundary restamps (timers are per user message), and the status strip clamps to the in-flight send during the send→ack window. Verified live: second message into a parked haiku session stampedstartedAt5ms after send.Session completes but is never marked completed; timer runs forever. The ACP adapter could emit
Steeredafter the turn'sDonewhen the_session/steeringresponse raced the prompt response — the engine un-parks onSteeredand then waits forever for aDonethat never comes, with the liveness heartbeat keeping the phantom fresh. Steering calls now settle at the turn boundary, before the turn closes;Steeredcan never trailDone(regression test:steer_racing_the_turn_end_never_emits_steered_after_done).Audit findings fixed (highlights)
acp-planid after the first park/steer boundary — plans never rendered again for the rest of the run.delete_spaceon a ≥250-chat space minted a forever-rejected batch that blocked every later write from the device, session-status rows included).ServerReset/PushRejectedwith compensating checkpoints, chat2 rooms join user-signal liveness probes, checkpoint Range-resume restarts when the blob changes mid-download.--examplefiltered out the comet bin — the smoke never ran) and driver fixes (resubscribe on stream end; parse the framed WatchDocMessages protocol).Verification
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.