Plan and Build: Feature Request to EARS Spec and Tested Code

Workflow

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.

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

  1. 1 spec Sequential developer:spec

    <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 …

  2. 2 blocked Sequential developer:brief

    <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. 3 build Loop max 3×
    • developer developer:general continue

      <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 fresh

      <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 fresh

      <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…

  4. 4 summary Sequential developer:brief

    <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.

  5. 5 stalled Sequential developer:brief

    <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"