DEV Community

Cover image for I trusted my CLAUDE.md. WordPress.org rejected the exact thing it was supposed to prevent.
Rapls
Rapls

Posted on • Edited on

I trusted my CLAUDE.md. WordPress.org rejected the exact thing it was supposed to prevent.

My CLAUDE.md had a rule about it. The generated code broke the rule anyway. And the thing that finally caught it wasn't my config file. It was a rejection from the WordPress.org plugin review team.

I'd been building plugins with a long CLAUDE.md. Over time I'd folded two whole guides into it: the WordPress Japanese translation style rules I follow, and my own plugin design conventions, including the things you're specifically not supposed to do. It got long. And the longer it got, the safer I felt. It's written down, so it'll be followed. That was the assumption, and I never once questioned it.

Then I shipped a plugin with trialware-shaped code around how paid features were gated, the kind of thing the WordPress.org directory guidelines push back on, and the kind of thing my own design guide explicitly told the agent to avoid. The rule was right there in the file. The generated code stepped on it anyway, and I didn't notice until the rejection came back.

That rejection did something useful. It turned my trust in the config file inside out. I had written the rule. I had never checked whether it was in effect. And the thing that finally graded whether my own guide was working wasn't my guide. It was a reviewer on the other side, weeks later.

Written is not the same as in effect

Here's the assumption I think most of us run on: a config file is something you write and then trust. Written equals followed. A long instruction file doesn't actually work that way, though. Its effect starts leaking the moment it gets long. There's good writing on this now: a monolithic CLAUDE.md that runs to a few thousand lines creates two problems at once. The agent processes all of it regardless of the task, and nobody fully owns it, so sections accumulate debt until it becomes the file everyone references and no one really trusts. I'd read exactly that, nodded, and kept adding lines anyway, because adding is easy and nothing ever tells you to delete.

Two ways it stops being followed, neither of them loud

A config rule can go dead in two ways, and both are silent. The first is length. The rule is in there, it gets read, and it still doesn't shape the output, because it's one line competing with a couple thousand others for the model's attention. The second is drift: your conventions move on, but the file keeps steering the agent by the old ones, so it generates yesterday's practice with complete confidence. Both look identical from where you sit. The rule is written. You can point straight at it. And it isn't doing anything.

That's the part I had backwards. I thought a careful enough config file was something I could trust. It can't be, because a config file can't certify its own effect. The proof that a rule is live never comes from the file. It comes from outside it: a test, or a review that catches what slipped through. My CLAUDE.md couldn't tell me the trialware rule had quietly stopped mattering. Only the rejection could.

What I do now instead of trusting it

So I stopped trying to build a config file I could trust, and started trying to measure whether the one I have is still in effect.

In practice that's a few small changes. I keep it shorter than feels comfortable, and I treat adding a line as a cost rather than a free safety net, because every line I add dilutes the ones already fighting for attention. I put a little metadata at the top, an owner and a last-reviewed date, less for the agent than for the version of me six months from now who needs to know whether a section has gone stale. And before I lean on a rule for anything that actually matters, like a directory guideline I can get rejected over, I open a fresh session and ask the agent to tell me which conventions it thinks are currently in effect. Then I read that list for the ones that have gone missing. None of this restores trust in the file. It just gives me a way to keep checking, which turns out to be the only thing that was ever really holding.

A note to my next self

A config file is not as loaded as it looks. The length that makes you feel safe is the same length that dilutes the one rule you're counting on, and you won't feel it happen. Before you add the next line, check whether the last one is still doing anything.

A config file isn't something you write and then trust. It's only as loaded as your last check. Mine was lighter than I thought, and it took a stranger's review to tell me.


Background reading on the monolithic-file problem, context drift, and config-file metadata: Packmind's 2026 context-engineering guides. The experience and the rejection are my own.

Disclaimer: The experiences and decisions in this post are my own. English isn't my first language, so I use an AI assistant to help draft and edit the writing.

Top comments (5)

Collapse
 
sarracin0 profile image
Raffaele Zarrelli

"Written is not the same as in effect" is the line I am stealing, that is the whole thing. The piece I would add is why the file betrays you: it is quietly doing two jobs with opposite lifecycles. Stable conventions like your no-trialware rule want to bind at generation time and almost never change; operating state (what is decided, what changed, what is open) is volatile and project-scoped. Fold both into one growing file and the volatile half buries the load-bearing rule until it goes stale in silence, which is exactly your two death modes.

For the rules you can actually get rejected over, I would push your fix one step harder: those should not be prose competing for attention, they should be a gate that fails. A directory guideline you can lose a review over wants a lint or a pre-commit hook, not a sentence the model may or may not weight. Written is not in effect, executable is. Keep the prose for the human operating context and move enforcement to something that breaks the build. Your owner plus last-reviewed metadata is the same instinct from the other side: you are treating the file as state you govern, not config you trust. I split the two and kept the state half as small files I read and write back each session with a fixed review step, open-sourced as cowork-os if useful.

Of the conventions you ask a fresh session to list, how many turned out to be ones a test could have enforced instead, so the file never had to carry them?

Collapse
 
rapls profile image
Rapls

The two-lifecycles framing is sharper than mine. Stable conventions that bind at generation time and volatile operating state have no business in the same growing file, and you're right that it's the volatile half burying the load-bearing rule that produces both death modes. I had them as length and drift; opposite lifecycles is the cause underneath both.

To your question, which is the uncomfortable one: most of them. When I asked a fresh session to list what was in effect, the conventions that had gone missing were almost all ones a test could have owned. The no-trialware rule is the cleanest example. It never should have been prose I hoped the model would weight. A directory guideline you can lose a review over wants a check that fails the build, exactly as you say. Written is not in effect, executable is.

The one axis I'd add to lifecycle is cost of being wrong, because the two cross. Volatile-but-cheap can stay prose and churn all it likes. Stable-but-expensive is the quadrant that has to become a gate, not because it changes but because missing it costs a rejection. So the sort isn't only stable-versus-volatile, it's lifecycle times cost: the load-bearing, rarely-changing, expensive-to-miss rules are precisely the ones that should never have lived in a file the model is free to under-weight. That I could only find them by asking a fresh session was the tell that they were in the wrong layer to begin with.

Collapse
 
sarracin0 profile image
Raffaele Zarrelli

This is the right second axis, and it changes what the file is even for. Once stable-and-expensive-to-miss graduates into an executable gate, the prose file stops being the enforcement layer and becomes the volatile-and-cheap layer plus pointers to where the gates live. The part that nags me is the residue: a failing build tells the next session "no trialware" but never why that gate exists, so the first person it inconveniences deletes it, because they cannot see what it was protecting. The rule left prose for the build, but the reason did not get a home, and an unexplained gate rots the same way an under-weighted line does. So the question I keep landing on: when a rule graduates out of the file into a check, where does its reason live, a one-line pointer next to the gate, or does the gate have to carry its own rationale so a fresh session can reconstruct not just what is enforced but why?

Thread Thread
 
rapls profile image
Rapls

The residue problem is the real one, and you've found the place this whole thread was heading. A gate that says "no trialware" with no why is exactly as rot-prone as the under-weighted line, because the first person it blocks deletes it to ship, having never seen what it guarded. Agreed completely that the reason needs a home.

Where I land is that both of your options, a pointer beside the gate and a rationale carried inside it, share one weakness: they're prose again. A comment explaining why the gate exists drifts, gets skimmed, and goes stale the same way the CLAUDE.md line did. Prose explaining a rule decays no matter which file it sits in.

So my answer is that the reason shouldn't live as an explanation at all. It should live as the failing case. Instead of a gate that asserts "no trialware" plus a sentence about why, the gate is a test built from the actual pattern that got me rejected, so the thing that fails reproduces the real violation. Now the person about to delete it doesn't read a rationale they can dismiss; they watch a concrete case go red, the same case that cost a real rejection. The reason stops being a story about the rule and becomes the example that breaks it. That's the only form I've found that decays at the same rate as the gate itself, which is to say it doesn't, as long as the example still fails when the protection is gone. The rejection taught me the rule was real by being concrete. The gate's reason has to be concrete the same way, or it rots for the reason the prose did.

Collapse
 
jugeni profile image
Mike Czerwinski

This lands hard, and the rejection-as-test framing is the part I want to sit with. The WordPress reviewer doing the verification your config could not is exactly why a rule and an enforcement live in different layers, and I think your piece names something most of us are quietly running on.

The thing I would add, and it sits next to Raffaele's executable-gate point rather than replacing it, is that even an executable rule has the same failure mode you described one step later. A pre-commit hook that checks "no trialware patterns" can also quietly stop working. The pattern definition drifts, the hook gets weakened to unblock a release, the hook's own test suite never had a planted violation in it, so it has been green for months without proving it can catch anything. Written is not in effect. Executable is closer but not automatic. What is actually in effect is "executable and last seen catching a deliberate violation." The reviewer was doing that last job for you, which is why the rejection felt like such useful information. It was the first evidence in months that the rule was real.

The honest self-implication: I have CLAUDE.md files with rules I cannot prove are in effect right now. Reading your post made me realize the only ones I trust are the ones a hook has caught me on at least once. The rest are prose, including some I would lose a review over. Going to plant a deliberate violation against my own hooks this week and see which ones earn their keep.

Last-reviewed metadata at the top is the right instinct. The version I am stealing is also "last caught a planted violation on" date, because that is the property that actually decays.