feat: implement multi-project support -- engine orchestration (#242)#1153
feat: implement multi-project support -- engine orchestration (#242)#1153
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughAdds project-scoped functionality across the codebase: AgentEngine gains project validation and project-aware run/resume paths with ProjectNotFoundError and ProjectAgentNotMemberError; budget subsystem gains project-level pre-flight and in-flight checks with ProjectBudgetExhaustedError, project cost queries, and project-aware BudgetChecker closures; CostRecord now includes optional project_id and cost recording propagates it; Task assignment filters by project_team; new observability event constants for project queries/enforcement/validation; and numerous unit tests for tracker, enforcer, cost recording, assignment, and engine behaviors. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure that dependencies are being submitted on PR branches. Re-running this action after a short time may resolve the issue. See the documentation for more information and troubleshooting advice. Scanned FilesNone |
There was a problem hiding this comment.
Code Review
This pull request implements project-level budget enforcement and team membership validation. It introduces project_id tracking in cost records, adds project-specific query methods to the CostTracker, and updates the AgentEngine to perform pre-flight project validation and budget checks. Additionally, the task assignment service now includes project team filtering. Feedback identifies several critical syntax errors involving outdated Python 2 exception handling and recommends adding safeguards for null project identifiers to avoid runtime validation failures.
| except MemoryError, RecursionError: | ||
| raise |
There was a problem hiding this comment.
This except syntax is for Python 2. In Python 3, multiple exceptions must be grouped in a tuple, like except (MemoryError, RecursionError):. This will cause a SyntaxError.
This outdated syntax appears multiple times in this pull request (e.g., here and on line 633, and in agent_engine.py). Please correct all occurrences.
except (MemoryError, RecursionError):| except MemoryError, RecursionError: | ||
| logger.exception( |
| except ProjectNotFoundError, ProjectAgentNotMemberError: | ||
| # ProjectBudgetExhaustedError (from _validate_project) | ||
| # is a BudgetExhaustedError subclass -- intentionally | ||
| # caught by the handler below, not here. | ||
| raise |
There was a problem hiding this comment.
This except syntax is for Python 2 and will cause a SyntaxError in Python 3. Multiple exceptions should be grouped in a tuple, like except (ProjectNotFoundError, ProjectAgentNotMemberError):.
except (ProjectNotFoundError, ProjectAgentNotMemberError):
# ProjectBudgetExhaustedError (from _validate_project)
# is a BudgetExhaustedError subclass -- intentionally
# caught by the handler below, not here.
raise| ProjectAgentNotMemberError: When the agent is not in the | ||
| project team (non-empty team only). | ||
| """ | ||
| project = await self._project_repo.get(task.project) # type: ignore[union-attr] |
There was a problem hiding this comment.
If task.project can be None, this could lead to a runtime error. self._project_repo.get(None) might be called, and if it returns None, ProjectNotFoundError would be raised with project_id=None. This would violate the NotBlankStr constraint in its constructor.
Please add a check at the beginning of this method to handle cases where task.project is None or empty, for example by raising a ValueError or returning early.
There was a problem hiding this comment.
Pull request overview
Implements the engine-layer orchestration needed for multi-project support by threading project_id/project_budget through execution, enforcing project-scoped budgets, and restricting agent assignment to project teams.
Changes:
- Add AgentEngine project validation (existence + team membership) and project budget preflight/in-flight enforcement.
- Tag cost records with
project_idand extend CostTracker/BudgetEnforcer APIs for project-scoped aggregation and enforcement. - Add assignment-time filtering to restrict eligible agents to a project’s team, plus new observability events and documentation/tests.
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/synthorg/engine/agent_engine.py | Adds project validation + threads project context through execution pipeline for budget checking and cost recording. |
| src/synthorg/engine/errors.py | Introduces project-specific engine errors with generic messages + structured attributes. |
| src/synthorg/engine/cost_recording.py | Records execution costs with optional project_id. |
| src/synthorg/engine/assignment/models.py | Adds project_team to assignment request model. |
| src/synthorg/engine/assignment/service.py | Filters available agents by project_team and emits new assignment events. |
| src/synthorg/budget/cost_record.py | Adds optional project_id to CostRecord. |
| src/synthorg/budget/tracker.py | Adds project-scoped cost/records query helpers and project_id filtering in _filter_records. |
| src/synthorg/budget/enforcer.py | Adds preflight project budget enforcement and extends in-flight budget checker inputs. |
| src/synthorg/budget/_enforcer_helpers.py | Adds project budget enforcement to the checker closure. |
| src/synthorg/budget/errors.py | Introduces ProjectBudgetExhaustedError. |
| src/synthorg/observability/events/budget.py | Adds project-level budget event constants. |
| src/synthorg/observability/events/execution.py | Adds project validation/cost event constants. |
| src/synthorg/observability/events/task_assignment.py | Adds project team filtering event constants. |
| docs/design/engine.md | Documents new project validation pipeline step and project-aware budget checking. |
| CLAUDE.md | Updates logging/event-constant guidance with new project-related events. |
| tests/unit/budget/conftest.py | Updates test helper to support project_id on cost records. |
| tests/unit/budget/test_cost_record_project.py | Tests CostRecord.project_id behavior/validation/immutability. |
| tests/unit/budget/test_tracker_project.py | Tests project-scoped tracker cost and record queries. |
| tests/unit/budget/test_enforcer_project.py | Tests BudgetEnforcer.check_project_budget() behavior. |
| tests/unit/budget/test_enforcer_project_inflight.py | Tests in-flight project budget checking via make_budget_checker(). |
| tests/unit/engine/test_cost_recording.py | Tests propagation of project_id through cost recording. |
| tests/unit/engine/test_agent_engine_project.py | Tests engine project validation + project budget integration. |
| tests/unit/engine/test_assignment_project_team.py | Tests assignment filtering by project team membership. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def _check_project_limit( | ||
| running_cost: float, | ||
| project_budget: float, | ||
| project_baseline: float, | ||
| agent_id: str, | ||
| ) -> bool: | ||
| """Return True if project budget is exhausted.""" | ||
| if project_budget <= 0: | ||
| return False | ||
| total_project = round( | ||
| project_baseline + running_cost, | ||
| BUDGET_ROUNDING_PRECISION, | ||
| ) | ||
| if total_project >= project_budget: | ||
| logger.warning( | ||
| BUDGET_PROJECT_BUDGET_EXCEEDED, | ||
| agent_id=agent_id, | ||
| total_project=total_project, | ||
| project_budget=project_budget, | ||
| ) | ||
| return True |
There was a problem hiding this comment.
In the in-flight budget checker, project budget exhaustion is logged without the project identifier (only agent_id/total_project/project_budget). With multi-project concurrency this makes the event hard to attribute. Consider threading project_id into the checker closure (e.g., as an additional _build_checker_closure arg) and include it in the BUDGET_PROJECT_BUDGET_EXCEEDED log fields.
| _validate_time_range(start, end) | ||
| logger.debug( | ||
| BUDGET_PROJECT_COST_QUERIED, | ||
| project_id=project_id, | ||
| start=start, | ||
| end=end, | ||
| ) |
There was a problem hiding this comment.
get_project_records() logs BUDGET_PROJECT_COST_QUERIED, which is semantically a “cost” aggregation event. This makes it difficult to distinguish “cost sum” queries from “records list” queries in observability. Either introduce a dedicated event constant for record queries (e.g., BUDGET_PROJECT_RECORDS_QUERIED) or rename/reuse the existing event to reflect both usages.
| # -- Project validation events -- | ||
| EXECUTION_PROJECT_VALIDATION_FAILED: Final[str] = "execution.project.validation_failed" | ||
| EXECUTION_PROJECT_COST_RECORDED: Final[str] = "execution.project.cost_recorded" |
There was a problem hiding this comment.
EXECUTION_PROJECT_COST_RECORDED is introduced here but is not referenced anywhere else in the codebase (only its definition exists). If it’s intended to be emitted during cost recording, wire it into the relevant logging path; otherwise, consider removing it to avoid dead/unused event constants.
| *, | ||
| completion_config: CompletionConfig | None = None, | ||
| effective_autonomy: EffectiveAutonomy | None = None, | ||
| provider: CompletionProvider | None = None, | ||
| project_id: str | None = None, | ||
| ) -> ExecutionResult: | ||
| """Resume execution from a checkpoint. | ||
|
|
||
| Policy: resumed executions run without a wall-clock timeout. | ||
| The loop's per-turn budget and max_turns still constrain | ||
| execution. | ||
| """ | ||
| checkpoint_json = self._validate_checkpoint_json( | ||
| recovery_result, | ||
| agent_id, | ||
| task_id, | ||
| ) | ||
| logger.info( | ||
| EXECUTION_RESUME_START, | ||
| agent_id=agent_id, | ||
| task_id=task_id, | ||
| resume_attempt=recovery_result.resume_attempt, | ||
| ) | ||
|
|
||
| try: | ||
| result, execution_id = await self._reconstruct_and_run_resume( | ||
| checkpoint_json, | ||
| recovery_result.error_message, | ||
| agent_id, | ||
| task_id, | ||
| failure_category=recovery_result.failure_category, | ||
| criteria_failed=recovery_result.criteria_failed, | ||
| completion_config=completion_config, | ||
| effective_autonomy=effective_autonomy, | ||
| provider=provider, | ||
| ) | ||
| except MemoryError, RecursionError: | ||
| raise | ||
| except Exception as exc: | ||
| logger.exception( | ||
| EXECUTION_RESUME_FAILED, | ||
| agent_id=agent_id, | ||
| task_id=task_id, | ||
| error=f"{type(exc).__name__}: {exc}", | ||
| ) | ||
| raise | ||
| else: | ||
| return await self._finalize_resume( | ||
| result, | ||
| identity, | ||
| execution_id, | ||
| agent_id, | ||
| task_id, | ||
| project_id=project_id, | ||
| ) |
There was a problem hiding this comment.
Project validation is now threaded into the resume pipeline via project_id, but the resumed execution loop’s budget checker is still created without any project budget context. This means a recovery/resume run may not enforce project-level budgets in-flight, potentially allowing overspend after a crash/retry. Consider threading the project budget into the resume path and passing project_id/project_budget into BudgetEnforcer.make_budget_checker() when resuming.
| logger.warning( | ||
| EXECUTION_PROJECT_VALIDATION_FAILED, | ||
| agent_id=agent_id, | ||
| task_id=task_id, | ||
| project=task.project, | ||
| reason="project_not_found", |
There was a problem hiding this comment.
The structured log fields here use project=task.project, while other budget/observability paths use project_id. For consistency and easier querying across domains, consider using project_id as the key name in these validation failure logs as well.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1153 +/- ##
==========================================
- Coverage 88.88% 88.86% -0.02%
==========================================
Files 856 856
Lines 50114 50208 +94
Branches 5035 5047 +12
==========================================
+ Hits 44544 44618 +74
- Misses 4614 4631 +17
- Partials 956 959 +3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/synthorg/engine/agent_engine.py (1)
1403-1463:⚠️ Potential issue | 🔴 CriticalRe-run project validation before checkpoint resume.
This path still goes straight from checkpoint reconstruction into the resumed loop without calling
_validate_project(). If the project is deleted or the agent is removed from the team after the original run, the resumed execution continues anyway; it also resumes without refreshed project-budget context. Please revalidate the checkpoint task’s project here and thread the resolved budget into the resumed checker and finalizer.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/engine/agent_engine.py` around lines 1403 - 1463, Before reconstructing the checkpoint in _resume_from_checkpoint, call self._validate_project(project_id) to re-run project validation (raise/fail if validation returns error) and capture the resolved budget (e.g., project_budget). Thread that project_budget into the resumed flow by adding a project_budget parameter to the calls and signatures of _reconstruct_and_run_resume and _finalize_resume (pass project_budget=<resolved_budget>), so the resumed checker and finalizer use the refreshed budget/context; ensure you update their definitions to accept and use the new project_budget argument.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/synthorg/budget/enforcer.py`:
- Around line 249-306: check_project_budget currently relies on the volatile
_cost_tracker.get_project_cost which prunes old records (168h), so project
budgets can be evaded once pruning runs; change enforcement to use a durable
lifetime aggregate or a cost query with an explicit window. Update the call site
in check_project_budget (and the corresponding in-flight baseline reader
referenced elsewhere) to call a new stable API on the tracker such as
get_project_lifetime_cost(project_id) or get_project_cost(project_id,
window="lifetime"), or compute/maintain a persisted Project.total_spent field
and read that instead; ensure the new API is implemented on CostTracker (or
persist aggregate on Project) to return non-pruned lifetime spend before
enforcing and use that method name in place of _cost_tracker.get_project_cost.
In `@src/synthorg/budget/tracker.py`:
- Around line 240-245: Change the type of public project identifier parameters
to NotBlankStr: update the get_project_cost signature (project_id: str ->
project_id: NotBlankStr) and the other public project_id query function in this
module to use NotBlankStr from core.types, add the import for NotBlankStr, and
keep the rest of the signatures/logic unchanged so blank/whitespace IDs validate
at call time rather than becoming silent misses.
In `@src/synthorg/engine/cost_recording.py`:
- Line 34: Change the project_id parameter type from str | None to NotBlankStr |
None and import NotBlankStr from core.types; update both occurrences of the
project_id annotation in this module (the two locations referenced) so
identifier contracts are consistent and whitespace validation is delegated to
NotBlankStr instead of manual checks.
In `@tests/unit/engine/test_cost_recording.py`:
- Around line 325-364: Combine the three tests (test_project_id_set_on_records,
test_project_id_none_by_default, test_project_id_applied_to_all_turns) into a
single parametrized pytest function using `@pytest.mark.parametrize` that drives
different (turns, project_id, expected_count, expected_project_ids) cases and
calls record_execution_costs with the same pattern (use _FakeTracker and _result
as before); inside the test assert len(tracker.records) == expected_count and
assert each tracker.records[i].project_id matches expected_project_ids (or is
None) so all three scenarios (single turn with "proj-100", default None, and two
turns with "proj-200") are covered by parameters while reusing the existing
helpers record_execution_costs, _FakeTracker, _result, and _turn.
---
Outside diff comments:
In `@src/synthorg/engine/agent_engine.py`:
- Around line 1403-1463: Before reconstructing the checkpoint in
_resume_from_checkpoint, call self._validate_project(project_id) to re-run
project validation (raise/fail if validation returns error) and capture the
resolved budget (e.g., project_budget). Thread that project_budget into the
resumed flow by adding a project_budget parameter to the calls and signatures of
_reconstruct_and_run_resume and _finalize_resume (pass
project_budget=<resolved_budget>), so the resumed checker and finalizer use the
refreshed budget/context; ensure you update their definitions to accept and use
the new project_budget argument.
🪄 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: c1ee888e-22c6-4961-a693-e20fa036f055
📒 Files selected for processing (23)
CLAUDE.mddocs/design/engine.mdsrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/budget/cost_record.pysrc/synthorg/budget/enforcer.pysrc/synthorg/budget/errors.pysrc/synthorg/budget/tracker.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/engine/assignment/models.pysrc/synthorg/engine/assignment/service.pysrc/synthorg/engine/cost_recording.pysrc/synthorg/engine/errors.pysrc/synthorg/observability/events/budget.pysrc/synthorg/observability/events/execution.pysrc/synthorg/observability/events/task_assignment.pytests/unit/budget/conftest.pytests/unit/budget/test_cost_record_project.pytests/unit/budget/test_enforcer_project.pytests/unit/budget/test_enforcer_project_inflight.pytests/unit/budget/test_tracker_project.pytests/unit/engine/test_agent_engine_project.pytests/unit/engine/test_assignment_project_team.pytests/unit/engine/test_cost_recording.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Agent
- GitHub Check: Build Backend
- GitHub Check: Test (Python 3.14)
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Every module with business logic MUST have: from synthorg.observability import get_logger then logger = get_logger(name), and NEVER use import logging or print() in application code
Use event name constants from synthorg.observability.events domain-specific modules (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool) instead of arbitrary log messages
Always use structured logging with logger.info(EVENT, key=value) format - never use logger.info('msg %s', val)
Use NotBlankStr from core.types for all identifier/name fields instead of manual whitespace validators
Use frozen Pydantic models for config/identity and separate mutable-via-copy models for runtime state that evolves, never mixing static config fields with mutable runtime fields in one model
For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction and wrap with MappingProxyType for read-only enforcement
Use allow_inf_nan=False in all ConfigDict declarations to reject NaN/Inf in numeric fields at validation time
Use
@computed_fieldfor derived values in Pydantic models instead of storing and validating redundant fields (e.g. TokenUsage.total_tokens)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
All public functions must have type hints and pass mypy strict mode type-checking
Google-style docstrings required on all public classes and functions (enforced by ruff D rules)
Do NOT use from future import annotations - Python 3.14 has PEP 649 native lazy annotations
Use except A, B: syntax without parentheses for exception handling (PEP 758) - ruff enforces this on Python 3.14
Keep functions under 50 lines and files under 800 lines
Use copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization) for Pydantic models to maintain ...
Files:
src/synthorg/budget/cost_record.pysrc/synthorg/observability/events/task_assignment.pysrc/synthorg/engine/assignment/service.pysrc/synthorg/observability/events/execution.pysrc/synthorg/engine/cost_recording.pysrc/synthorg/engine/assignment/models.pysrc/synthorg/budget/errors.pysrc/synthorg/observability/events/budget.pysrc/synthorg/engine/errors.pysrc/synthorg/budget/tracker.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/budget/enforcer.pysrc/synthorg/engine/agent_engine.py
src/**/*.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/budget/cost_record.pysrc/synthorg/observability/events/task_assignment.pysrc/synthorg/engine/assignment/service.pysrc/synthorg/observability/events/execution.pysrc/synthorg/engine/cost_recording.pysrc/synthorg/engine/assignment/models.pysrc/synthorg/budget/errors.pysrc/synthorg/observability/events/budget.pysrc/synthorg/engine/errors.pysrc/synthorg/budget/tracker.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/budget/enforcer.pysrc/synthorg/engine/agent_engine.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Mark tests with
@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, or@pytest.mark.slowas appropriateAlways run tests with -n 8 parallelism via pytest-xdist, never run tests sequentially locally
Use
@pytest.mark.parametrizefor testing similar cases instead of duplicating test codeUse Hypothesis
@givendecorator with profiles configured in tests/conftest.py (ci for CI with 10 examples, dev for 1000 examples, fuzz for 10000 examples), controlled via HYPOTHESIS_PROFILE env var; failing examples are saved to ~/.synthorg/hypothesis-examples/When Hypothesis finds a failing example, fix the underlying bug and add an explicit
@example(...) decorator to permanently cover the case in CINEVER skip, dismiss, or ignore flaky tests - always fix them fully and fundamentally; for timing-sensitive tests, mock time.monotonic() and asyncio.sleep() to make them deterministic instead of widening timing margins
NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT) in project-owned code, docstrings, comments, tests, or config examples - use generic names like example-provider, example-large-001, test-provider, test-small-001, or large/medium/small aliases instead
Maintain 80% minimum code coverage (enforced in CI); asyncio_mode is set to auto so
@pytest.mark.asynciois not needed for async tests; 30-second timeout per test is global in pyproject.toml (non-default overrides like timeout(60) are allowed)
Files:
tests/unit/budget/conftest.pytests/unit/engine/test_cost_recording.pytests/unit/budget/test_cost_record_project.pytests/unit/budget/test_tracker_project.pytests/unit/budget/test_enforcer_project.pytests/unit/budget/test_enforcer_project_inflight.pytests/unit/engine/test_assignment_project_team.pytests/unit/engine/test_agent_engine_project.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/budget/conftest.pytests/unit/engine/test_cost_recording.pytests/unit/budget/test_cost_record_project.pytests/unit/budget/test_tracker_project.pytests/unit/budget/test_enforcer_project.pytests/unit/budget/test_enforcer_project_inflight.pytests/unit/engine/test_assignment_project_team.pytests/unit/engine/test_agent_engine_project.py
docs/design/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
ALWAYS read the relevant docs/design/ page before implementing any feature or planning any issue, as the design spec is the starting point for architecture, data models, and behavior
When implementation deviates from the spec, alert the user and explain why before proceeding - every deviation needs explicit user approval and must be documented by updating the relevant docs/design/ page
Files:
docs/design/engine.md
🧠 Learnings (41)
📓 Common learnings
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...
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)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Budget: Cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError).
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T17:42:54.041Z
Learning: Every implementation plan must be presented to the user for accept/deny before coding starts
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T17:42:54.041Z
Learning: At every phase of planning and implementation, be critical and actively look for ways to improve the design in the spirit of robustness, correctness, simplicity, and future-proofing - surface improvements as suggestions, not silent changes, and let the user decide
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T17:42:54.041Z
Learning: Prioritize issues by dependency order, not priority labels - unblocked dependencies come first
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T17:42:54.041Z
Learning: Use feature branch naming convention <type>/<slug> where type is: feat, fix, refactor, docs, test, chore, perf, ci
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T17:42:54.041Z
Learning: Signed commits are required on main via branch protection - all commits must be GPG/SSH signed
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T17:42:54.041Z
Learning: NEVER create a PR directly using gh pr create - use /pre-pr-review command to run automated checks and review agents before creating the PR; use /pre-pr-review quick for trivial/docs-only changes
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T17:42:54.041Z
Learning: After finishing an issue implementation, create a feature branch, commit, and push - do NOT create a PR automatically or leave work uncommitted on main
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T17:42:54.041Z
Learning: When review agents find valid issues (including pre-existing issues in surrounding code, suggestions, and findings adjacent to the PR changes), fix them all - never skip or defer fixes as out of scope
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T17:42:54.041Z
Learning: Never use cd in Bash commands - the working directory is already set to the project root; use absolute paths or run commands directly; bash -c 'cd <dir> && <cmd>' is safe for tools without a -C flag
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T17:42:54.041Z
Learning: NEVER use Bash to write or modify files - use the Write or Edit tools instead; do not use cat, echo, sed -i, or tee for file creation or modification (read-only/inspection uses are fine)
📚 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/cost_record.pysrc/synthorg/budget/errors.pysrc/synthorg/observability/events/budget.pytests/unit/budget/test_tracker_project.pytests/unit/budget/test_enforcer_project.pytests/unit/budget/test_enforcer_project_inflight.pysrc/synthorg/budget/tracker.pytests/unit/engine/test_agent_engine_project.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/budget/enforcer.pydocs/design/engine.md
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.
Applied to files:
src/synthorg/budget/cost_record.pysrc/synthorg/budget/errors.pysrc/synthorg/observability/events/budget.pytests/unit/budget/test_enforcer_project.pytests/unit/budget/test_enforcer_project_inflight.pysrc/synthorg/budget/tracker.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/budget/enforcer.pydocs/design/engine.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/engine/coordination/**/*.py : Task coordination uses multi-agent pipeline with 4 dispatchers (SAS/centralized/decentralized/context-dependent), wave execution, and workspace lifecycle integration.
Applied to files:
src/synthorg/engine/assignment/service.pydocs/design/engine.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/assignment/service.pysrc/synthorg/engine/errors.pytests/unit/engine/test_agent_engine_project.pysrc/synthorg/engine/agent_engine.pydocs/design/engine.md
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/observability/events/execution.pysrc/synthorg/observability/events/budget.pysrc/synthorg/budget/_enforcer_helpers.pyCLAUDE.md
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/observability/events/execution.pysrc/synthorg/observability/events/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/observability/events/execution.pysrc/synthorg/observability/events/budget.py
📚 Learning: 2026-04-02T07:18:02.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T07:18:02.381Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly from the domain module
Applied to files:
src/synthorg/observability/events/execution.pysrc/synthorg/observability/events/budget.py
📚 Learning: 2026-03-18T21:23:23.586Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T21:23:23.586Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from the domain-specific module under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly from synthorg.observability.events.<domain>.
Applied to files:
src/synthorg/observability/events/execution.pysrc/synthorg/observability/events/budget.pyCLAUDE.md
📚 Learning: 2026-03-31T16:09:24.320Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T16:09:24.320Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from `synthorg.observability.events.<domain>` modules (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly and use in structured logging
Applied to files:
src/synthorg/observability/events/execution.pyCLAUDE.md
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly rather than using string literals
Applied to files:
src/synthorg/observability/events/execution.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`); import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`
Applied to files:
src/synthorg/observability/events/execution.pyCLAUDE.md
📚 Learning: 2026-04-08T15:24:49.467Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-08T15:24:49.467Z
Learning: Applies to src/synthorg/**/*.py : Event names must always use constants from domain-specific modules under `synthorg.observability.events`. Import directly from specific domain modules.
Applied to files:
src/synthorg/observability/events/execution.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability package (observability/): structured logging, correlation tracking, log sinks; event constants organized by domain under observability/events/ (e.g., events.api, events.tool, events.git, events.context_budget, events.backup)
Applied to files:
src/synthorg/observability/events/execution.pysrc/synthorg/observability/events/budget.pyCLAUDE.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:
src/synthorg/budget/errors.pysrc/synthorg/observability/events/budget.pysrc/synthorg/budget/tracker.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/budget/enforcer.pydocs/design/engine.md
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to src/**/*.py : Use event name constants from domain-specific modules under ai_company.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.) — import directly
Applied to files:
src/synthorg/observability/events/budget.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to tests/**/*.py : Fix flaky tests completely and fundamentally; for timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic instead of widening timing margins
Applied to files:
tests/unit/budget/test_tracker_project.py
📚 Learning: 2026-04-01T17:49:14.133Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T17:49:14.133Z
Learning: Applies to src/synthorg/{providers,engine}/**/*.py : Retryable errors are `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`; non-retryable errors raise immediately; `RetryExhaustedError` signals all retries failed
Applied to files:
src/synthorg/engine/errors.pysrc/synthorg/budget/enforcer.pysrc/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-16T19:13:34.746Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:13:34.746Z
Learning: Applies to src/synthorg/providers/**/*.py : 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.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Retryable errors (`is_retryable=True`): `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately without retry.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-04-08T15:24:49.467Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-08T15:24:49.467Z
Learning: Applies to src/synthorg/providers/**/*.py : Retryable errors include: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately without retry.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-04-06T16:35:12.934Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-06T16:35:12.934Z
Learning: All project conventions, commands, and standards are defined in CLAUDE.md - refer to it for project structure, package layout, code conventions, quick commands, git workflow, testing standards, design specifications, logging, resilience, and security patterns
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls
Applied to files:
CLAUDE.md
📚 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:
CLAUDE.md
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger`.
Applied to files:
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/**/*.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:
CLAUDE.md
📚 Learning: 2026-04-02T12:07:44.443Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T12:07:44.443Z
Learning: Applies to src/synthorg/**/*.py : Always use structured logging: `logger.info(EVENT, key=value)` — never `logger.info('msg %s', val)`
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger` (not `_logger`, not `log`).
Applied to files:
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/**/*.py : Use structured logging: always `logger.info(EVENT, key=value)` — never `logger.info("msg %s", val)`
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to 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:
CLAUDE.md
📚 Learning: 2026-03-31T20:07:03.035Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T20:07:03.035Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising; all state transitions must log at INFO; DEBUG for object creation, internal flow, entry/exit of key functions
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Structured kwargs in logging: always `logger.info(EVENT, key=value)` — never `logger.info('msg %s', val)`.
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: 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/engine/agent_engine.pydocs/design/engine.md
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Log all error paths at WARNING or ERROR with context before raising; log all state transitions at INFO; log object creation/internal flow/entry-exit at DEBUG
Applied to files:
src/synthorg/engine/agent_engine.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/memory/**/*.py : Memory package (memory/): pluggable MemoryBackend protocol, backends/ (Mem0 adapter), retrieval pipeline (ranking, RRF fusion, injection, formatting, non-inferable filtering), shared org memory (org/), consolidation/archival (density-aware: DensityClassifier, AbstractiveSummarizer, ExtractivePreserver, DualModeConsolidationStrategy)
Applied to files:
src/synthorg/engine/agent_engine.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/memory/**/*.py : Use MemoryBackend protocol with pluggable backends (Mem0 adapter available at backends/mem0/) for persistent agent memory
Applied to files:
src/synthorg/engine/agent_engine.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: Persistence backend: pluggable PersistenceBackend protocol in `src/synthorg/persistence/`, SQLite initial, SettingsRepository (namespaced settings CRUD).
Applied to files:
src/synthorg/engine/agent_engine.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/persistence/**/*.py : Persistence uses pluggable PersistenceBackend protocol. SQLite is the initial backend. Settings use SettingsRepository (namespaced settings CRUD).
Applied to files:
src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
src/synthorg/engine/agent_engine.py
🔇 Additional comments (14)
tests/unit/budget/conftest.py (1)
351-364: Good fixture extension for project-scoped records.The optional
project_idplumb-through is correct and backward-compatible for existing tests.src/synthorg/observability/events/task_assignment.py (1)
25-27: Event constants are clean and consistent.Names and values align with the existing task-assignment event namespace.
src/synthorg/observability/events/execution.py (1)
105-107: Looks good.These additions fit the existing execution event structure and support project-scoped observability cleanly.
src/synthorg/budget/errors.py (1)
64-85: Well-structured specialized error type.Subclassing
BudgetExhaustedErrorwithproject_id/project_budget/project_spentis the right shape for downstream handling and structured logging.src/synthorg/engine/errors.py (1)
72-108: Good security-conscious exception design.Generic messages plus structured
project_id/agent_idattributes are implemented cleanly.src/synthorg/budget/cost_record.py (1)
50-53:project_idfield addition is solid.Typing and default behavior are appropriate for optional project attribution.
src/synthorg/engine/assignment/models.py (1)
119-125: Good addition for project-team eligibility input.The immutable typed tuple is a clean interface for assignment filtering.
CLAUDE.md (1)
93-93: Good update to event-constant guidance.The added project-budget and project-assignment examples keep the logging policy aligned with the new observability events introduced in this PR.
tests/unit/budget/test_cost_record_project.py (1)
13-52: Solid focused coverage forproject_idbehavior.These tests correctly validate defaults, validation failure for blank values, and frozen-model immutability for the new field.
src/synthorg/engine/assignment/service.py (1)
17-18: Project-team eligibility filtering is correctly integrated.The pre-strategy filtering and explicit no-eligible early return behavior are clear, deterministic, and properly instrumented with domain event constants.
Also applies to: 81-112
src/synthorg/observability/events/budget.py (1)
52-55: Event additions are consistent with the budget domain taxonomy.The three new project-budget constants follow the existing naming and scoping conventions cleanly.
tests/unit/budget/test_tracker_project.py (1)
35-149: Comprehensive unit coverage for project-scoped tracker queries.The cases for isolation, unknown IDs, and start-time filtering are well targeted for
get_project_cost()andget_project_records().tests/unit/budget/test_enforcer_project_inflight.py (1)
57-119: In-flight project-budget behavior is tested at the right boundaries.The three scenarios map cleanly to the intended closure logic (exceeded, under, and disabled project budget).
tests/unit/budget/test_enforcer_project.py (1)
22-90: Strong scenario coverage forcheck_project_budget().The boundary and isolation cases, plus subclass-compatibility assertion, give good confidence in the new project-budget enforcement path.
| async def check_project_budget( | ||
| self, | ||
| project_id: str, | ||
| project_budget: float, | ||
| ) -> None: | ||
| """Check project-level budget and raise if exceeded. | ||
|
|
||
| Args: | ||
| project_id: Project identifier for cost lookup. | ||
| project_budget: Total project budget (from Project.budget). | ||
|
|
||
| Raises: | ||
| ProjectBudgetExhaustedError: When project spend >= budget. | ||
| """ | ||
| if project_budget <= 0: | ||
| return | ||
|
|
||
| try: | ||
| project_cost = await self._cost_tracker.get_project_cost( | ||
| project_id, | ||
| ) | ||
| except MemoryError, RecursionError: | ||
| raise | ||
| except Exception: | ||
| logger.exception( | ||
| BUDGET_PREFLIGHT_ERROR, | ||
| project_id=project_id, | ||
| reason="project_cost_query_failed", | ||
| ) | ||
| return | ||
|
|
||
| logger.debug( | ||
| BUDGET_PROJECT_ENFORCEMENT_CHECK, | ||
| project_id=project_id, | ||
| project_cost=project_cost, | ||
| project_budget=project_budget, | ||
| ) | ||
|
|
||
| if project_cost >= project_budget: | ||
| logger.warning( | ||
| BUDGET_PROJECT_BUDGET_EXCEEDED, | ||
| project_id=project_id, | ||
| project_cost=project_cost, | ||
| project_budget=project_budget, | ||
| ) | ||
| _fmt = format_cost | ||
| _cur = self._budget_config.currency | ||
| msg = ( | ||
| f"Project {project_id!r} budget exhausted: " | ||
| f"{_fmt(project_cost, _cur)} >= " | ||
| f"{_fmt(project_budget, _cur)}" | ||
| ) | ||
| raise ProjectBudgetExhaustedError( | ||
| msg, | ||
| project_id=project_id, | ||
| project_budget=project_budget, | ||
| project_spent=project_cost, | ||
| ) |
There was a problem hiding this comment.
Project budgets currently inherit the tracker’s 168-hour retention window.
Both the new pre-flight check and the in-flight project baseline read from CostTracker.get_project_cost(), but that tracker auto-prunes old records. Once pruning runs, older project spend disappears and a long-lived project can spend again even though its total project budget was already exhausted. This needs a durable lifetime aggregate, or an explicit project-budget window, before the feature can enforce total project budgets correctly.
Also applies to: 627-655
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/budget/enforcer.py` around lines 249 - 306, check_project_budget
currently relies on the volatile _cost_tracker.get_project_cost which prunes old
records (168h), so project budgets can be evaded once pruning runs; change
enforcement to use a durable lifetime aggregate or a cost query with an explicit
window. Update the call site in check_project_budget (and the corresponding
in-flight baseline reader referenced elsewhere) to call a new stable API on the
tracker such as get_project_lifetime_cost(project_id) or
get_project_cost(project_id, window="lifetime"), or compute/maintain a persisted
Project.total_spent field and read that instead; ensure the new API is
implemented on CostTracker (or persist aggregate on Project) to return
non-pruned lifetime spend before enforcing and use that method name in place of
_cost_tracker.get_project_cost.
| task_id: str, | ||
| *, | ||
| tracker: CostTracker | None, | ||
| project_id: str | None = None, |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use NotBlankStr | None for project_id to keep identifier contracts consistent.
project_id is an identifier field but is currently typed as str | None. Tightening this to NotBlankStr | None keeps the engine boundary consistent with the project-wide identifier typing rules and reduces accidental whitespace propagation.
♻️ Proposed fix
from synthorg.budget.cost_record import CostRecord
+from synthorg.core.types import NotBlankStr
from synthorg.observability import get_logger
@@
async def record_execution_costs( # noqa: PLR0913
@@
- project_id: str | None = None,
+ project_id: NotBlankStr | None = None,
) -> None:As per coding guidelines, "Use NotBlankStr from core.types for all identifier/name fields instead of manual whitespace validators".
Also applies to: 72-72
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/engine/cost_recording.py` at line 34, Change the project_id
parameter type from str | None to NotBlankStr | None and import NotBlankStr from
core.types; update both occurrences of the project_id annotation in this module
(the two locations referenced) so identifier contracts are consistent and
whitespace validation is delegated to NotBlankStr instead of manual checks.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
src/synthorg/budget/enforcer.py (2)
277-279:⚠️ Potential issue | 🟠 MajorProject budget enforcement still relies on a retention-pruned aggregate.
Both pre-flight and in-flight project checks query
get_project_cost(), which can undercount after pruning and allow overspend on long-lived projects. This keeps project-budget enforcement non-authoritative beyond retention.Also applies to: 640-642
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/budget/enforcer.py` around lines 277 - 279, The project budget checks call self._cost_tracker.get_project_cost which relies on retention-pruned aggregates and can undercount long-lived projects; update the enforcement code (both the pre-flight and in-flight callers of get_project_cost) to use an authoritative, un-pruned cost source instead of the pruned aggregate—either by calling a new method like get_project_cost_unpruned (or passing an explicit include_pruned=False/authoritative=True flag to get_project_cost) on _cost_tracker and implementing that path to query the raw/ledger-backed totals; ensure the _cost_tracker API, its implementation, and the preflight/inflight check callers are changed consistently so enforcement uses the un-pruned/authoritative totals.
600-601:⚠️ Potential issue | 🟠 MajorDo not coerce missing project context to an empty project ID.
Line 665 silently converts missing
project_idto"", which can hide invalid caller state and misattribute enforcement/logging. Require a real project ID wheneverproject_budget > 0, and keep the type asNotBlankStr | Noneat this boundary.♻️ Proposed fix
+from synthorg.core.types import NotBlankStr # noqa: TC001 @@ - project_id: str | None = None, + project_id: NotBlankStr | None = None, project_budget: float = 0.0, @@ + if project_budget > 0 and project_id is None: + msg = "project_id is required when project_budget > 0" + raise ValueError(msg) @@ - project_id=project_id or "", + project_id=project_id, )As per coding guidelines, "use
NotBlankStr(fromcore.types) for all identifier/name fields -- including optional (NotBlankStr | None) ... variants."Also applies to: 665-665
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/budget/enforcer.py` around lines 600 - 601, The code currently types project_id as str | None and silently coerces missing project_id to "" (the project_id or "" pattern around the project_budget check), which can hide invalid caller state; change the parameter annotation to NotBlankStr | None (imported from core.types), remove the coercion-to-empty-string logic, and add a runtime validation in the enforcer function/method that raises a clear exception (e.g., ValueError) when project_budget > 0 but project_id is None; allow project_id to remain None only when project_budget == 0. Ensure any logging/enforcement uses the validated NotBlankStr value and not a coerced empty string.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/synthorg/budget/_enforcer_helpers.py`:
- Around line 287-293: The project limit check in _check_project_limit (called
with running_cost, project_baseline, agent_id, project_id) uses only the
per-execution running_cost + project_baseline snapshot and thus misses
concurrent in-flight spend; replace the baseline+local check with a query to a
shared in-flight project accumulator/live tracker (e.g.,
get_or_create_project_inflight(project_id)) and compare running_cost +
live_inflight_cost against project_budget, performing the check-and-reserve
atomically (use a lock or atomic increment) and ensure you decrement/release the
reserved amount when the run finishes/fails; update both call sites (the
_check_project_limit invocation around running_cost/project_baseline and the
similar logic at the 397-420 region) to use the shared tracker and atomic
reserve/release pattern, referencing ctx.accumulated_cost.cost_usd, project_id,
and agent_id for locating and updating the tracker.
In `@src/synthorg/engine/agent_engine.py`:
- Around line 559-565: The code currently skips project validation when
self._project_repo is None, allowing a task with task.project set to proceed
without checks; update the agent engine to fail closed by adding an explicit
guard where task.project is truthy: if task.project and self._project_repo is
None, raise or return an error/abort before calling _validate_project or
continuing execution (same change should be applied to the other two sites where
the pattern appears around the blocks that call _validate_project at the noted
locations); ensure the guard prevents further execution and cost recording for
tasks referencing a project when no project repo is configured.
---
Duplicate comments:
In `@src/synthorg/budget/enforcer.py`:
- Around line 277-279: The project budget checks call
self._cost_tracker.get_project_cost which relies on retention-pruned aggregates
and can undercount long-lived projects; update the enforcement code (both the
pre-flight and in-flight callers of get_project_cost) to use an authoritative,
un-pruned cost source instead of the pruned aggregate—either by calling a new
method like get_project_cost_unpruned (or passing an explicit
include_pruned=False/authoritative=True flag to get_project_cost) on
_cost_tracker and implementing that path to query the raw/ledger-backed totals;
ensure the _cost_tracker API, its implementation, and the preflight/inflight
check callers are changed consistently so enforcement uses the
un-pruned/authoritative totals.
- Around line 600-601: The code currently types project_id as str | None and
silently coerces missing project_id to "" (the project_id or "" pattern around
the project_budget check), which can hide invalid caller state; change the
parameter annotation to NotBlankStr | None (imported from core.types), remove
the coercion-to-empty-string logic, and add a runtime validation in the enforcer
function/method that raises a clear exception (e.g., ValueError) when
project_budget > 0 but project_id is None; allow project_id to remain None only
when project_budget == 0. Ensure any logging/enforcement uses the validated
NotBlankStr value and not a coerced empty string.
🪄 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: 23f61f0e-47f4-45e1-9d57-658543a653f0
📒 Files selected for processing (10)
CLAUDE.mdREADME.mdsrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/budget/enforcer.pysrc/synthorg/budget/tracker.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/engine/cost_recording.pysrc/synthorg/observability/events/budget.pysrc/synthorg/observability/events/execution.pytests/unit/engine/test_cost_recording.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Dependency Review
- GitHub Check: Test (Python 3.14)
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
tests/**/*.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/engine/test_cost_recording.py
src/**/*.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/observability/events/execution.pysrc/synthorg/observability/events/budget.pysrc/synthorg/budget/tracker.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/engine/cost_recording.pysrc/synthorg/budget/enforcer.py
🧠 Learnings (66)
📓 Common learnings
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...
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)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Budget: Cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError).
📚 Learning: 2026-03-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:
README.mdsrc/synthorg/engine/agent_engine.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:
README.mdsrc/synthorg/engine/agent_engine.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:
README.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: Security: SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies: disabled/weighted/per-category/milestone), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume).
Applied to files:
README.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
README.mdsrc/synthorg/engine/agent_engine.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:
README.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/engine/coordination/**/*.py : Task coordination uses multi-agent pipeline with 4 dispatchers (SAS/centralized/decentralized/context-dependent), wave execution, and workspace lifecycle integration.
Applied to files:
README.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Documentation source in `docs/` (Markdown, built with Zensical). Design spec in `docs/design/` (7 pages: index, agents, organization, communication, engine, memory, operations). Architecture in `docs/architecture/` (overview, tech-stack, decision log). Roadmap in `docs/roadmap/`. Security in `docs/security.md`. Licensing in `docs/licensing.md`. Reference in `docs/reference/`. REST API reference in `docs/rest-api.md`. Library reference in `docs/api/` (auto-generated from docstrings). Custom templates in `docs/overrides/`. Config in `mkdocs.yml`.
Applied to files:
README.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to docs/design/*.md : Design spec pages: 7 pages in `docs/design/` — index, agents, organization, communication, engine, memory, operations
Applied to files:
README.md
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to docs/** : Docs source in docs/ (Markdown, built with Zensical); design spec in docs/design/ (7 pages: index, agents, organization, communication, engine, memory, operations)
Applied to files:
README.md
📚 Learning: 2026-04-06T16:35:12.934Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-06T16:35:12.934Z
Learning: All project conventions, commands, and standards are defined in CLAUDE.md - refer to it for project structure, package layout, code conventions, quick commands, git workflow, testing standards, design specifications, logging, resilience, and security patterns
Applied to files:
CLAUDE.md
📚 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:
CLAUDE.md
📚 Learning: 2026-04-02T12:07:44.443Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T12:07:44.443Z
Learning: Applies to src/synthorg/**/*.py : Always use structured logging: `logger.info(EVENT, key=value)` — never `logger.info('msg %s', val)`
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger`.
Applied to files:
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/**/*.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:
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/**/*.py : Use structured logging: always `logger.info(EVENT, key=value)` — never `logger.info("msg %s", val)`
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger` (not `_logger`, not `log`).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Structured kwargs in logging: always `logger.info(EVENT, key=value)` — never `logger.info('msg %s', val)`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to 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:
CLAUDE.mdsrc/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-31T20:07:03.035Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T20:07:03.035Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising; all state transitions must log at INFO; DEBUG for object creation, internal flow, entry/exit of key functions
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to tests/**/*.py : Parametrize: Prefer pytest.mark.parametrize for testing similar cases.
Applied to files:
tests/unit/engine/test_cost_recording.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to tests/**/*.py : Prefer `pytest.mark.parametrize` for testing similar cases.
Applied to files:
tests/unit/engine/test_cost_recording.py
📚 Learning: 2026-03-31T20:29:10.177Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T20:29:10.177Z
Learning: Applies to tests/**/*.py : Prefer `pytest.mark.parametrize` for testing similar cases
Applied to files:
tests/unit/engine/test_cost_recording.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to tests/**/*.py : Fix flaky tests completely and fundamentally; for timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic instead of widening timing margins
Applied to files:
tests/unit/engine/test_cost_recording.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/observability/events/execution.pysrc/synthorg/observability/events/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/observability/events/execution.pysrc/synthorg/observability/events/budget.py
📚 Learning: 2026-04-08T15:24:49.467Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-08T15:24:49.467Z
Learning: Applies to src/synthorg/**/*.py : Event names must always use constants from domain-specific modules under `synthorg.observability.events`. Import directly from specific domain modules.
Applied to files:
src/synthorg/observability/events/execution.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/observability/events/execution.pysrc/synthorg/observability/events/budget.pysrc/synthorg/budget/_enforcer_helpers.py
📚 Learning: 2026-03-18T21:23:23.586Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T21:23:23.586Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from the domain-specific module under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly from synthorg.observability.events.<domain>.
Applied to files:
src/synthorg/observability/events/execution.pysrc/synthorg/observability/events/budget.py
📚 Learning: 2026-04-02T07:18:02.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T07:18:02.381Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly from the domain module
Applied to files:
src/synthorg/observability/events/execution.pysrc/synthorg/observability/events/budget.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly rather than using string literals
Applied to files:
src/synthorg/observability/events/execution.py
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)
Applied to files:
src/synthorg/observability/events/budget.pysrc/synthorg/budget/tracker.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/engine/cost_recording.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.
Applied to files:
src/synthorg/observability/events/budget.pysrc/synthorg/budget/tracker.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability package (observability/): structured logging, correlation tracking, log sinks; event constants organized by domain under observability/events/ (e.g., events.api, events.tool, events.git, events.context_budget, events.backup)
Applied to files:
src/synthorg/observability/events/budget.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to src/**/*.py : Use event name constants from domain-specific modules under ai_company.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.) — import directly
Applied to files:
src/synthorg/observability/events/budget.py
📚 Learning: 2026-03-31T16:09:24.320Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T16:09:24.320Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from `synthorg.observability.events.<domain>` modules (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly and use in structured logging
Applied to files:
src/synthorg/observability/events/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/observability/events/budget.pysrc/synthorg/budget/tracker.pysrc/synthorg/budget/_enforcer_helpers.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use `NotBlankStr` from `core.types` for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Applied to files:
src/synthorg/budget/tracker.pysrc/synthorg/engine/cost_recording.py
📚 Learning: 2026-04-08T15:24:49.467Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-08T15:24:49.467Z
Learning: Applies to **/*.py : Use `NotBlankStr` (from `core.types`) for all identifier/name fields, including optional and tuple variants, instead of manual whitespace validators
Applied to files:
src/synthorg/budget/tracker.pysrc/synthorg/engine/cost_recording.py
📚 Learning: 2026-03-31T20:07:03.035Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T20:07:03.035Z
Learning: Applies to **/*.py : Use `NotBlankStr` (from `core.types`) for all identifier/name fields, including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants, instead of manual whitespace validators
Applied to files:
src/synthorg/budget/tracker.pysrc/synthorg/engine/cost_recording.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. For derived values use `computed_field` instead of storing + validating redundant fields. Use `NotBlankStr` (from `core.types`) for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.
Applied to files:
src/synthorg/budget/tracker.pysrc/synthorg/engine/cost_recording.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 with adopted conventions: use computed_field for derived values instead of storing + validating redundant fields; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.
Applied to files:
src/synthorg/budget/tracker.pysrc/synthorg/engine/cost_recording.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. Use `computed_field` for derived values instead of storing redundant fields. Use `NotBlankStr` for all identifier/name fields.
Applied to files:
src/synthorg/budget/tracker.py
📚 Learning: 2026-03-17T11:41:02.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T11:41:02.964Z
Learning: Applies to src/**/*.py : Models: Pydantic v2 (`BaseModel`, `model_validator`, `computed_field`, `ConfigDict`). Use `computed_field` for derived values instead of storing + validating redundant fields. Use `NotBlankStr` for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.
Applied to files:
src/synthorg/budget/tracker.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to **/*.py : Models: Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict). Use computed_field for derived values instead of storing + validating redundant fields. Use NotBlankStr (from core.types) for all identifier/name fields — including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants — instead of manual whitespace validators.
Applied to files:
src/synthorg/budget/tracker.py
📚 Learning: 2026-03-31T21:07:37.470Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T21:07:37.470Z
Learning: Applies to **/*.py : Use `except A, B:` (no parentheses) per PEP 758 exception syntax on Python 3.14
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (without parentheses) per PEP 758 for exception handling in Python 3.14
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-04-08T15:24:49.467Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-08T15:24:49.467Z
Learning: Applies to **/*.py : Use `except A, B:` (no parentheses) -- PEP 758 except syntax, enforced by ruff on Python 3.14
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (no parentheses) for exception handling — PEP 758 exception syntax enforced by ruff on Python 3.14
Applied to files:
src/synthorg/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 **/*.py : Use PEP 758 except syntax with `except A, B:` (no parentheses) for multiple exceptions—ruff enforces this on Python 3.14.
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to **/*.py : Handle errors explicitly; never silently swallow exceptions
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to **/*.py : Handle errors explicitly, never silently swallow exceptions
Applied to files:
src/synthorg/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 **/*.py : Handle errors explicitly—never silently swallow exceptions.
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to **/*.py : Use PEP 758 except syntax: `except A, B:` (no parentheses) — enforced by ruff on Python 3.14
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T21:11:37.538Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T21:11:37.538Z
Learning: Applies to **/*.py : Errors: handle explicitly, never silently swallow.
Applied to files:
src/synthorg/engine/agent_engine.py
📚 Learning: 2026-04-01T17:49:14.133Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T17:49:14.133Z
Learning: Applies to src/synthorg/{providers,engine}/**/*.py : Retryable errors are `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`; non-retryable errors raise immediately; `RetryExhaustedError` signals all retries failed
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Log all error paths at WARNING or ERROR with context before raising; log all state transitions at INFO; log object creation/internal flow/entry-exit at DEBUG
Applied to files:
src/synthorg/engine/agent_engine.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/memory/**/*.py : Memory package (memory/): pluggable MemoryBackend protocol, backends/ (Mem0 adapter), retrieval pipeline (ranking, RRF fusion, injection, formatting, non-inferable filtering), shared org memory (org/), consolidation/archival (density-aware: DensityClassifier, AbstractiveSummarizer, ExtractivePreserver, DualModeConsolidationStrategy)
Applied to files:
src/synthorg/engine/agent_engine.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/memory/**/*.py : Use MemoryBackend protocol with pluggable backends (Mem0 adapter available at backends/mem0/) for persistent agent memory
Applied to files:
src/synthorg/engine/agent_engine.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: Persistence backend: pluggable PersistenceBackend protocol in `src/synthorg/persistence/`, SQLite initial, SettingsRepository (namespaced settings CRUD).
Applied to files:
src/synthorg/engine/agent_engine.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/persistence/**/*.py : Persistence uses pluggable PersistenceBackend protocol. SQLite is the initial backend. Settings use SettingsRepository (namespaced settings CRUD).
Applied to files:
src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-16T19:13:34.746Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:13:34.746Z
Learning: Applies to src/synthorg/providers/**/*.py : 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.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Retryable errors (`is_retryable=True`): `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately without retry.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-04-08T15:24:49.467Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-08T15:24:49.467Z
Learning: Applies to src/synthorg/providers/**/*.py : Retryable errors include: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately without retry.
Applied to files:
src/synthorg/budget/enforcer.py
🔇 Additional comments (8)
README.md (1)
76-78: Documentation update is aligned with implementation.The updated orchestration and budget descriptions accurately reflect project-scoped team filtering and project-level budget controls added in this PR.
src/synthorg/engine/cost_recording.py (1)
12-12: Project ID typing and propagation look correct.
project_idis now type-constrained, stored on eachCostRecord, and emitted in the success event payload, which keeps cost attribution and observability consistent.Also applies to: 35-35, 73-73, 134-134
src/synthorg/observability/events/execution.py (1)
105-106: New execution event constant is correctly defined.The added project-validation event follows the existing execution event convention and placement.
tests/unit/engine/test_cost_recording.py (1)
321-375: Parametrized project-id coverage is well-structured.This is a clean consolidation of similar scenarios and validates propagation across both single-turn and multi-turn paths.
CLAUDE.md (1)
93-93: Logging convention update is consistent with new event surface.The added project assignment, project budget, and project validation event examples are aligned with this PR’s observability changes.
src/synthorg/observability/events/budget.py (1)
52-56: Project budget event constants are cleanly added.The new constants are well-scoped and match the established budget event naming pattern.
src/synthorg/budget/tracker.py (1)
241-313: Project-scoped tracker APIs are implemented consistently.
get_project_cost/get_project_recordsfollow existing query patterns (time-range validation, snapshot filtering, structured event logging), and theproject_idfilter integration is straightforward.Also applies to: 706-721
src/synthorg/engine/agent_engine.py (1)
1429-1437: Good stale-checkpoint protection.Re-validating the project before resume and re-threading
project_id/project_budgetinto the resumed loop closes the checkpoint hole after team or budget changes.Also applies to: 1452-1464
| or _check_project_limit( | ||
| running_cost, | ||
| project_budget, | ||
| project_baseline, | ||
| agent_id, | ||
| project_id, | ||
| ) |
There was a problem hiding this comment.
Project budget checks miss concurrent spend.
running_cost here is the current execution's ctx.accumulated_cost.cost_usd, so each closure only sees its own in-flight spend. If two agents run in the same project concurrently, both can reuse the same project_baseline snapshot and stay under project_budget individually while the combined project total overshoots it. That makes this a best-effort per-execution stop, not a true project-wide in-flight limit.
Please compare against a shared project accumulator/live tracker instead of project_baseline + local running_cost.
Also applies to: 397-420
🤖 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 287 - 293, The project
limit check in _check_project_limit (called with running_cost, project_baseline,
agent_id, project_id) uses only the per-execution running_cost +
project_baseline snapshot and thus misses concurrent in-flight spend; replace
the baseline+local check with a query to a shared in-flight project
accumulator/live tracker (e.g., get_or_create_project_inflight(project_id)) and
compare running_cost + live_inflight_cost against project_budget, performing the
check-and-reserve atomically (use a lock or atomic increment) and ensure you
decrement/release the reserved amount when the run finishes/fails; update both
call sites (the _check_project_limit invocation around
running_cost/project_baseline and the similar logic at the 397-420 region) to
use the shared tracker and atomic reserve/release pattern, referencing
ctx.accumulated_cost.cost_usd, project_id, and agent_id for locating and
updating the tracker.
| # Project validation and project-level budget check | ||
| if self._project_repo is not None: | ||
| _project_budget = await self._validate_project( | ||
| task=task, | ||
| agent_id=agent_id, | ||
| task_id=task_id, | ||
| ) |
There was a problem hiding this comment.
Fail closed when a project task arrives without project_repo.
If task.project is set and self._project_repo is None, this validation is skipped but the project id still flows into execution and cost recording. A wiring mistake then silently disables project existence checks, team membership checks, and project budget enforcement for that run.
🔒 Minimal guard
- if self._project_repo is not None:
+ if task.project and self._project_repo is None:
+ logger.warning(
+ EXECUTION_PROJECT_VALIDATION_FAILED,
+ agent_id=agent_id,
+ task_id=task_id,
+ project_id=task.project,
+ reason="project_repo_not_configured",
+ )
+ msg = "project_repo is required for project-scoped tasks"
+ raise ExecutionStateError(msg)
+ if self._project_repo is not None:
_project_budget = await self._validate_project(
task=task,
agent_id=agent_id,
task_id=task_id,
)Also applies to: 595-596, 695-696
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/engine/agent_engine.py` around lines 559 - 565, The code
currently skips project validation when self._project_repo is None, allowing a
task with task.project set to proceed without checks; update the agent engine to
fail closed by adding an explicit guard where task.project is truthy: if
task.project and self._project_repo is None, raise or return an error/abort
before calling _validate_project or continuing execution (same change should be
applied to the other two sites where the pattern appears around the blocks that
call _validate_project at the noted locations); ensure the guard prevents
further execution and cost recording for tasks referencing a project when no
project repo is configured.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/synthorg/engine/agent_engine.py (1)
1357-1395:⚠️ Potential issue | 🟠 MajorLet resume-time project/budget failures escape
_apply_recovery().
_resume_from_checkpoint()can now raiseProjectNotFoundError,ProjectAgentNotMemberError, andProjectBudgetExhaustedError, but the blanketexcept Exceptionconverts them back into the originalERRORresult. That hides the real stop reason and keeps the resumed run on the generic error path instead of using the dedicated handlers inrun().⚙️ Minimal fix
except MemoryError, RecursionError: raise + except BudgetExhaustedError: + raise + except ProjectNotFoundError, ProjectAgentNotMemberError: + raise except Exception as exc: logger.exception( EXECUTION_RECOVERY_FAILED,If you convert the missing-
project_repopath to anExecutionStateError, re-raise that here too.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/engine/agent_engine.py` around lines 1357 - 1395, The broad except Exception in _apply_recovery is swallowing ProjectNotFoundError, ProjectAgentNotMemberError, and ProjectBudgetExhaustedError raised by _resume_from_checkpoint, causing resumed runs to be treated as generic errors; update _apply_recovery to allow these to propagate (either by not catching them here or by converting the missing-project_repo path to raise ExecutionStateError in _resume_from_checkpoint and then re-raising ExecutionStateError in _apply_recovery), i.e., add an explicit except ExecutionStateError (or the three project exceptions) that re-raises before the generic except Exception block so run()’s dedicated handlers can run.
♻️ Duplicate comments (3)
src/synthorg/engine/agent_engine.py (1)
559-574:⚠️ Potential issue | 🟠 MajorDon't let project-scoped tasks run without
project_repo.This only warns and continues. The run still threads
task.projectinto execution and cost recording, so a wiring mistake disables project existence checks, team membership checks, and project budget enforcement while still charging the project. The same fail-open path exists during checkpoint resume.🔒 Minimal guard
- if self._project_repo is not None: + if task.project and self._project_repo is None: + logger.warning( + EXECUTION_PROJECT_VALIDATION_FAILED, + agent_id=agent_id, + task_id=task_id, + project_id=task.project, + reason="project_repo_not_configured", + ) + msg = "project_repo is required for project-scoped tasks" + raise ExecutionStateError(msg) + if self._project_repo is not None: _project_budget = await self._validate_project( task=task, agent_id=agent_id, task_id=task_id, ) - elif task.project: - logger.warning( - EXECUTION_PROJECT_VALIDATION_FAILED, - agent_id=agent_id, - task_id=task_id, - project_id=task.project, - reason="project_repo_not_configured", - )- if self._project_repo is not None: + if task.project and self._project_repo is None: + logger.warning( + EXECUTION_PROJECT_VALIDATION_FAILED, + agent_id=agent_id, + task_id=task_id, + project_id=task.project, + reason="project_repo_not_configured", + ) + msg = "project_repo is required for project-scoped tasks" + raise ExecutionStateError(msg) + if self._project_repo is not None: project_budget = await self._validate_project( task=task, agent_id=agent_id, task_id=task_id, )Also applies to: 1437-1446
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/engine/agent_engine.py` around lines 559 - 574, The code currently only logs a warning when task.project is set but self._project_repo is None, allowing project-scoped tasks to continue; change this to fail fast: where you check self._project_repo before calling _validate_project (the block that currently emits EXECUTION_PROJECT_VALIDATION_FAILED), instead raise or return a project-validation failure (e.g., raise a ProjectValidationError or set the task run to failed and stop execution) so the run does not proceed or record costs against the project; apply the same guard in the checkpoint-resume path that also calls _validate_project so both normal run and resume paths abort when project_repo is not configured.src/synthorg/budget/_enforcer_helpers.py (1)
284-309:⚠️ Potential issue | 🟠 MajorThe in-flight project stop is still per-execution, not per-project.
project_baseline + running_costonly includes this execution's local spend. Two agents in the same project can share the same baseline, both stay underproject_budgetindividually, and still overshoot it together. This needs a shared project accumulator/reservation, not a per-closure snapshot.Also applies to: 398-420
src/synthorg/budget/enforcer.py (1)
256-264:⚠️ Potential issue | 🟠 MajorProject budgets still depend on a pruned aggregate.
Both
check_project_budget()and the in-flight baseline inmake_budget_checker()still readCostTracker.get_project_cost(), and this file now documents that the tracker drops records after 168 hours. Once pruning runs, a long-lived project can spend again under an already-exhausted project budget. Please switch project-budget enforcement to a lifetime aggregate or an explicit project-budget window.Also applies to: 276-279, 637-642
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/budget/enforcer.py` around lines 256 - 264, check_project_budget() and the in-flight baseline in make_budget_checker() still use CostTracker.get_project_cost(), which is subject to 168h pruning and causes under-reporting for long-lived projects; replace those reads with a lifetime-safe aggregate or an explicit project-window query. Update calls in check_project_budget(), make_budget_checker(), and any other usages in this file that call CostTracker.get_project_cost() to either use a new/alternative API such as CostTracker.get_project_lifetime_cost() or pass an explicit long-lived window parameter (e.g., window=None or lifetime=True) so the returned cost is not pruned; if that API doesn't exist, add a lifetime-aggregate method on CostTracker and use it in these locations. Ensure unit tests and callers expecting pruned behavior are adjusted accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/synthorg/engine/agent_engine.py`:
- Around line 1357-1395: The broad except Exception in _apply_recovery is
swallowing ProjectNotFoundError, ProjectAgentNotMemberError, and
ProjectBudgetExhaustedError raised by _resume_from_checkpoint, causing resumed
runs to be treated as generic errors; update _apply_recovery to allow these to
propagate (either by not catching them here or by converting the
missing-project_repo path to raise ExecutionStateError in
_resume_from_checkpoint and then re-raising ExecutionStateError in
_apply_recovery), i.e., add an explicit except ExecutionStateError (or the three
project exceptions) that re-raises before the generic except Exception block so
run()’s dedicated handlers can run.
---
Duplicate comments:
In `@src/synthorg/budget/enforcer.py`:
- Around line 256-264: check_project_budget() and the in-flight baseline in
make_budget_checker() still use CostTracker.get_project_cost(), which is subject
to 168h pruning and causes under-reporting for long-lived projects; replace
those reads with a lifetime-safe aggregate or an explicit project-window query.
Update calls in check_project_budget(), make_budget_checker(), and any other
usages in this file that call CostTracker.get_project_cost() to either use a
new/alternative API such as CostTracker.get_project_lifetime_cost() or pass an
explicit long-lived window parameter (e.g., window=None or lifetime=True) so the
returned cost is not pruned; if that API doesn't exist, add a lifetime-aggregate
method on CostTracker and use it in these locations. Ensure unit tests and
callers expecting pruned behavior are adjusted accordingly.
In `@src/synthorg/engine/agent_engine.py`:
- Around line 559-574: The code currently only logs a warning when task.project
is set but self._project_repo is None, allowing project-scoped tasks to
continue; change this to fail fast: where you check self._project_repo before
calling _validate_project (the block that currently emits
EXECUTION_PROJECT_VALIDATION_FAILED), instead raise or return a
project-validation failure (e.g., raise a ProjectValidationError or set the task
run to failed and stop execution) so the run does not proceed or record costs
against the project; apply the same guard in the checkpoint-resume path that
also calls _validate_project so both normal run and resume paths abort when
project_repo is not configured.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: e3b76f17-59bd-453c-bb69-6adb6c38c1b8
📒 Files selected for processing (4)
src/synthorg/budget/_enforcer_helpers.pysrc/synthorg/budget/enforcer.pysrc/synthorg/engine/agent_engine.pytests/unit/engine/test_agent_engine_project.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Test (Python 3.14)
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
No
from __future__ import annotations-- Python 3.14 has PEP 649Use
except A, B:syntax (no parentheses) for exception handling -- ruff enforces PEP 758 on Python 3.14Type hints: all public functions, mypy strict mode
Docstrings: Google style, required on public classes/functions (enforced by ruff D rules)
Create new objects, never mutate existing ones. For non-Pydantic internal collections, use
copy.deepcopy()at construction +MappingProxyTypewrapping for read-only enforcementUse frozen Pydantic models for config/identity; separate mutable-via-copy models for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model
Pydantic v2: use
allow_inf_nan=Falsein allConfigDictdeclarations to rejectNaN/Infin numeric fields at validation timeUse
@computed_fieldfor derived values instead of storing + validating redundant fields in Pydantic modelsUse
NotBlankStrfromcore.typesfor all identifier/name fields -- including optional and tuple variants -- instead of manual whitespace validatorsPrefer
asyncio.TaskGroupfor fan-out/fan-in parallel operations in new code over barecreate_taskLine length: 88 characters (ruff)
Functions: < 50 lines, files < 800 lines
Handle errors explicitly, never silently swallow
Validate at system boundaries (user input, external APIs, config files)
Files:
src/synthorg/engine/agent_engine.pytests/unit/engine/test_agent_engine_project.pysrc/synthorg/budget/enforcer.pysrc/synthorg/budget/_enforcer_helpers.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Every module with business logic MUST have:
from synthorg.observability import get_loggerthenlogger = get_logger(__name__)Always use
loggeras the variable name (not_logger, notlog)Use event name constants from
synthorg.observability.eventsdomain modules instead of raw stringsAlways use structured kwargs in logging:
logger.info(EVENT, key=value)-- neverlogger.info("msg %s", val)All error paths must log at WARNING or ERROR with context before raising
All state transitions must log at INFO level
Use DEBUG level for object creation, internal flow, entry/exit of key functions
All provider calls go through
BaseCompletionProviderwhich applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling codeNever 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. Tests must usetest-provider,test-small-001, etc.Pure data models, enums, and re-exports do NOT need logging
Files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.pysrc/synthorg/budget/_enforcer_helpers.py
src/synthorg/!(observability)/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Never use
import logging/logging.getLogger()/print()in application code (exception: observability modules may use stdlib logging)
Files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.pysrc/synthorg/budget/_enforcer_helpers.py
src/**/*.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.pysrc/synthorg/budget/_enforcer_helpers.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Test markers:
@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slowUse
asyncio_mode = "auto"for async tests -- no manual@pytest.mark.asyncioneededPrefer
@pytest.mark.parametrizefor testing similar casesUse Hypothesis for property-based testing with
@given+@settings; profiles:ci(deterministic),dev(1000 examples),fuzz(10,000 examples, no deadline),extreme(500,000 examples)
Files:
tests/unit/engine/test_agent_engine_project.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/engine/test_agent_engine_project.py
🧠 Learnings (27)
📓 Common learnings
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...
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)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Budget: Cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError).
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)
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
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:35:19.613Z
Learning: Always read the relevant `docs/design/` page before implementing any feature or planning any issue
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:35:19.613Z
Learning: If implementation deviates from the design spec, alert the user and explain why -- user decides whether to proceed or update the spec. Do NOT silently diverge
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:35:19.613Z
Learning: Every implementation plan must be presented to the user for accept/deny before coding starts
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:35:19.613Z
Learning: At every phase of planning and implementation, be critical -- actively look for ways to improve the design in the spirit of what we're building
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:35:19.613Z
Learning: Prioritize issues by dependency order, not priority labels -- unblocked dependencies come first
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:35:19.613Z
Learning: Coverage: 80% minimum (enforced in CI)
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:35:19.613Z
Learning: Global timeout: 30 seconds per test in `pyproject.toml` -- do not add per-file markers unless overriding with different timeout
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:35:19.613Z
Learning: Always include `-n 8` when running pytest locally, never run tests sequentially
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:35:19.613Z
Learning: Commits: `<type>: <description>` -- types: feat, fix, refactor, docs, test, chore, perf, ci (enforced by commitizen commit-msg hook)
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:35:19.613Z
Learning: Signed commits: required on `main` via branch protection -- all commits must be GPG/SSH signed
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:35:19.613Z
Learning: Branches: `<type>/<slug>` from main
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:35:19.613Z
Learning: ALWAYS use `/pre-pr-review` to create PRs -- `gh pr create` is blocked by hookify
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:35:19.613Z
Learning: Never create a PR directly -- create a feature branch, commit, and push instead. Do not leave work uncommitted on main
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:35:19.613Z
Learning: Never use `cd` in Bash commands -- use absolute paths or run commands directly. Exception: `bash -c "cd <dir> && <cmd>"` is safe
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:35:19.613Z
Learning: Never use Bash to write or modify files -- use the Write or Edit tools. Do not use `cat >`, `cat << EOF`, `echo >`, `echo >>`, `sed -i`, `python -c "open(...).write(...)"`, or `tee`
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:35:19.613Z
Learning: Design spec is MANDATORY -- always read relevant `docs/design/` pages before implementing features. When deviations occur, update the design pages to reflect new reality
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:35:19.613Z
Learning: Fix everything valid that review agents find -- including pre-existing issues in surrounding code, suggestions, and adjacent findings. Never skip or defer
📚 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.pytests/unit/engine/test_agent_engine_project.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/engine/agent_engine.py
📚 Learning: 2026-03-31T21:07:37.470Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T21:07:37.470Z
Learning: Applies to **/*.py : Use `except A, B:` (no parentheses) per PEP 758 exception syntax on Python 3.14
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (without parentheses) per PEP 758 for exception handling in Python 3.14
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-04-08T15:24:49.467Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-08T15:24:49.467Z
Learning: Applies to **/*.py : Use `except A, B:` (no parentheses) -- PEP 758 except syntax, enforced by ruff on Python 3.14
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (no parentheses) for exception handling — PEP 758 exception syntax enforced by ruff on Python 3.14
Applied to files:
src/synthorg/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 **/*.py : Use PEP 758 except syntax with `except A, B:` (no parentheses) for multiple exceptions—ruff enforces this on Python 3.14.
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to **/*.py : Handle errors explicitly; never silently swallow exceptions
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to **/*.py : Handle errors explicitly, never silently swallow exceptions
Applied to files:
src/synthorg/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 **/*.py : Handle errors explicitly—never silently swallow exceptions.
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to **/*.py : Use PEP 758 except syntax: `except A, B:` (no parentheses) — enforced by ruff on Python 3.14
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T21:11:37.538Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T21:11:37.538Z
Learning: Applies to **/*.py : Errors: handle explicitly, never silently swallow.
Applied to files:
src/synthorg/engine/agent_engine.py
📚 Learning: 2026-04-01T17:49:14.133Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-01T17:49:14.133Z
Learning: Applies to src/synthorg/{providers,engine}/**/*.py : Retryable errors are `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`; non-retryable errors raise immediately; `RetryExhaustedError` signals all retries failed
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/budget/enforcer.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Log all error paths at WARNING or ERROR with context before raising; log all state transitions at INFO; log object creation/internal flow/entry-exit at DEBUG
Applied to files:
src/synthorg/engine/agent_engine.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/memory/**/*.py : Memory package (memory/): pluggable MemoryBackend protocol, backends/ (Mem0 adapter), retrieval pipeline (ranking, RRF fusion, injection, formatting, non-inferable filtering), shared org memory (org/), consolidation/archival (density-aware: DensityClassifier, AbstractiveSummarizer, ExtractivePreserver, DualModeConsolidationStrategy)
Applied to files:
src/synthorg/engine/agent_engine.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/memory/**/*.py : Use MemoryBackend protocol with pluggable backends (Mem0 adapter available at backends/mem0/) for persistent agent memory
Applied to files:
src/synthorg/engine/agent_engine.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: Persistence backend: pluggable PersistenceBackend protocol in `src/synthorg/persistence/`, SQLite initial, SettingsRepository (namespaced settings CRUD).
Applied to files:
src/synthorg/engine/agent_engine.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/persistence/**/*.py : Persistence uses pluggable PersistenceBackend protocol. SQLite is the initial backend. Settings use SettingsRepository (namespaced settings CRUD).
Applied to files:
src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to tests/**/*.py : Test markers: pytest.mark.unit, pytest.mark.integration, pytest.mark.e2e, pytest.mark.slow. Coverage: 80% minimum (enforced in CI).
Applied to files:
tests/unit/engine/test_agent_engine_project.py
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)
Applied to files:
tests/unit/engine/test_agent_engine_project.pysrc/synthorg/budget/enforcer.pysrc/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/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.
Applied to files:
src/synthorg/budget/enforcer.pysrc/synthorg/budget/_enforcer_helpers.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Budget: Cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError).
Applied to files:
src/synthorg/budget/enforcer.pysrc/synthorg/budget/_enforcer_helpers.py
📚 Learning: 2026-03-16T19:13:34.746Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:13:34.746Z
Learning: Applies to src/synthorg/providers/**/*.py : 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.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Retryable errors (`is_retryable=True`): `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately without retry.
Applied to files:
src/synthorg/budget/enforcer.py
📚 Learning: 2026-04-08T15:24:49.467Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-08T15:24:49.467Z
Learning: Applies to src/synthorg/providers/**/*.py : Retryable errors include: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately without retry.
Applied to files:
src/synthorg/budget/enforcer.py
🔇 Additional comments (1)
tests/unit/engine/test_agent_engine_project.py (1)
58-77: Tests properly set task.project via the shared fixture.The
sample_task_with_criteriafixture inconftest.py:133already definesproject="proj-001"in its Task construction. All tests in this file receive this fixture, so project validation is properly exercised.> Likely an incorrect or invalid review comment.
🤖 I have created a release *beep* *boop* --- ## [0.6.5](v0.6.4...v0.6.5) (2026-04-09) ### Features * add control-plane API endpoints batch ([#1118](#1118), [#1119](#1119), [#1120](#1120), [#1121](#1121)) ([#1138](#1138)) ([af11f0a](af11f0a)) * engine intelligence v2 -- trace enrichment, compaction, versioning eval ([#1139](#1139)) ([ed57dfa](ed57dfa)), closes [#1123](#1123) [#1125](#1125) [#1113](#1113) * generalize versioning to VersionSnapshot[T] for all entity types ([#1155](#1155)) ([5f563ce](5f563ce)), closes [#1131](#1131) [#1132](#1132) [#1133](#1133) * implement auxiliary tool categories -- design, communication, analytics ([#1152](#1152)) ([b506ba4](b506ba4)) * implement multi-project support -- engine orchestration ([#242](#242)) ([#1153](#1153)) ([74f1362](74f1362)) * implement SharedKnowledgeStore append-only + MVCC consistency model (Phase 1.5) ([#1134](#1134)) ([965d3a1](965d3a1)), closes [#1130](#1130) * implement shutdown strategies and SUSPENDED task status ([#1151](#1151)) ([6a0db11](6a0db11)) * persistent cost aggregation for project-lifetime budgets ([#1173](#1173)) ([5c212c5](5c212c5)), closes [#1156](#1156) * Prometheus /metrics endpoint and OTLP exporter ([#1122](#1122)) ([#1135](#1135)) ([aaeaae9](aaeaae9)), closes [#1124](#1124) * Prometheus metrics -- daily budget %, per-agent cost, per-agent budget % ([#1154](#1154)) ([581c494](581c494)), closes [#1148](#1148) ### Bug Fixes * communication hardening -- meeting cooldown, circuit breaker backoff, debate fallback ([#1140](#1140)) ([fe82894](fe82894)), closes [#1115](#1115) [#1116](#1116) [#1117](#1117) ### CI/CD * bump wrangler from 4.80.0 to 4.81.0 in /.github in the all group ([#1144](#1144)) ([b7c0945](b7c0945)) ### Maintenance * bump python from `6869258` to `5e59aae` in /docker/backend in the all group ([#1141](#1141)) ([01e99c2](01e99c2)) * bump python from `6869258` to `5e59aae` in /docker/sandbox in the all group ([#1143](#1143)) ([ea755bd](ea755bd)) * bump python from `6869258` to `5e59aae` in /docker/web in the all group ([#1142](#1142)) ([5416dd9](5416dd9)) * bump the all group across 1 directory with 2 updates ([#1181](#1181)) ([d3d5adf](d3d5adf)) * bump the all group across 1 directory with 3 updates ([#1146](#1146)) ([c609e6c](c609e6c)) * bump the all group in /cli with 2 updates ([#1177](#1177)) ([afd9cde](afd9cde)) * bump the all group in /site with 3 updates ([#1178](#1178)) ([7cff82a](7cff82a)) * bump the all group with 2 updates ([#1180](#1180)) ([199a1a8](199a1a8)) * bump vitest from 4.1.2 to 4.1.3 in /site in the all group ([#1145](#1145)) ([a8c1194](a8c1194)) * consolidated web deps (11 packages + hono security + test fixes) ([#1150](#1150)) ([63a9390](63a9390)), closes [#1147](#1147) [#1136](#1136) [#1137](#1137) * pin Docker Python base image to 3.14.x ([#1182](#1182)) ([8ffdd86](8ffdd86)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary
Implements the orchestration layer for multi-project support (#242). The data layer already existed (Project model, CRUD API, SQLite persistence, WS events) -- this PR adds engine-level project awareness so multiple projects can run concurrently with isolated budgets and team-scoped agent allocation.
Changes
Engine -- Project Validation (
agent_engine.py)project_repodependency onAgentEngine_validate_project()method validates project existence and agent team membership before executionBudgetEnforcer.check_project_budget()project_id,project_budget) threaded through the entire execution pipeline:run()->_execute()->_post_execution_pipeline()->_apply_recovery()->_resume_from_checkpoint()->_finalize_resume()ProjectNotFoundErrorandProjectAgentNotMemberErrorpropagate with generic messages (identifiers stored as attributes for structured logging only, followingSelfReviewErrorpattern)Budget -- Project-Level Isolation (
enforcer.py,tracker.py,_enforcer_helpers.py)CostRecord.project_idfield tags every cost record with its projectCostTracker.get_project_cost()andget_project_records()for project-scoped queriesBudgetEnforcer.check_project_budget()-- pre-flight project budget enforcementmake_budget_checker()extended withproject_id/project_budgetparams for in-flight checking_check_project_limit()in the budget checker closure enforces project budget during executionAssignment -- Project Team Filtering (
assignment/models.py,assignment/service.py)AssignmentRequest.project_teamfield (tuple of agent IDs)TaskAssignmentService.assign()filters available agents to project team members before strategy dispatchError Types (
budget/errors.py,engine/errors.py)ProjectBudgetExhaustedError(BudgetExhaustedError)with project_id, project_budget, project_spentProjectNotFoundError(EngineError)with project_id attributeProjectAgentNotMemberError(EngineError)with project_id, agent_id attributesObservability (
events/budget.py,events/execution.py,events/task_assignment.py)Documentation
docs/design/engine.md-- new pipeline step 3 (Project validation) with full descriptionCLAUDE.md-- new event constants added to logging sectionTest Plan
37 new tests across 6 new test files + 3 tests added to existing file:
test_cost_record_project.py-- CostRecord project_id field (4 tests)test_tracker_project.py-- CostTracker project queries (9 tests)test_enforcer_project.py-- Pre-flight project budget enforcement (7 tests)test_enforcer_project_inflight.py-- In-flight project budget checking (3 tests)test_agent_engine_project.py-- Engine project validation + budget integration (6 tests)test_assignment_project_team.py-- Assignment team filtering (5 tests)test_cost_recording.py-- Project ID propagation (3 new tests)Full suite: 16033 passed, 9 skipped. mypy clean. ruff clean.
Review Coverage
Pre-reviewed by 13 agents, 6 findings addressed:
check_project_budget()-- added try/except for consistency withmake_budget_checker()patternproject_idkwarg for debuggabilityavailable_agentscountCloses #242