plan-implement
Implement an approved implementation plan (a `*.plan.json`, RFC 0030) one derived step at a time — `plan:next` names the step and what it covers, you implement exactly that, `plan:verify` records the
SKILL.mdpackages/cli/templates/agent/core/skills/plan-implement/SKILL.md
GitHubPlan Implementation Skill
You implement a plan the way Guren derives it: step by step, in task order, with completion read from the code rather than reported by you.
The API rules in
__RULES_DIR__/(orm-models, controllers-http, routes-codegen, testing) and their full references in__SKILLS_DIR__/guren-api/reference/carry the verified signatures for what each step writes.
The loop
bunx guren plan:next docs/plans/<slug>/plan.json # the next step and its context (--json for the structure)
# implement exactly that step
bunx guren plan:verify docs/plans/<slug>/plan.json --step <id>
git commit # one step, one commit; name the step id in the message
Repeat until plan:next reports that every step is verified, or that no step
can be returned because every step left is held.
plan:next prints one step: its elements, the acceptance behaviours it must
write or see pass, and the verify commands. It never prints the whole plan, and
it refuses a working tree with uncommitted changes that are not the marked
step's own: finish or discard them first.
plan:next, plan:scaffold, plan:verify and plan:waive refuse a plan whose current hash no
approval names: one edited after it was approved, or one nobody approved. The
Stop hook stalls the marked step on it. Approving is the person's call, like
closing: report the refusal and wait. Do not run plan:approve yourself, and do
not edit the plan to get past it.
For an approved plan it also holds a step whose context changed after
approval: an element the step owns or names that another commit moved, so the
plan does not describe the application there. It lists each held step
with the element, how the step depends on it and what the reference checks say
now, and returns the next step that does not depend on one. A held step is a
person's decision (undo the change, or revise the plan with plan:revise so
each stale element states what the application holds now, such as an
existing action another commit renamed, and approve the revision): report
it, and do not edit the application back or the plan to make it pass.
What a step asks for
scaffold: run the commandplan:nextnames,bunx guren plan:scaffold <plan> --step <id>, and do not hand-write what it emits. It writes, for each model the step adds:- the table in
db/schema.ts, with every column option and foreign key the plan states; - the model class, with the plan's relationships and fillable;
- the step's validators, in
app/Http/Validators/<Model>Validator.ts; - each resource of the model, its payload typed as planned;
- each policy of the model, every ability denying until written, and an
app/Providers/<Policy>Provider.tsregistered increateApp()that hands the policy to the gate; - each added controller, with exactly the planned actions: each validates and authorizes as planned, then answers 501;
- the routes to those actions in
routes/<collection>.ts, which is not mounted yet; - the side-effect classes (jobs, events, listeners, mails, notifications).
Anything the step lists under "does not write" is the
httpstep's, by hand. It runs no codegen and no migration. A relationship it reports as left out is added in the step where what it needs exists; until thenplan:statusreads the model as drifted. It also lists what it wrote as a stub or not at all: validator rules, resource fields that throw, every action's response, and middleware other thanauth. Those arehttpwork too.guren checkreports the unmounted routes file as advisory until thehttpstep that mounts it is verified. It refuses a re-run once the step's files exist, and thenplan:verifyis what is left.- the table in
tests: run the commandplan:nextnames,bunx guren plan:scaffold <plan> --step <id>. It writes oneTestApptest per behaviour intests/plans/<plan>/<collection>.test.ts, titled with the acceptance id literally ([AC-comments-1] a signed-in user can comment on a post), with the request its route names and the expectations the plan states. Replace eachgiven()call (the setup, the signed-in actor, a path parameter) and eachunwritten()call with what it names, keep every title's id and request, and leave the tests failing. The file'sbeforeAllboots the app, so abeforeEachthat creates or clears rows runs against a configured database; open it withawait ready(), so a failed boot, not a database error, is what each test reports. The step verifies withtests:fail, so a test that already passes, or is skipped (test.skip,test.todo), fails the step. Each test must still request its behaviour's route through aTestApp, with a path it spells, in its own body or a function of the same file it calls:plan:verifyreads the requests before it runs the tests, and fails one that requests another route, nothing, or something it cannot read (a path the file does not spell, a request made by a helper imported from another file, or one on what a helper of the same file returns when nothing annotates its return type asTestApp). A verified run records each behaviour as seen failing, keyed on its test as the plan states it (everything but the description, with the route's method and path). That observation is kept: once the implementation exists no run can make it again, so a laterplan:verifyof the step does not ask a behaviour it covers to fail again.data: the schema, migration and model; verified bydb:migrateandtypecheck. After a scaffold step, what is left is the migration and any relationshipplan:scaffoldreported as left out. One it reported with a later step (judgedAt) belongs to that step:plan:nextlists it there, under the relationships of earlier models the step completes, and you write it in the declaring model's file then.http: controllers and routes (and validators, resources and policies when no scaffold step wrote them), untilguren checkpasses and the step's behaviours pass. After a scaffold step, first run the commandplan:nextnames,bunx guren plan:scaffold <plan> --step <id> --mount, which calls the scaffolded routes file from the entry registrar; do not mount it by hand. Then replace each action's 501 with its body and planned response, write each policy ability's rule in place of itsreturn false, map each resource fieldplan:scaffoldstubbed, and add the validator rules and middleware it listed. Keep anauthorize()call's[Model, record]form (a bare ORM record resolves no policy), and pass the foreign keys an action's comment lists throughcreate(data, { set }). An action is complete only when its route is mounted and validates through the route contract. A task's lasthttpstep also runstypecheck, over earlier parts too. A page an action renders that thepagesstep adds is not in.guren/pages.gen.tsuntil its file exists, soplan:nextlists those pages: create each as a stub with a default export and the plan'sProps, and leave the rest topages.pages: the Inertia pages; verified bytypecheckandguren check.
Implement only the elements the step lists, and the page stubs plan:next
names for an http step. An element of a later step is that step's work, and plan:status will read it as drifted from the plan if it lands
elsewhere. Do not edit the plan file: a change of design is a revision
through plan:revise (see the plan-write skill).
What plan:verify records
The verdict is verified, failed, blocked or incomplete, under
.guren/plans/<slug>.state.json (git-ignored). blocked is the environment's:
a script the app lacks, a database that is unreachable, a timeout. It is not a
failure of the implementation, and it is not yours to route around: say what is
blocked and stop.
After a revision
An approved revision gives the plan a new hash, and no record of the parent
hash counts under it, so every step comes back. plan:next says so for a step
verified against an earlier hash: re-check it with plan:verify --step <id>
before implementing anything, or run plan:verify without --step once for
the whole plan. Implement only what that run reports against the revised plan.
A tests step verifies again without a run for each behaviour whose test the
revision left as it was, since it was seen failing before its implementation.
A behaviour the revision changed (its expectation, input, setup, actor or
route; not its description) has to be seen failing again: update its test to
the revised plan first, and it fails against the code that implements the old
one. Its http step then asks for the new implementation.
A tests step has no such record when it was verified by a CLI older than
this rule, or when its test passed before it ever failed, as it does when an
earlier step already mounted the routes its behaviours request. Its behaviours
already pass, so it cannot verify: the Stop hook gives up on it at once, and
plan:next keeps returning it. It owns no element, so plan:close does not
wait for it. Report it and leave the close to the person. A task's actions and
the routes to them come after its tests step, so do not mount a route a
later task owns to finish an earlier step.
The Stop hook
While a step is marked, the Stop hook verifies it whenever you end a turn and
sends you back while it is not verified, up to three times. It gives up, and
says why, when something the step names went stale since approval, when the
step or one of its elements is blocked, when a tests step's behaviours
already pass and no record saw them fail, when nothing changed
since the last continuation, or after the third continuation. The step is then
recorded as stalled and plan:next returns it again, with the reason.
What a stall means
A stall is a decision for a person, and there are three answers: fix the
environment, revise the plan with plan:revise (and approve it), or accept an
element incomplete with
bunx guren plan:waive docs/plans/<slug>/plan.json <element-id> --reason "<why>"
which writes the reason into the decision log beside the plan, committed with it. A waived element is left out of the step's judgement, so the loop moves on.
A waiver only lifts an element. A behaviour that fails makes its tests
command fail, and the step stays failed whatever is waived, so a behaviour
the code will not satisfy is a revision rather than a waiver.
The waiver is the person's decision, never yours. Report the stall, say which
of the three you think it needs and why, and wait to be told. Do not run
plan:waive to get past a step, and do not work around a stall in the code.
After a task's last step
When the last step of a task is verified and committed, and the harness has a
code-review agent (Claude Code's does), ask it to read that task's change
against the plan elements the task covers. Every step is committed, so the
working tree shows nothing: give the agent the commit the task starts from,
the parent of the oldest commit naming its first step's id. Ids carry . and
+, so match them literally:
git log --reverse --fixed-strings --grep '<step-id>' --format=%H | head -1.
Its findings are advisory: a reviewer asked for gaps reports some whether or
not they exist, so weigh each one against the plan before acting on it. A
finding you do not accept is declined, with the reason. Fix one you accept,
run plan:verify --step <the task's last step> (once that step verifies, it
re-checks every earlier step whose files the fix changed; a run without
--step would first run the later tasks' unimplemented steps and leave those
re-checks pending), and commit the fix naming the step id it belongs to. Then
ask for the next round, and give the agent:
- the round number;
- the commit the task starts from, as in the first round;
- the commit the previous review ended at (
git rev-parse HEADwhen you asked for it); - the findings it posted, as it posted them;
- each finding you declined, with the reason.
It reads only what changed since that commit, reports each earlier finding as
fixed, still open or declined, and raises no new nits. After the third
round, if an Important finding you did not decline is still open, it says the
approach needs reconsidering rather than asking for another fix. Stop there:
tell the person which findings are open and what each fix tried, and wait to
be told.
When every step is verified
Closing the plan is the person's call, like approving it. Show them what closing would write:
bunx guren plan:close docs/plans/<slug>/plan.json --dry-run
and run it without --dry-run only when asked. It refuses while any element is
neither verified nor waived, and names them. It writes the plan's document under
docs/plans/ and draft blocks between <!-- guren:plan … --> markers in
docs/entities/<Entity>.md; edit the text outside the markers freely, since a
second close replaces only what is inside them.