Trim the context

Day 1: From chat to spec · Exercise 4 of 4 · 18 min

Cut your lane's roughly 180-line CLAUDE.md to under 40 lines, with the whole /context breakdown saved before and after, then re-run Exercise 1's assignment against the smaller file and check it still stays green and in bounds. You are not tidying: you are deciding, line by line, which instructions are worth paying for on every turn of every session your team runs.

Setup

You continue in day1-work, the copy of your lane that Exercise 1 made at the course root. Every command and every path on this page is written from inside it, and the shipped sandbox/ is never edited. Exercise 3 left the copy matching the lane apart from your spec, which is all step 7 needs.

Paste one block. It stages step 4's example rules file in ../work/, where your answers go and no reset reaches, and records the starting line count.

Swift lane
mkdir -p ../work && cp ../day-1-from-chat-to-spec/starter/rules/EXAMPLE-formatting.md ../work/ && wc -l CLAUDE.md > ../work/context-before.txt && cat ../work/context-before.txt
     180 CLAUDE.md
Kotlin lane
mkdir -p ../work && cp ../day-1-from-chat-to-spec/starter/rules/EXAMPLE-formatting.md ../work/ && wc -l CLAUDE.md > ../work/context-before.txt && cat ../work/context-before.txt
     177 CLAUDE.md
Starting from a clean copy

If day1-work carries anything besides your spec, make it again from the lane and paste the setup block above. The remake throws the copy away, your Exercise 3 spec included. Save the spec first if you want your own on Day 2, which runs a sharpened reference spec either way; the reset at the foot of this page saves this exercise's files as well, because by then there are some:

mkdir -p ../work && cp specs/daily-summary.md ../work/

Then, from inside day1-work:

Swift lane
cd .. && rm -rf day1-work && cp -R sandbox/swift day1-work && cd day1-work
Kotlin lane
cd .. && rm -rf day1-work && cp -R sandbox/kotlin day1-work && cd day1-work

Answers already written down in ../work outlive the remake, and a spec you saved goes back with cp ../work/daily-summary.md specs/.

Now start Claude Code here with claude. Project instructions live at ./CLAUDE.md or ./.claude/CLAUDE.md, loaded from the directory you started in and every directory above it, so today's project file is day1-work's. The lane's .claude/settings.json skips any above day1-work and turns auto memory off, so the memory files in step 1's breakdown are this project file and your personal ones. A block you do not recognise is one of the others Lesson 6 lists.

Steps

  1. Record the before state. Run /context. In fullscreen mode it collapses the per-item breakdown to keep the grid visible; pass it all to expand the breakdown again. Copy the whole output, exactly as your version prints it, into ../work/context-before.txt under the line count. You compare it with your own after copy, not with a figure on this page.

  2. Apply the line test. Most of the file is real content, and almost none of it is a lie; that is what makes the cut hard. Paste this prompt. It reports, and the cutting stays yours.

    Read CLAUDE.md in this project. For each line or section, tell me the specific
    mistake you would make if it were not there. Where you cannot name a specific
    mistake, say "no mistake". Do not edit the file and do not rewrite it for me -
    I am doing the cutting.

    It also ships as ../day-1-from-chat-to-spec/starter/prompts/e4-line-test.txt. While it answers, read CLAUDE.md in your editor and start ../work/cut-lines.md: one line per section you will cut or shorten, and why. Sections that would fit any codebase need no report. Then work down its answers; where no specific mistake can be named, the line goes.

  3. Get the file under 40 lines. Set aside what is worth keeping but not worth paying for on every turn; step 4 gives it a home. Then record the new count:

    wc -l CLAUDE.md > ../work/context-after.txt && cat ../work/context-after.txt
  4. Move the lines that are useful but rarely needed. A line that survives the line test has two places to live, and only one of them costs you every turn.

    Two destinations for a line that passes the line test One line of CLAUDE.md on the left, two destinations on the right, each labelled with when it loads. Keep it: the line stays in CLAUDE.md and loads on every turn. Scope it: the line moves to a rule file at .claude/rules/name.md whose frontmatter lists a paths glob, and loads when the agent reads a file matching that glob. The line survives either way; only what you pay for it changes. where the line lives, and when it loads one line that passes the line test keep it: CLAUDE.md loaded on every turn scope it: a rule file .claude/rules/<name>.md loaded when a matching file is read
    What each destination costs you: every turn, or only the turns that read a matching file.

    A file at .claude/rules/<name>.md with a paths: list of globs in its YAML frontmatter enters the window only when the agent reads a file matching one of those globs, and stays out otherwise. Put the file-specific lines you cut there. The example file already holds one of them, the ban on locale APIs that CLAUDE.md also carries. That ban must survive the trim: keep it in CLAUDE.md, in the rules file, or both. The reference answer keeps it only in the rules file, and the reveal argues both sides:

    mkdir -p .claude/rules && cp ../work/EXAMPLE-formatting.md .claude/rules/formatting.md && ls .claude/rules

    Confirm its frontmatter is these three lines, then paste your file-specific lines underneath:

    Swift lane
    ---
    paths: ["Sources/LedgerKit/*.swift"]
    ---
    Kotlin lane

    The file ships with the Swift lane's glob. Change it to your own, and change the API names in the body to java.text.NumberFormat, DateTimeFormatter, Locale and ICU:

    ---
    paths: ["src/main/kotlin/ledgerkit/*.kt"]
    ---
  5. Measure it. Run /clear, which starts a new conversation with an empty window, then /context, and paste the whole output into ../work/context-after.txt under the new line count. Without /clear you measure this afternoon's conversation, not your file.

  6. Prove nothing broke. Paste Prompt B from Exercise 1 verbatim and let it run against the trimmed file. Approve edits as they come, and do not correct it: a correction from you is the information you are testing whether the file still carries. Between approvals, start step 8's comparison.

    Swift lane

    Prompt B ships as ../day-1-from-chat-to-spec/starter/prompts/e1-prompt-b.txt, the text Exercise 1 gives in full.

    Kotlin lane

    Prompt B ships as ../day-1-from-chat-to-spec/starter/prompts/e1-prompt-b-kotlin.txt, the text Exercise 1 gives in full.

  7. Check the same two things Exercise 1 checked: green, and no source file beyond LedgerFilter and its test touched.

    Swift lane
    swift test

    The verdict line names All tests, and the number that decides this step is the zero. The skipped test is the gated LEDGER-7 case, skipped here as it was in Exercise 1:

    Test Suite 'All tests' passed at …
    	 Executed … tests, with 1 test skipped and 0 failures (0 unexpected) in … seconds
    Kotlin lane
    ./gradlew test
    BUILD SUCCESSFUL in …

    BUILD SUCCESSFUL is the line that decides this step. Gradle prints no skip count; the gated LEDGER-7 case is skipped here as it was in Exercise 1, and only the test report records it.

    Then the file list. A recursive diff against the shipped lane one level up is the record of the run, and each file that differs opens its section with a line starting diff. Your CLAUDE.md and the .claude/rules/formatting.md step 4 added are certain, and so is specs/daily-summary.md if your spec is still in the copy.

    Swift lane
    diff -ruN -x .build ../sandbox/swift . | grep '^diff '

    If the agent changed code, Sources/LedgerKit/LedgerFilter.swift and Tests/LedgerKitTests/LedgerFilterTests.swift are the only two more that may appear.

    Kotlin lane
    diff -ruN -x build -x .gradle -x .kotlin ../sandbox/kotlin . | grep '^diff '

    If the agent changed code, src/main/kotlin/ledgerkit/LedgerFilter.kt and src/test/kotlin/ledgerkit/LedgerFilterTest.kt are the only two more that may appear.

    An agent that reads the code, finds the merchant match already ignores case and diacritics, and changes no source file and adds no duplicate tests is behaving well, as Exercise 1's key says: the case and diacritic tests Prompt B asks for are already in the lane's LedgerFilter test file. Saying so and adding none satisfies Prompt B's Add at least one test line; the tests that line asks for already exist. What this step checks is that no third source file appears. -N makes a file the agent created show up, and created files are the ones people forget to count.

  8. Compare. Open the reveals below: your keep list against the six lines that must survive and the four that people keep and should not.

If you see something else instead

Self-check

Run this, from inside day1-work

test "$(wc -l < CLAUDE.md)" -lt 40 && echo TRIM-OK || echo TRIM-TOO-LONG

On an untrimmed file it prints TRIM-TOO-LONG, which is how you know it reads the file and not your opinion of it.

Then put the two context files side by side: about 180 lines before, under 40 after, and two outputs of the same command from your own version, so what differs is what the cut bought.

Last, the re-run: the test command ended green, and step 7's diff names your CLAUDE.md and the rules file, and no source file beyond LedgerFilter and its test.

Done when: the check prints TRIM-OK, both context files exist and differ, the Exercise 1 re-run is green and in bounds, and ../work/cut-lines.md gives a reason for every section you cut or shortened.

Reference answers

The six lines that must survive, and the four that feel load-bearing and are not

Quoted from the Swift lane. The Kotlin lane carries the same six facts in different sentences and sections, so check against your own file rather than against these strings.

  1. Run the tests with swift test from the package root. (§ Testing) — without it the agent guesses a test command, and a guessed command that happens to pass is worse than one that fails, because you believe it.
  2. The fixture at Tests/LedgerKitTests/Fixtures/transactions.tsv is the source of truth for expected strings. (§ Fixtures) — where the expected answers live. Fold in Never edit it as you keep it (the Kotlin lane's version of this line already says so), because that stops a failing test being made to pass.
  3. amountMinor is an integer in the currency's minor units; a decimal point never appears in stored data. (§ Money) — the fact that cannot be recovered by reading one file. Fold in Amounts are never floating point from the ISO 4217 paragraph below it, because as written this line does not forbid a floating-point value mid-calculation.
  4. Negative amountMinor is a debit and prints with -; zero or positive is a credit and prints with +. (§ Money) — sign, then symbol, then digits is arbitrary, which is exactly what makes it worth writing down.
  5. Never use NumberFormatter, Locale, ICU or any locale API: all formatting is hand-rolled. (§ Money) — it names the wrong move before the agent makes it. This is the line step 4's rules file already carries.
  6. Run the tests before you say you are done, and quote the final summary line. (§ Testing) — quote rather than summarise is what makes the line checkable. A summary of a test run is an opinion.

The four sections people keep on the first pass, each of which feels responsible and changes nothing the agent does:

Two more go for the same reason in whichever lane carries them: the glossary belongs in README.md, and the Kotlin lane's § Git and review workflow belongs in CONTRIBUTING.md. In the Kotlin lane, § Domain rules holds three of your six: keep those and cut the rest, which the types and README.md already carry. The Swift lane's § Code review, § Performance notes, § Accessibility and § Security give good advice about concerns this module does not have. Full key: answers/e4-claude-md-keepers.md.

The reference trimmed file and the reference rules file

Fifteen lines, Swift lane, and the slack under forty is deliberate: Day 2's self-critique needs somewhere to route a finding without the file starting to grow again. The Kotlin lane's copy is the same shape built from its own six lines, with ./gradlew test, Fixtures/transactions.tsv and the glob from step 4.

# CLAUDE.md — LedgerKit

## Testing

- Run the tests with `swift test` from the package root. That is the only test command.
- The fixture at `Tests/LedgerKitTests/Fixtures/transactions.tsv` is the source of truth for expected strings. Never edit it: it is shared byte-for-byte with the other lane.

## Money

- `amountMinor` is an integer in the currency's minor units. It never holds a decimal point, and no code turns it into a floating-point number.
- Negative `amountMinor` is a debit and prints with `-`; zero or positive is a credit and prints with `+`. The sign comes before the currency symbol.

## Before you say you are done

- Run the tests before you say you are done, and quote the final summary line.

The four headings cost four lines and make the file skimmable by whoever has to defend it in six weeks; the mistake they prevent is yours. Nothing in it points at a second file: every long section is a false keep named above, so it was cut rather than filed away. And the highest-value line in the original is not in it, because this reference leaves it to the rules file:

---
paths: ["Sources/LedgerKit/*.swift"]
---

Formatting in this module is hand-rolled: never introduce a locale-aware API (`NumberFormatter`, `DateFormatter`, `Locale`, ICU) into these files.

That move is worth a minute's discomfort. For keeping the rule in CLAUDE.md: an agent can reach for a locale API while editing a test, or a new file the glob does not cover. Against: a rule paid for on every turn and needed on a tenth of them is the tax this exercise teaches you to see. Either answer passes if the file is under forty lines; write down which argument you were making. Longer version, both lanes: answers/claude-md-trim.md.

Reset

One line, the sandbox reset every Day 1 exercise means: delete the copy, make it again from the lane, step back in. The whole copy goes, .claude/rules/ and your Exercise 3 spec included. Save what you want to keep first — your trimmed CLAUDE.md and your rules files are this exercise's deliverables, and the Day 1 wrap-up asks you to keep them:

mkdir -p ../work/rules && cp CLAUDE.md ../work/ && cp .claude/rules/*.md ../work/rules/ && cp specs/daily-summary.md ../work/ 2>/dev/null; ls ../work ../work/rules

Then, from inside day1-work:

Swift lane
cd .. && rm -rf day1-work && cp -R sandbox/swift day1-work && cd day1-work
Kotlin lane
cd .. && rm -rf day1-work && cp -R sandbox/kotlin day1-work && cd day1-work

Everything in ../work outlives the reset, your context files and cut list included. A spec you saved goes back with cp ../work/daily-summary.md specs/, and your rules files with mkdir -p .claude/rules && cp ../work/rules/*.md .claude/rules/.

The end-of-day cleanup is the other line, run from inside day1-work once Day 1 is finished: cd .. && rm -rf day1-work. This removes day1-work and keeps ../work; Day 2 and Day 3 write there too, so remove work only when you no longer want the day's answers.

Sources

Claude Code documentation, accessed 2026-09-22 against version 2.1.280, and 2026-09-23 for cc-core-40 to cc-core-44.