Skip to content

feat(app): manage paired computers through appwire - #1977

Merged
AbirAbbas merged 7 commits into
app/v3from
feat/app-devices
Oct 26, 2026
Merged

AbirAbbas merged 7 commits into
app/v3from
feat/app-devices

Conversation

@AbirAbbas

Copy link
Copy Markdown
Collaborator

What this adds

Paired computers can be seen and managed from the desktop, not only the terminal (/devices, codeaf devices). New appwire methods behind the hello feature devices.manage:

method params result
devices.list {} {devices: {devices: Device[], mode: quiet|on|off|unavailable, line?}}
devices.rename {name} {device, line} — this computer only
devices.remove {id} {id, line} — another computer; bad_params for self, not_found for an unknown id

Device {id, name, platform?, self, removed, last_seen?, online?, remove_line?}: the same facts codeaf devices prints; online is absent when presence is unknown; remove_line is the engine's own sentence about removing that computer, sent on removable rows so the desktop can show it before confirming. Event devices.changed {devices} after a rename/remove made through the app.

  • A never-paired (quiet) home and a sync-off home answer locally with this computer only and make no relay request.
  • A completed removal is not undone by an older roster read, and pairing again clears a removal learned under the old pairing without an engine restart.
  • CLI-only helpers the methods need were moved into small internal functions; the CLI's output is unchanged.

Pairing is not part of this change (#1950 reshapes pairing on dev; app-mode pairing follows once that reaches app/v3).

How it was checked

New tests through the real app backend with the in-process relay (two paired homes: list, rename seen from the other, remove refused for self, remove of the other, stale-read and re-pair cases), request counting for quiet and off homes, and direct wire-shape tests. Reviewed adversarially; all findings fixed. CI is dispatched by hand (it does not trigger for PRs into app/v3); results are in this branch's checks and are compared with the app/v3 baseline as in #1970 and #1971.


🤖 Generated with Claude Code

@AbirAbbas

Copy link
Copy Markdown
Collaborator Author

CI, dispatched by hand and compared with app/v3 575a390 (Full check run 40372386102, PR gate run 40372392409): the Full check on ca3fb93 (run 40395920849, every package) has no failure beyond the baseline's. Along the way CI caught an unused import, two test defects (a 503 standing in for an unreachable relay; an offline wait shorter than the relay's 15 s presence debounce) and a stale atlas symbol after the rename moved into syncsetup.RenameDevice; all fixed in this branch. The PR gate's only red is the light gate's lint in internal/factory/run/chatstage.go, untouched here and failing on the baseline.

🤖 Generated with Claude Code

AbirAbbas and others added 7 commits October 26, 2026 17:30
Add devices.list, devices.rename and devices.remove behind devices.manage,
with typed schema shapes and devices.changed snapshots. List this computer
first, preserve removed rows and omit unknown presence.

Reuse local-first rename and existing removal receipts. Answer quiet, off
and unavailable homes locally. Observe existing directory requests and
watch frames without starting polling, sockets or background relay reads.

Add in-process app-backend and wire-contract tests for paired homes,
presence, local modes, refusal codes and retained mutation results.

Tests were written but not run, as requested. No build or install was run.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Add optional Device.remove_line to the app roster and generated schema. Supply
the existing target-specific engine removal receipt only on active other
computers, so the desktop can show the engine's explanation before confirmation.
Keep the devices.remove result unchanged and omit copy on self and removed rows.

Extend real paired-home appwire coverage for list/event copy and omission, and
check the exact JSON and optional schema field. Update the manual and change
entry to document the additive field and replace the old copy assumption.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Order roster reads against completed removals across sync clients, including
clients outside the app device-operation mutex. Reject responses begun before
the latest removal and retain removed records on equal-version or unversioned
reads until a newer directory version says otherwise.

Add a regression using the real appwire backend and signed in-process directory:
delay an active roster response, remove through the app, release the older
response, fail refresh and require the retained removed record in an app event.
Document the corrected retention behavior and obsolete ordering assumption.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Scope device observations to the normalized relay address, identity and device.
Reset roster, version, presence, refusal and removal ordering when the app reads
a different local binding. Reject later HTTP and watch updates from old clients.

Cover successful terminal re-pairing after learned self removal on the same app
backend, replacement by an identity with a lower directory version, late old
responses/watch facts and a relay change with unchanged identity/device files.
Use real homes, registered appwire, signed HTTP and in-process relays. Update the
manual and change entry to explain recovery without restarting the engine.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
TestAppDevicesV1V3OlderRosterCannotUndoRemoval stood in for a relay outage
with a 503, which the engine rightly reports as an ordinary directory error;
close the test server instead so the refresh is a transport failure, and
require the engine's unreachable sentence. TestAppDevicesV1V5Existing-
WatchSuppliesPresence waited five seconds for the offline announcement that
the relay debounces for fifteen (OfflineDebounce); wait past the debounce.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The device rename moved from cmd/codeaf/devrename.go's tellFleet into
syncsetup.RenameDevice so the app and the CLI share it; the pairing atlas
still named tellFleet, which TestEveryFilePathExists refuses.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@AbirAbbas
AbirAbbas marked this pull request as ready for review October 26, 2026 21:31
@AbirAbbas
AbirAbbas merged commit 7fa34f1 into app/v3 Oct 26, 2026
@AbirAbbas
AbirAbbas deleted the feat/app-devices branch October 26, 2026 21:31
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