Finding (stall-log mining, 30-day window)
src/dazzle/render/fragment/renderer.py is 3,784 lines of one-class match-dispatch. Friction score: 190 (84 repeat reads + 12 edit failures + 14 errors).
The module-top docstring is excellent — it explains the exhaustiveness pattern and the test gate. The friction comes from size, not opacity:
- Agents read the file 3+ times per session to find the right
case Foo() arm before edits.
- 12 edit failures in the window — the file is large enough that the surrounding context grabbed in
old_string is often non-unique, triggering "found multiple occurrences" errors.
Proposed decomposition
Mirror the workspace_rendering pattern (#1057). Split by primitive family:
src/dazzle/render/fragment/
renderer.py # top-level dispatcher; ~200 lines
emit/
cards.py # ActionCard, DashboardCard, Card, ...
charts.py # BarChart, BoxPlot, Bullet, ...
forms.py # form_field, CreateButton, ...
tables.py # filterable_table, BulkActionToolbar, ...
text.py # text primitives + small bits
renderer.py becomes a 200-line file that does the match-dispatch and delegates each arm to a module-level emit function. The exhaustiveness check stays in renderer.py (one match block, all primitives). The per-family files house the 20–40 lines of emit logic each.
Expected effect: repeat reads drop from 84 → ~10 (agents read the dispatcher once, then jump to one family file). Edit failures drop because each family file is small enough to give unique old_string context.
Alternative (cheaper) fix
If decomposition is too disruptive: add a family-anchor comment header before each match-arm group (# === Cards ===, # === Charts ===) and a top-of-file index. This gives agents a coarse skip-table to jump directly to the right block without re-Reading the whole file.
Evidence
python scripts/stall_log_mine.py --days 30 → friction-ranked table, row 6.
Severity
MEDIUM. Decomposition is mechanical but high-effort; the cheaper anchor-header version would close 50–70% of the friction at very low cost.
Finding (stall-log mining, 30-day window)
src/dazzle/render/fragment/renderer.pyis 3,784 lines of one-class match-dispatch. Friction score: 190 (84 repeat reads + 12 edit failures + 14 errors).The module-top docstring is excellent — it explains the exhaustiveness pattern and the test gate. The friction comes from size, not opacity:
case Foo()arm before edits.old_stringis often non-unique, triggering "found multiple occurrences" errors.Proposed decomposition
Mirror the workspace_rendering pattern (#1057). Split by primitive family:
renderer.pybecomes a 200-line file that does the match-dispatch and delegates each arm to a module-level emit function. The exhaustiveness check stays inrenderer.py(one match block, all primitives). The per-family files house the 20–40 lines of emit logic each.Expected effect: repeat reads drop from 84 → ~10 (agents read the dispatcher once, then jump to one family file). Edit failures drop because each family file is small enough to give unique
old_stringcontext.Alternative (cheaper) fix
If decomposition is too disruptive: add a family-anchor comment header before each match-arm group (
# === Cards ===,# === Charts ===) and a top-of-file index. This gives agents a coarse skip-table to jump directly to the right block without re-Reading the whole file.Evidence
python scripts/stall_log_mine.py --days 30→ friction-ranked table, row 6.Severity
MEDIUM. Decomposition is mechanical but high-effort; the cheaper anchor-header version would close 50–70% of the friction at very low cost.