Files
6krrt/plans/review-code-analysis-refactoring-opportunities.md
adlee-was-taken a9fa56ee1d docs(plans): review opencode's refactoring survey, and commit the survey itself
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
2026-09-12 12:46:28 -04:00

174 lines
7.5 KiB
Markdown

# 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_overlay` and `summary_lines` into
`src/config_loader.py`, leaving `config.py` as 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.md`
already said 1155, so 897 predates even that. Anyone citing this document's
numbers should re-count.
- **"`src/config.py` 1300+"** -- 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 bare `Status:` form nor one of the five valid values. Corrected
in place to `Status: reference`, which is what it is -- a survey, not a work
plan. Three tests were failing on it the moment it landed in `plans/`.
---
## 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.