refactor: delete the dead v1 backend layers (#968) - #1161
Conversation
Roughly 3,400 lines of v1-era server code shipped in the installed wheel and the public import surface, describing endpoints that no longer exist. Dead auth-adjacent code is worse than absent code, because it implies controls that are not in force. Removed: * ui/models.py — 29 of 42 symbols (999 -> 107 lines). 21 classes had zero references; 6 more only *looked* referenced because the live v2 routers define their own identically-named classes locally. Only the settings/ API-key block that settings_v2 imports survives. * ui/response_models.py — the success envelope (ApiResponse, ApiError, PaginatedResponse, api_response) and four *_message helpers. No endpoint ever adopted them, while the module docstring advertised them as the house style. api_error/ErrorCodes/internal_error are live and stay. * ConnectionManager + WebSocketSubscriptionManager and POST /test/broadcast (with its CODEFRAME_ENABLE_TEST_ENDPOINTS gate). connect() was never called, so the subscriber list was permanently empty and every broadcast a no-op. SessionChatManager stays — session_chat_ws depends on it. * The v1 JSON config layer: ProjectConfig and its five member models plus the `Config` manager and its core/__init__ re-export. GlobalConfig, load_environment and the singleton accessors are load-bearing and stay. * accounts / sessions / verification tables — vestiges of a BetterAuth migration that never happened; no query has ever touched them. * python-jose (zero imports; JWT is fastapi-users -> PyJWT) and passlib (also zero imports). This drops the unmaintained `ecdsa` (CVE-2024-23342, wontfix) from the graph. bcrypt is now declared explicitly — api_keys.py imports it directly but was getting it only transitively. * rate_limit_auth / rate_limit_websocket decorators, never applied to a route and unapplicable by construction, plus the now-dead RATE_LIMIT_WEBSOCKET setting. * The unreachable app.state.default_workspace_path branch — nothing ever set that attribute, so the default always resolved to cwd. Three corrections to the issue's own claims: 1. response_models.py is NOT dead — 16 modules import api_error/ErrorCodes/ internal_error. Only 8 of its 12 public symbols went. 2. streaming_v2.py could not be deleted: tasks_v2 imports its helpers for the live SSE route. Only the never-decorated `router` object was dead. Moved to ui/streaming_utils.py so it cannot be re-mounted; server.py's include_router for it was adding zero routes. 3. Dropping the three tables needed a real migration, not a DDL delete. create_schema is idempotent CREATE-IF-NOT-EXISTS against a long-lived state.db, so removing the DDL alone would orphan them in every deployed database — and _create_indexes built idx_sessions_* outside the DDL block, so a bare delete would have broken every fresh install with "no such table: sessions". Ships as MIGRATIONS entry (2, ...) with SCHEMA_VERSION 1 -> 2. uv.lock regenerated: the Dockerfile builds with `uv sync --frozen`, so a stale lock passes every CI job and then fails the image build. Tests: 6461 passed. Deleted the suites whose only subject was deleted code (test_models.py, test_websocket_subscriptions.py, the already-fully-skipped test_websocket_integration.py, and the sprint-validation classes). web-ui: 1280 passed.
Pre-PR review found four dead things that this PR made dead, plus a missing assertion. All same-class as what the issue asks for. * pyasn1 is no longer a direct dependency. Its only parent chain was python-jose -> rsa -> pyasn1, both now gone, so the "security floor" pin was flooring a package with no consumer — and the comment above it named python-jose as a parent. * GlobalConfig.cors_origins (alias CORS_ORIGINS) removed. Its sole reader was get_cors_origins_list(), deleted here. Live CORS is parsed independently from CORS_ALLOWED_ORIGINS in server.py, so this was a knob an operator could set with no effect. `extra="ignore"` on the settings model means a leftover CORS_ORIGINS in someone's .env is inert, not an error. * .env.example kept the "Rate limit for WebSocket connections" comment after its RATE_LIMIT_WEBSOCKET line went, leaving it to read as documentation for the storage-backend block below it. * AGENT_SYSTEM_REFERENCE.md said "16 v2 router modules" over a list this PR cut to 15. The count was already wrong (there are 22 v2 REST routers), so it is now stated without a number. The added assertion pins the failure mode the migration exists to avoid: a surviving `CREATE INDEX ... ON sessions` would break every fresh install with "no such table: sessions". The fresh-database and legacy-upgrade tests now assert on sqlite_master indexes, by exact name — `interactive_sessions` is a live table whose own indexes must survive a substring match. Tests: 6461 passed.
|
Claude encountered an error after 7m 23s —— View job Bug-hunt review — PR #1161 (delete dead v1 backend layers)Reviewing for concrete defects only — logic errors, crash paths, security issues, data loss/races, breaking runtime contract changes. (Style, naming, architecture, and coverage are handled by a separate review.)
|
ReviewReviewed the full diff (35 files, +418/−3,879). This is a large deletion PR and it's unusually well-executed — I checked the highest-risk pieces by hand rather than trusting the PR description's own claims. What I verified independently
Minor observations (not blocking)
SecurityNo concerns. If anything this reduces attack surface: removes an unauthenticated-by-construction-looking broadcast primitive ( Test coverageStrong. The new Nothing here blocks merge from my read of the diff. |

Closes #968.
Deletes ~3,400 lines of v1-era server code that shipped in the installed wheel and the public import surface, describing endpoints that no longer exist. Dead auth-adjacent code is worse than absent code, because it implies controls that are not in force.
Net: +399 / −3,879 across 35 files.
Three corrections to the issue's own claims
The issue was written from a static scan; three of its claims did not survive verification, and one of them would have shipped a broken fresh install.
response_models.py"prescribes an envelope no endpoint uses"api_error/ErrorCodes/internal_error. Only 8 of its 12 public symbols were dead.streaming_v2is deleted and unmounted"tasks_v2.py:894,1271imports its helpers for the live SSE route. Only the never-decoratedrouterobject was dead.Two smaller ones: the v1 config block is at
config.py:607, not:495, and is not uniformly dead (GlobalConfig/load_environmentare load-bearing);python-joseis atpyproject.toml:63, not:58.The schema trap
create_schema()is idempotentCREATE TABLE IF NOT EXISTSagainst a long-livedstate.db— it is never rebuilt. So removing the DDL leaves the three tables, and theirREFERENCES users(id) ON DELETE CASCADEforeign keys, in every deployed database forever.Worse:
_create_indexesbuiltidx_sessions_user_id/idx_sessions_expires_atoutside the DDL block. Removing only theCREATE TABLEstatements would have made every freshDatabase.initialize()raisesqlite3.OperationalError: no such table: sessions.This ships instead as a real
MIGRATIONSentry —(2, _migration_002_drop_dead_auth_tables)withSCHEMA_VERSION1 → 2 — following the recipe the class docstring has carried since #953.What was removed
ui/models.py— 29 of 42 symbols (999 → 107 lines). 21 classes had zero references; 6 more only looked referenced because the live v2 routers define identically-named classes locally (TaskResponseintasks_v2.py:162,BlockerResponseinblockers_v2.py:37, …). Only the settings/API-key blocksettings_v2imports survives.ui/response_models.py— the success envelope (ApiResponse,ApiError,PaginatedResponse,api_response) and four*_messagehelpers. No endpoint ever adopted them, while the module docstring advertised them as the house style.ConnectionManager+WebSocketSubscriptionManagerandPOST /test/broadcastwith itsCODEFRAME_ENABLE_TEST_ENDPOINTSgate.connect()was never called anywhere, so the subscriber list was permanently empty and every broadcast a no-op.SessionChatManagerstays —session_chat_wsdepends on it.ProjectConfigand its five member models, theConfigmanager, and itscore/__init__re-export.accounts/sessions/verification— vestiges of a BetterAuth migration that never happened. Auth is stateless JWT; this is why logout cannot revoke.python-jose(zero imports — JWT is fastapi-users → PyJWT) andpasslib(also zero imports). Drops the unmaintainedecdsa(CVE-2024-23342, wontfix), plusrsaandpyasn1.bcryptis now declared explicitly:auth/api_keys.py:115imports it directly but was getting it only transitively.rate_limit_auth/rate_limit_websocketdecorators — never applied, and unapplicable by construction (the fastapi-users routes are mounted by library factories a decorator cannot reach; slowapi needs an HTTPRequest). Auth throttling isenforce_auth_rate_limit, untouched.app.state.default_workspace_pathbranch — nothing ever set that attribute, so the default always resolved tocwd.streaming_v2.pymoved toui/streaming_utils.py— it is a utility module, and out ofrouters/it cannot be re-mounted.uv.lockregenerated. Not optional:Dockerfile:33,36build withuv sync --frozen, so a stale lock passes every CI job and then fails the image build.Verification
uv run pytest tests/ --ignore=tests/e2e -m "not lifecycle"). web-ui: 1280 passed.ruffclean.main— 144 routes, same paths, methods and resolved dependency functions — and so isapp.openapi(). For a deletion of this size that is the load-bearing evidence: no endpoint and no auth dependency changed.main's ownSchemaManager, seeded with rows in all three tables: after upgrade all three tables and all their indexes are gone,user_version = 2, and a second run is a no-op. Downgrade is safe (old code recreates them viaCREATE IF NOT EXISTSand does not crash).uv sync --frozen --no-devenvironment —argon2-cffiandbcryptsurvive viafastapi-users → pwdlib[argon2,bcrypt], so removingpasslibstripped nothing.Reviewed pre-PR by
codex review(no findings) and an internal reviewer (no blocking findings; five nits, all fixed in 251f356).Deleted tests
tests/ui/test_models.py,tests/ui/test_websocket_subscriptions.py,tests/ui/test_websocket_integration.py(already 100% skipped since the v1/wsprotocol was removed), and the sprint-validation classes intests/config/test_config.py/tests/core/test_config_credentials.py. Each deleted test's only subject was deleted production code —test_load_environmentwas salvaged and moved, becauseload_environmentis live.Known limitations
CODEFRAME_ENABLE_TEST_ENDPOINTSis gone as a supported variable./test/broadcastwas its only consumer. Anything setting it in CI is now a no-op, not an error.CHANGELOG.mdstill mentions it, correctly, as history.uv lock --checkin CI. The absence of that guard is what letpython-josesit unused; adding it is new scope, so it is not in this PR. Worth a follow-up.SCHEMA_VERSIONhas no downgrade path. Consistent with the existing migration framework ([P2.3] Route all platform_store writes through the shared lock and add schema versioning #953), not a regression introduced here.