---
title: Plan the work
description: Record an implementation approach against the issue and its starting revision.
---

The planner reads the captured issue and a checkout of the starting commit.
It returns an approach, planned checks, and any questions that prevent implementation.
Those questions stop the workflow and remain in the work log. A plan with
no open questions moves the issue to `planned`; unresolved questions move
it to `blocked`.

Create `.foundry/marbles/_factory/plan.ts`:

<details>
<summary>Complete plan.ts</summary>

```ts
import { agent, Output, step } from "@foundry.sh/marbles";
import {
  chronicle,
  planSchema,
  recordEvent,
  recordSchema,
  workInput,
} from "./record";

const planner = agent({
  prompt:
    "Inspect the repository and its instructions without editing. Plan the requested issue against its acceptance criteria. Reuse modules that already own the work. Identify and justify any proposed package or dependency. Name the checks that will prove each criterion, including evidence that needs a person or external service. List questions that prevent implementation. Do not invent missing requirements.",
});
export const plan = step("factory-plan")
  .input(workInput)
  .output(recordSchema)
  .do(async ({ input: { work }, agents, workspaces, run }) => {
    if (work.plan && work.plan.questions.length === 0) {
      return work;
    }
    const output = await workspaces.current.git.withWorktree(
      { base: work.base },
      async () => {
        const session = await agents.session(planner);
        const reply = await session.generate(
          `${work.issue.body}\n\n${chronicle(work)}`,
          {
            output: Output.object({ schema: planSchema }),
          }
        );
        return planSchema.parse(reply.output);
      }
    );
    const next = await recordEvent(
      {
        ...work,
        plan: output,
        status: output.questions.length ? "blocked" : "planned",
      },
      run.id,
      "plan",
      JSON.stringify(output, null, 2)
    );
    if (output.questions.length) {
      throw new Error(
        "Planning needs answers. Read the work log before continuing."
      );
    }
    return next;
  });
```

</details>

Replace `factory.ts` with:

```ts
import { workflow } from "@foundry.sh/marbles";
import { intake } from "./_factory/intake";
import { plan } from "./_factory/plan";
import { issueInput } from "./_factory/record";

workflow("factory")
  .input(issueInput)
  .do(({ input }) => plan({ work: intake({}, input) }));
```

Run the workflow for the same issue and inspect `plan.approach`. If it
stops with questions, clarify the issue and run it again. The intake step
allows this while no implementation attempt has started.

The plan does not replace the acceptance criteria. The implementer receives
both, and the reviewer checks the result against the original request.
The [Agents library](https://github.com/Francois-Esquire/foundry/blob/main/packages/agents/README.md)
provides the planning session; the step records its structured result.

Next: [Implement the issue](/marbles/use-cases/software-factory/development/implement).
