Control Tower is a bootstrap for a Codex-driven multi-agent orchestration workflow.
It installs a tower command for humans and a tower-run command for Tower’s internal runtime operations. Together they wrap OpenAI Codex CLI, initialize a project-local .control-tower/ runtime, load Tower with persistent project memory, and provide delegated subagent entrypoints for:
BuilderImplementation specialist for product code, tests, and refactors.InspectorReview specialist for correctness, regressions, and quality checks.ScoutResearch specialist for discovery, options, and technical tradeoffs.Git-masterRepository operations specialist for branch, commit, and PR workflows.ScribeDocumentation and memory specialist for summaries, docs, and durable project state.
- Creates a portable
.control-tower/directory inside any repo you runtowerin. - Opens an init-time CLI setup flow with a fast default path and an optional detailed per-agent configurator.
- Starts Codex with a Tower-specific bootstrap prompt that includes project memory and agent contracts.
- Persists project memory in three tiers:
L0: fast snapshotL1: working summaryL2: imported Codex session logs
- Adds a decision graph under
.control-tower/state/decision-graph/so memory summaries stay linked to decisions, tasks, sessions, packets, and commits. - Imports Codex session JSONL files from
~/.codex/sessionsinto project memory. - Gives Tower a concrete delegation path via
tower-run delegate <agent> --packet <file>.
Clone this repo, then run:
./setup.shThat installs tower and tower-run into ~/.local/bin/.
If you want a one-line remote install instead of cloning first:
curl -fsSL https://raw.githubusercontent.com/yashturkar/control-tower/main/scripts/bootstrap_remote_install.sh | bashThat command:
- clones or updates Control Tower under
~/.local/share/control-tower/repo - installs
towerandtower-run - leaves the local clone available for future updates
Inside any Git repo:
tower init
tower starttower init defaults to a quick setup that keeps the standard agent lineup and enables dangerous bypass for every subagent except Scout, which stays sandboxed. It also records a repo-local session import cutoff so the first Tower sync starts from initialization time forward instead of back-importing older Codex history for the same repo path. If you choose custom, it opens the detailed per-agent configurator for enablement, bypass/sandbox, and model overrides. You can always edit .control-tower/state/agent-registry.json and .control-tower/state/project.json later.
Tower now runs without sandboxing or approval gates by default:
tower startIf you want to disable that for a specific session:
tower start --no-dangerousThe repo default lives in .control-tower/state/project.json:
{
"codex_defaults": {
"dangerously_bypass": true
}
}Resume the last Tower session for the current repo:
tower resumeUpdate the installed Tower CLI and refresh the current repo's .control-tower/ runtime:
tower updateInspect project state:
tower statusLow-level runtime example:
tower-run create-packet builder \
--title "Implement feature X" \
--objective "Add feature X and tests" \
--instruction "Modify the relevant source and tests" \
--expected-output "Updated source and tests" \
--definition-of-done "Feature X works and tests pass"
tower-run sync-memory --emit-scribe-packetAfter tower init, the target repo gets:
.control-tower/
agents/
docs/
logs/
memory/
packets/
schemas/
state/
The files are intended to be committed with the target repo so Tower can carry context across machines and collaborators.
flowchart TD
U["User"] --> TS["tower start"]
TS --> TB["Tower bootstrap prompt<br/>L0 + L1 + configured agents"]
TB --> D{"Tower needs specialist work?"}
D -- "No" --> TU["Tower replies to user"]
D -- "Implementation" --> B["tower-run delegate builder"]
D -- "Review" --> I["tower-run delegate inspector"]
D -- "Research" --> SC["tower-run delegate scout"]
D -- "Git / PR" --> G["tower-run delegate git-master"]
D -- "Docs / memory" --> S["tower-run delegate scribe"]
B --> RP["ResultPacket"]
I --> RP
SC --> RP
G --> RP
S --> RP
RP --> TR["Tower reads ResultPacket"]
TR --> TU
TR --> NH{"Need another handoff?"}
NH -- "Yes" --> D
NH -- "No" --> SM["tower-run sync-memory"]
SM --> L2["Beacon L2<br/>Imported Codex sessions"]
SM --> L1["Beacon L1<br/>Working memory"]
SM --> L0["Beacon L0<br/>Snapshot memory"]
SM --> BB["Black Box log"]
SM --> SP["Optional Scribe packet"]
SP --> S
L0 --> TB
L1 --> TB
TU --> U
Tower is intentionally non-coding. It delegates by creating a task packet and running:
tower-run create-packet builder \
--title "Implement feature X" \
--objective "Add feature X and tests" \
--instruction "Modify the relevant source and tests" \
--expected-output "Updated source and tests" \
--definition-of-done "Feature X works and tests pass"
tower-run delegate builder --packet .control-tower/packets/outbox/task.jsonSubagents run through codex exec with their own prompt, policy, and packet context. Their output is constrained to the ResultPacket schema.
The intended loop is:
- Tower creates a TaskPacket.
- Tower delegates to a subagent.
- The subagent returns a ResultPacket.
- Tower reads that ResultPacket, reports progress or success to the user, and decides whether to hand off to another subagent.
- After the chain is complete, Tower syncs memory and optionally routes durable curation to Scribe.
For chained work, Tower can seed the next packet from the previous ResultPacket. Example: Builder finishes implementation, then Tower creates a Git-master packet from that Builder result, delegates Git-master, then creates a Scribe packet from the Git-master result.
tower-run create-packet git-master \
--from-result .control-tower/packets/inbox/builder-result.json \
--title "Commit Builder changes" \
--task-type "git-operations" \
--objective "Review the Builder output, stage the intended files, and create a commit" \
--instruction "Use the Builder result packet as the source of truth for changed files" \
--expected-output "Commit hash and commit summary" \
--definition-of-done "Relevant changes are committed cleanly"tower-run sync-memory scans the local Codex session store and imports sessions whose cwd matches the current project root and whose timestamp is at or after the repo's recorded import cutoff. Imported sessions are copied into .control-tower/memory/l2/sessions/, a transcript index is maintained in .control-tower/state/session-index.json, a decision graph is refreshed under .control-tower/state/decision-graph/, and graph-backed L0 / L1 summaries are regenerated.
For higher-quality persistent memory, use tower-run sync-memory --emit-scribe-packet. That creates a Scribe task packet so Tower can delegate long-form curation of:
- session summaries
- open questions
- task ledgers
- decision registers
- architecture notes
- ADR/doc drift
Additional graph-oriented commands:
tower-run log-decision --title ... --topic ... --summary ...tower-run graph-statustower-run graph-view --webtower-run graph-view --tui --focus <node-id> --radius 2tower-run graph-export --format json --output graph.jsontower-run graph-export --format dot --output graph.dottower-run graph-export --format svg --output graph.svgtower-run graph-search [--query <text>] [--type <node-type>] [--include-edges] [--limit <N>]tower-run explain --commit <sha>tower-run explain --decision <decision-id>
Use these commands when you want explicit, queryable provenance instead of only narrative memory notes.
# 1) Record an explicit decision and attach evidence refs
tower-run log-decision \
--title "Adopt graph-backed memory" \
--topic memory-architecture \
--summary "Keep L0/L1 grounded in decision graph nodes and edges." \
--rationale "Makes commit-to-decision explanation deterministic." \
--source-ref docs/architecture/memory.md \
--related-ref src/control_tower/graph.py
# 2) Inspect high-level graph health
tower-run graph-status
# 3) Discover node IDs (list/search nodes, optionally include edges)
tower-run graph-search --query memory --type decision --include-edges
# 4) Explain provenance for a commit (supports short SHA too)
tower-run explain --commit 3ae65c6
# 5) Explain provenance for a specific decision id
tower-run explain --decision dec_20260320T165537_memory-architectureflowchart LR
E[event.observed] -->|references| D[decision: Adopt graph-backed memory]
D -->|references| A[artifact: src/control_tower/graph.py]
T[task: Sync memory summaries] -->|references| D
P[packet: scribe-docs-followup] -->|references| A
P -->|discussed_in| S[session: 2026-03-20T16:55Z]
C[commit: 3ae65c6] -->|caused_by| S
- Node types
decision: explicit or inferred architecture/process decisions.artifact: files or paths used as evidence/provenance anchors.session: imported Codex sessions for timeline grounding.task: items from the task ledger.packet: task/result packets in.control-tower/packets.commit: git commits observed in repo history.event: raw observed event records materialized into graph state.
- Edge types
references: generic provenance link (for example packet → artifact, decision → artifact).discussed_in: packet was discussed in a specific session.caused_by: commit is linked to its nearest session in time.
python33.9+codexinstalled and authenticated- a writable
~/.local/binon yourPATH
toweris the intended user interface.tower-runis the intended internal interface for Tower’s own orchestration primitives.tower resumeprefers the last tracked Tower session for the current repo. If none is recorded, it falls back tocodex resume --last.- The bootstrap uses only the Python standard library.
- The repo includes JSON schemas and prompt/policy templates, but the runtime is intentionally lightweight so it can be used as a starting point and extended.