How I Built an AI Architect Workflow That Replaced 3 Tools
From prompt engineering to multi-agent orchestration — how I designed a unified AI pipeline that handles code review, documentation, and testing.
I used to run three separate tools in CI: one for review comments, one for docs generation, and one for test scaffolding. Each had its own config, its own vendor, and its own opinion about my codebase. Replacing them with a single orchestrated pipeline cut CI time roughly in half and, more importantly, made the output consistent.
One context, three outputs
The key insight: all three tasks read the same input — the diff plus the surrounding code. So I build the context once, then fan out to specialised prompts.
type Task = "review" | "docs" | "tests";
const SYSTEM: Record<Task, string> = {
review: "You are a senior reviewer. Report only defects, with file:line.",
docs: "You write terse TSDoc. No prose, no marketing language.",
tests: "You write Vitest specs. Cover edge cases, not happy paths only.",
};
export async function runTask(task: Task, ctx: DiffContext) {
const res = await fetch("/api/ai", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "google/gemini-2.5-flash",
messages: [
{ role: "system", content: SYSTEM[task] },
{ role: "user", content: renderContext(ctx) },
],
}),
});
if (!res.ok) throw new Error(`${task} failed: ${res.status}`);
return (await res.json()).choices[0].message.content as string;
}
const [review, docs, tests] = await Promise.all([
runTask("review", ctx),
runTask("docs", ctx),
runTask("tests", ctx),
]);Building the context deliberately
Dumping the whole repository into the prompt is expensive and, past a point, actively harmful — the model loses the diff in the noise. I include the changed hunks, the full body of every symbol they touch, and nothing else.
export function renderContext(ctx: DiffContext) {
return [
"## Changed files",
...ctx.files.map((f) => `### ${f.path}\n\`\`\`${f.lang}\n${f.hunks}\n\`\`\``),
"## Referenced symbols",
...ctx.symbols.map((s) => `### ${s.name} (${s.path})\n\`\`\`ts\n${s.body}\n\`\`\``),
].join("\n\n");
}What made it trustworthy
- –Structured output — each task returns JSON validated with zod before it ever reaches a PR comment.
- –Deterministic gating — review findings below a confidence threshold are dropped, not posted.
- –A budget per PR: if token spend exceeds it, the run degrades to review-only instead of failing.
- –Every prompt version is committed, so a regression in output maps to a diff.
The pipeline is not smarter than the tools it replaced. It is just aware of the same context three times over, which turns out to be most of what those vendors were selling.