Skip to content

fix(android): preserve quiet Gateway turns and recover completion - #694

Open
Codename-11 wants to merge 5 commits into
devfrom
fix/android-idle-watchdog
Open

Codename-11 wants to merge 5 commits into
devfrom
fix/android-idle-watchdog

Conversation

@Codename-11

@Codename-11 Codename-11 commented Oct 8, 2026 •

Copy link
Copy Markdown
Owner

Summary

Android treated a quiet Gateway turn as failed and sent session.interrupt, including when another client submitted the turn. Quiet tools, provider waits, and compaction now keep their stream alive. Authoritative Gateway state recovers a missing completion frame and its saved answer without sending Stop.

Addresses #676 and preserves the passive-observation guarantees discussed in #365.

Changes

  • Remove idle interruption, local timeout failure, and obsolete per-kind and post-retarget deadlines.
  • Reuse read-only session.active_list checks during quiet periods. Fence settlement by turn, runtime/durable session, profile, socket, and progress generation.
  • Settle admitted unsolicited streams locally. Reconnect reads existing request snapshots; shutdown detaches without cancelling the producer. Pending asks retain their server-owned deadline, and explicit cancellation still works.
  • Distinguish authoritative settlement from ordinary reconnect completion. Missing-final recovery trusts bounded history without requiring partial text to match or the latest window to grow. Normal terminal delivery retains its persistence check.
  • Recover a lost start frame from an exact running activation after reconnect. Idle-only, foreign-session, and queued-successor snapshots cannot establish that proof. Fence activation by the current turn and socket.
  • Add transport and rendered regressions, update the contract documentation, and adapt source conformance to current upstream's extracted helper and additive request methods.

Verification

Candidate: b8b742b785410f04f16e907691b823d845c0d33d, incorporating dev at 95b74c91e5503fdb607ab793d74c2d99fef96ecc through a merge commit.

  • Negative control on 749b6c02ddc99f396964dd4603ea5ef5ad9c7775: both owner and unsolicited regressions failed with Gateway turn timed out through real MockWebServer WebSockets.
  • Lost-start negative control on the previous PR head 23248ea75f5aa275386f479a792085dcb685bc45: both new recovery tests failed because authoritative completion was ignored after a lost start frame.
  • On 0e0e26e0d2b8899c0cfedec33a5905ffac03b34b, scripts/android-lane.ps1 gradle :app:testSideloadDebugUnitTest --tests "*GatewayChatClientTest" --tests "*GatewayEventMapperTest" --tests "*ChatViewModelGatewayInboundTurnTest" --tests "*ChatViewModelRealtimeTurnTest" :app:testGooglePlayDebugUnitTest --tests "*GatewayChatClientTest" --console=plain: 439 sideload tests and 206 Google Play tests passed, with no failures, errors, or skips. The subsequent base merge does not change the Gateway client, chat ViewModel, or these test sources.
  • :app:standardPhoneApi36SideloadDebugAndroidTest with android.testInstrumentationRunnerArguments.class=com.hermesandroid.relay.viewmodel.GatewayExternalFixtureInstrumentedTest#quietTurns_surviveToolAndProviderSilenceAndRecoverUnsolicitedHistory and gatewayFixtureBaseUrl=http://10.0.2.2:8767: 1 passed. The quiet fixture recorded one submit, zero activations, and zero interrupts, and displayed the full saved answer after the missing final frame.
  • The same API 36 task with android.testInstrumentationRunnerArguments.class=com.hermesandroid.relay.viewmodel.GatewayForegroundRecoveryInstrumentedTest: 9 passed on 0e0e26e0d, then 9 passed again on the exact refreshed candidate b8b742b78, with no failures, errors, or skips. This includes lost-start recovery. The quiet-fixture result above is from the earlier pre-follow-up app source. All emulator runs used the serialized Android lane; they are not physical-device certification.
  • From test-fixtures/vanilla-gateway, python -m unittest discover -s tests -v: 38 passed.
  • python -m unittest discover -s scripts/tests -p check_gateway_scenario_conformance_test.py: 14 passed.
  • python scripts/check-gateway-scenario-conformance.py <clean-upstream> --scenario-manifest <scenario.json>: 7 manifests passed at vanilla upstream 08165d58931841cee713468ae89032af7c57060a: quiet_turn_recovery, terminal_gap_activate, terminal_gap_session_info, terminal_gap_active_list, cross_client_observation, clarify_batch, and queued_stop_resume. These are source-conformance checks, not a live provider run.
  • The four recovery/queue conformance manifests were repeated for the lost-start follow-up and passed. Fixture/checker sources are unchanged by the follow-up.
  • Hosted required CI passed on the exact candidate b8b742b785410f04f16e907691b823d845c0d33d: Android tests, build, lint, vanilla-upstream route contract, and the required-checks gate. Unaffected surfaces and release-only jobs were skipped by the path-aware workflow.
  • Physical-device certification remains outstanding. No phone installation or production deployment for this PR.

Screenshots

No layout change. Rendered stream and transcript behavior passed on the Standard Phone API 36 emulator.

Compatibility / risk

Client-only, with no new host routes or migrations. Unknown, absent, unsupported, or failed active-state reads remain unknown; they cannot invent completion. A host that provides no terminal or authoritative state still requires transport recovery or explicit Stop. Rollback is a revert.

Native request replay uses the existing read-only session.events.since surface. No interrupt-reason fields are added to the wire. The source checker still requires every existing request contract while allowing additional methods that Android can decline with JSON-RPC method-not-found.

Lineage / contributor credit

Checklist

  • Target branch is dev
  • Scope is focused and related issues/PRs are linked
  • Android lint and focused tests ran
  • Translation changes: N/A
  • Server/plugin changes: no production changes; fixture tests and conformance ran
  • Desktop changes: N/A
  • Docs/site changes: contract documentation updated; no site routes changed
  • Rendered behavior tested on API 36; physical-device proof remains separate
  • Commit messages follow Conventional Commits
  • CHANGELOG.md updated
  • Public writing hygiene checked
  • Source PRs linked and contributor authorship preserved

BrokeSkill and others added 5 commits October 7, 2026 21:23
Extend the preserved PR #675 commits with read-only quiet probes, exact unsolicited settlement, passive reconnect and shutdown, native ask replay, and missing-final history reconciliation. Cover owner and observer paths through real Gateway transport and rendered fixture tests. Update current-upstream conformance for extracted helpers and additive requests.

Adapt cancellation-only diagnostics from PR #668. Supersede PR #662's eventual interrupt lease; quiet tools use server-owned liveness instead.

Co-authored-by: ophirhan <ophirhan@users.noreply.github.com>
@ophirhan

Copy link
Copy Markdown
Contributor

Noting handoff from my open #668 (fix/watchdog-no-interrupt-on-retarget).

I agree with the lineage note here: the cancellation-only interrupt diagnostics were the durable piece; the local idle-timeout failure is correctly superseded by this PR’s quiet session.active_list / authoritative recovery path.

I’ve closed #668 as superseded by this consolidation (thanks for preserving co-authorship on the adapted commit). No further action needed from me on that branch unless something from #668’s Diagnostics Stop entry (chat_user_stop) is wanted as a tiny follow-up after this merges — happy to open that separately if useful.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants