Skip to content

T-3OVF-add-orchestrate-skill

Status: closed/done · Impact: high · Complexity: medium

Ship /sdlc:orchestrate — one skill that, when invoked, performs a single reconcile-and-dispatch tick over the project’s task/PR/worktree state. Combined with /loop /sdlc:orchestrate, this is the Phase 1 entry point for the T-FQCN-self-driving-orchestrator-loop epic: hands-off task pickup and PR shepherding inside a Claude Code session.

The orchestration work is fully manual. To move a task through the SDLC loop today, the user has to:

  1. Read git fetch origin && git log origin/main..HEAD --oneline and decide whether to rebase.

  2. Eyeball git worktree list and cross-reference each worktree against gh pr list to see what’s merged, conflicted, or waiting on review.

  3. Pick a ready task from docs/planning/tasks/ (currently 3 sit in status: ready with no readiness_verified_at stamp), invoke /sdlc:task-ensure-ready on it, then /sdlc:task-work.

  4. After a PR merges, manually run Step 11 of /sdlc:task-work (mark closed/done, tear down worktree, delete branches). The 10 active worktrees include at least one (2026-05-19-add-epic-entity-task-depends-on-dependencies) where Step 11 never ran despite PR

    #18 being merged.

There is no skill that performs this orchestration loop, no convention for where blocker outcomes get logged across ticks, and no place that records what the orchestrator did between user check-ins.

A skill at plugin/skills/orchestrate/SKILL.md (invocable as /sdlc:orchestrate) that performs one tick:

  1. Sync: git fetch origin && git pull --rebase --autostash on main. Abort the tick with a logged reason if the rebase has conflicts.
  2. Reconcile open PRs: list open PRs via gh pr list. For each, dispatch an Agent(general-purpose) sub-agent that invokes /sdlc:pr-check <pr-number> and returns one of {CLEAN, NEEDS-RESPONSE, CONFLICTS, CI-FAILED, MERGED} plus a one-line reason. Parent dispatches on the verdict:
    • MERGED → spawn sub-agent running /sdlc:task-close-out <task-slug>.
    • NEEDS-RESPONSE / CONFLICTS / CI-FAILED → spawn sub-agent in the worktree to attempt resolution (respond to comment, rebase, fix CI). If it can’t resolve, log and move on.
    • CLEAN → no-op.
  3. Dispatch ready tasks: query status: ready AND autonomy != human-only AND no existing worktree/branch. Cap dispatch at 2 concurrent task-work runs (parent counts active sub-agents). For each picked task, spawn Agent(general-purpose) running /sdlc:task-work <slug> with a tight return contract: DONE pr=#N | BLOCKED reason=... | NEEDS-DEFINITION slug=....
  4. Digest: append one Markdown line per tick to .claude/orchestrator-log.md (gitignored) summarizing: timestamp, PR verdicts, dispatched tasks, blockers skipped. This is the “what did the orchestrator do while I was away” surface.
  5. Notify: PushNotification only when state can’t progress without a human — e.g., gh pr list is empty AND there are zero LLM-pickable ready tasks AND there are tasks in proposed/needs-definition. Routine blockers (in-progress/blocked, a single failed sub-agent) do not notify.
  6. Return to caller. When wrapped in /loop, the model uses ScheduleWakeup to fire the next tick (default ~20 min idle, ~5 min when work is in flight).

Sub-agents are always dispatched via Agent (never direct Skill calls from the parent body) so per-task chatter stays out of the orchestrator’s transcript. Parent only sees one-line return verdicts.

  1. Scaffold plugin/skills/orchestrate/SKILL.md with frontmatter (description, allowed-tools: [Bash, Read, Agent, PushNotification]).
  2. Write the tick procedure prose, mirroring the structure of /sdlc:task-work (numbered steps, explicit failure modes, what the skill MUST/MUST NOT do).
  3. Encode the parallelism cap (2) as a literal in the skill body — no config file yet; revisit in Phase 2.
  4. Add the digest-log convention: path .claude/orchestrator-log.md, format <ISO timestamp> tick=<N> prs=<verdicts> tasks-dispatched=<slugs> blocked=<slugs>. Add .claude/orchestrator-log.md to .gitignore (or .claude/ already is — confirm).
  5. Add plugin/skills/orchestrate/invariants.yaml per the project’s skill-prose lint convention (mirror what task-work and import-planning ship). Invariants worth encoding: “parent must dispatch task-work via Agent, never via direct Skill”; “parallelism cap of 2”; “blockers are skipped, not notified”.
  6. Write a short README section pointing to /loop /sdlc:orchestrate as the recommended invocation.
  7. Dry-run by hand: invoke /sdlc:orchestrate once against the current repo state and walk through what it would do (or actually do, with gh pr list and worktree reconciliation live). Iterate if the tick takes the wrong action.
  • plugin/skills/orchestrate/SKILL.md (new) — the tick procedure.
  • plugin/skills/orchestrate/invariants.yaml (new) — lint invariants for the skill prose.
  • .gitignore — confirm .claude/orchestrator-log.md is ignored (likely already covered by .claude/).
  • docs/planning/epics/E0001.md — no edit; this task is already listed under the epic.
  • AC-1: /sdlc:orchestrate invocable; runs a single tick end-to-end against the live repo (10 worktrees, 4 open PRs, 3 unverified ready tasks) and produces a digest-log line plus reasonable dispatch decisions. (agent-manual)
  • AC-2: Tick never dispatches more than 2 concurrent /sdlc:task-work sub-agents. Verifiable by reading the SKILL.md and the invariants file. (auto)
  • AC-3: Tasks with status: in-progress/blocked or status: proposed/needs-definition are silently skipped on the next tick — no PushNotification fires for them. (agent-manual: set a fixture task to each state, confirm)
  • AC-4: Sub-agent dispatch uses Agent(general-purpose) for task-work and pr-check (verifiable in SKILL.md prose) — NOT direct Skill invocation. The orchestrator parent transcript per tick grows by <2k tokens excluding sub-agent return strings. (auto via lint invariant + agent-manual measurement)
  • AC-5: lint_skill_prose.py passes for the new SKILL.md against its invariants.yaml. (auto)
  • AC-6: After dry-run against current repo state, at least one of: a stale worktree torn down, a ready task picked up, or a PR triaged. The orchestrator demonstrably moves state forward. (agent-manual)
  • pr-check and task-close-out skills (separate tasks in this epic).
  • Cross-session durability (Phase 2 — see T-FQCN-self-driving-orchestrator-loop Out of scope).
  • Auto-merging PRs or auto-promoting tasks from draft/proposed to ready.
  • Resolving the existing worktree mess. The orchestrator should clean it up naturally on its first few ticks; if it can’t, file a follow-up.

Both must land first or in the same PR train; orchestrate can’t dry-run without them.

Phase 1 of epic T-FQCN-self-driving-orchestrator-loop. Designed in conversation 2026-05-19 — see epic Discovery context for the research that informed the Agent-dispatch-not-direct-Skill choice that this skill encodes.

Captured by /sdlc:task-work on 2026-05-20. PR: pending.

The AC-1 parenthetical state (“10 worktrees, 4 open PRs, 3 unverified ready tasks”) was stale by the time this run started — most of the worktree backlog had already been reconciled and the PR queue is empty. The AC body itself says “the live repo” so it self-heals; the dry-run was traced against current state (0 open PRs, 3 open/ready tasks, 1 in-flight worktree — this one).

  • AC-1: agent-manual — plugin/skills/orchestrate/SKILL.md exists and is structurally complete (frontmatter + 6 numbered steps + Failure modes + Notes). Dry-run trace against live state: Step 1 git fetch origin exits clean, Step 2 finds zero open PRs (so no pr-check dispatches), Step 3 scans for status: open/ready and finds three (2026-05-17-add-astro-docs-site, 2026-05-19-task-ensure-ready-accepts-in-progress, 2026-05-19-templates-html-comment-after-frontmatter) with one in-flight worktree — at cap=2, would dispatch one more (astro-docs-site, oldest). Digest line would be <ts> tick=1 sync=ok prs=<empty> tasks-dispatched=2026-05-17-add-astro-docs-site blocked=<none>.
  • AC-2: auto — invariants.yaml required_phrases includes "parallelism cap of 2" pinned to the “Parallelism cap” section; lint_skill_prose.py asserts the literal appears.
  • AC-3: agent-manual — Step 5 prose explicitly enumerates the three notify-only conditions (no PRs + no ready tasks + ≥1 needs-definition/blocked) and the “routine blockers do NOT notify” carve-out. Invariants pin both "only when genuinely stuck" and "Routine blockers do NOT notify" as required phrases. A fixture task in planning/needs-definition would land in the digest’s blocked=<slug> field, not trigger PushNotification, as long as any other condition fails.
  • AC-4: auto — invariants forbidden_phrases block direct-Skill dispatch from the parent body (Skill(/sdlc:pr-check, Skill(/sdlc:task-close-out, Skill(/sdlc:task-work are all absent). Required phrase "Agent(general-purpose)" is present under the “Dispatch contract” section. The <2k-token transcript-growth target in the AC body is a measurement claim that needs live-tick verification; spec-only at this point.
  • AC-5: auto — lint_skill_prose.py plugin/skills/*/SKILL.md exits 0 across 19 skills with 7 invariants files.
  • AC-6: agent-manual — same dry-run trace as AC-1: tick would dispatch /sdlc:task-work 2026-05-17-add-astro-docs-site, satisfying the “ready task picked up” branch of AC-6’s disjunction (the other two — stale worktree torn down, PR triaged — don’t fire because state is already clean). State-forward property confirmed.
  • The forbidden_phrases mechanism made the Agent-dispatch contract enforceable as lint: any future drift toward direct-Skill dispatch from the parent body trips immediately. This is exactly the kind of contract that previously only held by reviewer attention.
  • The required_phrases with section: hints pinned the parallelism cap and the dispatch surface to specific H2/H3 sections, so the linter knows where each invariant must appear (not just “somewhere in the file”). One required phrase trip caught a wording drift on the first lint run, before commit.
  • Following the same SKILL.md shape as pr-check and task-close-out (description + allowed-tools frontmatter, numbered steps, Failure modes, Notes) made the orchestrate body slot into the project’s existing convention without bespoke scaffolding.
  • AC-1’s parenthetical state (“10 worktrees, 4 open PRs, 3 unverified ready tasks”) had drifted to (1, 0, 3) by the time this task was picked up. The AC body self-heals via “the live repo” wording, but the parenthetical is misleading on read. Same general gap as the close-out task — a /sdlc:task-review-level relevance check on AC parenthetical state would catch this. Already tracked by T-IXV6-task-review-checks-ac-fixture-relevance (spawned from the previous task’s post-mortem). → T-IXV6-task-review-checks-ac-fixture-relevance
  • AC-4’s “parent transcript per tick grows by <2k tokens excluding sub-agent return strings” is a measurement claim that can’t be verified without running a real tick end-to-end and measuring. Spec-only today; needs a token-counting fixture once /loop /sdlc:orchestrate runs against the live repo for a few ticks. → T-BLYF-orchestrator-tick-token-budget-fixture
  • The skill has not been exercised live yet — AC-1 / AC-3 / AC-6 are all dry-run traces, not actual ticks. A first /reload-plugins + /loop /sdlc:orchestrate run would surface any prose-vs-implementation gaps. The hard parts (Agent dispatch shape, pr-check return parsing, task-work return parsing) are spec-only until then. → T-JWCO-orchestrate-first-live-tick-shakedown
  • The cross-skill invariant “parent transcript stays small” depends on sub-agents returning ONE LINE only. The current prompts say “return one verdict line” but the model has no hard cap; a sub-agent that monologues would violate the contract silently. Worth a follow-up — either tighten the prompt phrasing further or add a parent-side guard that truncates verbose returns. → T-TJB3-orchestrate-enforces-one-line-verdict
  • The “1 in-flight task counts toward the cap” rule is detected by checking for .claude/worktrees/<basename> + matching feat/<basename> branch. If a worktree exists but the task file’s status is already closed/done (stale worktree, didn’t tear down), the orchestrator would over-count and under-dispatch by one. Minor; the orchestrator’s own close-out passes will clean these up over time, but a sanity check that worktrees correspond to in-progress tasks would be cleaner. → 2026-05-20-orchestrate-validates-worktree-task-status

T-8896-add-pr-check-skill, T-RDKI-extract-task-close-out-skill


← Back to Tasks