<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ian A. Drilon</title>
    <description>The latest articles on DEV Community by Ian A. Drilon (@zneret03).</description>
    <link>https://dev.to/zneret03</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F908164%2F63df0db0-c1c3-4af5-b2f2-81f004ee3a72.jpg</url>
      <title>DEV Community: Ian A. Drilon</title>
      <link>https://dev.to/zneret03</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zneret03"/>
    <language>en</language>
    <item>
      <title>How to one shot your feature with this orchestration-workers graph engineering in Claude</title>
      <dc:creator>Ian A. Drilon</dc:creator>
      <pubDate>Tue, 18 Aug 2026 06:23:09 +0000</pubDate>
      <link>https://dev.to/zneret03/how-to-one-shot-your-feature-with-this-orchestration-workers-graph-engineering-in-claude-2cmf</link>
      <guid>https://dev.to/zneret03/how-to-one-shot-your-feature-with-this-orchestration-workers-graph-engineering-in-claude-2cmf</guid>
      <description>&lt;p&gt;I work in a workspace with eight repos. For a long time, every ticket started the same way.&lt;/p&gt;

&lt;p&gt;Read the ticket. Read its parent ticket. Grep the same files to check if the field already exists. Start the services. Run lint and tests in each repo. Then make the branches by hand, and open two pull requests that have to match.&lt;/p&gt;

&lt;p&gt;I counted the commands once. Fifty four of them are written down across those repos. Most of them I ran again for the next ticket.&lt;/p&gt;

&lt;p&gt;I tried to fix this with better prompts. It did not work. One agent with a long prompt still forgets, still guesses, and still hands me code with no proof that it works.&lt;/p&gt;

&lt;p&gt;What worked was building a graph.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9w0rqoh1zmustq4i76hi.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9w0rqoh1zmustq4i76hi.gif" alt="The graph, one ticket from input to pull requests" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What "graph" means here
&lt;/h2&gt;

&lt;p&gt;A graph is just boxes and arrows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;node&lt;/strong&gt; is one agent with one job.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;edge&lt;/strong&gt; is a handoff. Agent A finishes, agent B gets the result.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;barrier&lt;/strong&gt; is a place where nothing moves forward until every branch comes back.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;cycle&lt;/strong&gt; is an arrow that goes backward. Work that failed goes back to be fixed, then comes around again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is all. The rest of this post is how to build one in Claude Code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shape we are building
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ticket -&amp;gt; plan -&amp;gt; [check] -&amp;gt; survey -&amp;gt; build -&amp;gt; [gate] -&amp;gt; 4 QA lanes -&amp;gt; judge -&amp;gt; PRs
                    |                                         |
                    +-------- backward arrows ----------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One command in. A pull request out. If something fails, it loops until it is fixed, or until the round budget runs out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: put the harness in its own folder
&lt;/h2&gt;

&lt;p&gt;Do not put this inside a product repo. Make it a sibling folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;workspace/
  orchestrator/        &amp;lt;- the harness. no product code.
    .claude/agents/
    .claude/commands/
    .claude/workflows/
    docs/
  api-engine/          &amp;lt;- your real projects
  web-pilet/
  tech-docs/
  e2e-suite/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why: a ticket usually touches two or three repos at once. If the harness lives inside one repo, it can only see that repo. From the workspace root it can see all of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: write down what you keep repeating
&lt;/h2&gt;

&lt;p&gt;This is the boring step, and it is the one that pays.&lt;/p&gt;

&lt;p&gt;Every time you explain something to the model twice, write it in &lt;code&gt;docs/&lt;/code&gt; instead. Mine ended up as six files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which lint, type check and test command belongs to each repo&lt;/li&gt;
&lt;li&gt;which service owns which decision&lt;/li&gt;
&lt;li&gt;how to check if a query or field already exists before writing a new one&lt;/li&gt;
&lt;li&gt;what a plan document must contain&lt;/li&gt;
&lt;li&gt;the design rules, including what is not a bug&lt;/li&gt;
&lt;li&gt;the words we use for statuses and roles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;"The AI does not know our codebase" is not a model problem. It is an unwritten knowledge problem. Once it is written, every agent reads the same copy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: write your agents
&lt;/h2&gt;

&lt;p&gt;One file per agent in &lt;code&gt;.claude/agents/&lt;/code&gt;. Markdown with a small header.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;qa-backend&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Reads the server half of a diff and returns a PASS or FAIL verdict. Never edits files.&lt;/span&gt;
&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Read, Grep, Glob, Bash&lt;/span&gt;
&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;inherit&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

You are QA for the server side.

You read the diff. You do not edit files, and you do not praise.
Your verdict is acted on, so it has to be right in both directions.
A false PASS ships a bug. A false FAIL sends good work back.

Report every finding with a file and a line number, and a severity of
BLOCKER, MAJOR, MINOR or NIT.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things matter more than the prose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Give each agent one job.&lt;/strong&gt; I have six: a planner, a builder, and four reviewers (server, web, design, browser).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use &lt;code&gt;tools:&lt;/code&gt; to take away what an agent must not do.&lt;/strong&gt; My two code reviewers have no &lt;code&gt;Write&lt;/code&gt; and no &lt;code&gt;Edit&lt;/code&gt;. They cannot change the code they are judging, because the tool list does not let them. That is stronger than asking them nicely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: write the loop
&lt;/h2&gt;

&lt;p&gt;The loop is one JavaScript file in &lt;code&gt;.claude/workflows/&lt;/code&gt;. It starts with a &lt;code&gt;meta&lt;/code&gt; block, then plain code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;build-ticket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Plan a ticket, build it, check it, judge it.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Plan&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Build&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Gate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;QA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Judge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Plan&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`Read the ticket &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; and its parent. Write the plan to tech-docs/.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;agentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;planner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PLAN_SCHEMA&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;agent()&lt;/code&gt; starts a subagent and waits for it. &lt;code&gt;phase()&lt;/code&gt; groups the calls so you can watch progress. &lt;code&gt;args&lt;/code&gt; is whatever you passed in.&lt;/p&gt;

&lt;p&gt;You are writing normal code here. Loops, if statements, counters. That is the point. The control flow is yours, not the model's.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: type your handoffs
&lt;/h2&gt;

&lt;p&gt;This is the part I would keep if I had to throw everything else away.&lt;/p&gt;

&lt;p&gt;Pass a &lt;code&gt;schema&lt;/code&gt; and the subagent must answer in that shape. It cannot hand back three paragraphs of maybe.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FINDING&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;additionalProperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;severity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;line&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;problem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BLOCKER&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MAJOR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MINOR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NIT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;integer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;problem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;QA_SCHEMA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;additionalProperties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;verdict&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;findings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;summary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;verdict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PASS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FAIL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;findings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;array&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FINDING&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prose can be vague. "Severity is one of BLOCKER, MAJOR, MINOR, NIT" cannot be vague. Set &lt;code&gt;additionalProperties: false&lt;/code&gt; everywhere, so an agent cannot slip in a field you never asked for.&lt;/p&gt;

&lt;p&gt;Eleven of my twelve agent calls are typed this way. I will come back to the twelfth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not trust the verdict on its own.&lt;/strong&gt; A schema is happy to accept &lt;code&gt;verdict: PASS&lt;/code&gt; next to a BLOCKER finding. So check the evidence yourself, in code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BLOCKING&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BLOCKER&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MAJOR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;blockingOf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;qa&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;qa&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;findings&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;BLOCKING&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// PASS only counts when the findings agree with it.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;passed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;qa&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;qa&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;qa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;verdict&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PASS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;blockingOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;qa&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: one barrier, in the right place
&lt;/h2&gt;

&lt;p&gt;Run things at the same time when they do not need each other. Use &lt;code&gt;parallel()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;QA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lanes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;qa-backend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;agentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;qa-backend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;QA_SCHEMA&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;qa-frontend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;agentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;qa-frontend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;QA_SCHEMA&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hasDesign&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;touchedFrontend&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;lanes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;qa-design&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;agentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;qa-design&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DESIGN_SCHEMA&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="nx"&gt;lanes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;qa-e2e&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;agentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;qa-e2e&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;E2E_SCHEMA&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;parallel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lanes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// &amp;lt;- the barrier. nothing moves until all four return.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;parallel()&lt;/code&gt; waits for everything. That is a barrier, and I only have one, because only one decision needs all of the answers at once.&lt;/p&gt;

&lt;p&gt;Two lessons from getting this wrong:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run the test suites once, before the lanes, not inside them.&lt;/strong&gt; Four reviewers running the same test suite at the same time fight over the same test database and the same test cache. You get failures that are not real, and good work gets sent back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A lane that could not run did not pass.&lt;/strong&gt; My browser lane can return &lt;code&gt;NOT_RUN&lt;/code&gt; when the app will not start. &lt;code&gt;NOT_RUN&lt;/code&gt; blocks the round. It is not a neutral answer, and it is never a pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: point the backward arrows at the right node
&lt;/h2&gt;

&lt;p&gt;Here is the mistake I shipped and later fixed.&lt;/p&gt;

&lt;p&gt;I said "a failed review goes back to planning". That sounded wise. It was wrong, and my own diagram said it too.&lt;/p&gt;

&lt;p&gt;What actually makes sense:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;qaFailed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Design is the planner's call. A builder who decides a 4px gap is fine&lt;/span&gt;
  &lt;span class="c1"&gt;// has removed the only check on it.&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;designNeedsAdjudication&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;replanPrompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;agentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;planner&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;REPLAN_SCHEMA&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// &amp;lt;- back to build, with the findings attached&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most failures go straight back to the builder. Only the ones that are really an argument about the plan go to the planner. If you send every failure through planning, you rewrite the plan for a typo. If you send none, the builder gets to overrule the design.&lt;/p&gt;

&lt;p&gt;Four different failures point back at build in my loop: a failed test run, a failed review, a design fix, and an incomplete judgement. That is what makes it settle instead of drifting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: keep git out of the loop
&lt;/h2&gt;

&lt;p&gt;The loop never commits, never pushes, and never opens a pull request. My builder agent is told this in plain words, and the agent that owns git is a separate command with one confirmation step.&lt;/p&gt;

&lt;p&gt;Rules I wrote down and do not bend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one branch per repo that changed, never a shared branch&lt;/li&gt;
&lt;li&gt;never merge&lt;/li&gt;
&lt;li&gt;never force push&lt;/li&gt;
&lt;li&gt;never open a pull request from a loop that did not finish&lt;/li&gt;
&lt;li&gt;ask again next time, because approval for one ticket is not approval for the next&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reason is simple. The loop is fast and I am not reading every line as it goes. So the last step before anything leaves my machine is a human saying yes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 9: install it so every project sees it
&lt;/h2&gt;

&lt;p&gt;The harness resolves agents from the session's &lt;code&gt;.claude/&lt;/code&gt; folder. Sessions run from the workspace root. So link the files up one level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# from workspace/orchestrator/&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ../.claude/agents ../.claude/commands ../.claude/workflows
&lt;span class="k"&gt;for &lt;/span&gt;kind &lt;span class="k"&gt;in &lt;/span&gt;agents commands workflows&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  for &lt;/span&gt;f &lt;span class="k"&gt;in&lt;/span&gt; .claude/&lt;span class="nv"&gt;$kind&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-sfn&lt;/span&gt; &lt;span class="s2"&gt;"../../orchestrator/.claude/&lt;/span&gt;&lt;span class="nv"&gt;$kind&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"../.claude/&lt;/span&gt;&lt;span class="nv"&gt;$kind&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;done
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use per file links, not one folder link. The workspace &lt;code&gt;.claude/&lt;/code&gt; may hold other settings, and other tools add files of their own. Use a relative target so the folder can be moved.&lt;/p&gt;

&lt;p&gt;Symlinks mean an edit in the harness takes effect on the next run. No copy step to forget.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 10: run it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/build-ticket TKT-1234
/build-ticket TKT-1234 --plan-only
/build-ticket TKT-1234 --rounds 3 --dry-run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One note if you build this in Claude Code: workflows are not started on their own. You ask for one, or a command asks for one. Nothing here runs while you are asleep.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it cannot do
&lt;/h2&gt;

&lt;p&gt;Be honest about this part, in your README and to your team.&lt;/p&gt;

&lt;p&gt;The one agent with no typed output is the builder. The only node that writes code hands back prose, and that prose gets quoted into the review prompts. I have not worked out a good shape for "here is what I changed and why". It is the weakest edge in my graph.&lt;/p&gt;

&lt;p&gt;I also have no hours saved number. Nothing in the harness measures duration, so I will not invent one. What I can say is what changed in shape: I type one command and confirm once, instead of running fifty four commands across eight folders. I still read the diff myself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Own folder, next to your projects, no product code in it.&lt;/li&gt;
&lt;li&gt;Write down the things you keep repeating.&lt;/li&gt;
&lt;li&gt;One agent, one job. Take away the tools it must not use.&lt;/li&gt;
&lt;li&gt;Write the loop as code, so you own the control flow.&lt;/li&gt;
&lt;li&gt;Type every handoff, and then check the evidence in code anyway.&lt;/li&gt;
&lt;li&gt;One barrier, where the decision really needs everything.&lt;/li&gt;
&lt;li&gt;Send failures back to whoever should fix them, not always to the top.&lt;/li&gt;
&lt;li&gt;Keep git behind a human yes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you build one, I would like to know where you put your barrier, and where your backward arrows point.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>claude</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Unlocking Seamless Development: Mastering SSH Key Setup for Bitbucket and GitHub in WSL2</title>
      <dc:creator>Ian A. Drilon</dc:creator>
      <pubDate>Sat, 10 Jun 2023 12:29:25 +0000</pubDate>
      <link>https://dev.to/zneret03/how-to-create-ssh-and-connect-to-bitbucketgithub-in-wsl2-5cml</link>
      <guid>https://dev.to/zneret03/how-to-create-ssh-and-connect-to-bitbucketgithub-in-wsl2-5cml</guid>
      <description>&lt;h2&gt;
  
  
  Why use ssh?
&lt;/h2&gt;

&lt;p&gt;Using SSH as more secure option and HTTPS for basic, password-based Git usage. Since SSH is more secure than entering credentials over HTTPS, it is recommended for business dealing with sensitive and critical data. Once you generate you generate and connect your ssh key to your bitbucket/github, you can freely push and pull or make changes to your project repository&lt;/p&gt;

&lt;h3&gt;
  
  
  Why i'm doing this? 🤔🤔
&lt;/h3&gt;

&lt;p&gt;As your fellow developer, i was once struggling to setup my own SSH key to the version control that we are using in a company, by studying and discovering it on my own. I want to share how i setup my own SSH key and connect to the repository with no hassle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's get into it 🔥
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Make sure &lt;code&gt;openssh-server&lt;/code&gt; is installed in your WSL Distro (In my own case i use Ubuntu.22.04 LTS)&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

 &lt;span class="nx"&gt;sudo&lt;/span&gt; &lt;span class="nx"&gt;apt&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="nx"&gt;openssh&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Once the openssh-client installation is complete, in the terminal, check that openssh-client has been successfully installed by running the following command:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;ssh&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;V&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;the output will show the version of openssh you installed&lt;/p&gt;

&lt;h3&gt;
  
  
  Start the SSH agent
&lt;/h3&gt;

&lt;p&gt;To allow &lt;code&gt;git&lt;/code&gt; to use your SSH key, an SSH agent needs to be running in your machine. to do this, write this command in your terminal&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;ps&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;grep&lt;/span&gt; &lt;span class="nx"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;to start the agent:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;you might need this to add in your &lt;code&gt;~/.bashrc&lt;/code&gt; so that if you restart your terminal it will automatically run in the background&lt;/p&gt;

&lt;h3&gt;
  
  
  Create an SSH key 🔑 pair (private/public)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Open your terminal and navigate to your home by writing this command &lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;cd&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Generate your SSH key pair using ssh-keygen, write this command:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;keygen&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="nx"&gt;ed25519&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;C&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username@emaildomain.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="nx"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;&lt;a href="mailto:username@emaildomain.com"&gt;username@emaildomain.com&lt;/a&gt;&lt;/strong&gt; is the email you are using in bitbucket or github&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the &lt;strong&gt;ssh-key-name&lt;/strong&gt; is the name of you ssh key example &lt;strong&gt;work-ssh&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After adding those command, the terminal will ask for you passphrase, &lt;strong&gt;you can either provide a phrase for a password&lt;/strong&gt; or leave it blank, if you input a password each time SSH is used, such as using git command that contact Bitbucket/Github (such as, pull and fetch). Providing password will prevent other users with access to the device from using your keys.&lt;/p&gt;

&lt;p&gt;Once you completed the command above, it will output two files such as &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ssh-key-name&lt;/code&gt; - the &lt;em&gt;private key&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ssh-key-name&lt;/code&gt;.pub - the &lt;em&gt;public key&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These files will be stored in your &lt;code&gt;home&lt;/code&gt; directory, the &lt;code&gt;ssh-key-name&lt;/code&gt;.pub that is the one we will be using to connect in bitbucket/github&lt;/p&gt;

&lt;h3&gt;
  
  
  Add your key 🔑 to the SSH agent
&lt;/h3&gt;

&lt;p&gt;We will now add our key to the ssh agent, write this command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/ssh-key-nam&lt;/span&gt;&lt;span class="err"&gt;e
&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Ater adding our ssh key to ssh agent, we will now be able to add our ssh public key to the bitbucket/github&lt;/p&gt;

&lt;h3&gt;
  
  
  Provide Bitbucket Cloud with your public key 🔑
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;At bitbucket, select your avatar (Your profile and settings) from the upper right screen of your monitor&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Under &lt;strong&gt;Settings&lt;/strong&gt;, Select &lt;strong&gt;Personal Settings&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Under &lt;strong&gt;Security&lt;/strong&gt; tab, select &lt;strong&gt;SSH key&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;in the &lt;strong&gt;Add SSH Key&lt;/strong&gt; it will prompt you a dialog box, where in you can add your ssh key example: &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;ed25529&lt;/span&gt; &lt;span class="nx"&gt;LLoWYaPswHzVqQ7L7B07LzIJbntgmHqrE40t17nGXL71QX9IoFGKYoF5pJKUMvR&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;DZotTm&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;com&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;you will find this key inside of your &lt;code&gt;ssh-key-name&lt;/code&gt;.pub file just copy and paste, don't forget your label. For example &lt;code&gt;Work Machine&lt;/code&gt;. this will help you identify old or unwanted keys in the future.&lt;/p&gt;

&lt;p&gt;after adding your ssh public key, if you received an error that &lt;code&gt;SSH key is invalid&lt;/code&gt;, double check that you copied the entire contents of the public key (&lt;code&gt;.pub&lt;/code&gt; file)&lt;/p&gt;

&lt;h3&gt;
  
  
  Check that your SSH authentication works
&lt;/h3&gt;

&lt;p&gt;To test your ssh key if successfully, open a terminal on your machine and add this command&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;ssh&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="nx"&gt;git&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;bitbucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;org&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;if SSH can successfully connect with Bitbucket using your SSH keys, the command will produce output similar to &lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="nx"&gt;authenticated&lt;/span&gt; &lt;span class="nx"&gt;via&lt;/span&gt; &lt;span class="nx"&gt;ssh&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="nx"&gt;You&lt;/span&gt; &lt;span class="nx"&gt;can&lt;/span&gt; &lt;span class="nx"&gt;use&lt;/span&gt; &lt;span class="nx"&gt;git&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;connect&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;Bitbucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;Shell&lt;/span&gt; &lt;span class="nx"&gt;access&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Provide Github with your public key 🔑&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;Adding SSH key in your github is more simple than you expected&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Copy your SSH public key in your &lt;code&gt;.pub&lt;/code&gt; file&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the upper right corner in your github profile you can click your avatar, and click the &lt;strong&gt;Settings&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxbw61wma9f6l8mxq1wjd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxbw61wma9f6l8mxq1wjd.png" alt="github settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;In the &lt;strong&gt;SSH and GPG keys&lt;/strong&gt; Click the &lt;strong&gt;New SSH key&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;it will redirect you to the page like this&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ivqi066nwxwlxe7ctce.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ivqi066nwxwlxe7ctce.png" alt="SSH key and GPG keys"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Similar to the bitbucket we mentioned earlier, add your public key to the &lt;strong&gt;key&lt;/strong&gt; section and some descriptive title that will help your determine the old and new keys in the future&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select the type of key, either authentication or signing. For more information about commit signing you can refer to this link &lt;a href="https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;after adding all the necessary information, click the &lt;strong&gt;add SSH key&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If everything went well, github will send you the confirmation access directly to your account. For more information, see &lt;a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/sudo-mode" rel="noopener noreferrer"&gt;Sudo mode&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Congratulations 👏👏👏
&lt;/h3&gt;

&lt;p&gt;Congratulations on adding your first SSH key to your favorite or chosen version control system. By this blog post, i hope we can help more developers who are struggling on setting up their first SSH keys on your side projects, side hustles or company project. &lt;/p&gt;

</description>
      <category>ubuntu</category>
      <category>bash</category>
      <category>cli</category>
    </item>
  </channel>
</rss>
