New request
#1907: refactor(routes): split 8584-line api/routes.py into per-domain modules
We'll provision a sandbox, run an agent against the issue, and open a draft PR. You can pull the branch and iterate from there.
Summary
api/routes.py is 8,584 lines, 203 function definitions, dispatching every HTTP path through long if/elif chains. The file is the single biggest merge-conflict surface in the repo and exceeds the comfortable working set of any tool that loads files into a single buffer.
Numbers (v0.51.21)
$ wc -l api/routes.py
8584 api/routes.py
$ grep -c "^def " api/routes.py
203
The file contains the dispatch table for sessions, chat, files, cron, MCP, providers, profiles, kanban, oauth, onboarding, workspace, and approvals — concerns that have natural module boundaries and already have separate files for their implementation (api/streaming.py, api/profiles.py, api/kanban_bridge.py, api/onboarding.py, api/oauth.py, api/providers.py, etc.) but get wired up via routes.py.
Why it matters
- Merge conflicts — every contributor PR that adds an endpoint touches the same dispatch chain. Routine.
- Reading cost — agents and humans both pay full-file load cost for any handler change. The Claude/Opus/GPT load tax on this file alone is non-trivial when used as review context.
- Test isolation — handlers in the same module share the import surface; touching one route imports everything routes.py imports.
- Surfaces the real coupling — once split, cross-module imports become explicit and the actual coupling graph shows up.
Proposed shape
Split into api/routes/ package with one module per domain, plus a thin api/routes/__init__.py that aggregates the dispatch table:
api/routes/
__init__.py # main dispatch entry, imports all submodules, exposes handle_get/handle_post
_common.py # _check_csrf, j(), bad(), shared helpers
sessions.py # session CRUD, list, search, /retry, /undo
chat.py # /api/chat/start, /api/chat/cancel, /api/chat/stream
files.py # workspace file browser, upload, preview
cron.py # cron job CRUD, run, pause
mcp.py # MCP server CRUD, tool list
providers.py # provider settings, credential edit
profiles.py # profile CRUD, switch
kanban.py # kanban routes (delegates to kanban_bridge)
oauth.py # oauth callback handlers
onboarding.py # wizard steps
workspace.py # workspace CRUD, list
approvals.py # /api/approval/*
Constraints:
_check_csrfand the small set of helpers that all handlers use stay in_common.py- Module-level imports must be stable to avoid cold-start regressions (the current giant file is import-heavy at module load — splitting may surface ordering issues)
- All existing test paths continue to work — routes resolve to the same callables, no API contract change
Risk / scope
- M2 architectural — no behavior change, all tests should continue to pass
- ~5-10 PR sequence, one module per PR, each landable independently
- First PR is the scaffolding (
api/routes/__init__.py+_common.py+ one moved domain likeapprovals.pyto validate the pattern) - Test suite (4810 tests) is the safety net — any regression shows up as a route handler going missing
Reporter
Architecture review, May 8 2026 — flagged as the highest-leverage non-bug refactor: every contributor touches it, every agent reads it, every merge conflict involves it.
Labels
enhancement, architecture, refactor, M2
Splitting the monolithic 8,584-line api/routes.py into per-domain modules is a straight internal refactor to reduce conflicts and improve modularity.
- api/routes.py
- api/routes/__init__.py
- api/routes/sessions.py
- api/routes/chat.py
- api/routes/oauth.py
- api/routes/profiles.py
- api/routes/mcp.py
- api/routes/workspace.py
- api/routes/kanban.py