| name | orchestrate |
| description | Orchestrate feature implementation from plan to PR โ worktree isolation, TDD, review, and PR creation. |
| allowed-tools | Read, Grep, Glob, Bash, Agent, Write, Edit, EnterWorktree, ExitWorktree |
| argument-hint | [description | issue-number | phase N] |
/orchestrate
Orchestrate the full development workflow for Pastura: plan โ issue โ worktree โ TDD implementation
โ review โ PR.
Project-owned file, hand-reconciled against the claude-kit template. Edit it freely; kit updates
never touch it. Before accepting a back-port from /claude-kit:orchestrate-creator, read
docs/agent-tooling/orchestrate-kit-reconciliation.md
โ the divergences an upgrade would silently undo. The traps behind the shorter wording here live in
docs/agent-tooling/orchestrate-traps.md.
Constants
PLAN_MARKER: <!-- pastura-plan --> โ machine-readable marker embedded in issue plan comments
for resumption detection. Project-unique by construction; never change it to a generic name.
OWNER_REPO: gh repo view --json nameWithOwner -q '.nameWithOwner'. Resolve in Step 0, after
pre-flight check 1 (which decides whether gh is usable at all).
Project parameters
| Parameter | Value |
|---|
| Test command | scripts/xcodebuild.sh test [-only-testing PasturaTests/<Class>] โ bare and cwd-relative, never behind cd โฆ &&, an env-var prefix, or an absolute path (.claude/rules/xcodebuild-cli.md) |
| Lint command | swiftlint lint --quiet --strict |
| Commit-time gate | pre-commit hook (scripts/*-gate.sh, SwiftLint, build when the changeset needs it) |
| TDD | required โ Engine / LLM test-first; Data / UI implement-then-test (CLAUDE.md ยง Testing Strategy) |
| Plan-critique agent | claude-kit:critic, passed model: opus (it carries no pin) |
| Review agent | code-reviewer (.claude/agents/code-reviewer.md) |
Commit-gate note: the pre-commit hook enforces quality at commit time โ after a subagent's
changes, a diff spot-check suffices before committing; the hook is the gate.
Step 0: Input Detection & Pre-flight
Interpret $ARGUMENTS:
#N: Fetch issue via gh issue view N, use title/body as task spec. Check for an existing
plan (Resumption Detection below). Unauthenticated โ ask the user for the spec inline.
phase N: Read ONLY that Phase section of docs/ROADMAP.md.
- (empty): Ask what to implement.
- Other text: Use as inline task description.
Derive: TASK_TYPE (feat/fix, default feat); SLUG (kebab-case, ^[a-z0-9][a-z0-9-]{0,36}$;
sanitize or ask if it doesn't match).
Resumption Detection (#N only)
- Fetch issue comments, find
PLAN_MARKER:
gh api "repos/${OWNER_REPO}/issues/N/comments" --jq '.[] | select(.body | contains("<!-- pastura-plan -->")) | {id, body}' | tail -1
Use the last match.
- If found: set
RESUMING=true, ISSUE_NUMBER=N, capture COMMENT_ID. Parse checkboxes
(- [x] done vs - [ ] remaining), identify NEXT_ITEM. Extract TASK_TYPE, branch,
REVIEWER_MODEL, SESSION_MODEL from the ## Metadata block (normalize to lowercase
opus/sonnet; default opus if a field is absent). Derive SLUG from the branch.
- Coupling re-check: if the resumed plan has any Opus-tier item (
๐ญ or ๐ง ) but
REVIEWER_MODEL=sonnet or SESSION_MODEL=sonnet, warn and offer to upgrade to Opus before
continuing. If it is all Sonnet-tier with REVIEWER_MODEL=sonnet, grep the comment for
๐ต (tb) โ a deferred item is not strictly simple (Step 1.3) โ and on any hit offer the Opus
reviewer upgrade. Do not re-derive the classification: a resumed session lacks the context.
- If all items checked: do not silently proceed to review. A fully-checked last plan
usually means its PR already merged โ and on an umbrella issue that accumulates several
historical plan comments,
tail -1 lands on that finished plan, so auto-proceeding re-reviews
completed work instead of planning the new work the user actually wants. Ask: "#N's last plan
is complete (its PR likely merged) โ resume-review it, or start a NEW plan for new work on
#N?" Only ensure you are on the branch/worktree and skip to Step 4 on explicit
"resume-review"; otherwise treat as a fresh task (fall through to Step 1).
- Report "Found plan on #N. {DONE}/{TOTAL} complete. Resuming from item {NEXT_ITEM}." Skip
Steps 1 and 1b โ go to Step 2.
- If no plan: proceed normally.
Pre-flight (in order):
gh auth status โ if unauthenticated, run in degraded mode: no issue, no checkpoint sync,
no resumption (say so explicitly). Skip all gh steps; the plan lives only in-session.
git status โ warn on uncommitted changes.
- Verify on default branch (skip if
RESUMING): DEFAULT_BRANCH=$(gh repo view --json defaultBranchRef -q '.defaultBranchRef.name'). Degraded-mode fallback must yield a bare branch
name: git symbolic-ref --short refs/remotes/origin/HEAD | sed 's|^origin/||' (if origin/HEAD
is unset, git remote set-head origin -a first). If not on it, offer git switch.
git pull --ff-only origin "$DEFAULT_BRANCH" โ warn on failure, don't block. Skip if RESUMING.
- If already in a worktree, suggest
ExitWorktree first (unless resuming into the matching one).
Step 1: Plan โ Gate G1
-
Read CLAUDE.md and (if phase work) the relevant docs/ROADMAP.md section.
-
Format the plan as a numbered checkbox list, one item = one planned commit. Label each item with
the outcome of two independent questions โ tier (which model) and locus (main session or
subagent):
-
Q1 โ tier, by the nature of the work. A ๐ญ criterion โ new design patterns, actor
isolation / Sendable decisions, changes spanning layers, work near a dependency-rule boundary
(Engine โ Data), anything needing non-obvious architectural judgment โ Opus tier. A ๐ต
criterion โ existing-pattern reuse (a new Handler mirroring an existing one), test-only
changes following an existing test pattern, type/error-case additions, doc comments, minor
fixes โ Sonnet tier. Neither โ deferred. The ๐ญ criteria name the nature of the work, so a
matching item stays Opus-tier however settled its spec is โ that decides the model, never the
locus.
-
Q2 โ locus, by specifiability. Not "too hard for Sonnet?": difficulty or size alone does
not pull an item into the main session. A subagent inherits none of this conversation, so ask:
can you fill the Step 3 prompt slots now without a new design decision โ target file(s), plus
an existing pattern to mirror or an acceptance condition? Yes, even if hard โ subagent. No, or
you can't tell โ main session (that specific uncertainty โ not general unease). Also route to
the main session when delegation prompt + verify overhead exceeds the work itself (single-line
edits, short doc tweaks).
-
Q3 โ resolve:
| Q1 tier | Q2 locus | Label |
|---|
| Opus | subagent | ๐ญ |
| Opus | main session | ๐ง |
| Sonnet | subagent | ๐ต |
| Sonnet | main session | ๐ต (main) โ keeps its Sonnet tier |
| deferred | subagent | ๐ต (tb) โ specifiable but unclassified |
| deferred | main session | ๐ง โ unresolved and unverified by anyone else โ Opus |
- [ ] 1. ๐ต <description> (`<primary-file-path>`)
- [ ] 2. ๐ง <description> (`<primary-file-path>`)
Step 1b: Plan Critique (REQUIRED unless RESUMING)
Launch Agent(subagent_type: "claude-kit:critic", model: "opus", โฆ) to review the plan for blind
spots. If the agent type does not resolve (plugin not installed or trusted), stop and surface that
rather than skipping silently.
Prompt: "Review this implementation plan for the Pastura project. Focus on: scope creep beyond
the current phase, dependency-rule violations in the planned file locations, missing edge cases,
integration risks with existing modules, and assumptions not validated against the codebase. If the
plan declares a reviewer-model choice, add an axis evaluating whether it matches the sensitivity of
the touched paths, and whether each item's label (tier and locus) matches the work it describes.
Read the repo's CLAUDE.md and docs/ROADMAP.md for context.
Task: {TASK_DESCRIPTION}
Plan: {PLAN_BODY}
Output your full two-stage evaluation (axes, evaluation, summary table, top actions)."
- Any Critical verdict: present the report, ask "revise or proceed?". Revise โ back to
Step 1, regenerate, re-run 1b.
- OK/Warning only: present the summary as context, proceed to Step 2.
Step 2: Issue + Worktree โ Gate G2
2a: Issue & Plan Comment
2b: Worktree Setup
RESUMING: find existing worktree (git worktree list | grep {SLUG}) โ EnterWorktree; else
recreate from the remote branch; else fresh. If SESSION_MODEL=sonnet, prompt /model sonnet
first, then ask "Resume from item {NEXT_ITEM}/{TOTAL}?"
- Normal:
- "Issue #{ISSUE_NUMBER} created. Branch:
{TASK_TYPE}/{SLUG}" (or, degraded, just the branch).
- If
SESSION_MODEL=sonnet, tell the user to run /model sonnet now (or keep Opus). Then ask
"Create worktree and start?"
EnterWorktree with name: "{TASK_TYPE}/{SLUG}" (on collision, check git ls-remote --heads origin <branch>, append -2).
- Rename to conventional format โ
EnterWorktree sanitizes / to + and prepends worktree-:
git branch -m "$(git branch --show-current)" "{TASK_TYPE}/{SLUG}".
- Verify:
git branch --show-current.
Worktree path hygiene (holds for the rest of the session): the original checkout stays on another
branch, so a tool that resolves to it instead of this worktree acts on the wrong tree silently.
cwd inheritance for a non-isolation subagent is not guaranteed (it has resolved to the original
checkout in practice, yielding an empty phantom diff that reads as a false FAIL). So capture the root
once with WORKTREE_ROOT=$(git rev-parse --show-toplevel) and embed git -C {WORKTREE_ROOT} into
every subagent prompt that runs git โ never a bare git the subagent resolves against its own cwd,
never a $(โฆ) it re-runs, never a reused pre-worktree path. Same rule for absolute Edit/Write paths:
resolve them under {WORKTREE_ROOT}, and invalidate any carried over from a pre-worktree result.
-C is git-only: scripts/xcodebuild.sh stays bare (Project parameters), so have a delegated
implementer report pwd โ the wrapper resolves its root from cwd, and a run that landed in the
main checkout returns a green for the wrong tree.
Step 3: Implementation
Follow the plan. If RESUMING, start from NEXT_ITEM. Per item (K = plan item number), branch on
the label:
| Label | Branch |
|---|
๐ง | in-session โ the orchestrator implements it |
๐ญ | Agent(subagent_type: "claude-kit:implementer", model: "opus") |
๐ต / ๐ต (tb) | Agent(model: "sonnet") |
๐ต (main) | the in-session branch, at the session model |
In-session โ orchestrator implements directly
- Write the test first โ TDD is required in this project. Skip only for docs-only / test-only items.
- Run
scripts/xcodebuild.sh test -only-testing PasturaTests/<Class> โ confirm red.
- Write the implementation.
- Run the same command โ confirm green.
- Commit (Conventional Commits + emoji per CLAUDE.md).
- Checkpoint sync (skip in degraded mode) โ check off item
K in the plan comment:
BODY=$(gh api "repos/${OWNER_REPO}/issues/comments/${COMMENT_ID}" --jq '.body')
UPDATED=$(echo "$BODY" | sed "s/^- \[ \] ${K}\./- [x] ${K}./")
gh api "repos/${OWNER_REPO}/issues/comments/${COMMENT_ID}" -X PATCH -f body="$UPDATED" --jq '.url'
On gh failure, warn and continue โ never block on a sync failure.
Delegated โ ๐ญ Opus implementer or ๐ต Sonnet subagent
Launch without isolation (shares the worktree). Subagents run sequentially, never in
parallel. Give a ๐ต subagent Read, Grep, Glob, Bash, Write, Edit โ NOT EnterWorktree /
ExitWorktree. claude-kit:implementer declares no tools: key and pins effort: medium, so the
๐ญ prompt adds the two lines marked below. If the agent type does not resolve, stop and surface
that โ never quietly implement the item in-session instead. Bound the delegated scope so the item
stays reviewable in one pass (split budget: .claude/agents/code-reviewer.md).
Prompt template: "You are implementing item {K} of a plan for the Pastura iOS project.
Work inside {WORKTREE_ROOT} โ treat every path below as rooted there and run git via
git -C {WORKTREE_ROOT}; run scripts/xcodebuild.sh bare from that directory, never behind cd,
an env-var prefix, or an absolute path; do not rely on inherited cwd. Run pwd first and confirm
it is {WORKTREE_ROOT}.
Read the repo's CLAUDE.md first โ follow all its conventions.
Task: {ITEM_DESCRIPTION}. Target file(s): {PRIMARY_FILE_PATH}.
Reference: {existing similar file to mirror, if any}.
Procedure: if implementation, write/adjust the test in PasturaTests/, run
scripts/xcodebuild.sh test -only-testing PasturaTests/{TestClass}, confirm it fails, implement,
run again, confirm it passes. If docs-only or test-only, make the change directly. Do NOT
commit โ leave changes unstaged; the orchestrator reviews and commits. If tests still fail after
your best effort, return a summary of what you tried and the error output.
(๐ญ only) Do not call EnterWorktree, ExitWorktree, or Agent. End your report with one line:
OUTCOME: done | design-decision | scope-refusal | failed, and state whether you left modified or
new files in the worktree."
After the subagent returns:
git status โ verify expected changes only.
- Read
git diff fully before writing the commit message. Where the item removes or changes a
shape, grep the file for the OLD shape โ the prompt bounded what was asked, not what the item
mandates.
- Gate: a convention spot-check suffices โ the pre-commit hook enforces the rest.
- Commit.
- Checkpoint sync (same PATCH as above; skip in degraded mode).
๐ญ outcomes other than done: design-decision โ settle it yourself and finish the item
in-session; keep the partial work (no stash). scope-refusal โ no partial work exists; re-split the
item and dispatch the pieces. failed โ the fallback below.
Fallback (subagent could not make tests pass): take over immediately โ do not retry the same
tier. git stash -u to save partial work, then escalate by session model: SESSION_MODEL=opus
โ orchestrator finishes it in-session; SESSION_MODEL=sonnet โ delegate to
Agent(subagent_type: "claude-kit:implementer", model: "opus") (no isolation) with the item spec +
the error output; on return, review the diff and commit. If Opus also fails, report and offer
/model opus + retry directly.
After all items, run full verification from the main session: scripts/xcodebuild.sh test --tail 80 (the wrapper's pipefail-safe cap โ no external | tail), then swiftlint lint --quiet --strict. On failure, fix, verify locally, commit with ๐ fix:, re-run. Hard limit: 3
iterations โ if still failing, report and ask whether to proceed to Step 4.
Carve-out โ build-irrelevant branches: the skip predicate is mechanical:
git -C {WORKTREE_ROOT} diff --name-only {DEFAULT_BRANCH}...HEAD | scripts/precommit-gate-classify.sh
(expect one approval prompt). No build token โ skip the suite; no lint token โ skip SwiftLint.
CI's PR path-gating reuses the same script, so it is not a backstop โ state the skip in the PR body.
Step 4: Review โ Gate G3
Before launching the reviewer, git fetch origin {DEFAULT_BRANCH} and check git rev-list --count HEAD..origin/{DEFAULT_BRANCH}. If non-zero, offer a rebase before review; treat it as mandatory
when the diff touches large generated / data files (Localizable.xcstrings, lockfiles), where a
rebase or non-conflicting auto-merge can drop upstream entries without surfacing a conflict.
Launch Agent(subagent_type: "code-reviewer", model: "$REVIEWER_MODEL", โฆ) (lowercase
opus/sonnet, from Metadata; defaults Opus). The reviewer MUST emit a **Verdict**: PASS | FAIL
line โ the gate below parses it. Its scope budget counts added lines; if it returns
SCOPE_TOO_LARGE, split by added lines (per area or per axis) โ never downgrade REVIEWER_MODEL
to fit a diff.
Prompt: "Review all changes on this feature branch. Run git -C {WORKTREE_ROOT} diff {DEFAULT_BRANCH}...HEAD for the full diff (all commits since branching) โ use the -C path, do
not rely on cwd; a bare git can resolve to the original checkout and show an empty phantom diff.
Read every changed file in full. Evaluate against your complete checklist. Output your review in
your standard format, including a **Verdict**: PASS | FAIL line."
One round. The review is input to the orchestrator's judgment, not a loop:
- PASS โ Step 5.
- FAIL โ triage every finding in the main session โ no verification subagent. For each:
apply, or reject with a one-line reason (false positive, out of scope, or a disagreement you
will stand behind in the PR). Capture
FIX_BASE=$(git rev-parse HEAD), apply the accepted
findings, commit ๐ fix:.
- Re-review at most once, and only the fix diff (
git -C {WORKTREE_ROOT} diff {FIX_BASE}...HEAD plus the prior findings) โ and only when a fix introduced new logic rather
than applying the reviewer's own suggestion. Findings from that pass are triaged the same way;
there is no third round.
- Every rejected or unfixed finding goes into the PR body's
## Review section (Step 5). A
residual Critical the user should weigh is stated there, not silently dropped.
Step 5: PR Creation
Degraded mode: skip โ report the branch is ready to push/PR manually, stop.
Base branch: gh repo view --json defaultBranchRef -q '.defaultBranchRef.name'. Label from the
dominant commit prefix (featโenhancement, fixโbug, docsโdocumentation, refactorโrefactor,
testโtesting, choreโchore, ciโci, perfโperformance); add security if security-related.
If the label doesn't exist in the repo, drop it (same fallback as Step 2a).
Present the PR draft (informational; created automatically, no gate):
- Title: emoji prefix + Conventional format, < 70 chars.
- Body:
## Summary, ## Test plan (name any skipped suite and why), ## Review (reviewer model;
findings applied / rejected with reasons), ## Device QA, and the issue link (omit in degraded
mode). Use Closes #N only when this PR completes the issue; for a non-final PR of a
multi-PR / umbrella issue, use Part of #N.
## Device QA โ on-device steps are required when the diff touches a surface the simulator cannot
exercise: #if !targetEnvironment(simulator) blocks (enumerate with git grep), Metal / llama.cpp
inference paths (LlamaCppService, GGUF load), or a Pattern-6 executor freeze
(.claude/rules/swift-isolation.md). List the concrete steps; otherwise a single ๅฎๆฉQAไธ่ฆ line
with the one-line reason.
Compose the body in a file and pass --body-file (an inline heredoc trips the push-protection hook's
body scan). Push and create as two separate Bash calls โ never combine with && (a leading
git push breaks the gh pr create --base-anchored PR hooks):
git push -u origin <branch>
gh pr create --base "$BASE_BRANCH" --assignee "@me" [--label "$LABEL"] --title "..." --body-file <path>
After creation: print the PR URL; "wait for required checks, then merge manually."
Step 6: Cleanup
After merge (guidance only โ do NOT auto-execute): ExitWorktree action "remove";
git switch <default-branch> && git pull.
Post-merge remove may refuse: Pastura squash-merges, so the merged commit has a new SHA and
the worktree's local commits read as unmerged by ancestry โ ExitWorktree(action: "remove")
refuses, as it also can before the post-merge pull. Once the user confirms the merge landed,
re-invoke with discard_changes: true.