Skip to content
C.W.K.
Lesson 04 of 05 · published

Shared Ownership — Two Presets, One Hidden App

~13 min · shared-ownership, owners, reference-counting, overlap

Level 0Idle Hands
0 XP0/39 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"Overlapping active presets share ownership of a Tinker-hidden process; only releasing the final owner restores it." — Tinker architecture, hide invariant 5

The Overlap Problem

Presets overlap naturally. The editing preset hides Claude; a broadcast preset, set up later for a live stream, hides Claude too. Suppose Dad starts editing, then starts a broadcast while still editing, then finishes editing. Should Claude come back? Clearly not — the broadcast is still running and wants Claude hidden. But Tinker hid Claude only once, during editing, so there is only one receipt. Whose is it?

A Receipt Has a Set of Owners

Tinker's answer is to make ownership a set. Each ChangeReceipt carries owners, a set of strings such as preset/edit and preset/broadcast. When broadcast is applied, prepare emits a plan for Claude with needsApply: false (it is already hidden). Before checking that flag, the engine checks the journal: a receipt with the same key already exists, belongs to the same capability, and is in the applied phase. So instead of skipping Claude, the engine adds preset/broadcast to that receipt's owners. No second hide happens and no second receipt is written.

Releasing One Owner

Restoring a preset walks the receipts it owns. If a receipt has more than one owner, restore just removes this owner from the set and moves on — no undo. Only when a receipt's last owner releases it does the capability's undo run. It is reference counting, with the references named instead of counted, which is what lets the Access & Recovery tab show which presets are holding each app.

The Uncertain Exception

There is one case where sharing is refused. If the existing receipt for that key is still prepared — an uncertain change from an earlier interrupted action — or belongs to a different capability, the engine will not add a new owner to it. It throws "An unresolved change already owns …" and the new preset rolls back what it had confirmed so far. Joining an uncertain record would give a second preset a stake in a change nobody can vouch for; better to stop and let Dad resolve it in Recovery.

Code

Joining an existing receipt instead of hiding twice·swift
if let index = journal.changes.firstIndex(where: { $0.key == change.key }) {
  guard journal.changes[index].capability == capability.id,
        journal.changes[index].phase == .applied else {
    throw TinkerError("An unresolved change already owns \(change.key).")
  }
  var next = journal
  next.changes[index].owners.insert(owner)     // share it; no OS call
  try persist(next)
  continue
}
guard change.needsApply else { continue }       // pre-existing state is not ours
Restore removes one owner; only the last owner undoes·swift
for receipt in journal.changes.reversed() where receipt.owners.contains(owner) {
  if receipt.owners.count > 1 {
    var next = journal
    let i = next.changes.firstIndex { $0.key == receipt.key }!
    next.changes[i].owners.remove(owner)         // someone else still needs it
    try persist(next)
    continue
  }
  // last owner: an applied receipt is undone and dropped;
  // a prepared (uncertain) one is left in place for Recovery
}
A timeline of one app under two presets·text
t0  apply edit       Claude visible -> hide -> receipt {owners: edit}
t1  apply broadcast  Claude hidden, receipt exists -> {owners: edit, broadcast}
t2  restore edit     owners -> {broadcast}; Claude stays hidden
t3  restore broadcast last owner -> undo -> Claude visible, receipt removed

External links

Exercise

Extend the timeline: at t1.5 Dad shows Claude by hand with the Dock, while both presets are still active. What happens at t2 and at t3? Then add a t4 where Dad applies edit again. Does Claude get hidden?
Hint
t2 just drops edit from the owners. At t3, broadcast is the last owner; undo finds Claude already visible and returns noLongerApplicable, and the receipt is removed. At t4, Claude is visible and has no receipt, so a fresh plan with needsApply true is prepared and Claude is hidden again with a new receipt — re-applying a preset is a new request, not a re-hide of Dad's manual show.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.