House Notes

Give Claude Code project instructions you don't have to repeat

Our CLAUDE.md said money is integer cents. The first two fresh sessions returned dollars. Only the two runs where we confirmed the file loaded passed all four checks.

Verified on Claude Code 2.1.288, re-run 2026-10-03

Result: 8 of 8 checks
Touches: one CLAUDE.md and a disposable example, not global settings or saved memories.
Undo: remove the example folder; in a real project, revert only your approved additions.
Say to the agent: Write a short project contract, show the diff, then verify it in a fresh session without repeating its rules.
Owner: Will Mitchell

Run details

Status: published
Tool: Claude Code
Tool version: 2.1.288
Model: Sonnet 5.5
Runtime: Node.js 24.12.0
OS: Linux
Re-run: 2026-10-03

The rule was in the file. Was the file in the session?

We wanted Claude Code to stop asking, session after session, how one small project handles money. So we wrote the answer down: a six-rule CLAUDE.md for a quote calculator, starting with “Money is integer cents.”

Then we sent six fresh automated sessions the same request for that function. The first two wrote a plausible quote calculator, just not ours. They expected unitPrice or price instead of unitPriceCents, and they returned dollars. Four of the six did not pass all four checks. The two that did were the only runs where we checked that the file had loaded.

We can’t say the failed runs ignored the file, because we never checked whether it loaded for them. That is the lesson. Don’t declare success because a file exists, or because the model says it read it. Confirm the file loaded, then test what comes out.

This is about project instructions, not auto memory. You write project instructions; Claude maintains auto memory as it learns from work. A CLAUDE.md is not a transcript of your last session, a guarantee of compliance, or a replacement for tests. It is where the decisions you keep re-explaining belong, like a price format, a test command or a file boundary, in a project-root file your team reviews alongside the code.

The official memory documentation describes how Claude Code loads instructions from the working directory and its ancestors. A file deeper in the tree can load when Claude works there. Start in the project you intend to change, and check which files actually loaded rather than trusting the filename.

Try it without touching your real project

Use a new empty folder, Claude Code, and Node.js 24. Do not run the example inside a customer repository. Read downloaded files before running them.

  1. Write a small, reviewable project contract. This adds CLAUDE.md in the example folder. It does not change your global Claude configuration. If the folder already contains that file, stop and choose a fresh folder rather than overwriting it.

    +# Quote calculator conventions
    +
    +- Money is integer cents. Do not convert to decimal dollars.
    +- Export `quoteTotal(items)` from an ES module.
    +- Each item has `unitPriceCents` and `quantity`, both nonnegative safe integers.
    +- Return `{ totalCents, currency: "USD" }`, including for an empty list.
    +- Reject invalid inputs or an unsafe total with `RangeError`.
    +- Do not add dependencies.

    The plus signs mark additions in this diff; do not put them in the file. You can instead save the example instruction file as CLAUDE.md.

    Check: open your saved file. It names the input fields, output shape and failure behavior without describing unrelated projects. Never put passwords or customer data in instructions.

  2. Start a fresh Claude Code session in that folder. This starts a new conversation; it does not resume an old one. Run /context and look in the Memory files section for this project’s CLAUDE.md. /memory helps you find and edit memory locations, but a model saying “I read it” is not the same as seeing the loaded-file entry.

    Check: the actual example file appears in the loaded context. If it does not, stop here. Do not paste the missing rules into the prompt and call that automatic loading. Bare mode and safe mode intentionally skip instruction discovery.

  3. Ask for one small implementation. This adds quote.mjs in the example folder. Ask:

    Write the quoteTotal function for this project. Save it as quote.mjs. Do not add dependencies or change CLAUDE.md. Show me the diff.

    Check: review the diff. It exports quoteTotal, accepts unitPriceCents, returns totalCents with currency: 'USD', and adds no taxes, discounts or other features the contract never asked for. Treat generated code as code to review, not a command to run blindly.

  4. Test the behavior separately. Save the four-check test file beside quote.mjs, read it, then run:

    node --test quote.test.mjs

    Check: all four tests pass: an ordinary two-line quote, an empty quote, malformed inputs or invalid item values, and arithmetic that exceeds JavaScript’s safe integer range. The ordinary quote must total 4,498 cents, not 44.98 dollars. Do not edit the expected results to fit the implementation.

  5. Repeat with another new session. Keep CLAUDE.md, move the first implementation somewhere else inside your disposable folder, and ask a fresh session to write a new quote.mjs. Review that diff and rerun the same test file.

    Check: you did not repeat the pricing rules in the prompt or resume the earlier conversation. This checks an ordinary project workflow, not an isolated instruction experiment: a new session can still read earlier code and tests. A passing run is evidence for that run, not a promise about the next one.

PASS: the project’s instruction file is listed as loaded, and the implementation from each fresh session passes the unchanged four-check file. If either condition fails, the card has not passed.

What we measured, including the misses

On 2026-10-03 we made six fresh automated Sonnet 5.5 requests on one Linux workstation. Each used the same six-rule file and the same prompt: “Write the quoteTotal function for this project. Return only the complete JavaScript ES module source, without Markdown fences or explanation. Do not use tools.” We wrote the four test cases before the requests and kept them outside the model’s project folder.

8 of 8 checks
LauncherChecks passed across the two sessionsProject file confirmed loaded?
Restricted, tool-free0 of 8Not recorded (sessions 0/4, 0/4)
Tool-free, empty settings-source selection1 of 8Not recorded (sessions 0/4, 1/4)
Tool-free, project settings-source selected8 of 8Yes, both startup receipts (sessions 4/4, 4/4)

We also tested our own checks. Deliberate mutations exposed two missing assertions: a fractional quantity whose product is an integer, and malformed input containers or items returning the wrong error class. We strengthened the input case and rechecked all six saved outputs with the final downloadable checks; the table reports those results. We did not repair any generated implementation or relax an expected result.

For the final pair, we used --setting-sources project and a temporary InstructionsLoaded observer passed through --settings. It recorded only this example’s instruction-file path, memory_type: Project, and load_reason: session_start, not file contents, and it supplied no instructions to the model. Built-in tools and MCP servers were unavailable; each -p invocation used a distinct session, without continue, resume or session persistence. No saved user settings were changed.

Both final outputs matched the reference implementation, apart from quote formatting in the published copy, and they were identical to each other.

What you will see

  • Expected values to be strictly deep-equal: appeared when the empty quote returned a number or a different object instead of { totalCents: 0, currency: 'USD' }. Inspect the actual and expected values. This can be an output-contract mismatch, not bad addition.
  • unitPrice must be a finite number appeared when generated code expected unitPrice, but the project and test used unitPriceCents. Check the field names before changing the inputs. It can look like missing data when the implementation is using the wrong contract.

Neither message alone proves an instruction-loading defect. A loaded-file check and the generated diff tell missing context apart from implementation mistakes.

What this doesn’t show

  • A small example. This is one quote calculator on one Linux workstation, not a checkout, tax engine, memory benchmark, or productivity study. The test checks four behaviors; it is not a security audit and cannot establish that every instruction was followed.
  • Two passing runs, not a success rate. They are not independent evidence of a general success rate. The launcher and observation setup changed during diagnosis, so this is not a controlled benchmark isolating one flag’s causal effect.
  • The failures prove nothing about loading. We did not capture load receipts for the failed runs. They therefore do not prove that Claude ignored instructions it had received.
  • What we didn’t test. The initial failed trials used Claude Code 2.1.288, the reported model claude-sonnet-5-5, and Node.js 24.12.0. We did not test macOS, Windows, competing instructions, large repositories, or auto-memory recall. A new session may produce different code.
  • Context, not enforcement. Claude Code’s instruction guidance says these files provide context, not enforced configuration. Use tests for output contracts and permissions for actions that must be blocked. Keep your real project’s existing instructions, and add the smallest concrete rules that prevent work you actually repeat.

Reader poll

Self-selected, unverified responses. These counts describe this poll's responses.

JavaScript is needed to send an answer and load this poll's counts.

Enter the House