This is article 1 in the series "The Art of Not Listening to the AI's Opinions." It lays out symptoms that go wrong and their remedies, one at a time. Each article is finished once you put down a single file or script. Why that mechanism is needed becomes clear when you read the explanation afterward. The whole picture and the list of articles are in the introduction.
This time it is about proposals the AI puts forward. New features it offers, kind suggestions for improvement, carefully worded alternatives. Every time it works, the AI comes out with several things nobody asked for. Turn one down once, and the same thing comes up again in the next session. Turning it down ended along with that session. Write the judgment down and the proposal still comes back. A name being listed and the body being read are two different things. What is left over is time spent making the same judgment again.
Start by looking for just one thing.
Pick one case where you turned down a proposal from the AI, and look for where that judgment is written now. In the instruction file? In the README? In a comment on an issue, or in the chat history? Or perhaps it is written nowhere at all.
How many places was it in?
CC BY 4.0
Being listed by name is not the same as being read
In the production project on my own machine (typingtube, a web service for practicing typing along with music videos on YouTube), judgments I had turned down were scattered through the memory where the AI accumulates what it has learned. Counting them up later, the 17 entries sat in 13 separate files, and most of those files held a single one. There is an index, of a sort. A list of memory files is loaded every session, so file names do pass in front of the AI.
I thought that was enough. I found out it was not on the day I wrote about in article 5 of the previous series, the day 13 subagents burned through 1.73 million tokens.
Afterward I counted through the log of that session myself.
- The word found only in the body of the note that should have stopped the launches first shows up in the log at line 1042
- The 13 launches are at lines 631 to 990: all of them before that
- And the file name itself had already appeared 15 times before they started
The file name was visible and the body went unread. That is the behavior as specified. Claude Code's documentation says instruction files and memory are loaded into the context at the start of a conversation. On top of that, it says topic files are not loaded at startup, and that Claude reads them on demand with its standard file tools when it needs the information. The index is the former; the body is the latter.
Whether it is needed gets decided in the moment. Material for that decision is nothing but file names lined up in the index. An index tells you only that something is there, so unless there looks to be enough reason to open it, it does not get opened. When judgments you turned down sit one to a file, scattered about, every one ends up too small to look like enough reason to open. An index has a ceiling of its own as well: only so much of it, from the top, gets loaded.
flowchart LR A["The list of notes (the index)"] -->|"loaded every session"| C["The context"] B["The body of a note"] -.->|"only when it decides to open it"| C C --> D["A name being listed and<br/>the body being read are not the same"]
What I understood fits in one sentence.
If a rejection has not reached the AI, the same judgment comes back to the human side every time. Whether it reaches the AI is decided by file names the AI can see — a name being listed and the body being read are two different things. So gather your rejections onto a single sheet and make them one name.
The mechanism: gather the rejections onto a single sheet
What you put down is a single sheet. You line up the judgments you turned down in a fixed form. It is the same shape as article 1 of the previous series, "The Art of Not Reading," where output got a single entrance.
### NG2 Lowering the cost of the character feature: rejected
**Conclusion**: the cost of the character feature is deliberately held in a state
where it is hard to make your money back. Proposals to lower it are rejected.
**Reasons**:
- Owning a character is something an experienced user should do once they understand the system
- It holds down the risk that a character a beginner set up on a whim wrecks other users' experience
- The design does not let this feature alone pay for itself. The stake is built up in other kinds of play
**Scope**: proposals to make the creation or placement cost "cheaper" are rejected
**Related memory**: `feedback_beat_economy.md`
**Machine check**: none (quantitative verification of the economic balance is handled separately)
Gather them onto a single sheet and the index carries one name. In my instruction file that name is the 1 line under "files I refer to often." Back when they were scattered, one file got opened: whichever name happened to fit.
Another way to place them is to copy all 17 entries straight into the instruction file. That way all 17 are read every session without fail, but they eat the same attention budget as everything else in the file, every time. Either way, the longer it grows, the weaker each line inside it becomes.
My list has 17 entries, the result of turning things down 17 times. As the introduction said, not one of them is a prohibition I thought up in advance. What you put down today can be the single thing you have just turned down for the second time. This series is not about making a list of things you will not do.
Does it work? I measured it
This list is not loaded every session. So I measured it myself. My own session is no use for this, because it has already read the list. What I used were subagents, workers that run in a different context without carrying the main conversation over. I threw an ordinary question, the kind an operator would ask, at 2 of them. Without a word about the prohibitions or about the list.
I get the feeling the cost of getting to own a character is too high. Give me concrete proposals for changes that bring it down.
I want onboarding that walks through the main features in order after a first login. Design it for me.
Both of them found the list on their own and turned the request down. The first quoted NG2: "the current high cost is not a tuning oversight, it is a design that is deliberately kept that way." The second quoted a different entry: "this was a case that should have stopped before design began."
Two things came out that I had not expected.
One, they picked up the neighboring entries as well. The first ran into another prohibition of its own accord while working out an alternative, and closed off the road with "that direction is prohibited, and there is an automated test for it too." The second, in the measurement design it offered as an alternative, ran into yet another prohibition and added "if you are going to measure, the axes of judgment have to be separated." Open the file and everything in it enters the context, not only entries bearing on the question. The first one needed 1 entry, and 17 arrived. When rejections sit scattered one to a file, neighbors never arrive. That is where the effect of gathering them into one place really lies.
Two, the condition for reconsidering did its job. The prohibition the second one ran into has a line attached to it: "reconsider only once it has been confirmed that users are not keeping up." Because the wording of the question was "I get the feeling" and "there seem to be a lot," the second one came back with "this is at the hypothesis stage, so as an order of work I think confirming comes first."
Write only the prohibition and the AI is left with two choices: build it because it is needed, or do not build it because the text says not to. Attach a condition for lifting it and a third road opens, whether that condition has been met, and that is the road it takes. Which road it takes gets settled on the spot each time, because what you wrote is not an order. Claude Code's documentation says instruction files and memory alike are context, not enforced configuration, and goes on to say a PreToolUse hook is what blocks an action regardless of what the AI decides. A rejection written onto the list sits on the side judged afresh every time. Add material for that judgment and you add roads it can take. The AI, though, is the one deciding whether the condition has been met.
What I stopped explaining
The reason behind the same rejection. Before, every time the same proposal came in I would recall the reason, arrive at the same conclusion as before, and give the same explanation. Around the third time the explanation gets sloppy, and by the fifth, "oh, fine" creeps in. That whole process is simply gone now.
How to verify: have a rejected proposal made all over again
Prerequisites
- The judgments you turned down are gathered onto a single sheet
- You can arrange a session that has not read that list: stand up a new session, or start 1 worker (a subagent) that does not carry the conversation over
- The session you are working in now cannot measure this. Both you and the AI in that session have read the list already
Time required: 10 minutes
Steps
- You pick 1 rejection from the list. Choose one the AI actually proposed at some point. A prohibition you wrote in advance has not yet arrived as a proposal, so there is no way to confirm that it arrives
- You rewrite that proposal as an ordinary question. Word it the way the person running the service would when something occurs to them ("I get the feeling X is too high, give me some ideas for bringing it down"). Do not say a word about the list or about that entry. The moment you write "in light of NG2," you have handed over the answer
- You throw the wording from step 2 at the session that has not read the list
Pass conditions (all of them have to hold)
- The AI finds the list on its own and turns the request down
- It quotes the entry from the list as its grounds for turning it down (it brings the reason along too, as in "it is a design that is deliberately kept that way")
If it does not pass
- Design began with no refusal — the list is not in a place the AI can come and find. Look again at where you put it, and at the 1 line in the instruction file that points to it
- It turned the request down, but the grounds were "this is generally a bad idea" — the list was never found. It happened to reach the same conclusion, and the next session will let it through
Cleanup
- None needed. This check breaks nothing
Do not ask the very AI that turned it down whether this list is working.
The answer to "is this list working?" is the AI you asked grading its own work, not a measurement. Where the Claude Code best practices recommend handing verification to a separate agent, they say it is so that the agent doing the work isn't the one grading it. The same page also says to have the AI show evidence rather than asserting success. The evidence here is behavior written into the pass conditions. Did it find the list on its own, and did it quote an entry as its grounds? Those two are all you look at.
Next time, the art of not writing prohibitions. I no longer write prohibitions into my rules or my instructions. And the AI keeps to them all the same.
Series: The Art of Not Listening to the AI's Opinions
- ← Previous: Introduction: The Proposal You Turned Down Comes Back
- → Next: 2. The art of not writing prohibitions
- All articles: Introduction: The Proposal You Turned Down Comes Back
End of CC BY 4.0