Skip to content

Phase N writes an artifact to disk, a regex cascade extracts its path from the free-text output, and Phase N+1 consumes the path — never an in-memory handoff.

Pattern

Phase Gate with Artifact Extraction

The shape

Phase N output → extractor → Phase N+1 input.

Phase Nfree text

I've written the plan to specs/plan-cc73faf1-oauth.md following the feature template.

stdout / turn output

extract

Gateregex cascade

try patterns in order

1 → full path

2 → labeled

3 → standalone line

4 → any mention

first match wins

handoff

Phase N+1structured input

AgentPromptRequest

plan_path=

specs/plan-cc73faf1-oauth.md

resume-safe · subprocess-safe

The artifact is on disk. The handoff is one path. Phases coordinate through the filesystem — never through a shared Python process.

The extractor

Four patterns. Most specific first.

Order

Pattern

Specificity

1

r"specs/plan-[\w-]+\.md"

full path match

2

r"path:\s*(specs/.*\.md)"

labeled

3

r"^\s*(specs/.*\.md)\s*$"

standalone line

4

r"plan.*?(specs/.*\.md)"

any mention · fallback

First match wins. If pattern 1 hits, stop. Fall through only when it fails. The cascade absorbs presentation variance without accepting spurious matches.

Ordering is load-bearing. Invert it and the loosest pattern swallows everything — specific → labeled → positional → fallback, never the reverse.

Why regex, not JSON

Structured output is fragile. Extraction is robust.

JSON-schema outputbreaks on re-tune

{

"plan_path": "specs/plan-...md",

"status": "complete"

}

  • New Claude version re-tunes whitespace, escaping, field order
  • Parse fails when the model adds a prose preface
  • Forces the agent to wrap output in JSON — awkward for some tasks
  • One schema drift breaks every downstream phase

Regex from free textversion-stable

I've written the plan to

`specs/plan-cc73faf1-oauth.md`

following the feature

template.

  • Agent speaks naturally; path appears somewhere in the prose
  • Cascade tolerates punctuation, backticks, labels, line breaks
  • Model updates re-phrase the prose — the path still matches
  • No schema to maintain, no JSON-mode prompting tax

Structured output couples the handoff to the model's serialization. Regex couples it only to the path itself — the one thing that has to stay stable.

When nothing matches

Fail loudly. Never default.

✗ Silent default (wrong)

ifnot match:

# pick first spec in dir

path = glob("specs/*.md")[0]

The wrong plan gets implemented. No error. The pipeline runs green. The bug is only visible when someone reads the PR and asks why the feature doesn't match the ticket.

✓ Loud failure (right)

ifnot match:

raiseRuntimeError(

f"Could not extract plan path

from Phase 1: {output[:500]}"

)

Phase N+1 never starts. Operator sees the error, tightens the classifier or tells the agent to mention the path more explicitly. The bug is visible before it ships.

"First spec file in the directory" is almost always wrong — and wrong-invisibly. No match = no handoff = no Phase N+1.

What breaks if you violate it

Three failure modes. Each collapses a different capability.

In-memory dict handoff

Cannot resume from failure. Cannot run phases as subprocesses. Composite workflows collapse into a single monolithic process — one crash wipes the entire run.

Single pattern, no fallback

Brittle to model output variation. One Claude re-tune changes the phrasing and every pipeline breaks simultaneously. The cascade is what makes the extractor robust across versions.

Silent default on no-match

Wrong artifact gets consumed. The pipeline produces output — just the wrong output. Bug is invisible until production, often caught only by a human reviewer hours or days later.

The discipline

Phases hand off paths to artifacts, not references to memory.

A path is resumable, serializable, auditable — and survives the process that produced it. An in-memory dict is none of those things. The artifact on disk is the contract; the extractor is how the next phase finds it.