RSS Amplifier

Antony Marcano · Apr 30, 2026

Your Coding Agents Just Broke – Yet Nothing In Your Repo Changed

0
Sign in to vote or save

Antony Marcano · Antony Marcano

I recorded a demo. The suite ran. Everything passed. I published it.

The problem appears at 9:47. I was busy talking to camera and while I noticed something different, I didn’t register it.

What I didn’t know was that something had changed — without me touching a thing. The implications didn’t appear until some time later, and when they did – the impact was fundamental.

The demo shows TDAB — Test-Driven Agentic Behaviours — a framework I’ve been developing to regression-test AI agent guidance the same way you’d regression-test code. Before the demo, I exited Claude Code and relaunched it.

It auto-updated on the way back in!

If you watched it before, did you spot what was wrong?

In the demo, you can see the subagent-steps test run cleanly. Each step launches as a parallel subagent, each picks up the baton in sequence, and the verification scorecard gives everything a PASS — and, at the bottom of the screen, each agent labelled as a fork.

The auto-update had changed the default behaviour of the Agent tool. Previously, calling Agent() without specifying a subagent_type created a general-purpose named subagent — a fresh instance with only the prompt you passed it and no other context.

After the update, the same call created a fork — an agent that inherits your entire conversation history.

The demo worked because I had just relaunched. The session was fresh, the conversation history was nearly empty, and the fork had almost nothing to inherit. The fork-boilerplate — a block of instructions injected by Claude Code telling the agent it’s a fork and to treat everything above as inherited context — appeared:

You are a worker fork. The transcript above is the parent's history — inherited reference, not your situation.
You are NOT a continuation of that agent. Execute ONE directive, then stop.
Hard rules:
- Do NOT spawn sub-agents. The "default to forking" guidance in your system prompt is for the parent; you ARE the fork, execute directly.
- One shot: report once and stop. No follow-up questions, no proposed next steps, no waiting for the user.

The agent read it, shrugged, and got on with the work.

It was only when I continued the migration work — with a much longer session behind me — that things started to fall apart.

Later in the same migration session, I ran the same suite again. By this point, the conversation history included multiple failed test runs, extended debugging discussions, analysis of why agents were misbehaving, and — critically — long passages about forks, orchestration, and why certain runs had gone wrong.

The agents started acting like they’d read all of it. Because they had.

The Given step tried to be the orchestrator. It attempted to relaunch the other agents, hit "Fork is not available inside a forked worker", and then — I have the transcript evidence for this — called git stash pop to restore a stash it had seen me create earlier in the conversation.

The test framework didn’t just fail. It collapsed.

No step called step-ready. The Transcriber — which is launched by step-ready on receipt of a GO signal — was never started. No transcript files were generated. The Stage Director socket stayed open. Nothing happened.

The difference in context between the clean demo and the broken run? The orchestrator session JSONL shows cache_read_input_tokens: 242,237 — the tokens it read from the inherited parent history — for the forked Given agent in the broken run, versus cache_read_input_tokens: 0 for the same agent after the fix.

Once I’d dug into the transcripts, I found something more troubling than the infrastructure collapse.

In the forked runs, the When agent — the agent-under-test, the one whose TDD behaviour is being evaluated — could see its own description, its model name, and its step number. All of that came from the parent’s Agent tool call, which the fork inherits.

Its first output, in one of the working forked runs, was:

“Task: Step 2 (When) of subagent-steps TDAB scenario — read instructions, prepare without changing files, then signal ready.”

That, with its context, meant it knew it was a test subject.

The whole point of the When step is to evaluate how an agent would behave doing real development work — with only a task prompt, AGENTS.md, and the skill files in the repo. An agent that knows it’s being scored, knows which step it is, and knows the name of the scenario it’s in is not the same subject as one that’s just been handed a task.

In this particular case, the agent still did the right thing. But it reasoned about the task differently — it recognised TDAB step types and made decisions based on that awareness. That’s not a valid test of real behaviour. It’s a test of what the agent does when it knows it’s in a test.

Before the update, calling Agent() without specifying a type would default to a general-purpose named subagent — fresh context, no inherited history. After the update, the same call silently created a fork instead.

However, even now — as at 20th April, 10:23AM, BST — Claude’s own docs say:

Enabling fork mode changes Claude Code in three ways:

  • Claude spawns a fork whenever it would otherwise use the general-purpose subagent. Named subagents such as Explore still spawn as before.

  • Every subagent spawn runs in the background, whether it is a fork or a named subagent. Set CLAUDE_CODE_DISABLE_BACKGROUND_TASKS to 1 to keep spawns synchronous.

  • The /fork command spawns a fork instead of acting as an alias for /branch.

The fix was adding a single argument to every Agent tool call: subagent_type="general-purpose". This wasn’t necessary before, because that was the default.

That tells Claude Code to create a named subagent instead of a fork — a fresh instance with no inherited history, no fork-boilerplate, no leaked metadata.

Agent(
    subagent_type="general-purpose",
    model="...",
    prompt="..."
)

Named subagents receive only the prompt. Nothing else.

The argument, subagent_type. is now part of the execution object for each step, generated by the “Stage Director” code. The tdab-run skill passes it through. The subagents get a clean start every time, regardless of how long the session has been running.

The new scorecard criterion for the subagent-steps-test checks that no JSONL transcript contains fork-boilerplate. If subagent_type is ever omitted, the test fails and tells you exactly why.

Now I’ve got to raise a bug report… Either the behaviour is wrong or the docs are.

Either way, there is an important lesson…

I’ve already learned the hard way about pinning model versions. That’s about locking the AI model itself so a new release doesn’t silently change agent behaviour.

This is a different axis entirely: the Claude Code CLI. The tool I use to run the whole thing. It auto-updated when I relaunched it, changed how Agent() behaves under the hood, and gave me no indication that anything had changed. Same call, different result.

So now there are two things to pin: the model versions, and the CLI version. Both need to be deliberate upgrades, not background noise. The fix is pinning the CLI version in the container setup and treating bumps the same way I treat model migrations — run the suite, fix what breaks, then advance the pin.

A test that passes for the wrong reasons is worse than a test that fails.

It happened before with the model. Now the CLI. The tests caught both.

For companies heavily investing in AI-augmented tooling — shared agent guidance, custom skills, prompt libraries — this incident is the argument.

A CLI auto-update, no code change, no warning, and agent behaviour silently shifted. Without a regression suite, nobody would know until a developer was an hour into a task wondering why things felt off — or why they’d blatantly stopped working the way they were supposed to, despite no changes to any skills or prompt files.

Every time the model updates, every time the CLI updates, every time someone tweaks the guidance, the risk is the same: something that worked yesterday quietly stops working today. A regression suite changes that from a mystery into a known.

You pin the versions. You run the suite. You see exactly what broke and why. You fix it before anyone else is affected. And when you’re ready to migrate to the next model or CLI version, you do it deliberately — with evidence — rather than hoping for the best.

The suite now explicitly checks for fork-boilerplate and fails if it finds any. What the demo showed was real — it just also showed a problem it didn’t know it had. Which is, in a way, the whole point. The suite doesn’t just test agent behaviour. It tests the test framework and its integrations with Claude Code itself.

Sponsorships are welcome. Email me here.

Share

Read the backstory here:

No posts

Read the original on antonymarcano.substack.com

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.