"The open root for permission-aware, reversible Mac actions." — the doc comment above Capability
Why open, Not public
The root lives in a library target, TinkerCore, and its children may live elsewhere. In Swift, a public class can be used outside its module but not subclassed there; only open allows subclassing and overriding across a module boundary. Marking the class and its verbs open is the language-level statement of the design: this type exists to be extended. The engine lives beside the root in TinkerCore but knows only the root; the app and the tests sit outside that module, and either may add a child.
Three Stored Facts
Every capability carries an id (a stable type identifier such as applications.hide that appears in saved presets and in the journal, so it must never be renamed casually), a human name for menus and Settings, and a set of permissions. Today the permission enum has a single case, accessibility. It is still a set, not a boolean, because the engine checks every permission a step needs before the first change, and a future child may need something else.
Five Overridable Members
initialInput is the empty-but-valid input a new command starts from — for hiding, an empty list of bundle IDs. validate checks an input without acting, which is how Settings refuses to save a malformed preset. Then the three verbs. The root's default prepare, apply and undo all throw "Capability has no implementation." rather than crashing: a child that forgets to override fails visibly in the place it is used, and the rest of Tinker keeps running.
Inputs and Payloads Are JSON
Inputs and change payloads travel as JSONValue, the shared family type from CwkKit for arbitrary JSON. That choice is what makes presets editable as data and receipts writable to disk. Two tiny helpers, jsonValue(_:) and decodeValue(_:_:), convert between a child's typed Codable struct and that neutral form by round-tripping through the JSON encoder. The engine never needs to know a child's types; the child decodes its own input at the top of each verb.
Why @MainActor
The whole class is isolated to the main actor. AppKit's application objects are main-thread objects, the journal must be updated in one strict order, and the engine runs one action at a time. Putting capabilities, engine and app model on the same actor lets Swift 6's strict concurrency checking prove that no two threads touch that state at once. It does not make actions take turns: an await inside apply lets other main-actor work run, which is exactly how emergency release gets in. "One action at a time" is the engine's own runtime executing flag (track 4).