4 Commits

Author SHA1 Message Date
Yaojia Wang
f000a9d979 docs(plan): schedule the orphan tmux session client parity work
The server half shipped 2026-07-30 as feature A (4892fa7/f6ef19e/d39a0ab) and
both native clients are at zero. This is NOT a regression — OrphanSessionInfo is
deliberately a separate type rather than a flag on LiveSessionInfo, and
src/server.ts:600 names the reason: the clients decode /live-sessions, so a
variant shape there would have broken them.

It is worth planning anyway because SessionManager.shutdown only detaches the
tmux client for tmux-backed sessions (manager.ts:410-417), so a server restart
or a desktop-app quit turns every session into an orphan. The web launcher lists
them and re-adopts on tap; the phone still shows an empty list — which is the
walk-away case this product exists for.

The plan doc carries the eleven traps a client implementer will hit, each one a
shipped review finding or a documented server subtlety — notably that preview
returns cols/rows of literally 0 (both clients currently size thumbnails from
that response), that a 429 must not collapse to "no orphans", and that the
destructive confirmation must come from count-idle rather than card count (that
one nearly ended 33 shells while the dialog said 24).

Also corrects two now-false claims found while cross-referencing: the ROADMAP
row for tmux discovery was half-stale, and ANDROID_CLIENT_PLAN's do-not-consume
list still listed POST /projects/worktree (both clients consume it now) and
GET /sessions (iOS consumes it, Android does not — so "match iOS" now argues
for building it, not against).
2026-07-30 18:25:09 +02:00
Yaojia Wang
980dbaa928 feat(android): wire the dead code, render the git panel, make the token gate real
Four agents' worth of :app wiring. Everything here existed as tested code with
zero production call sites, or as a screen rendering a shape the server stopped
sending months ago.

Dead code that shipped as features. All three were fully implemented and
unit-tested while being reachable from nothing:

  - Session-list preview thumbnails. SessionsHome called SessionListScreen
    without the `thumbnails` argument, so the `= null` default won and every
    row rendered a placeholder forever — on the home session chooser, whose
    entire purpose is seeing what each session is doing.
  - Quick-reply chips and their palette editor.
  - PointerContextMenu (large-screen secondary click).

ThumbnailPipeline's formal cross-review had been explicitly skipped when it was
written — PROGRESS_ANDROID.md said so in as many words, and it is concurrency
code, so nothing had ever reviewed OR executed it. It has now been reviewed and
kept: the LruCache key, the fair Semaphore(2), the Mutex-guarded in-flight
dedup and the NonCancellable in-flight release are all sound, and a retained
map entry would have poisoned a key forever.

The v0.6 project-detail git panel. Android rendered the pre-w6 shape: one bare
dirty dot, a bare hash+subject commit row, and a worktree heading that renamed
itself to "Branch" at n=1 — the exact behaviour G5 removed. Now the sync band,
unpushed marking with a single upstream boundary, and "Worktrees (n)". The rule
that drives it is enforced, not documented: exactly one state may render green,
and no-upstream is not that state.

Remote approvals are no longer blind. status.preview reaches GateBanner and
PlanGateSheet, so the user can see the command or diff they are approving
instead of a tool name. Absent preview stays an ordinary gate, not an error.

The WEBTERM_TOKEN gate is no longer inert. NetworkModule was calling
OkHttpClientFactory.create(identityProvider) with no cookieJar, so the shared
client ran on CookieJar.NO_COOKIES and none of the cookie plumbing did
anything. It now installs the real AuthCookieJar — one client, so the cookie
rides the WS upgrade too — backed by the Tink-sealed store, and binds
InMemoryAuthCookieStore if a cipher cannot be constructed rather than ever
persisting a shell credential in the clear. The persister runs on an IO scope
because OkHttp calls a CookieJar synchronously and must never block on
DataStore or Tink.

And the user-facing half that made the gate unusable: a token-gated host
answers the pairing probe's unauthenticated GET with 401, which the frozen
taxonomy could only report as "this port is running something else" — so such
a host was impossible to pair AND the copy was misleading. Pairing now routes
to a token prompt and resumes through the same cert-gate choke point. The
token is a parameter, never a field and never part of any UI state.

Docs corrected rather than left aspirational: the plan asserted a cleartext
allowlist "permitted only for private LAN/Tailscale CIDRs", which the platform
cannot express at all — the network-security-config format has no netmask or
prefix syntax. PROGRESS_ANDROID.md now opens with what "COMPLETE" actually
meant, and PROGRESS_LOG.md records the whole pass including what is still
undone: A34/A35 have no code, and device QA remains ~0%.

Verified: ./gradlew test :app:assembleDebug :app:testDebugUnitTest koverVerify
-> BUILD SUCCESSFUL, 893 JVM tests, 0 failures (baseline was 617). The :app
run was re-measured with outputs deleted and --no-build-cache, and the APK
re-packaged, so Hilt graph validity is an observation rather than an
up-to-date claim.
2026-07-30 11:23:23 +02:00
Yaojia Wang
cf88e7c588 docs(android-plan): lock user decisions R1 (accept best-effort FCM) + R7 (use google-auth-library)
Some checks failed
relay-tripwire / cross-tenant-tripwire (push) Has been cancelled
R1: accept best-effort FCM background delivery as a documented limitation.
R7: server FCM sender uses google-auth-library (not hand-rolled RS256).
All three open questions (incl. R2 license) now decided; nothing blocks AW3/AW5.
2026-07-08 11:38:38 +02:00
Yaojia Wang
34e4a88059 docs: Android client implementation plan (multi-agent explore + review)
Phased build spec for an Android client at functional parity with the iOS
client. Produced by a multi-agent workflow: 10 iOS-subsystem explorers + 2
Android-tech researchers -> architect synthesis -> 4-lens adversarial review
-> finalize. Mirrors the iOS SPM package set as Gradle modules; 36 tasks
across waves AW0-AW6 with Owns/Depends/Verify, two de-risking spikes.

R2 (Termux license) independently re-verified and downgraded Critical->Low:
terminal-view/terminal-emulator are Apache-2.0 and on JitPack, not GPL-vendor.

Open questions for the user: R1 (FCM background-delivery acceptance),
R7 (FCM OAuth2 dependency).
2026-07-08 11:31:30 +02:00