How Ephemeral Workers Are Spawned from Slack and Webhooks in Munder-Difflin: Gated Worktree Teardown Safety

Munder-Difflin spawns isolated ephemeral workers by converting verified Slack webhook payloads into JSON spawn requests that create temporary Git worktrees, then enforces a gated teardown protocol requiring explicit "done" signals and clean Git status checks before safely removing or preserving the worktree.

Munder-Difflin implements a "god-triggered" ephemeral worker architecture where incoming Slack messages serve as the entry point for short-lived compute tasks. The system creates isolated Git worktrees for each worker and maintains strict safety invariants during teardown to prevent accidental loss of unintegrated changes.

Spawning Ephemeral Workers from Slack Webhooks

The spawning process begins with webhook verification and ends with an isolated worker process operating in its own Git worktree.

Slack Events API Handling

Incoming Slack interactions are handled by the SlackWebhookServer class defined in src/main/slack.ts. The server binds to a local HTTP port and verifies each request using the Slack signing secret through the SlackWebhookServer.verify() method located at line 86. Once verified, the server invokes the onMessage callback registered by the main process, forwarding the message text and metadata for worker creation.

Spawn Request Manifest Creation

The onMessage callback generates a spawn-request JSON file in the <HIVE_ROOT>/spawn-requests/ directory. The manifest specification, defined in src/main/hive.ts around line 1412, requires at minimum an objective field describing the work and a cwd field specifying the repository path. Optional fields include name, command, provider, tokenCap, and a slack object containing channel and thread_ts properties that route error messages back to the originating thread.

The main process in src/main/index.ts polls the spawn-requests directory and creates the worker environment under <HIVE_ROOT>/workers/worker-<id>. Each worker receives an ephemeral client secret minted by the Realtime-Michael endpoint and operates in an isolated worktree with isolate: true enforced by default.

Worker Tracking and Metadata

Spawned workers are tracked in the liveEphemeralWorkers map, which stores metadata including the Slack thread identifier and worktree path. According to src/renderer/src/store/store.ts, the system persists the channel and thread_ts values on messages enqueued for the worker, ensuring failures can be reported to the correct Slack thread.

Gated Worktree Teardown Safety Logic

When a worker finishes, the system invokes a multi-layered safety protocol before removing the worktree. This gated teardown ensures that work is either integrated by the human operator or deliberately preserved before deletion.

Initiating Stop Requests

The teardown process begins through manualStopEphemeralWorker(id), exposed in src/main/index.ts and accessible via the slack:stop IPC channel from the renderer process. The handler first validates that the worker's scratchDir still exists and that the worker is marked as live in the liveEphemeralWorkers map.

Verifying the "Done" State

Before any destructive action, the system verifies that the worker has explicitly signaled completion. The worker must send an outbox message with act: "done" handled by the informGod function in src/main/index.ts. This message sets worker.done = true. If this flag is not set, the stop request is rejected and the worktree is preserved, preventing premature deletion of active work.

Git Diff Checks and Failure Preservation

When the done state is confirmed, the system executes git diff --quiet to check for uncommitted changes. If pending modifications exist that have not been merged by the human operator, the worktree is moved to the preservation area at $HIVE_ROOT/preserved/ rather than deleted. Only worktrees with clean Git status proceed to deletion via fs.rmSync(..., { recursive: true }), wrapped in try-catch blocks to prevent main process crashes.

Background Garbage Collection

A periodic ephemeral-worker GC sweep runs in src/main/git.ts to clean up preserved worktrees. This sweep only purges directories that both lack integration markers and are not flagged with .done without a corresponding Git commit. The sweep never touches worktrees that retain active worker metadata or unintegrated changes.

Implementation Examples

Triggering a Worker from Slack

// src/main/slack.ts - Slack webhook registration
const slackServer = new SlackWebhookServer({
  port: config.slackPort,
  signingSecret: config.slackSigningSecret,
  channelId: config.slackChannelId,
  onMessage: async (msg) => {
    const request = {
      objective: msg.text,
      cwd: '/path/to/repo',
      slack: { channel: msg.channel, thread_ts: msg.thread_ts },
    };
    await writeFile(
      join(HIVE_ROOT, 'spawn-requests', `${uuidv4()}.json`),
      JSON.stringify(request, null, 2)
    );
  },
});
await slackServer.start();

Signaling Completion from the Worker

// Worker agent code
await hive.send({ 
  to: 'god', 
  act: 'inform', 
  subject: 'Job complete', 
  body: 'Result …' 
}, 'ephemeral-worker');

await hive.send({ 
  to: 'god', 
  act: 'done' 
}, 'ephemeral-worker'); // Required for teardown approval

Manual Stop via IPC

// Renderer process invoking gated teardown
await window.cth.manualStopEphemeralWorker('worker-abc123');

The IPC handler in src/preload/index.ts routes this call to the main process, which executes the gated teardown logic described above.

Summary

  • Slack Integration: src/main/slack.ts verifies webhook signatures and converts messages to JSON spawn requests in <HIVE_ROOT>/spawn-requests/.
  • Worker Isolation: Each worker receives an isolated Git worktree under <HIVE_ROOT>/workers/worker-<id> and an ephemeral client secret.
  • Gated Teardown: The manualStopEphemeralWorker function in src/main/index.ts enforces a strict protocol requiring the act: "done" signal and clean Git status.
  • Data Preservation: Worktrees with uncommitted changes are relocated to $HIVE_ROOT/preserved/ rather than deleted, with final cleanup handled by the GC sweep in src/main/git.ts.

Frequently Asked Questions

What happens if a worker crashes before signaling "done"?

If a worker process terminates without sending the act: "done" message, the worker.done flag remains false. The gated teardown logic in src/main/index.ts will reject any stop requests for this worker, preserving the worktree in its current state to prevent loss of partial progress.

How does the system handle uncommitted changes during teardown?

Before deletion, the system runs git diff --quiet to detect pending changes. If the repository is dirty, the worktree is moved to $HIVE_ROOT/preserved/ instead of being deleted. The background GC sweep in src/main/git.ts will only remove these preserved directories after confirming they have been properly integrated or manually reviewed.

Can spawn requests originate from webhooks other than Slack?

Yes. While src/main/slack.ts provides the Slack Events API integration, any process can create a valid spawn request by writing a JSON file to <HIVE_ROOT>/spawn-requests/ containing the required objective and cwd fields. The main process polls this directory agnostically, enabling webhook endpoints from GitHub, GitLab, or custom services to trigger ephemeral workers.

Where is the ephemeral worker's Slack thread metadata stored?

The Slack channel and thread_ts values are stored in the slack object within the spawn request JSON. Once the worker is spawned, this metadata is maintained in the liveEphemeralWorkers map alongside the worktree path, as referenced in src/renderer/src/store/store.ts, ensuring error messages route back to the correct thread.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →