Repository navigation
Split page.html, test_meter.py, and app.py so agents read less context - #140
Merged
Merged
Conversation
Large single files make coding-agent sessions expensive: agents cannot read them whole, so they search them repeatedly and every result stays in context. New source files stay under 80 KB; existing large files carry a ceiling about 5% above their current size that may only be lowered or removed. Adds the October session-cost survey and refactor plan. Co-Authored-By: Claude Code <noreply@anthropic.com>
page.html was 1.26 MB (about 315k tokens), larger than an agent's context window, so every dashboard change began with dozens of grep and sed probes whose results stayed in context. The October survey attributes about 46% of carried tool output in this repo's sessions to that navigation. The dashboard source now lives in dashboard/ as 42 parts (all under 80 KB): styles/, markup/, and js/ files grouped by view. dashboard/page.manifest lists their serving order and the server concatenates them byte for byte, so the served page is identical to the old page.html. No build step or dependency. - token_meter/dashboard_page.py: validated manifest, cached assembly - server: serves the assembled page; assets resolve beside dashboard/ - runtime manifest, start script, Windows runtime check, CI updated - tests read one assembled copy through tests/dashboard_page.py - scripts/check-page replaces the inline node parse command - scripts/split-dashboard re-splits a branch's own page.html by anchors, so open branches can port their dashboard edits Co-Authored-By: Claude Code <noreply@anthropic.com>
The dashboard now lives in dashboard/js/*.js. Work evidence classified .js outside a recognized frontend folder as backend, so dashboard edits would have read as backend work. Add dashboard(s)/ to the frontend folders. Co-Authored-By: Claude Code <noreply@anthropic.com>
tests/test_meter.py was 1.43 MB with 97 classes, so agents grepped it instead of opening the tests for the surface they changed. Its classes now live in 29 modules under 80 KB each (test_dashboard_*.py, test_pricing.py, test_menubar_tray.py, ...). The 230-method DashboardLayoutTests and the OpenCode tests are split by topic onto shared bases that hold their original setup and helpers (dashboard_layout_support.py, opencode_test_support.py); module-level fixtures move to meter_support.py. Coverage is unchanged: the same 1,034 test methods, byte-identical bodies, and the full suite passes serially and in parallel. Co-Authored-By: Claude Code <noreply@anthropic.com>
app.py was 631 KB. Move 421 top-level functions and constants (about 150 KB) into 12 modules under token_meter/app_helpers/ (trace parsing for Claude, Codex, and Cursor, normalization, the menu-bar payload, config-file parsing, and shared leaf helpers). app.py re-exports every moved name, so meter.NAME and token_meter.app.NAME are unchanged. Only code that is safe to move moved. A dependency analysis kept in app.py anything that tests patch through meter (including patched imported modules such as meter.threading), anything declared global or rebound, anything that reads __file__ or sys.modules, and anything that depends on those. Helper modules may import only app_helpers.common, never app.py; a guard test enforces this. The composition guards now cover the helpers too, and the app.py size ceiling drops to 510 KB. Function bodies are moved verbatim; the full suite passes unchanged. Co-Authored-By: Claude Code <noreply@anthropic.com>
…rules - scripts/show [FILE] NAME prints one Python or dashboard JS definition with line numbers, replacing wide grep and sed -n probes (about 46% of carried tool output in October's sessions). - specs/AGENTS.md is loaded on every turn of every session and subagent. The reporter, PR-status, and contributor-acknowledgment procedures, which the intake, github-ops, and communication skills already spell out, shrink to a pointer that keeps their authority limits. Co-Authored-By: Claude Code <noreply@anthropic.com>
- Dashboard parts no longer end in a blank line (git diff --check was red): the splitter now moves leading blank lines with each anchor. Re-split from the old page.html; the assembled page is still byte-identical. - /health reports page_ready and ok only when every manifest part exists, so a partial runtime no longer reads as ready while / returns 503. - specs/CONTRIBUTING.md documents a rehearsed three-way port for branches that still edit page.html or tests/test_meter.py; split-dashboard refuses a folder that already holds parts. - A contract test rejects patching names that moved into app_helpers through meter. - dashboard/ counts as frontend only as a folder (dashboards?/), with a negative test case. - specs/AGENTS.md delivery bullets restate their gating and scope limits. - Stale page.html references updated; plan header notes implementation. Co-Authored-By: Claude Code <noreply@anthropic.com>
The condensed bullet placed the commit after the gates, contradicting the intake and github-ops skills: gates bind to a commit, so the scoped commit comes first and the push, PR, issue handoff, and Slack reply follow once every gate passes. Also correct the health_state docstring. Co-Authored-By: Claude Code <noreply@anthropic.com>
Port main's edits to files this branch split or moved: - page.html changes (#138, #139) applied to dashboard/ parts with the CONTRIBUTING three-way recipe; the assembled page equals main's page.html. - tests/test_meter.py hunks moved to test_dashboard_models.py and test_menubar_tray.py; new page reads use the assembled page. - The debug skill's code map now points at dashboard/ (new release hash registered); its path test also searches app_helpers. - Size ceilings re-measured after the merge for test_git_delivery.py, services/work_insights.py, and services/git_delivery.py, which grew on main before the budget landed. Co-Authored-By: Claude Code <noreply@anthropic.com>
…ne salt CI on this branch failed with "UNIQUE constraint failed: delivery_metadata.key": two test workers created the same fresh ledger at once, and the salt was read, then inserted, in separate steps. The server and the MCP can race the same way on a new machine. Insert with OR IGNORE first and read the stored salt back, so the first writer wins and nobody fails. A test injects the competing write between the two steps; it fails on the old code with the CI error and passes now. Co-Authored-By: Claude Code <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A survey of October's 100 most expensive agent sessions found that almost all of the cost is context, not output. In this repo's 68 sessions in that set, about 46% of the tool output that stayed in context came from
grepandsed -nprobes into three oversized files:page.html(1.26 MB),tests/test_meter.py(1.43 MB), andtoken_meter/app.py(631 KB).page.htmlalone is bigger than the context window, so it can never be read whole. The analysis and plan are inspecs/plans/2026-11-06-session-cost-refactor.md, which is historical.This PR breaks those files into parts small enough to read whole, and adds guardrails so they don't grow back.
What changed
tests/contracts/test_source_size_budget.py): new source files must stay under 80 KB. Existing large files get a ceiling that can only be lowered or removed.page.html→dashboard/): 42 parts, each under 80 KB, grouped asstyles/,markup/, andjs/by view.dashboard/page.manifestsets the serving order, andtoken_meter/dashboard_page.pyconcatenates the parts. There is no build step and no new dependency.page.html./healthreports ready only when every part listed in the manifest exists.scripts/check-page), and docs.tests/test_meter.py→ 29 modules,tests/test_*.py): each module covers one surface. The same 1,034 test methods are kept with unchanged bodies, and no test is collected twice. Tests that read the page now use one assembled copy (tests/dashboard_page.py).app.py(631 KB → 485 KB): 413 top-level helpers moved verbatim intotoken_meter/app_helpers/, andapp.pyre-exports them, someter.NAMEis unchanged.meter, plus anything that depends on it, stayed inapp.py.app.pyand stop tests from patching moved names throughmeter.scripts/show [FILE] NAMEprints one definition with its line numbers.scripts/check-pageassembles the dashboard and parses its JavaScript.specs/AGENTS.mddelivery-flow bullets are condensed to pointers into the skills that already spell them out. The authority limits are kept inline.dashboard/scripts now count as frontend files.Open branches that edit
page.htmlortests/test_meter.pyThese branches will hit a modify/delete conflict.
specs/CONTRIBUTING.md→ "Porting a page.html or test_meter.py change" has a three-way port usingscripts/split-dashboard; the recipe was rehearsed on a recent branch. Branches older than the splitter's anchors have to be ported by hand.Validation
5785aca:git diff --checkis clean, and the assembled page matches777a6d1:page.html(cmp).scripts/check-pageprintsjs ok, andpy_compileandbash -npass.python3 scripts/run-testsruns 2540 tests: OK, 21 skipped./healthmatch base, and a missing part returns 503 on both/and/health.5785aca.Not verified
./scripts/installon macOS. The installed runtime was left onmain.Follow-ups
services/work_insights.pyanddomain/work.py.app.pyonce tests patch the modules that own it.specs/AGENTS.mdfurther. That needs a decision on which rules can live only in skills.🤖 Generated with Claude Code