Skip to content
Go back

Finish One Job, Then Keep Going: Extending a Claude Code Workflow Across Turns

Edit page

How a workflow grows across several chat turns without redoing the work that is already done

tool Workflow key input resumeFromRunId scope same session only
APPEND → RESUME Add the new step at the end of the saved script, then resume from the last run.

Every run saves its script to a file and returns a runId. Next turn, I edit that file and call Workflow({scriptPath, resumeFromRunId}). Steps at the front that did not change come back from the cache, instantly and for free. Only the new step runs for real.

One catch: the cache works on the longest unchanged prefix. Change an early step, and that step plus everything after it runs again. So add new work at the end.

0 tokensfor cached steps on resume
1 filethe saved script you keep editing
1 sessionresume does not cross sessions

01The four pieces

Think of a recipe card plus a box of finished dishes. You can add a line to the card; the dishes already cooked stay in the box.

recipeThe script file

The JavaScript that says which agents to run. Each run saves it as a file in this session's folder; the tool result prints the path. I edit it with Edit, then pass it back as scriptPath.

receiptThe run ID

A name for one run, like wf_…. It points to that run's saved results. You pass it as resumeFromRunId.

dish boxThe journal

journal.jsonl in the run's folder records what each agent() call returned. Resume reads it. I also read it when a result looks empty or odd.

order noteargs

Input handed to the script. It is part of the cache key: same script + same args = full cache hit. Use it to pass a result into a different workflow.

02The whole flow, turn by turn

Example: review a branch, then fix what was found, then double-check the fixes. Three chat turns, one growing script.

Turn 1 · you
"Use a workflow to review the changes on this branch."
  1. I call Workflow({script}) with phases Review → Verify.
  2. The tool returns right away with a runId and the saved scriptPath. The run continues in the background.
  3. When it ends, a notification wakes me. I read the result and report the confirmed findings to you.
Review ×N · liveVerify ×M · live→ run A
Turn 2 · you
"Good. Now fix the confirmed ones."
  1. I Edit the saved script and append a Fix phase after Verify. It uses the confirmed findings already in the script.
  2. I call Workflow({scriptPath, resumeFromRunId: "run A"}).
  3. Review and Verify did not change, so they replay from the journal in no time. Only Fix runs.
Review · cachedVerify · cachedFix · live→ run B
Turn 3 · you
"Add a final check that the tests pass."
  1. I append a Check phase and resume from the latest run, run B.
  2. Everything before Check is cached; Check runs live.
Review · cachedVerify · cachedFix · cachedCheck · live→ run C
Any turn · mid-run
"Wait — change the plan before it finishes."
  1. I stop the running workflow first with TaskStop.
  2. Then I edit the script and resume from that run's ID. Agents that already finished come from the cache.

In one line: each turn = edit the same file, add at the end, resume from the newest run ID. The script grows; the cost only covers the new part.

03What the script looks like

Turn 2's edit, simplified. Green lines are the append; nothing above them changes.

export const meta = {
  name: 'review-then-fix',
  description: 'Review branch changes, verify, then fix confirmed findings',
  phases: [{ title: 'Review' }, { title: 'Verify' }, { title: 'Fix' }],
}

const found = await pipeline(DIMENSIONS, d => agent(d.prompt, { phase: 'Review', schema: FINDINGS }))
const confirmed = await verifyAll(found)          // unchanged → replayed from the journal

// --- appended in turn 2 ---
const fixes = await parallel(confirmed.map(f => () =>
  agent(`Fix ${f.title} in ${f.file}`, { phase: 'Fix', isolation: 'worktree' })))
return { confirmed, fixes }

Two small rules: the meta block must be a plain literal, and the script cannot use Date.now(), Math.random(), or new Date(). Those would give a different answer on every replay and break the cache. Pass a timestamp in through args instead.

04The cache rule: longest unchanged prefix

Resume walks the agent() calls in order. It reuses them until it meets the first one that is new or different. From there on, everything runs live.

✓ Append at the end
Review — same → cached
↓
Verify — same → cached
↓
Fix — new → live
→ pay only for Fix
✗ Edit an early prompt
Review — prompt changed → live
↓
Verify — after the change → live
↓
Fix — live
→ pay for the whole run again
You change…Effect on resume
Nothing (same script, same args)✓ 100% cached
Add steps after the last one✓ old steps cached, new ones live
An early agent() prompt or its options✗ that step and all later steps rerun
args✗ any step whose prompt uses them reruns

Easy to get wrong. It is tempting to think "any unchanged call is cached." In fact, only the unchanged calls before the first change are cached. That is why appending is the safe habit.

05Other ways to continue

Resume is the default. Pick another path when the next job is not really "more of the same run."

SituationWhat I callWhy
Next job builds on this run's dataWorkflow({scriptPath, resumeFromRunId})Reuses finished agents; one growing script.
Want a clean rerun of the edited scriptWorkflow({scriptPath})No cache; everything runs fresh.
Next job is a different kind of workNew Workflow({script, args})Feed the last result in via args; each workflow stays small and readable.
One script should call anotherworkflow(name or {scriptPath}, args) inside the scriptRuns the child inline. Only one level deep.
Reuse it in later sessionsSave under .claude/workflows/, call by nameResume can't cross sessions; a saved workflow can be rerun anywhere.

For big tasks, the guidance is to run phases as separate workflows in sequence—understand → design → implement → review—so I read each result before choosing the next step. That keeps you in the loop between phases.

06Limits and gotchas

Hard limits
  • Same session only. A new session cannot resume an old run.
  • Stop before you resume a run that is still going (TaskStop).
  • Nesting is one level. A child workflow can't call another.
  • Opt-in. I only start a workflow when you ask (e.g. "use a workflow").
Habits that keep it cheap
  • Add new work at the end of the script.
  • Don't reword early prompts unless you want them to rerun.
  • Odd or empty result? Check journal.jsonl before guessing.
  • Mention the run ("extend the last workflow…") so I know which run ID to resume.

What to tell me: "Extend the last workflow with <new step> and resume." I'll append to the saved script, resume from the newest run ID, and only the new step will cost anything.

Source: Claude Code Workflow tool description and workflow-authoring reference (Resume section).
cached / safe runs live (costs tokens) cache broken run ID

Edit page
Share this post on:

Previous Post
一切网就断?一个 system 让 Clash 把自己锁死了
Next Post
从 Claude 的两个组件,到未来的软件