Project Board: instructions for AI agents
This repository plans its work in a Project Board file (*.pb): issues, milestones, Kanban columns, labels and
requirements in one JSON file that is versioned together with the code. You may read it and you may change it.
This sheet tells you how to do both without damaging it.
- Reference for every field and every event: https://github.com/arthurafarias/vscode-extension-project-board/blob/master/docs/pb-file-format.md
- JSON Schema of the file: https://raw.githubusercontent.com/arthurafarias/vscode-extension-project-board/master/schemas/pb-2.1.schema.json
1. Read the board
- Find the file:
git ls-files '*.pb'. - Look at
pbVersion. This sheet is for"2.1". If the file says anything else, change nothing and tell the user: an older file has to be upgraded first, a newer one needs a newer tool. - Read both parts of the file together:
treeis the board as it was when the file was last consolidated;walis the list of changes made since then, one event per line, oldest first.
The board is
treewith every event ofwalapplied in order. An issue that an event inwalclosed still says"state": "open"intree. Never answer fromtreealone. - If you can run commands, let the tool do the replay:
node <extension folder>/out/cli.js show <file> --json.
Where things are in tree:
| Member | Holds |
|---|---|
project, owner |
name and description of the project; who owns the board |
workflow |
the Kanban columns in board order; each has an id, a name and a category (todo, doing, done) |
labels |
id, name, color, description |
milestones |
id, title, description, dueDate, state (open, closed) |
issues |
id, number, kind (task, requirement, heading), title, body, state (open, closed), assignee, labelIds, milestoneId (null: backlog), status (the id of the Kanban column), parentId, startDate and dueDate (planned dates, YYYY-MM-DD or null), comments, links |
activity |
the history of the board, newest first |
People say “#12”; events say iss-…. Events name issues, labels, milestones and columns by id, never by number
or by name, so look the id up first. Something that was created in wal has its id in its create event.
2. Change the board with the command line tool
This is the safer way: the tool looks up ids, numbers the events and writes the lines. When the user has the board open in the editor, the tool hands the change to that editor instead of writing the file, so the user sees it at once and nothing they are typing is lost.
pb <command> [file] … in a VS Code terminal, where the extension puts pb on the PATH
node <extension folder>/out/cli.js <command> [file] … anywhere else
pb show [--json]
pb issues [--state open|closed|all] [--milestone <title>] [--label <name>] [--search <text>] [--json]
pb issue <number> [--json]
pb add-issue "<title>" [--body <b> | --body-file <path or ->] [--label <name>]… [--milestone <title>] [--kind task|requirement|heading]
pb edit <number> [--title <t>] [--body <b>] [--milestone <title|none>] [--add-label <name>] [--remove-label <name>] [--assignee <name>] [--start <date|none>] [--due <date|none>]
pb comment <number> --body <b> | --body-file <path or ->
pb close <number>… [--comment <text>]
pb reopen <number>…
pb move <number> <column name | todo | doing | done>
pb link <number> <satisfies|derives|verifies|refines|depends|conflicts|relates> <target number>
pb add-milestone "<title>" [--due <yyyy-mm-dd>]
pb apply --actions <json file or -> a list of { action, payload, ref? }
pb apply --events <json file or -> raw events as in section 3, without seq
pb validate | upgrade | consolidate | where
pb report --out <dir> [--format <id>]
pb export --format <id> [--out <path>]
pb import --from <path> [--format <id>] [--dry-run]
- The board is the
.pbfile named right after the command, else--file, elsePB_FILE, else the only.pbfile in the current folder. - Edits are signed with
--as <name>, elsePB_ACTOR, else “Claude” inside Claude Code, else the git user. show,issuesandissueprint the board as it is now; with--jsonthey give output to parse.- Issues are named by number, labels by name, milestones by title or a unique part of the title.
--body-file -reads the text from standard input. Use it for Markdown with several lines.apply --actionsruns many changes in one step."ref": "a"names what an action creates and"$a"uses it in a later one.apply --eventsappends the events of a JSON array, written as in section 3 but withoutseq: the tool numbers them.validatechecks a file and reports what is wrong with it. Run it after you edited the file by hand.consolidatefoldswalintotreeandupgradeconverts a file of an older format version. Both rewrite the whole file: run them only when the user asks for it.- Exit code 0 means done, 1 means the board refused (the message says why), 2 means the command was mistyped.
cli.md has the full reference.
3. Change the board by appending event lines
Use this when you cannot run the tool. An event is one line of JSON at the end of the wal list.
- Find the last line of
wal, the line above the closing ` ]near the end of the file, and read itsseq`. Your first event gets that number plus one, and each further event one more. - Write each event on one line: four spaces, a comma, then the event as compact JSON without line breaks. Put your lines directly above the closing ` ]`. Do not change anything else in the file.
- Give every event
seq,at(the current time in UTC, written like2026-03-04T10:00:00.000Z),by(your own name),op, and the members of that operation. - Give everything you create a new
id: a prefix, a dash and 12 random hexadecimal digits, for example fromopenssl rand -hex 6. The prefixes areiss(issue),c(comment),ms(milestone),lbl(label) andlnk(link). Do not copy the ids of the examples below. - Read the board again and check that the file is still valid JSON and that your change is there.
The examples continue a board whose last line has seq 6. It has an issue #2 with the id iss-2b4d6f8a0c1e, a
label “bug” with the id lbl-5e001f110423, a milestone “v1.0” with the id ms-1f6a0c93b2d4 and a column
“In progress” with the id doing.
Open an issue. Leave number out and the issue gets the next free number, here #3. The body is Markdown;
line breaks are written as \n.
,{"seq":7,"at":"2026-03-04T10:00:00.000Z","by":"Claude","op":"issue.create","issue":{"id":"iss-4f7d2c9b1a3e","title":"Reject CSV files larger than 10 MB","body":"Found while reading `src/import.ts`: the whole file is read into memory.\n\n- [ ] check the size before reading\n- [ ] show a clear error","kind":"task"}}
Comment on an issue. issueId is the issue, comment.id is new.
,{"seq":8,"at":"2026-03-04T10:01:00.000Z","by":"Claude","op":"comment.add","issueId":"iss-2b4d6f8a0c1e","comment":{"id":"c-8e1f3a5b7c9d","body":"Proposed layout: `docs/<milestone>.md`, one `##` section per issue."}}
Close an issue, and reopen it. The card moves to the Done column, and back, on its own.
,{"seq":9,"at":"2026-03-04T10:02:00.000Z","by":"Claude","op":"issue.update","id":"iss-2b4d6f8a0c1e","set":{"state":"closed"}}
,{"seq":10,"at":"2026-03-04T10:03:00.000Z","by":"Claude","op":"issue.update","id":"iss-2b4d6f8a0c1e","set":{"state":"open"}}
Put an issue into a milestone and a Kanban column. milestoneId null is the backlog; status is the id
of a column of tree.workflow.
,{"seq":11,"at":"2026-03-04T10:04:00.000Z","by":"Claude","op":"issue.update","id":"iss-4f7d2c9b1a3e","set":{"milestoneId":"ms-1f6a0c93b2d4","status":"doing"}}
Add a label to an issue. labelIds replaces the whole list: write every label the issue is to have
afterwards, the ones it already has included.
,{"seq":12,"at":"2026-03-04T10:05:00.000Z","by":"Claude","op":"issue.update","id":"iss-4f7d2c9b1a3e","set":{"labelIds":["lbl-5e001f110423"]}}
Create a milestone, and close it. Dates are written as YYYY-MM-DD.
,{"seq":13,"at":"2026-03-04T10:06:00.000Z","by":"Claude","op":"milestone.create","milestone":{"id":"ms-6b0d2f4a8c1e","title":"v1.1","description":"Import and export.","dueDate":"2026-06-30"}}
,{"seq":14,"at":"2026-03-04T10:07:00.000Z","by":"Claude","op":"milestone.update","id":"ms-6b0d2f4a8c1e","set":{"state":"closed"}}
Create a requirement and link a task to it. A requirement is an issue with "kind":"requirement". The link
is added to the task (issueId) and points at the requirement (targetId): the task satisfies the
requirement. The other link types are derives, verifies, refines, depends, conflicts and relates.
,{"seq":15,"at":"2026-03-04T10:08:00.000Z","by":"Claude","op":"issue.create","issue":{"id":"iss-7a9c1e3b5d2f","kind":"requirement","title":"The importer shall refuse files larger than 10 MB","body":"The limit is checked before the file is read."}}
,{"seq":16,"at":"2026-03-04T10:09:00.000Z","by":"Claude","op":"link.add","issueId":"iss-4f7d2c9b1a3e","link":{"id":"lnk-3c5e7a9b1d4f","type":"satisfies","targetId":"iss-7a9c1e3b5d2f"}}
Other fields that set of an issue.update accepts: title, body, assignee, kind, parentId, attrs.
Every other operation (labels, attachments, baselines, deleting things) is in the reference.
4. Rules
- Append only. Never rewrite, reorder or delete an existing line of
wal. To correct something, append another event that sets it right. - Never edit
treeby hand. It changes only through consolidation, and consolidation is the user’s call. - Never reuse an
idor aseq. A create event with an id that exists is ignored. - One event per line, one line per event. That keeps the changes of two branches mergeable.
- Keep the file valid JSON. A file that does not parse cannot be opened at all. Check it after every edit.
- Write Markdown in bodies: issue descriptions, comments and milestone descriptions. Titles are plain text.
- Sign as yourself.
byis the name of whoever made the change. An agent writes its own name, for exampleClaudeorCopilot, and never the name of the user or of a teammate. - Leave
remotesand theconnection.…events alone. They are the bookkeeping of the synchronization with remote trackers.
5. When an event is wrong
The reader of the file is tolerant: an event with an unknown op, with a target that does not exist or with an
id that is already taken is skipped with a warning, and the rest of the board is read as usual. The file is
not damaged, but your change did not happen. So after writing, read the board again (show, or tree plus
wal) and make sure the result is what you meant. If it is not, append a corrected event; do not edit the line
you wrote once it has been committed.