scalable/ai/langgraph agent teams/lesson 08 cph / /
lesson 08 / 08 · 15 min · updated ·

the sdlc as a graph

the capstone: refiner → implementer → review gates as a langgraph pipeline, swarms running the sdlc inside your pdlc, and the two decisions that stay human.

tl;dr. the capstone assembles the course into the system i actually run: an engineering team as a graph — refiner → designer → implementer, a fanned-out review tier, a bounded fix loop, and a draft pr as the only exit. the sdlc becomes the agent-run inner loop of your pdlc; the product decisions at either end stay human. every pattern from lessons one through seven has a seat.

sdlc inside pdlc

first, altitude. the product development lifecycle — noticing what's worth building, deciding it, and judging whether what shipped is right — is judgment work, and it stays yours. the software development lifecycle — refine, design, implement, test, review — is structured work with checkable outputs, and structured-with-checkable-outputs is precisely the profile agents handle. so the operating model is: the sdlc runs as an agent-driven inner loop inside the pdlc, and humans hold the two points where the loops touch — what goes in, what comes out. those are the two gates, and this graph is everything between them.

the engineering team — lessons 1–7 in one diagram
//  the engineering team, as a graph. every lesson is in here.
//
//   issue (queued by a human — GATE 1 already happened)
//     │
//     ▼
//  ┌─────────┐    ┌──────────┐    ┌─────────────┐
//  │ refiner │ ─► │ designer │ ─► │ implementer │
//  └─────────┘    └──────────┘    └──────┬──────┘
//   acceptance     approach            code on
//   criteria       decision            a branch
//                                        │
//                     ┌──────────────────┤ Send() — the gates
//                     ▼                  ▼         fan out (L6)
//               ┌──────────┐      ┌──────────┐
//               │ qa gate  │      │ sec gate │   ...legal gate
//               └────┬─────┘      └────┬─────┘
//                    └───────┬─────────┘
//                            ▼  all verdicts join (reducer)
//                      ┌───────────┐  any fail? → back to
//                      │  collect  │  implementer, with notes
//                      └─────┬─────┘  (bounded — 2 rounds)
//                            ▼  all pass
//                      ┌───────────┐
//                      │ open      │  draft pr. the graph's
//                      │ draft pr  │  authority ENDS here.
//                      └─────┬─────┘
//                            ▼
//                   human review — GATE 2 (L7)
//                   merge is a human verb.

read it with course eyes: the pipeline spine is fixed edges (lesson two — determinism is a feature). each persona is a narrow react agent (lesson three): only the implementer holds write-capable tools; every gate persona reads and judges. the review tier is a Send fan-out with a reducer join (lesson six). the fix loop is bounded in state, and the whole team compiles to a subgraph you could mount beside content and reliability teams in an org graph.

the review tier, concretely

fan out the gates, join the verdicts, route on the result
// the review tier: one verdict schema, three personas, Send().
const verdictSchema = z.object({
  gate: z.enum(["qa", "security", "legal"]),
  pass: z.boolean(),
  findings: z.array(z.string()),
});

function fanOutGates(state: typeof EngState.State) {
  return GATES.map((gate) =>
    new Send("runGate", { gate, branch: state.branch, diff: state.diff }),
  );
}

async function runGate({ gate, branch, diff }: GateInput) {
  const persona = personas[gate];       // narrow prompt + narrow tools (L3)
  const verdict = await persona
    .withStructuredOutput(verdictSchema)
    .invoke(gateContext(gate, branch, diff));
  return { verdicts: [verdict] };       // reducer joins the tier (L2)
}

function routeAfterGates(state: typeof EngState.State) {
  const failed = state.verdicts.filter((v) => !v.pass);
  if (failed.length === 0) return "openDraftPr";
  if (state.round >= 2) return "parkForHuman";   // bounded loop (L2)
  return "implementer";                          // back, with findings
}

the run, end to end

cron on the outside, graph on the inside, two exits
// the whole team, compiled once, run per issue. the outer loop
// is a scheduler, not an agent — cron is a fine orchestrator.
const engineering = buildEngineeringTeam().compile({
  checkpointer: postgresSaver,          // gates + crashes both need it (L7)
});

for (const issue of await linear.queuedForAgents()) {
  await engineering.invoke(
    { issue },
    { configurable: { thread_id: `issue-${issue.id}` } },
  );
  // each run ends in one of exactly two states:
  //   → a draft pr, waiting at the human gate
  //   → parked, with the gate findings attached
  // "the routines never merge. they're not allowed to."
}

notice how boring the outer loop is. no meta-agent decides what to run; a scheduler drains a queue that a human filled. that's gate one enforced by architecture — nothing enters the pipeline that wasn't deliberately queued. and the graph's final node opens a draft pr, so gate two is enforced the same way: the pipeline's authority ends at the pr boundary, and merge remains a human verb. neither gate relies on an agent choosing to behave; there is simply no edge in the graph that crosses either line.

watch it or lose it. a team that runs while you sleep needs receipts. trace every run — langsmith is the native fit here, opentelemetry works too — and keep an eval suite on the personas, because a prompt tweak that helps the refiner can quietly lobotomize a gate. the parked-run queue and the draft-pr list are your morning dashboard; if either grows week over week, the pipeline is telling you which persona is weakest. listen to it before you scale it.

what generalizes beyond engineering

the shape — narrow personas in a spine, fanned-out gates, bounded retries, human gates at the ends — is not about code. a content team is the same graph with writer/editor/fact-checker personas and "publish" as the human verb. so is a reliability team turning error reports into fix prs. run several such teams as subgraphs under one org graph and you've arrived, by construction, at the company-of-one setup: a swarm that runs the middle, and one human spending their scarce hours on the two decisions that were never the middle's to make.

when it breaks — one from each lesson

that's the course. one loop became a graph, the graph became a team, the team became a pipeline you can leave running overnight — with the two decisions that matter still waiting for you in the morning, where they belong.

scalable labs·cvr 30091604·github·linkedin·hello@scalable.dk