agentleFS
mfraunhofermfraunhofer/claude-skills/pm/SKILL.md

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).

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
  1. Distinctive:

    Spawn / issue chats: do the actual work (code, edits, research, TDD).

    line 55
  2. Distinctive:

    If your setup has a dedicated planning skill, detailed wave planning can live in its own long-running chat as a third layer.

    line 58
  3. Distinctive:

    Bundle by default: when slicing waves or prompts, FIRST check whether steps bundle cleanly (file disjoint, no hard gate in between, product …

    line 68
  4. Distinctive:

    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:

    line 82
  5. Very specific:

    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 …

    line 101
  6. Very specific:

    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 …

    line 150
  7. Very specific:

    What --pull moves, and what it explicitly does not: Only what follows from a measurement, never from an interpretation.

    line 213
  8. Distinctive:

    No waffle. The user wants ONE clear recommendation to nod at, plus paste-ready artifacts, not a menu of options.

    line 282
  9. Very specific:

    Actions are numbered steps 1/2/3, imperative, bam bam bam: No prose padding, no options menu, no "shall I…?" trailer.

    line 289
  10. Distinctive:

    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.

    line 299
  11. Very specific:

    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 …

    line 308
  12. Distinctive:

    When the user asks what assignments are open ("give me the table of dispatch files", "what is queued"), the answer is exactly this table, …

    line 325
  13. Very specific:

    Read <workspace>/.scratch/pm-inbox/*.md and move them to pm-inbox/_archive/

    line 353
  14. Very specific:

    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 …

    line 359
  15. Very specific:

    Run gh pr list / git log / git worktree list / git branch as a read-only source for auto-resync

    line 361
  16. Distinctive:

    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 …

    line 442
  17. Very specific:

    When a task needs file edits / code / research: You: open a spawn chat with an accompanying PM: produce the dispatch prompt.

    line 527
  18. Distinctive:

    Never ask what "box" means, it is a fixed convention.

    line 556
  19. Very specific:

    Read <workspace>/.scratch/pm-state.md.

    line 578
  20. Very specific:

    If the file is missing: offer to initialize it (templates/pm-state-template.md as the template, reconstruct state from gh pr list plus …

    line 581
  21. Distinctive:

    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.

    line 597
  22. Very specific:

    Reconcile instead of appending: When integrating a deploy/merge/done report, ALWAYS also pull the load-bearing frontmatter fields …

    line 612
  23. Distinctive:

    Update the state file (status, PR, worktree section if any).

    line 638
  24. Very specific:

    If status moves to done: the entry moves out of the active table into the done list.

    line 639
  25. Very specific:

    If cascading blocked rows clear: set them to todo and offer a dispatch prompt.

    line 640
  26. Distinctive:

    If a worktree is empty (PR merged plus bookkeeping done): actively say "you can close the chat for issue X, the worktree can go".

    line 641
  27. Very specific:

    Step 1, write the prompt as an .md to disk: NEVER just dump the prompt in the chat.

    line 684
  28. Very specific:

    Mandatory footer: every dispatch .md ends with this ✅ completion-check block (table: PM inbox report / core work / docs-ledger / …

    line 735
  29. Very specific:

    Mandatory ending "staged, not live": Every dispatch .md that touches code, a migration, or a deployed function ends on staged, never on an …

    line 740
  30. Distinctive:

    Mandatory status block in the report: every report carries the ✅ completion-check block at the very top (directly under the frontmatter, …

    line 814
  31. Very specific:

    Read all .md files under pm-inbox/ (not _archive/).

    line 827
  32. Distinctive:

    Idempotency check: if pm-state already tracks the issue correctly (same status, same PR number, history line exists, for example because a …

    line 829
  33. Distinctive:

    When a pattern shows up during PM maintenance that the schema does not cover:

    line 895
  34. Distinctive:

    Add a chronicle entry in the state file

    line 898
  35. Very specific:

    If the pattern is repeatable: update this SKILL.md too (as a spawn assignment, not in the PM chat, per Rule 1)

    line 899

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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:

ForbiddenBelongs instead in
Editing code / issue files / index filesspawn chat
Reading source, configs, or scripts to answer a questionspawn chat (if measurable: pm-facts.sh)
Building scripts / toolingspawn chat
Research, web lookups, WebFetchspawn chat (research subagent)
TDD, implementation, audits, visual reviewsspawn chat
Digging into issue contents ("what needs to go into issue 13")spawn chat
Rebuilding the state-file format or this skill itselfspawn 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:

  1. PM chat (this skill): maintains <workspace>/.scratch/pm-state.md, gives the overview, formulates assignments. Stays open for a long time.
  2. 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:

  1. 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.)
  2. 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).
  3. 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 .md plus 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:

  1. 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.
  2. 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.
  3. 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.
  4. 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 forward

The 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:

BlockWhat it reports
1 Not on the boardopen 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 timecards 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 firstthe 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 voicescomments, 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 sourcesfiles 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 forwardevery 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 stateCard moves toLabel
a PR carrying the reference is openin progressnone
PR merged, merge commit not yet in the shipped statereview"waiting for test"
merge commit is an ancestor of the production tipdoneclear 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:

RuleWhy
Every message to them appears in full in the chat firstYou 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 opinionGate 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 rememberedTheir 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.

  1. A handoff is ALWAYS an .md file 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.
  2. 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.
  3. 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.
  4. Bundle first. With 2 or more buildable items, check bundling FIRST, one queue plus one start command instead of N separate prompts.
  5. 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.
  6. 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.
  7. Never explain risk or governance unprompted. It belongs in the PR body and the deploy .md. In the PM chat, label it only (for example merge=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:

#FileWhat it doesUrgency
1<clickable relative path to the SPAWN-*.md>one or two sentences on what comes out of itwhen · 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 #N rather than parallel 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/*.md and move them to pm-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 next list: there, cleanup becomes a dispatch .md plus paste line like every other item
  • 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 chat
  • Run gh pr list / git log / git worktree list / git branch as 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 .md under .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.md

The 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

  1. Status head (## 📌 Status head) with the measured facts between the pm-facts markers, then handwritten: open decisions, blockers, active table, next up
  2. Legends: priority, status, PR, action classes, trigger phrases
  3. Done list: compressed list of finished issues (action always –)
  4. 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

CodeMeaninghas action
todoNot startedyes
doingChat running, code in a worktreeyes
reviewPR open, review runningyes
ciCI running / waiting for greenyes
merge-readyAll green, just the merge clickyes
mergedCode in, but bookkeeping still openyes
doneTruly everything done, moves to the done listno (–)
blockedWaiting on something elseyes
manualWaiting on a human action (test, click-test, decision)yes
(chore)Maintenance task without an issue numberyes

Rule: status done means no action left. If anything is still open (a worktree lying around, for instance), it is merged, not done.

PR

CodeMeaning
–no PR open
draftdraft, not ready
openopen, CI/review running, not ready
redCI red or findings blocking
readyall green, just the merge click
mergeddone
closedclosed, 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 quotemandatory 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 reportmaintains 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

  1. Stamp ground truth BEFORE reading: ~/.claude/skills/pm/scripts/pm-facts.sh "<workspace>" --stamp That 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>" --pull Same reason, different source: pm-facts.sh measures the repo, board-facts.sh measures the board and the collection sources under .scratch/. Without this call the PM chat plans out of pm-state.md again 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.
  2. 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.
  3. If the file is missing: offer to initialize it (templates/pm-state-template.md as the template, reconstruct state from gh pr list plus .scratch/ files).
  4. Run an auto-resync: scan external signals since last-updated from the frontmatter:
    • gh pr list --state all --limit 30, new PRs / status changes
    • git 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.)
  5. 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 --pull moved 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.
  6. 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.
  7. 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:

  1. 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.
  2. 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 ## 📜 Chronicle where 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.

  3. 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

  1. 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.
  2. Identify which issue / task it concerns.
  3. Update the state file (status, PR, worktree section if any).
  4. If status moves to done: the entry moves out of the active table into the done list.
  5. If cascading blocked rows clear: set them to todo and offer a dispatch prompt.
  6. If a worktree is empty (PR merged plus bookkeeping done): actively say "you can close the chat for issue X, the worktree can go".
  7. 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:

RuleWhy
The address is settled BEFORE the first messageTwo 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 ListAgentsThe 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 truthNo 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 launderingA 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:

  1. Read all .md files under pm-inbox/ (not _archive/).
  2. 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.
  3. Move processed reports to _archive/.
  4. 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

WayWhen
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 overwhen a chat was started without a PM-chat dispatch prompt
C: auto-resync via git / PRscatch-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

SkillRole
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:

  1. Propose a schema update
  2. Bump the version in the frontmatter
  3. Add a chronicle entry in the state file
  4. If the pattern is repeatable: update this SKILL.md too (as a spawn assignment, not in the PM chat, per Rule 1)
  5. 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

VersionChange
1.9.0The 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.0The 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.0Board 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.0Process 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.0Anti-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.0Routing 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.0Green 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.0Hard 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.0Handoff 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.0First version: PM dispatcher, state-file schema, inbox pattern, auto-resync, ground-truth duty.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.