Recently, Claude Code just... stopped. Not a bug in my code, not an outage. I had burned through my entire Opus token quota, and I was locked out for five days. Token quota jail. The only way out early was a helpdesk ticket asking for a quota boost.
That's an awkward ticket to write. I use this thing every day, I thought I was a pretty disciplined user, and here I was asking for more tokens like a kid asking for more allowance. Somewhere between submitting the ticket and getting it approved, it hit me that the problem probably wasn't the quota. It was me.
I'm a data consultant. My days are SQL Server and Snowflake queries, Python pipelines, and Excel reports that end up in front of stakeholders. For the last five months I've done pretty much all of it with Claude Code.
Every day I'd see videos and posts from Anthropic folks and others on how to use it "the right way"... and I'd think, I'm doing fine, I have a system. I did things my way and did my best to stay organized. What I didn't realize was that I'd slowly dug myself into a hole.
So once I was back in, the first thing I did was ask Claude to look at its own session logs and tell me, honestly, how I was using it. Where were all my tokens going?
The system I'd built
Every new request got the full treatment: six documents covering the original ask, requirements, design, acceptance criteria, decisions, and progress. The star of the show was PROGRESS.md. I told Claude, in so many words, "this file is YOUR memory, not mine." My instructions said to read it before answering any prompt and update it after every step.
And I never closed a chat. One chat per workstream, for as long as the work went on. The whole point was to not lose context.
What the logs actually said
Claude Code saves every session as a JSONL file on your machine. A short script over mine came back with this:
- My biggest client project lived in one chat for 145 days. 1,094 prompts. 99 compactions.
- In that one chat, Claude re-read PROGRESS.md 316 times. About 50 of those were full reads (~17K tokens each) and the rest were partial slices where it went back for more. Added up, that's roughly a million tokens spent re-reading the same file.
- The file itself had grown to 280 KB, about 70K tokens, too big for a single read to even return all of it, which is part of why Claude kept coming back. Around 78% of it was run-by-run log, stuff like "Run 5b: 1,269 mismatches…". The header still described where the project stood four months earlier.
- The median message was carrying about 110K tokens of context.
If you haven't run into it: a compaction is Claude Code summarizing a long conversation so it can keep going. It's lossy, by design. So after 99 of them, Claude was working off a summary of a summary... and then re-reading a giant log to make up for it. Which explains a lot, honestly. It always felt like I had to keep feeding it context, and now I knew why.
I thought I was preserving context. I was burying it. And paying for it, a few thousand tokens at a time, until the quota ran out.
The part that surprised me: my actual configuration, the instructions and memory index that load into every session, was only about 4.6K tokens. That was fine. The problem wasn't the setup. It was the habits.
What I'd gotten right (credit where due)
I don't want to undersell this, because the fix kept all of it:
- I had a playbook of lessons written as Rule / Why / How to apply / who caught it. Turns out that's exactly what good reusable instructions look like.
- Every spec had numbered, testable business rules with acceptance checks. Giving Claude something it can actually run and pass or fail is right at the center of Anthropic's own guidance.
- I'd taught Claude to restate what I was asking before starting, because I tend to brain-dump. That one saved me a lot of redo.
So the ideas were right. They were just sitting in the wrong places.
So what changed
PROGRESS.md was doing three jobs at once: where things stand, the full history, and everything I'd learned about the data. I split those up:
| What | Where it lives now | When Claude sees it |
|---|---|---|
| Rules I always want followed | Two short CLAUDE.md files (~75 lines total) | Every session |
| Domain knowledge: tables, gotchas, methods | Skills | Only when the task needs it |
| How to run a project, its source-of-truth files | That project's CLAUDE.md | When I'm working in that folder |
| Where things stand | STATUS.md, under 60 lines, rewritten in place | When I pick the project back up |
| History | git | Never in full |
| Rules that must NEVER break | Hooks | Enforced by the tool, not by Claude's judgment |
Skills were the big one for me. Only a one-line description loads by default, and the rest loads when a task actually needs it. I turned months of logs into a handful of skills, with one rule I learned the hard way: extract, don't summarize. Every fact points to the file and line it came from, and query logic never gets paraphrased... the skill just points to the SQL file. I'd tried a skill before that paraphrased my business logic, and it got about 10% of the definitions wrong. That's the kind of wrong you don't catch until a stakeholder does.
Data drifts too, so the main data skill keeps a few "freshness check" queries with their last results, and once a month I run a small /skill-audit command that reruns them. The very first run already caught two things worth adding.
The habit part (the hard part)
This is really about breaking a habit. The rule that replaced "never close the chat":
Chats are scratch paper. Files are memory.
Starting work? → New chat. One sentence: what this item is. Same item tomorrow? → Resume that chat. Different item? → New chat. Stopping or switching → /wrap-up Revisiting old work? → New chat: "Reopen X: <what changed>" /wrap-up is a small custom command. It rewrites STATUS.md, logs any decisions as one-liners, proposes (doesn't apply) anything worth adding to a skill, and suggests a commit. Once it's done, I could lose the chat and not lose anything that matters. That's the whole trick. The urge to keep the chat alive "just in case" is still there... but it's fading.
Folders follow the same idea. There's one root workspace under git, and a folder for each project I know I'll come back to, opened in its own VS Code window. Quick one-off questions stay in the root. My six-document scaffold turned into a /kickoff command that sizes the request first, so a quick question gets zero paperwork.
I also retired a multi-agent toolkit I'd installed and used exactly twice in five months. Fancier wasn't better.
Guardrails, because I like going hands-off
Once a plan is approved, I like letting Claude run while I do something else. Two things make me comfortable doing that against real data:
- A pre-tool hook that blocks any non-read SQL against production. Funny story: my old guard had a pattern typo and had never fired. Not once. So... test your guardrails, don't just write them.
- "Ask" permission rules for anything that leaves my machine or can't be undone: pushing code, writing to the ticket tracker, recursive deletes. So hands-off is hands-off for the safe stuff only.
Where I'm at now
It's early, the new setup is days old. Having said that:
- A new session starts with about 2K tokens of my instructions instead of a 70K-token log.
- The first project migration took an afternoon, and it had to earn the switch. I reran the last delivery from the new folder: static outputs matched exactly, and the differences in the live-data tabs were explained before I cut over.
- My permission rules went from 239 accumulated one-offs to under 60 general ones.
Quota got boosted, by the way. The plan is to never need that ticket again.
If you're where I was
- Don't wait for quota jail. Measure your own usage. Your session logs are already sitting on your disk. A few lines of Python told me more than any best-practices video did.
- Don't keep chats alive to keep context. Put the context in files, and give "where things stand", "what happened", and "what I know" their own homes.
- If something must never happen, make it a hook... and then test the hook.
Full disclosure: Claude helped me crunch the logs and draft this post. Seemed fitting.
How I measured. Claude Code writes each session to ~/.claude/projects/<project>/<session-id>.jsonl. This counts compactions and shows which files get re-read the most:
import json, os, collections, sys reads, compactions = collections.Counter(), 0 for line in open(sys.argv[1], encoding="utf-8"): if "compact_boundary" in line: compactions += 1 e = json.loads(line) if e.get("type") != "assistant": continue for block in e.get("message", {}).get("content") or []: if isinstance(block, dict) and block.get("name") == "Read": reads[os.path.basename(block["input"].get("file_path", ""))] += 1 print("compactions:", compactions) print("most re-read:", reads.most_common(3)) Run it as python measure.py <session-file>.jsonl.
Further reading: Claude Code best practices · How Claude remembers your project · Skills · Effective context engineering for AI agents



