Plan and Build: Feature Request to EARS Spec and Tested Code
WorkflowTurns a feature request into an EARS behavioral spec with testable requirements, stops with clarifying questions when anything is unresolved, then implements it in a developer/evaluator loop whose independent evaluator re-runs the checks, proves every requirement's test fails on the old code and passes on the new, and rejects tampered tests before summarizing the change.
Usage
echo "<your request>" | octomind workflow plan-and-build Reads your request from stdin. Add --dry-run to validate and print the plan without
running any steps.
Pipeline
-
<request> {{input}} </request> This is an AUTONOMOUS, non-interactive run — no human can answer questions. Write the spec for the request above, scoped to the change actually requested, then output the complete spec.md …
-
<spec> {{spec}} </spec> The spec above ended NEEDS CLARIFICATION — no implementation was attempted. Extract its open questions into a short numbered list the user can answer to unblock the build, then stop. Start with e…
- 3 build Loop
- developer developer:general
<spec> {{spec}} </spec> Implement the spec above end to end in the current project. Touch only what it requires, treat its Guardrails as hard constraints, and stop and report if a Guardrail would have to break. Prove ev…
- brief developer:brief
<spec> {{spec}} </spec> Read the spec above and the current unstaged git changes, then brief what the diff actually does versus what each FR asks for. Output only the brief.
- evaluator developer:general
<spec> {{spec}} </spec> <evidence> {{developer}} </evidence> <brief> {{brief}} </brief> You are the independent evaluator, in a fresh session. Trust nothing you did not run yourself: the developer's evidence and the bri…
-
<spec> {{spec}} </spec> Read the spec above and the implementation in the current unstaged git changes, then produce a concise spec-versus-diff briefing for the human reviewer. Output only the briefing.
-
<spec> {{spec}} </spec> <last_evaluation> {{evaluator}} </last_evaluation> The build loop ended WITHOUT evaluator approval — do not present this as success. Read the spec and the current unstaged changes, then report fo…
Definition
# Title: Plan and Build: Feature Request to EARS Spec and Tested Code
#
# Public workflow: turn a request into a behavioral spec, then implement that
# spec through a developer/evaluator loop that proves every requirement with a
# named, passing test. Graph routing: a spec that still needs clarification
# never reaches the build.
#
# Shape:
# spec (developer:spec, autonomous, spec-authoring skill preloaded — its
# validator gates the spec when [skills] auto_validation is on) ->
# SPEC STATUS: NEEDS CLARIFICATION -> blocked (questions for the human) -> end
# SPEC STATUS: READY -> build loop (developer -> brief -> evaluator, max 3) ->
# VERDICT: APPROVED -> summary -> end
# cap reached or VERDICT: BLOCKED -> stalled (report for a human) -> end
#
# Requirement contract: the spec gives every requirement a stable ID (FR-001…)
# and proves each in its Acceptance section. The developer maps each FR to the
# test that proves it. The evaluator trusts nothing it did not run: it re-runs
# the verification itself, runs the mapped tests against the pre-change code
# (each must fail there — fail-to-pass), scans the test diff for tampering, and
# checks the Guardrails mechanically against the diff.
#
# Revert contract: the fail-to-pass check swaps changed source files back to
# HEAD in place, so the evaluator never uses git checkout/restore/reset/stash
# (the tree holds uncommitted work). It copies each file to a scratch directory
# first, restores from that copy, and compares sha256 hashes; a mismatch ends the
# loop with VERDICT: BLOCKED. The evaluator has no timeout and no retries on
# purpose: killing or re-running it mid-swap could leave old code behind.
#
# Tip: set `model` on the evaluator to a different provider than the developer
# — LLM judges favor output from their own model. Public roles only.
name = "plan-and-build"
description = "Turns a feature request into an EARS behavioral spec with testable requirements, stops with clarifying questions when anything is unresolved, then implements it in a developer/evaluator loop whose independent evaluator re-runs the checks, proves every requirement's test fails on the old code and passes on the new, and rejects tampered tests before summarizing the change."
entry = "spec"
max_transitions = 4
# ── 1. Spec: request → behavioral spec ───────────────────────────────────────
[[steps]]
name = "spec"
role = "developer:spec"
session = "fresh"
skills = ["spec-authoring"]
prompt = """
<request>
{{input}}
</request>
This is an AUTONOMOUS, non-interactive run — no human can answer questions.
Write the spec for the request above, scoped to the change actually requested,
then output the complete spec.md content, ending with its SPEC STATUS line.
"""
# ── 2a. Blocked: the spec still needs a human ────────────────────────────────
[[steps]]
name = "blocked"
role = "developer:brief"
session = "fresh"
prompt = """
<spec>
{{spec}}
</spec>
The spec above ended NEEDS CLARIFICATION — no implementation was attempted.
Extract its open questions into a short numbered list the user can answer to
unblock the build, then stop.
Start with exactly: `BLOCKED ON CLARIFICATION — no changes made.`
"""
# ── 2b. Build loop: developer ↔ evaluator ────────────────────────────────────
[[steps]]
name = "build"
loop = true
max_iterations = 3
exit_when = { output = "evaluator", matches = '(?m)^VERDICT: (APPROVED|BLOCKED)' }
[[steps.run]]
name = "developer"
role = "developer:general"
session = "continue"
prompt = """
<spec>
{{spec}}
</spec>
Implement the spec above end to end in the current project. Touch only what it
requires, treat its Guardrails as hard constraints, and stop and report if a
Guardrail would have to break. Prove every requirement with a test: each FR and
each Acceptance item maps to a test that exercises it. An independent evaluator
will run every mapped test against the pre-change code, where each test for new
or changed behavior must fail — so assert the behavior itself, never the
implementation's own output. Never skip, delete, or loosen a test to get green.
Run the spec's verification command and iterate until it passes. End with:
- a requirement map, one line per FR: `FR-001 → <test name>` (or `manual: <check>`
only where the spec's Verification says no command can prove it);
- the exact command you ran and its output as evidence.
Output only the work summary, the map, and the evidence — no preamble.
"""
[[steps.run]]
name = "brief"
role = "developer:brief"
session = "fresh"
prompt = """
<spec>
{{spec}}
</spec>
Read the spec above and the current unstaged git changes, then brief what the
diff actually does versus what each FR asks for. Output only the brief.
"""
[[steps.run]]
name = "evaluator"
role = "developer:general"
session = "fresh"
prompt = """
<spec>
{{spec}}
</spec>
<evidence>
{{developer}}
</evidence>
<brief>
{{brief}}
</brief>
You are the independent evaluator, in a fresh session. Trust nothing you did not
run yourself: the developer's evidence and the brief are leads, not proof. Run
these checks in order, by requirement ID.
A. VERIFY — run the spec's verification command yourself, in full. It must pass
with no newly skipped or ignored tests. If it fails, report it and skip C.
B. MAP — every FR and every Acceptance item (Scenario, Property, Check) maps to a
test in the developer's map, and that test exists and exercises the stated
behavior. `manual:` is acceptable only where the spec's Verification says no
command can prove it.
C. FAIL-TO-PASS — prove the mapped tests have teeth against the pre-change code:
1. Record `git status --porcelain` and the sha256 of every modified or added
non-test file; copy each one to a scratch directory (`mktemp -d`).
2. Swap in the pre-change code, leaving test files alone: overwrite each
modified file with `git show HEAD:<path>`, and move each added non-test
file into the scratch directory. Never use git checkout, restore, reset, or
stash.
3. Run only the mapped tests. Each test for an ADDED or MODIFIED requirement
must fail — an assertion failure, or a compile error because the behavior
is missing. Each invariant test must still pass. Name every test that
passes on the old code: it proves nothing.
4. Restore every file from the scratch copy, then confirm the sha256 values
and `git status --porcelain` equal step 1. If they do not, restore again;
if they still do not, stop with `REVERT-FAILED: <path>` and VERDICT: BLOCKED.
D. TAMPERING — read the test-file diff. Fail on deleted or renamed-away tests;
new skip, ignore, or only markers; assertions removed or loosened; the unit
under test mocked out; expected values copied from the implementation's own
output; and any source branch that recognizes a test environment or specific
test inputs.
E. GUARDRAILS — compare `git status --porcelain` and `git diff --stat` against
the spec: no file outside its scope, no new dependency in a manifest or
lockfile when the spec forbids one, no protected surface changed.
List every failing check with specifics; this goes straight to the developer. If
A–E all pass, approve — do not invent criteria the spec never stated. A revert
failure is BLOCKED, never REVISE.
End with exactly one line: `VERDICT: APPROVED`, `VERDICT: REVISE`, or
`VERDICT: BLOCKED`. Nothing after it.
"""
# ── 3a. Summary: approved ────────────────────────────────────────────────────
[[steps]]
name = "summary"
role = "developer:brief"
session = "fresh"
prompt = """
<spec>
{{spec}}
</spec>
Read the spec above and the implementation in the current unstaged git changes,
then produce a concise spec-versus-diff briefing for the human reviewer.
Output only the briefing.
"""
# ── 3b. Stalled: loop hit its cap without approval ───────────────────────────
[[steps]]
name = "stalled"
role = "developer:brief"
session = "fresh"
prompt = """
<spec>
{{spec}}
</spec>
<last_evaluation>
{{evaluator}}
</last_evaluation>
The build loop ended WITHOUT evaluator approval — do not present this as success.
Read the spec and the current unstaged changes, then report for a human decision:
what was implemented and verified, and every gap the evaluator still flags.
If the last evaluation ends with `VERDICT: BLOCKED`, start with exactly:
`BLOCKED — source files may not match the developer's last state; check git status before anything else.`
Otherwise start with exactly: `DID NOT CONVERGE — human review required.`
"""
# ── Graph routes ─────────────────────────────────────────────────────────────
# Anything other than an explicit READY falls through to `blocked` — fail safe.
[[edges]]
from = "spec"
to = "build"
when = { matches = '(?m)^SPEC STATUS: READY' }
[[edges]]
from = "spec"
to = "blocked"
[[edges]]
from = "blocked"
to = "$end"
[[edges]]
from = "build"
to = "summary"
when = { output = "evaluator", matches = '(?m)^VERDICT: APPROVED' }
[[edges]]
from = "build"
to = "stalled"
[[edges]]
from = "summary"
to = "$end"
[[edges]]
from = "stalled"
to = "$end"