Skip to content

Your own project

GoalPoint the harness at your repository and tune it to your stack.

Add your repository

harness projects add ~/code/my-app --name my-app
harness projects list

Add a project on the dashboard's Projects page does the same. projects add registers the repository in $HARNESS_HOME/projects.json, which is ~/.harness/projects.json by default. It creates this in your repository:

PathWhat it isIn git
.harness/config.jsonthe project's settingsyes, versioned
.harness/tasks/one Markdown file per taskno
.harness/trace/the evidence every unit leavesno
.harness/JOURNAL.mdnotes agents leave for the next unitno

It appends those paths, .harness/artifacts/ and .env to the project's .gitignore. A folder counts as a harness project only when it has .harness/config.json. harness projects remove <id|name> unregisters a project and never deletes files.

Then create the first task with harness new --title "…" --project my-app, or with New task on the dashboard.

config.json

projects add writes only the keys you are likely to change:

{
  "baseBranch": "main",
  "autonomy": {
    "mode": "gated"
  },
  "limits": {
    "bounces": 2,
    "prototypeRounds": 3,
    "unitTimeoutMinutes": 60
  },
  "gate": null,
  "probe": null,
  "deliver": {
    "pr": false,
    "remote": "origin"
  }
}

Every other key keeps its default in code, so a changed default reaches every project.

KeyDefaultWhat it doesMore
baseBranchmainthe branch every task branch starts fromTask lifecycle
autonomy.modegatedgated: a plan and a prototype wait for you; full: they move onTask lifecycle
limits.bounces2how many rejections park a task for youContracts
limits.prototypeRounds3the most prototype rounds per taskTask lifecycle
limits.unitTimeoutMinutes60a unit that runs longer is a hard stopContracts
gatenullthe project's check command; a task's own gate: winsContracts
probenulla command that must exit 0 before a unit startsContracts
deliver.pr, deliver.remotefalse, originpush the branch and open a pull request for a done taskTask lifecycle

Keys you can add when you need them:

KeyDefaultWhat it doesMore
roles.modelscoordinators sonnet, judge opusthe model per role; the judge must differ from the implementer and the testerRoles
roles.mergeQaJudgefalseone review unit per task instead of twoRoles
notify.eventsall sevenwhich Telegram events to sendRunning the loop
agent.commandnulla command that replaces claude, like AGENT_CMD
agent.permissionModedontAskclaude --permission-mode: refuse what is not allowed instead of waiting
agent.allowedToolsa listclaude --allowedTools; read-only grep, cat, head, tail and wc, and sleep, are in; commits and pushes are absent on purpose
agent.settingSourcesproject,localclaude --setting-sources: your personal hooks never fire inside the loop
supervisor.*in codepoll and wait intervals, and barrenUnits: 3Running the loop
appnullstart the project's app, and a browser, for the qa and prototype units, described below

Your app for qa and prototype units

Set app when a unit should see your project running. The runner starts the app, never the agent:

"app": { "start": "pnpm exec next dev -p {port}" }
KeyDefaultWhat it does
app.startrequiredthe command that starts the app; without {port} the config is refused (exit 2)
app.ready"/"the path the runner polls until the app answers
app.timeoutSeconds60how long the runner waits for that answer
app.host"localhost"the host in the address the units get; Next 16's dev server serves its live-reload socket only on localhost, so a page on 127.0.0.1 never hydrates
app.stages["qa", "prototype"]the unit stages that get the app and the browser
app.browser"@playwright/mcp@0.0.83"the Playwright MCP package the browser runs through npx, pinned

The example project sets "app": { "start": "pnpm exec next dev -p {port}", "ready": "/" }, so its qa and prototype units get the app at http://localhost:<port>.

Before each unit of those stages, the runner picks a free port, starts the command in its own process group and waits for the app. The unit gets the address in HARNESS_APP_URL and as a line in ## This run, which also tells the agent not to start or stop a server. After the unit, whatever its outcome, the runner stops exactly that process group. If the app does not start, the task's Log says why and the unit runs without it. A run also stops an app whose runner died, and logs it.

The runner starts the app before it records the tree for the scope contract, so files the server writes at start are not the unit's. It also removes the AI-agent markers from the app's environment: Next.js 16.4 writes AGENTS.md into a project when it detects an agent, and that stray file would park a read-only unit. NODE_ENV and Next's own internals (__NEXT_*, NEXT_PRIVATE_*, NEXT_RUNTIME, NEXT_DEPLOYMENT_ID, TURBOPACK*) go too, so an app started from the dashboard's supervisor runs as a plain next dev. A tracked file the app rewrites while it runs is checked out again after it stops, and the task's Log names it.

The browser

A unit of an app.stages stage also gets a browser, but only when its app started: no app, no browser. Every other unit gets no MCP server at all. The runner writes .harness/trace/unit-NNNN.mcp.json and passes it with --mcp-config … --strict-mcp-config, adds mcp__playwright to the allowlist, which allows every tool of that one server, and sets HARNESS_MCP_CONFIG. The server runs as npx -y <app.browser> --headless --isolated --browser chrome, so npx must be on PATH: harness agents --check warns when it is not, and its exit code stays the same. harness preview shows these flags for an app stage.

The stage's coordinator passes the app's address to its specialist, because a specialist sees only its dispatch prompt. A specialist that lists its tools gets mcp__playwright in the --agents JSON; the judge never does.

Playwright MCP saves a named screenshot in the project root, not in the artifacts folder. After a browser unit, the runner moves new untracked png, jpg and webp files from the project root into .harness/artifacts/T-NNN/ and notes each one in the task's Log. A unit without a browser still parks on such a file as a stray file.

Your own specialist

A specialist is one Markdown file. Put it in <project>/.claude/agents/. The harness merges that folder with its base agents, and a file with a base agent's name replaces that agent.

---
name: a11y-reviewer
description: Checks every changed screen for keyboard access and contrast, and reports what fails.
model: sonnet
tools: Read, Grep, Glob
stages: [qa]
when: in the qa stage of a `kind: ui` task
---

You review accessibility. You change nothing.
FieldWhat it means
namethe agent's name; the file name when it is missing
descriptionrequired: one line the coordinator reads to decide whether to call it
modelits model; roles.models.<name> overrides it
toolsthe tools it may use
stagesthe stages that offer it; without stages, every stage offers it
whena hint for the coordinator: in which tasks to call it

Then add the standing rules to the file and check the registry:

harness agents --sync --project my-app
harness agents --check --project my-app

The coordinator of each stage reads the offered specialists and calls the ones the task needs.

Environment variables

VariableWhat it changes
HARNESS_HOMEwhere the project registry and the fallback .env live; ~/.harness by default
HARNESS_AGENTS_DIRthe folder of base agents; harness-demo's agents/ by default
AGENT_CMDa command that replaces claude for every unit; the fake agent uses it
HARNESS_GHa command that replaces gh for delivery
TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_IDTelegram notifications, described on Running the loop

Before the first real run

  1. The base branch is clean and up to date. The supervisor refuses a dirty tree it did not make.
  2. The gate passes when you run its command yourself.
  3. harness agents --check ends with ok.
  4. harness preview T-NNN shows the prompt you expect.
  5. The first run is short: use --once-through or --max-units <n>, and keep the dashboard open.
NextLessons→
Guide10 chapters · ~55 min
  1. 1Concepts
  2. 2Roles
  3. 3Coordination
  4. 4Task lifecycle
  5. 5Running the loop
  6. 6Contracts
  7. 7Dashboard
  8. 8Quickstart
  9. 9Your own projectyou are here
  10. 10Lessons
OverviewDemoExampleGitHub