feat: add analytics trends and budget forecast API endpoints#798
feat: add analytics trends and budget forecast API endpoints#798
Conversation
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice. Scanned FilesNone |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📜 Recent review details⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
🧰 Additional context used📓 Path-based instructions (2)**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
src/synthorg/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (16)📓 Common learnings📚 Learning: 2026-03-17T06:30:14.180ZApplied to files:
📚 Learning: 2026-03-19T07:13:44.964ZApplied to files:
📚 Learning: 2026-03-17T22:08:13.456ZApplied to files:
📚 Learning: 2026-03-24T05:06:57.759ZApplied to files:
📚 Learning: 2026-03-16T07:22:28.134ZApplied to files:
📚 Learning: 2026-03-14T16:18:57.267ZApplied to files:
📚 Learning: 2026-03-14T15:43:05.601ZApplied to files:
📚 Learning: 2026-03-24T05:06:57.759ZApplied to files:
📚 Learning: 2026-03-14T15:43:05.601ZApplied to files:
📚 Learning: 2026-03-14T16:18:57.267ZApplied to files:
📚 Learning: 2026-03-15T16:55:07.730ZApplied to files:
📚 Learning: 2026-03-19T21:11:37.538ZApplied to files:
📚 Learning: 2026-03-14T16:18:57.267ZApplied to files:
📚 Learning: 2026-03-16T20:14:00.937ZApplied to files:
📚 Learning: 2026-03-19T07:13:44.964ZApplied to files:
🔇 Additional comments (12)
WalkthroughAdds time-series analytics and budget forecasting endpoints and related implementation. New guarded endpoints: Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly expands the system's analytical capabilities by introducing dedicated API endpoints for time-series trend analysis and budget forecasting. It also enriches the existing analytics overview with critical budget and agent activity metrics. These changes provide users with more granular and predictive insights into operational performance and resource allocation, enabling proactive management and strategic planning. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces significant new functionality by adding API endpoints for analytics trends and budget forecasting, and extending the existing overview endpoint. The changes are well-structured, with pure functions for calculations in budget/trends.py and new controller logic in analytics.py. The addition of comprehensive unit tests is also a great inclusion. My review has identified a critical syntax issue that would break the application in a Python 3 environment, along with some medium-severity suggestions to improve maintainability and robustness by removing duplicated code, improving exception handling, and clarifying mathematical operations.
| async def _resolve_agent_counts( | ||
| app_state: AppState, | ||
| config_agent_count: int, | ||
| ) -> tuple[int, int]: |
|
|
||
| Uses AgentRegistryService when available, falls back to |
There was a problem hiding this comment.
Catching a broad Exception can hide unexpected errors and make debugging difficult. Any failure in get_total_cost will silently cause period_cost to fall back to total_cost. The exception should be logged to provide visibility into what went wrong. Consider catching more specific exceptions if possible.
A similar issue exists on lines 372-373 where the fallback is to 0.0 and there is no logging.
except Exception:
logger.warning("Failed to get period cost for budget calculation, falling back to total cost", exc_info=True)
period_cost = total_cost| ), | ||
| ) | ||
|
|
||
| @get("/forecast", guards=[require_read_access]) | ||
| async def get_forecast( | ||
| self, | ||
| state: State, | ||
| horizon_days: Annotated[ | ||
| int, | ||
| Parameter( | ||
| ge=1, | ||
| le=90, | ||
| description="Projection horizon in days", | ||
| ), | ||
| ] = 14, | ||
| ) -> ApiResponse[ForecastResponse]: | ||
| """Return budget spend projection. | ||
|
|
||
| Uses average daily spend over the lookback period | ||
| (equal to horizon_days) to project future spend. | ||
|
|
||
| Args: | ||
| state: Application state. | ||
| horizon_days: Number of days to project forward. | ||
|
|
||
| Returns: | ||
| Forecast data envelope. |
There was a problem hiding this comment.
This function is a duplicate of _generate_bucket_starts in src/synthorg/budget/trends.py. The PR description states that this duplication was to be eliminated. To adhere to the DRY principle and improve maintainability, this function should be made public in trends.py (e.g., as generate_bucket_starts) and imported for use here, rather than being reimplemented. After that, this local implementation can be removed.
| """Project future budget spend using average daily spend. | ||
|
|
||
| Computes the average daily spend from the provided records, | ||
| then projects forward for ``horizon_days``. Confidence is |
There was a problem hiding this comment.
The PR description mentions that days_until_exhausted was fixed to use math.ceil, but the implementation uses int(), which truncates the result. This is inconsistent and could be confusing for future maintainers.
If the goal is to calculate the number of full days the budget will last, using math.floor() is more explicit and clearer than int(). If the goal is to determine on which day the budget will be exhausted, then math.ceil() should be used. Given the tests expect truncation, math.floor() seems appropriate to clarify intent.
| then projects forward for ``horizon_days``. Confidence is | |
| days_until = max(math.floor(budget_remaining_usd / avg_daily), 0) |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #798 +/- ##
==========================================
- Coverage 92.33% 92.29% -0.04%
==========================================
Files 574 576 +2
Lines 29798 29957 +159
Branches 2889 2896 +7
==========================================
+ Hits 27514 27650 +136
- Misses 1803 1824 +21
- Partials 481 483 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
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/analytics.py`:
- Around line 286-291: Update the endpoint/docstring that returns trend data to
explicitly state that the ACTIVE_AGENTS metric is a flat-line over the returned
buckets (it uses the current count from _resolve_agent_counts and repeats that
value for each timestamp generated by generate_bucket_starts), so consumers know
TrendDataPoint values for ACTIVE_AGENTS are not historical but a repeated
snapshot; mention the behavior in the function's docstring and in any external
API documentation where ACTIVE_AGENTS is described, referencing the
ACTIVE_AGENTS identifier, the use of _resolve_agent_counts, TrendDataPoint, and
generate_bucket_starts.
In `@src/synthorg/budget/trends.py`:
- Around line 337-363: The function _compute_daily_spend currently computes
lookback_days using (timestamps[-1].date() - timestamps[0].date()).days which is
exclusive of the end date and therefore overstates avg_daily; change the
lookback_days calculation to use an inclusive range by adding 1 (e.g.,
lookback_days = max((timestamps[-1].date() - timestamps[0].date()).days + 1, 1))
so avg_daily, confidence, and days_until_exhausted use the correct inclusive day
count; preserve the rounding and existing uses of timestamps, total_cost,
avg_daily and confidence.
- Around line 181-202: generate_bucket_starts currently floors the incoming
start via _bucket_key which silently widens the window; change the function to
make alignment explicit by adding a boolean parameter (e.g., align_start: bool)
and only floor start when align_start is True, otherwise begin iteration at the
raw start value; update the docstring to document the new parameter and its
behavior, and keep using BucketSize and _bucket_key to compute the floored start
when requested so the loop that appends current buckets remains correct.
🪄 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: f0047b0a-04d5-4e37-a5f7-e342406ab05a
📒 Files selected for processing (9)
docs/design/operations.mdsrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/tracker.pysrc/synthorg/budget/trends.pysrc/synthorg/observability/events/analytics.pytests/unit/api/conftest.pytests/unit/api/controllers/test_analytics.pytests/unit/budget/test_trends.pytests/unit/observability/test_events.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). (2)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Backend
🧰 Additional context used
📓 Path-based instructions (7)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotationsin Python files -- Python 3.14 has PEP 649 native lazy annotations
Useexcept A, B:syntax without parentheses (PEP 758 except syntax) in exception handlers
All public functions and methods must have type hints; enforce strict mypy mode
Use Google-style docstrings on all public classes and functions; enforced by ruff D rules
Files:
tests/unit/observability/test_events.pysrc/synthorg/budget/tracker.pytests/unit/api/conftest.pysrc/synthorg/observability/events/analytics.pytests/unit/api/controllers/test_analytics.pytests/unit/budget/test_trends.pysrc/synthorg/budget/trends.pysrc/synthorg/api/controllers/analytics.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slowmarkers on all tests
Maintain 80% minimum code coverage (enforced in CI)
Useasyncio_mode = 'auto'-- no manual@pytest.mark.asyncioneeded on async tests
Global test timeout is 30 seconds per test; do not add per-filepytest.mark.timeout(30)markers; non-default overrides liketimeout(60)are allowed
ALWAYS run pytest with-n autofor parallelism via pytest-xdist; never run tests sequentially
Prefer@pytest.mark.parametrizefor testing similar cases
Tests must usetest-provider,test-small-001, etc. instead of real vendor names
Use Hypothesis for Python property-based testing with@given+@settingsdecorators; use profiles:ci(50 examples, default) anddev(1000 examples) controlled viaHYPOTHESIS_PROFILEenv var
Run Hypothesis dev profile with:HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties
For timing-sensitive tests, mocktime.monotonic()andasyncio.sleep()to make them deterministic instead of widening timing margins. For tasks that must block indefinitely until cancelled, useasyncio.Event().wait()instead ofasyncio.sleep(large_number)
NEVER skip, dismiss, or ignore flaky tests -- always fix them fully and fundamentally
Files:
tests/unit/observability/test_events.pytests/unit/api/conftest.pytests/unit/api/controllers/test_analytics.pytests/unit/budget/test_trends.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic must import and usefrom synthorg.observability import get_loggerwith variable namelogger
Never useimport logging,logging.getLogger(), orprint()in application code except in observability/setup.py and observability/sinks.py
Use event name constants from domain-specific modules undersynthorg.observability.events(e.g.,API_REQUEST_STARTEDfromevents.api); import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT
Use structured logging with kwargs:logger.info(EVENT, key=value)-- never use old-style string formatting likelogger.info('msg %s', val)
Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries,BaseTool), usecopy.deepcopy()at construction +MappingProxyTypewrapping for read-only enforcement
Usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence) fordict/listfields in frozen Pydantic models
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models withmodel_copy(update=...)for runtime state that evolves (e.g., agent execution state, task progress)
Never mix static config fields with mutable runtime fields in one Pydantic model
Use@computed_fieldfor derived values instead of storing + validating redundant fields (e.g.,TokenUsage.total_tokens)
UseNotBlankStrfromcore.typesfor all identifier/name fields, including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants, instead of manual whitespace validators
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls) instead of barecreate_task
Keep functions under 50 lines and files under 800 lines
Handle errors explicitly; never silently swallow exceptions
Validate at system boundaries (user input, exter...
Files:
src/synthorg/budget/tracker.pysrc/synthorg/observability/events/analytics.pysrc/synthorg/budget/trends.pysrc/synthorg/api/controllers/analytics.py
src/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.py: Runuv run ruff check src/ tests/for linting;uv run ruff check src/ tests/ --fixfor auto-fix;uv run ruff format src/ tests/for formatting
Runuv run mypy src/ tests/for strict type-checking
Files:
src/synthorg/budget/tracker.pysrc/synthorg/observability/events/analytics.pysrc/synthorg/budget/trends.pysrc/synthorg/api/controllers/analytics.py
docs/**
📄 CodeRabbit inference engine (CLAUDE.md)
Run
uv run zensical buildto build docs (output:_site/docs/);uv run zensical servefor local preview
Files:
docs/design/operations.md
docs/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Use Markdown for documentation in
docs/directory, built with Zensical (config:mkdocs.yml)
Files:
docs/design/operations.md
docs/design/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Design spec in
docs/design/(7 pages); use for all architecture, data models, and behavior decisions. Reference with format:docs/design/<page>
Files:
docs/design/operations.md
🧠 Learnings (30)
📓 Common learnings
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.
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)
📚 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:
tests/unit/observability/test_events.pysrc/synthorg/observability/events/analytics.pydocs/design/operations.md
📚 Learning: 2026-03-23T06:50:44.558Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-23T06:50:44.558Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`
Applied to files:
tests/unit/observability/test_events.pysrc/synthorg/observability/events/analytics.pydocs/design/operations.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:
tests/unit/observability/test_events.pysrc/synthorg/observability/events/analytics.pydocs/design/operations.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/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls
Applied to files:
tests/unit/observability/test_events.pysrc/synthorg/observability/events/analytics.pydocs/design/operations.md
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`); import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`
Applied to files:
tests/unit/observability/test_events.pysrc/synthorg/observability/events/analytics.pydocs/design/operations.md
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from synthorg.observability.events domain-specific modules (e.g., PROVIDER_CALL_START from events.provider). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Applied to files:
tests/unit/observability/test_events.pysrc/synthorg/observability/events/analytics.pydocs/design/operations.md
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
tests/unit/observability/test_events.pysrc/synthorg/observability/events/analytics.pydocs/design/operations.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 src/**/*.py : Use event name constants from domain-specific modules under ai_company.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.) — import directly
Applied to files:
tests/unit/observability/test_events.pysrc/synthorg/observability/events/analytics.pydocs/design/operations.md
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use event name constants from domain-specific modules under `ai_company.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`). Import directly: `from ai_company.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
tests/unit/observability/test_events.pysrc/synthorg/observability/events/analytics.pydocs/design/operations.md
📚 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:
tests/unit/observability/test_events.pysrc/synthorg/observability/events/analytics.pydocs/design/operations.md
📚 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/budget/tracker.pytests/unit/budget/test_trends.pysrc/synthorg/budget/trends.pysrc/synthorg/api/controllers/analytics.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:
src/synthorg/budget/tracker.pytests/unit/api/controllers/test_analytics.pytests/unit/budget/test_trends.pysrc/synthorg/budget/trends.pysrc/synthorg/api/controllers/analytics.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/conftest.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:
tests/unit/api/conftest.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:
tests/unit/api/conftest.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 docs/design/*.md : Design spec pages: 7 pages in `docs/design/` — index, agents, organization, communication, engine, memory, operations
Applied to files:
docs/design/operations.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/**/*.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/budget/trends.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: 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/budget/trends.pysrc/synthorg/api/controllers/analytics.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/analytics.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/analytics.py
📚 Learning: 2026-03-23T06:50:44.558Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-23T06:50:44.558Z
Learning: Applies to **/*.py : Use `except A, B:` syntax without parentheses (PEP 758 except syntax) in exception handlers
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.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/analytics.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/analytics.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/analytics.py
📚 Learning: 2026-03-23T06:50:44.558Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-23T06:50:44.558Z
Learning: Applies to src/synthorg/**/*.py : Handle errors explicitly; never silently swallow exceptions
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.py
📚 Learning: 2026-03-19T21:11:37.538Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T21:11:37.538Z
Learning: Applies to **/*.py : Errors: handle explicitly, never silently swallow.
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.py
🧬 Code graph analysis (5)
src/synthorg/budget/tracker.py (1)
web/src/api/types.ts (1)
BudgetConfig(407-414)
tests/unit/api/conftest.py (3)
tests/unit/hr/promotion/conftest.py (1)
tracker(104-106)src/synthorg/hr/performance/tracker.py (1)
PerformanceTracker(51-506)src/synthorg/api/state.py (1)
performance_tracker(294-299)
tests/unit/budget/test_trends.py (2)
src/synthorg/budget/trends.py (9)
BucketSize(49-53)TrendMetric(40-46)TrendPeriod(32-37)bucket_cost_records(208-247)bucket_success_rate(286-331)bucket_task_completions(250-283)period_to_timedelta(151-160)project_daily_spend(393-446)resolve_bucket_size(137-148)src/synthorg/hr/performance/models.py (1)
TaskMetricRecord(25-67)
src/synthorg/budget/trends.py (4)
web/src/api/types.ts (1)
CostRecord(382-392)src/synthorg/hr/performance/models.py (1)
TaskMetricRecord(25-67)src/synthorg/budget/tracker.py (1)
record(108-121)src/synthorg/api/dto.py (1)
is_success(485-487)
src/synthorg/api/controllers/analytics.py (6)
src/synthorg/api/errors.py (1)
ServiceUnavailableError(222-231)src/synthorg/api/state.py (2)
AppState(42-511)has_agent_registry(307-309)src/synthorg/budget/trends.py (9)
BucketSize(49-53)TrendDataPoint(59-70)TrendMetric(40-46)TrendPeriod(32-37)bucket_cost_records(208-247)bucket_success_rate(286-331)bucket_task_completions(250-283)project_daily_spend(393-446)resolve_bucket_size(137-148)src/synthorg/budget/tracker.py (2)
get_total_cost(123-146)get_records(194-234)src/synthorg/hr/performance/tracker.py (1)
get_task_metrics(374-400)src/synthorg/engine/agent_state.py (1)
idle(104-117)
🔇 Additional comments (7)
src/synthorg/api/controllers/analytics.py (7)
33-41: LGTM!Correctly uses
get_loggerfrom observability and imports event constants from the domain-specificanalyticsevents module, following project conventions.
47-147: LGTM!Response models are well-designed: all frozen for immutability, fields have appropriate constraints (
ge=0.0,le=1.0), and docstrings clearly document semantics including edge cases likebudget_used_percent > 100indicating overrun.
160-200: LGTM!Excellent error handling pattern: critical errors (
MemoryError,RecursionError) are re-raised to avoid masking resource exhaustion, while other failures degrade gracefully with logging. The division-by-zero is properly guarded by the early return on line 176.
203-233: LGTM!Good defensive coding with
max(total - len(active), 0)preventing negative idle counts if data is inconsistent. The service availability check viahas_agent_registryproperly guards the optional dependency.
322-345: LGTM!Good use of
asyncio.TaskGroupfor parallel queries as recommended by coding guidelines. TheExceptionGrouphandling logs context before re-raising the first exception, maintaining explicit error handling.
387-438: LGTM!Clean endpoint implementation with proper parameter validation via
Parameterconstraints. The structured logging captures essential context for debugging without sensitive data.
440-498: LGTM!The forecast endpoint correctly validates
horizon_days(1-90) at the API layer, passes deterministicnowtoproject_daily_spendfor testability, and properly constructs the response from theBudgetForecastresult.
| # ACTIVE_AGENTS: flat line at current count | ||
| active_count, _ = await _resolve_agent_counts(app_state, 0) | ||
| return tuple( | ||
| TrendDataPoint(timestamp=bs, value=float(active_count)) | ||
| for bs in generate_bucket_starts(start, now, bucket_size) | ||
| ) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider documenting the flat-line behavior for API consumers.
The ACTIVE_AGENTS metric returns the current count repeated across all buckets rather than historical data. While the code comment explains this, consider documenting this limitation in the endpoint docstring or API documentation so consumers understand the sparkline isn't showing true historical trends for this metric.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/api/controllers/analytics.py` around lines 286 - 291, Update the
endpoint/docstring that returns trend data to explicitly state that the
ACTIVE_AGENTS metric is a flat-line over the returned buckets (it uses the
current count from _resolve_agent_counts and repeats that value for each
timestamp generated by generate_bucket_starts), so consumers know TrendDataPoint
values for ACTIVE_AGENTS are not historical but a repeated snapshot; mention the
behavior in the function's docstring and in any external API documentation where
ACTIVE_AGENTS is described, referencing the ACTIVE_AGENTS identifier, the use of
_resolve_agent_counts, TrendDataPoint, and generate_bucket_starts.
| def generate_bucket_starts( | ||
| start: datetime, | ||
| end: datetime, | ||
| bucket_size: BucketSize, | ||
| ) -> list[datetime]: | ||
| """Generate all bucket start times in [start, end). | ||
|
|
||
| Args: | ||
| start: Period start (inclusive). | ||
| end: Period end (exclusive). | ||
| bucket_size: Granularity. | ||
|
|
||
| Returns: | ||
| Sorted list of bucket start datetimes. | ||
| """ | ||
| step = timedelta(hours=1) if bucket_size == BucketSize.HOUR else timedelta(days=1) | ||
| current = _bucket_key(start, bucket_size) | ||
| buckets: list[datetime] = [] | ||
| while current < end: | ||
| buckets.append(current) | ||
| current = current + step | ||
| return buckets |
There was a problem hiding this comment.
generate_bucket_starts() silently widens the requested window.
This helper says it returns bucket starts in [start, end), but it floors start before iterating. With unaligned inputs that emits a leading partial bucket before start, so a "7d" hourly range can come back with 169 points and a first timestamp that does not actually represent the requested interval. Please make alignment explicit instead of silently widening the range.
🛠️ One safe fix
def generate_bucket_starts(
start: datetime,
end: datetime,
bucket_size: BucketSize,
) -> list[datetime]:
"""Generate all bucket start times in [start, end).
@@
Returns:
Sorted list of bucket start datetimes.
+
+ Raises:
+ ValueError: If `start` or `end` is not aligned to `bucket_size`.
"""
step = timedelta(hours=1) if bucket_size == BucketSize.HOUR else timedelta(days=1)
- current = _bucket_key(start, bucket_size)
+ if start != _bucket_key(start, bucket_size) or end != _bucket_key(
+ end,
+ bucket_size,
+ ):
+ msg = "start and end must align to the selected bucket size"
+ raise ValueError(msg)
+ current = start
buckets: list[datetime] = []
while current < end:
buckets.append(current)
current = current + step
return buckets🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/budget/trends.py` around lines 181 - 202, generate_bucket_starts
currently floors the incoming start via _bucket_key which silently widens the
window; change the function to make alignment explicit by adding a boolean
parameter (e.g., align_start: bool) and only floor start when align_start is
True, otherwise begin iteration at the raw start value; update the docstring to
document the new parameter and its behavior, and keep using BucketSize and
_bucket_key to compute the floored start when requested so the loop that appends
current buckets remains correct.
| def _compute_daily_spend( | ||
| records: Sequence[CostRecord], | ||
| ) -> tuple[float, float, int]: | ||
| """Compute average daily spend and confidence inputs. | ||
|
|
||
| Args: | ||
| records: Non-empty cost records. | ||
|
|
||
| Returns: | ||
| Tuple of (avg_daily_spend, confidence, lookback_days). | ||
| """ | ||
| timestamps = sorted(r.timestamp for r in records) | ||
| lookback_days = max( | ||
| (timestamps[-1].date() - timestamps[0].date()).days, | ||
| 1, | ||
| ) | ||
| total_cost = round( | ||
| math.fsum(r.cost_usd for r in records), | ||
| BUDGET_ROUNDING_PRECISION, | ||
| ) | ||
| avg_daily = round(total_cost / lookback_days, BUDGET_ROUNDING_PRECISION) | ||
| days_with_data = len({r.timestamp.date() for r in records}) | ||
| confidence = round( | ||
| min(days_with_data / lookback_days, 1.0), | ||
| BUDGET_ROUNDING_PRECISION, | ||
| ) | ||
| return avg_daily, confidence, lookback_days |
There was a problem hiding this comment.
_compute_daily_spend() overestimates burn rate for multi-day samples.
(timestamps[-1].date() - timestamps[0].date()).days excludes one endpoint. For example, records on March 1, 2026 and March 2, 2026 yield lookback_days == 1, so $20 of spend is treated as $20/day instead of $10/day. That inflates avg_daily_spend_usd, days_until_exhausted, and confidence.
🛠️ Proposed fix
- lookback_days = max(
- (timestamps[-1].date() - timestamps[0].date()).days,
- 1,
- )
+ lookback_days = max(
+ (timestamps[-1].date() - timestamps[0].date()).days + 1,
+ 1,
+ )📝 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.
| def _compute_daily_spend( | |
| records: Sequence[CostRecord], | |
| ) -> tuple[float, float, int]: | |
| """Compute average daily spend and confidence inputs. | |
| Args: | |
| records: Non-empty cost records. | |
| Returns: | |
| Tuple of (avg_daily_spend, confidence, lookback_days). | |
| """ | |
| timestamps = sorted(r.timestamp for r in records) | |
| lookback_days = max( | |
| (timestamps[-1].date() - timestamps[0].date()).days, | |
| 1, | |
| ) | |
| total_cost = round( | |
| math.fsum(r.cost_usd for r in records), | |
| BUDGET_ROUNDING_PRECISION, | |
| ) | |
| avg_daily = round(total_cost / lookback_days, BUDGET_ROUNDING_PRECISION) | |
| days_with_data = len({r.timestamp.date() for r in records}) | |
| confidence = round( | |
| min(days_with_data / lookback_days, 1.0), | |
| BUDGET_ROUNDING_PRECISION, | |
| ) | |
| return avg_daily, confidence, lookback_days | |
| def _compute_daily_spend( | |
| records: Sequence[CostRecord], | |
| ) -> tuple[float, float, int]: | |
| """Compute average daily spend and confidence inputs. | |
| Args: | |
| records: Non-empty cost records. | |
| Returns: | |
| Tuple of (avg_daily_spend, confidence, lookback_days). | |
| """ | |
| timestamps = sorted(r.timestamp for r in records) | |
| lookback_days = max( | |
| (timestamps[-1].date() - timestamps[0].date()).days + 1, | |
| 1, | |
| ) | |
| total_cost = round( | |
| math.fsum(r.cost_usd for r in records), | |
| BUDGET_ROUNDING_PRECISION, | |
| ) | |
| avg_daily = round(total_cost / lookback_days, BUDGET_ROUNDING_PRECISION) | |
| days_with_data = len({r.timestamp.date() for r in records}) | |
| confidence = round( | |
| min(days_with_data / lookback_days, 1.0), | |
| BUDGET_ROUNDING_PRECISION, | |
| ) | |
| return avg_daily, confidence, lookback_days |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/budget/trends.py` around lines 337 - 363, The function
_compute_daily_spend currently computes lookback_days using
(timestamps[-1].date() - timestamps[0].date()).days which is exclusive of the
end date and therefore overstates avg_daily; change the lookback_days
calculation to use an inclusive range by adding 1 (e.g., lookback_days =
max((timestamps[-1].date() - timestamps[0].date()).days + 1, 1)) so avg_daily,
confidence, and days_until_exhausted use the correct inclusive day count;
preserve the rounding and existing uses of timestamps, total_cost, avg_daily and
confidence.
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 `@tests/unit/budget/test_trends.py`:
- Around line 364-385: The test comment in test_known_daily_spend is misleading
about span counting; update the inline comment in the test (around the
test_known_daily_spend function) to explicitly state that project_daily_spend
uses an exclusive span calculation (timestamp delta = 2 days for Mar 1 -> Mar 3)
rather than saying "3-day span", e.g. note that the implementation computes a
2-day delta and clamps to 2 so average = $30/2 = $15/day; keep assertions
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: d372bc7b-9ffc-48e3-8a1f-849e70d95426
📒 Files selected for processing (6)
CLAUDE.mdsrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/trends.pytests/unit/api/controllers/test_analytics.pytests/unit/budget/test_trends.pytests/unit/observability/test_events.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). (5)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Sandbox
- GitHub Check: Build Web
- GitHub Check: Build Backend
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotationsin Python files -- Python 3.14 has PEP 649 native lazy annotations
Useexcept A, B:syntax without parentheses (PEP 758 except syntax) in exception handlers
All public functions and methods must have type hints; enforce strict mypy mode
Use Google-style docstrings on all public classes and functions; enforced by ruff D rules
Files:
tests/unit/observability/test_events.pytests/unit/budget/test_trends.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/trends.pytests/unit/api/controllers/test_analytics.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slowmarkers on all tests
Maintain 80% minimum code coverage (enforced in CI)
Useasyncio_mode = 'auto'-- no manual@pytest.mark.asyncioneeded on async tests
Global test timeout is 30 seconds per test; do not add per-filepytest.mark.timeout(30)markers; non-default overrides liketimeout(60)are allowed
ALWAYS run pytest with-n autofor parallelism via pytest-xdist; never run tests sequentially
Prefer@pytest.mark.parametrizefor testing similar cases
Tests must usetest-provider,test-small-001, etc. instead of real vendor names
Use Hypothesis for Python property-based testing with@given+@settingsdecorators; use profiles:ci(50 examples, default) anddev(1000 examples) controlled viaHYPOTHESIS_PROFILEenv var
Run Hypothesis dev profile with:HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties
For timing-sensitive tests, mocktime.monotonic()andasyncio.sleep()to make them deterministic instead of widening timing margins. For tasks that must block indefinitely until cancelled, useasyncio.Event().wait()instead ofasyncio.sleep(large_number)
NEVER skip, dismiss, or ignore flaky tests -- always fix them fully and fundamentally
Files:
tests/unit/observability/test_events.pytests/unit/budget/test_trends.pytests/unit/api/controllers/test_analytics.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic must import and usefrom synthorg.observability import get_loggerwith variable namelogger
Never useimport logging,logging.getLogger(), orprint()in application code except in observability/setup.py and observability/sinks.py
Use event name constants from domain-specific modules undersynthorg.observability.events(e.g.,API_REQUEST_STARTEDfromevents.api); import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT
Use structured logging with kwargs:logger.info(EVENT, key=value)-- never use old-style string formatting likelogger.info('msg %s', val)
Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries,BaseTool), usecopy.deepcopy()at construction +MappingProxyTypewrapping for read-only enforcement
Usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence) fordict/listfields in frozen Pydantic models
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models withmodel_copy(update=...)for runtime state that evolves (e.g., agent execution state, task progress)
Never mix static config fields with mutable runtime fields in one Pydantic model
Use@computed_fieldfor derived values instead of storing + validating redundant fields (e.g.,TokenUsage.total_tokens)
UseNotBlankStrfromcore.typesfor all identifier/name fields, including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants, instead of manual whitespace validators
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls) instead of barecreate_task
Keep functions under 50 lines and files under 800 lines
Handle errors explicitly; never silently swallow exceptions
Validate at system boundaries (user input, exter...
Files:
src/synthorg/api/controllers/analytics.pysrc/synthorg/budget/trends.py
src/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.py: Runuv run ruff check src/ tests/for linting;uv run ruff check src/ tests/ --fixfor auto-fix;uv run ruff format src/ tests/for formatting
Runuv run mypy src/ tests/for strict type-checking
Files:
src/synthorg/api/controllers/analytics.pysrc/synthorg/budget/trends.py
🧠 Learnings (32)
📓 Common learnings
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.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: 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).
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)
📚 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:
tests/unit/observability/test_events.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:
tests/unit/observability/test_events.py
📚 Learning: 2026-03-23T06:50:44.558Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-23T06:50:44.558Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`
Applied to files:
tests/unit/observability/test_events.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls
Applied to files:
tests/unit/observability/test_events.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from synthorg.observability.events domain-specific modules (e.g., PROVIDER_CALL_START from events.provider). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Applied to files:
tests/unit/observability/test_events.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`); import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`
Applied to files:
tests/unit/observability/test_events.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/observability/test_events.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use event name constants from domain-specific modules under `ai_company.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`). Import directly: `from ai_company.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
tests/unit/observability/test_events.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 : Use event name constants from domain-specific modules under ai_company.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.) — import directly
Applied to files:
tests/unit/observability/test_events.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:
tests/unit/observability/test_events.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:
CLAUDE.mdtests/unit/budget/test_trends.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/trends.pytests/unit/api/controllers/test_analytics.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:
CLAUDE.mdtests/unit/budget/test_trends.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/trends.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:
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/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:
CLAUDE.md
📚 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:
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/communication/**/*.py : Communication package (communication/): message bus, dispatcher, messenger, channels, delegation, loop prevention, conflict resolution; meeting/ subpackage for meeting protocol (round-robin, position papers, structured phases), scheduler (frequency, participant resolver), orchestrator
Applied to files:
CLAUDE.md
📚 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:
CLAUDE.md
📚 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.md
📚 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: 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:
CLAUDE.mdsrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/trends.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.md
📚 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/analytics.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/analytics.py
📚 Learning: 2026-03-23T06:50:44.558Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-23T06:50:44.558Z
Learning: Applies to **/*.py : Use `except A, B:` syntax without parentheses (PEP 758 except syntax) in exception handlers
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.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/analytics.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/analytics.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/analytics.py
📚 Learning: 2026-03-23T06:50:44.558Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-23T06:50:44.558Z
Learning: Applies to src/synthorg/**/*.py : Handle errors explicitly; never silently swallow exceptions
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.py
📚 Learning: 2026-03-19T21:11:37.538Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T21:11:37.538Z
Learning: Applies to **/*.py : Errors: handle explicitly, never silently swallow.
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.py
🧬 Code graph analysis (4)
tests/unit/budget/test_trends.py (3)
src/synthorg/budget/trends.py (10)
BucketSize(45-49)TrendMetric(36-42)TrendPeriod(28-33)bucket_cost_records(208-247)bucket_success_rate(286-331)bucket_task_completions(250-283)generate_bucket_starts(178-202)period_to_timedelta(148-157)project_daily_spend(396-449)resolve_bucket_size(134-145)src/synthorg/hr/performance/models.py (1)
TaskMetricRecord(25-67)src/synthorg/api/dto.py (1)
is_success(485-487)
src/synthorg/api/controllers/analytics.py (4)
src/synthorg/api/errors.py (1)
ServiceUnavailableError(222-231)src/synthorg/api/state.py (2)
AppState(42-511)has_agent_registry(307-309)src/synthorg/budget/trends.py (12)
BucketSize(45-49)ForecastPoint(69-83)TrendDataPoint(55-66)TrendMetric(36-42)TrendPeriod(28-33)bucket_cost_records(208-247)bucket_success_rate(286-331)bucket_task_completions(250-283)generate_bucket_starts(178-202)period_to_timedelta(148-157)project_daily_spend(396-449)resolve_bucket_size(134-145)src/synthorg/budget/tracker.py (2)
get_total_cost(123-146)get_records(194-234)
src/synthorg/budget/trends.py (2)
web/src/api/types.ts (1)
CostRecord(382-392)src/synthorg/hr/performance/models.py (1)
TaskMetricRecord(25-67)
tests/unit/api/controllers/test_analytics.py (7)
src/synthorg/budget/tracker.py (2)
CostTracker(68-464)record(108-121)src/synthorg/hr/performance/models.py (1)
TaskMetricRecord(25-67)src/synthorg/hr/performance/tracker.py (2)
PerformanceTracker(51-506)record_task_metric(136-159)tests/unit/api/conftest.py (7)
test_client(210-243)cost_tracker(180-181)performance_tracker(195-196)fake_persistence(166-169)fake_message_bus(173-176)_seed_test_users(246-277)auth_service(161-162)tests/unit/api/fakes.py (1)
FakePersistenceBackend(416-519)src/synthorg/api/app.py (1)
create_app(407-639)src/synthorg/settings/registry.py (1)
get_registry(111-113)
🔇 Additional comments (16)
src/synthorg/budget/trends.py (2)
351-366:_compute_daily_spend()uses exclusive day counting, which may overestimate burn rate.The calculation
(timestamps[-1].date() - timestamps[0].date()).dayscounts the interval between dates, not the inclusive span. For records on March 1 and March 3, this yields 2 days, but the actual span covers 3 calendar days.This affects
avg_daily_spend_usdaccuracy: $30 spread across a 3-day window should be ~$10/day, not $15/day.If this is intentional (treating the span as "days of activity"), please add a comment clarifying the rationale. Otherwise, consider the inclusive fix:
🛠️ Proposed fix for inclusive day counting
lookback_days = max( - (timestamps[-1].date() - timestamps[0].date()).days, + (timestamps[-1].date() - timestamps[0].date()).days + 1, 1, )
1-24: LGTM! Well-structured pure functions module.The module correctly follows the pure-function pattern for bucketing and forecasting. Models use
frozen=Trueandallow_inf_nan=Falseas required by the coding guidelines.tests/unit/observability/test_events.py (1)
181-181: LGTM!The new
"analytics"module is correctly added to the expected set, and the test for analytics event constants follows the established pattern used by other event tests in this file.Also applies to: 238-247
CLAUDE.md (1)
106-106: LGTM!The package structure documentation accurately reflects the new trend analysis and budget forecasting capabilities added by this PR.
tests/unit/api/controllers/test_analytics.py (4)
27-65: LGTM! Clean test helpers.The
_make_cost_recordand_make_task_metrichelpers follow the vendor-agnostic naming convention (test-provider,test-small-001) and provide sensible defaults for concise test setup.
150-275: LGTM! Comprehensive trends endpoint coverage.The tests cover default parameters, period/metric variations, data aggregation, invalid inputs, and auth requirements. The async tests properly await tracker operations.
280-359: LGTM! Solid forecast endpoint coverage.Tests verify empty state, data-driven projections, custom horizons, validation boundaries, and authentication. Edge cases for invalid horizon values (0, 100) are appropriately covered.
364-402: LGTM! Graceful degradation test correctly verifies fallback behavior.The test properly constructs an app without a
PerformanceTrackerand verifies that the trends endpoint returns emptydata_pointsinstead of failing, matching the controller's error handling.tests/unit/budget/test_trends.py (3)
1-65: LGTM! Well-organized test module with clear helpers.The test helpers follow the vendor-agnostic naming guidelines and provide appropriate defaults. The file structure with section comments aids readability.
108-211: LGTM! Thorough bucketing tests.Tests cover empty records, daily/hourly bucketing, out-of-range exclusion, single records, sorting, and immutability via
ValidationErroron frozen models. Thepytest.approxusage for floating-point comparisons is appropriate.
352-479: LGTM! Comprehensive forecast projection tests.Edge cases for empty records, zero spend, budget exhaustion, confidence scoring, and sequential date projections are well covered. The frozen model validation test at lines 464-467 verifies immutability constraints.
src/synthorg/api/controllers/analytics.py (5)
54-96: LGTM! Well-designed response model with appropriate constraints.
OverviewMetricscorrectly usesallow_inf_nan=False,ge=0.0constraints, and documents thatbudget_used_percentcan exceed 100% for overruns. The frozen config ensures immutability.
168-208: LGTM! Robust budget context resolution with graceful fallback.The helper correctly handles missing budget config, logs warnings on query failures, and uses the fallback total cost. The PEP 758
except MemoryError, RecursionError:syntax is correctly used to re-raise critical errors.
278-328: LGTM! Clean metric dispatch with graceful degradation.The function handles each
TrendMetricappropriately. TheServiceUnavailableErrorcatch forperformance_tracker(lines 306-313) ensures the endpoint degrades gracefully instead of failing.The inline comment at lines 322-323 appropriately documents the flat-line behavior for
ACTIVE_AGENTS.
452-503: LGTM! Trends endpoint with proper parameter validation.The
Annotated[TrendPeriod, Parameter(...)]pattern provides clear API documentation. The endpoint correctly converts period to bucket size and delegates to the pure bucketing functions.
505-565: LGTM! Forecast endpoint with bounded horizon.The
ge=1, le=90constraint onhorizon_daysprevents unbounded projections. The lookback period matching the horizon is a sensible default for confidence calculation.
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 `@tests/unit/budget/test_trends.py`:
- Around line 451-463: Update the misleading comment in the
test_confidence_partial_coverage test: change the opening comment that currently
reads “4-day span -> confidence 0.5” to correctly describe the exclusive span
and expected confidence (span is 3 days, data on 2 days -> confidence 2/3) so it
matches the assertion `assert result.confidence == pytest.approx(2.0 / 3.0)` and
clarifies the behavior of project_daily_spend used in this test.
🪄 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: 1d8e7183-a8b1-4d71-9e47-29e35c0a5458
📒 Files selected for processing (1)
tests/unit/budget/test_trends.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). (5)
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Build Sandbox
- GitHub Check: Test (Python 3.14)
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotationsin Python files -- Python 3.14 has PEP 649 native lazy annotations
Useexcept A, B:syntax without parentheses (PEP 758 except syntax) in exception handlers
All public functions and methods must have type hints; enforce strict mypy mode
Use Google-style docstrings on all public classes and functions; enforced by ruff D rules
Files:
tests/unit/budget/test_trends.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slowmarkers on all tests
Maintain 80% minimum code coverage (enforced in CI)
Useasyncio_mode = 'auto'-- no manual@pytest.mark.asyncioneeded on async tests
Global test timeout is 30 seconds per test; do not add per-filepytest.mark.timeout(30)markers; non-default overrides liketimeout(60)are allowed
ALWAYS run pytest with-n autofor parallelism via pytest-xdist; never run tests sequentially
Prefer@pytest.mark.parametrizefor testing similar cases
Tests must usetest-provider,test-small-001, etc. instead of real vendor names
Use Hypothesis for Python property-based testing with@given+@settingsdecorators; use profiles:ci(50 examples, default) anddev(1000 examples) controlled viaHYPOTHESIS_PROFILEenv var
Run Hypothesis dev profile with:HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties
For timing-sensitive tests, mocktime.monotonic()andasyncio.sleep()to make them deterministic instead of widening timing margins. For tasks that must block indefinitely until cancelled, useasyncio.Event().wait()instead ofasyncio.sleep(large_number)
NEVER skip, dismiss, or ignore flaky tests -- always fix them fully and fundamentally
Files:
tests/unit/budget/test_trends.py
🧠 Learnings (3)
📓 Common learnings
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.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: 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).
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)
📚 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/budget/test_trends.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/budget/test_trends.py
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 `@tests/unit/budget/test_trends.py`:
- Around line 356-480: Add a deterministic assertion that when calling
project_daily_spend with an explicit now argument the projection start aligns
with that anchor: in a suitable test (e.g., extend
test_projections_have_sequential_dates or create a new test) call
project_daily_spend(records, horizon_days=..., now=<fixed datetime>) and assert
result.daily_projections[0].day == <fixed datetime>.date(); reference the
project_daily_spend function and the daily_projections list to locate where to
add the check so the projections' first day uses the provided now anchor.
🪄 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: ca8a024e-83e0-4ea5-b535-050cf4b3d9b7
📒 Files selected for processing (1)
tests/unit/budget/test_trends.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). (4)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Sandbox
- GitHub Check: Build Backend
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotationsin Python files -- Python 3.14 has PEP 649 native lazy annotations
Useexcept A, B:syntax without parentheses (PEP 758 except syntax) in exception handlers
All public functions and methods must have type hints; enforce strict mypy mode
Use Google-style docstrings on all public classes and functions; enforced by ruff D rules
Files:
tests/unit/budget/test_trends.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slowmarkers on all tests
Maintain 80% minimum code coverage (enforced in CI)
Useasyncio_mode = 'auto'-- no manual@pytest.mark.asyncioneeded on async tests
Global test timeout is 30 seconds per test; do not add per-filepytest.mark.timeout(30)markers; non-default overrides liketimeout(60)are allowed
ALWAYS run pytest with-n autofor parallelism via pytest-xdist; never run tests sequentially
Prefer@pytest.mark.parametrizefor testing similar cases
Tests must usetest-provider,test-small-001, etc. instead of real vendor names
Use Hypothesis for Python property-based testing with@given+@settingsdecorators; use profiles:ci(50 examples, default) anddev(1000 examples) controlled viaHYPOTHESIS_PROFILEenv var
Run Hypothesis dev profile with:HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties
For timing-sensitive tests, mocktime.monotonic()andasyncio.sleep()to make them deterministic instead of widening timing margins. For tasks that must block indefinitely until cancelled, useasyncio.Event().wait()instead ofasyncio.sleep(large_number)
NEVER skip, dismiss, or ignore flaky tests -- always fix them fully and fundamentally
Files:
tests/unit/budget/test_trends.py
🧠 Learnings (3)
📓 Common learnings
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.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: 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).
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)
📚 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/budget/test_trends.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/budget/test_trends.py
🧬 Code graph analysis (1)
tests/unit/budget/test_trends.py (2)
src/synthorg/budget/trends.py (10)
BucketSize(45-49)TrendMetric(36-42)TrendPeriod(28-33)bucket_cost_records(208-247)bucket_success_rate(286-331)bucket_task_completions(250-283)generate_bucket_starts(178-202)period_to_timedelta(148-157)project_daily_spend(396-449)resolve_bucket_size(134-145)src/synthorg/hr/performance/models.py (1)
TaskMetricRecord(25-67)
🔇 Additional comments (2)
tests/unit/budget/test_trends.py (2)
167-183: Good boundary coverage for[start, end)filtering.These cases validate inclusive start/exclusive end semantics for both cost and task bucketing, which protects against off-by-one regressions.
Also applies to: 253-263
544-579: Enum serialization contract tests look solid.These assertions provide stable API-wire guarantees for period/metric/bucket string values.
Add GET /api/v1/analytics/trends with period (7d/30d/90d) and metric (tasks_completed, spend, active_agents, success_rate) query params, returning bucketed time-series data points. Add GET /api/v1/analytics/forecast with horizon_days param, returning daily spend projections, days until budget exhaustion, and confidence. Extend GET /api/v1/analytics/overview with budget_remaining_usd, budget_used_percent, cost_7d_trend sparkline, active_agents_count, and idle_agents_count. Pure trend bucketing and forecast functions in budget/trends.py follow the existing category_analytics.py pattern. CostTracker gains a public budget_config property. PerformanceTracker wired into API test fixtures. Closes #771 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extract _resolve_budget_context, _fetch_trend_data_points helpers to bring all controller methods under 50-line limit - Extract _compute_daily_spend, _build_projections from project_daily_spend - Make generate_bucket_starts public in trends.py, remove duplicate from controller - Add logging on ServiceUnavailableError catch and budget fallback paths - Use math.ceil for days_until_exhausted (budget survives N full days) - Add now param to project_daily_spend for deterministic testing - Use StrEnum types in TrendsResponse instead of raw str - Add max(0) guard on idle agent count for TOCTOU safety - Add exc_info=True to agent registry warning log - Hoist _PERIOD_TIMEDELTA to module constant - Document budget_used_percent >100 means overrun - Update operations.md: event module count 52->53, expand analytics API row Pre-reviewed by 7 agents, 17 findings addressed Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…l reviewers - Add allow_inf_nan=False to OverviewMetrics, TrendsResponse, ForecastResponse - Add ge=0 constraint to days_until_exhausted on both BudgetForecast and ForecastResponse - Fix hardcoded rounding precision (2) to use BUDGET_ROUNDING_PRECISION constant - Fix dead code guard: reorder budget_config None check for type narrowing - Fix ExceptionGroup handler to raise controlled ServiceUnavailableError - Extract _assemble_overview helper to bring get_overview under 50-line limit - Extract _bucket_task_metric_data to bring _fetch_trend_data_points under 50 lines - Fix semantically inverted docstring for days_until_exhausted - Fix misleading "additional spend" wording in ForecastResponse - Fix get_forecast docstring about lookback period averaging - Fix generate_bucket_starts docstring to document start-flooring behavior - Fix _compute_daily_spend docstring to document non-empty precondition - Remove unused logger declaration from trends.py (pure functions only) - Add comment explaining DAY bucket divergence in overview sparkline - Expand active agents flat-line comment to explain why - Update CLAUDE.md budget/ package description with trend analysis - Refactor resolver and enum tests to use @pytest.mark.parametrize - Add direct tests for generate_bucket_starts (5 tests) - Add test for budget_remaining_usd=0 edge case (days_until_exhausted=0) - Add test for ServiceUnavailableError graceful degradation in trends - Add test_analytics_events_exist for event constant values Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… exclusive delta) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
790e8db to
481b54c
Compare
There was a problem hiding this comment.
Actionable comments posted: 7
♻️ Duplicate comments (2)
src/synthorg/budget/trends.py (2)
178-202:⚠️ Potential issue | 🟠 MajorStill unresolved:
generate_bucket_starts()widens[start, end).Flooring
starton Line 197 means an unaligned empty interval can still produce a bucket, and a 7-day hourly query can grow to 169 points with a first timestamp before the requested range. Make alignment explicit instead of silently widening the window here.🛠️ One safe fix
def generate_bucket_starts( start: datetime, end: datetime, bucket_size: BucketSize, ) -> list[datetime]: @@ - step = timedelta(hours=1) if bucket_size == BucketSize.HOUR else timedelta(days=1) - current = _bucket_key(start, bucket_size) + step = timedelta(hours=1) if bucket_size == BucketSize.HOUR else timedelta(days=1) + if start != _bucket_key(start, bucket_size) or end != _bucket_key( + end, + bucket_size, + ): + msg = "start and end must align to the selected bucket size" + raise ValueError(msg) + current = start🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/budget/trends.py` around lines 178 - 202, generate_bucket_starts currently floors start via _bucket_key which can widen the requested [start, end) by emitting a bucket that begins before start; change this so alignment is explicit and never yields a bucket that starts before the original start. Compute aligned = _bucket_key(start, bucket_size) and then set current = aligned if aligned >= start else aligned + step (where step is the hourly/daily timedelta already used); proceed with the same while loop using current so the first yielded bucket is always >= the original start. Reference: generate_bucket_starts, _bucket_key, current, start, step.
351-365:⚠️ Potential issue | 🟠 MajorStill unresolved: burn-rate span is off by one day.
(timestamps[-1].date() - timestamps[0].date()).daystreats March 1 → March 2 as a one-day sample. That doublesavg_daily_spend_usdand skewsprojected_total_usd,days_until_exhausted, andconfidence.🛠️ Minimal fix
lookback_days = max( - (timestamps[-1].date() - timestamps[0].date()).days, + (timestamps[-1].date() - timestamps[0].date()).days + 1, 1, )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/budget/trends.py` around lines 351 - 365, The lookback span is off-by-one: compute lookback_days from timestamps inclusive (so that March 1→March 2 yields 2 days) by adding 1 to the date-difference calculation; update the calculation that sets lookback_days (which currently uses (timestamps[-1].date() - timestamps[0].date()).days) to use +1 and keep the max(..., 1) guard, which will correct avg_daily, projected totals and confidence that rely on lookback_days (see variables lookback_days, timestamps, avg_daily, confidence, days_with_data).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/design/operations.md`:
- Line 1073: Update the `/api/v1/analytics` row to explicitly document the query
contract for the three sub-endpoints (`GET /overview`, `GET /trends`, `GET
/forecast`): add the allowed period enum `period=7d|30d|90d`, specify that
hourly bucketing is supported only when `period=7d` for `/trends`, and add the
forecast horizon enum `horizon_days=7|14|30` for `/forecast`; include brief
notes about default values and validation behavior so the design spec contains
the same constraints reflected in the controllers/tests.
In `@src/synthorg/api/controllers/analytics.py`:
- Around line 168-200: The fallback in _resolve_budget_context uses a
period_cost value (fallback_total_cost) that may not be scoped to the current
billing period (it can come from get_overview() or get_forecast()), which
produces incorrect remaining/percent/days calculations across resets; change
_resolve_budget_context to treat an out-of-period fallback as “unavailable”
(e.g., return _BudgetContext with used_percent/remaining set to a
sentinel/unavailable state) or require callers to pass a period-scoped fallback
(instead of generic fallback_total_cost) and validate it against
billing_period_start(budget_config.reset_day); update call sites that pass
get_overview() / get_forecast() to either provide a period-scoped total or
handle the unavailable response so budget_remaining_usd, budget_used_percent,
and days_until_exhausted are not computed from lifetime totals.
- Around line 509-516: The horizon_days parameter currently accepts any int
1..90; restrict it to the supported set {7,14,30} by changing the parameter
type/validation for horizon_days in the analytics controller: replace the
Annotated[int, Parameter(... ge=1, le=90 ...)] with a constrained type (either
typing.Literal[7,14,30] or a small Enum like ForecastHorizon with members
SEVEN=7, FOURTEEN=14, THIRTY=30) and keep the Parameter(...) description but
remove the ge/le bounds; update any usages of horizon_days in functions/methods
that expect an int to accept the Literal/Enum value (or cast to int if
necessary) so external input is validated to only 7, 14, or 30.
- Around line 536-538: The query fetching cost records uses only
start=lookback_start, so future-dated records may be included; update the call
to app_state.cost_tracker.get_records(...) to pass an explicit end parameter set
to the current time (e.g., a timezone-aware "now") so the lookup is bounded:
include end=now alongside start=lookback_start when calling get_records to
ensure averages and confidence calculations exclude future records.
- Around line 322-328: ACTIVE_AGENTS currently calls
_resolve_agent_counts(app_state, 0) which forces a zero fallback when the
registry is missing; change the second argument to the configured agent count
instead of 0 (e.g. _resolve_agent_counts(app_state,
app_state.config.agent_count)) so active_count reflects configured agents; keep
the rest of the logic that maps generate_bucket_starts(...) to
TrendDataPoint(timestamp=bs, value=float(active_count)).
In `@src/synthorg/budget/trends.py`:
- Around line 1-18: Add the standard module logger by importing get_logger from
synthorg.observability and defining logger = get_logger(__name__) near the top
of the module (e.g., after existing imports). Do not use import logging or
print(); ensure the symbol logger is available for subsequent functions/classes
in trends.py so future validation/error paths call
logger.debug/info/warning/error as needed.
In `@tests/unit/api/controllers/test_analytics.py`:
- Around line 242-251: Update test_trends_active_agents to seed at least one
active/configured agent before calling the trends endpoint, then assert the
actual numeric values in data["data_points"] rather than only the shape: create
an agent (via existing fixture or by POSTing through test_client), call GET
"/api/v1/analytics/trends" with params {"metric": "active_agents"}, parse
data["data_points"] and assert each point has a "value" field and that the
values equal the expected active-agent count (e.g., >= 1 or the exact seeded
count) across buckets (and that at least one value is > 0) to verify correct
behavior of test_trends_active_agents.
---
Duplicate comments:
In `@src/synthorg/budget/trends.py`:
- Around line 178-202: generate_bucket_starts currently floors start via
_bucket_key which can widen the requested [start, end) by emitting a bucket that
begins before start; change this so alignment is explicit and never yields a
bucket that starts before the original start. Compute aligned =
_bucket_key(start, bucket_size) and then set current = aligned if aligned >=
start else aligned + step (where step is the hourly/daily timedelta already
used); proceed with the same while loop using current so the first yielded
bucket is always >= the original start. Reference: generate_bucket_starts,
_bucket_key, current, start, step.
- Around line 351-365: The lookback span is off-by-one: compute lookback_days
from timestamps inclusive (so that March 1→March 2 yields 2 days) by adding 1 to
the date-difference calculation; update the calculation that sets lookback_days
(which currently uses (timestamps[-1].date() - timestamps[0].date()).days) to
use +1 and keep the max(..., 1) guard, which will correct avg_daily, projected
totals and confidence that rely on lookback_days (see variables lookback_days,
timestamps, avg_daily, confidence, days_with_data).
🪄 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: 5cfbe794-ce7b-4fbe-b98d-f82423a84c07
📒 Files selected for processing (10)
CLAUDE.mddocs/design/operations.mdsrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/tracker.pysrc/synthorg/budget/trends.pysrc/synthorg/observability/events/analytics.pytests/unit/api/conftest.pytests/unit/api/controllers/test_analytics.pytests/unit/budget/test_trends.pytests/unit/observability/test_events.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). (3)
- GitHub Check: Build Backend
- GitHub Check: Test (Python 3.14)
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotations-- Python 3.14 has PEP 649 native lazy annotations
Useexcept A, B:syntax (no parentheses) for multiple exception handling -- PEP 758 except syntax enforced by ruff on Python 3.14
All public functions and classes must have type hints; mypy strict mode is enforced
All public classes and functions must have Google-style docstrings; enforced by ruff D rules
Use immutability patterns: create new objects instead of mutating existing ones. For non-Pydantic internal collections, usecopy.deepcopy()at construction andMappingProxyTypewrapping for read-only enforcement. Fordict/listfields in frozen Pydantic models, usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence)
Use frozen Pydantic models (v2) for config/identity; use separate mutable-via-copy models withmodel_copy(update=...)for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model
Use Pydantic v2BaseModel,model_validator,computed_field, andConfigDict. Use@computed_fieldfor derived values instead of storing redundant fields. UseNotBlankStrfromcore.typesfor all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls). Prefer structured concurrency over barecreate_task
Functions must be < 50 lines; files must be < 800 lines
Handle errors explicitly; never silently swallow exceptions
Validate at system boundaries (user input, external APIs, config files)
Line length: 88 characters (enforced by ruff)
Files:
src/synthorg/budget/tracker.pytests/unit/observability/test_events.pytests/unit/api/conftest.pysrc/synthorg/observability/events/analytics.pytests/unit/budget/test_trends.pytests/unit/api/controllers/test_analytics.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/trends.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic must import logger:from synthorg.observability import get_loggerthenlogger = get_logger(__name__); never useimport loggingorprint()(exceptions:observability/setup.pyandobservability/sinks.pymay use stdlibloggingandprint(..., file=sys.stderr)for bootstrap code)
Always use structured logging with logger name aslogger(not_loggerorlog). Use event name constants fromsynthorg.observability.events.<domain>modules (e.g.,API_REQUEST_STARTEDfromevents.api,TOOL_INVOKE_STARTfromevents.tool). Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT. Always log aslogger.info(EVENT, key=value)-- neverlogger.info("msg %s", val)
Log WARNING or ERROR on all error paths before raising exceptions. Log INFO on all state transitions. Log DEBUG for object creation, internal flow, and entry/exit of key functions
Never implement retry logic in provider driver subclasses or calling code -- it is handled automatically byBaseCompletionProviderbase class.RetryConfigandRateLimiterConfigare set per-provider inProviderConfig. Retryable errors:RateLimitError,ProviderTimeoutError,ProviderConnectionError,ProviderInternalError. Non-retryable errors raise immediately.RetryExhaustedErrorsignals all retries failed -- catch at engine layer to trigger fallback chains. Rate limiter respectsRateLimitError.retry_after
Files:
src/synthorg/budget/tracker.pysrc/synthorg/observability/events/analytics.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/trends.py
**.md
📄 CodeRabbit inference engine (CLAUDE.md)
Commit messages must follow format:
<type>: <description>with types: feat, fix, refactor, docs, test, chore, perf, ci. Enforced by commitizen (commit-msg hook). Signed commits required onmainvia branch protection -- all commits must be GPG/SSH signed
Files:
CLAUDE.md
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use pytest markers:@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slowon all tests
Async tests useasyncio_mode = "auto"(configured in pyproject.toml) -- do not add manual@pytest.mark.asynciomarkers. Tests have a global 30-second timeout (configured in pyproject.toml) -- do not add per-file@pytest.mark.timeout(30)markers; only add non-default overrides like@pytest.mark.timeout(60)when needed
Always run pytest with-n autofor parallelism (pytest-xdist); never run tests sequentially. Always include coverage target--cov=synthorg --cov-fail-under=80when running full test suite
Prefer@pytest.mark.parametrizefor testing similar cases; use vendor-agnostic provider names in all project-owned code (usetest-provider,test-small-001,test-medium-001,test-large-001, orsmall/medium/largealiases). Vendor names may only appear in: (1) Operations design page, (2).claude/files, (3) third-party import paths. Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project code, docstrings, comments, or tests
Use Hypothesis for property-based testing with@givenand@settings. Control Hypothesis profiles viaHYPOTHESIS_PROFILEenv var:ci(50 examples, default) ordev(1000 examples). Run dev profile:HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties
Never skip, dismiss, or ignore flaky tests -- always fix them fundamentally. For timing-sensitive tests, mocktime.monotonic()andasyncio.sleep()to make them deterministic instead of widening timing margins. For tasks that must block indefinitely until cancelled, useasyncio.Event().wait()instead ofasyncio.sleep(large_number)
Files:
tests/unit/observability/test_events.pytests/unit/api/conftest.pytests/unit/budget/test_trends.pytests/unit/api/controllers/test_analytics.py
docs/design/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
ALWAYS read the relevant
docs/design/page before implementing any feature or planning any issue. DESIGN_SPEC.md is a pointer file to 7 design pages. The design spec is the starting point for architecture, data models, and behavior. If implementation deviates from the spec, alert the user and explain why -- user decides whether to proceed or update the spec. When a spec topic is referenced, read the relevant page before coding. Update the relevant design page when approved deviations occur
Files:
docs/design/operations.md
docs/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Documentation is written in Markdown, built with Zensical (config:
mkdocs.yml). Auto-generated reference docs use mkdocstrings + Griffe (AST-based). REST API reference indocs/rest-api.mdand auto-generateddocs/_generated/api-reference.html(generated viascripts/export_openapi.py)
Files:
docs/design/operations.md
🧠 Learnings (35)
📓 Common learnings
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.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: 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).
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)
📚 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/budget/tracker.pyCLAUDE.mdtests/unit/budget/test_trends.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/trends.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:
src/synthorg/budget/tracker.pyCLAUDE.mdtests/unit/budget/test_trends.pytests/unit/api/controllers/test_analytics.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/trends.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:
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/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:
CLAUDE.md
📚 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:
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/communication/**/*.py : Communication package (communication/): message bus, dispatcher, messenger, channels, delegation, loop prevention, conflict resolution; meeting/ subpackage for meeting protocol (round-robin, position papers, structured phases), scheduler (frequency, participant resolver), orchestrator
Applied to files:
CLAUDE.md
📚 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:
CLAUDE.md
📚 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/api/conftest.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: 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:
CLAUDE.mdsrc/synthorg/api/controllers/analytics.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/conftest.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:
tests/unit/observability/test_events.pydocs/design/operations.mdsrc/synthorg/observability/events/analytics.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:
tests/unit/observability/test_events.pydocs/design/operations.mdsrc/synthorg/observability/events/analytics.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls
Applied to files:
tests/unit/observability/test_events.pydocs/design/operations.mdsrc/synthorg/observability/events/analytics.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from synthorg.observability.events domain-specific modules (e.g., PROVIDER_CALL_START from events.provider). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Applied to files:
tests/unit/observability/test_events.pydocs/design/operations.mdsrc/synthorg/observability/events/analytics.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/observability/test_events.pydocs/design/operations.mdsrc/synthorg/observability/events/analytics.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`); import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`
Applied to files:
tests/unit/observability/test_events.pydocs/design/operations.mdsrc/synthorg/observability/events/analytics.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 : Use event name constants from domain-specific modules under ai_company.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.) — import directly
Applied to files:
tests/unit/observability/test_events.pydocs/design/operations.mdsrc/synthorg/observability/events/analytics.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use event name constants from domain-specific modules under `ai_company.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`). Import directly: `from ai_company.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
tests/unit/observability/test_events.pydocs/design/operations.mdsrc/synthorg/observability/events/analytics.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:
tests/unit/observability/test_events.pydocs/design/operations.mdsrc/synthorg/observability/events/analytics.py
📚 Learning: 2026-03-24T05:06:57.759Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T05:06:57.759Z
Learning: Applies to src/synthorg/**/*.py : Always use structured logging with logger name as `logger` (not `_logger` or `log`). Use event name constants from `synthorg.observability.events.<domain>` modules (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`. Always log as `logger.info(EVENT, key=value)` -- never `logger.info("msg %s", val)`
Applied to files:
tests/unit/observability/test_events.pydocs/design/operations.mdsrc/synthorg/observability/events/analytics.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 docs/design/*.md : Design spec pages: 7 pages in `docs/design/` — index, agents, organization, communication, engine, memory, operations
Applied to files:
docs/design/operations.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/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:
tests/unit/api/conftest.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/budget/test_trends.py
📚 Learning: 2026-03-24T05:06:57.759Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T05:06:57.759Z
Learning: Applies to tests/**/*.py : Never skip, dismiss, or ignore flaky tests -- always fix them fundamentally. For timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic instead of widening timing margins. For tasks that must block indefinitely until cancelled, use `asyncio.Event().wait()` instead of `asyncio.sleep(large_number)`
Applied to files:
tests/unit/budget/test_trends.py
📚 Learning: 2026-03-24T05:06:57.759Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T05:06:57.759Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (no parentheses) for multiple exception handling -- PEP 758 except syntax enforced by ruff on Python 3.14
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.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/analytics.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/analytics.py
📚 Learning: 2026-03-24T05:06:57.759Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T05:06:57.759Z
Learning: Applies to **/*.py : Handle errors explicitly; never silently swallow exceptions
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.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/analytics.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/analytics.py
📚 Learning: 2026-03-19T21:11:37.538Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T21:11:37.538Z
Learning: Applies to **/*.py : Errors: handle explicitly, never silently swallow.
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.py
🧬 Code graph analysis (4)
tests/unit/api/conftest.py (3)
tests/unit/hr/promotion/conftest.py (1)
tracker(104-106)src/synthorg/hr/performance/tracker.py (1)
PerformanceTracker(51-506)src/synthorg/api/state.py (1)
performance_tracker(294-299)
tests/unit/budget/test_trends.py (4)
src/synthorg/budget/trends.py (10)
BucketSize(45-49)TrendMetric(36-42)TrendPeriod(28-33)bucket_cost_records(208-247)bucket_success_rate(286-331)bucket_task_completions(250-283)generate_bucket_starts(178-202)period_to_timedelta(148-157)project_daily_spend(396-449)resolve_bucket_size(134-145)src/synthorg/core/enums.py (2)
Complexity(275-281)TaskType(255-263)src/synthorg/hr/performance/models.py (1)
TaskMetricRecord(25-67)src/synthorg/api/dto.py (1)
is_success(485-487)
src/synthorg/api/controllers/analytics.py (5)
src/synthorg/api/errors.py (1)
ServiceUnavailableError(222-231)src/synthorg/budget/trends.py (12)
BucketSize(45-49)ForecastPoint(69-83)TrendDataPoint(55-66)TrendMetric(36-42)TrendPeriod(28-33)bucket_cost_records(208-247)bucket_success_rate(286-331)bucket_task_completions(250-283)generate_bucket_starts(178-202)period_to_timedelta(148-157)project_daily_spend(396-449)resolve_bucket_size(134-145)src/synthorg/core/enums.py (1)
TaskStatus(226-252)src/synthorg/budget/tracker.py (2)
get_total_cost(123-146)get_records(194-234)src/synthorg/hr/performance/tracker.py (1)
get_task_metrics(374-400)
src/synthorg/budget/trends.py (1)
src/synthorg/hr/performance/models.py (1)
TaskMetricRecord(25-67)
| async def _resolve_budget_context( | ||
| app_state: AppState, | ||
| fallback_total_cost: float = 0.0, | ||
| ) -> _BudgetContext: | ||
| """Compute budget remaining and usage percentage. | ||
|
|
||
| Args: | ||
| app_state: Application state. | ||
| fallback_total_cost: Total cost to use if period query fails. | ||
|
|
||
| Returns: | ||
| Budget context with monthly, remaining, and used_percent. | ||
| """ | ||
| budget_config = app_state.cost_tracker.budget_config | ||
| monthly = budget_config.total_monthly if budget_config else 0.0 | ||
| if budget_config is None or monthly <= 0: | ||
| return _BudgetContext(monthly=0.0, remaining=0.0, used_percent=0.0) | ||
|
|
||
| period_start = billing_period_start(budget_config.reset_day) | ||
| try: | ||
| period_cost = await app_state.cost_tracker.get_total_cost( | ||
| start=period_start, | ||
| ) | ||
| except MemoryError, RecursionError: | ||
| raise | ||
| except Exception: | ||
| logger.warning( | ||
| API_REQUEST_ERROR, | ||
| endpoint="analytics.budget_context", | ||
| error="period_cost_query_failed", | ||
| exc_info=True, | ||
| ) | ||
| period_cost = fallback_total_cost |
There was a problem hiding this comment.
The degraded budget fallback can cross billing-period boundaries.
period_cost here is supposed to mean spend since billing_period_start(reset_day), but this fallback is populated from get_overview()'s lifetime total_cost or get_forecast()'s default 0.0. On that path, budget_remaining_usd, budget_used_percent, and days_until_exhausted can all be materially wrong after a reset. Please return an explicit unavailable state or require a period-scoped fallback instead.
Based on learnings, budget tracking includes billing periods.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/api/controllers/analytics.py` around lines 168 - 200, The
fallback in _resolve_budget_context uses a period_cost value
(fallback_total_cost) that may not be scoped to the current billing period (it
can come from get_overview() or get_forecast()), which produces incorrect
remaining/percent/days calculations across resets; change
_resolve_budget_context to treat an out-of-period fallback as “unavailable”
(e.g., return _BudgetContext with used_percent/remaining set to a
sentinel/unavailable state) or require callers to pass a period-scoped fallback
(instead of generic fallback_total_cost) and validate it against
billing_period_start(budget_config.reset_day); update call sites that pass
get_overview() / get_forecast() to either provide a period-scoped total or
handle the unavailable response so budget_remaining_usd, budget_used_percent,
and days_until_exhausted are not computed from lifetime totals.
| # ACTIVE_AGENTS: flat line -- no historical agent counts are | ||
| # tracked, so report the current snapshot across all buckets | ||
| active_count, _ = await _resolve_agent_counts(app_state, 0) | ||
| return tuple( | ||
| TrendDataPoint(timestamp=bs, value=float(active_count)) | ||
| for bs in generate_bucket_starts(start, now, bucket_size) | ||
| ) |
There was a problem hiding this comment.
ACTIVE_AGENTS drops to zero when the registry is missing or degraded.
_resolve_agent_counts() is documented to fall back to the configured agent count, but this path passes 0. In deployments without AgentRegistryService—or when it throws—every bucket here becomes 0 even if agents are configured.
Suggested fix
- active_count, _ = await _resolve_agent_counts(app_state, 0)
+ config_agents = await app_state.config_resolver.get_agents()
+ active_count, _ = await _resolve_agent_counts(
+ app_state,
+ len(config_agents),
+ )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/api/controllers/analytics.py` around lines 322 - 328,
ACTIVE_AGENTS currently calls _resolve_agent_counts(app_state, 0) which forces a
zero fallback when the registry is missing; change the second argument to the
configured agent count instead of 0 (e.g. _resolve_agent_counts(app_state,
app_state.config.agent_count)) so active_count reflects configured agents; keep
the rest of the logic that maps generate_bucket_starts(...) to
TrendDataPoint(timestamp=bs, value=float(active_count)).
| horizon_days: Annotated[ | ||
| int, | ||
| Parameter( | ||
| ge=1, | ||
| le=90, | ||
| description="Projection horizon in days", | ||
| ), | ||
| ] = 14, |
There was a problem hiding this comment.
Constrain horizon_days to the supported set.
The linked objective narrows forecasts to 7, 14, or 30, but this accepts any integer from 1 to 90. That widens the public API and leaves boundary validation weaker than the contract.
As per coding guidelines, validate at system boundaries (user input, external APIs, config files).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/api/controllers/analytics.py` around lines 509 - 516, The
horizon_days parameter currently accepts any int 1..90; restrict it to the
supported set {7,14,30} by changing the parameter type/validation for
horizon_days in the analytics controller: replace the Annotated[int,
Parameter(... ge=1, le=90 ...)] with a constrained type (either
typing.Literal[7,14,30] or a small Enum like ForecastHorizon with members
SEVEN=7, FOURTEEN=14, THIRTY=30) and keep the Parameter(...) description but
remove the ge/le bounds; update any usages of horizon_days in functions/methods
that expect an int to accept the Literal/Enum value (or cast to int if
necessary) so external input is validated to only 7, 14, or 30.
| """Time-series trend bucketing and budget forecast projections. | ||
|
|
||
| Provides pure functions to bucket cost records and task metrics into | ||
| time-series data points, and to project future budget spend from | ||
| historical data. Follows the same pure-function pattern as | ||
| :mod:`~synthorg.budget.category_analytics`. | ||
| """ | ||
|
|
||
| import math | ||
| from collections import defaultdict | ||
| from datetime import UTC, date, datetime, timedelta | ||
| from enum import StrEnum | ||
| from typing import TYPE_CHECKING | ||
|
|
||
| from pydantic import AwareDatetime, BaseModel, ConfigDict, Field | ||
|
|
||
| from synthorg.constants import BUDGET_ROUNDING_PRECISION | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Add the standard module logger.
src/synthorg/budget/trends.py is business logic under src/synthorg/, so it should follow the repo’s required get_logger(__name__) convention before more validation/error paths land here.
As per coding guidelines, "Every module with business logic must import logger: from synthorg.observability import get_logger then logger = get_logger(__name__); never use import logging or print()."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/budget/trends.py` around lines 1 - 18, Add the standard module
logger by importing get_logger from synthorg.observability and defining logger =
get_logger(__name__) near the top of the module (e.g., after existing imports).
Do not use import logging or print(); ensure the symbol logger is available for
subsequent functions/classes in trends.py so future validation/error paths call
logger.debug/info/warning/error as needed.
| def test_trends_active_agents(self, test_client: TestClient[Any]) -> None: | ||
| resp = test_client.get( | ||
| "/api/v1/analytics/trends", | ||
| params={"metric": "active_agents"}, | ||
| headers=_HEADERS, | ||
| ) | ||
| assert resp.status_code == 200 | ||
| data = resp.json()["data"] | ||
| assert data["metric"] == "active_agents" | ||
| assert isinstance(data["data_points"], list) |
There was a problem hiding this comment.
Assert the active-agent values, not just the response shape.
This still passes if /analytics/trends?metric=active_agents returns the wrong flat-line count for every bucket. Seed at least one configured or active agent and assert the repeated value so this endpoint behavior is actually covered.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/unit/api/controllers/test_analytics.py` around lines 242 - 251, Update
test_trends_active_agents to seed at least one active/configured agent before
calling the trends endpoint, then assert the actual numeric values in
data["data_points"] rather than only the shape: create an agent (via existing
fixture or by POSTing through test_client), call GET "/api/v1/analytics/trends"
with params {"metric": "active_agents"}, parse data["data_points"] and assert
each point has a "value" field and that the values equal the expected
active-agent count (e.g., >= 1 or the exact seeded count) across buckets (and
that at least one value is > 0) to verify correct behavior of
test_trends_active_agents.
- Expand operations.md analytics row with query params, defaults, validation - Pass end=now to get_records in forecast to exclude future-dated records Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (3)
src/synthorg/api/controllers/analytics.py (3)
509-516:⚠️ Potential issue | 🟠 MajorConstrain
horizon_daysto the supported set.The linked objective (
#771) narrows forecasts to7,14, or30days, but this accepts any integer from1to90. Consider usingtyping.Literal[7, 14, 30]or a dedicated enum to match the documented contract.As per coding guidelines, validate at system boundaries (user input, external APIs, config files).
,
🔧 Suggested fix using Literal
+from typing import Literal + +ForecastHorizon = Literal[7, 14, 30] + `@get`("/forecast", guards=[require_read_access]) async def get_forecast( self, state: State, horizon_days: Annotated[ - int, + ForecastHorizon, Parameter( - ge=1, - le=90, description="Projection horizon in days (7, 14, or 30)", ), ] = 14, ) -> ApiResponse[ForecastResponse]:🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/controllers/analytics.py` around lines 509 - 516, The parameter horizon_days currently accepts any int 1–90; restrict it to the supported set (7, 14, 30) at the API boundary by changing the type of horizon_days in analytics.py to a constrained type (e.g., typing.Literal[7, 14, 30] or a small Enum) instead of Annotated[int, Parameter(...)] and update any Parameter/validation metadata accordingly so requests outside {7,14,30} are rejected; ensure the handler/signature that references horizon_days (the function parameter named horizon_days) and any downstream assumptions are updated to use the new Literal/Enum type.
168-208:⚠️ Potential issue | 🟠 MajorThe degraded budget fallback can cross billing-period boundaries.
When
get_total_cost(start=period_start)fails, the function falls back tofallback_total_cost, which at the/overviewcall site istotal_cost(lifetime total fromget_total_cost()with no bounds), and at/forecastis0.0. Neither is scoped to the current billing period.After a billing reset,
budget_remaining_usdandbudget_used_percentcomputed from this fallback will be materially incorrect.Consider either returning an explicit "unavailable" state when the period query fails, or requiring callers to pass a period-scoped fallback.
,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/controllers/analytics.py` around lines 168 - 208, _resolve_budget_context currently uses fallback_total_cost when get_total_cost(start=period_start) fails, but that fallback may be lifetime-scoped (e.g., total_cost) and can cross billing periods; update _resolve_budget_context to return an explicit "unavailable" budget context when the period query fails (e.g., _BudgetContext with monthly set and remaining/used_percent as None or a sentinel) OR change the function signature to require a period-scoped fallback (rename fallback_total_cost to period_fallback_total_cost) and use that value only; modify callers (e.g., the /overview and /forecast code paths that call _resolve_budget_context) to either handle the unavailable sentinel or pass a proper period-scoped fallback so budget_remaining_usd and budget_used_percent are not computed from lifetime totals. Ensure references to billing_period_start, app_state.cost_tracker.get_total_cost, and _BudgetContext are updated accordingly.
322-328:⚠️ Potential issue | 🟠 Major
ACTIVE_AGENTSdrops to zero when the registry is missing or degraded.
_resolve_agent_counts(app_state, 0)passes0as the fallback. WhenAgentRegistryServiceis unavailable or throws, every bucket becomes0even if agents are configured.,
🔧 Suggested fix
# ACTIVE_AGENTS: flat line -- no historical agent counts are # tracked, so report the current snapshot across all buckets - active_count, _ = await _resolve_agent_counts(app_state, 0) + agents = await app_state.config_resolver.get_agents() + active_count, _ = await _resolve_agent_counts(app_state, len(agents)) return tuple( TrendDataPoint(timestamp=bs, value=float(active_count)) for bs in generate_bucket_starts(start, now, bucket_size) )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/controllers/analytics.py` around lines 322 - 328, The ACTIVE_AGENTS code is passing a literal 0 as the fallback to _resolve_agent_counts which causes every bucket to be reported as 0 when AgentRegistryService is missing; replace the hardcoded 0 with the configured/current agent count from app_state (e.g., app_state.config.agent_count or app_state.get_configured_agent_count()) and pass that value into _resolve_agent_counts(app_state, configured_count) so TrendDataPoint generation via generate_bucket_starts/TrendDataPoint reflects the configured snapshot instead of dropping to zero.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/design/operations.md`:
- Line 1073: Docs and implementation disagree on allowed forecast horizons: docs
show GET /forecast?horizon_days=1..90 while issue `#771` expects only 7, 14, or
30; fix by making the contract consistent—either restrict the implementation or
update the spec/docs. Update the GET /forecast handler and its validation
(horizon_days) to only accept 7, 14, or 30 and return 400 for others, and/or
change the `docs/design/operations.md` entry for `/api/v1/analytics` -> `GET
/forecast?horizon_days` to explicitly document the allowed values (7|14|30) and
default (14), ensuring the parameter name `horizon_days`, default, and error
behavior match the implementation and reference issue `#771`.
In `@src/synthorg/api/controllers/analytics.py`:
- Around line 297-299: The SPEND branch calls app_state.cost_tracker.get_records
without an end bound, which can include future-dated records; change the call in
the TrendMetric.SPEND handling to pass end=now (i.e.,
app_state.cost_tracker.get_records(start=start, end=now)) so bucket_cost_records
receives only records within [start, now] consistent with the forecast path that
uses end=now; update the invocation referenced in the SPEND conditional that
returns bucket_cost_records(records, start, now, bucket_size).
---
Duplicate comments:
In `@src/synthorg/api/controllers/analytics.py`:
- Around line 509-516: The parameter horizon_days currently accepts any int
1–90; restrict it to the supported set (7, 14, 30) at the API boundary by
changing the type of horizon_days in analytics.py to a constrained type (e.g.,
typing.Literal[7, 14, 30] or a small Enum) instead of Annotated[int,
Parameter(...)] and update any Parameter/validation metadata accordingly so
requests outside {7,14,30} are rejected; ensure the handler/signature that
references horizon_days (the function parameter named horizon_days) and any
downstream assumptions are updated to use the new Literal/Enum type.
- Around line 168-208: _resolve_budget_context currently uses
fallback_total_cost when get_total_cost(start=period_start) fails, but that
fallback may be lifetime-scoped (e.g., total_cost) and can cross billing
periods; update _resolve_budget_context to return an explicit "unavailable"
budget context when the period query fails (e.g., _BudgetContext with monthly
set and remaining/used_percent as None or a sentinel) OR change the function
signature to require a period-scoped fallback (rename fallback_total_cost to
period_fallback_total_cost) and use that value only; modify callers (e.g., the
/overview and /forecast code paths that call _resolve_budget_context) to either
handle the unavailable sentinel or pass a proper period-scoped fallback so
budget_remaining_usd and budget_used_percent are not computed from lifetime
totals. Ensure references to billing_period_start,
app_state.cost_tracker.get_total_cost, and _BudgetContext are updated
accordingly.
- Around line 322-328: The ACTIVE_AGENTS code is passing a literal 0 as the
fallback to _resolve_agent_counts which causes every bucket to be reported as 0
when AgentRegistryService is missing; replace the hardcoded 0 with the
configured/current agent count from app_state (e.g.,
app_state.config.agent_count or app_state.get_configured_agent_count()) and pass
that value into _resolve_agent_counts(app_state, configured_count) so
TrendDataPoint generation via generate_bucket_starts/TrendDataPoint reflects the
configured snapshot instead of dropping to zero.
🪄 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: dc432f03-1975-412f-b8fa-45526cb129b4
📒 Files selected for processing (2)
docs/design/operations.mdsrc/synthorg/api/controllers/analytics.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). (3)
- GitHub Check: Build Backend
- GitHub Check: Test (Python 3.14)
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
docs/design/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
ALWAYS read the relevant
docs/design/page before implementing any feature or planning any issue. DESIGN_SPEC.md is a pointer file to 7 design pages. The design spec is the starting point for architecture, data models, and behavior. If implementation deviates from the spec, alert the user and explain why -- user decides whether to proceed or update the spec. When a spec topic is referenced, read the relevant page before coding. Update the relevant design page when approved deviations occur
Files:
docs/design/operations.md
docs/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Documentation is written in Markdown, built with Zensical (config:
mkdocs.yml). Auto-generated reference docs use mkdocstrings + Griffe (AST-based). REST API reference indocs/rest-api.mdand auto-generateddocs/_generated/api-reference.html(generated viascripts/export_openapi.py)
Files:
docs/design/operations.md
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotations-- Python 3.14 has PEP 649 native lazy annotations
Useexcept A, B:syntax (no parentheses) for multiple exception handling -- PEP 758 except syntax enforced by ruff on Python 3.14
All public functions and classes must have type hints; mypy strict mode is enforced
All public classes and functions must have Google-style docstrings; enforced by ruff D rules
Use immutability patterns: create new objects instead of mutating existing ones. For non-Pydantic internal collections, usecopy.deepcopy()at construction andMappingProxyTypewrapping for read-only enforcement. Fordict/listfields in frozen Pydantic models, usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence)
Use frozen Pydantic models (v2) for config/identity; use separate mutable-via-copy models withmodel_copy(update=...)for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model
Use Pydantic v2BaseModel,model_validator,computed_field, andConfigDict. Use@computed_fieldfor derived values instead of storing redundant fields. UseNotBlankStrfromcore.typesfor all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls). Prefer structured concurrency over barecreate_task
Functions must be < 50 lines; files must be < 800 lines
Handle errors explicitly; never silently swallow exceptions
Validate at system boundaries (user input, external APIs, config files)
Line length: 88 characters (enforced by ruff)
Files:
src/synthorg/api/controllers/analytics.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic must import logger:from synthorg.observability import get_loggerthenlogger = get_logger(__name__); never useimport loggingorprint()(exceptions:observability/setup.pyandobservability/sinks.pymay use stdlibloggingandprint(..., file=sys.stderr)for bootstrap code)
Always use structured logging with logger name aslogger(not_loggerorlog). Use event name constants fromsynthorg.observability.events.<domain>modules (e.g.,API_REQUEST_STARTEDfromevents.api,TOOL_INVOKE_STARTfromevents.tool). Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT. Always log aslogger.info(EVENT, key=value)-- neverlogger.info("msg %s", val)
Log WARNING or ERROR on all error paths before raising exceptions. Log INFO on all state transitions. Log DEBUG for object creation, internal flow, and entry/exit of key functions
Never implement retry logic in provider driver subclasses or calling code -- it is handled automatically byBaseCompletionProviderbase class.RetryConfigandRateLimiterConfigare set per-provider inProviderConfig. Retryable errors:RateLimitError,ProviderTimeoutError,ProviderConnectionError,ProviderInternalError. Non-retryable errors raise immediately.RetryExhaustedErrorsignals all retries failed -- catch at engine layer to trigger fallback chains. Rate limiter respectsRateLimitError.retry_after
Files:
src/synthorg/api/controllers/analytics.py
🧠 Learnings (32)
📓 Common learnings
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.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: 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).
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)
📚 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:
docs/design/operations.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 docs/design/**/*.md : Design specification pages in `docs/design/` must be consulted before implementing features (7 pages: index, agents, organization, communication, engine, memory, operations)
Applied to files:
docs/design/operations.md
📚 Learning: 2026-03-24T05:06:57.759Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T05:06:57.759Z
Learning: Applies to docs/design/**/*.md : ALWAYS read the relevant `docs/design/` page before implementing any feature or planning any issue. DESIGN_SPEC.md is a pointer file to 7 design pages. The design spec is the starting point for architecture, data models, and behavior. If implementation deviates from the spec, alert the user and explain why -- user decides whether to proceed or update the spec. When a spec topic is referenced, read the relevant page before coding. Update the relevant design page when approved deviations occur
Applied to files:
docs/design/operations.md
📚 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: Always read the relevant `docs/design/` page before implementing any feature or planning any issue. DESIGN_SPEC.md is a pointer file linking to the 7 design pages (index, agents, organization, communication, engine, memory, operations).
Applied to files:
docs/design/operations.md
📚 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: Always read the relevant `docs/design/` page before implementing any feature or planning any issue — DESIGN_SPEC.md is a pointer file linking to 7 design pages (Agents, Organization, Communication, Engine, Memory, Operations)
Applied to files:
docs/design/operations.md
📚 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:
docs/design/operations.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:
docs/design/operations.md
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use event name constants from domain-specific modules under `ai_company.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`). Import directly: `from ai_company.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
docs/design/operations.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 src/**/*.py : Use event name constants from domain-specific modules under ai_company.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.) — import directly
Applied to files:
docs/design/operations.md
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
docs/design/operations.md
📚 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:
docs/design/operations.md
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from synthorg.observability.events domain-specific modules (e.g., PROVIDER_CALL_START from events.provider). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Applied to files:
docs/design/operations.md
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`); import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`
Applied to files:
docs/design/operations.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/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls
Applied to files:
docs/design/operations.md
📚 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:
docs/design/operations.md
📚 Learning: 2026-03-24T05:06:57.759Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T05:06:57.759Z
Learning: Applies to src/synthorg/**/*.py : Always use structured logging with logger name as `logger` (not `_logger` or `log`). Use event name constants from `synthorg.observability.events.<domain>` modules (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`. Always log as `logger.info(EVENT, key=value)` -- never `logger.info("msg %s", val)`
Applied to files:
docs/design/operations.md
📚 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:
src/synthorg/api/controllers/analytics.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/controllers/analytics.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: 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/controllers/analytics.py
📚 Learning: 2026-03-24T05:06:57.759Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T05:06:57.759Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (no parentheses) for multiple exception handling -- PEP 758 except syntax enforced by ruff on Python 3.14
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.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/analytics.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/analytics.py
📚 Learning: 2026-03-24T05:06:57.759Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T05:06:57.759Z
Learning: Applies to **/*.py : Handle errors explicitly; never silently swallow exceptions
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.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/analytics.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/analytics.py
📚 Learning: 2026-03-19T21:11:37.538Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T21:11:37.538Z
Learning: Applies to **/*.py : Errors: handle explicitly, never silently swallow.
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.py
📚 Learning: 2026-03-16T20:14:00.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:14:00.937Z
Learning: Applies to **/*.py : Validate: at system boundaries (user input, external APIs, config files).
Applied to files:
src/synthorg/api/controllers/analytics.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 **/*.py : Validate at system boundaries (user input, external APIs, config files)
Applied to files:
src/synthorg/api/controllers/analytics.py
🧬 Code graph analysis (1)
src/synthorg/api/controllers/analytics.py (7)
src/synthorg/api/dto.py (1)
ApiResponse(130-158)src/synthorg/api/errors.py (1)
ServiceUnavailableError(222-231)src/synthorg/api/guards.py (1)
require_read_access(121-145)src/synthorg/api/state.py (2)
AppState(42-511)has_agent_registry(307-309)src/synthorg/budget/trends.py (10)
BucketSize(45-49)TrendDataPoint(55-66)TrendMetric(36-42)TrendPeriod(28-33)bucket_cost_records(208-247)bucket_success_rate(286-331)bucket_task_completions(250-283)generate_bucket_starts(178-202)project_daily_spend(396-449)resolve_bucket_size(134-145)src/synthorg/budget/tracker.py (3)
budget_config(100-106)get_total_cost(123-146)get_records(194-234)src/synthorg/hr/performance/tracker.py (1)
get_task_metrics(374-400)
🔇 Additional comments (9)
src/synthorg/api/controllers/analytics.py (8)
1-50: LGTM!Imports are well-organized. Logger is correctly sourced from
synthorg.observability, event constants are imported from the domain-specificevents.analyticsmodule, andTYPE_CHECKINGis used appropriately for type-only imports.
54-155: LGTM!Response models are well-structured with frozen configs, appropriate field constraints (
ge=0,le=1), comprehensive docstrings, andallow_inf_nan=Falseto prevent serialization issues. The use oftuple[...]for collections aligns with immutability patterns.
211-242: LGTM!The helper correctly abstracts agent count resolution with graceful fallback and proper error logging. The PEP 758 exception syntax is used correctly.
244-276: LGTM!Clean dispatcher that routes task metric data to the appropriate bucketing function based on metric type.
331-385: LGTM!The assembly helper is well-structured, correctly uses the configured agent count as fallback for
_resolve_agent_counts, and properly computes the 7-day sparkline with daily buckets.
396-450: LGTM!Good use of
asyncio.TaskGroupfor concurrent data fetching. Exception handling properly logs the error count and converts toServiceUnavailableErrorwith a clear message.
452-503: LGTM!The trends endpoint correctly uses typed enums for
periodandmetricparameters, leveraging Litestar's built-in validation. Debug logging includes appropriate structured fields.
536-539: Good: Lookback query now bounded atnow.The
end=nowparameter was added, addressing the previous concern about future-dated records skewing forecasts.docs/design/operations.md (1)
1298-1301: LGTM!Event taxonomy count correctly updated to 53 to reflect the new
analyticsevent constants module.
| | `/api/v1/budget` | Spending, limits, projections | | ||
| | `/api/v1/approvals` | Pending human approvals queue | | ||
| | `/api/v1/analytics` | Performance metrics, dashboards | | ||
| | `/api/v1/analytics` | `GET /overview` (metrics summary with budget status, 7d spend sparkline, agent counts), `GET /trends?period=7d\|30d\|90d&metric=spend\|tasks_completed\|active_agents\|success_rate` (time-series bucketed metrics; hourly buckets for 7d, daily for 30d/90d; defaults: `period=7d`, `metric=spend`), `GET /forecast?horizon_days=1..90` (budget spend projection with daily projections and exhaustion estimate; default 14; 400 on out-of-range) | |
There was a problem hiding this comment.
Documentation specifies horizon_days=1..90 but issue #771 specifies 7/14/30.
The documentation now documents the analytics query contracts, which is good. However, horizon_days=1..90 matches the current implementation but diverges from the objective in issue #771 (which specifies only 7, 14, or 30). If the implementation is intentionally broader than the spec, update the issue/spec to reflect this decision; otherwise, align both doc and code to 7|14|30.
,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/design/operations.md` at line 1073, Docs and implementation disagree on
allowed forecast horizons: docs show GET /forecast?horizon_days=1..90 while
issue `#771` expects only 7, 14, or 30; fix by making the contract
consistent—either restrict the implementation or update the spec/docs. Update
the GET /forecast handler and its validation (horizon_days) to only accept 7,
14, or 30 and return 400 for others, and/or change the
`docs/design/operations.md` entry for `/api/v1/analytics` -> `GET
/forecast?horizon_days` to explicitly document the allowed values (7|14|30) and
default (14), ensuring the parameter name `horizon_days`, default, and error
behavior match the implementation and reference issue `#771`.
…ords Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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 `@src/synthorg/api/controllers/analytics.py`:
- Around line 360-367: The 7-day sparkline is producing 8 buckets because
bucket_cost_records (via generate_bucket_starts) floors the start to midnight;
fix by passing an aligned start-of-day for 6 days ago instead of now -
timedelta(days=7). Update the call that builds cost_7d (references: cost_7d,
bucket_cost_records, BucketSize.DAY, records_7d, now) to compute start =
start_of_day(now - timedelta(days=6)) (i.e., shift to 6 days ago and floor to
midnight) before calling bucket_cost_records so the sparkline yields exactly 7
daily points.
- Around line 186-190: The billing-period cost query currently calls
app_state.cost_tracker.get_total_cost(start=period_start) which includes
future-dated records; update the flow to pass the request-scoped now into
_resolve_budget_context() and use it to bound the cost query by calling
get_total_cost(start=period_start, end=now). Specifically, modify the call sites
that build the budget context (functions get_overview and get_forecast) to pass
the same request-scoped now into _resolve_budget_context() and ensure the
resolved code uses that now when calling get_total_cost (identify
billing_period_start(budget_config.reset_day) and
app_state.cost_tracker.get_total_cost to locate the change).
🪄 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: d1128141-5020-48ee-bdd5-2fb5b84026b6
📒 Files selected for processing (1)
src/synthorg/api/controllers/analytics.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). (4)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Backend
- GitHub Check: Build Sandbox
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotations-- Python 3.14 has PEP 649 native lazy annotations
Useexcept A, B:syntax (no parentheses) for multiple exception handling -- PEP 758 except syntax enforced by ruff on Python 3.14
All public functions and classes must have type hints; mypy strict mode is enforced
All public classes and functions must have Google-style docstrings; enforced by ruff D rules
Use immutability patterns: create new objects instead of mutating existing ones. For non-Pydantic internal collections, usecopy.deepcopy()at construction andMappingProxyTypewrapping for read-only enforcement. Fordict/listfields in frozen Pydantic models, usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence)
Use frozen Pydantic models (v2) for config/identity; use separate mutable-via-copy models withmodel_copy(update=...)for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model
Use Pydantic v2BaseModel,model_validator,computed_field, andConfigDict. Use@computed_fieldfor derived values instead of storing redundant fields. UseNotBlankStrfromcore.typesfor all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls). Prefer structured concurrency over barecreate_task
Functions must be < 50 lines; files must be < 800 lines
Handle errors explicitly; never silently swallow exceptions
Validate at system boundaries (user input, external APIs, config files)
Line length: 88 characters (enforced by ruff)
Files:
src/synthorg/api/controllers/analytics.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic must import logger:from synthorg.observability import get_loggerthenlogger = get_logger(__name__); never useimport loggingorprint()(exceptions:observability/setup.pyandobservability/sinks.pymay use stdlibloggingandprint(..., file=sys.stderr)for bootstrap code)
Always use structured logging with logger name aslogger(not_loggerorlog). Use event name constants fromsynthorg.observability.events.<domain>modules (e.g.,API_REQUEST_STARTEDfromevents.api,TOOL_INVOKE_STARTfromevents.tool). Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT. Always log aslogger.info(EVENT, key=value)-- neverlogger.info("msg %s", val)
Log WARNING or ERROR on all error paths before raising exceptions. Log INFO on all state transitions. Log DEBUG for object creation, internal flow, and entry/exit of key functions
Never implement retry logic in provider driver subclasses or calling code -- it is handled automatically byBaseCompletionProviderbase class.RetryConfigandRateLimiterConfigare set per-provider inProviderConfig. Retryable errors:RateLimitError,ProviderTimeoutError,ProviderConnectionError,ProviderInternalError. Non-retryable errors raise immediately.RetryExhaustedErrorsignals all retries failed -- catch at engine layer to trigger fallback chains. Rate limiter respectsRateLimitError.retry_after
Files:
src/synthorg/api/controllers/analytics.py
🧠 Learnings (16)
📓 Common learnings
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.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: 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).
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)
📚 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:
src/synthorg/api/controllers/analytics.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/controllers/analytics.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: 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/controllers/analytics.py
📚 Learning: 2026-03-24T05:06:57.759Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T05:06:57.759Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (no parentheses) for multiple exception handling -- PEP 758 except syntax enforced by ruff on Python 3.14
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.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/analytics.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/analytics.py
📚 Learning: 2026-03-24T05:06:57.759Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T05:06:57.759Z
Learning: Applies to **/*.py : Handle errors explicitly; never silently swallow exceptions
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.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/analytics.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/analytics.py
📚 Learning: 2026-03-19T21:11:37.538Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T21:11:37.538Z
Learning: Applies to **/*.py : Errors: handle explicitly, never silently swallow.
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.py
📚 Learning: 2026-03-16T20:14:00.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:14:00.937Z
Learning: Applies to **/*.py : Validate: at system boundaries (user input, external APIs, config files).
Applied to files:
src/synthorg/api/controllers/analytics.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 **/*.py : Validate at system boundaries (user input, external APIs, config files)
Applied to files:
src/synthorg/api/controllers/analytics.py
🧬 Code graph analysis (1)
src/synthorg/api/controllers/analytics.py (7)
src/synthorg/api/dto.py (1)
ApiResponse(130-158)src/synthorg/api/errors.py (1)
ServiceUnavailableError(222-231)src/synthorg/api/state.py (1)
AppState(42-511)src/synthorg/budget/trends.py (11)
BucketSize(45-49)TrendDataPoint(55-66)TrendMetric(36-42)TrendPeriod(28-33)bucket_cost_records(208-247)bucket_success_rate(286-331)bucket_task_completions(250-283)generate_bucket_starts(178-202)period_to_timedelta(148-157)project_daily_spend(396-449)resolve_bucket_size(134-145)src/synthorg/core/enums.py (1)
TaskStatus(226-252)src/synthorg/budget/tracker.py (3)
budget_config(100-106)get_total_cost(123-146)get_records(194-234)src/synthorg/hr/performance/tracker.py (1)
get_task_metrics(374-400)
- Align sparkline start to midnight 6 days ago for exactly 7 daily points - Thread now into _resolve_budget_context to bound get_total_cost with end=now Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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 `@src/synthorg/api/controllers/analytics.py`:
- Around line 442-446: The 7-day records query created with tg.create_task
calling app_state.cost_tracker.get_records should be bounded with end=now
(matching the SPEND fix in _fetch_trend_data_points) to avoid fetching records
beyond the current window; update the call site where t_7d is created to pass
end=now alongside start=now - timedelta(days=7) so bucket_cost_records doesn't
end up filtering out extra records.
- Around line 310-314: The task metrics query for TrendMetric.TASKS_COMPLETED
and TrendMetric.SUCCESS_RATE currently calls
app_state.performance_tracker.get_task_metrics(since=start) without an upper
bound; modify the call to include until=now so it becomes
get_task_metrics(since=start, until=now) (using the existing now variable) to
prevent fetching future-dated records and match the SPEND fix behavior.
🪄 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: a581f021-c34f-4710-92a5-dcdf3a98513b
📒 Files selected for processing (1)
src/synthorg/api/controllers/analytics.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). (4)
- GitHub Check: Build Backend
- GitHub Check: Build Sandbox
- GitHub Check: Test (Python 3.14)
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotations-- Python 3.14 has PEP 649 native lazy annotations
Useexcept A, B:syntax (no parentheses) for multiple exception handling -- PEP 758 except syntax enforced by ruff on Python 3.14
All public functions and classes must have type hints; mypy strict mode is enforced
All public classes and functions must have Google-style docstrings; enforced by ruff D rules
Use immutability patterns: create new objects instead of mutating existing ones. For non-Pydantic internal collections, usecopy.deepcopy()at construction andMappingProxyTypewrapping for read-only enforcement. Fordict/listfields in frozen Pydantic models, usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence)
Use frozen Pydantic models (v2) for config/identity; use separate mutable-via-copy models withmodel_copy(update=...)for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model
Use Pydantic v2BaseModel,model_validator,computed_field, andConfigDict. Use@computed_fieldfor derived values instead of storing redundant fields. UseNotBlankStrfromcore.typesfor all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls). Prefer structured concurrency over barecreate_task
Functions must be < 50 lines; files must be < 800 lines
Handle errors explicitly; never silently swallow exceptions
Validate at system boundaries (user input, external APIs, config files)
Line length: 88 characters (enforced by ruff)
Files:
src/synthorg/api/controllers/analytics.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic must import logger:from synthorg.observability import get_loggerthenlogger = get_logger(__name__); never useimport loggingorprint()(exceptions:observability/setup.pyandobservability/sinks.pymay use stdlibloggingandprint(..., file=sys.stderr)for bootstrap code)
Always use structured logging with logger name aslogger(not_loggerorlog). Use event name constants fromsynthorg.observability.events.<domain>modules (e.g.,API_REQUEST_STARTEDfromevents.api,TOOL_INVOKE_STARTfromevents.tool). Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT. Always log aslogger.info(EVENT, key=value)-- neverlogger.info("msg %s", val)
Log WARNING or ERROR on all error paths before raising exceptions. Log INFO on all state transitions. Log DEBUG for object creation, internal flow, and entry/exit of key functions
Never implement retry logic in provider driver subclasses or calling code -- it is handled automatically byBaseCompletionProviderbase class.RetryConfigandRateLimiterConfigare set per-provider inProviderConfig. Retryable errors:RateLimitError,ProviderTimeoutError,ProviderConnectionError,ProviderInternalError. Non-retryable errors raise immediately.RetryExhaustedErrorsignals all retries failed -- catch at engine layer to trigger fallback chains. Rate limiter respectsRateLimitError.retry_after
Files:
src/synthorg/api/controllers/analytics.py
🧠 Learnings (16)
📓 Common learnings
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.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: 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).
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)
📚 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:
src/synthorg/api/controllers/analytics.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/controllers/analytics.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: 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/controllers/analytics.py
📚 Learning: 2026-03-24T05:06:57.759Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T05:06:57.759Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (no parentheses) for multiple exception handling -- PEP 758 except syntax enforced by ruff on Python 3.14
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.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/analytics.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/analytics.py
📚 Learning: 2026-03-24T05:06:57.759Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T05:06:57.759Z
Learning: Applies to **/*.py : Handle errors explicitly; never silently swallow exceptions
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.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/analytics.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/analytics.py
📚 Learning: 2026-03-19T21:11:37.538Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T21:11:37.538Z
Learning: Applies to **/*.py : Errors: handle explicitly, never silently swallow.
Applied to files:
src/synthorg/api/controllers/analytics.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/analytics.py
📚 Learning: 2026-03-16T20:14:00.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:14:00.937Z
Learning: Applies to **/*.py : Validate: at system boundaries (user input, external APIs, config files).
Applied to files:
src/synthorg/api/controllers/analytics.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 **/*.py : Validate at system boundaries (user input, external APIs, config files)
Applied to files:
src/synthorg/api/controllers/analytics.py
🧬 Code graph analysis (1)
src/synthorg/api/controllers/analytics.py (6)
src/synthorg/api/errors.py (1)
ServiceUnavailableError(222-231)src/synthorg/api/state.py (3)
AppState(42-511)has_agent_registry(307-309)config_resolver(327-329)src/synthorg/budget/trends.py (10)
BucketSize(45-49)TrendDataPoint(55-66)TrendMetric(36-42)TrendPeriod(28-33)bucket_cost_records(208-247)bucket_success_rate(286-331)bucket_task_completions(250-283)generate_bucket_starts(178-202)project_daily_spend(396-449)resolve_bucket_size(134-145)src/synthorg/core/enums.py (1)
TaskStatus(226-252)src/synthorg/budget/tracker.py (3)
budget_config(100-106)get_total_cost(123-146)get_records(194-234)src/synthorg/hr/performance/tracker.py (1)
get_task_metrics(374-400)
🔇 Additional comments (12)
src/synthorg/api/controllers/analytics.py (12)
331-333:ACTIVE_AGENTSfallback to 0 when registry is unavailable.This concern was raised in a previous review. When
AgentRegistryServiceis unavailable, passing0as the fallback causes all buckets to show zero agents, which is misleading. Consider fetching the configured agent count as the fallback.
525-532:horizon_daysaccepts broader range than specification.This concern was raised in a previous review. The linked issue specifies horizon values of 7, 14, or 30 days, but this accepts any integer from 1 to 90. Consider constraining to
Literal[7, 14, 30]or an enum. As per coding guidelines, validate at system boundaries.
1-48: LGTM!Imports are well-organized with proper TYPE_CHECKING guards, structured logging setup, and analytics event constants. Compliant with Python 3.14 guidelines (no future annotations import).
54-96: LGTM!Well-structured frozen Pydantic model with appropriate field constraints. The
allow_inf_nan=Falsesetting properly prevents invalid float values in API responses. Documentation correctly notes thatbudget_used_percent > 100indicates overrun.
99-155: LGTM!Both response models are properly configured as frozen with appropriate field constraints. The
confidencefield correctly bounds to [0.0, 1.0], anddays_until_exhaustedis appropriately nullable.
168-214: LGTM!Good improvements: the
nowparameter enables deterministic time bounding, andget_total_costnow properly bounds withend=end. The PEP 758 exception syntax is correctly used for re-raising critical errors while gracefully degrading on other failures with appropriate warning logs.
217-248: LGTM!Clean implementation with proper registry availability check via
has_agent_registry. The graceful degradation to configured agent count is appropriate, and the PEP 758 exception syntax correctly re-raises critical errors.
250-282: LGTM!Clean dispatch helper that appropriately delegates to the specialized bucketing functions from
trends.py.
340-401: LGTM!Well-structured assembly helper. The sparkline alignment fix (midnight 6 days ago) correctly produces 7 daily buckets. Budget context and agent counts are properly resolved with appropriate fallbacks.
447-466: LGTM!Proper
ExceptionGrouphandling forTaskGroupfailures with appropriate logging and conversion toServiceUnavailableError(503).
468-519: LGTM!Clean implementation using
TrendPeriodandTrendMetricStrEnums for input validation. The delegation to_fetch_trend_data_pointskeeps the endpoint method focused.
548-582: LGTM!Well-implemented forecast endpoint. The lookback query correctly bounds with
end=now, andproject_daily_spendreceives deterministicnowparameter for reproducible projections. Structured logging withANALYTICS_FORECAST_QUERIEDevent provides good observability.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🤖 I have created a release *beep* *boop* --- #MAJOR CHANGES; We got a somewhat working webui :) ## [0.5.0](v0.4.9...v0.5.0) (2026-03-30) ### Features * add analytics trends and budget forecast API endpoints ([#798](#798)) ([16b61f5](16b61f5)) * add department policies to default templates ([#852](#852)) ([7a41548](7a41548)) * add remaining activity event types (task_started, tool_used, delegation, cost_incurred) ([#832](#832)) ([4252fac](4252fac)) * agent performance, activity, and history API endpoints ([#811](#811)) ([9b75c1d](9b75c1d)) * Agent Profiles and Detail pages (biography, career, performance) ([#874](#874)) ([62d7880](62d7880)) * app shell, Storybook, and CI/CD pipeline ([#819](#819)) ([d4dde90](d4dde90)) * Approvals page with risk grouping, urgency indicators, batch actions ([#889](#889)) ([4e9673d](4e9673d)) * Budget Panel page (P&L dashboard, breakdown charts, forecast) ([#890](#890)) ([b63b0f1](b63b0f1)) * build infrastructure layer (API client, auth, WebSocket) ([#815](#815)) ([9f01d3e](9f01d3e)) * CLI global options infrastructure, UI modes, exit codes, env vars ([#891](#891)) ([fef4fc5](fef4fc5)) * CodeMirror editor and theme preferences toggle ([#905](#905), [#807](#807)) ([#909](#909)) ([41fbedc](41fbedc)) * Company page (department/agent management) ([#888](#888)) ([cfb88b0](cfb88b0)) * comprehensive hint coverage across all CLI commands ([#900](#900)) ([937974e](937974e)) * config system extensions, per-command flags for init/start/stop/status/logs ([#895](#895)) ([32f83fe](32f83fe)) * configurable currency system replacing hardcoded USD ([#854](#854)) ([b372551](b372551)) * Dashboard page (metric cards, activity feed, budget burn) ([#861](#861)) ([7d519d5](7d519d5)) * department health, provider status, and activity feed endpoints ([#818](#818)) ([6d5f196](6d5f196)) * design tokens and core UI components ([#833](#833)) ([ed887f2](ed887f2)) * extend approval, meeting, and budget API responses ([#834](#834)) ([31472bf](31472bf)) * frontend polish -- real-time UX, accessibility, responsive, performance ([#790](#790), [#792](#792), [#791](#791), [#793](#793)) ([#917](#917)) ([f04a537](f04a537)) * implement human roles and access control levels ([#856](#856)) ([d6d8a06](d6d8a06)) * implement semantic conflict detection in workspace merge ([#860](#860)) ([d97283b](d97283b)) * interaction components and animation patterns ([#853](#853)) ([82d4b01](82d4b01)) * Login page + first-run bootstrap + Company page ([#789](#789), [#888](#888)) ([#896](#896)) ([8758e8d](8758e8d)) * Meetings page with timeline viz, token bars, contribution formatting ([#788](#788)) ([#904](#904)) ([b207f46](b207f46)) * Messages page with threading, channel badges, sender indicators ([#787](#787)) ([#903](#903)) ([28293ad](28293ad)) * Org Chart force-directed view and drag-drop reassignment ([#872](#872), [#873](#873)) ([#912](#912)) ([a68a938](a68a938)) * Org Chart page (living nodes, status, CRUD, department health) ([#870](#870)) ([0acbdae](0acbdae)) * per-command flags for remaining commands, auto-behavior wiring, help/discoverability ([#897](#897)) ([3f7afa2](3f7afa2)) * Providers page with backend rework -- health, CRUD, subscription auth ([#893](#893)) ([9f8dd98](9f8dd98)) * scaffold React + Vite + TypeScript + Tailwind project ([#799](#799)) ([bd151aa](bd151aa)) * Settings page with search, dependency indicators, grouped rendering ([#784](#784)) ([#902](#902)) ([a7b9870](a7b9870)) * Setup Wizard rebuild with template comparison, cost estimator, theme customization ([#879](#879)) ([ae8b50b](ae8b50b)) * setup wizard UX -- template filters, card metadata, provider form reuse ([#910](#910)) ([7f04676](7f04676)) * setup wizard UX overhaul -- mode choice, step reorder, provider fixes ([#907](#907)) ([ee964c4](ee964c4)) * structured ModelRequirement in template agent configs ([#795](#795)) ([7433548](7433548)) * Task Board page (rich Kanban, filtering, dependency viz) ([#871](#871)) ([04a19b0](04a19b0)) ### Bug Fixes * align frontend types with backend and debounce WS refetches ([#916](#916)) ([134c11b](134c11b)) * auto-cleanup targets newly pulled images instead of old ones ([#884](#884)) ([50e6591](50e6591)) * correct wipe backup-skip flow and harden error handling ([#808](#808)) ([c05860f](c05860f)) * improve provider setup in wizard, subscription auth, dashboard bugs ([#914](#914)) ([87bf8e6](87bf8e6)) * improve update channel detection and add config get command ([#814](#814)) ([6b137f0](6b137f0)) * resolve all ESLint warnings, add zero-warnings enforcement ([#899](#899)) ([079b46a](079b46a)) * subscription auth uses api_key, base URL optional for cloud providers ([#915](#915)) ([f0098dd](f0098dd)) ### Refactoring * semantic analyzer cleanup -- shared filtering, concurrency, extraction ([#908](#908)) ([81372bf](81372bf)) ### Documentation * brand identity and UX design system from [#765](#765) exploration ([#804](#804)) ([389a9f4](389a9f4)) * page structure and information architecture for v0.5.0 dashboard ([#809](#809)) ([f8d6d4a](f8d6d4a)) * write UX design guidelines with WCAG-verified color system ([#816](#816)) ([4a4594e](4a4594e)) ### Tests * add unit tests for agent hooks and page components ([#875](#875)) ([#901](#901)) ([1d81546](1d81546)) ### CI/CD * bump actions/deploy-pages from 4.0.5 to 5.0.0 in the major group ([#831](#831)) ([01c19de](01c19de)) * bump astral-sh/setup-uv from 7.6.0 to 8.0.0 in /.github/actions/setup-python-uv in the all group ([#920](#920)) ([5f6ba54](5f6ba54)) * bump codecov/codecov-action from 5.5.3 to 6.0.0 in the major group ([#868](#868)) ([f22a181](f22a181)) * bump github/codeql-action from 4.34.1 to 4.35.0 in the all group ([#883](#883)) ([87a4890](87a4890)) * bump sigstore/cosign-installer from 4.1.0 to 4.1.1 in the minor-and-patch group ([#830](#830)) ([7a69050](7a69050)) * bump the all group with 3 updates ([#923](#923)) ([ff27c8e](ff27c8e)) * bump wrangler from 4.76.0 to 4.77.0 in /.github in the minor-and-patch group ([#822](#822)) ([07d43eb](07d43eb)) * bump wrangler from 4.77.0 to 4.78.0 in /.github in the all group ([#882](#882)) ([f84118d](f84118d)) ### Maintenance * add design system enforcement hook and component inventory ([#846](#846)) ([15abc43](15abc43)) * add dev-only auth bypass for frontend testing ([#885](#885)) ([6cdcd8a](6cdcd8a)) * add pre-push rebase check hook ([#855](#855)) ([b637a04](b637a04)) * backend hardening -- eviction/size-caps and model validation ([#911](#911)) ([81253d9](81253d9)) * bump axios from 1.13.6 to 1.14.0 in /web in the all group across 1 directory ([#922](#922)) ([b1b0232](b1b0232)) * bump brace-expansion from 5.0.4 to 5.0.5 in /web ([#862](#862)) ([ba4a565](ba4a565)) * bump eslint-plugin-react-refresh from 0.4.26 to 0.5.2 in /web ([#801](#801)) ([7574bb5](7574bb5)) * bump faker from 40.11.0 to 40.11.1 in the minor-and-patch group ([#803](#803)) ([14d322e](14d322e)) * bump https://github.com/astral-sh/ruff-pre-commit from v0.15.7 to 0.15.8 ([#864](#864)) ([f52901e](f52901e)) * bump nginxinc/nginx-unprivileged from `6582a34` to `f99cc61` in /docker/web in the all group ([#919](#919)) ([df85e4f](df85e4f)) * bump nginxinc/nginx-unprivileged from `ccbac1a` to `6582a34` in /docker/web ([#800](#800)) ([f4e9450](f4e9450)) * bump node from `44bcbf4` to `71be405` in /docker/sandbox ([#827](#827)) ([91bec67](91bec67)) * bump node from `5209bca` to `cf38e1f` in /docker/web ([#863](#863)) ([66d6043](66d6043)) * bump picomatch in /site ([#842](#842)) ([5f20bcc](5f20bcc)) * bump recharts 2->3 and @types/node 22->25 in /web ([#802](#802)) ([a908800](a908800)) * Bump requests from 2.32.5 to 2.33.0 ([#843](#843)) ([41daf69](41daf69)) * bump smol-toml from 1.6.0 to 1.6.1 in /site ([#826](#826)) ([3e5dbe4](3e5dbe4)) * bump the all group with 3 updates ([#921](#921)) ([7bace0b](7bace0b)) * bump the minor-and-patch group across 1 directory with 2 updates ([#829](#829)) ([93e611f](93e611f)) * bump the minor-and-patch group across 1 directory with 3 updates ([#841](#841)) ([7010c8e](7010c8e)) * bump the minor-and-patch group across 1 directory with 3 updates ([#869](#869)) ([548cee5](548cee5)) * bump the minor-and-patch group in /site with 2 updates ([#865](#865)) ([9558101](9558101)) * bump the minor-and-patch group with 2 updates ([#867](#867)) ([4830706](4830706)) * consolidate Dependabot groups to 1 PR per ecosystem ([06d2556](06d2556)) * consolidate Dependabot groups to 1 PR per ecosystem ([#881](#881)) ([06d2556](06d2556)) * improve worktree skill with full dep sync and status enhancements ([#906](#906)) ([772c625](772c625)) * remove Vue remnants and document framework decision ([#851](#851)) ([bf2adf6](bf2adf6)) * update web dependencies and fix brace-expansion CVE ([#880](#880)) ([a7a0ed6](a7a0ed6)) * upgrade to Storybook 10 and TypeScript 6 ([#845](#845)) ([52d95f2](52d95f2)) --- 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
GET /api/v1/analytics/trends-- time-series sparkline data (tasks_completed, spend, active_agents, success_rate) with period param (7d/30d/90d), bucketed by day or hourGET /api/v1/analytics/forecast-- budget spend projection with horizon_days param, daily projections array, days_until_exhausted (ceiling-rounded), confidence scoreGET /api/v1/analytics/overview-- add budget_remaining_usd, budget_used_percent, cost_7d_trend sparkline, active_agents_count, idle_agents_countbudget/trends.py(followscategory_analytics.pypattern)budget_configpropertyobservability/events/analytics.pyReview coverage
Pre-reviewed by 7 specialized agents (code-reviewer, python-reviewer, test-analyzer, conventions-enforcer, issue-verifier, async-reviewer, docs-checker). 17 findings addressed:
_generate_bucket_starts(now public in trends.py)_resolve_budget_contexthelper for overview + forecastdays_until_exhaustedto usemath.ceil(budget survives N full days)nowparam toproject_daily_spendfor deterministic testingmax(0)guard on idle agent count for TOCTOU safetyTest plan
uv run python -m pytest tests/unit/budget/test_trends.py -m unit -n auto-- 35 pure function testsuv run python -m pytest tests/unit/api/controllers/test_analytics.py -m unit -n auto-- 22 endpoint testsuv run ruff check src/ tests/-- cleanuv run mypy src/ tests/-- cleanuv run pre-commit run --all-files-- all hooks passCloses #771
🤖 Generated with Claude Code