I usually have several coding-agent sessions open on the same repos at once, across two Claude Code accounts and a handful of other CLIs, and I check on most of them from my phone. Without shared rules they tripped over each other's files, started duplicate Docker stacks and burned quota on work a cheaper model could do. cstack is what I built to stop that. It is the setup I run every day, public under the MIT licence, and I did not design it as a framework for other people.
Everything in it does one of three jobs. It keeps parallel sessions out of each other's way, it spends fewer tokens, and it lets me work from my phone. This post covers the first two, and the numbers behind the second.
One folder, six agents
The six agents are Claude Code, Codex, OpenCode, dsh (DeepSeek's CLI), kimi and Grok Build. Each loads rules, skills and hooks its own way. The aim was one source of truth in six formats, without six copies to keep in sync by hand.
Claude Code gets the global rules through a symlink that install.sh makes, and scripts/sync-agents.py wires up the other five. For Codex, OpenCode, dsh and kimi it generates an AGENTS.md from the shared rules plus a short DELTA.md for that agent, then links their skills and hook config. Grok Build reads Claude's files itself, so it gets only its DELTA.md. Because the agents link to one folder, one edit reaches all of them, and sync-agents.py --check shows any drift.
The coordinating is done by 12 hooks, all written in Python, which Claude Code runs natively. Codex, OpenCode, dsh, kimi and Grok Build run the same scripts through agent_hook.py, an adapter for their hook events. State lives in small JSON files with file locks. There is no server to run.
| Hook | What it does |
|---|---|
claims.py | Claims each file a session edits, blocks a second session in the same worktree and warns across worktrees. Also keeps status notes and their history. |
queues.py | Gives one session at a time a turn on a shared target, like a staging deploy or a merge. |
guardrails.py | Checks commands and edits against rules learned from earlier failures. |
stacks.py | Shares one Docker stack, simulator or emulator between sessions by lease. |
memory_guard.py | Blocks new agents and heavy commands when the Mac is low on memory. |
memory_recall.py | Adds only the memories that match the prompt, from every account. |
effort_gate.py | Makes the model state the effort a task needs, and ask to change it early in a session. |
reset.py | Lets a long session clear or compact its own context at a break point. |
alerts.py | Plays a sound and shows a notification when a session finishes or needs me. A question left 5 minutes goes to the phone app. |
session_mirror.py | Every 15 seconds, copies session entries between Claude accounts so their sessions can message each other. |
agent_hook.py | Lets the five outside CLIs run the same hooks. |
codex_hook.py | A 9-line shim to agent_hook.py for Codex. |
The repo is mostly Python, with 9,607 lines across 24 files. Its only shell script is install.sh, at 197 lines. Of the hooks, the biggest are stacks.py (1,077 lines), claims.py (898), alerts.py (870) and agent_hook.py (768).
Keeping sessions out of each other's way
The first problem was two sessions editing one file. claims.py runs before every edit. It records which session is editing which file in claude-claims.json, inside the repo's git common dir, so every worktree and every account reads the same list. A second session that tries to edit a claimed file in the same worktree is blocked. From a different worktree it gets a warning instead.
A blocked session can message the one holding the claim, because each name in claims.py list is a SendMessage address. The rules ask for one handoff covering what is done, the branch, the files, the tests and what is left. The session with less done hands over, releases its claims and moves on, and if the two cannot agree within two messages they ask me.
Each session also keeps a status note. Every note is logged with its time and the files edited, so a new session can search the history for whether something was already done or tried. As of 27 Oct 2026, this site's repo has 68 logged events from 10 sessions since 23 Sep: 61 notes, 6 session ends and 1 release.
Two hooks handle shared things that are not files. queues.py gives one session at a time a turn on a target like a staging deploy. stacks.py leases Docker stacks, simulators and emulators. A session that asks for a stack another session already has up reuses it, so there is never a second copy. A launchd reaper runs every 10 minutes and stops stacks nobody has leased for 45 minutes.
Turning failures into rules
Some mistakes repeat across sessions. One session runs git stash on a repo other sessions are working in, and their uncommitted work goes into the stash with its own. Writing that down in one session's memory protects that session. A guardrail protects every session that comes after.
guardrails.py learn writes a rule into guardrails.json. A rule has a pattern that matches a command or a file path, an action (warn, stop, queue or deny), and a reason that says what could break and what to do instead. claims.py checks every command and edit against the rules before it runs. A stop asks for a check. If the edge case applies, the session fixes it or tells me what could break. If it does not, the session acks the rule with what it checked and tries again. When a risky command fails, claims.py suggests a new rule.
| Guardrails on my machine, as of 27 Oct 2026 | Count |
|---|---|
| Learned between 18 Sep and 27 Oct 2026 | 85 |
| Still active | 76 |
| Retired | 9 |
| Active: warn | 38 |
| Active: stop | 31 |
| Active: queue | 7 |
| Active: deny | 0 |
| Matching a command (the rest match a file path) | 80 of 85 |
| Sum of every rule's hit counter | 2,342 |
The rules say to retire a rule that turns out wrong, and 9 have been retired so far. None of the active rules is a deny.
Spending fewer tokens
The routing rules in cstack come from two tests I ran on my own work in September and October 2026: a routing benchmark and a bake-off on real commits. Cost is API list price, standing in for plan usage. A few rules rest on estimates instead of runs, and the README marks those.
Effort mattered more than the model
The routing benchmark had five tasks built from real study and open-source work, with planted bugs and defects so they could be scored. They were a fact-check of 18 claims, a bug fix with 29 hidden tests, an audit of 13 subtle claims, a PR review with 4 planted defects, and a mentor update judged blind. I ran 23 setups, two runs each, and Opus 5 and Codex judged the results blind. Their writing rankings matched closely (correlation 0.9).
view data
| effort | Cost per task |
|---|---|
| low | 0.33 USD |
| medium | 0.42 USD |
| high | 0.53 USD |
| extra-high | 0.9 USD |
| Opus 5 effort | Fact-check | Bug fix | Audit | Review | Writing /10 | $ per task |
|---|---|---|---|---|---|---|
| low | 100% | 100% | 96% | 100% | 8.0 | 0.33 |
| medium | 100% | 100% | 100% | 100% | 7.8 | 0.42 |
| high | 100% | 100% | 100% | 100% | 8.8 | 0.53 |
| extra-high | 100% | 100% | 100% | 100% | 6.8 | 0.90 |
Low effort lost 4 points on the audit task and nothing on the other three, while writing went the other way: extra-high scored 6.8 against 8.0 at low, and high effort wrote best at 8.8. A subagent inherits the session's effort unless it is given a model and effort of its own. A session running at extra-high runs every subagent it spawns at extra-high too, for 2.7 times the cost and no meaningful gain. So cstack pins subagents at low effort and keeps high effort for text a person reads.
I used to describe the 2.7x as a model-routing saving, and that was wrong. It is one model at two effort levels. It was also measured on Opus 5. Opus 5.5 has since replaced it as my main worker and I have not re-run the benchmark, so these numbers are Opus 5's. One more setup from the same run ruled out a common pattern, where a cheap model does the work and Opus reviews it. That scored 100% but cost $0.78 a task, against $0.33 for Opus 5 at low effort alone.
The bake-off on real commits
The benchmark tasks were small, 3 to 60 steps each. For the bake-off I took 10 tasks from the git history of one production React Native + Firebase app. Six were short, four were long, and one touched 20 files. I checked that each task's tests failed before the real commit and passed after it. Each model did each task once.
view data
| model | Cost of 10 tasks |
|---|---|
| Haiku | 1.44 USD |
| DeepSeek | 1.5 USD |
| Sonnet | 4.78 USD |
| Opus low | 8.22 USD |
| Kimi K3 | 14.7 USD |
| Opus high | 16.12 USD |
| Opus 5 | 20.6 USD |
| Fable | 26.95 USD |
Every model in that chart passed 10 of 10. Some outside it did not: Gemini 3.8 Flash and GLM-5.3 passed 9, and DeepSeek V4 Pro passed 7. The tasks were too easy to separate the top models on pass rate, so cost, time and code quality decided. From cheapest to dearest is about 19x. Sonnet 5.5 at low effort did the 10 tasks for 58% of what Opus 5.5 low cost. Opus 5.5 low cost about half of Opus 5.5 high, with the same pass rate and a code-quality score of 32 against 31, where lower is cleaner.
The cheap models came with catches. Haiku 5.5 gets 5 times dearer past a 100k-token prompt, and it was slow on long tasks, taking 30 minutes on one where Sonnet took 8. DeepSeek V4 Flash wrote 34-54% more defects than Claude in a blind quality review, so its code always gets a review pass. On AA-Omniscience, a public test rather than one of mine, it gave a wrong answer instead of "don't know" 96.5% of the time. It never writes factual prose for me.
On 10 and 11 Oct I also tried mixing Claude models. Every mix passed 10 of 10, so mixing bought no extra passes, and most of them cost more.
| Setup (all passed 10 of 10) | Cost of 10 tasks | Time |
|---|---|---|
| Sonnet 5.5 low alone | $4.78 | 32 min |
| Opus directs Haiku | $4.89 | 30 min |
| Opus directs Sonnet + Haiku | $6.73 | 26 min |
| Opus plans, Haiku codes | $12.44 (92% of it Opus) | 65 min |
| Haiku low with an Opus advisor | $3.10 | 61 min |
| Same, plus a reminder to consult the advisor | $12.84 | 70 min |
| Opus plans, Sonnet codes | $16.38 | 69 min |
Having Opus plan for a cheaper coder added $8-12 for nothing. The advisor setup was cheap until I added a reminder to consult Opus, after which Haiku consulted it on most turns and the cost went from $3.10 to $12.84. So one cheap model on its own is the default. An Opus lead directing cheaper subagents is worth it only when speed matters.
Opus 5.5 at low effort is the main worker, and the same model at high effort writes anything a person reads and does the final reviews. Sonnet 5.5 or Haiku 5.5 at low effort take coding where a test decides pass or fail. Fable 5.1 only gets the hardest coding. DeepSeek V4 Flash takes bulk work, always followed by a review.
Outside models are called through one script, second_opinion.py, which applies a policy per folder. Confidential folders get Codex only. Open folders also allow DeepSeek, Kimi and OpenCode, and personal folders add Gemini and Grok Build. Any folder not listed counts as confidential. Because the script reads the policy from the working directory, the calling agent cannot pick its own. Outside CLIs run inside a macOS sandbox-exec profile, and Codex uses its own sandbox.
Context size
Long sessions cost more per call. By my estimate, a call at 600-900k tokens of context costs about 3 times one at 200k, $0.38 against $0.13. In the week to 23 Sep, 59% of my Claude spend went on calls past 200k. After I turned on auto-compact at 250k in every account, the share over the four weeks to 24 Oct was 10-14%. That is a before and after, not a controlled test, so I cannot say auto-compact alone caused the drop.
Two smaller findings shaped the rules. At 563k, /compact cost $0.81 and /clear with a written handoff cost $0.69, so I pick the handoff when another session might continue the work. And 10 long agents were 71% of my past Opus usage, so agents stay short. reset.py lets a long session in kitty clear or compact its own context at a break point. It writes a handoff first, and the fresh context starts from it.
Reviews on the diff
Since 21 Oct every code change gets a Codex review before it counts as done. second_opinion.py --review puts the git diff in the prompt with a brief that asks for findings only, so the reviewer stays on the change and does not explore the repo. Before that, in September, 302 Codex reviews through MCP used 267M input tokens, a median of 553k each. Those were Codex tokens on its own plan, which is why reviews cost almost no Claude usage. I have no after-measurement for the diff-only version yet.
To pick reviewers I used 5 real changes whose tests passed but which held 17 bugs that a later review caught. Each reviewer's findings were graded blind.
view data
| reviewer | Bugs found of 17 |
|---|---|
| Opus 5.5 | 10 bugs |
| DeepSeek | 9 bugs |
| GLM-5.3 | 8 bugs |
| Grok 4.7 | 7 bugs |
| Sonnet | 6 bugs |
| Fable | 6 bugs |
| Astra | 5 bugs |
| Kimi K3 | 4 bugs |
| GPT-6.1 | 3 bugs |
Opus 5.5 at high effort found 10 and 9 over its two runs, for about $4.35 per 5 reviews, while Fable 5.1 found 6 and 4 for about $11.80. Opus found 4-5 bugs Fable missed and Fable found none that Opus missed, so Opus 5.5 took over as the Claude reviewer. Codex's reviewer, GPT-6 Astra, found 5, one of which no other model found. It still reviews every change because it runs on Codex's plan and costs almost no Claude usage. Today the review trio is DeepSeek V4 Flash, Opus 5.5 and Astra.
The worst result was on security. Of 5 known bugs in database security rules, 4 went unfound by every model. Those rules still need a person to read them.
What I got wrong, and the limits
- Small samples. The benchmark was 5 tasks with 2 runs per setup, of 3 to 60 steps, not 300-step jobs. The bake-off ran each task once per model on one app. Code-quality scores were one run each and noisy.
- One person's data. My tasks, my app and my habits. Judges also tend to favour their own model family. I would not expect the numbers to carry over to someone else's work unchanged.
- Opus 5, not 5.5. The effort numbers are Opus 5's. The bake-off has Opus 5.5 low at about half the cost of high with the same pass rate, which points the same way, but I have not re-run the five benchmark tasks.
- Some rules are estimates. The cost of long context, the 30-50k setup tokens every agent pays, and the ~7.5k-token skill listing are estimates, and the README marks them as such.
- macOS first. The sandbox uses
sandbox-exec, the memory guard reads macOS stats, and alerts use macOS sounds and kitty. On Linux the memory guard does nothing and the sandboxed routes fail. - Hooks fail open. A broken hook never blocks work, except a deliberate deny, and
claims.pygives up on a lock after 3 seconds. So a broken hook fails silently, and it is worth checking thatclaims.py listworks after install. There are no tests or CI for the hooks yet, though both are on the roadmap. - Prices age fast. Every number here is from September and October 2026.
Trying it
Clone the repo and run install.sh. With no flags it prints its plan and changes nothing.
git clone https://github.com/Clupai8o0/cstack ~/clupai
cd ~/clupai
./install.sh # prints the plan, changes nothing
./install.sh --yes --settings # links rules, skills, output style; merges hooks into ~/.claude/settings.json
export CLUPAI_HOME=~/clupai # add to your shell profileIt replaces ~/.claude/CLAUDE.md with a link to the shared rules and backs up the old file next to it. --no-rules keeps your own file instead. Edit model-policy.json before you call any outside model, since unlisted folders count as confidential. For your own numbers, bakeoff/ is a generic harness that runs a bake-off on your repo's history.
The code is at github.com/Clupai8o0/cstack, and the project page has the short version.
