Lab 9 — Subagents and background agents
This standalone snapshot teaches harness ask to delegate in two ways:
- a subagent runs in its own context window, with its own prompt and tool set, and returns only a summary to the main conversation.
- a background agent is a whole
harness askrun, detached, in its own git worktree, while you keep working.
The exercise matches the Claude Code Lab 9.
What changes
harness/agents.py:- Agents live in
.harness/agents/<name>.mdin the project, or~/.harness/agents/<name>.mdfor you alone. Front matter gives thename, adescriptionand a requiredtoolslist (exact names or patterns such asmcp__orders__*); the body is the agent’s system prompt. - The main conversation sees each agent’s name and description and a
run_agent(agent, task)tool. Delegating is allowed by default; the agent’s own tool calls are checked as usual.
- Agents live in
harness/chat.pyruns eachrun_agentcall as a child:- a fresh history containing only the task, the agent’s prompt, and only the tools it lists. A built-in policy denies any other tool.
- the same hooks, permissions, MCP tools, model client and trace as the
parent. Its tool lines are shown as
agent:tool. - its own transcript, next to the session file:
~/.harness/projects/<project>/sessions/<id>.agents/NN-<agent>.jsonl. - The parent receives one tool result: the agent’s final answer, with its call counts and transcript path.
- Agents can’t start other agents, and they run one after another; this harness has no parallel fan-out.
harness/background.py:harness ask --bg -n NAME "task"creates a git worktree at.harness/worktrees/NAMEon branchworktree-NAME, from your last commit, and starts a detachedharness askthere with--accept-edits(there’s nobody to answer a prompt) and--trace.harness agentslists background agents and their status;harness logs NAMEshows the output so far.harness rm NAMEremoves the worktree, the branch and the records. It refuses while the agent is running or if the worktree has uncommitted changes (--forcediscards them).
/agentslists the available agent definitions.
harness/subagents.py keeps the earlier
in-process model of a sub-agent task plan with isolated child transcripts.
Learner steps
Start from: labs/app/ at tag lab08-done (see the
track guide).
-
Install this lab and configure Foundry as before:
python -m venv .venv . .venv/bin/activate # Windows: .\.venv\Scripts\Activate.ps1 pip install -e '.[dev]' cp .env.example .env az login cd ../app
Part A: subagents
-
Read the two agent definitions in
assets/.harness/agents/:Agent Tools Job security-reviewerlist_files,read_file,git_status,git_log(read-only)Find input-validation, secret-handling and money bugs test-writerlist_files,read_file,write_file,edit_file,run_testsAdd missing tests; mark real app bugs xfailinstead of “fixing” themInstall and commit them:
cp -R ../lab09-background-agents/assets/. . git add .harness && git commit -m "chore: add review and test subagents" -
Delegate:
harness ask -n delegateType
/agents, then:Use the security-reviewer agent to review cli.py and pricing.py, and the test-writer agent to add tests for any untested CLI command. Then give me one combined summary.Observe:
- two
run_agentcalls, each followed by lines such asTool: security-reviewer:read_file(...). - your Lab 6 permission prompts (and Lab 2B hooks) still apply to the test-writer’s edits.
- did the reviewer stay read-only? Any tool outside its list is shown as
Hook: denied security-reviewer:.... /context: the main conversation holds the two summaries, not the files the agents read. Compare with Lab 7’s numbers. Each agent’s full transcript is in the.agents/folder named in its result.
/exitwhen done, review the new tests, and commit what you keep. - two
Part B: a background agent
-
Commit your work first: the background agent’s worktree is created from your last commit, so it won’t see uncommitted changes. Keep the worktrees out of git:
echo ".harness/worktrees/" >> .gitignore git add .gitignore && git commit -m "chore: ignore agent worktrees" -
Start it:
harness ask --bg -n coverage "Measure which functions in this project have no direct test and write COVERAGE.md with a table of function, file and tested yes/no. Do not modify code or commit." harness agentsKeep working in your terminal; check on it with:
harness logs coverageObserve:
git worktree listshows a second checkout under.harness/worktrees/coverageon aworktree-coveragebranch. The agent writesCOVERAGE.mdthere, not in yourapp/folder. That isolation is what makes it safe to run alongside your own edits. -
When
harness agentsshowsdone, bring the result over and clean up:cp .harness/worktrees/coverage/COVERAGE.md . rm .harness/worktrees/coverage/COVERAGE.md harness rm coverageCopy-Item .harness\worktrees\coverage\COVERAGE.md . Remove-Item .harness\worktrees\coverage\COVERAGE.md harness rm coverageTry
harness rm coveragebefore removingCOVERAGE.mdfrom the worktree: it refuses, so you don’t lose work by accident.Subagents and background agents multiply token cost. Check
/cost, orharness traceon the background agent’s trace file (shown byharness logs), and keep tasks small. - Record
- Which tools each subagent actually used, and any that were denied.
- Main-context size after delegation vs Lab 7’s
/contextnumbers. - Whether the background agent finished, and how you’d know if it hadn’t.
-
Checkpoint. Commit the useful results (new tests,
COVERAGE.md), then inapp/:python -m pytest -q git tag lab09-done -
Back in the lab folder, run the offline checks and inspect the declared capabilities:
pytest checks/ harness lab-info
harness ask keeps skills and MCP (Lab 8), tracing (Lab 7), permissions
(Lab 6), file memory (Lab 5), plan mode and todos (Lab 4), sessions (Lab 3)
and the Lab 2B tools, built-in policy, project hooks and rules. This is a
teaching harness, not a sandbox: only use it on the practice app.