Appearance
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.