The survey was written to the shared checkout and never committed, so it would have been lost the way WI-5 was. Both files land together: the survey as `reference`, the review beside it. Every claim was checked against the code rather than taken on trust, and the result splits cleanly: the two recommendations about small modules were read from source and are accurate (`Source` really is a Literal at proficiency.py:61 with exactly the six members quoted; the three do-not-refactor calls are all correct). The two about the largest files were not. `context_prune.py` has no `pinch()` function -- the survey names one, and quotes the file's 455 lines as that function's length. Its proposed `_summarize_prose()` would be inventing a feature: the pinch stage trims tool results and nothing else, deliberately. The real finding underneath is `prune_context` at 216 lines, which is the extract candidate. The `config.py` split names six classes that do not exist (`ProfileConfig`, `TiersConfig`, `AllowlistConfig`, `TierOverrideConfig`, `EnergyConfig`, `BalanceConfig`) and finds no home for 18 of the 31 real ones, nor for `load_config`. More to the point, it splits the wrong axis: of 72 validators, eleven hang off `RouterConfig` and cross-check sections against each other, so those 380 lines stay in one file under any split. The counter-proposal is one file, not nine -- lift the 112-line loader out and leave the schema. The package-name collision that split would create (`src/config/` beside the repo-root `config/` data directory, which has no `__init__.py`) was tested in a temp tree rather than argued about: a regular package wins over a namespace portion regardless of path order. Not a blocker, recorded because it is the first thing that looks like one. Also corrected: the survey's `**Status**:` header was neither the bare `Status:` form nor a valid value, so it failed three of `tests/test_plans_declare_status.py` the moment it reached `plans/`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VRQXz5SYZYVWscxS1QqF6U
7.5 KiB
Review: code-analysis-refactoring-opportunities.md
Status: done -- review complete, one recommendation accepted with a changed shape
Reviewed 2026-09-12 against src/ at 1e2e806. Every claim below was checked
by reading the code or running it, not by reasoning about it.
Verdict
Two of the four recommendations were written against the code. Two were not,
and both of those concern the largest files in the analysis --
config.py (1601 lines) and context_prune.py (455). The pattern is worth
naming, because it predicts where to look next time: the small modules got
accurate assessments and the large ones got plausible-sounding guesses.
| # | Recommendation | Verdict |
|---|---|---|
| 1 | Split config.py |
Right instinct, wrong plan -- see below |
| 2 | Extract context_prune.py helpers |
Premise is false; a real version exists |
| 3 | Source as Enum |
Accurate, and correctly ranked Low |
| 4 | Type alias standardization | Accurate, no objection |
The "What NOT to Refactor" table is correct on all three counts, verified:
cost_score/eco_score really are aliases of normalize_inverted
(scoring.py:58-59), and events.py really does hold four module-level
singletons (events.py:24-30).
1. Splitting config.py
The instinct is sound. The proposed layout is not usable as written.
The proposed structure names six classes that do not exist
config.py has 31 classes. The plan's nine-file tree names thirteen types,
of which these six are invented:
| Plan says | Reality |
|---|---|
ProfileConfig |
RoutingProfile |
TiersConfig |
TieringConfig |
AllowlistConfig |
does not exist |
TierOverrideConfig |
does not exist (ContextOverride is the nearest) |
EnergyConfig |
does not exist (LocalEnergyConfig is the nearest) |
BalanceConfig |
does not exist |
Profile enum |
the only enum is FlexPreference |
It also finds no home for 18 of the 31 real classes -- Objective,
ContextConfig, ProficiencyConfig, EscalationConfig, IterationConfig,
FreshnessConfig, PinchConfig, PinchRelevanceConfig, SessionCacheConfig,
ExplorationConfig, CircuitBreakerConfig, DatabaseConfig,
LocalVisionConfig, LocalComputeConfig, LocalDispatchModel,
DispatchProvider, LoggingConfig, DispatchSettingsConfig -- nor for
load_config, _merge_overlay or summary_lines.
The part that makes the file long is the part that cannot be split
Measured:
| span | lines | what |
|---|---|---|
| 1 - 1108 | 1108 | 30 leaf models, genuinely independent |
| 1109 - 1488 | 380 | RouterConfig and its validators |
| 1489 - 1601 | 112 | load_config, _merge_overlay, summary_lines |
config.py carries 72 validators, and eleven of them hang off
RouterConfig and deliberately reach across sections: local energy against
its tariff, classifier.mode against exactly one cloud primary, gaming mode
against a configured cloud classifier, routing.tool_use_category against the
real category list, profile names against BUILTIN_PROFILES,
default_profile against known profiles, default_provider against configured
providers, local-dispatch categories, balance_url against parser and
telemetry, and the verifier model against differing hosts.
Those 380 lines stay in one file under any split, because cross-checking sections is what they are for. The plan's stated benefit -- "clearer ownership of each config section" -- is weakest exactly where the file is hardest to read. Splitting moves the easy 1108 lines and leaves the hard 380 untouched.
The name collision is safe -- checked, not assumed
src/config.py becoming src/config/ puts a package next to the repo-root
config/ data directory, which has no __init__.py. The service runs from the
repo root, so sys.path[0] is '' and the data directory is found first.
Built the exact layout in a temp tree and imported it: a regular package wins
over a namespace portion regardless of path order, and config resolved to
src/config/__init__.py both with PYTHONPATH=src and with '' forced first.
Not a blocker. Recording it because it is the first thing that looks like
one.
What to do instead
If this is worth doing at all, the split that pays is not by config section but by kind, and it is one file, not nine:
- move
load_config,_merge_overlayandsummary_linesintosrc/config_loader.py, leavingconfig.pyas schema only.
That is 112 lines out, no package, no __init__.py shim, no import-resolution
question, and it separates the two things that actually differ: the schema is
declarative and rarely read top-to-bottom; the loader has real control flow
(overlay merging, ROUTER_IGNORE_LOCAL_CONFIG, backup rotation) and is read
when something goes wrong.
The nine-way split can follow later if the leaf models keep growing. It should
not go first: a 60-file import surface re-exported through
config/__init__.py is a lot of churn to buy navigation in the third of the
file that was never the problem.
2. Extracting context_prune.py helpers
There is no pinch() function. The module's public functions are
prune_context, trim_candidates, order_by_relevance, estimate_tokens and
extract_text, plus four private helpers already extracted (_text_only,
_tool_name, _first_user_turn_indexes, _with_text).
"455 lines" is the file's length, quoted as a function's.
Of the three proposed extractions, _summarize_prose() would be inventing a
feature: the pinch stage trims tool results and nothing else, on purpose --
CLAUDE.md states it, and there is no prose-summarization path to extract.
There is a real finding underneath. prune_context runs lines 240-455 --
216 lines, easily the longest function in the module and the genuine
extract candidate the analysis was reaching for. Anyone acting on item 2 should
start there, and should read the protected_max_chars note in docs/pinch.md
first, since prefix protection is the part with a documented gotcha.
3 and 4
Source really is a Literal at proficiency.py:61, and the six members in
the plan match the source exactly -- this one was read, not guessed.
Worth pricing before anyone does it: the strings appear in 14 comparison sites
across src/ and 32 across tests/, against only 3 type-annotation sites. A
plain Enum makes every one of those comparisons silently False. If it is
done, it has to be class Source(str, Enum), and the payoff is small enough
that "Optional" is the right ranking.
Item 4 is unobjectionable and equally optional.
Corrections to the document's own figures
- "897 tests" -- the suite is 1912 as of this review.
CLAUDE.mdalready said 1155, so 897 predates even that. Anyone citing this document's numbers should re-count. - "
src/config.py1300+" -- 1601. Directionally fine. - Every other line count in the Files Analyzed table is exact.
- The document did not pass
tests/test_plans_declare_status.py: its header read**Status**: Analysis complete, recommendations ready, which is neither the required bareStatus:form nor one of the five valid values. Corrected in place toStatus: reference, which is what it is -- a survey, not a work plan. Three tests were failing on it the moment it landed inplans/.
Unrelated, found while checking
config/ holds 47 config.local.yaml.bak.<epoch> files. The
persisted-config write path backs up on every admin write and never prunes.
Harmless today, but that directory is the one place holding the irreplaceable
overlay, and a backup set nobody can date-order by eye is not much of a safety
net. Worth a retention cap.