Skip to content

feat(settings): finish the Applications pane - main workstation, rename, remove, copy to shared #56

Closed

Description

@Adron

Summary

The Applications settings cluster shipped in PR #25 (+ #30) covering synced settings and the device registry. /help/app-settings documents six user actions in that section; verify which of them the shipped pane actually offers and build the rest. This issue is deliberately a verify-then-build, because the pane exists and some of these may already be present.

The documented actions (/help/app-settings, re-read 2026-09-07)

Action Route In DevicesView today?
Set as main workstation PATCH /api/user/app-settings/{appKey}/devices/{deviceId} verify
Rename a machine PATCH …/devices/{deviceId} verify
Remove a machine DELETE …/devices/{deviceId} verify
View settings (shared + per-machine, with last-updated and size) GET …/{appKey}, GET …/devices/{deviceId}/settings verify
Copy a machine's settings to shared PUT …/{appKey} from the device payload verify
Delete shared settings DELETE /api/user/app-settings/{appKey} verify

The rules that are easy to get wrong

  • The first machine registered automatically becomes the main workstation, and only one machine holds the role at a time. Promoting a machine demotes the previous holder — the UI must reflect that, not just set a flag.
  • Removing the main workstation auto-promotes another machine (the most recently used), server-side. After a remove, refetch rather than assuming which machine now holds the role.
  • Removing a machine deletes its per-machine settings but leaves shared settings alone. Say that in the confirmation.
  • Copy to shared replaces the shared settings wholesale — it is not a merge. Confirm destructively.
  • A new machine's first sign-in seeds from the main workstation's settings, then keeps its own per-machine settings thereafter.
  • This section is free — no subscription needed. Do not gate it.

Standing constraints from the shipped work — do not regress

  • appSettingsKey is "interlinedlist-macos" (AppEnvironment.appSettingsKey) and must stay stable. Changing it orphans every stored setting. There is no registration mechanism; the segment is a free-form namespace (proven by probe: an invented key returns 200 {"devices":[]}).
  • 404 is the ordinary first-run state, not a failure. An app key with nothing stored 404s, and bootstrap returns 404 {"source":"none"} for an unseen device. PR fix(app-settings): treat first-run 404s as empty, enable G17, and correct the appKey claim #30 mapped both to empty — keep it that way.
  • The populated payload shapes are still unverified because nothing was stored at the time. The DTOs are deliberately tolerant. Store real settings from the Mac, then re-probe and tighten — that is the main verification value in this issue.

This is also the sanctioned home for the Sync Agent's config

/help/app-settings frames per-machine settings as exactly the case the Document Sync Agent needs (a sync folder path is meaningful on one machine only). Migrating the agent's UserDefaults state here is in scope if the pane's read/write half is solid — otherwise split it out.

Division of labor

Verify first (no code)

  • Walk App/Features/Settings/DevicesView.swift and DevicesViewModel.swift against the six-action table and tick off what exists.
  • Write real settings from the Mac, then re-probe every GET and record the populated shapes in docs/spikes/.

Kit — only the routes the verify pass shows are missing, plus tightened DTOs once the shapes are known.

Domain — the promote/demote and remove-then-refetch semantics; copy-to-shared as an explicit replace.

App — the missing actions in DevicesView, each with a confirmation that states its actual consequence.

Tests

  • happy — rename, promote, remove, copy-to-shared each round-trip
  • invalid — promote a device that no longer exists
  • upstream-failure — remove succeeds but the refetch fails → the row disappears and the main-workstation badge shows unknown rather than a stale value
  • boundary — exactly one registered device (removing it); first-run 404s still map to empty

Acceptance criteria

  • Every action /help/app-settings documents is available in Settings ▸ Applications.
  • The populated payload shapes are recorded, and the DTOs are as tight as the evidence allows.
  • appSettingsKey is unchanged.
  • Full E2E gate green.

Notes

Follows G17 (work-consolidation.md), shipped in PR #25/#30. Size S–M depending on what the verify pass finds.

Activity

  1. added
    enhancementNew feature or request
    parityWeb-parity gap with the InterlinedList web app
    on Sep 7, 2026
  2. Adron commented on Sep 16, 2026

    @Adron
    MemberAuthor

    Implemented in PR #102. Work continues on the PR.

    The verify-then-build framing this issue asked for was the right call, and the answer was worse than "some may already be present": three actions had UI that could not work, three had no UI at all. All six Kit route builders existed — the routes were right, the bodies and decoders were not — which is why it read as further along than it was. Nothing in production consumed the settings half, so the broken paths were never exercised.

    Storing real settings and re-probing, as this issue said was its main verification value, found five wrong DTO guesses:

    1. Writes need baseVersion — the family is compare-and-set (409 + current when stale). A PUT carrying only theme deleted sidebarWidth: direct evidence for the "copy-to-shared replaces, it is not a merge" rule this issue flags.
    2. deviceName / isDefault, not name / isMainWorkstation — so every row would have shown a raw UUID with no Main badge, silently, because the fields were optional.
    3. PATCH states its own contract: at least one of deviceName or isDefault.
    4. POST/PATCH wrap the device in {"device":…} — decoding bare threw on success.
    5. bootstrap is one document plus a source tag, not the shared+device pair modelled — it decoded to empty on every launch.

    Two findings better than assumed: the server names the promoted successor on a delete, so the "refetch, do not assume" rule is satisfied by reading the response rather than a second call; and POST …/devices is an upsert keyed on deviceId, so unconditional registration would have overwritten the user's rename on every pane visit. Registration is guarded on absence.

    appSettingsKey is unchanged, and first-run 404s still map to empty.

    Two things deliberately not built, both argued in the PR: plumbing the 409's current document (needs typed error bodies in APIClient, which touches every endpoint's error path) and the Sync Agent UserDefaults migration — this issue scopes that in only if the read/write half is solid, and it was non-functional, so it earns its own issue now that this works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestparityWeb-parity gap with the InterlinedList web app

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions