Agent Orchestration Home
Slop ยท Runtime Layer

Agent Orchestration

Every LLM session in Slop - whether dispatched by the daemon for an issue worker or triggered on-demand from the console - runs through one shared engine: src/server/runs/driver.ts (runSkillSession). The daemon and UI share a single abstraction; run kind and trigger context differ.

๐Ÿค– Shared driver 3 harnesses โ™ป๏ธ Resume on restart 6 worker phases SSE live events

The Two Dispatch Paths

Two paths converge on the same driver. The autonomous path is daemon-driven and multi-phase; the on-demand path is a single skill invocation from the UI.

1
Autonomous Worker Path
daemon-driven ยท multi-phase ยท per-issue worktree
Daemon tick
  โ””โ”€ claimWorker() โ†’ Worker row (status: claimed)
       โ””โ”€ spawnRunner(workerId) โ†’ runWorker()
            โ”œโ”€ Phase 1: implement    โ†’ runImplementSession()
            โ”œโ”€ Phase 2: verify       โ†’ runVerifySession()
            โ”œโ”€ Phase 3: conflict?    โ†’ runConflictResolution()
            โ”œโ”€ Phase 4: CI fail?     โ†’ runCiFix()
            โ”œโ”€ Phase 5: PR?          โ†’ runPrReview() / runAddressReview()
            โ””โ”€ Phase 6: ship         โ†’ ShippingStrategy.finish()
2
On-Demand Skill Path
user-triggered ยท single skill ยท repo checkout
User clicks "Run skill" in console
  โ””โ”€ Server Action: runSkillRun(skillName, args, repoId?)
       โ””โ”€ SkillRunRegistry.start()
            โ””โ”€ runSkillSession({
                 cwd: repo checkout,
                 prompt: "/skillName args"
               })

The Driver - runSkillSession

Single entry point for every agent session in the system. Lives at src/server/runs/driver.ts.

ParameterTypeDescription
cwdstringWorking directory - worktree path for workers, repo checkout for on-demand
promptstringSkill command + arguments, e.g. "/implement-issue reuse-worktree"
onEvent(DriverEvent) => voidStream of driver events (started, message, completed, failed, timeout)
configRepoConfigRepoLike?Reads global config: model, effort, timeout
abortSignalAbortSignal?Cancellation hook
timeoutMsnumber?Hard kill timeout. Default: 1h for implement, 20m for verify
onAgentSpawned(pid) => void?Reports OS PID so daemon can reap orphans after restart
resumeSessionIdstring?Resume a prior session by ID (Claude and Copilot harnesses only)
overrides{ model?, effort? }?Per-issue model/effort override - takes precedence over global config
reapTreefn?Custom process reaper (injected in tests)
copilotClientFactoryfn?Copilot SDK seam (injected in tests)
checkAvailabilityfn?Harness availability check seam

Model โ†’ Harness Resolution

At every dispatch, the driver resolves the model in priority order: overrides.model โ†’ global config โ†’ default "opus". The model string then maps to a harness from the static catalog in src/lib/agent/agent-model.ts.

claude
@anthropic-ai/claude-agent-sdk
opus sonnet haiku fable + any unrecognized value
codex
child_process.spawn("codex")
gpt-5.4 gpt-5.5
copilot
@github/copilot-sdk
copilot-claude-sonnet-4.6 copilot-claude-sonnet-4.5 copilot-claude-haiku-4.5 copilot-gpt-5.4 copilot-gpt-5.3-codex copilot-gpt-5-mini
Harness unavailable = immediate failure. If the resolved harness is not installed or not authenticated, the driver fails immediately with a descriptive error. There is no silent fallback to another harness.

Environment Filtering

The agent subprocess receives only an explicit allowlist. Everything else (DB URLs, SLOP_SECRET, other secrets) is blocked by default.

System

PATHHOMEUSER LOGNAMESHELLTMPDIR TEMPTMP

Locale / Terminal

LANGLC_ALLLC_CTYPE LC_MESSAGESTERMCOLORTERM

Auth

ANTHROPIC_API_KEYANTHROPIC_BASE_URL OPENAI_API_KEYOPENAI_BASE_URL GITHUB_TOKENGH_TOKEN

SSH / Git

SSH_AUTH_SOCKSSH_AGENT_PID GIT_SSH_COMMANDGIT_SSH

Always Injected

SLOP_URL NODE_ENV
Copilot exception: GITHUB_TOKEN and GH_TOKEN are stripped from the Copilot harness env (buildCopilotAgentEnv). A non-entitled GITHUB_TOKEN would shadow the stored OAuth login and block subscription auth. Stripping them lets gh auth cover model authentication.

Security Hooks - PreToolUse

A PreToolUse Bash hook is registered for every driver session. Two rules are enforced:

Blocks git stash
The git stash stack is repo-global across all worktrees. A stash/pop in one worktree can corrupt unrelated worktrees. The hook rejects with a detailed error pointing the agent to revert in-place instead.
Blocks AskUserQuestion
Unattended worker runs inject UNATTENDED_SYSTEM_PROMPT and treat interactive questions as errors - the agent must operate without human input.

Worker Phases

Each phase creates a Run row and calls the driver. Phases run sequentially. Retries between phases use the normal daemon tick interval (default 30s) - no exponential backoff.

1

Implement

kind: implement status: implementing

Reads the issue, implements the change, runs tests, commits, optionally opens a PR. The agent runs in an isolated git worktree.

Skill
/implement-issue
Modifiers
reuse-worktree ยท ultracode ยท internal
Captures
sessionId ยท agentPid ยท summary ยท filesChanged ยท cost/tokens
2

Verify

kind: verify status: verifying

Runs /verify-gate (which runs /cover + /review internally). Writes a context file to the worktree and parses a verdict from the summary.

Skill
/verify-gate
Verdict: pass
Proceed to shipping
Verdict: findings
Increment attempt budget, re-dispatch with findings in context
Context file
.slop-verify-context.json - issue number, docsOnly flag, prior findings, HEAD SHA
Fail-safe: Missing or unparseable verdict is treated as findings, not pass - verification fails open-safe.
3

Conflict Resolution (conditional)

kind: conflict status: resolving_conflict

Triggered when the PR or local branch has conflicts with base after rebase. Budget: maxConflictAttempts (default 5). Exhausted โ†’ status: failed.

Skill
/resolve-conflict
Budget
5 attempts
4

CI Fix (conditional)

kind: ci_fix status: fixing_ci

Triggered when PR CI checks are failing after push. Budget: maxCiAttempts (default 5). Exhausted โ†’ status: failed.

Skill
/fix-ci
Budget
5 attempts
5

PR Review + Address Comments (conditional)

kind: pr_review / pr_address

5a Review: Runs /pr-review. Triggered auto when autoReviewMode is on and CI is green, or manually via the Review button. Parses a verdict anchored to the current head SHA - stale verdicts from earlier commits are ignored. Budget: 3 attempts.

5b Address: Runs /address-comments when verdict is request_changes. After addressing, worker returns to waiting_ci for CI to re-run - the loop continues.

Verdicts
approve ยท approve_with_comments ยท request_changes
Review budget
3 attempts
6

Shipping

Not a Run - git/GitHub direct

Not a Run row - the shipping strategy handles git/GitHub operations directly.

ModeStrategyWhat happens
localLocalShippingStrategyFast-forward merge into base (rebasing if needed), close issue
remoteRemoteShippingStrategyOpen PR, arm auto-merge, park in waiting_ci; lifecycle poll drives CI โ†’ merge โ†’ close

Run Registry - On-Demand Skills

SkillRunRegistry (src/server/runs/skill-run.ts) tracks all user-initiated runs (non-worker) in memory. Returns a runId immediately and runs async.

MethodDescription
start(skillName, args, overrides?, repoId?)Starts a skill run, returns runId immediately
abort(runId)Aborts a running skill
awaitDone(runId)Returns a Promise<void> that resolves when the run finishes
Audits
  • slop
  • architecture
  • security-scan
Targeted
  • perf
  • race-hunt
  • gotcha-hunt
Planning / Quality
  • suggest-points
  • root-cause
  • cover
  • review
Research
  • spec
  • research
  • issue
User Skills
  • ~/.claude/skills/*
Skill discovery via discoverSkills() always scans ~/.claude/skills/ regardless of the configured harness - skills wired for Claude are available even in Codex or Copilot sessions.

Context Passed Into Agent Sessions

๐Ÿ“

Via Prompt String

Skill command + modifier keywords (reuse-worktree, ultracode, internal) + issue number + any user-provided args. Composed at dispatch time.

๐Ÿ“

Via Working Directory

Worker: ~/.slop/worktrees/<owner@name>/<issueNumber>/ - isolated git worktree on a per-issue branch.

On-demand: the watched repo's local checkout (no worktree).

๐Ÿ—‚๏ธ

Via Context File

.slop-verify-context.json written to the worktree root before the verify phase. Contains issue number, docsOnly flag, prior findings, and the HEAD SHA from the end of implementation.

๐ŸŒ

Via SLOP_URL

Always injected. Skills that need to read internal issues call GET $SLOP_URL/api/internal-issues?repo=<slug>.

๐Ÿ”•

Via UNATTENDED_SYSTEM_PROMPT

Appended to every worker session. Forbids AskUserQuestion, skips interactive interview phases, instructs terse output, overrides any harness instruction that says to ask questions.

Context Handed Off Out of Agent Sessions

sessionId - Resume on Restart

Stored on the Worker row. On daemon restart, the driver calls resumeSessionId on the same agent session instead of re-implementing. Claude and Copilot only - Codex never stores a session ID and always restarts fresh from the surviving worktree.

agentPid - Orphan Reaping

Set the moment the agent subprocess spawns via onAgentSpawned. On daemon restart, any worker whose agentPid is still live is reaped before resuming - no zombie agents accumulate.

WorkerReport - Run Statistics

Captured live as the agent streams. Includes: summary, filesChanged, durationMs, costUsd, inputTokens, outputTokens, cacheReadTokens, numTurns, implementGateSha.

Run.report - Skill Output Snapshot

On-demand runs capture the first 2000 chars of the agent's final output as Run.report. Visible in the Agents tab detail page. The live SSE stream is not capped - full output available while active.

Codex + resume edge case: If resumeSessionId is set but the resolved harness is Codex (e.g. model changed between restart and resume), the driver throws immediately - the run fails and the worker must be manually restarted.

Status Vocabulary

Worker Statuses

Terminal statuses: merged ยท failed ยท cancelled. Display overrides: issueState === "closed" โ†’ show issue_closed; prState === "merged" โ†’ show merged regardless of internal status.

StatusPhaseDescription
claimedPre-startIssue picked up; worktree not yet running
implementingImpl/implement-issue agent is running
verifyingVerify/verify-gate agent is running
waiting_ciShippingPR open, awaiting CI checks
waiting_reviewPR reviewCI green, waiting for review dispatch
in_reviewPR review/pr-review agent is running
waiting_addressPR addressRequest-changes verdict, waiting for address dispatch
in_addressPR address/address-comments agent is running
resolving_conflictConflict loop/resolve-conflict agent is running
fixing_ciCI loop/fix-ci agent is running
waiting_mergeMerge gateAuto-merge off; waiting for operator Merge
mergingMergeMerge in progress
reportingPost-mergeRun-report filing is in progress
mergedTerminal โœ“PR merged, issue closed
failedTerminal โœ—Gave up; failed label added to issue
cancelledTerminal โœ—Stopped by operator or out-of-band close
pausedSuspendedIn-flight work intact; operator paused

Run Kinds

implement

Worker implement phase

verify

Worker verify gate

conflict

Conflict resolution re-dispatch

ci_fix

CI-failure fix re-dispatch

base_ci_fix

Base-branch CI fix - standalone worker (no issue link) triggered by operator when base CI is red

pr_review

PR review (managed or unmanaged)

pr_address

Address PR comments

suggest_points

LLM story-point scoring

run_report

Performance report synthesis

doc_save

Branch + commit + PR for doc edit

skill

Generic on-demand skill invocation

slop / arch / security

Named repo-wide audits

Event Flow - Bus โ†’ SSE โ†’ UI

Events flow from the driver through an in-memory bus, get persisted to SQLite, then fan out to the browser via Server-Sent Events. Non-worker/run events (e.g. repo.updated) are broadcast but never persisted.

Driver emits DriverEvent โ””โ”€ Worker/RunRegistry onEvent handler โ”œโ”€ createEventSummarizer() โ†’ SummarizedLine { message, level } | null โ”‚ Rules: assistant text โ†’ keep โ”‚ tool_result/Read/Grep โ†’ drop (noise) โ”‚ Bash output โ†’ scan for test tallies โ”‚ result โ†’ keep โ”œโ”€ appendEvent({ workerId | runId, type, message, level }) โ†’ DB write โ””โ”€ eventBus.publish({ type: "worker.event" | "run.event", ... }) โ†’ in-memory โ””โ”€ SSE handler subscriber โ”œโ”€ Filter by relevant entity (workerId or runId) โ”œโ”€ JSON-encode event โ””โ”€ Write `data: ...\n\n` โ†’ Response stream โ†’ browser โ””โ”€ React component updates via useSWR / useWorkerEvents / useAgentEvents
Persistence rule: Any event carrying a workerId field is written to the Event table under that worker. Any event carrying a runId is written under that run. Events with neither (e.g. repo.updated) are fan-out only - never persisted.

Memory Monitoring

The daemon's WorkerResourcePoll samples RSS memory each cycle for all live workers.

1

Sample Process Tree

sampleProcessTree(agentPid) - walks /proc or ps to sum RSS of the agent + all its child processes.

2

Breach Detection

First breach of memoryThresholdBytes latches Worker.memoryBreachedAt and emits one breach event. Subsequent cycles don't re-emit.

3

Optional Stop

If stopOnBreach config is set, the worker is stopped on breach.

4

Diagnostic Issue

Files an LLM-synthesized diagnostic issue in the watched repo (runs/worker-report-issue.ts) containing: peak RSS, breach timeline, and per-process breakdown.

Skill Bootstrap - harness/bootstrap.sh

Run once to wire skills into all three harness homes. Discovers all skills by SKILL.md presence and symlinks each by its directory name. Also backs up conflicting non-symlink targets, prunes stale links, links global config files, and wires Codex/Copilot config.

# For each skill directory containing SKILL.md: ln -sfn "$skilldir" "$HOME/.claude/skills/$name" # Claude ln -sfn "$skilldir" "$HOME/.codex/skills/$name" # Codex ln -sfn "$skilldir" "$HOME/.agents/skills/$name" # Copilot

Skill Categories in harness/skills/

core/
  • implement-issue
  • verify-gate
  • fix-ci
  • resolve-conflict
  • merge
  • issue
quality/
  • cover
  • review
  • perf
  • security-scan
  • race-hunt
  • gotcha-hunt
  • slop
documentation/
  • research
  • spec
  • root-cause
planning/
  • suggest-points
  • new-skill
  • shape
meta/
  • bootstrap
  • (internal skills)
setup/ ยท bmad/
  • BMad workflow integration