Skip to content

Add burn claude-md: CLAUDE.md hot-path cost attribution#51

Open
willwashburn wants to merge 1 commit intomainfrom
feat/burn-claude-md
Open

Add burn claude-md: CLAUDE.md hot-path cost attribution#51
willwashburn wants to merge 1 commit intomainfrom
feat/burn-claude-md

Conversation

@willwashburn
Copy link
Copy Markdown
Member

@willwashburn willwashburn commented Apr 23, 2026

Summary

  • New burn claude-md command: per-section cost attribution for the project's CLAUDE.md, with cross-session aggregation and a per-session cost breakdown
  • New burn claude-md advise subcommand: emits unified-diff TRIM hunks for the most expensive sections (read-only — burn never mutates CLAUDE.md)
  • Detects CLAUDE.md at the project root and at `.claude/CLAUDE.md`

Closes #10.

Output shape

```
CLAUDE.md at /Users/will/Projects/foo/CLAUDE.md (412 lines, ~3.1k tokens)

Cost per session: avg $0.18, p95 $0.34
Cost over 7d: $4.12 across 23 sessions

Sections ranked by cost:
lines heading tokens cost/session %file
12- 47 ## Architecture 1.8k $0.11 59.0%
48- 89 ## Testing conventions 890 $0.05 28.7%
90- 148 ## Tone and personality 410 $0.02 13.2%
```

Attribution math

Each turn's CLAUDE.md cost is computed directly: `claude_md_tokens × cacheReadPrice / 1M` per turn where `cacheRead >= claude_md_tokens`. This simplification of the issue's share-based formula falls out cleanly because CLAUDE.md size is constant across the session — the share-vs-direct difference vanishes. The `cacheRead < claude_md_tokens` check skips:

  • The first turn (CLAUDE.md is in `cacheCreate`, not `cacheRead`)
  • Turns where the file has been compacted out of cache

Section attribution is proportional: `section_cost = total_cost × (section_tokens / total_tokens)`.

Section parsing

  • Groups by H2 if any exist (matches the standard CLAUDE.md shape: H1 doc title + H2 sections)
  • Falls back to H1 if no H2s
  • Treats a heading-less file as a single "preamble" section
  • Skips fenced code blocks so example markdown inside them isn't parsed as headings
  • "Preamble" content (above the first grouping-level heading) is its own section

Test plan

  • `pnpm run build` — clean
  • `pnpm run test` — 193/193 passing (11 new tests)
  • Synthetic-attribution test (5 turns × known CLAUDE.md size) within ±10% of hand-computed truth
  • Section ratio test: section costs are proportional to token shares
  • "Skip when cache too small" test
  • Empty CLAUDE.md returns zero cost
  • Section parser tests: preamble, H2 grouping, H1 fallback, code-fence skip
  • `findClaudeMdFiles` finds root + nested
  • Advise diff test: hunk header line range matches the actual section

Notes / follow-ups

  • Per-session-time-travel (CLAUDE.md as it existed at session time, via git log) is out of scope for MVP — current implementation always uses HEAD's CLAUDE.md. Documented in the README via the `since` flag note.
  • Nested `src/**/CLAUDE.md` discovery is not yet implemented; only root and `.claude/CLAUDE.md` are scanned. Easy to add when sessions in the wild start using deep CLAUDE.md trees.
  • Rollup across worktrees via `projectKey` is already wired (`Query.project` matches both `project` and `projectKey`)

🤖 Generated with Claude Code


Open in Devin Review

CLAUDE.md rides in every turn's cached context, so a bloated CLAUDE.md
silently taxes every session in the repo. Burn already carries real
per-turn cacheRead values, so we can attribute its cost honestly:

  per-turn cost = claude_md_tokens × cacheRead_price (when cacheRead
                  is large enough to actually contain it)

The "in cache" check (turn.cacheRead >= claude_md_tokens) is a
conservative eviction signal: it skips the first turn (where CLAUDE.md
lives in cacheCreate, not cacheRead) and any turn where the file has
been compacted away. Cost-per-section is proportional to that section's
token share.

Section parsing groups by H2 if any exist (matching the standard
CLAUDE.md shape where the H1 is a doc title and H2s are the meaningful
sections), falls back to H1, and treats a heading-less file as a single
preamble. Code fences are skipped so example markdown inside them
isn't parsed as a real heading.

`burn claude-md advise` emits unified-diff TRIM hunks for the most
expensive sections — line ranges match the user's current file so the
diffs hand-apply cleanly. There is no --apply flag: burn never mutates
CLAUDE.md.

Closes #10

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 5 additional findings in Devin Review.

Open in Devin Review

if (!inFence) {
inFence = true;
fenceMarker = marker;
} else if (trimmed.startsWith(fenceMarker.charAt(0).repeat(fenceMarker.length))) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Fence closing check matches lines with trailing content, incorrectly exiting code blocks

The closing fence check at line 176 uses trimmed.startsWith(fenceMarker.charAt(0).repeat(fenceMarker.length)), which matches any line that begins with enough backticks/tildes — even if the line has trailing content (e.g. ````python). Per the CommonMark spec, a closing fence must contain only fence characters and optional whitespace. When a line like ````python appears inside a 3-backtick code block, this code incorrectly closes the fence, causing subsequent headings that are still inside the code block to be detected as real sections. Meanwhile, the actual closing ``` line is misinterpreted as opening a new fence, swallowing headings after the real code block.

Reproduction

Input:

inside block

## should-be-inside
```
## should-be-outside
```

Expected headings: only `## should-be-outside`.
Actual headings: only `## should-be-inside` (wrong one).

</details>

```suggestion
      } else if (new RegExp(`^${fenceMarker.charAt(0) === '`' ? '`' : '~'}{${fenceMarker.length},}\\s*$`).test(trimmed)) {
```

<!-- devin-review-badge-begin -->
<a href="https://app.devin.ai/review/agentworkforce/burn/pull/51" target="_blank">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
    <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open in Devin Review">
  </picture>
</a>
<!-- devin-review-badge-end -->

---
*Was this helpful? React with 👍 or 👎 to provide feedback.*

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLAUDE.md hot-path cost attribution with section-level recommendations

1 participant