Brokkr/docs/provider-contract.md

235 lines
11 KiB
Markdown

# Brokkr Provider Contract
Provider id: `brokkr.creative_tool_broker`
CultMesh base URI: `cultmesh://brokkr`
Eve is deprecated, and Thing 2 is its successor (`thing2:ruling:eve-dashboards-quarantined`, 2026-10-22). Brokkr advertises no operator-dashboard surface; it publishes typed CultMesh state meanwhile.
## Tool Kinds
- `unity-editor`
- `blender-editor`
## Capability Families
- `cultcache.mirror.publish`
- `cultcache.intent.watch`
- `host.status.read`
- `scene.tree.read`
- `component.state.read`
- `selection.read`
- `asset.catalog.read`
- `asset.scriptable_object.create`
- `asset.prefab.instantiate`
- `asset.prefab.variant.create`
- `gameobject.create`
- `component.attach`
- `component.property.write`
- `editor.assets.refresh`
- `editor.lifecycle.write`
- `editor.view.capture`
- `command.receipt.publish`
## Mirror Documents
- `brokkr.unity.host_snapshot.v0` at `unity/host/current`
- `brokkr.unity.command_intent.v0` at `unity/commands/{commandId}`
- `brokkr.unity.command_receipt.v0` at `unity/receipts/{commandId}`
- `brokkr.unity.snapshot_receipt.v0` at `unity/receipts/snapshots/{observedAt}`
- `brokkr.unity.quest_route.v0` at `unity/quest-routes/{routeId}`
- `brokkr.unity.warped_video_frame.v0` at `unity/quest/video/{frameId}`
- `brokkr.blender.host_snapshot.v0` at `blender/host/current`
- `brokkr.blender.command_intent.v0` at `blender/commands/{commandId}`
- `brokkr.blender.command_receipt.v0` at `blender/receipts/{commandId}`
- `brokkr.sync.session.v0` at `sync/sessions/{sessionId}`
- `brokkr.sync.object_binding.v0` at `sync/bindings/objects/{bindingId}`
- `brokkr.sync.var.v0` at `sync/vars/{syncVarId}`
- `brokkr.sync.timeline_binding.v0` at `sync/bindings/timelines/{bindingId}`
- `brokkr.sync.receipt.v0` at `sync/receipts/{receiptId}`
## Sync Organ
Brokkr owns sync correspondence and policy documents. Editor hosts still own
scene mutation. A sync record can request or describe a lane, but it is not real
editor state until Unity or Blender publishes accepted command receipts and a
fresh host snapshot.
Primary sync documents:
- `brokkr.sync.session.v0`: shared sync session and mode.
- `brokkr.sync.object_binding.v0`: Unity GameObject to Blender object/collection
correspondence.
- `brokkr.sync.var.v0`: per-lane sync options such as transform, parent,
material, component property, custom property, timeline frame, camera lens,
or Cinemachine virtual camera.
Editor surfaces may also publish ad hoc sync vars with an explicit kind,
paths, authority, enabled flag, and interpolation. The daemon executes
implemented kinds and preserves unsupported kinds as typed sync policy for
external consumers.
- `brokkr.sync.timeline_binding.v0`: Blender scene/action timeline to Unity
Timeline/Cinemachine correspondence.
- `brokkr.sync.receipt.v0`: daemon sync pass receipts.
Operational commands:
- `dotnet run --project brokkr-command/Brokkr.Command.csproj -- --unity-cache .brokkr/unity-editor.ccmp --action readHost`
- `dotnet run --project brokkr-command/Brokkr.Command.csproj -- --unity-cache .brokkr/unity-editor.ccmp --action createGameObject --name Marker --local-position 0,1,0`
- `brokkr-daemon sync-once --unity-cache .brokkr/unity-editor.ccmp --blender-cache .brokkr/blender-editor.ccmp`
- `brokkr-daemon sync-loop --unity-cache .brokkr/unity-editor.ccmp --blender-cache .brokkr/blender-editor.ccmp --interval-ms 500`
`brokkr-command` is a caller. Each invocation writes one intent through the
directory store (every intent field has a `--kebab-case` option; `--view` and
`--output` fill the capture fields), waits for the receipt, and exits 0 for
`accepted`, 1 for any other status, and 2 for a usage error or a timeout. It
never writes receipts, host snapshots or the agent-commands flag.
`sync-loop` owns scheduling only. It repeatedly invokes the same sync decision
primitive as `sync-once`, writes command intents and sync receipts through the
mirrors, and prints per-pass telemetry to stdout.
Each daemon pass writes the same `brokkr.sync.receipt.v0` document into both
editor mirror stores at `sync/receipts/{receiptId}`. Unity and Blender editor
panels may display the latest receipt, but the daemon remains the owner of pass
status.
Implemented sync lanes:
- Missing counterpart materialization for enabled object bindings. When an
authority side has an object and the target side does not, Brokkr emits
`createObject` or `createGameObject` and waits for the next host snapshot
before applying per-lane mutations.
- Unity GameObject transform to Blender object transform.
- Unity GameObject active state to Blender object visibility.
- Unity GameObject parent id to Blender object parent through `setObjectParent`.
- Unity Renderer material name to Blender object material assignment through
`assignMaterial`.
- Blender object visibility to Unity GameObject active state through
`setGameObjectActive`.
- Blender object parent name to Unity GameObject parent through
`setGameObjectParent`. The Blender parent must have an object binding so
Brokkr can resolve the Unity parent object id.
- Unity serialized component property to Blender object custom property through
`setObjectCustomProperty`. The syncvar kind may be `component-property` or
`custom-property`; authority and paths determine direction. It uses
`unityPropertyPath =
Component.Type::propertyPath` and `blenderPropertyPath = customProperties.name`.
- Blender object transform to Unity GameObject transform.
- Blender object material name to Unity Renderer material assignment through
`assignMaterial`.
- Blender scene frame to Unity timeline time through `setComponentProperty`.
- Blender scene camera transform/FOV to a Unity Cinemachine virtual camera
GameObject/component. The FOV write uses `componentType =
Cinemachine.CinemachineVirtualCamera` and property path `m_Lens.FieldOfView`.
- Blender object custom property to Unity serialized property through
`setComponentProperty`. The syncvar kind may be `custom-property` or
`component-property`; authority and paths determine direction. It uses
`blenderPropertyPath =
customProperties.name` and `unityPropertyPath = Component.Type::propertyPath`
when a Unity component should receive the value.
## Unity Command Actions
All Unity writes use `brokkr.unity.command_intent.v0` and receive
`brokkr.unity.command_receipt.v0`.
- `createGameObject`
- `attachComponent`
- `setGameObjectTransform`
- `setGameObjectActive`
- `setGameObjectParent`
- `setComponentProperty`
- `instantiatePrefab`
- `createPrefabVariant`
- `assignMaterial`
- `createScriptableObject`
- `refreshAssets`
- `setEditorPlayState`
- `setEditorPaused`
- `captureEditorView`
- `saveScene`
Unity owns the mutation. Brokkr advertises the command surface; Verse clients
write typed command intents; Unity executes recognized intents and publishes
receipts. `setComponentProperty` writes the target object by default; when
`componentType` is present, Unity resolves that component on the target
GameObject and writes the serialized property there.
`setGameObjectActive` uses `targetObjectId` and boolean `value`.
`setGameObjectParent` uses `targetObjectId` and `parentObjectId`; an empty
`parentObjectId` reparents the GameObject to the scene root.
`instantiatePrefab` uses `assetPath` as the source prefab, `name` as the
optional instance name, and `parentObjectId` as the optional parent.
`createPrefabVariant` uses `assetPath` as the source prefab and `value` as the
destination prefab path.
`createScriptableObject` uses `componentType` as the ScriptableObject type and
`assetPath` as the destination asset path.
`refreshAssets` forces an AssetDatabase refresh. `setEditorPlayState` and
`setEditorPaused` take boolean `value`; the editor service restarts from
`[InitializeOnLoad]` after the resulting domain reload and keeps draining.
`captureEditorView` renders `viewKind` (`scene` or `game`) to a PNG at `width`
by `height` (0 means the camera size, at most 8192). `outputPath` is a plain
relative path ending in `.png`, resolved under `<project>/.brokkr/captures/`;
anything else is denied. `saveScene` saves the open scenes.
### Admission
Before any intent runs, `BrokkrCommandPolicy` checks it against the project's
allowed-action list (window field "Allowed Actions", per project). By default
scene mutation and editor lifecycle actions are allowed; the asset-writing
actions `createScriptableObject` and `createPrefabVariant`, and `saveScene`,
are not, and an action the list does not name is denied. A denied intent gets a
receipt with `status: denied` and the reason in `message`, so a refusal is as
visible as an acceptance. `requestedBy` on the intent names the caller and is
echoed on the receipt; admission never reads it. The window's buttons write
intents like any other caller, so the operator and an agent pass the same gate.
## Unity Command Transport
Commands reach the editor through the project's directory store at
`<project>/.brokkr/unity-editor.ccmp` (`.records/` beside it). There is no
network listener: admission starts at filesystem permissions, and the editor sees
a written intent on its next pull, about once a second.
The editor executes intents only while the operator has enabled agent commands
for that project (window toggle "Agent Commands"). The flag is off by default
and installing the package never turns it on. The host snapshot carries it as
`agentCommandsEnabled`, so a caller can see why nothing executes.
A command id is single-use. Every writer mints a fresh `commandId` for each
act, and a receipt at `unity/receipts/{commandId}` answers exactly that id.
Once an id has a receipt, the editor never executes it again, whatever is
later written under it: reusing an answered id is a caller defect, and its
receipt is the answer that stands. A writer that repeats an intent mints a new
id for each repetition.
## Blender Command Actions
All Blender writes use `brokkr.blender.command_intent.v0` and receive
`brokkr.blender.command_receipt.v0`.
- `createObject`
- `deleteObject`
- `setObjectTransform`
- `setObjectVisibility`
- `setObjectParent`
- `setObjectCustomProperty`
- `selectObject`
- `assignMaterial`
- `importGltf`
- `setCamera`
- `setLight`
- `setWorld`
- `renderStill`
- `createNodeGroup`, `addNode`, `removeNode`, `linkSockets`, `unlinkSockets`, `addModifier`,
`removeModifier`, `setModifierInput` (Geometry Nodes graphs; fields in
`surfaces/blender/docs/node-actions.md`)
Blender owns the mutation. Brokkr advertises the command surface; Verse clients
write typed command intents; Blender executes recognized intents and publishes
receipts.
`setObjectParent` uses `targetObjectName` and optional `parentObjectName`; an
empty parent reparents the object to the Blender scene root.
## Operator Views
Eve is deprecated, and Thing 2 is its successor (`thing2:ruling:eve-dashboards-quarantined`, 2026-10-22). Brokkr advertises no operator-dashboard surface; it publishes typed CultMesh state meanwhile. The scene graph, component state, asset library, command affordances and command receipts are mirrored documents; a future Thing 2 view over them does not own editor truth.