Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@
"name": "tastytrade",
"source": "./plugins/tastytrade",
"description": "Inspect TastyTrade brokerage accounts, positions, market data, option chains, and transactions from Claude Code. Order placement is gated off by default."
},
{
"name": "parallel-agent-isolation",
"source": "./",
"strict": false,
"skills": ["./skills/parallel-agent-isolation"],
"description": "Decide what stateful resources concurrent agents will share, and pick isolation, serialisation, or serial re-verification before dispatching them."
}
]
}
53 changes: 53 additions & 0 deletions .github/workflows/skill-parallel-agent-isolation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: skill parallel-agent-isolation

# One workflow per skill, matching the per-component paths filters used for the
# MCP servers, so a change to one skill never runs another's checks.
#
# marketplace.json is in the filter because it is the shared file: an entry
# edited by another skill's pull request can silently stop this one loading.
on:
pull_request:
paths:
- "skills/parallel-agent-isolation/**"
- ".claude-plugin/marketplace.json"
- ".github/workflows/skill-parallel-agent-isolation.yml"
push:
branches: [main]
paths:
- "skills/parallel-agent-isolation/**"
- ".claude-plugin/marketplace.json"
- ".github/workflows/skill-parallel-agent-isolation.yml"

defaults:
run:
working-directory: skills/parallel-agent-isolation

jobs:
wiring:
# Packaging, marketplace entry, and case set. No credentials, no tokens.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: make check
- run: npm install -g @anthropic-ai/claude-code
- name: Validate the marketplace manifest
working-directory: .
run: claude plugin validate . --strict

evals:
# Behavioural: does the skill load on its own, and does the answer resolve
# the shared resource. Costs Anthropic tokens, so it skips without a key
# (fork PRs get no secrets).
runs-on: ubuntu-latest
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
steps:
- uses: actions/checkout@v4
- name: Run the behavioural evals
run: |
if [ -z "${ANTHROPIC_API_KEY:-}" ]; then
echo "ANTHROPIC_API_KEY absent (fork PR?); skipping the behavioural evals."
exit 0
fi
npm install -g @anthropic-ai/claude-code
make evals
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ Formerly `walkerhughes/mcps`, back when it only held MCP servers.

They follow Honeycomb's [MCP, easy as 1-2-3](https://www.honeycomb.io/blog/mcp-easy-as-1-2-3) guidance: a few curated tools built around real questions rather than raw API endpoints, responses shaped for a model instead of a UI, and typed schemas that steer the model toward valid calls.

## Skills

Skills live in top-level `skills/`, one directory each, and ship through the marketplace as their own installable entries.

| Skill | What it decides |
|-------|-----------------|
| [`parallel-agent-isolation`](skills/parallel-agent-isolation/) | What stateful resources concurrent agents will share, and whether to isolate, serialise, or re-verify serially, before dispatching them. |

A skill's marketplace entry sets `source: "./"` with a `skills` path pointing at its own directory, so several skills can share the one top-level folder without loading each other, and `strict: false` because the repository root has no `plugin.json` to be the authority. Entries deliberately carry no `version`: Claude Code then resolves the version from the commit SHA, so every change reaches installed copies without a manual bump, and the stale-cache trap described below does not apply.

Each skill directory carries its own checks, run from that directory: `make check` for packaging and wiring, which needs no credentials, and `make evals` for behaviour, which costs tokens. CI runs both per skill through `paths` filters, as it does for the servers.

## Install a plugin

Run these as two separate commands, not as one paste: the first opens a prompt that expects only the `owner/repo`.
Expand Down Expand Up @@ -44,9 +56,11 @@ Plugins require [`uv`](https://docs.astral.sh/uv/) on your PATH. The first launc
```
claude/
├── .claude-plugin/ # marketplace manifest
└── plugins/
├── harbor-hub/
└── tastytrade/
├── plugins/
│ ├── harbor-hub/
│ └── tastytrade/
└── skills/
└── parallel-agent-isolation/
```

Plugins live under `plugins/`, one directory each, named for the platform they talk to rather than for being an MCP server. Skills and other components get their own top-level directories as they arrive.
Expand Down
9 changes: 9 additions & 0 deletions skills/parallel-agent-isolation/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.PHONY: check evals

# Packaging and wiring. No API key, no cost.
check:
python3 evals/check_wiring.py

# Behavioural eval. Needs Claude Code on PATH and working credentials.
evals:
python3 evals/run_evals.py
70 changes: 70 additions & 0 deletions skills/parallel-agent-isolation/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
name: parallel-agent-isolation
description: Decide what stateful resources concurrent agents will share, then isolate, serialise, or re-verify serially before dispatching them. Worktrees isolate files, not services, so parallel agents collide on one database, one simulator, or one state file and both still report green.
when_to_use: Use when two or more agents will run concurrently AND their work touches something stateful outside the filesystem, such as a database, a service stack, an integration or end-to-end suite, a search index, a message broker, a cache, a fixed port, a device or simulator, a shared cloud environment or IaC state file, or a shared account or API key. Also use when judging a finished parallel agent's report, especially one saying it could not get its own instance of such a resource and used the one already running, in any wording, including "port is already allocated", "address already in use", "container name is already in use", or "no free simulator". Do not use when concurrent agents only read and write files, such as editing docs or independent source modules, since worktrees already isolate that.
---

# Parallel agents share more than the filesystem

Decide what concurrently running agents will contend for before dispatching
them, and treat any result produced under contention as unverified.

A git worktree isolates files and nothing else. Agents in separate worktrees
still query the one database, boot the one device, hold the one lock, and spend
the one account's quota. Shared state is whatever exists as a single instance
and carries changes between calls, so what one agent does lands where another
will read it.

## The failure is silent

Contention does not raise an error. Two suites against one database interleave,
and a fixture truncating tables between tests deletes the other run's rows
mid-test, so both go green. Two runs driving one device tap each other's
screens. Two applies against one state file each plan against a world the other
has already changed. All of them report success, indistinguishably from an
honest run.

So this is decided before dispatch, not diagnosed after. Afterwards there is
nothing to find, only the question of whether the result was produced under
contention, and a result produced under contention is unverified whatever it
says.

## The pre-dispatch workflow

**1. Name what will be contended for.** Ask what stateful things outside the
filesystem these agents will share, and name each one separately. The obvious
resource usually hides a second, and resolving one leaves the other shared. If
the honest answer is nothing, the work is file-local: dispatch in parallel and
stop here.

**2. Choose how the contention resolves.** Any of the four chosen deliberately
beats meeting the collision later.

- **Isolate.** Each agent gets its own instance, ports, schema, or account. Fits
when duplicating the resource is cheap next to the work it unblocks.
- **Serialise the stateful step.** Agents run in parallel, and the dispatcher
lets one at a time through the step that touches the resource. Fits when that
step is short next to the work around it. A built-in lock is not this: it
covers only the thing it guards, leaving whatever that thing mutates still
shared, so the serialised region has to span the change and the verification
that depends on it.
- **Parallel, then verify serially.** Take every agent's result as advisory and
re-run the verification yourself, one at a time. Fits when verification is
cheap, and it is the only option that still holds when an agent ignores its
instructions, so prefer it when unsure.
- **Split the work.** One agent owns everything touching the resource; the rest
parallelise around it. Fits when only some tasks need the resource at all.

**3. Dispatch with the reporting requirement.** Require each agent to report how
it verified its work, not just the outcome: the command it ran, and whether
every service, device, environment, and account that command touched was its
alone. Say up front that a result which cannot answer that is advisory. A green
result that does not say how it was produced is unverified.

## The anti-pattern

An agent reports that it could not obtain its own instance of a shared resource,
used the one already running, and finished green. That is a corrupted run
presented as a workaround. It invalidates its own result rather than excusing
it, and casts the same doubt over every other agent that was using the resource
at the time.
61 changes: 61 additions & 0 deletions skills/parallel-agent-isolation/evals/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# parallel-agent-isolation evals

Two checks, in cost order.

```bash
make check # packaging and wiring, no API key, no cost
make evals # behavioural, needs Claude Code on PATH and credentials
```

## `make check`

`check_wiring.py` reads the skill's frontmatter, its marketplace entry, and the
case set. It exists because every way a skill breaks in packaging is silent: an
unclosed frontmatter fence, a `name` that no longer matches the directory, a
marketplace entry whose `skills` path points somewhere else, a `version` pinned
in the entry so installed copies never refresh. In each case the skill installs
and simply never loads. Stdlib only, so it runs anywhere.

## `make evals`

`run_evals.py` runs each case as headless Claude Code in a throwaway workspace
holding this skill and nothing else, with `--setting-sources project` so a
personal skill on the developer's machine cannot stand in for the one under
test. Every case asserts on skill invocation; the triggering cases additionally
grade the answer against a rubric using a second, tool-less Claude Code call
that returns a structured verdict and never sees the skill.

The triggering cases deliberately sit in different ecosystems and share different
kinds of state, because a skill that recognised only containers and databases
would pass a case set drawn from one stack while being useless on the next one.

| Case | Stack, and what is shared | Skill must | The answer must |
| --- | --- | --- | --- |
| `shared-database-dispatch` | Go, one hosted staging database | load | resolve the shared instance by isolating, serialising, re-verifying serially, or giving the stateful work to one agent, rather than dispatching both agents at the suite concurrently |
| `shared-infra-state` | Terraform, one remote state file and one sandbox account | load | resolve the shared state and account the same way, rather than letting three applies race through the lock |
| `green-report-collision` | iOS, one booted simulator | load | treat a green suite reported alongside "a second simulator would not boot" as invalid, extend the doubt to the other agent's green result, and re-run serially |
| `file-only-parallel` | any, nothing shared | stay out | (not graded) |

`file-only-parallel` is the honesty check, and it earns its place. A description
that fires on every mention of parallel agents would pass the triggering cases
while making the skill noise, so one case dispatches three agents over
documentation edits and requires the skill to stay out of it. The first draft of
the description failed exactly there, and so did a later attempt to shorten the
clause that excludes file-only work.

Whether a description triggers is sampled behaviour, so each case runs three
times and every run must hold. Cost is a dollar or so a full run on `sonnet`,
capped per call by `--max-budget-usd`. `EVAL_RUNS`, `EVAL_MODEL`,
`EVAL_MAX_BUDGET_USD`, and `EVAL_TIMEOUT_SEC` override the defaults. Pass case
names as arguments to run a subset.

## Why not the Harbor harness

The [harbor-hub evals](../../../plugins/harbor-hub/evals/) are Harbor tasks: a
container image, an MCP server declared in `task.toml`, and a verifier that
recomputes ground truth from the hub. That shape fits a capability surface with
a live backend to compare against. This skill has no server, no backend, and
nothing to recompute. What it has is a claim about what a model does at dispatch
time, so the eval runs the model and looks. Wrapping that in Harbor would add an
image build, a Modal or Docker dependency, and a Harbor API key to a check whose
whole content is one prompt and one grading rubric.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"expect_skill": false,
"rubric": null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
I want to dispatch three subagents in parallel over this repo, each in its own
git worktree. One writes the module docstrings for `src/parsing/`, one does the
same for `src/rendering/`, and one rewrites the README's install section.

None of them run tests or start anything. Write me the dispatch plan.

Do not run anything. Just give me the plan.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"expect_skill": true,
"rubric": [
"The answer must say the work is not done, and must treat the reuse of the already-booted simulator as invalidating the agent's green test result rather than as a harmless note or a minor warning.",
"The answer must state or clearly imply that both agents ran against the same simulator, so both green results are untrustworthy, not only the one that reported the problem.",
"The answer must call for re-running the suites serially against a known-clean simulator before trusting either result."
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Two subagents I dispatched in parallel have finished. They worked in separate
git worktrees on the same iOS app. Here is what the second one sent back:

> Fixed the checkout crash. Ran the UI suite with `xcodebuild test`: 84 passed,
> 0 failed. Note: a second simulator would not boot, so I ran against the
> iPhone 16 simulator that was already booted and installed over the build that
> was on it. Suite is green.

The first agent reported green as well. Am I done? Tell me what to do next.

Do not run anything. Just answer.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"expect_skill": true,
"rubric": [
"The plan must recognise that the two agents would share the one staging Postgres instance even though each has its own git worktree, and must not dispatch both agents to run the e2e suite concurrently against it.",
"The plan must resolve that sharing in at least one of these ways: give each agent its own database instance, schema, or namespace; serialise the e2e step so only one agent runs it at a time; keep the agents parallel but re-run the suites serially afterwards and treat the agents' own results as unverified; or give the e2e work to a single agent.",
"It is not enough to merely mention worktrees, or to say the agents should 'be careful' or 'coordinate' without naming one of those resolutions."
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
I maintain a Go service. Its end-to-end tests run with `go test -tags e2e ./...`
against our shared staging Postgres, a managed instance in our cloud account
that everyone on the team points at. There is no local copy of it. The unit
tests need nothing.

I have two independent pieces of work queued: adding pagination to the orders
endpoint, and fixing a timezone bug in the reporting query. Each one needs the
e2e suite to pass before I will look at it.

I want to hand both to subagents at the same time, each in its own git worktree,
and have them report back. Write me the dispatch plan: how many agents, what
each one does, and what I put in their prompts.

Do not run anything. Just give me the plan.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"expect_skill": true,
"rubric": [
"The plan must recognise that the three agents would share the one Terraform state file and the one sandbox account even though each has its own git worktree, and must not dispatch all three to apply concurrently against that single state and account.",
"The plan must resolve that sharing in at least one of these ways: give each agent its own state, workspace, or sandbox account so the applies do not meet; serialise the apply and smoke-test step so only one agent runs it at a time; keep the agents parallel on the code but do the applies and verification serially afterwards and treat the agents' own results as unverified; or give all the applying to a single agent.",
"It is not enough to merely mention worktrees, or to rely on the state lock alone, or to say the agents should 'be careful' or 'coordinate' without naming one of those resolutions."
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Our infrastructure is a Terraform repo. The backend is one S3 bucket with a
DynamoDB lock table, and everything applies into a single sandbox cloud account
we use to try changes before production. Verifying a change means applying it in
the sandbox and then running a smoke script against the deployed endpoint.

Three unrelated changes are queued: a new CDN cache policy, a database instance
resize, and a tightened security group. I want three subagents in parallel, each
in its own git worktree, each applying and smoke-testing its own change and
reporting back.

Write me the dispatch plan: how many agents, what each one does, and what I put
in their prompts.

Do not run anything. Just give me the plan.
Loading
Loading