Supervisor communications and multi-agent coordination for the Dark Atelier platform. The human's assistive agent for running work across many agent sessions on many machines, under many harnesses.
Status: simulation, with a proven transport and a cross-host tier. The director software does not exist yet. What exists:
- a skill that has a session play the role by hand, plus the instruments it
uses and the requirements that fall out (
skills/director/,sim/requirements.md); - a Phase 0 probe that proves the message bus the software will ride: a
local NATS broker and a small MCP shim carry the director envelope between
sessions end to end, across Claude Code and codex, with store-and-forward,
deduplication, presence, and broker-enforced authorization
(
probe/nats-phase-0/); - a global tier that connects one host's broker to another's through a hub,
so a director reaches a remote supervisor without a rename and without
holding a hub credential (
probe/nats-global-tier/, design brief 8); - the marvel twin: the hand-run fleet declared as one marvel manifest, cast
from role library roles, with the check runbook that decides cutover
(
sim/twin/, design brief 7).
This repo is the authoritative source for all of it, and the requirements are the point.
An operator running several agent sessions is the message bus, and that is the defect. Sessions stop to ask a question and the question waits on one human's attention; when the session ends first, the work it did and the answer it needed are both lost. Observed with nine sessions on one laptop: five had died holding unanswered questions.
Director's product is therefore custody, not coordination. Nothing produced is lost and nothing asked is dropped. Message routing is downstream.
Harness vendors are building session-to-session messaging, and it is good.
It is also single-vendor, single-account, single-machine. Director must reach
sessions running Claude Code against Bedrock or the Claude platform on AWS,
codex, crush, opencode, raw SDK streams, and whatever is next, across hosts
and accounts. The measured capability table in
skills/director/reference/adapters.md is the argument: of four harnesses
installed on one machine, exactly one exposes presence or an address.
There is a subtler reason too. Claude Code's own cross-session messaging does
not deliver a message, it delivers the message wrapped in a policy paragraph
the receiving harness writes, which tells the receiver the sender is "very
likely" acting for the user. That hedge is a probability estimate standing
where a signature belongs, it is not ours to version or extend, and no other
harness ships anything like it. Building director on that feature means
inheriting safety we cannot inspect and cannot carry:
skills/director/reference/relay.md and
sim/specs/vendor-injected-receiver-policy.md.
| Read this | For |
|---|---|
| docs/getting-started.md | running the broker, provisioning it, building the shim, joining Claude Code and codex sessions, sending and receiving, authorization, the global tier, marvel |
| docs/shim-reference.md | every environment variable, tool, address scheme, subject, stream, and envelope field the shim uses |
| docs/architecture.md | the target shape in diagrams: bus, addressing, receive-is-a-poll, acknowledgement, the marvel seam, ask lifecycle, the performative handshake, the global tier |
| docs/use-cases.md | worked use cases from the simulation, each with a diagram and the requirements it exercises |
sim/requirements.md |
the requirements register, R-01 onward, each traced to the observation that earned it |
sim/design/ |
design briefs: identity at spawn, the seat lease, continuous custody, the shim heartbeat, the twin, the global tier, credential enrollment, broker supervision |
skills/director/ the skill: role, modes, output contract, capture triggers
reference/ measured capability table; the settled and open relay questions
scripts/dsi session inventory across all four adapters
scripts/dsx external state verification (the board has no expiry)
scripts/board-html render board.md as a self-contained local page (search,
filters, collapsible sections, auto-refresh); no network;
with a ledger, the ledger sections above the history
scripts/dws the workstream ledger: one row per stream, stage, last moved
scripts/ the other tools install.sh links; see "What install.sh links"
skills/stansfield/ full fleet roll call: enumerate every session across the
bus, marvel clusters and SendMessage, deduped to one
identity each, then optionally relay an action
commands/director.md thin command that invokes the skill
install.sh symlink or copy the skills and command into ~/.claude,
and harness-neutral tools into ~/.director/bin
cmd/director-mcp/ the shim (Go): eight tools, plus cancel_held and mark_handled in
presence-watch and handled modes; local and global tiers, preflight
config/ director-mcp.json, the MCP config `scripts/director start` hands to claude
scripts/ director (start a director session), director-install, the
director-mcp build and release scripts, and verify-*.sh checks
docs/ operator guide, shim reference, architecture and use-case diagrams
probe/nats-phase-0/ the transport probe: NATS broker config, provisioning, checks
PROGRESS.md sub-probe results 1 through 9 (poll is the receive, finding-160)
director-mcp-seat the launcher a seat registers; see "The bus seat, by hand"
authorization.conf the credential-to-subject binding (director#4)
cross-harness-demo.sh codex and headless Claude Code joining the bus
probe/nats-global-tier/
the global tier probe: a TLS hub and leaf-connected
clusters, so separate clusters share one director tier
brief.md what the tier is for and what it has to prove
nats-mechanics.md the NATS behaviour the tier leans on
recipe-mokuzai.md the worked setup for one leaf cluster
hub/ hub config, the CA ceremony, provisioning scripts
leaf-remote.conf.example the leaf side of the connection
verify-*.sh checks that a tier is actually carrying traffic
sim/ the simulation record
prompt-session-1.md the original direction, verbatim
requirements.md the requirements register (the point)
specs/ requirements firm enough to constrain the software
design/ design briefs 1 through 10 (candidates, not specs)
twin/ the marvel twin: manifest, cast launcher, check runbook
artifacts/ work products the simulation produced
sim/notes/ and the state root (~/.director/state by default) are
gitignored. They carry live operational detail about real sessions and are
never published; anything durable is rewritten clean into sim/specs/ or
into the platform's knowledge graph.
./install.sh # symlink into ~/.claude, edits go live
/director # sweep: what is blocked on you
/director standing # adopt the role for the sessionThat installs the director and stansfield skills and the command, creates
~/.director/bin (override with DIRECTOR_HOME), links the tools
listed under "What install.sh links" into it, and creates the state root. It
also writes default idea roots to idea-roots.conf in the state root when that
file is absent.
Three tools, each with its own scope:
install.sh(above): the skills, the command, the tools under "What install.sh links", and the state root.- By hand (next section): the bus seat and
director-mcp-seat. No tool installsdirector-mcp-seatyet. director-install:cast-launch, the launcher marvel runs for seats cast from the role library, anddirector-mcp, the shim, built from a commit withdirector-install director-mcp --ref <commit>and undone with--rollback. It does not installdirector-mcp-seat.
A host that runs seats cast from the role library installs the launcher from a commit, with no checkout needed:
director-install cast-launch --ref origin/main
director-install --status # what is installed and how far behind mainInto ~/.director/bin, one line each. Each is a script under skills/director/scripts/, except director-install, which is under scripts/.
| Tool | What it does |
|---|---|
board-html |
renders board.md as a local page (next section) |
dws |
the workstream ledger: one row per stream, stage, last moved |
page-namespace |
puts the install's namespace in a page's title and description |
dsi |
session inventory across the adapters |
dsx |
external state verification |
merge-guard |
the fail-closed read before a merge; prints PROCEED or a refusal |
relay-log |
writes a relay-log entry, stamped from the clock |
desk-snapshot |
writes the local snapshot of the Desk's open cards that dws checks |
lane-ledger |
renders the lane ledger page from director's own files |
card-ingest |
turns a seat's card file into a Desk write, and refuses a card missing a part |
draft-sweep |
read-only list of fleet drafts nobody has reviewed and PRs merged with no review |
dbi |
the stalled big ideas scan |
freebusy |
read-only free/busy table |
idle-shadow |
the idle scheduler's shadow run, read side |
dash-writer |
shapes the operations dashboard's documents for the Desk's second tab |
introspect |
the runtime fact check director runs at the sweep and at harvest |
composer-check |
looks at a pane before an inject that ends in Enter |
standing-hook |
prints the drain age and the standing checklist before each answer |
director-session |
starts one director session by hand, with the channel cue bound when asked |
director-install |
installs components from a commit (above) |
~/.director/bin/board-html renders $DIRECTOR_STATE/board.md into
board.html beside it: one self-contained file with search, tag filters,
collapsible sections, newest-first ordering and a light/dark theme. It is plain
Python 3 with no dependencies, so it works the same on macOS and Linux and from
any harness. Nothing is fetched and nothing leaves the machine.
The page is opt-in. Run board-html once to create it; from then on the
director skill rebuilds it after each board edit, because the page exists. A
page opened from disk cannot see its own rebuild, so it carries an
Auto-refresh toggle (off, 10s, 30s, 60s) that reloads and keeps your scroll
position, search and open sections.
The skill alone does not let a seat send a message. Sending runs over the
director MCP server, and nothing in install.sh puts it in place or
mentions a broker (install.sh does create ~/.director/bin, where it goes).
On this fleet that gap is closed by hand. It is honest to say what this is: Phase 0 probe material being run in
anger, not a packaged install. Writing the installer is a separate job.
Two pieces:
director-mcp, the MCP server, built fromcmd/director-mcp/. Install it withdirector-install director-mcp --ref <commit>, or build it and place it at~/.director/bin/director-mcp, or pointDIRECTOR_MCP_BINat it. The launcher refuses to start if it is missing. Each push to main that changes the shim also publishes a prerelease,director-mcp-alpha-<date>-<time>-<sha7>, with binaries for macOS and Linux (arm64 and amd64),checksums.txtand an attestation. Check a download withgh attestation verify <file> --repo ArcavenAE/director --bundle attestations.sigstore.json.director-mcp-seat, the launcher that works out who the seat is, underprobe/nats-phase-0/. The fleet runs it from~/.director/bin/director-mcp-seat, a symlink back to this repo.
Register it once, with no identity of its own. The same registration is then correct both for a marvel-managed session and for a seat you start yourself:
codex mcp add director -- ~/.director/bin/director-mcp-seatWhat the launcher needs, and where it gets it:
| Needs | In a marvel session | Started by hand |
|---|---|---|
| identity | MARVEL_SESSION, MARVEL_TEAM, MARVEL_WORKSPACE, stamped by marvel |
DIRECTOR_AGENT_ID falls back to director-seat; DIRECTOR_TEAM and DIRECTOR_WORKSPACE are required and it exits without them |
| bus credentials | DIRECTOR_NATS_USER and DIRECTOR_NATS_PASS, stamped by marvel |
user director, password read from ~/.marvel/state/nats/director.pass |
| broker | NATS_URL, default nats://127.0.0.1:4222 |
same |
That password file exists only if the cluster declares a seat, so a hand-run seat also needs this in the cluster config, then a daemon restart:
bus: { managed: true, seat: { workspace: <ws>, team: <team> } }One extra line is required for codex, which does not pass its environment to an MCP server. Without it a marvel-managed codex session cannot see marvel's stamps and falls back to the seat identity. It belongs in the role's manifest beside the command registration:
-c 'mcp_servers.director.env_vars=["MARVEL_SESSION","MARVEL_TEAM",
"MARVEL_WORKSPACE","DIRECTOR_NATS_USER","DIRECTOR_NATS_PASS",
"NATS_URL"]'
Diagnostic: a marvel-managed agent that appears on the roster under the seat id rather than its own session name is missing that line.
A session is the director only when it is started as one. scripts/director start runs claude --mcp-config config/director-mcp.json, so the director MCP
server comes from this repo and not from the folder the session opened in.
Identity never sits in the repo or in a session folder. It comes from one small
file per host, $HOME/.director/seat.env, or from flags.
First start. Install the server and its seat launcher (see "The bus seat,
by hand"), then write the seat file. One KEY=value per line, blank lines and
whole-line # comments allowed, nothing executed; no comment after a value.
Example, with placeholder values:
DIRECTOR_AGENT_ID=director
DIRECTOR_TEAM=ops
DIRECTOR_WORKSPACE=aae
DIRECTOR_CLUSTER=kinu
NATS_URL=nats://127.0.0.1:4222
DIRECTOR_AGENT_ID, DIRECTOR_TEAM and DIRECTOR_WORKSPACE are required. The
other allowed keys are DIRECTOR_CLUSTER (--cluster <name> overrides it),
DIRECTOR_GLOBAL_DOMAIN (empty keeps the global tier off), DIRECTOR_GLOBAL_ROLE,
and NATS_URL (default nats://127.0.0.1:4222). Any other key is refused.
Then run scripts/director start. The session is named director. The
launcher stops with the missing key names, and runs nothing, when a required key
(DIRECTOR_AGENT_ID, DIRECTOR_TEAM, DIRECTOR_WORKSPACE) is absent or the
file holds a line it does not allow.
Restart. scripts/director start --resume director resumes that session by
name. Use --cluster <name> to start against another cluster without editing
the file.
Recovery when the session is lost. The director's state is on the bus and
under $HOME/.director/state, not in the session, so a lost session costs
conversation context only. Run scripts/director start again (a new session
named director), or claude --resume to pick an older one and then
scripts/director start --resume <name>. Nothing about the identity has to be
rebuilt, because it was never in the session.
Prove the launcher, then clear the old entry. Earlier setups left a
project-local director-mcp entry for the aae-orc folder in
$HOME/.claude.json, which makes every Claude session started there join the
bus as the director. Do not remove it first: it is the only way left to start a
director until the launcher is proven. In order:
scripts/director start, in a new session.- Inside that session, run
scripts/director prove. It passes only when the session was started by the launcher and the director MCP server is running under it with the launch variable that only the config's env block sets, which a server started by the old entry does not have (it needspython3). It writes$HOME/.director/state/launch-proof. scripts/operator-steps/2026-10-20-remove-project-local-director-mcp.sh --check, then--apply. Both refuse without a proof from a launch that used the current config, written in the last 7 days.--applybacks the file up and removes that one entry, nothing else.
probe/nats-phase-0/start.sh & # local broker, loopback, JetStream
nats stream add AGENT_INBOX --subjects 'agent.*.*.*.inbox,agent.*.*.role.*.inbox' \
--storage file --retention limits --max-age 168h --max-msg-size 65536 --dupe-window 2m
nats stream add AGENT_AUDIT --subjects 'agent.audit' --storage file --retention limits --max-age 720h
nats kv add AGENT_STATE --ttl 90s --storage file
(cd cmd/director-mcp && go build -o director-mcp .)Then join a session by handing the harness the shim with an identity in its environment. The getting-started guide walks each step, both harnesses, and what to do when a message does not arrive. Under marvel, a cluster with a managed bus does the broker half itself.
multiclaude/ and tmate/ are third-party checkouts kept locally for
reference and are not vendored. multiclaude is the store-and-forward design
predecessor; tmate is prior art for reaching a terminal session you do not
share a machine with.
Session 1 of the simulation ran 2026-09-04 over nine sessions and three hours. It produced nine envelope fields, each earned by a specific observed failure, and one unpredicted result: the coordination was not the valuable part, the custody was. It also silently dropped five of its own messages while every send reported success, which is why liveness is a requirement here and not a nicety.