Spec to Code skill for Codex
Every requirement traced to code and a test. Codex stops treating a ticket as a loose suggestion. It splits the spec into numbered must, should and may requirements, logs ambiguities with proposed defaults, writes a test named after each requirement, and finishes with a table that links every requirement to the exact code and test that satisfy it, plus assumptions, deferrals and deviations stated plainly.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- spec-to-code Installs to ~/.agents/skills/spec-to-code/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install spec-to-code The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project spec-to-code Installs to .agents/skills/spec-to-code/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/spec-to-code.zipunzip -o spec-to-code.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) spec-to-code PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $spec-to-code, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Implements a feature faithfully from a written spec, ticket, PRD or issue by first breaking it into numbered, testable requirements, logging every ambiguity and assumption, mapping each requirement to code and a test in a traceability table, and reporting gaps instead of silently inventing behaviour. Use when asked to implement a Jira or Linear ticket, a GitHub issue, a design doc section, an RFC, an API spec or acceptance criteria, or when a reviewer says the code does not match the spec.
Use it when
- You have a written ticket, issue, PRD or RFC section to implement
- Acceptance criteria must be demonstrably met before review
- A reviewer or QA reports the code drifted from the spec
- The spec is long enough that requirements could be missed
Use something else when
- There is no spec yet and you need to write one: use feature-specs
- You need a staged plan for a large change before any code: use implementation-planning
- You are writing the acceptance criteria themselves: use acceptance-criteria
How to check the result
Every mandatory requirement has a row with a file reference and a named test that passes, and every file in the diff maps to a requirement, an assumption or a stated supporting change.
The mistake it prevents
Coding the spec's example values instead of its rule, or quietly narrowing a requirement that was awkward to implement without reporting it.
Done well means every statement in the spec that demands behaviour maps to code and to a test that would fail without it, every gap was raised as a question or recorded as an explicit assumption, and nothing outside the spec was added without saying so. The deliverable is the code plus a traceability table, not just the code.
Workflow
- [ ] 1. Collect the spec and everything it points to
- [ ] 2. Read AGENTS.md and the code the spec touches
- [ ] 3. Extract numbered requirements (R1..Rn) and non-goals
- [ ] 4. Log ambiguities; ask about blocking ones before coding
- [ ] 5. Write tests per requirement (failing first where practical)
- [ ] 6. Implement in requirement order, smallest vertical slice first
- [ ] 7. Fill the traceability table and run the full check suite
- [ ] 8. Report: done, assumed, deferred, deviated
1. Collect the whole spec
A ticket is rarely the whole spec. Pull in what it references before reading closely:
gh issue view 812 --comments # GitHub issue plus discussion (needs network)
gh pr view 790 --json body,comments # linked design PRs
For Jira or Linear tickets without a CLI, ask the user to paste the description, the acceptance criteria and any comments that changed scope. Comments often override the description; the most recent decision from the ticket owner wins. Note mockups, API schemas and linked docs you could not open; they are gaps, not optional extras.
2. Read the ground before reading the spec closely
AGENTS.md: test command, layering rules, naming, where new modules go.- The existing code paths the spec names. Specs describe intent in product language;
find the real entities (
rg -n "class Invoice|invoice_status" src/). - Existing tests for the area: they show what “done” has meant here before.
A spec that contradicts the code (“users already have a timezone field” when they do not) is a finding to raise now, not something to quietly fix by adding the field.
3. Extract requirements
Rewrite the spec as atomic, testable statements. One behaviour per line, numbered, with the source quoted or located so a reviewer can check your reading.
| Spec language | Requirement type | Treatment |
|---|---|---|
| “must”, “shall”, “will”, “is required” | Mandatory | Requirement with a test |
| “should”, “ideally”, “where possible” | Preferred | Implement if cheap and clear; otherwise list as deferred |
| “may”, “can”, “optionally” | Permitted | Do not build unless asked |
| Example values, screenshots | Illustrative | Use as test cases, not as the full rule |
| “e.g.”, “such as”, “etc.” | Open list | Ask for the full list or record which members you covered |
| Silence on errors, limits, permissions | Gap | Log as ambiguity |
Also write the non-goals: things the spec explicitly excludes and things a reasonable reader might expect but the spec does not mention. Scope creep starts with an unlisted non-goal.
4. Ambiguity log
Hunt for these gaps deliberately; they are where implementations diverge from intent:
- Empty, null, zero, maximum and duplicate inputs.
- Error behaviour: what the user sees, what status code, whether anything is retried.
- Permissions: who may do this, and what happens for everyone else.
- Time: time zones, “daily” boundaries, DST, ordering of concurrent updates.
- Existing data: does the rule apply retroactively? Is a migration or backfill needed?
- Compatibility: do existing API clients, events or exports change shape?
- Numbers: rounding mode, currency precision, units, inclusive vs exclusive ranges.
Classify each entry:
| Class | Rule |
|---|---|
| Blocking | Different answers lead to different data models or public contracts. Stop and ask before coding |
| Non-blocking | Choose the most conservative option, mark it ASSUMPTION in the log, isolate it so it is cheap to change |
| Cosmetic | Follow existing conventions; no need to mention |
Ask blocking questions in one batch, each with your proposed default, so the answer can be “yes to all”:
Before I implement #812 I need three decisions:
1. R4 says "refund the order". Partial refunds: allowed? Proposed: full only for now.
2. R6 "notify the customer": email only, or also in-app? Proposed: email via existing Mailer.
3. Existing 1,240 orders in `pending_review`: apply the new 14-day rule to them? Proposed: no.
5. Tests per requirement
Write at least one test per mandatory requirement, named so the requirement is visible:
def test_r3_refund_rejected_after_30_days(order_factory, clock):
order = order_factory(paid_at=clock.now() - timedelta(days=31))
with pytest.raises(RefundWindowClosed):
refund(order)
- Turn the spec’s examples into table-driven cases verbatim; they are the cheapest evidence that you read the spec the way its author meant.
- Add boundary cases the spec implies (day 30 vs day 31) even when it gives no example.
- For acceptance criteria in Given/When/Then form, keep the same structure in the test body so a product owner can read it.
6. Implement
- Order by dependency, then by risk: data model and contracts first, UI last.
- Ship the thinnest vertical slice that satisfies R1 end to end before widening.
- Keep assumptions in one obvious place (a constant, a config value, a policy function)
with a comment pointing to the log entry:
# ASSUMPTION A2 (#812): full refunds only. - Do not refactor neighbouring code, rename things or “improve” behaviour the spec did not ask for. If you find a real defect nearby, report it separately.
- If the spec turns out to be wrong (impossible, contradicts itself, breaks an invariant), stop and report with evidence rather than implementing a workaround.
7. Trace and verify
Run the project’s full checks from AGENTS.md (tests, types, lint). Then fill the table
by actually locating each item, not from memory:
rg -n "R[0-9]+" tests/ | sort # requirement-named tests
git diff --stat origin/main...HEAD # every changed file appears in some row
Every changed file must belong to a requirement, an assumption or a stated supporting change. A changed file with no row is scope creep or a missing requirement.
Decision rules
- Spec and existing behaviour disagree, and the spec is explicit: follow the spec, call out the behaviour change in the report.
- Spec is silent and existing behaviour exists: keep existing behaviour.
- Spec and design mockup disagree: ask; meanwhile follow the text for logic and the mockup for layout.
- A requirement is untestable as written (“fast”, “intuitive”): propose a measurable version (“p95 < 300 ms on the list endpoint”) and treat it as an assumption.
- More than about a third of requirements have blocking questions: the spec is not ready. Return the question list instead of a partial implementation.
Gotchas
- Implementing the example instead of the rule: the spec shows “USD 10.00” and the code hardcodes two decimals, breaking JPY.
- Gold-plating: extra filters, settings or endpoints nobody asked for still need review, tests and maintenance.
- Silent narrowing: implementing “admins and owners” as “admins” because owners were awkward, and never mentioning it.
- Treating ticket comments as noise. A later comment like “drop the CSV export for v1” is part of the spec.
- Marking a requirement done because code exists, while no test exercises it.
Output template
## Implementation of <ticket id>: <title>
### Requirements traceability
| ID | Requirement (source) | Code | Test | Status |
|---|---|---|---|---|
| R1 | Refund allowed within 30 days of payment (desc, para 2) | `billing/refunds.py:41` | `test_r1_refund_within_window` | Done |
| R2 | Customer is emailed on refund (AC-3) | `billing/notify.py:18` | `test_r2_refund_email_sent` | Done |
| R5 | Should support partial refunds (desc, para 4) | - | - | Deferred: needs decision Q1 |
### Assumptions
- A1: Window counted in UTC calendar days from `paid_at`. Change in `REFUND_WINDOW` policy.
### Open questions
- Q1: Partial refunds in scope for this ticket?
### Deviations from spec
- None. (Or: what differs, why, and who agreed.)
### Out of scope, noticed
- `orders/export.py` rounds totals with float math; unrelated, not changed.
### Verification
- `pytest tests/billing` 48 passed; `mypy src` clean; `ruff check` clean. What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "Spec to Code"
short_description: "Every requirement traced to code and a test"
default_prompt: "$spec-to-code Implement issue #812 exactly as written and show me how each acceptance criterion is covered" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.