Skip to content
praveenVnktshPublic

About

Your autonomous build harness. Work with linear boards, not agents.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

116 Commits

Folders and files

Repository files navigation

foreman

Move a card to Todo. Come back to a merged pull request.

Tests Linear Harnesses Runs locally MIT Stars

foreman is an autonomous build loop for your Linear board.
It plans, builds, reviews and merges the cards you pick up, on your own machine, while nobody is watching.

A Linear board run by foreman: cards in In Progress, In Review, Needs Human and Done, each labelled with its project and pull request


How it works

A card you move into Todo is the only dispatch authorisation. Everything after that happens without you.

flowchart LR
  You(["You move a card<br/>Backlog → Todo"]) -->|needs-plan| Plan["Plan<br/><small>draw the change, post it, wait for sign-off</small>"]
  You -->|no label| Build
  Plan -->|signed off| Build["Build<br/><small>fresh worktree · judge size · implement · test · open PR</small>"]
  Build --> Review["Review<br/><small>one light pass by a session that did not write it</small>"]
  Review -->|blocking finding| Fix["Fix<br/><small>once · merges on green checks</small>"] --> Gate
  Review -->|clean| Gate{"Evidence gate<br/><small>CI green · no risky paths</small>"}
  Gate -->|pass| Merge(["Merged · deploy watched · Done"])
  Gate -->|risky path| Human(["Parked for a human"])
Loading
  • Small cards skip planning. A card without the needs-plan label goes straight to a build agent. It judges the size itself: a small change is implemented directly, and a larger one is planned as a graph on the card and then built in the same session. Add needs-plan to a card you want to sign off before anything is built.
  • Review is light. One reviewer reads the diff once and files findings as blocking or note. Only a concrete bug, a security hole, a weakened check, data loss or a broken build blocks. A blocking finding buys one fix, and that fix merges on green checks without a second review.

Every few days a cleanup agent reads main and files one planned card for the quality work that review no longer blocks on.

Why foreman

  • You stay in charge of what gets built. No agent can put work into Todo or take it out of Backlog. A failed card waits for you to re-triage it; it never loops.
  • Nobody reviews their own work. The diff is reviewed by a session that did not write it, and the merge is gated on what GitHub holds now, never on a working tree.
  • It holds no state. Each tick re-derives every card's position from Linear, gh and git. Kill it, restart it or upgrade it mid-build and nothing is lost.
  • It lives outside the project it builds. A repository becomes buildable by adding one board.toml.
  • Know what runs on your host. Build agents run on your machine, in a throwaway git worktree, with permissions bypassed: they run the target's own bootstrap and test command there. The worktree, a per-role deny list, the required checks and the review are what contain them — not a sandbox (see Security). What never runs on your host is CI: it runs on GitHub-hosted runners, and a merged commit reaches this machine only when foreman pulls it. foreman never merges a pull request from outside the repository it builds.

Features

The loop

  • Plan → Build → Review → Merge as Linear columns, each stage a detached agent in its own git worktree. Planning is a separate stage only for cards labelled needs-plan.
  • A light review by a session that did not write the diff: one round, one reviewer, gating only on blocking findings, with one fix.
  • Evidence-based merges: required checks, risky paths from the diff, and deploy step conclusions, read from GitHub.
  • Deploy watching, including queued deploys and a fast-track label copied from the card to the pull request.
  • Scheduled cleanup that files one well-planned card instead of blocking merges.
  • Linear priority orders the queue; boards take turns round-robin.

Operating it

  • One tick, every board — a single foreman serves all your projects, each board with its own concurrency limit.
  • Dispatch to whichever agent works. A stage's model is an ordered list of candidates — opencode:foundry/gpt-5.6-sol, claude:opus — and the tick spawns the first not rate-limited, falling back across models and across CLIs. See Models and fallback.
  • A watchdog (supervise.sh) that restarts a stuck tick and can never dispatch a card itself.
  • Preflight refuses to dispatch when the machine cannot build, without blaming the card.
  • Release-gated updates. A merge to main cuts a GitHub Release — unless the commit says Release: skip — and foreman follows the latest one.

Quick start

You need git, gh, Python 3, a Linear API key, a Linear MCP config, and one supported harness.

git clone https://github.com/praveenVnktsh/foreman.git ~/.foreman/install
~/.foreman/install/bin/install.sh --harness claude
~/.foreman/install/bin/install-skills.sh
install -m 600 ~/.config/linear.key ~/.foreman/linear.key
cp path/to/linear-mcp.json ~/.foreman/mcp.json
~/.foreman/install/bin/boardctl add myproject --repo ~/Developer/myproject

Then keep it running and up to date with systemd user timers (Linux; on macOS use launchd or cron):

~/.foreman/install/bin/install-service.sh
~/.foreman/install/bin/install-self-update.sh
~/.foreman/install/bin/install-dashboard.sh

The dashboard is one page saying what the machine is doing — what is stuck, what is running, whether the host is fit — plus a box for sending a message the tick reads on its next pass. It binds loopback; tailscale serve --bg --set-path /foreman 8429 puts it on your tailnet and nowhere else.

The clone goes at ~/.foreman/install and nowhere else: foreman derives its home from where the clone sits, so a clone one directory deeper puts the home somewhere the linear.key and mcp.json above are not.

install-skills.sh is required: the tick runs /board, and the harness only finds skills in its own skills directory. The release branch, the rate-limit fallback and the other harnesses are covered in docs/INSTALLING.md.

Models and fallback

One foreman serves every board. Each stage names its model in foreman.toml, and [fallback] lists the models it may fall down through, strongest first. A tier is model or harness:model, so the tick falls back across providers and across CLIs:

[models]
plan = "opencode:foundry/gpt-6-astra"
build = "opencode:foundry/gpt-5.6-sol"

[fallback]
tiers = ["opencode:foundry/gpt-6-astra", "opencode:foundry/gpt-5.6-sol", "claude:opus"]

[fallback.floor]
plan = "claude:opus"          # the weakest model the plan stage may reach

dispatch.sh spawns the first tier whose model is not marked rate-limited, never below the stage's floor. A model is marked when a spawn refuses it, or when an agent dies mid-run on an API rate-limit error, and unmarked when the cooldown passes. Because one tick may spawn on more than one harness, the agent registry ($HARNESS_SH list) merges every harness it can use. The design is in docs/specs.

Make a repository buildable

Add a board.toml at its root. Only [linear], [checks] and [test] are needed; everything else is optional.

[linear]
team = "My Team"                  # the team NAME, not its key
project = "myproject"

[checks]
required = ["Tests"]              # job names that must pass
ci_workflow = "CI"                # the workflow's `name:`

[test]
command = "tests/run-all.sh"

[risk]
paths = ["migrations/"]           # a diff touching these waits for a human

[docs]
required = ["STYLEGUIDE.md"]      # every agent reads these first

[limits]
max_concurrent = 1

foreman builds itself: this repository's own board.toml is a working example. The full contract is in the design spec.

Architecture

flowchart LR
  Cron(["cron / systemd"]) --> Sup["supervise.sh<br/><small>watchdog</small>"]
  Sup --> Tick["tick agent<br/><small>/board skill</small>"]
  Tick --> Rec["reconcile<br/><small>Linear · gh · agents</small>"]
  Rec --> Dis["dispatch<br/><small>worktree · brief · spawn</small>"]
  Dis --> Agents["plan · build · review · cleanup<br/><small>detached agents</small>"]
  Agents --> GH[("GitHub<br/>PRs · checks · deploys")]
  Rec -.->|next tick re-derives| Rec
Loading

One tick end to end, with every file it touches, is drawn in docs/board-flow.md.

Documentation

Read For
docs/INSTALLING.md Install, models and fallback, releases, migration
docs/board-flow.md What one tick does, file by file
docs/specs/ Why the loop is shaped the way it is
AGENTS.md The map for anyone, or any agent, changing this code
STYLEGUIDE.md How code and prose are written here

Security

foreman is public, so CI runs only on GitHub-hosted runners and a merged commit reaches your machine only when foreman pulls it. foreman never merges a pull request from outside the repository it builds, and it pins every merge to the one head the board judged, so a head that moved since is never merged. Build agents do run the target's bootstrap and tests on your host; see Why foreman for what contains them.

The deny list is a partial layer. Every card agent runs as your user with permissions bypassed. skills/board/dispatch.sh gives each one Claude Code permissions.deny rules, which Claude Code applies even in bypass mode.

  • What it blocks: Claude Code's file-editing tools, and the Bash commands it recognises as file writes (redirections, tee, sed -i), on foreman's install, boards.toml, foreman.toml, every *.key, the inbox, each board's HALT, ids.env and last-cleanup, every history.jsonl, and every tracked top-level entry of the board's main checkout with its .git/config and .git/hooks/. Plan, build and cleanup agents are also kept off every card's cards/ directory, so no author can write its own review. A reviewer is not, because it writes its review there.
  • What it does not block: any other program the agent runs. python3 -c, a test script or a build step still writes wherever your user can.
  • Claude only. The Codex and OpenCode adapters drop --settings, so their agents get no deny list.

Full confinement means running the agents as a separate unix user that cannot write foreman's files. That is an infrastructure choice for the operator, and foreman does not make it for you.

Contributing

Issues and pull requests are welcome. Start with AGENTS.md and run tests/run-all.sh before opening one.

License

MIT. Extracted from a board orchestrator that grew up inside one private project, and generalised until nothing about that project remained.

About

Your autonomous build harness. Work with linear boards, not agents.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages