Close executor write-gate gaps and fix activity attribution - #576
Merged
Conversation
Reject execution_mode values outside {readonly, readwrite} at the tool
boundary: an unrecognized spelling satisfied neither write gate and ran
write-pattern code even with control_system.writes_enabled=false. Apply
the deployment-level kill switch in execute_file, which had no such
check, and share both gates between the two executor tools.
The activity listener emitted an agent-attributed frame for every store delete, so a person deleting from the gallery UI and the dispatch worker's retention sweep both appeared as agent actions in the web terminal. Store mutations now carry an actor tag (default "agent"); the gallery route tags its deletes "human", the retention sweep tags its deletes "system", and the activity listener drops non-agent events.
Every async emit site hand-rolled anyio.to_thread.run_sync(functools.partial(notify_agent_activity, ...)). Add notify_agent_activity_async as the single thread-hop helper and call it from all 16 sites. The off-the-event-loop property is pinned once at the helper's own test instead of per tool; the authoring/draft emits keep their sync wrappers because they bundle a config read on the worker thread.
panel-manager.js had grown past the interface-JS max-lines cap and ran under a file-specific eslint override. Move the rail's agent-attention surface (transient glow, persistent badges, the localStorage ack contract) to panel-agent-attention.js and the status-bar health readout to panel-status-bar.js, and drop the override — the module is back under the shared cap.
|
|
||
| register_artifact_delete_listener(record_actor) | ||
| try: | ||
| assert client.delete(f"/api/artifacts/{entry.id}").status_code == 200 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two safety fixes and two cleanups around the agent-activity surfaces.
Fixes
fix(executor)):execution_modewas an unvalidated free string. A value outsidereadonly/readwrite(e.g."ReadWrite") satisfied neither write gate and ran write-pattern code even withcontrol_system.writes_enabled=false;execute_fileadditionally had no deployment-level kill switch at all. Unknown modes are now rejected at the tool boundary and both tools share one deployment writes gate (_execution_gates.py).fix(artifacts)): the store-level activity listener reported every artifact delete as an agent action — including a person deleting from the gallery UI and the dispatch worker's retention sweep. Store mutations now carry a contextvar actor tag (defaultagent); the gallery route tagshuman, the retention sweep tagssystem, and the listener drops non-agent events.Cleanups
refactor(mcp)): 16 emit sites hand-rolledanyio.to_thread.run_sync(functools.partial(notify_agent_activity, ...)); they now await a singlenotify_agent_activity_asynchelper. The off-the-event-loop property is pinned once at the helper's test. The authoring/draft emits keep their sync wrappers because they bundle a config read on the worker thread.refactor(web)): the rail's agent-attention surface (glow, badges, localStorage ack contract) moved topanel-agent-attention.js, the status-bar health readout topanel-status-bar.js, and the file-specific eslint max-lines override is gone — the module is back under the shared 450 cap.Testing
execute_filekill switch, and actor-tagged deletes at the listener, gallery-route, and retention-sweep seams; the emit-site suite's uncanonical-mode test now asserts rejection instead of pass-through.tests/mcp_server+tests/agent_runner+tests/hooks: 2251 passed locally; interfaces/dispatch suites green.