Skip to content

Triumvirate fixes#25

Merged
Tengro merged 5 commits into
anima-research:mainfrom
Anarchid:triumvirate-fixes
Apr 21, 2026
Merged

Triumvirate fixes#25
Tengro merged 5 commits into
anima-research:mainfrom
Anarchid:triumvirate-fixes

Conversation

@Anarchid
Copy link
Copy Markdown
Collaborator

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)

  • Replace workspace:changed with workspace:created / :modified / :deleted;
    MountWatcher tracks op per path and sensibly collapses multi-event windows
    (delete wins; create+modify → created).
  • SyncResult.synced becomes Array<{path, op}>; syncFromFs distinguishes
    create vs. modify via tree state.
  • New MountConfig.wakeOnChange (bool | op[]) triggers inference for matching
    ops; watcher self-suppression keeps agents from waking on their own writes.
  • GatePolicyMatch gains mount (exact/glob) and pathGlob matchers; framework
    feeds mount+paths into GateEventInfo.

c293590 fix(workspace): start chokidar watchers regardless of
start()/initStore() order

  • start() iterated this.mounts, but mounts are populated by initStore()
    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

  • 'auto' (default, prior behavior) / 'manual' / string[] allow-list.
  • Motivated by a real blast: a focused Zulip miner was silently absorbing
    chatter from 119 auto-opened public streams. Default stays 'auto'; noisy
    recipes opt into 'manual'.
  • 3 new tests.

c2d5e84 feat(workspace): autoMaterialize mount flag

  • workspace--write/edit/delete previously only updated Chronicle tree state;
    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.
  • When autoMaterialize: true, each op materializes that single file
    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:

  1. EventGate now reconciles recipe-declared policies into existing on-disk
    gate.json at startup (previously seed-only). Additive by policy name — user
    edits preserved. Emits gate:config-reconciled trace.
  2. WorkspaceModule does a one-shot syncFromFs scan when starting a
    watch:'always' mount (chokidar's ignoreInitial:true left pre-existing files
    invisible).
  3. EventGate.getStatus() exposes totalEvaluations + defaultDecisions
    counters — "policy never fires" used to look identical to "event never
    arrived."
  • 3 new tests; 69 pass total.

Anarchid and others added 5 commits April 20, 2026 16:22
- 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>
@Tengro Tengro merged commit a538b6e into anima-research:main Apr 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants