---
title: Revise the candidate
description: Carry findings into another attempt without losing the issue history.
---

A revision is another attempt on the same issue. It keeps the plan, branch,
original base, and earlier evidence. The new request must explain which
findings or review comments to address.

Before requesting another attempt, reproduce the findings and decide which
ones warrant changes. Record accepted findings, rejected suggestions with
reasons, and unresolved questions. Pass the accepted request to the
implementer with the original scope. Do not turn every reviewer preference
into a new requirement. The [review lead](/marbles/use-cases/software-factory/maintenance/pull-requests#synthesize-before-revising)
owns this synthesis; the command below records the supplied note without
judging it.

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

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

```ts
import { step } from "@foundry.sh/marbles";
import { z } from "zod";
import {
  issueInput,
  maxAttempts,
  recordEvent,
  recordSchema,
  requireRecord,
} from "./record";

export const revisionInput = issueInput.extend({ note: z.string().min(1) });
export const revise = step("factory-revision-request")
  .input(revisionInput)
  .output(recordSchema)
  .do(async ({ input, run }) => {
    const work = await requireRecord(input.issue);
    if (
      !(
        work.change &&
        work.plan &&
        ["needs-changes", "declined", "pr-open"].includes(work.status)
      )
    ) {
      throw new Error(
        "Revise a reviewed candidate. Reconcile blocked work before continuing."
      );
    }
    if (work.attempts >= maxAttempts) {
      throw new Error("Attempt limit reached. Record a handoff for a person.");
    }
    return recordEvent(
      {
        ...work,
        decision: undefined,
        status: "planned",
        verification: undefined,
      },
      run.id,
      "revision requested",
      input.note
    );
  });
```

</details>

Replace `factory.ts` with this final implementation-and-review workflow:

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

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

workflow("factory-revise")
  .input(revisionInput)
  .do(({ input }) => verify({ work: implement({ work: revise({}, input) }) }));
```

```sh
marbles roll factory-revise --input '{"issue":42,"note":"Address the failed setup check and the reviewer finding about the test command."}'
```

The new implementation starts from the previous candidate and uses the same
branch. Its explanation and test results get another entry in the work log.
The original implementation remains visible.

## Stop when progress stalls

The sample allows three implementation attempts, including the first.
At that limit, hand the issue to a person with the latest commit, failed
checks, unresolved findings, and proposed next action. Retrying an
infrastructure failure is a separate decision from asking for another code
revision.

Blocked work needs inspection. A crash may have committed a change before
recording it. Resolve that mismatch before restarting a stage. A changed
requirement or branch base also needs a new plan and fresh verification;
do not carry an old approval onto the new commit.

Next: [Approve and deliver](/marbles/use-cases/software-factory/development/approve).
