◆ the manual · operate your own board end to end
Run an agent company, end to end.
The landing page is the pitch. This is the manual: install Dais, stand up a workspace, author the state machine your agents move work through, and operate the two gates that keep you in control. Every command on this page runs. Nothing invented, grounded in the tool that shipped this site.
00 · orient
One tool, many workspaces, one board.
Four ideas carry the whole system. Hold these and everything else is detail.
The tool vs. your workspace
DAIS_ROOT is where the tool lives (the dais binary + harness/, resolved automatically even through a PATH symlink). DAIS_HOME is your workspace: the folder holding projects/ and the board. One installed tool drives any number of workspaces.
The board is the truth
Everything lives in one SQLite file, dais.db: a tasks table (the work) and a runs history (every agent invocation). No server, no hidden state; dais status just reads the board.
Roles are config, agents run headless
A project declares who exists: lead, engineer, qa, and you, the founder. Each role is one agents/<role>.md file: config in the frontmatter, persona below. An agent runs headless on Claude by default, or on OpenAI's Codex when a role sets provider: openai. The founder is a human actor the machine parks on, never dispatches.
A task is the only atom. An epic, a bug fix, a proposal, a release: all tasks. A task advances through states by firing edges, and its state is whose turn it is. That's the entire model; the rest of this page is how you drive it.
DAIS_HOME resolves from the env var, else ~/.dais/config, else falls back to DAIS_ROOT, so a fresh clone runs self-contained until you point it somewhere.
01 · install
Install, init, point it.
Requirements: sqlite3, python3 (stdlib only), the Claude Code CLI (claude) for the agents, and gh for opening PRs. Runs on macOS (bash 3.2+) and Linux.
$ brew install eigensoftware/dais/dais # or: brew tap eigensoftware/dais && brew install dais $ dais init ~/mycompany # workspace: dais.yaml + CONTEXT.md + board + projects/ $ mkdir -p ~/.dais && echo "home=$HOME/mycompany" > ~/.dais/config # make ~/mycompany the default DAIS_HOME
Dais drives the Claude Code CLI, so install that separately (and OpenAI's codex CLI if any role sets provider: openai); brew install alone won't run agents. dais init is idempotent: re-run it to backfill anything missing.
From source (or to track main): clone and symlink the launcher onto your PATH, a pointer, not a copy.
$ git clone https://github.com/eigensoftware/dais ~/dais $ mkdir -p ~/.local/bin && ln -s ~/dais/dais ~/.local/bin/dais # dais on your PATH (a pointer, not a copy)
~/.local/bin must be on your PATH. Prefer a self-contained install? Run dais init ~/dais and skip the config line; with no config, DAIS_HOME falls back to the clone.
02 · first project
Scaffold a project, lint it, make it yours.
A workspace holds projects. Scaffold one from a stock template, validate it, and inspect what it will run. Four ship with the tool: coding (the full lifecycle this walkthrough uses), lean (one builder, your review is the gate), advisory (decisions, not code; zero outward edges), and marketing (a content lane).
$ dais scaffold myapp --template coding # roles + personas + a copy of the coding machine $ dais lint myapp # validate config, machine, and required files $ dais project myapp # what myapp runs: cast, models, dispatch map
Then make it yours by editing the files under projects/myapp/:
agents/*.md: the cast. Each file is one role: config in the YAML frontmatter (model,effort,provider,auth,trigger,playbook), persona prose below.machine.json: the lifecycle, and therolesblock that owns each role's access (draft / edit / review).project.yaml: repo, workspace-wide defaults (model, provider, playbook), the stage goal agents steer toward.CONTEXT.md: project memory, read first on every run (north star, targets, decisions, gotchas).
The full prompt an agent sees escalates in specificity: workspace CONTEXT → project CONTEXT → role playbook → role persona. Keep durable truth in the CONTEXT files and craft conventions in the playbook, and the personas stay short.
Providers: Claude or Codex, per role. Every role runs on Claude by default; set provider: openai in a role's frontmatter to run it on OpenAI's Codex instead, so a mixed org works (Codex QA reviewing Claude-built code). auth: subscription (the default) uses the provider CLI's own login; auth: api is metered and reads the provider's key from your environment or a gitignored .env, never from a config file.
03 · the machine
States are positions. Roles are actors. Edges are the only way to move.
This board runs the coding machine: authored JSON, fully editable. A task's state is whose turn it is, and the scheduler dispatches the single role with an outgoing edge from that state. You never set a status. You fire an edge:
front door back door proposed ─▶ ◆ proposal_review ─▶ ready ─▶ doing ─▶ qa_review ─▶ approved ─▶ ◆ greenlight ─▶ done you approve engineer qa (pool) you ship ▲ └─ fail ─▶ blocked ─▶ (spawns a fix) └─ promote ─▶ ready (the routine lane: follow-ups of approved work skip the gate)
Advancing is always the same shape. Ask a task what you can fire, then fire it:
$ dais edges dai-14 dai-14 [ready]: claim by engineer -> doing defer by founder -> deferred cancel by founder -> cancelled {confirm} $ dais fire dai-14 claim # engineer takes it → doing dai-14: ready --claim--> doing
The fire is atomic and a compare-and-swap: a transition and all its effects commit together or roll back together, and two racing fires can't both apply. Guards that need proof take a flag: --confirm, --typed <id>, --attest <fact>, --verify <check>.
Every edge in the coding machine
| from | verb | to | actor | guard / effect |
|---|---|---|---|---|
| proposed | submit | proposal_review | lead | · |
| proposed | promote | ready | lead | the routine lane: follow-ups of approved work skip the gate |
| proposal_review | approve | done | founder ◆ | confirm · spawns impl task |
| proposal_review | request_changes | proposed | founder | · |
| proposed | reject | cancelled | founder | confirm |
| note | acknowledge | done | founder | read it and close it; an FYI never becomes a build |
| ready | claim | doing | engineer | · |
| ready | needs_design | design | engineer | send it down the design lane before building |
| design | design_done | ready | designer | hands the spec back to the build queue |
| design | review_done | done | designer | a review-only pass, nothing left to build |
| design | skip_design | ready | founder | overrule the detour, build it as filed |
| design | invalidate | cancelled | designer | attest:invalid |
| design | defer | deferred | founder | park it mid-design |
| doing | complete | qa_review | engineer | opens a PR |
| doing | interrupt | ready | system | orphan-reconcile, automatic |
| qa_review | pass | approved | qa | verify:tests_pass |
| qa_review | fail | blocked | qa | spawns a fix task, blocks parent |
| blocked | unblocked | qa_review | system | when the fix is terminal |
| approved | released | done | system | fired by a release task's effect |
| approved | cancel | cancelled | founder | confirm · pull work out of the pool before it ships |
| release_open | assemble | release_review | engineer | aggregates the approved pool |
| release_review | greenlight | releasing | founder ◆ | typed_confirm · attest:migration_reviewed |
| release_review | abort | cancelled | founder | confirm |
| releasing | shipped | done | engineer | runs release · fires each child → done |
| releasing | release_error | release_failed | system | spawns a rollback task |
| release_failed | retry | release_open | founder | re-opens the release |
| release_failed | give_up | cancelled | founder | confirm |
| proposed / ready | defer | deferred | founder | park; undefer returns it |
| deferred | undefer | @history | founder | back to whichever state it was parked from |
| ready · deferred | cancel | cancelled | founder | confirm |
| ready | invalidate | cancelled | engineer | attest:invalid · the one edge an agent may cancel: retire a false-premise task (duplicate, moot, defect that doesn't exist) |
Actors: roles run headless, ◆ founder is you, system is automatic (an interrupt rewind, an unblocked dependency, a release effect). Only role edges are dispatched by the scheduler.
The design lane. Not every ready task should go straight to code. When an engineer picks one up and finds the shape is undecided, they fire needs_design instead of claiming it, and the task moves to design where the designer role owns it. The designer either fires design_done to hand a decided spec back to ready, or review_done when the pass was a review and there is nothing left to build. You can overrule the detour at any point with skip_design, which puts the task back in the build queue as filed. It is a real lane with a real role, not a status label: the same rule holds that a task in design is dispatched to the designer and nobody else.
The note state. note is for things filed at you rather than for you to build. An agent files what it learned or noticed, you read it, and you fire acknowledge to close it out. No impl task gets spawned, which is the whole point: an FYI that cannot quietly turn into work.
04 · the two gates
Two diamonds you own. Everything between runs itself.
Front door: ◆ proposal_review. A lead files a new direction as a proposed task, fleshes it to a spec, and fires submit. It parks at proposal_review until you fire approve, which spawns the implementation task(s). Nothing new gets built until you approve the direction. request_changes bounces it back; reject cancels it.
Back door: the ◆ release greenlight. QA-passed work parks in the approved pool. A release task sweeps it up (assemble) and waits at release_review for you to fire greenlight, guarded by typed_confirm and attest:migration_reviewed. Those are strong-human guards: no auto-approver can forge them, which is exactly what makes shipping structurally un-automatable.
$ dais edges dai-1 dai-1 [proposal_review]: approve by founder -> done {confirm} ⟶ spawns impl request_changes by founder -> proposed $ dais fire dai-1 approve --confirm dai-1: proposal_review --approve--> done · spawned dai-2 [impl] → ready
In dais top, waiting gates surface as a yellow ◆ NEED YOU token in the top bar and a PROPOSAL REVIEW ◆ you band: the only work the cockpit ever flags as yours, not an agent's.
05 · operate
The daily loop: look, fire, repeat.
Two ways to drive. By hand, you read the board and fire edges yourself. Or you let the loop dispatch agents for you. Either way, every agent does one well-scoped unit of work per run and then stops, so the board moves in small, inspectable steps, never a runaway.
Read the board
dais status: a one-glance dashboard of what's running now, what's waiting on you, the queues, and recent runs.dais top: the live cockpit (master-detail TUI); the primary way to operate.dais tasks <project> [--status S] [--assignee R]: filter the task list.dais edges <id>·dais actions <id>: what can fire from a task, and the exact command for each.
Move the work
dais fire <id> <verb>: advance a task by firing a declared edge (the only way state changes).dais task add <project> "title"·dais task set <id> …: file work and edit metadata (priority, notes, PR, dependencies).dais start <id>: run the role that owns this task's state right now (bypasses pause).
The loop
dais watch [secs]: the continuous auto-dispatcher; each tick runs the most valuable pending task, then stops.dais tick [project]: run exactly one scheduling tick (the router picks who runs).dais pause/dais resume: park or un-park the loop;dais schedule installruns it in the background (launchd / cron).
06 · releases
Batch the approved pool. You greenlight. The engineer ships.
A release is just another task, entering at release_open. The engineer fires assemble, which aggregates every task in the approved pool into it: the set it will close. It parks at release_review with the engineer's audit notes (merge order, what was verified per PR, the migration plan). You fire greenlight; the engineer fires shipped, which runs the release script and fires each encompassed task's edge to done, all in one atomic transition.
merge == deploy vs. merge ≠ deploy. On a project where a merge to main is the deploy (like this very site, which Cloudflare Pages builds from main), the greenlight is the prod gate; there is no second step. Where merge ≠ deploy, the release runs a separate deploy per the repo's runbook, still founder-gated. If shipping fails, the task drops to release_failed and spawns a rollback; the machine has the unhappy path too.
greenlight needs a phrase you type and a fact you attest, a release can't ship itself while you sleep. The board can assemble, audit, and stage a release autonomously, but the last inch is always your hands.07 · your own machine
Author states, edges, guards. Lint keeps it coherent, never sets policy.
The stock machines are starting points, not molds. A machine is JSON with three parts: states (tags: initial / terminal / pool / band), edges (from, to, one by actor, a verb, optional guards, optional effect), and checks (named shell commands a verify: guard runs). Copy it per project and draw the graph you want.
The guard vocabulary is closed. Compose these, don't invent:
confirm: a click. Weak; an auto-approver can satisfy it.typed_confirm: a human types a phrase. Strong.attest:<fact>: a human asserts an unverifiable fact. Strong.verify:<check>: an automatic check (tests, no-conflict, brand_voice) viachecks.<name>; absent that command it fails closed unless the firing role self-asserts with--verify.role:<r>: actor authorization.
The two strong-human guards are the whole protection mechanism: an outward edge carrying one is structurally un-automatable. Danger is declared per-edge, not coded per-action.
Effects fire edges, they never poke state. spawn creates tasks, aggregate pulls tasks into encompasses, script runs a merge/deploy/publish, then fires an edge on related tasks. A release closing eight children does it by firing eight real → done edges, each still subject to that edge's guards, so even spawned and aggregated work stays inside the machine and stays lintable.
Lint blocks only on incoherence, never policy: dangling references, a non-terminal state with no way out, ambiguous dispatch, a missing initial or terminal. Everything safety-flavored is a warning you can wave off. Green errors ⇒ build whatever shape you want.
Playbooks & new roles. Working conventions bind to the role, not the universal prompt, so one harness spans code, legal, and content; set a role's playbook or a project default. And you can let Claude draft a role for you:
$ dais role new myapp --desc "reviews copy for brand voice before it ships" # proposes a persona + routing (access / verb-ownership / playbook / precedence) # you confirm; dais lint guards that each state has exactly one dispatchable owner
08 · economics
No metered bills by default. The real budget is the window.
By default, agents run headless on the AI subscription you already pay for: your Claude plan, or a ChatGPT plan for roles you set to provider: openai. That default means no per-token API bill, so raw cost isn't the constraint. The real budget is the plan's rolling window, which is precisely why every role does one well-scoped unit of work per run and then stops.
Metering is an explicit opt-in: a role that sets auth: api runs against the provider's API key (ANTHROPIC_API_KEY or OPENAI_API_KEY) instead of the CLI login, and is billed per token. Nothing meters unless a role's frontmatter asks for it, so you reach for API auth deliberately, role by role.
Frugality lives in the machine (small steps, gated hand-offs, one agent per tick), not in a memo nobody reads. A board that ships steadily and never surprises you beats one that burns a window doing something you never approved. The gates make that trade for you, structurally.
▸ stand one up
Four commands from install to running company.
$ brew install eigensoftware/dais/dais $ dais init ~/mycompany $ dais scaffold myapp --template coding $ dais watch # lean back. gate what leaves the building.