docs/adr/0248-owner-settings-use-one-revision-fenced-api.md

ADR 0248: Owner settings use one revision-fenced API

Status: Accepted (2026-10-07; VUH-1813).

Decision

Fleet, persona, voice, Discord fields, worker-account holds, Linear follow/wake and host availability settings have one owner API. CLI and TUI read its current snapshot and send its revision with each update. They never retry a conflict or write settings.json after an API error. The service validates the update and rechecks revision and current owner authority before persistence. Response reads use the existing protocol parser for additive optional fields (ADR 0016); requests retain strict validation.

Keep-awake and automatic update preferences live at GET/POST /v1/operator/host-settings; their wording and schemas belong to @clankie/protocol. Non-Mac and hosted bodies refuse keep-awake; managed hosting keeps automatic updates enabled. A setting stores intent; changing a preference does not grant deployment, restart, credential or account authority.

Device forwarding preserves the original device identity and requires current Take Control authority before and after forwarding. Host settings, voice, worker-account holds and Linear follow/wake are explicit relay and hosted operator routes. Persona's paired-device projection remains talkativeness only; the operator route owns its full character settings. Linear GET snapshots gain revision; POST and the retained local PUT alias require an expectedRevision and a nested wake/following value. Old unfenced writes fail validation.

The explicit awake --local-setup on|off command may prepare a local Mac before the service runs; it refuses remote transports. Ordinary awake and update auto require the owner API. When a host preference is saved but its runtime application fails, the API returns 503 with saved: true and its saved snapshot; read GET to reconcile rather than blindly resubmitting.

Local setup remains available where the service may legitimately be down: first-run machine wiring (autostart, working directory, execution connections), local Claude profiles/Codex homes and signed Linear webhook URL setup. Provider credentials use their own broker/account boundaries. These separate setup operations never become an automatic fallback for ordinary owner settings.

Consequences

Every surface can share validation and detect a stale update. App/dashboard controls bind to the public contracts separately. Existing API consumers must read revisions and adopt the fenced envelopes. Checks exercise real HTTP, settings persistence, schema and credential boundaries; they do not restart a service, install a release or contact voice providers.

Desktop pet skins (VUH-2201, 2026-10-13)

The appearance document stores petSkin independently of leadSkin. It is a pack ID; absent or null selects original 2D Clankie. World/chat lead art follows leadSkin as before. Clients that cannot render a pet pack show original 2D Clankie without overwriting the saved ID. Updates preserve the other appearance fields, and use the same owner authority and revision fence.

Primary app navigation (VUH-2560, 2026-10-20)

appearance.navigation is an optional nonempty ordered list of unique app destinations: world, messages, work, terminal, settings. Absence retains World → Messages → Work → Terminal. The first item is the landing view; omitted items remain reachable in the app's expanded menu. There is one owner choice, without device overrides. Existing appearance GET/POST, revision fencing, authority, persistence and hosted forwarding apply unchanged. Other appearance writes preserve navigation. Older readers ignore this additive field.

CLI: clankie navigation reads the current list; clankie navigation set work,messages,world reads its revision and writes through the same API.

Default lead appearance (VUH-2785, 2026-10-25)

An absent appearance.leadSkin now resolves to clankie.lead.hoodie, the canonical lead chosen in VUH-2715. Explicit pack (Classic), custom IDs and existing saved selections remain unchanged. The default applies both when the appearance document is absent and when only the lead field is absent. The schema version and appearance GET/POST shapes remain unchanged; clients still receive an ordinary valid skin ID. The private app retires the Red outfits and migrates those saved IDs to Hoodie through this revision-fenced API. petSkin, worker art and navigation are independent and unchanged.