Ask, Plan, Confirm: Making Agents Stop Before They Start

The scariest thing about a genuinely capable coding agent is how quickly it commits. You type two sentences, and forty seconds later there are changes across nine files, half of which you did not want and one of which quietly changed a query you spent a week hardening. The agent was not wrong about how to implement the thing. It was wrong about what the thing was, and it never stopped to check.

I have watched this happen enough times that I stopped treating it as a prompting problem and started treating it as a workflow problem. A better one-shot prompt is not the fix. A gate is: the agent is not allowed to edit files until it has asked what it needs to ask, shown me a plan, and gotten a yes.

Why speed is the problem

Human engineers have a built-in pause. Before a senior developer touches your codebase they ask a couple of questions, sketch the approach, maybe drop a comment on the ticket. That pause is where the wrong-work gets caught, cheaply, in a sentence, instead of expensively, in a diff you have to read and reject.

Agents removed the pause. That is most of their value and most of their danger in the same motion. An agent that implements immediately optimizes for the wrong thing: it treats “produce a diff” as the goal, when the goal was “produce the diff we agreed on.” The gap between those two is where the deleted work and the silent scope creep live.

That gap is not a knowledge problem. The agent knows how to write the code. It just never checked that it was writing the right code.

Diagram explaining the importance of speed in work processes, contrasting 'no gate' and 'the gate' approaches. It highlights potential problems and costs associated with each method.

The same task, gated and ungated. Skipping the pause trades a small, predictable cost for a large, unpredictable one.

So I gave the pause back, deliberately, as a standing instruction on every agent in the InterlinedList repo.

The three beats

The workflow is written down in .claude/workflows/plan-first.md and every agent links to it. It is three beats, in order, and implementation is gated behind all three.

A flowchart titled 'The Gate' illustrating a process for implementation that requires three steps: Prompt, Ask, Plan, Confirm, and Implement. It emphasizes the sequence and conditions under which implementation occurs, highlighting the need for clarification and approval before proceeding.

The three beats, in order. Nothing is edited until all three pass, and work that grows past the approved plan loops back to re-plan rather than quietly expanding.

Ask. Surface what the prompt left open before committing to an approach. Ambiguous scope, unstated edge cases, a product decision hiding inside a technical request, whether a feature should be tier-gated. The migrations agent asks about column types and nullability and whether anything destructive is implied. The Next.js agent asks which surfaces are in and out. The rule has an escape hatch, because asking three questions about a one-line copy fix is its own kind of annoying: skip the questions only when the request is genuinely unambiguous and low-risk. When in doubt, ask. A pointed question is cheaper than a wrong build every single time.

Plan. Before touching files, lay out the shape of the change: the files and routes and components you will touch, the ones you will deliberately leave alone, the approach, any migration (additive, always), the tests the change needs, and anything risky. In this repo “risky” has a specific meaning: auth, IDOR, subscription gating, SSRF, secret handling, anything destructive or hard to reverse. The plan is a decision aid, not a document. It should be short enough to read in one breath and specific enough that approving it means something.

Confirm. Implement only after an explicit yes. If the plan changes in the back-and-forth, restate the revised version and get the yes again. And the part that actually matters over a long session: approval is scoped to the plan that was approved. If the work grows past it, the agent stops and re-plans instead of quietly expanding. That last clause is what keeps a “small fix” from turning into an afternoon of changes I never signed off on.

What it looks like per agent

I did not want one generic paragraph pasted eight times. The gate is the same, but what you ask about depends on the job, so each agent got the beats written for its lane.

A diagram featuring multiple lanes labeled with different topics: Migrations, Next.js, End-to-end, Docs, Unit testing, and Blog. Each lane has brief descriptions of its scope, accompanied by specific tags.

One gate, written for each lane. The two read-only reviewers pick the full gate back up the moment they move from finding to fixing.

The migrations agent plans the exact idempotent migration.sql and confirms it is purely additive before it applies anything. The unit-testing agent asks which behaviors to lock in and which boundaries to mock, then lists the cases each test file will assert. The e2e agent names the flows, the auth and seed prerequisites, and the breakpoints that matter. The docs agent confirms which of the three docs is in scope and whether a new page is needed. The blog agent (yes, this one) settles the angle and the section arc and which real code it will verify claims against before drafting a word.

The two read-only reviewers are the interesting edge. Security and UX do not implement, so there is no edit to gate. For them the gate degrades to its first beat: confirm the review scope if it is ambiguous (which routes, how deep, which breakpoints), then produce findings. But the moment the user says “now fix what you found,” they are implementers, and the full ask-plan-confirm gate snaps back on before they touch code. The reviewer does not get to slide from “here is a finding” into “and I fixed it” without crossing the same line everyone else crosses.

The obvious objection

This is slower. That is the point, and it is also not as true as it sounds. The plan step costs you a few seconds and one read. Rejecting a forty-second nine-file diff that went the wrong direction costs you the read plus the reject plus the re-prompt plus the nagging worry about what it touched that you did not catch. The gate front-loads a small, predictable cost to avoid a larger, unpredictable one. Over a day of handoffs it is not close.

It also composes with the other habit I built into these agents: every one of them does its work in an isolated git worktree, on its own branch, torn down when the task lands. Plan first, then do the approved work in a sandbox that cannot collide with anyone else. The worktree contains the blast radius. The plan makes sure there is not supposed to be a blast in the first place.

The pause was always the expensive part of good engineering. Worth teaching the machines to keep it.

I’m Adron, brainstorming and building InterlinedList.

A Research Agent on a Budget Leash, and How I’d Sequence the Whole Build

Part 8 of 8: Durable AI for InterlinedList


Back in Part 1, I said the whole series was one argument dressed up as eight posts. LLM tokens are the expensive thing, Temporal is not, and Temporal’s mechanics structurally push the token bill down. I also said the way I’d build it was to brainstorm against the real code first, then hand the agent structure instead of hope. This is where those two threads tie together.

The capstone I keep circling back to is the one that sounds the most like science fiction and is actually the most dangerous to ship carelessly: “research topic X from my saved links and draft a document with citations.” The agent retrieves. It reasons. It drafts. And if I’m not careful, it also burns through a month of my inference budget in one runaway loop while I’m asleep.

So this post is two things. The agent itself, and the playbook for building everything that came before it. The agent is the thing you build last, and the order it sits on top of is the part I actually want to argue for.

The Capstone Is Just the Earlier Pieces, Composed

This part should feel earned by now. The agentic-research-to-draft phase, the last of the proposals, doesn’t invent anything. It composes.

The link content from Part 5 (the crawl-and-cache that turns your saved URLs into readable text) is the agent’s source material. The retrieval from Part 5’s second half, the pgvector search over your own docs, lists, and messages, is how the agent finds the relevant three paragraphs instead of stuffing forty links into context. And the durable generation from Part 3, the workflow that survives a timeout and asks before it writes, is the drafting step.

Compose those three and you get an agent loop: retrieve, reason, retrieve again, draft, cite. Each of those steps is already an activity I’d have built and tested for its own feature. The agent is a workflow that calls them in a loop the model steers.

Which is exactly where it gets scary. A loop the model steers has no natural stopping point. It’ll keep deciding it needs one more search, one more fetch, one more pass at the draft. That’s not a bug in the model. That’s what an agent is. The bug would be letting it run unsupervised.

Temporal Supervising an LLM Agent Is a Nice Recursion

I like this framing because it’s a little bit funny. Temporal is an orchestrator whose entire job is supervising unreliable, long-running, retry-heavy work. An LLM agent is unreliable, long-running, retry-heavy work. So the agent runs inside a Temporal workflow, and the workflow is the adult in the room.

Three guardrails, all first-class workflow logic, none of them prompt-hope:

hard token budget. Before every model call, the workflow calls assertBudget, the same primitive from Part 7 that reads the AiGeneration ledger and throws a non-retryable BudgetExceeded when the user’s daily or monthly ceiling is hit. The agent can want another search all it likes. If the budget’s gone, the call never fires.

max-step cap. The loop counts its own iterations. Twelve steps, or whatever I tune it to, and it’s done. It drafts with what it has rather than spiraling.

wall-clock timeout. Temporal’s workflow timeout means even a wedged agent eventually stops, cleanly, with whatever it produced so far.

And here’s the piece that makes the whole thing cost-sane instead of terrifying: every step is memoized. If the agent fails at step 9 of a 12-step loop (provider hiccup, worker restart, whatever) the workflow resumes at step 9. It does not re-run steps 1 through 8. It does not re-pay for the eight model calls it already made. In a serverless handler, a mid-loop failure means starting the whole expensive agent over. Here it means retrying one step.

Sketching the loop, this is the shape I’d hand Claude to fill in:

// Proposed: the supervised agent loop as a Temporal workflow.
export async function researchAgentWorkflow(input: ResearchInput): Promise<Draft> {
  const MAX_STEPS = 12;
  const context: Snippet[] = [];

  for (let step = 0; step < MAX_STEPS; step++) {
    // Governor first: a hard ceiling the agent cannot talk past.
    await assertBudget(input.userId);

    // Cheap model plans the next move over the small retrieved context.
    const move = await planNextStep({
      model: "claude-haiku-4-5",
      goal: input.topic,
      context,
    });

    if (move.type === "done") break;

    // Retrieval is mechanical and nearly free. No expensive tier here.
    const hits = await retrieveFromLinks(input.userId, move.query);
    context.push(...hits); // stays small: top-k snippets, not whole pages
  }

  // Expensive tier runs exactly once, on the final synthesis.
  return await draftWithCitations({
    model: "claude-opus-4-8",
    topic: input.topic,
    context,
  });
}

The cost story lives in the model choices, not the prose. Retrieval and planning run on a cheap tier: Haiku for the “what should I look for next” decisions, which are frequent and small. Opus 4.8, the expensive tier, runs once, on the final synthesis, over a retrieval-augmented context that stayed deliberately small the whole way through. I’m not paying Opus prices to decide which link to read next. I pay them for the draft, and that’s the one thing in the loop I think is worth it.

No clever prompt is doing that. The structure is.

How I’d Sequence the Whole Build

If you’ve read the series straight through, you might be tempted to build the shiniest thing first. Don’t. The agent ships last, and the ordering underneath it is the real deliverable of this post, because every step ships value on its own and nothing is a big-bang.

Here’s the order I’d hand myself:

1. The foundation phase: the Temporal worker and the cost primitives. The worker, the client wiring, the llmCall activity with the model cascade, assertBudget, the response cache. This is the groundwork everything else stands on, and nothing works without it. Mostly infra, not code, and it’s a week.

2. The durable publish and idempotency fix, pure win, no LLM. This is the one from Part 4: the every-minute cron that can double-publish because a partial failure re-runs on the next tick. Rebuild it as a Temporal Schedule with the message id as the workflow id, so a given message can never be published twice. Zero model tokens. It fixes a current production hazard. If I could only ship one thing from this whole series, it’d be this.

3. Durable Generate plus confirm. Part 3. Long docs stop blowing the function budget, and a wrong list schema never gets silently written because the workflow pauses on a signal and waits for a human. This is the in-product generate button, on solid footing.

4. Link crawl, then retrieval. Part 5. Crawl the links into a cache (the cheapest, highest-cache-value piece in the set) then layer pgvector retrieval on top. This is “where did I save that article?”

5. Batch enrichment, then prompt-to-list. Part 6. The fan-out that tags everything and expands anything without re-billing completed calls, then the “build a list from a sentence” flow composed on top of it.

6. The AI calendar and digests. Part 4’s second half. Now that publishing is durable, the calendar planner and the scheduled digests have solid ground to schedule onto.

7. The cost dashboard, then the agent. Part 7’s dashboard makes “cheap” visible: tokens, cache-hit rate, budget remaining. I want that governor on-screen before I ship the most token-hungry feature in the whole set. Then, and only then, the agentic capstone.

The rule underneath the whole ordering is that value ships at every step. The publish fix helps users who never touch AI. The link cache is useful before any retrieval sits on it. The dashboard is useful before the agent exists. I’m never in a state where I’ve spent three weeks and have nothing a user can hold.

The Verification Is Where the Trust Actually Lives

The meta-point of the whole series is this one, so I’ll say it flat. You do not trust an autonomous agent because the prompt was good. You trust it because it runs inside guardrails, and because tests prove the properties you care about are there.

So the verification for the agentic capstone, and everything under it, is not an afterthought. It’s the thing that makes any of this shippable:

Vitest unit tests for the activity logic and the cost math. Does the cascade pick the cheap model for planning? Does assertBudget throw at the right ledger sum? Pure functions, no DB, fast.

Temporal replay tests that prove a workflow resumes correctly after a simulated worker crash. This is how I prove, not hope, that a retry doesn’t restart the expensive agent from step zero.

An idempotency test that starts two publish workflows with the same message id and asserts exactly one publish. That’s the double-post race, closed and locked.

Cost-assertion tests, the ones I care about most for the agent. Fail an activity mid-pipeline and assert the already-completed model calls are not re-invoked on retry. Memoization is money, and this test is how I know the money’s saved.

Playwright E2E for the generate to confirm to write flow, so the human-in-the-loop gate works in a real browser and not just in my head.

A security pass before any PR: SSRF on the crawler (everything through safeFetch), IDOR on every workflow write (where: { id, userId }), prompt-injection defaults (artifacts private by default so a poisoned link can’t publish on your behalf), and budget-bypass attempts.

And every schema change (the LinkContent table, the pgvector column, the response cache) goes through the strict additive migration workflow. Hand-written, idempotent, applied to both databases. No prisma db push, no shortcuts. That workflow has broken production before, precisely when someone skipped it.

What I Actually Learned Working With Claude

The lesson closes the loop the series opened. Supervise the agent with structure, let the tests carry the trust, and it turns out that’s also how I’ve been building with Claude the whole time.

You don’t hand an agent a vague goal and hope. You hand it constraints: a budget it can’t exceed, a step count it can’t blow past, a timeout it can’t outlast. Then you make it write the tests that prove those constraints hold, the replay test, the idempotency test, the cost-assertion test. What makes the agent trustworthy is the wall around it, not how nicely I phrased the ask.

And that’s true one level up too, which I didn’t expect going in. Sequencing the build so each step is independently verifiable (the publish fix before the calendar, the cache before retrieval, the dashboard before the agent) is the same move as capping the agent’s steps. Give the work boundaries, make the boundaries checkable, prove them before moving on. Whether the thing on the leash is a research agent or my own plan to build one, the leash is what I trust.

I’ve been brainstorming this out loud for eight posts. The pieces are grounded in code that already exists: the safeFetch guards, the DSL, the crosspost fan-out, the cron that really does risk a double-post. None of it is shipped. All of it is buildable, in the order above, with value at every step. So I’m going to start at step one and build the durable publish fix, because it’s a pure win with no model tokens and it closes a real hazard. I’d love for you to follow along, as I am going to move forward now and implement this. If you’re interested, subscribe!

Adron brainstorming and working on InterlinedList.

Tag Everything, Expand Anything, Without Re-Billing the Model

Part 6 of 8: Durable AI for InterlinedList


Every serverless AI feature I’ve built hits the same wall the second someone wants it at scale. “Can it tag all my untagged messages?” Sure, one message. “All of them?” Now we’re talking about a job that runs for minutes, touches hundreds of items, and will absolutely fall over partway through. And when it falls over, the naive version starts from zero and re-bills you for every item it already finished. Nobody wrote that behavior on purpose. It’s just what you get by default, and it’s expensive.

This is the post in the Durable AI series where durability pays off the most. Not “nice to have.” It’s the biggest cost saver in the whole series, and I’m putting that up front so you don’t skim past it.

The Jobs That Don’t Fit in a Request

There’s a whole class of work InterlinedList can’t do today and won’t be able to do inside a Vercel function, no matter how the AI plan lands. It’s the bulk stuff:

  • “Add 20 rows to this list matching X.”
  • “Tag every untagged message.”
  • “Summarize each finished book in my reading list into its own document.”
  • “Backfill link previews across all my docs.”

What these share is that they’re multi-minute, many-item, and retry-heavy. A serverless handler has a hard duration ceiling and no durable state between invocations. If item 34 of 50 throws a 429 from the model provider, the whole request dies and there’s no clean way to pick up at 35. So you either don’t offer the feature, or you ship a fragile version that quietly loses work and double-charges for the rest.

Today’s ground truth matters here, so let me be precise. Lists are a Subscriber feature. A list’s row data is stored as JSONB in ListDataRow.rowData, validated against a user-defined schema. The validator that gates every row lives in lib/lists/dsl-validator.ts. None of the batch AI machinery below exists yet: this is a brainstorm I’d hand to Claude to build, and AI assistance in the product is still “Coming Soon.” I’m designing it out loud.

The Shape: Fan Out, Cap Concurrency, Track Progress

The design I keep landing on is a generic fan-out workflow. One workflow supervises the whole batch; one item equals one activity. Each activity is independent and idempotent. It does its work, writes its result, and doesn’t care whether its neighbors succeeded or failed.

Temporal is the durable engine underneath, for reasons that are specific and not hand-wavy:

Partial progress is preserved. A completed activity’s result is persisted. When the workflow retries after a failure at item 47 of 50, it re-runs item 47 and only item 47. Items 1 through 46 already succeeded and their results are memoized, so it doesn’t touch them. On a batch of 50 LLM calls where one flakes, you re-bill one item, not fifty. This is the money argument, and I like it because it’s structural. You get it from the shape of the thing, not from remembering to code it.

Concurrency is capped. You don’t fire 50 model calls at once and trigger a 429 storm. A 429 storm is expensive twice over: the retries burn wall-clock, and on some providers they burn tokens too. Cap concurrency to a small number and you respect the provider’s rate limit (and your own BYO-key limit), so the retries never happen at all.

The job survives a worker restart. Deploy mid-batch, crash the worker, whatever. The workflow resumes from where it was, not from zero.

Here’s the constraint made concrete: a generic batchEnrich workflow that fans out over items with a bounded concurrency window.

// Workflow: supervises the batch, never touches the model or the DB directly.
export async function batchEnrich(input: BatchEnrichInput): Promise<void> {
  const { items, concurrency } = input; // e.g. concurrency = 5
  const running: Promise<void>[] = [];

  for (const item of items) {
    // enrichItem is an ACTIVITY: its result is durably memoized.
    // On a workflow retry, already-completed items are NOT re-invoked.
    // Temporal replays their recorded results instead of re-billing them.
    const p = enrichItemActivity(item).then(() => {
      recordProgress(item.id); // drives the live progress UI
    });
    running.push(p);

    // Cap in-flight work so we never exceed the provider / BYO-key rate limit.
    if (running.length >= concurrency) {
      await Promise.race(running);
      // prune settled promises so the window stays at `concurrency`
    }
  }

  await Promise.all(running);
}

The per-item activity is where the real work (and the idempotency) lives. It reuses code that already ships in the repo:

// Activity: one item. Independent, idempotent, individually retryable.
export async function enrichItemActivity(item: EnrichItem): Promise<void> {
  // 1. Cheap-model call for the mechanical part (tag / summarize).
  const enriched = await llmCall(item.prompt, { model: "cheap" });

  // 2. Validate against the list's real schema before writing anything.
  //    validateFormData lives in lib/lists/dsl-validator.ts, the same gate
  //    the app uses for every human-entered row.
  const result = validateFormData(item.fields, enriched.rowData);
  if (!result.isValid) throw new NonRetryableError(result.errors);

  // 3. Write through the existing materialize helpers, not raw Prisma.
  //    lib/materialize/build-list.ts / build-doc.ts already know how to
  //    turn structured data into rows and documents with ownership intact.
  await writeThroughMaterialize(item.target, enriched);
}

Nothing here reinvents validation or writing. validateFormData is the existing row validator. lib/materialize/build-list.ts and build-doc.ts are the existing builders that turn structured content into lists and documents. The persistence writers I sketched back in the foundation phase, the groundwork everything in this series stands on, handle the actual writes with IDOR checks intact. The workflow’s only job is orchestration: fan out, cap, track, resume.

The Cost Levers, Stacked

Cost is the spine running through this whole series, so let me connect the dots. Three levers push the LLM bill down, and this one feature stacks all three:

Batching. Tagging is mechanical and small. Instead of N calls to tag N messages, one call tags a batch of them: you collapse the per-request overhead and the token duplication of repeating the same system prompt N times over. Fan-out doesn’t have to mean one-model-call-per-item. The fan-out unit can itself be a batch.

A cheap-model default. Tagging and summarizing don’t need a frontier model. They need a competent, fast, cheap one. Claude Haiku 4.5 runs at $1 per million input tokens and $5 per million output; Opus 4.8 is $5 and $25. That’s a 5x swing for mechanical work where the cheap tier does the job fine. The default model for a batch-tag job should be the cheap one, and you escalate only for the rare task that needs it.

No redo on retry. Memoization again: the durable engine never re-invokes an already-billed call on a retry. This is the lever that costs you nothing to pull, because it’s baked into how the workflow resumes.

Put those together and a “tag everything” run over a few hundred messages costs cents instead of dollars, and a mid-run failure costs you one retry instead of a full re-run.

What I’d Tell Claude Before It Writes Anything

There’s a “working with Claude” lesson buried in all this, and honestly it’s why I wanted to write this post at all.

Resumability is a design constraint, not a feature you bolt on later. Ask an agent to “write a batch tagging job” and say nothing else, and you get a for loop that calls the model N times and stores results at the end. It works in the demo. Then it falls over on the first real batch, loses partial work, and re-bills everything on retry. Now you’re spending a day retrofitting durability into code that was never shaped for it, and durability doesn’t retrofit cleanly, because it changes what a “unit of work” even is.

So I state the constraints before the code, right in the prompt:

Each item is an independent, idempotent activity. The workflow must resume mid-batch without re-running finished items: a failure at item 47 of 50 re-processes only item 47. Cap concurrency at N to stay under the provider rate limit. Validate every item against the list schema before writing, and write through the existing materialize helpers, not raw Prisma.

Hand it that, and Claude generates the right thing: the generic batchEnrich workflow, the per-target activity adapters (list rows go through build-list, documents through build-doc, message tags through the tag writer), and the batched prompt that tags many items in one call. The constraints do the architectural work; the agent fills in the wiring. I own the seam (what’s an activity, what’s idempotent, where the concurrency cap lives) and let it own the code.

The failure mode I’m avoiding is the one where the constraint shows up after the code. State “don’t re-bill completed items” up front and it shapes the whole structure. State it after, and you’re rewriting.

The Surface

On the product side this becomes a “run a bulk action” panel: pick a target (a list, your messages, a set of docs), describe the enrichment, and watch live progress as items complete. Cancel is a Temporal Signal, a real mid-flight stop rather than a “please ignore the results” fiction. The workflow receives the signal, stops fanning out new items, lets the in-flight ones finish or abort, and reports what it got done. Progress and cancel both fall out of a workflow that already tracks per-item state.

Part 7 is the payoff post: the cost governor, where all of these levers get formalized into a budget ledger, a model cascade, and the response cache that make “cheap enough to ship” measurable instead of hoped-for.

Adron brainstorming and working on InterlinedList.

Monday’s Greeting & Miniature Emotive Micro-rant

I always find it painful for those of us that understand the semantic, etymologic, systemic, historical, and related first principles of things that have happened in our life time. For example the origination of “DevOps” or “Agile” and know the original coining, intent, and purpose of these terms and principles.

Also the simply things like “they’re”, “their”, and “there”, “where”, “were”, and “we’re”, or the comma usage in this very statement. Not just one or two misuses of these things, but the almost gas lighting nature of entire organizations (looking at whole parts of Microsoft) trying to redefine these things into other entirely new concepts, entire parts of society just ignoring or obliviously not learning these language elements, or a confluence of all these things coming together.

But even all that gas lighting and negligent use of ideas and words, the icing that makes the shit sandwich is, when the failures of society or organizations and people to know and use these concepts and terms and words correctly, then tells you – someone who was involved or knows the concepts and word usage well – that we’re somehow elitist or out of touch or don’t know what we’re talking about.

Utterly insane and levels of hubris that I just give no care to. It is almost as bad, and annoying, and frustrating I imagine as someone writing a code library, component, application, or inventing something and then having someone else explain it back to them wrong and tell them they’re wrong. Just wild madness among some to do this.

It’s painful, but also sometimes in that later case, hilarious to watch the person correct the confidently wrong, then mic drop with, “How do I know? Cuz I created the thing!” 🤣

The lesson, I suppose, that I’m inferring in this miniature emotive micro-rant, is don’t walk through life with the hubris and confidence that the wrong have. Walk through life with humility and learn to listen, always listen, even if you are the holder of knowledge, no matter the case, and be ready to learn and also teach.

With all that said, and my miniature emotive micro-rant complete – y’all have a great day and may this Monday not be like the trope Monday’s often have! Cheers!

When the AI Ghost Vanishes

You’re cruising along, vibe-coding your way through a new feature, and—poof—the AI assistant goes dark. Maybe it hallucinated a library that doesn’t exist. Maybe it repeated the same wrong snippet ad nauseam. Welcome to the moment of reckoning: your blind faith in “make me the code” meets cold, hard compiler errors.

Spinning the wheel of madness: You tweak a comment here. You change “public” to “private” there. You pray to the Codegen Deity. You hope it understands your increasingly desperate prompts.

Lose an hour or a day: You still haven’t fixed the NullReferenceException, and your caffeine cold-brew is now room temperature.

Blame the tool that’ll fix it!: It’s obviously a bug in the AI, right? Right? RIGHT? Your sanity is going to ebb, beware the blaming of tools!

This cycle feels familiar because it is, the tooling is great at scaffolding code, less so at understanding your context. When it bails on you, you’ll need a plan B.

Continue reading “When the AI Ghost Vanishes”