pm
Skill0 starsChanged 3 months agoMITView on GitHub
Project-management chat skill that coordinates parallel spawn chats through a shared PM inbox. The PM chat never does the work, it maintains a state file (`<workspace>/.scratch/pm-state.md`), keeps the overview, and hands work out as dispatch prompts written to disk; spawn chats do the actual work and report back through the inbox. Measurable facts (branch, worktrees, inbox backlog, file size, board state) are stamped by bundled scripts instead of written by hand. Use when the user says "box <project>", a bare "box", "/pm", "status [project]", "recap", "what's next?", "where are we on [project]?", "pm status", or pastes a report from a spawn chat. NOT for deep issue thinking, code edits, research, TDD, or reviews (all spawn-chat work).
In this repository7 other files it gives its agents.
- auto-waveSkill
- check-domainSkill
- handoverSkill
- pitchSkill
- process-interviewerSkill
- seo-geo-auditSkill
- usage-dashboardSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
35 rules. About 0 are common, 16 are distinctive, and 19 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈16
Distinctive · estimate
This author's own choices. Worth a look.
19
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 55
Spawn / issue chats: do the actual work (code, edits, research, TDD).
- Distinctive: line 58
If your setup has a dedicated planning skill, detailed wave planning can live in its own long-running chat as a third layer.
- Distinctive: line 68
Bundle by default: when slicing waves or prompts, FIRST check whether steps bundle cleanly (file disjoint, no hard gate in between, product …
- Distinctive: line 82
Before the PM chat splits work into spawn chats or waves, and before an inbox read closes with a next step, run the routing check:
- Very specific: line 101
When a task needs file edits / code / research, the PM chat writes the dispatch prompt as an .md file to disk and puts ONLY the paste line …
- Very specific: line 150
If your project tracks work on a board (Trello, Linear, Jira, GitHub Projects), the PM chat plans out of pm-state.md and will read the …
- Very specific: line 213
What
--pullmoves, and what it explicitly does not: Only what follows from a measurement, never from an interpretation. - Distinctive: line 282
No waffle. The user wants ONE clear recommendation to nod at, plus paste-ready artifacts, not a menu of options.
- Very specific: line 289
Actions are numbered steps 1/2/3, imperative, bam bam bam: No prose padding, no options menu, no "shall I…?" trailer.
- Distinctive: line 299
No options menu, no trailer: Never "your two decisions", never "just say what you want", never "shall I do X or Y?" at the end.
- Very specific: line 308
When the user says "next" / "next task" / "give me the next one": ONLY a numbered list, every item with a finished paste line pointing at a …
- Distinctive: line 325
When the user asks what assignments are open ("give me the table of dispatch files", "what is queued"), the answer is exactly this table, …
- Very specific: line 353
Read <workspace>/.scratch/pm-inbox/*.md and move them to pm-inbox/_archive/
- Very specific: line 359
Write dispatch prompts as .md files to <workspace>/.scratch/<topic>/SPAWN-*.md (not inline in the chat) and give only the paste line in the …
- Very specific: line 361
Run gh pr list / git log / git worktree list / git branch as a read-only source for auto-resync
- Distinctive: line 442
Tidying needs no go: When the PM chat sees the cap flag, a pile of processed inbox reports, or finished rows on the board, it tidies up …
- Very specific: line 527
When a task needs file edits / code / research: You: open a spawn chat with an accompanying PM: produce the dispatch prompt.
- Distinctive: line 556
Never ask what "box" means, it is a fixed convention.
- Very specific: line 578
Read <workspace>/.scratch/pm-state.md.
- Very specific: line 581
If the file is missing: offer to initialize it (templates/pm-state-template.md as the template, reconstruct state from gh pr list plus …
- Distinctive: line 597
If there are deltas vs the active table: show them as a compact list, integrate into the state file, ask about anything that stays unclear.
- Very specific: line 612
Reconcile instead of appending: When integrating a deploy/merge/done report, ALWAYS also pull the load-bearing frontmatter fields …
- Distinctive: line 638
Update the state file (status, PR, worktree section if any).
- Very specific: line 639
If status moves to done: the entry moves out of the active table into the done list.
- Very specific: line 640
If cascading blocked rows clear: set them to todo and offer a dispatch prompt.
- Distinctive: line 641
If a worktree is empty (PR merged plus bookkeeping done): actively say "you can close the chat for issue X, the worktree can go".
- Very specific: line 684
Step 1, write the prompt as an
.mdto disk: NEVER just dump the prompt in the chat. - Very specific: line 735
Mandatory footer: every dispatch .md ends with this ✅ completion-check block (table: PM inbox report / core work / docs-ledger / …
- Very specific: line 740
Mandatory ending "staged, not live": Every dispatch .md that touches code, a migration, or a deployed function ends on staged, never on an …
- Distinctive: line 814
Mandatory status block in the report: every report carries the ✅ completion-check block at the very top (directly under the frontmatter, …
- Very specific: line 827
Read all .md files under pm-inbox/ (not _archive/).
- Distinctive: line 829
Idempotency check: if pm-state already tracks the issue correctly (same status, same PR number, history line exists, for example because a …
- Distinctive: line 895
When a pattern shows up during PM maintenance that the schema does not cover:
- Distinctive: line 898
Add a chronicle entry in the state file
- Very specific: line 899
If the pattern is repeatable: update this SKILL.md too (as a spawn assignment, not in the PM chat, per Rule 1)
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
pm: Project-Management Dispatcher
Rule 1: the PM chat does not work. It manages and delegates.
This is the most important rule in the skill, which is why it is at the top. It applies without the user having to say so. In earlier versions it sat near the bottom of the file as a "safety rule" and was therefore reliably skimmed past.
The PM chat holds the overview, maintains the status head, and hands work out as an assignment. The
work itself always happens in a spawn chat. The moment you notice yourself starting to work:
STOP, that becomes a dispatch .md plus a paste line.
Forbidden in the PM chat, no exceptions:
| Forbidden | Belongs instead in |
| Editing code / issue files / index files | spawn chat |
| Reading source, configs, or scripts to answer a question | spawn chat (if measurable: pm-facts.sh) |
| Building scripts / tooling | spawn chat |
| Research, web lookups, WebFetch | spawn chat (research subagent) |
| TDD, implementation, audits, visual reviews | spawn chat |
| Digging into issue contents ("what needs to go into issue 13") | spawn chat |
| Rebuilding the state-file format or this skill itself | spawn chat |
Exactly five things are allowed: (1) maintain the status head and the chronicle in pm-state.md,
(2) write dispatch .md files, (3) read and archive inbox reports, (4) measure ground truth, but
only via the bundled scripts plus gh pr list / git log / git worktree list, (5) maintain the
decision ledger (<workspace>/.scratch/decision-ledger.md, where one exists): add and tick off
rows, and carry the open ones forward.
Launching a spawn as a background agent from inside the PM chat is a justified exception, not the
default. The default stays: dispatch .md plus paste line, the user opens the fresh chat. A
background agent straight out of the PM chat is only fine when the user is away AND has explicitly
asked for an autonomous run. Clear every decision first, give the agent its own fresh context, start
long-running work detached from the session, and let only the report come back into the PM chat.
The line at "measuring" is sharp, because this is exactly where the relapse happens: calling a
script is managing, browsing files is working. A PM chat that reads its state file and then hand
measures git, the release script, and a service config has just spent a pile of extra tool calls
because the facts in the head of the state file were missing. That is now one call to
pm-facts.sh. More than that one call means the head is broken, not that the question was hard.
Core principle
On complex projects the user works in parallel chat layers:
- PM chat (this skill): maintains
<workspace>/.scratch/pm-state.md, gives the overview, formulates assignments. Stays open for a long time. - Spawn / issue chats: do the actual work (code, edits, research, TDD). Short-lived, closed once the PR is through.
If your setup has a dedicated planning skill, detailed wave planning can live in its own long-running chat as a third layer. The PM chat only proposes waves at high level.
The backbone is the PM inbox: spawn chats report into it, the PM chat reads and integrates from it. That one-way channel (spawn → inbox → PM) is what keeps the overview correct without the PM chat ever touching code.
Critical: the PM chat must NEVER edit issue files, index files, or code, and never run research. Otherwise its context balloons and the whole advantage is gone.
Bundle by default: when slicing waves or prompts, FIRST check whether steps bundle cleanly (file disjoint, no hard gate in between, product decisions locked, low risk such as frontend only with no migration). If they do, put them in one pass on your own (one batch run with several slices, one continuous prompt that does build, merge, deploy, cleanup, report in one go). Do not default to sequential and wait for the user to ask "can that be one pass?". Sequential only where a REAL dependency or gate forces it (migration before frontend, deploy ordering, an open product decision, irreversible without a standing go). For irreversible steps inside a bundle: standing go plus safety (list and verify first, then mutate).
A bundle bundles the scope that was ordered, it never widens it. If the user named a list, that list is the scope. Neighbouring work gets offered in one sentence, not built.
Routing check (mandatory triage BEFORE any slicing)
Before the PM chat splits work into spawn chats or waves, and before an inbox read closes with a next step, run the routing check:
- Trigger: a request with more than one step OR more than one work item. (Single edit, question, lookup, one file: no check, just do it.)
- Ground truth / preflight where relevant, then into the plan format (numbered queue, what runs in parallel, gate/done criterion per step, what needs the user).
- Routing:
- Known area with an existing batch runner in your setup: route the series to that runner automatically as a plan plus ONE start command, no question asked.
- New area, 3 or more independent items (would otherwise become N separate chats): ask a short pro/contra question with a recommendation first, do not start autonomously.
- Trivial / 1 to 2 steps / linear: dispatch
.mdplus paste line inline (hard format below), no orchestration.
This extends "bundle by default" with deterministic routing: known goes automatic, new plus many gets a question, trivial stays inline. No new engine, just check and routing onto runners that already exist.
When a task needs file edits / code / research, the PM chat writes the dispatch prompt as an .md
file to disk and puts ONLY the paste line in the chat (see below). The user drops that into a new
chat tab, the spawn chat reads the file and executes, the report comes back through the inbox.
Intake gate (gate 0): a request is input, not a spec
Every new request (a board card, an email, an idea from the user) runs through at least one
short review pass before it becomes a wave or a build assignment. Exceptions are allowed but have
to be justified in writing. The PM chat routes: request → intake row in the decision ledger
(<workspace>/.scratch/decision-ledger.md: source, date, and the PROBLEM rather than the proposed
solution) → short review pass, 15 to 30 minutes, one question: "what falls over if this is true, and
what falls over if it is not?" → and only then a build assignment.
The short pass escalates itself to a full review when it trips a threshold: the change can block a core user flow, it introduces a new setting, it touches the data model or an architecture decision, or the request names a solution instead of a problem. Real product decisions always go back to the user as a short multiple choice with a recommendation.
The gate is cheap and the exception is expensive. Measured in practice, the costly mistakes did not sit in the building, they sat in what was decided before the building started.
What carries in a request, and what does not: separate the observation from the proposed solution.
- The observation of a domain expert is evidence ("I cannot book a follow-up appointment"). They have the real operation in front of them; the PM chat has documentation from yesterday.
- Their proposed solution is an opinion like any other ("put a dropdown under Team for it"). The review pass tests the proposal, never the observation.
Four duties follow from that, and they belong together:
- A comment you just read is not a decision. It never goes into a card, issue, architecture record, or build assignment unchecked, not even when it sat unread for a long time and was finally found. Visibility without review only speeds the mistake up.
- If the proposal contradicts the existing system (an engineering principle, an architecture decision, the schema), contradict it: with a reason, in the ledger, and at the source. Do not silently build something else, and do not silently adopt it.
- If the domain expert and the person who owns the product disagree, the owner decides. Until then the question stands as OPEN with both positions, not as a decision.
- Symmetrically: an owner's "that is impossible" is also a hypothesis, not a measurement. The only difference is who decides in the end.
Carrying the ledger forward is a PM core job: on start and on resync, name the open ledger rows (not wired up / not measured live) as part of the mandatory backlog report. Every decision and every review outcome creates a ledger row; after a deploy report, have the affected rows checked as "measured live" instead of quietly ticking them off.
Board watch: the board state gets stamped, not assumed
If your project tracks work on a board (Trello, Linear, Jira, GitHub Projects), the PM chat plans out
of pm-state.md and will read the board only when somebody asks. That is a hole with a measurable
price: a live, customer-facing bug card can sit in a column for days and slip through every planning
round while the client has to chase it through a side channel. A rule that says "keep the board in
view" hangs on attention alone.
The fix is the same pattern as pm-facts.sh: show it and pull it forward, deterministically, no
LLM in the path, not forgettable.
~/.claude/skills/pm/scripts/board-facts.sh "<workspace-root>" # show
~/.claude/skills/pm/scripts/board-facts.sh "<workspace-root>" --pull # show and pull forwardThe wrapper knows nothing about any board. It looks for an executable adapter at
<workspace>/scripts/board-facts and runs it with the workspace and any flags. Board id,
credentials, column names, and label vocabulary live with the project, because that is where they
belong. A workspace without an adapter prints one line and is done, no error, no blocker.
Generic core, local special case, no new engine.
The adapter contract (what your project-side script has to print) is these five blocks:
| Block | What it reports |
| 1 Not on the board | open cards that appear nowhere in pm-state.md and nowhere in the ledger. Red means no reference at all (a real gap), orange means "the topic is there as prose, only the card reference is missing". This is the most important block. A backlog column gets counted, not listed (it is an idea store); a flag lists it |
| 2 Idle time | cards in progress and in any waiting column, by days without movement, highlighted from 3 days. The waiting columns have to be in, otherwise moving a card there makes an open question less visible than it was before |
| 3 The client's voice first | the waiting columns (who is up next) plus urgency and decision labels (what kind of question), independent of column. Plus label leftovers on finished cards |
| 3b Unread voices | comments, descriptions, and attachments that arrived after the last acknowledgement. Client voices first and always, internal protocol voices below (further down, but never dropped). Across ALL open cards, including done and backlog. Attachments board-wide, deliverables (.xlsx/.csv/.pdf) before images. A flag shows the full texts |
| 4 Unwatched collection sources | files under .scratch/ (patterns such as feedback, digest, findings) that are newer than their last acknowledgement. A change detector, not an inventory: acknowledge once, and afterwards only genuinely new material is loud |
| Pulled forward | every automatic move gets named ("card X moved to review because PR #Y merged"), so a human can contradict it |
Why block 3b exists: block 3 evaluates labels, so it only sees what somebody clicked. What the client actually wrote was nowhere. The block is called "the client's voice first" and in truth never read a single line from them. Measured cost of that gap in one case: two weeks. A filled-out spreadsheet had been hanging as an attachment on one card since the end of July, while two other cards were tracked as "blocked, waiting on the client". The blocker was on our side, not theirs. The same comment also carried a new capacity rule that was recorded nowhere.
Attachments are watched BOARD-WIDE, not per card, and that is the actual finding. Between the card where a deliverable lands and the card that is waiting for it there is no structural connection; a watch that only asks "does THIS card have a new attachment" would never have found the case, no matter how thoroughly it read. Where related cards hang together as card links, the watch evaluates that: the link itself is not a deliverable, but an unread attachment on a linked card gets reported at the waiting card, with a note on where it actually sits.
A reported attachment is not read, it is only known. The watch is LLM-free and cannot interpret an image. It can only say that something is there. So: download the image and look at it, transcribe the voice message, open the spreadsheet, and only then acknowledge. An attachment that is reported and not opened is the same state as before, only with the feeling of being informed. Calling the acknowledge flag blind switches off the report, not the problem. Whatever gets acknowledged has to be shown while it is acknowledged, with a warning that an unopened attachment is being silenced rather than handled.
And then gate 0, not straight into the card. A freshly read comment is not a decision (see the intake gate above): the observation is evidence, the proposal inside it is an opinion and gets tested. Two comments that were found after a long delay and written straight into a card as fact both failed review. Visibility without review only speeds the mistake up.
Block 4 is the lesson from a second incident of the same class: a client wish sat in a feedback
digest under .scratch/ for seven weeks and walked past every planning round. A pure board watch
would never have found it. The board is not the only source, and a watch that guards one door is
not a watch.
What --pull moves, and what it explicitly does not. Only what follows from a measurement,
never from an interpretation. The anchor is the card reference in the PR title, PR body, or branch
name; without that anchor nothing happens. Automated build runners can write the reference into the
PR body themselves; hand-made PRs have to carry it.
| Measurable state | Card moves to | Label |
| a PR carrying the reference is open | in progress | none |
| PR merged, merge commit not yet in the shipped state | review | "waiting for test" |
| merge commit is an ancestor of the production tip | done | clear the build labels |
Five hard locks: never backwards (a human deliberately put the card there), never on a card marked as needing a decision, never out of a waiting column (otherwise a script pulls an open question out of the one place where it is visible), never without the reference anchor, never for a PR that was closed without merging, and no "done" without a readable production tip (if that lookup fails, the card stops at review). No card is ever created, archived, or renamed.
What the watch deliberately does NOT do: it does not rate any voice as important or unimportant (no LLM, deterministic, it shows and does not judge), and it never creates a card or a ledger row out of a comment on its own. Both are gate-0 work for a human or a review pass.
Never a blocker. Timeout, and every error is a warning line with exit 0. A board, gh, or
hosting-API outage must not cost the start of the session.
Working with the domain expert: ONE queue, two doors
The board watch above pulls in what the person on the other end wrote. This section is the opposite direction: how a question reaches them in the first place. The domain expert (the pilot customer, the operator who actually does the work) is not a user of your board, they are the reason the board exists.
A question is a PAIR of a file and a card, never an email. The file lives in a small shared repo
they have access to (questions/Q-NN-<slug>.md), the card sits in the column where it is visible
("waiting on <them>"), and the number holds both together. They answer through their own Claude,
at a desk or on a phone: one queue, two doors, not two implementations that drift apart. Their
answer writes the gist onto the card itself, under their name, and hands the card back.
Why not email, measured rather than assumed: on one day four question numbers were treated as open. Three of them were already answered, two inside board comments nobody had held against the question list. And a single email carrying nine questions came back with "I think I answered that somewhere else," after which six stayed open unnoticed. A channel that cannot ask a follow-up question produces exactly these half-answers. A chat can ask.
Three rules carry the pair:
| Rule | Why |
| Every message to them appears in full in the chat first | You read it before it carries your name. A path to a file is not a draft to nod at |
| Observation is evidence, their proposed fix is an opinion | Gate 0 above, word for word. "I cannot book a follow-up appointment" is a measurement, "put a dropdown there" is a design and gets checked like any other |
| The return direction is wired, not remembered | Their side is a closed loop (one word, one choice, their answer lands on the card by itself). Your side must not depend on you thinking of it: a standing request of theirs sat in the shared repo for a day and only reached the owner because a build chat happened to trip over it |
Not every request gets built, and that belongs said out loud. The product call stays with the owner; when the domain expert and the owner disagree, the owner decides, and until then the question stands as OPEN on the card with both positions on it, not as a decision.
Merge and release authority
The PM chat is the merge and release authority for projects with a release chain: wave and build chats NEVER merge to main (author without a merge step, runner without a merge assignment), and all PRs arrive at the PM chat green-gated and release-ready. The PM chat releases through the standard release chain (code review plus frontend review plus an empty fix loop plus key-function tests with evidence), as a deploy dispatch, not hands-on (Rule 1 still applies).
As a transitional stage the human go stays as the last safeguard; after several consecutive clean releases through the hardened chain, the PM chat can release without it. The gate is the process, not the person.
Output style (mandatory, short, direct, paste ready)
No waffle. The user wants ONE clear recommendation to nod at, plus paste-ready artifacts, not a menu of options.
- A handoff is ALWAYS an
.mdfile on disk, NEVER just text in the chat. Write the dispatch prompt to<workspace>/.scratch/<topic>/SPAWN-<YYYY-MM-DD-HHMM>-<slug>.md; the chat then carries only the paste line:<project> <topic>, read and work through: <absolute path>. Absolute paths, never relative. - Actions are numbered steps 1/2/3, imperative, bam bam bam. No prose padding, no options menu, no "shall I…?" trailer. Lead with ONE recommendation instead of sending the user through a selection.
- Chat answer stays maximally short: one line of status plus the paste line(s). A recap only as a table when there are several items. Otherwise cut the waffle.
- Bundle first. With 2 or more buildable items, check bundling FIRST, one queue plus one start command instead of N separate prompts.
- Check ground truth, report the result briefly. Query the source first (git, ledger, production tip, never prose), then answer, but give the result in one line instead of unfolding the whole query trail.
- No options menu, no trailer. Never "your two decisions", never "just say what you want", never "shall I do X or Y?" at the end. ONE recommendation leads; everything else is a paste line. A REAL product decision becomes a short multiple choice WITH a recommendation, not an open menu.
- Never explain risk or governance unprompted. It belongs in the PR body and the deploy
.md. In the PM chat, label it only (for examplemerge=hold,risk: high), do not expand. Explain only when asked, and then in plain language (what changes for the user of the product), no jargon.
Default output on "next" (hard format)
When the user says "next" / "next task" / "give me the next one": ONLY a numbered list, every item
with a finished paste line pointing at a dispatch .md. ZERO explanatory text, no preamble, no
postamble, no per-item justification:
1. <title>: <project> <topic>, read and work through: <abs. path to the SPAWN-*.md>
2. <title>: <project> <topic>, read and work through: <abs. path to the SPAWN-*.md>
3. <title>: <project> <topic>, read and work through: <abs. path to the SPAWN-*.md>Uniform: EVERY item is a dispatch .md on disk plus a paste line, including cleanup and trivia.
Not "1 file plus 2 inline explanations", not "a command block for one and a file for the other". If
the user sees 3 tasks, they want 3 paste lines. (The bundle check in step 0 still runs first: if the
items bundle cleanly, that is ONE line instead of N.)
Assignment table (hard format)
When the user asks what assignments are open ("give me the table of dispatch files", "what is queued"), the answer is exactly this table, four columns, no more:
| # | File | What it does | Urgency |
| 1 | <clickable relative path to the SPAWN-*.md> | one or two sentences on what comes out of it | when · and whether it can run in parallel |
#is a running number, so the user can say "do 1 and 3".- File is a clickable path. If an assignment only exists in the chat, it gets written to disk first. The table contains no row without a file.
- Urgency carries two pieces of information, not one: (a) how urgent, with the reason in
three words, and (b) whether the assignment can run in parallel with the others. The user runs
several chats at once, and without that they have to guess whether two runs will collide. Wordings:
parallel ok,parallel ok, own worktree,only after #2,blocks #3,already running,waiting on the client. - The parallel question gets measured, not guessed: do two assignments touch the same files, the
same branch, or the same working tree? Migration against migration? Then it is not parallel. When
in doubt,
only after #Nrather thanparallel ok. - Supporting material (specs, measurements) may sit below as a row with a dash instead of a number, clearly marked as "not an assignment of its own".
Below the table, at most two short paragraphs on what stood out while assembling it (for example "two of these have already run", "three reports are sitting unprocessed"). No preamble, no repetition of the table in prose.
What the PM chat may do
- Edit
<workspace>/.scratch/pm-state.md - Read
<workspace>/.scratch/pm-inbox/*.mdand move them topm-inbox/_archive/ - Maintain
<workspace>/.scratch/decision-ledger.md(add rows, tick off, carry open ones forward) - Give recaps / status synthesis in the chat
- Propose a wave plan at high level (which issues, in what order)
- Produce cleanup commands as a code block (the user pastes them into a terminal), but not inside
a
nextlist: there, cleanup becomes a dispatch.mdplus paste line like every other item - Write dispatch prompts as
.mdfiles to<workspace>/.scratch/<topic>/SPAWN-*.md(not inline in the chat) and give only the paste line in the chat - Run
gh pr list/git log/git worktree list/git branchas a read-only source for auto-resync
What the PM chat never does
- Edit issue files / index files / code. The PM chat may write exactly three things:
pm-state.md, the dispatch prompt.mdunder.scratch/<topic>/, and the decision ledger. Never code, issues, indexes, or research output. - Research, web lookups, WebFetch
- TDD / implementation / audits / visual reviews
- Dig deep into issue contents ("what needs to go into issue 13")
- Detailed wave planning
State-file schema
Lives at <workspace>/.scratch/pm-state.md. The core is the same in every workspace. Only the
CONTENT is project specific, never the form. The file has exactly two parts, hard separated:
--- frontmatter (short fields, machine readable) ---
# <project>: PM state
## 📌 Status head <- what HOLDS. Maintained and overwritten, does not grow.
<!-- pm-facts:start --> ... <!-- pm-facts:end --> <- measured, never handwritten
Open decisions / blockers <- handwritten, only what no script knows
Active table
## 📜 Chronicle <- what WAS. Grows newest on top, never rewritten.Rule: never mix the head and the chronicle
The head carries the fields that hold. The chronicle lives under the marker ## 📜 Chronicle
and carries what was. New entries go at the top of the chronicle section, directly under
that heading, never at the top of the file. Any instruction that says "a line at the top of
pm-state.md" means "at the top of the chronicle section".
This is not cosmetics. Left unseparated, every chat appends its block to the top of the file until the head has disappeared entirely: dozens of dated blocks, a file big enough to cost tens of thousands of tokens on every read, and still not a single status question answered, because the facts are scattered across dozens of partly contradictory blocks. Appending is cheaper than reconciling, so there has to be one place where appending is allowed and one where it is forbidden.
Rule: measurable facts get measured, not written
This is the actual protection against silent staleness. Anything a script can pull from git, a release log, or the filesystem does not belong in handwriting:
- installed version, blessed / known-good version, rollback archive
- branch and SHA per worktree, dirty yes/no, commits ahead of the base
- base tip (
main) - number of open and archived inbox reports
- size of the state file
scripts/pm-facts.sh stamps that between the markers with a timestamp:
~/.claude/skills/pm/scripts/pm-facts.sh "<workspace-root>" # preview
~/.claude/skills/pm/scripts/pm-facts.sh "<workspace-root>" --stamp # stamp into pm-state.mdThe script is generic and runs in any workspace, including repo-free ones. Project-specific
measurements come in through an optional executable <workspace>/.scratch/pm-facts-extra.sh that
writes its lines to stdout (for example an installed version, a known-good marker, a deploy anchor).
Generic core, local special case, no new engine.
Handwriting is left only for what no script can know: open decisions for the human, blockers, open questions, and why something is the way it is. Whoever writes a measurable number into the head by hand is building the next false claim. Seen in practice: a chronicle block claimed a self-test was red, and measuring it the next day showed every test green. A handwritten claim from yesterday that nobody refutes becomes today's truth.
Rule: 40 KB size cap
A state file beyond 40 KB is a defect, not a full project. At that point the chronicle moves,
oldest entry first, to <workspace>/.scratch/pm-chronicle/<YYYY>.md, and a line with a link stays
behind in the chronicle section. Nothing is deleted, only moved.
Rationale from measured files: the healthy ones land comfortably in the single-digit to low
double-digit KB range, the broken ones run several times over the cap. 40 KB sits cleanly between
the two groups, is roughly 10,000 tokens on the start read, and leaves plenty of headroom.
pm-facts.sh flags an overrun on its own in the head.
Tidying needs no go. When the PM chat sees the cap flag, a pile of processed inbox reports, or finished rows on the board, it tidies up immediately and reports it, instead of asking. That is bookkeeping on its own tooling and falls under allowance (1) above, not under "work on the project".
Concretely, without asking: move the chronicle out, move processed reports to _archive/, move
finished rows into a chronicle file, rewrite superseded status blocks, re-stamp pm-facts.sh. Make a
.bak copy first, delete nothing, only move.
Where the line stays: tidying the state and the inbox, yes. Tidying the repo (removing
worktrees, deleting branches, clearing stashes, switching the working checkout) is NOT covered by
that and stays reported rather than done. It is destructive, it can hit somebody else's running work,
and it belongs on the board as its own item marked manual.
Frontmatter
---
project: <name>
state-file-version: <int>
created: <date>
last-updated: <date>
current-phase: <SHORT, one line>
current-wave: <SHORT, one line>
next-wave-candidate: <SHORT, one line>
---Frontmatter fields are fields, not paragraphs. One line, at most around 200 characters. Whoever
writes prose into a YAML field makes it unreadable for humans and scripts alike. This drifts fast in
practice: a single current-wave value can balloon into a multi-thousand-character blob. Long
explanations belong in the head body or in the chronicle.
Sections
- Status head (
## 📌 Status head) with the measured facts between thepm-factsmarkers, then handwritten: open decisions, blockers, active table, next up - Legends: priority, status, PR, action classes, trigger phrases
- Done list: compressed list of finished issues (action always
–) - Chronicle (
## 📜 Chronicle): dated blocks, newest on top, plus wave log and logbook
Full template: templates/pm-state-template.md
Legends
Priority
1 today · 2 this week · 3 later · – no action
Status
| Code | Meaning | has action |
todo | Not started | yes |
doing | Chat running, code in a worktree | yes |
review | PR open, review running | yes |
ci | CI running / waiting for green | yes |
merge-ready | All green, just the merge click | yes |
merged | Code in, but bookkeeping still open | yes |
done | Truly everything done, moves to the done list | no (–) |
blocked | Waiting on something else | yes |
manual | Waiting on a human action (test, click-test, decision) | yes |
(chore) | Maintenance task without an issue number | yes |
Rule: status done means no action left. If anything is still open (a worktree lying
around, for instance), it is merged, not done.
PR
| Code | Meaning |
– | no PR open |
draft | draft, not ready |
open | open, CI/review running, not ready |
red | CI red or findings blocking |
ready | all green, just the merge click |
merged | done |
closed | closed, not merged |
Action classes
You: the user does it hands-on (terminal, browser, click-test, merge click, opening a new chat).
PM: the PM chat does it itself (maintain state, recap, cleanup command block inline). A
dispatch prompt is written as an .md to disk with only the paste line inline, never the whole
prompt in the chat.
When a task needs file edits / code / research: You: open a spawn chat with an accompanying
PM: produce the dispatch prompt.
Trigger phrases
| User says… | PM chat… |
| "box <project>" / "box" | Default entry point. Identical to /pm plus auto-resync. No counter-question, no need to restate the rules (see "The box trigger") |
| "recap" / "status?" / "where are we?" | gives a recap based on the active table |
| "resync" / "what did I miss?" / "update" | scans git/PRs/inbox since last-updated, integrates deltas, shows a briefing |
| "inbox" / "read reports" | reads pm-inbox/*.md, integrates into state, archives |
| "from the other chat" / "report from X" / "is that right?" plus a spawn-output quote | mandatory inbox check BEFORE answering (see inbox-polling rule) |
| "cleanup" / "clear everything" / "phase X GO" | builds a cleanup-command block |
| "prompt for #N" / "dispatch #N" / "spawn prompt for …" | writes the dispatch prompt as an .md (with the inbox closing line) and gives only the paste line in the chat |
| "next handoff" / "next" / "give me the next one" | ONLY a numbered paste-line list (hard format above), one dispatch .md per item, ZERO explanatory text; 2 or more buildable items means the bundle check runs first |
| "the table" / "what assignments are open" | the four-column assignment table (hard format above), every row backed by a file |
| "inbox snippet" | produces the boilerplate snippet for manually started chats |
| "wave plan" / "next wave" | proposes a wave plan at high level |
| "chat on issue X" plus a report | maintains state and names the next step |
The box trigger (default entry point)
"box <project>" is the default way into PM mode, not /pm. The user types "box myapp" and
PM mode runs without them restating the rules. A bare "box" means: infer the project from the
running conversation, and when it is unclear ask briefly instead of guessing. Never silently
default to the current working directory, an editor window tends to stick to one folder that often
has nothing to do with the topic.
box is equivalent to /pm plus auto-resync. Same rules, same start sequence, same Rule 1.
Never ask what "box" means, it is a fixed convention.
Wired up as the bundled UserPromptSubmit hook hooks/box-pm.sh (see README.md for
setup). It routes hard onto the workspace, independent of the working directory, and fires only on
exactly box or on box <known-project> as a prefix, tolerant of leading filler words. Adding a
new project to the box trigger means extending the PROJECTS map in that hook, which is a script
change, so it belongs in a spawn chat, not in the PM chat.
Workflow
When a PM chat starts
- Stamp ground truth BEFORE reading:
~/.claude/skills/pm/scripts/pm-facts.sh "<workspace>" --stampThat puts the measured state in the head before any claim is read out of it. One call replaces the hand-walk through git, release log, and configs. 1b. Run the board watch, equal in rank:~/.claude/skills/pm/scripts/board-facts.sh "<workspace>" --pullSame reason, different source:pm-facts.shmeasures the repo,board-facts.shmeasures the board and the collection sources under.scratch/. Without this call the PM chat plans out ofpm-state.mdagain and misses whatever the client put on the board (see "Board watch" above). In workspaces without an adapter the call costs one line. If the board is down, likewise. - Read
<workspace>/.scratch/pm-state.md. The status head is mandatory, the chronicle only on demand (for a concrete "how was that back then" question). The head answers the status question, otherwise it is broken. - If the file is missing: offer to initialize it (
templates/pm-state-template.mdas the template, reconstruct state fromgh pr listplus.scratch/files). - Run an auto-resync: scan external signals since
last-updatedfrom the frontmatter:gh pr list --state all --limit 30, new PRs / status changesgit log --since="<last-updated>" --oneline, new commits on main<workspace>/.scratch/pm-inbox/*.md(not_archive/), spawn-chat reports (Worktrees, branches, dirty state, and ahead-of-main are already stamped in the head, do not query them twice.)
- Report the backlog (mandatory, one line). State the number of open inbox reports and the
state-file size from the stamped head, plus the number of open ledger rows (not wired up, not
measured live) where a ledger exists, plus the board line from step 1b, and if
--pullmoved anything, name every move in plain words. Above 10 open reports or above the 40 KB cap: that is the first sentence of the recap, not a footnote. A backlog only grows into the hundreds because nobody says it out loud at the start. The same goes for an unread client voice and an unopened attachment (block 3b), for a card marked as urgent for the owner, and for an unwatched collection source: first sentence, not footnote. - If there are deltas vs the active table: show them as a compact list, integrate into the state file, ask about anything that stays unclear.
- Give a short recap in the chat (priority-1 action first).
Keep ground truth and repo hygiene in view (mandatory, PM core job)
Keeping the ACTUAL repo and deploy state in view is a PM core job, not the user's. If the user trips over a pile of stale branches, or thinks something is (not) deployed, the PM chat has not done its job. Three standing duties:
- Ground truth before any assertion. For any "is X deployed / merged / through / live?"
statement, NEVER quote the pm-state prose (it drifts), query the source:
pm-facts.sh, the migration ledger, the production tip,git log origin/main, live edge versions. Check first, then answer. Seen in practice: pm-state said "deploy on hold" while the phase had been live for about 24 hours, which produced a wrong answer straight out of stale prose. Reconcile instead of appending. When integrating a deploy/merge/done report, ALWAYS also pull the load-bearing frontmatter fields (
current-phase,current-wave,next-wave-candidate) and the active table up to the new state, not just hang a line in the chronicle. Exactly this "append only, fields go stale" pattern is what creates the drift.And because prose appeals do not hold, it is mechanically backed: the measurable facts in the head are stamped by
pm-facts.sh(appending cannot make them stale, because they get overwritten), and the chronicle has its own place under## 📜 Chroniclewhere appending is explicitly allowed. Reconciling is then only left for the handwriting: open decisions, blockers, active table. That is the whole trick. Earlier versions had this rule as prose and relied on compliance; it was broken in most workspaces anyway, because appending is cheaper than reconciling.- Surface hygiene drift actively. On resync, also scan
git branch --no-merged main,git worktree list,gh pr list --state open. If orphaned worktrees, squash-merged branch corpses, or long-open PRs pile up, name it BRIEFLY in the recap ("N dead worktrees, M merged branch leftovers, PR #X open") and offer a cleanup block, instead of waiting until the user trips over it. Separate clearly: real unmerged work (the branch has commits that are NOT on main) versus a squash-merge corpse (content long since on main, only the ref is lying around). Squash merges make ALL merged branches show up as--no-merged, so never conclude "unfinished" from that list alone.
When the user pastes a report from a spawn chat
- Check pm-inbox first (see the inbox-polling rule below): the report may already be a file and the verbal note is just the teaser.
- Identify which issue / task it concerns.
- Update the state file (status, PR, worktree section if any).
- If status moves to
done: the entry moves out of the active table into the done list. - If cascading
blockedrows clear: set them totodoand offer a dispatch prompt. - If a worktree is empty (PR merged plus bookkeeping done): actively say "you can close the chat for issue X, the worktree can go".
- Name the next action in the chat.
Inbox polling during a running session (mandatory)
Auto-resync at start is NOT enough. Spawn chats write reports while the user and the PM chat run in parallel, and the PM chat misses them if it does not poll the inbox periodically.
Mandatory inbox check before every substantial PM answer (recap, state update, dispatch-prompt
build, an outward action such as sending mail). An ls .scratch/pm-inbox/*.md costs 50ms and blocks
nothing. If a new file is there: read and integrate the report first, THEN answer the current
question, otherwise the answer is based on stale state.
Trigger phrases that MANDATE an inbox check (even when the user does not say "inbox"):
- "from the other chat" / "from the [name] chat"
- "update from" / "report from" / "he said something else"
- "the other chat is done" / "chat X is through"
- "I sent you something" / "I dropped the report in"
- "is that right?" plus a quote from a spawn-chat output
- any statement in which the user teases or references a spawn-chat output
What does NOT require a check: a status update the user mentions in passing (for example "I quickly verified F-18"), which gets maintained directly. The inbox check is only for reports FROM spawn chats.
When a PM: trigger is called
Produce short output inline (recap, cleanup command block). Exception, the dispatch prompt: that
is ALWAYS written as an .md to disk, and the chat carries only the paste line. No new chat needs
to be opened from the PM chat.
When an action needs file edits (building the dispatch prompt)
Step 0, routing check plus bundling (mandatory with 2 or more buildable items). First the routing check above: known area with an existing runner routes there automatically, no question; a new area with 3 or more independent items gets a pro/contra question. Otherwise: do 2 or more items bundle cleanly (file disjoint, no gate in between, product decisions locked, low risk such as frontend only with no migration)? Then NOT N separate prompts, but ONE queue plus ONE start command. Sequential only where a REAL dependency or gate forces it (migration before frontend, deploy ordering, an open product decision).
Step 1, write the prompt as an .md to disk. NEVER just dump the prompt in the chat. Write it
to:
<workspace>/.scratch/<topic>/SPAWN-<YYYY-MM-DD-HHMM>-<slug>.md
following this schema, where the inbox closing line is mandatory:
You are working on the <project> repo. Working directory: <absolute-path>.
Task: <what to do, in 1 to 2 sentences>
Steps:
1. <step 1>
2. <step 2>
…
Acceptance:
- <criterion 1>
- <criterion 2>
Write your final report to:
<workspace>/.scratch/pm-inbox/<YYYY-MM-DD-HHMM>-<topic-slug>.md
with frontmatter:
---
topic: <short title>
issue: <issue no. or none>
pr: <pr no. after merge or none>
status: <merged | open | red | review | manual | done | blocked>
worktree: <path or none>
---
## ✅ Completion check
| Check | Status |
|---|---|
| PM inbox report written | ✅ <path> |
| Core work done | ✅ / 🟡 partly / ❌ |
| Docs/ledger updated | ✅ / n/a |
| Problems/blockers | ✅ none / ❌ <short> |
Body: 3 to 10 sentences on what was done plus what comes next.
Also end your LAST chat answer with exactly this ✅ completion-check block. Use ❌ only when
something is genuinely missing or went wrong.
Message the PM chat as soon as you hit a product decision or an abort condition, and when you
are done. The PM chat pings you at the start, reply to that message. The report above stays
mandatory, the message never replaces it.Mandatory footer: every dispatch .md ends with this ✅ completion-check block (table: PM inbox
report / core work / docs-ledger / problems-blockers). The block sits at the very top of the
report (directly under the frontmatter, see above) and closes the last chat answer of the
executing chat, so every future spawn chat carries the green completion check automatically.
Mandatory ending "staged, not live". Every dispatch .md that touches code, a migration, or a
deployed function ends on staged, never on an invitation to deploy. The block goes into the
assignment verbatim, directly before the completion check:
## End of the assignment: staged, not live
- Commit on the feature branch, push, and a PR are fine (a PR is not a deploy, the
release-chain reviews hang off it).
- NO merge. NO deploy. Do NOT apply the migration.
- Add the finished work to the shared deploy queue.
- Then stop. The owner collects several finished assignments and runs ONE full review
plus ONE deploy over the queue.Why: N finished assignments used to mean N deploy decisions, N reviews, and N production mutations. Collected, the full diff gets reviewed ONCE and deployed ONCE. A batch runner can add the queue entry on its own at the end of a green run; hand-written assignments need the block explicitly.
Step 2, put ONLY the paste line in the chat:
<project> <topic>, read and work through: <absolute path to the SPAWN file>The user drops that single line into a new chat tab (the tab names itself after the first message), the spawn chat reads the file, executes, and writes its inbox report; the PM chat picks it up automatically on the next resync or trigger.
Step 3: open the live channel (mandatory, see below).
The live channel: a spawn chat is no longer a black box
Local Claude sessions can reach each other in both directions via ListAgents plus
SendMessage (measured three times per direction: send, reply back, follow-up). An incoming message
is shown to the receiving session as its own conversation turn, so it does not get lost.
What this solves, and it is not the report: today a spawn writes needs-decision into its report
and stops. The report then sits there until somebody reads it, which is after all the work is
done. Measured occasion: a review dispatch had nine questions fully drafted, five of them
duplicates of a question that had already been prepared elsewhere. A note sent mid-run caught it.
The PM chat pings FIRST, as soon as the new session shows up in ListAgents:
I am your PM chat. Reply to this message when you hit a product decision, when you hit an abort
condition, and when you are done. The pm-inbox report stays mandatory.That is not politeness, it is necessary: a spawn sees only unnamed neighbour sessions in its own list and cannot address the PM chat without this first message. An incoming message carries its sender, and that is something it can reply to.
The same two lines belong in the dispatch .md (section above, directly before the completion
check), so the spawn knows about the channel even if it missed the ping.
Four rules, three of them paid for by measurement:
| Rule | Why |
| The address is settled BEFORE the first message | Two equally fresh sessions were running. Two messages went to both (with a discard header) and arrived; the third went to only one out of convenience, and it was the wrong one, a foreign session that discarded it as instructed. The intended recipient never saw it. Sending to two addresses because you do not know the right one is not a procedure, it works exactly once |
Reply via the name from ListAgents | The sender name shown on a reply is the chat TITLE, not the addressable id. Address by the ListAgents name, plus its [ref] when the session does not belong to your own conversation |
| The message notifies, the report is the truth | No status travels by message only. And keep your out-of-band push channel for "the human is away from the machine"; a chat message does not reach them there |
| No permission laundering | A spawn never asks another chat to do something that was blocked in its own session. The tool forbids it explicitly, and you do not route around a gate through a neighbour |
Not measured, so not claimed: whether an idle session processes a message before somebody types
into it, and whether the waves of a headless batch runner show up in ListAgents at all (they run
without a UI). For those, the convention only holds once a run demonstrates it.
PM-inbox pattern
So spawn-chat results do not have to be copy-pasted by hand, spawn chats write their final report to a file. The PM chat reads and archives it.
Mandatory status block in the report: every report carries the ✅ completion-check block at the very top (directly under the frontmatter, table: PM inbox report / core work / docs-ledger / problems-blockers). The executing chat also ends its last chat answer with the same block. That way the PM chat (and the user) sees at a glance whether the task came through green or where it is stuck.
Paths:
<workspace>/.scratch/pm-inbox/, new reports<workspace>/.scratch/pm-inbox/_archive/, processed reports
File format: <YYYY-MM-DD-HHMM>-<topic-slug>.md
Processing on the inbox trigger or auto-resync:
- Read all
.mdfiles underpm-inbox/(not_archive/). - Per report: integrate into the active table (status, PR, worktree).
- Idempotency check: if pm-state already tracks the issue correctly (same status, same PR number, history line exists, for example because a wave runner already reconciled it), do not re-integrate: no second changelog line, no second version bump. Just archive and mark it as "already integrated" in the recap.
- Move processed reports to
_archive/. - Show a compact recap "N reports processed, status changes: …" (including the already-integrated ones as an info line).
Three ways spawn chats learn the inbox pattern
| Way | When |
A: inbox closing line in the dispatch prompt (.md plus paste line, see above) | when the PM chat builds the dispatch prompt (default path) |
| B: boilerplate snippet handed over | when a chat was started without a PM-chat dispatch prompt |
| C: auto-resync via git / PRs | catch-all for all uninstructed chats |
Boilerplate snippet (way B)
On the inbox snippet trigger the PM chat produces:
Before you go: write a final report to
<workspace>/.scratch/pm-inbox/<YYYY-MM-DD-HHMM>-<topic-slug>.md
with frontmatter:
---
topic: <short>
issue: <no. or none>
pr: <no. or none>
status: <merged | open | red | review | manual | done | blocked>
worktree: <path or none>
---
## ✅ Completion check
| Check | Status |
|---|---|
| PM inbox report written | ✅ <path> |
| Core work done | ✅ / 🟡 partly / ❌ |
| Docs/ledger updated | ✅ / n/a |
| Problems/blockers | ✅ none / ❌ <short> |
Body: 3 to 10 sentences on what was done plus what is open.
Also end your last chat answer with exactly this ✅ completion-check block.The user pastes that into an already-running spawn chat as the last instruction.
One core across all workspaces
Per workspace, only the CONTENT of the state file is specific, never its form. Improvements to the form, the schema, or the scripts apply immediately to every workspace and get applied everywhere in the same round, not "next time". A PM setup that runs differently in each project stops being one skill and becomes four half-remembered conventions.
Relation to other skills
| Skill | Role |
pm (this) | Dispatcher: state file, overview, assignments |
| a planner (own chat) | Planner: the next wave as an issue list plus paste-ready prompts |
| an executor (spawn chat) | Executor: inner build/TDD loop per issue |
| a reviewer (spawn chat) | Reviewer: plan stress-test against the domain |
Self-anneal
When a pattern shows up during PM maintenance that the schema does not cover:
- Propose a schema update
- Bump the version in the frontmatter
- Add a chronicle entry in the state file
- If the pattern is repeatable: update this SKILL.md too (as a spawn assignment, not in the PM chat, per Rule 1)
- Apply it in every workspace, not only in the one where it surfaced
Safety rule (short form of Rule 1)
PM-chat context is expensive (long sessions). If you notice yourself starting file edits / research /
deep thinking, STOP. That becomes a You: open a spawn chat with a dispatch prompt (as an .md
plus a paste line). Better to spend 30 seconds writing a dispatch prompt file than 5 minutes
inflating context. Full version with the prohibition table: Rule 1, at the top.
Changelog
| Version | Change |
| 1.9.0 | The live channel between the PM chat and its spawn chats, plus the outbound half of working with a domain expert. Until now a spawn was a black box: it wrote needs-decision into its report and stopped, and the report sat there until somebody read it, which is after all the work is done. Measured occasion: a review dispatch had nine questions drafted, five of them duplicates of a question already prepared elsewhere; a note sent mid-run caught it. (1) New step 3 in the dispatch build plus its own section: ListAgents plus SendMessage reach local sessions in both directions (measured three times per direction; an incoming message shows up as its own conversation turn). (2) The PM chat pings first, with the exact wording, because a spawn sees only unnamed neighbour sessions and cannot address the PM chat otherwise. (3) The same two lines go into the dispatch .md, in case the ping was missed. (4) Four rules, three paid for by measurement: settle the address before the first message (a message went to the wrong one of two equally fresh sessions; broadcasting to two addresses works exactly once), reply via the ListAgents name rather than the chat title shown on a reply, the message notifies while the report stays the truth, and no permission laundering through a neighbour chat. (5) Explicitly not claimed: whether an idle session processes a message unattended, and whether headless batch waves appear in ListAgents at all. Plus a new section "Working with the domain expert: ONE queue, two doors" as the counterpart to the board watch: a question is a pair of a file in a shared repo and a card on the board, answered through their own Claude at a desk or on a phone, with their answer writing itself onto the card. Why not email, measured: four question numbers were treated as open while three were already answered (two inside board comments nobody held against the question list), and one email carrying nine questions left six open unnoticed. A channel that cannot ask a follow-up produces half-answers. |
| 1.8.0 | The board watch reads comments, not just titles and labels. Measured trigger: "do you actually read the comments? I get the feeling you never do." Mechanically correct: the watch read columns, titles, labels, idle times, and files under .scratch/, but never a card description and never a comment. Block 3 is called "the client's voice first" and in truth only evaluated whether somebody had set a label. Three findings out of one afternoon of reading comments, all two weeks old, none of them in the state file: a filled-out spreadsheet that had been hanging as an attachment on a different card while two cards were tracked as "waiting on the client" (the blocker was on our side), an unrecorded capacity rule inside the same comment, and a comment that made half of another card's description obsolete. (1) New block 3b "unread voices" between 3 and 4 and at the top of the reporting duty: comments, description changes, and attachments that arrived after the last acknowledgement, client voices first and always, across ALL open cards including done and backlog, which is exactly where two of the three findings sat. (2) Attachments are watched BOARD-WIDE, not per card, the actual finding: between the card where a deliverable lands and the card waiting for it there is no structural connection. Card links are evaluated, and an unread attachment on a linked card is reported at the waiting card. (3) A reported attachment is not read, it is only known: the watch is LLM-free and cannot interpret an image. Open it, then acknowledge; calling the acknowledge flag blind switches off the report, not the problem, and whatever gets acknowledged has to be shown while it is acknowledged. (4) Gate 0 sharpened: "a request is input, not a spec" did not say WHAT in a request carries. Now: the observation of a domain expert is evidence, their proposal is an opinion; a comment you just read is not a decision; a proposal that contradicts the existing system gets contradicted with a reason; if the domain expert and the owner disagree, the owner decides and until then the question stands as OPEN. Trigger: two freshly found comments were written straight into a card as fact and both failed review. |
| 1.7.0 | Board watch at the start of every session. Measured trigger: a live, customer-facing bug card sat in a column for four days and slipped through every planning round, because the PM chat plans out of pm-state.md and read the board only when asked; the client had to chase it through a side channel. The rule "keep the board in view" had already existed for a week and hung on attention alone. (1) New generic scripts/board-facts.sh as a sibling of pm-facts.sh, wired in as step 1b of the start sequence, equal in rank. It knows nothing about any board: the board logic lives in a project-side adapter, and workspaces without one print a single line. Generic core, local special case, no new engine. (2) Four stamped blocks with a reporting duty: not on the board (no reference at all versus prose-only reference, backlog counted rather than listed), idle time from 3 days, the client's voice first including label leftovers on finished cards, and unwatched collection sources under .scratch/. (3) Block 4 is the lesson from a second incident the same day: a client wish sat in a feedback digest for seven weeks and walked past every planning round; a pure board watch would never have found it. (4) Automatic pull-forward only on a measurably unambiguous state (PR open / merged / merge commit in the production tip), anchored on the card reference in the PR, with five hard locks, and every move named in the report. (5) The backlog report now carries the board line too. Never a blocker: timeout, every error is a warning line with exit 0. |
| 1.6.0 | Process round. (1) Intake gate (gate 0): every request gets an intake row in the new decision ledger (<workspace>/.scratch/decision-ledger.md) and runs through at least one short review pass before anything gets built, with exceptions justified in writing. Maintaining the ledger is the fifth allowed PM activity (and the third allowed write), and carrying its open rows forward is part of the mandatory backlog report at start. (2) Merge and release authority: the PM chat releases (as a deploy dispatch) through the standard release chain; wave and build chats never merge to main. The human go stays as a transitional safeguard until the chain has proven itself. (3) Three hard formats added: the four-column assignment table (with an explicit "can this run in parallel" column, measured rather than guessed), the mandatory "staged, not live" ending on every dispatch that touches code, and tidying the state file and inbox without asking first (the repo stays reported, not tidied). |
| 1.5.0 | Anti-erosion round. Measured trigger: most state files had drifted off their own template, one had decayed into dozens of appended chronicle blocks with no head left. (1) Rule 1 "the PM chat does not work" moved from the bottom to the very top with a prohibition table and a sharp measuring line (one pm-facts.sh call is managing, browsing files is working). (2) box <project> as the default trigger in the table plus its own section, equivalent to /pm plus auto-resync, bundled hook documented. (3) Hard separation of status head and chronicle under the marker ## 📜 Chronicle; "at the top of pm-state.md" now means "at the top of the chronicle section" everywhere. (4) Measurable facts get measured: new generic scripts/pm-facts.sh stamps worktrees, branch, SHA, dirty state, ahead-of-base, inbox count, and file size between pm-facts markers, with project-specific measurements via an optional .scratch/pm-facts-extra.sh. Handwriting is left for decisions, blockers, and reasons. (5) Backlog report as a mandatory first sentence at start. (6) 40 KB size cap, chronicle then moves to .scratch/pm-chronicle/<YYYY>.md. (7) Frontmatter fields are fields (one line, around 200 characters). No new engine, one shell script. |
| 1.4.0 | Routing check as mandatory triage before any slicing (trigger of more than one step or item, ground truth, plan format, then routing: known area automatic, new area with 3 or more items gets a pro/contra question, trivial stays inline). Pulled onto step 0 of the dispatch-prompt build. No new engine. |
| 1.3.0 | Green completion check made permanent: every dispatch .md ends with the ✅ block (PM inbox report / core work / docs-ledger / problems-blockers), placed at the top of the report and as the close of the executing chat's last answer. |
| 1.2.0 | Hard format for the default output on next: numbered paste lines only, zero explanatory text, EVERY item (including cleanup and trivia) a dispatch .md plus paste line. Output style gained two rules: no options menu and no "just say what you want" trailer, and never explain risk or governance unprompted. |
| 1.1.0 | Handoff hardening. Dispatch prompts are written as .md files to disk (.scratch/<topic>/SPAWN-*.md), the chat carries only the paste line. New "output style" section. "Check bundling" as step 0 of the dispatch-prompt build. |
| 1.0.0 | First version: PM dispatcher, state-file schema, inbox pattern, auto-resync, ground-truth duty. |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

