---
title: "Prd To Goal"
description: "Decomposes a PRD, issue, or spec into a copy-pasteable single `/goal until ..., or stop after N turns` line. Use when running /goal against a spec, to reduce acceptance criteria to AND-joined boolean assertions."
canonical: "https://orchestkit.yonyon.ai/docs/reference/skills/prd-to-goal"
---

# Prd To Goal

Decomposes a PRD, issue, or spec into a copy-pasteable single `/goal until ..., or stop after N turns` line. Use when running /goal against a spec, to reduce acceptance criteria to AND-joined boolean assertions.

<span className="badge badge-blue">Command</span> <span className="badge badge-green">low</span>

```bash title="Invoke"
/ork:prd-to-goal
```

<ContextualSkillSidebar slug="prd-to-goal" />

> **Prd To Goal** Decomposes a PRD, issue, or spec into a copy-pasteable single `/goal until ..., or stop after N turns` line. Use when running /goal against a spec, to reduce acceptance criteria to AND-joined boolean assertions.


# /ork:prd-to-goal — PRD → /goal Decomposition

Converts a PRD / issue / spec into a single copy-pasteable `/goal` line. The hard part of `/goal` is not running it — it is writing an `until` clause that is *convergent* (terminates), *falsifiable* (testable boolean), and *observable* (the agent can actually check it without subjective judgement). This skill makes that decomposition reproducible.

## 1. When to use

**Use it when:**
- You have a written PRD, GitHub issue, or spec and want to run `/goal` against it.
- Past `/goal` runs drifted, looped, or burned tokens because the `until` clause was vague (`until tests pass`, `until done`, `until design is good`).
- You need to justify the abort budget — turns, tokens, no-progress threshold.

**Skip it when:**
- One-shot bug fix where the failing test *is* the acceptance criterion. Just run `/goal until pnpm test -- auth.spec.ts passes`.
- No written PRD exists. Run `/ork:write-prd` first — vibes do not decompose.
- The work is destructive or irreversible (DB migrations, mass file deletes). `/goal` retries; you do not want retries on `DROP TABLE`.

## 2. Inputs the skill accepts

| Input | How |
|---|---|
| Pasted PRD text | Provide as the argument, or paste into the chat after invoking. |
| GitHub issue | `gh issue view &lt;N&gt; --json title,body,labels` — the skill reads `body`. |
| Spec file | Path to a Markdown / text file; the skill `Read`s it. |
| ADR / design doc | Same as spec file. |

## 3. The decomposition algorithm

1. **Extract acceptance criteria.** Pull every `MUST`, `SHOULD`, `Definition of Done`, `Acceptance Criteria`, and checkbox-style line. If the doc has none, stop and tell the user to run `/ork:write-prd` first — there is nothing to converge on.
2. **Map each criterion to an observable boolean.** Each criterion must reduce to a single shell-checkable assertion. Examples of observable state:
   - `test -f path/to/file` (file exists)
   - `pnpm test -- pattern passes` (test command exits 0)
   - `gh pr view &lt;N&gt; --json state | jq -r .state == "MERGED"`
   - `wc -l &lt; src/auth.ts` returns a number within bound
   - `pnpm lint` exits 0
   - `curl -sf $URL` returns 2xx
3. **Reject non-observable criteria.** Drop or rewrite criteria that depend on subjective judgement (`code is clean`, `design feels right`, `users are happy`). Either find a proxy (`lint exits 0`, `Lighthouse score > 90`, `NPS survey ID exists`) or surface the criterion back to the user as out of scope for `/goal`.
4. **Compose the `until` clause.** AND-join the observable assertions in priority order — the cheapest, most likely-to-fail check first so the loop short-circuits early. Three to five assertions is the sweet spot; more than seven usually means the PRD is two PRDs.
5. **Fold the budget INTO the same condition.** Claude Code accepts one goal per
   session, replace-on-set, and the bound is written inside that single condition
   (`…, or stop after 15 turns`). It is not a second command. Sensible turn caps:
   - `15` for a single feature, `30` for a refactor, `5` for a bug fix.

   > **Never emit a second `/goal` line.** A second line does not add a rail — it
   > parses as a fresh condition, REPLACES the first, and leaves the session
   > goaled on the budget alone with every acceptance assertion discarded. The
   > `abort-if` form this skill used to emit was never Claude Code syntax at all:
   > `grep -c -a -F 'abort-if'` against the 2.1.226 binary returns `0`, and it
   > appears in none of the 357 archived changelogs. Verified 2026-08-08 (#3312).

## 4. Output template

The skill emits exactly ONE line, ready to paste:

```
/goal until <assertion_1> AND <assertion_2> AND <assertion_3>, or stop after <N> turns
```

No commentary, no markdown wrapper — the user copies the line straight into Claude Code.

### Optional: rubric emission (`.claude/rubric.json`)

When the user wants graded feedback beyond pass/fail booleans, the skill MAY also emit `.claude/rubric.json` conforming to `ork-rubric/1.0` (schema: `$\{CLAUDE_PLUGIN_ROOT\}/shared/rubric.schema.json`), mapping each acceptance criterion to one dimension:

```json
{
  "rubric": "ork-rubric/1.0",
  "skill": "prd-to-goal",
  "dimensions": [
    { "name": "regression_test_added", "weight": 0.4, "min_pass": 8, "min_blocker": 3 },
    { "name": "auth_suite_green",      "weight": 0.4, "min_pass": 10, "min_blocker": 5 },
    { "name": "lint_clean",            "weight": 0.2, "min_pass": 10, "min_blocker": 0 }
  ]
}
```

Scores are 0–10 (`min_pass` = soft floor, `min_blocker` = hard blocker regardless of composite); dimension weights MUST sum to 1.0 — see the schema for the full contract.

The file is deliberately **user-editable before the `/goal` run** — that is the point. Adjusting weights and `min_pass` thresholds is how the user injects judgement into the loop without rewriting assertions (rubric-as-environment-feedback, Lance Martin 2026-06-09). The post-timeout grader (§8) treats the rubric, if present, as the user's intent — senior to the literal assertion text.

## 5. Worked examples

### Example A — Bug fix PRD

Input (issue body):

```
Title: Login fails on emails containing "+"
Acceptance Criteria:
- New regression test in tests/auth/test_login.py covers email with "+"
- The new test passes
- All existing auth tests still pass
```

Output:

```
/goal until test -f tests/auth/test_login.py AND pnpm test -- tests/auth/test_login.py passes AND pnpm test -- tests/auth passes, or stop after 5 turns
```

Rationale: file existence is the cheapest check; the targeted test is the regression gate; the broad auth suite catches collateral damage.

### Example B — Feature PRD

Input (PRD excerpt):

```
Feature: User Avatar Endpoint
MUST:
- New route GET /users/:id/avatar registered
- Returns 200 with { url: string, updatedAt: ISO8601 } for known user
- Returns 404 for unknown user
- Integration test covers both cases
- OpenAPI spec updated
```

Output:

```
/goal until grep -q "users/:id/avatar" src/routes/users.ts AND pnpm test -- tests/integration/users.avatar.spec.ts passes AND grep -q "/users/{id}/avatar" openapi.yaml AND pnpm lint passes, or stop after 15 turns
```

Rationale: route `grep` catches a handler that was stubbed but never wired; the integration spec encodes both 200 and 404; OpenAPI grep enforces the docs MUST; lint guards against half-typed code shipping.

### Example C — Refactor PRD

Input (PRD excerpt):

```
Refactor: split monolithic src/auth.ts (1842 LOC) into per-strategy files
Definition of Done:
- src/auth.ts under 200 LOC
- New files in src/auth/strategies/*.ts cover oauth, jwt, password
- All auth tests still pass
- Lint clean
- No new files outside src/auth/
```

Output:

```
/goal until [ $(wc -l < src/auth.ts) -lt 200 ] AND test -f src/auth/strategies/oauth.ts AND test -f src/auth/strategies/jwt.ts AND test -f src/auth/strategies/password.ts AND pnpm test -- tests/auth passes AND pnpm lint passes, or stop after 30 turns
```

Rationale: the LOC bound is the convergent signal — without it, `/goal` can keep "improving" forever. File-existence checks pin the structural decomposition; tests + lint guard correctness. Higher budget because refactors run longer than bug fixes.

## 6. Recipe Library — pre-built loops

Where §3–5 *generate* a custom `/goal` line from a spec, the recipe library *ships* ready-made ones for the recurring autonomous loops. Each is a loop shape wrapped around an ork worker skill, following the same convergent / falsifiable / budgeted rules as a generated line.

| Recipe | Use when | Worker |
|---|---|---|
| 🧪 Coverage climb | raise coverage to a target, meaningfully | `/ork:cover` |
| 🔴 Production error sweep | clear a backlog of actionable errors | `/ork:fix-issue` |
| 📚 Docs-drift sweep | docs / reference drifted from code | `/ork:audit-full` |
| ⚡ Page-load budget | a page exceeds its latency budget | `/ork:performance` |
| 🧹 Repository cleanup | stale memory / state accumulated | `/ork:dream` |
| ✅ Quality streak | don't trust a single green (flaky suite) | `/ork:verify` |
| 🎫 Ticket → PR-ready | drive an issue to a CI-green PR | `/ork:fix-issue` → `/ork:create-pr` |
| 🧼 Type/lint zero | clear a type/lint backlog without suppressions | fixer agent |

Full recipes — the exact single-line `/goal until …, or stop after N turns` lines, convergent signal, and per-recipe guardrail: `references/recipe-library.md`. That file is the in-repo source intended for an `ork-loops` pack on skills.sh (not yet built; the channel Forward Future's Loop Library uses), so the loop *recipes* can travel while ork supplies the *machinery* each pass runs on.

## 7. Anti-patterns

| Bad | Why it fails | Good |
|---|---|---|
| `/goal until tests pass` | Which tests? Existing or new? On which command? The agent can pick whatever subset is already green and claim done. | `/goal until pnpm test -- tests/auth/test_login.py passes AND pnpm test -- tests/auth passes` |
| `/goal until the code looks clean` | Not falsifiable. The agent cannot check "looks clean" — it will either declare victory immediately or never. | `/goal until pnpm lint passes AND [ $(wc -l &lt; src/auth.ts) -lt 200 ]` |
| `/goal until done` | Unbounded. There is no terminating condition; combined with a generous `, or stop after 50 turns` this is how runs eat 500K tokens overnight. | Pick 3–5 observable assertions. If you cannot, the PRD is not ready — go to `/ork:write-prd`. |

## 8. Post-timeout assertion grader

If the `/goal` loop hits its turn bound, do NOT retry the same line and do NOT let the looping agent critique its own assertions. Spawn a FRESH-context grader that audits the assertion set itself:

```python
# Bare-eval pattern — independent context window, zero shared state:
Bash("CLAUDE_CODE_FORK_SUBAGENT=1 claude -p --bare \"$(cat /tmp/grader-prompt.txt)\"")
# or: Agent(subagent_type="general-purpose", prompt=grader_prompt)  # fresh spawn, no producer prose
```

The grader receives the `/goal` line, `.claude/rubric.json` (if emitted), a compressed summary of the last N turns, and freshly regenerated repo-state evidence. It returns one verdict:

| Verdict | Diagnosis | Action |
|---|---|---|
| `tighten` | Assertions too weak — agent could satisfy them without real success | Re-run with the stricter revised line |
| `loosen` | Assertions unsatisfiable as written (wrong path, impossible bound, pre-broken suite) | Re-run with the achievable revised line |
| `abort` | Task genuinely blocked (missing access, contradictory spec, broken env) | Stop; surface the blocker to the user |

Grading must happen in an independent context window — never self-critique (verifier sub-agents outperform self-critique because the grader does not share the producer's context; Lance Martin, 2026-06-09). Budget: one grader call per timeout, and the grader never loops itself.

Full pattern (prompt template, independence rules, worked example): `$\{CLAUDE_PLUGIN_ROOT\}/skills/chain-patterns/references/assertion-grader.md`.

## 9. Related skills

- `ork:write-prd` — if the input has no acceptance criteria, run this first.
- `ork:brainstorm` — useful when the PRD itself is contested and you want options before writing the goal.
- `ork:audit-full` — after `/goal` exits, run audit-full to confirm the assertions actually held end-to-end (the loop trusts the boolean; audit re-checks the intent).


---

## References (1)

### Recipe Library

# Loop Recipe Library — pre-built `/goal` loops

Pre-written, battle-tested single-line `/goal until …, or stop after N turns` recipes for the recurring autonomous loops. Where `prd-to-goal` *generates* a custom goal line from a spec, this library *ships* ready-made lines for jobs you run over and over.

Each recipe is a **loop shape** (when to stop) wrapped around an **ork skill** (the work each pass does). They follow the same rules as a generated line: AND-joined observable assertions in the `until`, a real turn bound inside the same condition, and a convergent signal so the loop terminates.

> **Distribution (planned):** this library is the in-repo source intended for an `ork-loops` pack on skills.sh — the same channel Forward Future's Loop Library uses (`npx skills add`). The pack itself is not built yet; today the recipes live here. Interop, not competition: we ship the loop *recipes*; ork supplies the *machinery* (rubric gates, 211 safety hooks, parallel agents) each pass runs on.

## How to use a recipe

1. **Replace the `&lt;TOKENS&gt;`.** Recipes are templates — swap `&lt;TEST_CMD&gt;`, `&lt;COVERAGE_CMD&gt;`, etc. for your project's commands (e.g. `npm test`, `pnpm test`, `pytest`). The `&lt;…&gt;` tokens are the only things you edit.
2. **Keep the turn budget, inside the same condition.** It is the safety rail, not boilerplate — tighten it, never drop it. Write it as `…, or stop after &lt;N&gt; turns` within the one `/goal` line. Do NOT emit it as a second `/goal` command: Claude Code takes one goal per session with replace-on-set, so a second line replaces the first and discards every acceptance assertion. The `abort-if` form these recipes previously carried was never Claude Code syntax (`grep -c -a -F 'abort-if'` on the 2.1.226 binary returns `0`); it is corrected throughout, see #3312.
3. **Name the worker.** Each recipe lists the ork skill that should run each pass — invoke it as the loop's worker (`/goal … then run /ork:cover each turn`).
4. **Mind the guardrails.** Every recipe lists the one way the loop gets gamed and how the assertions prevent it.

## Recipe → theme → worker map

| Recipe | Loop-Library theme | ork worker skill |
|---|---|---|
| 🧪 Coverage climb | 100% Test Coverage | `/ork:cover` |
| 🔴 Production error sweep | Production Error Sweep | `/ork:fix-issue` |
| 📚 Docs-drift sweep | Docs Sweep | `/ork:audit-full` |
| ⚡ Page-load budget | Sub-50ms Page-Load | `/ork:performance` |
| 🧹 Repository cleanup | Repository Cleanup | `/ork:dream` |
| ✅ Quality streak | Quality Streak | `/ork:verify` |
| 🎫 Ticket → PR-ready | Ticket-to-PR-Ready | `/ork:fix-issue` → `/ork:create-pr` |
| 🧼 Type/lint zero | (static-analysis baseline) | fixer agent |

---

## 🧪 Coverage climb

**Use when:** coverage is below your bar and you want it raised *meaningfully* (not gamed).
**Backs each pass:** `/ork:cover` · **Convergent signal:** coverage % climbs toward target; no-progress aborts the asymptote.

```
/goal until <COVERAGE_CMD> reports >= 90 AND <TEST_CMD> passes, or stop after 20 turns
```

**Guardrail:** coverage alone is gameable (assert-free tests bump the number). Keep the full suite green in the AND so tests stay meaningful. Pair with the **streak gate (#2540)** so a flaky green at 90% doesn't end the loop on a fluke.

## 🔴 Production error sweep

**Use when:** an error tracker / log has a backlog of *actionable, reproducible* errors.
**Backs each pass:** `/ork:fix-issue` · **Convergent signal:** open actionable-error count shrinks toward 0.

```
/goal until [ $(<ERROR_COUNT_CMD>) -eq 0 ] AND <TEST_CMD> passes, or stop after 25 turns
```

**Guardrail:** scope `&lt;ERROR_COUNT_CMD&gt;` to *actionable + reproducible* errors only — filter third-party noise, or the loop chases unfixable errors forever. Never point this at destructive remediation; `/goal` retries, and you don't want retries on data deletion.

## 📚 Docs-drift sweep

**Use when:** generated reference, links, or examples drift from the code.
**Backs each pass:** `/ork:audit-full` (docs lens) or your docs checker · **Convergent signal:** drift checks → all pass.

```
/goal until <DOCS_DRIFT_CMD> passes AND <LINK_CHECK_CMD> passes, or stop after 15 turns
```

**Guardrail:** `&lt;DOCS_DRIFT_CMD&gt;` must be deterministic (e.g. "regenerated reference == committed reference"), never an LLM "are the docs good?" judgement — subjective checks never converge.

## ⚡ Page-load budget

**Use when:** a page exceeds your latency budget (LCP / load time).
**Backs each pass:** `/ork:performance` · **Convergent signal:** metric descends toward budget; the no-progress detector is essential (perf has diminishing returns).

```
/goal until [ $(<LCP_MS_CMD>) -le 2000 ] AND <TEST_CMD> passes, or stop after 12 turns
```

**Guardrail:** keep `no_progress_for_2_turns` tight — perf loops plateau, and you want to stop *at* the plateau, not grind tokens past it. Always AND the test suite so an "optimization" that breaks behavior can't satisfy the budget.

## 🧹 Repository cleanup

**Use when:** memory files, stale branches, or dead state have accumulated.
**Backs each pass:** `/ork:dream` · **Convergent signal:** the dry-run reports nothing to prune (naturally terminating — each pass strictly reduces the stale set).

```
/goal until <DREAM_DRYRUN_CMD> reports 0 stale AND 0 duplicate AND 0 contradiction, or stop after 8 turns
```

**Guardrail:** run dream in **dry-run** inside the `until`-check; let the pass apply the changes. The safest loop here — it converges fast because the stale set only shrinks.

## ✅ Quality streak

**Use when:** you don't trust a single green (flaky suite, race conditions).
**Backs each pass:** `/ork:verify` · **Convergent signal:** a consecutive-pass counter reaches N.

```
rm -f .claude/chain/verify-streak.json   # reset: a stale met:true would exit the loop with 0 fresh runs
/goal until jq -e '.met==true' .claude/chain/verify-streak.json   # run /ork:verify --streak=3 each turn, or stop after 15 turns
```

**Worker:** `/ork:verify --streak=3` — the native streak gate (#2540, now shipped) re-runs the real suite each turn, increments on READY, and zeroes on any red.
**Guardrail:** `/goal` reads the `until`-clause at the *top* of each turn, before that turn's verify — so a `met:true` left by a *previous* completed streak (same scope) would exit immediately with zero fresh runs. The `rm` first line resets the ledger so the loop starts cold. Full race write-up: `verify/references/streak-gate.md` ("Stale-ledger guard").

## 🎫 Ticket → PR-ready

**Use when:** you want an issue taken from open to a CI-green, reviewer-ready PR.
**Backs each pass:** `/ork:fix-issue` → `/ork:create-pr` · **Convergent signal:** PR exists, CI green, links the issue.

```
/goal until gh pr list --head <BRANCH> --json number | jq -e 'length>0' AND gh pr checks <BRANCH> --json state | jq -e 'all(.state=="SUCCESS")', or stop after 20 turns
```

**Guardrail:** do **not** put "merged" in the `until`-clause — merging is a human gate (and ork never auto-closes issues; CI closes them on merge via `Closes #N`). Stop at reviewer-ready.

## 🧼 Type/lint zero

**Use when:** a codebase or migration has a backlog of type/lint errors.
**Backs each pass:** the relevant fixer agent · **Convergent signal:** error counts → 0.

```
/goal until <TYPECHECK_CMD> passes AND <LINT_CMD> passes AND [ $(grep -rc "@ts-ignore\|eslint-disable" src | paste -sd+ - | bc) -le <SUPPRESS_BASELINE> ], or stop after 20 turns
```

**Guardrail:** the suppression-count clause is load-bearing. Without it the loop "wins" by silencing (`@ts-ignore`, `eslint-disable`) instead of fixing — set `&lt;SUPPRESS_BASELINE&gt;` to the current count so it can only go down.

---

## Authoring your own recipe

A recipe is shippable when all four hold (same bar as a `prd-to-goal` line):

- **Convergent** — there is a monotone signal (count → 0, metric → budget, % → target) that the loop drives in one direction. No monotone signal ⇒ no termination.
- **Falsifiable** — every `until` assertion is a shell-checkable boolean, not a judgement.
- **Guarded** — you can name the one way the loop gets gamed, and an assertion that blocks it.
- **Budgeted** — the `, or stop after N turns` bound caps the run, and no-progress. A recipe without a budget is a token fire.

If you can't satisfy all four, the job isn't a loop yet — decompose it with `/ork:prd-to-goal` first.
