---
name: pm
description: Arrange the repository's public .pm adoption board — show status, create workstreams, capture inbox notes, promote notes into milestones, add tasks, mark work done, park blocked work with its unblock condition, and drop work that should no longer be done. Use only when the user explicitly invokes $pm or asks to update, arrange, or check the .pm board. Do not use for proposing or brainstorming new work (that is pm-brainstorm) or for ordinary code edits.
allowed-tools: Read, Write, Edit, Bash(ls:*), Bash(find:*), Bash(cat:*)
---
# Task: Arrange the `.pm` board
Usage: `/pm [status | new workstream
| add | promote | new milestone | add-task | done | block | unblock | drop ]`
`/pm` is the **only** skill that writes to `.pm/`. It arranges milestones and tasks under the conventions below. `/pm-brainstorm` proposes; `/pm` materializes. This file (`.agents/skills/pm/SKILL.md`) is the **canonical** definition of the board conventions — mission, hierarchy, sizing rule, quality gate, standing closing tasks, templates. `/pm-brainstorm` reads it at runtime and must not restate or diverge from it. Parse the subcommand from `$ARGUMENTS` (default = `status`).
## Mission — the TPM lens
This board exists to grow **adoption of Beancount.io in the open-source and agentic-coding community**. Act as the technical program manager for that goal: sequence work by adoption impact, not by what is fun to build. Every milestone must advance one of these pillars and name it in its `## Source + Goal linkage`:
| Pillar | Meaning |
| --- | --- |
| **A1 — Agent-native accounting** | A coding agent (Claude Code, Codex, …) can set up and maintain a beancount ledger end-to-end: skills, CLI, MCP surfaces, instruction-file quality. |
| **A2 — Frictionless onboarding** | A newcomer — human or agent — goes from zero to a working ledger in minutes: installers, `beancount-init`, quickstarts that work exactly as written. |
| **A3 — Community & distribution** | Reach and credibility: docs, examples, launches, package registries, GitHub presence, Telegram. Signals: installs, stars, skill invocations, contributors. |
## The `.pm` hierarchy
| Level | Path | Meaning | Effort |
| --- | --- | --- | --- |
| Workstream | `wN/` (`w1`, `w2`, `w3`, …) | a general-purpose worker queue; `README.md` + inbox notes | — |
| Inbox note | `wN/NNN.md` (`w1/005.md`) | one idea or a **sub-hour** unit of work, plain markdown | ≤ ~1h |
| Milestone | `wN/mN/` (`m1`, `m2`, …) | a shippable chunk: `README.md` + task files | **> ~1h**, multiple tasks |
| Task | `wN/mN/tNNN.md` | a single unit | tens of minutes |
## Rules (enforce every time)
- **The board is public.** `.pm/` is committed to a public repo. Write every board file for public consumption: no secrets or credentials, no user data, and no references to private repositories or their contents. Assume the community reads the board — it is itself an adoption surface.
- **Respect the anti-goals.** Read `.pm/DO_NOT_DO.md` before proposing or materializing work. Do not create milestones/tasks that conflict with it.
- **Workers are generalists.** A workstream is a worker queue, not a permanent topic, package, feature, or pillar lane. Keep workstream titles and provenance generic; put topic-specific scope in milestones, tasks, and inbox notes. Route new work by priority, dependencies, and available capacity, and allow one workstream to contain unrelated topics across A1/A2/A3.
- **Sizing rule.** A milestone must be **> ~1 hour of work across more than one task**. If a chunk is ≤ ~1h (tens of minutes, a task or two), do **NOT** create an `mN/` directory — record it as a loose inbox note `wN/NNN.md`. Tasks take tens of minutes; milestones take hours.
- **IDs must match the path.** A task's `id: wN/mN/tNNN` frontmatter must equal the directory it lives in. Never create a milestone dir whose path disagrees with the IDs inside it; if you find drift, flag and repair it, don't copy it.
- **Keep status in sync** across all three places it lives: the workstream `README.md` milestone checkbox, the milestone `README.md` `**Status:**` line + the `— DONE` marker in the task table, and each task's `status:` frontmatter.
- **Completed work must exit the open tree.** Moving completed work into `done/` is a mandatory exit condition, not optional cleanup. A mutating subcommand must not report success while an affected task with `status: done` remains at `wN/mN/tNNN.md`, or while an affected milestone with no open tasks remains at `wN/mN/`. Move completed tasks to `wN/mN/done/` and completed milestones to `wN/done/mN/`, then verify the old open paths no longer exist.
- **Unwanted work leaves the tree with its reason on record.** A milestone or inbox note that should no longer be done is removed by `drop`, never by unchecking, editing in place, or leaving it to rot: its files go, the workstream `README.md` keeps a one-line tombstone under `## Dropped`, and its number is never reused. `done` is for finished work and `drop` is for unwanted work — a milestone whose definition of done already holds is closed with `done`, not dropped, and work already under `done/` is history and is never dropped.
- **Blocked work waits in `blocked/` with its unblock condition.** Work that is still wanted but cannot proceed keeps its number, its files, and its pending checkbox, and moves aside: a milestone to `wN/blocked/mN/`, an inbox note to `wN/blocked/NNN.md`. A task cannot leave its milestone — its `id` must match its path — so a blocked task stays put with `status: blocked` and the milestone `README.md` carries the reason. Every blocked item states the exact blocker and an **Unblock:** condition, and names who can clear it when that is not this repository (a deploy, a credential, a store review, a decision only the user can make). "Blocked" with no written condition to clear it is not a status, it is an abandonment.
- **Numbering:** next free zero-padded 3-digit for inbox notes (`NNN`) and tasks (`tNNN`); next free `wN` / `mN`. Scan the tree first, including `## Dropped` tombstones and everything under `done/` and `blocked/`; don't reuse a number.
- Use `worker: worker1` unless the workstream README names another worker.
- **Milestones must be meaningful.** Every milestone must include direct pillar linkage (A1/A2/A3), an observable expected adoption outcome, and why this work matters now (dependency/risk/sequence rationale).
- **Every milestone ends with standing closing tasks**, appended after the implementation tasks whenever a milestone is materialized:
1. **Adoption surface** — _only when the milestone ships something a user or a coding agent touches._ Check the shipped change is discoverable and usable on every surface it exposes: the owning package's `README.md` (install/quickstart steps still work exactly as written), the root `README.md` and `AGENTS.md` package tables, the appropriate skill catalog (`skills/AGENTS.md` for customer workflows, `.agents/AGENTS.md` for development workflows) for new or changed skills, every scope's guidance in a real `AGENTS.md`, and the `.claude/skills` link to the real `.agents/skills` directory (root `AGENTS.md` compatibility rules). For agent-facing work, verify the behavior holds for both Claude Code and Codex — same instructions, same triggers. Flag drift as follow-up work rather than silently diverging. Omit this task only for milestones with no user- or agent-facing surface (pure infra, board mechanics, internal refactors) — note why it was omitted in the milestone's `## Source + Goal linkage`.
2. **Simplify** — run `/simplify` over the code this milestone changed (reuse / simplification / efficiency; behavior-preserving).
3. **Test coverage** — add meaningful tests for the behavior this milestone shipped. Tests must assert real behavior and failure modes; never game coverage with trivial, tautological, or snapshot-everything tests.
4. **Closeout** — the final task, added last. When the milestone's other tasks are all complete **and its definition of done is actually met**, close the milestone: set every remaining task's `status: done`, move each `tNNN.md` to `wN/mN/done/`, mark every row `— **DONE**` and set `**Status:** done` in the milestone `README.md`, move the whole `wN/mN/` directory to `wN/done/mN/`, and check `- [x]` in the workstream `README.md`. Completing this task _is_ the move — running `/pm done ` on it last triggers the milestone move (the `done` subcommand's step 4). Do **not** run it until the DoD holds: a milestone lands in `done/` when its observable end state is real, not merely when the code is written.
Each `depends_on` the last implementation task(s) (Simplify and Test coverage depend on Adoption surface when it's present; Closeout depends on Test coverage) and all count toward the `(N tasks)` total. `add-task` inserts new work **before** these (before Closeout) and updates their `depends_on`.
## Subcommands
### `status` (default)
Read the tree (`find .pm -type f -name '*.md'`, skipping `done/`) and `.pm/DO_NOT_DO.md`. Print, per open workstream: its milestones with `**Status:**`, and the **next actionable task** per milestone — the first non-done task whose `depends_on` are all satisfied. Also list open inbox notes, and everything under `blocked/` with the condition that would clear it. Then run a lightweight validation pass and flag:
- items conflicting with `.pm/DO_NOT_DO.md`,
- milestones missing `## Source + Goal linkage` or a pillar (A1/A2/A3),
- milestones whose definition of done is vague/non-testable,
- completed tasks or milestones that still sit in the open tree instead of their applicable `done/` directory,
- blocked items with no **Unblock:** condition on record, and blocked items whose condition now reads as met.
Touch no files.
### `new workstream `
Create the next free `wN/` with `README.md` from the workstream template below. The title must describe a generic worker queue, not a topic specialty.
### `add `
Create the next free inbox note `wN/NNN.md` with the idea as plain terse markdown (no frontmatter). This is the default home for **sub-hour** work.
### `promote ` / `new milestone `
Apply the **sizing rule first.**
- If the work is **> ~1h and splits into more than one task**: create `wN/mN/` with `README.md` (milestone template) + one `tNNN.md` per task (task template) **+ the standing closing tasks (Adoption surface when the milestone ships a user- or agent-facing change, then Simplify, then Test coverage, then Closeout)**, add the `- [ ] **mN** — …` line to the workstream `README.md`, and fill `## Source + Goal linkage` with source + pillar linkage + expected outcome + why-now rationale (note there why Adoption surface was included or omitted).
- If it is **≤ ~1h**: do NOT create a milestone. Keep/append it as an inbox note `wN/NNN.md` and tell the user why (too small for a milestone).
### `add-task `
Create the next `tNNN.md` from the task template and add its row to the milestone `README.md` table **before the standing closing tasks**, updating their `depends_on` to include it. Update the `(N tasks)` count in the workstream README.
### `done `
1. Set the task's frontmatter `status: done`. If the task is being closed because its work already landed outside it (a triage close — the acceptance criteria hold on `main` through another milestone or commit, not through work done for this task), append a `## Closed by triage` section to the task with the evidence — commit SHA, paths, test names — so the public record shows why it closed without work.
2. In the milestone `README.md`: mark the row `— **DONE**` and update the `**Status:**` line (e.g. `todo (t001 done)`).
3. **Move** the file to `wN/mN/done/tNNN.md`.
4. If no open tasks remain in the milestone, **move the whole milestone** to `wN/done/mN/` and check its box (`- [x]`) in the workstream `README.md`.
5. **Verify the exit condition before returning:** the completed task exists only under `done/`; and, when no open tasks remain, the milestone exists only at `wN/done/mN/`, its `README.md` says `**Status:** done`, and the workstream checkbox is checked. Do not report success until these moves and status updates are complete.
For an **inbox note** `wN/NNN.md` (including one under `blocked/`): append a `---` separator and one `**Resolved ** — , shipped in `` (``)` paragraph, so the public record says what closed it; move the file to `wN/done/NNN.md`; remove its bullet from the workstream `README.md` if it is listed there. Verify the open path no longer exists before returning.
Show the intended moves before mutating if the user passed `DRY_RUN=1`.
### `block `
Park work that cannot proceed, with its blocker on record. Refuse without a reason. Blocking is not dropping: the work is still wanted, so nothing is deleted, no number is retired, and the workstream checkbox stays unchecked.
1. Confirm the target is in the open tree and read it, so the record can name what is stuck.
2. Write the block **into the item**: a `## Blocked` section for a milestone or task (a note gets a `**Blocked ** — …` paragraph) stating what is blocked, the exact blocker, an **Unblock:** condition that would clear it, and who can clear it when that is not this repository — a deploy, a credential, a store review, a decision only the user can make. Record any partial work already shipped, so the next worker knows where the baseline is.
3. Move it: a milestone to `wN/blocked/mN/`, an inbox note to `wN/blocked/NNN.md`. A **task does not move** — its `id` must match its path — so set its frontmatter `status: blocked` and put the reason in its milestone's `## Blocked` section. When every open task in a milestone is blocked, block the milestone itself.
4. In the workstream `README.md`: keep the milestone's `- [ ]` line (blocked work is still pending), retarget its link to `./blocked/mN/README.md`, and append `— **blocked:** `.
5. **Verify the exit condition before returning:** the item sits under `blocked/` (or the task reads `status: blocked`), the blocker and its **Unblock:** condition are written down, and every link that pointed at the old path still resolves.
Show the intended moves before mutating if the user passed `DRY_RUN=1`.
### `unblock `
The reverse, once the unblock condition is actually met — verify it against the repository first, and say what cleared it.
1. Move the item back to `wN/mN/` or `wN/NNN.md`, or set the blocked task's `status:` back to `todo`.
2. Replace the `## Blocked` section (or the note's blocked paragraph) with a one-line record of what cleared it and when. Keep the history; do not erase that it was blocked.
3. Restore the workstream `README.md` link and drop the `— **blocked:** …` suffix.
4. **Verify the exit condition before returning:** the `blocked/` path no longer exists, the item is live again, and its links resolve.
### `drop `
Remove work that should no longer be done. Refuse without a reason, and refuse for anything already under `done/` — completed history is never rewritten. For a milestone whose definition of done already holds, use `done` on its tasks instead; for work that is still wanted but stuck, use `block`. `drop` records that the work is unwanted, not that it is finished or waiting.
For a **task** `wN/mN/tNNN`: delete the task file, remove its row from the milestone `README.md` table, update the `depends_on` of every task that named it, correct the `(N tasks)` count in the workstream `README.md`, and add the tombstone line under a `## Dropped` section in the milestone `README.md` rather than the workstream one. Never drop a task to make a milestone look complete — if the work is still needed, the milestone is blocked, not done.
1. Confirm the target exists in the open tree (`wN/mN/` or `wN/NNN.md`) and read it, so the tombstone can name what it was.
2. Delete the milestone directory (including any `wN/mN/done/` tasks) or the inbox note file.
3. In the workstream `README.md`: remove the milestone's `- [ ] **mN**` line, then add `- ~~**mN**~~ — — dropped : ` under a `## Dropped` section (create it directly after `## Milestones` if absent). Inbox notes get the same line keyed by `NNN`. Keep the reason public-safe and cite what justifies it — the `DO_NOT_DO.md` rule, ADR, commit, or superseding milestone.
4. If the reason is a rule the board should enforce going forward, say so in the response; add it to `.pm/DO_NOT_DO.md` only when the user asks.
5. **Verify the exit condition before returning:** the old path no longer exists, the checkbox line is gone, and the tombstone is present. Do not report success until all three hold.
Show the intended removals before mutating if the user passed `DRY_RUN=1`.
## Templates
### Workstream `README.md`
```markdown
# wN — ()
**Worker:** — general-purpose adoption worker; accepts the next highest-impact milestone across packages, topics, and A1/A2/A3 rather than owning a permanent specialty.
## Milestones
- [ ] **mN** — ( tasks) ← from
```
### Milestone `README.md`
```markdown
# wN · mN —
**Worker:** **Goal:** **Status:** todo
## Tasks (in order)
| id | title | est | depends_on |
| ---- | ------- | --- | ---------- |
| t001 | | 30m | — |
## Definition of done
## Source + Goal linkage
- **Source:**
- **Goal linkage:**
- **Expected outcome:**
- **Why now:**
```
### Task `tNNN.md`
```markdown
---
id: wN/mN/tNNN
title:
worker:
status: todo
estimate: 30m
depends_on: [wN/mN/tMMM]
---
## Objective
## Context
-
## Steps
1.
## Files
-
## Acceptance criteria
- [ ]
## Out of scope
-
```
### Inbox note `wN/NNN.md`
Plain terse markdown, no frontmatter — one idea or a sub-hour unit of work.
## Arguments
$ARGUMENTS