<?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: drmikecrowe</title>
    <description>The latest articles on DEV Community by drmikecrowe (@drmikecrowe).</description>
    <link>https://dev.to/drmikecrowe</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%2F236482%2F461a5f5c-6342-4928-a444-57e99a32b97a.png</url>
      <title>DEV Community: drmikecrowe</title>
      <link>https://dev.to/drmikecrowe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/drmikecrowe"/>
    <language>en</language>
    <item>
      <title>The PR That Fooled Me</title>
      <dc:creator>drmikecrowe</dc:creator>
      <pubDate>Wed, 26 Aug 2026 09:00:00 +0000</pubDate>
      <link>https://dev.to/drmikecrowe/the-pr-that-fooled-me-34dn</link>
      <guid>https://dev.to/drmikecrowe/the-pr-that-fooled-me-34dn</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was written with AI assistance (Claude) for structure and formatting. The PRs, the bug, and the opinions are entirely my own.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The best-looking PR in my repo had a data-loss race in it, and the model that wrote it reviewed its own work and found nothing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I've written before about &lt;a href="https://dev.to/posts/2026/2026-08-24-people-approve-agents-check/"&gt;where AI belongs in software delivery&lt;/a&gt;, and the short version of that post is: people approve, agents check. This post is the origin story. It's three weeks of commit history in one public repo, and I'm going to walk you through it in order, because the order is the argument.&lt;/p&gt;

&lt;h2&gt;
  
  
  Act one: the PR that looked perfect
&lt;/h2&gt;

&lt;p&gt;On July 28 I merged &lt;a href="https://github.com/drmikecrowe/harnessed/pull/169" rel="noopener noreferrer"&gt;PR #169&lt;/a&gt; in &lt;a href="https://github.com/drmikecrowe/harnessed" rel="noopener noreferrer"&gt;harnessed&lt;/a&gt;. Opus wrote most of it. Go look at the PR body, because the body is the point.&lt;/p&gt;

&lt;p&gt;(A word about the repo, since I'm about to link into it four times and I haven't formally announced it. &lt;code&gt;harnessed&lt;/code&gt; is a tool for people who want different configurations of their agentic harness in container isolation: you compose recipes of skills, MCP servers, and services into a named stack, and it launches in a pod with nothing leaking in from your host config. Toolchains come from mise, every dependency is pinned, updates respect a seven-day minimum release age so a poisoned package gets caught before it reaches a build, and images get Snyk and Socket scans at build time. It's my daily driver and I'm dogfooding it hard, but it's alpha: today it supports Claude Code and omp, with more harnesses planned. If that's a tool you've been wanting and you'd like to help shape it, come on in. Otherwise, the announcement post is coming when it's ready for you.)&lt;/p&gt;

&lt;p&gt;It has a measured-cost table justifying the design (a one-line edit to an install script cost 307 seconds through the container build and 4.3 seconds natively, so the installs moved to runtime volumes). It lists two premises that were tested and disproved along the way, with links to where each was recorded instead of quietly dropped. It documents four bugs found during the work, each fixed with a test that fails without the fix.&lt;/p&gt;

&lt;p&gt;That is a better PR writeup than most humans produce. I know because I'm one of the humans. When your AI hands you work at that level of polish, with benchmarks and disproved hypotheses and regression tests, skepticism starts to feel rude. I got comfortable. The polish was doing exactly what polish does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Act two: a second opinion I didn't ask nicely for
&lt;/h2&gt;

&lt;p&gt;Four days later I put up &lt;a href="https://github.com/drmikecrowe/harnessed/pull/185" rel="noopener noreferrer"&gt;PR #185&lt;/a&gt;, a rework of the CLI verbs. Same workflow, same model, same confident writeup: 1,766 tests passing, every removed test accounted for in a table, docs updated.&lt;/p&gt;

&lt;p&gt;This was also the first PR I ran through CodeRabbit, mostly out of curiosity. I had already asked Opus to review its own work. It found nothing worth stopping for.&lt;/p&gt;

&lt;p&gt;CodeRabbit found this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Two concurrent &lt;code&gt;--recipe&lt;/code&gt; invocations with the same recipe set derive the same name. Both can observe &lt;code&gt;preexisting=False&lt;/code&gt; before either mints, so both return a non-&lt;code&gt;None&lt;/code&gt; &lt;code&gt;minted_dir&lt;/code&gt;. [...] If one of the two concurrent launches fails &lt;code&gt;_build_stack&lt;/code&gt; while the other is still using or building the same stack, the failing process deletes a manifest the other process depends on.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A classic check-then-act race, in the one code path where concurrent same-name launches aren't exotic (sharing one stack across repos is the design goal, so two repos starting together is the expected case). A failed build could delete a manifest another live launch was using. Data loss, sitting inside a PR whose test table I had just admired.&lt;/p&gt;

&lt;p&gt;Here's the part that actually rattled me. The race wasn't even introduced by #185. It was pre-existing code that the refactor made visible. The model that wrote it, the model that refactored it, and the model that reviewed both had walked past it every time. A differently-trained model looked at it once and stopped.&lt;/p&gt;

&lt;p&gt;That's when the lesson landed. The author always finds a reason the work is right. That's true of me, and it turns out to be true of a frontier model reviewing its own output. Self-review isn't review.&lt;/p&gt;

&lt;h2&gt;
  
  
  Act three: moving the fight upstream
&lt;/h2&gt;

&lt;p&gt;I didn't fix the race in #185. I scoped it out deliberately (that PR was about CLI grammar, and mixing a concurrency fix into it is how you get neither reviewed well) and recorded it as &lt;a href="https://github.com/drmikecrowe/harnessed/issues/287" rel="noopener noreferrer"&gt;issue #287&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By the time that issue got picked up in mid-August, my process had changed shape. Read the issue comments in order and you can see the new machinery:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;strong&gt;DECISION&lt;/strong&gt; comment recording the fix approach, with current line numbers, so the implementer doesn't re-derive the research.&lt;/li&gt;
&lt;li&gt;A full &lt;strong&gt;SPEC&lt;/strong&gt; posted as a comment, before any code existed, with a line I now consider load-bearing: &lt;em&gt;"Approval of this spec is the gate that authorizes implementation."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;The spec itself went through adversarial review before I approved it, and the review changed it. Poking holes in a document costs minutes. Poking the same holes in an implementation costs a rewrite.&lt;/li&gt;
&lt;li&gt;A human (me) applying the &lt;code&gt;spec-approved&lt;/code&gt; label, timestamped.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That third point deserves a beat. When I started running adversarial reviews against code, the payoff was catching bugs. When I pointed the same treatment at specs, the payoff was bigger: every hole caught at spec time is a hole that never becomes code. The mint-race spec that got implemented was not the spec I first wrote.&lt;/p&gt;

&lt;h2&gt;
  
  
  Act four: what "done" looks like now
&lt;/h2&gt;

&lt;p&gt;The fix landed as &lt;a href="https://github.com/drmikecrowe/harnessed/pull/414" rel="noopener noreferrer"&gt;PR #414&lt;/a&gt;, and its body is an EVIDENCE document. Not a description of the change. Proof, organized so a reviewer can attack it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;spec-to-test mapping&lt;/strong&gt;: eight scenarios from the approved spec, each mapped to a named test.&lt;/li&gt;
&lt;li&gt;A concurrency test that &lt;strong&gt;deterministically fails against the old code&lt;/strong&gt; (two threads claim ownership) and passes with the fix (one does). Not "we added tests." A reproduction first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Five hand-planted mutants&lt;/strong&gt;, all killed, because if your test suite can't catch a deliberate mistake it wasn't going to catch an accidental one.&lt;/li&gt;
&lt;li&gt;Two rounds of &lt;strong&gt;adversarial review&lt;/strong&gt;, findings fixed and re-checked.&lt;/li&gt;
&lt;li&gt;And a section titled &lt;strong&gt;"Not proven"&lt;/strong&gt;: the container build path the suite never executes, the NFS caveat on advisory locks, the mutation tool that wouldn't run in a worktree, the fact that the final SHA's adversarial pass was substituted rather than re-run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last section is the one I'd defend hardest. Evidence that can't tell you what it doesn't cover isn't evidence, it's marketing. The "Not proven" list is what makes the rest of the document worth believing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The confidence is a constant. The quality is not.&lt;/strong&gt; Every PR in this story arrived with the same certainty. One of them carried a data-loss race. Nothing in the presentation distinguishes them, which means the presentation carries no information. You have to attack the work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Self-review doesn't work, for silicon or for me.&lt;/strong&gt; The reviewer needs to carry none of the author's assumptions. A fresh session helps. A different model with different training helps more (with one caveat: frontier models share training data and failure modes, so even two-model review has correlated blind spots; it's a design goal, not a guarantee).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upstream beats downstream.&lt;/strong&gt; Adversarial review of code catches mistakes. Adversarial review of the spec prevents them, at a fraction of the cost. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This costs tokens, and it's the cheapest spend in the budget.&lt;/strong&gt; Every adversarial pass is a second model re-reading finished work. I meant it when I called them the cheapest tokens I spent all year: the alternative was shipping the race and paying for it during someone's demo.&lt;/p&gt;

&lt;p&gt;If you want the team-scale version of this, with gates and human sign-off and the process we're building around it at Pinnacle Solutions Group, that's the &lt;a href="https://dev.to/posts/2026/2026-08-24-people-approve-agents-check/"&gt;People Approve, Agents Check&lt;/a&gt; post. This one is just the receipt trail: one repo, three weeks, and the PR that taught me to stop trusting a clean explanation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Have your own "the AI sounded so sure" stories? Hit me up on &lt;a href="https://github.com/drmikecrowe" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or wherever you found this post.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>codereview</category>
    </item>
    <item>
      <title>People Approve, Agents Check</title>
      <dc:creator>drmikecrowe</dc:creator>
      <pubDate>Mon, 24 Aug 2026 09:00:00 +0000</pubDate>
      <link>https://dev.to/drmikecrowe/people-approve-agents-check-4101</link>
      <guid>https://dev.to/drmikecrowe/people-approve-agents-check-4101</guid>
      <description>&lt;p&gt;&lt;em&gt;This post was written with AI assistance (Claude) for structure and formatting. The design, opinions, and specifics are entirely my own.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I think the industry is measuring the wrong thing, and I think the leaderboard is making software worse.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every vendor pitch I've sat through in the last year answers the same question: how much of the work can the AI do? Percentage of code generated. Tickets closed without a human. Autonomy tiers, like a self-driving car. That's a fine question if you're selling a model, but it's the wrong question if you're responsible for an engineering organization, because it smuggles in two assumptions. The first is that the bottleneck is typing. The second, which nobody says out loud in the meeting but everybody hears, is that a successful AI rollout ends with fewer engineers.&lt;/p&gt;

&lt;p&gt;Both are wrong, and the second one is expensively wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this came from
&lt;/h2&gt;

&lt;p&gt;This started with adversarial code review. I began having an independent reviewer, one carrying none of my assumptions, try to break my finished work instead of blessing it. Its entire brief was to find the input that makes the code fail. That alone was worth the tokens.&lt;/p&gt;

&lt;p&gt;I'll admit why I needed it. I had drifted into trusting the high-end models. When Opus hands you code, it hands it to you with total confidence: no hedging, no "you may want to check this," just a clean explanation of why it did what it did. I got comfortable, and you can probably see where this is going. When I started pointing a second model at that output with instructions to break it, I was genuinely shocked at the gap between how certain the first model sounded and how well the code actually held up. The confidence is a constant. The quality is not. Nothing has made me more careful than watching a model I trust get taken apart by a second model with no stake in being right.&lt;/p&gt;

&lt;p&gt;Then I moved the whole thing upstream. I was already writing specs first, so before any code existed I pointed the same adversarial treatment at the spec itself: find the requirement that can't be tested, find the two bullets that contradict each other, find the thing I assumed and never wrote down.&lt;/p&gt;

&lt;p&gt;That turned out to be even more valuable. My specs were full of holes I could not see, because I was the one who put them there, and every hole caught at spec time was a hole that never became code. Reviewing the output catches mistakes; reviewing the intent prevents them. The tokens I spent arguing with a machine about what I actually meant were the cheapest tokens I spent all year.&lt;/p&gt;

&lt;p&gt;That's the part I couldn't stop thinking about. If an adversarial loop does that for one engineer working alone, what does it do for a team? So we started building it into a real process at Pinnacle Solutions Group, the consulting firm I'm part of, and we're genuinely excited about where it's going. This is us thinking out loud about where AI belongs in software delivery, and I'd rather share it while it's still being shaped than wait until it's a polished case study.&lt;/p&gt;

&lt;p&gt;Two disclaimers. Helping engineering orgs put this in place is work Pinnacle Solutions Group sells, so read the whole thing with that in mind. And it's running on a live client engagement right now, being developed with them and fitted to how they already work. I'm describing a process I believe in and am actively proving, not results I can hand you. When I get to what it costs, I'll be specific about what I don't yet know.&lt;/p&gt;

&lt;p&gt;The personal arc, though, is public if you want to check my work. It played out in the open on a side project of mine: &lt;a href="https://github.com/drmikecrowe/harnessed/pull/185" rel="noopener noreferrer"&gt;the review that converted me&lt;/a&gt;, where a differently-trained reviewer found a concurrency bug in code that self-review had blessed, and &lt;a href="https://github.com/drmikecrowe/harnessed/pull/414" rel="noopener noreferrer"&gt;a complete evidence run&lt;/a&gt;, spec through adversarial review through proof, on the fix for that same finding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delivery breaks at the edges, not in the middle
&lt;/h2&gt;

&lt;p&gt;Think back through the projects that went badly for stupid reasons, not the ones with genuinely hard technical problems. Anecdotally, across the ones I've watched up close, nearly all of them broke at one of two boundaries.&lt;/p&gt;

&lt;p&gt;The first is the handoff from product to engineering. Somebody writes down what they want, and what they write down is incomplete: acceptance criteria you can't actually test, an assumption nobody stated, two requirements that quietly contradict each other. Nobody notices at write time. Somebody notices on day four of the sprint, in a meeting, after the money is already spent.&lt;/p&gt;

&lt;p&gt;The second is the handoff from "the work is done" to "the work is proven." A change gets reviewed by looking at code. Looking at code answers a question (is this reasonable code?) that nobody actually needed answered. The question is whether this change does what product asked for, and how we know.&lt;/p&gt;

&lt;p&gt;The middle, where the typing happens, is mostly fine. That's where every vendor is aiming.&lt;/p&gt;

&lt;p&gt;So we aimed somewhere else, and not out of contrarianism. The speedup in the middle is real, but it's a commodity: every tool on the market delivers it, and your team collects it no matter whose logo is on the invoice. The failures at the edges cost sprints, not minutes, and nobody was selling anything there.&lt;/p&gt;

&lt;p&gt;We put an AI agent at each of those two boundaries, and we gave it a job description with a hard limit on it: &lt;strong&gt;people approve, agents check.&lt;/strong&gt; An agent in this design never approves anything, never merges anything, and never decides what gets built. It reads, it tests, and it reports, and the verdict belongs to a person every time.&lt;/p&gt;

&lt;p&gt;Underneath, there's nothing exotic here. It's spec-driven development feeding test-driven development, wrapped in a workflow that makes both of them non-optional.&lt;/p&gt;

&lt;p&gt;None of the individual pieces is new. Planting a deliberate fault to prove the tests would catch it is mutation testing, an idea from the 1970s. Coverage on changed lines is standard CI configuration. Requirements reviews, design reviews, spec-first, test-first: all decades old. What I'd defend as new is where they sit. When AI writes most of the code, plausible work arrives faster than anyone can judge it, and these old disciplines are how judgment keeps up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gate 1: AI that protects human judgment
&lt;/h2&gt;

&lt;p&gt;The first gate sits between product and engineering. It runs on a story that's headed into refinement, and it answers one question: is this specified well enough to &lt;em&gt;be&lt;/em&gt; refined?&lt;/p&gt;

&lt;p&gt;An agent reads the story and puts it to this test: could an engineer prove, from what is written here, that the intent was met? That question does a lot of work. It forces every acceptance criterion to be verifiable rather than aspirational. It surfaces the assumption sitting under the third bullet that nobody wrote down. It catches the place where criterion two and criterion five can't both be true.&lt;/p&gt;

&lt;p&gt;This is aimed squarely at helping the people who write those stories. Product and marketing are often not technical, and they are being asked to express intent precisely enough for engineers to manifest it. That's a hard thing to do with no feedback loop. Today the feedback arrives four days into the sprint, in a meeting, in front of people. This gate gives them the same feedback in writing, in minutes, from a machine that doesn't sigh.&lt;/p&gt;

&lt;p&gt;A story that passes moves on to refinement. That's not an agent approving the work; it's an agent confirming that product has finished product's job, so engineering can start engineering's job. Nobody's clear, well-specified ticket sits in a queue waiting for a human to rubber-stamp what an agent already confirmed. What gets built is still decided by people in refinement, downstream of this gate, exactly as it was before.&lt;/p&gt;

&lt;p&gt;A story that fails bounces back to whoever wrote it, with written findings. And not vague ones like "this needs work," but specific ones: &lt;em&gt;criterion three has no observable outcome, here is what's missing; criteria one and four conflict under this condition; this assumes the user is already authenticated and the story never says so.&lt;/em&gt; Bad tickets bounce before they cost a sprint, and they bounce with a document instead of a meeting.&lt;/p&gt;

&lt;p&gt;The part that matters most: the gate does not fill in the gaps. It finds them and hands them back.&lt;/p&gt;

&lt;p&gt;The industry instinct is the opposite. Vague requirement? Let the model guess. It'll produce something plausible, and plausible is very close to correct, and the demo will go great. But deciding what the business actually wants is not a gap-filling exercise. It's the highest-judgment work in the building, done by the people with the context and the scars. Automating a plausible guess over the top of that doesn't remove the work, it just hides it until it's expensive.&lt;/p&gt;

&lt;p&gt;So we deliberately preserve the human conversation at exactly the point where the human is the value. What we change is the price and the timing: earlier, cheaper, in writing, before anyone has spent a sprint on the wrong thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Between the gates: building a specification worth implementing
&lt;/h2&gt;

&lt;p&gt;This is where spec-driven development actually lives, and it's the part I had wrong in my own head until recently. I'd lumped it all into "refinement," which is one word doing two jobs.&lt;/p&gt;

&lt;p&gt;Refinement answers &lt;em&gt;is this item ready&lt;/em&gt;: is it clear, is it testable, roughly how big, in what order. That's product's item, and Gate 1 is what protects it. Building the &lt;strong&gt;Implementation Plan&lt;/strong&gt; answers a different question, &lt;em&gt;how will we build this&lt;/em&gt;, and it belongs to the team. Those are two artifacts with two owners, and collapsing them is how you end up with a product owner refereeing a design argument.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pre-assembling the context
&lt;/h3&gt;

&lt;p&gt;Once a story clears Gate 1, an agent reads the codebase against it and assembles a context pack: which subsystems this touches, the patterns already in use there, the tests that cover that ground today, the prior art from the last time somebody did something similar.&lt;/p&gt;

&lt;p&gt;This is small and it's cheap and I think it's one of the highest-leverage pieces in the whole process, because it gets produced while nobody is waiting on it. If your team still holds a refinement meeting, everyone walks in already knowing what the story touches instead of speculating out loud, which is most of what sizing actually needs, without paying for a full design on every item in the backlog. Later, it's the input the planning agent needs, already gathered.&lt;/p&gt;

&lt;p&gt;When it runs is a knob, not a rule. A team that plans ahead can build the pack as soon as the story clears the gate. A team running lean can build it just in time, when an engineer picks up the story, so it reflects the codebase as it is that morning instead of three weeks ago. The orchestration bends to the team; the artifact is what matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Then the plan, when someone picks up the work
&lt;/h3&gt;

&lt;p&gt;An engineer takes the story. An agent drafts the Implementation Plan from the approved Requirements and the context pack: approach, the tests that will prove each acceptance criterion, risks, and how we back it out. A second agent, with none of the first one's context, attacks that draft. The engineer resolves the findings, owns the result, and signs it. Then code gets written.&lt;/p&gt;

&lt;p&gt;That agent draft is not a shortcut around the engineer. It's a better starting point than the engineer would produce alone under sprint pressure, because it actually read forty files instead of pattern-matching off four. What it cannot do is decide whether this approach is right for where the system is going, whether the rollback is real, or whether the risk list is honest. That's the signature.&lt;/p&gt;

&lt;p&gt;And when the plan reveals the estimate was wrong, that's the process working. You find out on day one, in writing, with a reason. A plan can bounce a story back the same way Gate 1 does.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the agent may fill this gap and not the other one
&lt;/h3&gt;

&lt;p&gt;Gate 1 refuses to fill gaps in the Requirements. Here an agent drafts a whole specification. That looks inconsistent. The difference is the cost of a wrong guess.&lt;/p&gt;

&lt;p&gt;A wrong guess about product intent is unrecoverable. Nothing downstream can detect it. Every test passes, the evidence is clean, and you shipped the wrong thing correctly. A wrong guess about technical approach is checkable. The Requirements check it, an adversarial reviewer checks it, and so does what we call &lt;strong&gt;the gauntlet&lt;/strong&gt;: an automated review that holds the implementation to a standard while it's built, with every changed line tested, the tests proven able to catch a deliberately planted fault, and every check the merge gate will run passing before the work is called done. So does the engineer who has to put their name on it. The blast radius is different, so the rule is different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gate 2: AI that forces human understanding
&lt;/h2&gt;

&lt;p&gt;The gauntlet ends with output: check results, coverage of the changed lines, planted faults caught. That output says the code was built to standard. It does not say the right thing was built, and treating those as the same claim is exactly the conflation this gate exists to prevent.&lt;/p&gt;

&lt;p&gt;That's what the &lt;strong&gt;Evidence&lt;/strong&gt; document is for. Its shape is simple: each acceptance criterion from the Requirements, how the change satisfies it, and how that was verified. Tests mapped to specific criteria. The gauntlet's results. Screenshots where the change touches something a person looks at. And one section every Evidence document must carry: what is &lt;em&gt;not&lt;/em&gt; proven. Proof that can't state its own limits isn't proof, it's marketing. The document isn't written at the end as paperwork; it accumulates as the work is built. Evidence is the proof that the acceptance criteria were met, assembled from what the gauntlet produced.&lt;/p&gt;

&lt;p&gt;Gate 2 is the formal check on that proof. An agent validates the Evidence against the Requirements before the change is ever put up for review: does what the gauntlet proved actually prove what product asked for? Then the engineer must read the Evidence against the Requirements and personally decide whether the work meets the intent. The question isn't whether the tests are green; it's whether the thing does what was wanted. The AI assembles the proof, and the engineer renders the verdict and signs their name to it.&lt;/p&gt;

&lt;p&gt;So a piece of work carries three named documents by the time it ships. &lt;strong&gt;Requirements&lt;/strong&gt; says what and how we'll know. &lt;strong&gt;Implementation Plan&lt;/strong&gt; says how, before the build. &lt;strong&gt;Evidence&lt;/strong&gt; proves it. Each one carries a human approval, and each one was checked by an agent first.&lt;/p&gt;

&lt;p&gt;But a signature is cheap. Nothing about signing a document stops an engineer from skimming it and signing anyway. If all this gate did was swap "skim a green build and merge" for "skim an Evidence doc and sign," I'd have added ceremony and called it rigor. A process that only asks for a signature gets signatures.&lt;/p&gt;

&lt;p&gt;That's why the signature isn't the mechanism. The next section is.&lt;/p&gt;

&lt;h2&gt;
  
  
  The forcing function: every change becomes knowledge transfer
&lt;/h2&gt;

&lt;p&gt;The engineer who signed the verdict then uses that same Evidence to walk reviewers and QA through the change, out loud, in front of colleagues. It's not a diff review, it's a narrative: here's what product asked for, here's how this satisfies it, here's how we know.&lt;/p&gt;

&lt;p&gt;That's the part you cannot fake. You can sign a document you didn't read, but you cannot stand in front of two colleagues and explain a change you don't understand, and everyone in the room knows it within about ninety seconds. The Evidence document isn't the enforcement. It's the script for a conversation that &lt;em&gt;is&lt;/em&gt; the enforcement.&lt;/p&gt;

&lt;p&gt;"Out loud" flexes with the team. For a distributed group that means a live call or a recorded walkthrough rather than a conference room, and the rule holds either way: the engineer explains the change to people who can ask questions.&lt;/p&gt;

&lt;p&gt;This is the thing every engineering leader is quietly afraid of: an engineer generating a change they don't understand, skimming a green build, and shipping it. I can't make that structurally impossible; anyone who tells you they can is selling. What I can do is put the moment of "explain this to your peers" between the AI's output and production, and make it the normal cost of shipping. Not understanding your own change stops being invisible.&lt;/p&gt;

&lt;p&gt;And then think about what that does over a quarter. Every single change becomes a teaching moment, delivered by someone who is required to understand it well enough to defend it. The team learns how the system is changing, update by update, from a person rather than from a diff nobody reads. Institutional knowledge stops being a thing that lives in three people's heads and starts being a thing the process produces as a byproduct.&lt;/p&gt;

&lt;p&gt;This is the same set of models everyone else is using, aimed at a different problem. The difference isn't the tooling; it's where the human sits, and what the process makes them do when they get there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your engineers matter more now, not less
&lt;/h2&gt;

&lt;p&gt;Nothing in this design is aimed at needing fewer engineers. It's aimed at raising the quality of what your engineers ship when they work with AI. Those are different goals, and they lead to opposite decisions at nearly every fork.&lt;/p&gt;

&lt;p&gt;A model will produce something plausible for almost any request. Plausible is the dangerous part: it compiles, reads well in review, passes the tests it was written alongside, and quietly does the wrong thing. The only reliable defense is a person who knows the system, knows what was actually asked for, and is equipped to tell the difference. No model removes that need, because judging whether the AI did what was intended is itself a judgment about intent.&lt;/p&gt;

&lt;p&gt;So the volume of code goes up and the value of engineering judgment goes up with it. Every gate exists to put a competent engineer in front of the right question at the right moment, with the evidence in hand to answer it. Gate 1 hands them a request worth building. Gate 2 hands them the proof and makes them rule on it. The walkthrough keeps the whole team sharp enough to keep doing that as the system grows.&lt;/p&gt;

&lt;p&gt;An org that cuts engineers to pay for AI ends up with more code and less capacity to judge it. That is the worst version of this. If your team gets faster, the win is that they take on work they couldn't before, not that there are fewer of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this costs
&lt;/h2&gt;

&lt;p&gt;This is the part vendors skip, so here it is up front: this process is slower than what the market currently expects from AI. Gate 1 adds a loop before refinement. The Implementation Plan adds a draft and an adversarial pass before code. Tests written from acceptance criteria are slower to write than tests written from finished code. Evidence takes real effort to produce, and the walkthrough takes real time from more than one person.&lt;/p&gt;

&lt;p&gt;The walkthrough is the line I expect the most pushback on, and the pushback is fair: multiple colleagues per change, out loud, does not scale like a CI job. I'm not going to argue it away, because it's the deliberate purchase. Quality of what ships and a team that understands what shipped are the two things this whole design exists to buy, and the walkthrough is where both get paid for. How deep it goes, and for which changes, is one of the things the current engagement is calibrating.&lt;/p&gt;

&lt;p&gt;It costs more tokens, too. Every adversarial pass is a second model reading work the first model already did. The context pack is a full codebase read before anyone has picked up the story. Attacking a spec, then attacking an implementation plan, then validating Evidence against the Requirements: none of that is free, and it is spend you would not have if you simply asked a model for the code and merged what came back.&lt;/p&gt;

&lt;p&gt;I think it's the best money in the budget. I called those the cheapest tokens I spent all year and I meant it literally: you are buying rework you never have to do, at a price you can see up front. But it's a real line item, it's larger than a single-agent workflow, and a client should agree to it going in rather than discover it on the first invoice. Spend ceilings and usage alerts go in on day one for exactly that reason. Adoption shouldn't come with an open tab.&lt;/p&gt;

&lt;p&gt;The middle is genuinely faster. An engineer with AI leverage and a clear spec outruns the same engineer without it, and it isn't close. But that gain is smaller than the number on the slide, because we are deliberately spending part of it on tests and proof.&lt;/p&gt;

&lt;p&gt;That said, the paragraphs above make this sound slower than it is. Most of what this process standardizes should already be standard operating procedure in most organizations: tests tied to the requirements, review that checks intent, proof that the work does what was asked. I'm not piling new work on top of delivery. What I'm mostly doing is refusing to let AI's speed become the excuse to skip work that was always owed. The velocity this trims is the false kind: the perceived gains of merging plausible code fast and paying it back later as rework.&lt;/p&gt;

&lt;p&gt;So what I'm claiming is confidence. Fewer sprints spent building the wrong thing, fewer changes that pass review and fail intent, and a team that understands what it shipped. If the number you're optimizing is lines per day, I'm the wrong call. If it's the ratio of shipped work that actually did what was asked, this is the trade I'd make.&lt;/p&gt;

&lt;h2&gt;
  
  
  What holds it up
&lt;/h2&gt;

&lt;p&gt;The gates only work if what they're checking is real. Two supporting pieces do that work.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The gauntlet.&lt;/strong&gt; Tests come from the acceptance criteria, not from the finished code, so they verify what was asked for instead of confirming whatever got built. The gauntlet's job is to prove that testing is real: every changed line covered, and planted faults confirming the suite would catch a genuine mistake. A suite that can't catch a deliberate mistake wasn't going to catch an accidental one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adversarial review.&lt;/strong&gt; An independent AI reviewer, carrying none of the author's assumptions, is briefed to break the work: find the input that makes it fail. Confirmed findings get fixed and re-checked. This is where the whole thing started for me, and it's still the piece I trust most. Self-review doesn't work. The author always finds a reason the work is right. One caveat: two frontier models share training data and failure modes, so this independence is a design goal with limits. The reviewer has none of the author's context, and that catches a lot, but it can still share the author's blind spots.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where this doesn't fit
&lt;/h2&gt;

&lt;p&gt;This process assumes you know what you're building. Exploratory work, spikes, R&amp;amp;D where the spec is the thing you're trying to discover: gating that behind Requirements is backwards, and we don't try. The honest artifact there is a timebox and a writeup, and the process starts after.&lt;/p&gt;

&lt;p&gt;It also assumes changes worth the ceremony. A dependency bump or a typo fix doesn't need an Implementation Plan and a walkthrough, and a process that pretends it does will get routed around within a month. Part of what we're calibrating with the current client is exactly that tiering: which changes get the full treatment, which get the gauntlet alone, and who decides.&lt;/p&gt;

&lt;p&gt;And a three-person team probably shouldn't run this as written. The walkthrough is priced for teams big enough that knowledge silos are a real risk. Below that size everyone already knows every change, and the gates worth keeping are the two adversarial reviews.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it lands in your org
&lt;/h2&gt;

&lt;p&gt;We customize the gates to fit, and the fit runs in two directions. The gates can hook onto the states your team already has: no new methodology, nothing replaced, no migration. Or they can anchor a leaner workflow, because once agents build the context, draft the plan, and prove the work, some of your ceremonies stop earning their place on the calendar, and a team that notices is free to act on it. Which direction to take is your call. The gates hold either way.&lt;/p&gt;

&lt;p&gt;And it's built to end. We lead the first run, coach the second, and step back to review only for the third. If your team still needs us on run four, we designed it wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to ask the next vendor, including us
&lt;/h2&gt;

&lt;p&gt;If you're evaluating AI in your delivery process, the autonomy percentage on the slide tells you almost nothing about whether your delivery will improve. Three better questions, and I'd expect to answer all of them myself:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where does the human judgment live, and what protects it?&lt;/strong&gt; If the answer is that the AI fills in whatever product left vague, you're buying a machine that converts unclear thinking into shipped code at high speed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you prove the work meets the intent?&lt;/strong&gt; If the answer is "code review," ask what question code review actually answers. Then ask who on the team will be able to explain the change six months from now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does this slow down?&lt;/strong&gt; Any honest process has a cost. A vendor who reports only acceleration is either not measuring or not telling you.&lt;/p&gt;

&lt;p&gt;And ask what the vendor thinks happens to your engineers. If the pitch is a headcount line, walk. We didn't get here by asking how much of the job the AI could take. We asked where delivery breaks, and the answer was the same two places it's always broken. The machines are genuinely useful there. They just aren't in charge there, and they need better engineers around them than they did before, not fewer.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is what I think about most days right now. We're building it with a client as I write this, shaping it to their process rather than handing them ours, and the parts that survive contact with a real team are the parts worth keeping. If you're wrestling with the same question of where AI belongs in your delivery process, and how you'd prove it worked, I'd genuinely like to hear how you're approaching it, whether or not you ever talk to us. Find me on &lt;a href="https://github.com/drmikecrowe" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or wherever you found this post.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>sdlc</category>
      <category>agents</category>
    </item>
    <item>
      <title>Context Binoculars: Understanding LLM Context Windows</title>
      <dc:creator>drmikecrowe</dc:creator>
      <pubDate>Sat, 13 Sep 2025 04:00:00 +0000</pubDate>
      <link>https://dev.to/drmikecrowe/context-binoculars-understanding-llm-context-windows-2h95</link>
      <guid>https://dev.to/drmikecrowe/context-binoculars-understanding-llm-context-windows-2h95</guid>
      <description>&lt;p&gt;Remember as a kid (or in my case as an adult) when you pulled out binoculars and gazed around the room? It’s a bit disorienting for sure, but also fun. And I guess if you’ve ever wondered about what I do for fun, now you know.&lt;/p&gt;

&lt;p&gt;It struck me the other day that this was the ideal representation for how LLM context works. While my demonstration you’re going to see below is extreme, this is the reality of what we’re facing when we’re trying to get LLMs to do big projects. The context window is everything, and you’re hearing more and more about context engineering in online articles. Ensuring that the LLM has exactly the right context greatly improves the accuracy of the results that you get out the other end.&lt;/p&gt;

&lt;p&gt;There's not much more to this article. All I'm really trying to do is viscerally explain what the context window is and give you a feeling of why managing your context is so important.&lt;/p&gt;

&lt;p&gt;Check out the visual demonstration here: &lt;a href="https://drmikecrowe.github.io/context-binoculars/" rel="noopener noreferrer"&gt;context-binoculars&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>How I Taught My AI Pair Programmer to Be Our Team's Tailwind CSS Cop - Part 3 of 6</title>
      <dc:creator>drmikecrowe</dc:creator>
      <pubDate>Sun, 07 Sep 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/drmikecrowe/how-i-taught-my-ai-pair-programmer-to-be-our-teams-tailwind-css-cop-part-3-of-6-b0o</link>
      <guid>https://dev.to/drmikecrowe/how-i-taught-my-ai-pair-programmer-to-be-our-teams-tailwind-css-cop-part-3-of-6-b0o</guid>
      <description>&lt;p&gt;&lt;strong&gt;Part 3 of 6: The AI-Assisted Development Workflow Series&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the third installment in a six-part series exploring how AI is transforming modern development workflows. In this series, I’ll walk through my journey of building an AI-assisted development environment, from basic infrastructure setup to advanced architectural enforcement and task orchestration.&lt;/p&gt;




&lt;p&gt;We’ve all been there. You start a new project with Tailwind CSS, and everything is beautiful. The utility-first approach is fast, flexible, and keeps you right in your HTML. But as the project grows and the team expands, the CSS landscape can start to feel like the Wild West. Utility classes get sprinkled everywhere, components start to blur the lines between structure and style, and soon you’re overriding margins and fighting for specificity.&lt;/p&gt;

&lt;p&gt;I love Tailwind, but I knew we needed to introduce some architectural discipline before things got out of hand. The problem is, architectural rules are only as good as their enforcement. Documentation gets stale, and nagging in code reviews doesn’t scale.&lt;/p&gt;

&lt;p&gt;So, I had a thought. What if I could teach our team’s AI pair programmer to be our automated style cop? What if it could not only understand our rules but actively enforce them?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Convention: A Three-Tier System for Sanity
&lt;/h2&gt;

&lt;p&gt;First, we needed a clear, simple convention. We decided on a three-tier component architecture designed to enforce a strict separation of concerns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Layout Components (&lt;code&gt;src/components/layout/&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UI Components (&lt;code&gt;src/components/ui/&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Feature &amp;amp; Page Components (&lt;code&gt;src/pages/&lt;/code&gt;, etc.)&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This system ensures that our UI components are truly reusable and that our layout logic is centralized and predictable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Hiring an AI for Architectural Enforcement
&lt;/h2&gt;

&lt;p&gt;With the convention defined, I turned to my AI assistant. I described the three-tier system and asked, “Can you build a custom ESLint rule to enforce this?”&lt;/p&gt;

&lt;p&gt;Working with the AI was like having a hyper-competent junior developer who knew the ESLint AST (Abstract Syntax Tree) better than I ever will. I provided the high-level architectural goals, and it handled the implementation details.&lt;/p&gt;

&lt;h3&gt;
  
  
  How the Custom Plugin Works
&lt;/h3&gt;

&lt;p&gt;The resulting ESLint rule is a simple but powerful detective. For every component file, it performs three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Location Check&lt;/strong&gt; : First, it looks at the component’s file path. Does it live in &lt;code&gt;src/components/layout/&lt;/code&gt;, &lt;code&gt;src/components/ui/&lt;/code&gt;, or somewhere else? This determines which rulebook to apply.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Class Inspection&lt;/strong&gt; : Next, it parses the JSX and extracts all the Tailwind classes from the &lt;code&gt;className&lt;/code&gt; prop. It’s smart enough to handle string literals and basic template literals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rule Application&lt;/strong&gt; : Finally, it applies the logic:

&lt;ul&gt;
&lt;li&gt;If it’s a &lt;strong&gt;UI Component&lt;/strong&gt; , it scans the class list for any margin patterns (&lt;code&gt;m-*&lt;/code&gt;, &lt;code&gt;mx-*&lt;/code&gt;, &lt;code&gt;mt-*&lt;/code&gt;, etc.) and flags them as errors.&lt;/li&gt;
&lt;li&gt;If it’s a &lt;strong&gt;Layout Component&lt;/strong&gt; , it checks for any non-layout classes (like &lt;code&gt;bg-red-500&lt;/code&gt;, &lt;code&gt;font-bold&lt;/code&gt;, etc.) and flags those.&lt;/li&gt;
&lt;li&gt;If it’s a &lt;strong&gt;Feature/Page Component&lt;/strong&gt; , it does nothing. They are free to compose as they see fit.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The AI generated the rule file, a utility file to hold the class definitions, and even modified our main &lt;code&gt;eslint.config.js&lt;/code&gt; to wire it all up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Results: The Moment of Truth
&lt;/h2&gt;

&lt;p&gt;I ran the linter across our codebase, and it worked perfectly. It immediately flagged several components that were violating our new convention. Here’s a summary of what it caught:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AppLayout.tsx &amp;amp; Sidebar.tsx&lt;/strong&gt; : These core layout components were caught using styling classes like &lt;code&gt;bg-gray-100&lt;/code&gt;, &lt;code&gt;text-purple-700&lt;/code&gt;, and &lt;code&gt;font-bold&lt;/code&gt;. The linter correctly identified these as style concerns that should not be in a layout component.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EmptyState.tsx &amp;amp; ErrorState.tsx&lt;/strong&gt; : These UI components had &lt;code&gt;mb-4&lt;/code&gt; and &lt;code&gt;mt-2&lt;/code&gt; margins, violating our “marginless UI” rule. The parent component should be responsible for this spacing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FileDropzone.tsx &amp;amp; MainContent.tsx&lt;/strong&gt; : These UI components were using &lt;code&gt;mx-auto&lt;/code&gt;, another margin-related property that is the responsibility of a layout container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;StatCard.tsx&lt;/strong&gt; : This UI component was using &lt;code&gt;mt-1&lt;/code&gt; and &lt;code&gt;ml-1&lt;/code&gt;, which are small but important violations of the architectural pattern.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these warnings was a clear, actionable task for us to clean up the codebase and align it with our new, more maintainable architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: More Than Just Code Generation
&lt;/h2&gt;

&lt;p&gt;This experience was a powerful demonstration of how to use LLMs for more than just generating boilerplate or fixing simple bugs. By teaching the AI our team’s specific architectural patterns, we were able to turn it into a partner for enforcing code quality and consistency at scale. It automated the tedious work of code validation, freeing us up to focus on building features.&lt;br&gt;&lt;br&gt;
tures.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>productivity</category>
      <category>mcp</category>
    </item>
    <item>
      <title>The Ultimate Design Review: Orchestrating AI with Task-Based Workflows - Part 6 of 6</title>
      <dc:creator>drmikecrowe</dc:creator>
      <pubDate>Sun, 07 Sep 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/drmikecrowe/the-ultimate-design-review-orchestrating-ai-with-task-based-workflows-part-6-of-6-1km0</link>
      <guid>https://dev.to/drmikecrowe/the-ultimate-design-review-orchestrating-ai-with-task-based-workflows-part-6-of-6-1km0</guid>
      <description>&lt;p&gt;&lt;strong&gt;Part 6 of 6: The AI-Assisted Development Workflow Series&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the final installment in our six-part series on building an AI-assisted development workflow. We’ve set up our infrastructure, taught the AI our coding standards, and established a robust “memory bank” of project rules. Now, it’s time to put it all to the test with the ultimate challenge: a comprehensive, end-to-end design review of the entire codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Want to see what we're building? I've created two example posts that show the actual output from this automated design review system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://mikesshinyobjects.tech/posts/2025/2025-09-06-1-design-review-feedback/" rel="noopener noreferrer"&gt;Design Review Feedback Example&lt;/a&gt;&lt;/strong&gt; - See the comprehensive analysis that the AI generates, including architectural strengths, critical issues, and detailed findings across all layers of the codebase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://mikesshinyobjects.tech/posts/2025/2025-09-06-2-design-review-action-items/" rel="noopener noreferrer"&gt;Design Review Action Items Example&lt;/a&gt;&lt;/strong&gt; - See how the AI transforms the analysis into a structured, actionable backlog with 18 major tasks and 72 subtasks, each with clear implementation steps and acceptance criteria.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These examples show the real output from running this system on an actual codebase. The AI identified critical architectural violations, generated specific improvement tasks, and created a comprehensive project plan - all automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Series Overview:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 1:&lt;/strong&gt; &lt;a href="https://dev.to/drmikecrowe/mcp-servers-ports-and-sharing-part-1-of-6-4agk"&gt;MCP Servers, Ports, and Sharing - Setting up the foundation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 2:&lt;/strong&gt; &lt;a href="https://dev.to/drmikecrowe/my-eslint-config-was-a-mess-i-asked-an-ai-to-fix-it-part-2-of-6-4g47"&gt;ESLint Configuration Refactoring - Cleaning up tooling with AI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3:&lt;/strong&gt; &lt;a href="https://dev.to/drmikecrowe/how-i-taught-my-ai-pair-programmer-to-be-our-teams-tailwind-css-cop-part-3-of-6-b0o"&gt;Custom Architectural Rules - Teaching AI to enforce design patterns&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4:&lt;/strong&gt; &lt;a href="https://dev.to/drmikecrowe/from-linter-chaos-to-orchestrated-tasks-part-4-of-6-23ah"&gt;Task Orchestration - Managing complex refactoring workflows&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 5:&lt;/strong&gt; &lt;a href="https://dev.to/drmikecrowe/project-rules-for-ai-part-5-of-6-560c"&gt;Project Rules for AI - Creating effective memory banks and guidelines&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 6:&lt;/strong&gt; &lt;a href="https://dev.to/drmikecrowe/the-ultimate-design-review-orchestrating-ai-with-task-based-workflows-part-6-of-6-1km0"&gt;The Ultimate Design Review - Putting it all together&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Challenge: From “Codebase” to “Action Plan”
&lt;/h2&gt;

&lt;p&gt;How do you review an entire &lt;code&gt;src/&lt;/code&gt; directory? You can’t just feed it to an AI and say, “find problems.” The context is too large, the potential feedback is too broad, and the output would be a wall of text, not an actionable plan. To understand the issue, grab a pair of binoculars and use them in your office: Have a coworker toss an index card somewhere in your office and then search for that with the binoculars. If you have a really large code base, have your coworker put it in the office somewhere. That’s what your fighting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The binoculars symbolize the AI context. It’s a fixed width window of information the AI can maintain&lt;/li&gt;
&lt;li&gt;The index card is what you are asking the AI to find&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My goal was to create a system that could:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Systematically analyze the entire codebase in manageable chunks.&lt;/li&gt;
&lt;li&gt;Evaluate each chunk against multiple, specific architectural rules.&lt;/li&gt;
&lt;li&gt;Produce a structured, prioritized, and actionable list of tasks.&lt;/li&gt;
&lt;li&gt;Be entirely automated and repeatable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To achieve this, I created a multi-layered prompting strategy that uses the &lt;a href="https://github.com/pinkpixel-dev/taskflow-mcp" rel="noopener noreferrer"&gt;TaskFlow MCP server&lt;/a&gt; to orchestrate the entire workflow. The process is driven by two key prompt files.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Master Plan: &lt;code&gt;new-design-review.md&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The first file, &lt;code&gt;@memory-bank/prompts/design-review/new-design-review.md&lt;/code&gt;, is the high-level orchestrator. It doesn’t perform the analysis itself; it instructs the AI on how to set up the entire project plan within TaskFlow tasks.&lt;/p&gt;

&lt;p&gt;Here’s the strategy it lays out:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create Two Buckets:&lt;/strong&gt; The first thing it does is create two separate requests in the task server: one for the &lt;em&gt;analysis process&lt;/em&gt; and one for the &lt;em&gt;action items&lt;/em&gt;. This is a critical separation of concerns. The analysis tasks will be marked as “done” once the review is complete, but the action items will form a living backlog of work to be done.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Divide and Conquer:&lt;/strong&gt; It then breaks the entire &lt;code&gt;src/&lt;/code&gt; directory into logical chunks: Core App Files, Components, Services, Machines, Hooks, etc. For each chunk, it creates a parent task in the “analysis” request.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Mini-Review Subtasks:&lt;/strong&gt; This is where the granularity comes in. For each parent task (e.g., &lt;code&gt;DR Part 3 - Components (Dashboard)&lt;/code&gt;), the prompt instructs the AI to create four specific subtasks:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Instruction Manual: &lt;code&gt;request-2-instructions.md&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If the first prompt is the “what,” then &lt;code&gt;@memory-bank/prompts/design-review/request-2-instructions.md&lt;/code&gt; is the “how.” This file is a set of instructions for the AI on how to behave &lt;em&gt;while it is executing&lt;/em&gt; the analysis subtasks.&lt;/p&gt;

&lt;p&gt;Its most critical directive is this: &lt;strong&gt;You MUST create new tasks in the “Action Items” request for every issue you find.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It strictly forbids the AI from simply summarizing its findings. Instead, it must translate every identified improvement into a well-formed task, complete with a title, description, and severity, and add it to the other request bucket. This is what turns a qualitative review into a quantitative project plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Results: An Actionable Backlog
&lt;/h2&gt;

&lt;p&gt;After running the AI through this workflow, the &lt;code&gt;taskflow-mcp&lt;/code&gt; server populates a YAML file with the results. The file from my design review, &lt;code&gt;@DESIGN-REVIEWS/2025-08-19/tasks.yaml&lt;/code&gt;, is over 800 lines long, but here are a few snippets that show the power of this system.&lt;/p&gt;

&lt;p&gt;First, you can see the completed analysis task for the “Core App Files,” with its four subtasks all marked as done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- id: task-62
  title: DR Part 1 - Core App Files
  description: |-
    Analyze the following files for design patterns, code quality, and architectural consistency:nn- src/App.tsxn- src/appMachineContext.tsn- src/config.tsn- src/context/DashboardDataContext.tsx
    ...
  done: true
  subtasks:
    - id: subtask-71
      title: Overall Design Analysis
      done: true
    - id: subtask-72
      title: Effect Pattern Analysis
      done: true
    - id: subtask-73
      title: Component Styling Analysis
      done: true
    - id: subtask-74
      title: State Machine Analysis
      done: true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More importantly, the &lt;em&gt;real&lt;/em&gt; output is the list of actionable tasks generated in the other request. The AI identified dozens of specific, granular improvements, each perfectly formatted according to the instruction prompt.&lt;/p&gt;

&lt;p&gt;Here are a few examples of the generated tasks:&lt;/p&gt;

&lt;p&gt;A high-priority refactoring task based on our Effect pattern rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- id: task-98
  title: Add Effect-based validation to ExcelService
  description: ExcelService.ts contains complex validation logic in generateExpenseReport function. Add Effect-based validation using E.succeed().pipe(E.filterOrFail()) pattern for report and expense data validation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A task to fix a clear violation of our Tailwind CSS architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- id: task-139
  title: Fix EmptyState.tsx UI component margin violations
  description: Remove margin classes from EmptyState.tsx component. The component contains margin classes mb-4, mb-2, mb-6 that should be handled by parent layout components. Read memory-bank/AGENTS.md and review memory-bank/project-rules/* before starting.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A task for a significant architectural improvement to break up a monolithic file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- id: task-99
  title: Extract ExcelService helper functions to separate modules
  description: ExcelService.ts is a 485-line monolith with many helper functions. Extract helper functions into separate modules (excel-formatting.ts, excel-data.ts, excel-styles.ts) to improve maintainability and testability.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion: The Sum of the Parts
&lt;/h2&gt;

&lt;p&gt;This automated design review is the culmination of our entire AI-assisted workflow. It seamlessly integrates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Agent Protocol (&lt;code&gt;AGENTS.md&lt;/code&gt;):&lt;/strong&gt; Provides the core principles for the AI’s analysis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Project Rules (&lt;code&gt;project-rules/&lt;/code&gt;):&lt;/strong&gt; Act as the specific, enforceable standards for the review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Prompting Strategy (&lt;code&gt;prompts/&lt;/code&gt;):&lt;/strong&gt; Orchestrates the complex workflow, breaking it down into manageable steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Task Server (&lt;code&gt;taskflow-mcp&lt;/code&gt;):&lt;/strong&gt; Captures the output, transforming a review into an actionable project backlog.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By investing the time to teach the AI &lt;em&gt;how&lt;/em&gt; we build software, we’ve unlocked the ability to automate one of the most time-consuming and critical parts of the development lifecycle. We’ve moved beyond simple code generation to a world where our AI partner can actively help us manage quality, reduce technical debt, and enforce architectural consistency at scale. It’s a powerful glimpse into the future of software development.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>productivity</category>
      <category>mcp</category>
    </item>
    <item>
      <title>From Linter Chaos to Orchestrated Tasks - Part 4 of 6</title>
      <dc:creator>drmikecrowe</dc:creator>
      <pubDate>Sun, 07 Sep 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/drmikecrowe/from-linter-chaos-to-orchestrated-tasks-part-4-of-6-23ah</link>
      <guid>https://dev.to/drmikecrowe/from-linter-chaos-to-orchestrated-tasks-part-4-of-6-23ah</guid>
      <description>&lt;p&gt;&lt;strong&gt;Part 4 of 6: The AI-Assisted Development Workflow Series&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the fourth installment in a six-part series exploring how AI is transforming modern development workflows. In this series, I’ll walk through my journey of building an AI-assisted development environment, from basic infrastructure setup to advanced architectural enforcement and task orchestration.&lt;/p&gt;




&lt;p&gt;In my last post, I talked about how my AI pair programmer and I created a custom ESLint rule to enforce our new frontend architecture. It was a huge success. The linter, now armed with our specific rules, scanned the codebase and… gave us a giant list of things to fix.&lt;/p&gt;

&lt;p&gt;This is a classic “good news, bad news” scenario. The good news? We had a precise, automated way to detect architectural drift. The bad news? We had a long, intimidating wall of terminal output listing every single violation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/home/mcrowe/.../AlertDialog.tsx
  19:8 warning Unregistered class detected: data-[state=open]:animate-in
  19:37 warning Unregistered class detected: data-[state=closed]:animate-out
...
/home/mcrowe/.../EmptyState.tsx
  25:21 error UI Component contains a forbidden margin class: 'mb-4'
...
✖ 60 problems (2 errors, 58 warnings)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A raw list of errors is a starting point, but it’s not a plan. How do you manage this work? How do you distribute it? How do you ensure each fix is made with the right context? Just handing a developer (or an AI) a raw log is inefficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tool: TaskFlow MCP for Orchestration
&lt;/h2&gt;

&lt;p&gt;This is where another tool in our AI-assisted workflow comes in: &lt;a href="https://github.com/pinkpixel-dev/taskflow-mcp" rel="noopener noreferrer"&gt;TaskFlow MCP&lt;/a&gt;. It’s an open-source task orchestration server designed specifically for managing development work between humans and AI agents.&lt;/p&gt;

&lt;p&gt;Instead of just working from a simple prompt, TaskFlow allows us to break down a high-level request (like “Fix all these linter warnings”) into a structured plan of discrete, trackable tasks.&lt;/p&gt;

&lt;p&gt;Crucially, as the user, I can insist that &lt;strong&gt;each task be configured with specific context and instructions&lt;/strong&gt;. This is the killer feature. It turns a simple “fix this” command into a rich, context-aware work order.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Process: Turning Chaos into a Plan
&lt;/h2&gt;

&lt;p&gt;I fed the messy ESLint output to my AI assistant and gave it a new prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"For each of the UI warnings below, use &lt;code&gt;plan_task&lt;/code&gt; to create a ‘Fix UI layout vs. styling overlap’ request. For each violation, create a sub-task. Ensure that each task specifically includes these instructions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read &lt;code&gt;memory-bank/AGENTS.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Review &lt;code&gt;memory-bank/project-rules/*&lt;/code&gt;"&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;The AI, using the TaskFlow MCP tool, did exactly that. It parsed the linter output, grouped the violations by file, and created a formal plan. Each task was not just a file path and a line number; it was a complete work package containing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A Clear Title&lt;/strong&gt; : e.g., “Fix AppLayout.tsx layout component styling violations”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Detailed Description&lt;/strong&gt; : Explaining exactly which classes were violating our architectural rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embedded Context&lt;/strong&gt; : The prerequisite instructions to review our project’s agent protocols and architectural rules before starting work.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Result: An Actionable, Context-Aware Plan
&lt;/h2&gt;

&lt;p&gt;Tasks are maintained locally&lt;/p&gt;

&lt;p&gt;The output wasn’t a log file; it was a professional task report, ready for any developer—human or AI—to pick up and execute. It transformed a chaotic list of problems into a manageable, parallelizable set of tasks.&lt;/p&gt;

&lt;p&gt;Here is the exact markdown file that the tool generated. It’s clear, organized, and ensures that whoever does the work has the full context required to do it right.&lt;/p&gt;




&lt;h1&gt;
  
  
  Task Status Report: Fix UI layout vs. styling overlap issues identified in ESLint warnings
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Generated on: 2025-08-17&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overall Progress: 0%
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Total Tasks:&lt;/strong&gt; 9&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Completed Tasks:&lt;/strong&gt; 0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approved Tasks:&lt;/strong&gt; 0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remaining Tasks:&lt;/strong&gt; 9&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Task Status
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Fix AppLayout.tsx layout component styling violations (🔄 In Progress)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Remove non-layout classes from AppLayout.tsx component. The component contains styling classes like bg-gray-100, rounded-md, hover:bg-gray-100, focus:ring-2, text-purple-700, ml-2, text-lg, font-bold, text-gray-900, bg-opacity-30, bg-black that should be moved to separate styling components or variants. Read memory-bank/AGENTS.md and review memory-bank/project-rules/* before starting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; 🔄 In Progress&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Approval:&lt;/strong&gt; ⏳ Not Ready&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Fix Sidebar.tsx layout component styling violations (🔄 In Progress)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Remove non-layout classes from Sidebar.tsx component. The component contains styling classes like ml-2, text-lg, font-bold, tracking-tight, text-gray-900, mt-4, text-sm, text-gray-500, hover:text-gray-700, rounded-md, font-medium, text-gray-600, transition-colors, hover:bg-blue-50, hover:text-blue-700, text-gray-400, mt-2 that should be moved to separate styling components or variants. Read memory-bank/AGENTS.md and review memory-bank/project-rules/* before starting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; 🔄 In Progress&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Approval:&lt;/strong&gt; ⏳ Not Ready&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Fix EmptyState.tsx UI component margin violations (🔄 In Progress)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Remove margin classes from EmptyState.tsx component. The component contains margin classes mb-4, mb-2, mb-6 that should be handled by parent layout components. Read memory-bank/AGENTS.md and review memory-bank/project-rules/* before starting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; 🔄 In Progress&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Approval:&lt;/strong&gt; ⏳ Not Ready&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Fix ErrorState.tsx UI component margin violations (🔄 In Progress)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Remove margin classes from ErrorState.tsx component. The component contains margin classes mt-4, mt-2 that should be handled by parent layout components. Read memory-bank/AGENTS.md and review memory-bank/project-rules/* before starting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; 🔄 In Progress&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Approval:&lt;/strong&gt; ⏳ Not Ready&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Fix FileDropzone.tsx UI component margin violations (🔄 In Progress)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Remove margin classes from FileDropzone.tsx component. The component contains margin classes mx-auto, mr-2 that should be handled by parent layout components. Read memory-bank/AGENTS.md and review memory-bank/project-rules/* before starting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; 🔄 In Progress&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Approval:&lt;/strong&gt; ⏳ Not Ready&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Fix MainContent.tsx UI component margin violations (🔄 In Progress)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Remove margin classes from MainContent.tsx component. The component contains margin class mx-auto that should be handled by parent layout components. Read memory-bank/AGENTS.md and review memory-bank/project-rules/* before starting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; 🔄 In Progress&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Approval:&lt;/strong&gt; ⏳ Not Ready&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Fix StatCard.tsx UI component margin violations (🔄 In Progress)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Remove margin classes from StatCard.tsx component. The component contains margin classes mt-1, mt-2, ml-1 that should be handled by parent layout components. Read memory-bank/AGENTS.md and review memory-bank/project-rules/* before starting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; 🔄 In Progress&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Approval:&lt;/strong&gt; ⏳ Not Ready&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Fix ClearSearchButton.tsx UI component margin violations (🔄 In Progress)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Remove margin classes from ClearSearchButton.tsx component. The component contains margin class my-auto that should be handled by parent layout components. Read memory-bank/AGENTS.md and review memory-bank/project-rules/* before starting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; 🔄 In Progress&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Approval:&lt;/strong&gt; ⏳ Not Ready&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Fix PageHeader.tsx UI component margin violations (🔄 In Progress)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Remove margin classes from PageHeader.tsx component. The component contains margin class mt-1 that should be handled by parent layout components. Read memory-bank/AGENTS.md and review memory-bank/project-rules/* before starting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Status:&lt;/strong&gt; 🔄 In Progress&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Approval:&lt;/strong&gt; ⏳ Not Ready&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>productivity</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Project Rules for AI - Part 5 of 6</title>
      <dc:creator>drmikecrowe</dc:creator>
      <pubDate>Sun, 07 Sep 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/drmikecrowe/project-rules-for-ai-part-5-of-6-560c</link>
      <guid>https://dev.to/drmikecrowe/project-rules-for-ai-part-5-of-6-560c</guid>
      <description>&lt;p&gt;&lt;strong&gt;Part 5 of 6: The AI-Assisted Development Workflow Series&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the fifth installment in a six-part series exploring how AI is transforming modern development workflows. In this series, I’ll walk through my journey of building an AI-assisted development environment, from basic infrastructure setup to advanced architectural enforcement and task orchestration.&lt;/p&gt;




&lt;p&gt;In the previous posts, we’ve taught our AI assistant &lt;em&gt;how&lt;/em&gt; to understand and enforce specific architectural rules. We’ve built a robust system for linting, testing, and managing complex tasks. But how do we ensure the AI behaves consistently and predictably over the long term? How do we give it a “personality” and a “memory” that aligns with our project’s philosophy?&lt;/p&gt;

&lt;p&gt;The answer lies in creating a comprehensive set of guidelines that the AI can consult before every action. In my setup, this is split into two key parts: the &lt;strong&gt;Agent Protocol&lt;/strong&gt; and the &lt;strong&gt;Project Rules&lt;/strong&gt;. Together, they form the “brain” of my AI development partner.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Agent Protocol: Defining the Personality
&lt;/h2&gt;

&lt;p&gt;The first piece of the puzzle is the &lt;code&gt;memory-bank/AGENTS.md&lt;/code&gt; file. I think of this as the AI’s constitution or its core programming. It doesn’t contain project-specific code rules; instead, it defines the AI’s high-level behavior, its interaction style, and its core principles.&lt;/p&gt;

&lt;p&gt;You can see some of the key sections from my &lt;code&gt;AGENTS.md&lt;/code&gt; file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core Philosophy &amp;amp; Attitude&lt;/strong&gt; : This sets the tone. I want an “Expert Peer,” not a subservient assistant. It should be direct, proactive, and value clean code principles above all else.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interaction Protocol&lt;/strong&gt; : This defines how we communicate. No lectures, no fluff. It must clarify ambiguity and warn me if I’m asking it to do something that violates a convention.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Generation &amp;amp; Modification&lt;/strong&gt; : This governs how it writes code. It should edit files directly, respect formatting, and, most importantly, follow a specific code review process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context &amp;amp; Memory Bank&lt;/strong&gt; : This establishes the hierarchy of knowledge. The &lt;code&gt;memory-bank/&lt;/code&gt; is the single source of truth, more important than the codebase, conversation history, or its own external knowledge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This file establishes a baseline of behavior. It ensures that no matter what the specific task is, the AI approaches it with the same professional, efficient, and principle-driven mindset.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Project Rules: The Letter of the Law
&lt;/h2&gt;

&lt;p&gt;If the &lt;code&gt;AGENTS.md&lt;/code&gt; is the constitution, the files in &lt;code&gt;memory-bank/project-rules/&lt;/code&gt; are the specific, legally-binding statutes of the project. These are granular, often file-specific rules that the AI &lt;em&gt;must&lt;/em&gt; follow.&lt;/p&gt;

&lt;p&gt;These rules cover everything from technology usage to coding patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;00-pnpm.md&lt;/code&gt;: “Always use pnpm for package management. Period.”&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;02-effect-pattern.md&lt;/code&gt;: A detailed, multi-point guide on how to use the Effect library, including import styles, composition patterns, and where &lt;code&gt;E.runPromise&lt;/code&gt; is allowed to be called (hint: only in XState machines).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;08-tailwind.md&lt;/code&gt;: A treatise on our Tailwind CSS architecture, enforcing the strict separation of layout and style components.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;09-state-machine-patterns.md&lt;/code&gt;: Defines the actor-based hierarchy for our XState implementation, ensuring actions are routed correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These rules are not suggestions; they are directives. The &lt;code&gt;AGENTS.md&lt;/code&gt; file instructs the AI to treat the &lt;code&gt;memory-bank/&lt;/code&gt; as its authoritative source of truth, and these files are the content of that memory. When I ask the AI to write a new component, it knows it must use CVA, it must not include margins, and it must get its available actions from a state machine actor—not because I told it to in the prompt, but because the rules demand it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting the Rules into Practice: Prompting with Precision
&lt;/h2&gt;

&lt;p&gt;Having this well-documented set of rules is powerful, but the real leverage comes from how I use them to guide the AI’s work. The rules aren’t just a passive library; they are an active part of my prompting strategy.&lt;/p&gt;

&lt;p&gt;A perfect example is my process for a comprehensive design review, which will be the topic of the next blog post. I don’t just ask the AI to “review the code.” I give it a highly structured plan that explicitly references the project rules at each step.&lt;/p&gt;

&lt;p&gt;Using a task planning tool, I create a master “Design Review” request, which is then broken down into smaller, logical chunks (e.g., “DR Part 1 - Core App Files”, “DR Part 2 - Components”). For each of these chunks, I create a series of subtasks, and this is where the magic happens. The description for each subtask points the AI to the exact rule it needs to enforce.&lt;/p&gt;

&lt;p&gt;For example, the subtask for analyzing Effect patterns looks like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Title&lt;/strong&gt; : “Effect Pattern Analysis”&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Description&lt;/strong&gt; : “Analyze Effect usage against &lt;code&gt;memory-bank/project-rules/02-effect-pattern.md&lt;/code&gt;. Identify pattern violations, missing Effect implementations, and improvement opportunities. Generate specific action items.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I do the same for other concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Component Styling Analysis&lt;/strong&gt; : “Review component styling against &lt;code&gt;memory-bank/project-rules/08-tailwind.md&lt;/code&gt;…”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State Machine Analysis&lt;/strong&gt; : “Assess state machine implementations against &lt;code&gt;memory-bank/project-rules/09-state-machine-patterns.md&lt;/code&gt;…”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach transforms the memory bank from a simple knowledge base into a set of precise instruments. I can direct the AI’s focus with surgical accuracy, ensuring that each part of the codebase is evaluated against the correct, pre-defined standards. It’s a clear, explicit contract: “For this task, these are the rules that matter.”&lt;/p&gt;

&lt;h2&gt;
  
  
  The Killer Feature: The AI Code Review
&lt;/h2&gt;

&lt;p&gt;This brings me to the single most valuable instruction in the entire system. Buried in the &lt;code&gt;AGENTS.md&lt;/code&gt; file is a simple-sounding, yet profound, directive:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Code Review Process&lt;/strong&gt; : After making ANY significant code changes, conduct an internal round table discussion with David Thomas (The Pragmatic Programmer), Andrew Hunt (The Pragmatic Programmer), and Uncle Bob (Clean Code). If they would approve, proceed; if not, iterate until they would approve.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is where the magic happens.&lt;/p&gt;

&lt;p&gt;This instruction forces the AI to pause and reflect. It takes its generated code, which already conforms to the strict &lt;strong&gt;Project Rules&lt;/strong&gt; I’ve pointed it to, and then evaluates it against the higher-level principles of clean, pragmatic, and maintainable code embodied by these three legends of software engineering.&lt;/p&gt;

&lt;p&gt;It’s a two-layer filter:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Does the code follow the rules?&lt;/strong&gt; (e.g., Is the Effect &lt;code&gt;pipe()&lt;/code&gt; structured correctly? Are the Tailwind classes semantic?)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is the code &lt;em&gt;good&lt;/em&gt;?&lt;/strong&gt; (e.g., Is it simple? Is it maintainable? Is there a better way to do this?)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This internal dialogue has been a game-changer. The AI doesn’t just produce code that works; it produces code that is thought-out, clean, and aligned with the project’s deepest architectural and philosophical goals. It catches things I would miss in a manual code review, and it does it instantly. It’s like having a senior architect constantly looking over your shoulder, providing expert feedback in real-time.&lt;/p&gt;

&lt;p&gt;By combining a high-level “personality” with a set of explicit, enforceable project rules, and then actively using those rules in our daily prompts, we create an AI assistant that is not just a tool, but a true partner in the development process.&lt;/p&gt;

&lt;p&gt;In the final post of this series, we’ll put all these pieces together and see how this system performs in a full-scale, complex design review.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>productivity</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Must read</title>
      <dc:creator>drmikecrowe</dc:creator>
      <pubDate>Sat, 06 Sep 2025 21:59:25 +0000</pubDate>
      <link>https://dev.to/drmikecrowe/must-read-1m1o</link>
      <guid>https://dev.to/drmikecrowe/must-read-1m1o</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/himorishige/getting-started-with-multi-mcp-using-hatago-mcp-hub-one-config-to-connect-them-all-2bjp" class="crayons-story__hidden-navigation-link"&gt;Getting Started with Multi-MCP Using Hatago MCP Hub — One Config to Connect Them All&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/himorishige" class="crayons-avatar  crayons-avatar--l  "&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3473639%2F7a720bf9-6781-43fa-a22a-997fd3002667.jpg" alt="himorishige profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/himorishige" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Hi MORISHIGE
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Hi MORISHIGE
                
                
              
              &lt;div id="story-author-preview-content-2813109" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/himorishige" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3473639%2F7a720bf9-6781-43fa-a22a-997fd3002667.jpg" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Hi MORISHIGE&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/himorishige/getting-started-with-multi-mcp-using-hatago-mcp-hub-one-config-to-connect-them-all-2bjp" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 1 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/himorishige/getting-started-with-multi-mcp-using-hatago-mcp-hub-one-config-to-connect-them-all-2bjp" id="article-link-2813109"&gt;
          Getting Started with Multi-MCP Using Hatago MCP Hub — One Config to Connect Them All
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mcp"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mcp&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/cloudflare"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;cloudflare&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/hono"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;hono&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/himorishige/getting-started-with-multi-mcp-using-hatago-mcp-hub-one-config-to-connect-them-all-2bjp" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/himorishige/getting-started-with-multi-mcp-using-hatago-mcp-hub-one-config-to-connect-them-all-2bjp#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              2&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            7 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>mcp</category>
      <category>ai</category>
      <category>cloudflare</category>
      <category>hono</category>
    </item>
    <item>
      <title>MCP Servers, Ports, and Sharing - Part 1 of 6</title>
      <dc:creator>drmikecrowe</dc:creator>
      <pubDate>Wed, 20 Aug 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/drmikecrowe/mcp-servers-ports-and-sharing-part-1-of-6-4agk</link>
      <guid>https://dev.to/drmikecrowe/mcp-servers-ports-and-sharing-part-1-of-6-4agk</guid>
      <description>&lt;p&gt;&lt;strong&gt;Part 1 of 6: The AI-Assisted Development Workflow Series&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the first installment in a six-part series exploring how AI is transforming modern development workflows. In this series, I’ll walk through my journey of using an AI-assisted development environment effectively, from basic infrastructure setup to advanced architectural enforcement and task orchestration.&lt;/p&gt;




&lt;p&gt;I’ve been spending a lot of time lately in the world of AI agents… wait, no, that’s not it. The &lt;em&gt;Model Context Protocol&lt;/em&gt;. MCP. It’s a fancy way of saying “a way for AI models to talk to tools,” and it’s pretty powerful. But like any new toy, it comes with its own set of “some assembly required” headaches. Today, I want to talk about one of those: managing MCP servers, avoiding port conflicts, and generally keeping your digital workspace from turning into a tangled mess of wires.&lt;/p&gt;

&lt;h2&gt;
  
  
  What in the World is an MCP Server?
&lt;/h2&gt;

&lt;p&gt;Think of an MCP server as a translator. Your AI model speaks “I want to do something,” and the MCP server translates that into “Okay, computer, run this specific command.” It’s a bridge between the high-level thinking of the AI and the low-level reality of your machine.&lt;/p&gt;

&lt;p&gt;You need to know about them because, suddenly, you’re not just running a single AI model; you’re running a whole suite of tiny little helper applications. Each one of these helpers, or “tools,” might need its own MCP server. Want to browse the web? That’s a server. Want to read and write files? That’s another server. Before you know it, you’ve got a whole digital office full of very specialized, very chatty interns.&lt;/p&gt;

&lt;p&gt;So, when do you start one? The simple answer is: when you want to use a tool. The more complicated answer is: when you want to use a tool that needs a dedicated process to listen for instructions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nomenclature
&lt;/h2&gt;

&lt;p&gt;Before we dive deeper, let’s clarify some terminology that might seem counterintuitive at first. In the MCP ecosystem, what we call an “MCP server” is actually a &lt;strong&gt;client&lt;/strong&gt; from the perspective of your editor or CLI tool.&lt;/p&gt;

&lt;p&gt;Here’s the mental model: Your editor (like VS Code, Cursor, Windsurf) or CLI tool is the “server” that coordinates everything. It’s the central hub that manages the conversation with the AI model and decides which tools to invoke. The MCP servers are specialized “clients” that connect to this central hub to provide specific capabilities.&lt;/p&gt;

&lt;p&gt;Think of it like a restaurant: Your editor is the head chef who takes orders and coordinates the kitchen. The MCP servers are like specialized sous chefs - each one is an expert at a particular type of cooking (browsing the web, reading files, querying databases, etc.). The head chef doesn’t know how to do everything, but they know which sous chef to call for each specific task.&lt;/p&gt;

&lt;p&gt;So when we say “MCP server,” we’re really talking about a specialized client that serves a particular function to the main AI coordination system. This naming convention can be confusing, but it’s become standard in the MCP community.&lt;/p&gt;

&lt;p&gt;Where this can get really confusing is if the tool you are wanting to use also requires communicating with a server. I really like &lt;a href="https://browsertools.agentdesk.ai/" rel="noopener noreferrer"&gt;BrowserTools MCP&lt;/a&gt;. This tool exposes your console logs to your browser, giving it access to your frontend development logs as you make changes. It’s a complex tool, though, which requires:&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.amazonaws.com%2Fuploads%2Farticles%2Fawfpxmiag4mwhkmymexp.png" 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.amazonaws.com%2Fuploads%2Farticles%2Fawfpxmiag4mwhkmymexp.png" alt=" " width="799" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So when you say “BrowserTools MCP,” you’re actually talking about three separate components working together - the browser extension, the MCP server, and the BrowserTools server. This is a perfect example of why port management becomes crucial in AI-assisted development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types of MCP Servers
&lt;/h2&gt;

&lt;p&gt;MCP servers come in two main flavors, and understanding the difference is crucial for managing your development environment effectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  stdio-based Servers
&lt;/h3&gt;

&lt;p&gt;These are the simplest type of MCP server. They communicate through standard input/output streams, which means they’re designed to be short-lived processes that start when needed and exit when the work is done. Think of them like command-line tools that your AI can invoke.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Characteristics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No port management needed&lt;/strong&gt; - They don’t bind to any network ports&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stateless by design&lt;/strong&gt; - Each invocation is independent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perfect for one-off tasks&lt;/strong&gt; - File operations, code generation, data processing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy to debug&lt;/strong&gt; - You can run them directly from the command line&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File system operations (reading, writing, searching files)&lt;/li&gt;
&lt;li&gt;Code formatting and linting&lt;/li&gt;
&lt;li&gt;Data transformation and analysis&lt;/li&gt;
&lt;li&gt;Simple API calls and data fetching&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Port-based Servers
&lt;/h3&gt;

&lt;p&gt;These are the more complex beasts that we’ll focus on in this post. They run as persistent HTTP servers that bind to specific ports on your machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Characteristics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Persistent processes&lt;/strong&gt; - They stay running and maintain state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port binding required&lt;/strong&gt; - Each server needs its own port&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stateful operations&lt;/strong&gt; - Can maintain context across multiple requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network communication&lt;/strong&gt; - Use HTTP/WebSocket protocols&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource intensive&lt;/strong&gt; - They consume memory and CPU while running&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database connections and queries&lt;/li&gt;
&lt;li&gt;Web browsing and page interaction&lt;/li&gt;
&lt;li&gt;Complex tool integrations (like GitHub, Slack, etc.)&lt;/li&gt;
&lt;li&gt;Real-time data streams and monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why This Matters for Your Workflow
&lt;/h3&gt;

&lt;p&gt;The distinction becomes important when you’re building an AI-assisted development environment. stdio-based servers are great for simple, stateless operations, but you need to understand which type of server you are utilizing.&lt;/p&gt;

&lt;p&gt;I tend to have multiple windows open of Cursor, or might be running Cursor and gemini in a terminal shell. What if I want to share an MCP server (like we will discuss in part 4) across my different windows/sessions?&lt;/p&gt;

&lt;p&gt;This is where the port management challenges come in. To make it even more complicated, you might have several port-based servers running simultaneously:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A browser automation server on port 3000&lt;/li&gt;
&lt;li&gt;A database connection server on port 3001&lt;/li&gt;
&lt;li&gt;A file system watcher on port 3002&lt;/li&gt;
&lt;li&gt;A custom tool integration on port 3003&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Managing these ports, ensuring they don’t conflict, and keeping track of which server is doing what becomes a real challenge. That’s exactly the problem we’ll solve with our tmux-based orchestration system.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Port Authority is Now in Session
&lt;/h2&gt;

&lt;p&gt;Many of these MCP servers use HTTP to communicate. That means they need to “bind” to a specific port on your computer. A port is like a numbered door on your computer. Only one application can be listening at a specific door at a time. If you try to start two servers on the same port, the second one will crash and burn, complaining about the port being “already in use.”&lt;/p&gt;

&lt;p&gt;This is a classic problem in software development, and it’s now a problem for those of us building with AI. But here’s the cool part: if an MCP server is just a simple HTTP server, you can often &lt;em&gt;share&lt;/em&gt; it between different AI models. As long as they’re all speaking the same language (i.e., the same MCP specification), they can all talk to the same server on the same port. No need to spin up a new server for each model.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Not-So-Secret Weapon: &lt;code&gt;tmux&lt;/code&gt; and &lt;code&gt;servers.mcp&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;As I started collecting more and more of these MCP servers, I realized I needed a better way to manage them. I’m a big fan of &lt;code&gt;tmux&lt;/code&gt;, a terminal multiplexer that lets you have multiple terminal sessions running in the same window. It’s like having a whole bunch of little command-line windows open at once, but without the clutter.&lt;/p&gt;

&lt;p&gt;So, I came up with a little convention for myself. I created a file called &lt;code&gt;servers.mcp&lt;/code&gt; that lists all the servers I want to run. It’s a simple text file, with each line containing a name for the server and the command to start it, separated by an equals sign.&lt;/p&gt;

&lt;p&gt;Here’s what my &lt;code&gt;servers.mcp&lt;/code&gt; file looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;browser-tools=npx -y @agentdeskai/browser-tools-server@latest
mpc-tasks=TRANSPORT=http PORT=4680 npx mcp-tasks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I wrote a little shell script called &lt;code&gt;start-mcp-servers.sh&lt;/code&gt; that reads this file and starts up a new &lt;code&gt;tmux&lt;/code&gt; session with a separate window for each server.&lt;/p&gt;

&lt;p&gt;Here’s the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

# MCP Server Launcher Script
# Reads servers.mcp and creates tmux windows for each server

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Check if tmux is installed
if ! command -v tmux &amp;amp;&amp;gt;/dev/null; then
    echo -e "${RED}Error: tmux is not installed${NC}"
    echo "Please install tmux first:"
    echo " Ubuntu/Debian: sudo apt install tmux"
    echo " macOS: brew install tmux"
    echo " Arch: sudo pacman -S tmux"
    exit 1
fi

# Change to the script's directory
cd "$(dirname "$0")"

# Check if servers.mcp exists
if [! -f "servers.mcp"]; then
    echo -e "${RED}Error: servers.mcp file not found${NC}"
    exit 1
fi

# Check if tmux session already exists
if tmux has-session -t mcp-servers 2&amp;gt;/dev/null; then
    echo -e "${YELLOW}MCP servers session already exists. Attaching...${NC}"
    tmux attach-session -t mcp-servers
    exit 0
fi

# Function to clean up tmux session if script is interrupted
cleanup() {
    echo -e "\n${YELLOW}Cleaning up...${NC}"
    tmux kill-session -t mcp-servers 2&amp;gt;/dev/null
    exit 1
}

# Set up signal handlers
trap cleanup SIGINT SIGTERM

echo -e "${BLUE}Starting MCP servers in tmux session...${NC}"

# Create new tmux session
tmux new-session -d -s mcp-servers -n "mcp-servers"

# Read servers.mcp file and create windows
window_count=0
while IFS='=' read -r title command;
do
    # Skip empty lines and comments
    if [[-z "$title" || "$title" =~ ^[[:space:]]*# ]]; then
        continue
    fi

    # Trim whitespace
    title=$(echo "$title" | xargs)
    command=$(echo "$command" | xargs)

    if [-n "$title"] &amp;amp;&amp;amp; [-n "$command"]; then
        echo -e "${GREEN}Creating window: $title${NC}"
        echo -e " Command: $command"

        if [$window_count -eq 0]; then
            # First window - rename the default window
            tmux rename-window -t mcp-servers:0 "$title"
            tmux send-keys -t mcp-servers:0 "$command &amp;amp;" C-m
        else
            # Create new window
            tmux new-window -t mcp-servers -n "$title" "$command"
        fi

        window_count=$((window_count + 1))
    fi
done &amp;lt;servers.mcp

if [$window_count -eq 0]; then
    echo -e "${RED}No valid servers found in servers.mcp${NC}"
    tmux kill-session -t mcp-servers 2&amp;gt;/dev/null
    exit 1
fi

echo -e "${GREEN}Created $window_count MCP server windows${NC}"
echo -e "${BLUE}Attaching to tmux session...${NC}"
echo -e "${YELLOW}Use Ctrl+B then D to detach from the session${NC}"
echo -e "${YELLOW}Use 'tmux attach -t mcp-servers' to reattach later${NC}"

# Attach to the session
tmux attach-session -t mcp-servers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when I want to start up my MCP environment, I just run &lt;code&gt;./start-mcp-servers.sh&lt;/code&gt;, and I get a nice, clean &lt;code&gt;tmux&lt;/code&gt; session with all my servers running in their own windows.&lt;/p&gt;

&lt;p&gt;Here’s what it looks like:&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.amazonaws.com%2Fuploads%2Farticles%2Ffaxxedi2uq095v00bvbz.png" 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.amazonaws.com%2Fuploads%2Farticles%2Ffaxxedi2uq095v00bvbz.png" alt="My tmux session with MCP servers running" width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This setup makes it easy to see what’s going on with each server, and I can easily restart a server if it crashes. It’s a simple solution, but it’s made my life a lot easier. And, most importantly, it keeps me from tripping over my own digital feet.&lt;br&gt;&lt;br&gt;
m tripping over my own digital feet.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>productivity</category>
      <category>mcp</category>
    </item>
    <item>
      <title>My ESLint Config Was a Mess. I Asked an AI to Fix It. - Part 2 of 6</title>
      <dc:creator>drmikecrowe</dc:creator>
      <pubDate>Tue, 19 Aug 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/drmikecrowe/my-eslint-config-was-a-mess-i-asked-an-ai-to-fix-it-part-2-of-6-4g47</link>
      <guid>https://dev.to/drmikecrowe/my-eslint-config-was-a-mess-i-asked-an-ai-to-fix-it-part-2-of-6-4g47</guid>
      <description>&lt;p&gt;&lt;strong&gt;Part 2 of 6: The AI-Assisted Development Workflow Series&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the second installment in a six-part series exploring how AI is transforming modern development workflows. In this series, I’ll walk through my journey of building an AI-assisted development environment, from basic infrastructure setup to advanced architectural enforcement and task orchestration.&lt;/p&gt;




&lt;p&gt;If you’re a frontend developer, you know the love-hate relationship we have with ESLint. We love that it keeps our code clean and consistent. We hate spending hours wrestling with config files, trying to get plugins to play nicely with each other, especially in a modern TypeScript and Tailwind CSS v4 world.&lt;/p&gt;

&lt;p&gt;My &lt;code&gt;eslint.config.js&lt;/code&gt; was starting to feel like a house of cards. I wanted to add better Tailwind CSS linting, but my first attempt with a popular plugin led to a cascade of peer dependency warnings and configuration errors. After a few failed attempts, I realized I was spending more time configuring the linter than writing code. This is not the way.&lt;/p&gt;

&lt;p&gt;So, I turned to my AI pair programmer with a simple plea: “Help me fix this mess.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is eslint locking up?
&lt;/h2&gt;

&lt;p&gt;The first issue I needed help with was that ESLint was trying to use type-aware rules with multiple TypeScript config files, which can cause it to hang when processing large codebases. Type-aware rules like &lt;code&gt;@typescript-eslint/no-unused-vars&lt;/code&gt; and &lt;code&gt;@typescript-eslint/no-floating-promises&lt;/code&gt; require ESLint to perform complex type checking across your entire project, and when you have multiple &lt;code&gt;tsconfig.json&lt;/code&gt; files or conflicting TypeScript configurations, this can lead to infinite loops or excessive memory usage.&lt;/p&gt;

&lt;p&gt;The solution was to create a &lt;strong&gt;dual-config approach&lt;/strong&gt; that separates type-aware and non-type-aware rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;App files&lt;/strong&gt; use the fast, non-type-aware config for quick feedback during development&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test files&lt;/strong&gt; use the comprehensive type-aware config for thorough validation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared configuration&lt;/strong&gt; provides common rules and plugins across both configs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation allows ESLint to run quickly during development while still providing comprehensive type checking where it matters most.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mission: Clean, Refactored ESLint Config
&lt;/h2&gt;

&lt;p&gt;My goal was to not just get a new Tailwind plugin working, but to clean up the entire ESLint configuration. The process, guided by the AI, was a masterclass in untangling complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Final Result
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Refactoring for Readability and DRYness
&lt;/h3&gt;

&lt;p&gt;The first thing it did was apply the Don’t Repeat Yourself (DRY) principle to my config. Instead of having separate, nearly identical objects for different TypeScript configurations, it created common, reusable building blocks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;COMMON_PLUGINS&lt;/code&gt;: A single object defining all the ESLint plugins we use, like &lt;code&gt;@typescript-eslint&lt;/code&gt;, &lt;code&gt;prettier&lt;/code&gt;, &lt;code&gt;react-hooks&lt;/code&gt;, and the new Tailwind plugins.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;COMMON_SETTINGS&lt;/code&gt;: A shared object for settings, like the import resolver paths.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;COMMON_RULES&lt;/code&gt;: A comprehensive object containing all the rules that apply across the entire project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This immediately made the config file shorter, cleaner, and much easier to understand. If we need to add a new rule everywhere, we now only have to add it in one place.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Installing and Configuring the &lt;em&gt;Right&lt;/em&gt; Plugins
&lt;/h3&gt;

&lt;p&gt;The initial journey involved some trial and error. We first tried &lt;code&gt;eslint-plugin-tailwindcss&lt;/code&gt;, but it had issues with our Tailwind v4 setup. The AI helped diagnose this and suggested a better alternative. The final commit shows the installation of two key packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;eslint-plugin-better-tailwindcss&lt;/code&gt;: A fantastic plugin for sorting classes and catching duplicates and conflicts.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@poupe/eslint-plugin-tailwindcss&lt;/code&gt;: Another great plugin that adds more advanced checks, like preferring theme tokens and validating CSS modifiers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Dialing in the Rules
&lt;/h3&gt;

&lt;p&gt;This was the most crucial part. The AI configured the new plugins with a sensible set of rules, turning some on as warnings and others as errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Tailwind CSS rules
'better-tailwindcss/sort-classes': 'warn',
'better-tailwindcss/no-duplicate-classes': 'error',
'better-tailwindcss/no-conflicting-classes': 'error',
'better-tailwindcss/no-unregistered-classes': 'off', // We turned this off because we use custom design tokens
'better-tailwindcss/enforce-shorthand-classes': 'warn',

// Poupe Tailwind CSS rules
'tailwindcss/no-conflicting-utilities': 'error',
'tailwindcss/prefer-theme-tokens': 'warn',
'tailwindcss/valid-modifier-syntax': 'error',
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;no-unregistered-classes&lt;/code&gt; rule is off. The AI correctly identified from the linter output that our custom design tokens (like &lt;code&gt;bg-primary&lt;/code&gt;) were being flagged, and recommended disabling this rule to avoid noise, which was exactly the right call.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Automatically Fixing What It Could
&lt;/h3&gt;

&lt;p&gt;The best part? The new &lt;code&gt;sort-classes&lt;/code&gt; rule didn’t just flag inconsistent class ordering—it fixed it. The git commit is full of small, satisfying changes where messy class strings were automatically reordered into a logical, consistent format:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div className='flex justify-center items-center h-32'&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div className='flex h-32 items-center justify-center'&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;This happened across dozens of files. It’s a small change, but it adds up to a huge improvement in code quality and developer experience, and it was all done automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Result: Effortless Code Quality
&lt;/h2&gt;

&lt;p&gt;After the AI finished its work, our ESLint setup was transformed. It’s now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Easy to read and maintain.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using modern, effective plugins&lt;/strong&gt; for our specific stack.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatically enforcing&lt;/strong&gt; a consistent style for our utility classes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This experience was a powerful reminder that AI assistants are more than just code generators. They can be expert systems administrators, helping to configure and maintain the complex tooling that modern development relies on. It took a task that was a source of frustration and turned it into a clean, automated part of our workflow. And for that, I am very grateful.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Final Architecture: Dual-Config with Shared Rules
&lt;/h2&gt;

&lt;p&gt;The AI helped me implement a sophisticated dual-config architecture that solves the performance issues while maintaining comprehensive linting coverage. Here’s how it works:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Shared Configuration (&lt;code&gt;eslint.shared.config.js&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The shared config provides the foundation with common plugins, settings, and rules that apply across the entire project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Common plugins used across all TypeScript configs
const COMMON_PLUGINS = {
    '@typescript-eslint': tseslint.plugin,
    'react-hooks': reactHooks,
    'react-refresh': reactRefresh,
    import: importPlugin,
    prettier: prettierPlugin,
    'react-dom': reactDom,
    perfectionist,
    'react-x': reactX,
    'unused-imports': unusedImports,
    visual: visualComplexity,
    'better-tailwindcss': betterTailwindcss,
    tailwindcss: poupeTailwindcssPlugin,
}

// Factory function to create TypeScript configs with different rule sets
const createTypeScriptConfig = (...tsRuleSets) =&amp;gt; ({
    languageOptions: {
        parser: tseslint.parser,
        parserOptions: {
            ecmaFeatures: {
                jsx: true,
            },
        },
    },
    plugins: COMMON_PLUGINS,
    rules: {
        ...COMMON_RULES,
        // Merge all provided TypeScript rule sets
        ...Object.assign({}, ...tsRuleSets),
    },
    settings: COMMON_SETTINGS,
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This factory pattern allows us to create different TypeScript configurations with varying levels of strictness.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. App Configuration (&lt;code&gt;eslint.app.config.js&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The app config implements the performance-optimized approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default [
    // Global ignores
    {
        ignores: [...sharedIgnores, 'shared/**'],
    },
    // App TypeScript files WITHOUT type-aware rules (for speed)
    {
        files: appFiles,
        languageOptions: {
            ...typescriptConfig.languageOptions,
            globals: browserGlobals,
        },
        plugins: typescriptConfig.plugins,
        rules: typescriptConfig.rules,
        settings: typescriptConfig.settings,
    },
    // Test files override WITH type-aware rules
    {
        files: ['**/*.test.{js,jsx,ts,tsx}'],
        languageOptions: {
            ...typescriptConfig.languageOptions,
            parserOptions: {
                ...typescriptConfig.languageOptions.parserOptions,
                project: './tsconfig.test.json',
                tsconfigRootDir: import.meta.dirname,
            },
            globals: {
                ...globals.jest,
                ...browserGlobals,
            },
        },
        plugins: typescriptConfig.plugins,
        rules: {
            ...typescriptConfig.rules,
            ...testConfig.rules,
        },
        settings: typescriptConfig.settings,
    },
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. The Performance Solution
&lt;/h3&gt;

&lt;p&gt;The key insight was creating two distinct TypeScript configurations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;typescriptConfig&lt;/code&gt;&lt;/strong&gt; : Uses &lt;code&gt;tseslint.configs.recommended&lt;/code&gt; and &lt;code&gt;tseslint.configs.stylistic&lt;/code&gt; - fast, syntax-based rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;typeAwareConfig&lt;/code&gt;&lt;/strong&gt; : Uses &lt;code&gt;tseslint.configs.recommendedTypeChecked&lt;/code&gt;, &lt;code&gt;tseslint.configs.strictTypeChecked&lt;/code&gt;, and &lt;code&gt;tseslint.configs.stylisticTypeChecked&lt;/code&gt; - comprehensive type-aware rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By applying the fast config to app files and the comprehensive config only to test files, we get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fast development feedback&lt;/strong&gt; - ESLint runs quickly during coding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thorough validation&lt;/strong&gt; - Type-aware rules catch issues in tests where they matter most&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainable architecture&lt;/strong&gt; - Shared configuration keeps everything DRY&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This architecture demonstrates how AI can help design sophisticated solutions that balance performance with functionality, turning a frustrating configuration problem into an elegant, scalable system.  &lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>productivity</category>
      <category>mcp</category>
    </item>
    <item>
      <title>When AppImages Fail You: Building a Robust Extraction Tool (and Why Cursor Drove Me to It)</title>
      <dc:creator>drmikecrowe</dc:creator>
      <pubDate>Sun, 10 Aug 2025 14:30:00 +0000</pubDate>
      <link>https://dev.to/drmikecrowe/when-appimages-fail-you-building-a-robust-extraction-tool-and-why-cursor-drove-me-to-it-26ha</link>
      <guid>https://dev.to/drmikecrowe/when-appimages-fail-you-building-a-robust-extraction-tool-and-why-cursor-drove-me-to-it-26ha</guid>
      <description>&lt;p&gt;Let me start this with a confession:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;I really wanted to like AppImages&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The promise is compelling: universal Linux binaries that run everywhere without installation. Just download, &lt;code&gt;chmod +x&lt;/code&gt;, and go. It’s the kind of elegant simplicity that makes you think “why didn’t we do this sooner?”&lt;/p&gt;

&lt;p&gt;Then reality hits.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cursor Catalyst
&lt;/h2&gt;

&lt;p&gt;This whole adventure started when I decided to try &lt;a href="https://cursor.sh/" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;, the AI-powered code editor that’s my daily code editor. Unfortunately, it’s not updated frequently in AUR, so I decided to try the AppImage. When I tried to run it, I got the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./Cursor-1.4.3.AppImage
zsh: no such file or directory: ./Cursor-1.4.3.AppImage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After some experimentation, I got it to run but I now started seeing the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mise ERROR Cursor-1.4.3-x86_64_5054c3a796764b4195108ade2714e281.AppImage is not a valid shim. This likely means you uninstalled a tool and the shim does not point to anything. Run `mise use &amp;lt;TOOL&amp;gt;` to reinstall the tool.
mise ERROR Run with --verbose or MISE_VERBOSE=1 for more information
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Regardless, this was a nogo for me, because &lt;a href="https://mise.jdx.dev/" rel="noopener noreferrer"&gt;mise&lt;/a&gt; has replaced direnv for me and I use it everywhere. Perplexity informed me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Mise can be run in many environments, but there are common issues when using mise within AppImages (such as when using the Cursor editor as an AppImage), especially with advanced shell integrations and shims. The error message you are seeing—“AppImage is not a valid shim. This likely means you uninstalled a tool and the shim does not point to anything”—is a known problem occurring specifically in AppImage-packed shells and terminals, often with Zsh.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The AppImage Reality Check
&lt;/h2&gt;

&lt;p&gt;Don’t get me wrong — AppImages solve real problems. Before them, we had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RPM/DEB Hell&lt;/strong&gt; : Dependency conflicts, version mismatches, and the joy of maintaining packages across different distributions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snap Confinement&lt;/strong&gt; : Sandboxing that sometimes broke legitimate app functionality&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flatpak Complexity&lt;/strong&gt; : Great technology, but the runtime management can get overwhelming&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AppImages promised to be different. Just a filesystem bundle with everything included. But in practice, they still have dependencies — on FUSE, on specific kernel features, on runtime libraries that may or may not be present.&lt;/p&gt;

&lt;p&gt;I started thinking about this differently. What if instead of trying to fix the technical issues, I just extracted everything and ran it natively?&lt;/p&gt;

&lt;h2&gt;
  
  
  Building an Extraction-First Manager
&lt;/h2&gt;

&lt;p&gt;The core idea was simple: instead of running AppImages, extract them and create proper desktop entries that point to the extracted executables. This completely sidesteps the FUSE requirement and gives us some nice benefits:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Extraction Chain
&lt;/h3&gt;

&lt;p&gt;The first challenge was that extraction isn’t always straightforward. Some AppImages work fine with the built-in &lt;code&gt;--appimage-extract&lt;/code&gt;, but others don’t. I ended up building a chain of fallback methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Method 1: Native AppImage extraction
if ( cd "$tmpdir" &amp;amp;&amp;amp; "$appimage" --appimage-extract &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 ); then
    printf "%s/squashfs-root" "$tmpdir"
    return 0
fi

# Method 2: Direct SquashFS with offset detection
if has_cmd unsquashfs; then
    offset=$(LC_ALL=C grep -aob -- 'hsqs' "$appimage" | head -n1 | cut -d: -f1 || true)
    if [[-n "${offset:-}"]]; then
        if ( cd "$tmpdir" &amp;amp;&amp;amp; unsquashfs -o "$offset" -d squashfs-root "$appimage" &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 ); then
            printf "%s/squashfs-root" "$tmpdir"
            return 0
        fi
    fi
fi

# Method 3: Enhanced binwalk analysis
if has_cmd binwalk &amp;amp;&amp;amp; has_cmd unsquashfs; then
    # ... more sophisticated offset detection
fi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach handles even the most stubborn AppImages. I’ve tested it on everything from simple utilities to complex applications like Cursor, and it consistently works where the standard AppImage runtime fails.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Idempotency Problem
&lt;/h3&gt;

&lt;p&gt;Here’s something that annoyed me: downloading &lt;code&gt;Cursor-1.4.3.AppImage&lt;/code&gt; and later &lt;code&gt;Cursor-1.5.0.AppImage&lt;/code&gt; would create two separate entries in my Applications folder. That’s not how updates should work.&lt;/p&gt;

&lt;p&gt;The solution was to use the application’s internal name instead of the filename. Every AppImage contains a &lt;code&gt;.desktop&lt;/code&gt; file that specifies the real application name. My script extracts this and uses it as the canonical identifier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;read_internal_desktop() {
    local root="$1"
    local d
    d=$(find "$root" -maxdepth 2 -type f -name '*.desktop' | head -n 1 || true)
    [[-n "${d:-}"]] &amp;amp;&amp;amp; printf "%s" "$d"
}

parse_desktop_key() {
    local df="$1" key="$2"
    awk -F= -v key="$key" 'tolower($1)==tolower(key){print $2; exit}' "$df"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when I install a newer version of Cursor, it updates the existing installation instead of creating a duplicate. Much better.&lt;/p&gt;

&lt;h2&gt;
  
  
  File Structure and Integration
&lt;/h2&gt;

&lt;p&gt;The end result is a clean, organized structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/Applications/cursor/ # Based on internal app name
├── cursor.AppImage # Original AppImage file
├── extracted/ # Extracted contents
│ ├── AppRun # Main executable
│ ├── cursor.desktop # Internal desktop file
│ ├── usr/ # Application files
│ └── ...
├── icon # Extracted icon
└── .appimage-manager-meta # Manager metadata

~/.local/share/applications/cursor.desktop # System desktop entry
~/.local/share/icons/hicolor/256x256/apps/cursor.png # System icon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The desktop entry points directly to the extracted &lt;code&gt;AppRun&lt;/code&gt; executable, completely bypassing any AppImage runtime requirements. KDE and GNOME see it as a normal application, complete with proper icons and metadata.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Developer Experience
&lt;/h2&gt;

&lt;p&gt;Using the tool is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Install or update
./appimage-manager.sh install ./Cursor-1.4.3.AppImage

# Later, updating to a newer version
./appimage-manager.sh install ./Cursor-1.5.0.AppImage # Updates existing install

# List what's installed
./appimage-manager.sh list

# Clean removal
./appimage-manager.sh uninstall cursor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script handles all the complexity: extraction, icon processing, desktop file creation, and menu integration. And since it’s extraction-based, everything starts faster than traditional AppImages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;This isn’t just about avoiding FUSE dependencies (though that’s nice). It’s about reliability. AppImages are supposed to be the “just works” solution for Linux software distribution, but too often they don’t. By extracting first and running natively, we get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No Runtime Dependencies&lt;/strong&gt; : No FUSE, no AppImage runtime, no mysterious failures&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster Startup&lt;/strong&gt; : No filesystem mounting overhead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better Integration&lt;/strong&gt; : Standard desktop entries that every DE understands&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cleaner Updates&lt;/strong&gt; : Idempotent installs based on actual app names&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Troubleshooting&lt;/strong&gt; : When something breaks, you can inspect the extracted files directly&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Irony
&lt;/h2&gt;

&lt;p&gt;The funny thing? By avoiding the AppImage runtime entirely, I ended up with a more reliable way to use AppImages. They still serve their purpose as distribution bundles — I just treat them as fancy zip files instead of executable containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Source and Available
&lt;/h2&gt;

&lt;p&gt;I’ve released the &lt;a href="https://github.com/drmikecrowe/appimage-extracted-installer/" rel="noopener noreferrer"&gt;AppImage Extract Installer&lt;/a&gt; under Apache 2.0. It’s a single bash script with no dependencies beyond standard Linux utilities (though &lt;code&gt;squashfs-tools&lt;/code&gt; and &lt;code&gt;binwalk&lt;/code&gt; enable the enhanced extraction features).&lt;/p&gt;

&lt;p&gt;If you’re dealing with AppImage frustrations, give it a try. And if you find bugs or have suggestions, the issue tracker is open.&lt;/p&gt;

&lt;p&gt;Sometimes the best way to fix a technology is to work around it entirely.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have your own AppImage horror stories? Found a better solution? Hit me up on &lt;a href="https://github.com/drmikecrowe" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or wherever you found this post. I’m always interested in hearing how others solve these kinds of practical problems.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Introducing VPEM-Visual Studio Profile Extension Manager</title>
      <dc:creator>drmikecrowe</dc:creator>
      <pubDate>Tue, 08 Oct 2024 11:22:04 +0000</pubDate>
      <link>https://dev.to/drmikecrowe/introducing-vpem-visual-studio-profile-extension-manager-4a1m</link>
      <guid>https://dev.to/drmikecrowe/introducing-vpem-visual-studio-profile-extension-manager-4a1m</guid>
      <description>&lt;p&gt;VS Code Profile Extension Manager (VPEM) is a powerful command-line tool designed to help you manage your Visual Studio Code extensions across different profiles. It allows you to dump, categorize, and apply extensions with ease, streamlining your VS Code setup process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Status
&lt;/h2&gt;

&lt;p&gt;This project has been developed strictly on Linux, though it should work everywhere.  If you find an issue, please report it in the &lt;a href="https://github.com/drmikecrowe/vscode-profile-extension-manager/issues" rel="noopener noreferrer"&gt;GitHub issue tracker&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Executables are built using &lt;a href="https://github.com/pyinstaller/pyinstaller" rel="noopener noreferrer"&gt;pyinstaller&lt;/a&gt; and are available for Linux, Windows and MacOS.  Again, if any issues are found, please report them in the &lt;a href="https://github.com/drmikecrowe/vscode-profile-extension-manager/issues" rel="noopener noreferrer"&gt;GitHub issue tracker&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why?
&lt;/h2&gt;

&lt;p&gt;I tend to use a lot of extensions.  Further, I find myself switching frequently between TypeScript, Python, and now C#.  Sometimes I work in Azure.  Other times in AWS.&lt;/p&gt;

&lt;p&gt;I realized that multiple profiles might make more sense than tons of extensions in a single profile.  However, it's also really easy to experiment with extensions and leave unused ones polluting the profile.  This tool helps me keep things organized.&lt;/p&gt;

&lt;p&gt;For example, here's how to apply a group of extensions to a profile:&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.amazonaws.com%2Fuploads%2Farticles%2Fwxpybf6waw6g0n4biwev.png" 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.amazonaws.com%2Fuploads%2Farticles%2Fwxpybf6waw6g0n4biwev.png" alt="Apply Extensions" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Process
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;First, dump your extensions for all your profiles&lt;/li&gt;
&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2Fb7ljf1ap96z6si7h347i.png" 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.amazonaws.com%2Fuploads%2Farticles%2Fb7ljf1ap96z6si7h347i.png" alt="Dump Extensions" width="799" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next, categorize extensions by searching for common strings and assign to categories&lt;/li&gt;
&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2Fjawdjkl1qzdo38fd1dox.png" 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.amazonaws.com%2Fuploads%2Farticles%2Fjawdjkl1qzdo38fd1dox.png" alt="Categorize" width="799" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finally, apply extensions to a profile&lt;/li&gt;
&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2Fa58vjdqoa1gejnybzh9q.png" 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.amazonaws.com%2Fuploads%2Farticles%2Fa58vjdqoa1gejnybzh9q.png" alt=" " width="488" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;To install VPEM, you can use pip:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended way:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pipx &lt;span class="nb"&gt;install &lt;/span&gt;pipx &lt;span class="nb"&gt;install &lt;/span&gt;vscode_profile_extension_manager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, you may download the released binaries in the repo.  However, these are mass-produced by actions I've pulled from various places, so I can't guarantee they're tested.&lt;/p&gt;

&lt;p&gt;Finally, you can also use &lt;a href="https://github.com/jpillora/installer" rel="noopener noreferrer"&gt;jpillora/installer&lt;/a&gt; to automate the installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-q&lt;/span&gt; https://i.jpillora.com/drmikecrowe/vscode-profile-extension-manager! | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;Here are some basic usage examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dump extensions from a profile:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   vpem dump &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="s2"&gt;"Default"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Apply extensions to a profile:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   vpem apply &lt;span class="nt"&gt;--profile&lt;/span&gt; &lt;span class="s2"&gt;"Work"&lt;/span&gt; &lt;span class="nt"&gt;--category&lt;/span&gt; &lt;span class="s2"&gt;"Python Development"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;List all categories:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   vpem list-categories
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Contributing
&lt;/h2&gt;

&lt;p&gt;We welcome contributions to VPEM! If you'd like to contribute, please follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fork the repository&lt;/li&gt;
&lt;li&gt;Create a new branch for your feature or bug fix&lt;/li&gt;
&lt;li&gt;Make your changes and commit them with a clear message&lt;/li&gt;
&lt;li&gt;Push your changes to your fork&lt;/li&gt;
&lt;li&gt;Create a pull request&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Please make sure to update tests as appropriate and adhere to the project's coding standards.&lt;/p&gt;

&lt;h2&gt;
  
  
  License
&lt;/h2&gt;

&lt;p&gt;This project is licensed under the Apache-2.0 License - see the &lt;a href="//LICENSE.md"&gt;LICENSE&lt;/a&gt; file for details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Support
&lt;/h2&gt;

&lt;p&gt;If you encounter any issues or have questions, please file an issue on the &lt;a href="https://github.com/drmikecrowe/vscode-profile-extension-manager/issues" rel="noopener noreferrer"&gt;GitHub issue tracker&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>extensions</category>
    </item>
  </channel>
</rss>
