select_set raises in Blender for an object with no base in the view layer, so a scene
with off-layer objects could fail the export or mask its result. A selection naming an
off-layer object is refused by name; no selection means the selection is never read or
written. Docs state the unchecked texture sidecars and the TOCTOU residue.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Mirrors renderStill path safety: no overwrite, no links, no escape. Advertises asset.export.gltf.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
cultmesh_py (CultLib GameCult/CultLib#33) refuses peer puts and deletes unless a
write authority grants them. The add-on's clients put command intents, and the
session token guard already authenticates them, so the target passes
CultMeshGrantAllWrites() when cultmesh_py has it. Older cultmesh_py already
accepts every write and is left alone.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- check_relative: spaces before the first dot are dropped (CON .txt), superscript COM/LPT digits are device names.
- gltf_references refuses a reference to a missing file; drops the broad ValueError catch and redundant relative_to so the specific refusal (a link) reaches the receipt.
- _render_still makes folders only after every Blender-side refusal (unknown engine used to leave them behind).
- Tests for each.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- resolve_inside maps OSError from resolving a component (WinError 448 on a mklink /J junction) to a link refusal.
- Direct check_relative test for '..' and NUL; OSError mapping test.
- headless_smoke.py makes real junctions on Windows (outside and inside the root) and an existing-output case.
- install.md documents the path rules and the residual TOCTOU.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
WIP for review fixes, tests pass (61); mutation confirmation and Starfire re-run pending.
- importGltf parses .gltf JSON and .glb's JSON chunk, checks every buffers/images uri.
- Names are relative plain paths: no drive, UNC, extended, ':' (ADS), '..', device names.
- No symlink/junction component under the root; output must not already exist.
- Path and file stamps re-checked right before use; residual TOCTOU documented in code.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found by running the actions on Blender 5.2.3 headless: the render engine enum is
dynamic, bl_rna lists only BLENDER_EEVEE, so CYCLES and WORKBENCH were refused.
The fake in the tests now behaves like the real enum. surfaces/blender/tests/live/
headless_smoke.py runs every scene action against real bpy.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Panel offers only the Copy Session Token button; the token is not drawn.
- Fake-bpy tests: allow_non_loopback carries an update callback that stops a running
non-loopback server; copy operator writes the clipboard; the panel never draws the token.
- Real-server test: a DELETE with shardId and a valid token leaves no token in the shard log.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- The guarded server removes sessionToken from put/delete messages before the store,
which copies envelope fields into the shard log it serves.
- Copy Session Token operator.
- Panel shows cultmesh_py's loaded __file__, not the preference path.
- Unticking Allow Non-Loopback (and any refused start) stops a running non-loopback server.
- stop_server revokes, purges and clears state even if the socket stop raises.
- Tests: shard-log token leak on the real server, node half of the start purge, purge
persisted across reopening the .ccmp, allow=True through start_server, finally-path.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Paths for importGltf and renderStill are confined to the Import Directory and
Render Output Directory preferences (empty disables the action; .. , absolute
paths and escaping symlinks are refused). Every field is validated before bpy is
touched. renderStill restores render settings afterwards. Tests use a fake bpy;
Blender was not run.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Optional per-session token (preference, default off): random secrets token at
Start Server, shown in the panel, required in the sessionToken envelope field on
every document put and delete, revoked on Stop, never persisted.
- Always on: queued command intents (node records and debug command JSONs) are
purged on server start and stop; the panel shows the bind address and the
cultlib_py_src path; a non-loopback Serve Host is refused unless the
Allow Non-Loopback Bind preference is ticked, with a panel warning while running.
- Tests without Blender, plus real cultmesh_py server tests when CULTLIB_PACKAGES is set.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Per thing2:ruling:eve-dashboards-quarantined. Removes eveSurfaces from the provider advertisement and its schema, the two surface URIs, the eve.gui/eve.tui publish capabilities, and Eve wording in docs.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Directory.Build.props names CultLibRoot (CULTLIB_ROOT, else the sibling checkout) and pins CultLibRevision to cultlib-unity-v2.0.0 (42dce361). Directory.Build.targets fails the build unless that checkout is at the pin and clean, as Aetheria's guard does. Project references use CultLibRoot.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Eyes pass 2026-10-10: tools, limits and security posture of the official
Blender Lab MCP, the community BlenderMCP and Blender Agent.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The editor service is the one command owner, the sink is off by default,
command ids are single-use, and stale intents expire on enable in one
conditional cache commit. Soul pass 4 verdict: merge. Recorded follow-up:
TryCommitEnableAsync does not retry when a concurrent PutAsync leaves
staged writes; unreachable on Unity's main thread and fails safe.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The enable commit is conditional (ExpectUnchanged) and lives in the new
Runtime BrokkrCommandStore, with the pull-recompute-retry loop in
BrokkrCommandDrain.EnableAsync, so the tests exercise the shipped code. An
intent landing between the pull and the commit is expired, not run; eight
refusals in a row fail the enable loudly with the sink off. The probe is now
a thin fault-injecting wrapper over the real store, not a copy of it.
- Every editor write to the command lane is one cache commit; durable shard
logs are off (no server, no reader).
- No receipt is pruned before ten minutes, whatever the count, and the CLI
refuses a wait over five minutes. Fixed the comments that claimed otherwise.
- Pending and Prunable count a document only under the key its own id names.
- README and snapshot comment: the token stops stale EditorPrefs, it does not
authenticate writers; a restored or sync-conflict copy of the editor's own
store can rerun answered intents.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Database.PutAsync appends a mutation-log entry per record, so expiring 2000
intents took 17 s even with one flush. The drain's two bulk writes are now
atomic CultCache commits, so the enable cannot half-apply and the cost is per
commit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Soul pass 2 findings S1, S2, S3, S4.
- Enabling the sink is the drain's EnableAsync: every intent already in the
store gets an "expired" receipt and the enable marker is written, all in one
flush. No decision compares StoredAt with a clock any more, so a fast
writer clock and the Rust daemon's secs.nanosZ stamps stop mattering.
- The operator's EditorPrefs hold a token, and the sink runs only while the
store carries the marker with that token. A re-clone or a copied .brokkr has
no such marker, so a stale "enabled" authorizes nothing and the editor drops
it. The epoch and agentCommandsEnabledAt (host snapshot key 19) are gone.
- Selection is one pass over the store per tick (Pending), not a copy and sort
per intent.
- Receipts are capped at the newest 1000, pruned 128 pairs per tick with the
intent deleted before its receipt.
- A marker that could not be written is withdrawn, so the intent reads as
never started and is retried, not "interrupted".
- The drain writes through IBrokkrCommandStore; puts stage and one flush makes
them durable.
- The CLI timeout text no longer says the intent "stays written" without the
expiry caveat; the check-then-write window on --command-id is documented.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A store that fails to open is not always unreadable (a held lock does it too),
so the message says what happened. The catch-all's test now corrupts the store
after the CLI opened it, which the open's own handling cannot see.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The sink-off gate lives in the Unity service, not the drain, so the harness test
proved nothing about the machine. The new test reaches the CLI's last-resort
exit code with a failure that happens after the store opened.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Any exception now exits 2 with a message. An empty or legacy store fails with
a message that names the path instead of aborting with 134.
- A --command-id that already has a receipt is refused with exit 2, so a stale
receipt is never reported as the answer to a new intent.
- The wait treats the editor's "attempted" marker as not yet a verdict.
- The action theory passes --output so captureEditorView clears admission now
that the harness runs the real policy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The window republished a snapshot it had captured earlier, which could carry a
stale agentCommandsEnabled. Publish Mirror and Auto Publish now ask the service
to capture and publish a fresh one; the window keeps the returned snapshot for
display only.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
BrokkrCommandDrain owns every rule between a pending intent and its receipt.
The service and the scenario harness now run the same drain instead of two
copies of the loop.
- An "attempted" receipt is written and flushed before Execute. A crash or a
failing final receipt write can no longer re-run an intent; the next drain
rewrites a leftover marker as "interrupted" and the operator decides.
- The host snapshot carries the sink's enable epoch. Intents stored before the
latest enable get an "expired" receipt and never run (operator ruling
2026-09-30).
- One drain handles at most 16 intents, expired and denied ones included.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
editor.assets.refresh, editor.lifecycle.write and editor.view.capture join
the Unity surface, in the provider contract and the advertisement test. The
donor's prefab mirror rows are not taken; that pipeline is parked.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
BrokkrCommandPolicy (Runtime, pure, tested outside Unity) is consulted by
BrokkrEditorService before every BrokkrUnityCommandExecutor.Execute; it is the
only call path into the executor. The per-project allowed-action list defaults
to scene mutation plus lifecycle; createScriptableObject, createPrefabVariant
and saveScene are shipped denied (parked Q4 = A), and an action the list does
not name is denied. A denial gets a receipt with status denied and a reason.
The window's buttons write intents like any other caller, so operator and
agent share the gate.
Re-landed from the donor as fresh code: refreshAssets, setEditorPlayState,
setEditorPaused, captureEditorView, and the play-state fields on the host
snapshot (keys 14-17). The donor's captureEditorView wrote to any rooted
outputPath; it now resolves the path through one function that accepts only a
plain relative .png path under <project>/.brokkr/captures and refuses drive
letters, rooted or UNC paths, stream suffixes and any .. segment on every host
OS, and dimensions are bounded to 8192. requestedBy joins the intent (key 17)
and is echoed on the receipt (key 6). saveScene is new.
The window gains an Allowed Actions field bound to a per-project preference.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Re-landed from the donor as a fresh file (read with git show, not merged).
Changes from the donor: the CultLibRoot default is ..\..\CultLib instead of a
directory that does not exist; every field of brokkr.unity.command_intent.v0
has a --kebab option (the donor mapped six of seventeen), and unknown options
are refused before anything is written; keys come from BrokkrCommandLedger;
exit is 0 accepted, 1 other receipt, 2 usage error or timeout; readHost opens
the store read-only and prints the agent-commands flag; a timeout says when
the editor reports the sink off. The intent gains the capture fields at keys
13-16, which the CLI needs and A4's executor will read.
Tests run the built CLI as a process against the editor probe: field mapping,
fresh id per invocation, exit code from the receipt, timeout leaves only the
intent, unknown option writes nothing, and every action name reaches the
editor once with one receipt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>