feat: runtime sink configuration via SettingsService#934
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
📜 Recent review details⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
🧰 Additional context used📓 Path-based instructions (4)**/*.{py,ts,tsx,md}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
src/synthorg/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
src/synthorg/**/!(setup|sinks).py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (35)📓 Common learnings📚 Learning: 2026-03-16T06:24:56.341ZApplied to files:
📚 Learning: 2026-03-19T07:12:14.508ZApplied to files:
📚 Learning: 2026-03-17T06:30:14.180ZApplied to files:
📚 Learning: 2026-03-17T06:30:14.180ZApplied to files:
📚 Learning: 2026-03-17T22:08:13.456ZApplied to files:
📚 Learning: 2026-03-19T07:12:14.508ZApplied to files:
📚 Learning: 2026-03-20T21:44:04.528ZApplied to files:
📚 Learning: 2026-03-16T07:22:28.134ZApplied to files:
📚 Learning: 2026-03-14T15:43:05.601ZApplied to files:
📚 Learning: 2026-03-16T07:22:28.134ZApplied to files:
📚 Learning: 2026-03-14T16:18:57.267ZApplied to files:
📚 Learning: 2026-03-14T16:18:57.267ZApplied to files:
📚 Learning: 2026-03-14T15:43:05.601ZApplied to files:
📚 Learning: 2026-03-30T16:36:12.059ZApplied to files:
📚 Learning: 2026-03-15T16:55:07.730ZApplied to files:
📚 Learning: 2026-03-30T16:36:12.059ZApplied to files:
📚 Learning: 2026-03-30T16:36:12.059ZApplied to files:
📚 Learning: 2026-03-14T16:18:57.267ZApplied to files:
📚 Learning: 2026-03-17T06:43:14.114ZApplied to files:
📚 Learning: 2026-03-17T22:08:13.456ZApplied to files:
📚 Learning: 2026-03-16T07:22:28.134ZApplied to files:
📚 Learning: 2026-03-19T11:33:01.580ZApplied to files:
📚 Learning: 2026-03-19T07:12:14.508ZApplied to files:
📚 Learning: 2026-03-15T18:38:44.202ZApplied to files:
📚 Learning: 2026-03-15T18:28:13.207ZApplied to files:
📚 Learning: 2026-03-17T06:43:14.114ZApplied to files:
📚 Learning: 2026-03-15T19:14:27.144ZApplied to files:
📚 Learning: 2026-03-20T11:18:48.128ZApplied to files:
📚 Learning: 2026-03-16T06:24:56.341ZApplied to files:
📚 Learning: 2026-03-19T11:33:01.580ZApplied to files:
📚 Learning: 2026-03-15T18:38:44.202ZApplied to files:
📚 Learning: 2026-03-17T22:08:13.456ZApplied to files:
📚 Learning: 2026-03-30T16:36:12.059ZApplied to files:
📚 Learning: 2026-03-16T20:14:00.937ZApplied to files:
🔇 Additional comments (20)
WalkthroughAdds two observability settings, 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Code Review
This pull request enables hot-reloading of the logging pipeline by introducing sink_overrides and custom_sinks settings. It adds an ObservabilitySettingsSubscriber to react to configuration changes and a sink_config_builder to merge default sinks with runtime overrides. Critical feedback was provided regarding the use of legacy Python 2 exception handling syntax in the new subscriber, which will cause SyntaxError in a Python 3 environment. Additionally, a minor simplification was suggested for the routing overrides dictionary conversion.
| "observability", | ||
| "custom_sinks", | ||
| ) | ||
| except MemoryError, RecursionError: |
| # Parse root level separately for accurate error reporting. | ||
| try: | ||
| root_level = LogLevel(root_level_result.value.upper()) | ||
| except ValueError, AttributeError: |
| custom_sinks_json=custom_result.value, | ||
| log_dir=self._log_dir, | ||
| ) | ||
| except MemoryError, RecursionError: |
| build_result.config, | ||
| routing_overrides=dict(build_result.routing_overrides) or None, | ||
| ) | ||
| except MemoryError, RecursionError: |
| try: | ||
| configure_logging( | ||
| build_result.config, | ||
| routing_overrides=dict(build_result.routing_overrides) or None, |
There was a problem hiding this comment.
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure that dependencies are being submitted on PR branches. Re-running this action after a short time may resolve the issue. See the documentation for more information and troubleshooting advice. Scanned FilesNone |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #934 +/- ##
==========================================
+ Coverage 92.15% 92.20% +0.04%
==========================================
Files 596 598 +2
Lines 31541 31720 +179
Branches 3059 3088 +29
==========================================
+ Hits 29068 29248 +180
+ Misses 1950 1949 -1
Partials 523 523 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 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/observability/sink_config_builder.py`:
- Around line 144-184: The parser _parse_rotation_override currently assumes raw
is a dict and probes keys; add an explicit type check at the top of
_parse_rotation_override (e.g., if not isinstance(raw, dict): raise
ValueError("rotation must be an object/dict")) so non-object JSON values
(strings, lists, numbers) fail validation instead of silently reusing the base
RotationConfig; then apply the same guard in the other rotation-parsing sites
noted (the blocks around the other occurrences at the referenced ranges) so each
parsing path validates that the rotation override is a mapping before accessing
keys like "strategy", "max_bytes", or "backup_count".
- Around line 203-215: The code currently coerces non-boolean JSON values via
truthiness; change the handling of override["enabled"] and
override["json_format"] to require actual booleans: when "enabled" is present,
first check isinstance(override["enabled"], bool) and raise ValueError if not a
bool, then if override["enabled"] is False check identifier == _CONSOLE_ID and
raise the existing error or return None; when "json_format" is present check
isinstance(override["json_format"], bool) and raise ValueError if not, then set
updates["json_format"] = override["json_format"]. Apply the same explicit bool
validation to the other occurrence referenced (around lines 250-251) so both
sites validate types rather than coercing truthiness.
- Around line 74-101: The code currently checks sink identifier names against
_VALID_OVERRIDE_KEYS instead of validating the keys inside each override dict,
so unknown inner fields are silently accepted; in _parse_sink_overrides, change
the validation to iterate over value (the override dict) and ensure each
inner_field is in _VALID_OVERRIDE_KEYS (raise ValueError listing the offending
field and allowed keys) and keep the existing type checks; apply the same
pattern in the corresponding parser for custom sinks (e.g., _parse_custom_sinks)
by validating each custom sink's dict keys against _VALID_CUSTOM_SINK_KEYS and
raising on unknown fields.
- Around line 18-30: Add module-level logger by importing get_logger from
synthorg.observability and defining logger = get_logger(__name__) at top of
sink_config_builder.py, then instrument all validation failure paths in
functions such as build_log_config_from_settings and any helpers used by
ObservabilitySettingsSubscriber to log context and error details (use
logger.warning or logger.error as appropriate) immediately before each raise so
the warning/error contains the failing setting name/value and the reason for
failure; ensure messages are clear and include the exception or validation
message where available.
In `@src/synthorg/settings/definitions/observability.py`:
- Around line 35-68: The two SettingDefinition registrations for sink_overrides
and custom_sinks are missing yaml_path so YAML-configured values aren’t
considered; update the SettingDefinition calls for the keys "sink_overrides" and
"custom_sinks" (the _r.register(...) blocks where
namespace=SettingNamespace.OBSERVABILITY) to include a yaml_path parameter (e.g.
"observability.sink_overrides" and "observability.custom_sinks") so settings
resolution follows DB > env > YAML > code defaults.
In `@src/synthorg/settings/subscribers/observability_subscriber.py`:
- Around line 158-179: The try/except currently lets configure_logging clear the
live handlers before failing, so save the existing logging pipeline first and
restore it on non-fatal exceptions: before calling configure_logging in
observability_subscriber.py (the block that logs
SETTINGS_OBSERVABILITY_REBUILD_FAILED), snapshot the current logger/handlers (or
call a restore helper) then attempt configure_logging(build_result.config,
routing_overrides=...). On MemoryError or RecursionError re-raise as before; on
any other Exception restore the saved handlers/pipeline, then emit the
SETTINGS_OBSERVABILITY_REBUILD_FAILED error (including exc_info) and return —
this yields a rollback path instead of leaving logging degraded. Ensure you
reference configure_logging and the error handling around
SETTINGS_OBSERVABILITY_REBUILD_FAILED/subscriber_name/key when implementing the
restore.
- Around line 123-135: The current code silently coerces enable_correlation
using str(correlation_result.value).lower() == "true", which treats any
malformed value as False; instead validate and reject invalid values the same
way as root_level parsing does: attempt to parse correlation_result.value as a
boolean (explicitly accept only "true" or "false" strings or proper bool types),
and on invalid input log SETTINGS_OBSERVABILITY_VALIDATION_FAILED with
subscriber=self.subscriber_name, key=key and exc_info=True and return; mirror
the error handling used around LogLevel(root_level_result.value.upper())
(references: enable_correlation, correlation_result, root_level_result,
LogLevel, root_level, SETTINGS_OBSERVABILITY_VALIDATION_FAILED).
🪄 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: 122b8e1c-96be-4c6b-9a67-080ba74642a8
📒 Files selected for processing (14)
docs/design/operations.mdsrc/synthorg/api/app.pysrc/synthorg/observability/events/settings.pysrc/synthorg/observability/setup.pysrc/synthorg/observability/sink_config_builder.pysrc/synthorg/observability/sinks.pysrc/synthorg/settings/definitions/observability.pysrc/synthorg/settings/subscribers/__init__.pysrc/synthorg/settings/subscribers/observability_subscriber.pytests/integration/observability/test_runtime_sink_config_integration.pytests/unit/observability/test_sink_config_builder.pytests/unit/observability/test_sink_routing.pytests/unit/settings/test_observability_subscriber.pyweb/public/mockServiceWorker.js
📜 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: Dashboard Test
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Build Sandbox
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotations—Python 3.14 has PEP 649
PEP 758 except syntax: useexcept A, B:(no parentheses)—ruff enforces this on Python 3.14
Type hints: all public functions, mypy strict mode
Docstrings: Google style, required on public classes/functions (enforced by ruff D rules)
Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement
Config vs runtime state: frozen Pydantic models for config/identity; separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model
Models: Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Adopted conventions: use allow_inf_nan=False in all ConfigDict declarations; use@computed_fieldfor derived values; use NotBlankStr for all identifier/name fields
Async concurrency: prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code; prefer structured concurrency over bare create_task
Line length: 88 characters (ruff)
Functions: < 50 lines, files < 800 lines
Errors: handle explicitly, never silently swallow
Validate: at system boundaries (user input, external APIs, config files)
Files:
src/synthorg/settings/subscribers/__init__.pytests/unit/observability/test_sink_routing.pysrc/synthorg/api/app.pysrc/synthorg/observability/setup.pysrc/synthorg/observability/sinks.pytests/unit/settings/test_observability_subscriber.pysrc/synthorg/observability/events/settings.pysrc/synthorg/settings/subscribers/observability_subscriber.pytests/unit/observability/test_sink_config_builder.pysrc/synthorg/observability/sink_config_builder.pytests/integration/observability/test_runtime_sink_config_integration.pysrc/synthorg/settings/definitions/observability.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic MUST have: from synthorg.observability import get_logger then logger = get_logger(name)
Variable name: always logger (not _logger, not log)
Event names: always use constants from domain-specific modules under synthorg.observability.events; import directly from synthorg.observability.events.
Structured kwargs: always logger.info(EVENT, key=value)—never logger.info('msg %s', val)
All error paths must log at WARNING or ERROR with context before raising
All state transitions must log at INFO
DEBUG for object creation, internal flow, entry/exit of key functions
All provider calls go through BaseCompletionProvider which applies retry + rate limiting automatically; never implement retry logic in driver subclasses or calling code
Files:
src/synthorg/settings/subscribers/__init__.pysrc/synthorg/api/app.pysrc/synthorg/observability/setup.pysrc/synthorg/observability/sinks.pysrc/synthorg/observability/events/settings.pysrc/synthorg/settings/subscribers/observability_subscriber.pysrc/synthorg/observability/sink_config_builder.pysrc/synthorg/settings/definitions/observability.py
src/synthorg/**/!(setup|sinks).py
📄 CodeRabbit inference engine (CLAUDE.md)
Never use import logging / logging.getLogger() / print() in application code; exception: observability/setup.py and observability/sinks.py may use stdlib logging and print() for bootstrap code
Files:
src/synthorg/settings/subscribers/__init__.pysrc/synthorg/api/app.pysrc/synthorg/observability/events/settings.pysrc/synthorg/settings/subscribers/observability_subscriber.pysrc/synthorg/observability/sink_config_builder.pysrc/synthorg/settings/definitions/observability.py
**/*.{py,ts,tsx,md}
📄 CodeRabbit inference engine (CLAUDE.md)
Vendor-agnostic everywhere: NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples; use generic names: example-provider, example-large-001, etc.; vendor names only in: design spec, .claude/ files, third-party imports, provider presets
Files:
src/synthorg/settings/subscribers/__init__.pytests/unit/observability/test_sink_routing.pysrc/synthorg/api/app.pysrc/synthorg/observability/setup.pydocs/design/operations.mdsrc/synthorg/observability/sinks.pytests/unit/settings/test_observability_subscriber.pysrc/synthorg/observability/events/settings.pysrc/synthorg/settings/subscribers/observability_subscriber.pytests/unit/observability/test_sink_config_builder.pysrc/synthorg/observability/sink_config_builder.pytests/integration/observability/test_runtime_sink_config_integration.pysrc/synthorg/settings/definitions/observability.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Markers:@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slow
Async: asyncio_mode = 'auto'—no manual@pytest.mark.asyncioneeded
Timeout: 30 seconds per test (global in pyproject.toml); non-default overrides like timeout(60) are allowed
Parametrize: Prefer@pytest.mark.parametrizefor testing similar cases
Property-based testing: Python uses Hypothesis (@given+@settings); Hypothesis profiles: ci (50 examples, default) and dev (1000 examples); run dev profile: HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties
Flaky tests: NEVER skip, dismiss, or ignore flaky tests—always fix them fully and fundamentally; for timing-sensitive tests, mock time.monotonic() and asyncio.sleep(); for tasks that must block indefinitely, use asyncio.Event().wait()
Files:
tests/unit/observability/test_sink_routing.pytests/unit/settings/test_observability_subscriber.pytests/unit/observability/test_sink_config_builder.pytests/integration/observability/test_runtime_sink_config_integration.py
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability includes structured logging via `get_logger(__name__)`, correlation tracking, and log sinks.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability must use structured logging with correlation tracking and log sinks
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Settings: Runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge, ConfigResolver (typed composed reads for controllers), validation, registry, change notifications via message bus. Per-namespace setting definitions in definitions/ submodule (api, company, providers, memory, budget, security, coordination, observability, backup).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability package (observability/): structured logging, correlation tracking, log sinks; event constants organized by domain under observability/events/ (e.g., events.api, events.tool, events.git, events.context_budget, events.backup)
📚 Learning: 2026-03-30T16:36:12.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T16:36:12.059Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events; import directly from synthorg.observability.events.<domain>
Applied to files:
src/synthorg/settings/subscribers/__init__.pysrc/synthorg/observability/events/settings.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)
Applied to files:
src/synthorg/settings/subscribers/__init__.pysrc/synthorg/api/app.pydocs/design/operations.mdtests/unit/settings/test_observability_subscriber.pysrc/synthorg/observability/events/settings.pysrc/synthorg/settings/subscribers/observability_subscriber.pysrc/synthorg/settings/definitions/observability.py
📚 Learning: 2026-03-18T21:23:23.586Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T21:23:23.586Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from the domain-specific module under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly from synthorg.observability.events.<domain>.
Applied to files:
src/synthorg/settings/subscribers/__init__.pysrc/synthorg/observability/events/settings.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import `from synthorg.observability import get_logger` and define `logger = get_logger(__name__)`
Applied to files:
src/synthorg/settings/subscribers/__init__.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability package (observability/): structured logging, correlation tracking, log sinks; event constants organized by domain under observability/events/ (e.g., events.api, events.tool, events.git, events.context_budget, events.backup)
Applied to files:
src/synthorg/settings/subscribers/__init__.pysrc/synthorg/api/app.pysrc/synthorg/observability/setup.pydocs/design/operations.mdsrc/synthorg/observability/sinks.pysrc/synthorg/observability/events/settings.pysrc/synthorg/settings/subscribers/observability_subscriber.pytests/unit/observability/test_sink_config_builder.pysrc/synthorg/observability/sink_config_builder.pytests/integration/observability/test_runtime_sink_config_integration.pysrc/synthorg/settings/definitions/observability.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Always 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`
Applied to files:
src/synthorg/settings/subscribers/__init__.pysrc/synthorg/observability/events/settings.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/settings/subscribers/__init__.pysrc/synthorg/observability/events/settings.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability includes structured logging via `get_logger(__name__)`, correlation tracking, and log sinks.
Applied to files:
src/synthorg/settings/subscribers/__init__.pytests/unit/observability/test_sink_routing.pysrc/synthorg/api/app.pysrc/synthorg/observability/setup.pydocs/design/operations.mdsrc/synthorg/observability/sinks.pysrc/synthorg/settings/subscribers/observability_subscriber.pytests/unit/observability/test_sink_config_builder.pysrc/synthorg/observability/sink_config_builder.pytests/integration/observability/test_runtime_sink_config_integration.pysrc/synthorg/settings/definitions/observability.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from synthorg.observability.events domain-specific modules (e.g., PROVIDER_CALL_START from events.provider). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Applied to files:
src/synthorg/settings/subscribers/__init__.pysrc/synthorg/observability/events/settings.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly rather than using string literals
Applied to files:
src/synthorg/settings/subscribers/__init__.pysrc/synthorg/observability/events/settings.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability must use structured logging with correlation tracking and log sinks
Applied to files:
tests/unit/observability/test_sink_routing.pysrc/synthorg/observability/setup.pydocs/design/operations.mdsrc/synthorg/observability/sinks.pysrc/synthorg/settings/subscribers/observability_subscriber.pytests/unit/observability/test_sink_config_builder.pysrc/synthorg/observability/sink_config_builder.pytests/integration/observability/test_runtime_sink_config_integration.pysrc/synthorg/settings/definitions/observability.py
📚 Learning: 2026-03-30T16:36:12.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T16:36:12.059Z
Learning: Applies to src/synthorg/**/!(setup|sinks).py : Never use import logging / logging.getLogger() / print() in application code; exception: observability/setup.py and observability/sinks.py may use stdlib logging and print() for bootstrap code
Applied to files:
src/synthorg/observability/setup.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings use runtime-editable persistence with precedence: DB > env > YAML > code defaults. 8 namespaces with Fernet encryption for sensitive values.
Applied to files:
docs/design/operations.mdsrc/synthorg/settings/definitions/observability.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Settings: Runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge, ConfigResolver (typed composed reads for controllers), validation, registry, change notifications via message bus. Per-namespace setting definitions in definitions/ submodule (api, company, providers, memory, budget, security, coordination, observability, backup).
Applied to files:
docs/design/operations.md
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/observability/events/settings.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls
Applied to files:
src/synthorg/observability/events/settings.py
🔇 Additional comments (8)
web/public/mockServiceWorker.js (1)
24-27: Good same-origin guard placement.This early return correctly blocks cross-origin
messageevents before client resolution and command handling, which addresses the postMessage validation risk.src/synthorg/settings/subscribers/__init__.py (1)
9-11: Subscriber export wiring looks correct.
ObservabilitySettingsSubscriberis properly imported and re-exported, matching the dispatcher integration pattern.Also applies to: 19-19
tests/unit/observability/test_sink_routing.py (1)
7-7: Good move to public routing constant usage in tests.These assertions now target
SINK_ROUTING(public API) instead of a private implementation detail.Also applies to: 70-71, 74-75, 80-81, 86-87, 115-115, 118-118, 130-130
src/synthorg/api/app.py (1)
89-90: Runtime observability subscriber wiring is correct.Including
ObservabilitySettingsSubscriberin dispatcher subscriptions with explicitlog_dirfallback is solid.Also applies to: 747-753
docs/design/operations.md (1)
1401-1411: Docs update is clear and implementation-aligned.The new runtime sink settings and hot-reload semantics are documented accurately and concretely.
Also applies to: 1415-1417
src/synthorg/observability/setup.py (1)
11-11: Routing override plumbing is well integrated.The
routing_overridesthreading and default-routing merge behavior are clean and maintain idempotent setup behavior.Also applies to: 19-19, 180-204, 212-212, 323-327, 340-342, 371-376
src/synthorg/observability/events/settings.py (1)
35-48: New settings observability event constants look good.Names follow the existing event taxonomy and improve subscriber observability coverage.
src/synthorg/observability/sinks.py (1)
56-56: Public routing table + injectable routing in handler factory is a solid change.This keeps default routing intact while enabling controlled runtime overrides.
Also applies to: 206-207, 219-221, 226-226, 236-239
| import json | ||
| from dataclasses import dataclass | ||
| from types import MappingProxyType | ||
| from typing import Any | ||
|
|
||
| from synthorg.observability.config import ( | ||
| DEFAULT_SINKS, | ||
| LogConfig, | ||
| RotationConfig, | ||
| SinkConfig, | ||
| ) | ||
| from synthorg.observability.enums import LogLevel, RotationStrategy, SinkType | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Add the module logger and log validation failures before raising.
This is business logic under src/synthorg/**, but the module never defines logger = get_logger(__name__) and every validation path below raises silently. ObservabilitySettingsSubscriber wraps one call site, but build_log_config_from_settings() is also called directly and future callers will miss the required warning/error event. As per coding guidelines, "Every module with business logic MUST have: from synthorg.observability import get_logger then logger = get_logger(name)" and "All error paths must log at WARNING or ERROR with context before raising."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/observability/sink_config_builder.py` around lines 18 - 30, Add
module-level logger by importing get_logger from synthorg.observability and
defining logger = get_logger(__name__) at top of sink_config_builder.py, then
instrument all validation failure paths in functions such as
build_log_config_from_settings and any helpers used by
ObservabilitySettingsSubscriber to log context and error details (use
logger.warning or logger.error as appropriate) immediately before each raise so
the warning/error contains the failing setting name/value and the reason for
failure; ensure messages are clear and include the exception or validation
message where available.
| # Rebuild the logging pipeline. configure_logging is not atomic: | ||
| # it clears old handlers before attaching new ones, so a failure | ||
| # here may leave the pipeline degraded. | ||
| try: | ||
| configure_logging( | ||
| build_result.config, | ||
| routing_overrides=dict(build_result.routing_overrides) or None, | ||
| ) | ||
| except MemoryError, RecursionError: | ||
| raise | ||
| except Exception: | ||
| logger.error( | ||
| SETTINGS_OBSERVABILITY_REBUILD_FAILED, | ||
| subscriber=self.subscriber_name, | ||
| key=key, | ||
| note=( | ||
| "configure_logging failed -- old pipeline was already " | ||
| "torn down; logging may be degraded" | ||
| ), | ||
| exc_info=True, | ||
| ) | ||
| return |
There was a problem hiding this comment.
A failed rebuild still tears down the last known-good pipeline.
configure_logging() clears the existing handlers before attaching the new set (see src/synthorg/observability/setup.py:323-378), so an exception here leaves logging degraded even though this method claims to preserve the current configuration. Hot reload needs a staged reattach or a rollback to the last successful config/handler set.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/settings/subscribers/observability_subscriber.py` around lines
158 - 179, The try/except currently lets configure_logging clear the live
handlers before failing, so save the existing logging pipeline first and restore
it on non-fatal exceptions: before calling configure_logging in
observability_subscriber.py (the block that logs
SETTINGS_OBSERVABILITY_REBUILD_FAILED), snapshot the current logger/handlers (or
call a restore helper) then attempt configure_logging(build_result.config,
routing_overrides=...). On MemoryError or RecursionError re-raise as before; on
any other Exception restore the saved handlers/pipeline, then emit the
SETTINGS_OBSERVABILITY_REBUILD_FAILED error (including exc_info) and return —
this yields a rollback path instead of leaving logging degraded. Ensure you
reference configure_logging and the error handling around
SETTINGS_OBSERVABILITY_REBUILD_FAILED/subscriber_name/key when implementing the
restore.
Add per-sink enable/disable, level/format overrides, rotation config, and custom sink creation -- all hot-reloadable without restart via ObservabilitySettingsSubscriber. Also fix MSW postMessage origin check (CodeQL alert #149, CWE-020/CWE-940). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-reviewed by 4 agents, 13 findings addressed: - Wrap SinkBuildResult.routing_overrides with MappingProxyType - Re-raise MemoryError/RecursionError in all except blocks - Split LogLevel parse from sink config parse for accurate error msgs - Widen configure_logging catch and fix misleading error message - Add contextual error messages for int() rotation field conversion - Validate file_path is a non-empty string (reject null/int/bool) - Add 8 missing edge case tests (rotation strategy, routing type, multiple simultaneous overrides, custom rotation, log_dir, etc.) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Convert TypeError to ValueError in sink_config_builder for consistent
public API contract (callers use `except ValueError`)
- Require actual JSON booleans for enabled/json_format (reject string
"false" which is truthy in Python)
- Add type check in _parse_level for non-string JSON values
- Validate unknown fields in sink_overrides, custom_sinks, and rotation
dicts (reject typos like "levle" instead of "level")
- Add rotation type check (reject "rotation": "disabled")
- Validate enable_correlation against {"true", "false"} instead of
silently coercing any non-"true" to False
- Add asyncio.Lock for serializing concurrent pipeline rebuilds
- Use asyncio.gather for parallel settings reads
- Extract helpers to satisfy <50 line function limit
- Add stderr fallback for configure_logging failure path
- Accept Mapping type for routing_overrides (eliminate MappingProxyType
-> dict -> MappingProxyType round-trip)
- Add yaml_path for sink_overrides and custom_sinks settings
- Remove dead SETTINGS_OBSERVABILITY_CONSOLE_PROTECTED event constant
- Fix docstrings (preservation claim, _parse_level case docs, concurrency)
- Add library reference for sink_config_builder module
- Reduce app.py from 810 to 789 lines (inline _build_bridge, compact comment)
- Cap custom_sinks at 20 entries, routing_prefixes at 50
- Use cast() instead of type: ignore[assignment]
- Add 28 new tests: strict bool validation, unknown field rejection,
limits, enable_correlation validation, MemoryError/RecursionError
propagation, rebuild lock
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
d33f5f6 to
97042dc
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (1)
src/synthorg/settings/subscribers/observability_subscriber.py (1)
175-200:⚠️ Potential issue | 🟠 MajorA failed hot reload still tears down the live pipeline.
configure_logging()clears the root handlers before attaching the new ones. If a sink init fails here, this catch only reports the failure after teardown, so the process can be left with degraded/no logging instead of rolling back to the last known-good handler set. That still misses the safe reattach requirement from the PR objective.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/settings/subscribers/observability_subscriber.py` around lines 175 - 200, The try/except around configure_logging allows the old logging pipeline to be torn down before a failing reconfigure can roll back; change the flow in observability_subscriber.py so configure_logging is applied to a temporary/new handler set and only swapped into the root loggers on success (or restore the previous handlers on exception). Specifically, update the code around the configure_logging call so it does not clear root handlers in-place: call a non-destructive initialization path or build the new handlers first, then atomically replace the root handlers (or reattach the saved old handlers on any Exception) before logging SETTINGS_OBSERVABILITY_REBUILD_FAILED using self.subscriber_name and key; ensure MemoryError/RecursionError still propagate.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/design/operations.md`:
- Around line 1415-1417: Update the sentence describing hot-reload behavior to
reflect that ObservabilitySettingsSubscriber triggers a full logging pipeline
rebuild via configure_logging() not only when sink settings change but also when
root_log_level or enable_correlation change; mention all three triggers (sink
settings, root_log_level, enable_correlation) and keep the note that
configure_logging() is idempotent and custom sink file paths are reserved even
if disabled.
In `@src/synthorg/observability/sink_config_builder.py`:
- Around line 381-390: The loop that builds prefixes currently calls str(prefix)
which allows non-string JSON values to pass; change the validation in the
routing_prefixes handling (the loop over raw that builds prefixes) to require
each entry be an instance of str and non-empty after stripping: if not
isinstance(prefix, str) or not prefix.strip(): raise ValueError with the same
contextual message referencing routing_prefixes[{i}] and file_path; only then
append the stripped string to prefixes. This enforces that routing_prefixes
entries are actual non-empty strings.
- Around line 236-251: The code currently coerces values for raw["max_bytes"]
and raw["backup_count"] using int(...), which accepts booleans and truncates
floats; instead validate types and reject non-integers: for both "max_bytes" and
"backup_count" in raw, fetch value = raw["..."], ensure type(value) is int (or
use isinstance(value, int) and explicitly exclude bool), and if not raise
ValueError with the existing error message; only assign updates["max_bytes"] or
updates["backup_count"] when the value is a true integer.
In `@src/synthorg/settings/subscribers/observability_subscriber.py`:
- Around line 184-189: Replace the emergency fallback print() in
observability_subscriber.py (inside the configure_logging hot-reload path where
key is logged) with a direct sys.stderr.write() call and an explicit
sys.stderr.flush() (or call the project's bootstrap helper if available),
constructing the same message string (including key!r) and writing it to stderr
so it conforms to the rule banning print() outside observability/setup.py and
observability/sinks.py; ensure you remove the print(...) and use
sys.stderr.write(message + "\n") then sys.stderr.flush() to preserve
newline/flush behavior.
---
Duplicate comments:
In `@src/synthorg/settings/subscribers/observability_subscriber.py`:
- Around line 175-200: The try/except around configure_logging allows the old
logging pipeline to be torn down before a failing reconfigure can roll back;
change the flow in observability_subscriber.py so configure_logging is applied
to a temporary/new handler set and only swapped into the root loggers on success
(or restore the previous handlers on exception). Specifically, update the code
around the configure_logging call so it does not clear root handlers in-place:
call a non-destructive initialization path or build the new handlers first, then
atomically replace the root handlers (or reattach the saved old handlers on any
Exception) before logging SETTINGS_OBSERVABILITY_REBUILD_FAILED using
self.subscriber_name and key; ensure MemoryError/RecursionError still propagate.
🪄 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: a86df6f5-10ad-4347-88e9-9b03920aaf31
📒 Files selected for processing (15)
docs/api/observability.mddocs/design/operations.mdsrc/synthorg/api/app.pysrc/synthorg/observability/events/settings.pysrc/synthorg/observability/setup.pysrc/synthorg/observability/sink_config_builder.pysrc/synthorg/observability/sinks.pysrc/synthorg/settings/definitions/observability.pysrc/synthorg/settings/subscribers/__init__.pysrc/synthorg/settings/subscribers/observability_subscriber.pytests/integration/observability/test_runtime_sink_config_integration.pytests/unit/observability/test_sink_config_builder.pytests/unit/observability/test_sink_routing.pytests/unit/settings/test_observability_subscriber.pyweb/public/mockServiceWorker.js
📜 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: Dashboard Test
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{py,ts,tsx,md}
📄 CodeRabbit inference engine (CLAUDE.md)
Vendor-agnostic everywhere: NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples; use generic names: example-provider, example-large-001, etc.; vendor names only in: design spec, .claude/ files, third-party imports, provider presets
Files:
docs/api/observability.mdtests/unit/observability/test_sink_routing.pysrc/synthorg/settings/subscribers/__init__.pysrc/synthorg/api/app.pysrc/synthorg/observability/setup.pysrc/synthorg/observability/events/settings.pydocs/design/operations.mdsrc/synthorg/observability/sinks.pytests/unit/settings/test_observability_subscriber.pysrc/synthorg/settings/definitions/observability.pysrc/synthorg/settings/subscribers/observability_subscriber.pysrc/synthorg/observability/sink_config_builder.pytests/integration/observability/test_runtime_sink_config_integration.pytests/unit/observability/test_sink_config_builder.py
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotations—Python 3.14 has PEP 649
PEP 758 except syntax: useexcept A, B:(no parentheses)—ruff enforces this on Python 3.14
Type hints: all public functions, mypy strict mode
Docstrings: Google style, required on public classes/functions (enforced by ruff D rules)
Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement
Config vs runtime state: frozen Pydantic models for config/identity; separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model
Models: Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Adopted conventions: use allow_inf_nan=False in all ConfigDict declarations; use@computed_fieldfor derived values; use NotBlankStr for all identifier/name fields
Async concurrency: prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code; prefer structured concurrency over bare create_task
Line length: 88 characters (ruff)
Functions: < 50 lines, files < 800 lines
Errors: handle explicitly, never silently swallow
Validate: at system boundaries (user input, external APIs, config files)
Files:
tests/unit/observability/test_sink_routing.pysrc/synthorg/settings/subscribers/__init__.pysrc/synthorg/api/app.pysrc/synthorg/observability/setup.pysrc/synthorg/observability/events/settings.pysrc/synthorg/observability/sinks.pytests/unit/settings/test_observability_subscriber.pysrc/synthorg/settings/definitions/observability.pysrc/synthorg/settings/subscribers/observability_subscriber.pysrc/synthorg/observability/sink_config_builder.pytests/integration/observability/test_runtime_sink_config_integration.pytests/unit/observability/test_sink_config_builder.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Markers:@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slow
Async: asyncio_mode = 'auto'—no manual@pytest.mark.asyncioneeded
Timeout: 30 seconds per test (global in pyproject.toml); non-default overrides like timeout(60) are allowed
Parametrize: Prefer@pytest.mark.parametrizefor testing similar cases
Property-based testing: Python uses Hypothesis (@given+@settings); Hypothesis profiles: ci (50 examples, default) and dev (1000 examples); run dev profile: HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties
Flaky tests: NEVER skip, dismiss, or ignore flaky tests—always fix them fully and fundamentally; for timing-sensitive tests, mock time.monotonic() and asyncio.sleep(); for tasks that must block indefinitely, use asyncio.Event().wait()
Files:
tests/unit/observability/test_sink_routing.pytests/unit/settings/test_observability_subscriber.pytests/integration/observability/test_runtime_sink_config_integration.pytests/unit/observability/test_sink_config_builder.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic MUST have: from synthorg.observability import get_logger then logger = get_logger(name)
Variable name: always logger (not _logger, not log)
Event names: always use constants from domain-specific modules under synthorg.observability.events; import directly from synthorg.observability.events.
Structured kwargs: always logger.info(EVENT, key=value)—never logger.info('msg %s', val)
All error paths must log at WARNING or ERROR with context before raising
All state transitions must log at INFO
DEBUG for object creation, internal flow, entry/exit of key functions
All provider calls go through BaseCompletionProvider which applies retry + rate limiting automatically; never implement retry logic in driver subclasses or calling code
Files:
src/synthorg/settings/subscribers/__init__.pysrc/synthorg/api/app.pysrc/synthorg/observability/setup.pysrc/synthorg/observability/events/settings.pysrc/synthorg/observability/sinks.pysrc/synthorg/settings/definitions/observability.pysrc/synthorg/settings/subscribers/observability_subscriber.pysrc/synthorg/observability/sink_config_builder.py
src/synthorg/**/!(setup|sinks).py
📄 CodeRabbit inference engine (CLAUDE.md)
Never use import logging / logging.getLogger() / print() in application code; exception: observability/setup.py and observability/sinks.py may use stdlib logging and print() for bootstrap code
Files:
src/synthorg/settings/subscribers/__init__.pysrc/synthorg/api/app.pysrc/synthorg/observability/events/settings.pysrc/synthorg/settings/definitions/observability.pysrc/synthorg/settings/subscribers/observability_subscriber.pysrc/synthorg/observability/sink_config_builder.py
🧠 Learnings (41)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Settings: Runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge, ConfigResolver (typed composed reads for controllers), validation, registry, change notifications via message bus. Per-namespace setting definitions in definitions/ submodule (api, company, providers, memory, budget, security, coordination, observability, backup).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability includes structured logging via `get_logger(__name__)`, correlation tracking, and log sinks.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability package (observability/): structured logging, correlation tracking, log sinks; event constants organized by domain under observability/events/ (e.g., events.api, events.tool, events.git, events.context_budget, events.backup)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability must use structured logging with correlation tracking and log sinks
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability package (observability/): structured logging, correlation tracking, log sinks; event constants organized by domain under observability/events/ (e.g., events.api, events.tool, events.git, events.context_budget, events.backup)
Applied to files:
docs/api/observability.mdsrc/synthorg/settings/subscribers/__init__.pysrc/synthorg/api/app.pysrc/synthorg/observability/setup.pysrc/synthorg/observability/events/settings.pydocs/design/operations.mdsrc/synthorg/observability/sinks.pytests/unit/settings/test_observability_subscriber.pysrc/synthorg/settings/definitions/observability.pysrc/synthorg/settings/subscribers/observability_subscriber.pysrc/synthorg/observability/sink_config_builder.pytests/integration/observability/test_runtime_sink_config_integration.pytests/unit/observability/test_sink_config_builder.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability includes structured logging via `get_logger(__name__)`, correlation tracking, and log sinks.
Applied to files:
tests/unit/observability/test_sink_routing.pysrc/synthorg/api/app.pysrc/synthorg/observability/setup.pydocs/design/operations.mdsrc/synthorg/observability/sinks.pytests/unit/settings/test_observability_subscriber.pysrc/synthorg/settings/definitions/observability.pysrc/synthorg/settings/subscribers/observability_subscriber.pysrc/synthorg/observability/sink_config_builder.pytests/integration/observability/test_runtime_sink_config_integration.pytests/unit/observability/test_sink_config_builder.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability must use structured logging with correlation tracking and log sinks
Applied to files:
tests/unit/observability/test_sink_routing.pysrc/synthorg/observability/setup.pydocs/design/operations.mdsrc/synthorg/observability/sinks.pysrc/synthorg/settings/definitions/observability.pysrc/synthorg/settings/subscribers/observability_subscriber.pysrc/synthorg/observability/sink_config_builder.pytests/integration/observability/test_runtime_sink_config_integration.pytests/unit/observability/test_sink_config_builder.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)
Applied to files:
src/synthorg/settings/subscribers/__init__.pysrc/synthorg/api/app.pysrc/synthorg/observability/events/settings.pydocs/design/operations.mdtests/unit/settings/test_observability_subscriber.pysrc/synthorg/settings/definitions/observability.pysrc/synthorg/settings/subscribers/observability_subscriber.py
📚 Learning: 2026-03-30T16:36:12.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T16:36:12.059Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events; import directly from synthorg.observability.events.<domain>
Applied to files:
src/synthorg/settings/subscribers/__init__.pysrc/synthorg/observability/events/settings.py
📚 Learning: 2026-03-18T21:23:23.586Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T21:23:23.586Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from the domain-specific module under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly from synthorg.observability.events.<domain>.
Applied to files:
src/synthorg/settings/subscribers/__init__.pysrc/synthorg/observability/events/settings.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/communication/**/*.py : Communication package (communication/): message bus, dispatcher, messenger, channels, delegation, loop prevention, conflict resolution; meeting/ subpackage for meeting protocol (round-robin, position papers, structured phases), scheduler (frequency, participant resolver), orchestrator
Applied to files:
src/synthorg/api/app.py
📚 Learning: 2026-03-30T16:36:12.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T16:36:12.059Z
Learning: Applies to src/synthorg/**/!(setup|sinks).py : Never use import logging / logging.getLogger() / print() in application code; exception: observability/setup.py and observability/sinks.py may use stdlib logging and print() for bootstrap code
Applied to files:
src/synthorg/observability/setup.pysrc/synthorg/settings/subscribers/observability_subscriber.py
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions. Pure data models, enums, and re-exports do NOT need logging.
Applied to files:
src/synthorg/observability/setup.pysrc/synthorg/settings/subscribers/observability_subscriber.pysrc/synthorg/observability/sink_config_builder.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from synthorg.observability.events domain-specific modules (e.g., PROVIDER_CALL_START from events.provider). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Applied to files:
src/synthorg/observability/events/settings.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/observability/events/settings.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/observability/events/settings.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use event name constants from domain-specific modules under `ai_company.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`). Import directly: `from ai_company.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/observability/events/settings.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Always 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`
Applied to files:
src/synthorg/observability/events/settings.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly rather than using string literals
Applied to files:
src/synthorg/observability/events/settings.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings use runtime-editable persistence with precedence: DB > env > YAML > code defaults. 8 namespaces with Fernet encryption for sensitive values.
Applied to files:
docs/design/operations.mdsrc/synthorg/settings/definitions/observability.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Settings: Runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge, ConfigResolver (typed composed reads for controllers), validation, registry, change notifications via message bus. Per-namespace setting definitions in definitions/ submodule (api, company, providers, memory, budget, security, coordination, observability, backup).
Applied to files:
docs/design/operations.md
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (without parentheses) per PEP 758 for exception handling in Python 3.14
Applied to files:
src/synthorg/settings/subscribers/observability_subscriber.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to **/*.py : Handle errors explicitly; never silently swallow exceptions
Applied to files:
src/synthorg/settings/subscribers/observability_subscriber.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (no parentheses) for exception handling — PEP 758 exception syntax enforced by ruff on Python 3.14
Applied to files:
src/synthorg/settings/subscribers/observability_subscriber.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to **/*.py : Handle errors explicitly, never silently swallow exceptions
Applied to files:
src/synthorg/settings/subscribers/observability_subscriber.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to **/*.py : Handle errors explicitly—never silently swallow exceptions.
Applied to files:
src/synthorg/settings/subscribers/observability_subscriber.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to **/*.py : Use PEP 758 except syntax with `except A, B:` (no parentheses) for multiple exceptions—ruff enforces this on Python 3.14.
Applied to files:
src/synthorg/settings/subscribers/observability_subscriber.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to **/*.py : Use PEP 758 except syntax: `except A, B:` (no parentheses) — enforced by ruff on Python 3.14
Applied to files:
src/synthorg/settings/subscribers/observability_subscriber.py
📚 Learning: 2026-03-30T16:36:12.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T16:36:12.059Z
Learning: Applies to **/*.py : PEP 758 except syntax: use `except A, B:` (no parentheses)—ruff enforces this on Python 3.14
Applied to files:
src/synthorg/settings/subscribers/observability_subscriber.py
📚 Learning: 2026-03-15T16:55:07.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T16:55:07.730Z
Learning: Applies to **/*.py : Use PEP 758 except syntax: use `except A, B:` (no parentheses) — ruff enforces this on Python 3.14.
Applied to files:
src/synthorg/settings/subscribers/observability_subscriber.py
📚 Learning: 2026-03-30T16:36:12.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T16:36:12.059Z
Learning: Applies to **/*.py : Errors: handle explicitly, never silently swallow
Applied to files:
src/synthorg/settings/subscribers/observability_subscriber.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : All error paths must log at WARNING or ERROR with context before raising.
Applied to files:
src/synthorg/settings/subscribers/observability_subscriber.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions.
Applied to files:
src/synthorg/settings/subscribers/observability_subscriber.pysrc/synthorg/observability/sink_config_builder.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, and key function entry/exit
Applied to files:
src/synthorg/settings/subscribers/observability_subscriber.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have `from synthorg.observability import get_logger` followed by `logger = get_logger(__name__)`.
Applied to files:
src/synthorg/observability/sink_config_builder.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`
Applied to files:
src/synthorg/observability/sink_config_builder.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger` (not `_logger`, not `log`).
Applied to files:
src/synthorg/observability/sink_config_builder.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import logger via `from synthorg.observability import get_logger` and initialize with `logger = get_logger(__name__)`
Applied to files:
src/synthorg/observability/sink_config_builder.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use import logging / logging.getLogger() / print() in application code.
Applied to files:
src/synthorg/observability/sink_config_builder.py
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger`.
Applied to files:
src/synthorg/observability/sink_config_builder.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import `from synthorg.observability import get_logger` and define `logger = get_logger(__name__)`
Applied to files:
src/synthorg/observability/sink_config_builder.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Handle errors explicitly, never silently swallow. Validate at system boundaries (user input, external APIs, config files).
Applied to files:
src/synthorg/observability/sink_config_builder.py
📚 Learning: 2026-03-30T16:36:12.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T16:36:12.059Z
Learning: Applies to **/*.py : Validate: at system boundaries (user input, external APIs, config files)
Applied to files:
src/synthorg/observability/sink_config_builder.py
📚 Learning: 2026-03-16T20:14:00.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:14:00.937Z
Learning: Applies to **/*.py : Validate: at system boundaries (user input, external APIs, config files).
Applied to files:
src/synthorg/observability/sink_config_builder.py
🔇 Additional comments (14)
web/public/mockServiceWorker.js (1)
24-27: Good security hardening with early same-origin guard.This is a solid fix for message-source validation and correctly short-circuits untrusted origins before any client resolution or command handling.
src/synthorg/api/app.py (3)
403-406: No review action needed for this comment-only clarification.
617-621: Bridge initialization refactor is clean and equivalent.The inline conditional keeps behavior intact while reducing indirection.
726-732: Observability subscriber registration is correctly integrated.The new subscriber is added with an explicit
log_dirfallback and included in dispatcher subscribers.src/synthorg/settings/definitions/observability.py (1)
35-70: Good addition of runtime sink settings definitions.The new
sink_overridesandcustom_sinksregistrations are consistent with the observability settings schema and are correctly wired as JSON settings.docs/api/observability.md (1)
37-39: Docs update is aligned with the new observability surface.Adding the
sink_config_builderAPI section here is clear and discoverable.tests/unit/observability/test_sink_routing.py (2)
7-7: Nice move to assert via the public routing table.Switching tests to
SINK_ROUTINGimproves API-level contract coverage.
70-71: Routing assertions look solid and explicit.The updated sink/prefix checks are clear and keep the expected routing surface tightly validated.
Also applies to: 74-75, 80-81, 86-87, 115-115, 118-130
src/synthorg/settings/subscribers/__init__.py (1)
9-11: Subscriber re-export is correctly wired.
ObservabilitySettingsSubscriberis properly surfaced through the subscribers package API.Also applies to: 19-19
src/synthorg/observability/setup.py (2)
183-217: Routing override merge logic is implemented well.Copy-merge +
MappingProxyTypekeeps default routing immutable while supporting runtime extension.
327-331:configure_loggingextension is clean and consistent.The new
routing_overridesparameter is properly documented and passed through to handler attachment.Also applies to: 344-347, 375-380
src/synthorg/observability/events/settings.py (1)
35-45: New settings-observability event constants look consistent.Naming and value patterns match the existing event taxonomy in this module.
src/synthorg/observability/sinks.py (2)
57-77: PublicSINK_ROUTINGexport is a good API improvement.This makes routing expectations explicit and testable without relying on private internals.
207-209: Handler-level routing injection is correctly wired.
build_handlernow cleanly supports override routing while preserving the default routing behavior.Also applies to: 220-223, 227-240
- operations.md: clarify all 4 watched keys trigger pipeline rebuild - sink_config_builder: require isinstance(str) for routing prefixes instead of str() coercion that silently accepts non-strings - sink_config_builder: reject bool and float for max_bytes/backup_count (int(True)==1 and int(3.7)==3 were silently accepted) - observability_subscriber: replace print() with sys.stderr.write() to comply with CLAUDE.md print() ban outside setup.py/sinks.py Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🤖 I have created a release *beep* *boop* --- ## [0.5.2](v0.5.1...v0.5.2) (2026-03-31) ### Features * harden activity feed API ([#838](#838), [#839](#839), [#840](#840)) ([#937](#937)) ([c0234ad](c0234ad)) * provider usage metrics, model capabilities, and active health probing ([#935](#935)) ([1434c9c](1434c9c)) * runtime sink configuration via SettingsService ([#934](#934)) ([16c3f23](16c3f23)) * Settings page comprehensive redesign ([#936](#936)) ([#939](#939)) ([6d9ac8b](6d9ac8b)) ### Maintenance * bump astro from 6.1.1 to 6.1.2 in /site in the all group ([#940](#940)) ([ffa24f0](ffa24f0)) * bump pygments from 2.19.2 to 2.20.0 ([#931](#931)) ([9993088](9993088)) * bump the all group with 2 updates ([#942](#942)) ([aea37f8](aea37f8)) * bump typescript-eslint from 8.57.2 to 8.58.0 in /web in the all group ([#941](#941)) ([24f024c](24f024c)) * split CLAUDE.md into subdirectory files for cli/ and web/ ([#932](#932)) ([f5cfe07](f5cfe07)) --- 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>
Summary
ObservabilitySettingsSubscribersink_overrides,custom_sinks) in the observability namespace with full validation (console sink protection, path traversal prevention, duplicate path detection)configure_loggingandbuild_handlerto support custom routing overrides for user-defined sinkspostMessagehandler missing origin check (CodeQL alert feat: implement crash recovery with fail-and-reassign strategy #149, CWE-020/CWE-940)Test plan
sink_config_builder(overrides, custom sinks, validation, combined, edge cases)ObservabilitySettingsSubscriber(protocol, rebuild, error handling, namespace guard, idempotency)Closes #564
🤖 Generated with Claude Code