Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 135 additions & 8 deletions bun.lock

Large diffs are not rendered by default.

49 changes: 49 additions & 0 deletions docs/admissions-recovery-2026-10-30.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Admissions recovery review — 2026-10-30

Addresses Shrey's Desktop #60 review and the follow-up on merged Mobile #1.

## Behavior

- Invalid JSON and invalid journal structures fail closed and report mobile storage failure. Pairing failure preserves the original file.
- Explicit Reset stops access, privately quarantines invalid admissions bytes, revokes device access, and removes tunnel identity. A durable recovery marker is written before quarantine, so a process crash cannot silently reopen without lost-history protection. A failed storage operation rejects Reset and reports an error.
- Healthy journals are never removed by Reset; unresolved forwarding records remain unresolved.
- After corruption recovery, old conversations remain readable. An exact matching stored user message can reconcile without sending. Unknown or mismatched old prompts are rejected with instructions to inspect Desktop and start a new conversation on the phone.
- New conversations created through the connector use a fresh random identity, reject caller-selected IDs, and are recorded durably before they can send. This avoids relying on wall clocks to distinguish old/new conversations. Restart and healthy Reset retain that proof; recovering a newly corrupt journal loses the allowlist and blocks those now-uncertain sessions too.
- The Mobile source and Desktop generated source/tests match byte-for-byte. The pinned integration generator is tested independently on its documented source pin; the Desktop PR follow-up is also merged locally with current dev `f8c7feacae8a8cf89d9b9cb05a780e306f845d7d`.

## Evidence

The original tests failed before the recovery implementation. A fresh source reviewer then reproduced a backward-clock duplicate-delivery risk in the first timestamp design. The clock regression failed before replacing that design with durable fresh session identities, then passed afterward.

| Check | Result |
| --- | --- |
| Mobile connector suite | 65 passed |
| Mirrored Desktop connector suite | 65 passed |
| Real local HTTP pairing/recovery/prompt/reset/restart regression | Passed inside both connector suites |
| Mobile Jest / security / TypeScript / lint | 76 / 15 passed; typecheck and lint passed |
| Desktop source suite on recovery branch | 170 passed |
| Recovery-branch server suite | 324 passed; 2 existing skips |
| Actual Desktop HTTP routes/database + local test-model recovery | Passed; two intended prompts, two model calls, two upstream submissions; retries did not add submissions |
| App unit suite on recovery branch | 748 passed; 1 baseline Arabic UI parity failure |
| App browser-condition suite | 55 passed |
| Mobile Settings Chromium cases | 5 passed |
| Integration generator / host browser helper | 3 / 3 passed |
| Desktop/app/e2e/server typechecks | Passed |

The app unit failure is `i18n parity > non-English locales have every English key and required plural variants`: four missing `ui.message.outputLimit*` keys in Arabic. It was reproduced from the current dev dictionaries/test in an isolated temporary directory. This change does not alter the parity test or UI locale dictionaries.

These checks establish source and local fixture behavior. They do not establish installed Electron, physical Android/iOS, native Windows/macOS, store signing, or release acceptance. No installation, deployment, push, PR, or review reply was made for this follow-up. Targeted packaged Linux checks are recorded below.

## Additional actual-server verification

The Desktop `packages/opencode/test/server/httpapi-session.test.ts` regression uses the actual HTTP routes and database with the mirrored connector and a local deterministic model server. It checks an original prompt/reply, corrupt journal rejection, explicit recovery, reconciliation of the original stored retry, 409 rejection of unknown old delivery, connector-generated session creation, the new conversation reply, and Reset/reopen retry. Both upstream submission counts and model-call counts remain exactly two for the two intended prompts. This complements the gateway pairing fixture; it does not substitute for an installed Electron/phone test. The complete recovery-branch server suite passed 324 tests with two existing skips.

## Targeted packaged Linux qualification

A delegated gpt-6-luna/max worker built a combined temporary tree from support `c76b871` plus an uncommitted, conflict-free merge of recovery `22f91ba`. Later recovery `4d6ac75` adds a server test/documentation only; recovery product source is unchanged. The unpacked Linux package is `/tmp/bc-desktop-qualification.F13MBy/packages/desktop/dist/linux-unpacked`. Its `resources/app.asar` SHA-256 is `d7a49b14cc36e6e029db41cbdcb8b409b2862e17c4e6b27a006d1579208b15b6` (independently checked by the primary agent). The bundled CLI version smoke test passed.

The packaged Electron 42.3.3 app launched with `isPackaged=true`, version 1.15.46, inside isolated user/network namespaces and Xvfb with a temporary home/profile and no provider credentials. From the top-level renderer, the shipped preload's `exportDebugLogs()` called the registered main IPC and produced a mode-0600 ZIP (705–707 bytes across repeated runs) containing only `support-bundle.json`. Its nine allowlisted top-level keys were `formatVersion`, `generated`, `version`, `os`, `channel`, `auth`, `config`, `database`, and `errors`; auth was signed out. The Settings screen was behind sign-in, so this verifies the packaged export IPC path alongside the separate Chromium Settings UI tests.

A synthetic corrupt journal was then written under isolated userData. The renderer's shipped `mobileAccess({type:'reset'})` IPC preserved the exact damaged bytes in one `.corrupt-*` backup, removed the active damaged journal, persisted a `.recovery` marker, and returned off/disabled without an error. Backup and marker modes were 0600. No tunnel was enabled, and the namespace had loopback only. These checks do not establish real hosted-tunnel connectivity from the package, physical-phone pairing, native Windows/macOS behavior, signing, or release acceptance. The package was not installed or published.

Detailed delegated report and retained harness: `/tmp/bc-desktop-qualification.F13MBy/LOCAL_LINUX_QUALIFICATION.md`. The combined staged source tree was `39464cee6416c16391412040016f27ee55bc77ef`.
Binary file added docs/assets/mobile-access/after-provisioning.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/mobile-access/before-provisioning.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 53 additions & 0 deletions docs/mobile-access-acceptance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Mobile access acceptance

The connector uses the existing native local Desktop sidecar and `https://tunnel.bharatcode.ai`. Mobile never starts another agent. WSL-selected runtimes are outside the current connection scope; do not infer Windows/WSL parity from Linux source checks.

## October 27 reliability fixes

Mobile-access controller construction no longer reads or writes storage. Guarded restoration and explicit actions load device state; invalid or inaccessible mobile state reports an error in Mobile devices settings without rejecting Desktop initialization. Failed reads never replace the registry. An explicit, confirmed revoke-all/reset preserves an invalid registry as a private `devices.json.corrupt-*` backup before creating clean state. Permission or other I/O failures do not trigger this recovery. Backups remain inside the Desktop private user-data directory and must not be attached to public issues.

Reset and Disable cancel in-progress provisioning before joining the action queue. They also invalidate queued Pair or Restore work; asynchronous storage reads cannot start another connection after cancellation. A failed enabled-preference write closes the newly created gateway/tunnel and leaves access disabled. The settings component permits Reset during provisioning, keeps other controls busy until outstanding actions settle, and ignores stale action/poll responses. Storage errors have their own localized message.

Directory listing enumerates a directory before opening regular-file handles, avoiding reliance on POSIX directory handles. File-handle type/size checks and resolved project confinement remain in place. Actual Windows/macOS acceptance remains a separate check.

## Reproducible source checks

Run from package directories:

```sh
# packages/mobile-connector
node --test

# packages/desktop
bun run test
bun run typecheck

# packages/app
bun run typecheck
bun run typecheck:e2e
bun run test:unit
bun run test:browser
bun x playwright test --config e2e/reproduction/mobile-settings/playwright.config.ts
```

The dedicated browser fixture renders the production Settings component, language provider and controls. It supplies deterministic responses only at the IPC boundary. It covers storage errors, provisioning/reset cancellation, matching-code approval, decline and confirmed individual revocation. Its screenshots demonstrate renderer behavior, not actual Electron IPC, certificate issuance or a phone connection. The cancellation test controls the browser clock so a later polling refresh cannot hide a stale cancelled-action error.

The pre-existing Arabic localization-parity failure at the reviewed base concerns four `ui.message.outputLimit*` keys. Record that failure if still present; do not suppress it or relabel the complete app suite green. Exact-source qualification, artifact identity and results must be recorded before claiming a build is ready for user acceptance.

## Final user acceptance

Record Desktop candidate hash/source commit, Mobile version/build, OS/device and approximate time with each result. Use a disposable phone identity and preserve existing paired devices. Run destructive reset/storage-recovery checks only in a disposable Desktop profile.

- [ ] Open the real Desktop Settings → Mobile devices, with the intended account and native local runtime. Verify startup/off status and existing devices.
- [ ] Start first-time connection; verify progress, Cancel and Reset. On a later connection, confirm the saved identity/certificate is reused.
- [ ] Scan the real QR with the physical phone camera. Confirm both screens show the same six-digit code; approve using the real Desktop button. A different/mismatched code must never be approved.
- [ ] Decline a disposable claim, refresh an invitation and test expiry; old claims must not gain access.
- [ ] Open the link with Mobile already running and from a cold standalone start. Neither path should require a development launcher or manual route refresh.
- [ ] Open an existing native Desktop conversation and a fresh test conversation. Send a bounded real-model prompt; see incremental text on the phone and the same conversation on Desktop.
- [ ] Exercise supported model/agent selection, Stop, questions/permissions, files and working changes in a disposable project.
- [ ] Interrupt the phone network during a response, restore it, and verify recovery/history without a duplicate user prompt. Exercise background/resume and Desktop sleep/resume separately.
- [ ] Revoke only the disposable phone during streaming. Further requests must be unauthorized and Send disabled; already-buffered text may arrive. Other devices must remain paired.
- [ ] Re-pair and verify the old credential remains rejected. Turn off remote access and verify it remains off after a user-controlled Desktop restart; deliberate re-enablement should preserve authorized devices.
- [ ] In an isolated profile, exercise Reset during certificate issuance and corrupt-registry recovery. Confirm remote access is off, phone credentials are revoked, and private damaged-state bytes are retained. Retry failed hosted deletion without discarding its stored identity.

Camera, physical Android/iOS, real Desktop pointer interaction, hosted transport, signed distribution and platform coverage must be reported independently. Passing fixture/source checks does not check these boxes.
35 changes: 35 additions & 0 deletions docs/mobile-access-fixes-2026-10-28.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Mobile-access follow-up, October 28, 2026

## Desktop changes

The optional mobile feature no longer reads its directory during controller construction or lets malformed/unavailable storage reject Desktop initialization. Settings reports a fixed storage error without exposing or overwriting saved data. Explicit reset preserves a private backup of a damaged registry before rebuilding it.

Reset/disable invalidate pending provisioning before entering the action queue. Queued pair and restore operations check cancellation before opening new resources. Failed persistence closes a newly opened tunnel. Settings keeps reset usable during provisioning and ignores late responses from superseded operations. File enumeration also avoids opening directories as regular files.

The pairing QR has a four-module clear border, verified from its generated PNG pixels. This rendering fix is separate from the Mobile camera initialization failure fixed by Expo Camera 58.0.13.

## Verification

Fresh October 28 checks passed: 52 connector tests; 168 Desktop tests; Desktop package and release typechecks; two generator tests and three Linux browser-helper tests in the Mobile companion. The earlier renderer qualification passed five settings Playwright cases, 55 browser tests, 30 sidecar route tests, and app/e2e typechecks.

The earlier app unit run had 748 passes and one Arabic translation-parity failure reproduced on the clean base, involving four existing `ui.message.outputLimit` keys. No unrelated translations were changed and no failure was suppressed. Earlier detailed results remain in [the October 25 report](mobile-access-verification-2026-10-25.md).

## Settings renderer fixtures

These screenshots render the production SettingsMobile component with the same isolated Playwright fixture (1440 × 1080, fake IPC response, and a held pending Pair request). The before image uses the original PR #60 head d9d6c3c; Reset is disabled during provisioning. The after image uses the reliability-fix renderer from 6331ba9; Reset remains available to cancel provisioning. Both are synthetic renderer evidence only: no Desktop profile, sidecar, tunnel, phone, camera, QR image, or pairing payload was used.

![Before: SettingsMobile renderer fixture with Reset disabled during synthetic provisioning](assets/mobile-access/before-provisioning.png)

![After: SettingsMobile renderer fixture with Reset enabled during the same synthetic provisioning state](assets/mobile-access/after-provisioning.png)

## Physical Android result

On October 28, the user scanned the installed Desktop QR with a OnePlus CPH2691 running Android 17. The phone reached matching-code approval, the user approved on Desktop, and real Desktop conversations loaded. This was a physical camera scan and user-operated approval, rather than a pasted link or an automated IPC approval.

The phone ran the separate `BharatCode Camera QA` package, built from Mobile source `0d799e09a9d5a275f4958ba88eb1c8cadadfa6c4` with temporary diagnostic counters and `expo-camera` built from source. R8 remained enabled. Installed APK SHA-256: `8462f4085100d318051b704bd6b345a0b992036244813115ee2fb231469f1653`. The existing installed Desktop and its existing sidecar handled pairing; this is not acceptance of a newly packaged Desktop build.

No camera images, QR contents or credentials were recorded by the counters. No phone permissions were changed by automation, and the existing app data and Desktop profile were preserved. This does not establish iOS, store signing, or all-platform release acceptance.

## Release boundary

The previous Linux AppImage was built from `6331ba9`, before the QR-border change. It must not be represented as the final artifact. Official publication follows `.github/workflows/README.md`: build an exact-source cohort, record manual platform acceptance, then publish those same accepted bytes. A source PR or the physical Mobile result above does not replace that gate.
Loading