mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-08-21 22:44:31 +00:00
optimize: keep sidechain tool calls in the junk-read and read:edit signals
Only duplicate-reads has a structural reason to skip them: a subagent starts on a fresh context, so re-reading what its parent read is a necessary read, not a repeat. Reading node_modules or editing without reading is the same waste whoever does it, and the CLAUDE.md rule both findings suggest binds subagents too - filtering them there discarded most of the evidence on a subagent-heavy corpus.
This commit is contained in:
parent
8ecd14ccdf
commit
29b531fced
4 changed files with 23 additions and 16 deletions
|
|
@ -28,7 +28,7 @@
|
|||
- **The resident `codeburn serve` child.** The first real panel request is also the cache warm-up, so startup never runs an artificial warm-up query beside a duplicate one-shot child; each served command carries its own read-only option allowlist, and anything outside it falls back to a normal spawn; the child exits when its stdin closes, so it can never outlive the app. Requests whose response exceeds the 16 MiB frame limit still replace the child, but that deliberate kill no longer spends the resident's unexpected-death budget. (#972)
|
||||
|
||||
### Fixed
|
||||
- **`optimize` no longer treats subagent transcripts as your sessions.** Claude Code writes each subagent's transcript to its own `subagents/agent-*.jsonl` file with `isSidechain: true` on every entry, and optimize counted each one as a user-started session. That inflated the session count in the header and fed the session-level detectors a population that fails their tests by construction: a sidechain is handed a large context and returns a short answer (context-heavy), and it never commits or opens a PR because its parent does (low-worth). The session count, the low-worth / context-heavy / cost-outlier / capability-reliability detectors, the coaching notes, the file-churn table, and the model-default recommendation now all run on user-started sessions only, and the raw read/edit, junk-read and duplicate-read detectors skip calls made inside a sidechain transcript. Classification is sticky across the whole file, so calls that appear before the first marked entry are reclassified too, and `isSidechain` now survives the compact parser's 32 KB large-line path and warm-cache range rebuilds. Nothing is deleted from spend: sidechain tokens, calls and cost stay in every total, in `status`, and in the configuration-overhead findings, and the optimize result cache keys on sidechain identity so a run cannot be served a pre-fix result. Absent markers still read as user-started, so no cache re-parse is needed. (#974)
|
||||
- **`optimize` no longer treats subagent transcripts as your sessions.** Claude Code writes each subagent's transcript to its own `subagents/agent-*.jsonl` file with `isSidechain: true` on every entry, and optimize counted each one as a user-started session. That inflated the session count in the header and fed the session-level detectors a population that fails their tests by construction: a sidechain is handed a large context and returns a short answer (context-heavy), and it never commits or opens a PR because its parent does (low-worth). Excluded from sidechains now: the header session count, the `low-worth-sessions`, `context-bloat`, `cost-outliers` and `capability-reliability` detectors, the coaching notes, the file-churn table, the median time-to-first-edit, the worst one-shot category, and the model-default recommendation - plus `duplicate-reads`, because a subagent starts on a fresh context and re-reading what its parent read is a necessary read, not a repeat. Everything else keeps the full population: `build-folder-reads` and `read-edit-ratio` still count calls made inside a sidechain, since reading `node_modules` or editing without reading is the same waste whoever does it and the `CLAUDE.md` rule they suggest binds subagents too, and so do the MCP, cache-bloat, ghost-command and configuration-overhead findings. Classification is sticky across the whole file, so calls that appear before the first marked entry are reclassified too, and `isSidechain` now survives the compact parser's 32 KB large-line path and warm-cache range rebuilds. Nothing is deleted from spend: sidechain tokens, calls and cost stay in every total and in `status`, and the optimize result cache keys on sidechain identity so a run cannot be served a pre-fix result. Absent markers still read as user-started, so no cache re-parse is needed. (#974)
|
||||
- **`optimize` no longer offers `claude mcp remove` for claude.ai connectors, and its MCP schema-cost estimate is per session.** A `claude_ai_*` namespace that no readable local MCP config claims is a claude.ai connector, managed through `/mcp` or claude.ai Settings rather than as a local MCP server (a local server that carries the prefix keeps its removal command and gains a same-name connector note); low-coverage findings now render them as a manual follow-up and build `--apply` plans only for exact local server names found in readable MCP config, so mixed findings remove only the local subset and the "apply-able" subtotal counts only that subset. The same change replaces the old global schema-cost cap with per-session, per-server proportional attribution — a more accurate model that lowers `mcp-low-coverage` estimates for everyone, connectors or not (on a large corpus roughly by half). (#975, #991)
|
||||
- **Bash command splitting was quadratic on long whitespace-heavy commands.** The separator regex retried its leading `\s*` from every offset; matching the separator alone and widening over whitespace by hand makes cold parse ~24% and warm ~40% faster on large corpora, output unchanged.
|
||||
- **Cold parse no longer retains full message bodies through cached previews.** `flatSlice` skipped its Buffer round-trip for strings already within the bound, but provider adapters pre-truncate user-message previews with `.slice(0, 500)` before the cache-site call — those pre-sliced views are still V8 SlicedStrings pinning their large parent, so the retention that OOM'd cold parses of large histories survived. The round-trip now always runs.
|
||||
|
|
|
|||
10
README.md
10
README.md
|
|
@ -157,11 +157,13 @@ codeburn optimize --format json # setup health + findings as JSON
|
|||
|
||||
`codeburn optimize` scans your sessions and your `~/.claude/` setup for waste patterns:
|
||||
|
||||
For Claude Code, the optimize session count, behavioral findings, coaching, and
|
||||
model-default recommendations use user-started (main) sessions. Subagent
|
||||
For Claude Code, the optimize session count, the per-session findings, coaching,
|
||||
and model-default recommendations use user-started (main) sessions. Subagent
|
||||
sidechain transcripts are excluded from that population because their delegated
|
||||
context and delivery behavior are structurally different; their tokens, calls,
|
||||
and cost still count in all spend totals and configuration-overhead findings.
|
||||
context and delivery behavior are structurally different, and so is the re-read
|
||||
finding, since a subagent starts on a fresh context. Findings about how Claude
|
||||
uses tools (junk reads, read:edit ratio) and every spend, MCP, and
|
||||
configuration-overhead finding keep counting them.
|
||||
|
||||
- Files Claude re-reads across sessions (same content, same context, over and over)
|
||||
- Low Read:Edit ratio (editing without reading leads to retries and wasted tokens)
|
||||
|
|
|
|||
|
|
@ -958,7 +958,6 @@ export function localMcpServerNames(projectCwds: Iterable<string>, homeDir = hom
|
|||
// ============================================================================
|
||||
|
||||
export function detectJunkReads(calls: ToolCall[], dateRange?: DateRange): WasteFinding | null {
|
||||
calls = calls.filter(call => call.isSidechain !== true)
|
||||
const dirCounts = new Map<string, number>()
|
||||
let totalJunkReads = 0
|
||||
let recentJunkReads = 0
|
||||
|
|
@ -1009,6 +1008,10 @@ export function detectJunkReads(calls: ToolCall[], dateRange?: DateRange): Waste
|
|||
}
|
||||
|
||||
export function detectDuplicateReads(calls: ToolCall[], dateRange?: DateRange): WasteFinding | null {
|
||||
// A sidechain re-reading what its parent read is not a repeat: a subagent
|
||||
// starts on a fresh context and has to read it. Junk reads and the
|
||||
// read:edit ratio keep the full call population - that waste is waste
|
||||
// whoever does it, and the CLAUDE.md rule they suggest binds subagents too.
|
||||
calls = calls.filter(call => call.isSidechain !== true)
|
||||
const sessionFiles = new Map<string, Map<string, { count: number; recent: number }>>()
|
||||
|
||||
|
|
@ -2618,7 +2621,6 @@ export const EDIT_TOOL_NAMES = new Set(['Edit', 'Write', 'FileEditTool', 'FileWr
|
|||
export const BASH_TOOL_NAMES = new Set(['Bash', 'BashTool', 'PowerShellTool'])
|
||||
|
||||
export function detectLowReadEditRatio(calls: ToolCall[]): WasteFinding | null {
|
||||
calls = calls.filter(call => call.isSidechain !== true)
|
||||
let reads = 0
|
||||
let edits = 0
|
||||
let recentEdits = 0
|
||||
|
|
|
|||
|
|
@ -380,22 +380,25 @@ describe('scanJsonlFile', () => {
|
|||
expect(result.userMessages).toEqual(['delegate this'])
|
||||
})
|
||||
|
||||
it('excludes marked sidechain calls from raw human-behavior detectors', () => {
|
||||
it('keeps sidechain calls out of duplicate reads but in junk reads and the read:edit ratio', () => {
|
||||
const sidechain = { sessionId: 'agent-reviewer', project: 'p1', isSidechain: true }
|
||||
const editCalls = Array.from({ length: 10 }, (_, index) => ({
|
||||
name: 'Edit', input: { file_path: `/src/${index}.ts` },
|
||||
sessionId: 'agent-reviewer', project: 'p1', isSidechain: true,
|
||||
name: 'Edit', input: { file_path: `/src/${index}.ts` }, ...sidechain,
|
||||
}))
|
||||
const junkReads = Array.from({ length: 6 }, () => ({
|
||||
name: 'Read', input: { file_path: '/app/node_modules/pkg/index.js' },
|
||||
sessionId: 'agent-reviewer', project: 'p1', isSidechain: true,
|
||||
name: 'Read', input: { file_path: '/app/node_modules/pkg/index.js' }, ...sidechain,
|
||||
}))
|
||||
const repeatReads = Array.from({ length: 6 }, () => ({
|
||||
name: 'Read', input: { file_path: '/app/src/a.ts' },
|
||||
sessionId: 'agent-reviewer', project: 'p1', isSidechain: true,
|
||||
name: 'Read', input: { file_path: '/app/src/a.ts' }, ...sidechain,
|
||||
}))
|
||||
|
||||
expect(detectLowReadEditRatio(editCalls)).toBeNull()
|
||||
expect(detectJunkReads(junkReads)).toBeNull()
|
||||
// A subagent editing without reading, or reading into node_modules, is the
|
||||
// same waste as the parent doing it, and the CLAUDE.md rule both suggest
|
||||
// binds subagents too - so the full call population feeds them.
|
||||
expect(detectLowReadEditRatio(editCalls)?.id).toBe('read-edit-ratio')
|
||||
expect(detectJunkReads(junkReads)?.id).toBe('build-folder-reads')
|
||||
// A re-read is only waste when the context already held the file; a
|
||||
// sidechain starts fresh and has to read it.
|
||||
expect(detectDuplicateReads(repeatReads)).toBeNull()
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue