feat: harden activity feed API (#838, #839, #840)#937
Conversation
Validate event_type filter against ActivityEventType enum (400 on invalid values), redact model names and costs from cost_incurred events for read-only roles, and add degraded_sources field to PaginatedResponse so API consumers can detect partial data. Closes #838 Closes #839 Closes #840 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Budget config handler now guards MemoryError/RecursionError/ ServiceUnavailableError (consistency with all other fetchers) - Cost redaction is fail-closed: regex non-match produces generic fallback instead of leaking data; missing auth user also redacts - ActivityEvent.event_type typed as ActivityEventType (not NotBlankStr) - PaginatedResponse adds allow_inf_nan=False per convention - Degraded source names extracted to module-level constants - _build_timeline helper extracted to reduce list_activities size - Duplicate test boilerplate consolidated into _make_app_with_broken_tracker - Defensive test for ActivityEventType superset invariant - Round-trip test for regex/format coupling in cost redaction - Explicit parentheses on _safe_delegation_query return Pre-reviewed by 6 agents, 12 findings addressed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WalkthroughAdds a typed Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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 |
There was a problem hiding this comment.
Code Review
This pull request implements graceful degradation and role-based redaction for the activity feed, refactoring the timeline construction and updating the API response to include degraded source information. Critical syntax errors were identified in several exception handling blocks within the activities controller where multiple exceptions are not correctly parenthesized as tuples, which will cause runtime failures in Python 3.
| try: | ||
| budget_cfg = await app_state.config_resolver.get_budget_config() | ||
| currency = budget_cfg.currency | ||
| except MemoryError, RecursionError: |
There was a problem hiding this comment.
This line contains a syntax error. In Python 3, multiple exceptions in an except clause must be parenthesized as a tuple. Using a comma without parentheses is interpreted as the old Python 2 syntax for assigning the exception to a variable, which is a SyntaxError in Python 3 when multiple types are provided.
except (MemoryError, RecursionError):| until=now, | ||
| ) | ||
| ), False | ||
| except MemoryError, RecursionError: |
| end=now, | ||
| ) | ||
| ), False | ||
| except MemoryError, RecursionError: |
| end=now, | ||
| ) | ||
| ), False | ||
| except MemoryError, RecursionError: |
| try: | ||
| return await coro | ||
| return (await coro), False | ||
| except MemoryError, RecursionError: |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #937 +/- ##
==========================================
+ Coverage 92.18% 92.21% +0.03%
==========================================
Files 600 600
Lines 31915 31940 +25
Branches 3109 3108 -1
==========================================
+ Hits 29420 29454 +34
+ Misses 1966 1957 -9
Partials 529 529 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/synthorg/api/controllers/activities.py`:
- Around line 61-177: _build_timeline is too long and mutates a shared degraded
list across many exception branches; split it into clear helpers: (1) create an
_orchestrate_fetches(app_state, agent_id, since, now) that runs
_fetch_cost_records, _fetch_tool_invocations, and _fetch_delegation_records
inside an asyncio.TaskGroup and returns the fetch results or None plus an
immutable tuple/list of degraded source names for those fetches, (2) create
_collect_task_metrics(app_state, agent_id, since, now) that returns task_metrics
and any degraded source names from _fetch_task_metrics without mutating external
state, and (3) create _resolve_currency(app_state) that encapsulates the budget
config await and returns (currency, optional_degraded_name) handling the same
exception mapping; then have _build_timeline call these helpers, combine their
degraded lists into a new list (do not mutate a shared list), and pass results
into merge_activity_timeline before returning (ensure helper names
_orchestrate_fetches, _collect_task_metrics, _resolve_currency, and existing
fetch functions like _fetch_task_metrics, _fetch_cost_records,
_fetch_tool_invocations, _fetch_delegation_records are used to locate code).
- Around line 108-114: The except ExceptionGroup as eg block is re-raising the
subgroup object (eg.subgroup(ServiceUnavailableError)) instead of the underlying
ServiceUnavailableError instance; update that block so after obtaining svc =
eg.subgroup(ServiceUnavailableError) you extract the contained
ServiceUnavailableError from the subgroup (via the subgroup's exceptions list)
and raise that actual exception from eg (preserving the original cause), keeping
the existing handling for fatal = eg.subgroup((MemoryError, RecursionError))
unchanged.
In `@src/synthorg/api/dto.py`:
- Around line 198-201: The degraded_sources field currently uses tuple[str, ...]
which permits blank strings; change its type to tuple[NotBlankStr, ...] and
update the Field declaration to use that type (keep the same default and
description). Import NotBlankStr from core.types if not already imported, and
ensure any downstream uses/annotations referencing degraded_sources reflect the
new tuple[NotBlankStr, ...] type.
In `@src/synthorg/hr/activity.py`:
- Around line 262-265: The warning currently passes the raw event.description to
logger.warning (HR_ACTIVITY_REDACTION_MISMATCH) which can leak sensitive
model/cost text; replace that argument with a sanitized fallback (e.g., a
redacted or masked version, summary, or safe context stub) derived from
event.description so no raw cost/model tokens are logged. Locate the
logger.warning call referencing HR_ACTIVITY_REDACTION_MISMATCH and
event.description and change the description parameter to a scrubbed value (for
example a redacted_description produced by applying the same redaction regex or
returning a fixed "[REDACTED]"/safe excerpt) while keeping the fallback behavior
and context but never emitting unredacted content.
In `@tests/unit/api/controllers/test_activities.py`:
- Around line 528-605: Replace the five nearly identical tests in
TestCostEventRedaction with a single parametrized test (e.g.,
test_cost_description_redaction_parametrized) using `@pytest.mark.parametrize`
over role and expected redaction outcomes; inside the test call await
cost_tracker.record(_make_cost_record()), request GET "/api/v1/activities" with
headers=make_auth_headers(role), assert resp.status_code == 200, extract desc =
resp.json()["data"][0]["description"], and assert expectations based on the
parametrized values (e.g., whether "test-medium-001" is present or absent and
whether the redacted string "API call (500+100 tokens)" or token counts appear).
Ensure to include the same fixtures (test_client, cost_tracker) and preserve
original assertions for CEO/manager/pair_programmer (unredacted) and
observer/board_member (redacted).
🪄 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: e691a9b3-37e0-4274-b2bc-fa3a4870b344
📒 Files selected for processing (8)
src/synthorg/api/controllers/activities.pysrc/synthorg/api/dto.pysrc/synthorg/hr/activity.pysrc/synthorg/hr/enums.pysrc/synthorg/observability/events/hr.pytests/unit/api/controllers/test_activities.pytests/unit/api/test_dto.pytests/unit/hr/test_activity.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). (7)
- GitHub Check: Build Backend
- GitHub Check: Build Sandbox
- GitHub Check: Build Web
- GitHub Check: Test (Python 3.14)
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Analyze (python)
- GitHub Check: Dependency Review
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Do not usefrom __future__ import annotationsin Python code; rely on PEP 649 native lazy annotations in Python 3.14+
Useexcept A, B:syntax without parentheses for exception handling in Python 3.14, as enforced by ruff (PEP 758)
Provide type hints for all public functions in Python code; strict mypy mode is required
Use Google-style docstrings on all public classes and functions in Python, as enforced by ruff D rules
Create new objects instead of mutating existing ones; never mutate existing objects in Python code
For non-Pydantic internal collections (registries,BaseTool), usecopy.deepcopy()at construction andMappingProxyTypewrapping for read-only enforcement
Fordict/listfields in frozen Pydantic models, rely onfrozen=Truefor field reassignment prevention and usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization)
Use frozen Pydantic models for config and identity; use separate mutable-via-copy models (usingmodel_copy(update=...)) for runtime state that evolves
Never mix static config fields with mutable runtime fields in one Pydantic model
Use Pydantic v2 withBaseModel,model_validator,computed_field, andConfigDict
Useallow_inf_nan=Falsein allConfigDictdeclarations in Pydantic models to rejectNaN/Infin numeric fields at validation time
Use@computed_fieldfor derived values in Pydantic models instead of storing and validating redundant fields (e.g.,TokenUsage.total_tokens)
UseNotBlankStrfromcore.typesfor all identifier and name fields in Python code, including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants, instead of manual whitespace validators
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new Python code (e.g., multiple tool invocations, parallel agent calls); prefer structured concurrency over barecreate_task
Keep Python functions under 50 line...
Files:
tests/unit/api/test_dto.pysrc/synthorg/observability/events/hr.pysrc/synthorg/api/dto.pysrc/synthorg/hr/enums.pytests/unit/hr/test_activity.pysrc/synthorg/hr/activity.pytests/unit/api/controllers/test_activities.pysrc/synthorg/api/controllers/activities.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Mark tests with@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, or@pytest.mark.slowin Python test code
Enforce 80% minimum code coverage in Python test code, as enforced in CI
Useasyncio_mode = "auto"for pytest async tests; do not manually add@pytest.mark.asyncioin Python test files
Set a 30-second global timeout per test in Python test files (configured inpyproject.toml); do not add per-filepytest.mark.timeout(30)markers; non-default overrides liketimeout(60)are allowed
Always include-n autowhen running pytest for Python tests; never run tests sequentially (parallelism viapytest-xdist)
Use@pytest.mark.parametrizefor testing similar cases in Python test files
Usetest-provider,test-small-001, etc. in Python test code, not real vendor names
Use Hypothesis for property-based testing in Python; apply@givenand@settingsdecorators for property tests
Use Hypothesis profiles for property tests:ci(50 examples, default) anddev(1000 examples), controlled viaHYPOTHESIS_PROFILEenv var; run dev profile withHYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties
Never skip, dismiss, or ignore flaky Python tests; always fix them fully and fundamentally by mockingtime.monotonic()andasyncio.sleep()for determinism instead of widening timing margins
Useasyncio.Event().wait()for tasks that must block indefinitely until cancelled in Python tests (instead ofasyncio.sleep(large_number)), as it is cancellation-safe with no timing assumptions
Files:
tests/unit/api/test_dto.pytests/unit/hr/test_activity.pytests/unit/api/controllers/test_activities.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every Python module with business logic must import and use the logger:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)
Never useimport logging/logging.getLogger()/print()in application Python code, except inobservability/setup.pyandobservability/sinks.pywhich may use stdlibloggingandprint(..., file=sys.stderr)for bootstrap code
Always name the logger variableloggerin Python code, not_loggerorlog
Use event name constants from the domain-specific module undersynthorg.observability.eventsfor all logging in Python code (e.g.,API_REQUEST_STARTEDfromevents.api,TOOL_INVOKE_STARTfromevents.tool,GIT_COMMAND_STARTfromevents.git)
Always use structured kwargs in logger calls:logger.info(EVENT, key=value), neverlogger.info("msg %s", val)in Python code
Log at WARNING or ERROR with context before raising errors in Python code; all error paths must be logged
Log at INFO level for all state transitions in Python code
Log at DEBUG level for object creation, internal flow, and entry/exit of key functions in Python code
All provider calls must go throughBaseCompletionProviderwhich applies retry and rate limiting automatically; never implement retry logic in driver subclasses or calling code
SetRetryConfigandRateLimiterConfigper-provider inProviderConfigin Python code
Retryable errors (is_retryable=True) in Python code include:RateLimitError,ProviderTimeoutError,ProviderConnectionError,ProviderInternalError; non-retryable errors raise immediately
UseRateLimitError.retry_afterfrom providers in the rate limiter; automatically pause future requests in Python code
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned Python code, docstrings, comments, tests, or config examples; use generic names likeexample-provider,example-large-001,example-medium-001,large/medium/smallaliases in...
Files:
src/synthorg/observability/events/hr.pysrc/synthorg/api/dto.pysrc/synthorg/hr/enums.pysrc/synthorg/hr/activity.pysrc/synthorg/api/controllers/activities.py
🧠 Learnings (16)
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/hr/**/*.py : HR package (hr/): hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)
Applied to files:
src/synthorg/observability/events/hr.pytests/unit/hr/test_activity.pysrc/synthorg/hr/activity.pytests/unit/api/controllers/test_activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from the domain-specific module under `synthorg.observability.events` for all logging in Python code (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`, `GIT_COMMAND_START` from `events.git`)
Applied to files:
src/synthorg/observability/events/hr.pysrc/synthorg/hr/activity.pysrc/synthorg/api/controllers/activities.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/observability/events/hr.pysrc/synthorg/hr/activity.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 : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings
Applied to files:
src/synthorg/observability/events/hr.pysrc/synthorg/hr/activity.pysrc/synthorg/api/controllers/activities.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/hr/**/*.py : HR engine must provide: hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, trend detection), promotion/demotion
Applied to files:
src/synthorg/observability/events/hr.pytests/unit/hr/test_activity.pytests/unit/api/controllers/test_activities.py
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)
Applied to files:
tests/unit/hr/test_activity.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/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.
Applied to files:
tests/unit/hr/test_activity.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:
tests/unit/hr/test_activity.pysrc/synthorg/api/controllers/activities.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 tests/**/*.py : Fix flaky tests completely and fundamentally; for timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic instead of widening timing margins
Applied to files:
tests/unit/api/controllers/test_activities.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/engine/**/*.py : Engine package (engine/): agent orchestration, parallel execution, task decomposition, routing, TaskEngine (centralized single-writer), task lifecycle/recovery/shutdown, workspace isolation, coordination (4 dispatchers: SAS/centralized/decentralized/context-dependent, wave execution), approval gates (escalation detection, context parking/resume), stagnation detection (ToolRepetitionDetector, corrective prompt injection), AgentRuntimeState (execution status), context budget management, conversation compaction (oldest-turns summarizer)
Applied to files:
tests/unit/api/controllers/test_activities.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/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
tests/unit/api/controllers/test_activities.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/synthorg/api/**/*.py : Litestar API must include setup wizard, auth/, auto-wiring, and lifecycle management
Applied to files:
tests/unit/api/controllers/test_activities.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/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)
Applied to files:
tests/unit/api/controllers/test_activities.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 : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (via `model_copy(update=...)`) for runtime state that evolves
Applied to files:
tests/unit/api/controllers/test_activities.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: Engine: Agent orchestration, execution loops, parallel execution, task decomposition, routing, task assignment, centralized single-writer task state engine (TaskEngine), task lifecycle, recovery, shutdown, workspace isolation, coordination (multi-agent pipeline: TopologyDispatcher protocol, 4 dispatchers — SAS/centralized/decentralized/context-dependent, wave execution, workspace lifecycle integration, CoordinationSectionConfig company config bridge, build_coordinator factory), coordination error classification, prompt policy validation, checkpoint recovery (checkpoint/, per-turn persistence, heartbeat detection, CheckpointRecoveryStrategy), approval gate (escalation detection, context parking/resume, EscalationInfo/ResumePayload models), stagnation detection (stagnation/, StagnationDetector protocol, ToolRepetitionDetector, dual-signal analysis, corrective prompt injection), agent runtime state (AgentRuntimeState, lightweight per-agent execution status for dashboard queries and recove...
Applied to files:
tests/unit/api/controllers/test_activities.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/api/controllers/activities.py
Source fixes (13 files): - Decompose _build_timeline into 3 functions (<50 lines each) - Fix ExceptionGroup to check per-task instead of blanket degradation - Add has_write_role() public helper in guards.py (replaces _WRITE_ROLES import) - Make _fetch_task_metrics async for consistency - Parallelize delegation queries with TaskGroup - Fix config_resolver ServiceUnavailableError to degrade gracefully - Tighten _COST_DESC_PATTERN regex anchoring ([^(]+, [^)]+) - Replace assert with explicit ValueError guard in activity.py - Rename loop variable to redacted_event (remove noqa) - Upgrade CareerEvent.event_type from NotBlankStr to LifecycleEventType - Add import-time superset assertion for ActivityEventType - Split provider DTOs to dto_providers.py (dto.py: 835->518 lines) - Sweep allow_inf_nan=False across all ConfigDict in dto.py - Fix "USD" -> "base currency" in CreateTaskRequest docstring - Deduplicate degraded_sources via dict.fromkeys() - Fix 5 docstrings (build_timeline, redact_cost_events, list_activities) Frontend fixes: - Add degraded_sources field to PaginatedResponse type - Add ActivityEvent interface matching backend schema - Fix ActivityItem.action_type from WsEventType to ActivityEventType - Add filtering params (type, agent_id, last_n_hours) to listActivities Test improvements (149 tests pass): - Parametrize 5 cost redaction role tests into 2 - Add tests: cost tracker, budget config, ServiceUnavailableError, unauthenticated redaction, mixed timeline order - Fix weak >= 1 assertions to exact counts - Module-level pytestmark replaces per-class decorators Documentation: - CLAUDE.md hr/ description: add event types, cost redaction - agents.md, operations.md: update activity endpoint descriptions Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add __all__ to dto.py for explicit re-export of provider DTOs - Fix type narrowing for del_task.result() (inline condition) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extract underlying exception from ExceptionGroup subgroup before re-raising (Litestar won't match ExceptionGroup wrappers) - Use NotBlankStr for degraded_sources field (CLAUDE.md convention) - Remove unredacted description from redaction-mismatch log warning Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/src/api/types.ts (1)
554-562:⚠️ Potential issue | 🟠 MajorType mismatch in
ActivityItem: interface declaresActivityEventTypebut receivesWsEventTypevalues.
wsEventToActivityItem(line 141) assignsevent.event_type(aWsEventTypestring like'task.created','agent.hired') directly to theaction_typefield, which theActivityIteminterface declares asActivityEventType(enum values like'task_started','hired').This breaks type safety in two places:
- Type violation:
WsEventTypevalues assigned toActivityEventType-typed field- Component error:
ActivityFeedItem.getActionDotColor(activity.action_type)passesActivityEventTypeto a function expectingWsEventType(line 44)The values are incompatible:
WsEventType:'task.created','task.status_changed','agent.hired','agent.fired','agent.status_changed','budget.record_added','budget.alert','message.sent','task.updated','task.assigned'ActivityEventType:'task_started','task_completed','status_changed','hired','fired','cost_incurred','tool_used','delegation_sent','delegation_received','promoted','demoted','onboarded','offboarded'Options to resolve:
- Change
ActivityItem.action_typetoWsEventTypeif only WebSocket events should be stored- Map
WsEventType→ActivityEventTypevalues inwsEventToActivityItemfor consistency with the interface- Update
ActivityEventTypeto include all event types and ensureACTION_DOT_COLORSkeys align with the enum🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/api/types.ts` around lines 554 - 562, ActivityItem.action_type currently expects ActivityEventType but wsEventToActivityItem assigns raw WsEventType values, causing type mismatches and downstream errors (e.g., ActivityFeedItem.getActionDotColor). Fix by translating WsEventType → ActivityEventType inside wsEventToActivityItem (map each WsEventType string like 'task.created'/'agent.hired' etc. to the corresponding ActivityEventType token), update ACTION_DOT_COLORS keys to match ActivityEventType if needed, and ensure ActivityFeedItem.getActionDotColor and any consumers use ActivityEventType; alternatively if you prefer storing raw WS values, change ActivityItem.action_type to WsEventType and adjust consumers accordingly — pick one approach and make the corresponding consistent change across ActivityItem, wsEventToActivityItem, ACTION_DOT_COLORS, and ActivityFeedItem.getActionDotColor.
♻️ Duplicate comments (2)
src/synthorg/api/dto.py (1)
194-197:⚠️ Potential issue | 🟡 MinorUse
NotBlankStrfordegraded_sources.These values are source identifiers on a public model, and
tuple[str, ...]still accepts blank strings. The field type should betuple[NotBlankStr, ...]per coding guidelines.♻️ Proposed fix
- degraded_sources: tuple[str, ...] = Field( + degraded_sources: tuple[NotBlankStr, ...] = Field( default=(), description="Data sources that failed gracefully (partial data)", )As per coding guidelines, "Use
NotBlankStrfromcore.typesfor all identifier and name fields in Python code, including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants, instead of manual whitespace validators."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/dto.py` around lines 194 - 197, The degraded_sources field currently uses tuple[str, ...] which allows blank strings; change its type to tuple[NotBlankStr, ...] and import NotBlankStr from core.types, updating the Field declaration for degraded_sources to use the NotBlankStr tuple type so identifiers cannot be blank (search for the degraded_sources Field in the DTO and replace its annotation and import accordingly).src/synthorg/api/controllers/activities.py (1)
121-127:⚠️ Potential issue | 🟠 MajorExtract and re-raise the underlying
ServiceUnavailableError.Line 127 raises the
ExceptionGroupsubgroup (svc), not the underlyingServiceUnavailableError. Litestar's exception handlers useisinstancechecks and won't matchExceptionGroup, causing the error to fall through to the generic 500 handler instead of the expected 503.🐛 Proposed fix
svc = eg.subgroup(ServiceUnavailableError) if svc is not None: - raise svc from eg + raise svc.exceptions[0] from eg🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/controllers/activities.py` around lines 121 - 127, The except block catches an ExceptionGroup (eg) and currently re-raises the subgroup object (svc) rather than the actual ServiceUnavailableError instance, so Litestar’s isinstance-based handlers won’t match; change the logic after svc = eg.subgroup(ServiceUnavailableError) to extract the real ServiceUnavailableError from svc (e.g., find the first exception in svc.exceptions that is an instance of ServiceUnavailableError) and raise that concrete exception (raise the ServiceUnavailableError instance from eg) instead of raising the ExceptionGroup subgroup object; keep the existing handling for fatal = eg.subgroup((MemoryError, RecursionError)) as-is.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/api/endpoints/activities.ts`:
- Around line 10-14: The API now returns ActivityEvent but your stores expect
ActivityItem, so add a transformation layer that maps ActivityEvent →
ActivityItem before writing to state (either inside listActivities or via a new
mapper function like mapActivityEventToItem); specifically map event_type →
action_type, pull agent_name, task_id, department from related_ids (e.g.
related_ids['agent_name'] etc.), ensure id is populated (use ActivityEvent.id if
present or derive a stable id from event fields), and return/store readonly
ActivityItem[] so BudgetState and AnalyticsState (and ActivityFeedItem) receive
the expected shape; update places that call listActivities or that assign
activities (budget.ts and analytics.ts) to use the mapper output.
---
Outside diff comments:
In `@web/src/api/types.ts`:
- Around line 554-562: ActivityItem.action_type currently expects
ActivityEventType but wsEventToActivityItem assigns raw WsEventType values,
causing type mismatches and downstream errors (e.g.,
ActivityFeedItem.getActionDotColor). Fix by translating WsEventType →
ActivityEventType inside wsEventToActivityItem (map each WsEventType string like
'task.created'/'agent.hired' etc. to the corresponding ActivityEventType token),
update ACTION_DOT_COLORS keys to match ActivityEventType if needed, and ensure
ActivityFeedItem.getActionDotColor and any consumers use ActivityEventType;
alternatively if you prefer storing raw WS values, change
ActivityItem.action_type to WsEventType and adjust consumers accordingly — pick
one approach and make the corresponding consistent change across ActivityItem,
wsEventToActivityItem, ACTION_DOT_COLORS, and
ActivityFeedItem.getActionDotColor.
---
Duplicate comments:
In `@src/synthorg/api/controllers/activities.py`:
- Around line 121-127: The except block catches an ExceptionGroup (eg) and
currently re-raises the subgroup object (svc) rather than the actual
ServiceUnavailableError instance, so Litestar’s isinstance-based handlers won’t
match; change the logic after svc = eg.subgroup(ServiceUnavailableError) to
extract the real ServiceUnavailableError from svc (e.g., find the first
exception in svc.exceptions that is an instance of ServiceUnavailableError) and
raise that concrete exception (raise the ServiceUnavailableError instance from
eg) instead of raising the ExceptionGroup subgroup object; keep the existing
handling for fatal = eg.subgroup((MemoryError, RecursionError)) as-is.
In `@src/synthorg/api/dto.py`:
- Around line 194-197: The degraded_sources field currently uses tuple[str, ...]
which allows blank strings; change its type to tuple[NotBlankStr, ...] and
import NotBlankStr from core.types, updating the Field declaration for
degraded_sources to use the NotBlankStr tuple type so identifiers cannot be
blank (search for the degraded_sources Field in the DTO and replace its
annotation and import accordingly).
🪄 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: 6b143ac4-9154-4c4e-a6ff-be13fd4153b4
📒 Files selected for processing (13)
CLAUDE.mddocs/design/agents.mddocs/design/operations.mdsrc/synthorg/api/controllers/activities.pysrc/synthorg/api/dto.pysrc/synthorg/api/dto_providers.pysrc/synthorg/api/guards.pysrc/synthorg/hr/activity.pysrc/synthorg/hr/enums.pytests/unit/api/controllers/test_activities.pytests/unit/hr/test_activity.pyweb/src/api/endpoints/activities.tsweb/src/api/types.ts
📜 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: Build Sandbox
- GitHub Check: Build Backend
- GitHub Check: Dashboard Test
- GitHub Check: Test (Python 3.14)
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Do not usefrom __future__ import annotationsin Python code; rely on PEP 649 native lazy annotations in Python 3.14+
Useexcept A, B:syntax without parentheses for exception handling in Python 3.14, as enforced by ruff (PEP 758)
Provide type hints for all public functions in Python code; strict mypy mode is required
Use Google-style docstrings on all public classes and functions in Python, as enforced by ruff D rules
Create new objects instead of mutating existing ones; never mutate existing objects in Python code
For non-Pydantic internal collections (registries,BaseTool), usecopy.deepcopy()at construction andMappingProxyTypewrapping for read-only enforcement
Fordict/listfields in frozen Pydantic models, rely onfrozen=Truefor field reassignment prevention and usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization)
Use frozen Pydantic models for config and identity; use separate mutable-via-copy models (usingmodel_copy(update=...)) for runtime state that evolves
Never mix static config fields with mutable runtime fields in one Pydantic model
Use Pydantic v2 withBaseModel,model_validator,computed_field, andConfigDict
Useallow_inf_nan=Falsein allConfigDictdeclarations in Pydantic models to rejectNaN/Infin numeric fields at validation time
Use@computed_fieldfor derived values in Pydantic models instead of storing and validating redundant fields (e.g.,TokenUsage.total_tokens)
UseNotBlankStrfromcore.typesfor all identifier and name fields in Python code, including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants, instead of manual whitespace validators
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new Python code (e.g., multiple tool invocations, parallel agent calls); prefer structured concurrency over barecreate_task
Keep Python functions under 50 line...
Files:
src/synthorg/api/guards.pysrc/synthorg/hr/enums.pytests/unit/hr/test_activity.pysrc/synthorg/hr/activity.pytests/unit/api/controllers/test_activities.pysrc/synthorg/api/dto_providers.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/api/dto.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every Python module with business logic must import and use the logger:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)
Never useimport logging/logging.getLogger()/print()in application Python code, except inobservability/setup.pyandobservability/sinks.pywhich may use stdlibloggingandprint(..., file=sys.stderr)for bootstrap code
Always name the logger variableloggerin Python code, not_loggerorlog
Use event name constants from the domain-specific module undersynthorg.observability.eventsfor all logging in Python code (e.g.,API_REQUEST_STARTEDfromevents.api,TOOL_INVOKE_STARTfromevents.tool,GIT_COMMAND_STARTfromevents.git)
Always use structured kwargs in logger calls:logger.info(EVENT, key=value), neverlogger.info("msg %s", val)in Python code
Log at WARNING or ERROR with context before raising errors in Python code; all error paths must be logged
Log at INFO level for all state transitions in Python code
Log at DEBUG level for object creation, internal flow, and entry/exit of key functions in Python code
All provider calls must go throughBaseCompletionProviderwhich applies retry and rate limiting automatically; never implement retry logic in driver subclasses or calling code
SetRetryConfigandRateLimiterConfigper-provider inProviderConfigin Python code
Retryable errors (is_retryable=True) in Python code include:RateLimitError,ProviderTimeoutError,ProviderConnectionError,ProviderInternalError; non-retryable errors raise immediately
UseRateLimitError.retry_afterfrom providers in the rate limiter; automatically pause future requests in Python code
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned Python code, docstrings, comments, tests, or config examples; use generic names likeexample-provider,example-large-001,example-medium-001,large/medium/smallaliases in...
Files:
src/synthorg/api/guards.pysrc/synthorg/hr/enums.pysrc/synthorg/hr/activity.pysrc/synthorg/api/dto_providers.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/api/dto.py
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: Always reuse existing components fromweb/src/components/ui/before creating new React components in the web dashboard; never create duplicate components
Never hardcode hex colors, font-family, or pixel spacing in React web dashboard code; use design tokens instead
Never use real vendor names in the web dashboard code; use generic names likeexample-provider,example-large-001,example-medium-001in React/TypeScript code
Use ESLint with zero warnings for the web dashboard (conditional pre-commit hook onweb/src/**/*.{ts,tsx})
web/src/**/*.{ts,tsx}: Use design tokens exclusively for colors, typography, and spacing -- never hardcode hex values,rgba()values with hardcoded colors, pixel values for layout spacing, orfontFamilydeclarations directly in.tsx/.tsfiles.
Use Tailwind semantic classes (text-foreground,bg-card,text-accent,text-success,bg-danger) or CSS variables (var(--so-*)) for colors instead of hardcoded hex values.
Usefont-sansorfont-monofor typography instead of settingfontFamilydirectly.
Use density-aware tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind spacing for layout spacing instead of hardcoded pixel values.
Use design token variables (var(--so-shadow-card-hover),border-border,border-bright) for shadows and borders instead of hardcoded values.
Do not recreate status dots inline -- use the<StatusBadge>component instead.
Do not build card-with-header layouts from scratch -- use the<SectionCard>component instead.
Do not create metric displays withtext-metric font-bold-- use the<MetricCard>component instead.
Do not render initials circles manually -- use the<Avatar>component instead.
Do not create complex (>8 line) JSX inside.map()-- extract to a shared component instead.
Files:
web/src/api/endpoints/activities.tsweb/src/api/types.ts
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Mark tests with@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, or@pytest.mark.slowin Python test code
Enforce 80% minimum code coverage in Python test code, as enforced in CI
Useasyncio_mode = "auto"for pytest async tests; do not manually add@pytest.mark.asyncioin Python test files
Set a 30-second global timeout per test in Python test files (configured inpyproject.toml); do not add per-filepytest.mark.timeout(30)markers; non-default overrides liketimeout(60)are allowed
Always include-n autowhen running pytest for Python tests; never run tests sequentially (parallelism viapytest-xdist)
Use@pytest.mark.parametrizefor testing similar cases in Python test files
Usetest-provider,test-small-001, etc. in Python test code, not real vendor names
Use Hypothesis for property-based testing in Python; apply@givenand@settingsdecorators for property tests
Use Hypothesis profiles for property tests:ci(50 examples, default) anddev(1000 examples), controlled viaHYPOTHESIS_PROFILEenv var; run dev profile withHYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties
Never skip, dismiss, or ignore flaky Python tests; always fix them fully and fundamentally by mockingtime.monotonic()andasyncio.sleep()for determinism instead of widening timing margins
Useasyncio.Event().wait()for tasks that must block indefinitely until cancelled in Python tests (instead ofasyncio.sleep(large_number)), as it is cancellation-safe with no timing assumptions
Files:
tests/unit/hr/test_activity.pytests/unit/api/controllers/test_activities.py
🧠 Learnings (75)
📚 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/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
CLAUDE.mdtests/unit/api/controllers/test_activities.py
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/hr/**/*.py : HR package (hr/): hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)
Applied to files:
CLAUDE.mdtests/unit/hr/test_activity.pytests/unit/api/controllers/test_activities.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/hr/**/*.py : HR engine must provide: hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, trend detection), promotion/demotion
Applied to files:
CLAUDE.mdtests/unit/hr/test_activity.pytests/unit/api/controllers/test_activities.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: Engine: Agent orchestration, execution loops, parallel execution, task decomposition, routing, task assignment, centralized single-writer task state engine (TaskEngine), task lifecycle, recovery, shutdown, workspace isolation, coordination (multi-agent pipeline: TopologyDispatcher protocol, 4 dispatchers — SAS/centralized/decentralized/context-dependent, wave execution, workspace lifecycle integration, CoordinationSectionConfig company config bridge, build_coordinator factory), coordination error classification, prompt policy validation, checkpoint recovery (checkpoint/, per-turn persistence, heartbeat detection, CheckpointRecoveryStrategy), approval gate (escalation detection, context parking/resume, EscalationInfo/ResumePayload models), stagnation detection (stagnation/, StagnationDetector protocol, ToolRepetitionDetector, dual-signal analysis, corrective prompt injection), agent runtime state (AgentRuntimeState, lightweight per-agent execution status for dashboard queries and recove...
Applied to files:
CLAUDE.mddocs/design/agents.mdtests/unit/api/controllers/test_activities.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/engine/**/*.py : Engine package (engine/): agent orchestration, parallel execution, task decomposition, routing, TaskEngine (centralized single-writer), task lifecycle/recovery/shutdown, workspace isolation, coordination (4 dispatchers: SAS/centralized/decentralized/context-dependent, wave execution), approval gates (escalation detection, context parking/resume), stagnation detection (ToolRepetitionDetector, corrective prompt injection), AgentRuntimeState (execution status), context budget management, conversation compaction (oldest-turns summarizer)
Applied to files:
CLAUDE.mdtests/unit/api/controllers/test_activities.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: Documentation source in `docs/` (Markdown, built with Zensical). Design spec in `docs/design/` (7 pages: index, agents, organization, communication, engine, memory, operations). Architecture in `docs/architecture/` (overview, tech-stack, decision log). Roadmap in `docs/roadmap/`. Security in `docs/security.md`. Licensing in `docs/licensing.md`. Reference in `docs/reference/`. REST API reference in `docs/rest-api.md`. Library reference in `docs/api/` (auto-generated from docstrings). Custom templates in `docs/overrides/`. Config in `mkdocs.yml`.
Applied to files:
CLAUDE.md
📚 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 docs/** : Docs source in docs/ (Markdown, built with Zensical); design spec in docs/design/ (7 pages: index, agents, organization, communication, engine, memory, operations)
Applied to files:
CLAUDE.md
📚 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/core/**/*.py : Core module must contain shared domain models, base classes, resilience config (RetryConfig, RateLimiterConfig)
Applied to files:
CLAUDE.md
📚 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 docs/design/*.md : Design spec pages: 7 pages in `docs/design/` — index, agents, organization, communication, engine, memory, operations
Applied to files:
CLAUDE.md
📚 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:
CLAUDE.mdsrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)
Applied to files:
tests/unit/hr/test_activity.pysrc/synthorg/api/dto.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/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.
Applied to files:
tests/unit/hr/test_activity.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:
tests/unit/hr/test_activity.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from the domain-specific module under `synthorg.observability.events` for all logging in Python code (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`, `GIT_COMMAND_START` from `events.git`)
Applied to files:
src/synthorg/hr/activity.pysrc/synthorg/api/controllers/activities.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 : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings
Applied to files:
src/synthorg/hr/activity.pysrc/synthorg/api/controllers/activities.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/hr/activity.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/hr/activity.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/hr/activity.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 tests/**/*.py : Fix flaky tests completely and fundamentally; for timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic instead of widening timing margins
Applied to files:
tests/unit/api/controllers/test_activities.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 tests/**/*.py : Test markers: `pytest.mark.unit`, `pytest.mark.integration`, `pytest.mark.e2e`, `pytest.mark.slow`. Coverage: 80% minimum. Async: `asyncio_mode = 'auto'` — no manual `pytest.mark.asyncio` needed. Timeout: 30 seconds per test. Parallelism: `pytest-xdist` via `-n auto` — ALWAYS include `-n auto` when running pytest, never run tests sequentially.
Applied to files:
tests/unit/api/controllers/test_activities.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 tests/**/*.py : Test markers: pytest.mark.unit, pytest.mark.integration, pytest.mark.e2e, pytest.mark.slow. Coverage: 80% minimum (enforced in CI).
Applied to files:
tests/unit/api/controllers/test_activities.py
📚 Learning: 2026-03-30T19:05:44.281Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.281Z
Learning: Applies to tests/**/*.py : Never skip, dismiss, or ignore flaky Python tests; always fix them fully and fundamentally by mocking `time.monotonic()` and `asyncio.sleep()` for determinism instead of widening timing margins
Applied to files:
tests/unit/api/controllers/test_activities.py
📚 Learning: 2026-03-16T10:40:25.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T10:40:25.144Z
Learning: Applies to tests/**/*.py : Async testing: asyncio_mode = 'auto' — no manual pytest.mark.asyncio needed.
Applied to files:
tests/unit/api/controllers/test_activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to tests/**/*.py : Mark tests with `pytest.mark.unit`, `pytest.mark.integration`, `pytest.mark.e2e`, or `pytest.mark.slow` in Python test code
Applied to files:
tests/unit/api/controllers/test_activities.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 tests/**/*.py : NEVER skip, dismiss, or ignore flaky tests — always fix them fully and fundamentally. For timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic instead of widening timing margins
Applied to files:
tests/unit/api/controllers/test_activities.py
📚 Learning: 2026-03-30T19:05:44.281Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.281Z
Learning: Applies to tests/**/*.py : Use `pytest.mark.parametrize` for testing similar cases in Python test files
Applied to files:
tests/unit/api/controllers/test_activities.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 tests/**/*.py : Parametrize: Prefer pytest.mark.parametrize for testing similar cases.
Applied to files:
tests/unit/api/controllers/test_activities.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 tests/**/*.py : Prefer `pytest.mark.parametrize` for testing similar cases.
Applied to files:
tests/unit/api/controllers/test_activities.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/synthorg/api/**/*.py : Litestar API must include setup wizard, auth/, auto-wiring, and lifecycle management
Applied to files:
tests/unit/api/controllers/test_activities.pysrc/synthorg/api/controllers/activities.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/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)
Applied to files:
tests/unit/api/controllers/test_activities.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 : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (via `model_copy(update=...)`) for runtime state that evolves
Applied to files:
tests/unit/api/controllers/test_activities.pysrc/synthorg/api/dto.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.
Applied to files:
src/synthorg/api/dto_providers.pysrc/synthorg/api/dto.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 : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`.
Applied to files:
src/synthorg/api/dto_providers.pysrc/synthorg/api/dto.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig` in Python code
Applied to files:
src/synthorg/api/dto_providers.pysrc/synthorg/api/dto.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 : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. For derived values use `computed_field` instead of storing + validating redundant fields. Use `NotBlankStr` (from `core.types`) for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.
Applied to files:
src/synthorg/api/dto_providers.pysrc/synthorg/api/dto.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 Pydantic v2 BaseModel, model_validator, computed_field, ConfigDict.
Applied to files:
src/synthorg/api/dto_providers.pysrc/synthorg/api/dto.py
📚 Learning: 2026-03-16T19:13:36.562Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:13:36.562Z
Learning: Applies to src/synthorg/providers/**/*.py : RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig.
Applied to files:
src/synthorg/api/dto_providers.pysrc/synthorg/api/dto.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately.
Applied to files:
src/synthorg/api/dto_providers.pysrc/synthorg/api/dto.py
📚 Learning: 2026-03-15T18:42:17.990Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:42:17.990Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`
Applied to files:
src/synthorg/api/dto_providers.pysrc/synthorg/api/dto.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 : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors (`is_retryable=True`): `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately without retry. `RetryExhaustedError` signals that all retries failed — the engine layer catches this to trigger fallback chains. Rate limiter respects `RateLimitError.retry_after` from providers — automatically pauses future requests.
Applied to files:
src/synthorg/api/dto_providers.pysrc/synthorg/api/dto.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Use Pydantic v2 with `BaseModel`, `model_validator`, `computed_field`, and `ConfigDict`
Applied to files:
src/synthorg/api/dto_providers.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 **/*.py : Use `except A, B:` syntax (without parentheses) per PEP 758 for exception handling in Python 3.14
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Use `except A, B:` syntax without parentheses for exception handling in Python 3.14, as enforced by ruff (PEP 758)
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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 : Prefer `asyncio.TaskGroup` for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare `create_task`. Existing code is being migrated incrementally.
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Prefer `asyncio.TaskGroup` for fan-out/fan-in parallel operations in new Python code (e.g., multiple tool invocations, parallel agent calls); prefer structured concurrency over bare `create_task`
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/engine/**/*.py : Catch `RetryExhaustedError` in the engine layer to trigger fallback chains; this signals that all retries failed in Python code
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-17T18:52:05.142Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T18:52:05.142Z
Learning: Applies to **/*.py : Async concurrency: prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Applied to files:
src/synthorg/api/controllers/activities.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/api/**/*.py : Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
Applied to files:
src/synthorg/api/controllers/activities.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/api/**/*.py : API package (api/): Litestar REST + WebSocket with controllers, guards, channels, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint, provider management endpoint (CRUD + test + presets), backup endpoint, RFC 9457 structured errors, AppState hot-reload slots, service auto-wiring (Phase 1 at construction, Phase 2 on startup), lifecycle helpers
Applied to files:
src/synthorg/api/controllers/activities.pysrc/synthorg/api/dto.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/api/**/*.py : REST API: Litestar framework, controllers with guards, channels for WebSocket, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. RFC 9457 structured errors (ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation).
Applied to files:
src/synthorg/api/controllers/activities.pysrc/synthorg/api/dto.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/api/**/*.py : Use Litestar for REST API and WebSocket API with JWT + API key + WS ticket authentication, RFC 9457 structured errors, and content negotiation.
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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 : Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state
Applied to files:
src/synthorg/api/dto.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 frozen Pydantic models for config/identity; use 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.
Applied to files:
src/synthorg/api/dto.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 : For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Use `allow_inf_nan=False` in all `ConfigDict` declarations in Pydantic models to reject `NaN`/`Inf` in numeric fields at validation time
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Use frozen Pydantic models for config and identity; use separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state that evolves
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to **/*.py : Config vs runtime state: use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state. Never mix static config fields with mutable runtime fields in one model.
Applied to files:
src/synthorg/api/dto.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 : Use `NotBlankStr` from `core.types` for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Applied to files:
src/synthorg/api/dto.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 Pydantic v2 with adopted conventions: use computed_field for derived values instead of storing + validating redundant fields; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Use `NotBlankStr` from `core.types` for all identifier and name fields in Python code, including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants, instead of manual whitespace validators
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-17T11:41:02.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T11:41:02.964Z
Learning: Applies to src/**/*.py : Models: Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`). Use `computed_field` for derived values instead of storing + validating redundant fields. Use `NotBlankStr` for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.
Applied to files:
src/synthorg/api/dto.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 **/*.py : Models: Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use computed_field for derived values instead of storing + validating redundant fields. Use NotBlankStr (from core.types) for all identifier/name fields — including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants — instead of manual whitespace validators.
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. Use `computed_field` for derived values instead of storing redundant fields. Use `NotBlankStr` for all identifier/name fields.
Applied to files:
src/synthorg/api/dto.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 : Retryable errors (`is_retryable=True`): `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately without retry.
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/**/*.py : Retryable errors (`is_retryable=True`) in Python code include: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`; non-retryable errors raise immediately
Applied to files:
src/synthorg/api/dto.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/api/dto.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 **/*.py : 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.
Applied to files:
src/synthorg/api/dto.py
- Add mapActivityEventToItem mapper (REST ActivityEvent -> ActivityItem) - listActivities now returns mapped ActivityItem for store compatibility - ActivityItem.action_type accepts ActivityEventType | WsEventType union - Extend ACTION_DOT_COLORS with ActivityEventType keys for REST path - getActionDotColor typed for both event type domains Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/synthorg/api/controllers/activities.py`:
- Around line 121-127: The ExceptionGroup handler in _run_async_fetchers
currently re-raises fatal and ServiceUnavailableError subgroups without logging;
before raising the fatal subgroup (from fatal = eg.subgroup((MemoryError,
RecursionError))) and before raising the svc subgroup (svc =
eg.subgroup(ServiceUnavailableError)), emit a structured log (logger.error for
fatal, logger.warning or logger.error for svc depending on severity) that
includes the local endpoint/source context variables used elsewhere (e.g.,
endpoint, source, and any request id) and the subgroup/exception details, then
re-raise as before so the context is preserved in logs prior to raising.
In `@src/synthorg/api/dto.py`:
- Around line 516-527: The module-level __all__ currently only lists
provider-related names, which hides existing DTOs like PaginatedResponse,
ApiResponse and the task/approval DTOs; either remove the __all__ declaration
entirely or expand it to include all public DTO names (e.g., add
"PaginatedResponse", "ApiResponse", the task/approval DTO class names, and any
other exported symbols) so the module's public surface is not inadvertently
reduced; update the __all__ variable in dto.py accordingly (referencing the
__all__ symbol and the DTO class names defined in that file).
In `@src/synthorg/hr/activity.py`:
- Around line 260-270: The code in the ActivityEventType.COST_INCURRED branch
uses match.group(1) to build redacted = f"API call ({match.group(1)})", which
still exposes token counts; change it so that when _COST_DESC_PATTERN matches
you also return a fully generic string (e.g. "API call (details redacted)")
instead of interpolating match.group(1). Update the assignment of redacted in
the match branch to the same generic redaction used in the else branch, leaving
the logger.warning and other logic unchanged.
🪄 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: 04e1400c-1dbd-4d9f-9727-197beae5e7eb
📒 Files selected for processing (3)
src/synthorg/api/controllers/activities.pysrc/synthorg/api/dto.pysrc/synthorg/hr/activity.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Dashboard Test
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Backend
- GitHub Check: Build Sandbox
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Do not usefrom __future__ import annotationsin Python code; rely on PEP 649 native lazy annotations in Python 3.14+
Useexcept A, B:syntax without parentheses for exception handling in Python 3.14, as enforced by ruff (PEP 758)
Provide type hints for all public functions in Python code; strict mypy mode is required
Use Google-style docstrings on all public classes and functions in Python, as enforced by ruff D rules
Create new objects instead of mutating existing ones; never mutate existing objects in Python code
For non-Pydantic internal collections (registries,BaseTool), usecopy.deepcopy()at construction andMappingProxyTypewrapping for read-only enforcement
Fordict/listfields in frozen Pydantic models, rely onfrozen=Truefor field reassignment prevention and usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization)
Use frozen Pydantic models for config and identity; use separate mutable-via-copy models (usingmodel_copy(update=...)) for runtime state that evolves
Never mix static config fields with mutable runtime fields in one Pydantic model
Use Pydantic v2 withBaseModel,model_validator,computed_field, andConfigDict
Useallow_inf_nan=Falsein allConfigDictdeclarations in Pydantic models to rejectNaN/Infin numeric fields at validation time
Use@computed_fieldfor derived values in Pydantic models instead of storing and validating redundant fields (e.g.,TokenUsage.total_tokens)
UseNotBlankStrfromcore.typesfor all identifier and name fields in Python code, including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants, instead of manual whitespace validators
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new Python code (e.g., multiple tool invocations, parallel agent calls); prefer structured concurrency over barecreate_task
Keep Python functions under 50 line...
Files:
src/synthorg/hr/activity.pysrc/synthorg/api/dto.pysrc/synthorg/api/controllers/activities.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every Python module with business logic must import and use the logger:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)
Never useimport logging/logging.getLogger()/print()in application Python code, except inobservability/setup.pyandobservability/sinks.pywhich may use stdlibloggingandprint(..., file=sys.stderr)for bootstrap code
Always name the logger variableloggerin Python code, not_loggerorlog
Use event name constants from the domain-specific module undersynthorg.observability.eventsfor all logging in Python code (e.g.,API_REQUEST_STARTEDfromevents.api,TOOL_INVOKE_STARTfromevents.tool,GIT_COMMAND_STARTfromevents.git)
Always use structured kwargs in logger calls:logger.info(EVENT, key=value), neverlogger.info("msg %s", val)in Python code
Log at WARNING or ERROR with context before raising errors in Python code; all error paths must be logged
Log at INFO level for all state transitions in Python code
Log at DEBUG level for object creation, internal flow, and entry/exit of key functions in Python code
All provider calls must go throughBaseCompletionProviderwhich applies retry and rate limiting automatically; never implement retry logic in driver subclasses or calling code
SetRetryConfigandRateLimiterConfigper-provider inProviderConfigin Python code
Retryable errors (is_retryable=True) in Python code include:RateLimitError,ProviderTimeoutError,ProviderConnectionError,ProviderInternalError; non-retryable errors raise immediately
UseRateLimitError.retry_afterfrom providers in the rate limiter; automatically pause future requests in Python code
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned Python code, docstrings, comments, tests, or config examples; use generic names likeexample-provider,example-large-001,example-medium-001,large/medium/smallaliases in...
Files:
src/synthorg/hr/activity.pysrc/synthorg/api/dto.pysrc/synthorg/api/controllers/activities.py
🧠 Learnings (48)
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from the domain-specific module under `synthorg.observability.events` for all logging in Python code (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`, `GIT_COMMAND_START` from `events.git`)
Applied to files:
src/synthorg/hr/activity.pysrc/synthorg/api/controllers/activities.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 : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings
Applied to files:
src/synthorg/hr/activity.pysrc/synthorg/api/controllers/activities.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/hr/activity.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/hr/activity.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Use `allow_inf_nan=False` in all `ConfigDict` declarations in Pydantic models to reject `NaN`/`Inf` in numeric fields at validation time
Applied to files:
src/synthorg/api/dto.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 : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (via `model_copy(update=...)`) for runtime state that evolves
Applied to files:
src/synthorg/api/dto.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 frozen Pydantic models for config/identity; use 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.
Applied to files:
src/synthorg/api/dto.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 Pydantic v2 BaseModel, model_validator, computed_field, ConfigDict.
Applied to files:
src/synthorg/api/dto.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 : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. For derived values use `computed_field` instead of storing + validating redundant fields. Use `NotBlankStr` (from `core.types`) for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.
Applied to files:
src/synthorg/api/dto.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 : Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state
Applied to files:
src/synthorg/api/dto.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 : For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-15T18:42:17.990Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:42:17.990Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Use frozen Pydantic models for config and identity; use separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state that evolves
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to **/*.py : Config vs runtime state: use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state. Never mix static config fields with mutable runtime fields in one model.
Applied to files:
src/synthorg/api/dto.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 : Use `NotBlankStr` from `core.types` for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Applied to files:
src/synthorg/api/dto.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 Pydantic v2 with adopted conventions: use computed_field for derived values instead of storing + validating redundant fields; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Use `NotBlankStr` from `core.types` for all identifier and name fields in Python code, including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants, instead of manual whitespace validators
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-17T11:41:02.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T11:41:02.964Z
Learning: Applies to src/**/*.py : Models: Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`). Use `computed_field` for derived values instead of storing + validating redundant fields. Use `NotBlankStr` for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. Use `computed_field` for derived values instead of storing redundant fields. Use `NotBlankStr` for all identifier/name fields.
Applied to files:
src/synthorg/api/dto.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 **/*.py : Models: Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use computed_field for derived values instead of storing + validating redundant fields. Use NotBlankStr (from core.types) for all identifier/name fields — including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants — instead of manual whitespace validators.
Applied to files:
src/synthorg/api/dto.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 **/*.py : 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.
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.
Applied to files:
src/synthorg/api/dto.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 : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`.
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig` in Python code
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-16T19:13:36.562Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:13:36.562Z
Learning: Applies to src/synthorg/providers/**/*.py : RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig.
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately.
Applied to files:
src/synthorg/api/dto.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 : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors (`is_retryable=True`): `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately without retry. `RetryExhaustedError` signals that all retries failed — the engine layer catches this to trigger fallback chains. Rate limiter respects `RateLimitError.retry_after` from providers — automatically pauses future requests.
Applied to files:
src/synthorg/api/dto.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/api/**/*.py : API package (api/): Litestar REST + WebSocket with controllers, guards, channels, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint, provider management endpoint (CRUD + test + presets), backup endpoint, RFC 9457 structured errors, AppState hot-reload slots, service auto-wiring (Phase 1 at construction, Phase 2 on startup), lifecycle helpers
Applied to files:
src/synthorg/api/dto.pysrc/synthorg/api/controllers/activities.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 **/*.py : Use `except A, B:` syntax (without parentheses) per PEP 758 for exception handling in Python 3.14
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Use `except A, B:` syntax without parentheses for exception handling in Python 3.14, as enforced by ruff (PEP 758)
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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 : Prefer `asyncio.TaskGroup` for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare `create_task`. Existing code is being migrated incrementally.
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Prefer `asyncio.TaskGroup` for fan-out/fan-in parallel operations in new Python code (e.g., multiple tool invocations, parallel agent calls); prefer structured concurrency over bare `create_task`
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/engine/**/*.py : Catch `RetryExhaustedError` in the engine layer to trigger fallback chains; this signals that all retries failed in Python code
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-17T18:52:05.142Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T18:52:05.142Z
Learning: Applies to **/*.py : Async concurrency: prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Applied to files:
src/synthorg/api/controllers/activities.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/api/**/*.py : Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
Applied to files:
src/synthorg/api/controllers/activities.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/api/**/*.py : REST API: Litestar framework, controllers with guards, channels for WebSocket, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. RFC 9457 structured errors (ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation).
Applied to files:
src/synthorg/api/controllers/activities.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/api/**/*.py : Use Litestar for REST API and WebSocket API with JWT + API key + WS ticket authentication, RFC 9457 structured errors, and content negotiation.
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/synthorg/api/**/*.py : Litestar API must include setup wizard, auth/, auto-wiring, and lifecycle management
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.py
🔇 Additional comments (1)
src/synthorg/api/controllers/activities.py (1)
334-357: Fail-closed redaction and degradation signaling look good.Redacting before pagination and returning
degraded_sourcesin the same envelope keeps the API contract consistent for read-only callers.
| if event.event_type == ActivityEventType.COST_INCURRED: | ||
| match = _COST_DESC_PATTERN.match(event.description) | ||
| if match: | ||
| redacted = f"API call ({match.group(1)})" | ||
| else: | ||
| logger.warning( | ||
| HR_ACTIVITY_REDACTION_MISMATCH, | ||
| event_type=event.event_type.value, | ||
| description_length=len(event.description), | ||
| ) | ||
| redacted = "API call (details redacted)" |
There was a problem hiding this comment.
Token counts are still exposed to read-only roles.
redacted = f"API call ({match.group(1)})" keeps the <input+output tokens> tuple from the original description, so observers/board members can still infer per-call usage volume. Use a fully generic description on the matched path too.
🔒 Minimal fix
if event.event_type == ActivityEventType.COST_INCURRED:
match = _COST_DESC_PATTERN.match(event.description)
if match:
- redacted = f"API call ({match.group(1)})"
+ redacted = "API call (details redacted)"
else:
logger.warning(
HR_ACTIVITY_REDACTION_MISMATCH,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if event.event_type == ActivityEventType.COST_INCURRED: | |
| match = _COST_DESC_PATTERN.match(event.description) | |
| if match: | |
| redacted = f"API call ({match.group(1)})" | |
| else: | |
| logger.warning( | |
| HR_ACTIVITY_REDACTION_MISMATCH, | |
| event_type=event.event_type.value, | |
| description_length=len(event.description), | |
| ) | |
| redacted = "API call (details redacted)" | |
| if event.event_type == ActivityEventType.COST_INCURRED: | |
| match = _COST_DESC_PATTERN.match(event.description) | |
| if match: | |
| redacted = "API call (details redacted)" | |
| else: | |
| logger.warning( | |
| HR_ACTIVITY_REDACTION_MISMATCH, | |
| event_type=event.event_type.value, | |
| description_length=len(event.description), | |
| ) | |
| redacted = "API call (details redacted)" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/hr/activity.py` around lines 260 - 270, The code in the
ActivityEventType.COST_INCURRED branch uses match.group(1) to build redacted =
f"API call ({match.group(1)})", which still exposes token counts; change it so
that when _COST_DESC_PATTERN matches you also return a fully generic string
(e.g. "API call (details redacted)") instead of interpolating match.group(1).
Update the assignment of redacted in the match branch to the same generic
redaction used in the else branch, leaving the logger.warning and other logic
unchanged.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/api/endpoints/activities.ts`:
- Around line 11-21: The id fallback in mapActivityEventToItem uses
event.timestamp which can produce duplicate React keys; update
mapActivityEventToItem to generate a deterministic unique fallback by combining
timestamp with other event fields (e.g., event.event_type and a stable short
hash of event.description or related_ids) or, if nondeterministic uniqueness is
acceptable, append a random/UUID suffix (e.g., crypto.randomUUID() or
Math.random()) so id = `${event.timestamp}-${event.event_type}-${hashOrUUID}`;
ensure the change affects the returned id and preserves existing fields like
task_id and agent_id.
- Line 15: The code incorrectly assumes related_ids contains agent_name; update
the frontend to stop expecting agent_name in related_ids by changing the logic
in the activity rendering (the place using agent_name:
event.related_ids.agent_name ?? event.related_ids.agent_id ?? 'System') to
instead use the agent_id or 'System' immediately and resolve/display the
human-readable name via a separate lookup: call the agents API (or use an
existing agents cache/selector) to fetch the agent's name by
event.related_ids.agent_id (or map IDs to names), and then replace the displayed
value with the fetched name when available; ensure symbols referenced are the
agent_name key removal and the lookup using event.related_ids.agent_id and the
component/function that renders ActivityEvent entries.
🪄 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: 81fe9966-bd69-4b10-8d3d-60e9ede61d9b
📒 Files selected for processing (3)
web/src/api/endpoints/activities.tsweb/src/api/types.tsweb/src/pages/dashboard/ActivityFeedItem.tsx
📜 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). (1)
- GitHub Check: Test (Python 3.14)
🧰 Additional context used
📓 Path-based instructions (1)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: Always reuse existing components fromweb/src/components/ui/before creating new React components in the web dashboard; never create duplicate components
Never hardcode hex colors, font-family, or pixel spacing in React web dashboard code; use design tokens instead
Never use real vendor names in the web dashboard code; use generic names likeexample-provider,example-large-001,example-medium-001in React/TypeScript code
Use ESLint with zero warnings for the web dashboard (conditional pre-commit hook onweb/src/**/*.{ts,tsx})
web/src/**/*.{ts,tsx}: Use design tokens exclusively for colors, typography, and spacing -- never hardcode hex values,rgba()values with hardcoded colors, pixel values for layout spacing, orfontFamilydeclarations directly in.tsx/.tsfiles.
Use Tailwind semantic classes (text-foreground,bg-card,text-accent,text-success,bg-danger) or CSS variables (var(--so-*)) for colors instead of hardcoded hex values.
Usefont-sansorfont-monofor typography instead of settingfontFamilydirectly.
Use density-aware tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind spacing for layout spacing instead of hardcoded pixel values.
Use design token variables (var(--so-shadow-card-hover),border-border,border-bright) for shadows and borders instead of hardcoded values.
Do not recreate status dots inline -- use the<StatusBadge>component instead.
Do not build card-with-header layouts from scratch -- use the<SectionCard>component instead.
Do not create metric displays withtext-metric font-bold-- use the<MetricCard>component instead.
Do not render initials circles manually -- use the<Avatar>component instead.
Do not create complex (>8 line) JSX inside.map()-- extract to a shared component instead.
Files:
web/src/pages/dashboard/ActivityFeedItem.tsxweb/src/api/types.tsweb/src/api/endpoints/activities.ts
🧠 Learnings (3)
📚 Learning: 2026-03-30T19:05:44.281Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.281Z
Learning: Applies to web/src/**/*.{ts,tsx} : Never hardcode hex colors, font-family, or pixel spacing in React web dashboard code; use design tokens instead
Applied to files:
web/src/pages/dashboard/ActivityFeedItem.tsx
📚 Learning: 2026-03-30T19:06:09.478Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-03-30T19:06:09.478Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not recreate status dots inline -- use the `<StatusBadge>` component instead.
Applied to files:
web/src/pages/dashboard/ActivityFeedItem.tsx
📚 Learning: 2026-03-30T19:06:09.478Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-03-30T19:06:09.478Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not render initials circles manually -- use the `<Avatar>` component instead.
Applied to files:
web/src/pages/dashboard/ActivityFeedItem.tsx
🔇 Additional comments (4)
web/src/pages/dashboard/ActivityFeedItem.tsx (1)
12-27: LGTM! Dot color mapping covers all backendActivityEventTypevalues.All 13 REST
ActivityEventTypevalues from the backend enum are mapped, and the colors use semantic Tailwind classes (bg-success,bg-danger,bg-warning,bg-accent) consistent with design token guidelines. ThePartial<Record<...>>type correctly allows the default fallback for any unmapped future values.web/src/api/types.ts (2)
149-151: LGTM!degraded_sourcestyping aligns with backend contract.The optional
degraded_sources?: readonly string[]correctly mirrors the backend'stuple[NotBlankStr, ...] = ()default—both variants receive the field because the backend doesn't distinguish success/error at the class level. Marking it optional handles the default empty case cleanly.
542-548: LGTM!ActivityEventinterface matches backend model.The interface correctly mirrors the backend
ActivityEventPydantic model withevent_type: ActivityEventType,timestamp: string,description: string, andrelated_ids: Record<string, string>.web/src/api/endpoints/activities.ts (1)
23-28: LGTM!listActivitiescorrectly mapsActivityEventtoActivityItem.The function properly fetches
ActivityEventfrom the API and transforms each item viamapActivityEventToItembefore returning, addressing the type mismatch between REST responses and consumer expectations. The spread operator preserves pagination metadata while replacingdata.
- Log before re-raising fatal/ServiceUnavailableError from ExceptionGroup - Expand __all__ in dto.py to include all public DTO symbols Token count preservation in redacted descriptions is by design (issue #839 Option 2: strip model name and cost, keep token counts). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
src/synthorg/api/controllers/activities.py (2)
79-84: 🛠️ Refactor suggestion | 🟠 MajorReturn degraded-source tuples instead of mutating a shared list.
The new helper stack still passes a caller-owned
degradedlist around and appends into it from multiple layers. Have each helper return its own degraded-source tuple and merge once in_build_timeline()so the control flow stays explicit.As per coding guidelines, "Create new objects instead of mutating existing ones; never mutate existing objects in Python code."
Also applies to: 185-196, 218-268
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/controllers/activities.py` around lines 79 - 84, The helpers currently mutate the caller-owned degraded list (seen in _run_async_fetchers) — change the helper signatures to return a (result, degraded_sources) tuple instead of accepting/mutating a degraded list, have _run_async_fetchers collect those tuples from each helper, and let _build_timeline merge all degraded_sources into a single list before returning; update all call sites to stop passing the degraded list (e.g., the helper functions that previously took degraded as a parameter and any callers in _run_async_fetchers and _build_timeline) so each function creates and returns its own degraded list and the top-level merges them once.
121-139:⚠️ Potential issue | 🟠 MajorUnwrap to the first matching leaf exception before re-raising.
When the agent-specific delegation path fails inside
_fetch_delegation_records()'s innerTaskGroup,eg.subgroup(...)preserves that nestedExceptionGroup. In that case,fatal.exceptions[0]/svc.exceptions[0]can still be anExceptionGroup, so theServiceUnavailableErrorhandler is bypassed again and the fatal branch does not re-raise the leaf exception you intended.🛠️ Suggested direction
+def _first_matching_leaf( + group: BaseExceptionGroup, + exc_types: type[BaseException] | tuple[type[BaseException], ...], +) -> BaseException | None: + for exc in group.exceptions: + if isinstance(exc, BaseExceptionGroup): + if leaf := _first_matching_leaf(exc, exc_types): + return leaf + continue + if isinstance(exc, exc_types): + return exc + return None + except ExceptionGroup as eg: - fatal = eg.subgroup((MemoryError, RecursionError)) + fatal = _first_matching_leaf(eg, (MemoryError, RecursionError)) if fatal is not None: ... - raise fatal.exceptions[0] from eg - svc = eg.subgroup(ServiceUnavailableError) + raise fatal from eg + svc = _first_matching_leaf(eg, ServiceUnavailableError) if svc is not None: ... - raise svc.exceptions[0] from eg + raise svc from egRun this to verify the nesting behavior:
#!/bin/bash python - <<'PY' class ServiceUnavailableError(Exception): pass svc_outer = ExceptionGroup( "outer", [ExceptionGroup("inner", [ServiceUnavailableError("svc")])], ) svc = svc_outer.subgroup(ServiceUnavailableError) print("service subgroup child type:", type(svc.exceptions[0]).__name__) fatal_outer = ExceptionGroup( "outer", [ExceptionGroup("inner", [MemoryError("oom")])], ) fatal = fatal_outer.subgroup((MemoryError, RecursionError)) print("fatal subgroup child type:", type(fatal.exceptions[0]).__name__) PYExpected result: both lines print
ExceptionGroup, showingsubgroup(...).exceptions[0]is not guaranteed to be the leaf exception.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/controllers/activities.py` around lines 121 - 139, The ExceptionGroup subgroup result (variables fatal and svc in the _fetch_delegation_records() except block) may itself contain nested ExceptionGroup nodes, so before raising fatal.exceptions[0] or svc.exceptions[0] unwrap down to the first non-ExceptionGroup leaf; i.e., detect if fatal.exceptions[0] (or svc.exceptions[0]) is an ExceptionGroup and iteratively replace it with its .exceptions[0] until you have a concrete exception instance, then raise that leaf (using raise ... from eg) so you re-raise the actual leaf ServiceUnavailableError or MemoryError/RecursionError instead of an ExceptionGroup root.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/synthorg/api/controllers/activities.py`:
- Around line 185-205: Before re-raising fatal/503 or other exceptions in
_resolve_currency and in _fetch_task_metrics, emit a local structured log entry
(WARNING or ERROR) with endpoint="activities" and the source name, then
re-raise; specifically, inside the exception branches in _resolve_currency (the
except MemoryError/RecursionError passthrough and the generic except Exception)
call the app_state logger (e.g. app_state.logger.warning or .error) with a
message including the source (budget/config) and endpoint="activities" before
raising, and make the analogous change in _fetch_task_metrics for its fatal/503
paths; ensure the log includes enough context (source name and endpoint) and
then re-raise the original exception.
---
Duplicate comments:
In `@src/synthorg/api/controllers/activities.py`:
- Around line 79-84: The helpers currently mutate the caller-owned degraded list
(seen in _run_async_fetchers) — change the helper signatures to return a
(result, degraded_sources) tuple instead of accepting/mutating a degraded list,
have _run_async_fetchers collect those tuples from each helper, and let
_build_timeline merge all degraded_sources into a single list before returning;
update all call sites to stop passing the degraded list (e.g., the helper
functions that previously took degraded as a parameter and any callers in
_run_async_fetchers and _build_timeline) so each function creates and returns
its own degraded list and the top-level merges them once.
- Around line 121-139: The ExceptionGroup subgroup result (variables fatal and
svc in the _fetch_delegation_records() except block) may itself contain nested
ExceptionGroup nodes, so before raising fatal.exceptions[0] or svc.exceptions[0]
unwrap down to the first non-ExceptionGroup leaf; i.e., detect if
fatal.exceptions[0] (or svc.exceptions[0]) is an ExceptionGroup and iteratively
replace it with its .exceptions[0] until you have a concrete exception instance,
then raise that leaf (using raise ... from eg) so you re-raise the actual leaf
ServiceUnavailableError or MemoryError/RecursionError instead of an
ExceptionGroup root.
🪄 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: d48be14c-f0e9-45a3-9141-3fbebc95d751
📒 Files selected for processing (2)
src/synthorg/api/controllers/activities.pysrc/synthorg/api/dto.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: Dashboard Test
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Sandbox
- GitHub Check: Build Web
- GitHub Check: Build Backend
- GitHub Check: Dependency Review
- GitHub Check: Analyze (go)
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Do not usefrom __future__ import annotationsin Python code; rely on PEP 649 native lazy annotations in Python 3.14+
Useexcept A, B:syntax without parentheses for exception handling in Python 3.14, as enforced by ruff (PEP 758)
Provide type hints for all public functions in Python code; strict mypy mode is required
Use Google-style docstrings on all public classes and functions in Python, as enforced by ruff D rules
Create new objects instead of mutating existing ones; never mutate existing objects in Python code
For non-Pydantic internal collections (registries,BaseTool), usecopy.deepcopy()at construction andMappingProxyTypewrapping for read-only enforcement
Fordict/listfields in frozen Pydantic models, rely onfrozen=Truefor field reassignment prevention and usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization)
Use frozen Pydantic models for config and identity; use separate mutable-via-copy models (usingmodel_copy(update=...)) for runtime state that evolves
Never mix static config fields with mutable runtime fields in one Pydantic model
Use Pydantic v2 withBaseModel,model_validator,computed_field, andConfigDict
Useallow_inf_nan=Falsein allConfigDictdeclarations in Pydantic models to rejectNaN/Infin numeric fields at validation time
Use@computed_fieldfor derived values in Pydantic models instead of storing and validating redundant fields (e.g.,TokenUsage.total_tokens)
UseNotBlankStrfromcore.typesfor all identifier and name fields in Python code, including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants, instead of manual whitespace validators
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new Python code (e.g., multiple tool invocations, parallel agent calls); prefer structured concurrency over barecreate_task
Keep Python functions under 50 line...
Files:
src/synthorg/api/controllers/activities.pysrc/synthorg/api/dto.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every Python module with business logic must import and use the logger:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)
Never useimport logging/logging.getLogger()/print()in application Python code, except inobservability/setup.pyandobservability/sinks.pywhich may use stdlibloggingandprint(..., file=sys.stderr)for bootstrap code
Always name the logger variableloggerin Python code, not_loggerorlog
Use event name constants from the domain-specific module undersynthorg.observability.eventsfor all logging in Python code (e.g.,API_REQUEST_STARTEDfromevents.api,TOOL_INVOKE_STARTfromevents.tool,GIT_COMMAND_STARTfromevents.git)
Always use structured kwargs in logger calls:logger.info(EVENT, key=value), neverlogger.info("msg %s", val)in Python code
Log at WARNING or ERROR with context before raising errors in Python code; all error paths must be logged
Log at INFO level for all state transitions in Python code
Log at DEBUG level for object creation, internal flow, and entry/exit of key functions in Python code
All provider calls must go throughBaseCompletionProviderwhich applies retry and rate limiting automatically; never implement retry logic in driver subclasses or calling code
SetRetryConfigandRateLimiterConfigper-provider inProviderConfigin Python code
Retryable errors (is_retryable=True) in Python code include:RateLimitError,ProviderTimeoutError,ProviderConnectionError,ProviderInternalError; non-retryable errors raise immediately
UseRateLimitError.retry_afterfrom providers in the rate limiter; automatically pause future requests in Python code
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned Python code, docstrings, comments, tests, or config examples; use generic names likeexample-provider,example-large-001,example-medium-001,large/medium/smallaliases in...
Files:
src/synthorg/api/controllers/activities.pysrc/synthorg/api/dto.py
🧠 Learnings (58)
📚 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/api/controllers/activities.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/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Use `except A, B:` syntax without parentheses for exception handling in Python 3.14, as enforced by ruff (PEP 758)
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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 : Prefer `asyncio.TaskGroup` for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare `create_task`. Existing code is being migrated incrementally.
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Prefer `asyncio.TaskGroup` for fan-out/fan-in parallel operations in new Python code (e.g., multiple tool invocations, parallel agent calls); prefer structured concurrency over bare `create_task`
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/engine/**/*.py : Catch `RetryExhaustedError` in the engine layer to trigger fallback chains; this signals that all retries failed in Python code
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-17T18:52:05.142Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T18:52:05.142Z
Learning: Applies to **/*.py : Async concurrency: prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Applied to files:
src/synthorg/api/controllers/activities.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 src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising.
Applied to files:
src/synthorg/api/controllers/activities.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 : All error paths must log at WARNING or ERROR with context before raising
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/**/*.py : Log at WARNING or ERROR with context before raising errors in Python code; all error paths must be logged
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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 src/**/*.py : All error paths must log at WARNING or ERROR with context before raising
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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/api/**/*.py : Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
Applied to files:
src/synthorg/api/controllers/activities.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/api/**/*.py : API package (api/): Litestar REST + WebSocket with controllers, guards, channels, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint, provider management endpoint (CRUD + test + presets), backup endpoint, RFC 9457 structured errors, AppState hot-reload slots, service auto-wiring (Phase 1 at construction, Phase 2 on startup), lifecycle helpers
Applied to files:
src/synthorg/api/controllers/activities.pysrc/synthorg/api/dto.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/api/**/*.py : REST API: Litestar framework, controllers with guards, channels for WebSocket, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. RFC 9457 structured errors (ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation).
Applied to files:
src/synthorg/api/controllers/activities.pysrc/synthorg/api/dto.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/api/**/*.py : Use Litestar for REST API and WebSocket API with JWT + API key + WS ticket authentication, RFC 9457 structured errors, and content negotiation.
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/synthorg/api/**/*.py : Litestar API must include setup wizard, auth/, auto-wiring, and lifecycle management
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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 : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from the domain-specific module under `synthorg.observability.events` for all logging in Python code (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`, `GIT_COMMAND_START` from `events.git`)
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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 : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (via `model_copy(update=...)`) for runtime state that evolves
Applied to files:
src/synthorg/api/dto.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 frozen Pydantic models for config/identity; use 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.
Applied to files:
src/synthorg/api/dto.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 Pydantic v2 BaseModel, model_validator, computed_field, ConfigDict.
Applied to files:
src/synthorg/api/dto.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 : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. For derived values use `computed_field` instead of storing + validating redundant fields. Use `NotBlankStr` (from `core.types`) for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.
Applied to files:
src/synthorg/api/dto.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 : Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Use `allow_inf_nan=False` in all `ConfigDict` declarations in Pydantic models to reject `NaN`/`Inf` in numeric fields at validation time
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-15T18:42:17.990Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:42:17.990Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`
Applied to files:
src/synthorg/api/dto.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 : For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Use frozen Pydantic models for config and identity; use separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state that evolves
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to **/*.py : Config vs runtime state: use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state. Never mix static config fields with mutable runtime fields in one model.
Applied to files:
src/synthorg/api/dto.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 : Use `NotBlankStr` from `core.types` for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Applied to files:
src/synthorg/api/dto.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 Pydantic v2 with adopted conventions: use computed_field for derived values instead of storing + validating redundant fields; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Use `NotBlankStr` from `core.types` for all identifier and name fields in Python code, including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants, instead of manual whitespace validators
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-17T11:41:02.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T11:41:02.964Z
Learning: Applies to src/**/*.py : Models: Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`). Use `computed_field` for derived values instead of storing + validating redundant fields. Use `NotBlankStr` for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. Use `computed_field` for derived values instead of storing redundant fields. Use `NotBlankStr` for all identifier/name fields.
Applied to files:
src/synthorg/api/dto.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 **/*.py : Models: Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use computed_field for derived values instead of storing + validating redundant fields. Use NotBlankStr (from core.types) for all identifier/name fields — including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants — instead of manual whitespace validators.
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately.
Applied to files:
src/synthorg/api/dto.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 : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig`.
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/**/*.py : Set `RetryConfig` and `RateLimiterConfig` per-provider in `ProviderConfig` in Python code
Applied to files:
src/synthorg/api/dto.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 : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors (`is_retryable=True`): `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately without retry. `RetryExhaustedError` signals that all retries failed — the engine layer catches this to trigger fallback chains. Rate limiter respects `RateLimitError.retry_after` from providers — automatically pauses future requests.
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-16T19:13:36.562Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:13:36.562Z
Learning: Applies to src/synthorg/providers/**/*.py : RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig.
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)
Applied to files:
src/synthorg/api/dto.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 : Retryable errors (`is_retryable=True`): `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately without retry.
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/**/*.py : Retryable errors (`is_retryable=True`) in Python code include: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`; non-retryable errors raise immediately
Applied to files:
src/synthorg/api/dto.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/api/dto.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.
Applied to files:
src/synthorg/api/dto.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/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
src/synthorg/api/dto.py
- Fix mapActivityEventToItem: use agent_id (not agent_name) from related_ids; deterministic id from timestamp+event_type+agent_id - Add logging before re-raise in _fetch_task_metrics and _resolve_currency for operational visibility Mutable degraded accumulator and flat ExceptionGroup subgroup findings rejected: accumulator pattern is deliberate (deduped at return), and fetchers don't nest TaskGroups so subgroup().exceptions[0] is always a leaf exception. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/synthorg/api/controllers/activities.py (3)
507-510:⚠️ Potential issue | 🟡 MinorLog before re-raising fatal/503 errors.
Consistent with the other fetch helpers, these re-raise paths should log with context before propagating.
🛠️ Proposed fix
except MemoryError, RecursionError: + logger.error( + API_REQUEST_ERROR, + endpoint="activities", + source=_SRC_DELEGATION_RECORD_STORE, + detail="fatal error", + exc_info=True, + ) raise except ServiceUnavailableError: + logger.warning( + API_REQUEST_ERROR, + endpoint="activities", + source=_SRC_DELEGATION_RECORD_STORE, + detail="service unavailable", + exc_info=True, + ) raiseAs per coding guidelines, "Log at WARNING or ERROR with context before raising errors in Python code; all error paths must be logged."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/controllers/activities.py` around lines 507 - 510, The except branches that catch MemoryError/RecursionError and ServiceUnavailableError currently re-raise without logging; update the exception handlers that wrap the fetch helper (the except handling MemoryError, RecursionError and the except ServiceUnavailableError) to log a contextual message at WARNING or ERROR level including the exception details and any identifying context (e.g., request id, user id, function name) before re-raising the exception so the error path is recorded.
449-452:⚠️ Potential issue | 🟡 MinorLog before re-raising fatal/503 errors.
The
MemoryError/RecursionErrorandServiceUnavailableErrorbranches re-raise without logging, losing endpoint/source context. While these exceptions will propagate and may be logged elsewhere, the guideline requires logging at the error path for operational visibility.🛠️ Proposed fix
except MemoryError, RecursionError: + logger.error( + API_REQUEST_ERROR, + endpoint="activities", + source=_SRC_COST_TRACKER, + detail="fatal error", + exc_info=True, + ) raise except ServiceUnavailableError: + logger.warning( + API_REQUEST_ERROR, + endpoint="activities", + source=_SRC_COST_TRACKER, + detail="service unavailable", + exc_info=True, + ) raiseAs per coding guidelines, "Log at WARNING or ERROR with context before raising errors in Python code; all error paths must be logged."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/controllers/activities.py` around lines 449 - 452, The except branches that catch MemoryError/RecursionError and ServiceUnavailableError currently re-raise without logging; update the two except handlers that catch MemoryError, RecursionError and ServiceUnavailableError to call the module logger (e.g., logger.error or process_logger.error) with contextual information (endpoint/function name, request id or user context if available) and include the exception details (use exc_info=True) immediately before re-raising; ensure you only add logging and keep the final raise unchanged so the original exception still propagates.
482-485:⚠️ Potential issue | 🟡 MinorLog before re-raising fatal/503 errors.
Same issue as
_fetch_cost_records: the re-raise paths lack logging for operational context.🛠️ Proposed fix
except MemoryError, RecursionError: + logger.error( + API_REQUEST_ERROR, + endpoint="activities", + source=_SRC_TOOL_INVOCATION_TRACKER, + detail="fatal error", + exc_info=True, + ) raise except ServiceUnavailableError: + logger.warning( + API_REQUEST_ERROR, + endpoint="activities", + source=_SRC_TOOL_INVOCATION_TRACKER, + detail="service unavailable", + exc_info=True, + ) raiseAs per coding guidelines, "Log at WARNING or ERROR with context before raising errors in Python code; all error paths must be logged."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/controllers/activities.py` around lines 482 - 485, The except blocks that currently read "except MemoryError, RecursionError: raise" and "except ServiceUnavailableError: raise" must log contextual information before re-raising; locate these except clauses in src/synthorg/api/controllers/activities.py (pattern similar to _fetch_cost_records) and add a logger call (use the module's existing logger variable, e.g., logger or process_logger) that logs at WARNING or ERROR with a descriptive message and includes exception details (exc_info=True or stack_info) before the raise so operational context is captured.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/synthorg/api/controllers/activities.py`:
- Around line 507-510: The except branches that catch MemoryError/RecursionError
and ServiceUnavailableError currently re-raise without logging; update the
exception handlers that wrap the fetch helper (the except handling MemoryError,
RecursionError and the except ServiceUnavailableError) to log a contextual
message at WARNING or ERROR level including the exception details and any
identifying context (e.g., request id, user id, function name) before re-raising
the exception so the error path is recorded.
- Around line 449-452: The except branches that catch MemoryError/RecursionError
and ServiceUnavailableError currently re-raise without logging; update the two
except handlers that catch MemoryError, RecursionError and
ServiceUnavailableError to call the module logger (e.g., logger.error or
process_logger.error) with contextual information (endpoint/function name,
request id or user context if available) and include the exception details (use
exc_info=True) immediately before re-raising; ensure you only add logging and
keep the final raise unchanged so the original exception still propagates.
- Around line 482-485: The except blocks that currently read "except
MemoryError, RecursionError: raise" and "except ServiceUnavailableError: raise"
must log contextual information before re-raising; locate these except clauses
in src/synthorg/api/controllers/activities.py (pattern similar to
_fetch_cost_records) and add a logger call (use the module's existing logger
variable, e.g., logger or process_logger) that logs at WARNING or ERROR with a
descriptive message and includes exception details (exc_info=True or stack_info)
before the raise so operational context is captured.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4da6278e-d966-478b-8cf8-cf6e259daf55
📒 Files selected for processing (2)
src/synthorg/api/controllers/activities.pyweb/src/api/endpoints/activities.ts
📜 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)
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Build Sandbox
- GitHub Check: Dashboard Test
- GitHub Check: Test (Python 3.14)
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (3)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: Always reuse existing components fromweb/src/components/ui/before creating new React components in the web dashboard; never create duplicate components
Never hardcode hex colors, font-family, or pixel spacing in React web dashboard code; use design tokens instead
Never use real vendor names in the web dashboard code; use generic names likeexample-provider,example-large-001,example-medium-001in React/TypeScript code
Use ESLint with zero warnings for the web dashboard (conditional pre-commit hook onweb/src/**/*.{ts,tsx})
web/src/**/*.{ts,tsx}: Use design tokens exclusively for colors, typography, and spacing -- never hardcode hex values,rgba()values with hardcoded colors, pixel values for layout spacing, orfontFamilydeclarations directly in.tsx/.tsfiles.
Use Tailwind semantic classes (text-foreground,bg-card,text-accent,text-success,bg-danger) or CSS variables (var(--so-*)) for colors instead of hardcoded hex values.
Usefont-sansorfont-monofor typography instead of settingfontFamilydirectly.
Use density-aware tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind spacing for layout spacing instead of hardcoded pixel values.
Use design token variables (var(--so-shadow-card-hover),border-border,border-bright) for shadows and borders instead of hardcoded values.
Do not recreate status dots inline -- use the<StatusBadge>component instead.
Do not build card-with-header layouts from scratch -- use the<SectionCard>component instead.
Do not create metric displays withtext-metric font-bold-- use the<MetricCard>component instead.
Do not render initials circles manually -- use the<Avatar>component instead.
Do not create complex (>8 line) JSX inside.map()-- extract to a shared component instead.
Files:
web/src/api/endpoints/activities.ts
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Do not usefrom __future__ import annotationsin Python code; rely on PEP 649 native lazy annotations in Python 3.14+
Useexcept A, B:syntax without parentheses for exception handling in Python 3.14, as enforced by ruff (PEP 758)
Provide type hints for all public functions in Python code; strict mypy mode is required
Use Google-style docstrings on all public classes and functions in Python, as enforced by ruff D rules
Create new objects instead of mutating existing ones; never mutate existing objects in Python code
For non-Pydantic internal collections (registries,BaseTool), usecopy.deepcopy()at construction andMappingProxyTypewrapping for read-only enforcement
Fordict/listfields in frozen Pydantic models, rely onfrozen=Truefor field reassignment prevention and usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization)
Use frozen Pydantic models for config and identity; use separate mutable-via-copy models (usingmodel_copy(update=...)) for runtime state that evolves
Never mix static config fields with mutable runtime fields in one Pydantic model
Use Pydantic v2 withBaseModel,model_validator,computed_field, andConfigDict
Useallow_inf_nan=Falsein allConfigDictdeclarations in Pydantic models to rejectNaN/Infin numeric fields at validation time
Use@computed_fieldfor derived values in Pydantic models instead of storing and validating redundant fields (e.g.,TokenUsage.total_tokens)
UseNotBlankStrfromcore.typesfor all identifier and name fields in Python code, including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants, instead of manual whitespace validators
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new Python code (e.g., multiple tool invocations, parallel agent calls); prefer structured concurrency over barecreate_task
Keep Python functions under 50 line...
Files:
src/synthorg/api/controllers/activities.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every Python module with business logic must import and use the logger:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)
Never useimport logging/logging.getLogger()/print()in application Python code, except inobservability/setup.pyandobservability/sinks.pywhich may use stdlibloggingandprint(..., file=sys.stderr)for bootstrap code
Always name the logger variableloggerin Python code, not_loggerorlog
Use event name constants from the domain-specific module undersynthorg.observability.eventsfor all logging in Python code (e.g.,API_REQUEST_STARTEDfromevents.api,TOOL_INVOKE_STARTfromevents.tool,GIT_COMMAND_STARTfromevents.git)
Always use structured kwargs in logger calls:logger.info(EVENT, key=value), neverlogger.info("msg %s", val)in Python code
Log at WARNING or ERROR with context before raising errors in Python code; all error paths must be logged
Log at INFO level for all state transitions in Python code
Log at DEBUG level for object creation, internal flow, and entry/exit of key functions in Python code
All provider calls must go throughBaseCompletionProviderwhich applies retry and rate limiting automatically; never implement retry logic in driver subclasses or calling code
SetRetryConfigandRateLimiterConfigper-provider inProviderConfigin Python code
Retryable errors (is_retryable=True) in Python code include:RateLimitError,ProviderTimeoutError,ProviderConnectionError,ProviderInternalError; non-retryable errors raise immediately
UseRateLimitError.retry_afterfrom providers in the rate limiter; automatically pause future requests in Python code
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned Python code, docstrings, comments, tests, or config examples; use generic names likeexample-provider,example-large-001,example-medium-001,large/medium/smallaliases in...
Files:
src/synthorg/api/controllers/activities.py
🧠 Learnings (31)
📚 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/api/controllers/activities.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/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Use `except A, B:` syntax without parentheses for exception handling in Python 3.14, as enforced by ruff (PEP 758)
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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 : Prefer `asyncio.TaskGroup` for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare `create_task`. Existing code is being migrated incrementally.
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Prefer `asyncio.TaskGroup` for fan-out/fan-in parallel operations in new Python code (e.g., multiple tool invocations, parallel agent calls); prefer structured concurrency over bare `create_task`
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/engine/**/*.py : Catch `RetryExhaustedError` in the engine layer to trigger fallback chains; this signals that all retries failed in Python code
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-17T18:52:05.142Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T18:52:05.142Z
Learning: Applies to **/*.py : Async concurrency: prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Applied to files:
src/synthorg/api/controllers/activities.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 src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising.
Applied to files:
src/synthorg/api/controllers/activities.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 : All error paths must log at WARNING or ERROR with context before raising
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/**/*.py : Log at WARNING or ERROR with context before raising errors in Python code; all error paths must be logged
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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 src/**/*.py : All error paths must log at WARNING or ERROR with context before raising
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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/api/**/*.py : Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
Applied to files:
src/synthorg/api/controllers/activities.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/api/**/*.py : API package (api/): Litestar REST + WebSocket with controllers, guards, channels, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint, provider management endpoint (CRUD + test + presets), backup endpoint, RFC 9457 structured errors, AppState hot-reload slots, service auto-wiring (Phase 1 at construction, Phase 2 on startup), lifecycle helpers
Applied to files:
src/synthorg/api/controllers/activities.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/api/**/*.py : REST API: Litestar framework, controllers with guards, channels for WebSocket, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. RFC 9457 structured errors (ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation).
Applied to files:
src/synthorg/api/controllers/activities.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/api/**/*.py : Use Litestar for REST API and WebSocket API with JWT + API key + WS ticket authentication, RFC 9457 structured errors, and content negotiation.
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/synthorg/api/**/*.py : Litestar API must include setup wizard, auth/, auto-wiring, and lifecycle management
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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 : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from the domain-specific module under `synthorg.observability.events` for all logging in Python code (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`, `GIT_COMMAND_START` from `events.git`)
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.py
🔇 Additional comments (10)
src/synthorg/api/controllers/activities.py (7)
1-51: LGTM!Imports are well-organized with proper
TYPE_CHECKINGguards, logger is correctly initialized usingget_logger(__name__), and event constants are imported from the domain-specific module. The source name constants provide consistent degraded-source identification.
61-77: LGTM!The helper cleanly extracts task results and handles all completion states (cancelled, exception, success with degradation flag). The mutable accumulator pattern aligns with the deliberate design choice noted in the commit.
79-182: LGTM!The concurrent fetcher orchestration properly uses
asyncio.TaskGroupfor structured concurrency. TheExceptionGrouphandling correctly extracts and re-raises the underlying exception (not the subgroup wrapper) with proper logging before each re-raise path.
185-223: LGTM!Currency resolution with graceful degradation is well-implemented. Fatal errors are logged at ERROR level before re-raising, and transient failures log at WARNING before falling back to
DEFAULT_CURRENCY.
225-276: LGTM!The timeline builder cleanly orchestrates data fetching through extracted helpers and produces a deduplicated
degraded_sourceslist. Thedict.fromkeys()pattern for order-preserving deduplication is appropriate.
278-376: LGTM!The controller correctly implements all three linked issues:
- Enum-typed
event_typeparameter ensures 400 for invalid values and self-documenting OpenAPI- Fail-closed cost redaction when auth identity is missing or lacks write role
degraded_sourcespopulated in response for partial data detection
521-564: LGTM!Delegation record fetching correctly handles both org-wide and agent-specific paths, using
asyncio.TaskGroupfor concurrent queries when filtering by agent. The degradation flag combination (sent_deg or recv_deg) properly surfaces any partial failure.web/src/api/endpoints/activities.ts (3)
1-8: LGTM!The
ActivityFilterParamsinterface is well-typed withActivityEventTypefor the filter and literal union forlast_n_hours, matching the backend's enum validation and allowed window values.
10-23: ID generation is now deterministic and unique.The composite key
${timestamp}-${event_type}-${agentId}addresses the duplicate React key concern from previous reviews.Note:
agent_namestill displays the agent ID rather than a human-readable name sincerelated_idsonly contains IDs. Per previous review, this requires either a backend change to include names or a frontend agent lookup. Accepting as known limitation for now.
25-31: LGTM!The function correctly fetches, unwraps pagination, and transforms each
ActivityEventtoActivityItemwhile preserving the paginated result structure.
Add structured logging (error for fatal, warning for ServiceUnavailableError) before re-raising in _fetch_cost_records, _fetch_tool_invocations, and _safe_delegation_query, consistent with the pattern already applied to _fetch_task_metrics and _resolve_currency. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/synthorg/api/controllers/activities.py`:
- Around line 61-77: The _extract_task_result function currently mutates the
degraded list; change its signature to return a tuple (tuple[Any, ...], bool)
i.e. (data, is_degraded) instead of accepting/decorating a degraded list, and
remove any appends inside _extract_task_result; callers (e.g., places that call
_extract_task_result like for cost_task) must be updated to unpack the returned
(data, is_degraded) and append the source name (e.g. _SRC_COST_TRACKER) to their
local degraded list only when is_degraded is True, preserving existing
error/None/cancel handling inside _extract_task_result but returning the
degraded flag instead of mutating.
🪄 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: d7cddd5c-0035-4161-97ab-197069ef9600
📒 Files selected for processing (1)
src/synthorg/api/controllers/activities.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). (7)
- GitHub Check: Build Sandbox
- GitHub Check: Build Web
- GitHub Check: Build Backend
- GitHub Check: Dashboard Test
- GitHub Check: Test (Python 3.14)
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Do not usefrom __future__ import annotationsin Python code; rely on PEP 649 native lazy annotations in Python 3.14+
Useexcept A, B:syntax without parentheses for exception handling in Python 3.14, as enforced by ruff (PEP 758)
Provide type hints for all public functions in Python code; strict mypy mode is required
Use Google-style docstrings on all public classes and functions in Python, as enforced by ruff D rules
Create new objects instead of mutating existing ones; never mutate existing objects in Python code
For non-Pydantic internal collections (registries,BaseTool), usecopy.deepcopy()at construction andMappingProxyTypewrapping for read-only enforcement
Fordict/listfields in frozen Pydantic models, rely onfrozen=Truefor field reassignment prevention and usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization)
Use frozen Pydantic models for config and identity; use separate mutable-via-copy models (usingmodel_copy(update=...)) for runtime state that evolves
Never mix static config fields with mutable runtime fields in one Pydantic model
Use Pydantic v2 withBaseModel,model_validator,computed_field, andConfigDict
Useallow_inf_nan=Falsein allConfigDictdeclarations in Pydantic models to rejectNaN/Infin numeric fields at validation time
Use@computed_fieldfor derived values in Pydantic models instead of storing and validating redundant fields (e.g.,TokenUsage.total_tokens)
UseNotBlankStrfromcore.typesfor all identifier and name fields in Python code, including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants, instead of manual whitespace validators
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new Python code (e.g., multiple tool invocations, parallel agent calls); prefer structured concurrency over barecreate_task
Keep Python functions under 50 line...
Files:
src/synthorg/api/controllers/activities.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every Python module with business logic must import and use the logger:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)
Never useimport logging/logging.getLogger()/print()in application Python code, except inobservability/setup.pyandobservability/sinks.pywhich may use stdlibloggingandprint(..., file=sys.stderr)for bootstrap code
Always name the logger variableloggerin Python code, not_loggerorlog
Use event name constants from the domain-specific module undersynthorg.observability.eventsfor all logging in Python code (e.g.,API_REQUEST_STARTEDfromevents.api,TOOL_INVOKE_STARTfromevents.tool,GIT_COMMAND_STARTfromevents.git)
Always use structured kwargs in logger calls:logger.info(EVENT, key=value), neverlogger.info("msg %s", val)in Python code
Log at WARNING or ERROR with context before raising errors in Python code; all error paths must be logged
Log at INFO level for all state transitions in Python code
Log at DEBUG level for object creation, internal flow, and entry/exit of key functions in Python code
All provider calls must go throughBaseCompletionProviderwhich applies retry and rate limiting automatically; never implement retry logic in driver subclasses or calling code
SetRetryConfigandRateLimiterConfigper-provider inProviderConfigin Python code
Retryable errors (is_retryable=True) in Python code include:RateLimitError,ProviderTimeoutError,ProviderConnectionError,ProviderInternalError; non-retryable errors raise immediately
UseRateLimitError.retry_afterfrom providers in the rate limiter; automatically pause future requests in Python code
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned Python code, docstrings, comments, tests, or config examples; use generic names likeexample-provider,example-large-001,example-medium-001,large/medium/smallaliases in...
Files:
src/synthorg/api/controllers/activities.py
🧠 Learnings (31)
📚 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/api/controllers/activities.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/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Use `except A, B:` syntax without parentheses for exception handling in Python 3.14, as enforced by ruff (PEP 758)
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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 : Prefer `asyncio.TaskGroup` for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare `create_task`. Existing code is being migrated incrementally.
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to **/*.py : Prefer `asyncio.TaskGroup` for fan-out/fan-in parallel operations in new Python code (e.g., multiple tool invocations, parallel agent calls); prefer structured concurrency over bare `create_task`
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/engine/**/*.py : Catch `RetryExhaustedError` in the engine layer to trigger fallback chains; this signals that all retries failed in Python code
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-17T18:52:05.142Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T18:52:05.142Z
Learning: Applies to **/*.py : Async concurrency: prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Applied to files:
src/synthorg/api/controllers/activities.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 src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising.
Applied to files:
src/synthorg/api/controllers/activities.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 : All error paths must log at WARNING or ERROR with context before raising
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/**/*.py : Log at WARNING or ERROR with context before raising errors in Python code; all error paths must be logged
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.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 src/**/*.py : All error paths must log at WARNING or ERROR with context before raising
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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/api/**/*.py : Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
Applied to files:
src/synthorg/api/controllers/activities.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/api/**/*.py : API package (api/): Litestar REST + WebSocket with controllers, guards, channels, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint, provider management endpoint (CRUD + test + presets), backup endpoint, RFC 9457 structured errors, AppState hot-reload slots, service auto-wiring (Phase 1 at construction, Phase 2 on startup), lifecycle helpers
Applied to files:
src/synthorg/api/controllers/activities.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/api/**/*.py : REST API: Litestar framework, controllers with guards, channels for WebSocket, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. RFC 9457 structured errors (ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation).
Applied to files:
src/synthorg/api/controllers/activities.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/api/**/*.py : Use Litestar for REST API and WebSocket API with JWT + API key + WS ticket authentication, RFC 9457 structured errors, and content negotiation.
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/synthorg/api/**/*.py : Litestar API must include setup wizard, auth/, auto-wiring, and lifecycle management
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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 : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-30T19:05:44.280Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T19:05:44.280Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from the domain-specific module under `synthorg.observability.events` for all logging in Python code (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`, `GIT_COMMAND_START` from `events.git`)
Applied to files:
src/synthorg/api/controllers/activities.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/api/controllers/activities.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/api/controllers/activities.py
🔇 Additional comments (9)
src/synthorg/api/controllers/activities.py (9)
45-51: LGTM!The degraded source constants are well-defined and provide clear, consistent identifiers for tracking data source failures. Good practice to centralize these for use in both implementation and tests.
79-182: LGTM!The concurrent fetcher orchestration uses
asyncio.TaskGroupas required by guidelines, handlesExceptionGroupcorrectly by extracting and re-raising the underlying exceptions, and logs with context before re-raising fatal/service errors. The degraded-source tracking properly distinguishes between failed and successful tasks.
185-223: LGTM!The currency resolution properly handles fatal errors (re-raise with logging) and transient errors (fallback to
DEFAULT_CURRENCYwith degraded flag). The PEP 758 exception syntax is correctly applied.
225-276: LGTM!The timeline builder properly coordinates data fetching, handles degraded sources, and returns an immutable tuple. The
dict.fromkeys()pattern for deduplication while preserving order is appropriate.
353-360: LGTM! Fail-closed redaction is correctly implemented.The redaction logic properly implements fail-closed behavior:
- Redacts when
auth_userisNone(missing scope)- Redacts when
auth_useris not anAuthenticatedUser- Redacts when user role is not in the write set (observer, board_member)
This satisfies
#839's requirement to prevent exposure of cost details to read-only roles while preserving data for write roles.
382-427: LGTM!The task metrics fetcher properly handles all error categories with appropriate logging before re-raising fatal and service unavailable errors, and gracefully degrades on other exceptions.
430-521: LGTM!Both cost records and tool invocations fetchers follow the established pattern with proper error handling, logging, and graceful degradation. The early returns when trackers are unavailable (
has_cost_tracker,has_tool_invocation_tracker) correctly returnFalsefor the degraded flag since the source is simply not configured rather than failed.
524-560: LGTM!The safe delegation query wrapper provides consistent error handling for delegation store operations with proper logging and degradation signaling.
563-606: LGTM!The delegation records fetcher correctly uses
asyncio.TaskGroupfor concurrent sent/received queries when filtering by agent. The nestedTaskGroupstructure (this function inside_run_async_fetchers) is handled correctly sinceExceptionGroup.subgroup()recursively searches through nested groups.
| def _extract_task_result( | ||
| task: asyncio.Task[tuple[tuple[Any, ...], bool]] | None, | ||
| source_name: str, | ||
| degraded: list[str], | ||
| ) -> tuple[Any, ...]: | ||
| """Extract a completed task's data, appending to degraded if needed.""" | ||
| if task is None or task.cancelled(): | ||
| degraded.append(source_name) | ||
| return () | ||
| if task.exception() is not None: | ||
| degraded.append(source_name) | ||
| return () | ||
| data, is_degraded = task.result() | ||
| if is_degraded: | ||
| degraded.append(source_name) | ||
| return data | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider returning degraded status instead of mutating the list.
The function mutates the degraded list parameter, which conflicts with the coding guideline to avoid mutating existing objects. Consider returning a tuple (data, source_name | None) where the caller accumulates degraded sources, or return (data, is_degraded) and let the caller decide whether to append.
♻️ Alternative approach
-def _extract_task_result(
- task: asyncio.Task[tuple[tuple[Any, ...], bool]] | None,
- source_name: str,
- degraded: list[str],
-) -> tuple[Any, ...]:
- """Extract a completed task's data, appending to degraded if needed."""
- if task is None or task.cancelled():
- degraded.append(source_name)
- return ()
- if task.exception() is not None:
- degraded.append(source_name)
- return ()
- data, is_degraded = task.result()
- if is_degraded:
- degraded.append(source_name)
- return data
+def _extract_task_result(
+ task: asyncio.Task[tuple[tuple[Any, ...], bool]] | None,
+) -> tuple[tuple[Any, ...], bool]:
+ """Extract a completed task's data and degradation status."""
+ if task is None or task.cancelled():
+ return (), True
+ if task.exception() is not None:
+ return (), True
+ data, is_degraded = task.result()
+ return data, is_degradedThen the caller accumulates:
cost_records, cost_deg = _extract_task_result(cost_task)
if cost_deg:
degraded.append(_SRC_COST_TRACKER)As per coding guidelines, "Create new objects instead of mutating existing ones; never mutate existing objects in Python code."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/api/controllers/activities.py` around lines 61 - 77, The
_extract_task_result function currently mutates the degraded list; change its
signature to return a tuple (tuple[Any, ...], bool) i.e. (data, is_degraded)
instead of accepting/decorating a degraded list, and remove any appends inside
_extract_task_result; callers (e.g., places that call _extract_task_result like
for cost_task) must be updated to unpack the returned (data, is_degraded) and
append the source name (e.g. _SRC_COST_TRACKER) to their local degraded list
only when is_degraded is True, preserving existing error/None/cancel handling
inside _extract_task_result but returning the degraded flag instead of mutating.
🤖 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
ActivityEventTypeStrEnum (13 members) and use as theevent_typequery parameter type -- invalid values now return 400 instead of silent empty results, and OpenAPI schema self-documents valid valuescost_incurredevent descriptions for read-only roles (observer, board member) -- write roles see full details; fail-closed design (redact by default if auth identity is missing)degraded_sourcesfield toPaginatedResponse-- each data source fetcher reports degradation via(data, is_degraded)return tuples, accumulated into the response so API consumers can detect partial dataKey design decisions
degraded_sourcesonPaginatedResponserather than a specialized response type -- the field defaults to()so all existing endpoints are unaffectedActivityEventTypeinhr/enums.pyalongsideLifecycleEventType-- a superset with a defensive test ensuring the invariant holdsFiles changed (8)
src/synthorg/hr/enums.pyActivityEventTypeStrEnum (13 members)src/synthorg/hr/activity.pyredact_cost_events()with fail-closed fallbacksrc/synthorg/api/controllers/activities.py_build_timelinehelper, degradation tracking with source constantssrc/synthorg/api/dto.pydegraded_sourcesfield +allow_inf_nan=FalseonPaginatedResponsesrc/synthorg/observability/events/hr.pyHR_ACTIVITY_REDACTION_MISMATCHevent constanttests/unit/api/controllers/test_activities.pytests/unit/api/test_dto.pydegraded_sourcesdefault + JSON serialization teststests/unit/hr/test_activity.pyTest plan
event_typevalues return 400degraded_sources: []degraded_sourcesLifecycleEventTypevalues are a subset ofActivityEventType_cost_record_to_activityoutput matches redaction regexPre-reviewed by 6 agents (code-reviewer, silent-failure-hunter, type-design-analyzer, conventions-enforcer, async-concurrency-reviewer, issue-resolution-verifier), 12 findings addressed.
Closes #838
Closes #839
Closes #840
🤖 Generated with Claude Code