Skip to content

feat(files): file browser redesign on PC and phone, and RemEx 3.1.0 (versionCode 39) - #56

Merged
clindsay94 merged 10 commits into
mainfrom
claude/clever-cerf-lkvh1u
Oct 8, 2026
Merged

clindsay94 merged 10 commits into
mainfrom
claude/clever-cerf-lkvh1u

Conversation

@clindsay94

Copy link
Copy Markdown
Owner

Checklist

  • Title includes the bead ID: no bead, by Connor's call for this work ("Skip beads for this work")
  • scripts/verify.ps1 was run (-Scope all, Linux). Everything passes except 10 remex.agent.tests failures. The untouched base commit fails the same 10 in this container: headless capture, running as root, and a Remex.Agent vs remex.agent path-case bug. So -Check reports the receipt as FAIL for those 10 only.
  • If Kotlin changed: built and verified with the release variant (testReleaseUnitTest, lintRelease, assembleRelease)
  • If any user-facing string changed: all 9 locale files were updated on both platforms
  • If touching capture, the stream, the decoder, SurfaceView, pairing, or the session guard: read docs/REGRESSION-GUARDS.md first. This PR touches none of those. It adds one guard (phone-only bookmarks never reach the PC), widens the phone-relay guard, and fixes two stale anchors.
  • If any .axaml changed: looked at the result via the ui-verify skill. The skill needs a live Windows desktop, which this session doesn't have. The new File Transfer view was rendered headlessly instead, with PNG snapshots. The 13-cell ui-palette-sweep.ps1 still needs a run on Windows.

What changed and why

The redesigned file screens on both platforms, as specified in docs/specs/2026-10-08-file-browser-redesign.md (§7 says what was delivered and where it differs from the plan).

  1. Protocol. Two additions, neither of which changes protocolVersion, and both gated behind capability flags so older peers are never asked:

    • A new file_read_range_request / file_read_range_response pair, answered by both the PC and the phone.
    • The phone now answers file_hash_request.

    The PC's phone relay forwards both, with paths and ranges checked before they leave the PC. Hashes display as lowercase hex, and the C# and Kotlin versions share one set of test vectors.

  2. PC plumbing. Transfers say Verified only when a hash was really compared. This phase also adds the preview pipeline: classifier, text decoder, tokenizer, and image, text-head and live-tail loaders.

  3. PC screen. An Explorer-style layout: a tree (This PC plus phones), contents with thumbnails, right-click menus, keyboard shortcuts and drag between devices, a live preview, and a SHA-256 panel. A transfer strip runs along the bottom.

  4. Phone plumbing. Kotlin twins of the preview code, held to the C# rules by one shared vector file. Range reads, hashing and browsing against the PC; DocumentsContract listing on the phone; phone-only bookmarks; and Verified on phone transfers.

  5. Phone screen.

    • One adaptive tree for This phone and PC.
    • Tapping a file previews it, with a live tail for logs.
    • The fingerprint card can compute, copy, compare and cross-check against the other device.
    • Send to PC and Save to phone.
    • The toolbar is one primary action plus More.
    • Older PCs get an "update RemEx" note rather than requests they can't answer.
  6. Docs. FILE_SHARING.md, CHANGELOG.md, REGRESSION-GUARDS.md, API_CONTRACTS.md §3.

  7. Release. 3.1.0, Android versionCode 39, set through build-remex.ps1 -Version 3.1.0 -VersionCode 39.

Testing

  • .NET: core 1642/1642, desktop 3929 passed with 4 skipped, render 60/60, agent the 10 container-only failures above.
  • Android: unit tests 2004/2004, and lintRelease passes.
  • Defect injection: every new guard was broken on purpose and went red (more than 40 injections across C# and Kotlin), then green again once restored. Six tests turned out to be blind and were fixed before landing.
  • Review: an independent review of the Kotlin code found 3 HIGH problems, all fixed:
    • Switching PC folders could leave the old listing on screen under the new folder.
    • Release lint failed.
    • The phone had no fallback for older PCs.
  • Still to do on real hardware: the PC palette sweep on Windows, and the Android screen on the emulator across seed, dynamic and fallback colours × light/dark × contrast.

🤖 Generated with Claude Code

https://claude.ai/code/session_01E9cDSGibUBVRtkDqkLMk7V


Generated by Claude Code

clindsay94 and others added 10 commits October 8, 2026 06:24
…oth platforms

Explorer-style three-pane PC screen and an adaptive two-sided Android
screen, one new range-read message for live preview, and file_hash_request
relayed to phones read-only. Records every decision from the planning Q&A
and the six-phase delivery plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E9cDSGibUBVRtkDqkLMk7V
PowerShell 7.6 appends its own platform newline to a string piped into a
native command: CRLF on Windows (why the explicit trailing LF exists), but a
bare LF on Linux, where the explicit one became an empty stdin record. git
check-ignore rejects that as an empty pathspec and exits 128, so every Linux
run threw "git check-ignore failed" before building anything. The explicit
LF is now Windows-only; Windows behaviour is byte-for-byte unchanged.

Reproduced in isolation (exit 128 with the trailing LF, exit 0 without) and
confirmed by a full -Scope all run reaching the end on Linux.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E9cDSGibUBVRtkDqkLMk7V
…n phase 1)

Protocol and hosts for docs/specs/2026-10-08-file-browser-redesign.md.

- New file_read_range_request/response: up to 1 MiB of one file from an
  offset or from the end (live tail), base64 on the control channel.
  Additive, capability-flagged (fileCapabilities.readRange / hash).
- PC host: FileTransferService.ReadRangeAsync / ReadVolumeRangeAsync,
  resolved like a download source (same root, consent and restricted-path
  checks); opened ReadWrite|Delete so a live log can be tailed.
- Phone host: answers range reads and file_hash_request through
  facade.resolve (the browse gate, per request), off the collector, under
  small permit pools; a provider-reported size of 0 is treated as unknown.
- PhoneFileRelay forwards both read-only requests (product decision,
  recorded in REGRESSION-GUARDS), after ReadRefusal checks root, path and
  range; hashes get a 30-minute wait like copy/move.
- FileTransferClient.ReadRangeRemoteAsync (the PC UI's receiver) and an
  optional hash timeout for phone-sized hashes.
- HashFormat (C# + Kotlin twins, shared vectors): hex for display, hex or
  Base64 accepted when pasted, constant-time compare.

Review: csharp-reviewer and kotlin-reviewer both PASS; all their MEDIUM and
LOW findings are fixed here. New guards defect-injected and seen red: relay
ReadRefusal, hash timeout, service bounds, HashFormat length, phone folder
check, range bounds, capability flags, unknown-size handling, read cap.

Gate (verify.ps1 -Scope all, Linux): Android unit tests and lintRelease
pass; core, desktop and render suites pass. The receipt says FAIL because of
10 remex.agent.tests failures that fail identically on a clean checkout of
the base commit in this container (headless capture x7, root-only revoker
injection, two HostIdentityUnification tests with a Remex.Agent/remex.agent
path-case bug), plus ConnectionBecomesRegistryVisibleTests failing once
under load (passes 3/3 alone, passed in the previous full run).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E9cDSGibUBVRtkDqkLMk7V
…gn phase 2)

- A transfer that really compared SHA-256 hashes now finishes "Verified"
  and keeps the hash (hex for display); one whose host sent no hash stays
  "Done". Rows say "Verifying…" while the ends compare. Every path reports
  it: v3/v2 uploads, downloads (only when a hash was compared), and both
  PC-started phone transfers through PhoneFileRelay. Delivered through a
  small ITransferVerificationSink on the IProgress the queue already
  passes, so no work delegate changed. New string in all 9 locales.
- remex.desktop/Services/FilePreview: preview classifier, text decoder
  (BOMs, binary sniff, no half characters at a read's edge), per-line
  syntax colouring (logs incl. .NET fail:/warn:, JSON, XML, code), a
  full-size image loader in 1 MiB reads refusing >40 MiB after one read,
  and a live TextTail that reads only what was added, restarts on
  rotation and bounds an endless line. Bound into the screen in phase 3.

Defect-injected and seen red: download only-when-compared, IsVerified
needs a hash, tail carry bound, image size limit, UTF-8 cut handling.

Gate (verify.ps1 -Scope dotnet, Linux): core, desktop (3903) and render
suites pass; the receipt says FAIL only for the same 10 remex.agent.tests
failures that fail identically on the pre-change base in this container
(headless capture x7, root-only revoker injection, HostIdentityUnification
path case). Android untouched since phase 1's passing Android run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E9cDSGibUBVRtkDqkLMk7V
…review and SHA-256 tools (redesign phase 3)

Replaces the old source-picker screen with three panes:
- Folders: This PC and each connected phone as a lazily loaded tree
  (shared folders, plus a phone's volumes), kept in step with browsing.
- Contents: breadcrumbs, thumbnails in list and grid (ThumbnailCache),
  right-click menus, keyboard shortcuts, drag between devices.
- Preview: follows the selection; full-quality zoomable images, coloured
  text/code/logs with a live tail, details, and an Integrity section to
  compute, copy, compare and verify-against a SHA-256 on another device.
A bottom transfer strip counts active/verified/failed transfers.

Phone-to-phone and folder drops are refused with their own reason
(a test now pins the message; the old one passed on any status).

Gate (verify.ps1 -Scope dotnet, Linux container): core 1642/1642,
desktop 3924 passed/4 skipped, render 60/60. Agent: the same 10
environment-only failures as the base commit (headless capture, root
permissions, path case), none in touched code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E9cDSGibUBVRtkDqkLMk7V
…rified" on phone transfers (redesign phase 4)

- Kotlin twins of the PC preview pipeline (classifier, decoder,
  tokenizer, image/text/tail loaders) under ui/files/preview, held to
  the C# rules by one shared vector file kept byte-identical in
  remex.desktop.tests/Fixtures and the Android test resources.
- PcFileRequests: range-read and SHA-256 requests to the PC, matched
  by request id, routed from the file view model's message feed.
- LocalDocuments: DocumentsContract listing (one query per folder),
  path walking through SharedPathPolicy, range reads and on-phone
  SHA-256.
- PhoneBookmarksRepository: phone-only bookmarks in their own DataStore
  key; "Share with PC" writes the existing Access-from-PC list; grants
  are released only when no list (or whole-device browse) holds them.
- QueuedTransfer.verified: set only when a hash was compared; queue
  rows say Verified with a copy-hash button, and the finish
  notification says "Transfer verified". 3 strings x 9 locales + 1.

Every new guard was defect-injected (15 Kotlin + 1 C#); two blind
tests were fixed before landing. Gate (verify.ps1 -Scope all, Linux):
Android unit 1974/1974 and lintRelease pass; .NET as phase 3, with the
same 10 environment-only agent failures as the base commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E9cDSGibUBVRtkDqkLMk7V
…e preview and SHA-256 tools (redesign phase 5)

- One folder tree for This phone and PC: permanent pane on expanded
  width, a drawer elsewhere; contents and preview through the
  list-detail scaffold, full-screen preview with back on phones.
- This phone: shared folders plus phone-only bookmarks (Add folder);
  "Share with PC" puts one on the existing Access-from-PC list.
- Tapping a file previews it: zoomable images (HEIC included),
  coloured text and logs with Live tail, and a SHA-256 card (compute,
  copy, compare, check against the other device).
- Send to PC / Save to phone through a folder picker of the other
  device, on the existing transfer engine; multi-select on both sides.
- Toolbar reduced to search, sort/view and one primary action + More.
- Mixed versions (spec 2.4): an older PC gets an "update" note instead
  of requests it can't answer; fingerprint buttons follow `hash`.
- Review fixes: switching PC folder clears the old listing first (a
  delete could otherwise hit a same-named file in the new folder);
  listings keep the tree current; preview work stops when it closes;
  phone rows are keyed by document id; grants stay held while a
  transfer uses them; release-lint string reads moved to composition.
- Quick-access strip removed (the tree replaces it), with its 3 strings.
- New guard: nothing under service/ may read phone-only bookmarks
  (REGRESSION-GUARDS.md); two stale engine anchors re-pointed.

58 strings x 9 locales. Every new guard defect-injected (red, then
green). Gate (verify.ps1 -Scope all, Linux): Android unit 2004/2004,
lintRelease passed; .NET with the same 10 environment-only agent
failures as the base commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E9cDSGibUBVRtkDqkLMk7V
…edesign delivered (redesign phase 6)

- FILE_SHARING.md: a "Files screen" section in plain English: the tree,
  live preview, phone-only folders and the Share with PC switch (still
  one consent list), Verified vs Done, and the fingerprint tools.
- Spec section 7: what landed per phase, and where it differs from the
  plan (medium-width Android is one pane at a time, files not folders
  across devices, no phone-side rename/delete, the older-PC fallback,
  same-named phone files), plus what still needs Connor's machines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E9cDSGibUBVRtkDqkLMk7V
Version set with build-remex.ps1 -Version 3.1.0 -VersionCode 39, which
writes version.properties and Directory.Build.props together
(VersionSourceOfTruthTests). CHANGELOG: Unreleased becomes 3.1.0 —
the redesigned file screens on PC and phone.

Gate (verify.ps1 -Scope all, Linux): all suites green apart from the
same 10 environment-only agent failures as the base commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E9cDSGibUBVRtkDqkLMk7V
- What's New on both apps lists the seven 3.1 highlights in all nine
  languages (WhatsNewCount 9 -> 7; AboutScreen.kt follows).
- PC version strings: app.manifest assemblyIdentity 1.13.0.0 -> 3.1.0.0,
  RemEx.iss fallback 2.2.0 -> 3.1.0. build-remex.ps1 now rewrites both
  on every version change (Sync-PinnedVersionStrings), and
  VersionSourceOfTruthTests fails if they drift.
- build-remex.ps1 named RemEx.sln; the file is Remex.sln, so every PC
  build on Linux died at dotnet restore. Fixed, with
  BuildScriptPathCaseTests checking every repo path the script names,
  case included (defect-injected red).
- Release docs: RELEASE-NOTES-3.1.0.md, PLAY-WHATS-NEW-3.1.0.md (9
  locales, each under 500 characters), README "What's new in 3.1" and
  3.1.0 downloads, install docs and version examples, CHANGELOG Fixed.

Verified: Windows publish stamps Remex.Agent.exe 3.1.0.0 (file and
manifest); Linux package builds as remex-agent-v3.1.0-linux-x64.tar.gz
(70.1 MB); APK is 3.1.0 (39). Gate (verify.ps1 -Scope all, Linux): all
green apart from the same 10 environment-only agent failures.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E9cDSGibUBVRtkDqkLMk7V

Copy link
Copy Markdown
Owner Author

CI: both red checks are already red on main, and neither comes from this PR.

  • Linux - build, and test what is proven to run there. This fails at the build step with NETSDK1147: ... workloads must be installed: android for remex.core (net10.0-android). main fails at the same step (runs 39–44, most recently run 37728027340 on 2455dcd). This PR doesn't change remex.core's target frameworks or the workflow. Proposed fix, kept out of this PR: add a step before the build in the linux job of .github/workflows/dotnet.yml:

        - name: Install the Android workload (remex.core also targets net10.0-android)
          run: dotnet workload install android
  • Windows - build and test. The only failures are the 4 WindowsUnelevatedLauncherIntegrationTests ([ElevatedWindowsOnlyFact]). They fail the same way on main (5 failures there; the 5th is ConnectionBecomesRegistryVisibleTests). The runner is elevated with UAC off, so Windows can't produce the medium-integrity token these tests check for. Everything this PR adds passed on the Windows runner:

    • Core 1642/1642, Desktop 3932/3933 (1 skipped), Render 60/60.
    • That includes the Windows-only tests that can't run in the Linux container where this was developed.

    Proposed fix, also kept out of this PR: have ElevatedWindowsOnlyFact skip when HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA is 0, because an unelevated launch can't happen without UAC.

I'm not re-running either job: both fail deterministically on main.


Generated by Claude Code

@clindsay94
clindsay94 merged commit dc181e2 into main Oct 8, 2026
2 of 4 checks passed
@clindsay94
clindsay94 deleted the claude/clever-cerf-lkvh1u branch October 9, 2026 06:21
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