feat(api): auto-wire backend services at startup#555
Conversation
Auto-create all backend services in create_app() when not explicitly provided, eliminating 503 errors on dashboard pages when running via Docker/uvicorn factory mode. Phase 1 (construction time): message_bus, cost_tracker, task_engine, provider_registry -- these don't need connected persistence. Phase 2 (on_startup after persistence connects): settings_service and settings_dispatcher -- these need persistence.settings which requires a connected backend. - Add API channel merging into auto-wired bus config for bridge compat - Add AppState.set_settings_service() for deferred Phase 2 injection - Add API_SERVICE_AUTO_WIRED event constant for structured logging - Update health check tests for auto-wired bus being always available - Update server.py docstring to reflect auto-wiring capability Closes #550 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mypy cannot track that set_settings_service() mutates _settings_service through a method call, so it narrows has_settings_service to always False from the constructor default. Add type: ignore[unreachable] on the follow-up assertions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Address findings from 8 review agents (13 items): - Extract Phase 1 + Phase 2 auto-wiring into auto_wire.py (reduces app.py from 1018 to 915 lines, fixes file size regression) - Fix partial state corruption: move set_settings_service() call after dispatcher.start() so AppState isn't mutated if dispatcher fails - Fix name shadowing: rename _auto_wire_settings bool to _should_auto_wire - Remove dead code in on_startup except block (_auto_wired_dispatcher can never be non-None when assignment fails) - Add provider_registry parameter to create_app() for opt-out parity - Wrap all Phase 1 constructors in try/except with structured logging - Add contextual logging for SettingsEncryptor.from_env() failures - Add debug log for ConfigResolver + ProviderManagement creation - Update CLAUDE.md Package Structure to mention auto-wiring - Add missing tests: provider_registry, channel merging, param override Pre-reviewed by 8 agents, 13 findings addressed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use isinstance narrowing for InMemoryMessageBus before accessing _config attribute to satisfy mypy's protocol type checking. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (8)
📜 Recent review details⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
🧰 Additional context used📓 Path-based instructions (7)**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
tests/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
{src,tests}/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
src/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
src/synthorg/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
src/synthorg/{api,auth,backup,budget,cli,communication,config,core,engine,hr,memory,persistence,observability,providers,security,settings,templates,tools}/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
src/synthorg/api/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (15)📚 Learning: 2026-03-18T11:08:24.271ZApplied to files:
📚 Learning: 2026-03-15T11:48:14.867ZApplied to files:
📚 Learning: 2026-03-18T11:08:24.271ZApplied to files:
📚 Learning: 2026-03-18T11:08:24.271ZApplied to files:
📚 Learning: 2026-03-18T11:08:24.271ZApplied to files:
📚 Learning: 2026-03-18T11:08:24.271ZApplied to files:
📚 Learning: 2026-03-18T11:08:24.271ZApplied to files:
📚 Learning: 2026-03-18T11:08:24.271ZApplied to files:
📚 Learning: 2026-03-18T11:08:24.271ZApplied to files:
📚 Learning: 2026-03-18T11:08:24.271ZApplied to files:
📚 Learning: 2026-03-18T11:08:24.271ZApplied to files:
📚 Learning: 2026-03-18T11:08:24.271ZApplied to files:
📚 Learning: 2026-03-15T11:48:14.867ZApplied to files:
📚 Learning: 2026-03-18T11:08:24.271ZApplied to files:
📚 Learning: 2026-03-18T11:08:24.271ZApplied to files:
🧬 Code graph analysis (3)tests/unit/api/test_app.py (5)
src/synthorg/api/app.py (4)
src/synthorg/api/auto_wire.py (4)
🔇 Additional comments (20)
📝 WalkthroughSummary by CodeRabbitRelease Notes
WalkthroughThis PR implements production auto-wiring of backend services at startup in two phases: Phase 1 wires non-persistence-dependent services (message bus, cost tracker, provider registry, task engine) at app construction, and Phase 2 wires settings-dependent services (SettingsService, ConfigResolver, ProviderManagementService) after persistence connects during startup. Changes
Sequence Diagram(s)sequenceDiagram
participant App as create_app()
participant Config as RootConfig
participant Phase1 as auto_wire_phase1()
participant Persistence as PersistenceBackend
participant Phase2 as auto_wire_settings()
participant AppState
participant Dispatcher as SettingsChangeDispatcher
App->>Config: Extract effective_config
App->>Phase1: Call with config, no persistence
Phase1->>Phase1: Create InMemoryMessageBus from config
Phase1->>Phase1: Create CostTracker from config.budget
Phase1->>Phase1: Create ProviderRegistry from config.providers
Phase1->>Phase1: Log warning (no persistence for task_engine)
Phase1-->>App: Return Phase1Result (bus, cost_tracker, registry)
App->>AppState: Construct with wired services from Phase1
App->>App: Build lifecycle with should_auto_wire_settings=True
Note over App: On startup signal...
App->>Persistence: _init_persistence (already connected)
Persistence-->>App: Connected
App->>Phase2: Call auto_wire_settings with persistence now available
Phase2->>Phase2: Create SettingsService(persistence, registry, config)
Phase2->>Phase2: Build and start dispatcher
Dispatcher-->>Phase2: Started successfully
Phase2->>AppState: set_settings_service(settings_service)
AppState->>AppState: Initialize ConfigResolver
AppState->>AppState: Initialize ProviderManagementService
Phase2->>App: Return started dispatcher
Note over App: Phase 2 complete, full app operational
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
✨ Simplify code
📝 Coding Plan
Comment |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the robustness and ease of deployment for the API by introducing an automatic service wiring system. It ensures that critical backend services are properly initialized at startup, even in environments where they are not explicitly configured, thereby mitigating common 503 errors. The changes streamline the application's lifecycle management by separating service initialization into two distinct phases based on their dependencies, centralizing this logic, and updating how the application state manages these services. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces an auto-wiring mechanism for backend services, significantly improving the application's startup process by automatically configuring services that are not explicitly provided. This change centralizes service initialization logic, reducing boilerplate and potential 503 errors in factory mode. The implementation is well-structured, separating Phase 1 (construction time) and Phase 2 (on_startup) auto-wiring into a new auto_wire.py module. Comprehensive unit tests have been added to cover various auto-wiring scenarios, including explicit service overrides and persistence-dependent services. Documentation has been updated to reflect these changes. The only minor issues identified are the use of deprecated exception syntax in a few places and an outdated docstring for a complex function.
src/synthorg/api/app.py
Outdated
|
|
||
|
|
||
| def _build_lifecycle( # noqa: PLR0913 | ||
| def _build_lifecycle( # noqa: PLR0913, C901, D417 |
There was a problem hiding this comment.
The D417 noqa indicates that the docstring for _build_lifecycle is missing descriptions for some arguments. It's important to keep docstrings up-to-date for maintainability and clarity, especially for complex functions. Please add descriptions for should_auto_wire_settings and effective_config to the docstring.
| except MemoryError, RecursionError: | ||
| raise |
src/synthorg/api/auto_wire.py
Outdated
| except Exception: | ||
| logger.exception( |
src/synthorg/api/auto_wire.py
Outdated
| except Exception: | ||
| logger.exception( |
src/synthorg/api/auto_wire.py
Outdated
| except Exception: | ||
| logger.exception( |
| except Exception: | ||
| logger.exception( |
| except Exception: | ||
| logger.exception( |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #555 +/- ##
==========================================
- Coverage 92.49% 92.45% -0.05%
==========================================
Files 542 544 +2
Lines 26655 26783 +128
Branches 2544 2554 +10
==========================================
+ Hits 24655 24762 +107
- Misses 1598 1615 +17
- Partials 402 406 +4 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
- Extract lifecycle helpers (lifecycle.py) from app.py (915->588 lines) - Fix dispatcher resource leak on post-start failure in auto_wire_settings - Add BuildDispatcherFn Protocol replacing Callable[..., ...] - Split auto_wire_phase1 into helper functions (<50 lines each) - Make set_settings_service atomic (construct locals before assigning) - Wrap build_dispatcher() call in try/except with specific logging - Add deferred import comment explaining import cycle - Remove env var name from error log, redact DB path in info log - Document all 10 params in _build_lifecycle docstring (remove D417 noqa) - Improve _should_auto_wire comment clarity - Remove unused caplog fixture, tighten >= 1 to == 1 assertion - Add 8 new tests: Phase 2 rollback, dispatcher shutdown, start failure invariant, SettingsService creation failure, channel deduplication, cost tracker/message bus error paths - Update CLAUDE.md package structure for lifecycle.py Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🤖 I have created a release *beep* *boop* --- ## [0.3.5](v0.3.4...v0.3.5) (2026-03-18) ### Features * **api:** auto-wire backend services at startup ([#555](#555)) ([0e52c47](0e52c47)) ### Bug Fixes * **api:** resolve WebSocket 403 rejection ([#549](#549)) ([#556](#556)) ([60453d2](60453d2)) * **cli:** verify SLSA provenance via GitHub attestation API ([#548](#548)) ([91d4f79](91d4f79)), closes [#532](#532) ### Performance * **test:** speed up test suite -- reduce Hypothesis examples and eliminate real sleeps ([#557](#557)) ([d5f3a41](d5f3a41)) ### Refactoring * replace _ErrorResponseSpec NamedTuple with TypedDict ([#554](#554)) ([71cc6e1](71cc6e1)) ### Maintenance * **docker:** suppress pydantic v1 warning on Python 3.14 ([#552](#552)) ([cbe1f05](cbe1f05)), closes [#551](#551) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
Summary
create_app()when not explicitly provided, eliminating 503 errors on dashboard pages when running via Docker/uvicorn factory modepersistence.settingswhich requires a connected backendauto_wire.pymodule extracting all auto-wire logic from app.pyAppState.set_settings_service()for deferred Phase 2 injection (also creates ConfigResolver + ProviderManagementService)Test plan
Pre-reviewed by 8 agents, 13 findings addressed.
Closes #550
🤖 Generated with Claude Code