One task, one track, across every repo it touches, with any agent.
Each task gets its own track: one git worktree per repo, a shared branch, and a generated AGENTS.md. Main clones stay untouched. The agent stops at two gates, and you decide.
$ brew install rans0/tap/siding
siding approve
main line never blocked
Any agent that runs shell commands can use it. Adapters only add conveniences.
TOML config. Markdown docs and knowledge. JSON track state. No database, no daemon.
The single hard dependency. gh, node and agents are optional.
Agent stops after the PRD, and again with the uncommitted diff.
Stages run prd → GATE 1 → build → GATE 2 → ship. The agent runs siding submit and stops. Only you run siding approve; in Claude Code, Codex, Cursor and OpenCode a hook enforces it.
Every task is a folder with one worktree per repo and the same branch name in each. Several tracks run at once, each in its own terminal, on its own ports.
~/work/
├── siding.toml # workspace config
├── repos/ # main clones, never edited
│ ├── backend/
│ └── web/
├── tracks/add-promo/
│ ├── AGENTS.md # rules, repos, skills for this stage
│ ├── .track.json # stage + base commit
│ ├── backend/ # worktree · rans/add-promo
│ └── web/ # worktree · rans/add-promo
├── docs/2026-10-18_add-promo/
│ └── prd.md plan.md testing.md log.md
└── knowledge/
├── rules/ skills/ repos/
└── inbox/ # lesson proposals
Worktrees branch off repos/. A hook blocks edits there. No git checkout between tasks.
Rules, worktree paths, branches, repo facts and the skills for the current stage. Regenerated when the stage changes.
Existing branches are reused. done checks everything first and refuses on uncommitted or unpushed work.
Each worktree gets a .env with env_ports shifted by slot × port_stride and its own COMPOSE_PROJECT_NAME.
done, the agent writes up to retro_max lesson proposals to knowledge/inbox/, one Markdown file each.siding learn or the board's Inbox: accept, edit, skip or delete, one at a time.knowledge/repos/<repo>.md with a verified commit marker. skill: and rule: proposals become skills or rules.AGENTS.md. siding doctor flags facts whose paths no longer exist.Quick reference below. The full README covers adopting an existing workspace, epics with siding next, hotfixes and deep reviews.
$ siding init ~/work && cd ~/work $ siding repo add git@github.com:acme/backend.git $ siding repo add git@github.com:acme/web.git $ siding adapter install claude # or: codex, cursor, opencode, agents $ siding new add-promo backend,web # tracks/add-promo/{backend,web} $ siding open add-promo # agent in this terminal, also live on the board $ siding ui # board at http://127.0.0.1:4747 $ siding approve add-promo # you pass Gate 1, later Gate 2 $ siding pr add-promo # commit, push, one PR per repo $ siding done add-promo # after log.md is written
Something looks wrong? Run siding doctor.
| Command | What it does |
|---|
# siding.toml [defaults] base = "origin/main" retro_max = 5 port_stride = 100 [repos.backend] base = "origin/develop" # optional, overrides defaults.base setup = "go mod download" # runs in each new worktree env_ports = ["PORT=8080", "DB_PORT=5432"] # .siding.local.toml (per machine, gitignored) branch_prefix = "rans" # branches become rans/<task> ref_only = ["docs-site"] agent = "claude" # what `siding open` starts
Required tools and skills are pinned under [required.*] and fetched by siding adapter install. siding new refuses while any is missing.
| Harness | Support |
|---|---|
| Claude Code | CLAUDE.md, /siding slash command, hook blocking edits in repos/, ponytail plugin inside tracks only |
| Codex | .agents/skills/siding skill, .codex/hooks.json guard |
| Cursor | /siding command, .agents/skills/siding skill, .cursor/hooks.json guard |
| OpenCode | /siding command, .opencode/plugins/siding-guard.js guard |
| Any other agent | Instruction-only through AGENTS.md (siding adapter install agents) |
siding is MIT licensed and small on purpose: Go stdlib first, plain files, git as the only hard dependency. Issues and PRs welcome.
git clone https://github.com/rans0/siding
cd siding
go build ./cmd/siding
go vet ./... && go test ./...
siding init /tmp/ws # scratch workspace
t.TempDir(), no network.cmd/siding: flag parsing and output onlyinternal/core: everything else, shared by CLI and boardinternal/ui: board handlers, terminal websocketweb/: board templates and CSStemplates/, starter-knowledge/: embedded into the binaryfilepath.Join, JSON via json.Marshal.Keep the agent PTY alive after the starting terminal closes.
Today the board shows no terminal on Windows.
Add an entry to adapters, handle its hook in Guard, cover it in TestGuardPayloads and TestAdapterFiles.