Skip to main content

pi-matt-implement-flow: implement a ticket graph in pi

Coordinate the implementation stage of a Matt-style ticket graph in pi, with coder worktrees, ticket review, integration gates and a recorded workflow.

Source facts

Repository
toomanyopenfiles/pi-matt-implement-flow
Last source activity
September 29, 2026 at 21:18
Detected SKILL.md language
English
Stars
1
Forks
0

Examples

The creator's post shows a completed v1-two-state-router report, including a recovered failed implementation attempt. The screenshot is a creator run record, not a run reproduced here.

Uses

Use when a specification and tickets already define the work and its dependencies. The workflow repeatedly computes the ready frontier, integrates completed tickets and finishes with a branch review.

Prerequisites

pi with the configured agent roles, a specification and ticket graph, a Git checkout, and the project's test command. Open tickets need a valid dependency frontier.

How to use

Supply the specification, ticket source and test gate. The workflow verifies the graph and baseline, dispatches ready tickets, checks each finished ticket, routes corrections back to its coder, and merges through an integration gate. The local ledger retains the evidence needed to resume and audit the run.

Limitations

The workflow is an adaptation for pi, rather than the original Matt framework. An empty frontier with unfinished tickets requires investigating the graph. Infrastructure failures and validator disagreements must remain visible; they are not permission to bypass the workflow.

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
pi-matt-implement-flow
description
Orchestrate the implement stage of a Matt-style ticket graph: read spec + tickets, compute the frontier, dispatch parallel coder subagents (each in its own managed worktree), run a two-axis review per ticket, send fixes back to the same coder, merge with an integration-test gate, recompute the frontier, and finish with a whole-branch final review. Replaces hand-worked blockers-first ticket queues and per-ticket /clear.
disable-model-invocation
true
# Implement, orchestrated (pi) You are the **orchestrator**. You write no feature code: you dispatch, verify, merge, and keep the **frontier** moving until the spec is built on one branch. The tickets came from `/to-tickets`: a **task graph** of tracer-bullet slices, each declaring the tickets that **block** it. The frontier is every open ticket whose blockers are all closed and that is `ready-for-agent` (tickets from `/to-tickets` are already agent-ready — never send them through `/triage`). Work it with up to N `coder` subagents at once. N comes from the skill argument (`/pi-matt-implement-flow [N]`); otherwise from `mattImplementFlow.maxConcurrent` in pi settings; otherwise 3. Talk to subagents through **context pointers** (paths, branch names, commit SHAs). Content they can read themselves stays out of the message. This file is your instructions; the ledger, the event stream, and the orchestration notes (see Ledger) are your memory. **After any compaction, re-read this file, then run `build` + `check` and read their output before doing anything else.** ## Names you dispatch Agents are registered as a pi package. Always use the full names — the bare `coder` resolves to the user's `worker` alias, and the bare `reviewer` to the builtin: - `pi-matt-implement-flow.coder` - `pi-matt-implement-flow.reviewer` - `pi-matt-implement-flow.final-reviewer` Skills are baked into the agents (`tdd`/`codebase-design` for the coder, `code-review` for the reviewers). You never call skills on their behalf. On a merge conflict you follow `resolving-merge-conflicts` yourself. Verify registration once before the first dispatch (`subagent({ action: "list", capabilities: true })`). If the three agents are missing, stop and tell the user to run `pi install <this package path>`. ## Preconditions Check all of these before dispatching; on a failure, stop and tell the user what to change. - **Clean tree**: `git status --porcelain` must be empty (pi rejects worktree dispatch on a dirty tree — untracked files count). If it is dirty, classify first: tracker/ledger runtime files → fix the ignore rules (below); your own pending status writes → commit them as `chore(tickets): ...`; anything else → stop and ask. Never stash on your own. - **Ignore rules for runtime state**: ensure `.gitignore` covers `.pi/matt-implement/` (append a marked block if missing; mention it once to the user). If ticket-status writes would dirty git (a git-visible truth layer, e.g. `.scratch/`) and that directory is not ignored, plan to commit ticket-status writes separately (see the loop). - **Git repo with at least one commit** (worktrees cannot be created from an unborn HEAD). - **Tracker setup artifacts (范本识别)**: the issue tracker should have been provided to you — the target repo's `docs/agents/issue-tracker.md` (with the `triage-labels.md` beside it), pointed at from its `## Agent skills` block. Both are `/setup-matt-pocock-skills` products and the run's single tracker configuration — there is no `--tracker` flag repeating the choice. At every tracker-touching ledger command (`init` / `snapshot-init` / `claim` / `sync`) the script reads the artifacts, identifies the upstream template (H1 heading + anchor phrases; user-edited prose is tolerated), and loads the matching contract preset. Two conditions are explicit stop-and-report failures, never a guess, a degradation, or a derived wizard: **artifact missing** → tell the user to run `/setup-matt-pocock-skills` and stop; **template unrecognized** → the script refuses with `仅支持 local / github / gitlab 三种` — pass that refusal on and stop. Follow that file; if the tickets it names cannot be found, stop and ask the user where they are. - **Contract capabilities (按契约能力执行)**: every tracker difference in this file is a declared capability of the identified contract, never a per-tracker branch — reading this file tells you the behavior because the behavior follows the capabilities. Which tracker steps exist at all (snapshot pull, pre-seal sync, 占坑) is likewise a declaration: contracts with a tracker write surface have them, contracts where the ticket files are the truth layer have none (zero change). The four capabilities that shape the flow: - **占坑强度 (claim strength)**: the spec claim is an advisory lock — it reserves the feature and stops a second session before it writes; it is not an atomic mutex. - **closeWithComment**: whether a close carries its closing comment in one action, or the comment is noted first and the close follows — the pre-seal sync orders the actions. - **收尾面 (closing surface)**: the review surface the feature branch closes through — a PR, an MR, or none; the pre-seal sync always precedes marking it ready. - **票集边来源 (ticket-set edges)**: where the ticket set's blocking edges come from — native sub-issues, parent look-ups, or the declared ticket list. - **Spec 引用 (spec reference)**: the identifier that locates this run's spec (tracker doc convention, or the user's argument), in the reference form the identified contract declares — a spec file path, or a native ticket reference (`42` / `#42` / a full issue URL). Every review needs it; the `init` event pins the local spec file it resolves to. - **占坑 (claim the spec)** — when the contract has a claim (a tracker write surface), it is the run's first write on the tracker, before any pull: `node <this-package>/scripts/ledger.js claim --runtime-dir .pi/matt-implement/<slug> --spec <spec 引用>` — the contract's claim template reserves the spec to this run so the tracker shows the feature as in-flight. Conflicts (the spec already claimed by someone else) come back as a script refusal naming the assignee: stop and ask before any further write — change target, take over, or coordinate. The claim is released at the end: the pre-seal sync closes the spec, or the abandon sync (`sync --mode abandon`) unassigns it. Contracts without a tracker write surface have no claim step — the ticket files are the truth layer (zero change). - **Test command**: determine the project's full-suite command (`package.json` `scripts.test`, Makefile, …). If ambiguous, ask once and pass it to the `init` event. - **Graph**: every ticket has a `Blocked by` line (or a native blocking link, where the contract's edge sources provide one) and the initial frontier is non-empty. An empty frontier with open tickets means a cycle — stop and report. ## Flow configuration This run's shape — whether each ticket gets a reviewer, the per-ticket fix budget, and the coder concurrency — is read from the package-private `mattImplementFlow` key in pi settings (`<repo>/.pi/settings.json` wins per field over `~/.pi/agent/settings.json`; never write any other settings key), then **frozen as `init` flags** in the ledger: | key | default | meaning | |---|---|---| | `reviewer` | `true` | per-ticket two-axis review + fix loop; `false` = merge straight after the platform test gate (the whole-branch final-reviewer still runs) | | `maxFixRounds` | `2` | fix attempts per ticket before escalation; only meaningful when `reviewer` is on | | `maxConcurrent` | `3` | parallel coders; the skill argument wins | Pass them to `init` as `--reviewer on|off --max-fix-rounds N --max-concurrent N`; omitted flags mean the defaults. The ledger script enforces the frozen shape from that point on: with `reviewer=off` it rejects `verdict`/`fix` events and lets `merge` proceed without a verdict, and the fix budget is whatever the snapshot recorded. Configure via `/matt-flow-config` → "Configure flow options"; changes apply from the next run's `init`, never mid-run. ## Ledger Your memory has three layers, each with exactly one owner. Terms (per `CONTEXT.md`): ledger 台账 / event stream 事件流 / orchestration notes 编排笔记 / record 记账 / seal 封账 / reconcile 对账. The old phrase "event log" is retired — never use it. - **Event stream** — `.pi/matt-implement/<feature-slug>/events.jsonl`. Append-only machine facts and your **only** state write surface: one JSON line per structured event, stamped by the script with the authoritative timestamp, a monotonic sequence number, a format version, and the git HEAD at write time. You never provide timestamps; you never touch this file directly. - **Ledger** — `.pi/matt-implement/<feature-slug>/ledger.md`. The derived human-readable view (header with `state: running|complete` / ticket table / event timeline / reconciliation). **The script owns its write authority — you never hand-write or edit it, not even one cell.** A drifted or damaged ledger is regenerated deterministically with `build`, never patched. - **Orchestration notes** — `.pi/matt-implement/<feature-slug>/notes.md`. Prose memory: process narrative, lessons, the user's verbal decisions. Facts ("what happened, when") go to the event stream; prose ("why, what we learned") goes here. Prose never competes with the event stream as a source of truth. - **You never hand-write the ledger.** Every state transition is recorded (记账) with one script command: `node <this-package>/scripts/ledger.js add <type> --runtime-dir .pi/matt-implement/<feature-slug> [flags]` (`<this-package>` is the directory containing this SKILL.md). - **Event types, flags-style (never raw JSON)**: `init` / `dispatch` / `settled` / `verdict` / `fix` / `merge` / `escalate` / `final` / `anomaly` / `pr` / `close` — run `--help` for each type's exact flag set; free text is always `--note`. - **The script validates before writing**: bad payloads are rejected with a reason — fix and retry immediately (your context is freshest now); state-machine violations and definite git contradictions are rejected outright; facts that are merely not-yet-verifiable (e.g. a worktree not yet in `git worktree list`) come back as warnings and the event is recorded. - **There are no bypass flags.** When you disagree with the validator, record `anomaly --note "..."` and stop to report. Three command disciplines: 1. **Record on every state transition**: dispatch, settle, verdict, fix dispatch, merge, escalation, the final review's verdict (终审裁决 — one run-level `final` event per round), PR transitions; `close` (封账) seals the run — the ledger flips to `state: complete` and every further record is rejected. 2. **Reconcile (对账) before every dispatch and every merge**: `node <this-package>/scripts/ledger.js check --runtime-dir ...` — non-zero exit means ledger-truth drift, listed item by item. Fix the world to match truth or truth to match the world; never the ledger by hand. When the run reads a tracker snapshot, the ticket files this reads are the snapshot's copies — the tracker body is a delayed mirror outside the truth layer; fix `Status:` on the snapshot, never on the tracker's own body. 3. **Regenerate after compaction**: `node <this-package>/scripts/ledger.js build --runtime-dir ...` prints the full four-section ledger; continue from its output plus the orchestration notes, never from memory. Review bundles go to `.pi/matt-implement/<feature-slug>/reviews/<NN>-r<k>.diff`, findings to `.../findings/<NN>-r<k>.md` — pass the findings path to the `verdict` event via `--findings`. ## The loop Restate the plan to the user in at most ten lines (branch, ticket count, first frontier, N), then proceed without waiting. ### Cold resume (续跑) — the same command, an unfinished run Before Round 0, check whether this run already exists: if `.pi/matt-implement/<slug>/events.jsonl` exists and the ledger header reads `state: running` (not sealed), the same command is a **continuation (续跑)**, not a new run — skip ahead and keep going: 1. **Skip init and the pull — both.** Never re-record `init` (the script rejects a second one), and never re-run `snapshot-init`: an existing snapshot is refused, never overwritten or re-pulled — a re-pull would let the tracker's lagging state overwrite the local truth (ADR-0003). The refusal is the continuation guard, not an error to work around. **One recovery wedge inside the guard**: if the snapshot already exists but the event stream carries **no `init` event** (a crash between `snapshot-init` and the `init` recording — the pull landed, the ledger did not), record `init` with `--spec .pi/matt-implement/<slug>/tracker/spec.md`, the snapshot's own spec copy — the truth layer enumerates ticket files by the same `dirname(spec)/issues/` convention — and continue with the rest of Round 0; the pull is still never re-run. 2. **Rebuild, then reconcile**: `node <this-package>/scripts/ledger.js build --runtime-dir .pi/matt-implement/<slug>` (台账再生) followed by `check` (对账); read their output — the regenerated ledger carries the full state and any ledger-truth drift item by item — then read the orchestration notes. 3. **Continue from the frontier** the ledger reports: remaining tickets, the fix loop, merges, and the final gate all pick up where the event stream left off. (续跑 is a run-level action. A subagent's retained-context resume inside the loop is a platform mechanism — the glossary keeps the two apart.) ### Round 0 — graph, branch, baseline 1. Read the spec and every ticket, resolve the flow configuration (above), then record `init`(记账)— `--branch --branch-base --baseline-sha --spec --test-command [--reviewer on|off --max-fix-rounds N --max-concurrent N --tickets 01,02,1042]` — as the ledger's first event. There is no `--tracker` flag: the script auto-identifies the tracker from the setup artifacts and records the identified tracker with the event. The flow flags freeze this run's shape; everything downstream is derived from it; the baseline the init pins is what later failures are attributable against. `--tickets` (the same list snapshot-init takes) freezes the run's ticket-set boundary — once recorded, out-of-boundary dispatches are refused mid-run; omit it when the set resolves from the contract's edge sources (native sub-issues / parent look-ups). `--spec` is the spec file the run actually reads: - **The contract materializes a tracker snapshot** — pull first, read after: `node <this-package>/scripts/ledger.js snapshot-init --runtime-dir .pi/matt-implement/<slug> --spec <spec 引用>`(the reference form the contract declares;add `--tickets 01,02,1042` only when the ticket-set resolution needs the fallback list)materializes the whole tracker into the **tracker snapshot** under `.pi/matt-implement/<slug>/tracker/` — the spec with a `Source:` line for its tracker origin, one local-shaped file per ticket. The snapshot is the pending-push truth; the tracker body is its delayed mirror, not written to mid-run. From here on the orchestrator, the coder briefs, the ledger, and both reviewers read and write those local paths with zero form fork, and `init`'s `--spec` is the snapshot's `spec.md`. A refusal because a snapshot already exists is the continuation guard — the run already started; go to Cold resume (above). - **The truth layer is already the local files** — nothing to pull: the spec and tickets under `.scratch/<slug>/` are the local files every brief has always pointed at (zero change). 2. **Environment survey**: read `.gitignore` and enumerate every runtime path a coder worktree will not contain (`.scratch/`, `.pi/`, `data/`, …). Write the survey into a **环境事实 (environment facts)** section of the orchestration notes with exactly three elements: the gitignored path list, where real data actually lives, and the validation discipline (validate against real data through the scratch directory). Every coder brief's Worktree-reality parenthetical and its data paths are picked per ticket from this section — filtering is your job; coordination prose (routing decisions, user rulings, process narrative) never enters a brief, and coders never read the notes file whole. The survey is prose: it lands only in the orchestration notes, never in the event stream (no new event type). 3. On the default branch, create `feat/<feature-slug>` from HEAD; on any other branch, stay on it and record it. 4. Run the full test suite once; record the baseline (green or the failing list) in the orchestration notes. 5. With a remote, push the branch and open a **draft closing surface** — a PR where the host runs pull requests, an MR where it runs merge requests — whose body's closing keywords cover the spec and merged tickets only — escalated tickets stay open (their sync keeps them open with an explanatory comment), so never list them in the closing list; record `pr --state opened-draft`. Optionally, when a survey finding is a durable repo-level lesson (e.g. a CLI syntax trap), suggest graduating it into the committed `AGENTS.md` — coder worktrees carry committed files automatically, at zero brief cost. ### Each round — dispatch the frontier Count running coders; while below N and the frontier is non-empty, claim the next tickets(认领)— write `Status: claimed` on the ticket's truth-layer file — the tracker snapshot's copy when the run reads one, the ticket file itself when the files are the truth layer; never a tracker-body write — tracker-side progress happens only at the pre-seal sync — and dispatch one wave — **exactly** one top-level subagent workflow call with `async: true`: ```js const results = await runs.all([ { key: "t-01", agent: "pi-matt-implement-flow.coder", task: `<coder brief — see Briefs>`, worktree: true, gate: { command: "node <this-package>/scripts/mechanical-report.js --base <baseCommit> --test-command \"<testCommand>\"", output: "json", schema: { type: "object", required: ["headSha", "testResult", "changedFiles", "validationOutput"], properties: { headSha: { type: "string" }, testResult: { type: "string" }, changedFiles: { type: "array", items: { type: "string" } }, validationOutput: { type: "array", items: { type: "string" } } }, additionalProperties: false }, timeoutMs: 600000 } } // ...one entry per claimed ticket, up to N ]); return results.map(r => ({ key: r.key, ok: r.ok, runId: r.runId ?? null, structured: r.structuredOutput ?? null, artifacts: r.artifactPaths ?? null })); ``` The `schema` in the dispatch above is a copy of the mechanical-report contract. Its single source of truth is `scripts/mechanical-report.js` (`REPORT_SCHEMA`, self-describing via `--print-schema`) — the copy must stay verbatim-identical (the self-check cross-compares them; drift fails the suite): ```json { "type": "object", "required": [ "headSha", "testResult", "changedFiles", "validationOutput" ], "properties": { "headSha": { "type": "string" }, "testResult": { "type": "string" }, "changedFiles": { "type": "array", "items": { "type": "string" } }, "validationOutput": { "type": "array", "items": { "type": "string" } } }, "additionalProperties": false } ```
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub