feat: cumulative risk-unit action budgets (#806) and automated reporting (#245)#1063
feat: cumulative risk-unit action budgets (#806) and automated reporting (#245)#1063
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (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). (6)
🧰 Additional context used📓 Path-based instructions (4)**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
src/synthorg/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
src/synthorg/**/!(observability)/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
src/**/*.py⚙️ CodeRabbit configuration file
Files:
🧠 Learnings (1)📓 Common learnings🔇 Additional comments (2)
WalkthroughAdds cumulative risk-unit budgeting and an automated reporting subsystem. Security: introduces a pluggable multi-dimensional risk scoring model ( 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 |
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure that dependencies are being submitted on PR branches. Re-running this action after a short time may resolve the issue. See the documentation for more information and troubleshooting advice. Scanned FilesNone |
There was a problem hiding this comment.
Code Review
This pull request implements a comprehensive risk budgeting and automated reporting framework. It introduces services for tracking cumulative action risk, scoring actions across multiple dimensions, and enforcing risk limits alongside monetary budgets. A new reporting service generates periodic summaries of spending, performance, and risk trends, accessible via new API endpoints. Security enforcement is also enhanced with a shadow mode for calibration. Review feedback identifies critical syntax errors in exception handling and suggests memory optimizations for record filtering.
| start=start, | ||
| end=end, | ||
| ) | ||
| except MemoryError, RecursionError: |
| timestamp=datetime.now(UTC), | ||
| ) | ||
| await self._risk_tracker.record(record) | ||
| except MemoryError, RecursionError: |
src/synthorg/budget/risk_tracker.py
Outdated
| result = records | ||
| if agent_id is not None: | ||
| result = [r for r in result if r.agent_id == agent_id] | ||
| if task_id is not None: | ||
| result = [r for r in result if r.task_id == task_id] | ||
| if action_type is not None: | ||
| result = [r for r in result if r.action_type == action_type] | ||
| if start is not None: | ||
| result = [r for r in result if r.timestamp >= start] | ||
| if end is not None: | ||
| result = [r for r in result if r.timestamp < end] | ||
| return result |
There was a problem hiding this comment.
The current implementation of _filter_records creates a new list for each filter condition. With a large number of records (up to _AUTO_PRUNE_THRESHOLD which is 100,000), this can be memory-intensive.
Using a generator expression avoids creating these intermediate lists, making the filtering more memory-efficient.
| result = records | |
| if agent_id is not None: | |
| result = [r for r in result if r.agent_id == agent_id] | |
| if task_id is not None: | |
| result = [r for r in result if r.task_id == task_id] | |
| if action_type is not None: | |
| result = [r for r in result if r.action_type == action_type] | |
| if start is not None: | |
| result = [r for r in result if r.timestamp >= start] | |
| if end is not None: | |
| result = [r for r in result if r.timestamp < end] | |
| return result | |
| gen = (r for r in records) | |
| if agent_id is not None: | |
| gen = (r for r in gen if r.agent_id == agent_id) | |
| if task_id is not None: | |
| gen = (r for r in gen if r.task_id == task_id) | |
| if action_type is not None: | |
| gen = (r for r in gen if r.action_type == action_type) | |
| if start is not None: | |
| gen = (r for r in gen if r.timestamp >= start) | |
| if end is not None: | |
| gen = (r for r in gen if r.timestamp < end) | |
| return list(gen) |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1063 +/- ##
==========================================
+ Coverage 90.75% 90.77% +0.02%
==========================================
Files 711 722 +11
Lines 40521 41099 +578
Branches 4056 4100 +44
==========================================
+ Hits 36773 37306 +533
- Misses 3045 3085 +40
- Partials 703 708 +5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Implements cumulative “risk-unit” budgeting alongside existing cost controls, adds security shadow/disabled enforcement modes, and introduces an automated reporting subsystem (templates, period computation, service, and API endpoints).
Changes:
- Add multi-dimensional risk scoring (
RiskScore) + default scorer mapping for all built-inActionTypevalues. - Add in-memory cumulative risk tracking + risk budget enforcement hooks in
BudgetEnforcer. - Add automated report models/service + Reports API endpoints and observability events.
Reviewed changes
Copilot reviewed 34 out of 34 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/synthorg/security/service.py |
Adds SecurityEnforcementMode handling (disabled + shadow mode verdict conversion). |
src/synthorg/security/risk_scorer.py |
Introduces risk scoring protocol/models and DefaultRiskScorer with a built-in score map. |
src/synthorg/security/config.py |
Adds SecurityEnforcementMode to SecurityConfig. |
src/synthorg/security/autonomy/change_strategy.py |
Maps RISK_BUDGET_EXHAUSTED to autonomy downgrade behavior. |
src/synthorg/security/__init__.py |
Exposes risk scorer types + SecurityEnforcementMode via package exports. |
src/synthorg/observability/events/security.py |
Adds shadow-mode event constant. |
src/synthorg/observability/events/risk_budget.py |
Adds risk budget event constants (scorer/tracker/enforcement). |
src/synthorg/observability/events/reporting.py |
Adds reporting event constants. |
src/synthorg/core/enums.py |
Adds DowngradeReason.RISK_BUDGET_EXHAUSTED. |
src/synthorg/budget/risk_tracker.py |
Adds RiskTracker with TTL eviction + aggregation queries. |
src/synthorg/budget/risk_record.py |
Adds immutable RiskRecord model with validation. |
src/synthorg/budget/risk_config.py |
Adds risk budget configuration models and validation. |
src/synthorg/budget/risk_check.py |
Adds RiskCheckResult DTO for pre-flight risk checks. |
src/synthorg/budget/report_templates.py |
Adds Pydantic models for report templates (performance/task completion/risk trends/comprehensive). |
src/synthorg/budget/report_config.py |
Adds report period/template/scheduling config enums/models. |
src/synthorg/budget/errors.py |
Adds RiskBudgetExhaustedError as a BudgetExhaustedError subclass. |
src/synthorg/budget/enforcer.py |
Adds risk budget pre-flight checks and risk recording in BudgetEnforcer. |
src/synthorg/budget/automated_reports.py |
Adds AutomatedReportService and UTC period computation utilities. |
src/synthorg/budget/__init__.py |
Re-exports new risk/reporting types and services. |
src/synthorg/api/controllers/reports.py |
Adds /api/v1/reports/generate and /api/v1/reports/periods endpoints. |
tests/unit/security/test_shadow_mode.py |
Unit tests for active/shadow/disabled enforcement behavior. |
tests/unit/security/test_risk_scorer.py |
Unit tests for risk scoring model/weights/default scorer coverage. |
tests/unit/observability/test_events.py |
Updates event module discovery expectations for new domains. |
tests/unit/budget/test_risk_tracker.py |
Unit tests for risk tracking, aggregation, and pruning. |
tests/unit/budget/test_risk_record.py |
Unit tests for RiskRecord validation/immutability. |
tests/unit/budget/test_risk_config.py |
Unit tests for risk budget config validation + BudgetConfig integration. |
tests/unit/budget/test_report_templates.py |
Unit tests for report template models and computed/validation fields. |
tests/unit/budget/test_report_config.py |
Unit tests for report config enums/models. |
tests/unit/budget/test_enforcer_risk.py |
Unit tests for BudgetEnforcer risk checks + recording. |
tests/unit/budget/test_automated_reports.py |
Unit tests for reporting period computation and report generation flows. |
tests/unit/budget/conftest.py |
Adds factories for risk budget config models in budget test fixtures. |
docs/design/operations.md |
Documents risk budgets, shadow mode, and automated reporting; updates currency default references. |
CLAUDE.md |
Updates repository architecture notes to include reporting/risk-budget/security additions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| """Resolve the report service from app state.""" | ||
| app_state: AppState = state._app_state # noqa: SLF001 | ||
| service: AutomatedReportService | None = getattr( | ||
| app_state, | ||
| "report_service", | ||
| None, | ||
| ) | ||
| if service is None: |
There was a problem hiding this comment.
_get_report_service() reads AppState via private state._app_state and then looks for a report_service attribute that AppState does not define (AppState uses __slots__). As a result this will always fall back to None and the endpoint will consistently return 503 even when the app is correctly configured. Use the public state.app_state accessor (as other controllers do) and expose the report service as a real AppState slot/property (or store it on the Litestar State in a consistent, typed way).
| """Resolve the report service from app state.""" | |
| app_state: AppState = state._app_state # noqa: SLF001 | |
| service: AutomatedReportService | None = getattr( | |
| app_state, | |
| "report_service", | |
| None, | |
| ) | |
| if service is None: | |
| """Resolve the report service from application state.""" | |
| app_state: AppState = state.app_state | |
| service: AutomatedReportService | None = getattr( | |
| state, | |
| "report_service", | |
| None, | |
| ) | |
| if service is None: | |
| service = getattr( | |
| app_state, | |
| "report_service", | |
| None, | |
| ) | |
| if service is None: |
| @model_validator(mode="after") | ||
| def _validate_limits(self) -> Self: | ||
| """Ensure limit hierarchy: per_task <= per_agent_daily <= total_daily.""" | ||
| if self.total_daily_risk_limit > 0: | ||
| if self.per_task_risk_limit > self.total_daily_risk_limit: | ||
| msg = ( | ||
| f"per_task_risk_limit ({self.per_task_risk_limit}) " | ||
| f"must be <= total_daily_risk_limit " | ||
| f"({self.total_daily_risk_limit})" | ||
| ) | ||
| raise ValueError(msg) | ||
| if self.per_agent_daily_risk_limit > self.total_daily_risk_limit: | ||
| msg = ( | ||
| f"per_agent_daily_risk_limit " | ||
| f"({self.per_agent_daily_risk_limit}) must be <= " | ||
| f"total_daily_risk_limit " | ||
| f"({self.total_daily_risk_limit})" | ||
| ) | ||
| raise ValueError(msg) | ||
| return self |
There was a problem hiding this comment.
RiskBudgetConfig._validate_limits() docstring says it enforces per_task <= per_agent_daily <= total_daily, but the implementation only checks each limit against total_daily_risk_limit and never validates per_task_risk_limit <= per_agent_daily_risk_limit. Either add that check (likely) or update the docstring to match the intended behavior so configs like per_task=50, per_agent_daily=10 don’t silently pass validation.
src/synthorg/budget/enforcer.py
Outdated
| task_risk = await self._risk_tracker.get_task_risk( | ||
| task_id, | ||
| start=day_start, | ||
| ) |
There was a problem hiding this comment.
The per-task risk check is filtered with start=day_start, which effectively makes per_task_risk_limit a per-task-per-day limit. That doesn’t match the config/docs naming (“per task”) and also differs from how per-task cost limits are enforced (they’re task-lifetime, not day-windowed). Consider removing the start=day_start filter for the per-task check (or renaming the config field to clarify it’s daily-scoped).
| task_risk = await self._risk_tracker.get_task_risk( | |
| task_id, | |
| start=day_start, | |
| ) | |
| task_risk = await self._risk_tracker.get_task_risk(task_id) |
There was a problem hiding this comment.
Actionable comments posted: 15
🤖 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/reports.py`:
- Around line 82-94: The function _get_report_service is raising
ServiceUnavailableError without logging; update it to either call the shared
service-resolution helper in the state module (the helper used in state.py lines
181-186) or add a structured warning before raising that includes context (e.g.,
the State/AppState identity and the missing attribute name "report_service");
ensure the warning uses the app logger at WARNING level and then raise
ServiceUnavailableError as before so every error path is logged with context.
In `@src/synthorg/budget/automated_reports.py`:
- Around line 228-234: The log call using REPORTING_GENERATION_COMPLETED
currently hardcodes has_spending=True and has_performance=True; change it to
compute these flags from the actual report objects (e.g., set has_spending =
spending.total_cost > 0 and has_performance = len(performance.agent_snapshots) >
0 or similar emptiness checks) and pass those computed booleans to logger.info
alongside period and has_risk_trends (which can remain
risk_trends.total_risk_units > 0); update the logger.info invocation that
references period, has_spending, has_performance, and has_risk_trends to use
these computed values.
- Around line 329-348: The cost_records parameter is weakly typed as
tuple[object, ...]; import CostRecord inside a TYPE_CHECKING block (e.g., from
synthorg.budget.tracker import CostRecord) and change the
_build_performance_report signature to accept cost_records: tuple[CostRecord,
...]; then replace the getattr-based access in the cost aggregation loop with
direct attribute access (use r.agent_id and r.cost_usd) to restore type safety
and clarity.
In `@src/synthorg/budget/config.py`:
- Around line 238-242: Move the risk_budget Field declaration so it appears with
the other model fields (immediately after the currency field) and before any
`@model_validator` methods; specifically relocate the risk_budget:
RiskBudgetConfig = Field(...) block to sit just after the currency field
definition in the BudgetConfig class so that all fields (including risk_budget)
are declared prior to the BudgetAlertConfig/AutoDowngradeConfig/BudgetConfig
model validators.
In `@src/synthorg/budget/enforcer.py`:
- Around line 497-604: The three similar enforcement branches inside
check_risk_budget are duplicated; replace them with a small data-driven loop
inside the same method: build an iterable of check descriptors referencing the
risk_cfg attributes and corresponding tracker call and log constant (e.g.,
tuples that map per_task_risk_limit ->
self._risk_tracker.get_task_risk(task_id,...)-> RISK_BUDGET_TASK_LIMIT_EXCEEDED,
per_agent_daily_risk_limit -> self._risk_tracker.get_agent_risk(agent_id,...)->
RISK_BUDGET_DAILY_LIMIT_EXCEEDED, total_daily_risk_limit ->
self._risk_tracker.get_total_risk(...)-> RISK_BUDGET_LIMIT_EXCEEDED), then
iterate, call the appropriate get_*_risk method, compare to the limit, log with
the matching constant, construct the same message string, and raise
RiskBudgetExhaustedError with the same fields (agent_id, task_id,
risk_units_used, risk_limit) when exceeded; keep all behavior, messages, and use
existing symbols (check_risk_budget, risk_cfg, self._risk_tracker,
get_task_risk, get_agent_risk, get_total_risk, RISK_BUDGET_TASK_LIMIT_EXCEEDED,
RISK_BUDGET_DAILY_LIMIT_EXCEEDED, RISK_BUDGET_LIMIT_EXCEEDED,
RiskBudgetExhaustedError) so the function stays under 50 lines and semantics
remain identical.
- Around line 525-533: The code currently resets per-task budgets at UTC
midnight by calling _risk_tracker.get_task_risk(task_id, start=day_start), which
uses daily_period_start(); change the call so per_task_risk_limit is enforced
over the task lifetime instead of the day window — e.g., call
_risk_tracker.get_task_risk(task_id) (or pass the task creation timestamp if
available) so the risk sum starts at task creation rather than day_start; update
references around risk_cfg.per_task_risk_limit and remove/don't use
daily_period_start for this per-task check.
- Around line 497-523: check_risk_budget currently compares only recorded usage
and therefore lets the action that would push usage over a limit through; before
comparing limits you must score the pending action and add that projected cost
to the current totals. In check_risk_budget, after verifying risk_cfg and
self._risk_tracker, call the risk scoring API on the tracker to obtain the
pending action's risk (e.g., score = await self._risk_tracker.score(...) or the
appropriate scorer method) and compute projected_total = current_total + score
for each scope (per-task, per-agent daily, total daily), then compare
projected_total against each limit and return/raise accordingly (still returning
RiskCheckResult on allowed). Apply the same change pattern to the other
risk-check blocks referenced (around lines 529-585 and 629-639) so every
enforcement path uses current + projected when deciding allowance.
In `@src/synthorg/budget/errors.py`:
- Around line 51-52: Change the optional identifier types from plain str | None
to NotBlankStr | None for the identifier fields agent_id and task_id in this
module; update the imports to bring NotBlankStr from core.types and adjust any
type hints or docstrings that mention these fields (look for references to
agent_id and task_id in the errors module and the relevant function/class
signatures) so they use NotBlankStr | None consistently.
In `@src/synthorg/budget/report_templates.py`:
- Around line 73-74: The datetime fields in the Pydantic models (e.g.,
generated_at and the other datetime fields referenced in this file) currently
accept naive datetimes; add a Pydantic validator (or root_validator) in the
relevant model classes in report_templates.py that checks each datetime field's
tzinfo and either rejects naive datetimes (raise ValueError) or normalizes them
to timezone-aware values (attach UTC via datetime.replace(tzinfo=timezone.utc)
or convert via astimezone(timezone.utc)); update the Field declarations to
document the requirement and apply the validator to the specific attributes
(generated_at and the other datetime attribute names in these models) so all
incoming datetimes are guaranteed timezone-aware.
In `@src/synthorg/budget/risk_config.py`:
- Around line 64-83: The validator _validate_limits currently enforces per_task
and per_agent_daily against total_daily_risk_limit but misses the intermediate
check; add a check inside _validate_limits to raise a ValueError when
per_task_risk_limit > per_agent_daily_risk_limit (using the same message style
as the other checks), so the enforced invariant is per_task_risk_limit <=
per_agent_daily_risk_limit <= total_daily_risk_limit; reference the attributes
per_task_risk_limit, per_agent_daily_risk_limit, and total_daily_risk_limit and
place the new condition alongside the existing comparisons before returning
self.
In `@src/synthorg/security/autonomy/change_strategy.py`:
- Line 22: The class/module docstring that lists downgrade reasons is missing
DowngradeReason.RISK_BUDGET_EXHAUSTED; update the strategy docstring (the
class-level downgrade list in the ChangeStrategy/strategy docstring) to mention
RISK_BUDGET_EXHAUSTED and its mapping to AutonomyLevel.SUPERVISED so the
documentation matches the runtime mapping (DowngradeReason.RISK_BUDGET_EXHAUSTED
-> AutonomyLevel.SUPERVISED).
In `@src/synthorg/security/risk_scorer.py`:
- Around line 190-223: The _DEFAULT_SCORE_MAP currently omits an explicit guard
so new members of ActionType silently fall back to _UNKNOWN_SCORE; add an
exhaustiveness check and test to fail fast: write a unit test that computes
set(ActionType) and compares it to set(_DEFAULT_SCORE_MAP.keys()) (or asserts
equality) so any missing mapping causes a test failure, and optionally add a
runtime assertion near _DEFAULT_SCORE_MAP (e.g., assert set(ActionType) ==
set(_DEFAULT_SCORE_MAP)) to catch drift at import time; apply the same pattern
for the other map referenced (lines 272-279) to ensure every ActionType has an
explicit score mapping.
In `@tests/unit/budget/test_enforcer_risk.py`:
- Around line 155-167: The test currently uses BudgetConfig() with the default
(disabled) risk budget so the BudgetEnforcer takes the disabled-budget fast
path; update the test case(s) (e.g., test_check_skipped_when_no_risk_tracker and
the similar test at lines ~211-223) to construct
BudgetConfig(risk_budget=RiskBudgetConfig(enabled=True)) so the enforcer's
check_risk_budget and record_risk paths reach the _risk_tracker-is-None branch;
keep the rest of the setup (CostTracker, BudgetEnforcer, assertions) unchanged
to verify the missing-dependency behavior.
- Around line 130-145: The test is currently tripping the per-task limiter
because _make_risk_record defaults task_id="task-1" while the test calls
enforcer.check_risk_budget for "task-1"; change the recorded risk to a different
task so the total-daily limiter is exercised instead. Update the test to call
_make_risk_record(..., task_id="task-2") (or change the check_risk_budget call
to a different task id) while leaving total_daily_risk_limit at 1.0 so that
enforcer.check_risk_budget("agent-b", "task-1", "code:write") will raise via the
total-daily path; references: _make_enforcer, _make_risk_record,
enforcer.check_risk_budget.
In `@tests/unit/budget/test_risk_config.py`:
- Around line 70-78: Add a new unit test mirroring the existing per-task
validation to assert that providing per_agent_daily_risk_limit greater than
total_daily_risk_limit raises a ValueError; create a test function (e.g.,
test_agent_daily_limit_exceeds_total_rejected) that constructs RiskBudgetConfig
with per_agent_daily_risk_limit=200.0 and total_daily_risk_limit=100.0 inside a
pytest.raises(ValueError,
match=r"per_agent_daily_risk_limit.*total_daily_risk_limit") block to exercise
the cross-field validation in RiskBudgetConfig.
🪄 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: 8428d679-9636-4ca9-a75e-14c4e507c252
📒 Files selected for processing (34)
CLAUDE.mddocs/design/operations.mdsrc/synthorg/api/controllers/reports.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/automated_reports.pysrc/synthorg/budget/config.pysrc/synthorg/budget/enforcer.pysrc/synthorg/budget/errors.pysrc/synthorg/budget/report_config.pysrc/synthorg/budget/report_templates.pysrc/synthorg/budget/risk_check.pysrc/synthorg/budget/risk_config.pysrc/synthorg/budget/risk_record.pysrc/synthorg/budget/risk_tracker.pysrc/synthorg/core/enums.pysrc/synthorg/observability/events/reporting.pysrc/synthorg/observability/events/risk_budget.pysrc/synthorg/observability/events/security.pysrc/synthorg/security/__init__.pysrc/synthorg/security/autonomy/change_strategy.pysrc/synthorg/security/config.pysrc/synthorg/security/risk_scorer.pysrc/synthorg/security/service.pytests/unit/budget/conftest.pytests/unit/budget/test_automated_reports.pytests/unit/budget/test_enforcer_risk.pytests/unit/budget/test_report_config.pytests/unit/budget/test_report_templates.pytests/unit/budget/test_risk_config.pytests/unit/budget/test_risk_record.pytests/unit/budget/test_risk_tracker.pytests/unit/observability/test_events.pytests/unit/security/test_risk_scorer.pytests/unit/security/test_shadow_mode.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Agent
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Analyze (python)
- GitHub Check: Dependency Review
🧰 Additional context used
📓 Path-based instructions (5)
**/*.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) per PEP 758 for Python 3.14 exception handling, enforced by ruff.
All public functions and classes require type hints and Google-style docstrings. Type hints must satisfy mypy strict mode.
Use line length of 88 characters, enforced by ruff.
Files:
src/synthorg/observability/events/security.pytests/unit/observability/test_events.pysrc/synthorg/core/enums.pysrc/synthorg/budget/config.pytests/unit/budget/conftest.pysrc/synthorg/security/config.pysrc/synthorg/budget/errors.pysrc/synthorg/observability/events/reporting.pysrc/synthorg/security/autonomy/change_strategy.pytests/unit/budget/test_risk_record.pytests/unit/budget/test_risk_config.pysrc/synthorg/security/service.pysrc/synthorg/security/__init__.pysrc/synthorg/budget/risk_check.pytests/unit/budget/test_report_config.pysrc/synthorg/budget/risk_record.pytests/unit/security/test_shadow_mode.pytests/unit/budget/test_risk_tracker.pytests/unit/budget/test_report_templates.pysrc/synthorg/budget/report_templates.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/risk_config.pysrc/synthorg/budget/enforcer.pysrc/synthorg/api/controllers/reports.pytests/unit/budget/test_enforcer_risk.pysrc/synthorg/security/risk_scorer.pysrc/synthorg/budget/report_config.pysrc/synthorg/observability/events/risk_budget.pysrc/synthorg/budget/automated_reports.pytests/unit/security/test_risk_scorer.pytests/unit/budget/test_automated_reports.pysrc/synthorg/budget/risk_tracker.py
src/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.py: Create new objects rather than mutating existing ones. For non-Pydantic internal collections (registries,BaseTool), usecopy.deepcopy()at construction +MappingProxyTypewrapping for read-only enforcement.
For non-Pydantic internal collections and non-frozen Pydantic models, usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (usingmodel_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Use Pydantic v2 (BaseModel,model_validator,computed_field,ConfigDict). Useallow_inf_nan=Falsein allConfigDictdeclarations to rejectNaN/Infin numeric fields.
Use@computed_fieldfor derived values instead of storing + validating redundant fields (e.g.TokenUsage.total_tokens).
UseNotBlankStr(fromcore.types) for 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). Prefer structured concurrency over 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, external APIs, config files).
Files:
src/synthorg/observability/events/security.pysrc/synthorg/core/enums.pysrc/synthorg/budget/config.pysrc/synthorg/security/config.pysrc/synthorg/budget/errors.pysrc/synthorg/observability/events/reporting.pysrc/synthorg/security/autonomy/change_strategy.pysrc/synthorg/security/service.pysrc/synthorg/security/__init__.pysrc/synthorg/budget/risk_check.pysrc/synthorg/budget/risk_record.pysrc/synthorg/budget/report_templates.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/risk_config.pysrc/synthorg/budget/enforcer.pysrc/synthorg/api/controllers/reports.pysrc/synthorg/security/risk_scorer.pysrc/synthorg/budget/report_config.pysrc/synthorg/observability/events/risk_budget.pysrc/synthorg/budget/automated_reports.pysrc/synthorg/budget/risk_tracker.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/observability/events/security.pysrc/synthorg/core/enums.pysrc/synthorg/budget/config.pysrc/synthorg/security/config.pysrc/synthorg/budget/errors.pysrc/synthorg/observability/events/reporting.pysrc/synthorg/security/autonomy/change_strategy.pysrc/synthorg/security/service.pysrc/synthorg/security/__init__.pysrc/synthorg/budget/risk_check.pysrc/synthorg/budget/risk_record.pysrc/synthorg/budget/report_templates.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/risk_config.pysrc/synthorg/budget/enforcer.pysrc/synthorg/api/controllers/reports.pysrc/synthorg/security/risk_scorer.pysrc/synthorg/budget/report_config.pysrc/synthorg/observability/events/risk_budget.pysrc/synthorg/budget/automated_reports.pysrc/synthorg/budget/risk_tracker.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic MUST have:from synthorg.observability import get_loggerthenlogger = get_logger(__name__).
Never useimport logging/logging.getLogger()/print()in application code. Exception:observability/setup.py,observability/sinks.py,observability/syslog_handler.py, andobservability/http_handler.pymay use stdlib logging and stderr print for handler construction and bootstrap.
Always useloggeras the variable name (not_logger, notlog).
Use event name constants from domain-specific modules undersynthorg.observability.events(e.g.,API_REQUEST_STARTEDfromevents.api,TOOL_INVOKE_STARTfromevents.tool). Import directly and uselogger.info(EVENT, key=value).
Always use structured kwargs in logging:logger.info(EVENT, key=value)— never uselogger.info("msg %s", val)style formatting.
All error paths must log at WARNING or ERROR with context before raising.
All state transitions must log at INFO level.
Use DEBUG level for object creation, internal flow, and entry/exit of key functions.
Maintain 80% minimum test coverage, enforced in CI with--cov=synthorg --cov-fail-under=80.
Files:
src/synthorg/observability/events/security.pysrc/synthorg/core/enums.pysrc/synthorg/budget/config.pysrc/synthorg/security/config.pysrc/synthorg/budget/errors.pysrc/synthorg/observability/events/reporting.pysrc/synthorg/security/autonomy/change_strategy.pysrc/synthorg/security/service.pysrc/synthorg/security/__init__.pysrc/synthorg/budget/risk_check.pysrc/synthorg/budget/risk_record.pysrc/synthorg/budget/report_templates.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/risk_config.pysrc/synthorg/budget/enforcer.pysrc/synthorg/api/controllers/reports.pysrc/synthorg/security/risk_scorer.pysrc/synthorg/budget/report_config.pysrc/synthorg/observability/events/risk_budget.pysrc/synthorg/budget/automated_reports.pysrc/synthorg/budget/risk_tracker.py
{src,tests}/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names:
example-provider,example-large-001,example-medium-001,example-small-001,large/medium/smallas aliases. Vendor names may only appear in: (1) Operations design page provider list, (2).claude/files, (3) third-party import paths, (4) provider presets. Tests usetest-provider,test-small-001, etc.
Files:
src/synthorg/observability/events/security.pytests/unit/observability/test_events.pysrc/synthorg/core/enums.pysrc/synthorg/budget/config.pytests/unit/budget/conftest.pysrc/synthorg/security/config.pysrc/synthorg/budget/errors.pysrc/synthorg/observability/events/reporting.pysrc/synthorg/security/autonomy/change_strategy.pytests/unit/budget/test_risk_record.pytests/unit/budget/test_risk_config.pysrc/synthorg/security/service.pysrc/synthorg/security/__init__.pysrc/synthorg/budget/risk_check.pytests/unit/budget/test_report_config.pysrc/synthorg/budget/risk_record.pytests/unit/security/test_shadow_mode.pytests/unit/budget/test_risk_tracker.pytests/unit/budget/test_report_templates.pysrc/synthorg/budget/report_templates.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/risk_config.pysrc/synthorg/budget/enforcer.pysrc/synthorg/api/controllers/reports.pytests/unit/budget/test_enforcer_risk.pysrc/synthorg/security/risk_scorer.pysrc/synthorg/budget/report_config.pysrc/synthorg/observability/events/risk_budget.pysrc/synthorg/budget/automated_reports.pytests/unit/security/test_risk_scorer.pytests/unit/budget/test_automated_reports.pysrc/synthorg/budget/risk_tracker.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slowfor test markers.
Configureasyncio_mode = "auto"in pytest — no manual@pytest.mark.asyncioneeded for async tests.
Default test timeout is 30 seconds per test (global inpyproject.toml). Non-default overrides liketimeout(60)are allowed, but do not add per-filepytest.mark.timeout(30)markers.
Prefer@pytest.mark.parametrizefor testing similar cases.
Python uses Hypothesis for property-based testing (@given+@settings). Hypothesis profiles:ci(deterministic,max_examples=10+derandomize=True),dev(1000 examples),fuzz(10,000 examples, no deadline),extreme(500,000 examples, no deadline). Controlled viaHYPOTHESIS_PROFILEenv var.
Failing Hypothesis examples are persisted to~/.synthorg/hypothesis-examples/via_WriteOnlyDatabaseintests/conftest.py. When Hypothesis finds a failure, it is a real bug — fix the underlying bug and add an explicit@example(...)decorator to permanently cover the case in CI.
Never skip, dismiss, or ignore flaky tests — always fix them fully. For timing-sensitive tests, mocktime.monotonic()andasyncio.sleep()to make them deterministic. For tasks that must block indefinitely until cancelled, useasyncio.Event().wait()instead ofasyncio.sleep(large_number).
Files:
tests/unit/observability/test_events.pytests/unit/budget/conftest.pytests/unit/budget/test_risk_record.pytests/unit/budget/test_risk_config.pytests/unit/budget/test_report_config.pytests/unit/security/test_shadow_mode.pytests/unit/budget/test_risk_tracker.pytests/unit/budget/test_report_templates.pytests/unit/budget/test_enforcer_risk.pytests/unit/security/test_risk_scorer.pytests/unit/budget/test_automated_reports.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/observability/test_events.pytests/unit/budget/conftest.pytests/unit/budget/test_risk_record.pytests/unit/budget/test_risk_config.pytests/unit/budget/test_report_config.pytests/unit/security/test_shadow_mode.pytests/unit/budget/test_risk_tracker.pytests/unit/budget/test_report_templates.pytests/unit/budget/test_enforcer_risk.pytests/unit/security/test_risk_scorer.pytests/unit/budget/test_automated_reports.py
🧠 Learnings (52)
📓 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: Security: SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies: disabled/weighted/per-category/milestone), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)
Applied to files:
src/synthorg/observability/events/security.pysrc/synthorg/security/config.pysrc/synthorg/security/autonomy/change_strategy.pyCLAUDE.mdsrc/synthorg/security/service.pysrc/synthorg/security/__init__.pysrc/synthorg/budget/risk_record.pytests/unit/security/test_shadow_mode.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/risk_config.pysrc/synthorg/budget/enforcer.pysrc/synthorg/security/risk_scorer.pydocs/design/operations.mdtests/unit/security/test_risk_scorer.pysrc/synthorg/budget/risk_tracker.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/security/**/*.py : Security module includes SecOps agent, rule engine (soft-allow/hard-deny), audit log, output scanner, risk classifier, autonomy levels (4 strategies), timeout policies.
Applied to files:
src/synthorg/observability/events/security.pysrc/synthorg/security/config.pysrc/synthorg/security/autonomy/change_strategy.pyCLAUDE.mdsrc/synthorg/security/service.pysrc/synthorg/security/__init__.pysrc/synthorg/budget/risk_record.pytests/unit/security/test_shadow_mode.pysrc/synthorg/budget/enforcer.pysrc/synthorg/security/risk_scorer.pydocs/design/operations.mdtests/unit/security/test_risk_scorer.pysrc/synthorg/budget/risk_tracker.py
📚 Learning: 2026-04-02T07:18:02.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T07:18:02.381Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly from the domain module
Applied to files:
tests/unit/observability/test_events.pysrc/synthorg/observability/events/reporting.pysrc/synthorg/budget/enforcer.pysrc/synthorg/observability/events/risk_budget.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.pysrc/synthorg/observability/events/reporting.pyCLAUDE.mdsrc/synthorg/budget/enforcer.pysrc/synthorg/observability/events/risk_budget.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.pysrc/synthorg/observability/events/reporting.pysrc/synthorg/budget/enforcer.pysrc/synthorg/observability/events/risk_budget.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
tests/unit/observability/test_events.pysrc/synthorg/observability/events/reporting.pysrc/synthorg/budget/enforcer.pysrc/synthorg/observability/events/risk_budget.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.pysrc/synthorg/observability/events/reporting.pysrc/synthorg/observability/events/risk_budget.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.pysrc/synthorg/observability/events/risk_budget.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly rather than using string literals
Applied to files:
tests/unit/observability/test_events.pysrc/synthorg/observability/events/reporting.py
📚 Learning: 2026-03-31T16:09:24.320Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T16:09:24.320Z
Learning: Applies to src/synthorg/**/*.py : 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 and use in structured logging
Applied to files:
tests/unit/observability/test_events.pysrc/synthorg/observability/events/reporting.pysrc/synthorg/observability/events/risk_budget.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.pysrc/synthorg/observability/events/reporting.pysrc/synthorg/budget/enforcer.pysrc/synthorg/observability/events/risk_budget.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.pysrc/synthorg/observability/events/reporting.pysrc/synthorg/budget/enforcer.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/budget/config.pytests/unit/budget/conftest.pysrc/synthorg/budget/errors.pytests/unit/budget/test_risk_config.pysrc/synthorg/budget/risk_check.pytests/unit/budget/test_report_config.pysrc/synthorg/budget/risk_record.pytests/unit/budget/test_report_templates.pysrc/synthorg/budget/report_templates.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/risk_config.pysrc/synthorg/budget/enforcer.pytests/unit/budget/test_enforcer_risk.pysrc/synthorg/budget/report_config.pysrc/synthorg/observability/events/risk_budget.pydocs/design/operations.mdsrc/synthorg/budget/automated_reports.pysrc/synthorg/budget/risk_tracker.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/config.pytests/unit/budget/conftest.pysrc/synthorg/security/autonomy/change_strategy.pysrc/synthorg/budget/risk_check.pysrc/synthorg/budget/risk_record.pysrc/synthorg/budget/report_templates.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/risk_config.pysrc/synthorg/budget/enforcer.pytests/unit/budget/test_enforcer_risk.pysrc/synthorg/security/risk_scorer.pysrc/synthorg/budget/report_config.pysrc/synthorg/observability/events/risk_budget.pydocs/design/operations.mdsrc/synthorg/budget/automated_reports.pysrc/synthorg/budget/risk_tracker.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/config.pytests/unit/budget/conftest.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/enforcer.pydocs/design/operations.md
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (via `model_copy(update=...)`) for runtime state that evolves
Applied to files:
src/synthorg/security/config.pysrc/synthorg/budget/risk_check.pysrc/synthorg/budget/risk_record.pysrc/synthorg/budget/report_templates.pysrc/synthorg/budget/risk_config.pysrc/synthorg/budget/report_config.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state
Applied to files:
src/synthorg/security/config.pysrc/synthorg/budget/risk_check.pysrc/synthorg/budget/risk_record.pysrc/synthorg/budget/report_templates.pysrc/synthorg/budget/risk_config.pysrc/synthorg/budget/report_config.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Applied to files:
src/synthorg/security/config.pysrc/synthorg/budget/risk_check.pysrc/synthorg/budget/risk_record.pysrc/synthorg/budget/report_templates.pysrc/synthorg/budget/risk_config.pysrc/synthorg/budget/report_config.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: Security: SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies: disabled/weighted/per-category/milestone), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume).
Applied to files:
src/synthorg/security/config.pyCLAUDE.mdsrc/synthorg/security/service.pysrc/synthorg/security/__init__.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:
src/synthorg/observability/events/reporting.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use event name constants from domain-specific modules under `ai_company.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`). Import directly: `from ai_company.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/observability/events/reporting.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.mdsrc/synthorg/security/__init__.pysrc/synthorg/budget/report_templates.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/enforcer.pysrc/synthorg/budget/report_config.pydocs/design/operations.mdsrc/synthorg/budget/automated_reports.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/api/**/*.py : API package (api/): Litestar REST + WebSocket with controllers, guards, channels, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint, provider management endpoint (CRUD + test + presets), backup endpoint, RFC 9457 structured errors, AppState hot-reload slots, service auto-wiring (Phase 1 at construction, Phase 2 on startup), lifecycle helpers
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/reports.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/api/**/*.py : REST API: Litestar framework, controllers with guards, channels for WebSocket, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. RFC 9457 structured errors (ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation).
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/reports.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/synthorg/api/**/*.py : Litestar API must include setup wizard, auth/, auto-wiring, and lifecycle management
Applied to files:
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-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/api/**/*.py : Use Litestar for REST API and WebSocket API with JWT + API key + WS ticket authentication, RFC 9457 structured errors, and content negotiation.
Applied to files:
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-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-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Documentation source in `docs/` (Markdown, built with Zensical). Design spec in `docs/design/` (7 pages: index, agents, organization, communication, engine, memory, operations). Architecture in `docs/architecture/` (overview, tech-stack, decision log). Roadmap in `docs/roadmap/`. Security in `docs/security.md`. Licensing in `docs/licensing.md`. Reference in `docs/reference/`. REST API reference in `docs/rest-api.md`. Library reference in `docs/api/` (auto-generated from docstrings). Custom templates in `docs/overrides/`. Config in `mkdocs.yml`.
Applied to files:
CLAUDE.mddocs/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/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.mdsrc/synthorg/budget/report_templates.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Settings: Runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge, ConfigResolver (typed composed reads for controllers), validation, registry, change notifications via message bus. Per-namespace setting definitions in definitions/ submodule (api, company, providers, memory, budget, security, coordination, observability, backup).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.
Applied to files:
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/observability/**/*.py : Observability includes structured logging via `get_logger(__name__)`, correlation tracking, and log sinks.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T18:42:17.990Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:42:17.990Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`
Applied to files:
src/synthorg/budget/risk_check.pysrc/synthorg/budget/risk_config.pysrc/synthorg/budget/report_config.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 BaseModel, model_validator, computed_field, ConfigDict.
Applied to files:
src/synthorg/budget/risk_check.pysrc/synthorg/budget/risk_config.pysrc/synthorg/budget/report_config.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to tests/**/*.py : Test markers: `pytest.mark.unit`, `pytest.mark.integration`, `pytest.mark.e2e`, `pytest.mark.slow`. Coverage: 80% minimum. Async: `asyncio_mode = 'auto'` — no manual `pytest.mark.asyncio` needed. Timeout: 30 seconds per test. Parallelism: `pytest-xdist` via `-n auto` — ALWAYS include `-n auto` when running pytest, never run tests sequentially.
Applied to files:
tests/unit/budget/test_report_templates.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/templates/**/*.py : Templates: pre-built company templates, personality presets, and builder.
Applied to files:
src/synthorg/budget/report_templates.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. For derived values use `computed_field` instead of storing + validating redundant fields. Use `NotBlankStr` (from `core.types`) for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.
Applied to files:
src/synthorg/budget/risk_config.pysrc/synthorg/budget/report_config.py
📚 Learning: 2026-04-04T10:51:32.691Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T10:51:32.691Z
Learning: Applies to src/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Applied to files:
src/synthorg/budget/risk_config.py
📚 Learning: 2026-04-01T09:09:43.948Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T09:09:43.948Z
Learning: Applies to **/*.py : Use `copy.deepcopy()` at construction and `MappingProxyType` wrapping for read-only enforcement in non-Pydantic internal collections (registries, BaseTool)
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : For non-Pydantic internal collections (registries, `BaseTool`), use `copy.deepcopy()` at construction and wrap with `MappingProxyType` for read-only enforcement
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to **/*.py : Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-04-04T10:51:32.691Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T10:51:32.691Z
Learning: Applies to src/**/*.py : Create new objects rather than mutating existing ones. For non-Pydantic internal collections (registries, `BaseTool`), use `copy.deepcopy()` at construction + `MappingProxyType` wrapping for read-only enforcement.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import `from synthorg.observability import get_logger` and define `logger = get_logger(__name__)`
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import logger via `from synthorg.observability import get_logger` and initialize with `logger = get_logger(__name__)`
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: Applies to docs/design/*.md : Update the relevant `docs/design/` page when approved deviations occur to reflect the new reality
Applied to files:
docs/design/operations.md
📚 Learning: 2026-04-04T10:51:32.691Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T10:51:32.691Z
Learning: Applies to {docs,mkdocs.yml} : Documentation: `docs/` (Markdown, built with Zensical, config: `mkdocs.yml`). Design spec: `docs/design/` (12 pages). Architecture: `docs/architecture/`. Roadmap: `docs/roadmap/`. REST API reference: `docs/rest-api.md` + `docs/_generated/api-reference.html` (generated by `scripts/export_openapi.py`).
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: Engine: Agent orchestration, execution loops, parallel execution, task decomposition, routing, task assignment, centralized single-writer task state engine (TaskEngine), task lifecycle, recovery, shutdown, workspace isolation, coordination (multi-agent pipeline: TopologyDispatcher protocol, 4 dispatchers — SAS/centralized/decentralized/context-dependent, wave execution, workspace lifecycle integration, CoordinationSectionConfig company config bridge, build_coordinator factory), coordination error classification, prompt policy validation, checkpoint recovery (checkpoint/, per-turn persistence, heartbeat detection, CheckpointRecoveryStrategy), approval gate (escalation detection, context parking/resume, EscalationInfo/ResumePayload models), stagnation detection (stagnation/, StagnationDetector protocol, ToolRepetitionDetector, dual-signal analysis, corrective prompt injection), agent runtime state (AgentRuntimeState, lightweight per-agent execution status for dashboard queries and recove...
Applied to files:
docs/design/operations.md
📚 Learning: 2026-03-31T14:28:28.895Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:28:28.895Z
Learning: Applies to src/synthorg/**/*.py : Maintain 80% minimum test coverage (enforced in CI)
Applied to files:
tests/unit/security/test_risk_scorer.py
…ing (#245) - RiskScore model (4 dimensions: reversibility, blast_radius, data_sensitivity, external_visibility) with weighted-sum risk_units - RiskScorer protocol + DefaultRiskScorer mapping all ActionTypes - RiskRecord model (parallel to CostRecord, append-only) - RiskBudgetConfig with per-task, per-agent daily, total daily limits - RiskTracker service (asyncio.Lock, TTL eviction, aggregation queries) - BudgetEnforcer gains check_risk_budget() and record_risk() - RiskBudgetExhaustedError (subclass of BudgetExhaustedError) - SecurityEnforcementMode enum (active/shadow/disabled) - Shadow mode in SecOpsService (full pipeline, audit, but ALLOW) - RISK_BUDGET_EXHAUSTED added to DowngradeReason - AutomatedReportService composing existing trackers - Report templates: spending, performance, task completion, risk trends - Reports API controller (POST /generate, GET /periods) - Design spec updates (Risk Budget + Automated Reporting sections) Closes #806 Closes #245
Pre-reviewed by 10 agents, 24 findings addressed: - Shadow mode: only replace non-ALLOW verdicts (preserve audit trail) - Shadow mode: use SECURITY_SHADOW_WOULD_BLOCK event (correct domain) - RiskRecord.action_type: use NotBlankStr + stronger category:action validator - RiskBudgetConfig: add per_agent_daily <= total_daily validation - report_templates: risk_by_action_type uses NotBlankStr - automated_reports: fix timezone bug in compute_period_range (astimezone) - automated_reports: fix math.fsum for all float aggregation - automated_reports: fix date string round-trip (key on date directly) - automated_reports: extract helpers to keep functions under 50 lines - automated_reports: eliminate N+1 async queries (batch fetch) - automated_reports: document task completion approximation - enforcer: add error handling to record_risk (catch-and-log) - controller: remove duplicate REPORTING_GENERATION_STARTED log - CLAUDE.md: update Package Structure (budget/, security/, api/) - operations.md: add reports to API Surface table - operations.md: fix currency default USD -> EUR - tests: add record_risk with missing scorer test - tests: add shadow mode reason format + preserved fields tests - tests: add RiskScorer protocol conformance test
…nd CodeRabbit Source fixes: - Fix is_success bug (getattr used wrong field name) in automated_reports.py - Register ReportsController in ALL_CONTROLLERS - Add require_write_access guard on POST /generate endpoint - Add logging before raising ServiceUnavailableError in reports controller - Use AwareDatetime for all datetime fields in report templates - Add start < end validator to ComprehensiveReport - Add RISK_BUDGET_RECORD_FAILED event constant, use on failure path - Refactor check_risk_budget: data-driven loop, under 50 lines - Add projected risk enforcement (current + pending action) - Fix per-task risk check to use task lifetime, not daily window - Add error handling around risk tracker async calls - Add per_task <= per_agent_daily validation to RiskBudgetConfig - Add reason-required validator to RiskCheckResult when denied - Add templates uniqueness validator to ReportScheduleConfig - Move risk_budget field before validators in BudgetConfig - Use generator-based filtering in RiskTracker - Use asyncio.TaskGroup for parallel sub-report generation - Fix cost_records typing (CostRecord, not object), remove getattr - Move TaskMetricRecord import out of TYPE_CHECKING - Fix REPORTING_GENERATION_COMPLETED log flags and add has_task_completion - Use NotBlankStr for error identifier fields - Add exhaustiveness guard for _DEFAULT_SCORE_MAP Docstring/docs fixes: - Add risk_tracker/risk_scorer to BudgetEnforcer Args - Add enforcement_mode to SecurityConfig Attributes - Update evaluate_pre_tool steps list for shadow mode - Add RISK_BUDGET_EXHAUSTED to change strategy docstring - Add reporting/risk_budget event domains to CLAUDE.md - Update operations.md event taxonomy count (58 -> 62) and subsystem list Test fixes: - Fix total-daily limiter test (was tripping per-task first) - Fix no-tracker tests (were exiting via disabled fast path) - Fix ComprehensiveReport test (start == end now rejected) - Add per_task > per_agent_daily validation tests - Add RISK_BUDGET_EXHAUSTED downgrade test - Add shadow mode ESCALATE-to-ALLOW test - Add shared make_risk_score/make_risk_record helpers to conftest
… failures The tests used _NOW=2026-04-03 12:00 UTC with 24h expiry, so sessions expired after 2026-04-04 12:00 UTC. Queries filtering by expires_at > now returned empty results once real wall clock passed the expiry. Fix: wrap all time-sensitive store operations in _patch_now() which freezes datetime.now to _NOW + 5min, matching the approach already used by test_list_by_user_excludes_expired and cleanup tests.
626a0c5 to
afd9423
Compare
There was a problem hiding this comment.
Actionable comments posted: 13
🤖 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/reports.py`:
- Around line 82-98: Replace the hardcoded event name in _get_report_service:
instead of logger.warning("api.service.unavailable", ...) import and use the
appropriate event constant from synthorg.observability.events (the domain event
constant for service unavailable) and pass that constant to logger.warning;
ensure you add the import for the constant at the top of the module and keep the
same keyword arg service="report_service" and behavior when service is None in
AutomatedReportService resolution.
In `@src/synthorg/budget/automated_reports.py`:
- Around line 432-435: The daily bucketing uses r.timestamp.date() in the
record's local timezone which can misplace records near midnight; update the
grouping in automated_reports.py so that daily keys are computed from the
timestamp normalized to UTC (e.g., convert r.timestamp to UTC and then take
.date()) before appending into daily (dict[_dt.date, list[RiskRecord]]),
ensuring all references to r.timestamp in the loop use the UTC-normalized value.
In `@src/synthorg/budget/enforcer.py`:
- Around line 529-533: The call to self._risk_scorer.score(action_type) happens
outside the guarded block so an exception from a custom scorer can abort
check_risk_budget(); move the scoring into the same try/except that wraps
tracker/scorer usage (or wrap only the scorer call in its own try/except) so
errors from self._risk_scorer.score(...) are caught, logged, and treated as
graceful degradation (set projected to 0.0 on failure) rather than propagating;
update code around projected and the try block that follows to ensure consistent
error handling for _risk_scorer.score and the tracker logic.
In `@src/synthorg/budget/report_templates.py`:
- Around line 126-132: The completion_rate property uses floating-point
arithmetic which can accumulate precision errors; update completion_rate to
perform the calculation using Decimal (from decimal) or math.fsum: convert
total_assigned and total_completed to Decimal (or use math.fsum on any iterable
sums if totals are aggregates), compute (total_completed / total_assigned) *
Decimal('100'), and round/quantize to two decimal places before returning; also
add the necessary import (Decimal) and ensure you handle zero/None
total_assigned as currently done in completion_rate.
In `@src/synthorg/budget/risk_record.py`:
- Around line 42-48: Add a defensive Pydantic validator to the model that
declares risk_units (e.g., RiskRecord) to ensure risk_units equals the nested
risk_score.risk_units; implement either a field validator on "risk_units" or a
root_validator that compares values and raises ValueError if they differ,
referencing the attributes risk_units and risk_score (and the model name
RiskRecord or whatever class holds those fields) so any future misuse is caught
at model validation time.
In `@src/synthorg/security/service.py`:
- Around line 226-248: When converting a non-ALLOW verdict to a shadow-mode
ALLOW in the SecurityService (the block guarded by self._config.enforcement_mode
== SecurityEnforcementMode.SHADOW), preserve the original verdict.approval_id
when constructing the new SecurityVerdict so escalation IDs from
_handle_escalation are not lost; update the SecurityVerdict(...) call that sets
verdict=SecurityVerdictType.ALLOW to include approval_id=verdict.approval_id
while leaving all other preserved fields unchanged (the logger.warning and
SECURITY_SHADOW_WOULD_BLOCK usage can remain as-is).
In `@tests/unit/budget/conftest.py`:
- Around line 157-193: Duplicate test helper functions exist; keep the canonical
helpers in conftest.py (make_risk_score and make_risk_record) and remove the
local copies from tests/unit/budget/test_risk_tracker.py, updating that test
file to import and use the conftest helpers instead (or rename the conftest
helpers to the underscore-prefixed names used by the test if you prefer
_make_risk_score/_make_risk_record), and adjust any call sites to match the
keyword-only signatures in make_risk_score/make_risk_record if needed.
In `@tests/unit/budget/test_automated_reports.py`:
- Around line 43-63: The helper _make_risk_record in this test is a duplicate of
the helper in conftest and test_risk_tracker; remove the local _make_risk_record
function and instead import the shared fixture/function from
tests.unit.budget.conftest (use the exported name make_risk_record) so tests use
the common RiskRecord/RiskScore construction; update any local references to
_make_risk_record to call make_risk_record and ensure imports include from
tests.unit.budget.conftest import make_risk_record.
In `@tests/unit/budget/test_enforcer_risk.py`:
- Around line 102-149: Add a regression test that preloads recorded usage just
under a limit and asserts that check_risk_budget raises when the projected usage
(recorded + score(action_type).risk_units) would exceed it; use the helpers
_make_enforcer and _make_risk_record and assert RiskBudgetExhaustedError from
enforcer.check_risk_budget, and parameterize the new case across the three
scopes (per-task, per-agent-daily, total-daily) by setting each corresponding
limit (per_task_risk_limit, per_agent_daily_risk_limit, total_daily_risk_limit)
to a value slightly above the recorded usage but below recorded + action risk,
record the smaller usage via risk_tracker.record(...), then call
enforcer.check_risk_budget(agent_id, task_id, action_type) where the
action_type's score will push the projection over the limit and expect a
RiskBudgetExhaustedError.
In `@tests/unit/budget/test_report_config.py`:
- Around line 35-54: Add a new test method named
test_duplicate_templates_rejected inside the TestReportScheduleConfig class
mirroring test_duplicate_periods_rejected: call
ReportScheduleConfig(templates=(ReportTemplateName.COMPREHENSIVE,
ReportTemplateName.COMPREHENSIVE)) inside a pytest.raises(ValueError,
match=r"[Dd]uplicate") context to assert duplicate template validation
(references: ReportScheduleConfig, ReportTemplateName.COMPREHENSIVE,
test_duplicate_periods_rejected).
In `@tests/unit/budget/test_risk_tracker.py`:
- Around line 13-43: Remove the duplicated local helpers _make_risk_score and
_make_risk_record and instead import the shared helpers make_risk_score and
make_risk_record from the test conftest; update any usages in this test file to
call make_risk_score/make_risk_record so you reuse the central fixtures (ensure
the imported functions accept the same parameters and return
RiskScore/RiskRecord as expected).
🪄 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: 3b2d5171-ce47-40ad-b301-4202e04c0919
📒 Files selected for processing (37)
CLAUDE.mddocs/design/operations.mdsrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/reports.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/automated_reports.pysrc/synthorg/budget/config.pysrc/synthorg/budget/enforcer.pysrc/synthorg/budget/errors.pysrc/synthorg/budget/report_config.pysrc/synthorg/budget/report_templates.pysrc/synthorg/budget/risk_check.pysrc/synthorg/budget/risk_config.pysrc/synthorg/budget/risk_record.pysrc/synthorg/budget/risk_tracker.pysrc/synthorg/core/enums.pysrc/synthorg/observability/events/reporting.pysrc/synthorg/observability/events/risk_budget.pysrc/synthorg/observability/events/security.pysrc/synthorg/security/__init__.pysrc/synthorg/security/autonomy/change_strategy.pysrc/synthorg/security/config.pysrc/synthorg/security/risk_scorer.pysrc/synthorg/security/service.pytests/unit/api/auth/test_session_store.pytests/unit/budget/conftest.pytests/unit/budget/test_automated_reports.pytests/unit/budget/test_enforcer_risk.pytests/unit/budget/test_report_config.pytests/unit/budget/test_report_templates.pytests/unit/budget/test_risk_config.pytests/unit/budget/test_risk_record.pytests/unit/budget/test_risk_tracker.pytests/unit/observability/test_events.pytests/unit/security/autonomy/test_change_strategy.pytests/unit/security/test_risk_scorer.pytests/unit/security/test_shadow_mode.py
| @computed_field # type: ignore[prop-decorator] | ||
| @property | ||
| def completion_rate(self) -> float: | ||
| """Completion rate as a percentage (0--100).""" | ||
| if self.total_assigned <= 0: | ||
| return 0.0 | ||
| return round(self.total_completed / self.total_assigned * 100, 2) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider using Decimal or math.fsum for completion_rate to avoid floating-point accumulation issues.
The computed field performs division followed by multiplication by 100 and rounding. While adequate for most cases, if this is used in financial compliance or auditing contexts, floating-point precision could matter.
This is a minor concern since the values are percentages bounded 0-100 and rounded to 2 decimal places.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/budget/report_templates.py` around lines 126 - 132, The
completion_rate property uses floating-point arithmetic which can accumulate
precision errors; update completion_rate to perform the calculation using
Decimal (from decimal) or math.fsum: convert total_assigned and total_completed
to Decimal (or use math.fsum on any iterable sums if totals are aggregates),
compute (total_completed / total_assigned) * Decimal('100'), and round/quantize
to two decimal places before returning; also add the necessary import (Decimal)
and ensure you handle zero/None total_assigned as currently done in
completion_rate.
- Use API_SERVICE_UNAVAILABLE event constant instead of hardcoded string - UTC-normalize timestamps for daily risk trend bucketing - Move risk scorer call inside try/except for graceful degradation - Deduplicate _make_risk_record helpers (use shared conftest helpers) - Add projected risk regression tests (per-task, per-agent, total) - Add duplicate templates validation test
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
src/synthorg/budget/automated_reports.py (1)
141-157:⚠️ Potential issue | 🟠 MajorUse
PerformanceTrackerdata for completion counts when it exists.This still reports every task with a cost record as completed. When
self._performance_trackeris configured, failed tasks and abandoned retries will be counted as successes here. Preferget_task_metrics()fortotal_assigned/total_completed, and keep the cost-record heuristic only as the fallback path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/budget/automated_reports.py` around lines 141 - 157, Replace the current unconditional cost-record heuristic with a conditional path that prefers PerformanceTracker metrics: if self._performance_tracker is truthy, call its async get_task_metrics(start, end) and use the returned total_assigned and total_completed for the TaskCompletionReport; otherwise fall back to the existing _cost_tracker.get_records -> task_ids heuristic. Keep generated_at=now and ensure the method still returns a TaskCompletionReport in both branches.src/synthorg/budget/enforcer.py (1)
500-579: 🛠️ Refactor suggestion | 🟠 MajorTrim
check_risk_budget()back under the repo size limit.The body is still well over the 50-line ceiling, and the three scope-specific branches are duplicating the same
limit/current/projected/event/labelpattern. A small data-driven loop inside this method would keep the behavior aligned without adding more one-off helpers.As per coding guidelines,
src/synthorg/**/*.py: "Keep functions under 50 lines and files under 800 lines."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/budget/enforcer.py` around lines 500 - 579, The check_risk_budget method is over the 50-line limit and repeats the same _enforce_risk_limit call pattern three times; refactor it to a compact data-driven loop: build a small list/tuple of (limit_value, current_risk_coro, event_const, label) using risk_cfg.per_task_risk_limit, risk_cfg.per_agent_daily_risk_limit, risk_cfg.total_daily_risk_limit and the corresponding awaitable calls to self._risk_tracker.get_task_risk(task_id), get_agent_risk(agent_id, start=day_start), and get_total_risk(start=day_start), then iterate that list and call self._enforce_risk_limit(limit, await current_risk_coro, projected, event_const, label, agent_id, task_id); keep pre-checks (risk_cfg, _risk_tracker, _risk_scorer), scoring logic (self._risk_scorer.score(...).risk_units), exception handling, and return RiskCheckResult(risk_units=projected).
🤖 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/budget/automated_reports.py`:
- Around line 353-369: The org average is computed from per-agent averages
(all_quality) which weights agents equally; instead aggregate raw non-None
TaskMetricRecord.quality_score values (or track a running sum and count) while
iterating agents so each scored task contributes equally. Modify the loop that
builds snapshots (use _build_agent_snapshot and AgentPerformanceSummary) to
either extract and extend all_quality with individual
TaskMetricRecord.quality_score values for that agent or maintain two
accumulators (total_quality_sum and total_quality_count) and compute org_avg =
round(total_quality_sum/total_quality_count, 2) if count>0; ensure you reference
TaskMetricRecord.quality_score and update
PerformanceMetricsReport.average_quality_score accordingly.
---
Duplicate comments:
In `@src/synthorg/budget/automated_reports.py`:
- Around line 141-157: Replace the current unconditional cost-record heuristic
with a conditional path that prefers PerformanceTracker metrics: if
self._performance_tracker is truthy, call its async get_task_metrics(start, end)
and use the returned total_assigned and total_completed for the
TaskCompletionReport; otherwise fall back to the existing
_cost_tracker.get_records -> task_ids heuristic. Keep generated_at=now and
ensure the method still returns a TaskCompletionReport in both branches.
In `@src/synthorg/budget/enforcer.py`:
- Around line 500-579: The check_risk_budget method is over the 50-line limit
and repeats the same _enforce_risk_limit call pattern three times; refactor it
to a compact data-driven loop: build a small list/tuple of (limit_value,
current_risk_coro, event_const, label) using risk_cfg.per_task_risk_limit,
risk_cfg.per_agent_daily_risk_limit, risk_cfg.total_daily_risk_limit and the
corresponding awaitable calls to self._risk_tracker.get_task_risk(task_id),
get_agent_risk(agent_id, start=day_start), and get_total_risk(start=day_start),
then iterate that list and call self._enforce_risk_limit(limit, await
current_risk_coro, projected, event_const, label, agent_id, task_id); keep
pre-checks (risk_cfg, _risk_tracker, _risk_scorer), scoring logic
(self._risk_scorer.score(...).risk_units), exception handling, and return
RiskCheckResult(risk_units=projected).
🪄 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: 9e1383c5-a5f5-46d0-83cd-ec718b7c4dd3
📒 Files selected for processing (7)
src/synthorg/api/controllers/reports.pysrc/synthorg/budget/automated_reports.pysrc/synthorg/budget/enforcer.pytests/unit/budget/test_automated_reports.pytests/unit/budget/test_enforcer_risk.pytests/unit/budget/test_report_config.pytests/unit/budget/test_risk_tracker.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 Backend
- GitHub Check: Build Web
- GitHub Check: Dependency Review
- 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 exception handling — PEP 758 except syntax. ruff enforces this on Python 3.14.
Line length is 88 characters (ruff).
Files:
tests/unit/budget/test_report_config.pytests/unit/budget/test_risk_tracker.pysrc/synthorg/budget/enforcer.pytests/unit/budget/test_automated_reports.pytests/unit/budget/test_enforcer_risk.pysrc/synthorg/api/controllers/reports.pysrc/synthorg/budget/automated_reports.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, and@pytest.mark.slowmarkers on test functions.
Useasyncio_mode = 'auto'for pytest async tests. No manual@pytest.mark.asyncioneeded.
Global test timeout is 30 seconds. Do not add per-filepytest.mark.timeout(30)markers. Non-default overrides liketimeout(60)are allowed.
Prefer@pytest.mark.parametrizefor testing similar cases.
Tests must usetest-provider,test-small-001and similar generic names, never real vendor names.
Use Hypothesis for property-based testing with@givenand@settingsdecorators.
Never skip, dismiss, or ignore flaky tests — always fix them fully and fundamentally. Mocktime.monotonic()andasyncio.sleep()for timing-sensitive tests. Useasyncio.Event().wait()for indefinite blocking instead ofasyncio.sleep(large_number).
Files:
tests/unit/budget/test_report_config.pytests/unit/budget/test_risk_tracker.pytests/unit/budget/test_automated_reports.pytests/unit/budget/test_enforcer_risk.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/budget/test_report_config.pytests/unit/budget/test_risk_tracker.pytests/unit/budget/test_automated_reports.pytests/unit/budget/test_enforcer_risk.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: All public functions and classes must have type hints. mypy strict mode is enforced.
Use Google-style docstrings on all public classes and functions. Required and enforced by ruff D rules.
Create new objects, never mutate existing ones. For non-Pydantic internal collections, usecopy.deepcopy()at construction and wrap withMappingProxyTypefor read-only enforcement.
Separate frozen Pydantic models for config/identity from mutable-via-copy models for runtime state. Never mix static config fields with mutable runtime fields in one model.
Use Pydantic v2 withBaseModel,model_validator,computed_field,ConfigDict. Useallow_inf_nan=Falsein allConfigDictdeclarations.
Use@computed_fieldfor derived values instead of storing and validating redundant fields (e.g.TokenUsage.total_tokens).
UseNotBlankStr(fromcore.types) for 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). Prefer structured concurrency over barecreate_task.
Keep functions under 50 lines and files under 800 lines.
Handle errors explicitly, never silently swallow them.
Validate at system boundaries (user input, external APIs, config files).
Every module with business logic must import and use:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)
Always use the variable namelogger(not_logger, notlog).
Always use constants from domain-specific modules undersynthorg.observability.eventsfor event names (e.g.,API_REQUEST_STARTED,TOOL_INVOKE_START). Import directly from the domain module.
Always use structured logging with kwargs:logger.info(EVENT, key=value). Never use format strings likelogger.info('msg %s', val).
All error paths must log at WARNING or ERROR...
Files:
src/synthorg/budget/enforcer.pysrc/synthorg/api/controllers/reports.pysrc/synthorg/budget/automated_reports.py
src/synthorg/**/!(observability)/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Never use
import logging,logging.getLogger(), orprint()in application code. Exception:observability/setup.py,observability/sinks.py,observability/syslog_handler.py, andobservability/http_handler.pymay use stdlib logging and stderr print.
Files:
src/synthorg/budget/enforcer.pysrc/synthorg/api/controllers/reports.pysrc/synthorg/budget/automated_reports.py
src/synthorg/budget/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Configurable currency formatting in budget tracking — handled by Budget module.
Files:
src/synthorg/budget/enforcer.pysrc/synthorg/budget/automated_reports.py
src/**/*.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/budget/enforcer.pysrc/synthorg/api/controllers/reports.pysrc/synthorg/budget/automated_reports.py
🧠 Learnings (36)
📓 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)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Security: SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies: disabled/weighted/per-category/milestone), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)
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-17T06:30:14.180Z
Learning: Applies to src/synthorg/security/**/*.py : Security module includes SecOps agent, rule engine (soft-allow/hard-deny), audit log, output scanner, risk classifier, autonomy levels (4 strategies), timeout policies.
📚 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_report_config.pysrc/synthorg/budget/enforcer.pytests/unit/budget/test_automated_reports.pytests/unit/budget/test_enforcer_risk.pysrc/synthorg/budget/automated_reports.py
📚 Learning: 2026-04-04T12:29:37.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T12:29:37.456Z
Learning: Applies to src/synthorg/budget/**/*.py : Configurable currency formatting in budget tracking — handled by Budget module.
Applied to files:
tests/unit/budget/test_report_config.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/enforcer.pytests/unit/budget/test_enforcer_risk.pysrc/synthorg/budget/automated_reports.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/enforcer.py
📚 Learning: 2026-03-31T21:07:37.470Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T21:07:37.470Z
Learning: Applies to **/*.py : Use `except A, B:` (no parentheses) per PEP 758 exception syntax on Python 3.14
Applied to files:
src/synthorg/budget/enforcer.pysrc/synthorg/budget/automated_reports.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (without parentheses) per PEP 758 for exception handling in Python 3.14
Applied to files:
src/synthorg/budget/enforcer.pysrc/synthorg/budget/automated_reports.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/budget/enforcer.pysrc/synthorg/budget/automated_reports.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/budget/enforcer.pysrc/synthorg/budget/automated_reports.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/budget/enforcer.pysrc/synthorg/budget/automated_reports.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/budget/enforcer.pysrc/synthorg/budget/automated_reports.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/budget/enforcer.pysrc/synthorg/budget/automated_reports.py
📚 Learning: 2026-04-04T12:29:37.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T12:29:37.456Z
Learning: Applies to src/synthorg/**/*.py : Keep functions under 50 lines and files under 800 lines.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-04-01T09:09:43.948Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T09:09:43.948Z
Learning: Applies to **/*.py : Use `copy.deepcopy()` at construction and `MappingProxyType` wrapping for read-only enforcement in non-Pydantic internal collections (registries, BaseTool)
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : For non-Pydantic internal collections (registries, `BaseTool`), use `copy.deepcopy()` at construction and wrap with `MappingProxyType` for read-only enforcement
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to **/*.py : Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/budget/enforcer.pysrc/synthorg/api/controllers/reports.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability package (observability/): structured logging, correlation tracking, log sinks; event constants organized by domain under observability/events/ (e.g., events.api, events.tool, events.git, events.context_budget, events.backup)
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from synthorg.observability.events domain-specific modules (e.g., PROVIDER_CALL_START from events.provider). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/budget/enforcer.pysrc/synthorg/api/controllers/reports.py
📚 Learning: 2026-04-02T07:18:02.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T07:18:02.381Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly from the domain module
Applied to files:
src/synthorg/budget/enforcer.pysrc/synthorg/api/controllers/reports.py
📚 Learning: 2026-03-18T21:23:23.586Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T21:23:23.586Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from the domain-specific module under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly from synthorg.observability.events.<domain>.
Applied to files:
src/synthorg/budget/enforcer.pysrc/synthorg/api/controllers/reports.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)
Applied to files:
src/synthorg/budget/enforcer.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/security/**/*.py : Security module includes SecOps agent, rule engine (soft-allow/hard-deny), audit log, output scanner, risk classifier, autonomy levels (4 strategies), timeout policies.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import `from synthorg.observability import get_logger` and define `logger = get_logger(__name__)`
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-04-04T12:29:37.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T12:29:37.456Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import and use: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import logger via `from synthorg.observability import get_logger` and initialize with `logger = get_logger(__name__)`
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/api/**/*.py : API package (api/): Litestar REST + WebSocket with controllers, guards, channels, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint, provider management endpoint (CRUD + test + presets), backup endpoint, RFC 9457 structured errors, AppState hot-reload slots, service auto-wiring (Phase 1 at construction, Phase 2 on startup), lifecycle helpers
Applied to files:
src/synthorg/api/controllers/reports.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly rather than using string literals
Applied to files:
src/synthorg/api/controllers/reports.py
📚 Learning: 2026-04-04T12:29:37.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T12:29:37.456Z
Learning: Applies to src/synthorg/**/*.py : Always use constants from domain-specific modules under `synthorg.observability.events` for event names (e.g., `API_REQUEST_STARTED`, `TOOL_INVOKE_START`). Import directly from the domain module.
Applied to files:
src/synthorg/api/controllers/reports.py
📚 Learning: 2026-03-31T16:09:24.320Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T16:09:24.320Z
Learning: Applies to src/synthorg/**/*.py : 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 and use in structured logging
Applied to files:
src/synthorg/api/controllers/reports.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`); import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`
Applied to files:
src/synthorg/api/controllers/reports.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings
Applied to files:
src/synthorg/api/controllers/reports.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use event name constants from domain-specific modules under `ai_company.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`). Import directly: `from ai_company.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/api/controllers/reports.py
📚 Learning: 2026-04-04T12:29:37.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T12:29:37.456Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (no parentheses) for exception handling — PEP 758 except syntax. ruff enforces this on Python 3.14.
Applied to files:
src/synthorg/budget/automated_reports.py
- Use per-task quality scores for org average (not per-agent averages) - Prefer PerformanceTracker for task completion when available - Refactor check_risk_budget to data-driven loop with lazy coroutines
…isk_budget Replaces untyped lambdas with functools.partial to satisfy mypy strict mode (no-untyped-call). Also avoids eager coroutine creation that caused PytestUnraisableExceptionWarning when early checks raised.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/synthorg/budget/enforcer.py (1)
107-120:⚠️ Potential issue | 🟠 MajorDon't let an enabled risk budget run without its required services.
These branches silently degrade enforcement: no tracker makes the pre-flight check allow-all, and no scorer makes the pending action project as
0.0and skip recording afterward. That leavesrisk_budget.enabledlooking active while protection is only partial or absent. Validate this wiring in__init__, or log a one-time warning and disable the feature explicitly.Also applies to: 519-521, 532-535, 629-635
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/budget/enforcer.py` around lines 107 - 120, The constructor allows risk budgeting to be "enabled" while required services are None, causing silent no-op enforcement; in __init__ validate the wiring: check self._budget_config.risk_budget.enabled (or equivalent flag) and if True but either self._risk_tracker or self._risk_scorer is None, emit a one-time warning via the module logger and explicitly disable the feature by setting the budget/risk enabled flag to False (or raise a clear ValueError if you prefer strictness); apply the same pattern/guard anywhere else that references self._risk_tracker/self._risk_scorer to avoid projecting 0.0 or skipping recording (refer to attributes/methods named _risk_tracker, _risk_scorer, _budget_config, and the class __init__).
🤖 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/budget/automated_reports.py`:
- Around line 205-207: The call to compute_period_range(period, ref) happens
before the surrounding try in generate_comprehensive_report, so a ValueError
from compute_period_range can escape without emitting
REPORTING_GENERATION_STARTED or REPORTING_GENERATION_FAILED; fix by moving the
compute_period_range call (and any dependent now = datetime.now(UTC)) inside the
try block in generate_comprehensive_report (and the similar site around lines
286-288) or wrap it in a small try/except that logs an ERROR/WARNING with
context (including period and reference_time) before re-raising; ensure the log
uses the same reporting failure event/messages used elsewhere so all error paths
are consistently logged.
- Around line 110-113: compute_period_range() defines end as exclusive but the
call to PerformanceTracker.get_task_metrics(since=start, until=end) uses a <=
filter and will double-count tasks finishing exactly at end; update the calls in
automated_reports.py (both the metrics fetch at the block with variable metrics
and the similar call around lines 148-151) to honor the exclusive bound by
passing an adjusted until (e.g., end minus the smallest time unit used by your
timestamps, such as datetime.timedelta(microseconds=1)) or alternatively change
PerformanceTracker.get_task_metrics() to use < until rather than <= until so
tasks at the rollover boundary are not included in both periods.
In `@src/synthorg/budget/enforcer.py`:
- Around line 501-506: The public risk-budget methods (e.g., check_risk_budget)
accept raw str identifiers and a free-form action_type; change their signatures
to use NotBlankStr from core.types for agent_id and task_id and the domain
ActionType (or equivalent enum) for action_type, and validate/convert incoming
values at the boundary so whitespace-only or invalid strings cannot create
separate RiskTracker buckets; update the other public risk-budget method(s)
referenced at the later block (the one around lines 614-619) the same way and
ensure callers pass the validated types.
- Around line 657-662: Change the logging call that currently uses logger.debug
with RISK_BUDGET_RECORD_ADDED to an info-level audit log and include the task
scope: replace logger.debug(...) with logger.info(...) and add task_id=task_id
(or extract task_id from the local context if named differently) alongside
agent_id, action_type, and risk_units (score.risk_units) so the successful
budget record transition is recorded at INFO with task-level context; update the
call site where RISK_BUDGET_RECORD_ADDED is emitted (the logger.debug line) in
enforcer.py.
---
Outside diff comments:
In `@src/synthorg/budget/enforcer.py`:
- Around line 107-120: The constructor allows risk budgeting to be "enabled"
while required services are None, causing silent no-op enforcement; in __init__
validate the wiring: check self._budget_config.risk_budget.enabled (or
equivalent flag) and if True but either self._risk_tracker or self._risk_scorer
is None, emit a one-time warning via the module logger and explicitly disable
the feature by setting the budget/risk enabled flag to False (or raise a clear
ValueError if you prefer strictness); apply the same pattern/guard anywhere else
that references self._risk_tracker/self._risk_scorer to avoid projecting 0.0 or
skipping recording (refer to attributes/methods named _risk_tracker,
_risk_scorer, _budget_config, and the class __init__).
🪄 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: c6ca0943-dda2-4962-a3e2-89037825fbb6
📒 Files selected for processing (2)
src/synthorg/budget/automated_reports.pysrc/synthorg/budget/enforcer.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: Build Backend
- GitHub Check: Build Sandbox
- GitHub Check: Build Web
- GitHub Check: Test (Python 3.14)
- GitHub Check: Dependency Review
- GitHub Check: Analyze (go)
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotations— Python 3.14 has PEP 649 native lazy annotations
Useexcept A, B:syntax (no parentheses) for exception handling — PEP 758 except syntax. ruff enforces this on Python 3.14.
Line length is 88 characters (ruff).
Files:
src/synthorg/budget/enforcer.pysrc/synthorg/budget/automated_reports.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: All public functions and classes must have type hints. mypy strict mode is enforced.
Use Google-style docstrings on all public classes and functions. Required and enforced by ruff D rules.
Create new objects, never mutate existing ones. For non-Pydantic internal collections, usecopy.deepcopy()at construction and wrap withMappingProxyTypefor read-only enforcement.
Separate frozen Pydantic models for config/identity from mutable-via-copy models for runtime state. Never mix static config fields with mutable runtime fields in one model.
Use Pydantic v2 withBaseModel,model_validator,computed_field,ConfigDict. Useallow_inf_nan=Falsein allConfigDictdeclarations.
Use@computed_fieldfor derived values instead of storing and validating redundant fields (e.g.TokenUsage.total_tokens).
UseNotBlankStr(fromcore.types) for 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). Prefer structured concurrency over barecreate_task.
Keep functions under 50 lines and files under 800 lines.
Handle errors explicitly, never silently swallow them.
Validate at system boundaries (user input, external APIs, config files).
Every module with business logic must import and use:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)
Always use the variable namelogger(not_logger, notlog).
Always use constants from domain-specific modules undersynthorg.observability.eventsfor event names (e.g.,API_REQUEST_STARTED,TOOL_INVOKE_START). Import directly from the domain module.
Always use structured logging with kwargs:logger.info(EVENT, key=value). Never use format strings likelogger.info('msg %s', val).
All error paths must log at WARNING or ERROR...
Files:
src/synthorg/budget/enforcer.pysrc/synthorg/budget/automated_reports.py
src/synthorg/**/!(observability)/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Never use
import logging,logging.getLogger(), orprint()in application code. Exception:observability/setup.py,observability/sinks.py,observability/syslog_handler.py, andobservability/http_handler.pymay use stdlib logging and stderr print.
Files:
src/synthorg/budget/enforcer.pysrc/synthorg/budget/automated_reports.py
src/synthorg/budget/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Configurable currency formatting in budget tracking — handled by Budget module.
Files:
src/synthorg/budget/enforcer.pysrc/synthorg/budget/automated_reports.py
src/**/*.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/budget/enforcer.pysrc/synthorg/budget/automated_reports.py
🧠 Learnings (28)
📓 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)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Security: SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies: disabled/weighted/per-category/milestone), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume).
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/security/**/*.py : Security module includes SecOps agent, rule engine (soft-allow/hard-deny), audit log, output scanner, risk classifier, autonomy levels (4 strategies), timeout policies.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)
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).
📚 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/enforcer.pysrc/synthorg/budget/automated_reports.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/enforcer.pysrc/synthorg/budget/automated_reports.py
📚 Learning: 2026-03-31T21:07:37.470Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T21:07:37.470Z
Learning: Applies to **/*.py : Use `except A, B:` (no parentheses) per PEP 758 exception syntax on Python 3.14
Applied to files:
src/synthorg/budget/enforcer.pysrc/synthorg/budget/automated_reports.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (without parentheses) per PEP 758 for exception handling in Python 3.14
Applied to files:
src/synthorg/budget/enforcer.pysrc/synthorg/budget/automated_reports.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/budget/enforcer.pysrc/synthorg/budget/automated_reports.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/budget/enforcer.pysrc/synthorg/budget/automated_reports.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/budget/enforcer.pysrc/synthorg/budget/automated_reports.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/budget/enforcer.pysrc/synthorg/budget/automated_reports.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/budget/enforcer.pysrc/synthorg/budget/automated_reports.py
📚 Learning: 2026-04-04T12:29:37.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T12:29:37.456Z
Learning: Applies to src/synthorg/**/*.py : Keep functions under 50 lines and files under 800 lines.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-04-01T09:09:43.948Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T09:09:43.948Z
Learning: Applies to **/*.py : Use `copy.deepcopy()` at construction and `MappingProxyType` wrapping for read-only enforcement in non-Pydantic internal collections (registries, BaseTool)
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : For non-Pydantic internal collections (registries, `BaseTool`), use `copy.deepcopy()` at construction and wrap with `MappingProxyType` for read-only enforcement
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to **/*.py : Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement.
Applied to files:
src/synthorg/budget/enforcer.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/enforcer.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability package (observability/): structured logging, correlation tracking, log sinks; event constants organized by domain under observability/events/ (e.g., events.api, events.tool, events.git, events.context_budget, events.backup)
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from synthorg.observability.events domain-specific modules (e.g., PROVIDER_CALL_START from events.provider). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-04-02T07:18:02.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T07:18:02.381Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly from the domain module
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-18T21:23:23.586Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T21:23:23.586Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from the domain-specific module under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly from synthorg.observability.events.<domain>.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)
Applied to files:
src/synthorg/budget/enforcer.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/security/**/*.py : Security module includes SecOps agent, rule engine (soft-allow/hard-deny), audit log, output scanner, risk classifier, autonomy levels (4 strategies), timeout policies.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import `from synthorg.observability import get_logger` and define `logger = get_logger(__name__)`
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-04-04T12:29:37.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T12:29:37.456Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import and use: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import logger via `from synthorg.observability import get_logger` and initialize with `logger = get_logger(__name__)`
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-04-04T12:29:37.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T12:29:37.456Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (no parentheses) for exception handling — PEP 758 except syntax. ruff enforces this on Python 3.14.
Applied to files:
src/synthorg/budget/automated_reports.py
🔇 Additional comments (1)
src/synthorg/budget/automated_reports.py (1)
447-450: UTC day bucketing looks good.Normalizing each risk timestamp to UTC before taking
.date()keeps near-midnight events aligned with the same report window ascompute_period_range().
| async def check_risk_budget( | ||
| self, | ||
| agent_id: str, | ||
| task_id: str, | ||
| action_type: str, | ||
| ) -> RiskCheckResult: |
There was a problem hiding this comment.
Use validated identifier types on the new public risk-budget methods.
agent_id and task_id are new public boundary fields, but keeping them as raw str lets whitespace-only values create separate buckets in RiskTracker. That can undercount the intended agent/task totals, and action_type should likewise use the domain action type instead of a free-form string so invalid values do not drift into the graceful-degradation path.
Minimal fix
- agent_id: str,
- task_id: str,
+ agent_id: NotBlankStr,
+ task_id: NotBlankStr,Also applies to: 614-619
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/budget/enforcer.py` around lines 501 - 506, The public
risk-budget methods (e.g., check_risk_budget) accept raw str identifiers and a
free-form action_type; change their signatures to use NotBlankStr from
core.types for agent_id and task_id and the domain ActionType (or equivalent
enum) for action_type, and validate/convert incoming values at the boundary so
whitespace-only or invalid strings cannot create separate RiskTracker buckets;
update the other public risk-budget method(s) referenced at the later block (the
one around lines 614-619) the same way and ensure callers pass the validated
types.
- Move compute_period_range inside try block for consistent error logging - Fix get_task_metrics to use exclusive upper bound (< until, not <=) - Promote record_risk success log to INFO with task_id context - Warn in constructor when risk_budget.enabled but tracker/scorer missing
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/synthorg/hr/performance/tracker.py (1)
443-447:⚠️ Potential issue | 🟡 MinorInconsistent upper-bound semantics with
get_task_metrics.
get_task_metricsnow usescompleted_at < until(exclusive), butget_collaboration_metricsstill usesrecorded_at <= until(inclusive). If both methods are queried with the same time window fromcompute_period_range, collaboration records exactly at the boundary will be included while task metrics will not.♻️ Proposed fix for consistency
if until is not None: - records = [r for r in records if r.recorded_at <= until] + records = [r for r in records if r.recorded_at < until] return tuple(records)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/hr/performance/tracker.py` around lines 443 - 447, The inclusive upper-bound in get_collaboration_metrics is inconsistent with get_task_metrics: change the filter using recorded_at <= until to use recorded_at < until so both methods treat the `until` boundary as exclusive; update the filtering in the get_collaboration_metrics function (the branch that builds `records` using `since`, `until`, and `recorded_at`) to mirror the `completed_at < until` semantics used in get_task_metrics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/synthorg/hr/performance/tracker.py`:
- Around line 443-447: The inclusive upper-bound in get_collaboration_metrics is
inconsistent with get_task_metrics: change the filter using recorded_at <= until
to use recorded_at < until so both methods treat the `until` boundary as
exclusive; update the filtering in the get_collaboration_metrics function (the
branch that builds `records` using `since`, `until`, and `recorded_at`) to
mirror the `completed_at < until` semantics used in get_task_metrics.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: b2f9f66a-4d11-4e7a-af7d-7a663c88da48
📒 Files selected for processing (3)
src/synthorg/budget/automated_reports.pysrc/synthorg/budget/enforcer.pysrc/synthorg/hr/performance/tracker.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Sandbox
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotations— Python 3.14 has PEP 649 native lazy annotations
Useexcept A, B:syntax (no parentheses) for exception handling — PEP 758 except syntax. ruff enforces this on Python 3.14.
Line length is 88 characters (ruff).
Files:
src/synthorg/hr/performance/tracker.pysrc/synthorg/budget/automated_reports.pysrc/synthorg/budget/enforcer.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: All public functions and classes must have type hints. mypy strict mode is enforced.
Use Google-style docstrings on all public classes and functions. Required and enforced by ruff D rules.
Create new objects, never mutate existing ones. For non-Pydantic internal collections, usecopy.deepcopy()at construction and wrap withMappingProxyTypefor read-only enforcement.
Separate frozen Pydantic models for config/identity from mutable-via-copy models for runtime state. Never mix static config fields with mutable runtime fields in one model.
Use Pydantic v2 withBaseModel,model_validator,computed_field,ConfigDict. Useallow_inf_nan=Falsein allConfigDictdeclarations.
Use@computed_fieldfor derived values instead of storing and validating redundant fields (e.g.TokenUsage.total_tokens).
UseNotBlankStr(fromcore.types) for 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). Prefer structured concurrency over barecreate_task.
Keep functions under 50 lines and files under 800 lines.
Handle errors explicitly, never silently swallow them.
Validate at system boundaries (user input, external APIs, config files).
Every module with business logic must import and use:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)
Always use the variable namelogger(not_logger, notlog).
Always use constants from domain-specific modules undersynthorg.observability.eventsfor event names (e.g.,API_REQUEST_STARTED,TOOL_INVOKE_START). Import directly from the domain module.
Always use structured logging with kwargs:logger.info(EVENT, key=value). Never use format strings likelogger.info('msg %s', val).
All error paths must log at WARNING or ERROR...
Files:
src/synthorg/hr/performance/tracker.pysrc/synthorg/budget/automated_reports.pysrc/synthorg/budget/enforcer.py
src/synthorg/**/!(observability)/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Never use
import logging,logging.getLogger(), orprint()in application code. Exception:observability/setup.py,observability/sinks.py,observability/syslog_handler.py, andobservability/http_handler.pymay use stdlib logging and stderr print.
Files:
src/synthorg/hr/performance/tracker.pysrc/synthorg/budget/automated_reports.pysrc/synthorg/budget/enforcer.py
src/**/*.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/hr/performance/tracker.pysrc/synthorg/budget/automated_reports.pysrc/synthorg/budget/enforcer.py
src/synthorg/budget/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Configurable currency formatting in budget tracking — handled by Budget module.
Files:
src/synthorg/budget/automated_reports.pysrc/synthorg/budget/enforcer.py
🧠 Learnings (50)
📓 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)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Security: SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies: disabled/weighted/per-category/milestone), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)
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/security/**/*.py : Security module includes SecOps agent, rule engine (soft-allow/hard-deny), audit log, output scanner, risk classifier, autonomy levels (4 strategies), timeout policies.
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).
📚 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/automated_reports.pysrc/synthorg/budget/enforcer.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/automated_reports.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-31T21:07:37.470Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T21:07:37.470Z
Learning: Applies to **/*.py : Use `except A, B:` (no parentheses) per PEP 758 exception syntax on Python 3.14
Applied to files:
src/synthorg/budget/automated_reports.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (without parentheses) per PEP 758 for exception handling in Python 3.14
Applied to files:
src/synthorg/budget/automated_reports.pysrc/synthorg/budget/enforcer.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/budget/automated_reports.pysrc/synthorg/budget/enforcer.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/budget/automated_reports.pysrc/synthorg/budget/enforcer.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/budget/automated_reports.pysrc/synthorg/budget/enforcer.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/budget/automated_reports.pysrc/synthorg/budget/enforcer.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/budget/automated_reports.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-04-04T12:29:37.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T12:29:37.456Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (no parentheses) for exception handling — PEP 758 except syntax. ruff enforces this on Python 3.14.
Applied to files:
src/synthorg/budget/automated_reports.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/budget/automated_reports.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/budget/automated_reports.py
📚 Learning: 2026-03-15T16:55:07.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T16:55:07.730Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising.
Applied to files:
src/synthorg/budget/automated_reports.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/enforcer.py
📚 Learning: 2026-04-04T12:29:37.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T12:29:37.456Z
Learning: Applies to src/synthorg/**/*.py : Keep functions under 50 lines and files under 800 lines.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-04-04T12:29:37.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T12:29:37.456Z
Learning: Applies to src/synthorg/**/*.py : Use `NotBlankStr` (from `core.types`) for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use `NotBlankStr` from `core.types` for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-31T20:07:03.035Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T20:07:03.035Z
Learning: Applies to **/*.py : Use `NotBlankStr` (from `core.types`) for all identifier/name fields, including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants, instead of manual whitespace validators
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. For derived values use `computed_field` instead of storing + validating redundant fields. Use `NotBlankStr` (from `core.types`) for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.
Applied to files:
src/synthorg/budget/enforcer.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/budget/enforcer.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 with adopted conventions: use computed_field for derived values instead of storing + validating redundant fields; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-04-04T12:29:37.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T12:29:37.456Z
Learning: Applies to src/synthorg/**/*.py : Validate at system boundaries (user input, external APIs, config files).
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-04-01T14:22:06.315Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T14:22:06.315Z
Learning: Applies to {**/*.py,web/src/**/*.{ts,tsx}} : Validate at system boundaries (user input, external APIs, config files)
Applied to files:
src/synthorg/budget/enforcer.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/budget/enforcer.py
📚 Learning: 2026-04-04T12:29:37.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T12:29:37.456Z
Learning: Applies to src/synthorg/**/*.py : All state transitions must log at INFO level.
Applied to files:
src/synthorg/budget/enforcer.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 : All state transitions must log at INFO.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to src/**/*.py : All state transitions must log at INFO level
Applied to files:
src/synthorg/budget/enforcer.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 : All state transitions must log at INFO level
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, and key function entry/exit
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions. Pure data models, enums, and re-exports do NOT need logging.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-31T20:07:03.035Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T20:07:03.035Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising; all state transitions must log at INFO; DEBUG for object creation, internal flow, entry/exit of key functions
Applied to files:
src/synthorg/budget/enforcer.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 : Log all error paths at WARNING or ERROR with context before raising; log all state transitions at INFO; log object creation/internal flow/entry-exit at DEBUG
Applied to files:
src/synthorg/budget/enforcer.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 DEBUG logging for object creation, internal flow, and entry/exit of key functions.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-04-01T09:09:43.948Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T09:09:43.948Z
Learning: Applies to **/*.py : Use `copy.deepcopy()` at construction and `MappingProxyType` wrapping for read-only enforcement in non-Pydantic internal collections (registries, BaseTool)
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : For non-Pydantic internal collections (registries, `BaseTool`), use `copy.deepcopy()` at construction and wrap with `MappingProxyType` for read-only enforcement
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to **/*.py : Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability package (observability/): structured logging, correlation tracking, log sinks; event constants organized by domain under observability/events/ (e.g., events.api, events.tool, events.git, events.context_budget, events.backup)
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from synthorg.observability.events domain-specific modules (e.g., PROVIDER_CALL_START from events.provider). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-04-02T07:18:02.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T07:18:02.381Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly from the domain module
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-18T21:23:23.586Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T21:23:23.586Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from the domain-specific module under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly from synthorg.observability.events.<domain>.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)
Applied to files:
src/synthorg/budget/enforcer.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/security/**/*.py : Security module includes SecOps agent, rule engine (soft-allow/hard-deny), audit log, output scanner, risk classifier, autonomy levels (4 strategies), timeout policies.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import `from synthorg.observability import get_logger` and define `logger = get_logger(__name__)`
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-04-04T12:29:37.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-04T12:29:37.456Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import and use: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import logger via `from synthorg.observability import get_logger` and initialize with `logger = get_logger(__name__)`
Applied to files:
src/synthorg/budget/enforcer.py
🔇 Additional comments (10)
src/synthorg/budget/enforcer.py (3)
511-516: Use validated identifier types on the new public risk-budget methods.
agent_id,task_id, andaction_typeare public boundary parameters but use rawstr. Whitespace-only values could create separate buckets inRiskTracker, undercounting intended totals. Per coding guidelines: "UseNotBlankStrfor all identifier/name fields" and "Validate at system boundaries."This also applies to
record_riskat lines 624-628.
540-577: Clean data-driven enforcement loop.The refactored
check_risk_budgetuses a tuple of check descriptors to avoid branch duplication, and correctly:
- Computes projected risk inside the try block (line 544)
- Uses task lifetime for per-task check (no
start=day_start)- Uses daily window only for per-agent and total daily limits
667-674: LGTM! State transition logged at INFO with context.The successful risk record log correctly uses INFO level and includes
task_idcontext as required by guidelines.src/synthorg/hr/performance/tracker.py (1)
417-418: LGTM! Exclusive upper bound aligns with period semantics.This change correctly treats
untilas exclusive, matching the(start, end)tuple contract documented incompute_period_rangewhere end is exclusive.src/synthorg/budget/automated_reports.py (6)
205-216: Previous concern addressed:compute_period_rangeis now inside the try block.The period computation and logging are now within the guarded block, so
ValueErrorfrom naive datetime validation will be caught and logged viaREPORTING_GENERATION_FAILED.
244-251: Log metadata now reflects actual report content.The boolean flags are computed from report data (
len(spending.by_task) > 0, etc.) rather than hardcoded, providing meaningful observability signals.
377-384: Per-task quality scores correctly aggregated.The org-wide average now uses individual
TaskMetricRecord.quality_scorevalues rather than per-agent averages, so each task contributes equally regardless of agent distribution.
448-451: UTC normalization for daily bucketing.The daily trend correctly normalizes timestamps to UTC before taking
.date(), preventing timezone-related misplacement of records near midnight.
346-348: Strong typing forcost_records.The parameter now uses
tuple[CostRecord, ...]with proper TYPE_CHECKING import, enabling type-safe direct attribute access.
218-234: Good use of structured concurrency.Using
asyncio.TaskGroupfor fan-out/fan-in of the four sub-report generators follows the coding guidelines and ensures proper exception propagation.
…stency Mirrors the get_task_metrics fix: recorded_at < until (not <=) so both query methods treat the until boundary as exclusive.
🤖 I have created a release *beep* *boop* --- ## [0.6.2](v0.6.1...v0.6.2) (2026-04-06) ### Features * add issue analyzer script for priority/scope management ([#1084](#1084)) ([1ccba27](1ccba27)) * config fixes and deferred improvements from PR [#1058](#1058) review ([#1067](#1067)) ([2cac2d3](2cac2d3)), closes [#1061](#1061) [#1060](#1060) * cumulative risk-unit action budgets ([#806](#806)) and automated reporting ([#245](#245)) ([#1063](#1063)) ([4689816](4689816)) * fine-tuning pipeline + CompositeBackend + workflow lifecycle ([#1065](#1065)) ([85b05bc](85b05bc)), closes [#1001](#1001) [#850](#850) [#1058](#1058) * memory consolidation upgrades (LLM Merge, Search-and-Ask, diversity penalty, distillation capture) ([#1071](#1071)) ([174e2be](174e2be)), closes [#704](#704) * migrate web dashboard from Radix UI to Base UI, activate CSP nonce, rebuild org chart page, and fix agent routing ([#1083](#1083)) ([ebc6921](ebc6921)) * v0.7.0 engine foundations -- structured failure diagnosis + auditable decisions ([#1072](#1072)) ([d341d37](d341d37)) * workflow templates and versioning with diff and rollback ([#1069](#1069)) ([7af94de](7af94de)), closes [#1006](#1006) [#1008](#1008) ### Documentation * unify REST API docs under /docs/openapi/ and patch sitemap ([#1073](#1073)) ([af19382](af19382)) ### Maintenance * bump hypothesis from 6.151.10 to 6.151.11 in the all group ([#1086](#1086)) ([3176318](3176318)) * bump nginxinc/nginx-unprivileged from `f99cc61` to `601c823` in /docker/web in the all group ([#1085](#1085)) ([5eb99ac](5eb99ac)) * bump the all group in /web with 3 updates ([#1087](#1087)) ([8deae44](8deae44)) --- 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
Implements cumulative risk-unit action budgets with shadow mode (#806) and an automated reporting system (#245).
Risk-Unit Action Budgets (#806)
Risk assessment was previously per-action and stateless -- an agent could execute 50 MEDIUM-risk actions in a row with no escalation. This PR adds cumulative risk tracking parallel to cost tracking.
risk_unitsscalarDefaultRiskScorermapping all 26 ActionType values to multi-dimensional risk scores (CRITICAL ~0.88, HIGH ~0.62, MEDIUM ~0.31, LOW ~0.05)check_risk_budget()pre-flight checks andrecord_risk()scoring+recordingAutomated Reporting (#245)
POST /api/v1/reports/generate,GET /api/v1/reports/periodsTest Plan
Files Changed
Closes #806
Closes #245