Skip to content

How to plan and execute non-trivial work

Turn an approved spec into a locked plan, checked implementation, independent review, and merge-ready change.

Use this when: You are about to start a feature, refactor, or multi-file change that goes beyond a one-line edit. Prerequisites: core pack installed, a working directory with edit/commit/gate access, and familiarity with the four mandatory spec sections; see Prerequisites below. Result: A spec and plan in docs/specs/<feature>/, a green-gates PR, and an adversarial-reviewer-clean diff ready to ship.

You have a feature to build, a refactor to drive, a migration to run — anything past a one-line edit. This guide walks the path from “I’m about to start” through “the PR is green and the reviewer is clean”, using the two skills that drive it: new-spec and work-loop.

Use the new spec skill to design webhook retries, then use work-loop to implement it.

For the why behind this discipline, read the core pack as a system. This guide is task-oriented; it tells you what to type and what to expect back.

  • The core pack installed in your target repo.
  • A working directory where you can edit, commit, and run gates (lint / typecheck / test).
  • Familiarity with the four mandatory spec sections (Objective, Boundaries, Testing Strategy, Acceptance Criteria) — see docs/CONVENTIONS.md.

Two skills, one workflow:

SituationSkill to invoke
New feature or significant change — no spec yetnew-spec, then work-loop
Spec already exists in docs/specs/<feature>/work-loop (it reads the spec)
Multi-file bug fixbug-fix — see how to fix a bug
One-line edit, typo, single config tweakSkip the loop — overhead isn’t worth it

If you’re unsure whether the change is trivial, default to the loop. The cost of running it on a small task is one extra minute; the cost of vibe-coding a non-trivial task is a re-do.

Step 1 — Run new-spec (when no spec exists)

Section titled “Step 1 — Run new-spec (when no spec exists)”

The skill writes docs/specs/<feature>/spec.md and plan.md, and gates body content on assumption sign-off.

Name the skill in your invocation. Two worked invocations — one backend, one frontend:

use the new-spec skill to design webhook retries with exponential
backoff for the payment-events stream

(retry logic is a state machine — Testing Strategy will pick TDD for this one.)

use the new-spec skill to spec the saved-filters chip on the
product-search results page

(the chip is a visible UI element — Testing Strategy will pair it with visual / manual QA, possibly automated.)

Natural phrasings (let's spec out X, new spec: Y, write a spec for Z) match the skill’s description and often trigger it, but description matching isn’t guaranteed. Lead with use the new-spec skill to … whenever you want the discipline to fire reliably.

new-spec ingests whatever input you give it and surfaces what’s missing. Any of these shapes work:

  • A one-line idea (“add 2FA for admin login”).
  • A requirements doc or PRD pasted into the message.
  • A linked ticket — Linear, Jira, GitHub issue. Reference the URL; the skill reads what’s there.
  • An upstream brief from a portfolio team (Feature Intent, Component Brief, or whatever shape your org produces).
  • A bug report that’s grown into a feature (“this thing should work differently”).

The skill doesn’t care which shape you brought. The assumption checkpoint is where missing information gets named, regardless of input shape — so an idea-shaped input surfaces more Unverified items, a brief-shaped input surfaces fewer. The output is the same: a spec and plan grounded in confirmed assumptions.

  1. The skill scaffolds docs/specs/<feature>/spec.md and plan.md from the bundled templates.
  2. It drafts assumption candidates across Technical / Product / Process categories, runs one targeted check per candidate (a repo read, a web lookup, or a read-only probe), then stops and emits an ASSUMPTIONS I'M MAKING: block split into Verified (each with a one-line citation of the check) and Unverified (each needing your input). The check happens before the bullet gets filed, not after.
  3. You read the Unverified list and confirm or revise. If the Unverified list is empty, the skill surfaces the Verified list with the highest-stakes item called out and asks you to confirm that one specifically — a vague “looks good” doesn’t count.
  4. Spec body fills in: Objective, Boundaries (including at least one structural Never do), Testing Strategy with a verification mode per outcome, Acceptance Criteria.
  5. Plan body fills in: tasks with Tests: before Approach:, explicit Depends on:, verification mode per task.
  6. shaping-reviewer first reads the draft contract cold for scope and observability. adversarial-reviewer then reads the complete spec and plan for construction risk; security-reviewer and quality-engineer remain later code-review lenses, each owning concerns the others do not repeat.
  7. Every adversarial-reviewer report is persisted first, then classified from the persisted bytes. A report that is clean — by exact bytes, or by structure with nothing but blank lines around the clean sentence — closes the round without an adjudicator call. A report carrying findings, or security-reviewer’s coverage-disclosure footer, goes through finding-adjudicator before the author classifies it or edits either artifact. Only sustained findings can change the spec or plan; refuted findings stay in the audit, and an indeterminate result stops for your direction.
  8. Before each repair, the author marks the finding draft-origin or prior-round-repair. If the review history cannot establish either one, unresolved origin stops for your direction. When a green gate supports a disposition, the author also states what that gate proves and one relevant blind spot; the spec-status lint’s own module contract remains the source for its scope.
  9. Review iterates to direct or adjudicated clean — usually one to two passes. If it cannot reach clean in three, the skill stops and asks for human direction; the spec likely has a structural problem, not a wording one.
  10. The skill updates docs/specs/README.md and reminds you that spec drift is a bug — update the spec in the same PR when implementation diverges.

If you want to stop here (pure planning, no build yet), this is the natural exit point. The spec and plan are durable; come back to work-loop whenever you’re ready.

With a spec in place, invoke the loop by name. Same two domains carried through:

use the work-loop skill to implement docs/specs/webhook-retries

(TDD-mode tasks for retry logic; goal-based for wiring.)

use the work-loop skill to drive the saved-filters-chip spec

(visual / manual QA on the chip; goal-based on the URL-state plumbing.)

Naming the skill is the reliable form. The work-loop description also matches phrasings like “implement the X spec” or “let’s work on Y”, but the explicit form fires the loop’s full discipline — gates, adversarial review, stasis detection, the state machine — even on edges where description matching wouldn’t pick it up automatically.

The full procedure lives in the work-loop SKILL.md. The short version:

  • PLAN — reads spec.md and plan.md, picks verification modes if not already set, and designs construction tests up front. For a TDD task, it stores the exact stub code in plan.md and validates syntax plus the intended red from disposable scratch; it does not create a repository test file. A spec-plan run can therefore stop after approval with clean gates. PLAN also asks you to name the declined-pattern register: one to three things you were tempted to add (a layer, a flag, a defensive wrapper) and explicitly declined. The register pairs with the spec’s Boundaries section so REVIEW can catch drift toward declined temptations as self-contradiction in the diff. Pre-EXECUTE adversarial review fires automatically on spec amendments or on any of the four structural triggers (new module, new dependency, new abstraction, new top-level directory).
  • EXECUTE — implements task by task. Once full mode enters CODE-IMPLEMENTATION, a TDD task copies the approved plan block unchanged into the real test path, verifies byte identity, proves the intended red, and continues through green and refactor. Goal-based tasks run the Done when: one-liner; manual-QA tasks record the visual check.
  • GATES — lint, typecheck, tests. Mechanical, ordered, no editing the gate to make it pass.
  • REVIEWadversarial-reviewer reads the diff cold against AGENTS.md + CONVENTIONS.md + spec.md. Findings come back as Blockers / Concerns / Nits with one-sentence fixes. The loop records each pass’s finding fingerprints to state.json via loop-cohort review record, which is what enables stasis detection in the next phase. Pass --operation-id <run-id>:<transition-sequence> — read the sequence from loop-engine status after the transition that opened the round — and the round is named on disk. Re-issuing the identical recording under that same id is then a no-op rather than a second round, so a session that dies before it knows whether the write landed can simply repeat it. A different payload under a used id is refused rather than silently accepted. Specialist reviewers (security-reviewer, quality-engineer) run when the diff warrants.
  • DECIDE — intent fit decides each finding: in-intent work that cannot share this unit becomes the next review unit in the same session; excluded work is acknowledged in the PR and captured only if its owner asks. Stasis detection fires if the same findings come back two iterations in a row — stop and surface to a human rather than spinning a third pass.

Two no-stub records are closed exceptions. Use no stub (mode) when the chosen verification mode is not TDD. Use no stub (implementation-discovered) only when the callable seam genuinely cannot be known before implementation, and record both its discovery predicate and proof obligation. Neither record is a reason to repeat the acceptance criterion as behavior prose.

For the end-to-end narrative with the parts in context, read core-pack.md § How they tie together.

  • Gates green and review clean → ship.
  • loop-cohort.py check exits non-zero → the script tells you which cap fired (implementation retry cap or review retry cap). Read the message. A cap firing means the loop stopped converging, so the default answer is to reset and start a new run, not to push past it. The review cap alone has an escape hatch — --allow-retry-cap-override, which must be passed to both loop-engine.py transition findings-remain and the matching loop-cohort.py review record, since either half alone leaves the engine and the cohort a round apart. It is for a human who has looked at why the loop is not converging and decided to continue anyway; an agent running unattended should stop and surface the cap instead. The implementation cap has no override by design.
  • Diff is shrinking but findings aren’t → you’re spot-fixing without addressing root cause. Back to PLAN.

If any of these fire and the work isn’t done, the task is bigger than you thought. Re-plan rather than expanding scope silently.

This how-to has two exits, and they end in different places.

If you stopped after PLAN — you have an approved spec and plan at docs/specs/<feature>/, both carrying your explicit approval. Nothing has been implemented. Start work-loop when you are ready, or return to the spec when the intended outcome changes.

If you ran the loop through — you have the change implemented against that spec, every mechanical gate green, the adversarial reviewer and any triggered security or quality reviewer reporting clean, findings resolved or explicitly dispositioned, the spec marked Shipped and the plan Done, and a merge-ready PR. The merge decision is yours; the loop does not take it.

work-loop reads docs/specs/<feature>/ and picks up from both engine-state.json (FSM phase) and state.json (execution counters and wave progress). Phrase as “resume the X work” or “continue on docs/specs/X”. The Session Resumption protocol in the skill reads loop-engine status --json first, then loop-cohort status --json, and routes by last_event. On a review round it also compares last_review_record_operation_id against the id it would compute now: equal means the round was recorded and the loop moves on, and anything else means it was not.

Both files are gitignored session-scratch — on a fresh checkout (new machine, after git clean, a teammate’s box), the loop re-initializes with engine first: loop-engine init --json produces the run_id; loop-cohort init <spec-dir> --run-id <run_id> registers it. Any other init order causes the engine’s identity preflight to reject every subsequent transition.

Once approve-plan runs (during the G-plan sequence after the plan approver fires plan-approved), plan check-current --require-schedule guards the subsequent plan-locked transition against the approved plan hash. A post-approval plan.md edit will cause the plan-locked transition to be rejected — the approved plan is immutable in substance. If EXECUTE discovers a fundamentally missing or wrong task, surface that to a human and start a new run rather than editing in place. Safe post-approval edits are the ones the loop itself mandates plus pure formatting: the preamble **Status:** token, checkbox bookkeeping ([ ][x]), trailing whitespace, and line endings are all normalized out of the hash. Anything else — task text, a Depends on: edge, re-indenting a criterion, or free text appended after the status token — invalidates it. Record an execution observation through the verification-ledger procedure.

Parallel fan-out (dispatch-decision, worktree, auto-parallel) is disabled in Phase 1 — those verbs exit non-zero without touching state.json. The full parallel-dispatch design (opt-in, gated, wave-scheduled) is documented in references/supervisor-mode.md for future phases.

When an implementer subagent is installed, the controller dispatches each plan task sequentially, one implementer at a time, using the schedule order.

  • Genuine one-line edits — typo, config tweak, copy-paste fix. The loop’s overhead exceeds the work.
  • Spikes and throwaway exploration. If the output is going to be thrown away, the spec / plan / review machinery adds friction for no gain. Mark the work explicitly as a spike and skip the loop.
  • Investigation before implementation. If you don’t yet know what to build, you’re not ready for new-spec. Investigate first; come back with a question shaped like a feature.

For bug-shaped work that crosses multiple files, see how to fix a bug — the bug-fix skill is the right entry point.