Post-Mortem ⏱️ 6 min read

The Fork Guard Trusted an Empty Folder

TL;DR

💥 The bug: The safety check protecting our original skills accepted "a folder with that name exists" as proof of identity, and another command auto-created that folder for any name you typed.
🔍 The audit: We turned our audit process on the Evolution engine itself: 30 defects, including 2 with a live path to deleting real skills.
🔧 The fix: v3.3.4 demands real birth records before any destructive command, rejects unsafe names outright, and re-proves every fix with a 30-check test suite.

The Engine That Edits Our Other Skills

The Godmode Evolution engine is a skill whose whole job is improving other skills. It copies a skill into a "fork" (a safe duplicate), scores every session the fork runs, and rewrites the fork when the scores say something is weak.

The entire product rests on one promise: the original skill is never modified. Forks evolve, originals stay sacred.

Fork

Copy the skill into a sibling folder. The copy is the lab rat; the original never gets touched.

Score

Every session gets graded across 6-10 dimensions, with evidence receipts so scores stay honest.

Mutate

When a dimension keeps scoring low, the engine rewrites the fork and benchmarks the change before keeping it.

Some of those operations delete folders. evo prune archives a failed fork to a graveyard, then removes its live directory.

Anything that deletes needs a gatekeeper. Ours is called the fork-safety guard, and it is the one component that must never be wrong.

What Does the Fork Guard Actually Do?

The fork guard is the safety check in front of every destructive command in the Evolution engine. Before anything overwrites or deletes a skill folder, the guard must confirm the target is a registered fork the engine created, and never an original skill you installed.

⌨️ evo prune --variant X — someone asks to delete fork X

🛡️ Fork guard: "is X actually a fork we created?"

✅ Yes → archive to graveyard, then delete the live folder

❌ No → refuse loudly, delete nothing

That check ran on every prune, revert, and rollback. The question the audit asked: what evidence does the guard accept?

The Bug: An Empty Folder Counted as ID

The guard accepted three forms of proof, and the first one was fatal: if a registry folder with that variant's name existed, it was treated as a registered fork. No birth record required, just the folder.

Here's the problem. The scoring command (evo eval) auto-creates that exact registry folder for any variant name you pass it, as a side effect of writing its logs.

Chain the two together and the cardinal promise collapses in two commands. Score anything under the name of a real skill, and that real skill silently becomes "a fork" the engine will happily bulldoze.

# 1. A stray eval, wrong --variant value, nothing looks broken:
evo eval --skill one-shot-scripts --variant one-shot --scorecard ...

# side effect: .evo/one-shot-scripts/one-shot/ now exists

# 2. Later, a cleanup pass:
evo prune --skill one-shot-scripts --variant one-shot

# old guard: "folder exists, must be a fork" -> deletes the REAL
# ~/.claude/skills/one-shot/ skill. Promise broken.

Think of a demolition office: the rule says "only demolish buildings on our own construction list." But the clerk checks the rule by looking for a paper folder with the address on it, and the front desk auto-creates an empty folder for any address someone merely asks about. Ask one question about 12 Main St, and the bulldozer is cleared to flatten a real house.

The fix in v3.3.4: folder existence is no longer evidence of anything. The guard now demands a real birth record: a forked_from marker written at fork time, or a lineage log with actual events in it.

Click through the four scenarios to see both guards rule on them:

Same input, both guard versions. The v3.3.4 column is what ships today.

The Second Hole: Names That Escape the Tree

The second destructive-path bug was quieter. Skill and variant names become folder path segments, and some of those paths end up at a recursive delete.

Nothing checked the names. A variant called ..\..\something would be glued into a path that walks out of the engine's directory tree before the delete fires.

The patch adds one strict rule at the lowest layer, where every path gets built. Reject empty names, ., .., and anything containing a slash or backslash, before a single byte is touched.

How the Audit Ran

This engine has audited plenty of other skills. This time we pointed the process at the engine itself, from a fresh session with no loyalty to the code it was reading.

🤖 Two subagents sweep in parallel: one reads all 25 runner source files, one reads all 14 engine docs

🧾 Findings merge into one list, ranked by severity

🔬 Every HIGH and consequential MEDIUM gets hand-verified against source, line by line

🛠️ Patch lands as v3.3.4, one fix at a time

✅ A 30-check suite replays the attacks and proves each one now fails

The verification suite is the part we're proudest of. It doesn't just test the happy path; it re-runs the actual poisoning attack and asserts the guard now refuses.

Audit statValue
Total defects found30 (3 high, 13 medium, 14 low)
Destructive-path bugs2, both in the fork-safety layer
Files patched13 runner source files + 6 engine docs
Verification checks30 written, 30 passing
Version shipped3.3.2 → 3.3.4
Real skills harmed0 (the bug was found before it ever fired)

What Else Got Fixed

The other 28 findings weren't headline material, but several changed real behavior. The pattern across them: silent wrong beats loud wrong every time, so make wrong loud.

FixWhat was happening
No silent acceptsCommitting a mutation with no benchmark results quietly counted as ACCEPTED. Now it refuses unless you pass results or explicitly approve.
Score math mismatchThe adjusted score was computed from a different weight set than the main score for some task types. One word of code, skewed numbers.
Benchmarks that couldn't passA suite with 1 genuine pass and 1 skipped check counted as a FAIL. Skips are now neutral, as documented.
Archives lost subfoldersVersion snapshots only copied top-level files, so restoring a multi-folder skill silently dropped its companion scripts. Both directions are now fully recursive.
Crash-safe loggingThe human-verdict command rewrote the whole feedback history file in place. A crash mid-write could truncate months of data. Now atomic.
Docs vs runner driftSix docs referenced flags, thresholds, and gate counts that didn't match the code. All reconciled against the source of truth.

Three old forks also predate the runner's record-keeping, so the patch backfilled honest forked_from markers for them, with a note saying exactly when and why. Tightening a guard must not orphan the legitimate residents.

The Lesson: Audit Guardrails From Outside the Loop

A self-improving system mutates everything around it, but its own safety rails only change when a human decides they should. That means the rails rot at a different speed than the code they protect.

The eval command's folder side effect was added innocently, months after the guard was written. Neither change was wrong alone; together they armed the bulldozer.

Do

Audit the safety layer from a fresh context that treats "folder exists" and every other assumption as a claim to verify.

Don't

Let the system that benefits from passing checks be the only thing that ever reads the checker's code.

Core insight: the guard didn't fail because its logic was sloppy. It failed because a neighboring feature quietly changed what its evidence meant. Guardrails need re-auditing every time anything near them changes, and that audit has to come from outside the loop.

This is the third time this process has paid for itself; the same outside-the-loop sweep previously caught 16 defects in Evolution v3.1 and a scorer reading the wrong field in our quality loop.

Evolve your skills on rails that hold

The Evolution engine forks, scores, and mutates your Claude Code skills, and v3.3.4 ships with the hardened guard described above.

See the Evolution engine View pricing