Triumvirate fixes#25
Merged
Merged
Conversation
- MountWatcher carries op (created/modified/deleted) per path; collapses
multi-event windows sensibly (delete wins; create+modify → created)
- WorkspaceModule emits workspace:created / workspace:modified /
workspace:deleted; workspace:changed is replaced (breaking)
- SyncResult.synced is now Array<{path, op}>; syncFromFs detects
create-vs-modify via tree state
- MountConfig.wakeOnChange (boolean | op[]): requests inference for
matching ops on that mount; watcher self-suppression keeps agents
from waking on their own writes
- GatePolicyMatch gains mount (exact/glob) and pathGlob (any-path
match); framework feeds mount+paths into GateEventInfo for
workspace events
- event-gate tests: +2 cases for mount and pathGlob matching (29 → 31)
Used by connectome-host's knowledge pipeline to wire clerk ↔ miner ↔
reviewer via shared filesystem directories (tickets/, reports/).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…re() order WorkspaceModule.start() iterated this.mounts to create MountWatchers, but this.mounts is populated by initStore() which hosts call *after* AgentFramework.create() — meaning after start() has already run. Result: when wakeOnChange was wired up in 02a4f13, no chokidar watchers ever started, so no workspace:created/modified/deleted events ever fired, so no agent woke on file drops. Extract watcher setup into ensureRunning() and call it from both start() and initStore() — whichever runs second actually boots the watchers. Idempotent guard (watchers.has(name)) keeps workspace:mounted events from firing twice. Verified with a minimal repro: file drop → workspace:created → gate:decision → inference triggered. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds McplServerConfig.channelSubscription: - 'auto' (default) — every registered channel is opened (prior behavior) - 'manual' — nothing auto-opens; agent must call channel_open - string[] — allow-list of channel ids Framework calls ChannelRegistry.setSubscriptionPolicy(serverId, policy) before addServer() so the policy is in place when channels/register fires during the handshake. Addresses a real production blast: a focused miner agent connected to Zulip was silently absorbing chatter from 119 public streams because the Zulip MCPL server registers every visible stream and the framework auto-opened all of them. Default stays 'auto' so existing recipes (clerk, frontdesk) keep listening passively; recipes that shouldn't (knowledge-miner, zulip-miner) opt into 'manual'. Tests: 3 cases covering auto / manual / allow-list behaviors. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
workspace--write/edit/delete previously only touched Chronicle's tree state; files never landed on disk until an explicit materialize call. That was fine for single-agent sessions but breaks any cross-agent pipeline where another agent's chokidar watcher needs to see the write — e.g. miner producing reports into ./output that the reviewer wakes on. New MountConfig.autoMaterialize: when true, each write/edit/delete materializes that single file immediately. The local watcher's suppress() absorbs the echo so the agent doesn't self-wake; external watchers (same directory, different MountWatcher instance) fire normally. Delete calls unlink() directly — materializeToFs intentionally doesn't propagate tree-removals, so autoMaterialize handles delete separately. Verified with a two-process repro (miner writes → reviewer's watcher fires → gate policy matches). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… counters Three related fixes exposed by a reviewer→clerk wake failure: the clerk never woke on reviewed artifacts even though the recipe had a reviewed-responses gate policy wired. 1. EventGate now reconciles recipe-declared policies into an existing on-disk gate.json at startup. Previously gate.json was seeded only when the file didn't exist, so any recipe policy added between session starts was ignored. Reconciliation is by policy name and additive — existing policies (including user edits via workspace--edit _config/gate.json) are left alone; new ones are appended. Emits gate:config-reconciled trace when changes apply. 2. WorkspaceModule now does a one-shot syncFromFs scan when starting a watch:'always' mount. Chokidar runs with ignoreInitial:true so files already on disk at session start were invisible — agents had to be manually prompted to call workspace--sync before seeing what a producer wrote while they were down. Initial scan uses syncFromFs (which diffs disk vs. tree state), so only files new-to-this- session's-tree fire workspace:created events. 3. EventGate.getStatus() exposes totalEvaluations + defaultDecisions counters (triggered/skipped, overall and per eventType). Fixes the matchCount-0-is-ambiguous observability gap: previously "policy never fires" looked identical to "event never arrived", so diagnosing silent drops required traces or speculation. Tests: 3 new cases (reconcile-add, reconcile-preserve-user-edits, default-counts). 69 pass total. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2 tasks
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.
Workspace + Gate: wire up cross-agent file-event pipelines
Five commits that together make filesystem-event-driven multi-agent
pipelines actually work end-to-end (motivated by connectome-host's clerk ↔
miner ↔ reviewer pipeline over shared tickets/ and reports/ directories).
Commits
02a4f13 feat(workspace): split fs events by op + wakeOnChange mount flag
(breaking)
MountWatcher tracks op per path and sensibly collapses multi-event windows
(delete wins; create+modify → created).
create vs. modify via tree state.
ops; watcher self-suppression keeps agents from waking on their own writes.
feeds mount+paths into GateEventInfo.
c293590 fix(workspace): start chokidar watchers regardless of
start()/initStore() order
which hosts call after AgentFramework.create() — so no watchers ever booted
after the wakeOnChange commit. Extract setup into idempotent ensureRunning()
called from both entry points.
c0042b6 feat(mcpl): per-server channelSubscription policy
chatter from 119 auto-opened public streams. Default stays 'auto'; noisy
recipes opt into 'manual'.
c2d5e84 feat(workspace): autoMaterialize mount flag
files never hit disk without an explicit materialize call — fine for single
agents, broken for cross-agent pipelines where another agent's chokidar
watcher needs to see the write.
immediately; local watcher's suppress() absorbs the echo, external watchers
fire normally. Delete handled separately (unlink) because materializeToFs
doesn't propagate tree-removals.
d5691dd fix(gate,workspace): reconcile policies + initial scan +
default-drop counters
Three fixes exposed by a reviewer→clerk wake failure:
gate.json at startup (previously seed-only). Additive by policy name — user
edits preserved. Emits gate:config-reconciled trace.
watch:'always' mount (chokidar's ignoreInitial:true left pre-existing files
invisible).
counters — "policy never fires" used to look identical to "event never
arrived."