Skip to content

Plan Pipeline β€” End-to-End Runbook

You write a spec. Claude writes the code across every repo the work touches. You review the PRs as they appear and merge them when you're happy.

This page covers the entire flow from "I have an idea" to "all the work PRs are merged" β€” what you do, what the pipeline does for you, and every Slack DM you'll receive along the way.


The whole flow at a glance

flowchart LR
  Idea[Idea] --> Ask["Ask agent to write plan"]
  Ask --> PR["Plan PR opens<br/>(plan-and-design-repo)"]
  PR -->|auto| Stamp["Auto: Linear epic created,<br/>linearIssueId stamped,<br/>cost/ROI stamped,<br/>plan-review-agent verdict"]
  Stamp --> You["You: iterate, get human approval, merge"]
  You -->|auto| Dispatch["Auto: fanout dispatches<br/>per (repo Γ— planItem)"]
  Dispatch --> Wave1["Wave 1: PRs open<br/>in target repos"]
  Wave1 --> Review["You: review + merge each PR"]
  Review -->|deps unblocked auto| Wave2["Wave 2: deferred PRs<br/>open as upstream merges"]
  Wave2 --> Done["Linear epic auto-moves<br/>to Done"]

Step 1 β€” Write the plan

The simplest path is to ask Cursor, Claude Code, or Codex to write it for you. The agentic-planning skill is mandatory on every agent surface β€” it loads automatically when you ask any agent to "plan", "spec", or "ticket" anything.1

Example prompt

"Plan: add a pricing-cohorts admin endpoint and admin UI in cred-platform-ts. The endpoint stores cohort rows with id/name/ruleType, the UI is an /admin/pricing-cohorts page that lists them and has a create modal. UI depends on the API existing first. Two planItems."

The agent will ask 1–3 clarifying questions, then write the plan file to plans/<slug>/v1.md in plan-and-design-repo with every required field filled in.

What's manual vs. automatic in the plan file

Field Source Notes
id, title, targetRepos You (or the agent on your behalf) targetRepos is the only real architectural decision
planItems[].summary, acceptanceCriteria, files You / agent This is the spec β€” be specific, name file paths
phases, planItems[].dependsOn You / agent Only needed if you have ordering between items
budgetUsd, costRationale You / agent Usually 2–5; over $5 needs a budgetException block
ownerUserId You β€” hand-write user_<your-github-handle> Currently manual. Not auto-stamped from PR author (yet)
templateVersion: 5 You β€” constant Schema rejects anything else
status: draft You β€” constant on open Don't flip to approved; merge IS approval
parentLinearKey: "new" You β€” optional Set to "new" for a root-level Linear epic; omit to let the bridge auto-resolve a parent
linearIssueId Auto-stamped cred-planner-agent[bot] writes it on PR open
costEstimate, roiEstimate, valueRatio Auto-stamped Via plan-pr-value-stamp.yml on every push

The full schema lives at plan-and-design-repo/schemas/plan.schema.json.


Step 2 β€” Open the PR

git checkout -b <your-handle>/<slug>
git add plans/<slug>/v1.md
git commit -m "plan(<slug>): one-line description"
git push -u origin <your-handle>/<slug>
gh pr create --base main --fill

Local npm run validate may complain about missing linearIssueId

That's expected before the auto-stamp commit lands. Push first; the stamp arrives ~30 seconds later.


Step 3 β€” Watch the bots do their work (~2 min)

Bots will hit your PR in quick succession. You don't need to do anything yet β€” just read what shows up.

Bot / Workflow What it does Source
cred-planner-agent[bot] auto-stamps linearIssueId Calls linear-bridge β†’ creates Linear epic (root-level if parentLinearKey: new, else auto-resolves a parent) β†’ commits the COM-XXXXX key back to your branch plan-pr-opened.yml
Plan PR value-stamp Calls value-bridge/estimate β†’ writes costEstimate, roiEstimate, valueRatio into frontmatter. Fingerprint-gated β€” only re-stamps when plan body bytes change plan-pr-value-stamp.yml
cred-plan-review-agent[bot] posts verdict APPROVE / REQUEST_CHANGES / ESCALATE to human β€” read the line comments plan-review.yml
cred-design-review-agent[bot] (UI plans only) Reviews co-located design files at designs/<slug>/v1.md design-review.yml

Backend-only plans (target repos in config/repo-policy.yml#nonUiRepos) don't need a design β€” design-review skips silently.

Pull before you push more commits

The auto-stamp commit moved your branch forward. Run git pull --ff-only before adding fixes.


Step 4 β€” Iterate with plan-review-agent

The agent's verdict comes with concrete asks. Common ones:

  • Tighten a vague summary β€” Claude quotes summaries verbatim into its impl prompt, so vague spec β†’ vague code later
  • Add missing files: entries β€” fanout refuses to dispatch a slot with zero files
  • Raise budgetUsd β€” if the scope is bigger than the budget allows
  • Split a too-large planItem β€” better isolation, easier review
  • Add dependsOn β€” if you missed an ordering constraint

To iterate: edit v1.md, commit, push. The bots re-review on the new commit automatically. Usually 1–2 rounds.


Step 5 β€” Merge

Branch protection requires one human-authored APPROVED review (bots' approvals don't count toward the gate). Once that's in and CI is green:

Squash-merge the PR.

You don't have to flip status: draft β†’ status: approved in frontmatter. The "merge IS approval" doctrine2 treats the merge itself as the signal β€” branch protection + plan-review-agent + human review together define "approved" for the pipeline.

After merge, you're done with plan-and-design-repo. Now wait for work PRs.

Want to skip the fanout?

Add the autobuild-excluded label to the plan PR before merging. post-merge-dispatch will skip the repository_dispatch entirely. Useful for plans that exist purely as documentation or for plans that supersede an earlier version.


Step 6 β€” Fanout PRs appear (timing varies)

Within ~10–15 minutes of plan merge, work PRs start opening in your target repos. The pattern depends on your plan structure.

Multi-repo plans open in waves β€” you have to merge the upstream PRs before the downstream ones appear

If your plan has cross-repo or cross-item dependsOn relationships, the fanout will NOT open all the PRs at once. It opens the upstream PRs first, waits for you to merge them, and only then opens the downstream PRs.

Example: a plan targeting cred-api-commercial (API change) + cred-web-commercial (UI consuming the API) with P2-ui dependsOn P1-api:

  1. T+15 min: PR opens in cred-api-commercial only. No PR in cred-web-commercial yet. This is correct β€” don't panic.
  2. You review + merge the cred-api-commercial PR.
  3. T+~5 min after merge: the Stale PR Sweeper picks up the merge state change.
  4. T+~15 min after merge: PR opens in cred-web-commercial.

Your job during the wait: review + merge the upstream PR. Don't go hunting for the downstream PR β€” it's intentionally deferred. Linear sub-issue for the deferred item will carry a comment like Deferred: waiting on P1-api.

What happens under the hood

sequenceDiagram
  participant Plan as plan-and-design-repo<br/>(PR merged)
  participant Dispatch as post-merge-dispatch.yml
  participant Bridge as cred-agent-ai<br/>"Author Agent plan-merged bridge"
  participant Fanout as author-agent-plan-merged.yml<br/>(reusable, workflow_call only)
  participant Targets as Target repos

  Plan->>Dispatch: pull_request.closed (merged)
  Dispatch->>Bridge: repository_dispatch:<br/>author-agent-plan-merged
  Bridge->>Fanout: workflow_call
  Note over Fanout: plan-context job:<br/>parse plan, build matrix
  Note over Fanout: target-repo-preflight:<br/>check AGENTS.md, app install,<br/>default branch per slot
  Note over Fanout: sync-linear-tickets:<br/>create Linear sub-issues
  Note over Fanout: fan-out-prs:<br/>one slot per (repo Γ— item)
  Fanout->>Targets: Open PRs<br/>(cred-author-agent[bot])

The bridge is intentionally thin β€” it just validates the dispatch payload and calls the canonical workflow via workflow_call. The canonical workflow no longer accepts direct repository_dispatch (a poisoned-record incident, COM-40223).

Pattern A β€” Simple plan (no dependsOn, no phases)

All planItems fan out in parallel. Three planItems β†’ three PRs at roughly the same time. You don't need to merge them in any particular order.

Pattern B β€” Plan with cross-item dependsOn (single repo)

Items with no upstream deps open immediately. Items with deps wait β€” they don't get a PR until their upstream PR is merged.

T+0     you merge the plan PR
T+15    βœ… PR for P1-api opens in cred-platform-ts (no deps)
        ⏸  P2-ui (dependsOn: [P1-api]) β€” NO PR YET, intentionally deferred
        └─ Linear sub-issue for P2 says: "Deferred: waiting on P1-api"

        πŸ‘‰ YOU: review + merge the P1-api PR like a normal code review

T+X     you merge the P1-api PR
T+X+5   sweeper detects the merge
T+X+15  βœ… PR for P2-ui opens in cred-platform-ts
        └─ Linear sub-issue for P2 transitions out of "Deferred" state

        πŸ‘‰ YOU: review + merge the P2-ui PR

You don't trigger the second wave. The Cred Stale PR Sweeper runs every 5 minutes (schedule: */5 * * * *) and also on relevant repository_dispatch events. It picks up the merged upstream and re-dispatches deferred slots automatically.

Pattern C β€” Cross-repo dependencies (the multi-repo wave pattern)

Same as Pattern B, but the upstream and downstream items are in different target repos. The deferral logic looks across all targetRepos. Concrete walkthrough using the example from the callout above:

Plan:
  targetRepos:
    - credinvest/cred-api-commercial
    - credinvest/cred-web-commercial
  planItems:
    - P1-api  (repo: cred-api-commercial)
    - P2-ui   (repo: cred-web-commercial, dependsOn: [P1-api])

──────────────────────────────────────────────────────────────────────

T+0     βœ… You merge the plan PR in plan-and-design-repo

T+15    βœ… cred-api-commercial: PR #N opens         ← upstream PR
        ⏸  cred-web-commercial: no PR yet           ← deferred
        ⬩  Linear: P2 sub-issue marked "Deferred: waiting on P1-api"

        πŸ‘‰ YOU now: review + merge cred-api-commercial PR #N

[hours/days later β€” you may be iterating with the author-agent on PR #N
 via `address-comments` mode, or pushing fixes yourself]

T+X     βœ… cred-api-commercial PR #N merges

T+X+5   ⏱  Sweeper tick (runs every 5 min) detects the merge

T+X+15  βœ… cred-web-commercial: PR #M opens          ← downstream PR
        ⬩  Linear: P2 sub-issue moves to "In Progress"

        πŸ‘‰ YOU now: review + merge cred-web-commercial PR #M

T+Y     βœ… cred-web-commercial PR #M merges
        ⬩  Linear: parent epic auto-moves to "Done" (all sub-issues done)

The key thing to internalise: "PR for one repo, nothing for the other" is the expected state, not a stuck pipeline. The downstream repo is waiting on you to merge upstream. The sweeper will reliably auto-open the downstream PR within ~15 min of you merging upstream.

If you have THREE repos chained (A β†’ B β†’ C), you get three waves: one PR in A, you merge, one PR in B, you merge, one PR in C, you merge. Each wave is ~15 min after the previous merge.

Pattern D β€” Multi-phase

If you used phases with gateCriteria: phase-1 items fan out first. Phase-2 items wait until ALL phase-1 PRs merge AND gateCriteria is satisfied. Same wave behavior as Pattern C, but the unit is "phase" instead of "individual planItem".

phase-1: items P1, P2, P3      β†’ 3 PRs open at T+15
                                  (in parallel β€” no deps inside the phase)

[you merge all 3 phase-1 PRs]

phase-2: items P4, P5          β†’ 2 PRs open ~15 min after the LAST
                                  phase-1 PR merges

How to tell which pattern you're in

Look at your plan file:

If your plan has… You're in pattern… Expected PR behavior
One targetRepo, one planItem A Single PR opens immediately
One targetRepo, multiple planItems, no dependsOn between them A All N PRs open at once in the same repo
Any dependsOn between items in the same repo B Upstream PR first; downstream PR after upstream merges
Multiple targetRepos with dependsOn crossing repos C One repo's PR opens first; other repos' PRs open as you merge
phases: with multiple phases, items split across them D Phase-by-phase waves

Step 7 β€” Reviewing the work PRs

Each PR shows up in your target repo, authored by cred-author-agent[bot], formatted as:

feat/fix(<repo>): <planItem-id> β€” <summary first 80 chars>… (COM-XXXXX)

The branch is cred-author-agent/<your-slug>-v1-<planItem-id>. PR body links the Linear sub-issue + the parent plan. Same branch protection, same CI gates as a human PR.

How to review

Situation Action
PR looks correct, CI green Merge it. Same as any human PR.
PR has a bug or wrong approach Drop a comment: "@cred-author-agent the cohort filter should also accept null ruleType." The author-agent's address-comments mode re-runs Claude with your feedback and pushes a fix.
CI is failing address-comments mode usually auto-fixes. If stuck after 2 retries, push a fix yourself.
The whole approach is wrong Comment with reasoning. If the spec itself is wrong, open v2.md of the plan with supersedes: 1, fix it, merge. v1 in-flight PRs are superseded.
PR opened in [DRAFT] with almost no diff Scaffold-only PR β€” Claude couldn't act on the spec. See "Scaffold-only PR" Slack DM below.

Author-agent modes

  • address-comments β€” runs when a human comments on the PR, when CI posts a failure, or via merge-agent dispatch
  • rebase β€” when the base branch has moved
  • recovery β€” manual fallback when an earlier run died mid-flight

Linear automation

Everything Linear-side is automatic. You don't open Linear tickets manually for plan-driven work.

Linear event Who handles it When
Parent epic created linear-bridge via plan-pr-opened.yml On plan PR open
Epic In Review β†’ In Progress post-plan-merge-linear.yml (COM-36916) On plan PR merge
Sub-issues created per planItem sync-linear-tickets job in author-agent-plan-merged.yml During fanout
Sub-issue state synced with PR state Native Linear ↔ GitHub integration (primary) + cred-linear-sync.yml every 4h (gap-filler for squash-merge edge cases) Continuous
Blocker auto-unblocked on completion cred-linear-sync.yml blocker-watcher mode every 4h Continuous
Parent epic Done Linear's native sub-issue rollup when all sub-issues are Done Automatic

If a sub-issue's state ever drifts from the PR state β€” usually because squash-merge strips the COM-XXXXX reference from the merge commit β€” the 4-hour cred-linear-sync.yml job reconciles it. You don't need to touch Linear.


Slack notifications β€” what you'll receive

All DMs route to you personally via the slackUserId mapped to your ownerUserId in cred-agent-ai/.github/data/plan-author-slack-ids.json. If you're not in that file, the DM falls back to a channel-wide webhook post.

Add yourself to the registry

If you haven't, open a PR adding an entry to .github/data/plan-author-slack-ids.json with your name, GitHub login (lowercase), and Slack user ID. Otherwise every plan you author pings the whole channel instead of just you.

DMs you'll see

When Emoji Message
Plan PR opened & reviewed :white_check_mark: Plan review approved "Plan is approved. After CI green + human merge, the post-merge dispatch fires fan-out to your target repos."
Plan PR opened & reviewed :no_entry: Plan review requested changes "Open the PR review for the bot-author findings + citations. Address them and push a new commit β€” plan-review-agent will re-evaluate."
Plan PR opened & reviewed :raising_hand: Plan review escalated to human "The agent could not produce a confident verdict and asked a human to look. Tag a reviewer per CODEOWNERS."
UI plans only β€” design review verdict :white_check_mark: / :no_entry: Mirrors plan-review tone, but for the co-located design file
Plan PR merged with autobuild-excluded (notice) "PR #N merged with the 'autobuild-excluded' label β€” no repository_dispatch fired." So you know fanout was intentionally skipped.
Fanout dispatched :rocket: Plan fan-out dispatched "cred-agent-ai will now open one PR per target repo (N repos). Expect Slack updates as each fan-out slot opens / refreshes / fails."
Cross-repo dependency deferral :hourglass_flowing_sand: Plan fan-out deferred β€” cross-repo dependency Lists which planItems are deferred and why. Next: wait for the upstream PR(s) to merge; the sweeper auto-re-dispatches when they do.
Plan-context job failed :octagonal_sign: Plan fan-out aborted β€” plan-context job failed Cause + next steps + run link. No PRs were opened.
Scaffold-only PR opened :construction: Plan fan-out produced a scaffold-only PR Claude wrote stubs but no real implementation. PR is labeled needs-implementation β€” Merge Agent gate blocks merge until implementation lands. Usually means the planItem spec was too vague; open v2.md with sharper instructions.
Stuck draft PR nudge (periodic) (varies) Sent by cred-stuck-fanout-pr-nudge.yml β€” periodically pings you about draft fanout PRs that haven't progressed in a while
Daily digest (channel-wide, not a DM) β€” Cost / Tokens / Speed / Quality / Regressions / Interventions rollup posted to the agent ops channel

Every DM falls back to a channel-wide webhook post if the bot DM fails (no Slack mapping, conversations.open error, etc.) β€” you'll never silently lose a notification.


When things look stuck

Symptom First thing to check
Plan merged 20 min ago, no PRs anywhere Open the cred-agent-ai Actions tab β†’ Author Agent plan-merged bridge run for your slug. The link-back step summary explains why (deferred / blocked / preflight failure / matrix empty).
PR opened in one repo, the others didn't This is not stuck β€” this is the multi-repo wave pattern. The missing PRs are deferred because they dependsOn the open one. Merge the open PR; the deferred ones auto-open ~15 min after. See Pattern C. Confirm by checking the Linear sub-issue β€” it'll say Deferred: waiting on <item-id>.
Fanout PR keeps failing CI and author-agent isn't fixing it The acceptance criteria was too vague, or the change is fundamentally wrong. Push a fix yourself, or comment with explicit guidance, or close + open v2.md.
Fanout PR says [DRAFT] with scaffold-only diff summary / acceptanceCriteria was too vague. Open v2.md of the plan, tighten the spec, merge β€” the v1 PR gets superseded.
Slack pinged the wrong channel about your plan You're not in plan-author-slack-ids.json. Open a PR adding yourself.
Plan PR merged but no Post-merge dispatch run Open the plan-and-design-repo Actions tab β€” there's a Post-merge dispatch (plan-merged) workflow per merged plan PR. If it's failed, the dispatch token may be expired. Ping the platform on-call.

TL;DR β€” the daily flow

  1. Ask Cursor/Claude Code: "Plan this work for me." It writes the plan and opens the PR.
  2. Skim the planItems' summaries and acceptanceCriteria for the 3 things that matter: file paths named, contract clear, target repos right.
  3. Iterate with the plan-review-agent's verdict. 1–2 rounds usually.
  4. Get one human approval and merge. No status flip needed.
  5. Go do other work. Slack will DM you as fanout PRs open, get deferred, succeed, or need attention.
  6. Review and merge the PRs that appear in your target repos. Drop comments to redirect the author-agent if needed. If your plan has cross-repo deps, you'll get PRs in waves β€” merge the upstream repo's PR first, then the next wave opens ~15 min later.
  7. Done. Linear closes itself.

Don'ts

  • ❌ Don't open work PRs in target repos yourself β€” let the pipeline open them
  • ❌ Don't fire repository_dispatch manually
  • ❌ Don't flip status: approved in frontmatter before merge β€” merge is the gate
  • ❌ Don't create Linear tickets for plan work β€” they're auto-created
  • ❌ Don't push code commits to the plan PR β€” only the plan file should change there
  • ❌ Don't skip the agentic-planning skill β€” it's mandatory on every agent surface

References


  1. Enforced in cred-agent-ai/AGENTS.md β€” "Mandatory skills (load before any planning)". The canonical skill file lives at cred-agent-ai/.agents/skills/agentic-planning/SKILL.md

  2. Documented in post-merge-dispatch.yml header (COM-36627).