Skip to content

feat(settings): register API config in SettingsService with 2-phase init#518

Merged
Aureliolo merged 3 commits intomainfrom
feat/2phase-app-init
Mar 17, 2026
Merged

feat(settings): register API config in SettingsService with 2-phase init#518
Aureliolo merged 3 commits intomainfrom
feat/2phase-app-init

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

  • Register 10 API settings (server, CORS, rate limiting, auth) in the settings registry so they appear in the Settings UI and resolve through the DB > env > YAML > code-default priority chain
  • Add get_api_config() composed-read method to ConfigResolver — resolves 4 runtime-editable settings in parallel via asyncio.TaskGroup, merges onto base config via model_copy(update=...)
  • Document the 2-phase init pattern in app.py: Phase 1 reads RootConfig at construction (immutable), Phase 2 uses SettingsService after persistence connects
  • 6 bootstrap-only settings marked restart_required=True (Litestar bakes middleware/CORS at construction)
  • Update CLAUDE.md and docs/design/operations.md namespace count 8→9

Settings registered (10 total)

Runtime-editable (4): rate_limit_max_requests, rate_limit_time_unit, jwt_expiry_minutes, min_password_length

Bootstrap-only (6): server_host, server_port, api_prefix, cors_allowed_origins, rate_limit_exclude_paths, auth_exclude_paths

Closes #504

Test plan

  • 6 unit tests in TestGetApiConfig (defaults, DB overrides, bootstrap preservation, error propagation, enum resolution)
  • 2 integration tests with real SQLite + SettingsService + ConfigResolver round-trip
  • Existing test_all_namespaces_have_definitions auto-validates new API namespace
  • Full suite: 8825 passed, 94.44% coverage
  • mypy strict: clean
  • ruff lint + format: clean

Review coverage

Pre-reviewed by 13 agents, 6 findings addressed:

  • docs-consistency: namespace count + list updated in CLAUDE.md and operations.md
  • code-reviewer + type-design: _FakeRateLimitConfig.time_unit typed as RateLimitTimeUnit for type fidelity
  • python-reviewer: RateLimitTimeUnit import moved to module level
  • code-reviewer: AsyncGenerator annotation fixed
  • comment-analyzer: bootstrap-only settings enumerated explicitly in docstrings/comments

Register 10 API settings (server, CORS, rate limiting, auth) in the
settings registry so they appear in the Settings UI. Four are runtime-
editable via ConfigResolver.get_api_config(); six are bootstrap-only
(restart_required) since Litestar bakes middleware/CORS at construction.

Document the 2-phase init pattern in app.py: Phase 1 reads RootConfig
at construction, Phase 2 uses SettingsService after persistence connects.
Pre-reviewed by 13 agents, 6 findings addressed:
- CLAUDE.md + operations.md: update namespace count 8→9, add api
- test_resolver: use RateLimitTimeUnit in fake model + assertions
- test_resolver: move RateLimitTimeUnit import to module level
- test_settings_integration: fix AsyncGenerator annotation
- resolver.py + app.py: enumerate all 6 bootstrap-only setting keys
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 17, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 17, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added an API settings namespace with runtime-editable options for server, CORS, rate limiting, and authentication.
    • API settings can be persisted and override defaults at runtime.
  • Documentation

    • Updated design and operations docs to reflect the expanded settings structure and API namespace.
  • Tests

    • Added integration and unit tests validating API settings persistence, resolution, and error behaviors.

Walkthrough

Adds a new "api" settings namespace with definitions and enum, implements ConfigResolver.get_api_config(), extends create_app() to accept injectable services for a two‑phase init, updates docs, and adds unit and integration tests exercising API settings resolution and overrides.

Changes

Cohort / File(s) Summary
Settings enums & exports
src/synthorg/settings/enums.py, src/synthorg/settings/definitions/__init__.py
Added API enum member and exported the api definitions module from the package.
API settings definitions
src/synthorg/settings/definitions/api.py
New module registering ~10 API setting definitions (server, CORS, rate limiting, authentication) into the central settings registry with metadata (yaml_path, restart_required, ranges, enums).
Settings resolver
src/synthorg/settings/resolver.py
Added async get_api_config() to compose an ApiConfig by resolving runtime-editable API settings (parallel tasks, enum resolution, error handling) and merging them onto YAML base config.
App creation / wiring
src/synthorg/api/app.py
Extended create_app() signature to accept many optional injectable services (persistence, message_bus, cost_tracker, approval_store, auth_service, task_engine, coordinator, agent_registry, meeting_orchestrator, meeting_scheduler, performance_tracker, settings_service) and documented intended two‑phase initialization.
Documentation
CLAUDE.md, docs/design/operations.md
Updated design docs to reflect the new API namespace and increased settings namespace count (8 → 9) and adjusted Web UI Features mention.
Integration tests
tests/integration/settings/test_settings_integration.py
Added end-to-end integration tests using SQLite backend, SettingsService, and ConfigResolver to verify DB override persistence and resolver round-trip.
Unit tests
tests/unit/settings/test_resolver.py
Added comprehensive unit tests for API config composition, default/override precedence, enum resolution, partial-failure behavior, and propagation of errors from settings layer.

Sequence Diagram(s)

sequenceDiagram
  participant App as App (construct)
  participant Litestar as Litestar Server
  participant Persistence as Persistence Backend
  participant Settings as SettingsService
  participant Resolver as ConfigResolver

  App->>Litestar: create_app(phase1 minimal config, injectable services)
  Note right of Litestar: Server starts with bootstrap-only config
  Litestar->>Persistence: connect (on_startup)
  Persistence-->>Litestar: connected
  Litestar->>Settings: initialize SettingsService (uses Persistence)
  Settings-->>Litestar: ready
  Litestar->>Resolver: resolve API config via get_api_config()
  Resolver->>Settings: parallel get(setting) tasks
  Settings-->>Resolver: resolved values / exceptions
  Resolver-->>Litestar: composed ApiConfig
  Litestar->>App: apply runtime-config (CORS, auth, rate-limit) — Phase 2
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 79.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(settings): register API config in SettingsService with 2-phase init' clearly and specifically summarizes the main changes: registering API settings and implementing 2-phase initialization.
Description check ✅ Passed The description comprehensively explains the changeset including the 10 registered settings, 2-phase init pattern, bootstrap-only constraints, and test coverage.
Linked Issues check ✅ Passed All coding requirements from #504 are met: 10 settings registered [#504], 2-phase init documented in app.py [#504], get_api_config() method implements parallel resolution [#504], bootstrap-only settings marked with restart_required [#504].
Out of Scope Changes check ✅ Passed All changes directly support the stated objectives: settings definitions, ConfigResolver enhancements, documentation updates, and tests for API configuration; no extraneous changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/2phase-app-init
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/2phase-app-init
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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 API's configuration management by integrating API-specific settings into the existing robust settings system. It introduces a dedicated API settings namespace, allowing for dynamic adjustments of certain parameters while clearly defining and documenting the lifecycle of immutable bootstrap settings. This change improves configurability, maintainability, and clarity regarding how API settings are managed and applied within the application.

Highlights

  • API Settings Registration: Ten API-related settings covering server, CORS, rate limiting, and authentication have been registered in the settings registry, making them discoverable in the Settings UI and resolvable through the standard priority chain (DB > env > YAML > code-default).
  • ConfigResolver Enhancement: A new get_api_config() method was added to ConfigResolver to perform composed reads of API settings. This method efficiently resolves four runtime-editable settings in parallel using asyncio.TaskGroup and merges them with the base configuration.
  • Two-Phase Initialization Documentation: The app.py file now includes documentation for a 2-phase initialization pattern. This clarifies that certain settings (bootstrap-only) are immutable after application construction, while others (runtime-editable) can be resolved via SettingsService post-startup.
  • Setting Categorization: The 10 new API settings are explicitly categorized: 4 are runtime-editable, and 6 are marked as bootstrap-only with restart_required=True due to their impact on Litestar's core configuration at construction time.
Changelog
  • CLAUDE.md
    • Updated the settings directory description to reflect the addition of the 'api' namespace and incremented the namespace count from 8 to 9.
  • docs/design/operations.md
    • Updated the description of the settings module to include the new 'api' namespace and incremented the namespace count from 8 to 9.
  • src/synthorg/api/app.py
    • Added detailed comments explaining the 2-phase initialization pattern for Litestar applications, distinguishing between bootstrap-only and runtime-editable settings.
  • src/synthorg/settings/definitions/init.py
    • Imported and exposed the new api settings definition module to the settings definitions package.
  • src/synthorg/settings/definitions/api.py
    • Added a new module defining 10 API-related settings, including server host/port, API prefix, CORS origins, rate limiting parameters, JWT expiry, and minimum password length, with appropriate restart_required flags.
  • src/synthorg/settings/enums.py
    • Added the API member to the SettingNamespace enumeration.
  • src/synthorg/settings/resolver.py
    • Imported ApiConfig for type hinting and implemented the get_api_config method to resolve API settings using asyncio.TaskGroup for parallel fetching of runtime-editable values.
  • tests/integration/settings/test_settings_integration.py
    • Added a new integration test file to verify the end-to-end flow of API settings, including database overrides and default value resolution through SettingsService and ConfigResolver.
  • tests/unit/settings/test_resolver.py
    • Imported RateLimitTimeUnit, introduced fake API configuration models (_FakeAuthConfig, _FakeRateLimitConfig, _FakeCorsConfig, _FakeServerConfig, _FakeApiConfig) into _FakeRootConfig, and added comprehensive unit tests for the get_api_config method, covering defaults, database overrides, preservation of bootstrap-only fields, and error propagation.
Activity
  • The pull request underwent a thorough pre-review process by 13 agents.
  • Six findings identified during the review were addressed, including documentation consistency updates in CLAUDE.md and operations.md.
  • Type fidelity was improved by correctly typing _FakeRateLimitConfig.time_unit as RateLimitTimeUnit.
  • A Python reviewer's suggestion to move RateLimitTimeUnit import to the module level was implemented.
  • An AsyncGenerator annotation was fixed.
  • Docstrings and comments were updated to explicitly enumerate bootstrap-only settings.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 17, 2026 10:00 — with GitHub Actions Inactive
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively integrates API configuration into the settings service, introducing a new 'api' namespace and a get_api_config method in the ConfigResolver. The implementation correctly uses a two-phase initialization pattern, distinguishing between bootstrap and runtime settings, and leverages asyncio.TaskGroup for efficient parallel fetching of runtime-editable values. The changes are well-tested with both unit and integration tests. My main feedback concerns a few missing yaml_path properties in the new setting definitions, which prevents them from being configured via the primary YAML file.

Comment on lines +62 to +70
SettingDefinition(
namespace=SettingNamespace.API,
key="cors_allowed_origins",
type=SettingType.JSON,
default='["http://localhost:5173"]',
description="Origins permitted to make cross-origin requests",
group="CORS",
restart_required=True,
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This setting is missing the yaml_path property. Without it, this setting cannot be configured from the main YAML file and will fall back to an environment variable or the hardcoded default. This seems like an oversight for an important security configuration.

    SettingDefinition(
        namespace=SettingNamespace.API,
        key="cors_allowed_origins",
        type=SettingType.JSON,
        default='["http://localhost:5173"]',
        description="Origins permitted to make cross-origin requests",
        group="CORS",
        restart_required=True,
        yaml_path="api.cors.allowed_origins",
    )

Comment on lines +102 to +111
SettingDefinition(
namespace=SettingNamespace.API,
key="rate_limit_exclude_paths",
type=SettingType.JSON,
default='["/api/v1/health"]',
description="Paths excluded from rate limiting",
group="Rate Limiting",
level=SettingLevel.ADVANCED,
restart_required=True,
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This setting is missing the yaml_path property. Without it, this setting cannot be configured from the main YAML file and will fall back to an environment variable or the hardcoded default.

    SettingDefinition(
        namespace=SettingNamespace.API,
        key="rate_limit_exclude_paths",
        type=SettingType.JSON,
        default='["/api/v1/health"]',
        description="Paths excluded from rate limiting",
        group="Rate Limiting",
        level=SettingLevel.ADVANCED,
        restart_required=True,
        yaml_path="api.rate_limit.exclude_paths",
    )

Comment on lines +145 to +154
SettingDefinition(
namespace=SettingNamespace.API,
key="auth_exclude_paths",
type=SettingType.JSON,
default="[]",
description="Paths excluded from authentication middleware",
group="Authentication",
level=SettingLevel.ADVANCED,
restart_required=True,
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This setting is missing the yaml_path property. Without it, this setting cannot be configured from the main YAML file and will fall back to an environment variable or the hardcoded default. This seems like an oversight for an important security configuration.

    SettingDefinition(
        namespace=SettingNamespace.API,
        key="auth_exclude_paths",
        type=SettingType.JSON,
        default="[]",
        description="Paths excluded from authentication middleware",
        group="Authentication",
        level=SettingLevel.ADVANCED,
        restart_required=True,
        yaml_path="api.auth.exclude_paths",
    )

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/synthorg/settings/definitions/api.py`:
- Around line 144-155: The SettingDefinition registered via _r.register for
namespace SettingNamespace.API with key "auth_exclude_paths" is missing its
yaml_path and should be added so YAML-based config resolution works; update the
SettingDefinition instantiation (the call that sets type=SettingType.JSON,
default="[]", description="Paths excluded from authentication middleware",
group="Authentication", level=SettingLevel.ADVANCED, restart_required=True) to
include yaml_path="api.auth.exclude_paths".
- Around line 61-71: The SettingDefinition for the setting key
"cors_allowed_origins" is missing a yaml_path, so YAML config is skipped during
resolution; update the SettingDefinition (namespace SettingNamespace.API, key
"cors_allowed_origins") to include yaml_path="api.cors.allowed_origins" so the
resolver will read base YAML under api.cors.allowed_origins before falling back
to env or default, keeping the rest of the SettingDefinition (type, default,
description, group, restart_required) unchanged.
- Around line 101-112: The SettingDefinition for the API setting with key
"rate_limit_exclude_paths" is missing a yaml_path; update the SettingDefinition
(where SettingNamespace.API and key="rate_limit_exclude_paths" are used) to
include yaml_path="api.rate_limit.exclude_paths" so YAML-based configuration
matches other rate limit settings and remains consistent with the existing rate
limit group/namespace.

In `@tests/integration/settings/test_settings_integration.py`:
- Around line 24-102: Update the docstrings in this module to Google-style for
all public fixtures, the TestApiSettingsIntegration class, and its test methods:
replace the current summary-only triple-quoted strings for db_path, backend,
config, settings_service, resolver, TestApiSettingsIntegration,
test_db_override_flows_through_resolver, and test_defaults_without_db_overrides
with Google-style docstrings that include a short summary and parameter/returns
sections where applicable (e.g., Args: tmp_path: Path for db_path; Returns: str
for db_path; for async fixtures note Returns and yield behavior; for the class
include a short description and omit Args; for test methods include Args only if
they accept fixtures), ensuring the formatting matches Google-style (summary
line, blank line, sections like Args/Returns) so ruff D rules pass.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5a3c9c0b-e019-464f-b592-07a3953a8e18

📥 Commits

Reviewing files that changed from the base of the PR and between 32f553d and 1e69dc2.

📒 Files selected for processing (10)
  • CLAUDE.md
  • docs/design/operations.md
  • src/synthorg/api/app.py
  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/settings/definitions/api.py
  • src/synthorg/settings/enums.py
  • src/synthorg/settings/resolver.py
  • tests/integration/settings/__init__.py
  • tests/integration/settings/test_settings_integration.py
  • tests/unit/settings/test_resolver.py
📜 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). (6)
  • GitHub Check: Deploy Preview
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Backend
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Web
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Do not use from __future__ import annotations — Python 3.14 has PEP 649 native lazy annotations.
Use except A, B: syntax (no parentheses) — PEP 758 except syntax enforced by ruff on Python 3.14.
Add type hints to all public functions; enforce mypy strict mode compliance.
Use Google-style docstrings on all public classes and functions; enforce via ruff D rules.
Keep functions under 50 lines and files under 800 lines.
Handle errors explicitly and never silently swallow exceptions.
Validate at system boundaries (user input, external APIs, config files).
Set line length to 88 characters (ruff enforces).
Use uv for Python dependency management. Install with uv sync, run tests with -n auto for parallelism, lint with ruff check, format with ruff format, type-check with mypy.
Scripts in scripts/ have relaxed ruff rules: print and deferred imports are allowed.

Files:

  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/settings/enums.py
  • src/synthorg/settings/resolver.py
  • src/synthorg/api/app.py
  • tests/integration/settings/test_settings_integration.py
  • tests/unit/settings/test_resolver.py
  • src/synthorg/settings/definitions/api.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Create new objects instead of mutating existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping. For dict/list fields in frozen Pydantic models, rely on frozen=True and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization).
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (with model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Adopt Pydantic v2 conventions: use @computed_field for derived values instead of storing redundant fields; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Every module with business logic must have: from synthorg.observability import get_logger then logger = get_logger(__name__). Never use import logging, logging.getLogger(), or print() in application code.
Always use logger as the variable name (not _logger, not log). Use event name constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Always use structured kwargs for logging: logger.info(EVENT, key=value) — never use string interpolation like logger.info('msg %s', val).
Log all error paths at WARNING or ERROR with context before raising. Log all state transitions at INFO. Log object creation, internal flow, and entry/exit of key functions at DEBUG.
Do NOT use import logging or logging.getLogger(). Pure data models, ...

Files:

  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/settings/enums.py
  • src/synthorg/settings/resolver.py
  • src/synthorg/api/app.py
  • src/synthorg/settings/definitions/api.py
**/*.{md,py,ts,tsx,vue,go,yml,yaml,json,toml}

📄 CodeRabbit inference engine (CLAUDE.md)

Use conventional commits: <type>: <description> where types are feat, fix, refactor, docs, test, chore, perf, ci. Commits must be GPG/SSH signed on main branch.

Files:

  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/settings/enums.py
  • CLAUDE.md
  • src/synthorg/settings/resolver.py
  • docs/design/operations.md
  • src/synthorg/api/app.py
  • tests/integration/settings/test_settings_integration.py
  • tests/unit/settings/test_resolver.py
  • src/synthorg/settings/definitions/api.py
src/synthorg/settings/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Runtime-editable settings persistence with DB > env > YAML > code defaults priority. 8 namespaces via definitions/. Fernet encryption for sensitive values. ConfigResolver for typed composed reads. Change notifications via message bus. Validation and registry.

Files:

  • src/synthorg/settings/definitions/__init__.py
  • src/synthorg/settings/enums.py
  • src/synthorg/settings/resolver.py
  • src/synthorg/settings/definitions/api.py
**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Read relevant docs/design/ page before implementing features or planning issues. Design spec is the starting point for architecture, data models, and behavior.

Files:

  • CLAUDE.md
  • docs/design/operations.md
docs/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Documentation in Markdown built with Zensical. Design spec: 7 pages under docs/design/ (index, agents, organization, communication, engine, memory, operations). Architecture: docs/architecture/. Roadmap: docs/roadmap/. Security: docs/security.md. Licensing: docs/licensing.md. Reference: docs/reference/. REST API: docs/rest-api.md with generated Scalar UI. Library reference: docs/api/ (auto-generated from docstrings via mkdocstrings + Griffe).

Files:

  • docs/design/operations.md
src/synthorg/api/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Litestar REST + WebSocket API with JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. Use RFC 9457 structured errors: ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation.

Files:

  • src/synthorg/api/app.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Mark tests with @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, or @pytest.mark.slow. Maintain 80% minimum code coverage.
Use asyncio_mode = 'auto' for async tests — no manual @pytest.mark.asyncio needed. Set test timeout to 30 seconds per test. Always include -n auto for parallelism via pytest-xdist.
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned test code, docstrings, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, test-provider, test-small-001, etc. Vendor names may only appear in Operations design page, .claude/ files, or third-party import paths.
Use Hypothesis for property-based testing with @given + @settings. Profiles: ci (200 examples, default) and dev (1000 examples) via HYPOTHESIS_PROFILE env var. Never skip, dismiss, or ignore flaky tests — fix them fully. Mock time.monotonic() and asyncio.sleep() for deterministic timing-sensitive tests instead of widening margins.

Files:

  • tests/integration/settings/test_settings_integration.py
  • tests/unit/settings/test_resolver.py
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T07:15:40.520Z
Learning: Applies to src/synthorg/settings/**/*.py : Runtime-editable settings persistence with DB > env > YAML > code defaults priority. 8 namespaces via `definitions/`. Fernet encryption for sensitive values. ConfigResolver for typed composed reads. Change notifications via message bus. Validation and registry.
📚 Learning: 2026-03-17T07:15:40.520Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T07:15:40.520Z
Learning: Applies to src/synthorg/settings/**/*.py : Runtime-editable settings persistence with DB > env > YAML > code defaults priority. 8 namespaces via `definitions/`. Fernet encryption for sensitive values. ConfigResolver for typed composed reads. Change notifications via message bus. Validation and registry.

Applied to files:

  • src/synthorg/settings/definitions/__init__.py
  • CLAUDE.md
  • src/synthorg/settings/resolver.py
  • docs/design/operations.md
  • tests/integration/settings/test_settings_integration.py
  • tests/unit/settings/test_resolver.py
  • src/synthorg/settings/definitions/api.py
📚 Learning: 2026-03-17T07:15:40.520Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T07:15:40.520Z
Learning: Applies to src/synthorg/persistence/**/*.py : Pluggable PersistenceBackend protocol with SQLite initial backend. SettingsRepository for namespaced settings CRUD. See Memory & Persistence design page.

Applied to files:

  • CLAUDE.md
  • tests/integration/settings/test_settings_integration.py
  • src/synthorg/settings/definitions/api.py
📚 Learning: 2026-03-17T07:15:40.520Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T07:15:40.520Z
Learning: Applies to src/synthorg/security/**/*.py : SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies: disabled/weighted/per-category/milestone), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume).

Applied to files:

  • CLAUDE.md
  • docs/design/operations.md
📚 Learning: 2026-03-17T07:15:40.520Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T07:15:40.520Z
Learning: Applies to docs/**/*.md : Documentation in Markdown built with Zensical. Design spec: 7 pages under `docs/design/` (index, agents, organization, communication, engine, memory, operations). Architecture: `docs/architecture/`. Roadmap: `docs/roadmap/`. Security: `docs/security.md`. Licensing: `docs/licensing.md`. Reference: `docs/reference/`. REST API: `docs/rest-api.md` with generated Scalar UI. Library reference: `docs/api/` (auto-generated from docstrings via mkdocstrings + Griffe).

Applied to files:

  • CLAUDE.md
  • docs/design/operations.md
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to src/synthorg/providers/**/*.py : RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig.

Applied to files:

  • src/synthorg/settings/resolver.py
  • tests/unit/settings/test_resolver.py
📚 Learning: 2026-03-17T07:15:40.520Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T07:15:40.520Z
Learning: Applies to src/synthorg/engine/**/*.py : Agent orchestration with TaskEngine (centralized single-writer task state), task lifecycle, recovery, shutdown, workspace isolation. Coordination: multi-agent pipeline via TopologyDispatcher protocol (4 dispatchers: SAS/centralized/decentralized/context-dependent), wave execution, workspace lifecycle, CoordinationSectionConfig bridge, build_coordinator factory. Approval gate: escalation detection, context parking/resume, EscalationInfo/ResumePayload. Stagnation detection: StagnationDetector protocol, ToolRepetitionDetector, dual-signal analysis, corrective prompt injection. Checkpoint recovery: per-turn persistence, heartbeat detection, CheckpointRecoveryStrategy. AgentRuntimeState: lightweight per-agent execution status for dashboard + recovery.

Applied to files:

  • docs/design/operations.md
📚 Learning: 2026-03-17T07:15:40.520Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T07:15:40.520Z
Learning: Applies to src/synthorg/hr/**/*.py : HR engine: hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration sampling, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping).

Applied to files:

  • docs/design/operations.md
📚 Learning: 2026-03-17T07:15:40.520Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T07:15:40.520Z
Learning: Applies to src/synthorg/api/**/*.py : Litestar REST + WebSocket API with JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. Use RFC 9457 structured errors: `ErrorCategory`, `ErrorCode`, `ErrorDetail`, `ProblemDetail`, `CATEGORY_TITLES`, `category_title`, `category_type_uri`, content negotiation.

Applied to files:

  • src/synthorg/settings/definitions/api.py
🧬 Code graph analysis (3)
tests/integration/settings/test_settings_integration.py (3)
src/synthorg/settings/registry.py (1)
  • get_registry (111-113)
src/synthorg/settings/resolver.py (1)
  • ConfigResolver (33-463)
src/synthorg/settings/service.py (1)
  • SettingsService (152-637)
tests/unit/settings/test_resolver.py (5)
src/synthorg/settings/models.py (1)
  • SettingValue (212-232)
src/synthorg/settings/service.py (1)
  • get (189-273)
src/synthorg/settings/errors.py (1)
  • SettingNotFoundError (8-9)
src/synthorg/settings/enums.py (1)
  • SettingNamespace (6-21)
src/synthorg/api/state.py (1)
  • settings_service (237-239)
src/synthorg/settings/definitions/api.py (3)
src/synthorg/settings/enums.py (3)
  • SettingLevel (39-47)
  • SettingNamespace (6-21)
  • SettingType (24-36)
src/synthorg/settings/models.py (1)
  • SettingDefinition (34-140)
src/synthorg/settings/registry.py (2)
  • get_registry (111-113)
  • register (30-50)
🔇 Additional comments (11)
docs/design/operations.md (1)

1044-1044: LGTM!

The documentation correctly reflects the addition of the API namespace, updating the namespace count from 8 to 9 and listing all namespaces in order. The description of ConfigResolver using asyncio.TaskGroup for parallel resolution aligns with the implementation in resolver.py.

src/synthorg/settings/enums.py (1)

13-13: LGTM!

The API enum member is correctly added to SettingNamespace. The alphabetical ordering (API first) is appropriate and consistent with the enum's purpose of categorizing runtime-editable settings by subsystem.

src/synthorg/settings/definitions/__init__.py (1)

7-29: LGTM!

The api module is correctly imported and exported, following the established pattern for namespace definition modules. Alphabetical ordering is maintained in both the import block and __all__ list.

CLAUDE.md (1)

131-132: LGTM!

The CLAUDE.md documentation correctly reflects the expansion from 8 to 9 namespaces and includes api as the first namespace in the definitions list, matching the enum ordering and actual implementation.

src/synthorg/settings/resolver.py (1)

332-395: LGTM!

The get_api_config() implementation is well-structured and follows the established patterns from get_budget_config() and get_coordination_config():

  • Uses asyncio.TaskGroup for parallel resolution of the 4 runtime-editable settings
  • Correctly handles ExceptionGroup by logging and re-raising the first exception
  • Preserves bootstrap-only fields (CORS, server, exclude_paths, api_prefix) via model_copy
  • Docstring clearly documents which settings are resolved vs. preserved
  • The deferred import of RateLimitTimeUnit avoids circular imports
tests/unit/settings/test_resolver.py (2)

74-119: LGTM!

The fake config models for API testing are well-structured and mirror the actual ApiConfig hierarchy. The _FakeRootConfig is correctly extended with the api field, enabling composed-read testing for the new get_api_config() method.


638-750: LGTM!

The TestGetApiConfig test class provides comprehensive coverage:

  • Defaults: Verifies all 4 runtime-editable settings resolve correctly
  • Overrides: Confirms DB values take precedence over defaults
  • Bootstrap preservation: Validates that CORS, server, exclude_paths, and api_prefix are preserved from YAML config
  • Error propagation: Tests both SettingNotFoundError and ValueError are properly unwrapped from ExceptionGroup
  • Enum resolution: Confirms RateLimitTimeUnit enum members resolve correctly

The test structure follows the established patterns from TestGetBudgetConfig and TestGetCoordinationConfig.

src/synthorg/api/app.py (2)

475-490: LGTM!

The 2-phase initialization comment block is a valuable architectural documentation addition. It clearly explains:

  1. Phase 1 (construct): Bootstrap-only settings read from RootConfig at construction time (immutable after Litestar builds)
  2. Phase 2 (on_startup): Runtime-editable settings resolved through SettingsService after persistence connects

This distinction is critical for understanding why certain API settings require restart while others can be changed at runtime.


492-507: LGTM!

The settings_service parameter is correctly integrated into create_app:

  • Added to signature with None default for backward compatibility
  • Included in the None-check warning (Line 567) alongside other required services
  • Documented in the docstring
  • Passed through to AppState construction (Line 602)
tests/integration/settings/test_settings_integration.py (2)

24-68: Good end-to-end fixture composition.

This setup correctly exercises real SQLite persistence with SettingsService and ConfigResolver, which is the right integration depth for this feature.


76-102: Strong coverage of override vs default resolution paths.

These tests validate both DB override flow-through and fallback defaults for API settings in a focused way.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.15%. Comparing base (32f553d) to head (d7fd554).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #518      +/-   ##
==========================================
+ Coverage   93.12%   93.15%   +0.03%     
==========================================
  Files         504      505       +1     
  Lines       24452    24480      +28     
  Branches     2333     2333              
==========================================
+ Hits        22770    22805      +35     
+ Misses       1335     1328       -7     
  Partials      347      347              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Settings definitions (api.py):
- Add yaml_path for cors_allowed_origins, rate_limit_exclude_paths,
  auth_exclude_paths (Gemini, CodeRabbit, code-reviewer)
- Add max_value=10000 on rate_limit_max_requests (security-reviewer)
- Raise min_password_length min_value 8→12 (security-reviewer)
- Reduce jwt_expiry_minutes max_value 43200→10080 (security-reviewer)
- Annotate section comments for mixed bootstrap/runtime groups

Documentation:
- Clarify resolver docstring: bootstrap settings "require restart"
- Add rate-limit middleware note to 2-phase init comment (app.py)

Tests:
- Add partial failure test (1 failing + 3 succeeding settings)
- Add invalid enum value test ("weekly" → ValueError)
- Parametrize enum resolution over all RateLimitTimeUnit members
- Fix _FakeAuthConfig.jwt_algorithm to Literal type
- Fix _FakeApiConfig.api_prefix to NotBlankStr type
- Fix test_preserves_unregistered_fields docstring (add api_prefix)
- Update integration test docstrings to Google style
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/synthorg/api/app.py`:
- Around line 484-489: The AuthService is being constructed in
_init_persistence() using static app_state.config.api.auth so runtime-editable
settings like jwt_expiry_minutes and min_password_length from
SettingsService/ConfigResolver never take effect; modify the startup flow so
AuthService is created (or re-initialized) using the resolved runtime config
from ConfigResolver.get_api_config() after persistence/migrations complete
(Phase 2), or add a post-startup refresh step that calls
ConfigResolver.get_api_config() and updates/replaces the AuthService instance
with values from that result; ensure references to app_state.config.api.auth are
replaced or synchronized with the ConfigResolver.get_api_config() output so
AuthService uses the active runtime settings.

In `@src/synthorg/settings/definitions/api.py`:
- Around line 76-100: The two SettingDefinition entries for keys
"rate_limit_max_requests" and "rate_limit_time_unit" must be marked restart-only
because Litestar captures max_requests/time_unit at app construction and won't
re-read ConfigResolver; update the SettingDefinition for namespace
SettingNamespace.API with key "rate_limit_max_requests" and the one with key
"rate_limit_time_unit" to include restart_required=True so changes require an
application restart (i.e., add restart_required=True to those SettingDefinition
constructors).
- Around line 45-56: The registered API settings (SettingDefinition entries
under SettingNamespace.API such as "api_prefix", "cors_allowed_origins",
"rate_limit_exclude_paths", and "auth_exclude_paths") are never read during
startup because create_app() still constructs router/CORS/auth/rate-limit from
RootConfig.api; update the startup flow to load these values from the
SettingsService before building the app. Concretely: add a pre-construction step
in create_app() (or its caller) to query SettingsService for the keys registered
in api settings and merge/override RootConfig.api (or pass a resolved config
object) so that functions that build the router, CORS config, and middleware
consume the resolved values; ensure the code path that builds routes in
src/synthorg/api/app.py uses the resolved settings rather than RootConfig.api to
honor DB changes to api_prefix, cors_allowed_origins, rate_limit_exclude_paths,
and auth_exclude_paths.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c92d7151-60c4-4a77-a1a3-970c760793aa

📥 Commits

Reviewing files that changed from the base of the PR and between 1e69dc2 and d7fd554.

📒 Files selected for processing (5)
  • src/synthorg/api/app.py
  • src/synthorg/settings/definitions/api.py
  • src/synthorg/settings/resolver.py
  • tests/integration/settings/test_settings_integration.py
  • tests/unit/settings/test_resolver.py
📜 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). (6)
  • GitHub Check: Deploy Preview
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Do not use from __future__ import annotations — Python 3.14 has PEP 649 native lazy annotations.
Use except A, B: syntax (no parentheses) — PEP 758 except syntax enforced by ruff on Python 3.14.
Add type hints to all public functions; enforce mypy strict mode compliance.
Use Google-style docstrings on all public classes and functions; enforce via ruff D rules.
Keep functions under 50 lines and files under 800 lines.
Handle errors explicitly and never silently swallow exceptions.
Validate at system boundaries (user input, external APIs, config files).
Set line length to 88 characters (ruff enforces).
Use uv for Python dependency management. Install with uv sync, run tests with -n auto for parallelism, lint with ruff check, format with ruff format, type-check with mypy.
Scripts in scripts/ have relaxed ruff rules: print and deferred imports are allowed.

Files:

  • src/synthorg/settings/definitions/api.py
  • tests/unit/settings/test_resolver.py
  • src/synthorg/settings/resolver.py
  • src/synthorg/api/app.py
  • tests/integration/settings/test_settings_integration.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Create new objects instead of mutating existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping. For dict/list fields in frozen Pydantic models, rely on frozen=True and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization).
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (with model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Adopt Pydantic v2 conventions: use @computed_field for derived values instead of storing redundant fields; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Every module with business logic must have: from synthorg.observability import get_logger then logger = get_logger(__name__). Never use import logging, logging.getLogger(), or print() in application code.
Always use logger as the variable name (not _logger, not log). Use event name constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Always use structured kwargs for logging: logger.info(EVENT, key=value) — never use string interpolation like logger.info('msg %s', val).
Log all error paths at WARNING or ERROR with context before raising. Log all state transitions at INFO. Log object creation, internal flow, and entry/exit of key functions at DEBUG.
Do NOT use import logging or logging.getLogger(). Pure data models, ...

Files:

  • src/synthorg/settings/definitions/api.py
  • src/synthorg/settings/resolver.py
  • src/synthorg/api/app.py
**/*.{md,py,ts,tsx,vue,go,yml,yaml,json,toml}

📄 CodeRabbit inference engine (CLAUDE.md)

Use conventional commits: <type>: <description> where types are feat, fix, refactor, docs, test, chore, perf, ci. Commits must be GPG/SSH signed on main branch.

Files:

  • src/synthorg/settings/definitions/api.py
  • tests/unit/settings/test_resolver.py
  • src/synthorg/settings/resolver.py
  • src/synthorg/api/app.py
  • tests/integration/settings/test_settings_integration.py
src/synthorg/settings/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Runtime-editable settings persistence with DB > env > YAML > code defaults priority. 8 namespaces via definitions/. Fernet encryption for sensitive values. ConfigResolver for typed composed reads. Change notifications via message bus. Validation and registry.

Files:

  • src/synthorg/settings/definitions/api.py
  • src/synthorg/settings/resolver.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Mark tests with @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, or @pytest.mark.slow. Maintain 80% minimum code coverage.
Use asyncio_mode = 'auto' for async tests — no manual @pytest.mark.asyncio needed. Set test timeout to 30 seconds per test. Always include -n auto for parallelism via pytest-xdist.
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned test code, docstrings, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, test-provider, test-small-001, etc. Vendor names may only appear in Operations design page, .claude/ files, or third-party import paths.
Use Hypothesis for property-based testing with @given + @settings. Profiles: ci (200 examples, default) and dev (1000 examples) via HYPOTHESIS_PROFILE env var. Never skip, dismiss, or ignore flaky tests — fix them fully. Mock time.monotonic() and asyncio.sleep() for deterministic timing-sensitive tests instead of widening margins.

Files:

  • tests/unit/settings/test_resolver.py
  • tests/integration/settings/test_settings_integration.py
src/synthorg/api/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Litestar REST + WebSocket API with JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. Use RFC 9457 structured errors: ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation.

Files:

  • src/synthorg/api/app.py
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T07:15:40.520Z
Learning: Applies to src/synthorg/settings/**/*.py : Runtime-editable settings persistence with DB > env > YAML > code defaults priority. 8 namespaces via `definitions/`. Fernet encryption for sensitive values. ConfigResolver for typed composed reads. Change notifications via message bus. Validation and registry.
📚 Learning: 2026-03-17T07:15:40.520Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T07:15:40.520Z
Learning: Applies to src/synthorg/settings/**/*.py : Runtime-editable settings persistence with DB > env > YAML > code defaults priority. 8 namespaces via `definitions/`. Fernet encryption for sensitive values. ConfigResolver for typed composed reads. Change notifications via message bus. Validation and registry.

Applied to files:

  • src/synthorg/settings/definitions/api.py
  • tests/unit/settings/test_resolver.py
  • src/synthorg/settings/resolver.py
  • tests/integration/settings/test_settings_integration.py
📚 Learning: 2026-03-17T07:15:40.520Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T07:15:40.520Z
Learning: Applies to src/synthorg/persistence/**/*.py : Pluggable PersistenceBackend protocol with SQLite initial backend. SettingsRepository for namespaced settings CRUD. See Memory & Persistence design page.

Applied to files:

  • src/synthorg/settings/definitions/api.py
  • tests/integration/settings/test_settings_integration.py
📚 Learning: 2026-03-17T07:15:40.520Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T07:15:40.520Z
Learning: Applies to src/synthorg/api/**/*.py : Litestar REST + WebSocket API with JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. Use RFC 9457 structured errors: `ErrorCategory`, `ErrorCode`, `ErrorDetail`, `ProblemDetail`, `CATEGORY_TITLES`, `category_title`, `category_type_uri`, content negotiation.

Applied to files:

  • src/synthorg/settings/definitions/api.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to src/synthorg/providers/**/*.py : RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig.

Applied to files:

  • src/synthorg/settings/resolver.py
📚 Learning: 2026-03-17T07:15:40.520Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T07:15:40.520Z
Learning: Applies to **/*.py : Use Google-style docstrings on all public classes and functions; enforce via ruff D rules.

Applied to files:

  • tests/integration/settings/test_settings_integration.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
Learning: Applies to **/*.py : Docstrings: Google style, required on public classes and functions (enforced by ruff D rules).

Applied to files:

  • tests/integration/settings/test_settings_integration.py
🧬 Code graph analysis (3)
src/synthorg/settings/definitions/api.py (3)
src/synthorg/settings/enums.py (3)
  • SettingLevel (39-47)
  • SettingNamespace (6-21)
  • SettingType (24-36)
src/synthorg/settings/models.py (1)
  • SettingDefinition (34-140)
src/synthorg/settings/registry.py (2)
  • get_registry (111-113)
  • register (30-50)
tests/unit/settings/test_resolver.py (4)
src/synthorg/settings/models.py (1)
  • SettingValue (212-232)
src/synthorg/settings/service.py (1)
  • get (189-273)
src/synthorg/settings/errors.py (1)
  • SettingNotFoundError (8-9)
src/synthorg/settings/resolver.py (1)
  • ConfigResolver (33-463)
tests/integration/settings/test_settings_integration.py (5)
src/synthorg/config/schema.py (1)
  • RootConfig (387-651)
src/synthorg/settings/registry.py (1)
  • get_registry (111-113)
tests/unit/settings/test_resolver.py (1)
  • resolver (138-142)
src/synthorg/settings/resolver.py (1)
  • ConfigResolver (33-463)
tests/unit/settings/test_service.py (1)
  • service (106-113)
🔇 Additional comments (3)
src/synthorg/settings/resolver.py (1)

360-395: Nice copy-on-write composition here.

The TaskGroup fan-out plus nested model_copy(update=...) calls keep the read parallel without mutating the frozen API config tree.

tests/unit/settings/test_resolver.py (1)

640-805: Good coverage for the new composed read.

These cases hit defaults, overrides, enum coercion, and partial TaskGroup failure behavior, which are the brittle edges in get_api_config().

tests/integration/settings/test_settings_integration.py (1)

75-114: Useful end-to-end coverage here.

Using real SQLite + SettingsService alongside the resolver closes the gap that AsyncMock-based unit tests can’t.

Comment on lines +484 to +489
# Phase 2 (on_startup): After persistence connects and migrations
# run, SettingsService + ConfigResolver become available. Runtime-
# editable settings (rate_limit_max_requests, rate_limit_time_unit,
# jwt_expiry_minutes, min_password_length) are resolved through
# ConfigResolver.get_api_config() by consumers that need current
# values post-startup.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

The auth settings called “runtime-editable” never reach the active AuthService.

This note says jwt_expiry_minutes and min_password_length resolve post-startup, but _init_persistence() still constructs AuthService directly from app_state.config.api.auth. With the current flow, pre-existing DB overrides for those fields are ignored unless the auth service is built from ConfigResolver.get_api_config() (or otherwise refreshed from settings after startup).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/api/app.py` around lines 484 - 489, The AuthService is being
constructed in _init_persistence() using static app_state.config.api.auth so
runtime-editable settings like jwt_expiry_minutes and min_password_length from
SettingsService/ConfigResolver never take effect; modify the startup flow so
AuthService is created (or re-initialized) using the resolved runtime config
from ConfigResolver.get_api_config() after persistence/migrations complete
(Phase 2), or add a post-startup refresh step that calls
ConfigResolver.get_api_config() and updates/replaces the AuthService instance
with values from that result; ensure references to app_state.config.api.auth are
replaced or synchronized with the ConfigResolver.get_api_config() output so
AuthService uses the active runtime settings.

Comment on lines +45 to +56
_r.register(
SettingDefinition(
namespace=SettingNamespace.API,
key="api_prefix",
type=SettingType.STRING,
default="/api/v1",
description="URL prefix for all API routes",
group="Server",
level=SettingLevel.ADVANCED,
restart_required=True,
yaml_path="api.api_prefix",
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

These bootstrap keys are registered in the settings DB chain, but the current startup path never reads them from settings.

create_app() still builds the router, CORS config, and auth/rate-limit middleware from RootConfig.api, and the new note in src/synthorg/api/app.py explicitly says api_prefix, cors_allowed_origins, rate_limit_exclude_paths, and auth_exclude_paths are not resolved through SettingsService. With the current flow, DB edits to these keys will show up in the Settings UI but still won’t affect the next app start unless you add a pre-construction bridge from the registry back into startup config.

Also applies to: 61-71, 103-114, 147-158

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/settings/definitions/api.py` around lines 45 - 56, The
registered API settings (SettingDefinition entries under SettingNamespace.API
such as "api_prefix", "cors_allowed_origins", "rate_limit_exclude_paths", and
"auth_exclude_paths") are never read during startup because create_app() still
constructs router/CORS/auth/rate-limit from RootConfig.api; update the startup
flow to load these values from the SettingsService before building the app.
Concretely: add a pre-construction step in create_app() (or its caller) to query
SettingsService for the keys registered in api settings and merge/override
RootConfig.api (or pass a resolved config object) so that functions that build
the router, CORS config, and middleware consume the resolved values; ensure the
code path that builds routes in src/synthorg/api/app.py uses the resolved
settings rather than RootConfig.api to honor DB changes to api_prefix,
cors_allowed_origins, rate_limit_exclude_paths, and auth_exclude_paths.

Comment on lines +76 to +100
_r.register(
SettingDefinition(
namespace=SettingNamespace.API,
key="rate_limit_max_requests",
type=SettingType.INTEGER,
default="100",
description="Maximum requests per time window",
group="Rate Limiting",
min_value=1,
max_value=10000,
yaml_path="api.rate_limit.max_requests",
)
)

_r.register(
SettingDefinition(
namespace=SettingNamespace.API,
key="rate_limit_time_unit",
type=SettingType.ENUM,
default="minute",
description="Rate limit time window",
group="Rate Limiting",
enum_values=("second", "minute", "hour", "day"),
yaml_path="api.rate_limit.time_unit",
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

These two settings are still restart-only in practice.

The new note in src/synthorg/api/app.py says Litestar captures max_requests and time_unit at construction and never rereads ConfigResolver.get_api_config(). So, even after the broader bootstrap-resolution gap is fixed, these definitions need restart_required=True unless the middleware is rebuilt on change.

🔧 Minimal metadata fix
 _r.register(
     SettingDefinition(
         namespace=SettingNamespace.API,
         key="rate_limit_max_requests",
         type=SettingType.INTEGER,
         default="100",
         description="Maximum requests per time window",
         group="Rate Limiting",
+        restart_required=True,
         min_value=1,
         max_value=10000,
         yaml_path="api.rate_limit.max_requests",
     )
 )
@@
 _r.register(
     SettingDefinition(
         namespace=SettingNamespace.API,
         key="rate_limit_time_unit",
         type=SettingType.ENUM,
         default="minute",
         description="Rate limit time window",
         group="Rate Limiting",
+        restart_required=True,
         enum_values=("second", "minute", "hour", "day"),
         yaml_path="api.rate_limit.time_unit",
     )
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/settings/definitions/api.py` around lines 76 - 100, The two
SettingDefinition entries for keys "rate_limit_max_requests" and
"rate_limit_time_unit" must be marked restart-only because Litestar captures
max_requests/time_unit at app construction and won't re-read ConfigResolver;
update the SettingDefinition for namespace SettingNamespace.API with key
"rate_limit_max_requests" and the one with key "rate_limit_time_unit" to include
restart_required=True so changes require an application restart (i.e., add
restart_required=True to those SettingDefinition constructors).

@Aureliolo Aureliolo merged commit 29f7481 into main Mar 17, 2026
33 checks passed
@Aureliolo Aureliolo deleted the feat/2phase-app-init branch March 17, 2026 11:33
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 17, 2026 11:33 — with GitHub Actions Inactive
Aureliolo added a commit that referenced this pull request Mar 17, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.3.1](v0.3.0...v0.3.1)
(2026-03-17)


### Features

* **api:** RFC 9457 Phase 2 — ProblemDetail and content negotiation
([#496](#496))
([30f7c49](30f7c49))
* **cli:** verify container image signatures and SLSA provenance on pull
([#492](#492))
([bef272d](bef272d)),
closes [#491](#491)
* **engine:** implement context budget management in execution loops
([#520](#520))
([181eb8a](181eb8a)),
closes [#416](#416)
* implement settings persistence layer (DB-backed config)
([#495](#495))
([4bd99f7](4bd99f7)),
closes [#450](#450)
* **memory:** implement dual-mode archival in memory consolidation
([#524](#524))
([4603c9e](4603c9e)),
closes [#418](#418)
* migrate config consumers to read through SettingsService
([#510](#510))
([32f553d](32f553d))
* **settings:** implement settings change subscriptions for service
hot-reload ([#526](#526))
([53f908e](53f908e)),
closes [#503](#503)
* **settings:** register API config in SettingsService with 2-phase init
([#518](#518))
([29f7481](29f7481))
* **tools:** add SSRF prevention for git clone URLs
([#505](#505))
([492dd0d](492dd0d))
* **tools:** wire RootConfig.git_clone to GitCloneTool instantiation
([#519](#519))
([b7d8172](b7d8172))


### Bug Fixes

* **api:** replace JWT query parameter with one-time ticket for
WebSocket auth
([#493](#493))
([22a25f6](22a25f6)),
closes [#343](#343)


### Documentation

* add uv cache lock contention handling to worktree skill
([#500](#500))
([bd85a8d](bd85a8d))
* document RFC 9457 dual response formats in OpenAPI schema
([#506](#506))
([8dd2524](8dd2524))


### Maintenance

* upgrade jsdom from 28 to 29
([#499](#499))
([1ea2249](1ea2249))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: refactor app-creation config reads to use SettingsService (2-phase init)

1 participant