Byt ägare & överlämning — flow doc

Living doc — how changing an object’s owner works now. For why, see ADR-0078, ADR-0080 and the use-case (UC-103). For history, see the spec. This doc is rewritten in place as the flow changes; it does not restate the ADR/spec.

What it is

Two ways to move ownership between ägare (owners):

An object has exactly one ägare (ADR-0078), so a transfer always moves objects from one owner to exactly one other owner.

How it works

Single object — “Byt ägare” (iOS, existing)

changeObjectOwner moves one object’s owner. It is membership-checked and dual-writes Postgres (owner_organization_id) + Neo4j (ownerOrganisationId — British spelling). This is the building block the with-contents transfer generalises.

With contents — gated transaction (API live; iOS UI in progress)

  1. InitiateinitiateOwnershipTransfer(rootObjectId, objectIds, toOrganizationId):
    • Requires the Ägare (owner) role in the giving owner.
    • Freezes the exact set of objects into a frozen list (object_ids, max 1000 objects; the World root is excluded). Only objects the initiator has ownership rights to can be included.
    • Objects with a pending sync (temp-id, not yet on the server) cannot be part of a gated transfer — the UI filters them out with an explanation (temp→server-id handoff class).
  2. Gate / auto-complete — if the initiator also has owner/admin in the receiving owner, the transfer completes immediately (you do not accept from yourself — the Göran Hamberg case). Otherwise it becomes pending: the receiving owner’s owners see an incoming transfer in-app and get an email (“Paul vill ge dig Verktygslådan med 12 saker”).
  3. ResolveacceptOwnershipTransfer(id) / declineOwnershipTransfer(id) by a receiving owner (an admin cannot accept — deliberate); revokeOwnershipTransfer(id) by the initiator while pending.
  4. On accept — the id list is re-validated: objects moved or deleted during the wait are skipped and reported back as skippedObjectIds. The remaining objects change owner in batch (same dual-write pattern as changeObjectOwner, executemany / UNWIND), and the transfer is marked completed.

NORMAL / CURRENT preview semantics

The branch shown for selection is the root’s NORMAL subtree (where things belong) plus what is CURRENT there — items physically present but belonging elsewhere are marked “står här men hör hemma annanstans — följer inte med om du inte bockar i”. Location is not ownership: someone else’s things sitting in your box do not transfer unless you tick them.

Local-first

The initiator’s UI never waits — the transfer shows immediately as “väntar på Göran”, and status updates arrive via sync. The receiving accept is cross-user (server-mediated by definition), but the accept button responds optimistically.

Receipt history (archived, not deleted)

Rows in ownership_transfers are archived, never deleted — they are the receipt/kvitto history. This is deliberate groundwork for future sale (kind is gift now, sale later) and a future NFT ownership gate, where the on-chain confirmation becomes one more gate condition in the same transaction lifecycle (an addition, not a rebuild — ADR-0080).

Current UI status

Known edges