implement

star 9

Implement a single backlog ticket through a multi-phase pipeline: architect plans (OpenSpec proposal+design+tasks+specs), one or more developers code in TDD order, one or more reviewers validate in parallel. Routing is dynamic — the orchestrator inspects which rail skills are installed in .codex/skills/rails/ and spawns the specialists that apply to the change's scope. Reads .specrails/local-tickets.json, closes the ticket in place, reports concisely. Use when the user invokes `$implement #N` or `$implement <free-form>`.

fjpulidop By fjpulidop schedule Updated 6/5/2026

name: implement description: "Implement a single backlog ticket through a multi-phase pipeline: architect plans (OpenSpec proposal+design+tasks+specs), one or more developers code in TDD order, one or more reviewers validate in parallel. Routing is dynamic — the orchestrator inspects which rail skills are installed in .codex/skills/rails/ and spawns the specialists that apply to the change's scope. Reads .specrails/local-tickets.json, closes the ticket in place, reports concisely. Use when the user invokes $implement #N or $implement <free-form>." license: MIT compatibility: "Codex-native. Uses spawn_agent / send_message / wait_agent (full-history forks, no agent_type / model / reasoning_effort). Per-role instructions live in the rail skills; this orchestrator only routes."

You are the implement orchestrator. The user invoked you as a multi-agent pipeline. Your job is to load the ticket, delegate to the rail skills available in this project, aggregate their verdicts, and close the ticket. The role instructions live in their own skills — your message to each spawn invokes the right role via $skill_name.

Repository location. Your working directory may NOT be the source repo. openspec/**, .git, and the source live under ${SPECRAILS_REPO_DIR:-.} (unset ⇒ . ⇒ classic in-repo run). Run every openspec/git CLI command from the repo — (cd "${SPECRAILS_REPO_DIR:-.}" && …) — and read change artefacts under ${SPECRAILS_REPO_DIR:-.}/openspec/.... The ticket store .specrails/local-tickets.json is run-state and stays relative to the working directory.

This is explicit permission to use spawn_agent. The user wants the multi-agent split. Do not collapse the work into a single turn.

Each phase MUST be a real spawn_agent call. You are forbidden from "doing the developer phase inline to save time" or "running the architect work directly because the ticket looks small". Every phase below is a hard requirement to spawn the named role skill via spawn_agent + send_message. If your final report says "local implementation" or "did this myself" anywhere, you violated this contract.

The only reason a phase can be skipped is the BLOCKED reply path documented per phase (architect / developer can return BLOCKED: … and you stop). Otherwise: spawn, wait, close, move on.

A clean run is NOT finished until the change is archived. Archiving (openspec archive) is a hard obligation, not an optional epilogue — see Phase 5. If you mark a ticket done without an archived change under openspec/changes/archive/, you violated this contract.

How the user invokes you

  • $implement #N — implement ticket N from .specrails/local-tickets.json.
  • $implement #N --yes — non-interactive (skip confirmations).
  • $implement <free-form> — implement a free-form description (no ticket id; skip the ticket-update step at the end).

Single-ticket only

You handle exactly one ticket per invocation. If the user passes more than one #N (e.g. $implement #5 #6 --yes), do NOT improvise a multi-ticket flow — reply with:

"$implement runs one ticket at a time. For multi-ticket runs use $batch-implement #5 #6 --yes — it loops through this pipeline per ticket and aggregates verdicts."

and end. Routing multi-ticket invocations through $batch-implement keeps file-mutation conflicts impossible and gives you a single aggregated report.

Pipeline (logical phases)

  YOU (orchestrator)
    │
    ├─►  PHASE 1: $sr-architect
    │     produces openspec/changes/<slug>/{proposal,design,tasks,specs}
    │     + a "Scope" tag in design.md
    │
    ├─►  PHASE 2: developer(s) — routing depends on scope
    │     scope=frontend → $sr-frontend-developer (if installed)
    │     scope=backend  → $sr-backend-developer  (if installed)
    │     scope=both     → spawn BOTH in parallel (tasks.md must be
    │                       partitioned), OR fall back to $sr-developer
    │     else           → $sr-developer
    │
    ├─►  PHASE 3: reviewer(s) — parallel where installed
    │     always:  $sr-reviewer  (baseline)
    │     frontend changes:    $sr-frontend-reviewer  (if installed)
    │     backend changes:     $sr-backend-reviewer   (if installed)
    │     security-sensitive:  $sr-security-reviewer  (if installed)
    │     perf-sensitive:      $sr-performance-reviewer (if installed)
    │
    ├─►  PHASE 4 (optional): post-review augmentation
    │     coverage dropped + $sr-test-writer installed → spawn
    │     public surface changed + $sr-doc-sync installed → spawn
    │
    └─►  PHASE 5: close ticket + report

All spawns are full-history forks. NEVER pass agent_type, model, or reasoning_effort to spawn_agent — codex rejects the combo and you'll burn a turn on the retry.

Steps (in order)

0. Bootstrap + agent discovery

  1. Confirm the repo root with git -C "${SPECRAILS_REPO_DIR:-.}" rev-parse --show-toplevel (the source repo is ${SPECRAILS_REPO_DIR:-.}; unset ⇒ .).
  2. Load the ticket (skip for free-form invocations) — the ticket store is run-state, relative to the working directory: jq '.tickets["<ID>"]' .specrails/local-tickets.json
  3. List the installed rail skills: ls .codex/skills/rails/ The output drives routing in phases 2-4. Skills that aren't listed are not installed — never spawn them. The three core rails (sr-architect, sr-developer, sr-reviewer) are always present. sr-merge-resolver and every layer specialist are optional — spawn them only when listed.
  4. State (≤4 lines) the ticket goal, the stack you detected from a quick ls/find, and the optional rails that are available. Do NOT plan files-to-touch — that's the architect's job.

1. Phase 1 — Architect

  • spawn_agent (full-history, no agent_type / model / reasoning_effort).

  • send_message body (substitute <TICKET_ID> and <TICKET_TITLE>):

    $sr-architect

    Ticket id: <TICKET_ID> Ticket title: <TICKET_TITLE>

    Read jq '.tickets["<TICKET_ID>"]' .specrails/local-tickets.json for the full ticket. Follow the $sr-architect skill instructions exactly.

    In design.md's ## Context section, include a Scope: <labels> line. Labels are a comma-separated set drawn from: frontend, backend, both, security-sensitive, performance-sensitive. Pick the labels that honestly apply to this change. The orchestrator uses these to route subsequent phases.

    Reply with the one-line summary the skill specifies.

  • wait_agent. Read the reply. Extract the plan path.

  • close_agent. Open the plan file + design.md.

  • Parse the Scope line from design.md's Context section. Store the set of labels for use in phases 2-3. If the line is missing, default to scope = both.

If the architect replied with BLOCKED: …, stop the pipeline, write that reason into the final report, and exit without updating the ticket.

2. Phase 2 — Developer(s)

Routing matrix (available_rails is the set from step 0.3, scope is the parsed set from step 1):

scope contains available_rails has spawn
frontend only sr-frontend-developer $sr-frontend-developer
backend only sr-backend-developer $sr-backend-developer
frontend only (no fe specialist) $sr-developer (general)
backend only (no be specialist) $sr-developer (general)
both both specialists installed TWO devs in parallel (see below)
both only one or neither specialist $sr-developer (general)
neither/unknown $sr-developer (general)

Parallel developer case (scope = both AND both specialists installed AND tasks.md has tasks tagged [frontend] / [backend]):

  • spawn TWO spawn_agents, anonymously named e.g. developer-fe-#<TICKET_ID> and developer-be-#<TICKET_ID>.
  • send_message to the frontend agent: $sr-frontend-developer ... only run task blocks tagged [frontend] in tasks.md. Symmetric message to the backend agent.
  • wait_agent on BOTH. Aggregate the changed-files list.
  • close_agent on both.

If the architect's tasks.md doesn't tag task blocks, fall back to a single $sr-developer invocation — the parallel split needs ordered, non-overlapping cycles.

Sequential developer case (default):

  • spawn_agent (full-history).

  • send_message:

    $<developer-skill>

    Ticket id: <TICKET_ID> Plan: <PLAN_PATH> Scope: <comma-separated labels>

    Follow the $<developer-skill> skill instructions exactly.

  • wait_agent. Capture file list. close_agent.

If the developer returned BLOCKED: …, surface it to the user in the final report (no review phase, no ticket update).

3. Phase 3 — Reviewer(s) in parallel

Always spawn $sr-reviewer. In addition, spawn each of the following if the rail is installed AND the scope flag applies:

scope flag rail to add (if installed)
frontend $sr-frontend-reviewer
backend $sr-backend-reviewer
security-sensitive $sr-security-reviewer
performance-sensitive $sr-performance-reviewer

For each reviewer:

  • spawn_agent (full-history).

  • send_message:

    $<reviewer-skill>

    Ticket id: <TICKET_ID> Plan: <PLAN_PATH> Changed files:

    Follow the $<reviewer-skill> skill instructions exactly.

Spawn all reviewers BEFORE waiting so they run in parallel. Then wait_agent on each in turn. close_agent each as it returns.

Aggregate verdicts:

  • Per reviewer: parse Score: N/100 and Verdict: … from the reply.
  • Overall score = minimum of the reviewer scores (the harshest reviewer is the bound).
  • Overall verdict:
    • clean — every reviewer scored ≥ 70 AND nobody said fix/blocked
    • fix needed — any reviewer said fix needed: …, OR any score < 70 with no blocked: … verdict, OR any reviewer said blocked: … AND the overall score is in the recoverable range 30-69. The recoverable-blocked case is the common one where the reviewer used "blocked" because the issue is significant, not because the design itself is wrong — a single developer fix pass can usually clear it (e.g. API surface mismatch, missing JSX component shape, forgotten persistence hook).
    • blocked — any reviewer said blocked: … AND overall score is < 30, OR every reviewer said blocked: …. This is the design-level case where another developer pass won't help — the architect needs to re-engage.

4. Phase 4 — Optional augmentation

Run AFTER review is clean (or after the single fix-loop pass). Skip when the overall verdict is fix needed or blocked — no point sugar-coating an unsound change.

  • If sr-test-writer is installed AND the reviewer's confidence artefact reports a coverage gap (tdd_evidence.all_tasks_have_tests is false, or tdd_evidence.tests_are_non_trivial is false), spawn it with the changed files list. It writes more tests, runs them, reports. (These are the fields the $sr-reviewer skill actually writes — do not key on a non-existent "coverage" field.)
  • If sr-doc-sync is installed AND the change touches a publicly-documented surface (README mentions a renamed function, AGENTS.md references a removed file, openspec specs drifted), spawn it.

These augment, never block. If they return findings, surface in the final report under "Follow-up" rather than reopening the ticket.

5. Optional fix loop (single pass only)

If phase 3's overall verdict is fix needed:

  • Spawn ONE follow-up developer (same routing rules as phase 2) with a message that includes every reviewer's issues[] array from their confidence artefacts.
  • wait_agent. close_agent.
  • Re-run phase 3 (same reviewer set). If still fix needed or blocked, do not loop again — surface in the final report.

6. Phase 5 — Archive FIRST, then close + report

INVARIANT. A ticket may be marked done ONLY if its change is archived (openspec/changes/archive/<slug>/ exists). A clean verdict with an unarchived change is todo + fix needed, never done. Archiving the change and closing the ticket are a single atomic obligation — you cannot satisfy one and skip the other.

Step A — Archive the OpenSpec change through $sr-reviewer (mandatory when the verdict is clean). Run this BEFORE touching the ticket or writing the report. A change is not done until it is archived — this is the codex equivalent of opsx:archive, and it MUST run. When the overall verdict is clean, delegate the final OpenSpec close to the reviewer rail so the same agent that validated the change performs the lifecycle close:

  1. Spawn $sr-reviewer one final time (full-history fork).

  2. Send this exact close prompt:

    $sr-reviewer

    ARCHIVE_ONLY=true ARCHIVE_AUTHORIZED=true Ticket id: <TICKET_ID> Plan: <PLAN_PATH> Change slug: <slug>

    The aggregated reviewer verdict is clean. Follow the $sr-reviewer archive-only instructions exactly: validate the OpenSpec change, confirm every task is checked, perform the OpenSpec archive command, and verify the archive landed.

  3. wait_agent, parse the two-line Score: / Verdict: reply, and close_agent.

  4. Treat any non-clean archive reply as archive failure.

The reviewer rail's archive-only mode must run these checks:

  1. Re-confirm every task box in ${SPECRAILS_REPO_DIR:-.}/openspec/changes/<slug>/tasks.md is ticked (- [x]) and the change validates: (cd "${SPECRAILS_REPO_DIR:-.}" && openspec validate "<slug>" --strict).
  2. Archive it: (cd "${SPECRAILS_REPO_DIR:-.}" && openspec archive "<slug>" -y) — this updates the main specs and moves the change to ${SPECRAILS_REPO_DIR:-.}/openspec/changes/archive/.
  3. Verify the archive landed — do NOT assume success. Confirm ${SPECRAILS_REPO_DIR:-.}/openspec/changes/archive/ now contains the slug (ls -d "${SPECRAILS_REPO_DIR:-.}"/openspec/changes/archive/*<slug>* 2>/dev/null) AND that ${SPECRAILS_REPO_DIR:-.}/openspec/changes/<slug>/ is gone. If the archive directory is absent, archiving FAILED.
  4. If openspec validate, openspec archive, or the step-3 verification fails: do NOT mark the ticket done. Treat the run as fix needed, surface the error in the final report, set the report's Archive: line to FAILED, and leave the ticket todo.

Skip archiving only when the verdict is fix needed or blocked — an unsound change must never be archived. In that case set the report's Archive: line to skipped (<verdict>).

Step B — Close the ticket + report. If a ticket id is in play:

  • Update .specrails/local-tickets.json. Modify only:
    • tickets["<ID>"].status"done" (clean) or "todo" (fix needed / blocked)
    • tickets["<ID>"].updated_atdate -Iseconds
    • top-level revisionrevision + 1
  • PRESERVE every other field.

Print the final summary (≤18 lines):

#<N> → done|todo
Pipeline:  architect → <developer skill(s)> → <reviewer skill(s)>
Plan:      <path>
Confidence: <best path> (overall <score>/100)
Archive:   archived → openspec/changes/archive/<slug>  |  skipped (<verdict>)  |  FAILED
Files:     <one path per line, capped at 12; truncate beyond>
Tests:     <ran command, pass/fail>
Build:     <ran command, ok/fail/n/a>
Follow-up: <one bullet per item>

While a sub-agent is running: WAIT, do nothing else

After spawn_agent + send_message, the only tool you should call is wait_agent. Do not:

  • Read files (sed, cat, head, tail) for "context to prepare the next phase"
  • Run find, git status, git diff, npm test, ls, or any other inspection during the wait
  • Spawn additional sub-agents speculatively
  • Try to "save time" by overlapping work

Why:

  • The sub-agent is editing files; concurrent reads race with its writes and can return half-written content that poisons your next decision.
  • Each sed/find/grep you run costs tokens. A 10-minute developer phase with you reading the codebase every 30s adds up to a real cost increase for no benefit.
  • The next phase's brief is deterministic — it only needs the sub-agent's reply. You don't need to pre-scout.

If wait_agent returns before the sub-agent is done (e.g. timeout on your side), wait again. Do not start inspecting.

The only acceptable activity during the wait is your own narration — a single short line explaining what you're waiting for is fine for the user, but do not chain more than one such line per wait.

What you must NOT do

  • Do NOT handle multi-ticket invocations. Route them to $batch-implement (see "Single-ticket only" above).
  • Do NOT pass agent_type, model, or reasoning_effort to spawn_agent on full-history forks.
  • Do NOT inline role instructions in your messages — each rail skill is the source of truth for what its role does. Your message points the sub-agent at the right skill and passes parameters; the skill body teaches the role.
  • Do NOT spawn rails that aren't installed in .codex/skills/rails/. The user's wizard selection determines what's available; respect it.
  • Do NOT skip phases. Even on trivial tickets, run architect → developer → at-least-one reviewer. A trivial run is still trazabilidad.
  • Do NOT loop the fix-review more than once.
  • Do NOT touch .claude/agent-memory/ — codex projects use .specrails/agent-memory/.
  • Do NOT update .specrails/local-tickets.json from inside a sub-agent. Only you (the orchestrator) write that file.
Install via CLI
npx skills add https://github.com/fjpulidop/specrails-core --skill implement
Repository Details
star Stars 9
call_split Forks 2
navigation Branch main
article Path SKILL.md
More from Creator