feat: configurable currency system replacing hardcoded USD#854
feat: configurable currency system replacing hardcoded USD#854
Conversation
|
Caution Review failedPull request was closed or merged during review WalkthroughThe PR implements configurable display currency across the codebase. It adds a new currency utility module ( Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Code Review
This pull request implements comprehensive multi-currency support across the platform, transitioning from a hardcoded USD display to a configurable ISO 4217 currency system. Key changes include the addition of a dedicated currency utility module for locale-independent formatting, the inclusion of currency metadata in Pydantic models and API responses, and the integration of currency-aware logic into agent prompts and activity timelines. The default system currency is now EUR, and the frontend has been updated to dynamically resolve the display currency from the budget settings. I have no feedback to provide.
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure that dependencies are being submitted on PR branches. Re-running this action after a short time may resolve the issue. See the documentation for more information and troubleshooting advice. Scanned FilesNone |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #854 +/- ##
==========================================
- Coverage 92.31% 92.30% -0.01%
==========================================
Files 585 588 +3
Lines 30745 30837 +92
Branches 2958 2961 +3
==========================================
+ Hits 28381 28463 +82
- Misses 1870 1879 +9
- Partials 494 495 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 28
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
src/synthorg/engine/run_result.py (1)
77-82:⚠️ Potential issue | 🟡 MinorDescription now conflicts with the actual field semantics.
On Line 77, the description says “configured currency”, but this property is still named
total_cost_usdand returnscost_usd(Line 82). That creates a contract/docs mismatch for API consumers. Either keep USD wording here, or rename/replace this field with a truly display-currency value once conversion/formatting is explicitly applied.Proposed minimal fix (documentation aligned to current behavior)
`@computed_field`( # type: ignore[prop-decorator] - description="Total cost in configured currency", + description="Total cost in USD", )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/engine/run_result.py` around lines 77 - 82, The property total_cost_usd currently returns execution_result.context.accumulated_cost.cost_usd but its decorator description says "Total cost in configured currency", causing a doc/contract mismatch; update the decorator description to accurately state "Total cost in USD" (or similar explicit USD wording) so the documented semantics match the actual return value of total_cost_usd, leaving the property name and returned field (execution_result.context.accumulated_cost.cost_usd) unchanged.src/synthorg/engine/parallel_models.py (1)
217-224:⚠️ Potential issue | 🟠 MajorDescription conflicts with
total_cost_usdsemantics.Line 217 now says “configured currency,” but this field still aggregates
total_cost_usdvalues and exposes thetotal_cost_usdname. That is a contract mismatch and can mislead consumers about units. Please either keep this explicitly USD/base-currency in metadata, or rename/migrate the field to a currency-agnostic name if the value truly changed semantics.Suggested fix (metadata-only path)
`@computed_field`( # type: ignore[prop-decorator] - description="Total cost in configured currency across all agents", + description="Total cost in base currency (USD) across all agents", )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/engine/parallel_models.py` around lines 217 - 224, The property total_cost_usd aggregates result.total_cost_usd but its description claims "configured currency," causing a units mismatch; update the metadata/description for total_cost_usd to explicitly state USD (e.g., "Total cost in USD across all agents") so the name and description align, or alternatively rename the property to a currency-agnostic identifier (e.g., total_cost) and change the aggregation to use a currency-configured value (from each o.result) consistently; locate the total_cost_usd property and adjust the description string or rename the property and its usages accordingly.web/src/__tests__/utils/format.property.test.ts (1)
5-25: 🧹 Nitpick | 🔵 TrivialLGTM! Consider adding a test for default currency behavior.
The property-based tests correctly validate USD and EUR formatting. However, there's no test verifying the default currency behavior when no
currencyCodeargument is passed. Given that Issue#810specifies default USD for backward compatibility (while the PR summary mentions EUR default), a test for the default case would help ensure consistent behavior.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/__tests__/utils/format.property.test.ts` around lines 5 - 25, Add a property-based test that verifies formatCurrency(value) (call with no currencyCode) uses the default currency (expected USD per Issue `#810`) by asserting the output contains '$'; locate and modify the tests near the existing formatCurrency tests in format.property.test.ts (the existing USD and EUR tests reference formatCurrency) and add a new it(...) using fc.assert/fc.property with similar double generation to assert formatCurrency(value) contains '$'.src/synthorg/budget/optimizer.py (1)
632-636:⚠️ Potential issue | 🟡 MinorHardcoded
$symbol remains in routing suggestion reason.This routing suggestion message still uses hardcoded
$for currency formatting, which is inconsistent with the PR's objective to replace all hardcoded USD references.🔧 Proposed fix using format_cost
suggestions.append( RoutingSuggestion( agent_id=agent_id, current_model=most_used, suggested_model=candidate.model_id, current_cost_per_1k=round( current_resolved.total_cost_per_1k, BUDGET_ROUNDING_PRECISION, ), suggested_cost_per_1k=round( candidate.total_cost_per_1k, BUDGET_ROUNDING_PRECISION, ), reason=( f"Switch from {most_used!r} " - f"(${current_resolved.total_cost_per_1k:.4f}/1k) " + f"({format_cost(current_resolved.total_cost_per_1k, self._budget_config.currency)}/1k) " f"to {candidate.model_id!r} " - f"(${candidate.total_cost_per_1k:.4f}/1k) " + f"({format_cost(candidate.total_cost_per_1k, self._budget_config.currency)}/1k) " f"-- same context window, lower cost" ), ), )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/budget/optimizer.py` around lines 632 - 636, The routing suggestion string in synthorg.budget.optimizer.py uses a hardcoded "$" when formatting costs; update this message to use the project's currency formatter (e.g., format_cost) instead of "$". Replace the interpolations that use current_resolved.total_cost_per_1k and candidate.total_cost_per_1k so they are passed through format_cost (keeping the same precision/notation behavior) and build the message using most_used and candidate.model_id as before; ensure the final string reads the same but without any hardcoded USD symbol.src/synthorg/engine/prompt.py (1)
140-165:⚠️ Potential issue | 🟠 MajorStop hardcoding
"EUR"across the prompt API.These helpers now default to EUR, but the feature is supposed to stay backward-compatible and the repo already has
DEFAULT_CURRENCY. Any direct caller that omitscurrencywill render the wrong symbol, and duplicating the literal across every helper in this chain invites drift.🔧 Suggested change
-from synthorg.budget.currency import format_cost, get_currency_symbol +from synthorg.budget.currency import ( + DEFAULT_CURRENCY, + format_cost, + get_currency_symbol, +) @@ - currency: str = "EUR", + currency: str = DEFAULT_CURRENCY, @@ - currency: str = "EUR", + currency: str = DEFAULT_CURRENCY,Apply the same replacement to every
currencydefault in this file, includingformat_task_instruction().Also applies to: 395-396, 558-559, 658-659, 746-747, 811-815
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/engine/prompt.py` around lines 140 - 165, Replace the hardcoded currency default "EUR" with the repository constant DEFAULT_CURRENCY for this function's currency parameter and for every other currency default in this file (including format_task_instruction and the occurrences around lines referenced in the review). Update the function signatures' default value from "EUR" to DEFAULT_CURRENCY, ensure DEFAULT_CURRENCY is imported or referenced from its defining module, and run tests/linters to confirm no unresolved names remain.
🤖 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/agents.py`:
- Around line 185-190: The activity endpoint now hard-depends on
app_state.config_resolver.get_budget_config() which can raise and break timeline
rendering; wrap the await app_state.config_resolver.get_budget_config() call in
a try/except, and if it fails log a warning (including the exception) and set
currency = DEFAULT_CURRENCY before calling merge_activity_timeline; ensure you
still pass currency to merge_activity_timeline and keep
merge_activity_timeline/lifecycle_events/task_metrics usage unchanged so
timeline is rendered even when budget config fails.
In `@src/synthorg/api/controllers/departments.py`:
- Line 62: The health response always shows EUR because
_assemble_department_health never reads or passes the configured currency to the
builders; update _assemble_department_health to accept or retrieve the
department's configured currency (e.g., department.currency) and pass that value
into both the normal and degraded builders/constructors used in that function so
the currency Field (default or overridden) is honored; ensure you update calls
to _assemble_department_health and any builders used on both paths so the
configured currency flows into the response.
In `@src/synthorg/budget/_optimizer_helpers.py`:
- Line 13: The downgrade recommendation and other optimizer strings still
hardcode USD-style "$.../1k" output; update _build_downgrade_recommendation to
use the existing format_cost(...) helper so amounts are rendered in the
configured currency (e.g. produce f"{format_cost(unit_cost)}/1k" instead of
"$.../1k"). Likewise, replace the other hardcoded "$.../1k" constructions
referenced (around the earlier anomaly-description builders at the blocks
flagged near lines 118-119, 149-152, 186-189) to call format_cost(...) and
append "/1k" where appropriate so all cost strings use format_cost consistently.
Ensure you import/retain format_cost (already present) and only change the
string assembly in the functions that emit the per‑1k cost text, including
_build_downgrade_recommendation.
In `@src/synthorg/budget/config.py`:
- Around line 202-208: The BudgetConfig currency Field currently defaults to
"EUR", which breaks backward compatibility; change the default value for the
BudgetConfig.currency Field back to "USD" (leave min_length, max_length,
pattern, and description intact) so any code constructing BudgetConfig() before
runtime settings will continue to display USD by default.
In `@src/synthorg/budget/cost_record.py`:
- Line 30: The docstring for the cost_usd field incorrectly says "configured
currency"; update the descriptions at the cost_usd declarations (references to
cost_usd in this module) to say "base currency" or "USD (base currency)" to
match the persisted field name and behavior; ensure both occurrences (the field
docstring and any comment at its usage) reflect that no conversion is performed
and keep the symbol name cost_usd unchanged.
In `@src/synthorg/budget/currency.py`:
- Around line 52-73: The formatter currently assumes 2 decimals for all
currencies not in ZERO_DECIMAL_CURRENCIES, which misrenders ISO 4217 currencies
with 3 minor units (e.g. BHD, KWD, OMR); fix by adding explicit minor-units
metadata (e.g. a MINOR_UNITS: dict[str, int] mapping) and use that mapping when
formatting amounts instead of the binary ZERO_DECIMAL_CURRENCIES check, or
alternatively enforce config validation to only accept currencies supported by
the formatter (0 or 2 decimal currencies). Update uses of
ZERO_DECIMAL_CURRENCIES and any formatting/validation code that references it to
consult MINOR_UNITS (default to 2 if missing) or to reject unsupported
currencies so formatting is correct.
- Around line 15-16: DEFAULT_CURRENCY is hardcoded to "EUR", which forces
format_cost and format_cost_detail to default to euros; change DEFAULT_CURRENCY
to use the canonical system default instead (e.g., replace the literal "EUR"
with the shared system default constant or a call like
get_system_default_currency()) so the shared formatter inherits the app-wide
default currency; update imports/usage accordingly and ensure format_cost and
format_cost_detail continue to reference DEFAULT_CURRENCY.
In `@src/synthorg/budget/optimizer_models.py`:
- Around line 316-317: The docstring for budget_remaining_usd (and other *_usd
fields in this module) incorrectly says "configured currency" — update those
field docs to state that values are in the base currency (USD) unless explicitly
converted for display; search for budget_remaining_usd and any other *_usd names
(e.g., budget_spent_usd, budget_total_usd) in optimizer_models.py and change
their descriptions to say "value in base currency (USD) unless converted" rather
than "configured currency."
In `@src/synthorg/budget/trends.py`:
- Around line 74-75: The docstring language for projected_spend_usd (and other
*_usd occurrences) is misleading because these fields are computed/stored in
base USD with no conversion path; update the descriptions in the docstring(s)
referencing projected_spend_usd, any other "*_usd" fields, and the display-only
scope (lines around the Projected cumulative spend comment and the occurrences
at Line 83 and Line 378) to clearly state these values are in base currency
(USD) and are not converted—e.g., "Projected cumulative spend for this day,
expressed in base currency (USD); no currency conversion is performed
(display-only)". Ensure references to cost_usd and other *_usd variables are
consistent with that wording.
In `@src/synthorg/communication/message.py`:
- Line 43: The field cost_usd in the Message model has conflicting semantics
(doc says "configured currency" but name implies USD); change the model to use
currency-agnostic fields: replace cost_usd with cost_amount: float and add
cost_currency: str on the Message class (and any constructor/factory like
from_dict/serialize helpers), then provide a backwards-compatible deprecated
alias property/method cost_usd that returns cost_amount only when cost_currency
== "USD" (or raises/logs a deprecation warning otherwise) so consumers using
cost_usd get deterministic behavior while new code uses cost_amount +
cost_currency.
In `@src/synthorg/engine/agent_state.py`:
- Around line 28-29: The docstring/description for accumulated_cost_usd is
wrong: it refers to "configured currency" but the field is sourced from
context.accumulated_cost.cost_usd and is stored as USD/base currency. Update the
two descriptions (the one near accumulated_cost_usd and the duplicate at the
other occurrence) to state that the value is in USD (base currency), not the
configured/display currency, and ensure any mention of "configured currency" is
removed or replaced with "USD (base currency)"; reference the
accumulated_cost_usd field and the context.accumulated_cost.cost_usd source when
making the change.
In `@src/synthorg/engine/assignment/models.py`:
- Around line 23-24: The docstring now says the amount is in the configured
currency but the field name total_cost_usd still implies USD; update the model
to avoid confusing API consumers by either renaming the field to total_cost and
adding a deprecated alias total_cost_usd that maps to the new field, or if you
must keep the name for compatibility, add a clear comment above total_cost_usd
stating it is retained for backward compatibility and its value is in the
configured currency; modify any serialization/validation logic in the same
module (e.g., where total_cost_usd is referenced) to support the alias/rename so
external contracts remain stable.
In `@src/synthorg/engine/coordination/models.py`:
- Around line 178-182: The field name total_cost_usd contradicts its
description; rename the model field to total_cost: float = Field(default=0.0,
ge=0.0, description="Total cost in configured currency") and preserve backward
compatibility by providing a deprecated compatibility alias: implement a
read-only property or attribute named total_cost_usd that returns
self.total_cost and emits a deprecation warning when accessed, and enable
Pydantic population by field name (Config.allow_population_by_field_name = True)
so incoming payloads using the old key can still be accepted (or alternatively
set alias='total_cost_usd' on the new total_cost field if you prefer to accept
the old name on input); update relevant tests/docs accordingly.
In `@src/synthorg/engine/loop_protocol.py`:
- Line 48: The doc for the field cost_usd is now misleading because it documents
configured currency instead of USD; update the API to avoid ambiguity by
introducing a new field (e.g., cost or cost_configured) to hold the
configured-currency amount and revert cost_usd to always represent a
USD-denominated value (or deprecate cost_usd and keep it as an alias for
backward compatibility). Concretely: add the new field name to the data model in
loop_protocol.py, update docstrings/comments for both cost_usd and the new
field, implement serialization/deserialization compatibility so incoming
payloads with the old key map to the new field or are converted to USD if a
conversion rate is available, and mark cost_usd deprecated if you choose the new
field route; reference the cost_usd symbol and the new field name (cost or
cost_configured) when making these changes so consumers can be updated
consistently.
In `@src/synthorg/engine/metrics.py`:
- Line 28: The docstring/description for the metric cost_per_task incorrectly
states "configured currency" while the metric value is populated from
result.total_cost_usd; either perform a conversion from USD to the configured
currency before assigning cost_per_task or, more simply, update the description
to reflect that cost_per_task is currently in USD (or base currency) to match
the data source. Locate the cost_per_task documentation in metrics.py and change
the wording to indicate USD/base currency (or implement conversion from
result.total_cost_usd to the configured currency prior to setting cost_per_task)
so the doc and the value are consistent.
In `@src/synthorg/hr/activity.py`:
- Around line 100-104: The function _task_metric_to_activity currently hardcodes
currency="EUR"; import and use the shared DEFAULT_CURRENCY constant instead
(replace the default param and any literal "EUR" usages) so callers inherit the
global default; apply the same change to the other functions that hardcode EUR
referenced in the diff (the functions around the ranges 142-146 and 229-238) by
updating their default parameters and any string formatting to use
DEFAULT_CURRENCY and ensure DEFAULT_CURRENCY is imported at the top of
src/synthorg/hr/activity.py.
In `@src/synthorg/hr/performance/models.py`:
- Line 63: TaskMetricRecord.cost_usd and LlmCalibrationRecord.cost_usd still use
the legacy "_usd" suffix while their Field descriptions refer to the "configured
currency"; preserve the field names for API compatibility but update each Field
description to explicitly state this is a legacy "_usd" named field whose
numeric value is in the configured currency, and add a short note to the
migration documentation explaining the legacy naming tradeoff and where
consumers should map/rename the field; update descriptions on both symbols and
add the migration note referenced for lines 258-260.
In `@src/synthorg/hr/performance/summary.py`:
- Around line 74-78: The field name cost_per_task_usd is inconsistent with the
new "configured currency" description; update the model by adding a new alias
field cost_per_task that maps to the same value (same Field config:
default=None, ge=0.0, same description but referencing configured currency) and
mark cost_per_task_usd as a legacy/compatibility field by updating its
description to say "legacy name retained for API compatibility; value is in
configured currency" (or make cost_per_task_usd an alias to cost_per_task),
ensuring both names reference the same underlying value so consumers can use
either; also add a short note to the changelog/migration docs about the naming
discrepancy.
In `@src/synthorg/persistence/repositories.py`:
- Line 162: The repository docstring/contract text claiming "configured
currency" is inaccurate because aggregate() currently sums cost_usd records with
no conversion; update the wording to indicate the total is returned in the base
currency (e.g., "total cost in USD" or "total cost in base currency") so the
contract matches the implementation, and keep this aligned with the aggregate()
method and any repository-level conversion logic if added later.
In `@src/synthorg/providers/base.py`:
- Around line 339-342: The docstring for cost_per_1k_input and
cost_per_1k_output must explicitly state these rates are expressed in the
internal/base currency (not the user/display currency) because compute_cost()
populates TokenUsage.cost_usd in that same base currency; update the text around
cost_per_1k_input and cost_per_1k_output in the compute_cost() docstring (and
any related docstring for TokenUsage.cost_usd) to clearly say "per 1,000 tokens
in the internal/base currency (the currency used to populate
TokenUsage.cost_usd)" so callers understand the currency semantics.
In `@src/synthorg/providers/models.py`:
- Line 22: The docstring for TokenUsage.cost_usd incorrectly states “configured
currency” while the field is named cost_usd and there is no conversion logic;
either revert the description to state this is an estimated cost in USD (base
currency) or add an explicit new field (e.g., cost_display or
cost_in_configured_currency) plus conversion logic where TokenUsage objects are
constructed; update the docstring for TokenUsage.cost_usd (and the similar note
at the other occurrence) to match the chosen approach and reference the token
usage construction site/function so the conversion is implemented where tokens
are tallied.
In `@src/synthorg/providers/routing/models.py`:
- Around line 16-17: The model cost fields cost_per_1k_input and
cost_per_1k_output are defined without a fixed currency, but the routing logic
compares remaining_budget directly to their sum; fix by treating
cost_per_1k_input/cost_per_1k_output as base-currency amounts and convert any
display-currency remaining_budget into the same base currency before comparisons
(or store/require remaining_budget in base currency). Update every place that
does comparisons or arithmetic with remaining_budget and these cost fields
(where remaining_budget is compared to cost_per_1k_input + cost_per_1k_output)
to call the currency conversion helper (e.g., convert_to_base_currency(amount,
currency) or the existing exchanger utility) so the comparison is always done in
the base currency. Ensure constructor/serialization notes for the model document
that costs are base-currency values and add validation/tests for conversion in
the affected methods.
In `@src/synthorg/settings/definitions/budget.py`:
- Around line 136-149: The SettingDefinition for the budget currency currently
sets default="EUR", which changes registry default; update the SettingDefinition
(namespace=SettingNamespace.BUDGET, key="currency") to use default="USD" so
fresh installs and deployments without budget.currency override retain USD as
the registry default; keep the rest of the fields (description,
validator_pattern, yaml_path, group) unchanged.
In `@tests/unit/budget/test_config.py`:
- Around line 325-356: Consolidate the multiple rejection tests
(test_currency_lowercase_rejected, test_currency_too_short_rejected,
test_currency_too_long_rejected, test_currency_numeric_rejected,
test_currency_empty_rejected) into a single pytest.mark.parametrize test that
iterates invalid currency values and asserts that BudgetConfig(currency=value)
raises ValidationError; this reduces repetition while preserving each
invalid-case assertion.
In `@tests/unit/settings/test_resolver.py`:
- Around line 356-357: The test currently asserts the default currency as "EUR"
for the ("budget","currency") key (and the other occurrence noted) but Issue
`#810` requires the default be "USD"; update the assertions and any test fixtures
in test_resolver.py that set or expect ("budget","currency") to use "USD"
instead of "EUR" (including the other mentioned occurrence) and run the unit
tests to ensure no other tests rely on the old default.
In `@web/src/stores/settings.ts`:
- Around line 14-26: The store currently defaults currency to 'EUR' and swallows
fetch errors; change the initial currency value in useSettingsStore from 'EUR'
to 'USD' and update fetchCurrency to log failures and invalid values instead of
silently ignoring them: when calling settingsApi.getNamespaceSettings('budget')
handle the catch by logging the error (e.g., console.error or the project
logger) and do not change the store on failure, and when currencyEntry exists
but fails CURRENCY_PATTERN.log a warning indicating the invalid value; keep the
successful set({ currency: ... }) behavior in fetchCurrency so valid settings
still override the default.
In `@web/src/utils/format.ts`:
- Around line 39-41: The default currency for formatCurrency changed to 'EUR'
but must remain backward-compatible with 'USD'; restore the default parameter in
export function formatCurrency(value: number, currencyCode: string = 'USD') so
callers that omit currencyCode (or render before settings hydrate) continue to
show USD, and ensure any tests/uses relying on the old default still pass.
- Around line 43-51: The Intl.NumberFormat call in web/src/utils/format.ts is
forcing minimumFractionDigits and maximumFractionDigits which breaks ISO 4217
currency precision (e.g., JPY); remove the minimumFractionDigits and
maximumFractionDigits options from the Intl.NumberFormat instantiation so the
formatter uses each currency's native minor-unit rules, keep the try/catch and
fallback `${currencyCode} ${value.toFixed(2)}` as-is, and update any tests
(notably the one around line 113 expecting 4 decimals for 0.0001 USD) to reflect
the new default currency precision behavior.
---
Outside diff comments:
In `@src/synthorg/budget/optimizer.py`:
- Around line 632-636: The routing suggestion string in
synthorg.budget.optimizer.py uses a hardcoded "$" when formatting costs; update
this message to use the project's currency formatter (e.g., format_cost) instead
of "$". Replace the interpolations that use current_resolved.total_cost_per_1k
and candidate.total_cost_per_1k so they are passed through format_cost (keeping
the same precision/notation behavior) and build the message using most_used and
candidate.model_id as before; ensure the final string reads the same but without
any hardcoded USD symbol.
In `@src/synthorg/engine/parallel_models.py`:
- Around line 217-224: The property total_cost_usd aggregates
result.total_cost_usd but its description claims "configured currency," causing
a units mismatch; update the metadata/description for total_cost_usd to
explicitly state USD (e.g., "Total cost in USD across all agents") so the name
and description align, or alternatively rename the property to a
currency-agnostic identifier (e.g., total_cost) and change the aggregation to
use a currency-configured value (from each o.result) consistently; locate the
total_cost_usd property and adjust the description string or rename the property
and its usages accordingly.
In `@src/synthorg/engine/prompt.py`:
- Around line 140-165: Replace the hardcoded currency default "EUR" with the
repository constant DEFAULT_CURRENCY for this function's currency parameter and
for every other currency default in this file (including format_task_instruction
and the occurrences around lines referenced in the review). Update the function
signatures' default value from "EUR" to DEFAULT_CURRENCY, ensure
DEFAULT_CURRENCY is imported or referenced from its defining module, and run
tests/linters to confirm no unresolved names remain.
In `@src/synthorg/engine/run_result.py`:
- Around line 77-82: The property total_cost_usd currently returns
execution_result.context.accumulated_cost.cost_usd but its decorator description
says "Total cost in configured currency", causing a doc/contract mismatch;
update the decorator description to accurately state "Total cost in USD" (or
similar explicit USD wording) so the documented semantics match the actual
return value of total_cost_usd, leaving the property name and returned field
(execution_result.context.accumulated_cost.cost_usd) unchanged.
In `@web/src/__tests__/utils/format.property.test.ts`:
- Around line 5-25: Add a property-based test that verifies
formatCurrency(value) (call with no currencyCode) uses the default currency
(expected USD per Issue `#810`) by asserting the output contains '$'; locate and
modify the tests near the existing formatCurrency tests in
format.property.test.ts (the existing USD and EUR tests reference
formatCurrency) and add a new it(...) using fc.assert/fc.property with similar
double generation to assert formatCurrency(value) contains '$'.
🪄 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: b45bc671-9aff-4933-99f1-b202badd3c8a
📒 Files selected for processing (61)
src/synthorg/api/controllers/activities.pysrc/synthorg/api/controllers/agents.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/api/controllers/budget.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/api/dto.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/_optimizer_helpers.pysrc/synthorg/budget/config.pysrc/synthorg/budget/cost_record.pysrc/synthorg/budget/cost_tiers.pysrc/synthorg/budget/currency.pysrc/synthorg/budget/enforcer.pysrc/synthorg/budget/hierarchy.pysrc/synthorg/budget/optimizer.pysrc/synthorg/budget/optimizer_models.pysrc/synthorg/budget/quota.pysrc/synthorg/budget/tracker.pysrc/synthorg/budget/trends.pysrc/synthorg/communication/message.pysrc/synthorg/config/schema.pysrc/synthorg/core/company.pysrc/synthorg/core/project.pysrc/synthorg/core/role.pysrc/synthorg/core/task.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/engine/agent_state.pysrc/synthorg/engine/assignment/models.pysrc/synthorg/engine/coordination/models.pysrc/synthorg/engine/loop_protocol.pysrc/synthorg/engine/metrics.pysrc/synthorg/engine/parallel_models.pysrc/synthorg/engine/prompt.pysrc/synthorg/engine/prompt_template.pysrc/synthorg/engine/run_result.pysrc/synthorg/engine/task_engine_models.pysrc/synthorg/hr/activity.pysrc/synthorg/hr/models.pysrc/synthorg/hr/performance/ci_quality_strategy.pysrc/synthorg/hr/performance/models.pysrc/synthorg/hr/performance/summary.pysrc/synthorg/persistence/repositories.pysrc/synthorg/providers/base.pysrc/synthorg/providers/capabilities.pysrc/synthorg/providers/models.pysrc/synthorg/providers/routing/models.pysrc/synthorg/settings/definitions/budget.pysrc/synthorg/settings/resolver.pysrc/synthorg/templates/schema.pytests/unit/budget/test_config.pytests/unit/budget/test_currency.pytests/unit/budget/test_enforcer.pytests/unit/engine/test_run_result.pytests/unit/hr/test_activity.pytests/unit/settings/test_resolver.pyweb/src/__tests__/utils/format.property.test.tsweb/src/__tests__/utils/format.test.tsweb/src/api/types.tsweb/src/stores/settings.tsweb/src/utils/format.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Web
- GitHub Check: Build Sandbox
- GitHub Check: Build Backend
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (8)
src/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.py: Do NOT use 'from future import annotations' - Python 3.14 has PEP 649 native lazy annotations.
Use PEP 758 except syntax: use 'except A, B:' (no parentheses) - ruff enforces this on Python 3.14.
All public functions must have type hints. Code must pass mypy in strict mode.
Docstrings must use Google style format and are required on all public classes and functions - enforced by ruff D rules.
Create new objects for immutability - never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping for read-only enforcement. For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves (e.g. agent execution state, task progress). Never mix static config fields with mutable runtime fields in one model.
Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use@computed_fieldfor derived values instead of storing + validating redundant fields (e.g. TokenUsage.total_tokens); use NotBlankStr (from core.types) for all identifier/name fields - including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants - instead of manual whitespace validators.
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Maximum line length is 88 characters - enforced by ruff.
Functions must be less than 50 lines; files must be less than 800 lines.
Always handle errors explicitly - never silently swallow exceptions.
Validate input at system boundaries (user input, external APIs, config files).
Every module...
Files:
src/synthorg/budget/hierarchy.pysrc/synthorg/api/controllers/agents.pysrc/synthorg/core/company.pysrc/synthorg/budget/cost_record.pysrc/synthorg/budget/trends.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/persistence/repositories.pysrc/synthorg/engine/task_engine_models.pysrc/synthorg/hr/performance/ci_quality_strategy.pysrc/synthorg/settings/resolver.pysrc/synthorg/providers/base.pysrc/synthorg/engine/prompt_template.pysrc/synthorg/engine/parallel_models.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/core/role.pysrc/synthorg/hr/performance/models.pysrc/synthorg/engine/loop_protocol.pysrc/synthorg/core/task.pysrc/synthorg/engine/coordination/models.pysrc/synthorg/budget/enforcer.pysrc/synthorg/providers/models.pysrc/synthorg/api/dto.pysrc/synthorg/config/schema.pysrc/synthorg/engine/agent_state.pysrc/synthorg/providers/capabilities.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/cost_tiers.pysrc/synthorg/providers/routing/models.pysrc/synthorg/budget/optimizer_models.pysrc/synthorg/budget/quota.pysrc/synthorg/hr/models.pysrc/synthorg/templates/schema.pysrc/synthorg/communication/message.pysrc/synthorg/engine/run_result.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/core/project.pysrc/synthorg/engine/assignment/models.pysrc/synthorg/hr/performance/summary.pysrc/synthorg/budget/optimizer.pysrc/synthorg/engine/metrics.pysrc/synthorg/budget/_optimizer_helpers.pysrc/synthorg/budget/tracker.pysrc/synthorg/engine/prompt.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/hr/activity.pysrc/synthorg/budget/config.pysrc/synthorg/settings/definitions/budget.pysrc/synthorg/budget/currency.pysrc/synthorg/api/controllers/budget.py
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
NEVER use 'cd' in Bash commands - the working directory is already set to the project root. Use absolute paths or run commands directly. Do NOT prefix commands with 'cd C:/Users/Aurelio/synthorg &&'.
Files:
src/synthorg/budget/hierarchy.pysrc/synthorg/api/controllers/agents.pysrc/synthorg/core/company.pysrc/synthorg/budget/cost_record.pysrc/synthorg/budget/trends.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/persistence/repositories.pysrc/synthorg/engine/task_engine_models.pysrc/synthorg/hr/performance/ci_quality_strategy.pysrc/synthorg/settings/resolver.pysrc/synthorg/providers/base.pysrc/synthorg/engine/prompt_template.pyweb/src/__tests__/utils/format.property.test.tssrc/synthorg/engine/parallel_models.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/core/role.pysrc/synthorg/hr/performance/models.pysrc/synthorg/engine/loop_protocol.pysrc/synthorg/core/task.pysrc/synthorg/engine/coordination/models.pysrc/synthorg/budget/enforcer.pyweb/src/utils/format.tssrc/synthorg/providers/models.pysrc/synthorg/api/dto.pysrc/synthorg/config/schema.pysrc/synthorg/engine/agent_state.pyweb/src/__tests__/utils/format.test.tssrc/synthorg/providers/capabilities.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/cost_tiers.pysrc/synthorg/providers/routing/models.pytests/unit/budget/test_config.pysrc/synthorg/budget/optimizer_models.pysrc/synthorg/budget/quota.pysrc/synthorg/hr/models.pysrc/synthorg/templates/schema.pysrc/synthorg/communication/message.pysrc/synthorg/engine/run_result.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/core/project.pysrc/synthorg/engine/assignment/models.pytests/unit/budget/test_enforcer.pytests/unit/engine/test_run_result.pysrc/synthorg/hr/performance/summary.pysrc/synthorg/budget/optimizer.pysrc/synthorg/engine/metrics.pyweb/src/api/types.tstests/unit/hr/test_activity.pysrc/synthorg/budget/_optimizer_helpers.pysrc/synthorg/budget/tracker.pysrc/synthorg/engine/prompt.pytests/unit/settings/test_resolver.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/hr/activity.pyweb/src/stores/settings.tssrc/synthorg/budget/config.pytests/unit/budget/test_currency.pysrc/synthorg/settings/definitions/budget.pysrc/synthorg/budget/currency.pysrc/synthorg/api/controllers/budget.py
src/synthorg/providers/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/providers/**/*.py: All provider calls go through BaseCompletionProvider which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code - it's handled by the base class.
RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig. Retryable errors (is_retryable=True): RateLimitError, ProviderTimeoutError, ProviderConnectionError, ProviderInternalError. Non-retryable errors raise immediately without retry. RetryExhaustedError signals that all retries failed - the engine layer catches this to trigger fallback chains. Rate limiter respects RateLimitError.retry_after from providers.
Files:
src/synthorg/providers/base.pysrc/synthorg/providers/models.pysrc/synthorg/providers/capabilities.pysrc/synthorg/providers/routing/models.py
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx}: ALWAYS reuse existing components from web/src/components/ui/ before creating new ones: StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button.
Use Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)). NEVER hardcode hex values in .tsx/.ts files.
Use font-sans or font-mono (maps to Geist tokens). NEVER set fontFamily directly.
Use density-aware tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacing.
Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders.
Do NOT recreate status dots inline - use StatusBadge. Do NOT build card-with-header layouts from scratch - use SectionCard. Do NOT create metric displays with 'text-metric font-bold' - use MetricCard. Do NOT render initials circles manually - use Avatar. Do NOT create complex (>8 line) JSX inside .map() - extract to a shared component. Do NOT use rgba() with hardcoded values - use design token variables.
A PostToolUse hook (scripts/check_web_design_system.py) runs automatically on every Edit/Write to web/src/ files. It catches hardcoded hex colors, rgba values, fontFamily declarations, new components without Storybook stories, duplicate patterns, and complex .map() blocks. Fix all violations before proceeding - do not suppress or ignore hook output.
React 19 is required. Use React hooks patterns and functional components throughout.
Files:
web/src/__tests__/utils/format.property.test.tsweb/src/utils/format.tsweb/src/__tests__/utils/format.test.tsweb/src/api/types.tsweb/src/stores/settings.ts
web/src/__tests__/**/*.test.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use Vitest for unit and property tests. Mirror src/ directory structure. Use fast-check for property-based testing (fc.assert + fc.property).
Files:
web/src/__tests__/utils/format.property.test.tsweb/src/__tests__/utils/format.test.ts
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Mark all tests with@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, or@pytest.mark.slowmarkers.
Maintain 80% minimum code coverage - enforced in CI.
asyncio_mode = "auto" is configured - no manual@pytest.mark.asyncioneeded. Global timeout is 30 seconds per test in pyproject.toml - do not add per-file pytest.mark.timeout(30) markers; non-default overrides like timeout(60) are allowed.
Always run pytest with '-n auto' for parallelism via pytest-xdist - never run tests sequentially.
Prefer@pytest.mark.parametrizefor testing similar cases.
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/small as aliases. Tests must use test-provider, test-small-001, etc.
For timing-sensitive tests, mock time.monotonic() and asyncio.sleep() to make them deterministic instead of widening timing margins. For tasks that must block indefinitely until cancelled, use asyncio.Event().wait() instead of asyncio.sleep(large_number) - it is cancellation-safe and carries no timing assumptions. NEVER skip, dismiss, or ignore flaky tests - always fix them fully and fundamentally.
Use Hypothesis for property-based testing with@given+@settings. Hypothesis profiles: ci (50 examples, default) and dev (1000 examples), controlled via HYPOTHESIS_PROFILE env var.
Files:
tests/unit/budget/test_config.pytests/unit/budget/test_enforcer.pytests/unit/engine/test_run_result.pytests/unit/hr/test_activity.pytests/unit/settings/test_resolver.pytests/unit/budget/test_currency.py
web/src/api/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
API endpoints should be organized into domain-specific modules (18 domains total) within web/src/api/.
Files:
web/src/api/types.ts
web/src/stores/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use Zustand for global state management. Separate stores by domain (auth, WebSocket, domain shells).
Files:
web/src/stores/settings.ts
🧠 Learnings (15)
📚 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/hierarchy.pysrc/synthorg/api/controllers/agents.pysrc/synthorg/core/company.pysrc/synthorg/budget/cost_record.pysrc/synthorg/budget/trends.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/engine/task_engine_models.pysrc/synthorg/settings/resolver.pysrc/synthorg/engine/prompt_template.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/core/role.pysrc/synthorg/core/task.pysrc/synthorg/budget/enforcer.pysrc/synthorg/api/dto.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/cost_tiers.pysrc/synthorg/budget/optimizer_models.pysrc/synthorg/budget/quota.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/core/project.pytests/unit/budget/test_enforcer.pytests/unit/engine/test_run_result.pysrc/synthorg/budget/optimizer.pysrc/synthorg/budget/_optimizer_helpers.pysrc/synthorg/budget/tracker.pysrc/synthorg/engine/prompt.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/hr/activity.pysrc/synthorg/budget/config.pytests/unit/budget/test_currency.pysrc/synthorg/settings/definitions/budget.pysrc/synthorg/budget/currency.pysrc/synthorg/api/controllers/budget.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/hierarchy.pysrc/synthorg/api/controllers/agents.pysrc/synthorg/core/company.pysrc/synthorg/budget/trends.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/engine/task_engine_models.pysrc/synthorg/settings/resolver.pysrc/synthorg/engine/prompt_template.pysrc/synthorg/core/task.pysrc/synthorg/budget/enforcer.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/cost_tiers.pysrc/synthorg/budget/optimizer_models.pysrc/synthorg/budget/quota.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/optimizer.pysrc/synthorg/budget/_optimizer_helpers.pysrc/synthorg/budget/tracker.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/hr/activity.pysrc/synthorg/budget/config.pysrc/synthorg/settings/definitions/budget.pysrc/synthorg/budget/currency.pysrc/synthorg/api/controllers/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/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:
src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/__tests__/**/*.test.ts : Use Vitest for unit and property tests. Mirror src/ directory structure. Use fast-check for property-based testing (fc.assert + fc.property).
Applied to files:
web/src/__tests__/utils/format.property.test.tsweb/src/__tests__/utils/format.test.ts
📚 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 web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).
Applied to files:
web/src/__tests__/utils/format.property.test.ts
📚 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.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/quota.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/optimizer.pyweb/src/api/types.tssrc/synthorg/api/controllers/budget.py
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/**/*.test.tsx : Run Vitest with coverage scoped to files changed vs origin/main.
Applied to files:
web/src/__tests__/utils/format.test.ts
📚 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/engine/prompt.pysrc/synthorg/hr/activity.py
📚 Learning: 2026-03-15T18:42:17.990Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:42:17.990Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`
Applied to files:
src/synthorg/engine/prompt.pysrc/synthorg/hr/activity.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/hr/activity.py
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to src/**/*.py : Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use computed_field for derived values instead of storing + validating redundant fields (e.g. TokenUsage.total_tokens); 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/hr/activity.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/hr/activity.py
📚 Learning: 2026-03-26T19:11:08.600Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T19:11:08.600Z
Learning: Applies to web/src/stores/**/*.ts : Use Zustand for global state management. Separate stores by domain (auth, WebSocket, domain shells).
Applied to files:
web/src/stores/settings.ts
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)
Applied to files:
src/synthorg/settings/definitions/budget.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings use runtime-editable persistence with precedence: DB > env > YAML > code defaults. 8 namespaces with Fernet encryption for sensitive values.
Applied to files:
src/synthorg/settings/definitions/budget.py
723f994 to
a26c543
Compare
There was a problem hiding this comment.
Actionable comments posted: 14
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/synthorg/engine/task_engine_models.py (1)
61-90:⚠️ Potential issue | 🟡 MinorRestore
budget_limitdocstring to indicate base currency, not configured currency.The
budget_limitfield is directly compared againstctx.accumulated_cost.cost_usdinloop_protocol.py:212andenforcer.pybudget logic. The docstring stating "Maximum spend in configured currency" is misleading since the comparison is against base currency (USD). Change the description to "Maximum spend in base currency" or similar.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/engine/task_engine_models.py` around lines 61 - 90, The Task model's budget_limit Field description incorrectly says "configured currency" but is compared against ctx.accumulated_cost.cost_usd and enforcer budget checks in the runtime, so update the Field description for budget_limit in task_engine_models.Task to indicate base currency (e.g., "Maximum spend in base currency (USD)") so documentation matches the USD comparisons in loop and enforcer logic.src/synthorg/core/company.py (1)
464-484:⚠️ Potential issue | 🟡 MinorFix docstring for
budget_monthlyto reflect base currency storage.The docstring states "in configured currency" but the code actually stores and compares this value in base currency (USD). In
tracker.pyline 425,total_cost / budget_monthly * 100compares USD costs directly against the budget without any currency conversion. Per the PR objectives stated inbudget/currency.py, "internal cost storage remains in a single base currency"—this field should be documented as such, not as configured currency.The
default=100.0is appropriate for USD but not for other currencies, which confirms the field is meant to store base currency values.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/core/company.py` around lines 464 - 484, Update the budget_monthly Field docstring to state that the value is stored and compared in the system base currency (USD) rather than the configured currency; specifically change the description on the budget_monthly Field (the Field with default=100.0, ge=0.0) to mention "base currency (USD)" so it matches how costs are calculated (see tracker calculation using total_cost / budget_monthly) and the project policy in budget/currency.py; leave the default value as-is.src/synthorg/api/controllers/analytics.py (1)
594-602:⚠️ Potential issue | 🟠 Major
ForecastResponse.currencyis not populated; will always return"USD"regardless of configured currency.The
/analytics/overviewendpoint correctly setscurrency=budget_cfg.currency, but/analytics/forecastdoes not populate thecurrencyfield inForecastResponse. This causes inconsistent currency display between the two endpoints when a non-USD currency is configured.🐛 Proposed fix
+ budget_cfg = await app_state.config_resolver.get_budget_config() + return ApiResponse( data=ForecastResponse( horizon_days=horizon_days, projected_total_usd=forecast.projected_total_usd, daily_projections=forecast.daily_projections, days_until_exhausted=forecast.days_until_exhausted, confidence=forecast.confidence, avg_daily_spend_usd=forecast.avg_daily_spend_usd, + currency=budget_cfg.currency, ), )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/controllers/analytics.py` around lines 594 - 602, ForecastResponse.currency is never set in the /analytics/forecast response so it always appears as USD; update the code that constructs ForecastResponse (the ForecastResponse(...) call in this file) to populate the currency field from the configured budget currency (the same source used by /analytics/overview, e.g. budget_cfg.currency or the variable holding the configured currency) so the returned ForecastResponse.currency matches the configured currency.
♻️ Duplicate comments (2)
src/synthorg/hr/performance/summary.py (1)
39-40:⚠️ Potential issue | 🟡 MinorAlign field description with
_usdbase-currency semantics.Line 39 documents
cost_per_task_usdas USD/base currency, but Line 77 removes the unit from the field description. Keep both explicit and consistent to avoid schema confusion.Suggested doc fix
- description=("Average cost per task (30d window, falling back to 7d)"), + description=( + "Average cost per task in USD (base currency) " + "(30d window, falling back to 7d)" + ),Also applies to: 77-77
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/hr/performance/summary.py` around lines 39 - 40, The docstring/field description inconsistency for the field cost_per_task_usd must be fixed: update both occurrences so they explicitly state the unit and that values are in USD (base currency) and the window fallback (30d, falling back to 7d) to match the `_usd` naming semantics; locate the field description for cost_per_task_usd in the summary dataclass/module and make both the earlier description (currently mentioning USD) and the later one (currently missing unit) identical and explicit about "USD (base currency)" and the 30d/7d window.src/synthorg/providers/routing/models.py (1)
16-17:⚠️ Potential issue | 🟠 MajorDocument routing cost/budget fields as base-currency values.
At Line 16-17 and Line 70-74, these values are used in direct numeric comparisons (
remaining_budgetvscost_per_1k_input + cost_per_1k_output). Leaving units unspecified risks mixed-unit comparisons and incorrect model filtering.Suggested doc fix
- cost_per_1k_input: Cost per 1 000 input tokens. - cost_per_1k_output: Cost per 1 000 output tokens. + cost_per_1k_input: Cost per 1 000 input tokens in USD (base currency). + cost_per_1k_output: Cost per 1 000 output tokens in USD (base currency). - remaining_budget: Per-request cost ceiling. Compared against + remaining_budget: Per-request cost ceiling in USD (base currency). + Compared against each model's ``total_cost_per_1k`` (i.e. ``cost_per_1k_input + cost_per_1k_output``) to filter models that exceed this threshold. This is **not** a total session budget -- use the budget module for that. - "Per-request cost ceiling (compared against model " + "Per-request cost ceiling in USD (base currency), compared against model " "total_cost_per_1k). Not a total session budget."Also applies to: 30-30, 35-35, 70-74, 95-97
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/providers/routing/models.py` around lines 16 - 17, Docstring and comparison ambiguity: cost_per_1k_input and cost_per_1k_output must be documented and used as base-currency amounts per 1,000 tokens so they are not mixed with remaining_budget. Update the field docs for cost_per_1k_input and cost_per_1k_output to explicitly state "amount in base currency per 1,000 tokens", and change any numeric comparisons that use remaining_budget (e.g., the logic that checks remaining_budget vs cost_per_1k_input + cost_per_1k_output) to convert token-based costs into base-currency totals before comparing—compute actual_cost = (tokens_needed / 1000) * (cost_per_1k_input + cost_per_1k_output) or normalize remaining_budget into a per-1k-token equivalent, then compare using the normalized values; ensure variables referenced include cost_per_1k_input, cost_per_1k_output, and remaining_budget so the change applies everywhere those names are used.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/design/engine.md`:
- Line 96: The phrase "in configured currency" on the budget_limit field is
misleading; update the description for budget_limit to state that the value is
stored as a base-currency numeric limit (no runtime currency conversion) and is
only rendered using the configured currency formatting for display; explicitly
mention budget_limit remains in base currency and that any displayed
symbol/formatting is presentation-only so readers know there's no semantic
conversion.
In `@src/synthorg/api/controllers/activities.py`:
- Around line 142-150: Wrap the call to
app_state.config_resolver.get_budget_config() in a try/except to mirror
agents.py: on exception log a warning (include the exception) and fall back to a
sensible default budget config before calling merge_activity_timeline; update
the variable budget_cfg accordingly so merge_activity_timeline(...) still
receives a valid currency and other fields even if config resolution fails.
In `@src/synthorg/api/controllers/budget.py`:
- Line 24: The controller mixes the DEFAULT_CURRENCY constant with hardcoded
"USD" in response DTO defaults, causing divergent API contracts; update all
places in this file (e.g. _build_summaries(), any response DTO default values)
to use the single shared constant synthorg.budget.currency.DEFAULT_CURRENCY
instead of literal "USD", or alternatively make the DTOs require currency so
missing values fail fast—replace every hardcoded "USD" occurrence in this module
(including the sections noted around lines ~50-55, ~77-82, ~112-117, ~162-167,
~188-191) with DEFAULT_CURRENCY and ensure _build_summaries() and the response
model definitions reference that single symbol.
- Around line 40-56: The API is exposing a misleading payload where
total_cost_usd remains in USD while the currency field can be a different
configured value; update the model fields (e.g., the Pydantic Field named
currency and any similar fields that pair with total_cost_usd/cost_usd) to make
the public contract explicit by renaming currency to display_currency (or
similar) and adjusting its Field description to state "display-only; no
conversion performed" (also update any docstrings mentioning "in configured
currency"), and propagate the renaming/description change to all corresponding
usages (places noted around the other occurrences) so clients cannot assume an
amount conversion is implied.
In `@src/synthorg/api/controllers/departments.py`:
- Around line 63-69: DepartmentHealth currently hardcodes the currency default
as "USD"; change it to reuse the existing DEFAULT_CURRENCY constant to avoid
duplicate defaults. Update the Field for the currency attribute in the
DepartmentHealth model to set default=DEFAULT_CURRENCY (and add/import
DEFAULT_CURRENCY at top of the module if not already present) so the model and
helper functions share the same source of truth.
In `@src/synthorg/api/dto.py`:
- Around line 478-484: Several instantiations of CoordinationResultResponse are
relying on the Field default "USD" and must be made explicit; find every new
CoordinationResultResponse(...) call (including the ones in the controller and
unit tests) and add an explicit currency argument (e.g.
currency=runtime_currency or currency=settings.DEFAULT_CURRENCY) so the
runtime/configured currency is passed rather than silently using the model
default; update the five missing call sites and adjust test expectations in
test_dto to assert the explicit currency value.
In `@src/synthorg/budget/cost_tiers.py`:
- Around line 50-56: The Field descriptions for price_range_min and
price_range_max are ambiguous about currency units; update their descriptions to
explicitly state these values are in the system's base currency (not a
displayed/converted currency), e.g. reference price_range_min and
price_range_max and mention "base currency" or "base-currency units" and clarify
that None for price_range_max = unbounded; keep the numeric constraints (ge=0.0)
unchanged and ensure wording is consistent between both Field descriptions.
In `@src/synthorg/budget/currency.py`:
- Around line 12-15: Import and use the module-level structured logger and the
budget observability event constant, then log validation failures at WARNING
with context before raising: add e.g. "from synthorg.logging import logger" (or
your project's logger import) and "from synthorg.budget.observability.events
import BUDGET_INVALID_COST_FORMAT" at the top of the module, and inside
format_cost (and the other validation block around lines 135-140) replace direct
raises with a logger.warning call that includes the event constant and
contextual fields (e.g. value, value_type, formatter) and then re-raise the same
exception; ensure the log message uses structured kwargs
(event=BUDGET_INVALID_COST_FORMAT, cost=value, reason='invalid_formatter' or
similar) so every validation error is logged before raising.
In `@src/synthorg/budget/tracker.py`:
- Line 136: The docstrings saying "Rounded total cost in configured currency"
are incorrect because the methods return aggregated cost_usd without conversion;
update the two docstrings (the one containing the string "Rounded total cost in
configured currency." and the other docstring at the second occurrence) to state
that the returned units are the base currency (cost_usd / USD) and not the
configured display currency, and optionally add a note that conversion is not
performed in this class so callers must convert if needed.
In `@src/synthorg/config/schema.py`:
- Around line 63-72: The descriptions for the configuration fields
cost_per_1k_input and cost_per_1k_output are ambiguous about currency; update
their Field description strings in the schema (cost_per_1k_input and
cost_per_1k_output in src/synthorg/config/schema.py) to explicitly state the
expected currency (e.g., "Cost per 1k input tokens (USD)" and "Cost per 1k
output tokens (USD)") so they clearly match the downstream result.total_cost_usd
base-currency behavior, or alternatively indicate they follow budget.currency if
you choose that design—pick one policy and make the wording explicit for both
fields.
In `@src/synthorg/hr/performance/models.py`:
- Line 63: The Field descriptions for the cost fields are missing the "in USD
(base currency)" clarification; update the cost_usd Field description in
TaskMetricRecord and LlmCalibrationRecord to match their class docstrings (e.g.,
change description="Cost of the task" / "Cost of the LLM call" to include "in
USD (base currency)") so the OpenAPI/JSON schema is authoritative and consistent
with the docstrings; locate the cost_usd Field declarations in TaskMetricRecord
and LlmCalibrationRecord and modify their description text accordingly.
In `@src/synthorg/providers/capabilities.py`:
- Around line 26-27: The docstrings for the cost fields removed the currency
unit and must be restored: update the documentation for cost_per_1k_input and
cost_per_1k_output (and the additional occurrences at the other two locations)
to explicitly state the base currency (e.g., "in USD" or the project's
configured base currency) and clarify "per 1,000 tokens"; locate the fields by
the identifiers cost_per_1k_input and cost_per_1k_output in
src/synthorg/providers/capabilities.py and change their descriptions to include
the base-currency wording so downstream routing/budget arithmetic is
unambiguous.
In `@web/src/__tests__/utils/format.test.ts`:
- Around line 100-104: The JPY test for formatCurrency currently only checks for
the yen sign and thousands separator, so it would still pass if fractional
digits are present; update the 'formats JPY values' test (the one calling
formatCurrency(1000, 'JPY')) to assert that the formatted string omits
fractional digits by ensuring it does not contain a decimal point or any
fractional portion (for example assert it does not contain '.' or '.00', or use
a regex to match exactly no decimal fraction), so the test fails if
formatCurrency regresses to producing "¥1,000.00".
In `@web/src/utils/format.ts`:
- Around line 43-50: The fallback currently always uses value.toFixed(2) and
thus ignores zero-decimal currencies; update the catch path to use the same
minor-unit logic as the normal formatting code instead of hardcoding 2 decimals:
compute the number of fraction digits for currencyCode (reuse the existing
minor-units lookup or helper used elsewhere in this file, e.g.,
CURRENCY_MINOR_UNITS or getCurrencyMinorUnits) and call
value.toFixed(minorUnits) in the return so JPY/KRW fall back to no decimals.
Ensure you reference currencyCode and value in the catch block and keep the
error log as-is.
---
Outside diff comments:
In `@src/synthorg/api/controllers/analytics.py`:
- Around line 594-602: ForecastResponse.currency is never set in the
/analytics/forecast response so it always appears as USD; update the code that
constructs ForecastResponse (the ForecastResponse(...) call in this file) to
populate the currency field from the configured budget currency (the same source
used by /analytics/overview, e.g. budget_cfg.currency or the variable holding
the configured currency) so the returned ForecastResponse.currency matches the
configured currency.
In `@src/synthorg/core/company.py`:
- Around line 464-484: Update the budget_monthly Field docstring to state that
the value is stored and compared in the system base currency (USD) rather than
the configured currency; specifically change the description on the
budget_monthly Field (the Field with default=100.0, ge=0.0) to mention "base
currency (USD)" so it matches how costs are calculated (see tracker calculation
using total_cost / budget_monthly) and the project policy in budget/currency.py;
leave the default value as-is.
In `@src/synthorg/engine/task_engine_models.py`:
- Around line 61-90: The Task model's budget_limit Field description incorrectly
says "configured currency" but is compared against ctx.accumulated_cost.cost_usd
and enforcer budget checks in the runtime, so update the Field description for
budget_limit in task_engine_models.Task to indicate base currency (e.g.,
"Maximum spend in base currency (USD)") so documentation matches the USD
comparisons in loop and enforcer logic.
---
Duplicate comments:
In `@src/synthorg/hr/performance/summary.py`:
- Around line 39-40: The docstring/field description inconsistency for the field
cost_per_task_usd must be fixed: update both occurrences so they explicitly
state the unit and that values are in USD (base currency) and the window
fallback (30d, falling back to 7d) to match the `_usd` naming semantics; locate
the field description for cost_per_task_usd in the summary dataclass/module and
make both the earlier description (currently mentioning USD) and the later one
(currently missing unit) identical and explicit about "USD (base currency)" and
the 30d/7d window.
In `@src/synthorg/providers/routing/models.py`:
- Around line 16-17: Docstring and comparison ambiguity: cost_per_1k_input and
cost_per_1k_output must be documented and used as base-currency amounts per
1,000 tokens so they are not mixed with remaining_budget. Update the field docs
for cost_per_1k_input and cost_per_1k_output to explicitly state "amount in base
currency per 1,000 tokens", and change any numeric comparisons that use
remaining_budget (e.g., the logic that checks remaining_budget vs
cost_per_1k_input + cost_per_1k_output) to convert token-based costs into
base-currency totals before comparing—compute actual_cost = (tokens_needed /
1000) * (cost_per_1k_input + cost_per_1k_output) or normalize remaining_budget
into a per-1k-token equivalent, then compare using the normalized values; ensure
variables referenced include cost_per_1k_input, cost_per_1k_output, and
remaining_budget so the change applies everywhere those names are used.
🪄 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: 10dc761f-02b0-4af1-ac34-c7dcde114be6
📒 Files selected for processing (67)
CLAUDE.mddocs/design/engine.mddocs/design/operations.mdsrc/synthorg/api/controllers/activities.pysrc/synthorg/api/controllers/agents.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/api/controllers/budget.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/api/dto.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/budget/_optimizer_helpers.pysrc/synthorg/budget/config.pysrc/synthorg/budget/cost_record.pysrc/synthorg/budget/cost_tiers.pysrc/synthorg/budget/currency.pysrc/synthorg/budget/enforcer.pysrc/synthorg/budget/hierarchy.pysrc/synthorg/budget/optimizer.pysrc/synthorg/budget/optimizer_models.pysrc/synthorg/budget/quota.pysrc/synthorg/budget/tracker.pysrc/synthorg/budget/trends.pysrc/synthorg/communication/message.pysrc/synthorg/config/schema.pysrc/synthorg/core/company.pysrc/synthorg/core/project.pysrc/synthorg/core/role.pysrc/synthorg/core/task.pysrc/synthorg/engine/_prompt_helpers.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/engine/agent_state.pysrc/synthorg/engine/assignment/models.pysrc/synthorg/engine/coordination/models.pysrc/synthorg/engine/loop_protocol.pysrc/synthorg/engine/metrics.pysrc/synthorg/engine/parallel_models.pysrc/synthorg/engine/prompt.pysrc/synthorg/engine/prompt_template.pysrc/synthorg/engine/run_result.pysrc/synthorg/engine/task_engine_models.pysrc/synthorg/hr/activity.pysrc/synthorg/hr/models.pysrc/synthorg/hr/performance/ci_quality_strategy.pysrc/synthorg/hr/performance/models.pysrc/synthorg/hr/performance/summary.pysrc/synthorg/persistence/repositories.pysrc/synthorg/providers/base.pysrc/synthorg/providers/capabilities.pysrc/synthorg/providers/models.pysrc/synthorg/providers/routing/models.pysrc/synthorg/settings/definitions/budget.pysrc/synthorg/settings/resolver.pysrc/synthorg/templates/schema.pytests/unit/budget/test_config.pytests/unit/budget/test_currency.pytests/unit/budget/test_enforcer.pytests/unit/engine/test_run_result.pytests/unit/hr/test_activity.pytests/unit/settings/test_resolver.pyweb/src/__tests__/utils/format.property.test.tsweb/src/__tests__/utils/format.test.tsweb/src/api/endpoints/budget.tsweb/src/api/types.tsweb/src/stores/settings.tsweb/src/utils/format.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Build Sandbox
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotations-- Python 3.14 has PEP 649 native lazy annotations
Use PEP 758 except syntax:except A, B:(no parentheses) instead ofexcept (A, B):-- ruff enforces this on Python 3.14
All public functions and classes must have type hints; mypy strict mode is enforced
Use Google-style docstrings on all public classes and functions; enforced by ruff D rules
Create new objects instead of mutating existing ones; for non-Pydantic internal collections usecopy.deepcopy()at construction andMappingProxyTypewrapping for read-only enforcement
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls) over barecreate_taskfor structured concurrency
Keep functions under 50 lines and files under 800 lines
Line length must be 88 characters as enforced by ruff
Validate explicitly at system boundaries (user input, external APIs, config files); never silently swallow errors
Files:
src/synthorg/budget/trends.pytests/unit/settings/test_resolver.pysrc/synthorg/settings/resolver.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/persistence/repositories.pysrc/synthorg/engine/run_result.pysrc/synthorg/hr/performance/ci_quality_strategy.pysrc/synthorg/providers/base.pysrc/synthorg/budget/hierarchy.pysrc/synthorg/budget/tracker.pysrc/synthorg/engine/parallel_models.pysrc/synthorg/budget/cost_record.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/engine/task_engine_models.pysrc/synthorg/providers/models.pysrc/synthorg/engine/agent_state.pysrc/synthorg/templates/schema.pysrc/synthorg/budget/optimizer_models.pysrc/synthorg/core/role.pysrc/synthorg/budget/quota.pysrc/synthorg/core/company.pysrc/synthorg/engine/assignment/models.pysrc/synthorg/api/controllers/agents.pytests/unit/budget/test_config.pysrc/synthorg/config/schema.pysrc/synthorg/budget/config.pytests/unit/hr/test_activity.pysrc/synthorg/providers/routing/models.pysrc/synthorg/engine/coordination/models.pysrc/synthorg/hr/models.pysrc/synthorg/hr/performance/models.pytests/unit/engine/test_run_result.pysrc/synthorg/core/project.pysrc/synthorg/engine/metrics.pysrc/synthorg/hr/activity.pysrc/synthorg/budget/optimizer.pysrc/synthorg/engine/loop_protocol.pysrc/synthorg/budget/cost_tiers.pysrc/synthorg/providers/capabilities.pysrc/synthorg/core/task.pytests/unit/budget/test_enforcer.pysrc/synthorg/communication/message.pysrc/synthorg/api/dto.pysrc/synthorg/engine/prompt_template.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/budget/enforcer.pysrc/synthorg/hr/performance/summary.pysrc/synthorg/budget/_optimizer_helpers.pysrc/synthorg/engine/prompt.pysrc/synthorg/engine/_prompt_helpers.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/currency.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/settings/definitions/budget.pytests/unit/budget/test_currency.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/api/controllers/budget.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic must import and use logger:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)
Never useimport logging,logging.getLogger(), orprint()in application code; only use the structured logger fromsynthorg.observability
Always use the logger variable namelogger(not_logger, notlog)
Use event name constants fromsynthorg.observability.events.<domain>modules (e.g.,API_REQUEST_STARTEDfromevents.api); import directly and always use structured kwargs:logger.info(EVENT, key=value)never string formatting
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
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (withmodel_copy(update=...)) for runtime state that evolves
Use Pydantic v2 with@computed_fieldfor derived values instead of storing redundant fields; useNotBlankStr(fromcore.types) for all identifier/name fields instead of manual validators
All provider calls go throughBaseCompletionProviderwhich applies retry + rate limiting automatically; never implement retry logic in driver subclasses or calling code
Validate at system boundaries: user input, external APIs, config files; handle errors explicitly, never silently swallow
Files:
src/synthorg/budget/trends.pysrc/synthorg/settings/resolver.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/persistence/repositories.pysrc/synthorg/engine/run_result.pysrc/synthorg/hr/performance/ci_quality_strategy.pysrc/synthorg/providers/base.pysrc/synthorg/budget/hierarchy.pysrc/synthorg/budget/tracker.pysrc/synthorg/engine/parallel_models.pysrc/synthorg/budget/cost_record.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/engine/task_engine_models.pysrc/synthorg/providers/models.pysrc/synthorg/engine/agent_state.pysrc/synthorg/templates/schema.pysrc/synthorg/budget/optimizer_models.pysrc/synthorg/core/role.pysrc/synthorg/budget/quota.pysrc/synthorg/core/company.pysrc/synthorg/engine/assignment/models.pysrc/synthorg/api/controllers/agents.pysrc/synthorg/config/schema.pysrc/synthorg/budget/config.pysrc/synthorg/providers/routing/models.pysrc/synthorg/engine/coordination/models.pysrc/synthorg/hr/models.pysrc/synthorg/hr/performance/models.pysrc/synthorg/core/project.pysrc/synthorg/engine/metrics.pysrc/synthorg/hr/activity.pysrc/synthorg/budget/optimizer.pysrc/synthorg/engine/loop_protocol.pysrc/synthorg/budget/cost_tiers.pysrc/synthorg/providers/capabilities.pysrc/synthorg/core/task.pysrc/synthorg/communication/message.pysrc/synthorg/api/dto.pysrc/synthorg/engine/prompt_template.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/budget/enforcer.pysrc/synthorg/hr/performance/summary.pysrc/synthorg/budget/_optimizer_helpers.pysrc/synthorg/engine/prompt.pysrc/synthorg/engine/_prompt_helpers.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/currency.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/settings/definitions/budget.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/api/controllers/budget.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, or@pytest.mark.slowto categorize tests
Maintain 80% minimum code coverage as enforced in CI
Useasyncio_mode = 'auto'for async tests; no manual@pytest.mark.asyncioneeded
Use@pytest.mark.parametrizefor testing similar cases instead of test duplication
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, tests, or config examples; use generic names:example-provider,example-large-001,test-provider,test-small-001,large/medium/small
Use Hypothesis with@given+@settingsfor property-based testing; avoid flaky timing-based tests by mockingtime.monotonic()andasyncio.sleep()or usingasyncio.Event().wait()for indefinite blocks
Files:
tests/unit/settings/test_resolver.pytests/unit/budget/test_config.pytests/unit/hr/test_activity.pytests/unit/engine/test_run_result.pytests/unit/budget/test_enforcer.pytests/unit/budget/test_currency.py
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use TypeScript 6.0+ syntax; remove
baseUrl(deprecated in TS 6), usemoduleResolution: 'bundler'or'nodenext', list required types intypesarray
Files:
web/src/__tests__/utils/format.property.test.tsweb/src/api/endpoints/budget.tsweb/src/utils/format.tsweb/src/api/types.tsweb/src/__tests__/utils/format.test.tsweb/src/stores/settings.ts
web/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Import
cnfrom@/lib/utilsfor conditional class merging in TypeScript utility code
Files:
web/src/__tests__/utils/format.property.test.tsweb/src/api/endpoints/budget.tsweb/src/utils/format.tsweb/src/api/types.tsweb/src/__tests__/utils/format.test.tsweb/src/stores/settings.ts
docs/design/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
ALWAYS read the relevant design specification page in
docs/design/before implementing any feature or planning any issue; design spec is the starting point for architecture, data models, and behavior; alert user and explain if implementation deviates from spec; update the spec page when deviations are approved
Files:
docs/design/engine.mddocs/design/operations.md
🧠 Learnings (35)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)
Applied to files:
CLAUDE.mdsrc/synthorg/budget/trends.pysrc/synthorg/settings/resolver.pysrc/synthorg/budget/hierarchy.pysrc/synthorg/budget/tracker.pysrc/synthorg/budget/cost_record.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/engine/task_engine_models.pysrc/synthorg/budget/optimizer_models.pysrc/synthorg/core/role.pysrc/synthorg/budget/quota.pydocs/design/operations.mdsrc/synthorg/core/company.pysrc/synthorg/api/controllers/agents.pysrc/synthorg/budget/config.pysrc/synthorg/providers/routing/models.pytests/unit/engine/test_run_result.pysrc/synthorg/core/project.pysrc/synthorg/hr/activity.pysrc/synthorg/budget/optimizer.pysrc/synthorg/budget/cost_tiers.pysrc/synthorg/core/task.pytests/unit/budget/test_enforcer.pysrc/synthorg/api/dto.pysrc/synthorg/engine/prompt_template.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/budget/enforcer.pysrc/synthorg/budget/_optimizer_helpers.pysrc/synthorg/engine/prompt.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/currency.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/settings/definitions/budget.pytests/unit/budget/test_currency.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/api/controllers/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/**/*.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/budget/_enforcer_helpers.pysrc/synthorg/budget/enforcer.pysrc/synthorg/engine/_prompt_helpers.pysrc/synthorg/budget/__init__.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.
Applied to files:
CLAUDE.mdsrc/synthorg/budget/trends.pysrc/synthorg/settings/resolver.pysrc/synthorg/budget/hierarchy.pysrc/synthorg/budget/tracker.pysrc/synthorg/budget/cost_record.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/budget/optimizer_models.pysrc/synthorg/core/role.pysrc/synthorg/budget/quota.pydocs/design/operations.mdsrc/synthorg/api/controllers/agents.pysrc/synthorg/budget/config.pysrc/synthorg/providers/routing/models.pysrc/synthorg/hr/activity.pysrc/synthorg/budget/optimizer.pysrc/synthorg/engine/loop_protocol.pysrc/synthorg/budget/cost_tiers.pysrc/synthorg/core/task.pysrc/synthorg/api/dto.pysrc/synthorg/engine/prompt_template.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/budget/enforcer.pysrc/synthorg/budget/_optimizer_helpers.pysrc/synthorg/budget/__init__.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/settings/definitions/budget.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/api/controllers/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/api/**/*.py : API package (api/): Litestar REST + WebSocket with controllers, guards, channels, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint, provider management endpoint (CRUD + test + presets), backup endpoint, RFC 9457 structured errors, AppState hot-reload slots, service auto-wiring (Phase 1 at construction, Phase 2 on startup), lifecycle helpers
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/api/**/*.py : REST API: Litestar framework, controllers with guards, channels for WebSocket, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. RFC 9457 structured errors (ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/api/**/*.py : Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-19T07: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-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Budget: Cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError).
Applied to files:
CLAUDE.mddocs/design/operations.mdweb/src/api/types.tssrc/synthorg/budget/optimizer.pysrc/synthorg/budget/enforcer.pysrc/synthorg/budget/__init__.pysrc/synthorg/api/controllers/analytics.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability package (observability/): structured logging, correlation tracking, log sinks; event constants organized by domain under observability/events/ (e.g., events.api, events.tool, events.git, events.context_budget, events.backup)
Applied to files:
CLAUDE.mdsrc/synthorg/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/hr/**/*.py : HR package (hr/): hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-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:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-26T21:34:31.847Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T21:34:31.847Z
Learning: Applies to web/src/**/*.tsx : Use Vitest with fast-check for property-based testing (`fc.assert` + `fc.property`); set `types: ['vitest/globals']` in tsconfig
Applied to files:
web/src/__tests__/utils/format.property.test.ts
📚 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 web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).
Applied to files:
web/src/__tests__/utils/format.property.test.ts
📚 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/config.pysrc/synthorg/hr/activity.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/hr/activity.py
📚 Learning: 2026-03-15T18:42:17.990Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:42:17.990Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`
Applied to files:
src/synthorg/hr/activity.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising
Applied to files:
tests/unit/budget/test_enforcer.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:
tests/unit/budget/test_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:
tests/unit/budget/test_enforcer.py
📚 Learning: 2026-03-26T21:34:31.847Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T21:34:31.847Z
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:
tests/unit/budget/test_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:
tests/unit/budget/test_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:
tests/unit/budget/test_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:
tests/unit/budget/test_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_helpers.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_helpers.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/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:
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-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls
Applied to files:
src/synthorg/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 : 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:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-26T21:34:31.847Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T21:34:31.847Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from `synthorg.observability.events.<domain>` modules (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and always use structured kwargs: `logger.info(EVENT, key=value)` never string formatting
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: 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:
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/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)
Applied to files:
src/synthorg/settings/definitions/budget.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings use runtime-editable persistence with precedence: DB > env > YAML > code defaults. 8 namespaces with Fernet encryption for sensitive values.
Applied to files:
src/synthorg/settings/definitions/budget.py
a26c543 to
af0ca88
Compare
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/synthorg/hr/performance/models.py (1)
369-372: 🧹 Nitpick | 🔵 TrivialAdd currency specification for consistency.
The
avg_cost_per_taskfield is derived fromcost_usdvalues and is also in USD (base currency), but its description omits the currency specification. For consistency with the explicit currency documentation added tocost_usdfields, consider updating both the docstring (line 350) and Field description to specify "in USD (base currency)".📝 Suggested update for consistency
Update the class docstring:
- avg_cost_per_task: Average cost per task, None if insufficient data. + avg_cost_per_task: Average cost per task in USD (base currency), None if insufficient data.Update the Field description:
avg_cost_per_task: float | None = Field( default=None, ge=0.0, - description="Average cost per task", + description="Average cost per task in USD (base currency)", )Also applies to: 350-350
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/hr/performance/models.py` around lines 369 - 372, Update the docstring and the Field description to explicitly state the currency is USD (base currency): modify the class docstring referenced around line 350 and the Field for avg_cost_per_task (symbol: avg_cost_per_task) so both read something like "in USD (base currency)" to match the existing cost_usd field documentation; ensure any other cost-related Field descriptions (e.g., cost_usd) remain consistent with this phrasing.src/synthorg/providers/routing/models.py (1)
93-100:⚠️ Potential issue | 🟡 MinorClarify that
remaining_budgetis in base currency.The field description no longer mentions USD, but since
remaining_budgetis compared directly againsttotal_cost_per_1k(which is documented as USD base currency), it should also state this to avoid ambiguity.📝 Suggested fix
remaining_budget: float | None = Field( default=None, ge=0.0, description=( - "Per-request cost ceiling (compared against model " - "total_cost_per_1k). Not a total session budget." + "Per-request cost ceiling in USD (base currency); compared " + "against model total_cost_per_1k. Not a total session budget." ), )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/providers/routing/models.py` around lines 93 - 100, The Field description for remaining_budget should explicitly state that the value is in the same base currency as total_cost_per_1k (USD); update the description on the remaining_budget Field (the remaining_budget declaration) to mention "USD" (or "base currency (USD)") and clarify it is a per-request cost ceiling compared against total_cost_per_1k so callers know the unit matches.src/synthorg/budget/_optimizer_helpers.py (1)
258-343:⚠️ Potential issue | 🟠 MajorPass
currency=self._budget_config.currencyto_build_downgrade_recommendation()atoptimizer.py:572.The function correctly accepts a
currencyparameter and uses it to format cost strings in the recommendation reason. However, the call site (optimizer.py:572–577) does not pass it, causing the function to default toDEFAULT_CURRENCY(USD). This creates silent inconsistency—anomaly descriptions in the same report use the configured currency viaself._budget_config.currency, but downgrade recommendations will always show USD costs regardless of configuration.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/budget/_optimizer_helpers.py` around lines 258 - 343, Call _build_downgrade_recommendation with the configured currency instead of relying on its DEFAULT_CURRENCY: find the call site in the optimizer where _build_downgrade_recommendation(...) is invoked and add currency=self._budget_config.currency to the arguments so the recommendation reason uses the configured budget currency; ensure the symbol names _build_downgrade_recommendation and self._budget_config.currency are used exactly as shown.
♻️ Duplicate comments (5)
src/synthorg/config/schema.py (1)
50-51:⚠️ Potential issue | 🟡 MinorAlign docstring attributes with field descriptions to avoid mixed currency messaging.
These attribute docstrings still omit the “USD (base currency)” context now present on the
Fielddescriptions, which can reintroduce ambiguity in generated docs.Suggested docstring alignment
- cost_per_1k_input: Cost per 1 000 input tokens. - cost_per_1k_output: Cost per 1 000 output tokens. + cost_per_1k_input: Cost per 1 000 input tokens in USD (base currency). + cost_per_1k_output: Cost per 1 000 output tokens in USD (base currency).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/config/schema.py` around lines 50 - 51, Update the class/module docstring attributes for cost_per_1k_input and cost_per_1k_output to match the Field descriptions by explicitly indicating the currency context (e.g. "USD (base currency)"); locate the docstring entries for cost_per_1k_input and cost_per_1k_output in schema.py and amend their text to include the same "USD (base currency)" wording used in the corresponding Field(...) descriptions so docs are consistent.src/synthorg/providers/base.py (1)
339-342:⚠️ Potential issue | 🟡 MinorRestore explicit base-currency semantics in this docstring.
compute_cost()still producesTokenUsage.cost_usd; without explicit wording here, rate-unit expectations are ambiguous for provider implementers.Suggested fix
- cost_per_1k_input: Cost per 1 000 input tokens - (finite and >= 0). - cost_per_1k_output: Cost per 1 000 output tokens - (finite and >= 0). + cost_per_1k_input: Cost per 1 000 input tokens in base currency + used for ``TokenUsage.cost_usd`` (finite and >= 0). + cost_per_1k_output: Cost per 1 000 output tokens in base currency + used for ``TokenUsage.cost_usd`` (finite and >= 0).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/providers/base.py` around lines 339 - 342, Docstring lacks explicit base-currency semantics: update the compute_cost() docstring (in the provider base class) to state that cost_per_1k_input and cost_per_1k_output are expressed in USD per 1,000 tokens (finite and >= 0) and that implementations must compute and populate TokenUsage.cost_usd in USD using those rates; also clarify any rounding/aggregation behavior expected when converting per-1k rates to actual token counts so implementers know how to derive cost_usd from input_tokens and output_tokens.src/synthorg/api/dto.py (1)
478-484:⚠️ Potential issue | 🟠 MajorMake
currencyrequired instead of defaulting to"USD".With the current default, missing propagation silently returns USD. The mapper in
src/synthorg/api/controllers/coordination.pycurrently omits this argument, so non-USD tenants can get incorrect responses.Suggested fix
- currency: str = Field( - default="USD", + currency: str = Field( + ..., min_length=3, max_length=3, pattern=r"^[A-Z]{3}$", description="ISO 4217 currency code", )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/dto.py` around lines 478 - 484, The DTO currently defaults currency to "USD" which masks missing values; change the Field declaration for currency in src/synthorg/api/dto.py to be required (use Field(...), remove default and min/max/pattern can remain) so missing currency raises validation errors, and update the mapper in src/synthorg/api/controllers/coordination.py to explicitly supply the tenant/request currency when constructing the DTO (locate the mapping function that omits the currency and add the currency argument from the request/tenant context).src/synthorg/budget/currency.py (1)
135-140:⚠️ Potential issue | 🟡 MinorAdd structured logging for validation failures before raising.
Per coding guidelines, all error paths in
src/synthorg/**/*.pymust log at WARNING or ERROR with context before raising. TheValueErrorexceptions at lines 136-137 and 139-140 are raised without logging.🔧 Suggested fix
+from synthorg.observability import get_logger +from synthorg.observability.events.budget import ( + BUDGET_FORMAT_INVALID_VALUE, +) + +logger = get_logger(__name__) + def format_cost( value: float, currency: str = DEFAULT_CURRENCY, *, precision: int | None = None, ) -> str: ... if not math.isfinite(value): msg = f"Cannot format non-finite cost value: {value!r}" + logger.warning(BUDGET_FORMAT_INVALID_VALUE, value=value, reason="non_finite") raise ValueError(msg) if precision is not None and precision < 0: msg = f"precision must be non-negative, got {precision}" + logger.warning(BUDGET_FORMAT_INVALID_VALUE, precision=precision, reason="negative_precision") raise ValueError(msg)As per coding guidelines: "All error paths must log at WARNING or ERROR with context before raising."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/budget/currency.py` around lines 135 - 140, Add structured logging immediately before raising the ValueError checks that validate inputs in src.synthorg.budget.currency: when checking math.isfinite(value) and when checking precision < 0, log a warning or error with clear context (include the variable values `value` and `precision` and the operation e.g. "formatting cost") using the module logger (e.g., logger = logging.getLogger(__name__) or existing module-level logger) and then raise the ValueError as before so that both validation failures are logged with context prior to raising.src/synthorg/api/controllers/budget.py (1)
35-56:⚠️ Potential issue | 🟠 MajorDocument these amounts as display-only, or clients will misread them.
These DTOs now describe
total_cost_usdas being "in configured currency", but the controller still populates them from rawcost_usdvalues and only swaps the companioncurrencycode. With a non-USD setting, that makes the payload look like the numeric amount was converted when it was not. Please either rename this to something likedisplay_currencyor make the field descriptions/docstrings explicit thatcurrencyis display-only and no conversion is performed.Also applies to: 59-83, 95-118, 138-168
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/synthorg/api/controllers/analytics.py`:
- Around line 166-172: The backend ForecastResponse Pydantic model defines a
currency field (see currency: str = Field(...)); update the corresponding
TypeScript ForecastResponse interface to include currency: string so frontend
types match the backend and consumers get type checking and formatting support
for ISO 4217 currency codes.
In `@src/synthorg/budget/_enforcer_helpers.py`:
- Around line 135-153: The _ALERT_LEVEL_ORDER dict is mutable after import which
allows later corruption; change the code to build the mapping into a temporary
plain dict, run the existing validation checks against that temp (checking keys
and unique values), then wrap and assign the final name _ALERT_LEVEL_ORDER using
types.MappingProxyType(temp) so external code cannot mutate it; reference the
symbol _ALERT_LEVEL_ORDER and the enum BudgetAlertLevel when making these
changes and add the import for MappingProxyType from the types module.
In `@src/synthorg/core/role.py`:
- Around line 40-41: The docstring for Authority.budget_limit incorrectly states
the limit is in the "configured currency"; update the wording to make clear the
budget_limit is expressed in the system base currency (not converted by
configured/display currency) and that configured currency only affects
formatting/display. Locate the Authority class docstring and any parameter
descriptions referencing budget_limit (e.g., "budget_limit: Maximum spend per
task in configured currency") and replace with text like "budget_limit: Maximum
spend per task in base currency; configured currency is used only for
display/formatting." Ensure the same clarification is applied to the other
occurrence(s) of this wording in the file (the second budget_limit description).
In `@src/synthorg/core/task.py`:
- Around line 66-67: The docstring for Task.budget_limit incorrectly says
"configured currency"; update the wording to state this field is a base-currency
numeric limit (i.e., enforced in base currency, not converted), so change
occurrences in the Task class docstring and the parameter description for
budget_limit to mention "base currency" or "base-currency numeric limit" and
that the value is a numeric limit in the system's base currency; locate
references to Task.budget_limit and adjust both places where "configured
currency" appears.
In `@src/synthorg/providers/routing/models.py`:
- Around line 16-19: The docstring for the token cost fields is redundant:
update the descriptions for cost_per_1k_input and cost_per_1k_output to remove
the duplicated "per 1,000 tokens" phrase so each reads clearly (e.g., "Cost per
1,000 input tokens in USD (base currency)." and similarly for output). Locate
the fields cost_per_1k_input and cost_per_1k_output in models.py and replace the
current lines with the simplified phrasing.
In `@src/synthorg/templates/schema.py`:
- Around line 316-317: Update the docstring for the budget_monthly field to
state that amounts are in the base currency (not a runtime/conversion currency)
— replace phrases like "configured currency" with "base currency" in the
budget_monthly documentation (and the surrounding docstring block that spans the
autonomy/budget description), e.g., adjust the docstring in the schema where
budget_monthly is defined so it clearly reads "Default monthly budget in base
currency" and make the same wording change in the related docblock that
currently covers lines ~346-350.
In `@tests/unit/budget/test_enforcer.py`:
- Around line 160-174: Combine the two tests into one parametrized pytest test
that covers both the default and custom currency cases: call the test e.g.
test_currency_returns_configured_or_default and parametrize over inputs (a
BudgetConfig built with no currency and one with currency="GBP") and expected
outputs (use DEFAULT_CURRENCY for the default case instead of the hardcoded
"USD"). Instantiate BudgetEnforcer with BudgetConfig and CostTracker (symbols:
BudgetEnforcer, BudgetConfig, CostTracker) and assert enforcer.currency equals
the expected value; import DEFAULT_CURRENCY from the module that defines it and
reference that symbol in the expected values.
---
Outside diff comments:
In `@src/synthorg/budget/_optimizer_helpers.py`:
- Around line 258-343: Call _build_downgrade_recommendation with the configured
currency instead of relying on its DEFAULT_CURRENCY: find the call site in the
optimizer where _build_downgrade_recommendation(...) is invoked and add
currency=self._budget_config.currency to the arguments so the recommendation
reason uses the configured budget currency; ensure the symbol names
_build_downgrade_recommendation and self._budget_config.currency are used
exactly as shown.
In `@src/synthorg/hr/performance/models.py`:
- Around line 369-372: Update the docstring and the Field description to
explicitly state the currency is USD (base currency): modify the class docstring
referenced around line 350 and the Field for avg_cost_per_task (symbol:
avg_cost_per_task) so both read something like "in USD (base currency)" to match
the existing cost_usd field documentation; ensure any other cost-related Field
descriptions (e.g., cost_usd) remain consistent with this phrasing.
In `@src/synthorg/providers/routing/models.py`:
- Around line 93-100: The Field description for remaining_budget should
explicitly state that the value is in the same base currency as
total_cost_per_1k (USD); update the description on the remaining_budget Field
(the remaining_budget declaration) to mention "USD" (or "base currency (USD)")
and clarify it is a per-request cost ceiling compared against total_cost_per_1k
so callers know the unit matches.
---
Duplicate comments:
In `@src/synthorg/api/dto.py`:
- Around line 478-484: The DTO currently defaults currency to "USD" which masks
missing values; change the Field declaration for currency in
src/synthorg/api/dto.py to be required (use Field(...), remove default and
min/max/pattern can remain) so missing currency raises validation errors, and
update the mapper in src/synthorg/api/controllers/coordination.py to explicitly
supply the tenant/request currency when constructing the DTO (locate the mapping
function that omits the currency and add the currency argument from the
request/tenant context).
In `@src/synthorg/budget/currency.py`:
- Around line 135-140: Add structured logging immediately before raising the
ValueError checks that validate inputs in src.synthorg.budget.currency: when
checking math.isfinite(value) and when checking precision < 0, log a warning or
error with clear context (include the variable values `value` and `precision`
and the operation e.g. "formatting cost") using the module logger (e.g., logger
= logging.getLogger(__name__) or existing module-level logger) and then raise
the ValueError as before so that both validation failures are logged with
context prior to raising.
In `@src/synthorg/config/schema.py`:
- Around line 50-51: Update the class/module docstring attributes for
cost_per_1k_input and cost_per_1k_output to match the Field descriptions by
explicitly indicating the currency context (e.g. "USD (base currency)"); locate
the docstring entries for cost_per_1k_input and cost_per_1k_output in schema.py
and amend their text to include the same "USD (base currency)" wording used in
the corresponding Field(...) descriptions so docs are consistent.
In `@src/synthorg/providers/base.py`:
- Around line 339-342: Docstring lacks explicit base-currency semantics: update
the compute_cost() docstring (in the provider base class) to state that
cost_per_1k_input and cost_per_1k_output are expressed in USD per 1,000 tokens
(finite and >= 0) and that implementations must compute and populate
TokenUsage.cost_usd in USD using those rates; also clarify any
rounding/aggregation behavior expected when converting per-1k rates to actual
token counts so implementers know how to derive cost_usd from input_tokens and
output_tokens.
🪄 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: 5246b9e3-0ded-4953-ab0c-b4125f1f7798
📒 Files selected for processing (67)
CLAUDE.mddocs/design/engine.mddocs/design/operations.mdsrc/synthorg/api/controllers/activities.pysrc/synthorg/api/controllers/agents.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/api/controllers/budget.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/api/dto.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/budget/_optimizer_helpers.pysrc/synthorg/budget/config.pysrc/synthorg/budget/cost_record.pysrc/synthorg/budget/cost_tiers.pysrc/synthorg/budget/currency.pysrc/synthorg/budget/enforcer.pysrc/synthorg/budget/hierarchy.pysrc/synthorg/budget/optimizer.pysrc/synthorg/budget/optimizer_models.pysrc/synthorg/budget/quota.pysrc/synthorg/budget/tracker.pysrc/synthorg/budget/trends.pysrc/synthorg/communication/message.pysrc/synthorg/config/schema.pysrc/synthorg/core/company.pysrc/synthorg/core/project.pysrc/synthorg/core/role.pysrc/synthorg/core/task.pysrc/synthorg/engine/_prompt_helpers.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/engine/agent_state.pysrc/synthorg/engine/assignment/models.pysrc/synthorg/engine/coordination/models.pysrc/synthorg/engine/loop_protocol.pysrc/synthorg/engine/metrics.pysrc/synthorg/engine/parallel_models.pysrc/synthorg/engine/prompt.pysrc/synthorg/engine/prompt_template.pysrc/synthorg/engine/run_result.pysrc/synthorg/engine/task_engine_models.pysrc/synthorg/hr/activity.pysrc/synthorg/hr/models.pysrc/synthorg/hr/performance/ci_quality_strategy.pysrc/synthorg/hr/performance/models.pysrc/synthorg/hr/performance/summary.pysrc/synthorg/persistence/repositories.pysrc/synthorg/providers/base.pysrc/synthorg/providers/capabilities.pysrc/synthorg/providers/models.pysrc/synthorg/providers/routing/models.pysrc/synthorg/settings/definitions/budget.pysrc/synthorg/settings/resolver.pysrc/synthorg/templates/schema.pytests/unit/budget/test_config.pytests/unit/budget/test_currency.pytests/unit/budget/test_enforcer.pytests/unit/engine/test_run_result.pytests/unit/hr/test_activity.pytests/unit/settings/test_resolver.pyweb/src/__tests__/utils/format.property.test.tsweb/src/__tests__/utils/format.test.tsweb/src/api/endpoints/budget.tsweb/src/api/types.tsweb/src/stores/settings.tsweb/src/utils/format.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Web
- GitHub Check: Build Backend
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
docs/design/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
ALWAYS read the relevant design specification page in
docs/design/before implementing any feature or planning any issue; design spec is the starting point for architecture, data models, and behavior; alert user and explain if implementation deviates from spec; update the spec page when deviations are approved
Files:
docs/design/engine.mddocs/design/operations.md
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotations-- Python 3.14 has PEP 649 native lazy annotations
Use PEP 758 except syntax:except A, B:(no parentheses) instead ofexcept (A, B):-- ruff enforces this on Python 3.14
All public functions and classes must have type hints; mypy strict mode is enforced
Use Google-style docstrings on all public classes and functions; enforced by ruff D rules
Create new objects instead of mutating existing ones; for non-Pydantic internal collections usecopy.deepcopy()at construction andMappingProxyTypewrapping for read-only enforcement
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls) over barecreate_taskfor structured concurrency
Keep functions under 50 lines and files under 800 lines
Line length must be 88 characters as enforced by ruff
Validate explicitly at system boundaries (user input, external APIs, config files); never silently swallow errors
Files:
src/synthorg/settings/resolver.pysrc/synthorg/persistence/repositories.pysrc/synthorg/api/controllers/agents.pysrc/synthorg/budget/trends.pytests/unit/settings/test_resolver.pysrc/synthorg/budget/hierarchy.pysrc/synthorg/hr/performance/ci_quality_strategy.pysrc/synthorg/budget/tracker.pysrc/synthorg/engine/task_engine_models.pysrc/synthorg/budget/optimizer_models.pysrc/synthorg/engine/assignment/models.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/engine/prompt_template.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/core/task.pysrc/synthorg/engine/run_result.pysrc/synthorg/providers/models.pysrc/synthorg/engine/metrics.pysrc/synthorg/communication/message.pysrc/synthorg/budget/cost_record.pysrc/synthorg/engine/parallel_models.pysrc/synthorg/budget/optimizer.pysrc/synthorg/budget/quota.pysrc/synthorg/engine/loop_protocol.pytests/unit/hr/test_activity.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/hr/activity.pysrc/synthorg/hr/models.pysrc/synthorg/templates/schema.pysrc/synthorg/core/role.pysrc/synthorg/budget/config.pytests/unit/engine/test_run_result.pysrc/synthorg/engine/agent_state.pysrc/synthorg/budget/__init__.pysrc/synthorg/engine/coordination/models.pysrc/synthorg/config/schema.pysrc/synthorg/api/dto.pysrc/synthorg/core/project.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/engine/_prompt_helpers.pysrc/synthorg/providers/capabilities.pysrc/synthorg/hr/performance/models.pytests/unit/budget/test_enforcer.pysrc/synthorg/providers/base.pysrc/synthorg/budget/cost_tiers.pysrc/synthorg/budget/enforcer.pysrc/synthorg/settings/definitions/budget.pysrc/synthorg/hr/performance/summary.pysrc/synthorg/budget/_optimizer_helpers.pysrc/synthorg/core/company.pysrc/synthorg/budget/currency.pytests/unit/budget/test_config.pytests/unit/budget/test_currency.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/engine/prompt.pysrc/synthorg/providers/routing/models.pysrc/synthorg/api/controllers/budget.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic must import and use logger:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)
Never useimport logging,logging.getLogger(), orprint()in application code; only use the structured logger fromsynthorg.observability
Always use the logger variable namelogger(not_logger, notlog)
Use event name constants fromsynthorg.observability.events.<domain>modules (e.g.,API_REQUEST_STARTEDfromevents.api); import directly and always use structured kwargs:logger.info(EVENT, key=value)never string formatting
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
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (withmodel_copy(update=...)) for runtime state that evolves
Use Pydantic v2 with@computed_fieldfor derived values instead of storing redundant fields; useNotBlankStr(fromcore.types) for all identifier/name fields instead of manual validators
All provider calls go throughBaseCompletionProviderwhich applies retry + rate limiting automatically; never implement retry logic in driver subclasses or calling code
Validate at system boundaries: user input, external APIs, config files; handle errors explicitly, never silently swallow
Files:
src/synthorg/settings/resolver.pysrc/synthorg/persistence/repositories.pysrc/synthorg/api/controllers/agents.pysrc/synthorg/budget/trends.pysrc/synthorg/budget/hierarchy.pysrc/synthorg/hr/performance/ci_quality_strategy.pysrc/synthorg/budget/tracker.pysrc/synthorg/engine/task_engine_models.pysrc/synthorg/budget/optimizer_models.pysrc/synthorg/engine/assignment/models.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/engine/prompt_template.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/core/task.pysrc/synthorg/engine/run_result.pysrc/synthorg/providers/models.pysrc/synthorg/engine/metrics.pysrc/synthorg/communication/message.pysrc/synthorg/budget/cost_record.pysrc/synthorg/engine/parallel_models.pysrc/synthorg/budget/optimizer.pysrc/synthorg/budget/quota.pysrc/synthorg/engine/loop_protocol.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/hr/activity.pysrc/synthorg/hr/models.pysrc/synthorg/templates/schema.pysrc/synthorg/core/role.pysrc/synthorg/budget/config.pysrc/synthorg/engine/agent_state.pysrc/synthorg/budget/__init__.pysrc/synthorg/engine/coordination/models.pysrc/synthorg/config/schema.pysrc/synthorg/api/dto.pysrc/synthorg/core/project.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/engine/_prompt_helpers.pysrc/synthorg/providers/capabilities.pysrc/synthorg/hr/performance/models.pysrc/synthorg/providers/base.pysrc/synthorg/budget/cost_tiers.pysrc/synthorg/budget/enforcer.pysrc/synthorg/settings/definitions/budget.pysrc/synthorg/hr/performance/summary.pysrc/synthorg/budget/_optimizer_helpers.pysrc/synthorg/core/company.pysrc/synthorg/budget/currency.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/engine/prompt.pysrc/synthorg/providers/routing/models.pysrc/synthorg/api/controllers/budget.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, or@pytest.mark.slowto categorize tests
Maintain 80% minimum code coverage as enforced in CI
Useasyncio_mode = 'auto'for async tests; no manual@pytest.mark.asyncioneeded
Use@pytest.mark.parametrizefor testing similar cases instead of test duplication
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, tests, or config examples; use generic names:example-provider,example-large-001,test-provider,test-small-001,large/medium/small
Use Hypothesis with@given+@settingsfor property-based testing; avoid flaky timing-based tests by mockingtime.monotonic()andasyncio.sleep()or usingasyncio.Event().wait()for indefinite blocks
Files:
tests/unit/settings/test_resolver.pytests/unit/hr/test_activity.pytests/unit/engine/test_run_result.pytests/unit/budget/test_enforcer.pytests/unit/budget/test_config.pytests/unit/budget/test_currency.py
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use TypeScript 6.0+ syntax; remove
baseUrl(deprecated in TS 6), usemoduleResolution: 'bundler'or'nodenext', list required types intypesarray
Files:
web/src/api/endpoints/budget.tsweb/src/utils/format.tsweb/src/api/types.tsweb/src/__tests__/utils/format.test.tsweb/src/__tests__/utils/format.property.test.tsweb/src/stores/settings.ts
web/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Import
cnfrom@/lib/utilsfor conditional class merging in TypeScript utility code
Files:
web/src/api/endpoints/budget.tsweb/src/utils/format.tsweb/src/api/types.tsweb/src/__tests__/utils/format.test.tsweb/src/__tests__/utils/format.property.test.tsweb/src/stores/settings.ts
🧠 Learnings (40)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)
📚 Learning: 2026-03-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/settings/resolver.pyCLAUDE.mdsrc/synthorg/api/controllers/agents.pysrc/synthorg/budget/trends.pydocs/design/operations.mdsrc/synthorg/budget/hierarchy.pysrc/synthorg/budget/tracker.pysrc/synthorg/engine/task_engine_models.pysrc/synthorg/budget/optimizer_models.pysrc/synthorg/engine/prompt_template.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/core/task.pysrc/synthorg/budget/cost_record.pysrc/synthorg/budget/optimizer.pysrc/synthorg/budget/quota.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/hr/activity.pysrc/synthorg/core/role.pysrc/synthorg/budget/config.pytests/unit/engine/test_run_result.pysrc/synthorg/budget/__init__.pysrc/synthorg/api/dto.pysrc/synthorg/core/project.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/providers/capabilities.pytests/unit/budget/test_enforcer.pysrc/synthorg/budget/cost_tiers.pysrc/synthorg/budget/enforcer.pysrc/synthorg/settings/definitions/budget.pysrc/synthorg/budget/_optimizer_helpers.pysrc/synthorg/core/company.pysrc/synthorg/budget/currency.pytests/unit/budget/test_currency.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/engine/prompt.pysrc/synthorg/providers/routing/models.pysrc/synthorg/api/controllers/budget.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/settings/resolver.pyCLAUDE.mdsrc/synthorg/api/controllers/agents.pysrc/synthorg/budget/trends.pydocs/design/operations.mdsrc/synthorg/budget/hierarchy.pysrc/synthorg/budget/tracker.pysrc/synthorg/budget/optimizer_models.pysrc/synthorg/engine/prompt_template.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/core/task.pysrc/synthorg/budget/cost_record.pysrc/synthorg/budget/optimizer.pysrc/synthorg/budget/quota.pysrc/synthorg/engine/loop_protocol.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/hr/activity.pysrc/synthorg/budget/config.pysrc/synthorg/budget/__init__.pysrc/synthorg/api/dto.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/providers/capabilities.pysrc/synthorg/budget/cost_tiers.pysrc/synthorg/budget/enforcer.pysrc/synthorg/settings/definitions/budget.pysrc/synthorg/budget/_optimizer_helpers.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/providers/routing/models.pysrc/synthorg/api/controllers/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/**/*.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/budget/__init__.pysrc/synthorg/budget/enforcer.pysrc/synthorg/budget/_enforcer_helpers.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.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/api/**/*.py : REST API: Litestar framework, controllers with guards, channels for WebSocket, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. RFC 9457 structured errors (ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/api/**/*.py : Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-19T07: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-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Budget: Cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError).
Applied to files:
CLAUDE.mddocs/design/operations.mdsrc/synthorg/budget/optimizer.pysrc/synthorg/budget/__init__.pyweb/src/api/types.tssrc/synthorg/budget/enforcer.pysrc/synthorg/api/controllers/analytics.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability package (observability/): structured logging, correlation tracking, log sinks; event constants organized by domain under observability/events/ (e.g., events.api, events.tool, events.git, events.context_budget, events.backup)
Applied to files:
CLAUDE.mdsrc/synthorg/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/hr/**/*.py : HR package (hr/): hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-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:
src/synthorg/engine/agent_engine.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 src/ai_company/!(observability)/**/*.py : All error paths must log at WARNING or ERROR with context before raising.
Applied to files:
src/synthorg/api/controllers/activities.pysrc/synthorg/budget/currency.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/hr/activity.py
📚 Learning: 2026-03-15T18:42:17.990Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:42:17.990Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`
Applied to files:
src/synthorg/hr/activity.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/hr/activity.pysrc/synthorg/config/schema.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/__init__.pysrc/synthorg/budget/enforcer.pysrc/synthorg/api/controllers/budget.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/engine/_prompt_helpers.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising
Applied to files:
tests/unit/budget/test_enforcer.pysrc/synthorg/budget/currency.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:
tests/unit/budget/test_enforcer.pysrc/synthorg/budget/currency.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:
tests/unit/budget/test_enforcer.pysrc/synthorg/budget/currency.py
📚 Learning: 2026-03-26T21:34:31.847Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T21:34:31.847Z
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:
tests/unit/budget/test_enforcer.pysrc/synthorg/budget/currency.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:
tests/unit/budget/test_enforcer.pysrc/synthorg/budget/currency.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:
tests/unit/budget/test_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:
tests/unit/budget/test_enforcer.pysrc/synthorg/budget/currency.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 provider calls go through `BaseCompletionProvider` which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code — it's handled by the base class.
Applied to files:
src/synthorg/providers/base.py
📚 Learning: 2026-03-26T21:34:31.847Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T21:34:31.847Z
Learning: Applies to src/synthorg/**/*.py : All provider calls go through `BaseCompletionProvider` which applies retry + rate limiting automatically; never implement retry logic in driver subclasses or calling code
Applied to files:
src/synthorg/providers/base.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : All provider calls go through `BaseCompletionProvider` which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code.
Applied to files:
src/synthorg/providers/base.py
📚 Learning: 2026-03-17T18:52:05.142Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T18:52:05.142Z
Learning: Applies to src/synthorg/providers/**/*.py : All provider calls go through BaseCompletionProvider which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code — it's handled by the base class.
Applied to files:
src/synthorg/providers/base.py
📚 Learning: 2026-03-26T21:34:31.847Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T21:34:31.847Z
Learning: Applies to web/src/**/*.tsx : Use Vitest with fast-check for property-based testing (`fc.assert` + `fc.property`); set `types: ['vitest/globals']` in tsconfig
Applied to files:
web/src/__tests__/utils/format.property.test.ts
📚 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 web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).
Applied to files:
web/src/__tests__/utils/format.property.test.ts
📚 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-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-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls
Applied to files:
src/synthorg/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 : 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:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-26T21:34:31.847Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T21:34:31.847Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from `synthorg.observability.events.<domain>` modules (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and always use structured kwargs: `logger.info(EVENT, key=value)` never string formatting
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: 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:
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/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)
Applied to files:
src/synthorg/settings/definitions/budget.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings use runtime-editable persistence with precedence: DB > env > YAML > code defaults. 8 namespaces with Fernet encryption for sensitive values.
Applied to files:
src/synthorg/settings/definitions/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/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_helpers.py
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/synthorg/api/controllers/analytics.py (1)
578-604: 🧹 Nitpick | 🔵 TrivialConsider consolidating budget config fetches to avoid redundant async call.
get_forecastcalls_resolve_budget_context()at line 578, which internally accessesapp_state.cost_tracker.budget_config, then separately callsget_budget_config()at line 595 just for the currency. This results in two separate accesses to budget configuration.Consider fetching
budget_cfgonce at the start and passing it to_resolve_budget_context, or extracting currency from the same source used by_resolve_budget_context.♻️ Proposed refactor to fetch budget_cfg once
records = await app_state.cost_tracker.get_records( start=lookback_start, end=now, ) - budget = await _resolve_budget_context(app_state, now=now) + budget_cfg = await app_state.config_resolver.get_budget_config() + budget = await _resolve_budget_context(app_state, now=now) forecast = project_daily_spend( records, horizon_days=horizon_days, budget_total_monthly=budget.monthly, budget_remaining_usd=budget.remaining, now=now, ) logger.debug( ANALYTICS_FORECAST_QUERIED, horizon_days=horizon_days, projected_total_usd=forecast.projected_total_usd, days_until_exhausted=forecast.days_until_exhausted, ) - budget_cfg = await app_state.config_resolver.get_budget_config() return ApiResponse( data=ForecastResponse( horizon_days=horizon_days, projected_total_usd=forecast.projected_total_usd, daily_projections=forecast.daily_projections, days_until_exhausted=forecast.days_until_exhausted, confidence=forecast.confidence, avg_daily_spend_usd=forecast.avg_daily_spend_usd, currency=budget_cfg.currency, ), )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/controllers/analytics.py` around lines 578 - 604, get_forecast currently calls _resolve_budget_context(app_state, now=...) which reads budget config, and then separately awaits app_state.config_resolver.get_budget_config() just to get currency, causing redundant async access; fix by fetching budget_cfg once (await app_state.config_resolver.get_budget_config()) before calling _resolve_budget_context and either pass budget_cfg into _resolve_budget_context (add an optional parameter) or have get_forecast extract the currency from the budget object returned by _resolve_budget_context (e.g., budget.currency) and remove the extra get_budget_config() call, updating references to budget_cfg/currency accordingly.
♻️ Duplicate comments (2)
src/synthorg/core/task.py (1)
66-66:⚠️ Potential issue | 🟡 MinorAvoid hardcoding
USDinbudget_limitdocs; describe base-currency semantics generically.At Line 66 and Line 124, the wording still pins a specific currency (
USD) in a PR that introduces configurable display currency. Prefer wording like “base currency (display formatting followsbudget.currency)” so API/docs stay accurate if base-currency policy changes.Suggested wording update
- budget_limit: Maximum spend for this task in USD (base currency). + budget_limit: Maximum spend for this task in base currency (display formatting follows budget.currency). ... - description="Maximum spend for this task in USD (base currency)", + description="Maximum spend for this task in base currency (display formatting follows budget.currency)",Also applies to: 124-124
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/core/task.py` at line 66, Update the docstring for the budget_limit parameter to avoid hardcoding "USD": change the description of budget_limit (in src/synthorg/core/task.py, e.g., the Task or related function where budget_limit is documented) to refer to the base currency generically and note that display formatting follows budget.currency (for example: "Maximum spend for this task in base currency (display formatting follows budget.currency)"). Ensure the same wording change is applied to both occurrences referenced (around the budget_limit doc at lines ~66 and ~124).src/synthorg/api/dto.py (1)
478-484:⚠️ Potential issue | 🟠 MajorMake
currencyexplicit and clarify thattotal_cost_usdstays in base currency.
src/synthorg/api/controllers/coordination.py:85-104still constructs this DTO withoutcurrency, so the new default silently serializes"USD"regardless of the resolved setting. Once callers start passing non-USD codes, the unchangedtotal_cost_usdfield will also read like a converted amount unless its contract says “base currency” (or the field is renamed).🧭 Suggested DTO-side tightening
- total_cost_usd: float = Field(ge=0.0) + total_cost_usd: float = Field( + ge=0.0, + description="Total cost in USD (base currency)", + ) currency: str = Field( - default="USD", min_length=3, max_length=3, pattern=r"^[A-Z]{3}$", - description="ISO 4217 currency code", + description="Display currency code (no conversion applied)", )Then update
_map_result_to_response()to pass the resolved currency explicitly.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/dto.py` around lines 478 - 484, The DTO currently defaults currency to "USD" which masks the resolved setting and can make total_cost_usd misleading; change the Field for currency in the DTO (currency: str = Field(..., min_length=3, max_length=3, pattern=r"^[A-Z]{3}$", description="ISO 4217 currency code")) so it is explicit/required (remove the default), add or update the description for total_cost_usd to state it is the amount in the system/base currency, and then update the controller function that builds this DTO (the construction in src/synthorg/api/controllers/coordination.py and the helper _map_result_to_response()) to pass the resolved currency value explicitly when instantiating the DTO.
🤖 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/budget.py`:
- Around line 301-307: The current sequential awaits for
app_state.config_resolver.get_budget_config(),
app_state.cost_tracker.get_records(...), and
app_state.cost_tracker.get_agent_cost(...) in get_agent_spending() add avoidable
latency; run the independent get_budget_config() in parallel with the tracker
calls using an asyncio.TaskGroup: spawn tasks for get_budget_config(),
get_records(agent_id, task_id) and get_agent_cost(agent_id) (or the exact two
tracker calls referenced around lines 339-345), await them all, then pass the
resolved currency and records into _build_summaries(); ensure you replace the
sequential awaits with the TaskGroup fan-out/fan-in pattern and use the task
results for currency, records, and agent cost.
In `@src/synthorg/api/dto.py`:
- Line 228: The budget_limit field needs an explicit Pydantic Field description
so the JSON schema/API docs show it represents USD; update the model's
budget_limit declaration to use Field(description="Maximum spend in USD (base
currency).") (and add/ensure the Field import from pydantic is present) so the
schema includes the description for clients.
In `@src/synthorg/budget/_enforcer_helpers.py`:
- Around line 143-153: The current validation only checks keys and uniqueness in
_raw_order/_ALERT_LEVEL_ORDER but not whether numeric values respect the
expected severity ordering used by _emit_alert; update the validation to enforce
monotonic severity order by iterating BudgetAlertLevel in its defined ordinal
order and verifying that the mapped integer values are strictly increasing (or
match a canonical ordinal mapping) for each successive member; if any value
violates the monotonic increase, raise a RuntimeError with a clear message
referencing _ALERT_LEVEL_ORDER and BudgetAlertLevel so incorrect swaps or
out-of-range assignments are rejected at import time.
- Line 253: The closure-local variable last_alert in _build_checker_closure() is
reset to NORMAL for every checker instance, causing repeated
BUDGET_ALERT_THRESHOLD_CROSSED emissions across tasks; move persistent state out
of the closure by storing the last-emitted BudgetAlertLevel on a shared object
(e.g., the Enforcer instance or the billing-period state) and have
_build_checker_closure() read/update that shared field instead of using the
local last_alert list so threshold crossings are deduplicated across tasks.
- Around line 37-106: The _apply_downgrade function is currently too long and
mixes multiple responsibilities. Refactor it by extracting smaller helper
functions to handle specific tasks: model resolution and associated skip
logging, alias-to-target lookup with logging, and the model identity update.
This will make each step easier to test and keep _apply_downgrade under 50 lines
as per the guideline.
In `@src/synthorg/hr/performance/models.py`:
- Around line 369-373: The field avg_cost_per_task on the Pydantic model is
inconsistent with other cost fields that use a _usd suffix; rename
avg_cost_per_task to avg_cost_per_task_usd (and update all references/usages,
e.g., in serializers, tests, and any code accessing the attribute) to match
cost_usd naming, or if API compatibility prevents renaming, add a clear code
comment above the Field explaining this intentional exception and keep the
current name; ensure the Field description remains "Average cost per task in USD
(base currency)" and update any related OpenAPI/type docs accordingly.
In `@tests/unit/budget/test_enforcer.py`:
- Around line 154-182: The currency-focused parametrized tests
(TestCurrencyProperty and its test_returns_configured_or_default) should be
moved out of the oversized tests/unit/budget/test_enforcer.py into a new
dedicated module (e.g., tests/unit/budget/test_enforcer_currency.py); copy the
TestCurrencyProperty class and any other currency cases referenced (also from
the block at lines noted around 237-260) into that new file, update imports so
BudgetEnforcer, BudgetConfig, CostTracker, and DEFAULT_CURRENCY are imported in
the new module, and leave the original file with those tests removed to keep it
under the 800-line guideline.
---
Outside diff comments:
In `@src/synthorg/api/controllers/analytics.py`:
- Around line 578-604: get_forecast currently calls
_resolve_budget_context(app_state, now=...) which reads budget config, and then
separately awaits app_state.config_resolver.get_budget_config() just to get
currency, causing redundant async access; fix by fetching budget_cfg once (await
app_state.config_resolver.get_budget_config()) before calling
_resolve_budget_context and either pass budget_cfg into _resolve_budget_context
(add an optional parameter) or have get_forecast extract the currency from the
budget object returned by _resolve_budget_context (e.g., budget.currency) and
remove the extra get_budget_config() call, updating references to
budget_cfg/currency accordingly.
---
Duplicate comments:
In `@src/synthorg/api/dto.py`:
- Around line 478-484: The DTO currently defaults currency to "USD" which masks
the resolved setting and can make total_cost_usd misleading; change the Field
for currency in the DTO (currency: str = Field(..., min_length=3, max_length=3,
pattern=r"^[A-Z]{3}$", description="ISO 4217 currency code")) so it is
explicit/required (remove the default), add or update the description for
total_cost_usd to state it is the amount in the system/base currency, and then
update the controller function that builds this DTO (the construction in
src/synthorg/api/controllers/coordination.py and the helper
_map_result_to_response()) to pass the resolved currency value explicitly when
instantiating the DTO.
In `@src/synthorg/core/task.py`:
- Line 66: Update the docstring for the budget_limit parameter to avoid
hardcoding "USD": change the description of budget_limit (in
src/synthorg/core/task.py, e.g., the Task or related function where budget_limit
is documented) to refer to the base currency generically and note that display
formatting follows budget.currency (for example: "Maximum spend for this task in
base currency (display formatting follows budget.currency)"). Ensure the same
wording change is applied to both occurrences referenced (around the
budget_limit doc at lines ~66 and ~124).
🪄 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: dd08b943-a27d-4e69-a73e-f7964b5bbc80
📒 Files selected for processing (19)
src/synthorg/api/controllers/analytics.pysrc/synthorg/api/controllers/budget.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/api/dto.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/budget/hierarchy.pysrc/synthorg/budget/optimizer.pysrc/synthorg/budget/quota.pysrc/synthorg/config/schema.pysrc/synthorg/core/project.pysrc/synthorg/core/role.pysrc/synthorg/core/task.pysrc/synthorg/hr/models.pysrc/synthorg/hr/performance/models.pysrc/synthorg/providers/base.pysrc/synthorg/providers/routing/models.pysrc/synthorg/templates/schema.pytests/unit/budget/test_enforcer.pyweb/src/api/types.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Build Sandbox
- GitHub Check: Build Web
- GitHub Check: Build Backend
- GitHub Check: Test (Python 3.14)
- 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
Use PEP 758 except syntax:except A, B:(no parentheses) instead ofexcept (A, B):-- ruff enforces this on Python 3.14
All public functions and classes must have type hints; mypy strict mode is enforced
Use Google-style docstrings on all public classes and functions; enforced by ruff D rules
Create new objects instead of mutating existing ones; for non-Pydantic internal collections usecopy.deepcopy()at construction andMappingProxyTypewrapping for read-only enforcement
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls) over barecreate_taskfor structured concurrency
Keep functions under 50 lines and files under 800 lines
Line length must be 88 characters as enforced by ruff
Validate explicitly at system boundaries (user input, external APIs, config files); never silently swallow errors
Files:
src/synthorg/api/controllers/setup_models.pysrc/synthorg/budget/quota.pysrc/synthorg/core/task.pysrc/synthorg/providers/base.pysrc/synthorg/hr/models.pysrc/synthorg/core/project.pysrc/synthorg/budget/optimizer.pysrc/synthorg/templates/schema.pysrc/synthorg/core/role.pytests/unit/budget/test_enforcer.pysrc/synthorg/config/schema.pysrc/synthorg/hr/performance/models.pysrc/synthorg/api/dto.pysrc/synthorg/providers/routing/models.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/hierarchy.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/api/controllers/budget.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic must import and use logger:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)
Never useimport logging,logging.getLogger(), orprint()in application code; only use the structured logger fromsynthorg.observability
Always use the logger variable namelogger(not_logger, notlog)
Use event name constants fromsynthorg.observability.events.<domain>modules (e.g.,API_REQUEST_STARTEDfromevents.api); import directly and always use structured kwargs:logger.info(EVENT, key=value)never string formatting
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
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (withmodel_copy(update=...)) for runtime state that evolves
Use Pydantic v2 with@computed_fieldfor derived values instead of storing redundant fields; useNotBlankStr(fromcore.types) for all identifier/name fields instead of manual validators
All provider calls go throughBaseCompletionProviderwhich applies retry + rate limiting automatically; never implement retry logic in driver subclasses or calling code
Validate at system boundaries: user input, external APIs, config files; handle errors explicitly, never silently swallow
Files:
src/synthorg/api/controllers/setup_models.pysrc/synthorg/budget/quota.pysrc/synthorg/core/task.pysrc/synthorg/providers/base.pysrc/synthorg/hr/models.pysrc/synthorg/core/project.pysrc/synthorg/budget/optimizer.pysrc/synthorg/templates/schema.pysrc/synthorg/core/role.pysrc/synthorg/config/schema.pysrc/synthorg/hr/performance/models.pysrc/synthorg/api/dto.pysrc/synthorg/providers/routing/models.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/hierarchy.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/api/controllers/budget.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, or@pytest.mark.slowto categorize tests
Maintain 80% minimum code coverage as enforced in CI
Useasyncio_mode = 'auto'for async tests; no manual@pytest.mark.asyncioneeded
Use@pytest.mark.parametrizefor testing similar cases instead of test duplication
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, tests, or config examples; use generic names:example-provider,example-large-001,test-provider,test-small-001,large/medium/small
Use Hypothesis with@given+@settingsfor property-based testing; avoid flaky timing-based tests by mockingtime.monotonic()andasyncio.sleep()or usingasyncio.Event().wait()for indefinite blocks
Files:
tests/unit/budget/test_enforcer.py
**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Use TypeScript 6.0+ syntax; remove
baseUrl(deprecated in TS 6), usemoduleResolution: 'bundler'or'nodenext', list required types intypesarray
Files:
web/src/api/types.ts
web/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Import
cnfrom@/lib/utilsfor conditional class merging in TypeScript utility code
Files:
web/src/api/types.ts
🧠 Learnings (13)
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)
Applied to files:
src/synthorg/api/controllers/setup_models.pysrc/synthorg/budget/quota.pysrc/synthorg/core/task.pysrc/synthorg/core/project.pysrc/synthorg/budget/optimizer.pysrc/synthorg/templates/schema.pysrc/synthorg/core/role.pytests/unit/budget/test_enforcer.pysrc/synthorg/api/dto.pysrc/synthorg/providers/routing/models.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/hierarchy.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/api/controllers/budget.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/quota.pysrc/synthorg/core/task.pysrc/synthorg/budget/optimizer.pysrc/synthorg/templates/schema.pysrc/synthorg/core/role.pytests/unit/budget/test_enforcer.pysrc/synthorg/api/dto.pysrc/synthorg/providers/routing/models.pysrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/hierarchy.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/api/controllers/budget.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/optimizer.pytests/unit/budget/test_enforcer.pyweb/src/api/types.tssrc/synthorg/api/controllers/analytics.pysrc/synthorg/budget/_enforcer_helpers.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising
Applied to files:
tests/unit/budget/test_enforcer.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:
tests/unit/budget/test_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:
tests/unit/budget/test_enforcer.py
📚 Learning: 2026-03-26T21:34:31.847Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T21:34:31.847Z
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:
tests/unit/budget/test_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:
tests/unit/budget/test_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:
tests/unit/budget/test_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:
tests/unit/budget/test_enforcer.py
📚 Learning: 2026-03-15T11:48:14.867Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T11:48:14.867Z
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_helpers.py
📚 Learning: 2026-03-26T21:34:31.847Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T21:34:31.847Z
Learning: Applies to **/*.py : Create new objects instead of mutating existing ones; for non-Pydantic internal collections use `copy.deepcopy()` at construction and `MappingProxyType` wrapping for read-only enforcement
Applied to files:
src/synthorg/budget/_enforcer_helpers.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/api/controllers/budget.py
🔇 Additional comments (13)
src/synthorg/hr/models.py (1)
40-40: Documentation update is consistent and clear.The docstring and
Fielddescription now match and clearly indicate base-currency semantics forestimated_monthly_cost.Also applies to: 61-61
src/synthorg/budget/hierarchy.py (1)
102-110: Doc wording update is clear and aligned with base-currency semantics.The
total_monthlydocstring and field description now better distinguish stored base currency from display currency behavior.src/synthorg/hr/performance/models.py (4)
37-37: Documentation clarification is accurate.The updated docstring correctly specifies that the
cost_usdfield stores values in USD as the base currency, consistent with the PR's display-only currency configuration approach.
63-66: Field description now matches docstring.The updated Field description correctly aligns with the class docstring (line 37) and addresses the past review concern about inconsistency. This ensures OpenAPI/JSON schema consumers see the authoritative "(base currency)" clarification.
221-221: Documentation clarification is accurate.The updated docstring correctly specifies the base currency semantics, consistent with the
TaskMetricRecord.cost_usddocumentation pattern.
261-264: Field description now matches docstring.The updated Field description correctly aligns with the class docstring (line 221), addressing the past review concern and ensuring schema consistency.
src/synthorg/core/role.py (1)
40-40: Documentation accurately clarifies base currency semantics.The wording "in USD (base currency)" correctly addresses the prior review concern by making clear that
budget_limitstores values in the base currency (USD), not the configured display currency. The explicit mention of USD and the "(base currency)" clarification effectively distinguish this from display-only currency formatting introduced elsewhere in the PR.Also applies to: 60-60
web/src/api/types.ts (1)
394-394: LGTM! Currency fields correctly added to response DTOs.The
currency: stringadditions toDailySummary,PeriodSummary,BudgetConfig,AgentSpending,OverviewMetrics, andForecastResponsealign with the backend Pydantic models that expose ISO 4217 currency codes. This resolves the previously flagged missingcurrencyfield inForecastResponse.Also applies to: 403-403, 426-426, 432-432, 447-447, 480-480
src/synthorg/api/controllers/analytics.py (2)
79-92: LGTM! Field descriptions correctly clarify base currency semantics.The updated descriptions for
total_cost_usdandbudget_remaining_usdappropriately clarify these values are in "USD (base currency)", which aligns with the PR's display-only currency design where internal storage remains in the base currency.
384-420: LGTM! Currency correctly threaded through overview assembly.The
budget_cfgfetch and currency propagation toOverviewMetricsis properly implemented. The resolver guarantees a valid ISO 4217 currency code per the relevant context snippet.src/synthorg/providers/base.py (1)
339-342: LGTM! Docstrings now clearly specify base currency semantics.The updated parameter descriptions for
cost_per_1k_inputandcost_per_1k_outputappropriately clarify that these rates are "in USD (base currency)", which resolves ambiguity about the currency context for cost calculations that populateTokenUsage.cost_usd.src/synthorg/config/schema.py (1)
50-51: LGTM! Provider cost field documentation correctly clarifies base currency.The updated docstring attributes and Field descriptions for
cost_per_1k_inputandcost_per_1k_outputnow explicitly state "in USD (base currency)", resolving the previously flagged ambiguity about what currency users should specify when configuring provider costs.Also applies to: 63-72
tests/unit/budget/test_enforcer.py (1)
941-955: Correct patch target after the helper extraction.Patching
synthorg.budget._enforcer_helpers.loggerkeeps the dedup test pointed at the logger that_emit_alert()now uses.
| budget_cfg = await app_state.config_resolver.get_budget_config() | ||
| currency = budget_cfg.currency | ||
| records = await app_state.cost_tracker.get_records( | ||
| agent_id=agent_id, | ||
| task_id=task_id, | ||
| ) | ||
| daily, period = _build_summaries(records) | ||
| daily, period = _build_summaries(records, currency=currency) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Run config lookup and tracker queries in parallel.
get_budget_config() is independent from both get_records() and get_agent_cost(), so awaiting them sequentially adds avoidable endpoint latency. A small asyncio.TaskGroup in each handler keeps the code structured and matches the repo’s concurrency guideline.
♻️ Example for `list_cost_records()`
+import asyncio
...
- budget_cfg = await app_state.config_resolver.get_budget_config()
- currency = budget_cfg.currency
- records = await app_state.cost_tracker.get_records(
- agent_id=agent_id,
- task_id=task_id,
- )
+ async with asyncio.TaskGroup() as tg:
+ budget_task = tg.create_task(app_state.config_resolver.get_budget_config())
+ records_task = tg.create_task(
+ app_state.cost_tracker.get_records(
+ agent_id=agent_id,
+ task_id=task_id,
+ )
+ )
+ budget_cfg = budget_task.result()
+ currency = budget_cfg.currency
+ records = records_task.result()Apply the same pattern in get_agent_spending(). As per coding guidelines, "Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls) over bare create_task for structured concurrency".
Also applies to: 339-345
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/api/controllers/budget.py` around lines 301 - 307, The current
sequential awaits for app_state.config_resolver.get_budget_config(),
app_state.cost_tracker.get_records(...), and
app_state.cost_tracker.get_agent_cost(...) in get_agent_spending() add avoidable
latency; run the independent get_budget_config() in parallel with the tracker
calls using an asyncio.TaskGroup: spawn tasks for get_budget_config(),
get_records(agent_id, task_id) and get_agent_cost(agent_id) (or the exact two
tracker calls referenced around lines 339-345), await them all, then pass the
resolved currency and records into _build_summaries(); ensure you replace the
sequential awaits with the TaskGroup fan-out/fan-in pattern and use the task
results for currency, records, and agent cost.
| assigned_to: Optional assignee agent ID. | ||
| estimated_complexity: Complexity estimate. | ||
| budget_limit: Maximum USD spend. | ||
| budget_limit: Maximum spend in USD (base currency). |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let me check the structure of the file and locate the relevant lines
head -250 src/synthorg/api/dto.py | tail -30Repository: Aureliolo/synthorg
Length of output: 1022
🏁 Script executed:
# Let me get a better view of the CreateTaskRequest class and the budget_limit field
sed -n '200,250p' src/synthorg/api/dto.pyRepository: Aureliolo/synthorg
Length of output: 1636
🏁 Script executed:
# Also check the actual line 241 where budget_limit is defined
sed -n '235,245p' src/synthorg/api/dto.pyRepository: Aureliolo/synthorg
Length of output: 406
🏁 Script executed:
# Search for CreateTaskRequest class to understand its full context
ast-grep --pattern 'class CreateTaskRequest {
$$$
}'Repository: Aureliolo/synthorg
Length of output: 44
🏁 Script executed:
# Check how Field descriptions are used elsewhere in the codebase
rg 'Field\(' src/synthorg/api/dto.py -A 3 | head -50Repository: Aureliolo/synthorg
Length of output: 1666
🏁 Script executed:
# Let me check if there's any custom schema generation or validation that might use docstrings
rg 'model_json_schema|json_schema|description' src/synthorg/api/dto.py -B 2 -A 2Repository: Aureliolo/synthorg
Length of output: 2898
🏁 Script executed:
# Verify Pydantic v2 behavior and check if there are any schema_extra or other config modifications
rg 'ConfigDict|json_schema_extra|description_field' src/synthorg/api/dto.pyRepository: Aureliolo/synthorg
Length of output: 1109
🏁 Script executed:
# Check how UpdateTaskRequest handles budget_limit to see the pattern
sed -n '250,280p' src/synthorg/api/dto.pyRepository: Aureliolo/synthorg
Length of output: 1056
🏁 Script executed:
# Verify Pydantic v2's schema generation behavior with docstrings
python3 <<'EOF'
from pydantic import BaseModel, Field
# Test 1: Class docstring vs Field description
class TestModel1(BaseModel):
"""Test model.
Attributes:
value: This is the value in docstring.
"""
value: float = Field(default=0.0)
class TestModel2(BaseModel):
"""Test model.
Attributes:
value: This is the value in docstring.
"""
value: float = Field(default=0.0, description="This is the value in Field")
# Check schema generation
schema1 = TestModel1.model_json_schema()
schema2 = TestModel2.model_json_schema()
print("=== Schema without Field description ===")
print(schema1['properties']['value'])
print("\n=== Schema with Field description ===")
print(schema2['properties']['value'])
EOFRepository: Aureliolo/synthorg
Length of output: 292
🏁 Script executed:
# Verify there are no other budget_limit Field definitions or overrides elsewhere
rg 'budget_limit' src/synthorg/api/dto.pyRepository: Aureliolo/synthorg
Length of output: 275
🏁 Script executed:
# Check if there's any automatic schema processing or generation that might handle class docstrings
rg 'json_schema|schema_extra|Field description' src/synthorg/api/ -lRepository: Aureliolo/synthorg
Length of output: 89
🏁 Script executed:
# Check the openapi.py file for any custom schema generation
cat src/synthorg/api/openapi.pyRepository: Aureliolo/synthorg
Length of output: 23101
Add description parameter to budget_limit field.
In Pydantic v2, class docstring Attributes: sections do not propagate to the generated JSON schema. Only Field(description=...) appears in the schema and API documentation. Currently, clients cannot see that budget_limit represents USD spend.
Fix
- budget_limit: float = Field(default=0.0, ge=0.0)
+ budget_limit: float = Field(
+ default=0.0,
+ ge=0.0,
+ description="Maximum spend in USD (base currency)",
+ )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/api/dto.py` at line 228, The budget_limit field needs an
explicit Pydantic Field description so the JSON schema/API docs show it
represents USD; update the model's budget_limit declaration to use
Field(description="Maximum spend in USD (base currency).") (and add/ensure the
Field import from pydantic is present) so the schema includes the description
for clients.
| if set(_raw_order) != set(BudgetAlertLevel): | ||
| msg = ( | ||
| f"_ALERT_LEVEL_ORDER keys {set(_raw_order)} do not match " | ||
| f"BudgetAlertLevel members {set(BudgetAlertLevel)}" | ||
| ) | ||
| raise RuntimeError(msg) | ||
| if len(set(_raw_order.values())) != len(BudgetAlertLevel): | ||
| msg = ( | ||
| f"_ALERT_LEVEL_ORDER values must be unique, got: {sorted(_raw_order.values())}" | ||
| ) | ||
| raise RuntimeError(msg) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Validate the alert ordering itself, not just key coverage.
The current guard only checks that all keys exist and that the ints are unique. A future edit like swapping WARNING/CRITICAL or assigning NORMAL: 99 would still pass import-time validation while breaking _emit_alert() ordering. Compare against the expected ordinal mapping, or at least enforce monotonic severity order, before freezing it.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/budget/_enforcer_helpers.py` around lines 143 - 153, The current
validation only checks keys and uniqueness in _raw_order/_ALERT_LEVEL_ORDER but
not whether numeric values respect the expected severity ordering used by
_emit_alert; update the validation to enforce monotonic severity order by
iterating BudgetAlertLevel in its defined ordinal order and verifying that the
mapped integer values are strictly increasing (or match a canonical ordinal
mapping) for each successive member; if any value violates the monotonic
increase, raise a RuntimeError with a clear message referencing
_ALERT_LEVEL_ORDER and BudgetAlertLevel so incorrect swaps or out-of-range
assignments are rejected at import time.
| Returns: | ||
| Sync callable returning ``True`` when budget is exhausted. | ||
| """ | ||
| last_alert: list[BudgetAlertLevel] = [BudgetAlertLevel.NORMAL] |
There was a problem hiding this comment.
Persist monthly alert dedup state outside the per-task closure.
Line 253 resets last_alert to NORMAL every time _build_checker_closure() runs. src/synthorg/budget/enforcer.py:434-471 creates a fresh checker per task, so once monthly spend is already above a threshold, the first check of every later task re-emits BUDGET_ALERT_THRESHOLD_CROSSED even though no new threshold was crossed. Store the last-emitted level on enforcer or billing-period state instead of inside the closure.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/budget/_enforcer_helpers.py` at line 253, The closure-local
variable last_alert in _build_checker_closure() is reset to NORMAL for every
checker instance, causing repeated BUDGET_ALERT_THRESHOLD_CROSSED emissions
across tasks; move persistent state out of the closure by storing the
last-emitted BudgetAlertLevel on a shared object (e.g., the Enforcer instance or
the billing-period state) and have _build_checker_closure() read/update that
shared field instead of using the local last_alert list so threshold crossings
are deduplicated across tasks.
| # ── Currency property ──────────────────────────────────────────────── | ||
|
|
||
|
|
||
| @pytest.mark.unit | ||
| class TestCurrencyProperty: | ||
| """Tests for BudgetEnforcer.currency property.""" | ||
|
|
||
| @pytest.mark.parametrize( | ||
| ("currency_override", "expected"), | ||
| [ | ||
| (None, DEFAULT_CURRENCY), | ||
| ("GBP", "GBP"), | ||
| ], | ||
| ids=["default", "custom"], | ||
| ) | ||
| def test_returns_configured_or_default( | ||
| self, | ||
| currency_override: str | None, | ||
| expected: str, | ||
| ) -> None: | ||
| if currency_override is None: | ||
| cfg = _make_budget_config(total_monthly=100.0) | ||
| else: | ||
| cfg = BudgetConfig(currency=currency_override) | ||
| enforcer = BudgetEnforcer( | ||
| budget_config=cfg, | ||
| cost_tracker=CostTracker(budget_config=cfg), | ||
| ) | ||
| assert enforcer.currency == expected |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Move these currency cases into a dedicated test module.
tests/unit/budget/test_enforcer.py is already well past the 800-line cap, and adding more currency-focused coverage here makes the file harder to navigate. Please move these cases into something like tests/unit/budget/test_enforcer_currency.py.
As per coding guidelines, "Keep functions under 50 lines and files under 800 lines".
Also applies to: 237-260
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/unit/budget/test_enforcer.py` around lines 154 - 182, The
currency-focused parametrized tests (TestCurrencyProperty and its
test_returns_configured_or_default) should be moved out of the oversized
tests/unit/budget/test_enforcer.py into a new dedicated module (e.g.,
tests/unit/budget/test_enforcer_currency.py); copy the TestCurrencyProperty
class and any other currency cases referenced (also from the block at lines
noted around 237-260) into that new file, update imports so BudgetEnforcer,
BudgetConfig, CostTracker, and DEFAULT_CURRENCY are imported in the new module,
and leave the original file with those tests removed to keep it under the
800-line guideline.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/synthorg/api/dto.py (1)
463-471: 🧹 Nitpick | 🔵 TrivialAdd
currencytoCoordinationResultResponseclass attribute docs.The new field is present in the model but missing from the
Attributes:section.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/dto.py` around lines 463 - 471, The docstring for CoordinationResultResponse is missing the new currency field; update the Attributes section of the CoordinationResultResponse class to add a line documenting currency (e.g., "currency: Currency code used for total_cost_usd.") so the model attribute list matches the actual class definition and clarifies the semantics of the currency field.
♻️ Duplicate comments (1)
src/synthorg/api/dto.py (1)
229-242:⚠️ Potential issue | 🟡 MinorDocstring-only update won’t expose
budget_limitunits in OpenAPI schema.Line 229 updates class doc text, but Line 242 still omits
Field(description=...),
so generated schema/docs forbudget_limitremain underspecified.Suggested patch
- budget_limit: float = Field(default=0.0, ge=0.0) + budget_limit: float = Field( + default=0.0, + ge=0.0, + description="Maximum spend in base currency units", + )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/dto.py` around lines 229 - 242, The OpenAPI schema lacks units for the budget_limit field; update the budget_limit Field declaration (the Field used with budget_limit) to include a description string that specifies the units (e.g., "Maximum spend in USD (base currency)") and any constraints (ge=0.0) so the generated docs show units and intent; locate the budget_limit attribute in the DTO class where Field(default=0.0, ge=0.0) is used and add description="Maximum spend in USD (base currency)" to that Field call.
🤖 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/coordination.py`:
- Around line 185-186: The code currently emits API_COORDINATION_FAILED for a
benign budget-config fallback which will skew failure metrics; define and use a
dedicated non-failure event constant (e.g. API_COORDINATION_FALLBACK or
COORDINATION_BUDGET_FALLBACK) instead of API_COORDINATION_FAILED, update the
coordinator call that currently passes API_COORDINATION_FAILED with
error="budget config unavailable, using default currency" to use the new
fallback constant, and add the new constant to the event/constants definition so
downstream metrics/logging treat it as a degraded/fallback event rather than a
failure.
- Around line 183-189: The bare except in the currency-fallback path swallows
fatal exceptions; change it to "except Exception as e:" then re-raise fatal
exceptions (e.g., MemoryError, RecursionError, SystemExit, KeyboardInterrupt)
the same way _execute and _publish_ws_event do, before calling
logger.warning(API_COORDINATION_FAILED, error=..., exc_info=True) and falling
back to DEFAULT_CURRENCY; reference the existing symbols
API_COORDINATION_FAILED, DEFAULT_CURRENCY, and logger to locate and update the
handler.
In `@src/synthorg/api/dto.py`:
- Around line 479-485: The currency field currently uses a plain str with manual
validators (min_length, max_length, pattern) and should use the NotBlankStr type
from core.types to follow DTO identifier conventions; replace the type
annotation on currency with NotBlankStr (keeping default=DEFAULT_CURRENCY and
the description) and remove the redundant min_length/max_length/pattern
validators so the field uses NotBlankStr's validation semantics; update the
import list in this module to include NotBlankStr if not already imported and
adjust any references to Field(...) for the currency attribute accordingly.
---
Outside diff comments:
In `@src/synthorg/api/dto.py`:
- Around line 463-471: The docstring for CoordinationResultResponse is missing
the new currency field; update the Attributes section of the
CoordinationResultResponse class to add a line documenting currency (e.g.,
"currency: Currency code used for total_cost_usd.") so the model attribute list
matches the actual class definition and clarifies the semantics of the currency
field.
---
Duplicate comments:
In `@src/synthorg/api/dto.py`:
- Around line 229-242: The OpenAPI schema lacks units for the budget_limit
field; update the budget_limit Field declaration (the Field used with
budget_limit) to include a description string that specifies the units (e.g.,
"Maximum spend in USD (base currency)") and any constraints (ge=0.0) so the
generated docs show units and intent; locate the budget_limit attribute in the
DTO class where Field(default=0.0, ge=0.0) is used and add description="Maximum
spend in USD (base currency)" to that Field call.
🪄 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: 1ccdf331-4b99-4026-af84-ae1e87ed1b1e
📒 Files selected for processing (2)
src/synthorg/api/controllers/coordination.pysrc/synthorg/api/dto.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Build Sandbox
- GitHub Check: Build Web
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Backend
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotations-- Python 3.14 has PEP 649 native lazy annotations
Use PEP 758 except syntax:except A, B:(no parentheses) instead ofexcept (A, B):-- ruff enforces this on Python 3.14
All public functions and classes must have type hints; mypy strict mode is enforced
Use Google-style docstrings on all public classes and functions; enforced by ruff D rules
Create new objects instead of mutating existing ones; for non-Pydantic internal collections usecopy.deepcopy()at construction andMappingProxyTypewrapping for read-only enforcement
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls) over barecreate_taskfor structured concurrency
Keep functions under 50 lines and files under 800 lines
Line length must be 88 characters as enforced by ruff
Validate explicitly at system boundaries (user input, external APIs, config files); never silently swallow errors
Files:
src/synthorg/api/dto.pysrc/synthorg/api/controllers/coordination.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic must import and use logger:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)
Never useimport logging,logging.getLogger(), orprint()in application code; only use the structured logger fromsynthorg.observability
Always use the logger variable namelogger(not_logger, notlog)
Use event name constants fromsynthorg.observability.events.<domain>modules (e.g.,API_REQUEST_STARTEDfromevents.api); import directly and always use structured kwargs:logger.info(EVENT, key=value)never string formatting
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
Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (withmodel_copy(update=...)) for runtime state that evolves
Use Pydantic v2 with@computed_fieldfor derived values instead of storing redundant fields; useNotBlankStr(fromcore.types) for all identifier/name fields instead of manual validators
All provider calls go throughBaseCompletionProviderwhich applies retry + rate limiting automatically; never implement retry logic in driver subclasses or calling code
Validate at system boundaries: user input, external APIs, config files; handle errors explicitly, never silently swallow
Files:
src/synthorg/api/dto.pysrc/synthorg/api/controllers/coordination.py
🧠 Learnings (6)
📓 Common learnings
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-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.
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/api/**/*.py : REST API: Litestar framework, controllers with guards, channels for WebSocket, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. RFC 9457 structured errors (ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation).
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately.
Applied to files:
src/synthorg/api/dto.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/api/controllers/coordination.py
🔇 Additional comments (1)
src/synthorg/api/controllers/coordination.py (1)
86-98: Currency threading into coordination response is clean and consistent.Passing
currencythrough_map_result_to_response()and setting it in
CoordinationResultResponseis a solid integration point.
| except Exception: | ||
| logger.warning( | ||
| API_COORDINATION_FAILED, | ||
| error="budget config unavailable, using default currency", | ||
| exc_info=True, | ||
| ) | ||
| currency = DEFAULT_CURRENCY |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n src/synthorg/api/controllers/coordination.py | head -200 | tail -40Repository: Aureliolo/synthorg
Length of output: 1554
🏁 Script executed:
# Search for exception handling patterns in the file
rg "except" src/synthorg/api/controllers/coordination.py -B 2 -A 3Repository: Aureliolo/synthorg
Length of output: 1237
🏁 Script executed:
# Get the full context around the flagged lines
sed -n '170,200p' src/synthorg/api/controllers/coordination.py | cat -nRepository: Aureliolo/synthorg
Length of output: 1190
Guard fatal exceptions in currency fallback path, consistent with module pattern.
Lines 180–189 use a bare except Exception: that catches and swallows MemoryError and RecursionError. Other exception handlers in this module (_execute and _publish_ws_event) explicitly re-raise fatal exceptions. Apply the same guard here.
Suggested patch
try:
budget_cfg = await app_state.config_resolver.get_budget_config()
currency = budget_cfg.currency
+ except MemoryError, RecursionError:
+ raise
except Exception:
logger.warning(
API_COORDINATION_FAILED,
error="budget config unavailable, using default currency",
exc_info=True,
)
currency = DEFAULT_CURRENCY🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/api/controllers/coordination.py` around lines 183 - 189, The
bare except in the currency-fallback path swallows fatal exceptions; change it
to "except Exception as e:" then re-raise fatal exceptions (e.g., MemoryError,
RecursionError, SystemExit, KeyboardInterrupt) the same way _execute and
_publish_ws_event do, before calling logger.warning(API_COORDINATION_FAILED,
error=..., exc_info=True) and falling back to DEFAULT_CURRENCY; reference the
existing symbols API_COORDINATION_FAILED, DEFAULT_CURRENCY, and logger to locate
and update the handler.
| API_COORDINATION_FAILED, | ||
| error="budget config unavailable, using default currency", |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Use a dedicated non-failure event for config fallback logging.
Logging this path with API_COORDINATION_FAILED (Line 185) can skew failure-rate
metrics even when coordination succeeds. Consider a separate fallback/degraded
event constant.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/api/controllers/coordination.py` around lines 185 - 186, The
code currently emits API_COORDINATION_FAILED for a benign budget-config fallback
which will skew failure metrics; define and use a dedicated non-failure event
constant (e.g. API_COORDINATION_FALLBACK or COORDINATION_BUDGET_FALLBACK)
instead of API_COORDINATION_FAILED, update the coordinator call that currently
passes API_COORDINATION_FAILED with error="budget config unavailable, using
default currency" to use the new fallback constant, and add the new constant to
the event/constants definition so downstream metrics/logging treat it as a
degraded/fallback event rather than a failure.
| currency: str = Field( | ||
| default=DEFAULT_CURRENCY, | ||
| min_length=3, | ||
| max_length=3, | ||
| pattern=r"^[A-Z]{3}$", | ||
| description="ISO 4217 currency code", | ||
| ) |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use NotBlankStr for currency to match DTO identifier typing standards.
currency is an identifier-like code and should use NotBlankStr consistently.
Suggested patch
- currency: str = Field(
+ currency: NotBlankStr = Field(
default=DEFAULT_CURRENCY,
min_length=3,
max_length=3,
pattern=r"^[A-Z]{3}$",
description="ISO 4217 currency code",
)As per coding guidelines, "Use NotBlankStr (from core.types) for all identifier/name fields instead of manual validators".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/api/dto.py` around lines 479 - 485, The currency field currently
uses a plain str with manual validators (min_length, max_length, pattern) and
should use the NotBlankStr type from core.types to follow DTO identifier
conventions; replace the type annotation on currency with NotBlankStr (keeping
default=DEFAULT_CURRENCY and the description) and remove the redundant
min_length/max_length/pattern validators so the field uses NotBlankStr's
validation semantics; update the import list in this module to include
NotBlankStr if not already imported and adjust any references to Field(...) for
the currency attribute accordingly.
Add a budget.currency setting (ISO 4217, default EUR) that controls
display formatting across the entire stack. Internal storage field
names (cost_usd) are unchanged for backward compatibility.
- Add currency field to BudgetConfig model with regex validation
- Register budget/currency setting definition (STRING, ^[A-Z]{3}$)
- Wire currency through ConfigResolver.get_budget_config()
- New budget/currency.py module: format_cost(), format_cost_detail(),
get_currency_symbol() with symbol table for ~25 common currencies
- Replace hardcoded $ in Jinja2 prompt templates with {{ currency_symbol }}
- Thread currency_symbol through prompt.py call chain (build_system_prompt,
_render_with_trimming, _render_and_estimate, _build_template_context,
format_task_instruction)
- Update budget enforcer error messages to use format_cost()
- Update HR activity descriptions to use format_cost_detail()
- Add currency field to API budget response models (AgentSpending,
DailySummary, PeriodSummary, CostRecordListResponse)
- Frontend: formatCurrency() accepts optional currencyCode parameter
- Frontend: settings store gains currency state with fetchCurrency()
- Update all "Cost in USD" docstrings to "Cost in configured currency"
- Full test coverage: test_currency.py + updated existing tests
Closes #810
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-reviewed by 6 agents, 16 findings addressed: - Pass currency to merge_activity_timeline callers in activities and agents controllers (was silently using default) - Add currency field to OverviewMetrics and DepartmentHealth API response models for consistency with budget models - Replace hardcoded $ in optimizer.py and _optimizer_helpers.py with format_cost() using configured currency - Change prompt chain from currency_symbol to currency code -- enables format_cost() for zero-decimal currency handling (JPY) - Fix agent_engine fallback from hardcoded "$" to DEFAULT_CURRENCY - Add DEFAULT_CURRENCY constant, MappingProxyType for CURRENCY_SYMBOLS - Fix format_cost negative values: -$10.50 instead of $-10.50 - Add try/catch to frontend formatCurrency for invalid codes - Add error handling + validation to settings store fetchCurrency - Update frontend types.ts with currency field on response models - Update ~30 remaining "USD" docstrings across the codebase - Add tests: BudgetConfig currency validation, format_task_instruction with non-default currency, merge_activity_timeline with currency, BudgetEnforcer.currency property and error message formatting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Change default currency from EUR to USD for backward compatibility - Replace ZERO_DECIMAL_CURRENCIES with MINOR_UNITS dict (supports 0/2/3 decimal currencies including BHD, KWD, OMR) - Fix department health endpoint always returning EUR regardless of config - Add try/except resilience around budget config fetch in agents controller - Replace hardcoded $ in optimizer/downgrade recommendation strings with format_cost() calls - Add ISO 4217 validation (pattern, min/max length) to API response model currency fields - Add currency field to CoordinationResultResponse and ForecastResponse - Add currency field to frontend CostRecordListResponseBody/Result - Add NaN/Infinity guard and negative precision validation to format_cost() - Remove hardcoded minimumFractionDigits from frontend Intl.NumberFormat - Add error logging to frontend settings store fetchCurrency - Fix _usd field docstrings: say "USD (base currency)" not "configured currency" for internal storage fields - Replace literal "EUR" defaults with DEFAULT_CURRENCY constant in 13 function signatures across activity, prompt, optimizer helpers - Disambiguate CNY symbol from JPY (CN¥ vs ¥) - Extract enforcer helpers to _enforcer_helpers.py (846 -> 524 lines) - Extract prompt helpers to _prompt_helpers.py (840 -> 772 lines) - Parametrize test_currency and test_config rejection tests - Update all test assertions from EUR to USD default - Update design docs (operations.md, engine.md, CLAUDE.md) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add try/except resilience around get_budget_config() in activities controller (mirrors agents.py pattern) - Replace hardcoded "USD" defaults with DEFAULT_CURRENCY constant in all API response model Field definitions (budget, analytics, departments) - Fix ForecastResponse.currency never populated in forecast endpoint - Fix fallback toFixed(2) in frontend formatCurrency to respect zero-decimal and three-decimal currencies - Add JPY no-decimal assertion to frontend format test - Clarify base-currency semantics in docstrings: cost_tiers, tracker, config/schema, performance models, capabilities, routing models, company budget_monthly, task_engine budget_limit - Update engine.md budget_limit comment Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add currency field to frontend ForecastResponse TypeScript interface - Make _ALERT_LEVEL_ORDER immutable via MappingProxyType - Pass currency=self._budget_config.currency to _build_downgrade_recommendation - Fix all remaining "configured currency" docstrings on numeric amount fields to say "USD (base currency)" (14 occurrences across 7 files) - Fix redundant "per 1,000 tokens" in routing model docstrings - Add "USD (base currency)" to remaining_budget Field description - Clarify base-currency semantics in role.py, task.py, providers/base.py, config/schema.py, templates/schema.py, performance/models.py - Parametrize enforcer currency test with DEFAULT_CURRENCY constant Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tion - Replace hardcoded "USD" with DEFAULT_CURRENCY in CoordinationResultResponse - Pass currency from budget config to _map_result_to_response in coordination controller (with try/except fallback) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The default currency was incorrectly changed to USD based on CodeRabbit feedback that conflicted with the user's explicit decision to keep EUR. Revert DEFAULT_CURRENCY, BudgetConfig, settings definition, frontend store, and formatCurrency defaults back to EUR. Update all test assertions accordingly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
b593a17 to
be13b79
Compare
🤖 I have created a release *beep* *boop* --- #MAJOR CHANGES; We got a somewhat working webui :) ## [0.5.0](v0.4.9...v0.5.0) (2026-03-30) ### Features * add analytics trends and budget forecast API endpoints ([#798](#798)) ([16b61f5](16b61f5)) * add department policies to default templates ([#852](#852)) ([7a41548](7a41548)) * add remaining activity event types (task_started, tool_used, delegation, cost_incurred) ([#832](#832)) ([4252fac](4252fac)) * agent performance, activity, and history API endpoints ([#811](#811)) ([9b75c1d](9b75c1d)) * Agent Profiles and Detail pages (biography, career, performance) ([#874](#874)) ([62d7880](62d7880)) * app shell, Storybook, and CI/CD pipeline ([#819](#819)) ([d4dde90](d4dde90)) * Approvals page with risk grouping, urgency indicators, batch actions ([#889](#889)) ([4e9673d](4e9673d)) * Budget Panel page (P&L dashboard, breakdown charts, forecast) ([#890](#890)) ([b63b0f1](b63b0f1)) * build infrastructure layer (API client, auth, WebSocket) ([#815](#815)) ([9f01d3e](9f01d3e)) * CLI global options infrastructure, UI modes, exit codes, env vars ([#891](#891)) ([fef4fc5](fef4fc5)) * CodeMirror editor and theme preferences toggle ([#905](#905), [#807](#807)) ([#909](#909)) ([41fbedc](41fbedc)) * Company page (department/agent management) ([#888](#888)) ([cfb88b0](cfb88b0)) * comprehensive hint coverage across all CLI commands ([#900](#900)) ([937974e](937974e)) * config system extensions, per-command flags for init/start/stop/status/logs ([#895](#895)) ([32f83fe](32f83fe)) * configurable currency system replacing hardcoded USD ([#854](#854)) ([b372551](b372551)) * Dashboard page (metric cards, activity feed, budget burn) ([#861](#861)) ([7d519d5](7d519d5)) * department health, provider status, and activity feed endpoints ([#818](#818)) ([6d5f196](6d5f196)) * design tokens and core UI components ([#833](#833)) ([ed887f2](ed887f2)) * extend approval, meeting, and budget API responses ([#834](#834)) ([31472bf](31472bf)) * frontend polish -- real-time UX, accessibility, responsive, performance ([#790](#790), [#792](#792), [#791](#791), [#793](#793)) ([#917](#917)) ([f04a537](f04a537)) * implement human roles and access control levels ([#856](#856)) ([d6d8a06](d6d8a06)) * implement semantic conflict detection in workspace merge ([#860](#860)) ([d97283b](d97283b)) * interaction components and animation patterns ([#853](#853)) ([82d4b01](82d4b01)) * Login page + first-run bootstrap + Company page ([#789](#789), [#888](#888)) ([#896](#896)) ([8758e8d](8758e8d)) * Meetings page with timeline viz, token bars, contribution formatting ([#788](#788)) ([#904](#904)) ([b207f46](b207f46)) * Messages page with threading, channel badges, sender indicators ([#787](#787)) ([#903](#903)) ([28293ad](28293ad)) * Org Chart force-directed view and drag-drop reassignment ([#872](#872), [#873](#873)) ([#912](#912)) ([a68a938](a68a938)) * Org Chart page (living nodes, status, CRUD, department health) ([#870](#870)) ([0acbdae](0acbdae)) * per-command flags for remaining commands, auto-behavior wiring, help/discoverability ([#897](#897)) ([3f7afa2](3f7afa2)) * Providers page with backend rework -- health, CRUD, subscription auth ([#893](#893)) ([9f8dd98](9f8dd98)) * scaffold React + Vite + TypeScript + Tailwind project ([#799](#799)) ([bd151aa](bd151aa)) * Settings page with search, dependency indicators, grouped rendering ([#784](#784)) ([#902](#902)) ([a7b9870](a7b9870)) * Setup Wizard rebuild with template comparison, cost estimator, theme customization ([#879](#879)) ([ae8b50b](ae8b50b)) * setup wizard UX -- template filters, card metadata, provider form reuse ([#910](#910)) ([7f04676](7f04676)) * setup wizard UX overhaul -- mode choice, step reorder, provider fixes ([#907](#907)) ([ee964c4](ee964c4)) * structured ModelRequirement in template agent configs ([#795](#795)) ([7433548](7433548)) * Task Board page (rich Kanban, filtering, dependency viz) ([#871](#871)) ([04a19b0](04a19b0)) ### Bug Fixes * align frontend types with backend and debounce WS refetches ([#916](#916)) ([134c11b](134c11b)) * auto-cleanup targets newly pulled images instead of old ones ([#884](#884)) ([50e6591](50e6591)) * correct wipe backup-skip flow and harden error handling ([#808](#808)) ([c05860f](c05860f)) * improve provider setup in wizard, subscription auth, dashboard bugs ([#914](#914)) ([87bf8e6](87bf8e6)) * improve update channel detection and add config get command ([#814](#814)) ([6b137f0](6b137f0)) * resolve all ESLint warnings, add zero-warnings enforcement ([#899](#899)) ([079b46a](079b46a)) * subscription auth uses api_key, base URL optional for cloud providers ([#915](#915)) ([f0098dd](f0098dd)) ### Refactoring * semantic analyzer cleanup -- shared filtering, concurrency, extraction ([#908](#908)) ([81372bf](81372bf)) ### Documentation * brand identity and UX design system from [#765](#765) exploration ([#804](#804)) ([389a9f4](389a9f4)) * page structure and information architecture for v0.5.0 dashboard ([#809](#809)) ([f8d6d4a](f8d6d4a)) * write UX design guidelines with WCAG-verified color system ([#816](#816)) ([4a4594e](4a4594e)) ### Tests * add unit tests for agent hooks and page components ([#875](#875)) ([#901](#901)) ([1d81546](1d81546)) ### CI/CD * bump actions/deploy-pages from 4.0.5 to 5.0.0 in the major group ([#831](#831)) ([01c19de](01c19de)) * bump astral-sh/setup-uv from 7.6.0 to 8.0.0 in /.github/actions/setup-python-uv in the all group ([#920](#920)) ([5f6ba54](5f6ba54)) * bump codecov/codecov-action from 5.5.3 to 6.0.0 in the major group ([#868](#868)) ([f22a181](f22a181)) * bump github/codeql-action from 4.34.1 to 4.35.0 in the all group ([#883](#883)) ([87a4890](87a4890)) * bump sigstore/cosign-installer from 4.1.0 to 4.1.1 in the minor-and-patch group ([#830](#830)) ([7a69050](7a69050)) * bump the all group with 3 updates ([#923](#923)) ([ff27c8e](ff27c8e)) * bump wrangler from 4.76.0 to 4.77.0 in /.github in the minor-and-patch group ([#822](#822)) ([07d43eb](07d43eb)) * bump wrangler from 4.77.0 to 4.78.0 in /.github in the all group ([#882](#882)) ([f84118d](f84118d)) ### Maintenance * add design system enforcement hook and component inventory ([#846](#846)) ([15abc43](15abc43)) * add dev-only auth bypass for frontend testing ([#885](#885)) ([6cdcd8a](6cdcd8a)) * add pre-push rebase check hook ([#855](#855)) ([b637a04](b637a04)) * backend hardening -- eviction/size-caps and model validation ([#911](#911)) ([81253d9](81253d9)) * bump axios from 1.13.6 to 1.14.0 in /web in the all group across 1 directory ([#922](#922)) ([b1b0232](b1b0232)) * bump brace-expansion from 5.0.4 to 5.0.5 in /web ([#862](#862)) ([ba4a565](ba4a565)) * bump eslint-plugin-react-refresh from 0.4.26 to 0.5.2 in /web ([#801](#801)) ([7574bb5](7574bb5)) * bump faker from 40.11.0 to 40.11.1 in the minor-and-patch group ([#803](#803)) ([14d322e](14d322e)) * bump https://github.com/astral-sh/ruff-pre-commit from v0.15.7 to 0.15.8 ([#864](#864)) ([f52901e](f52901e)) * bump nginxinc/nginx-unprivileged from `6582a34` to `f99cc61` in /docker/web in the all group ([#919](#919)) ([df85e4f](df85e4f)) * bump nginxinc/nginx-unprivileged from `ccbac1a` to `6582a34` in /docker/web ([#800](#800)) ([f4e9450](f4e9450)) * bump node from `44bcbf4` to `71be405` in /docker/sandbox ([#827](#827)) ([91bec67](91bec67)) * bump node from `5209bca` to `cf38e1f` in /docker/web ([#863](#863)) ([66d6043](66d6043)) * bump picomatch in /site ([#842](#842)) ([5f20bcc](5f20bcc)) * bump recharts 2->3 and @types/node 22->25 in /web ([#802](#802)) ([a908800](a908800)) * Bump requests from 2.32.5 to 2.33.0 ([#843](#843)) ([41daf69](41daf69)) * bump smol-toml from 1.6.0 to 1.6.1 in /site ([#826](#826)) ([3e5dbe4](3e5dbe4)) * bump the all group with 3 updates ([#921](#921)) ([7bace0b](7bace0b)) * bump the minor-and-patch group across 1 directory with 2 updates ([#829](#829)) ([93e611f](93e611f)) * bump the minor-and-patch group across 1 directory with 3 updates ([#841](#841)) ([7010c8e](7010c8e)) * bump the minor-and-patch group across 1 directory with 3 updates ([#869](#869)) ([548cee5](548cee5)) * bump the minor-and-patch group in /site with 2 updates ([#865](#865)) ([9558101](9558101)) * bump the minor-and-patch group with 2 updates ([#867](#867)) ([4830706](4830706)) * consolidate Dependabot groups to 1 PR per ecosystem ([06d2556](06d2556)) * consolidate Dependabot groups to 1 PR per ecosystem ([#881](#881)) ([06d2556](06d2556)) * improve worktree skill with full dep sync and status enhancements ([#906](#906)) ([772c625](772c625)) * remove Vue remnants and document framework decision ([#851](#851)) ([bf2adf6](bf2adf6)) * update web dependencies and fix brace-expansion CVE ([#880](#880)) ([a7a0ed6](a7a0ed6)) * upgrade to Storybook 10 and TypeScript 6 ([#845](#845)) ([52d95f2](52d95f2)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary
budget.currencysetting (ISO 4217, default EUR) controlling display formatting across the entire stack -- no conversion, display-onlysrc/synthorg/budget/currency.pymodule withformat_cost(),format_cost_detail(),get_currency_symbol(),DEFAULT_CURRENCY,MINOR_UNITS(0/2/3-decimal currencies), symbol table for ~25 common currenciesbuild_system_prompt-> Jinja2 templates ->format_task_instruction) with proper zero-decimal formatting$in budget enforcer errors, optimizer messages, anomaly descriptions, downgrade recommendations, and activity feed descriptionscurrencyfield to API response models:AgentSpending,DailySummary,PeriodSummary,CostRecordListResponse,OverviewMetrics,DepartmentHealth,ForecastResponse,CoordinationResultResponseformatCurrency()respects each currency's native minor-unit rules; settings store fetches currency with validation and error logging_usdfield docstrings -- internal storage fields say "USD (base currency)", display-layer fields carrycurrencyfield for formattingCURRENCY_SYMBOLSand_ALERT_LEVEL_ORDERuseMappingProxyType;MINOR_UNITSreplaces binaryZERO_DECIMAL_CURRENCIES_enforcer_helpers.py(846->524 lines) and_prompt_helpers.py(840->772 lines) to stay under 800-line limitget_budget_config()in activities, agents, and coordination controllersTest plan
test_currency.py(format_cost, format_cost_detail, get_currency_symbol, MINOR_UNITS, NaN/Infinity guards, negative precision, 3-decimal currencies)Post-reviewed by 15 local agents + CodeRabbit + Gemini across 4 review rounds.
Closes #810