Table of Contents
1. The Four-Agent Pipeline
Four specialized Claude Code agents, chained so each one's output becomes the next one's input.
Raw business requirements (prose, from a product owner)
│
▼
┌─────────────────────┐
│ Agent 1: SpecWriter │ Requirements → Technical Specification
└──────────┤───────────┘
▼
Technical spec (the 20 precise business rules — Chapter 3)
│
▼
┌─────────────────────┐
│ Agent 2: CodeSmith │ Tech Spec → Implementation
└──────────┤───────────┘
▼
validator.js + app wiring (Chapter 3's rule engine)
│
▼
┌─────────────────────┐
│ Agent 3: ReviewGuard │ Code Review vs. business rules + eng. standards
└──────────┤───────────┘
▼
Review report (findings, severity, standards violations)
│
▼
┌─────────────────────┐
│ Agent 4: TestPilot │ Test design → execution → published report
└──────────┤───────────┘
▼
Test report (pass/fail per rule, ready to ship)
| Agent | Role | Input | Output | Primary fault lines (Ch. 6 / 8) |
|---|---|---|---|---|
| SpecWriter | Turns ambiguous business prose into a precise technical spec | Raw requirements text | Structured 20-rule tech spec | Context Collapse, Hallucination |
| CodeSmith | Implements the tech spec | Tech spec | validator.js + UI wiring | Plan Fragility, Integration Trap |
| ReviewGuard | Reviews code against business rules and engineering standards | Code, tech spec, coding-standards doc | Review report | Hallucination, Black Box Behavior |
| TestPilot | Designs, runs, and publishes tests | Tech spec, code | Executed test report | Plan Fragility, Blind Autonomy |
What SpecWriter Actually Receives
Real business requirements rarely arrive as a clean rule table — they arrive as prose, and prose is ambiguous. Here's the input SpecWriter is actually handed:
"Woody's Hardware wants a new automated screening step for our store credit card applications. Basically we want to auto-reject anyone who's too young, doesn't live in one of our states, can't afford the payments, has bad credit, or already has one of our cards. Also don't let people apply if they haven't agreed to a credit check, and obviously we need their basic info to be legit (real name, real SSN format, etc). We don't want to give out cards larger than what people can reasonably pay back either. Let's also make sure we're not approving people who just went bankrupt recently, and that they've been at their current address a little while so we know they're stable. Oh, and obviously the application itself needs to be filled out completely before we even look at it."
Compare this to Chapter 3's clean 20-rule table — SpecWriter's actual job is producing that table from this. Notice three distinct problems buried in the prose, each demanding a different response from a genuinely "smart" agent (Chapter 2's terminology):
- Vague thresholds — "too young," "a little while," "recently," "bad credit," "reasonably pay back" all imply a rule (R1, R16, R10, R11, R14/R15) without stating its exact value. A smart SpecWriter flags these as assumptions needing confirmation, not silently invented facts.
- Entirely missing rules — the prose never mentions citizenship/residency (R4), minimum income (R6), employment status (R7), contact-info validity (R3), ZIP format (R17), or the open-credit-lines cap (R18) at all. A smart SpecWriter flags these as gaps, rather than silently omitting them or silently fabricating full rules for something never asked about.
- Clear enough as-is — existing cardholder (R12), consent (R13), name validity (R19), and completeness (R20) are unambiguous even without exact numbers.
This three-way distinction — specified, ambiguous, and missing entirely — is exactly the shape of the eval strategy for Agent 1, in Section 2.
2. Designing the Eval Strategy
Why Four Separate Evals Aren't Enough on Their Own
Chapter 4 drew a direct line from classic QA to evals: unit tests check one component in isolation, integration tests check that components work correctly together. A four-agent pipeline needs the exact same two layers — a per-agent eval for each stage, and one end-to-end eval for the pipeline as a whole, because per-agent evals alone can hide a failure that only shows up once the stages are chained.
Per-Agent Golden Dataset Design
| Agent | Ground truth source | Eval method(s) | What it catches |
|---|---|---|---|
| SpecWriter | The true, already-agreed 20 rules from Chapter 3 | Rubric scoring (coverage, assumption transparency, gap detection) — there's no code yet, so execution-based scoring doesn't apply here | Invented thresholds, silently dropped rules, false confidence about ambiguous requirements |
| CodeSmith | Chapter 3's Golden Dataset (GD-001 through GD-010) | Execution-based — run the generated validator.js against all 10 golden cases | Incorrect rule implementation, boundary-condition bugs (Chapter 3's GD-007 pattern), cascading logic errors |
| ReviewGuard | Code samples with deliberately injected business-rule bugs and standards violations | Rule-level precision/recall (Chapter 3's methodology) plus an LLM-as-judge rubric for standards adherence | Missed real bugs, invented false findings, style/security violations left uncaught |
| TestPilot | The 20-rule tech spec, plus an independent re-execution of whatever tests TestPilot generates | Rubric for test-design quality (coverage, boundary case, cascading case) plus execution-based report-fidelity checking | Incomplete test coverage, and — critically — a published "all tests passing" report that doesn't match what actually happened when re-run independently |
Notice CodeSmith's eval reuses Chapter 3's Golden Dataset directly — the same ten cases originally built to test whether an agent could read and comprehend validator.js now double as the acceptance tests for an agent that writes it. One well-designed Golden Dataset, two completely different uses.
The Failure Only an End-to-End Eval Catches: Silent Spec Drift
Here's a concrete scenario worth walking through in full, because it's the single most important reason the pipeline needs one more eval layer on top of the four above.
Suppose SpecWriter, facing the ambiguous "just went bankrupt recently" phrase, silently assumes a 5-year lookback instead of the true 7-year rule (R10) — and doesn't flag it as an assumption. What happens next?
- CodeSmith correctly implements a 5-year bankruptcy check, exactly as specified. Its eval against Chapter 3's golden cases might still pass, if none of the ten cases happen to fall in the 5-to-7-year gap — a real risk if the dataset isn't specifically designed to catch this.
- ReviewGuard reviews the code against SpecWriter's tech spec — which now says 5 years — and correctly confirms the code matches the spec. Its eval scores perfectly, because it's grading fidelity to the (wrong) spec, not fidelity to the business's real intent.
- TestPilot writes tests derived from the same 5-year spec, runs them, and every test passes. Its eval scores perfectly too.
Every single per-agent eval is green. The pipeline nonetheless ships an agent that will wrongly approve a bankrupt applicant in year 6. This is a pipeline-level instance of exactly the risk Chapter 5 warned about: each stage can hit its own local metric perfectly while the true, original objective — matching the business's real intent — silently fails. The fix is the same one Chapter 5 argued for at the metric level, applied at the pipeline level: track an end-to-end metric, scored against the original ground truth, independent of what any individual stage claims about itself.
def end_to_end_correctness_evaluator(run, example):
"""The real acceptance test: does the FINAL shipped code, produced by
the whole four-agent pipeline starting from raw business prose, correctly
implement all 20 rules against Chapter 3's golden cases — regardless of
which stage any drift crept in at."""
final_validate_fn = run.outputs["code"]["validate_application_fn"]
correct = sum(
1 for case in WOODYS_GOLDEN_CASES
if matches_expected(final_validate_fn(case["input"]), case["expected"])
)
return {"key": "end_to_end_accuracy", "score": correct / len(WOODYS_GOLDEN_CASES)}
This evaluator never looks at what SpecWriter, CodeSmith, ReviewGuard, or TestPilot claimed about their own work — it only checks the final artifact against the original Chapter 3 ground truth, which is exactly what makes it immune to the drift scenario above.
3. Step by Step: Wiring the Pipeline into LangSmith
1Instrument the Pipeline with Nested Traces
LangSmith supports parent/child trace relationships natively, which is exactly the shape of a four-agent pipeline: one parent trace for the whole run, one child span per agent.
from langsmith import traceable
@traceable(name="woodys_hardware_sdlc_pipeline")
def run_full_pipeline(business_requirements: str) -> dict:
tech_spec = spec_writer_agent(business_requirements)
generated_code = code_smith_agent(tech_spec)
review_report = review_guard_agent(generated_code, tech_spec, CODING_STANDARDS_DOC)
test_report = test_pilot_agent(tech_spec, generated_code)
return {
"tech_spec": tech_spec,
"code": generated_code,
"review": review_report,
"test_report": test_report,
}
@traceable(name="spec_writer_agent")
def spec_writer_agent(business_requirements: str) -> dict:
...
@traceable(name="code_smith_agent")
def code_smith_agent(tech_spec: dict) -> dict:
...
@traceable(name="review_guard_agent")
def review_guard_agent(code: dict, tech_spec: dict, standards_doc: str) -> dict:
...
@traceable(name="test_pilot_agent")
def test_pilot_agent(tech_spec: dict, code: dict) -> dict:
...
Because each function is independently @traceable, one pipeline run in the LangSmith UI shows a single parent trace with four clearly labeled child spans — exactly Chapter 6's Black Box Behavior fault line, solved by construction: any failure is attributable to a specific agent, not a mystery inside one giant opaque call.
2Build the Datasets
Five datasets: one per agent, plus one for the end-to-end pipeline.
from langsmith import Client
client = Client()
spec_writer_dataset = client.create_dataset(
dataset_name="woodys-specwriter-golden-v1",
description="Raw business prose → expected 20-rule tech spec, with ambiguous/omitted rule tags",
)
client.create_example(
inputs={"business_requirements": RAW_REQUIREMENTS_TEXT},
outputs={
"expected_rules": CHAPTER_3_RULES, # the true 20 rules
"ambiguous_in_source": ["R1", "R5", "R8", "R9", "R10", "R11", "R14", "R15", "R16"],
"omitted_from_source": ["R3", "R4", "R6", "R7", "R17", "R18"],
},
dataset_id=spec_writer_dataset.id,
)
code_smith_dataset = client.create_dataset(
dataset_name="woodys-codesmith-golden-v1",
description="Tech spec in → generated code scored against Chapter 3's GD-001..GD-010",
)
client.create_example(
inputs={"tech_spec": CHAPTER_3_RULES},
outputs={"golden_cases": WOODYS_GOLDEN_CASES}, # reused directly from Chapter 3
dataset_id=code_smith_dataset.id,
)
# review_guard_dataset and test_pilot_dataset follow the same pattern —
# see Chapter 8, Phase 2, for the full create_example() walkthrough.
pipeline_dataset = client.create_dataset(
dataset_name="woodys-e2e-pipeline-golden-v1",
description="Raw business requirements → final shipped code, scored end-to-end",
)
client.create_example(
inputs={"business_requirements": RAW_REQUIREMENTS_TEXT},
outputs={"golden_cases": WOODYS_GOLDEN_CASES},
dataset_id=pipeline_dataset.id,
)
3Write the Evaluators
The three SpecWriter evaluators operationalize the three-way distinction from Section 1:
def spec_coverage_evaluator(run, example):
"""Rubric: of the rules the business DID address (clearly or ambiguously),
did the generated tech spec capture all of them?"""
generated_ids = {r["id"] for r in run.outputs["tech_spec"]}
addressed_ids = (
set(example.outputs["ambiguous_in_source"])
| {"R2", "R12", "R13", "R19", "R20"} # clearly specified, no number needed
)
return {"key": "spec_coverage", "score": len(generated_ids & addressed_ids) / len(addressed_ids)}
def assumption_transparency_evaluator(run, example):
"""Did the agent flag ambiguous thresholds as assumptions, instead of
silently inventing a precise value and presenting it as given?"""
flagged = set(run.outputs.get("flagged_assumptions", []))
ambiguous = set(example.outputs["ambiguous_in_source"])
return {"key": "assumption_transparency", "score": len(flagged & ambiguous) / len(ambiguous)}
def gap_detection_evaluator(run, example):
"""Did the agent flag rules the business never mentioned at all,
instead of silently omitting or silently fabricating them?"""
flagged_gaps = set(run.outputs.get("flagged_gaps", []))
true_gaps = set(example.outputs["omitted_from_source"])
return {"key": "gap_detection", "score": len(flagged_gaps & true_gaps) / len(true_gaps)}
CodeSmith's evaluator is the direct reuse of Chapter 3's Golden Dataset shown in Section 2. ReviewGuard's and TestPilot's follow the rule-level precision/recall pattern from Chapter 3 and the report-fidelity pattern below:
def report_fidelity_evaluator(run, example):
"""Execution-based: independently rerun whatever tests TestPilot generated,
and check that its PUBLISHED report matches what actually happened —
catches an agent that reports 'all tests passing' without truly running them,
a direct instance of Chapter 6's Hallucination fault line applied to a test agent."""
published = run.outputs["published_test_report"]
actual = rerun_tests_independently(run.outputs["generated_test_cases"])
matches = sum(1 for p, a in zip(published, actual) if p == a)
return {"key": "report_fidelity", "score": matches / len(actual)}
4Run the Experiments
from langsmith.evaluation import evaluate
evaluate(
spec_writer_agent,
data="woodys-specwriter-golden-v1",
evaluators=[spec_coverage_evaluator, assumption_transparency_evaluator, gap_detection_evaluator],
experiment_prefix="specwriter-v1",
)
evaluate(
code_smith_agent,
data="woodys-codesmith-golden-v1",
evaluators=[code_correctness_evaluator],
experiment_prefix="codesmith-v1",
)
# ...review_guard and test_pilot experiments follow the same call shape...
evaluate(
run_full_pipeline,
data="woodys-e2e-pipeline-golden-v1",
evaluators=[end_to_end_correctness_evaluator],
experiment_prefix="pipeline-e2e-v1",
)
Each experiment_prefix is what makes version-over-version comparison possible later — exactly Chapter 4's regression-testing discipline, now applied per agent and to the pipeline as a whole.
5Build the Dashboard, With Ship Gates at Both Layers
Following Chapter 4's two-tier pattern, but now across five scorecards instead of one:
| Metric | Hard gate | Soft threshold |
|---|---|---|
| SpecWriter: gap detection | ≥ 95% (a missed gap silently corrupts everything downstream) | — |
| SpecWriter: assumption transparency | ≥ 90% | ≥ 98% target |
| CodeSmith: golden-case accuracy | ≥ 95% | ≥ 100% target |
| ReviewGuard: review recall | ≥ 90% | ≥ 98% target |
| TestPilot: report fidelity | ≥ 99% (a fabricated "all passing" report is a release-blocking risk) | — |
| Pipeline: end-to-end accuracy | ≥ 95% | ≥ 100% target |
The end-to-end row is deliberately listed separately and held to its own hard gate — a pipeline is not allowed to ship on the strength of four green per-agent scorecards alone, precisely because of the silent-drift scenario in Section 2.
6Close the Loop
Exactly Chapter 7's compounding loop, now running across five datasets instead of one: real pipeline runs get traced, real failures — a spec gap TestPilot's tests didn't catch, a review ReviewGuard missed — get promoted into the relevant dataset, and every future experiment is scored against an ever-growing, production-grounded set of cases instead of only the ones written by hand on day one.
4. Main LangSmith Features That Make It a Strong Choice for This Kind of Pipeline
| Feature | Why it matters here specifically |
|---|---|
| Nested / hierarchical tracing | A four-agent pipeline is naturally a tree, not a flat list — one parent trace with four labeled child spans makes every failure immediately attributable to a specific agent, directly solving Chapter 6's Black Box Behavior fault line. |
| Datasets built from production traces | Real business requirements SpecWriter mishandles in production become new golden cases automatically, closing exactly the gap Chapter 5 raised about a small, static, hand-authored dataset going stale. |
| Flexible evaluator framework | Supports plain Python functions, off-the-shelf evaluators, and LLM-as-judge scoring side by side — every eval method from Chapter 2 (execution-based, rubric, LLM-as-judge) is a first-class citizen, not a workaround. |
| Experiment versioning & comparison | experiment_prefix-tagged runs let a team compare CodeSmith-v1 against CodeSmith-v2 side by side on the dashboard — the regression-testing discipline from Chapter 4, built in rather than bolted on. |
| Prompt Hub / playground with versioning | Lets a team iterate on, say, ReviewGuard's system prompt against real past traces before shipping the change — Chapter 4's classic-QA-style change control, applied to a prompt instead of code. |
| Annotation queues | Routes ReviewGuard's or TestPilot's outputs to a human reviewer at scale — the human spot-audit method from Chapter 2, and the human-in-the-loop pattern Chapter 6 recommends for the Blind Autonomy fault line. |
| Native LangChain/LangGraph integration, SDK-based framework independence | A multi-agent pipeline like this one is a natural fit for LangGraph's orchestration, with tracing wired in automatically — but the same @traceable SDK works regardless of which framework (or none) built the agents. |
| Governed data-platform integrations | Woody's Hardware's applications carry real PII (SSNs, income, addresses) — evals that run against data already inside a governed platform, without a new data-egress approval, are exactly the wedge a regulated business like this one needs. |
5. Key Takeaways
- A four-agent SDLC pipeline — requirements, coding, review, testing — needs both per-agent evals, tailored to each stage's specific task and fault-line profile, and one end-to-end eval, scored independently against the original ground truth.
- Per-agent evals alone can all show green while the pipeline still fails: the "silent spec drift" scenario shows how an ambiguous requirement, silently resolved wrong by SpecWriter, can propagate through CodeSmith, ReviewGuard, and TestPilot without any single stage's own metric ever catching it — a pipeline-level instance of Chapter 5's Goodhart's Law.
- A Golden Dataset built for one purpose can serve a second purpose cleanly: Chapter 3's ten cases, built to grade code comprehension, double here as CodeSmith's acceptance tests for code generation.
- LangSmith's nested tracing, dataset-from-production-traces workflow, flexible evaluator framework, and experiment versioning map directly onto the needs of a real multi-agent pipeline, not just a single agent.
- Ship gates belong at both layers — per-agent thresholds catch stage-specific regressions, but a dedicated end-to-end hard gate is what actually protects the business's real intent.
6. Glossary
| Term | Meaning |
|---|---|
| Pipeline (multi-agent) | A sequence of specialized agents where each one's output becomes the next one's input — here, requirements → spec → code → review → tests. |
| Nested / hierarchical trace | A parent trace (the whole pipeline run) containing labeled child spans (each individual agent's call), making failures attributable to a specific stage. |
| Silent spec drift | A requirements ambiguity resolved incorrectly at an early pipeline stage, propagating undetected through every later stage because each one is only checked against the (already wrong) output of the stage before it. |
| End-to-end evaluator | An eval scored against the pipeline's original ground truth, independent of what any individual stage claims about its own correctness — the only layer that catches silent spec drift. |
| Report fidelity | Whether a test-running agent's published results match what actually happens when its tests are independently rerun — guards against a fabricated "all passing" report. |
This chapter completes the applied arc of the Eval Basics series: Chapters 1–6 built the concepts, Chapter 7 mapped them onto LangSmith, Chapter 8 trained a person to use it, Chapter 9 checked the knowledge, and this chapter shows all of it running together across a real, four-agent Claude Code pipeline — from a business owner's rough sentence to a shipped, tested feature for Woody's Hardware.