# @markjaquith/agency

Agency manages durable agentic work across repositories. Epics, tasks, and
phases live as Markdown documents in a filesystem-backed workbase. Repository
aliases and managed workspaces provide each execution unit with the code it may
read or write.

## Requirements

- [Bun](https://bun.sh) 1.3 or newer (1.4 or newer on Windows ARM64)
- Git
- [GitHub CLI](https://cli.github.com/) for `agency pr`
- OpenCode, Claude Code, or a configured agent for `agency work`

## Installation

```bash
bun install -g @markjaquith/agency
```

For development, run `bun link` from this repository.

Package with lifecycle scripts enabled (`bun pm pack` or `npm pack`). The
`prepack` hook compiles the interactive Solid UI because OpenTUI's runtime JSX
transform skips files installed under `node_modules`. The `postpack` hook removes
the generated sibling so subsequent development runs use the source UI.

## Local Usage Logging

Agency records privacy-safe CLI usage events locally so command journeys,
failures, and flag adoption can be analyzed. Events are stored in SQLite at
`$XDG_STATE_HOME/agency/usage.sqlite3` (or
`~/.local/state/agency/usage.sqlite3`) and retained for 90 days by default.
Each event contains the parser-derived command and subcommand path, flag names,
timing, a bounded outcome code, Agency version, invocation source, explicit test
attribution, and ordered journey correlation. Journey IDs are one-way hashes of
`AGENCY_SESSION_ID`; raw session IDs, positional arguments, entity IDs, paths,
flag values, free-form input, and the current directory are never recorded.

Export events as JSON Lines with `agency usage export`. Set
`AGENCY_NO_USAGE_LOG=1` to opt out, `AGENCY_USAGE_RETENTION_DAYS` to change
retention, or `AGENCY_USAGE_DB` to select a different database path. Expired
events are pruned on every read and write. Set `AGENCY_INVOCATION_SOURCE` to one
of `human`, `agent`, or `automation`, and set `AGENCY_USAGE_TEST=1` for explicit
test attribution. Logging is best effort and never changes command output or
exit behavior. Databases created by versions before this privacy boundary are
cleared because their command paths may contain positional values.

## Core Model

- A **workbase** is the root containing durable documents and local repository
  state.
- An **epic** orchestrates tasks, may inspect repositories, and never writes
  code.
- A **task** describes one durable outcome and may stand alone or belong to an
  epic.
- A **phase** belongs to a multi-phase task and represents one PR or intended
  PR.
- An **execution unit** is either a single-phase task or a phase. It has exactly
  one writable `repo`, optional read-only `repos`, a branch, a base, and a
  `string | null` PR URL.

Entity IDs come from directory names. Structured metadata lives in YAML 1.2
frontmatter; prose below it supplies human and agent context.

New epic, task, and phase documents use the same core prose sections:
`Outcome` states the intended result, `Plan` describes the current approach, and
`Important Decisions` preserves consequential choices and their rationale. These
sections are creation defaults rather than validation requirements, so existing
and customized documents remain valid.

## Workbase Layout

```text
workbase/
  AGENTS.md                # optional user-owned workbase instructions
  .agency/
    AGENTS.md              # managed Agency instructions
  .opencode/
    opencode.jsonc         # managed planning agent, instructions, and reference
    tui.jsonc              # managed TUI plugin registration
    plugins/agency-repository-skills.ts # managed workbase access and checkout skills
    tui/agency-debug.ts    # managed /agency-debug TUI diagnostic
  agency.json              # tracked config and portable repository declarations
  repos/                   # ignored local materializations
    frontend/              # bare Git repository or symlink
    backend/
  epics/
    checkout/
      EPIC.md
  tasks/
    refresh-copy/          # single-phase task
      TASK.md
      code/                # created by agency work
        frontend/
    build-checkout/        # multi-phase task
      TASK.md
      phases/
        backend-api/
          PHASE.md
          code/
            backend/
        frontend-ui/
          PHASE.md
          code/
            frontend/
            backend/
```

Agency keeps discovery and other observational commands read-only. Run
`agency integration status` to inspect `.agency/AGENTS.md`, the managed
OpenCode configuration and plugins, then `agency integration sync` to create
missing files or refresh checksum-safe
managed files. Customized files are reported but never overwritten. Sync also
removes checksum-valid retired managed artifacts while
preserving customized files at their former paths. The root
`AGENTS.md` is user-owned and is not inspected or modified by Agency.

The managed `.agency/AGENTS.md` is the complete in-workbase operating contract
for agents. It is created by `agency init`, updated by checksum-safe integration
sync, and requires no separately installed Agency skill. CLI help remains the
source of truth for exact command syntax; this README provides the detailed
product and protocol reference.

When upgrading an existing workbase, synchronization moves a checksum-valid
Agency-managed root `AGENTS.md` to `.agency/AGENTS.md` once the OpenCode config
can load the hidden file. A customized root file, including a symlink, is
preserved as user-owned content.

The OpenCode config loads Agency's hidden instructions in addition to any
user-owned root `AGENTS.md`, advertises the complete workbase as one portable
reference, and replaces the built-in Plan agent with `agency-plan`. That
planning agent can update `TASK.md`, `PHASE.md`, and `EPIC.md`, inspect the
workbase through read-only Agency commands, and use explicit Agency CLI
permissions to create or update planning structure. Its normal research tools
and the complete Agency CLI remain available; managed Agency instructions and
reported authority govern each operation.
The TUI-only `/agency-debug` command reports TUI companion initialization and
whether the server plugin registered writable-checkout skills. It uses a native
toast and does not submit a prompt to an LLM. When no writable checkout skill
directory is available, server initialization is reported as indeterminate
rather than inferred from plugin discovery.
OpenCode discovers the config and plugin from Agency launch directories and
their ancestors.
The plugin uses OpenCode's plural discovery directory and exports both the V1
server function and the `opencode2` module wrapper. Integration sync migrates a
checksum-valid legacy singular-path plugin and preserves customized files.
The plugin grants whole-workbase access dynamically, while the portable
reference advertises that context to agents. Bash and Agency operations must
still follow the write authority reported by `agency context`.

Agency's package lifecycle installs one Pi extension at
`~/.pi/agent/extensions/agency.ts`. Pi loads it globally, so it works from
workbase roots and nested epic, task, phase, or checkout directories without
project trust or an explicit `--extension` flag. Outside an Agency workbase it
registers no resources and changes no prompt. Inside one, it loads the managed
Agency instructions, advertises the complete workbase, and exposes skills from
the writable checkout's `.claude/skills`, `.agents/skills`,
`.opencode/{skill,skills}`, and `.pi/skills` directories. `agency context`
remains the authority for writes; reference checkouts remain read-only.

Repository aliases and canonical fetch remotes are declared in tracked
`agency.json`; local Git clones and symlinks remain ignored under
`repos/{alias}`. A declaration contains no local path, symlink target, worktree,
or credential. The optional `vcs` field accepts only `"git"`; omitting it also
selects Git:

```json
{
	"version": 2,
	"vcs": "git",
	"repositories": {
		"frontend": {
			"remote": "git@example.com:team/frontend.git"
		}
	}
}
```

Agency materializes declared remotes as bare Git repositories and creates
managed Git worktrees for execution units. Linked aliases remain symlinks to
existing Git repositories.

Existing version 2 workbases without `repositories` remain valid. Run
`agency repo setup` to preview deterministic adoption of legacy local aliases;
`agency repo setup --apply` writes declarations only when a portable origin is
unambiguous. Workbase configuration may also provide custom writable-worktree
creation and removal commands.

### Custom Branch Names

Set `branchNameCommand` to an argv template to choose the branch recorded for a
new execution unit when `--branch` is omitted:

```json
{
	"version": 2,
	"branchNameCommand": ["wt-resolve-new-branch-name", "{ticket}"]
}
```

Agency invokes the command directly, without a shell, from the workbase root and
uses its trimmed stdout as the branch name. The command has 120 seconds to
finish. A non-zero exit, empty output, timeout, or output rejected by
`git check-ref-format --branch` fails creation without falling back. Explicit
`--branch` always wins. Multi-phase task containers and review tasks do not have
writable branches and therefore do not invoke this command.

Available placeholders are:

| Placeholder      | Value                                              |
| ---------------- | -------------------------------------------------- |
| `{id}`           | ID of the task or phase being created              |
| `{ticket}`       | Task ticket URL, or `{id}` when there is no ticket |
| `{ticketUrl}`    | Task ticket URL, or an empty string when absent    |
| `{repo}`         | Writable repository alias                          |
| `{base}`         | Base branch                                        |
| `{workbaseRoot}` | Absolute workbase root                             |
| `{taskId}`       | Task ID                                            |
| `{phaseId}`      | Phase ID, or an empty string when creating a task  |

Resolver failures are reported with the `BRANCH_NAME_COMMAND_FAILED` error code
in JSON output.

Matching `AGENCY_ID`, `AGENCY_TICKET`, `AGENCY_TICKET_URL`, `AGENCY_REPO`,
`AGENCY_BASE`, `AGENCY_WORKBASE_ROOT`, `AGENCY_TASK_ID`, and `AGENCY_PHASE_ID`
environment variables are also set. Without `branchNameCommand`, task branches
remain `task/<id>` and phase branches default to `task/<task-id>-<phase-id>`.
Callers should omit `--branch` when they want the workbase policy; do not pass a
hard-coded value merely to reproduce Agency's built-in default.

### Custom Worktree Command

Git workbases create worktrees with Git. Set `worktreeCreateCommand` to an
argv template when another tool should create writable worktrees:

```json
{
	"version": 2,
	"worktreeCreateCommand": [
		"my-worktree-tool",
		"--repo",
		"{repo}",
		"--destination",
		"{worktree}",
		"--branch",
		"{branch}"
	]
}
```

Available placeholders are:

- `{repo}`: absolute repository alias path under `repos/`
- `{worktree}`: absolute checkout path Agency requires
- `{branch}`: execution branch the custom command must create or check out
- `{base}`: configured execution base

`{repo}` and `{worktree}` are required. Agency invokes the command directly
without a shell, sets matching `AGENCY_REPO`, `AGENCY_WORKTREE`,
`AGENCY_BRANCH`, and `AGENCY_BASE` environment variables, and verifies that the
requested destination exists afterward.

Worktrunk can be configured per workbase without changing the user's Worktrunk
path settings:

```json
{
	"version": 2,
	"worktreeCreateCommand": [
		"wt",
		"-C",
		"{repo}",
		"-y",
		"--config-set",
		"worktree-path=\"{worktree}\"",
		"switch",
		"--create",
		"--base",
		"{base}",
		"{branch}",
		"--no-cd",
		"--format",
		"json"
	]
}
```

Custom commands own writable branch creation. Agency checks for conflicting
worktrees first, invokes the command only when the branch is not checked out,
and verifies that `{worktree}` exists afterward.

Agency also reconciles known tool-owned artifacts such as Worktrunk's
`.worktree.lock` into each managed worktree's local Git exclude file. This keeps
new and existing managed worktrees clean without changing the repository's
tracked ignore configuration or overwriting user-maintained local excludes.

The configured command applies only to the writable checkout.
Supplemental read-only repositories remain detached Git worktrees at their
declared refs so they do not acquire writable branches.

### Custom Worktree Remove Command

Git workbases remove worktrees with `git worktree remove`. Set
`worktreeRemoveCommand` to an argv template when another tool should remove
writable worktrees:

```json
{
	"version": 2,
	"worktreeRemoveCommand": [
		"wt",
		"-C",
		"{repo}",
		"-y",
		"remove",
		"{worktree}",
		"--no-delete-branch",
		"--foreground",
		"--format",
		"json"
	]
}
```

It accepts the same placeholders and environment variables as
`worktreeCreateCommand`, and `{repo}` and `{worktree}` are required. Agency
invokes it directly without a shell whenever it removes an existing writable
checkout, including `agency worktree remove`, rebuilds, archives, and rollback
of a failed materialization. Agency still refuses dirty or mismatched checkouts
before running the command.

The command must remove the checkout synchronously, unregister it from Git, and
preserve the execution branch; Agency verifies all three afterward. Rollback of
a newly created checkout may delete the branch because Agency discards it
anyway. Reference checkouts and stale registrations are still removed or pruned
with Git.

### Post-checkout Commands

Each repository declaration may provide a `postCheckoutCommand` argv
template for repository-specific setup. Agency invokes it directly, without a
shell, with the new checkout as its working directory:

```json
{
	"version": 2,
	"repositories": {
		"frontend": {
			"remote": "git@example.com:team/frontend.git",
			"postCheckoutCommand": ["bun", "install", "--frozen-lockfile"]
		}
	}
}
```

The hook runs for each newly created managed Git worktree, including writable
and reference worktrees, after Agency has validated it. It does not run for a
reused worktree or for inspection-only commands. A custom
`worktreeCreateCommand` completes and is validated before this hook runs.

Available placeholders and matching environment variables are:

| Placeholder        | Environment              | Value                                          |
| ------------------ | ------------------------ | ---------------------------------------------- |
| `{repoAlias}`      | `AGENCY_REPO_ALIAS`      | Repository alias                               |
| `{repositoryPath}` | `AGENCY_REPOSITORY_PATH` | Absolute source repository path under `repos/` |
| `{checkoutPath}`   | `AGENCY_CHECKOUT_PATH`   | Absolute managed checkout path                 |
| `{checkoutKind}`   | `AGENCY_CHECKOUT_KIND`   | `writable` or `reference`                      |
| `{requestedRef}`   | `AGENCY_REQUESTED_REF`   | Requested branch, reference, or review commit  |
| `{base}`           | `AGENCY_BASE`            | Configured execution base                      |
| `{vcs}`            | `AGENCY_VCS`             | `git`                                          |
| `{workbaseRoot}`   | `AGENCY_WORKBASE_ROOT`   | Absolute workbase root                         |
| `{taskId}`         | `AGENCY_TASK_ID`         | Task ID                                        |
| `{phaseId}`        | `AGENCY_PHASE_ID`        | Phase ID                                       |

`{base}` and `{phaseId}` and their environment variables are empty strings when
they do not apply. Dry runs report a planned `post-checkout` operation but never
execute it. Verbose output identifies the repository and expanded command.

Hook success is part of checkout creation. A non-zero exit or failure to start
rolls back the checkout and any branch created by the same operation; if cleanup
also fails, Agency reports the exact manual recovery action. A later command
retries checkout creation and the hook rather than reusing an uninitialized
checkout. Hook commands should be idempotent so a retry is safe after any
external effects the failed invocation may have completed.

### Agents

OpenCode (`opencode2` and `opencode`), Pi, and Claude Code are built-in agent presets.
Without an explicit agent, Agency uses the first available executable in this
order: `opencode2`, `opencode`, `pi`, then `claude`. Select a preset or configured
agent with `agency work --agent <name>`. To select a built-in agent globally,
set `agent` in `$XDG_CONFIG_HOME/agency/agency.json` (or
`~/.config/agency/agency.json`):

```json
{
	"agent": "pi"
}
```

The selection precedence is `--agent` (including the legacy `--opencode` and
`--claude` aliases), then `AGENCY_AGENT`, then `agent`, then automatic
detection. Supported global values are `opencode2`, `opencode`, `pi`, and
`claude`; an unavailable configured agent fails rather than falling back. A
launch is fresh unless `AGENCY_SESSION_ID` is already set; resumed launches use
the agent's `resumeCommand` when configured. The built-in presets use
`--continue` only for resumed launches, except autonomous OpenCode V2 launches,
which open a fresh TUI session so the generated continuation prompt cannot be
routed to an unrelated prior session. By default Agency opens the agent without
a prompt. `--auto` uses its autonomous command and sends the generated task,
phase, or epic prompt. OpenCode V2 receives a launch-only environment marker;
Agency's managed TUI companion retries the native submit command until the exact
prompt appears as a persisted user message. It records whether OpenCode submitted
the prompt natively or submission followed a companion dispatch, and shows a
bounded error with a manual recovery instruction when delivery is not observed.

For built-in OpenCode V2 launches, Agency submits through the selected CLI's
authenticated session API, then opens the TUI with `--session` and an empty
composer. V2's native `--prompt` only fills the composer. Agency sets session
environment before submission and passes that same environment into the TUI;
reconnecting the TUI does not replay the prompt. V1 keeps its native launch path.
See [OpenCode auto-start](docs/opencode-auto-start.md) for details and real
startup verification.

Custom agents are direct argv commands, never shell snippets:

```json
{
	"version": 2,
	"agents": {
		"custom": {
			"command": ["my-agent"],
			"autoCommand": ["my-agent", "--prompt", "{prompt}"],
			"resumeCommand": ["my-agent", "resume", "{sessionId}"],
			"autoResumeCommand": ["my-agent", "resume", "{sessionId}", "{prompt}"],
			"environment": { "MY_AGENT_TARGET": "{target}" }
		}
	}
}
```

Available placeholders are `{prompt}`, `{workbase}`, `{target}`, `{task}`,
`{phase}`, and `{sessionId}`. Task and phase
placeholders are empty when they do not apply. `{prompt}` is empty unless
`--auto` is set. If `resumeCommand` is omitted, the fresh command is also used
for resumed sessions. If `autoResumeCommand` is omitted, `autoCommand` is used;
configured agents without `autoCommand` reject `--auto`.

Every agent receives the same `AGENCY_AGENT`, `AGENCY_SESSION_ID`,
`AGENCY_WORKBASE`, `AGENCY_TARGET`,
`AGENCY_TASK_ID`, `AGENCY_PHASE_ID`, and `AGENCY_PROMPT` environment. Configured
environment is added without overriding these normalized values.
Autonomous OpenCode V2 launches additionally receive
`AGENCY_TUI_AUTOSUBMIT=1`; the client-side managed TUI companion consumes it,
and manual and non-V2 launches do not receive it.
Execution-unit agents also receive `AGENCY_WRITABLE_CHECKOUT` with the
authoritative writable checkout path.
`AGENCY_PROMPT` is empty unless `--auto` is set.
Autonomous prompts begin `Agency worker launch target: <target>.`, carrying the
same canonical target as `AGENCY_TARGET`. This is the process-local fallback for
agent clients that attach to a long-lived process and lose launch environment
variables. A worker must verify either signal against `agency context . --json`
before acting; a matching worker performs the task directly and must not invoke
`agency work` for the same target. Managed guidance also fails safe for older
generated prompts when their document paths, current directory, and active valid
context all agree. External session state is not part of this identity contract.
The managed OpenCode plugin validates the marker against Agency context, binds it
to the receiving OpenCode session, and injects an explicit active-worker system
instruction. Its V1 integration also restores Agency identity for that session's
shell environment. OpenCode V2's shell hook is location-scoped and does not
identify the invoking session, so Agency deliberately avoids leaking one
session's worker identity into another; the validated marker and injected system
instruction remain the V2 fallback when the long-lived server did not inherit
the client's launch environment.
For execution units, the built-in `pi` agent and the `opencode2` and `opencode`
agents running OpenCode V2 launch from the authoritative writable checkout so
their project and Git interfaces reflect the implementation repository.
OpenCode V1 stops configuration discovery at the checkout's Git root, so V1
launches remain rooted in the task or phase directory. Epic and multi-phase task orchestration remains
rooted in its Agency document directory. Ancestor discovery still supplies the
workbase `AGENTS.md` and managed OpenCode config.
Agency's managed OpenCode plugin grants the active workbase external-directory
access and exposes existing checkout-local `.claude/skills`, `.agents/skills`,
and `.opencode/{skill,skills}` definitions. V1 adds those source directories to
`skills.paths`; V2 registers their discovered skill definitions through the
plugin API and injects the managed Agency instructions through a session context
hook. The global Pi
extension provides equivalent whole-workbase context and additionally discovers
checkout-local `.pi/skills` through Pi's `resources_discover` lifecycle.
`agency work` supplies the checkout directly; plain OpenCode and Pi launches
from a task or phase directory resolve a materialized execution-unit checkout
through `agency context` and instruct the agent to change to it before
implementation or Git operations. A multi-phase
task root has no single checkout, so launch from its phase directory when using
plain OpenCode or Pi. Other checkout-local configuration is not composed.
`--print-command` prints the cwd, command template, and non-secret environment
keys without launching the agent. Built-in OpenCode auto launches also report a
`startup` note: V2 resolves the final `--session` argv during actual startup, so
print-only mode creates neither a session nor a prompt.

### Custom Chooser Command

Interactive selection uses an OpenTUI Solid split footer by default. Type to
fuzzy-filter choices, use arrow keys or Ctrl-N/Ctrl-P to move, press Enter to
select, and press Escape or Ctrl-C to cancel. To use an external chooser
instead, configure an argv command in `agency.json`:

```json
{
	"version": 2,
	"chooserCommand": ["fzf", "--ansi", "--delimiter=\\t", "--with-nth=2.."]
}
```

Agency writes one `key<TAB>label` record per choice to the command's stdin. The
command must write the selected opaque key or selected record to stdout; commands
such as `["gum", "filter"]` therefore work without wrappers. Exit codes 1 and
130 or empty stdout cancel external selection. Other nonzero exits and unknown
keys are errors.

Selectors are opened only when stdin and stdout are terminals and neither
`--no-input` nor JSON output is active. External chooser labels use color only
when stdout is a terminal, `TERM` is not `dumb`, and `NO_COLOR` is unset. The
native OpenTUI selector uses plain labels without ANSI styling or icon-font
dependencies.

## Frontmatter

### Epic

```yaml
---
ticketUrl: https://example.com/tickets/checkout
description: Coordinate the checkout experience across frontend and backend.
repos:
  - repo: frontend
    ref: main
  - repo: backend
    ref: main
tasks:
  - id: backend-api
  - id: frontend-ui
    dependsOn:
      - backend-api
---
```

### Single-Phase Task

```yaml
---
ticketUrl: https://example.com/tickets/refresh-copy
description: Refresh user-facing checkout copy.
epic: checkout
repo: frontend
repos:
  - repo: backend
    ref: main
branch: task/refresh-copy
base: main
pr: null
status: open
---
```

Tasks may also record a purpose and one-way investigation handoff provenance:

```yaml
purpose: implementation
handoff:
  source:
    kind: phase
    taskId: investigate-checkout
    phaseId: reproduce
  sourceRevision: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
```

The source revision is historical evidence, not a readiness dependency or a
claim that the source still has that content. Source rename, archive, restore,
or later edits do not rewrite it. Destination rename, archive, and restore
preserve it unchanged. Task show, context, and graph JSON expose the record as
task metadata; graph output does not add an edge for it.

### Multi-Phase Task

```yaml
---
ticketUrl: https://example.com/tickets/build-checkout
description: Deliver checkout through sequenced backend and frontend changes.
epic: checkout
phases:
  - id: backend-api
  - id: frontend-ui
    dependsOn:
      - backend-api
---
```

Each listed phase has a `phases/{id}/PHASE.md` containing its execution fields:

```yaml
---
description: Build the checkout interface against the new backend API.
repo: frontend
repos:
  - repo: backend
    ref: main
branch: task/checkout-ui
base: task/checkout-api
pr: null
status: open
---
```

Epic task dependencies belong in `EPIC.md`. Phase dependencies belong in the
owning `TASK.md`. Stable IDs do not encode ordering in directory names.

## Quick Start

```bash
agency workbase init ~/work
cd ~/work

agency repo add frontend git@github.com:example/frontend.git
agency repo link backend ~/Dev/backend

agency task new

agency validate
agency context tasks/refresh-copy --json
agency work tasks/refresh-copy
cd tasks/refresh-copy
agency pr create --fill
```

After cloning an existing workbase on another machine, restore its declared
repositories before preparing work:

```bash
agency repo setup --dry-run
agency repo setup --apply
agency validate
```

## Commands

### Interactive Actions

`agency` with no subcommand (or `agency act`) opens on **Workstream**, a flat list
of non-archived tasks and phases. When run inside an epic, task, phase, or one of
its checkout descendants, it opens that item's actions first; Escape returns to
Workstream, where **Tab** reaches global Workbase actions. `agency .` is shorthand
for `agency act .` and explicitly focuses the item containing the current path.
Otherwise, select an item to see its available actions. After an action,
Agency reloads the graph and returns to Workstream so changed items and available
actions are current, and archived items no longer appear.
Press **Tab** to cycle to **Workbase** for workbase actions and then to
[**Supervise**](#supervisor); **Shift-Tab** cycles back. Workstream and Workbase
have a blank row above a chevron-prefixed filter with the placeholder “type to
filter”; `?` with an empty filter shows the active tab's keys.
Each tab remembers its filter and selection. The active
tab shares its background with the panel beneath it. Creation
works even in an empty workbase. The guided flow collects required inputs,
selects the sole repository automatically, and suggests IDs, base branches, and
phase branches. Suggestions remain editable; choosing a base never adds a
completion dependency.

In the built-in TUI, status changes (**Reopen**, **Drop**, **Complete without a
pull request**, and **Finish review**) do not block. Each one joins a single
background queue shown at the bottom of the screen, and Workstream immediately
shows its expected status, so you can act on several items in quick succession.
Queued actions run one at a time in order, with the same revision guards and
availability checks as foreground actions. A later action on the same item is
based on the earlier one's result. If an action fails, its status change is
rolled back, the error is shown, and later queued actions on that item are
skipped. Other actions wait for the queue to empty before they run, and exiting
also waits for queued actions to finish.

Splitting a task / adding a phase and turning an investigation into implementation
start in **Workstream**: select the source item, then choose its action.

Workstream items occupy two rows: a muted type icon and ID on the upper left,
the main repository and colored status below, and the description wrapping across
both rows on the right. Selection highlights the entire two-row item. Other item
pickers retain their compact, flat rows with the same Nerd Font icons.
Blue is the shared focus/active accent. Type and ordinary action icons are muted;
status uses green for done, yellow for blocked, and red for dropped. Destructive
actions use red. Icons and labels convey the meaning independently of color.
Filtering matches full IDs and metadata even when a displayed name is shortened.

The built-in guided flow keeps one full-screen session across menus, text inputs,
and execution, with the tabs visible throughout. Item actions use colored icons
and return to the same item's refreshed action menu, with the latest outcome
above the prompt. Text-entry prompts support Shift-Return for a newline and
Return to submit, including multiline outcomes and completion summaries.
Wizard editors fill the available height and keep an editing hint visible below
the input; longer content scrolls with the cursor as the terminal resizes.
Submitting a blank required field retries that prompt with an explanation and
keeps earlier answers. Only input validation is retried; execution failures are
reported without automatically repeating a mutation.
Task, phase, review, and handoff ID prompts also check Agency's ID format and
currently known duplicate IDs before continuing. Native commands still perform
the final validation when executing, including concurrent changes.
In wizard inputs, Escape clears entered text first; with an
empty input it returns one prompt, retaining earlier answers and recomputing later
defaults. Escape from the first input returns to the menu; Escape from an item's
action menu returns to Workstream. Tab switches sections from any step. On
the front screen, Escape clears a filter first; it closes one layer at a time
and never quits. Ctrl-C quits from any screen. Throughout the TUI a held Enter
never submits, and confirmations ignore Enter for a moment after they open. On exit it restores the shell and leaves a compact recap of
completed actions, affected items, and commands. Dry runs are labeled as previews;
completed steps remain in the recap if a later step is cancelled or fails. Work
handoffs restore the terminal before starting the interactive worker, then reopen
the TUI when the worker returns. A configured
external chooser continues to offer goals and **Browse items** through that chooser.

| Goal                                 | Choose in `act`                                                                             | Discovery/action ID                                                                                                                                |
| ------------------------------------ | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Manage repositories                  | Add, link, set up, materialize, fetch, verify, rename, update, unlink, or remove aliases    | `repo-add`, `repo-link`, `repo-setup`, `repo-materialize`, `repo-fetch`, `repo-verify`, `repo-rename`, `repo-remote`, `repo-unlink`, `repo-remove` |
| Create work                          | Create a standard task, multi-phase task, investigation, epic, or a task inside an epic     | `task-create`, `multi-phase-create`, `investigation-create`, `epic-create`, `task-create-in-epic`                                                  |
| Split work                           | Add a phase / split this task; name the existing work's first phase                         | `split`                                                                                                                                            |
| Work on a task or phase              | Work on this item                                                                           | `work`                                                                                                                                             |
| Move from investigation to execution | Create implementation follow-up                                                             | `handoff`                                                                                                                                          |
| Review someone else's work           | Review a PR, or a remote branch/commit; finish a review                                     | `review`, `review-ref`, `finish-review`                                                                                                            |
| Publish or update PR status          | Push without a PR, refresh provider state, create a PR, mark a GitHub PR ready, or close it | `push`, `sync`, `pr`, `pr-ready`, `pr-close`                                                                                                       |
| Maintain review work                 | Fetch and repin a review task to its current source                                         | `review-refresh`                                                                                                                                   |
| Organize work                        | Rename an item, move a task into/out of an epic, or add/remove a sibling dependency         | `rename`, `move-to-epic`, `remove-from-epic`, `dependency-add`, `dependency-remove`                                                                |
| Archive terminal work                | Archive                                                                                     | `archive`                                                                                                                                          |
| See work status                      | See current or ready work                                                                   | `current-work`, `ready-work`                                                                                                                       |
| Check or refresh the workbase        | Validate, diagnose, reconcile all state, or inspect/update managed integration files        | `validate`, `doctor`, `sync-all`, `integration-status`, `integration-sync`                                                                         |

After creating a task, phase, review, or implementation follow-up, choose **Work
on the new item now** or **Finish**. Only choosing Work prepares checkouts and
launches the configured runner; creation alone leaves the item for later.
Investigation handoff creates a distinct implementation item with source and
revision provenance. Completing a non-PR outcome requires a durable summary.
**Finish review** marks a review task done without a summary; review tasks offer
it instead of non-PR completion. PR-backed completion comes from provider
reconciliation after merge.

**Refresh Agency state** reads the provider and reconciles local records. The
explicit **GitHub PR ready/close** actions mutate the recorded GitHub URL, then
refresh Agency state. They are offered only for recorded GitHub PR URLs; actual
provider state and authorization are checked by the underlying command. Native
provider-aware PR creation remains available. Existing lifecycle services own
validation, preparation, revision guards, and archive protections.

Pass a task/phase directory, a file inside it (including `TASK.md` or `PHASE.md`),
or a positional task ID to open that item's actions immediately. Absolute and
relative paths work; a workbase-root path opens Workbase actions. Explicit
`--epic <id>`, `--task <id>`, and `--task <id> --phase <id>` also skip item selection.
Use `--action <id>` to start
at a specific scenario. `--dry-run` collects inputs and prints exact commands,
including automatic bookkeeping commands, without executing or offering Work.
Cancelling before dispatch makes no changes; cancelling after creation leaves
the newly created item. The graph is refreshed before item actions are dispatched.

```bash
agency act .
agency act --action task-create --dry-run
agency act --task investigate --action handoff --json
```

For agents, `--json` never prompts or executes. Its compact, runtime-validated
result includes workbase actions and repository aliases, current working items,
and matching targets with readiness, document revisions, available `actions`,
and `blockedActions` with reasons. `--action` filters discovery to one scenario.
Every action includes a concise `description`, Nerd Font `icon`, and semantic
hex `color`, in addition to its label and availability.
Targets and current working items share the same identity and metadata fields:
`id`, `kind`, `key`, `status`, `description`, `repo` (the declared main repository,
when present), `repositories`, `readiness`, and `revision`. Descriptions retain
their original line breaks rather than the compact Workstream display wrapping.
Input descriptors follow wizard collection order; narrative fields advertise
`multiline: true`. Pass a multiline value as one argv element, preserving newlines.
`command` is exact argv only when no inputs are missing and the action is
available. Otherwise substitute the required `inputs` into `commandTemplate`'s
`<input-id>` placeholders. The resulting argv is ready to run: standard task
creation has no `--purpose` flag, and investigation creation includes a fixed
`--purpose investigation`. Optional inputs are excluded from the template;
append their native `option` and value only when wanted (for example,
`--depends-on <phase-id>`). Human prompting and suggestions are not part of the
machine protocol. Run commands from the
returned workbase root. `followUpCommands` are automatic bookkeeping;
`nextActions` require a separate explicit choice and are never implied by
creation. Re-discover after mutations rather than reusing old revisions.

`--auto` applies to Work, including the post-creation choice. `--draft` applies
to PR creation. Discovery is based on local state and does not fetch GitHub or
promise that remote operations or checkout preparation will succeed.

### Target Context

`agency context [target] --json` returns compact bootstrap context without
modifying the workbase or fetching repositories. At the workbase root it returns
a discovery catalog of all epics, tasks, and phases, including frontmatter,
paths, and document revisions. Elsewhere it returns context for an epic, task,
or phase. The target defaults to the current directory; entity directories,
document paths, checkout descendants, and bare task IDs are accepted.
Archived entity paths and selectors are also accepted. Archived context is
explicitly marked with `target.archived: true` and never grants writable
document, repository checkout, or reference authority; restore the item before
attempting mutation or execution.

Root discovery and entity context are compact by default. Root discovery includes
a hint to run `agency context . --full --json` when document prose is needed.
`--compact` remains available as an explicit request for the compact projection.

The result includes workbase and target identity, ancestor frontmatter with
SHA-256 hashes, dependency and readiness state, aggregate status, writable and
reference authority, local materialization state, recorded PR state, and
validation warnings. Only `done` satisfies a dependency; `dropped` is terminal
but remains a blocker.

`authority.writable` identifies the writable repository checkout, while
`authority.documents.writable` lists the absolute paths of Agency documents the
target may maintain. A single-phase task lists its `TASK.md`; a phase lists its
owning `TASK.md` and active `PHASE.md`; orchestration targets list none. Use
Agency commands rather than direct edits for structural frontmatter mutations.

Pass `--full` to include document prose and low-level VCS details. Compact output
retains identity, hashes, authority, paths, graph state, materialization state,
recorded PR state, and validation warnings.

Pass `--include ticket` to add a live `ticket` field read from the target's
`ticketUrl` (a phase uses its task's ticket). See [Ticket Status](#ticket-status).

Pass `--include runtime` to add a `runtime` object for an execution-unit target:
the unit's reconciled runtime binding and any inferred agent panes (see
[Runtime Bindings](#runtime-bindings)). Other targets report `runtime: null`.

### Workbase Graph

`agency graph --json` exports the complete workbase as graph contract version 1.
Nodes use stable IDs (`epic:<id>`, `task:<id>`, `phase:<task>/<phase>`,
`repository:<alias>`, and `execution-unit:<kind>/<id>`). Typed edges are `owns`,
`depends_on`, `writes`, and `references`.

Every work node includes status, readiness, `blockedBy`, detailed blockers,
terminal state, reverse dependents, and aggregate progress. Only `done` satisfies
a dependency; `dropped` is terminal but does not satisfy dependents. The graph
summary counts the statuses of all execution units, independent of filters.

```text
agency graph [--json | --jsonl] [--ready | --blocked]
  [--status <status>...] [--repository <alias>...] [--kind <kind>...]
  [--include <bodies|workspace|git|pr|ticket|runtime>...]
```

Filters are applied after graph state is computed. Returned edges always have
both endpoints in the filtered node set. Durable frontmatter and document hashes
are always present; prose, absolute workspace paths, Git inspection, live PR
inspection, and live ticket status are opt-in include layers. The `ticket` layer
annotates epic, task, and execution-unit nodes that pass the filters.

### Ticket Status

The `ticket` include layer reads the live status of an item's `ticketUrl`
through a read-only ticket provider. Jira URLs on `*.atlassian.net` are read
with `acli jira workitem view`; GitHub issue URLs are read with `gh issue view`.
Each result has a `state`:

- `none`: the item has no `ticketUrl`.
- `unsupported`: no provider recognizes the URL.
- `unavailable`: the provider CLI is missing or the lookup failed; `reason`
  explains why. The rest of the command still succeeds.
- `available`: includes `provider`, `key`, the provider's `status`, a normalized
  `statusCategory` (`todo`, `in_progress`, or `done`), `summary`, and
  `resolution`.

Lookups are deduplicated by URL, run with bounded concurrency, and cached
briefly in memory.

`--jsonl` emits a versioned `meta` record, one record per node and edge, then an
`end` record with counts. Combining the metadata with the streamed node and edge
records reconstructs the same result as `--json`.

### Runtime Bindings

When `agency work` claims an execution unit and launches its agent inside a
terminal runtime, it records a binding: runtime kind, pane, tab, and workspace
IDs, the agent session ID when Agency knows it (for example, an OpenCode V2
auto-start session), the Agency session ID, and a timestamp. Herdr is currently
the only runtime. Recording reads Herdr's inherited `HERDR_ENV`,
`HERDR_PANE_ID`, `HERDR_TAB_ID`, and `HERDR_WORKSPACE_ID` directly and runs no
subprocess; outside Herdr it records nothing. Recording is best-effort: it is
bounded to two seconds and its failures never block or fail the launch.
Inspection detects Herdr from `HERDR_ENV`/`HERDR_BIN_PATH` or a `herdr` binary on
`PATH`.

Bindings are local operational state, not workbase content. They are stored as
one JSON file per unit under
`$AGENCY_STATE_DIR/runtime-bindings/<workbase-key>/` (default state directory:
`$XDG_STATE_HOME/agency`, else `~/.local/state/agency`). Relaunching a unit
replaces its binding, and launching a unit in a pane removes other units'
bindings to that pane.

`agency graph --include runtime` and `agency context --include runtime` reconcile
bindings read-only against a live runtime snapshot. Each execution-unit node
gains `runtime.binding` (or `null`) with a `state`:

- `live`: the bound agent session is still in its recorded pane. When the session
  ID is unknown, an agent in the recorded pane counts only while its working
  directory is inside the unit's checkout or item directory, because pane IDs
  are reused; if several such bindings claim one pane, the latest launch wins.
- `moved`: the bound agent session is visible in exactly one other pane.
- `duplicated`: the bound agent session is visible in more than one pane.
- `gone`: the bound agent is no longer visible; `panes` lists the recorded pane
  if it remains open (without an agent, or hosting other work).
- `unknown`: no runtime is available to check.

Unbound agent panes whose working directory lies inside a unit's checkout or item
directory appear in that node's `runtime.inferred` list with `matchedBy:
"checkout"` or `"directory"`; these are lower-confidence than recorded bindings.
The graph's top-level `runtime` object reports the provider, its availability and
any error, `unmanaged` agent panes that match no unit, and `orphaned` bindings
whose unit is no longer active (for example, after archiving).

### Attention Signals

`agency attention` reports deterministic "needs attention" signals across every
registered workbase, the current workbase, and any workbase in which a live
agent pane runs. It joins Agency state with live pull request (`gh`), ticket,
and runtime state, and never executes anything.

```sh
agency attention [--json] [--archived] [--workbase <selector>]
```

| Signal                   | Condition                                                                                                                                                                                                                                                                                              |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `complete_session_open`  | Terminal or archived work whose PR is merged or absent (closed is fine for dropped work) and whose ticket is done or absent still has a live session. Only agents that are not working are offered for closing.                                                                                        |
| `ticket_status_mismatch` | Evaluated once per ticket across every unit (in any workbase) that shares it. The ticket is delivered when every sharing unit is terminal, at least one is done with a merged PR, and none is done with an unmerged PR. A done ticket is flagged only while sharing units are active and none is done. |
| `pr_merged_item_open`    | The PR is merged but the unit is still open, working, or delegated.                                                                                                                                                                                                                                    |
| `pr_ready_to_merge`      | The open, non-draft PR is approved, its checks ran and passed, and GitHub reports a `CLEAN` (or `HAS_HOOKS`) merge state. The merge pins the reviewed head with `--match-head-commit`.                                                                                                                 |
| `claim_without_session`  | A `working` unit with no PR (or a closed one) has no live or inferred session (requires a runtime). Review tasks are offered `finish-review` instead of `drop`.                                                                                                                                        |
| `duplicate_session`      | One agent session is open in more than one pane. Closing a copy is offered only when a binding or the focused pane says which to keep.                                                                                                                                                                 |
| `session_cwd_missing`    | An agent pane's working directory no longer exists.                                                                                                                                                                                                                                                    |
| `agent_waiting`          | A bound agent of active work is idle, done, or blocked.                                                                                                                                                                                                                                                |
| `unmanaged_session`      | An agent pane belongs to no item. Panes at a workbase root or in epic or multi-phase task directories count as orchestration and are not reported.                                                                                                                                                     |

Archived units are inspected when a binding or live pane still points at them;
`--archived` inspects every archived unit, which may issue many ticket lookups.
`--workbase` inspects one workbase and drops signals for panes that belong to
any other known workbase.

Each signal carries subject references (workbase, item, runtime panes, PR,
ticket), structured evidence, and suggested actions in the versioned
[`schemas/agency-action-v1.schema.json`](schemas/agency-action-v1.schema.json)
contract, shared with triage and the supervisor. The action schema is a
top-level `oneOf` with one complete variant per `type`: `agency`, `runtime`,
`command`, `prompt_agent`, `delegate` (`via` `prompt_bound_agent` or
`kickoff`), or `open_url`. `risk` is `read-only`, `reversible`, or
`destructive`, and destructive actions need a second confirmation.

`preconditions` are state facts an executor must recheck immediately before it
runs an action. Pane IDs are reused, so `pane_matches` also pins the pane's tab,
working directory, agent session, and allowed agent statuses, and
`tab_agent_panes` requires a tab's agent panes to be exactly the listed set
before a whole-tab close. `pull_request_head` pins the reviewed commit.

`agency` actions carry an `agency act` action ID, its item `selector`, any
input `values`, and exactly one of `command` or `commandTemplate` as discovered.
Executors re-resolve the argv at execution time with
`agency act --action <id> [--task <task> [--phase <phase>]] --json` from the
payload's `cwd`, rather than trusting a stored argv. Ticket transitions name
their target status explicitly and are never automatic. Jira status names
default to `Done` and `In Progress`; override them per workbase in
`agency.json`:

```json
{
	"tickets": { "jira": { "doneStatus": "Closed", "reopenStatus": "Reopened" } }
}
```

The report itself follows
[`schemas/agency-attention-v1.schema.json`](schemas/agency-attention-v1.schema.json).

### Triage

`agency triage` adds judgment to attention signals. It assesses every supervised
item (a unit with signals, a live agent pane, or a `working` or `delegated`
claim, plus panes that signals report outside any unit) and ranks recommended
actions in the `agency-action-v1` schema. It proposes; it never executes.

```sh
agency triage [--json] [--item <ref>] [--archived] [--workbase <selector>]
```

Triage runs headless `opencode run --standalone` (OpenCode V2) for each batch
of up to eight changed items from one workbase. Every run is isolated: a fresh
temporary project directory outside `$HOME` and its own `OPENCODE_CONFIG_DIR`,
so your global OpenCode plugins, instructions, and permissions do not load.
Only your `model`, `small_model`, and provider settings are carried over from
your global OpenCode config; credentials stay in OpenCode's data directory.
`AGENCY_*`, `OPENCODE_*`, and Herdr pane location variables are removed from the
child environment. Set `AGENCY_OPENCODE_BIN` to use another `opencode`
executable. Interrupting `agency triage` stops the run and removes its files.

The `agency-triage` agent denies every tool by default. It may read the run's
attachments and files inside the batch's workbase, and run a short list of
read-only commands: `gh pr view`/`checks`/`diff` and `gh issue view` with a full
`https://github.com/` URL, `acli jira workitem view`/`search`,
`herdr pane read`/`list`, and `agency context`/`graph`/`status`. Redirection,
`--web`/`-w`, `--watch`, `-R`/`--repo`, `--hostname`, and a second URL are
denied even on allowed commands.

The agent receives a locked, versioned contract as its system prompt and three
attachments. The first is the structured snapshot: items with Agency, PR,
ticket, and pane state, signals with their actions, available `agency act`
actions, bounded transcript tails of idle, done, or blocked agents, the previous
assessment, and each item's own recent actions, active snoozes, and active
dismissals. The others are the
[`schemas/agency-triage-v1.schema.json`](schemas/agency-triage-v1.schema.json)
response schema and the action schema. Each reply item names a snapshot item
`key` and gives a `status` (`needs_input`, `blocked`, `in_progress`,
`awaiting_review`, `ready_to_merge`, `complete`, `idle`, or `unknown`), a
one-line `summary`, the agent's open `question` or `null`, and ranked
`actions`.

Agency validates the reply against the schema and the requested keys and gives
every action the item's own subject. An action that is not a verbatim copy of
one of the item's signal actions is model-written: its ID must start with
`triage:`, it may reference only the item's panes, tabs, and unit, pane targets
need a `pane_matches` precondition, `agency` actions must copy an available
action's argv, inputs, selector, and `cwd` exactly (filling template `values`),
and no model-written action can be `read-only`. Its ID is
listed in the assessment's `modelActionIds`. Agency retries once in the same
session with any validation errors, then reports a structured failure.

Your own instructions are added to the user message, global first. Both the
global `agency.json` (in `$XDG_CONFIG_HOME/agency/`, else `~/.config/agency/`)
and each workbase's `agency.json` accept an inline `prompt` and a `promptFile`
relative to that file's directory; the global file also selects the model:

```json
{
	"triage": {
		"promptFile": "triage.md",
		"model": "anthropic/claude-sonnet-4-5"
	}
}
```

Triage is incremental. Each item's fingerprint covers its snapshot facts, a hash
of its transcript tails, its own action log entries, the contract and agent
profile, the prompts, and the model; only items whose fingerprint changed are
re-evaluated, and the rest reuse cached assessments (`source: "cached"`).
Snoozes and dismissals are applied to every reported assessment: snoozed or
dismissed actions are removed, and a snoozed or dismissed item reports
`suppressed` with no actions. A dismissal with a fingerprint lapses when the
item changes: supervisor dismissals pin `<signals>:<triage>` fingerprints, and
triage compares its own part, or the signals part when the dismissal predates
any triage of the item. Full runs prune lapsed dismissals and those for items
that are gone. If re-evaluation fails, an earlier assessment is reported
as `stale`, otherwise the item is `failed`. `--item` re-evaluates one item,
selected by task, `task/phase`, execution unit ID, pane ID, or item key. The
report follows
[`schemas/agency-triage-report-v1.schema.json`](schemas/agency-triage-report-v1.schema.json).

Supervision state lives in `$AGENCY_STATE_DIR/supervision/`, never in workbase
documents: `triage-cache.json`, the `actions.jsonl` action log (compacted to its
newest 1,000 entries), `snoozes.json`, and `dismissals.json`. Each update holds a
lock file and replaces the file atomically, so concurrent runs merge.

### Supervisor

```sh
agency supervise [--workbase <selector>]
```

**Supervise** is the third tab of the Agency TUI (`agency act`), built on the
[attention signals](#attention-signals). `agency supervise` opens the same TUI
directly on it, so a long-lived Herdr tab can run it; `agency act` starts the
supervisor only when Supervise is first shown, then keeps it refreshing in the
background without disturbing the focus, selection, or text input of the tab in
view. Supervise covers every registered workbase (or `--workbase`) wherever the
TUI was opened. When you leave Supervise after selecting an entry, Workstream
and Workbase switch to that entry's workbase (refreshes never move them), and
they reload after Supervise runs an action. Opened outside every workbase,
they list the registered workbases to choose from. Starting Work from act
waits for a running Supervise action and stops triage first. Tab and
Shift-Tab switch tabs (except while a Supervise dialog is open). It groups everything needing
attention by workbase, plus an Unmanaged group for agent panes outside any item.
Each entry shows a status badge (such as Needs You, Blocked, Ready to Merge,
Done, Needs Ticket, or Stale), its runtime location (`workspace › tab`), a
one-line summary, and its ranked suggested actions. Signals for one execution
unit merge into one entry. The view refreshes on runtime events (Herdr
`events.subscribe`, debounced) and every minute for pull request and ticket
changes.

[Triage](#triage) results merge into the same entries: cached assessments show
immediately (labelled with the time they were made), and `r` or `R` runs triage
in the background (the header shows progress) and refreshes when it finishes.
Triage's status, summary, and open question replace the signal summary (a more
urgent signal badge is kept), its recommended actions rank first, and items
triage knows without a signal appear too. When the model's action duplicates a
signal action, the signal's own copy, with its preconditions and risk, is the
one shown. Actions tagged `triage` were recommended by triage and `model` ones
were written by the model; the confirmation says so. Every triage action is
decoded against the action schema (invalid ones are dropped with a warning) and
runs through the same executor checks as signal actions. Closing a pane, tab,
or workspace is always destructive, and so is any command or agency action the
model wrote, whatever risk it claims. Hiding follows live snoozes and
dismissals. A dismissal pins both the entry's signals and triage's fingerprint,
so it lapses when either changes. `e` opens `triage.promptFile` from the
workbase's `agency.json`; when none is set it opens `triage.md` beside it and,
once saved with content and confirmed, records `"promptFile": "triage.md"`
(changing only that key, atomically, and refusing if `agency.json` changed in
the meantime). An inline `triage.prompt` is kept and still applies first.
`agency supervise` cleans up on SIGINT, SIGTERM, or SIGHUP: running triage stops,
the terminal is restored, and it exits 0. Quitting with Ctrl-C first lets an
action already running finish, then stops triage.

| Key                           | Action                                                                 |
| ----------------------------- | ---------------------------------------------------------------------- |
| `↑`/`↓`, `j`/`k`, `C-p`/`C-n` | Move between entries                                                   |
| `←`/`→`, `1`–`9`              | Choose an action                                                       |
| `Enter`                       | Preview, confirm, and run the action (Agency actions: wizard first)    |
| `f`                           | Focus the entry's pane                                                 |
| `r` / `R`                     | Triage the selected entry, or everything (takes minutes), then refresh |
| `e`                           | Edit the workbase's triage prompt file in `$EDITOR`                    |
| `s` / `d`                     | Snooze, or dismiss until the entry changes                             |
| `Tab` / `S-Tab`               | Switch tabs                                                            |
| `?` / `Esc` / `C-c`           | Help / close one dialog or help (never quits) / quit                   |

Before running, the supervisor shows exactly what will run: the argv, runtime
operation, or prompt, and the directory it runs in. Agency and command actions
run only inside a registered or current workbase; a cwd anywhere else is
refused. Enter is ignored for a moment after a dialog opens and
held keys never act, so a double press cannot confirm; further actions are
ignored while one is pending. After confirmation it rechecks every precondition
against fresh state (document revisions, item status, pull request state and
head, ticket status, and paths first, then one runtime snapshot for pane
identity and a tab's exact agent panes) and skips the action if anything
changed. Destructive actions need a second confirmation (`y`), which the
executor itself requires. If a refresh moves the selection, the supervisor says
so and the next Enter only acknowledges the change.
`agency` actions open the act wizard for their item first, prefilled from the
action's `selector` and `values`, so inputs are collected in the same place as
in Workstream; the wizard only collects. The supervisor then re-resolves the
action with `agency act --action <id> --json` (never running a stored argv),
previews and confirms it as above, and rediscovers it just before running,
skipping it if its command changed. Work and fresh
delegations start in a new, unfocused tab. `prompt_agent` and delegation to a
bound agent require the same agent session still in its pane, idle and not
blocked by a dialog. When that agent is gone or blocked, the supervisor offers a
fresh kickoff with the same instructions; a working agent is never doubled.
Agency commands (including through a path or `bunx`/`npx`) must arrive as
`agency` actions, and only `https` URLs open.
Every outcome, including declined confirmations, is appended to the local
action log; snoozes and dismissals persist in local supervision state under
`$AGENCY_STATE_DIR/supervision`.

### Next Ready Work

`agency next` lists ready execution units in descending unlock priority, with
their epic and task context. `agency next --select` returns only the highest-
priority ready unit in human output.

`agency next --json` returns the same ranked `ready` set plus every `excluded`
execution unit. Excluded entries retain status, terminal state, `blockedBy`, and
detailed dependency, validation, or status blockers for orchestrators.

`agency work` consults this shared readiness model before materializing. Blocked,
done, and dropped targets are rejected unless `--force` is supplied explicitly.
Task-aware `agency pr create` applies Agency readiness and validation checks;
untargeted `agency pr` invocations leave command semantics to `gh`.

### Reconciliation

`agency sync` first compares portable repository declarations with local
materializations, then compares every execution declaration with local branch
and worktree registration, checkout dirtiness, resolved reference commits, and
pull request state, merge state, and mergeability. It reports
structured `changes`, `warnings`, `unresolved`, and per-execution evidence. The
default mode applies safe reconciliation transitions; `--dry-run` is explicitly
observational.

Pass `<task-id>` to scope reconciliation to one task and its repositories. With
no target, invocation inside an epic, task, or phase scopes to that current item;
invocation at the workbase root retains whole-workbase scope. A multi-phase task
scope includes all of its phases; add `[phase-id]` to select one phase. Scoped
sync does not query, materialize, or reconcile unrelated work.

`agency sync` performs only these safe transitions:

- materialize declared but missing repositories from their canonical remotes;
- adopt legacy materializations only when they have an unambiguous portable origin;
- materialize missing checkouts when no registration, branch, or path conflicts;
- record or refresh a single PR whose head and base match the declaration; and
- mark work done after its authoritative PR is merged.

Apply never overwrites linked or invalid repositories, repairs remote drift,
modifies dirty checkouts, moves worktrees, switches branches, resets reference
commits, or chooses among conflicting remotes or PRs.
Those conditions remain visible in `warnings` or `unresolved` with a suggested
action.

### Workbase and Repositories

```text
agency workbase init [path] [--json]
agency init [path] [--json] # Alias
agency workbase add <path> [--name <name>] [--json]
agency workbase list [--json]
agency workbase show <id|name|path> [--json]
agency workbase name <id|name|path> <name> [--json]
agency workbase name <id|name|path> --clear [--json]
agency workbase remove <id|name|path> [--json]
agency workbase prune [--json]
agency workbase default [<id|name|path> | --clear] [--json]
agency integration status [--json]
agency integration sync [--json]
agency repo setup [--dry-run | --apply] [--json]
agency repo add <alias> <remote> [--json]
agency repo link <alias> <path> [--json]
agency repo materialize <alias> [--json]
agency repo list [--json]
agency repo show <alias> [--json]
agency repo fetch <alias> [--json]
agency repo remove <alias> [--json]
agency repo unlink <alias> [--json]
agency repo rename <alias> <new-alias> [--json]
agency repo remote <alias> [remote] [--json]
agency repo verify <alias> [--json]
```

Repository JSON output exposes state facets rather than hiding partial setup:
`declared`, `materialized`, `linked`, `missing`, `invalid`, and
`remote-drifted`. A normal bare clone is declared and materialized; a local
checkout is declared and linked; a fresh workbase clone is declared and missing
until setup is applied.

`repo add`, `link`, `remote`, `rename`, and `remove` update the portable
declaration transactionally with local state. `repo remove` removes both the
declaration and an unused local materialization. `repo unlink` removes only this
machine's symlink and retains the declaration, leaving an actionable missing
state. Linking a local checkout over an unused managed clone likewise retains the
portable remote for other machines. `repo remote` updates managed clones but
never mutates an external linked checkout; drift remains visible until that
checkout is updated explicitly. Credential-bearing URLs, file URLs, and local
paths are never accepted as declarations.

Registered workbases are stored in
`$XDG_CONFIG_HOME/agency/workbases.json` (or `~/.config/agency/workbases.json`).
Each registration has a stable ID and may have a unique name. A default workbase
is used when the current directory is outside every workbase. `prune` removes
registrations whose workbase configuration no longer exists.
`repo add` creates a bare clone. `repo link` creates a symlink to an existing Git
repository. For Git workbases, `repo materialize` replaces a linked alias with a
managed bare clone while migrating its registered worktrees in place; active
Agency references continue to use the same alias. The command refuses remote
drift and stale worktree registrations. Alias names are then used by all
documents and commands. Remove,
unlink, and rename refuse aliases referenced by active work or backed by linked
worktrees, and report each blocker.

Commands that print Agency-owned results accept `--json`, including initialization,
integration inspection/sync, repository mutations, entity creation/list/show,
status, validation, graph export, reconciliation, and PR creation.
Entity create, list, and show results include a stable SHA-256 `revision` of the
complete Markdown document.

### Item Selectors

Commands that act on an existing epic, task, or phase accept the same positional
selectors. A selector is an item ID or a path to the item's document or
directory, or to anything inside it, including its generated `code/` checkouts.
When the selector is omitted, the item containing the current directory is used.
A bare selector is an ID whenever that item exists, so existing ID usage is
unchanged. A `[phase]` selector is `<task> <phase-id>`, a phase path, or a
phase ID of the task containing the current directory. Restore and archive show
selectors refer to archived items under `archive/`. For example, from a phase
directory:

```text
agency phase status working --if-revision <hash>
agency task show .
agency worktree inspect
```

`agency pr create` keeps its `gh pr create` passthrough when no target is given;
use `agency pr create .` for the current task or phase.

### Epics

```text
agency epic new <id> --ticket-url <url> [--description <text>]
  --repo <alias>:<ref> [--repo <alias>:<ref>...] [--work [--auto]]
agency epic create <id> --ticket-url <url> [--description <text>] [--json]
  --repo <alias>:<ref> [--repo <alias>:<ref>...]
agency epic list [filters] [--json]
agency epic show [epic] [--json]
agency epic update [epic] [--ticket-url <url>] [--description <text>]
  [--clear-description] [--repo <alias>:<ref>...] [--json]
agency epic rename [epic] <new-id> [--json]
```

Creating a task with `--epic <id>` adds the task to the epic and writes the task
back-reference.

### Tasks

Create a task interactively with the OpenTUI Solid footer. When exactly one
repository is available, Agency selects it without presenting a redundant
choice. This command requires a TTY and fails with `--no-input`:

```text
agency task new [id] [--work [--auto]]
```

`--work` starts work on the newly created entity. Add `--auto` to pass the
generated context prompt to the selected agent. These launch options are also
available on `epic new` and `phase new`; they cannot be combined with `--json`.

Create a single-phase task:

```text
agency task create <id> --repo <alias>
  [--ticket-url <url>] [--description <text>] [--epic <id>]
  [--reference <alias>:<ref>...] [--branch <name>] [--base <name>] [--json]
```

The branch defaults to `task/<id>` and the base defaults to `main`.
`task create` is always noninteractive and requires `--repo` for a single-phase
task. Use it instead of `task new` in scripts and agent workflows.

For deterministic callers, creation also accepts recalled context:

```text
agency task create <id>
  --context-repo <alias> --context-base <base> --context-slug <id>
  [--authoritative-source <absolute-path-or-http-url>...] --json
```

Recalled values are used instead of rediscovery but must agree with equivalent
explicit flags. The preferred slug must equal the created ID, repository aliases
are validated by normal task creation, and authoritative sources must be absolute
paths or HTTP(S) URLs.

Machine output adds fields without changing the version 1 protocol envelope. It
includes `selector`, absolute `documentPath`, the document `revision`, full
`validation`, normalized `recalledContext`, and `evidence`. Evidence version 1 is
an auditable local payload containing the canonical workbase root, target and
document identities, aggregate workbase revision, configuration revision,
repository-mapping revision, execution-contract version, validity result, recalled
context, and a digest over those fields. It is not a signature or an authority
grant. A different workbase, target, document revision, document set,
configuration, repository mapping, contract version, or payload digest
invalidates reuse. Older creation output without evidence remains compatible;
preflight simply validates again. The published machine schema is
`schemas/agency-execution-v1.schema.json`.

Create investigation-only work with the existing task architecture:

```text
agency task create <id> --purpose investigation --repo <alias> [options]
```

This records `purpose: investigation` and generates explicit Investigation
Boundary, Evidence, Findings, Recommendation, Implementation Handoff, and
Important Decisions sections. A no-change recommendation is a valid result;
implementation does not belong in the investigation task merely because that
task has execution authority.

Create a distinct implementation task from an investigation task or phase:

```text
agency task handoff <investigation-task> <new-task> --repo <alias> [options] --json
agency task handoff <investigation-task> <new-task> --source-phase <phase> --repo <alias> [options] --json
```

The destination ID must be new across active and archived work; Agency never
restores, reopens, renames, or reuses a matching item. Explicit requests for a
new, separate, or follow-up item always override reuse, even when the subject or
suggested ID matches existing work. The transaction writes only the destination
task and any requested epic backlink, while revision-checking the source.

JSON returns the exact destination selector, directory and document path,
branch/base and revision; source selector, document path and captured revision;
the validation report; and the later `worktree prepare` target and command.
Handoff creation does not prepare a worktree, change status, launch an agent, or
perform UI actions. Run `agency context <new-task> --json` to verify authority,
and prepare or launch only when separately requested.

Create a multi-phase task container:

```text
agency task create <id> --multi-phase
  [--ticket-url <url>] [--description <text>] [--epic <id>] [--json]
```

Create a pinned, read-only review task from the selected alias's origin:

```text
agency task create <id> --review <alias> --pull-request <url-or-number>
agency task create <id> --review <alias> --ref <remote-ref>
agency review refresh [task] [--if-revision <hash>] [--json]
agency review finish [task] [--if-revision <hash>] [--json]
```

Review creation fetches the source and records its exact 40-character commit.
GitHub pull requests use the base alias's `refs/pull/<number>/head`, including
fork pull requests exposed through that ref. Review workspaces contain one
detached checkout and no writable branch. Source movement is observed separately
from the pin and applied only by `review refresh`; sync, doctor, work, cleanup,
and archive never move the pin implicitly. Dirty or structurally unexpected
review checkouts block refresh and cleanup. Review tasks support the normal status
lifecycle, but reject phase conversion and delivery PR operations. Because a
review has no delivery pull request, `review finish` marks an open, working, or
delegated review done without the `--no-pull-request --summary` requirement
that applies to other work. Both commands take a standard task selector (see
Item Selectors), such as `.` or the review checkout path.
Each active or archived review task owns one internal task-scoped pin ref. A
refresh advances that ref transactionally; failed creation removes it. Archiving
retains the pin so a deleted source can still be restored and inspected.

### Noninteractive Use

Agency never prompts when `--no-input` is set or stdin/stderr are not TTYs.
`--json` also disables prompts and selectors, even when a TTY is available.
Commands with explicit inputs continue normally. `--workbase <id|name|path>`
selects a workbase directly; `--cwd <path>` performs the same inference Agency
would perform from that directory. These options are mutually exclusive and take
precedence over ambient cwd and the configured default.

Targeted commands accept `--epic`, `--task`, and `--phase` where those entity
kinds apply. A phase selector requires a task selector. Entity selectors cannot
be mixed with positional target IDs, and an epic selector cannot be mixed with
task or phase selectors. This makes commands such as
`agency phase status working --task ship --phase release --workbase primary --no-input`
fully independent of process cwd and prompts.

Inspect tasks:

```text
agency task list [filters] [--json]
agency task show [task] [--json]
agency task status [task] <open|working|done|dropped>
  [--no-pull-request --summary <text> [--evidence-url <url>]] [--json]
agency task update [task] [metadata options] [--json]
agency task rename [task] <new-id> [--json]
agency task move [task] (--epic <epic-id> | --no-epic) [--json]
agency task dependency <add|remove> [task] <dependency> [--json]
```

Task updates can replace or clear descriptions, tickets, repository references,
and pull request URLs, or replace writable repository, branch, and base metadata.
Repository, reference, and branch changes refuse to run while code is
materialized. A base change is allowed on a materialized task or phase only when
its checkout is on the declared branch, clean, has no rebase or merge in
progress, and HEAD already descends from the new base (the delivery remote's
tracking ref, or the local branch when no tracking ref exists). Rebase the
branch first, then record the new base. Moving a
task with scoped incoming or outgoing dependencies also refuses until those
dependencies are removed.

To add a phase to an existing single-phase task, name the phase that will own
the task's current execution fields with `--first-phase`:

```text
agency phase create refresh-copy verification
  --first-phase implementation
  --repo frontend --branch task/refresh-copy-verification --base main
  --depends-on implementation
```

Agency converts `TASK.md` to the multi-phase shape, creates both phase documents,
and moves existing worktrees from the task's `code/` directory into the first
phase. Dependencies remain explicit through `--depends-on`.

### Phases

```text
agency phase new [task] <phase-id>
  --repo <alias> --branch <name> --base <name> [--work [--auto]]
agency phase create [task] <phase-id>
  --repo <alias> --branch <name> --base <name>
  [--description <text>] [--reference <alias>:<ref>...]
  [--depends-on <phase-id>...] [--first-phase <phase-id>] [--json]

agency phase list [task] [filters] [--json]
agency phase show [phase] [--json]
agency phase status [phase] <open|working|done|dropped>
  [--no-pull-request --summary <text> [--evidence-url <url>]] [--json]
agency phase update [phase] [metadata options] [--json]
agency phase rename [phase] <new-id> [--json]
agency phase dependency <add|remove> [phase] <dependency> [--json]
```

Dependency additions append without reordering existing declarations and reject
unknown IDs, self-dependencies, and cycles. Rename operations update structured
references as one rollback-capable mutation and refuse when a materialized
worktree would make the directory move unsafe. Mutation JSON includes changed
paths and the focused validation scope.

Epic, task, and phase update, rename, move, and dependency mutations accept
`--if-revision <hash>`. The option is optional for interactive human use. When
provided, Agency fails with a structured `REVISION_CONFLICT` containing the
expected and current revisions if the target changed. Multi-document mutations
also recheck every affected document after taking the mutation lock and before
writing anything.

Single-phase tasks and phases store status in YAML. New execution units start
`open`, and `agency work` marks the selected execution unit `working` immediately
before launch. Running `agency work` again can relaunch `working` work.
By default, `done` requires an authoritative merged pull request and is applied
by `agency sync`. Work whose intended outcome genuinely requires no pull
request may instead use an explicit `--no-pull-request --summary <text>` status
transition. Agency records the summary, completion time, and optional evidence
URL durably; reopening removes that evidence. This exceptional path refuses work
that already has a recorded pull request.
The interactive work selector displays status markers before execution units.
Existing working and delegated work may be returned to `open` or assigned a
terminal outcome. Done and dropped work are terminal and may only
remain unchanged or transition to open; reopen terminal work before changing its
outcome.

`delegated` remains readable for existing workbases but cannot be newly assigned.

Human list output is a compact table with lifecycle, readiness, parent,
repository, branch, recorded PR, and worktree state where applicable. List and
status views accept composable `--status <status>` and `--repository <alias>`
filters, plus `--ready`, `--blocked`, `--pr`, and `--no-pr`. Status and repository
filters are repeatable. Rows follow task and phase declaration order; plain text
labels remain complete without color or icon fonts.

### Archive

```text
agency archive list [--kind <kind>] [--status <status>] [--repository <alias>]
agency archive show <epic|task> [id-or-path]
agency archive show phase [phase]
agency archive epic [epic] [--dry-run] [--json]
agency archive task [task] [--dry-run] [--json]
agency archive tasks [--dry-run] [--json]
agency archive phase [phase] [--dry-run] [--json]
agency archive <path> [--dry-run] [--json]
agency archive [--dry-run] [--json]
agency restore epic [epic] [--dry-run] [--json]
agency restore task [task] [--dry-run] [--json]
agency restore phase [phase] [--dry-run] [--json]
```

With no target, `agency archive` infers the active epic, task, or phase containing
the current directory. An existing path within one of those items is inferred
the same way. Collection roots are ambiguous, and paths outside active work item
trees are rejected. Archived work keeps its hierarchy under
`archive/`. Epic archiving includes its
listed tasks. A task can be archived only when its effective status is terminal
(`done` or `dropped`). Multi-phase task status is derived from its phases, every
phase must be terminal, and a task with no phases is not eligible.

`archive tasks` plans the maximal safe cohort of terminal tasks and applies by
default; `--dry-run` performs the same preflight without mutation. A dependency
is work required by a candidate, while a dependent is work that requires the
candidate. Dependencies within the selected cohort can be archived together,
but a retained dependent excludes its dependency, including exclusions that
propagate through a dependency chain. Dirty or otherwise unsafe managed
checkouts and occupied archive destinations are reported as per-task
skips. Invalid workbase structure and infrastructure failures abort the command.

Task and phase archiving update active parent documents. Agency removes
registered worktrees before moving files, refuses dirty worktrees, and preserves
branches. Bulk application updates shared parents once and archives the entire
selected cohort in one rollback-capable transaction; it never archives an empty
parent epic implicitly. Versioned lifecycle provenance preserves parent
declarations and dependency edges for restoration. Archived IDs are reserved
until restored.

#### Automatic archiving

```text
agency config auto-archive                 # show the effective setting
agency config auto-archive on              # enable for this workbase
agency config auto-archive off             # disable
```

The boolean `autoArchive` setting in the workbase's `agency.json` defaults to
`false`. Enabling it authorizes automatic task archiving on future terminal
transitions; it does not sweep existing terminal work. `agency act --json`
lists these configuration commands, and context/status JSON includes the
effective setting.

Explicit `task status` / `phase status` changes to `done` or `dropped`, non-PR
completion, `review finish`, and merged-PR reconciliation automatically
attempt to archive the owning task. A multi-phase task waits until every phase
is terminal; individual phases and epics are never auto-archived. Sync defers
cleanup until its reconciliation pass finishes, and `sync --dry-run` never
archives anything.

Automatic cleanup uses the same preflight as `archive task --dry-run`, without
forcing past dirty checkouts, retained dependents, or other safety
failures. A blocked attempt leaves the terminal status intact and the task
unarchived, with the reason reported in human output and the triggering command's
JSON `autoArchive` result (`sync` returns an array). After resolving the blocker,
use explicit `archive` to retry safely. Successful automatic archiving removes
clean managed checkouts, so paths inside the task may cease to exist.

### Work, Publication, and Pull Requests

```text
agency work [<directory> | --epic <epic-id>] [--agent <name>] [--auto [--instructions <text>]] [--print-command]
agency work prepare [target] [--evidence <json-or-path>] [--force] [--dry-run] [--json]
agency worktree <list|inspect|prepare|remove|rebuild|repair>
agency push [--force-with-lease] [--json]
agency rebase [task [phase-id]] [--onto <branch>] [--from <commit>] [--dry-run] [--if-revision <hash>] [--json]
agency rebase [task [phase-id]] --continue | --abort [--json]
agency pr create <task> [phase-id] [--draft] [--title <title>] [--head <branch>] [--base <branch>] [--label <label>] [--force] [--force-with-lease] [--json]
agency pr [args...]
```

Inside an epic, task, or phase, `agency work` defaults to that current item.
At the workbase root it presents the full hierarchy in the native OpenTUI
selector or the configured external chooser. An explicit directory can target a
different item. Outside a workbase, Agency first presents the registered
workbases, then the selected workbase's hierarchy.

Agency automatically uses the first available agent in this order: `opencode2`,
`opencode`, then `claude`. `--opencode` and `--claude` remain aliases for
requiring their corresponding built-in presets. Launches are interactive and
promptless by default; use `--auto` to send Agency's generated context prompt.
`--instructions <text>` appends supervisor instructions to that prompt.

`agency work prepare` resolves an execution unit and creates or reuses its
writable and reference worktrees, or its single pinned review checkout, without
launching an agent or changing status. Its target may be a task ID or any path
inside the task or phase, including its `TASK.md` or `PHASE.md` document.
Managed workbase guidance provides exact fast paths for common intents:
single-phase creation; create-and-start; materialization; remote PR sync; phase
conversion; archive; review creation and start; status inspection; drop;
continuation; publication and PR creation; non-PR completion; initial multi-phase
setup; investigation handoff; review refresh and finish; and base changes.
Its JSON result includes the workspace, validation result, whether supplied
evidence was `reused` or `refreshed` with stable reason strings, refreshed
evidence, and a versioned `agency-execution-v1` contract. The contract reports
whether preparation was previewed or applied, a stable target identity, prepared
workspace and document paths, and canonical Agency-native commands for starting
work and reading context. It does not prescribe how callers present or execute
prepared work.
The evidence argument may be an evidence object, task-creation JSON, or a path to
either. Use `--dry-run` to report planned fetch, branch, and worktree changes
without applying them. Validation reuse never skips
readiness, repository, ownership, reference-drift, dirty-workspace, or worktree
safety checks.

Both `work` and `work prepare` accept `--allow-working-dependencies` with an
explicit execution-unit target (task ID, directory, or `--task` with optional
`--phase`). This narrowly admits an `open` unit whose blockers are exclusively
dependencies with status `working`. Open, delegated, dropped, or missing
dependencies and mixed validation/status blockers remain blocked; terminal
targets cannot be reopened. Ready and normally resumable working units retain
their existing behavior. Interactive target selection, orchestration targets,
and combining this option with `--force` are rejected. Validation,
repository/reference checks, dirty-worktree protections, and active worktree
locks are unchanged, including in dry-run mode.

Preparation never launches or changes status. Its `commands.work.argv` preserves
the explicit opt-in, but validation evidence does not store it or authorize later
calls: pass the flag again for each prepare/work invocation. Dependency changes
invalidate evidence normally, and readiness and locks are checked again even
when evidence is reused. Execution identity and evidence schemas are unchanged.
The existing broader `--force` behavior, including lock overrides and reopening
terminal work on launch, is unchanged for callers using it alone.

The authoritative implementation locations for this contract are
`src/commands/task.ts` (creation output),
`src/workbase/execution-contract.ts` (evidence and execution schemas),
`src/commands/work.ts` (launch preflight),
`src/services/WorktreeService.ts` (workspace safety), and
`src/workbase/AGENTS.md` (generated OpenCode guidance). These paths are the
deterministic source-location fixture for compatible orchestrators.

`agency worktree list` and `inspect` report each declared checkout's expected and
registered path, branch, commit, Agency owner, dirtiness, and conflicts. `prepare`
is the explicit lifecycle form of `agency work prepare`. `remove`, `rebuild`, and
`repair` preflight every writable and reference checkout before changing any of
them and accept `--dry-run`. Removal preserves branches. Rebuild rejects dirty or
conflicting worktrees. Repair is deliberately conservative: it repairs safe Git
registration issues and materializes missing checkouts, but never switches a
branch, resets a commit, or discards uncommitted work.

Agency launches every agent beside its epic or task document. Single-phase tasks
and phases first fetch repositories and create or reuse worktrees under `code/`,
then launch the execution agent from the task directory with absolute context
paths. An explicit directory or `--epic` target bypasses the hierarchy chooser.

Each writable `(repo, branch)` pair may belong to only one task or phase. Agency
validation reports duplicate ownership, and `agency work` checks Git's worktree
registry before creating or reusing a checkout. It reuses only an exact
path/branch match; if the branch is checked out elsewhere or the target path has
the wrong branch, the command fails with the conflicting path instead of forcing
another checkout.

Read-only references use `<alias>:<ref>` on the CLI and `{ repo, ref }` in YAML.
Agency resolves the ref to a commit and creates a detached worktree. Existing
reference worktrees are reused only while their commit still matches the declared
ref; use a commit SHA as `ref` when reproducibility matters.

`agency push` publishes the execution unit identified by current Agency context
without creating a pull request. It requires a valid, registered writable
checkout in `working` state, fetches the configured delivery remote, verifies
that the declared base is in the publication history, validates every outgoing
commit's description, author, and conflict state, and refuses non-fast-forward
updates unless `--force-with-lease` is given. Local commit validation runs before network access when cached remote
state is available, and the network fetch is limited to the declared base and
delivery branch.

YAML `branch` must exactly match the checked-out local branch, the worktree must
be clean, and `HEAD` is pushed with upstream tracking. Missing commit descriptions
or authors stop publication with exact commits and remediation commands. Push
reports deterministic fetch, inspection, validation, and publication progress on
stderr, including while `--json` reserves stdout for one machine result. Git
authentication is non-interactive. Fetch defaults to a 30-second deadline and
one retry for transient failures; push defaults to a 120-second deadline. Set
`AGENCY_PUSH_FETCH_TIMEOUT_MS` or `AGENCY_PUSH_TIMEOUT_MS` to positive millisecond
values to override them. After a failed or timed-out push, Agency compares the
exact remote delivery ref with the expected tip before reporting success, a safe
retryable failure, or an unknown publication outcome.

A non-fast-forward update fails with `PUSH_REMOTE_DIVERGENCE`. When the local
branch rewrote the remote tip it last observed (after an amend, rebase, or
squash), the failure reports `rewritten: true` and remediation naming
`agency push --force-with-lease`. That option leases against the remote-tracking
tip recorded before the fetch: publication is refused if the remote branch moved
since then, if it existed but was never observed locally, or if its tip is not in
the local branch's reflog (fetched but never integrated). Otherwise Agency pushes
with `--force-with-lease=refs/heads/<branch>:<observed>` and reports the replaced
tip as `forcedOver`. `agency pr create --force-with-lease` applies the same lease
and reflog checks to its publication, and its non-fast-forward failures use the
same code and remediation. Human-mode failures print the remediation line.

`agency rebase` moves a materialized execution unit's branch onto a new base and
records that base only after Git finishes. It requires a non-terminal unit whose
checkout is clean, on the declared branch, and has no Git operation in progress.
It fetches the current and new bases from the delivery remote (falling back to
local refs with a warning when the remote is unreachable) and runs
`git rebase --onto <new-base> <merge-base-with-current-base>`, so commits that are
reachable only from the old base are not replayed. `--from <commit>` overrides
that starting point, which is useful when the old base no longer resolves.
Without `--onto`, the branch is rebased onto the latest current base and no
metadata changes. `--dry-run` reports the commits to replay and drop without
rebasing. On success the `base` field and a `## Base History` entry are written
under the revision captured when the rebase started.

When Git stops on conflicts, the command fails with `REBASE_CONFLICT`, the
conflicted paths, and the exact `--continue` and `--abort` commands; base
metadata is unchanged until `--continue` completes the rebase. Rebase never adds
or removes dependencies. It warns when rewritten history must be republished
with `agency push --force-with-lease`, when a recorded pull request
still targets the old base (change it before `agency sync`, which adopts the
pull request base), and when other units are based on the rebased branch.

`agency act` offers **Rebase onto a new base** for materialized, non-terminal
execution units. While an Agency rebase is stopped on conflicts, it offers
**Continue rebase** and **Abort rebase** instead. In the TUI, the first rebase
warning is shown in the status line, and every warning appears in the exit
recap.

Task-aware `agency pr create <task-id> [phase-id]` uses Agency's delivery flow,
including readiness checks and durable PR recording. It accepts draft, title,
declared head/base confirmation, and repeatable label options; a contradicting
head or base is rejected rather than recording inconsistent delivery metadata.
Other `agency pr`
invocations forward every argument to `gh pr`. From an execution task or phase
directory, including descendants, passthrough runs in that execution unit's
authoritative writable checkout. Otherwise it runs in the caller's current
directory.

### Status and Validation

```text
agency status [filters] [--json]
agency validate [path] [--json]
```

Validation checks JSON and YAML parsing, Effect Schema conformance, repository
aliases, parent/child backlinks, phase directories, duplicate references,
unknown dependencies, and dependency cycles. YAML duplicate keys, anchors,
aliases, and custom tags are rejected. When path is omitted outside a workbase,
Agency prompts for a registered workbase.

## Machine Protocol

`--json` emits exactly one JSON value on stdout for success or failure. It takes
precedence over `--silent`; progress, warnings, and verbose diagnostics remain on
stderr. Version 1 success responses have this shape:

```json
{ "version": 1, "ok": true, "result": { "root": "/work/agency" } }
```

Failures exit nonzero and use the same versioned envelope:

```json
{
	"version": 1,
	"ok": false,
	"error": {
		"code": "CLI_USAGE",
		"message": "Unknown command 'unknown'.\n\nUsage: agency <command> [options]",
		"fields": {
			"detail": "Unknown command 'unknown'.",
			"usage": "agency <command> [options]"
		},
		"retryable": false,
		"remediation": "Correct the arguments using the usage value in error.fields."
	}
}
```

Every error contains a stable `code`, human-readable `message`, structured
`fields`, and `retryable`. `remediation` is included when Agency knows a specific
recovery action. Version 1 defines these codes:

| Code                      | Meaning                                                   |
| ------------------------- | --------------------------------------------------------- |
| `CLI_USAGE`               | Invalid command, option, argument, or option combination  |
| `WORKBASE_NOT_FOUND`      | No workbase could be resolved                             |
| `WORKBASE_CONFIG_INVALID` | Invalid workbase configuration                            |
| `WORKBASE_REGISTRY_ERROR` | Invalid or inaccessible workbase registry                 |
| `FILE_NOT_FOUND`          | A required path does not exist                            |
| `FILESYSTEM_ERROR`        | A filesystem operation failed                             |
| `FRONTMATTER_INVALID`     | A durable document has invalid frontmatter                |
| `VALIDATION_FAILED`       | Workbase validation reported issues                       |
| `REPOSITORY_ERROR`        | Repository operation failed                               |
| `EPIC_ERROR`              | Epic operation failed                                     |
| `TASK_ERROR`              | Task operation failed                                     |
| `PHASE_ERROR`             | Phase operation failed                                    |
| `REVISION_CONFLICT`       | A durable document changed since inspection               |
| `ARCHIVE_ERROR`           | Archive operation failed                                  |
| `WORKTREE_ERROR`          | Worktree operation failed                                 |
| `PULL_REQUEST_ERROR`      | Pull request operation failed                             |
| `CONTEXT_ERROR`           | A context target or required document is invalid          |
| `GRAPH_ERROR`             | Workbase graph construction failed                        |
| `EXECUTION_BLOCKED`       | Readiness or lifecycle blockers prevent execution         |
| `SYNC_ERROR`              | Reconciliation validation, inspection, or provider failed |
| `PROCESS_ERROR`           | A child process failed and may be retried                 |
| `PROTOCOL_OUTPUT_ERROR`   | A command violated the machine output contract            |
| `COMMAND_FAILED`          | An otherwise unclassified command failure                 |

The Effect schemas are exported from `@markjaquith/agency` and
`@markjaquith/agency/protocol`. The distributable JSON Schemas are exported as
`@markjaquith/agency/schemas/agency-envelope-v1.json` and
`@markjaquith/agency/schemas/agency-graph-v1.json`. Representative envelope
payloads are exported as `@markjaquith/agency/fixtures/protocol/success.json` and
`@markjaquith/agency/fixtures/protocol/error.json`.

Success, help, and version output exit `0`; usage and command failures exit `1`.
There are no error-specific exit statuses. `graph --jsonl` streams versioned
records on success instead of wrapping them in an envelope; JSONL failures still
use one error envelope. Revision and concurrency behavior is documented under
Tasks and Phases; selector behavior under Noninteractive Use; projection
behavior under Target Context and Workbase Graph; and retry behavior in the
machine error contract above.

## Development

```bash
bun install
bun link
bun run build
```

Run focused tests with `bun test <test-file>`. Run formatting with `bun format`.

Run `bun run test:global-install` for the network-backed package smoke test. It
packs the checkout and uses temporary Bun global directories, caches, and homes
to test clean installation and coexistence with newer global TypeScript and
web-tree-sitter versions. It checks peer resolution, the installed CLI, native
rendering, and the packaged Pi installation hook.

OpenTUI requires web-tree-sitter `0.25.10`, and its bun-ffi-structs dependency
requires TypeScript `^5`. Agency declares these as production dependencies and
bundles the OpenTUI packages so Bun's global hoisting cannot separate them from
their compatible peers. Keep the root optional native packages aligned with
`@opentui/core`'s optional dependencies when updating OpenTUI: they let installs
on other platforms fetch binaries absent from the packing host. The smoke test
also verifies installation with the bundled host binary removed.

The TUI uses `@opentui/core` and `@opentui/solid`, pinned together at 0.5.11,
with the binding's required `solid-js` 1.9.12. Follow the upstream
[Solid bindings](https://github.com/anomalyco/opentui/blob/v0.5.11/packages/web/src/content/docs/bindings/solid.mdx) and
[lifecycle guidance](https://github.com/anomalyco/opentui/blob/v0.5.11/packages/web/src/content/docs/core-concepts/lifecycle.mdx):

- Use Solid signals and OpenTUI hooks for reactive state, keys, and dimensions.
- Use declarative `focused` and component `keyBindings`/`onSubmit` for local
  input behavior; reserve `useKeyboard` for navigation and shared shortcuts.
- The renderer creator owns cleanup. `destroy()` restores terminal modes and
  output streams and disposes the Solid root; keep it in a cleanup/finalizer path.
- Keep the lazy Solid preload in `interactive-loader.ts` so non-TUI commands
  avoid initializing the renderer and runtime-loaded TSX uses the right transform.
- Verify upgrades with the UI and PTY tests, including worker handoffs and
  terminal restoration.

The `@babel/core` override selects 7.29.6 to fix
[GHSA-4x5r-pxfx-6jf8](https://github.com/advisories/GHSA-4x5r-pxfx-6jf8);
OpenTUI Solid 0.5.11 otherwise pins vulnerable 7.28.0. Remove the override when
the upstream dependency resolves to a patched version, after running `bun audit`
and the TUI tests. Effect 4 consolidates Schema into the `effect` package, so
there is no separate `@effect/schema` dependency; Effect 4.0.0 is outside the
[GHSA-38f7-945m-qr2g](https://github.com/advisories/GHSA-38f7-945m-qr2g)
range that previously set the 3.20.0 minimum.

### Guided-flow ownership

The `act` implementation has four boundaries:

| Module                        | Responsibility                                                                                               |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `src/commands/act-actions.ts` | Defines availability, discovery argv, and executable plans using existing lifecycle commands.                |
| `src/commands/act-prompts.ts` | Collects inputs and owns wizard replay. Preparation must be replayable; only `plan.run` performs operations. |
| `src/commands/act.ts`         | Coordinates navigation, checks the selected item's freshness, executes plans, and records the recap.         |
| `src/utils/interactive.tsx`   | Owns rendering, focus, keyboard handling, and terminal restoration; it has no workbase lifecycle logic.      |

The Effect scope owns the interactive session. A worker handoff explicitly closes
that session and permits a new one afterward. External renderer destruction
(such as SIGTERM) requests application shutdown and interrupts in-flight work;
it must never be interpreted as Back or reopen the interface. PTY regressions
exercise both paths against real processes and check terminal restoration.

## License

MIT
