refactor(api): move activities lifecycle-cap fallback to ApiBridgeConfig snapshot#1840
Conversation
…fig snapshot Closes #1797
…te_api_bridge_config, watched-key startup check, validation-on-mutate, snapshot pattern docs
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
There was a problem hiding this comment.
Code Review
This pull request implements the "Bridge-config snapshot pattern" for hot-reloadable application settings, specifically migrating the activities controller's lifecycle events cap to a centralized ApiBridgeConfig on AppState. The changes include a new ApiBridgeSettingsSubscriber for hot-reloading and documentation of the pattern. Review feedback highlights a syntax error in exception handling that may affect Python versions older than 3.13 and suggests making the settings subscriber type-aware to support non-integer configuration fields.
| except MemoryError, RecursionError: | ||
| raise |
There was a problem hiding this comment.
The except MemoryError, RecursionError: syntax (without parentheses) is only valid in Python 3.13+ (per PEP 758). If the project supports older Python versions, this will raise a SyntaxError. Additionally, the PR description states that a polish pass parenthesised these in newly-touched files, but this new function still uses the unparenthesised form.
except (MemoryError, RecursionError):
raise| ) | ||
| return | ||
| try: | ||
| value = await self._app_state.config_resolver.get_int(namespace, key) |
There was a problem hiding this comment.
The subscriber is hardcoded to use get_int, which will fail or produce incorrect results if a float or bool field from ApiBridgeConfig (such as ws_auth_timeout_seconds) is added to _WATCHED. To make this pattern truly reusable as documented, the resolution should be type-aware based on the field's annotation in the model.
| except MemoryError, RecursionError: | ||
| raise |
There was a problem hiding this comment.
Similar to the issue in lifecycle_helpers.py, this unparenthesised except clause contradicts the PR description's claim about the polish pass and risks SyntaxError on Python versions earlier than 3.13.
| except MemoryError, RecursionError: | |
| raise | |
| except (MemoryError, RecursionError): | |
| raise |
Merging this PR will not alter performance
Comparing Footnotes
|
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/reference/configuration-precedence.md`:
- Around line 177-179: Update the paragraph to remove the claim that
model_copy(update=...) itself re-validates field bounds; instead state that the
current implementation performs re-validation via model_validate(...) (i.e.,
model_copy(update=...) relies on model_validate for re-validation) so an
out-of-range value is caught by model_validate and the prior snapshot is
retained; replace the misleading sentence about model_copy re-validating
directly with this corrected description and reference model_copy(update=...)
and model_validate(...) in the text.
In `@src/synthorg/api/controllers/audit.py`:
- Around line 42-50: The module-level comment describing the "log-once guard for
the settings-resolution fallback" contains migration-framing language and
references to other controllers (e.g. "activities controller",
"app_state.api_bridge_config", "ApiBridgeSettingsSubscriber") which should be
removed; replace the comment with a concise WHY-only description that explains
the local purpose and behavior of the guard (that it prevents repeated identical
warnings during settings outages by setting a flag on first failure and clearing
it on successful resolution) and omit any reviewer citations, migration status,
or cross-controller comparisons so the comment documents only the rationale for
the guard in this module.
In `@src/synthorg/api/controllers/coordination_metrics.py`:
- Around line 37-45: The comment block describing the module-level log-once
guard should be rewritten to focus only on the local intent and behavior (what
the guard does and why it exists) and must remove migration/history references
such as "activities controller", "app_state.api_bridge_config", and
"ApiBridgeSettingsSubscriber"; keep a concise explanation that this guard
prevents repeated identical warnings during a settings-resolution failure, that
the flag is set on first failure and cleared on next successful resolution, and
where it is used in this module (i.e., the log-once guard for the
settings-resolution fallback) so readers understand its purpose without
migration framing.
In `@src/synthorg/settings/subscribers/api_bridge_subscriber.py`:
- Around line 10-15: The module docstring currently contains migration-framing
language about controllers migrating and should be cleaned to remove
reviewer/transition commentary; edit the top-level docstring in
api_bridge_subscriber.py to state only the purpose of the setting (e.g., that it
watches api.max_lifecycle_events_per_query consumed by ActivityController as its
LIMIT clamp) and remove the phrase "as controllers migrate..." and any
migration/back-ref text, leaving plain documentation of ApiBridgeConfig/_WATCHED
and what is being watched.
- Around line 64-67: The docstring is out of sync: it states the subscriber
builds a new snapshot with model_copy(update=...) and calls
AppState.swap_api_bridge_config, but the implementation actually calls
mutate_api_bridge_config; update the docstring in the subscriber to describe
that the subscriber resolves the integer via ConfigResolver, applies the change
by calling AppState.mutate_api_bridge_config (not swapping the entire snapshot),
and explain that mutate_api_bridge_config mutates only the relevant fields
rather than replacing the whole config; also remove or replace the mention of
model_copy(update=...) unless that exact method is used in the code.
🪄 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: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 1ab22fad-d34a-41db-b8f0-e18fc76c6239
📒 Files selected for processing (14)
docs/reference/configuration-precedence.mdscripts/no_magic_numbers_baseline.txtsrc/synthorg/api/controllers/activities.pysrc/synthorg/api/controllers/audit.pysrc/synthorg/api/controllers/coordination_metrics.pysrc/synthorg/api/lifecycle_helpers.pysrc/synthorg/api/state.pysrc/synthorg/api/state_services.pysrc/synthorg/settings/subscribers/__init__.pysrc/synthorg/settings/subscribers/api_bridge_subscriber.pytests/unit/api/controllers/test_activities.pytests/unit/api/test_apply_api_bridge_config_snapshot.pytests/unit/api/test_state.pytests/unit/settings/test_api_bridge_subscriber.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). (8)
- GitHub Check: Deploy Preview
- GitHub Check: Build Backend
- GitHub Check: Build Web Assets (melange)
- GitHub Check: Test (Python 3.14)
- GitHub Check: CodSpeed Python benchmarks
- GitHub Check: Lighthouse Site
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Mark tests with@pytest.mark.{unit,integration,e2e,slow}. Use async auto-detection. Global timeout 30s. Coverage minimum 80%
Windows test environment: unit tests useWindowsSelectorEventLoopPolicy(3.14 IOCP teardown race); subprocess tests override back
Every Mock must declarespec=ConcreteClass; baseline atscripts/mock_spec_baseline.txt
UseFakeClockfromtests._shared.fake_clock; inject viaclock=parameter in tests
Hypothesis properties: maintain 10 deterministic CI examples; treat test failures as real bugs (fix + add@example(...)). Never skip/xfail flaky tests; fix fundamentally. Useasyncio.Event().wait()not largesleep()
Files:
tests/unit/api/test_apply_api_bridge_config_snapshot.pytests/unit/settings/test_api_bridge_subscriber.pytests/unit/api/test_state.pytests/unit/api/controllers/test_activities.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/api/test_apply_api_bridge_config_snapshot.pytests/unit/settings/test_api_bridge_subscriber.pytests/unit/api/test_state.pytests/unit/api/controllers/test_activities.py
{src/**/*.py,tests/**/*.py,web/src/**/*.{ts,tsx}}
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER use real vendor names in project code/tests; use
example-provider,test-provider,example-{large,medium,small}-001. Real vendor names allowed only in.claude/, third-party imports,providers/presets.py,web/public/provider-logos/
Files:
tests/unit/api/test_apply_api_bridge_config_snapshot.pysrc/synthorg/api/controllers/audit.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/coordination_metrics.pysrc/synthorg/api/state_services.pysrc/synthorg/settings/subscribers/api_bridge_subscriber.pytests/unit/settings/test_api_bridge_subscriber.pysrc/synthorg/api/lifecycle_helpers.pysrc/synthorg/settings/subscribers/__init__.pytests/unit/api/test_state.pytests/unit/api/controllers/test_activities.pysrc/synthorg/api/controllers/activities.py
src/synthorg/!(persistence)/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Only
src/synthorg/persistence/may import sqlite/psycopg or emit raw SQL; all other modules must use abstraction layer
Files:
src/synthorg/api/controllers/audit.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/coordination_metrics.pysrc/synthorg/api/state_services.pysrc/synthorg/settings/subscribers/api_bridge_subscriber.pysrc/synthorg/api/lifecycle_helpers.pysrc/synthorg/settings/subscribers/__init__.pysrc/synthorg/api/controllers/activities.py
src/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.py: Follow configuration precedence: DB > env > YAML > code default viaSettingsService/ConfigResolver; never useos.environ.get()outside startup
Numeric constants must live insettings/definitions/; only allowlist 0/1/-1, HTTP codes, hex masks, and powers-of-2 in code. Enforced byscripts/check_no_magic_numbers.py
Comments should document WHY only; no reviewer citations, issue back-refs, or migration framing. Enforced bycheck_no_review_origin_in_code.pyandcheck_no_migration_framing.py
Do not usefrom __future__ import annotations(Python 3.14+ has PEP 649); use PEP 758 exceptexcept A, B:requires parens when binding
Require type hints on public functions; enforce mypy strict mode. Use Google-style docstrings. Keep line length 88; functions <50 lines; files <800 lines
Define error classes as<Domain><Condition>Errorinheriting fromDomainError, never directly fromException/RuntimeError. Enforced bycheck_domain_error_hierarchy.py
Use Pydantic v2 frozen models withextra="forbid"on API DTOs (Request/Response/Snapshot/Result/Envelope/Status/Info/Summary suffixes); use@computed_fieldfor derived properties; useNotBlankStrfor identifiers
Create args models at every system boundary; useparse_typed()for every external dict ingestion. Enforced bycheck_boundary_typed.py
Ensure immutability viamodel_copy(update=...)orcopy.deepcopy(); use deepcopy at system boundaries
Useasyncio.TaskGroupfor fan-out/fan-in; helper functions must catchExceptionbut re-raiseMemoryError/RecursionError
Inject Clock viaclock: Clock | None = Noneparameter; tests injectFakeClockfromtests._shared.fake_clock
Services own_lifecycle_lock; timed-out stops mark services as unrestartable
Wrap untrusted content viawrap_untrusted()fromengine.prompt_safety; useHTMLParseGuardfor HTML parsing (SEC-1)
Import logger viafrom synthorg.observability import get_logger; variable must be namedlogger. Never us...
Files:
src/synthorg/api/controllers/audit.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/coordination_metrics.pysrc/synthorg/api/state_services.pysrc/synthorg/settings/subscribers/api_bridge_subscriber.pysrc/synthorg/api/lifecycle_helpers.pysrc/synthorg/settings/subscribers/__init__.pysrc/synthorg/api/controllers/activities.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/api/controllers/audit.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/coordination_metrics.pysrc/synthorg/api/state_services.pysrc/synthorg/settings/subscribers/api_bridge_subscriber.pysrc/synthorg/api/lifecycle_helpers.pysrc/synthorg/settings/subscribers/__init__.pysrc/synthorg/api/controllers/activities.py
{README.md,docs/**/*.md,data/runtime_stats.yaml}
📄 CodeRabbit inference engine (CLAUDE.md)
Numeric claims in README and public docs must be sourced from
data/runtime_stats.yamlvia<!--RS:NAME-->markers
Files:
docs/reference/configuration-precedence.md
docs/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Use D2 for architecture/nested containers with theme 200 (Dark Mauve); use mermaid for flowcharts/sequence/pipelines; use markdown tables for tabular data; CI pinned to D2 v0.7.1
Files:
docs/reference/configuration-precedence.md
🧠 Learnings (1)
📚 Learning: 2026-05-05T09:04:46.195Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1760
File: scripts/_dual_backend_parity_lib.py:215-216
Timestamp: 2026-05-05T09:04:46.195Z
Learning: This repository targets Python 3.14+ and follows PEP 758. Therefore, reviewer tooling should NOT treat unparenthesized multi-exception `except` clauses written without an `as` clause (e.g., `except MemoryError, RecursionError:`) as syntax errors. Only flag `except`-clause problems when they are genuinely invalid for Python 3.14+.
Applied to files:
tests/unit/api/test_apply_api_bridge_config_snapshot.pysrc/synthorg/api/controllers/audit.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/coordination_metrics.pysrc/synthorg/api/state_services.pysrc/synthorg/settings/subscribers/api_bridge_subscriber.pytests/unit/settings/test_api_bridge_subscriber.pysrc/synthorg/api/lifecycle_helpers.pysrc/synthorg/settings/subscribers/__init__.pytests/unit/api/test_state.pytests/unit/api/controllers/test_activities.pysrc/synthorg/api/controllers/activities.py
🔇 Additional comments (18)
src/synthorg/api/state.py (1)
421-435: Good snapshot initialization and lock pairing.Default-constructing
ApiBridgeConfig()plus a dedicated lock gives consumers a safe pre-startup snapshot and supports atomic swaps cleanly.src/synthorg/api/state_services.py (1)
1046-1120: Lock-scoped mutate/swap implementation looks solid.The atomic merge +
model_validate(...)path prevents lost updates and preserves validator enforcement for hot-reload edits.tests/unit/api/test_state.py (1)
530-567: Nice targeted coverage for the new bridge snapshot behavior.These tests validate pre-startup defaults and swap semantics directly on
AppState, which reduces regression risk for the new snapshot pathway.src/synthorg/settings/subscribers/__init__.py (1)
3-5: LGTM!The new
ApiBridgeSettingsSubscriberimport and re-export follows the established pattern for other subscribers in this package.Also applies to: 23-23
tests/unit/settings/test_api_bridge_subscriber.py (4)
29-60: LGTM!The helper factory properly uses
create_autospecwithspec=for bothSettingsServiceandConfigResolver, and the test structure cleanly separates subscriber construction from assertions.
63-78: LGTM!Protocol conformance tests correctly verify
SettingsSubscriberimplementation,watched_keyscontent, andsubscriber_namebehavior.
81-145: LGTM!The rebuild tests comprehensively cover the critical paths: successful swap with field preservation, resolver failure (no swap + re-raise),
MemoryErrorpropagation, and PydanticValidationErrorhandling that keeps the prior snapshot intact.
148-171: LGTM!The unexpected-routing tests correctly verify that unknown namespaces and keys are ignored without modifying the existing snapshot.
src/synthorg/api/lifecycle_helpers.py (4)
18-18: LGTM!New imports for
API_BRIDGE_CONFIG_RESOLVE_FAILEDevent constant andApiBridgeSettingsSubscriberare correctly placed with existing imports.Also applies to: 34-34
764-795: LGTM!The
_apply_api_bridge_config_snapshothelper correctly follows the project's resolver-failure pattern: guard onhas_config_resolver, re-raise system errors, emit a single structured warning on transient failures while retaining the default snapshot, and only swap on success.
594-604: LGTM!The
ApiBridgeSettingsSubscriberinstantiation and registration follows the established pattern for other subscribers in_build_settings_dispatcher.
812-812: LGTM!Calling
_apply_api_bridge_config_snapshotafter the approval-urgency invariant check ensures the API bridge config is snapshotted early in the startup sequence.src/synthorg/api/controllers/activities.py (2)
362-362: LGTM!Reading
lifecycle_capdirectly fromapp_state.api_bridge_config.max_lifecycle_events_per_queryis the correct simplification: synchronous access to a pre-resolved frozen snapshot, with hot-swap support via the subscriber.
359-368: LGTM!The
list_activitiesendpoint correctly reads the lifecycle cap from the bridge-config snapshot and passes it tolist_events. The simplified flow eliminates the previous async resolution overhead.tests/unit/api/test_apply_api_bridge_config_snapshot.py (2)
27-46: LGTM!The helper factories correctly use
create_autospec(ConfigResolver, instance=True)and provide clean abstractions for test setup.
48-103: LGTM!The test class comprehensively covers the critical paths: no-resolver (keeps default), happy-path (swaps snapshot), resolver failure (keeps default + single structured warning), and
MemoryErrorpropagation. The warning capture via monkeypatch correctly validates the event name and structured fields.tests/unit/api/controllers/test_activities.py (2)
719-784: LGTM!The
TestActivityFeedLifecycleCaptests correctly verify the integration: the spy pattern captures thelimitpassed tolist_events, and the hot-swap test confirms thatswap_api_bridge_configtakes effect immediately on subsequent requests. Thefinallyblock properly restores the original snapshot.
786-798: LGTM!The controller surface tests provide valuable regression guards, ensuring the legacy
_MAX_LIFECYCLE_EVENTS,_resolve_lifecycle_cap, and_lifecycle_cap_fallback_loggedsymbols remain removed.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1840 +/- ##
=======================================
Coverage 84.73% 84.74%
=======================================
Files 1799 1800 +1
Lines 104779 104835 +56
Branches 9187 9190 +3
=======================================
+ Hits 88786 88843 +57
+ Misses 13758 13756 -2
- Partials 2235 2236 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
- docs/reference/configuration-precedence.md: clarify mutate_* re-validates via model_validate (Pydantic v2 skips validators on bare model_copy(update=...)) - src/synthorg/api/controllers/audit.py: drop migration framing from log-once guard comment - src/synthorg/api/controllers/coordination_metrics.py: drop migration framing from log-once guard comment - src/synthorg/settings/subscribers/api_bridge_subscriber.py: drop 'as controllers migrate' framing from module docstring; sync class docstring with mutate_api_bridge_config (was claiming model_copy/swap) Skipped (factually wrong): - gemini lifecycle_helpers.py:785 / api_bridge_subscriber.py:115 unparenthesised except: PEP 758 form is mandated by CLAUDE.md and CodeRabbit baseline learning (Py3.14+ repo) - gemini api_bridge_subscriber.py:112 type-aware get_int: _WATCHED only carries one int field; type-dispatch is YAGNI scaffolding without a non-int consumer
The migration-framing comment trim in the prior commit removed 4 lines from each of audit.py and coordination_metrics.py preambles, which shifted the pinned limit=50 default-arg sites from line 108→104 and 100→96. Update the baseline coordinates to match; no new magic-number sites introduced.
<!-- HIGHLIGHTS_START --> ## Highlights > _AI-generated summary (model: `openai/gpt-4.1-mini` via GitHub Models). Commit-based changelog below._ ### What you'll notice - Improved error logging and Prometheus instrumentation provide better system monitoring. - Eliminated race conditions in CI tagging for more reliable development releases. - Fixed critical configuration access and kill-switch bugs to enhance system stability. - Enhanced client experience with retry-after headers and better websocket reconnect behavior. ### What's new - Introduced composite indexes and cursor pagination for faster data queries. - Added server-sent events rate limiting and Ollama input sanitization for improved security. ### Under the hood - Centralized workflow error mappings to standardize error handling. - Refactored API lifecycle fallback to use a configuration snapshot for consistency. - Tightened startup settings baseline and reduced controller error baseline to zero. - Replaced flaky contributor-assistant GitHub action with a custom stable step. - Consolidated Renovate dependency groups to avoid update conflicts. - Upgraded in-toto-golang dependency to fix security vulnerabilities and dropped unnecessary CVE waivers. - Extensive lock file maintenance and multiple infrastructure and Python dependency updates. <!-- HIGHLIGHTS_END --> :robot: I have created a release *beep* *boop* --- ## [0.8.2](v0.8.1...v0.8.2) (2026-05-10) ### Features * close audit gaps in error logging and Prometheus instrumentation ([#1821](#1821)) ([ef00fdc](ef00fdc)) ### Bug Fixes * **ci:** eliminate dev-release tag-vs-downstream race + CI hygiene audit ([#1827](#1827)) ([b7b9a59](b7b9a59)) * **config:** close 6 settings reachability + kill-switch gaps ([#1798](#1798)) ([410cb3b](410cb3b)) * correctness / safety fixes from 2026-05-05 audit (Wave 28) ([#1823](#1823)) ([d01e624](d01e624)) ### Performance * composite indexes + cursor pagination + clock seam + SSE rate-limit + Ollama sanitization + retry-after web client + WS reconnect jitter ([#1822](#1822)) ([d1faf86](d1faf86)) ### Refactoring * **api:** move activities lifecycle-cap fallback to ApiBridgeConfig snapshot ([#1840](#1840)) ([7a56e9c](7a56e9c)) * centralise workflow error mapping and shared error codes ([#1778](#1778) sub-tasks A + E) ([#1843](#1843)) ([11132cd](11132cd)) * drive controller-error baseline to zero ([#1778](#1778) sub-task A tail) ([#1846](#1846)) ([e96ae20](e96ae20)) * slim CLAUDE.md, port pr-review-toolkit agents, sync .opencode parity ([#1833](#1833)) ([e6372b8](e6372b8)) * tighten settings → startup-trace baseline (8 → 0) ([#1847](#1847)) ([3376ee2](3376ee2)) ### Documentation * fix CLAUDE.md inaccuracies and drop drift-prone counts ([#1844](#1844)) ([371925f](371925f)) ### Tests * replace test placeholders with real subsystem wiring ([#1845](#1845)) ([ddbb666](ddbb666)) ### CI/CD * **cla:** replace flaky contributor-assistant action with custom read-path step ([#1819](#1819)) ([11aeafe](11aeafe)) * tidy dev-release notes + stagger renovate lockfile day ([#1824](#1824)) ([ec746a9](ec746a9)) ### Maintenance * cleanup roundup, sub-tasks a/c/d/g/h/j/l/m of [#1781](#1781) ([#1838](#1838)) ([099b871](099b871)) * close remaining 5 sub-tasks of [#1781](#1781) (b/e/f/i/k) ([#1852](#1852)) ([59cf0b2](59cf0b2)) * collapse Renovate dep groups into Python / Web / Infrastructure to remove cross-PR overlap ([#1813](#1813)) ([4cbd857](4cbd857)) * **deps,security:** bump in-toto-golang v0.11.0 + drop two patched CVE waivers ([#1851](#1851)) ([0b8b5bb](0b8b5bb)) * disable Renovate vulnerabilityAlerts so security flows into normal updates ([#1834](#1834)) ([6b7d15f](6b7d15f)) * Lock file maintenance ([#1820](#1820)) ([ccbad73](ccbad73)) * Lock file maintenance ([#1842](#1842)) ([13b68a5](13b68a5)) * Lock file maintenance ([#1853](#1853)) ([db6650b](db6650b)) * Update dhi.io/nats:2.14-debian13 Docker digest to eb768bf ([#1841](#1841)) ([37f84fc](37f84fc)) * Update Infrastructure dependencies ([#1815](#1815)) ([75b12fe](75b12fe)) * Update Infrastructure dependencies ([#1831](#1831)) ([3f3c50b](3f3c50b)) * Update Python dependencies ([#1817](#1817)) ([e11332f](e11332f)) * Update Python dependencies ([#1832](#1832)) ([4515c8e](4515c8e)) --- 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: synthorg-repo-bot[bot] <279117679+synthorg-repo-bot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Closes #1797.
Summary
_MAX_LIFECYCLE_EVENTS = 10_000constant (and its module-level log-once guard) fromsrc/synthorg/api/controllers/activities.py. The activities controller now reads the cap fromapp_state.api_bridge_config.max_lifecycle_events_per_query(a frozenApiBridgeConfigsnapshot held onAppState), which makesApiBridgeConfigthe single source of truth for the operator-facing default._apply_api_bridge_config_snapshottolifecycle_helpers.py. Called from_apply_bridge_configat startup; resolves the fullApiBridgeConfigonce viaConfigResolver.get_api_bridge_config()and atomically swaps it ontoAppState. On resolver failure the default snapshot is retained and a singleapi.bridge_config.resolve_failedwarning is emitted (centralised replacement for the prior per-request log-once fallback).ApiBridgeSettingsSubscriber(src/synthorg/settings/subscribers/api_bridge_subscriber.py). Watches(api, max_lifecycle_events_per_query); on change it resolves the new value and callsapp_state.mutate_api_bridge_config({key: value}). The watched-key set is asserted againstApiBridgeConfig.model_fieldsat module import so a typo or rename surfaces at boot rather than at the next operator hot-reload. Wired into_build_settings_dispatcher.mutate_api_bridge_config(and a_api_bridge_config_lockthreading.Lock) onAppState. Combines a re-validating partial update with the swap inside one critical section, so two concurrent operator edits cannot lose each other's writes. Re-validation goes throughApiBridgeConfig.model_validate(prev | updates)rather thanmodel_copy(update=...)because Pydantic v2'supdate=skips validators, which would otherwise let an out-of-bounds operator value (e.g.50againstField(ge=100, le=1_000_000)) land silently. The matchingValidationErrorpath leaves the prior snapshot in place and propagates to the subscriber's swap-failed log.scripts/no_magic_numbers_baseline.txt; bumps two unrelated baseline line numbers shifted by adjacent comment additions inaudit.py/coordination_metrics.py.docs/reference/configuration-precedence.mdso subsequent migrations (audit, coordination_metrics, meetings, approvals carry the same anti-pattern) have written guidance.Test plan
uv run python -m pytest tests/ -m unit -n 8— full unit suite green (27,920 passed, 18 skipped from optional extras).uv run mypy src/ tests/clean.uv run ruff check src/ tests/+uv run ruff format --check src/ tests/clean.uv run python scripts/check_no_magic_numbers.pyclean.tests/unit/api/test_state.py::TestAppStateApiBridgeConfig— default snapshot, swap replaces, swap is idempotent for same instance.tests/unit/api/test_apply_api_bridge_config_snapshot.py— no-resolver no-op, happy-path swap, resolver-failure keeps default + emits one structured warning, MemoryError propagates.tests/unit/settings/test_api_bridge_subscriber.py— protocol conformance, lifecycle-cap change preserves every other field, resolver failure does not swap, MemoryError propagates, out-of-range value triggers PydanticValidationErrorand prior snapshot is retained, unknown namespace / key are no-op.tests/unit/api/controllers/test_activities.py::TestActivityFeedLifecycleCap+TestActivitiesControllerSurface— default cap flows through tolifecycle_events.list_events, mid-test swap takes effect immediately, legacy symbols (_MAX_LIFECYCLE_EVENTS,_resolve_lifecycle_cap,_lifecycle_cap_fallback_logged) are gone.Review coverage
Pre-PR review pipeline: 21 review agents (docs-consistency, comment-quality-rot, code-reviewer, python-reviewer, pr-test-analyzer, silent-failure-hunter, comment-analyzer, type-design-analyzer, security-reviewer, issue-resolution-verifier, logging-audit, resilience-audit, conventions-enforcer, api-contract-drift, test-quality-reviewer, async-concurrency-reviewer, plus five mini-pass agents from the codebase-audit catalogue).
Findings: 8 valid, 6 false positives. All 8 valid findings addressed in this PR (CancelledError guard on the new helper for parity with peer
_resolve_*helpers; out-of-range Pydantic validation test; pattern documented; startup-time watched-key guard; concurrent-writerthreading.Lock+ dedicatedmutate_api_bridge_config; richer changed-fields swap log; explicit@pytest.mark.unit; cross-reference in audit/coordination_metrics comments).False positives are catalogued in
_audit/pre-pr-review/triage.md; the dominant class was agents flaggingexcept A, B:as a "Python 2 syntax error" — CLAUDE.md mandates this PEP 758 form.Issue-resolution-verifier: all 5 acceptance criteria RESOLVED at confidence 100.
Polish pass (
code-simplifier) ran after triage; only stylistic exception-syntax tweaks applied (parenthesisedexcept (A, B):in newly-touched files), no behavioural changes.