Skip to content

XERK-126: fix repo cloning on Android - #312

Merged
xerhab merged 1 commit into
mainfrom
XERK-126
Jul 25, 2026
Merged

XERK-126: fix repo cloning on Android#312
xerhab merged 1 commit into
mainfrom
XERK-126

Conversation

@xerhab

@xerhab xerhab commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

The bug

In the Android app every host reported "Cloning unavailable — this host reports no GitHub credentials", while the same hosts cloned fine from the web dashboard.

It is a wire-shape mismatch, not a credentials problem. GithubInfo decoded the availability flag as ok; the agent's collect_github() has always named it available and sends no ok key at all:

{"available": true, "login": "xerhab", "repos": [{"nameWithOwner": "xerktech/Turma", ...}]}

TurmaJson sets ignoreUnknownKeys = true, so available was dropped and ok fell to its false default on every host — the clone bar rendered its greyed-out note and nothing else. The hub passes the github block through untouched, so the web UI (which reads gh.available) was unaffected.

Fix: rename the field to the wire key.

While in there

With the bar reachable again it was clear it had never really been used, so it now matches index.html's cloneBar/cloneBody — closing the PARITY.md P1 line "Clone bar: collapse + search + multi-select + 🔒 private marker + clone-job status rows":

  • collapsible header naming the gh login (Clone from GitHub · as <login>)
  • search box over the repo list
  • multi-select list with the 🔒 private marker and an "already here" row for repos the host already has (kept listed rather than hidden — a missing row reads as a lost repo)
  • free-text owner/repo box, and a Clone N button firing one POST per spec
  • clone-job status rows (Cloning X… / ✓ Cloned X / ⚠ Clone of X failed: <reason>), which stay visible while the panel is collapsed. Without these a queued clone gave no feedback whatsoever until the repo silently appeared in the tree — so even with the decode fixed, cloning was indistinguishable from still-broken.
  • offline hosts can browse but not fire (the clone rides the heartbeat)

The list/spec/job-row logic is extracted to core/Clone.kt (cloneCandidates / cloneSpecs / cloneRepoName / cloneJobRow), per the repo's pure-core convention, leaving the Compose widget a thin renderer.

How it was verified

  • The decode fix is exercised against the real thing. I ran the agent's own collect_github() on a live host and pinned its verbatim output into CloneTest as a decode regression test — that payload is where the available/ok mismatch above comes from, so the bug is reproduced and closed against real agent output rather than a hand-written fixture.
  • CloneTest adds 12 cases (decode incl. the login: null unconfigured-host path, candidate list + search, spec parsing/dedupe, the three job-row states).
  • Full Android gate green locally: gradle testDebugUnitTest assembleDebug151 tests, 0 failures.

Not verified on a device. The Compose changes compile and assemble, but the agent image ships no emulator or system image and the project has no JVM UI-test harness (only instrumented androidTest, which CI doesn't run), so the rendered bar has not been driven on hardware. That is why the widget's logic was pushed down into core/Clone.kt — the behavior is tested even though the pixels are not.

Parity / docs

  • android/PARITY.md: the clone-bar P1 line moves to Done; the one remaining gap (the web's optimistic pre-echo clone row — Android shows a "clone queued" snackbar and the real row a beat later) is recorded as P2.
  • CLAUDE.md: one line under "GitHub block and cloning" naming available as the key every client must gate on, so this doesn't get re-broken.

The Android clone bar showed "no GitHub credentials" on every host while the
web dashboard cloned fine. GithubInfo decoded the availability flag as `ok`;
the agent's collect_github() has always named it `available` and sends no `ok`
key at all, so with ignoreUnknownKeys it silently defaulted to false everywhere
and the bar rendered its greyed-out note instead of the picker.

Rename the field to the wire key, and guard it with a decode test pinned to a
payload captured verbatim from a live agent.

With the bar reachable again, catch it up with index.html's cloneBar/cloneBody
(the P1 parity line this closes): collapsible header naming the gh login, a
search box, a multi-select list marking private repos and ones already on the
host, free-text owner/repo, a "Clone N" button firing one POST per spec, and
the agent's clone-job rows (cloning / cloned / failed + reason) — which stay
visible while collapsed, since without them a queued clone gave no feedback at
all until the repo silently appeared. Offline hosts browse but can't fire.

The list/spec/job-row logic lives in core/Clone.kt so it is JVM-tested against
the web behavior rather than eyeballed on a phone.
@xerhab
xerhab merged commit 3a45c05 into main Jul 25, 2026
1 check passed
@xerhab
xerhab deleted the XERK-126 branch July 25, 2026 20:51
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.

1 participant