Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📜 Recent review details⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
🧰 Additional context used📓 Path-based instructions (4)**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
src/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
src/synthorg/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
src/synthorg/api/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (23)📚 Learning: 2026-03-16T07:22:28.134ZApplied to files:
📚 Learning: 2026-03-14T16:18:57.267ZApplied to files:
📚 Learning: 2026-03-14T15:43:05.601ZApplied to files:
📚 Learning: 2026-03-16T07:22:28.134ZApplied to files:
📚 Learning: 2026-03-14T16:18:57.267ZApplied to files:
📚 Learning: 2026-03-14T15:43:05.601ZApplied to files:
📚 Learning: 2026-03-26T13:22:36.844ZApplied to files:
📚 Learning: 2026-03-17T22:08:13.456ZApplied to files:
📚 Learning: 2026-03-17T18:52:05.142ZApplied to files:
📚 Learning: 2026-03-26T13:22:36.844ZApplied to files:
📚 Learning: 2026-03-19T07:12:14.508ZApplied to files:
📚 Learning: 2026-03-15T16:55:07.730ZApplied to files:
📚 Learning: 2026-03-14T16:18:57.267ZApplied to files:
📚 Learning: 2026-03-16T06:24:56.341ZApplied to files:
📚 Learning: 2026-03-17T22:08:13.456ZApplied to files:
📚 Learning: 2026-03-26T13:22:36.844ZApplied to files:
📚 Learning: 2026-03-16T07:22:28.134ZApplied to files:
📚 Learning: 2026-03-17T06:43:14.114ZApplied to files:
📚 Learning: 2026-03-17T22:08:13.456ZApplied to files:
📚 Learning: 2026-03-19T07:13:44.964ZApplied to files:
📚 Learning: 2026-03-19T07:12:14.508ZApplied to files:
📚 Learning: 2026-03-17T06:30:14.180ZApplied to files:
📚 Learning: 2026-03-19T07:13:44.964ZApplied to files:
🔇 Additional comments (7)
WalkthroughAdds persisted activity sources and plumbing: a Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the agent activity timeline by integrating five new event types: task_started, tool_used, delegation_sent, delegation_received, and cost_incurred. These additions provide a much richer and more granular view of agent operations, from task initiation and tool interactions to financial costs and inter-agent delegations. The changes involve new data models and dedicated storage mechanisms for tool invocations and delegation records, along with updates to the core activity merging logic and the API controller to efficiently process and present this expanded dataset. The implementation prioritizes robustness, including graceful degradation for any data sources that might be temporarily unavailable. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
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 introduces comprehensive activity tracking for agents, including tool invocations and delegation records. Key changes involve adding ToolInvocationTracker and DelegationRecordStore services, integrating them into the AppState, and extending the /api/v1/activities endpoint to merge these new event types into a unified chronological timeline. The TaskMetricRecord model was updated to include a started_at field, and corresponding logic was added to generate 'task_started' events. Documentation has been updated to reflect these new capabilities. A critical issue was identified where Python 2 except syntax is used in multiple places, which will lead to SyntaxError in Python 3.
| except MemoryError, RecursionError: | ||
| raise |
There was a problem hiding this comment.
This except syntax is for Python 2. For Python 3, multiple exceptions must be grouped in a tuple. This will cause a SyntaxError at runtime.
This pattern is repeated in this file for _fetch_cost_records (L189), _fetch_tool_invocations (L218), and _fetch_delegation_records (L266). Please update all occurrences.
except (MemoryError, RecursionError):
raise| except MemoryError, RecursionError: | ||
| raise |
| except MemoryError, RecursionError: | ||
| raise |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #832 +/- ##
========================================
Coverage 92.30% 92.31%
========================================
Files 580 584 +4
Lines 30343 30491 +148
Branches 2924 2934 +10
========================================
+ Hits 28009 28147 +138
- Misses 1846 1854 +8
- Partials 488 490 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/synthorg/tools/invoker.py (1)
106-128:⚠️ Potential issue | 🔴 CriticalTool invocation tracking is not wired through AgentEngine to ToolInvoker.
The
invocation_trackerparameter is correctly added toToolInvoker, butAgentEngine._make_tool_invoker()does not pass it when constructingToolInvokerinstances. SinceAgentEngineitself does not receivetool_invocation_trackerfromAppState, the tracker remainsNoneat runtime, causingrecord_tool_invocationto silently skip recording tool usage events.For production tracking to work:
AgentEngine.__init__must accepttool_invocation_trackerparameterAgentEngine._make_tool_invoker()must passself._tool_invocation_trackerto theToolInvokerconstructor🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/tools/invoker.py` around lines 106 - 128, AgentEngine currently doesn't propagate the ToolInvocationTracker into ToolInvoker, so tool usage isn't recorded; update AgentEngine.__init__ to accept a tool_invocation_tracker parameter and store it as self._tool_invocation_tracker, then modify AgentEngine._make_tool_invoker to pass that tracker into the ToolInvoker constructor (invocation_tracker=self._tool_invocation_tracker) so ToolInvoker receives the tracker and record_tool_invocation will work.src/synthorg/hr/activity.py (1)
8-20: 🛠️ Refactor suggestion | 🟠 MajorAdd the standard module logger scaffold.
This
src/synthorg/hrbusiness-logic module still doesn't importget_logger/ definelogger = get_logger(__name__), so the new timeline-conversion logic is out of step with the repo's observability baseline.As per coding guidelines, "Every module with business logic MUST have:
from synthorg.observability import get_loggerthenlogger = get_logger(__name__)"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/hr/activity.py` around lines 8 - 20, This module is missing the standard module logger; import get_logger from synthorg.observability and define logger = get_logger(__name__) near the top of src/synthorg/hr/activity.py (alongside the other imports) so the module follows the repo observability baseline; ensure the import uses the exact symbol get_logger and the logger variable name logger so existing logging calls (or future ones) use the standard logger.tests/unit/hr/test_activity.py (1)
212-228: 🧹 Nitpick | 🔵 TrivialStable sort assertion depends on insertion order, not explicit guarantees.
The comment claims "Stable sort: lifecycle events appear before task metrics at same timestamp," but this relies on the current insertion order in
merge_activity_timeline(lifecycle first, then task metrics). If the merge function's internal order changes, this test will fail unexpectedly.Consider either:
- Documenting this as an implementation detail that tests rely on, or
- Asserting only that both events exist without enforcing relative order for same-timestamp events.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/unit/hr/test_activity.py` around lines 212 - 228, The test test_identical_timestamps_stable_sort relies on merge_activity_timeline insertion order to assert lifecycle events precede task metrics at the same timestamp; change the test to avoid relying on that implementation detail by asserting both events are present regardless of order (e.g., collect timeline event_type values from timeline produced by merge_activity_timeline and assert that "hired" and "task_completed" are both present) instead of checking timeline[0] and timeline[1]; reference the existing helpers _make_lifecycle_event, _make_task_metric and LifecycleEventType.HIRED when updating the assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/synthorg/api/controllers/activities.py`:
- Around line 149-173: _fetch_task_metrics is synchronous and blocks the event
loop when called outside the TaskGroup; change the call site to run it inside
the TaskGroup using asyncio.to_thread(...) or make _fetch_task_metrics async and
await the I/O in a background thread. Specifically, keep the function logic but
when invoking performance_tracker.get_task_metrics (or any call to
_fetch_task_metrics) start it with
asyncio.to_thread(app_state.performance_tracker.get_task_metrics, agent_id=...,
since=..., until=...) inside the TaskGroup so task metrics fetch runs
concurrently with cost/tool/delegation fetches; ensure exception handling
mirrors the existing try/except behavior and that the returned shape remains
tuple[TaskMetricRecord, ...].
In `@src/synthorg/communication/delegation/record_store.py`:
- Around line 35-51: record_sync currently mutates self._records without
acquiring self._lock while _snapshot reads under self._lock; to fix, make
record_sync async and acquire the asyncio lock (use "async def record_sync(...)"
and "async with self._lock:" around the append and logger call) so both writers
and readers use the same protection, and update callers (e.g.
DelegationService._record_delegation) to await the new async record_sync; ensure
the method signature and all call sites are updated accordingly to prevent
blocking or race conditions.
In `@src/synthorg/communication/delegation/service.py`:
- Around line 196-202: The log on the failure path is using the success constant
DELEGATION_CREATED; change that logger.warning call to emit a distinct failure
event (e.g. DELEGATION_CREATION_FAILED or DELEGATION_CREATE_FAILED) so success
and failure metrics aren’t conflated—update the constant reference in the
logger.warning call (and add the new failure constant to the events/constants
module if it doesn’t exist) in the same failure block where logger.warning(...,
delegator=request.delegator_id, delegatee=request.delegatee_id, note="Failed to
record delegation in activity store", exc_info=True) is called.
- Around line 190-193: The current write via
self._record_store.record_sync(record) uses the in-memory RecordStore and loses
data on restart; change to a durable persistence: update the
RecordStore.record_sync implementation (the class and method named record_sync
in RecordStore) to persist delegation records to a shared durable store (e.g.,
DB table, key/value store, or append-only log) instead of appending an in-memory
list, and update the call site in service.py (the code invoking
self._record_store.record_sync) to handle/await any async DB call or handle
returned errors; ensure record structure (IDs/timestamps/org_id) is persisted
and that the persistence layer provides queryable APIs for
delegation_sent/delegation_received so other workers can read them.
In `@src/synthorg/hr/activity.py`:
- Around line 154-168: The current logic places raw record.error_message into
the ActivityEvent.description which can leak sensitive data and exceed the
1024-char cap; update the construction of desc (used when record.is_success is
False) to avoid exposing raw errors by replacing record.error_message with a
sanitized summary: e.g., if record.error_message exists, extract only a short,
safe token such as the first line stripped of paths/stack traces and limit it to
a safe length (e.g. 200 chars) or else use a generic message like "error details
omitted"; ensure this change is applied where desc is built (referencing
record.error_message, record.tool_name, and ActivityEvent description) so
ActivityEvent.description never contains the full raw error and respects size
limits.
In `@src/synthorg/tools/invocation_bridge.py`:
- Around line 52-58: The event constant TOOL_INVOKE_EXECUTION_ERROR is
misleading here; define a new constant TOOL_INVOCATION_RECORD_FAILED in
synthorg.observability.events.tool and import it into invocation_bridge.py, then
replace TOOL_INVOKE_EXECUTION_ERROR in the logger.warning call (and keep the
same tool_call_id, tool_name, note, exc_info fields) with
TOOL_INVOCATION_RECORD_FAILED; ensure the new event constant follows the
existing event naming/format conventions and update any tests or references
expecting the old semantic if needed.
- Around line 35-38: The current flow in invocation_bridge.py returns early on
many error paths and never calls record_tool_invocation, so failed attempts
aren't recorded; update the code that uses invoker._invocation_tracker and
invoker._agent_id to ensure record_tool_invocation is invoked for every exit by
either (a) moving the record_tool_invocation call into a single finally block
that runs after the tool lookup/permission/validation/security/execution/parking
logic, or (b) adding explicit calls to record_tool_invocation (using the same
signature as the successful path) in each early-return branch that returns a
ToolResult; reference the identifiers record_tool_invocation,
invoker._invocation_tracker, invoker._agent_id, and ToolResult when making the
change.
In `@src/synthorg/tools/invocation_tracker.py`:
- Around line 24-33: ToolInvocationTracker currently stores _records in-process
so tool_used events are lost on restart and invisible across workers; replace
the in-memory list with a persistent backend by injecting a storage client
(e.g., a database or shared cache) into ToolInvocationTracker, remove or make
_records an optional local cache, and update its write/read methods (the
append/write code that records invocations and any query methods that read the
timeline) to persist and query from the injected storage asynchronously while
keeping the asyncio.Lock only for local concurrency; ensure the new constructor
accepts the storage client, use unique keys/indexes for org-wide queries, and
add migrations/initialization where needed so the timeline is durable and
consistent across multiple workers.
In `@tests/unit/api/controllers/test_activities.py`:
- Around line 335-401: Add regression tests that verify /api/v1/activities still
returns 200 when the optional sources are absent or failing: create a test
(e.g., test_feed_graceful_degradation_tool_missing) that does not provide the
tool_invocation_tracker fixture (or monkeypatches ToolInvocationTracker.record
to raise) then calls TestClient.get("/api/v1/activities") and asserts
status_code == 200 and that other activity sources (e.g., lifecycle events) are
still present; similarly add a test (e.g.,
test_feed_graceful_degradation_delegation_missing) that omits or makes
DelegationRecordStore.record_sync raise and asserts the same; use the existing
patterns in test_feed_with_tool_invocations and test_feed_with_delegation_events
to locate where to add these cases and to assert body["pagination"]["total"] and
presence/absence of event types.
---
Outside diff comments:
In `@src/synthorg/hr/activity.py`:
- Around line 8-20: This module is missing the standard module logger; import
get_logger from synthorg.observability and define logger = get_logger(__name__)
near the top of src/synthorg/hr/activity.py (alongside the other imports) so the
module follows the repo observability baseline; ensure the import uses the exact
symbol get_logger and the logger variable name logger so existing logging calls
(or future ones) use the standard logger.
In `@src/synthorg/tools/invoker.py`:
- Around line 106-128: AgentEngine currently doesn't propagate the
ToolInvocationTracker into ToolInvoker, so tool usage isn't recorded; update
AgentEngine.__init__ to accept a tool_invocation_tracker parameter and store it
as self._tool_invocation_tracker, then modify AgentEngine._make_tool_invoker to
pass that tracker into the ToolInvoker constructor
(invocation_tracker=self._tool_invocation_tracker) so ToolInvoker receives the
tracker and record_tool_invocation will work.
In `@tests/unit/hr/test_activity.py`:
- Around line 212-228: The test test_identical_timestamps_stable_sort relies on
merge_activity_timeline insertion order to assert lifecycle events precede task
metrics at the same timestamp; change the test to avoid relying on that
implementation detail by asserting both events are present regardless of order
(e.g., collect timeline event_type values from timeline produced by
merge_activity_timeline and assert that "hired" and "task_completed" are both
present) instead of checking timeline[0] and timeline[1]; reference the existing
helpers _make_lifecycle_event, _make_task_metric and LifecycleEventType.HIRED
when updating the assertions.
🪄 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: 8990a2c5-de6c-441a-a770-a5e78b43a65c
📒 Files selected for processing (21)
CLAUDE.mddocs/design/agents.mddocs/design/operations.mdsrc/synthorg/api/app.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/api/state.pysrc/synthorg/communication/delegation/record_store.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/hr/activity.pysrc/synthorg/hr/performance/models.pysrc/synthorg/observability/events/delegation.pysrc/synthorg/observability/events/tool.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/tools/invocation_record.pysrc/synthorg/tools/invocation_tracker.pysrc/synthorg/tools/invoker.pytests/unit/api/conftest.pytests/unit/api/controllers/test_activities.pytests/unit/communication/delegation/test_record_store.pytests/unit/hr/test_activity.pytests/unit/tools/test_invocation_tracker.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Backend
- GitHub Check: Analyze (python)
- GitHub Check: Dependency Review
🧰 Additional context used
📓 Path-based instructions (5)
docs/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Documentation is built with Zensical (config: mkdocs.yml). Docs structure: docs/design/ (10 pages), docs/architecture/, docs/roadmap/, docs/security.md, docs/licensing.md, docs/reference/, docs/rest-api.md + docs/_generated/api-reference.html (generated by scripts/export_openapi.py), docs/api/ (auto-generated via mkdocstrings + Griffe AST-based). Landing page: site/ (Astro with /get/ CLI install page, contact form, SEO)
Files:
docs/design/agents.mddocs/design/operations.md
docs/design/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Design spec pages are the mandatory starting point for architecture, data models, and behavior. Always read the relevant docs/design/ page before implementing features. If implementation deviates from spec, alert the user with explanation. Update docs/design/ pages when deviations are approved
Files:
docs/design/agents.mddocs/design/operations.md
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotations-- Python 3.14 has PEP 649 native lazy annotations
Useexcept A, B:syntax without parentheses for exception handling (PEP 758) -- ruff enforces this on Python 3.14
Add type hints to all public functions and classes; mypy strict mode required
Use Google-style docstrings on all public classes and functions (enforced by ruff D rules)
Immutability enforcement: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping. For dict/list fields in frozen Pydantic models, rely on frozen=True and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization)
Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict); use@computed_fieldfor derived values instead of storing redundant fields; use NotBlankStr for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (multiple tool invocations, parallel agent calls). Use structured concurrency over bare create_task
Keep functions under 50 lines, files under 800 lines
Line length: 88 characters (ruff enforced)
Explicitly handle all errors; never silently swallow exceptions
Validate at system boundaries: user input, external APIs, config files
Files:
src/synthorg/observability/events/delegation.pysrc/synthorg/observability/events/tool.pytests/unit/tools/test_invocation_tracker.pysrc/synthorg/tools/invoker.pysrc/synthorg/api/app.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/hr/activity.pytests/unit/api/controllers/test_activities.pysrc/synthorg/api/state.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/api/controllers/activities.pytests/unit/communication/delegation/test_record_store.pysrc/synthorg/tools/invocation_tracker.pysrc/synthorg/tools/invocation_record.pytests/unit/api/conftest.pytests/unit/hr/test_activity.pysrc/synthorg/hr/performance/models.pysrc/synthorg/communication/delegation/record_store.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic MUST have:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)
Use logger.info(EVENT, key=value) with structured kwargs for logging; never use logger.info('msg %s', val) formatting
Never useimport logging/logging.getLogger()/print()in application code. Exception: observability/setup.py and observability/sinks.py may use stdlib logging and print() for bootstrap code only
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
All provider calls go through BaseCompletionProvider which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code. RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig. Retryable errors (is_retryable=True): RateLimitError, ProviderTimeoutError, ProviderConnectionError, ProviderInternalError. Non-retryable errors raise immediately. RetryExhaustedError signals all retries failed -- engine layer catches this to trigger fallback chains. Rate limiter respects RateLimitError.retry_after
Always read the relevant docs/design/ page before implementing any feature or planning any issue. DESIGN_SPEC.md is a pointer file. When a spec topic is referenced (e.g., 'the Agents page'), read the relevant docs/design/ page before coding
If implementation deviates from the design spec (better approach, scope evolved), alert the user and explain why. User decides whether to proceed or update spec. Do NOT silently diverge. When approved deviations occur, update relevant docs/design/ page to reflect new reality
Files:
src/synthorg/observability/events/delegation.pysrc/synthorg/observability/events/tool.pysrc/synthorg/tools/invoker.pysrc/synthorg/api/app.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/hr/activity.pysrc/synthorg/api/state.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/tools/invocation_tracker.pysrc/synthorg/tools/invocation_record.pysrc/synthorg/hr/performance/models.pysrc/synthorg/communication/delegation/record_store.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Mark all tests with@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, or@pytest.mark.slow
Always run pytest with-n autofor parallelism via pytest-xdist; never run tests sequentially. Minimum coverage: 80% (enforced in CI). Timeout: 30 seconds per test globally (do not add per-file markers; non-default overrides like timeout(60) are allowed)
Vendor-agnostic naming everywhere: NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, large/medium/small aliases. Vendor names allowed only in: (1) docs/design/operations.md provider list, (2) .claude/ skill/agent files, (3) third-party import paths. Tests must use test-provider, test-small-001, etc.
Use Hypothesis for property-based testing (@given+@settingswith HYPOTHESIS_PROFILE env var: 'ci' for 50 examples (default), 'dev' for 1000 examples). Run dev profile: HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties. .hypothesis/ is gitignored
Never skip, dismiss, or ignore flaky tests -- always fix them fully. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep() for determinism. For tasks blocking indefinitely until cancelled, use asyncio.Event().wait() instead of asyncio.sleep(large_number) -- it is cancellation-safe
Files:
tests/unit/tools/test_invocation_tracker.pytests/unit/api/controllers/test_activities.pytests/unit/communication/delegation/test_record_store.pytests/unit/api/conftest.pytests/unit/hr/test_activity.py
🧠 Learnings (29)
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Engine: Agent orchestration, execution loops, parallel execution, task decomposition, routing, task assignment, centralized single-writer task state engine (TaskEngine), task lifecycle, recovery, shutdown, workspace isolation, coordination (multi-agent pipeline: TopologyDispatcher protocol, 4 dispatchers — SAS/centralized/decentralized/context-dependent, wave execution, workspace lifecycle integration, CoordinationSectionConfig company config bridge, build_coordinator factory), coordination error classification, prompt policy validation, checkpoint recovery (checkpoint/, per-turn persistence, heartbeat detection, CheckpointRecoveryStrategy), approval gate (escalation detection, context parking/resume, EscalationInfo/ResumePayload models), stagnation detection (stagnation/, StagnationDetector protocol, ToolRepetitionDetector, dual-signal analysis, corrective prompt injection), agent runtime state (AgentRuntimeState, lightweight per-agent execution status for dashboard queries and recove...
Applied to files:
docs/design/agents.md
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/hr/**/*.py : HR package (hr/): hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)
Applied to files:
docs/design/agents.mdsrc/synthorg/api/controllers/activities.pytests/unit/api/conftest.pytests/unit/hr/test_activity.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/observability/events/delegation.pysrc/synthorg/observability/events/tool.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/delegation.pysrc/synthorg/observability/events/tool.py
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
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/tool.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/tool.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/tool.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/tool.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings
Applied to files:
src/synthorg/observability/events/tool.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls
Applied to files:
src/synthorg/observability/events/tool.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use event name constants from domain-specific modules under `ai_company.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`). Import directly: `from ai_company.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/observability/events/tool.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)
Applied to files:
src/synthorg/tools/invoker.pysrc/synthorg/api/state.pyCLAUDE.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/security/**/*.py : Security module includes SecOps agent, rule engine (soft-allow/hard-deny), audit log, output scanner, risk classifier, autonomy levels (4 strategies), timeout policies.
Applied to files:
src/synthorg/tools/invoker.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/communication/**/*.py : Communication package (communication/): message bus, dispatcher, messenger, channels, delegation, loop prevention, conflict resolution; meeting/ subpackage for meeting protocol (round-robin, position papers, structured phases), scheduler (frequency, participant resolver), orchestrator
Applied to files:
src/synthorg/api/app.pysrc/synthorg/api/state.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
src/synthorg/api/app.pytests/unit/api/controllers/test_activities.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/activities.pytests/unit/api/conftest.pyCLAUDE.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/backup/**/*.py : Backup package (backup/): scheduled/manual/lifecycle backups of persistence DB, agent memory, company config. BackupService orchestrator, BackupScheduler (periodic asyncio task), RetentionManager (count + age pruning), tar.gz compression, SHA-256 checksums, manifest tracking, validated restore with atomic rollback and safety backup. handlers/ subpackage: ComponentHandler protocol + concrete handlers (PersistenceComponentHandler, MemoryComponentHandler, ConfigComponentHandler)
Applied to files:
src/synthorg/api/app.pysrc/synthorg/api/state.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.
Applied to files:
src/synthorg/api/app.pytests/unit/api/conftest.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)
Applied to files:
src/synthorg/api/app.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: Applies to src/synthorg/**/*.py : Prefer `asyncio.TaskGroup` for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare `create_task`. Existing code is being migrated incrementally.
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)
Applied to files:
src/synthorg/api/controllers/activities.pytests/unit/api/conftest.pytests/unit/hr/test_activity.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.
Applied to files:
src/synthorg/api/controllers/activities.pytests/unit/hr/test_activity.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/hr/**/*.py : HR engine must provide: hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, trend detection), promotion/demotion
Applied to files:
tests/unit/hr/test_activity.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Settings: Runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge, ConfigResolver (typed composed reads for controllers), validation, registry, change notifications via message bus. Per-namespace setting definitions in definitions/ submodule (api, company, providers, memory, budget, security, coordination, observability, backup).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Documentation source in `docs/` (Markdown, built with Zensical). Design spec in `docs/design/` (7 pages: index, agents, organization, communication, engine, memory, operations). Architecture in `docs/architecture/` (overview, tech-stack, decision log). Roadmap in `docs/roadmap/`. Security in `docs/security.md`. Licensing in `docs/licensing.md`. Reference in `docs/reference/`. REST API reference in `docs/rest-api.md`. Library reference in `docs/api/` (auto-generated from docstrings). Custom templates in `docs/overrides/`. Config in `mkdocs.yml`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-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:
CLAUDE.md
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to docs/** : Docs source in docs/ (Markdown, built with Zensical); design spec in docs/design/ (7 pages: index, agents, organization, communication, engine, memory, operations)
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 with shadcn/ui + Tailwind CSS 4. Use React hooks (auth, login lockout, WebSocket, polling, optimistic updates), Zustand stores, and lazy-loaded page components. Maintain component organization: ui/ (shadcn primitives), layout/ (app shell, sidebar, status bar), feature dirs for pages
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to docs/**/*.md : Documentation is built with Zensical (config: mkdocs.yml). Docs structure: docs/design/ (10 pages), docs/architecture/, docs/roadmap/, docs/security.md, docs/licensing.md, docs/reference/, docs/rest-api.md + docs/_generated/api-reference.html (generated by scripts/export_openapi.py), docs/api/ (auto-generated via mkdocstrings + Griffe AST-based). Landing page: site/ (Astro with /get/ CLI install page, contact form, SEO)
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T21:20:09.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:20:09.993Z
Learning: Applies to web/src/components/** : Vue components organized by feature (agents/, approvals/, budget/, common/, dashboard/, layout/, messages/, org-chart/, tasks/).
Applied to files:
CLAUDE.md
🔇 Additional comments (17)
CLAUDE.md (1)
122-122: LGTM!The documentation update accurately reflects the new invocation tracking functionality added to the tools subsystem.
src/synthorg/hr/performance/models.py (1)
53-83: LGTM!The new
started_atfield and temporal ordering validator are well-implemented:
- Correctly enforces strict ordering (
started_at < completed_at)- Error message includes ISO-formatted timestamps for debugging
- Pattern is consistent with the existing
_validate_expiration_orderingvalidator inCollaborationOverridedocs/design/agents.md (1)
308-308: LGTM!The documentation accurately reflects the expanded activity timeline with the five new event types (task_started, tool_used, cost_incurred, delegation_sent, delegation_received) merged alongside existing lifecycle and task completion events.
src/synthorg/observability/events/tool.py (1)
58-61: LGTM!The new event constants follow the established naming convention (
<domain>.<noun>.<verb>) and are consistent with the existing event taxonomy in this file.src/synthorg/tools/invoker.py (1)
408-410: Recording invocation after building result is correct.The placement of
record_tool_invocationafter_build_resultand beforereturnis appropriate — it ensures the fullToolResult(including any error state) is captured, and the await ensures recording completes before returning.src/synthorg/observability/events/delegation.py (1)
26-29: LGTM!The new delegation record store event constants follow the established naming convention and are consistent with the existing event taxonomy in this file.
docs/design/operations.md (1)
1066-1068: LGTM!The documentation accurately reflects:
- The expanded event types in the per-agent activity endpoint (lifecycle, task, cost, tool, delegation)
- The new org-wide activity feed endpoint with filtering capabilities
Both entries are consistent with the implementation and follow the existing table format.
src/synthorg/api/app.py (1)
429-430: Confirm: Activity services need auto-wiring strategy or clear documentation.Verification shows these services are never instantiated in the codebase—there are no factory functions, no auto-wiring logic in
auto_wire_phase1, and no instances created anywhere. Whenrun_servercallscreate_app(config=config), bothtool_invocation_trackeranddelegation_record_storeremainNonein production.Unlike other optional services (persistence, message_bus, cost_tracker) which are auto-wired in
auto_wire_phase1, these new services have no conditional initialization. The AppState properties will raise 503 if accessed whenNone.Either:
- Add auto-wiring logic (similar to cost_tracker pattern) if these should work in production, or
- Document this as intentional MVP (features opt-in for testing only).
Without clarification, the current state risks production failures when activity events are triggered.
src/synthorg/api/controllers/activities.py (2)
107-119: Good use ofasyncio.TaskGroupfor parallel fetches.The structured concurrency pattern correctly parallelizes independent data source fetches. This aligns with best practices for fan-out operations.
258-265: The org-wide activity timeline intentionally shows bothdelegation_sentanddelegation_receivedperspectives for each delegation record, as validated by the test suite. This behavior is correct as designed—agent-specific views (filtered by agent_id) return only the relevant perspective, while org-wide views surface the complete delegation context from both sides. The code comment at line 261 already clarifies this intent.tests/unit/hr/test_activity.py (2)
527-539: Test documents intentional dual-perspective behavior.This test explicitly validates that passing the same
DelegationRecordto bothdelegation_records_sentanddelegation_records_receivedproduces two distinct events. This confirms the design intent, but as noted in the controller review, this causes double-counting in org-wide queries.
1-680: Comprehensive test coverage for new event types.The tests thoroughly cover all five new activity event types (
task_started,cost_incurred,tool_used,delegation_sent,delegation_received) with edge cases including:
- Conditional
task_startedgeneration based onstarted_at- Tool failures with/without error messages
- Optional
task_idin tool invocations- Dual-perspective delegation events
- Merge ordering verification
src/synthorg/tools/invocation_bridge.py (1)
44-46: Truncation to 2048 chars matches model constraint.Good alignment between the bridge's truncation (
result.content[:2048]) and theToolInvocationRecord.error_messagefield'smax_length=2048constraint.src/synthorg/tools/invocation_record.py (2)
53-59: Validator allowsis_success=Falsewitherror_message=None.The validator enforces that success cannot have an error message, but does not require failures to provide one. This asymmetry is reasonable since some tool failures may lack descriptive messages. The test
test_tool_used_failure_no_error_messageintests/unit/hr/test_activity.pyconfirms this is intentional.
15-51: Well-structured immutable record model.The model correctly uses:
frozen=Truefor immutability (append-only pattern)NotBlankStrfor all identifier fieldsAwareDatetimeensuring timezone-aware timestampsmax_length=2048onerror_messagematching the bridge truncationdefault_factoryfor auto-generated UUIDssrc/synthorg/communication/delegation/record_store.py (2)
146-175: Clean pure-function helpers for validation and filtering.
_validate_time_rangeand_filterare well-isolated, pure functions with clear semantics:
- Time range validation:
start < endwhen both provided- Filter semantics:
start <= timestamp < end(half-open interval)
53-135: Query methods correctly filter by role.
get_records_as_delegatorfilters bydelegator_idandget_records_as_delegateefilters bydelegatee_id, ensuring records appear in exactly one perspective per agent.get_all_recordscorrectly returns unfiltered results for org-wide queries.
| def _fetch_task_metrics( | ||
| app_state: AppState, | ||
| agent_id: str | None, | ||
| since: datetime, | ||
| now: datetime, | ||
| ) -> tuple[TaskMetricRecord, ...]: | ||
| """Fetch task metrics, falling back to empty on failure.""" | ||
| try: | ||
| return app_state.performance_tracker.get_task_metrics( | ||
| agent_id=agent_id, | ||
| since=since, | ||
| until=now, | ||
| ) | ||
| except MemoryError, RecursionError: | ||
| raise | ||
| except ServiceUnavailableError: | ||
| raise | ||
| except Exception: | ||
| logger.warning( | ||
| API_REQUEST_ERROR, | ||
| endpoint="activities", | ||
| error="performance_tracker_unavailable", | ||
| exc_info=True, | ||
| ) | ||
| return () |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Synchronous function _fetch_task_metrics breaks parallel execution.
_fetch_task_metrics is a synchronous function (no async def) and is called directly on line 106, outside the TaskGroup. This means task metrics are fetched before the parallel cost/tool/delegation fetches begin, serializing what could be concurrent.
If performance_tracker.get_task_metrics is synchronous by design, consider wrapping it in asyncio.to_thread() inside the TaskGroup to avoid blocking the event loop, or document why serial execution is acceptable here.
♻️ Proposed fix to parallelize task metrics fetch
- task_metrics = _fetch_task_metrics(app_state, agent_id, since, now)
async with asyncio.TaskGroup() as tg:
+ metrics_task = tg.create_task(
+ asyncio.to_thread(_fetch_task_metrics, app_state, agent_id, since, now),
+ )
cost_task = tg.create_task(
_fetch_cost_records(app_state, agent_id, since, now),
)
...
+ task_metrics = metrics_task.result()
cost_records = cost_task.result()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/api/controllers/activities.py` around lines 149 - 173,
_fetch_task_metrics is synchronous and blocks the event loop when called outside
the TaskGroup; change the call site to run it inside the TaskGroup using
asyncio.to_thread(...) or make _fetch_task_metrics async and await the I/O in a
background thread. Specifically, keep the function logic but when invoking
performance_tracker.get_task_metrics (or any call to _fetch_task_metrics) start
it with asyncio.to_thread(app_state.performance_tracker.get_task_metrics,
agent_id=..., since=..., until=...) inside the TaskGroup so task metrics fetch
runs concurrently with cost/tool/delegation fetches; ensure exception handling
mirrors the existing try/except behavior and that the returned shape remains
tuple[TaskMetricRecord, ...].
| def record_sync(self, delegation: DelegationRecord) -> None: | ||
| """Append a delegation record (sync, for cooperative scheduling). | ||
|
|
||
| Safe to call from sync code under asyncio cooperative scheduling | ||
| since a plain list append cannot be interrupted by another | ||
| coroutine. | ||
|
|
||
| Args: | ||
| delegation: Immutable delegation record to store. | ||
| """ | ||
| self._records.append(delegation) | ||
| logger.debug( | ||
| DELEGATION_RECORD_STORED, | ||
| delegation_id=delegation.delegation_id, | ||
| delegator_id=delegation.delegator_id, | ||
| delegatee_id=delegation.delegatee_id, | ||
| ) |
There was a problem hiding this comment.
Inconsistent locking: record_sync writes without lock while _snapshot reads with lock.
record_sync appends to _records without acquiring _lock, while _snapshot acquires the lock before reading. This is inconsistent:
- If
list.append()is atomic under CPython's GIL (as the docstring claims), then_snapshot's lock is unnecessary overhead. - If the lock is needed for correctness, then
record_syncshould also acquire it.
The docstring's rationale about cooperative scheduling is valid for single-threaded asyncio, but the lock's presence suggests the design anticipated some form of concurrent access protection.
Consider either:
- Adding lock acquisition to
record_sync(viaasyncio.Lockrequires making it async), or - Removing the lock from
_snapshotand documenting the GIL/cooperative-scheduling assumption explicitly.
♻️ Option A: Make record_sync async with lock
- def record_sync(self, delegation: DelegationRecord) -> None:
- """Append a delegation record (sync, for cooperative scheduling).
-
- Safe to call from sync code under asyncio cooperative scheduling
- since a plain list append cannot be interrupted by another
- coroutine.
-
- Args:
- delegation: Immutable delegation record to store.
- """
- self._records.append(delegation)
+ async def record(self, delegation: DelegationRecord) -> None:
+ """Append a delegation record.
+
+ Args:
+ delegation: Immutable delegation record to store.
+ """
+ async with self._lock:
+ self._records.append(delegation)Note: This would require updating DelegationService._record_delegation to call await self._record_store.record(...) from an async context.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def record_sync(self, delegation: DelegationRecord) -> None: | |
| """Append a delegation record (sync, for cooperative scheduling). | |
| Safe to call from sync code under asyncio cooperative scheduling | |
| since a plain list append cannot be interrupted by another | |
| coroutine. | |
| Args: | |
| delegation: Immutable delegation record to store. | |
| """ | |
| self._records.append(delegation) | |
| logger.debug( | |
| DELEGATION_RECORD_STORED, | |
| delegation_id=delegation.delegation_id, | |
| delegator_id=delegation.delegator_id, | |
| delegatee_id=delegation.delegatee_id, | |
| ) | |
| async def record(self, delegation: DelegationRecord) -> None: | |
| """Append a delegation record. | |
| Args: | |
| delegation: Immutable delegation record to store. | |
| """ | |
| async with self._lock: | |
| self._records.append(delegation) | |
| logger.debug( | |
| DELEGATION_RECORD_STORED, | |
| delegation_id=delegation.delegation_id, | |
| delegator_id=delegation.delegator_id, | |
| delegatee_id=delegation.delegatee_id, | |
| ) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/communication/delegation/record_store.py` around lines 35 - 51,
record_sync currently mutates self._records without acquiring self._lock while
_snapshot reads under self._lock; to fix, make record_sync async and acquire the
asyncio lock (use "async def record_sync(...)" and "async with self._lock:"
around the append and logger call) so both writers and readers use the same
protection, and update callers (e.g. DelegationService._record_delegation) to
await the new async record_sync; ensure the method signature and all call sites
are updated accordingly to prevent blocking or race conditions.
| if self._record_store is not None: | ||
| try: | ||
| self._record_store.record_sync(record) | ||
| except MemoryError, RecursionError: |
There was a problem hiding this comment.
Delegation activity is still stored in process-local memory.
record_sync() currently just appends to an in-memory list in src/synthorg/communication/delegation/record_store.py:31-52, so this write is lost on restart and invisible to other workers. That makes delegation_sent / delegation_received incomplete for an org-wide feed and misses the linked issue's persisted/queryable requirement.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/communication/delegation/service.py` around lines 190 - 193, The
current write via self._record_store.record_sync(record) uses the in-memory
RecordStore and loses data on restart; change to a durable persistence: update
the RecordStore.record_sync implementation (the class and method named
record_sync in RecordStore) to persist delegation records to a shared durable
store (e.g., DB table, key/value store, or append-only log) instead of appending
an in-memory list, and update the call site in service.py (the code invoking
self._record_store.record_sync) to handle/await any async DB call or handle
returned errors; ensure record structure (IDs/timestamps/org_id) is persisted
and that the persistence layer provides queryable APIs for
delegation_sent/delegation_received so other workers can read them.
| tracker = invoker._invocation_tracker # noqa: SLF001 | ||
| agent_id = invoker._agent_id # noqa: SLF001 | ||
| if tracker is None or agent_id is None: | ||
| return |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Count early return paths in _invoke_single that bypass record_tool_invocation
rg -n "return tool_or_error|return permission_error|return param_error|return security_error|return parking_error" src/synthorg/tools/invoker.pyRepository: Aureliolo/synthorg
Length of output: 248
🏁 Script executed:
#!/bin/bash
# Examine the structure of _invoke_single in invoker.py to see control flow
sed -n '354,411p' src/synthorg/tools/invoker.py | cat -nRepository: Aureliolo/synthorg
Length of output: 2490
🏁 Script executed:
#!/bin/bash
# Find the line number where record_tool_invocation is called in invoker.py
rg -n "record_tool_invocation" src/synthorg/tools/invoker.pyRepository: Aureliolo/synthorg
Length of output: 184
🏁 Script executed:
#!/bin/bash
# Check invocation_bridge.py to understand what record_tool_invocation does
cat -n src/synthorg/tools/invocation_bridge.py | head -60Repository: Aureliolo/synthorg
Length of output: 2385
Early returns bypass the invocation recording.
Confirmed: record_tool_invocation is called only at line 56, after successful execution. Early-exit paths at tool lookup failure (line 15), permission check (line 19), parameter validation (line 23), security check (line 31), tool execution error (line 35), and parking metadata tracking (line 46) all return ToolResult objects without recording them.
These unrecorded failures prevent visibility into failed invocation attempts in the activity timeline. If comprehensive invocation tracking (including permission denials, validation failures, and security blocks) is desired, consider either calling record_tool_invocation from each early-return path or moving the call to a finally block.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/tools/invocation_bridge.py` around lines 35 - 38, The current
flow in invocation_bridge.py returns early on many error paths and never calls
record_tool_invocation, so failed attempts aren't recorded; update the code that
uses invoker._invocation_tracker and invoker._agent_id to ensure
record_tool_invocation is invoked for every exit by either (a) moving the
record_tool_invocation call into a single finally block that runs after the tool
lookup/permission/validation/security/execution/parking logic, or (b) adding
explicit calls to record_tool_invocation (using the same signature as the
successful path) in each early-return branch that returns a ToolResult;
reference the identifiers record_tool_invocation, invoker._invocation_tracker,
invoker._agent_id, and ToolResult when making the change.
| class ToolInvocationTracker: | ||
| """In-memory, append-only tool invocation tracking service. | ||
|
|
||
| Records tool invocation outcomes and provides filtered queries | ||
| for the activity timeline. | ||
| """ | ||
|
|
||
| def __init__(self) -> None: | ||
| self._records: list[ToolInvocationRecord] = [] | ||
| self._lock: asyncio.Lock = asyncio.Lock() |
There was a problem hiding this comment.
This tracker is still worker-local, not persisted.
_records lives only in app memory, so tool_used events disappear on restart and the org-wide feed only sees invocations handled by the same process. That misses the linked issue's persisted/queryable requirement and will be inconsistent in multi-worker deployments.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/tools/invocation_tracker.py` around lines 24 - 33,
ToolInvocationTracker currently stores _records in-process so tool_used events
are lost on restart and invisible across workers; replace the in-memory list
with a persistent backend by injecting a storage client (e.g., a database or
shared cache) into ToolInvocationTracker, remove or make _records an optional
local cache, and update its write/read methods (the append/write code that
records invocations and any query methods that read the timeline) to persist and
query from the injected storage asynchronously while keeping the asyncio.Lock
only for local concurrency; ensure the new constructor accepts the storage
client, use unique keys/indexes for org-wide queries, and add
migrations/initialization where needed so the timeline is durable and consistent
across multiple workers.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
src/synthorg/hr/activity.py (1)
154-173:⚠️ Potential issue | 🟠 MajorRaw tool error text exposure in activity descriptions remains unaddressed.
The converter appends
record.error_messagedirectly to the description when the tool invocation fails (lines 161-162). This has two issues:
- Security: Raw error messages can leak internal paths, prompts, or exception text to API consumers.
- Length overflow:
error_messagecan be up to 2048 characters, whileActivityEvent.descriptionhasmax_length=1024. The combined string ("Tool X failed: " + error_message) could exceed this limit and cause validation failures.🔒 Proposed fix
if record.is_success: desc = f"Tool {record.tool_name} executed successfully" else: - suffix = f": {record.error_message}" if record.error_message else "" - desc = f"Tool {record.tool_name} failed{suffix}" + desc = f"Tool {record.tool_name} failed"If error details are needed for debugging, consider exposing them through
related_idsor a separate secure field rather than in the user-facing description.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/hr/activity.py` around lines 154 - 173, The description currently appends raw record.error_message in _tool_invocation_to_activity which can leak sensitive data and exceed ActivityEvent.description's max_length; change it to a generic failure description (e.g., "Tool {record.tool_name} failed") and do NOT append raw error text to description, and if you need error details for debugging put a sanitized and length-limited version into related_ids (e.g., related_ids["tool_error"] = sanitize_and_truncate(record.error_message, 512)) or a separate secure field, ensuring any use of record.error_message is both sanitized and truncated before storing.src/synthorg/api/controllers/activities.py (1)
106-146: 🧹 Nitpick | 🔵 TrivialTaskGroup usage is correct, but sync fetch serializes task metrics retrieval.
The
asyncio.TaskGroupcorrectly parallelizes the async fetches for cost, tool, and delegation records. TheExceptionGrouphandling properly propagates fatal errors while gracefully degrading on non-fatal failures.However,
_fetch_task_metrics(line 106) is synchronous and called before theTaskGroup, meaning task metrics are fetched serially before the parallel fetches begin. Ifperformance_tracker.get_task_metricsperforms I/O, consider wrapping it inasyncio.to_thread()inside theTaskGroupto avoid blocking.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/controllers/activities.py` around lines 106 - 146, _fetch_task_metrics is called synchronously before the asyncio.TaskGroup, causing potential blocking; move it into the TaskGroup as a background thread task (use asyncio.to_thread) so it runs in parallel with _fetch_cost_records, _fetch_tool_invocations, and _fetch_delegation_records, then read its result from the created task like cost_task.result(); specifically, create a task via tg.create_task(asyncio.to_thread(_fetch_task_metrics, app_state, agent_id, since, now)) (or convert _fetch_task_metrics into an async function and create a normal task), ensure the new task variable (e.g., metrics_task) is handled the same as cost_task/tool_task/del_task in the ExceptionGroup and when extracting the final value for merge_activity_timeline.
🤖 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/communication/delegation/service.py`:
- Around line 191-195: The current except block around
self._record_store.record_sync(record) re-raises MemoryError/RecursionError
leaving prior state mutated; on such fatal store errors undo the prior guard and
audit changes before re-raising: call the inverse of _guard.record_delegation
(e.g. a rollback/release method on _guard) and remove the appended record from
_audit_trail (or otherwise mark it as failed) if record_sync fails, then
re-raise the original exception so delegate() doesn't leave dedupe blockers or a
visible audit entry for a failed call.
In `@tests/unit/communication/delegation/test_service.py`:
- Around line 454-457: Tests are mutating the private attribute _record_store
after construction instead of exercising the public constructor wiring; update
the three test locations to pass the store via the DelegationService constructor
(i.e., use DelegationService(..., record_store=store) or change the
_build_service helper to accept a record_store argument and forward it into
DelegationService.__init__) so the tests validate the new constructor wiring for
DelegationRecordStore instead of assigning to service._record_store.
---
Duplicate comments:
In `@src/synthorg/api/controllers/activities.py`:
- Around line 106-146: _fetch_task_metrics is called synchronously before the
asyncio.TaskGroup, causing potential blocking; move it into the TaskGroup as a
background thread task (use asyncio.to_thread) so it runs in parallel with
_fetch_cost_records, _fetch_tool_invocations, and _fetch_delegation_records,
then read its result from the created task like cost_task.result();
specifically, create a task via
tg.create_task(asyncio.to_thread(_fetch_task_metrics, app_state, agent_id,
since, now)) (or convert _fetch_task_metrics into an async function and create a
normal task), ensure the new task variable (e.g., metrics_task) is handled the
same as cost_task/tool_task/del_task in the ExceptionGroup and when extracting
the final value for merge_activity_timeline.
In `@src/synthorg/hr/activity.py`:
- Around line 154-173: The description currently appends raw
record.error_message in _tool_invocation_to_activity which can leak sensitive
data and exceed ActivityEvent.description's max_length; change it to a generic
failure description (e.g., "Tool {record.tool_name} failed") and do NOT append
raw error text to description, and if you need error details for debugging put a
sanitized and length-limited version into related_ids (e.g.,
related_ids["tool_error"] = sanitize_and_truncate(record.error_message, 512)) or
a separate secure field, ensuring any use of record.error_message is both
sanitized and truncated before storing.
🪄 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: 781518c3-73bc-4d10-88ff-f2b410f5e1bb
📒 Files selected for processing (16)
src/synthorg/api/controllers/activities.pysrc/synthorg/api/state.pysrc/synthorg/communication/delegation/models.pysrc/synthorg/communication/delegation/record_store.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/hr/activity.pysrc/synthorg/observability/events/delegation.pysrc/synthorg/observability/events/tool.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/tools/invocation_record.pytests/unit/api/controllers/test_activities.pytests/unit/communication/delegation/test_record_store.pytests/unit/communication/delegation/test_service.pytests/unit/hr/performance/test_models.pytests/unit/tools/test_invocation_bridge.pytests/unit/tools/test_invocation_record.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Backend
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotations-- Python 3.14 has PEP 649 native lazy annotations
Useexcept A, B:syntax without parentheses for exception handling (PEP 758) -- ruff enforces this on Python 3.14
Add type hints to all public functions and classes; mypy strict mode required
Use Google-style docstrings on all public classes and functions (enforced by ruff D rules)
Immutability enforcement: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping. For dict/list fields in frozen Pydantic models, rely on frozen=True and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization)
Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict); use@computed_fieldfor derived values instead of storing redundant fields; use NotBlankStr for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (multiple tool invocations, parallel agent calls). Use structured concurrency over bare create_task
Keep functions under 50 lines, files under 800 lines
Line length: 88 characters (ruff enforced)
Explicitly handle all errors; never silently swallow exceptions
Validate at system boundaries: user input, external APIs, config files
Files:
src/synthorg/communication/delegation/models.pytests/unit/tools/test_invocation_bridge.pytests/unit/communication/delegation/test_service.pytests/unit/api/controllers/test_activities.pysrc/synthorg/observability/events/delegation.pysrc/synthorg/observability/events/tool.pysrc/synthorg/communication/delegation/service.pytests/unit/tools/test_invocation_record.pytests/unit/hr/performance/test_models.pysrc/synthorg/api/state.pysrc/synthorg/hr/activity.pytests/unit/communication/delegation/test_record_store.pysrc/synthorg/communication/delegation/record_store.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/tools/invocation_record.pysrc/synthorg/tools/invocation_bridge.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic MUST have:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)
Use logger.info(EVENT, key=value) with structured kwargs for logging; never use logger.info('msg %s', val) formatting
Never useimport logging/logging.getLogger()/print()in application code. Exception: observability/setup.py and observability/sinks.py may use stdlib logging and print() for bootstrap code only
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
All provider calls go through BaseCompletionProvider which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code. RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig. Retryable errors (is_retryable=True): RateLimitError, ProviderTimeoutError, ProviderConnectionError, ProviderInternalError. Non-retryable errors raise immediately. RetryExhaustedError signals all retries failed -- engine layer catches this to trigger fallback chains. Rate limiter respects RateLimitError.retry_after
Always read the relevant docs/design/ page before implementing any feature or planning any issue. DESIGN_SPEC.md is a pointer file. When a spec topic is referenced (e.g., 'the Agents page'), read the relevant docs/design/ page before coding
If implementation deviates from the design spec (better approach, scope evolved), alert the user and explain why. User decides whether to proceed or update spec. Do NOT silently diverge. When approved deviations occur, update relevant docs/design/ page to reflect new reality
Files:
src/synthorg/communication/delegation/models.pysrc/synthorg/observability/events/delegation.pysrc/synthorg/observability/events/tool.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/api/state.pysrc/synthorg/hr/activity.pysrc/synthorg/communication/delegation/record_store.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/tools/invocation_record.pysrc/synthorg/tools/invocation_bridge.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Mark all tests with@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, or@pytest.mark.slow
Always run pytest with-n autofor parallelism via pytest-xdist; never run tests sequentially. Minimum coverage: 80% (enforced in CI). Timeout: 30 seconds per test globally (do not add per-file markers; non-default overrides like timeout(60) are allowed)
Vendor-agnostic naming everywhere: NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, large/medium/small aliases. Vendor names allowed only in: (1) docs/design/operations.md provider list, (2) .claude/ skill/agent files, (3) third-party import paths. Tests must use test-provider, test-small-001, etc.
Use Hypothesis for property-based testing (@given+@settingswith HYPOTHESIS_PROFILE env var: 'ci' for 50 examples (default), 'dev' for 1000 examples). Run dev profile: HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties. .hypothesis/ is gitignored
Never skip, dismiss, or ignore flaky tests -- always fix them fully. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep() for determinism. For tasks blocking indefinitely until cancelled, use asyncio.Event().wait() instead of asyncio.sleep(large_number) -- it is cancellation-safe
Files:
tests/unit/tools/test_invocation_bridge.pytests/unit/communication/delegation/test_service.pytests/unit/api/controllers/test_activities.pytests/unit/tools/test_invocation_record.pytests/unit/hr/performance/test_models.pytests/unit/communication/delegation/test_record_store.py
🧠 Learnings (42)
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to **/*.py : Config vs runtime state: use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state. Never mix static config fields with mutable runtime fields in one model.
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 BaseModel, model_validator, computed_field, ConfigDict.
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-16T23:05:29.577Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T23:05:29.577Z
Learning: Applies to **/*.py : For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to **/*.py : Config vs runtime state: frozen Pydantic models for config/identity; separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Applied to files:
src/synthorg/communication/delegation/models.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/communication/delegation/models.py
📚 Learning: 2026-03-15T18:42:17.990Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:42:17.990Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`
Applied to files:
src/synthorg/communication/delegation/models.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/communication/delegation/models.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/delegation.pysrc/synthorg/observability/events/tool.pysrc/synthorg/tools/invocation_bridge.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/delegation.pysrc/synthorg/observability/events/tool.py
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
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/delegation.pysrc/synthorg/observability/events/tool.pysrc/synthorg/tools/invocation_bridge.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/delegation.pysrc/synthorg/observability/events/tool.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/tool.pysrc/synthorg/tools/invocation_bridge.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/tool.pysrc/synthorg/tools/invocation_bridge.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls
Applied to files:
src/synthorg/observability/events/tool.pysrc/synthorg/tools/invocation_bridge.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings
Applied to files:
src/synthorg/observability/events/tool.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use event name constants from domain-specific modules under `ai_company.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`). Import directly: `from ai_company.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/observability/events/tool.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/communication/delegation/service.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/tools/invocation_bridge.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/communication/delegation/service.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/tools/invocation_bridge.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/communication/delegation/service.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/tools/invocation_bridge.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/communication/delegation/service.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/tools/invocation_bridge.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/communication/delegation/service.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/tools/invocation_bridge.py
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to **/*.py : Use `except A, B:` syntax without parentheses for exception handling (PEP 758) -- ruff enforces this on Python 3.14
Applied to files:
src/synthorg/communication/delegation/service.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/tools/invocation_bridge.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/communication/delegation/service.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/tools/invocation_bridge.py
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to **/*.py : Explicitly handle all errors; never silently swallow exceptions
Applied to files:
src/synthorg/communication/delegation/service.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/tools/invocation_bridge.py
📚 Learning: 2026-03-15T16:55:07.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T16:55:07.730Z
Learning: Applies to **/*.py : Use PEP 758 except syntax: use `except A, B:` (no parentheses) — ruff enforces this on Python 3.14.
Applied to files:
src/synthorg/communication/delegation/service.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions.
Applied to files:
src/synthorg/communication/delegation/service.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, and key function entry/exit
Applied to files:
src/synthorg/communication/delegation/service.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : All error paths must log at WARNING or ERROR with context before raising.
Applied to files:
src/synthorg/communication/delegation/service.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/communication/**/*.py : Communication package (communication/): message bus, dispatcher, messenger, channels, delegation, loop prevention, conflict resolution; meeting/ subpackage for meeting protocol (round-robin, position papers, structured phases), scheduler (frequency, participant resolver), orchestrator
Applied to files:
src/synthorg/api/state.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
src/synthorg/api/state.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/backup/**/*.py : Backup package (backup/): scheduled/manual/lifecycle backups of persistence DB, agent memory, company config. BackupService orchestrator, BackupScheduler (periodic asyncio task), RetentionManager (count + age pruning), tar.gz compression, SHA-256 checksums, manifest tracking, validated restore with atomic rollback and safety backup. handlers/ subpackage: ComponentHandler protocol + concrete handlers (PersistenceComponentHandler, MemoryComponentHandler, ConfigComponentHandler)
Applied to files:
src/synthorg/api/state.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)
Applied to files:
src/synthorg/api/state.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Prefer `asyncio.TaskGroup` for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare `create_task`. Existing code is being migrated incrementally.
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-17T18:52:05.142Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T18:52:05.142Z
Learning: Applies to **/*.py : Async concurrency: prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-24T22:47:39.223Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T22:47:39.223Z
Learning: Applies to **/*.py : Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (multiple tool invocations, parallel agent calls). Use structured concurrency over bare create_task
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/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/api/controllers/activities.py
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/hr/**/*.py : HR package (hr/): hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)
Applied to files:
src/synthorg/api/controllers/activities.py
🔇 Additional comments (21)
tests/unit/api/controllers/test_activities.py (4)
369-417: Delegation event tests are correct.The tests properly verify:
- Org-wide feed returns both
delegation_sentanddelegation_receivedevents from the same record- Agent-scoped filtering correctly returns only
delegation_sentfor the delegatorHowever, the past review comment about adding graceful-degradation coverage for the new activity sources (tool invocations, delegation records) remains unaddressed. Consider adding tests that verify
/api/v1/activitiesstill returns 200 whentool_invocation_trackerordelegation_record_storeare absent or raise exceptions.
29-43: Deterministic time freezing is well-implemented.The
_FrozenDatetimesubclass andautousefixture correctly freeze time in the controller module, ensuring test determinism. This aligns with the coding guideline to mock time for timing-sensitive tests.
300-320: Cost record integration test is correct.The test properly verifies that cost records are surfaced in the activity feed with the correct
event_typeand uses vendor-agnostic naming (test-provider,test-medium-001).
351-367: Tool invocation integration test is correct.The test properly verifies that tool invocation records are surfaced in the activity feed with
event_type="tool_used".src/synthorg/tools/invocation_bridge.py (2)
35-57: Implementation follows best-effort pattern correctly.The exception handling properly uses PEP 758 syntax (
except MemoryError, RecursionError:) and theTOOL_INVOCATION_RECORD_FAILEDevent constant addresses the previous review feedback about semantic accuracy.Note: Per the past review comment, this function is only called after successful tool execution (see
invoker.py:409). Early-exit paths (permission denials, validation failures, security blocks) do not record invocations. This is a design choice—if comprehensive tracking of all invocation attempts is desired, consider recording from earlier points or using afinallyblock pattern.
1-34: Module setup and documentation are well-structured.The module clearly documents its best-effort nature and the function signature is properly typed with a Google-style docstring.
src/synthorg/communication/delegation/record_store.py (3)
24-56: Concurrency model is documented, addressing previous review concern.The class docstring (lines 30-33) now explicitly documents the intentional locking asymmetry:
record_syncdoesn't acquire the lock (atomiclist.appendunder cooperative asyncio)_lockserializes concurrent async readers onlyThis documentation addresses the previous review comment about inconsistent locking by explaining the design rationale. The approach is valid for single-threaded asyncio contexts.
58-145: Query methods are well-structured with consistent patterns.All three query methods follow the same pattern:
- Validate time range
- Acquire immutable snapshot under lock
- Log the query
- Filter and return
The docstrings correctly document the half-open interval semantics (
start <= timestamp < end).
148-180: Pure helper functions are correctly implemented.
_validate_time_rangeprovides clear error messages and_filtercorrectly implements the half-open interval semantics along with optional role-based filtering.tests/unit/communication/delegation/test_record_store.py (3)
1-66: Basic store tests are well-implemented.The test setup with
_make_recordhelper and basic CRUD/filtering tests provide good foundational coverage forDelegationRecordStore.
84-161: Time range and combined filtering tests are comprehensive.The tests correctly verify:
- Half-open interval semantics (
start <= timestamp < end)- Error handling for invalid time ranges
- Combined role + time range filtering
163-188: Delegatee time range filtering test is correct.Good symmetry with the delegator test, ensuring both perspectives are equally tested.
src/synthorg/api/controllers/activities.py (3)
1-31: Imports are correctly updated for new activity sources.The new imports for
CostRecord,DelegationRecord,TaskMetricRecord, andToolInvocationRecordproperly support the expanded timeline merging.
167-191: Task metrics fetcher implements graceful degradation correctly.The exception handling follows the established pattern: re-raising fatal errors (
MemoryError,RecursionError,ServiceUnavailableError) while gracefully degrading to an empty tuple for other failures with appropriate warning logging.
194-299: Async fetchers implement graceful degradation consistently.The cost, tool invocation, and delegation record fetchers all:
- Check the
has_*guard before accessing the service- Apply time range filtering
- Re-raise fatal errors while gracefully degrading on others
The org-wide delegation logic correctly fetches all records once and passes them to both
sentandreceivedperspectives, which aligns with themerge_activity_timelinedesign.src/synthorg/hr/activity.py (3)
1-21: Module documentation accurately reflects expanded functionality.The docstring correctly describes all event sources merged into the timeline.
117-151: Task started and cost record converters are well-implemented.The
_task_metric_to_started_activitycorrectly uses an assertion to enforce thestarted_atprecondition (caller must ensure it's not None). The_cost_record_to_activityprovides useful summary information in the description.
176-258: Delegation converters and merge function are correctly implemented.The delegation converters properly set
agent_idbased on perspective (delegator for sent, delegatee for received). Themerge_activity_timelinefunction maintains backward compatibility with positional-onlylifecycle_eventsandtask_metricsarguments while adding keyword-only parameters for the new sources.src/synthorg/tools/invocation_record.py (1)
15-59: ToolInvocationRecord model is well-designed.The model correctly:
- Uses
NotBlankStrfor identifier fields- Enforces immutability with
frozen=True- Validates the consistency constraint between
is_successanderror_message- Sets appropriate
max_length=2048for error messages (matching the truncation ininvocation_bridge.py)src/synthorg/api/state.py (2)
64-156: AppState initialization correctly extended for new services.The new
_tool_invocation_trackerand_delegation_record_storeslots, init parameters, and assignments follow the established pattern for optional services inAppState.
193-400: New service properties follow established patterns.The
has_*boolean guards and accessor properties forcost_tracker,tool_invocation_tracker, anddelegation_record_storeare consistent with the existing pattern for optional services inAppState.
| if self._record_store is not None: | ||
| try: | ||
| self._record_store.record_sync(record) | ||
| except MemoryError, RecursionError: | ||
| raise |
There was a problem hiding this comment.
Don't leave delegation state half-committed on fatal store errors.
By the time record_sync() runs on Line 193, _guard.record_delegation() and _audit_trail.append(record) have already executed. If Lines 194-195 re-raise, delegate() fails but retries can still be dedup-blocked and get_audit_trail() already shows a record for a call that never returned success.
Suggested fix
- self._guard.record_delegation(
- request.delegator_id,
- request.delegatee_id,
- request.task.id,
- )
record = DelegationRecord(
delegation_id=str(uuid4()),
delegator_id=request.delegator_id,
delegatee_id=request.delegatee_id,
original_task_id=request.task.id,
delegated_task_id=sub_task.id,
timestamp=datetime.now(UTC),
refinement=request.refinement,
)
- self._audit_trail.append(record)
if self._record_store is not None:
try:
self._record_store.record_sync(record)
except MemoryError, RecursionError:
raise
except Exception:
logger.warning(
DELEGATION_RECORD_STORE_FAILED,
delegator=request.delegator_id,
delegatee=request.delegatee_id,
exc_info=True,
)
+ self._guard.record_delegation(
+ request.delegator_id,
+ request.delegatee_id,
+ request.task.id,
+ )
+ self._audit_trail.append(record)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/communication/delegation/service.py` around lines 191 - 195, The
current except block around self._record_store.record_sync(record) re-raises
MemoryError/RecursionError leaving prior state mutated; on such fatal store
errors undo the prior guard and audit changes before re-raising: call the
inverse of _guard.record_delegation (e.g. a rollback/release method on _guard)
and remove the appended record from _audit_trail (or otherwise mark it as
failed) if record_sync fails, then re-raise the original exception so delegate()
doesn't leave dedupe blockers or a visible audit entry for a failed call.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/synthorg/hr/activity.py (1)
241-256: 🧹 Nitpick | 🔵 TrivialConsider the insertion order dependency for stable sort behavior.
The current implementation adds events in this order: lifecycle → task_completed → task_started → cost → tool → delegation_sent → delegation_received. Python's
sort()is stable, so events with identical timestamps preserve this insertion order.The test
test_identical_timestamps_stable_sortat line 226-228 asserts lifecycle appears before task_completed at the same timestamp, which depends on this insertion order. This is fine as-is, but if the insertion order ever changes, the stable sort assertion would break silently.Consider adding a brief comment documenting this intentional ordering, or acknowledge this is acceptable behavior.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/hr/activity.py` around lines 241 - 256, The current assembly of activities relies on insertion order to achieve stable ordering for identical timestamps (calls to _lifecycle_to_activity, _task_metric_to_activity/_task_metric_to_started_activity, _cost_record_to_activity, _tool_invocation_to_activity, _delegation_to_sent_activity, _delegation_to_received_activity before sorting), which the test `test_identical_timestamps_stable_sort` depends on; add a concise inline comment above the activities list explaining that the specific insertion order is intentional and relied upon because Python's sort is stable (or alternatively document this behavior in the function/class docstring) so future maintainers don't accidentally reorder these extends and break the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/synthorg/hr/activity.py`:
- Around line 241-256: The current assembly of activities relies on insertion
order to achieve stable ordering for identical timestamps (calls to
_lifecycle_to_activity,
_task_metric_to_activity/_task_metric_to_started_activity,
_cost_record_to_activity, _tool_invocation_to_activity,
_delegation_to_sent_activity, _delegation_to_received_activity before sorting),
which the test `test_identical_timestamps_stable_sort` depends on; add a concise
inline comment above the activities list explaining that the specific insertion
order is intentional and relied upon because Python's sort is stable (or
alternatively document this behavior in the function/class docstring) so future
maintainers don't accidentally reorder these extends and break the test.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 3c7058d3-7b17-44b7-b82f-cffd83b9851e
📒 Files selected for processing (2)
src/synthorg/hr/activity.pytests/unit/hr/test_activity.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotations-- Python 3.14 has PEP 649 native lazy annotations
Useexcept A, B:syntax without parentheses for exception handling (PEP 758) -- ruff enforces this on Python 3.14
Add type hints to all public functions and classes; mypy strict mode required
Use Google-style docstrings on all public classes and functions (enforced by ruff D rules)
Immutability enforcement: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping. For dict/list fields in frozen Pydantic models, rely on frozen=True and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization)
Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict); use@computed_fieldfor derived values instead of storing redundant fields; use NotBlankStr for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (multiple tool invocations, parallel agent calls). Use structured concurrency over bare create_task
Keep functions under 50 lines, files under 800 lines
Line length: 88 characters (ruff enforced)
Explicitly handle all errors; never silently swallow exceptions
Validate at system boundaries: user input, external APIs, config files
Files:
src/synthorg/hr/activity.pytests/unit/hr/test_activity.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic MUST have:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)
Use logger.info(EVENT, key=value) with structured kwargs for logging; never use logger.info('msg %s', val) formatting
Never useimport logging/logging.getLogger()/print()in application code. Exception: observability/setup.py and observability/sinks.py may use stdlib logging and print() for bootstrap code only
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
All provider calls go through BaseCompletionProvider which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code. RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig. Retryable errors (is_retryable=True): RateLimitError, ProviderTimeoutError, ProviderConnectionError, ProviderInternalError. Non-retryable errors raise immediately. RetryExhaustedError signals all retries failed -- engine layer catches this to trigger fallback chains. Rate limiter respects RateLimitError.retry_after
Always read the relevant docs/design/ page before implementing any feature or planning any issue. DESIGN_SPEC.md is a pointer file. When a spec topic is referenced (e.g., 'the Agents page'), read the relevant docs/design/ page before coding
If implementation deviates from the design spec (better approach, scope evolved), alert the user and explain why. User decides whether to proceed or update spec. Do NOT silently diverge. When approved deviations occur, update relevant docs/design/ page to reflect new reality
Files:
src/synthorg/hr/activity.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Mark all tests with@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, or@pytest.mark.slow
Always run pytest with-n autofor parallelism via pytest-xdist; never run tests sequentially. Minimum coverage: 80% (enforced in CI). Timeout: 30 seconds per test globally (do not add per-file markers; non-default overrides like timeout(60) are allowed)
Vendor-agnostic naming everywhere: NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, large/medium/small aliases. Vendor names allowed only in: (1) docs/design/operations.md provider list, (2) .claude/ skill/agent files, (3) third-party import paths. Tests must use test-provider, test-small-001, etc.
Use Hypothesis for property-based testing (@given+@settingswith HYPOTHESIS_PROFILE env var: 'ci' for 50 examples (default), 'dev' for 1000 examples). Run dev profile: HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties. .hypothesis/ is gitignored
Never skip, dismiss, or ignore flaky tests -- always fix them fully. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep() for determinism. For tasks blocking indefinitely until cancelled, use asyncio.Event().wait() instead of asyncio.sleep(large_number) -- it is cancellation-safe
Files:
tests/unit/hr/test_activity.py
🧠 Learnings (4)
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)
Applied to files:
tests/unit/hr/test_activity.py
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/hr/**/*.py : HR package (hr/): hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)
Applied to files:
tests/unit/hr/test_activity.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.
Applied to files:
tests/unit/hr/test_activity.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/hr/**/*.py : HR engine must provide: hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, trend detection), promotion/demotion
Applied to files:
tests/unit/hr/test_activity.py
🔇 Additional comments (10)
src/synthorg/hr/activity.py (3)
117-133: LGTM! Clean conversion with appropriate precondition.The
assert record.started_at is not Noneis a valid internal contract since the docstring clearly states the caller must ensurestarted_atis not None, and the caller (merge_activity_timeline) properly filters withif r.started_at is not Noneat line 248.
154-172: LGTM! Error message properly redacted.The implementation correctly omits
record.error_messagefrom the description, preventing leakage of internal paths, stack traces, or sensitive data through the activity feed API. This aligns with the commit message "fix: redact raw error messages from tool_used activity descriptions."
175-211: LGTM! Dual-perspective delegation converters are well-designed.The converters correctly distinguish the perspective:
delegation_sent:agent_id= delegator, includesdelegatee_iddelegation_received:agent_id= delegatee, includesdelegator_idThis enables proper agent-specific timeline queries from both directions.
tests/unit/hr/test_activity.py (7)
65-125: LGTM! Well-structured factory helpers with vendor-agnostic naming.The new factory helpers
_make_cost_record,_make_tool_invocation, and_make_delegation_recordfollow the same pattern as existing helpers with keyword-only arguments and sensible defaults. The use oftest-medium-001andtest-providercomplies with the vendor-agnostic naming guideline.
243-250: LGTM! Good backward compatibility test.This test verifies the PR objective of maintaining backward compatibility for positional-only calls to
merge_activity_timeline.
256-319: LGTM! Comprehensive task_started event coverage.Tests cover all key scenarios:
- Event generation when
started_atis present- No event when
started_atis None- Both events from same record
- Correct ordering relative to other event types
325-372: LGTM! Solid cost_incurred event coverage.Tests validate the event fields, description format including token counts and cost, and proper merge ordering.
402-421: LGTM! Critical test for error message redaction.Lines 420-421 explicitly verify that the raw
error_message("Permission denied") is NOT exposed in the activity description. This test ensures the security fix remains in place.
542-574: LGTM! Comprehensive integration test for all event types.The
test_merge_all_event_typestest is an excellent integration-style test that:
- Exercises all 7 event sources in one call
- Verifies the expected count (7 events)
- Validates descending timestamp ordering
This provides confidence that all converters work together correctly.
580-681: LGTM! Existing career events tests preserved.No changes to the
TestFilterCareerEventsclass, maintaining existing coverage.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/synthorg/engine/agent_engine.py`:
- Around line 227-228: The Args docstring for the AgentEngine constructor was
not updated after adding new parameters; update the class/function docstring
(the Args: section for __init__ in agent_engine.py) to include entries for
tool_invocation_tracker: ToolInvocationTracker | None and provider_registry:
<appropriate type> (describe purpose, default/None behavior), following
Google-style docstring format and matching existing wording style for other
parameters so the public API docs remain accurate.
In `@tests/unit/communication/delegation/test_service.py`:
- Around line 493-510: Add a sibling unit test mirroring
test_memory_error_in_record_store_propagates that asserts RecursionError is
propagated: mock DelegationRecordStore (spec=DelegationRecordStore) and set
store.record_sync.side_effect = RecursionError("recursion"), build the service
with that record_store using _build_service, create the same DelegationRequest
and agents, and assert that service.delegate(request, delegator, delegatee)
raises RecursionError; reference the existing
test_memory_error_in_record_store_propagates, DelegationRecordStore.record_sync,
and service.delegate to place the new test.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 77695e06-dd48-49ee-8d27-fd5e036c3f4d
📒 Files selected for processing (2)
src/synthorg/engine/agent_engine.pytests/unit/communication/delegation/test_service.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Backend
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotations-- Python 3.14 has PEP 649 native lazy annotations
Useexcept A, B:syntax without parentheses for exception handling (PEP 758) -- ruff enforces this on Python 3.14
Add type hints to all public functions and classes; mypy strict mode required
Use Google-style docstrings on all public classes and functions (enforced by ruff D rules)
Immutability enforcement: create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction + MappingProxyType wrapping. For dict/list fields in frozen Pydantic models, rely on frozen=True and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization)
Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict); use@computed_fieldfor derived values instead of storing redundant fields; use NotBlankStr for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (multiple tool invocations, parallel agent calls). Use structured concurrency over bare create_task
Keep functions under 50 lines, files under 800 lines
Line length: 88 characters (ruff enforced)
Explicitly handle all errors; never silently swallow exceptions
Validate at system boundaries: user input, external APIs, config files
Files:
src/synthorg/engine/agent_engine.pytests/unit/communication/delegation/test_service.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic MUST have:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)
Use logger.info(EVENT, key=value) with structured kwargs for logging; never use logger.info('msg %s', val) formatting
Never useimport logging/logging.getLogger()/print()in application code. Exception: observability/setup.py and observability/sinks.py may use stdlib logging and print() for bootstrap code only
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
All provider calls go through BaseCompletionProvider which applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code. RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig. Retryable errors (is_retryable=True): RateLimitError, ProviderTimeoutError, ProviderConnectionError, ProviderInternalError. Non-retryable errors raise immediately. RetryExhaustedError signals all retries failed -- engine layer catches this to trigger fallback chains. Rate limiter respects RateLimitError.retry_after
Always read the relevant docs/design/ page before implementing any feature or planning any issue. DESIGN_SPEC.md is a pointer file. When a spec topic is referenced (e.g., 'the Agents page'), read the relevant docs/design/ page before coding
If implementation deviates from the design spec (better approach, scope evolved), alert the user and explain why. User decides whether to proceed or update spec. Do NOT silently diverge. When approved deviations occur, update relevant docs/design/ page to reflect new reality
Files:
src/synthorg/engine/agent_engine.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Mark all tests with@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, or@pytest.mark.slow
Always run pytest with-n autofor parallelism via pytest-xdist; never run tests sequentially. Minimum coverage: 80% (enforced in CI). Timeout: 30 seconds per test globally (do not add per-file markers; non-default overrides like timeout(60) are allowed)
Vendor-agnostic naming everywhere: NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names: example-provider, example-large-001, example-medium-001, example-small-001, large/medium/small aliases. Vendor names allowed only in: (1) docs/design/operations.md provider list, (2) .claude/ skill/agent files, (3) third-party import paths. Tests must use test-provider, test-small-001, etc.
Use Hypothesis for property-based testing (@given+@settingswith HYPOTHESIS_PROFILE env var: 'ci' for 50 examples (default), 'dev' for 1000 examples). Run dev profile: HYPOTHESIS_PROFILE=dev uv run python -m pytest tests/ -m unit -n auto -k properties. .hypothesis/ is gitignored
Never skip, dismiss, or ignore flaky tests -- always fix them fully. For timing-sensitive tests, mock time.monotonic() and asyncio.sleep() for determinism. For tasks blocking indefinitely until cancelled, use asyncio.Event().wait() instead of asyncio.sleep(large_number) -- it is cancellation-safe
Files:
tests/unit/communication/delegation/test_service.py
🧠 Learnings (4)
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/engine/**/*.py : Engine package (engine/): agent orchestration, parallel execution, task decomposition, routing, TaskEngine (centralized single-writer), task lifecycle/recovery/shutdown, workspace isolation, coordination (4 dispatchers: SAS/centralized/decentralized/context-dependent, wave execution), approval gates (escalation detection, context parking/resume), stagnation detection (ToolRepetitionDetector, corrective prompt injection), AgentRuntimeState (execution status), context budget management, conversation compaction (oldest-turns summarizer)
Applied to files:
src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-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-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)
Applied to files:
src/synthorg/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/communication/**/*.py : Communication package (communication/): message bus, dispatcher, messenger, channels, delegation, loop prevention, conflict resolution; meeting/ subpackage for meeting protocol (round-robin, position papers, structured phases), scheduler (frequency, participant resolver), orchestrator
Applied to files:
src/synthorg/engine/agent_engine.pytests/unit/communication/delegation/test_service.py
🔇 Additional comments (3)
tests/unit/communication/delegation/test_service.py (2)
80-86: Constructor-basedrecord_storewiring is correctly validated.Nice change: the helper now injects
record_storethroughDelegationService(...), so tests exercise the public API rather than private attribute mutation.Also applies to: 124-129
454-471: Positive-path persistence test is solid.This test cleanly verifies that a successful delegation writes exactly one record with the expected delegator/delegatee IDs.
src/synthorg/engine/agent_engine.py (1)
293-293: Tracker wiring intoToolInvokerlooks correct.Line [293] persists the injected tracker on the engine, and Line [1191] forwards it to
ToolInvokerwith the correct keyword/type. This closes the runtime gap where invocations could be missed when the tracker is provided.Also applies to: 1191-1191
9b18e73 to
49cbfad
Compare
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/synthorg/hr/activity.py (1)
1-20: 🛠️ Refactor suggestion | 🟠 MajorAdd the required module logger.
This is business-logic code under
src/synthorg/**/*.py, but the module still doesn't importget_loggeror definelogger = get_logger(__name__).♻️ Minimal fix
from typing import TYPE_CHECKING from pydantic import AwareDatetime, BaseModel, ConfigDict, Field +from synthorg.observability import get_logger from synthorg.core.types import NotBlankStr # noqa: TC001 from synthorg.hr.enums import LifecycleEventType @@ +logger = get_logger(__name__) +As per coding guidelines "Every module with business logic MUST import:
from synthorg.observability import get_loggerand instantiatelogger = get_logger(__name__)".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/hr/activity.py` around lines 1 - 20, The module is missing the required logger; import get_logger from synthorg.observability and add a module-level logger variable. Specifically, add "from synthorg.observability import get_logger" to the top import block and instantiate "logger = get_logger(__name__)" (module-level) in src/synthorg/hr/activity.py so functions and classes in this file (e.g., any timeline builders) can use logger consistently.
♻️ Duplicate comments (2)
src/synthorg/communication/delegation/service.py (1)
191-202:⚠️ Potential issue | 🟠 MajorOrdering concern: guard/audit mutations happen before store persistence.
The
_guard.record_delegation()and_audit_trail.append()calls (lines 176-190) execute before therecord_store.record_sync()attempt. IfMemoryErrororRecursionErroris raised at line 194-195, the delegation is already recorded in the guard (blocking retries via dedup) and audit trail, butdelegate()will propagate the exception to the caller without returning success.This creates a state inconsistency: the guard blocks future attempts, but the caller sees a failure.
Consider moving guard/audit mutations after the store persistence completes successfully, or implementing rollback logic for the guard/audit on fatal errors.
🛠️ Suggested reordering to avoid half-committed state
- self._guard.record_delegation( - request.delegator_id, - request.delegatee_id, - request.task.id, - ) record = DelegationRecord( delegation_id=str(uuid4()), delegator_id=request.delegator_id, delegatee_id=request.delegatee_id, original_task_id=request.task.id, delegated_task_id=sub_task.id, timestamp=datetime.now(UTC), refinement=request.refinement, ) - self._audit_trail.append(record) if self._record_store is not None: try: self._record_store.record_sync(record) except MemoryError, RecursionError: raise except Exception: logger.warning( DELEGATION_RECORD_STORE_FAILED, delegator=request.delegator_id, delegatee=request.delegatee_id, exc_info=True, ) + # Only commit guard/audit after store persistence succeeds or is skipped + self._guard.record_delegation( + request.delegator_id, + request.delegatee_id, + request.task.id, + ) + self._audit_trail.append(record)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/communication/delegation/service.py` around lines 191 - 202, The guard and audit are being mutated before persistence which can leave a half-committed state if _record_store.record_sync(record) raises; move the calls to _guard.record_delegation(...) and _audit_trail.append(...) so they execute only after _record_store.record_sync(...) completes successfully in delegate(), or alternatively add rollback logic that removes the guard entry and audit append in the MemoryError/RecursionError/other fatal-exception handlers so the system state remains consistent with persistence.src/synthorg/tools/invocation_tracker.py (1)
24-33:⚠️ Potential issue | 🟠 Major
tool_usedtracking is still process-local and ephemeral.This tracker stores records only in worker memory. That means activity history disappears on restart, diverges across multiple API workers, and grows without bound over process lifetime. The linked objective called for a persisted/queryable source, so
tool_usedis still incomplete for real deployments.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/tools/invocation_tracker.py` around lines 24 - 33, ToolInvocationTracker currently keeps records only in-memory (see class ToolInvocationTracker, attribute _records and __init__), which makes tool_used ephemeral and inconsistent across workers; persist records to a durable, queryable store instead (e.g., add a persistence backend interface and implementation using your app DB or a lightweight append log), replace or augment writes to self._records with calls to that backend (write on record creation and read/filtering methods should query the store), ensure thread-safety by keeping or adapting _lock around local cache writes only, and migrate/convert existing ToolInvocationRecord reads/writes to use the new persistence layer so activity history survives restarts and is consistent across processes.
🤖 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/communication/delegation/models.py`:
- Line 112: The models in this file are inconsistent: DelegationRecord sets
model_config = ConfigDict(frozen=True, allow_inf_nan=False) while
DelegationRequest and DelegationResult only use ConfigDict(frozen=True). Make
them consistent by adding allow_inf_nan=False to the model_config of
DelegationRequest and DelegationResult (i.e., update their model_config
declarations to ConfigDict(frozen=True, allow_inf_nan=False)) so all three
classes (DelegationRequest, DelegationResult, DelegationRecord) use the same
defensive setting.
In `@src/synthorg/engine/agent_engine.py`:
- Line 95: Move the runtime import of ToolInvocationTracker into the
TYPE_CHECKING block to avoid an unnecessary runtime dependency: remove the
top-level "from synthorg.tools.invocation_tracker import ToolInvocationTracker"
and instead add "from synthorg.tools.invocation_tracker import
ToolInvocationTracker" inside the existing TYPE_CHECKING section so that the
AgentEngine __init__ signature (tool_invocation_tracker: ToolInvocationTracker |
None) and the instance attribute self.tool_invocation_tracker keep their type
information without importing at runtime.
In `@src/synthorg/hr/activity.py`:
- Around line 175-210: The delegation activity converters
_delegation_to_sent_activity and _delegation_to_received_activity currently only
include original_task_id in related_ids; add the child/delegated task identifier
so clients can correlate events. Update both functions to include a
"delegated_task_id" entry in the returned ActivityEvent.related_ids (e.g.,
"delegated_task_id": str(record.delegated_task_id)) alongside the existing
original_task_id and other IDs; keep the rest of the payload unchanged.
In `@tests/unit/api/controllers/test_activities.py`:
- Around line 300-417: Add tests that mimic the pattern in
test_graceful_degradation_no_performance_tracker to ensure /api/v1/activities
returns 200 and partial data when optional trackers fail: create new async tests
(e.g., test_graceful_degradation_no_tool_invocation_tracker and
test_graceful_degradation_no_delegation_store) that inject a
ToolInvocationTracker which raises on record() or a DelegationRecordStore whose
methods raise, call test_client.get("/api/v1/activities"), and assert
resp.status_code == 200 and that returned body contains other available event
types (e.g., lifecycle or cost events) while not crashing; reuse the same
assertions style and pagination checks as in existing tests to confirm graceful
degradation.
In `@tests/unit/communication/delegation/test_service.py`:
- Around line 473-529: Combine the three similar tests into one parametrized
pytest case that iterates over the different exceptions and expected outcomes:
parametrize over record_store.record_sync.side_effect values
(RuntimeError("storage failure"), MemoryError("oom"), RecursionError("max
depth")) and expected behavior (for RuntimeError assert
service.delegate(request, delegator, delegatee).success is True; for MemoryError
and RecursionError assert service.delegate raises the respective exception using
pytest.raises). Use the same setup logic from the original tests
(_build_service(record_store=store), _make_task(), _make_agent(...),
DelegationRequest) and reference record_sync, DelegationRecordStore,
service.delegate, and _build_service in the single parametrized test.
In `@tests/unit/hr/performance/test_models.py`:
- Around line 118-122: Move the local imports (timedelta and TaskMetricRecord)
to the module-level imports alongside other test imports; then combine the two
“started after/equal completed” tests into a single parametrized test using
pytest.mark.parametrize (referencing test_started_at_after_completed_at_rejected
and test_started_at_equal_completed_at_rejected) that supplies started_offset
values (timedelta(hours=1) and timedelta(0)) and asserts TaskMetricRecord raises
ValidationError with the same match; keep using NOW, TaskType, Complexity and
the same construction of TaskMetricRecord inside the parametrized test to
preserve test data.
In `@tests/unit/tools/test_invocation_bridge.py`:
- Around line 102-110: The test currently only asserts that
record_tool_invocation re-raises MemoryError; add coverage for the
RecursionError branch by parameterizing the test to run for both exception types
(MemoryError and RecursionError). Update test_memory_error_propagates (or rename
to a parameterized test) to accept an exception class, set
tracker.record.side_effect to that exception (using
AsyncMock/spec=ToolInvocationTracker), and assert that await
record_tool_invocation(invoker, tool_call, result) raises the provided exception
type; keep existing setup (invoker from _make_invoker, tool_call from
_make_tool_call, result from _make_result) and use pytest.mark.parametrize to
enumerate (MemoryError, RecursionError).
---
Outside diff comments:
In `@src/synthorg/hr/activity.py`:
- Around line 1-20: The module is missing the required logger; import get_logger
from synthorg.observability and add a module-level logger variable.
Specifically, add "from synthorg.observability import get_logger" to the top
import block and instantiate "logger = get_logger(__name__)" (module-level) in
src/synthorg/hr/activity.py so functions and classes in this file (e.g., any
timeline builders) can use logger consistently.
---
Duplicate comments:
In `@src/synthorg/communication/delegation/service.py`:
- Around line 191-202: The guard and audit are being mutated before persistence
which can leave a half-committed state if _record_store.record_sync(record)
raises; move the calls to _guard.record_delegation(...) and
_audit_trail.append(...) so they execute only after
_record_store.record_sync(...) completes successfully in delegate(), or
alternatively add rollback logic that removes the guard entry and audit append
in the MemoryError/RecursionError/other fatal-exception handlers so the system
state remains consistent with persistence.
In `@src/synthorg/tools/invocation_tracker.py`:
- Around line 24-33: ToolInvocationTracker currently keeps records only
in-memory (see class ToolInvocationTracker, attribute _records and __init__),
which makes tool_used ephemeral and inconsistent across workers; persist records
to a durable, queryable store instead (e.g., add a persistence backend interface
and implementation using your app DB or a lightweight append log), replace or
augment writes to self._records with calls to that backend (write on record
creation and read/filtering methods should query the store), ensure
thread-safety by keeping or adapting _lock around local cache writes only, and
migrate/convert existing ToolInvocationRecord reads/writes to use the new
persistence layer so activity history survives restarts and is consistent across
processes.
🪄 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: d95529f3-3717-496f-910c-6abd25e6cac4
📒 Files selected for processing (27)
CLAUDE.mddocs/design/agents.mddocs/design/operations.mdsrc/synthorg/api/app.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/api/state.pysrc/synthorg/communication/delegation/models.pysrc/synthorg/communication/delegation/record_store.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/hr/activity.pysrc/synthorg/hr/performance/models.pysrc/synthorg/observability/events/delegation.pysrc/synthorg/observability/events/tool.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/tools/invocation_record.pysrc/synthorg/tools/invocation_tracker.pysrc/synthorg/tools/invoker.pytests/unit/api/conftest.pytests/unit/api/controllers/test_activities.pytests/unit/communication/delegation/test_record_store.pytests/unit/communication/delegation/test_service.pytests/unit/hr/performance/test_models.pytests/unit/hr/test_activity.pytests/unit/tools/test_invocation_bridge.pytests/unit/tools/test_invocation_record.pytests/unit/tools/test_invocation_tracker.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Web
- GitHub Check: Build Backend
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Do NOT usefrom __future__ import annotations- Python 3.14 has PEP 649 with native lazy annotations
Useexcept A, B:syntax (no parentheses) per PEP 758 - ruff enforces this on Python 3.14
Files:
src/synthorg/tools/invoker.pytests/unit/tools/test_invocation_tracker.pysrc/synthorg/communication/delegation/models.pysrc/synthorg/tools/invocation_bridge.pytests/unit/tools/test_invocation_record.pytests/unit/communication/delegation/test_service.pysrc/synthorg/api/app.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/observability/events/tool.pytests/unit/api/conftest.pytests/unit/hr/performance/test_models.pytests/unit/tools/test_invocation_bridge.pytests/unit/communication/delegation/test_record_store.pysrc/synthorg/hr/activity.pysrc/synthorg/api/state.pytests/unit/api/controllers/test_activities.pysrc/synthorg/tools/invocation_tracker.pysrc/synthorg/tools/invocation_record.pysrc/synthorg/hr/performance/models.pytests/unit/hr/test_activity.pysrc/synthorg/communication/delegation/record_store.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/observability/events/delegation.py
src/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.py: All public functions must have type hints. Type checking enforced by mypy in strict mode.
Google-style docstrings required on all public classes and functions, enforced by ruff D rules
Use Pydantic v2 conventions:BaseModel,model_validator,computed_field,ConfigDict. Use@computed_fieldfor derived values instead of storing redundant fields. UseNotBlankStrfor all identifier/name fields.
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls) over barecreate_task
Line length must be 88 characters (enforced by ruff)
Functions must be less than 50 lines, files less than 800 lines
Handle errors explicitly, never silently swallow them. All error paths must log at WARNING or ERROR with context before raising.
Never useimport logging/logging.getLogger()/print()in application code (exceptobservability/setup.pyandobservability/sinks.pywhich may use stdlib logging and stderr print for bootstrap)
Always useloggeras the variable name (not_logger, notlog)
Always use event name constants from domain-specific modules undersynthorg.observability.events(e.g.,API_REQUEST_STARTEDfromevents.api). Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT
Use structured logging: alwayslogger.info(EVENT, key=value)- never use printf-style formatting likelogger.info("msg %s", val)
All state transitions must log at INFO level
DEBUG logging is appropriate for object creation, internal flow, and entry/exit of key functions
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names:example-provider,example-large-001,example-medium-001, etc. Vendor names only in: (1) Operations design page, (2).claude/skill files, (3) third-party import paths.
Files:
src/synthorg/tools/invoker.pysrc/synthorg/communication/delegation/models.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/api/app.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/observability/events/tool.pysrc/synthorg/hr/activity.pysrc/synthorg/api/state.pysrc/synthorg/tools/invocation_tracker.pysrc/synthorg/tools/invocation_record.pysrc/synthorg/hr/performance/models.pysrc/synthorg/communication/delegation/record_store.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/observability/events/delegation.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Every module with business logic MUST import:
from synthorg.observability import get_loggerand instantiatelogger = get_logger(__name__)
Files:
src/synthorg/tools/invoker.pysrc/synthorg/communication/delegation/models.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/api/app.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/observability/events/tool.pysrc/synthorg/hr/activity.pysrc/synthorg/api/state.pysrc/synthorg/tools/invocation_tracker.pysrc/synthorg/tools/invocation_record.pysrc/synthorg/hr/performance/models.pysrc/synthorg/communication/delegation/record_store.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/observability/events/delegation.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Tests must usetest-provider,test-small-001, etc. instead of real vendor names
Use@pytest.mark.parametrizefor testing similar cases
Mark all tests with appropriate pytest markers:@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slow
Use Hypothesis for property-based testing in Python with@givenand@settingsdecorators. Run dev profile with 1000 examples viaHYPOTHESIS_PROFILE=devenvironment variable.
Never skip, dismiss, or ignore flaky tests. For timing-sensitive tests, mocktime.monotonic()andasyncio.sleep()to make them deterministic. For tasks blocking indefinitely, useasyncio.Event().wait()instead ofasyncio.sleep(large_number).
Files:
tests/unit/tools/test_invocation_tracker.pytests/unit/tools/test_invocation_record.pytests/unit/communication/delegation/test_service.pytests/unit/api/conftest.pytests/unit/hr/performance/test_models.pytests/unit/tools/test_invocation_bridge.pytests/unit/communication/delegation/test_record_store.pytests/unit/api/controllers/test_activities.pytests/unit/hr/test_activity.py
docs/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Documentation uses Markdown and is built with Zensical (config:
mkdocs.yml). API reference auto-generated from OpenAPI schema viascripts/export_openapi.py. Library reference auto-generated via mkdocstrings + Griffe.
Files:
docs/design/agents.mddocs/design/operations.md
src/synthorg/api/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/api/**/*.py: REST API defined with Litestar. Errors must use RFC 9457 format insrc/synthorg/api/
Litestar API must include setup wizard, auth/, auto-wiring, and lifecycle management
Files:
src/synthorg/api/app.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/activities.py
🧠 Learnings (56)
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/security/**/*.py : Security package (security/): SecOps agent, rule engine (soft-allow/hard-deny, fail-closed), audit log, output scanner, output scan response policies (redact/withhold/log-only/autonomy-tiered), risk classifier, risk tier classifier, action type registry, ToolInvoker security integration, progressive trust (4 strategies), autonomy levels (presets, resolver, change strategy), timeout policies (park/resume)
Applied to files:
src/synthorg/tools/invoker.pyCLAUDE.mdsrc/synthorg/engine/agent_engine.pysrc/synthorg/api/state.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/security/**/*.py : Security module includes SecOps agent, rule engine (soft-allow/hard-deny), audit log, output scanner, risk classifier, autonomy levels (4 strategies), timeout policies.
Applied to files:
src/synthorg/tools/invoker.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 BaseModel, model_validator, computed_field, ConfigDict.
Applied to files:
src/synthorg/communication/delegation/models.pysrc/synthorg/tools/invocation_record.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/communication/delegation/models.pysrc/synthorg/tools/invocation_record.py
📚 Learning: 2026-03-16T23:05:29.577Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T23:05:29.577Z
Learning: Applies to **/*.py : For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-15T18:42:17.990Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:42:17.990Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`
Applied to files:
src/synthorg/communication/delegation/models.pysrc/synthorg/tools/invocation_record.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to **/*.py : Config vs runtime state: use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state. Never mix static config fields with mutable runtime fields in one model.
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to **/*.py : Config vs runtime state: frozen Pydantic models for config/identity; separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Applied to files:
src/synthorg/communication/delegation/models.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/communication/delegation/models.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Prefer `asyncio.TaskGroup` for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare `create_task`. Existing code is being migrated incrementally.
Applied to files:
src/synthorg/tools/invocation_bridge.pysrc/synthorg/tools/invocation_tracker.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (no parentheses) for exception handling — PEP 758 exception syntax enforced by ruff on Python 3.14
Applied to files:
src/synthorg/tools/invocation_bridge.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to **/*.py : Handle errors explicitly; never silently swallow exceptions
Applied to files:
src/synthorg/tools/invocation_bridge.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to **/*.py : Handle errors explicitly, never silently swallow exceptions
Applied to files:
src/synthorg/tools/invocation_bridge.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to **/*.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/tools/invocation_bridge.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to **/*.py : Handle errors explicitly—never silently swallow exceptions.
Applied to files:
src/synthorg/tools/invocation_bridge.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (no parentheses) per PEP 758 - ruff enforces this on Python 3.14
Applied to files:
src/synthorg/tools/invocation_bridge.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to **/*.py : Use PEP 758 except syntax: `except A, B:` (no parentheses) — enforced by ruff on Python 3.14
Applied to files:
src/synthorg/tools/invocation_bridge.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-18T21:23:23.586Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T21:23:23.586Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from the domain-specific module under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly from synthorg.observability.events.<domain>.
Applied to files:
src/synthorg/tools/invocation_bridge.pysrc/synthorg/observability/events/tool.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/tools/invocation_bridge.pysrc/synthorg/observability/events/tool.pysrc/synthorg/observability/events/delegation.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/tools/invocation_bridge.pysrc/synthorg/observability/events/tool.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls
Applied to files:
src/synthorg/tools/invocation_bridge.pysrc/synthorg/observability/events/tool.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:
docs/design/agents.md
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/hr/**/*.py : HR package (hr/): hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)
Applied to files:
docs/design/agents.mdtests/unit/api/conftest.pytests/unit/hr/test_activity.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Settings: Runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge, ConfigResolver (typed composed reads for controllers), validation, registry, change notifications via message bus. Per-namespace setting definitions in definitions/ submodule (api, company, providers, memory, budget, security, coordination, observability, backup).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
CLAUDE.mdsrc/synthorg/api/app.pysrc/synthorg/engine/agent_engine.pytests/unit/api/conftest.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Documentation source in `docs/` (Markdown, built with Zensical). Design spec in `docs/design/` (7 pages: index, agents, organization, communication, engine, memory, operations). Architecture in `docs/architecture/` (overview, tech-stack, decision log). Roadmap in `docs/roadmap/`. Security in `docs/security.md`. Licensing in `docs/licensing.md`. Reference in `docs/reference/`. REST API reference in `docs/rest-api.md`. Library reference in `docs/api/` (auto-generated from docstrings). Custom templates in `docs/overrides/`. Config in `mkdocs.yml`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Documentation structure: `docs/design/` (10 pages), `docs/architecture/`, `docs/roadmap/`, `docs/security.md`, `docs/licensing.md`, `docs/reference/`, `docs/rest-api.md` + generated API reference
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: 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:
CLAUDE.md
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to docs/** : Docs source in docs/ (Markdown, built with Zensical); design spec in docs/design/ (7 pages: index, agents, organization, communication, engine, memory, operations)
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)
Applied to files:
CLAUDE.mdsrc/synthorg/api/app.py
📚 Learning: 2026-03-15T21:20:09.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:20:09.993Z
Learning: Applies to web/src/components/** : Vue components organized by feature (agents/, approvals/, budget/, common/, dashboard/, layout/, messages/, org-chart/, tasks/).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 + shadcn/ui + Tailwind CSS for web dashboard. TypeScript required in `web/src/`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/communication/**/*.py : Communication package (communication/): message bus, dispatcher, messenger, channels, delegation, loop prevention, conflict resolution; meeting/ subpackage for meeting protocol (round-robin, position papers, structured phases), scheduler (frequency, participant resolver), orchestrator
Applied to files:
tests/unit/communication/delegation/test_service.pysrc/synthorg/api/app.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/api/state.py
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to src/synthorg/api/**/*.py : Litestar API must include setup wizard, auth/, auto-wiring, and lifecycle management
Applied to files:
src/synthorg/api/app.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/backup/**/*.py : Backup package (backup/): scheduled/manual/lifecycle backups of persistence DB, agent memory, company config. BackupService orchestrator, BackupScheduler (periodic asyncio task), RetentionManager (count + age pruning), tar.gz compression, SHA-256 checksums, manifest tracking, validated restore with atomic rollback and safety backup. handlers/ subpackage: ComponentHandler protocol + concrete handlers (PersistenceComponentHandler, MemoryComponentHandler, ConfigComponentHandler)
Applied to files:
src/synthorg/api/app.pysrc/synthorg/api/state.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.
Applied to files:
src/synthorg/api/app.pytests/unit/api/conftest.py
📚 Learning: 2026-03-15T16:55:07.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T16:55:07.730Z
Learning: Applies to **/*.py : Use PEP 758 except syntax: use `except A, B:` (no parentheses) — ruff enforces this on Python 3.14.
Applied to files:
src/synthorg/communication/delegation/service.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions.
Applied to files:
src/synthorg/communication/delegation/service.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, and key function entry/exit
Applied to files:
src/synthorg/communication/delegation/service.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/engine/**/*.py : Engine package (engine/): agent orchestration, parallel execution, task decomposition, routing, TaskEngine (centralized single-writer), task lifecycle/recovery/shutdown, workspace isolation, coordination (4 dispatchers: SAS/centralized/decentralized/context-dependent, wave execution), approval gates (escalation detection, context parking/resume), stagnation detection (ToolRepetitionDetector, corrective prompt injection), AgentRuntimeState (execution status), context budget management, conversation compaction (oldest-turns summarizer)
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability package (observability/): structured logging, correlation tracking, log sinks; event constants organized by domain under observability/events/ (e.g., events.api, events.tool, events.git, events.context_budget, events.backup)
Applied to files:
src/synthorg/observability/events/tool.pysrc/synthorg/observability/events/delegation.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/tool.pysrc/synthorg/observability/events/delegation.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings
Applied to files:
src/synthorg/observability/events/tool.py
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to src/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`
Applied to files:
src/synthorg/observability/events/tool.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/api/conftest.pytests/unit/hr/test_activity.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST import: `from synthorg.observability import get_logger` and instantiate `logger = get_logger(__name__)`
Applied to files:
src/synthorg/api/state.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import `from synthorg.observability import get_logger` and define `logger = get_logger(__name__)`
Applied to files:
src/synthorg/api/state.py
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to tests/**/*.py : Never skip, dismiss, or ignore flaky tests. For timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic. For tasks blocking indefinitely, use `asyncio.Event().wait()` instead of `asyncio.sleep(large_number)`.
Applied to files:
tests/unit/api/controllers/test_activities.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to tests/**/*.py : NEVER skip, dismiss, or ignore flaky tests — always fix them fully and fundamentally. For timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic instead of widening timing margins
Applied to files:
tests/unit/api/controllers/test_activities.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.
Applied to files:
tests/unit/hr/test_activity.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/hr/**/*.py : HR engine must provide: hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, trend detection), promotion/demotion
Applied to files:
tests/unit/hr/test_activity.py
📚 Learning: 2026-03-17T18:52:05.142Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T18:52:05.142Z
Learning: Applies to **/*.py : Async concurrency: prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to src/**/*.py : 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`
Applied to files:
src/synthorg/api/controllers/activities.py
src/synthorg/engine/agent_engine.py
Outdated
| from synthorg.providers.models import ChatMessage | ||
| from synthorg.security.audit import AuditLog | ||
| from synthorg.security.autonomy.models import EffectiveAutonomy # noqa: TC001 | ||
| from synthorg.tools.invocation_tracker import ToolInvocationTracker # noqa: TC001 |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider moving ToolInvocationTracker import to TYPE_CHECKING block.
The import is used only for type hints in the constructor signature (tool_invocation_tracker: ToolInvocationTracker | None) and stored on self. For consistency with similar optional dependencies (lines 99-132), consider moving this to the TYPE_CHECKING block to avoid unnecessary runtime imports.
♻️ Suggested change
-from synthorg.tools.invocation_tracker import ToolInvocationTracker # noqa: TC001
+
if TYPE_CHECKING:
from synthorg.api.approval_store import ApprovalStore
# ... existing imports ...
+ from synthorg.tools.invocation_tracker import ToolInvocationTracker🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/engine/agent_engine.py` at line 95, Move the runtime import of
ToolInvocationTracker into the TYPE_CHECKING block to avoid an unnecessary
runtime dependency: remove the top-level "from synthorg.tools.invocation_tracker
import ToolInvocationTracker" and instead add "from
synthorg.tools.invocation_tracker import ToolInvocationTracker" inside the
existing TYPE_CHECKING section so that the AgentEngine __init__ signature
(tool_invocation_tracker: ToolInvocationTracker | None) and the instance
attribute self.tool_invocation_tracker keep their type information without
importing at runtime.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/synthorg/hr/activity.py (1)
8-20: 🛠️ Refactor suggestion | 🟠 MajorAdd the module logger required for
src/synthorg/business logic.This file now carries more HR timeline logic, but it still doesn't initialize
logger = get_logger(__name__), which makes it an outlier from the repo-wide observability contract.As per coding guidelines
src/synthorg/**/*.py: Every module with business logic MUST import:from synthorg.observability import get_loggerand instantiatelogger = get_logger(__name__)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/hr/activity.py` around lines 8 - 20, This module is missing the standardized module logger; import get_logger from synthorg.observability and create the module-level logger by adding logger = get_logger(__name__) near the top of the file (after the imports). Ensure you add the import statement for get_logger and instantiate the logger variable so subsequent functions/classes in this module can use logger consistently.
♻️ Duplicate comments (1)
src/synthorg/engine/agent_engine.py (1)
197-230: 🧹 Nitpick | 🔵 TrivialDocument
provider_registryin the constructorArgsblock too.The updated docstring covers
tool_invocation_tracker, but the public signature at Line 229 still exposesprovider_registrywithout a matchingArgsentry, so the constructor docs remain incomplete.As per coding guidelines
src/**/*.py: Google-style docstrings required on all public classes and functions, enforced by ruff D rules🤖 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 197 - 230, The constructor's docstring Args block is missing documentation for the provider_registry parameter; update the __init__ docstring (the Args section for the constructor of the class containing __init__) to add an entry describing provider_registry: mention its type (ProviderRegistry | None), purpose (registry of completion/other providers used by the engine), and default behavior when None, matching the style of the existing tool_invocation_tracker entry so doc checks (ruff D) pass.
🤖 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/communication/delegation/models.py`:
- Line 22: The wrapper models DelegationRequest, DelegationResult, and
DelegationRecord currently set model_config = ConfigDict(allow_inf_nan=False)
but that does not propagate to nested Task instances; update the Task model to
include model_config = ConfigDict(frozen=True, allow_inf_nan=False) so Task's
float fields (e.g., budget_limit) reject inf/NaN, or alternatively add an
explicit field validator on the wrapper models
(DelegationRequest/DelegationResult) that checks task.budget_limit for isfinite
and raises a ValueError; note DelegationRecord has no float fields so its config
has no effect.
In `@tests/unit/api/controllers/test_activities.py`:
- Around line 300-417: Add one end-to-end test that records a "task_started"
lifecycle event and verifies it appears in the activities feed; create a new
async test (e.g. test_feed_with_task_started) that uses the same helper
_make_lifecycle_event to save a lifecycle event with event_type="task_started"
and a timestamp (e.g. _NOW - timedelta(hours=1)), then call GET
"/api/v1/activities" and assert resp.status_code == 200,
body["pagination"]["total"] == 1 and body["data"][0]["event_type"] ==
"task_started" so the controller and pagination for task_started are exercised.
---
Outside diff comments:
In `@src/synthorg/hr/activity.py`:
- Around line 8-20: This module is missing the standardized module logger;
import get_logger from synthorg.observability and create the module-level logger
by adding logger = get_logger(__name__) near the top of the file (after the
imports). Ensure you add the import statement for get_logger and instantiate the
logger variable so subsequent functions/classes in this module can use logger
consistently.
---
Duplicate comments:
In `@src/synthorg/engine/agent_engine.py`:
- Around line 197-230: The constructor's docstring Args block is missing
documentation for the provider_registry parameter; update the __init__ docstring
(the Args section for the constructor of the class containing __init__) to add
an entry describing provider_registry: mention its type (ProviderRegistry |
None), purpose (registry of completion/other providers used by the engine), and
default behavior when None, matching the style of the existing
tool_invocation_tracker entry so doc checks (ruff D) pass.
🪄 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: 9b0d4a13-95cb-4244-b26a-9bc6ac64ff58
📒 Files selected for processing (8)
src/synthorg/communication/delegation/models.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/hr/activity.pytests/unit/api/controllers/test_activities.pytests/unit/communication/delegation/test_service.pytests/unit/hr/performance/test_models.pytests/unit/hr/test_activity.pytests/unit/tools/test_invocation_bridge.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Backend
- GitHub Check: Build Sandbox
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Do NOT usefrom __future__ import annotations- Python 3.14 has PEP 649 with native lazy annotations
Useexcept A, B:syntax (no parentheses) per PEP 758 - ruff enforces this on Python 3.14
Files:
src/synthorg/engine/agent_engine.pysrc/synthorg/hr/activity.pysrc/synthorg/communication/delegation/models.pytests/unit/api/controllers/test_activities.pytests/unit/tools/test_invocation_bridge.pytests/unit/hr/test_activity.pytests/unit/communication/delegation/test_service.pytests/unit/hr/performance/test_models.py
src/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.py: All public functions must have type hints. Type checking enforced by mypy in strict mode.
Google-style docstrings required on all public classes and functions, enforced by ruff D rules
Use Pydantic v2 conventions:BaseModel,model_validator,computed_field,ConfigDict. Use@computed_fieldfor derived values instead of storing redundant fields. UseNotBlankStrfor all identifier/name fields.
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls) over barecreate_task
Line length must be 88 characters (enforced by ruff)
Functions must be less than 50 lines, files less than 800 lines
Handle errors explicitly, never silently swallow them. All error paths must log at WARNING or ERROR with context before raising.
Never useimport logging/logging.getLogger()/print()in application code (exceptobservability/setup.pyandobservability/sinks.pywhich may use stdlib logging and stderr print for bootstrap)
Always useloggeras the variable name (not_logger, notlog)
Always use event name constants from domain-specific modules undersynthorg.observability.events(e.g.,API_REQUEST_STARTEDfromevents.api). Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT
Use structured logging: alwayslogger.info(EVENT, key=value)- never use printf-style formatting likelogger.info("msg %s", val)
All state transitions must log at INFO level
DEBUG logging is appropriate for object creation, internal flow, and entry/exit of key functions
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names:example-provider,example-large-001,example-medium-001, etc. Vendor names only in: (1) Operations design page, (2).claude/skill files, (3) third-party import paths.
Files:
src/synthorg/engine/agent_engine.pysrc/synthorg/hr/activity.pysrc/synthorg/communication/delegation/models.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Every module with business logic MUST import:
from synthorg.observability import get_loggerand instantiatelogger = get_logger(__name__)
Files:
src/synthorg/engine/agent_engine.pysrc/synthorg/hr/activity.pysrc/synthorg/communication/delegation/models.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Tests must usetest-provider,test-small-001, etc. instead of real vendor names
Use@pytest.mark.parametrizefor testing similar cases
Mark all tests with appropriate pytest markers:@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slow
Use Hypothesis for property-based testing in Python with@givenand@settingsdecorators. Run dev profile with 1000 examples viaHYPOTHESIS_PROFILE=devenvironment variable.
Never skip, dismiss, or ignore flaky tests. For timing-sensitive tests, mocktime.monotonic()andasyncio.sleep()to make them deterministic. For tasks blocking indefinitely, useasyncio.Event().wait()instead ofasyncio.sleep(large_number).
Files:
tests/unit/api/controllers/test_activities.pytests/unit/tools/test_invocation_bridge.pytests/unit/hr/test_activity.pytests/unit/communication/delegation/test_service.pytests/unit/hr/performance/test_models.py
🧠 Learnings (30)
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/engine/**/*.py : Engine package (engine/): agent orchestration, parallel execution, task decomposition, routing, TaskEngine (centralized single-writer), task lifecycle/recovery/shutdown, workspace isolation, coordination (4 dispatchers: SAS/centralized/decentralized/context-dependent, wave execution), approval gates (escalation detection, context parking/resume), stagnation detection (ToolRepetitionDetector, corrective prompt injection), AgentRuntimeState (execution status), context budget management, conversation compaction (oldest-turns summarizer)
Applied to files:
src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Prefer `asyncio.TaskGroup` for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare `create_task`. Existing code is being migrated incrementally.
Applied to files:
src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.
Applied to files:
src/synthorg/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:
src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST import: `from synthorg.observability import get_logger` and instantiate `logger = get_logger(__name__)`
Applied to files:
src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-16T19:13:36.562Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:13:36.562Z
Learning: Applies to src/synthorg/providers/**/*.py : RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig.
Applied to files:
src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import `from synthorg.observability import get_logger` and define `logger = get_logger(__name__)`
Applied to files:
src/synthorg/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-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/hr/**/*.py : HR package (hr/): hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)
Applied to files:
src/synthorg/hr/activity.pytests/unit/hr/test_activity.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Applied to files:
src/synthorg/communication/delegation/models.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/communication/delegation/models.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 BaseModel, model_validator, computed_field, ConfigDict.
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-15T18:42:17.990Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:42:17.990Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-16T23:05:29.577Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T23:05:29.577Z
Learning: Applies to **/*.py : For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to **/*.py : Config vs runtime state: use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state. Never mix static config fields with mutable runtime fields in one model.
Applied to files:
src/synthorg/communication/delegation/models.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/communication/delegation/models.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to **/*.py : Config vs runtime state: frozen Pydantic models for config/identity; separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to tests/**/*.py : Never skip, dismiss, or ignore flaky tests. For timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic. For tasks blocking indefinitely, use `asyncio.Event().wait()` instead of `asyncio.sleep(large_number)`.
Applied to files:
tests/unit/api/controllers/test_activities.pytests/unit/hr/performance/test_models.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to tests/**/*.py : NEVER skip, dismiss, or ignore flaky tests — always fix them fully and fundamentally. For timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic instead of widening timing margins
Applied to files:
tests/unit/api/controllers/test_activities.pytests/unit/hr/performance/test_models.py
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)
Applied to files:
tests/unit/hr/test_activity.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.
Applied to files:
tests/unit/hr/test_activity.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/hr/**/*.py : HR engine must provide: hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, trend detection), promotion/demotion
Applied to files:
tests/unit/hr/test_activity.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to tests/**/*.py : Prefer `pytest.mark.parametrize` for testing similar cases.
Applied to files:
tests/unit/communication/delegation/test_service.py
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to tests/**/*.py : Use `pytest.mark.parametrize` for testing similar cases
Applied to files:
tests/unit/communication/delegation/test_service.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to tests/**/*.py : Parametrize: Prefer pytest.mark.parametrize for testing similar cases.
Applied to files:
tests/unit/communication/delegation/test_service.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/communication/**/*.py : Communication package (communication/): message bus, dispatcher, messenger, channels, delegation, loop prevention, conflict resolution; meeting/ subpackage for meeting protocol (round-robin, position papers, structured phases), scheduler (frequency, participant resolver), orchestrator
Applied to files:
tests/unit/communication/delegation/test_service.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to tests/**/*.py : Test markers: `pytest.mark.unit`, `pytest.mark.integration`, `pytest.mark.e2e`, `pytest.mark.slow`. Coverage: 80% minimum. Async: `asyncio_mode = 'auto'` — no manual `pytest.mark.asyncio` needed. Timeout: 30 seconds per test. Parallelism: `pytest-xdist` via `-n auto` — ALWAYS include `-n auto` when running pytest, never run tests sequentially.
Applied to files:
tests/unit/hr/performance/test_models.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/hr/performance/test_models.py
🔇 Additional comments (1)
tests/unit/hr/performance/test_models.py (1)
119-162: Good temporal-ordering coverage.The valid, invalid, and
Nonecases pin the newstarted_atcontract cleanly, and the invalid-ordering checks are nicely consolidated with parametrization.
| """ | ||
|
|
||
| model_config = ConfigDict(frozen=True) | ||
| model_config = ConfigDict(frozen=True, allow_inf_nan=False) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
python -m pip install --quiet 'pydantic==2.12.5'
python - <<'PY'
from pydantic import BaseModel, ConfigDict, ValidationError
class Task(BaseModel):
budget_limit: float
class Wrapper(BaseModel):
model_config = ConfigDict(frozen=True, allow_inf_nan=False)
task: Task
for value in (float("inf"), float("nan")):
try:
Wrapper.model_validate({"task": {"budget_limit": value}})
except ValidationError as exc:
print(f"REJECTED {value!r}: {exc.errors()[0]['msg']}")
else:
print(f"ACCEPTED {value!r}")
PYRepository: Aureliolo/synthorg
Length of output: 87
allow_inf_nan=False on wrapper models doesn't validate nested Task floats.
Pydantic ConfigDict on a parent model doesn't propagate to nested BaseModel instances—each validates independently with its own config. Since Task has no allow_inf_nan=False, wrapper models here (DelegationRequest, DelegationResult, DelegationRecord) won't reject delegated tasks with budget_limit=inf / NaN.
To enforce this validation: either add allow_inf_nan=False to the Task model itself, or add an explicit field validator in the wrapper models. (Note: DelegationRecord has no float fields, so the config is inert there regardless.)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/communication/delegation/models.py` at line 22, The wrapper
models DelegationRequest, DelegationResult, and DelegationRecord currently set
model_config = ConfigDict(allow_inf_nan=False) but that does not propagate to
nested Task instances; update the Task model to include model_config =
ConfigDict(frozen=True, allow_inf_nan=False) so Task's float fields (e.g.,
budget_limit) reject inf/NaN, or alternatively add an explicit field validator
on the wrapper models (DelegationRequest/DelegationResult) that checks
task.budget_limit for isfinite and raises a ValueError; note DelegationRecord
has no float fields so its config has no effect.
…legation, cost_incurred) Add 5 new activity event types to complete the 10-type spec from #772: - task_started: derived from TaskMetricRecord.started_at field - cost_incurred: from existing CostRecord via CostTracker - tool_used: new ToolInvocationRecord model + ToolInvocationTracker store - delegation_sent/received: new DelegationRecordStore with dual-perspective querying (delegator vs delegatee) Each type has a converter function in hr/activity.py, merge integration via keyword-only args (backward compatible), controller wiring with graceful degradation, and comprehensive unit tests. Closes #812 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extract _record_invocation to invocation_bridge.py (invoker.py < 800 lines) - Add ServiceUnavailableError re-raise to tool/delegation fetchers - Fix _record_invocation log level: DEBUG -> WARNING with exc_info - Add error handling around DelegationService record_sync call - Add started_at < completed_at model_validator on TaskMetricRecord - Add success/error_message cross-field validator on ToolInvocationRecord - Use asyncio.TaskGroup for parallel async fetches in controller - Update DelegationService docstring with record_store param - Update docs/design/agents.md activity timeline description - Update docs/design/operations.md route table (add /activities) - Update CLAUDE.md tools/ package description - Add missing test cases (failure w/o error_message, start==end boundary) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add ExceptionGroup handling around TaskGroup in activity controller - Add has_cost_tracker guard for consistent graceful degradation - Fix wrong event constants: DELEGATION_CREATED -> DELEGATION_RECORD_STORE_FAILED, TOOL_INVOKE_EXECUTION_ERROR -> TOOL_INVOCATION_RECORD_FAILED - Replace type: ignore with assert for started_at precondition - Fix docstrings: activity module, converter, validator, record_store, bridge - Add allow_inf_nan=False to DelegationRecord (convention consistency) - Document record_sync lock bypass in DelegationRecordStore - Fix non-deterministic _NOW in controller tests with frozen datetime - Add delegator/delegatee time range tests to record_store - Add tests: invocation_bridge (7), invocation_record validator (6), TaskMetricRecord temporal validator (4), DelegationService record_store (3) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Avoids leaking internal paths, stack traces, or partial secrets through the activity feed API response. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Without this wiring, invocation_tracker was always None at runtime and tool invocations were never recorded. Also uses constructor wiring in delegation service tests instead of private attr access. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Document tool_invocation_tracker in AgentEngine Args docstring - Add RecursionError propagation test for delegation record store Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add allow_inf_nan=False to DelegationRequest and DelegationResult - Move ToolInvocationTracker import to TYPE_CHECKING in AgentEngine - Add delegated_task_id to delegation activity related_ids - Add graceful degradation tests for broken tool/delegation trackers - Parametrize fatal-error propagation tests (MemoryError + RecursionError) - Parametrize temporal validation tests, move imports to module level Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add task_started controller test via TaskMetricRecord.started_at - Document provider_registry in AgentEngine Args docstring Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2f9cc45 to
73c83c0
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
src/synthorg/communication/delegation/record_store.py (1)
24-38:⚠️ Potential issue | 🟠 MajorThis activity source is still process-local.
The backing list on Line 37 means
delegation_sent/delegation_receiveddisappear on restart and split across workers. That falls short of the persisted/queryable source the activity feed needs; this should be backed by shared durable storage instead of in-memory state.Also applies to: 40-50
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/communication/delegation/record_store.py` around lines 24 - 38, DelegationRecordStore currently keeps records in an in-memory list (_records) so delegation_sent/delegation_received disappear on restart and are not shared across workers; replace the in-memory backing with a durable, shared storage backend (e.g., a database or persistent queue) and update DelegationRecordStore methods (constructor __init__, record_sync, and the async query methods for delegation_sent / delegation_received) to read/write via that backend instead of list append/iteration; ensure record_sync becomes transactional or retries on failure and the async readers acquire _lock only when needed but query the shared store so records persist and are visible across processes.src/synthorg/communication/delegation/service.py (1)
176-202:⚠️ Potential issue | 🟠 MajorDon't mutate local delegation state before the store write can fail.
By Line 176 and Line 190, the guard and audit trail are already updated before Line 193 can re-raise. If
record_sync()hitsMemoryError/RecursionError,delegate()fails but retries are still dedupe-blocked andget_audit_trail()already exposes a delegation that never completed.♻️ One way to avoid leaving half-committed state on fatal store errors
- self._guard.record_delegation( - request.delegator_id, - request.delegatee_id, - request.task.id, - ) record = DelegationRecord( delegation_id=str(uuid4()), delegator_id=request.delegator_id, delegatee_id=request.delegatee_id, original_task_id=request.task.id, delegated_task_id=sub_task.id, timestamp=datetime.now(UTC), refinement=request.refinement, ) - self._audit_trail.append(record) if self._record_store is not None: try: self._record_store.record_sync(record) except MemoryError, RecursionError: raise except Exception: logger.warning( DELEGATION_RECORD_STORE_FAILED, delegator=request.delegator_id, delegatee=request.delegatee_id, exc_info=True, ) + self._guard.record_delegation( + request.delegator_id, + request.delegatee_id, + request.task.id, + ) + self._audit_trail.append(record)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/communication/delegation/service.py` around lines 176 - 202, Build the DelegationRecord object first but defer mutating local state: if self._record_store is not None, call self._record_store.record_sync(record) inside the try/except and only on success call self._guard.record_delegation(request.delegator_id, request.delegatee_id, request.task.id) and self._audit_trail.append(record); re-raise MemoryError and RecursionError as currently written and log other exceptions (logger.warning) without mutating guard/audit state; if self._record_store is None keep the original behavior (append and guard) so delegate() still records locally.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/synthorg/api/controllers/activities.py`:
- Around line 180-183: The except blocks that catch MemoryError, RecursionError
and ServiceUnavailableError should log the failure with source/fetcher context
before re-raising; update each catch (the blocks handling MemoryError,
RecursionError and ServiceUnavailableError in activities.py and the similar
blocks at the other locations) to call a structured logger (e.g., logger.error
or logger.warning) including the source/fetcher identifier and include exception
details (exc_info=True) immediately prior to re-raising, or implement a small
helper like log_and_reraise(exc, source_name, fetcher_name) and call that from
each except block to avoid duplication.
- Around line 267-297: The current single try block around both
store.get_records_as_delegator and store.get_records_as_delegatee discards a
successful result if the other call fails; split the two lookups so each is
attempted and handled independently: call store.get_records_as_delegator(...) in
its own try/except (preserving its successful result into the local variable
sent or setting sent = () on failure and logging/returning as the original
excepts dictate), then call store.get_records_as_delegatee(...) in a separate
try/except (preserving received or setting received = ()), keeping the same
exception types (MemoryError, RecursionError re-raises; ServiceUnavailableError
re-raises; other Exceptions log API_REQUEST_ERROR with the same fields and
exc_info=True). Ensure the org-wide branch remains unchanged and that the
function ultimately returns the pair (sent, received).
---
Duplicate comments:
In `@src/synthorg/communication/delegation/record_store.py`:
- Around line 24-38: DelegationRecordStore currently keeps records in an
in-memory list (_records) so delegation_sent/delegation_received disappear on
restart and are not shared across workers; replace the in-memory backing with a
durable, shared storage backend (e.g., a database or persistent queue) and
update DelegationRecordStore methods (constructor __init__, record_sync, and the
async query methods for delegation_sent / delegation_received) to read/write via
that backend instead of list append/iteration; ensure record_sync becomes
transactional or retries on failure and the async readers acquire _lock only
when needed but query the shared store so records persist and are visible across
processes.
In `@src/synthorg/communication/delegation/service.py`:
- Around line 176-202: Build the DelegationRecord object first but defer
mutating local state: if self._record_store is not None, call
self._record_store.record_sync(record) inside the try/except and only on success
call self._guard.record_delegation(request.delegator_id, request.delegatee_id,
request.task.id) and self._audit_trail.append(record); re-raise MemoryError and
RecursionError as currently written and log other exceptions (logger.warning)
without mutating guard/audit state; if self._record_store is None keep the
original behavior (append and guard) so delegate() still records locally.
🪄 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: a91b025a-11a0-4cb0-8040-43617bd4a417
📒 Files selected for processing (27)
CLAUDE.mddocs/design/agents.mddocs/design/operations.mdsrc/synthorg/api/app.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/api/state.pysrc/synthorg/communication/delegation/models.pysrc/synthorg/communication/delegation/record_store.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/hr/activity.pysrc/synthorg/hr/performance/models.pysrc/synthorg/observability/events/delegation.pysrc/synthorg/observability/events/tool.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/tools/invocation_record.pysrc/synthorg/tools/invocation_tracker.pysrc/synthorg/tools/invoker.pytests/unit/api/conftest.pytests/unit/api/controllers/test_activities.pytests/unit/communication/delegation/test_record_store.pytests/unit/communication/delegation/test_service.pytests/unit/hr/performance/test_models.pytests/unit/hr/test_activity.pytests/unit/tools/test_invocation_bridge.pytests/unit/tools/test_invocation_record.pytests/unit/tools/test_invocation_tracker.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Sandbox
- GitHub Check: Build Backend
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
docs/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Documentation uses Markdown and is built with Zensical (config:
mkdocs.yml). API reference auto-generated from OpenAPI schema viascripts/export_openapi.py. Library reference auto-generated via mkdocstrings + Griffe.
Files:
docs/design/agents.mddocs/design/operations.md
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Do NOT usefrom __future__ import annotations- Python 3.14 has PEP 649 with native lazy annotations
Useexcept A, B:syntax (no parentheses) per PEP 758 - ruff enforces this on Python 3.14
Files:
src/synthorg/tools/invoker.pysrc/synthorg/api/app.pysrc/synthorg/engine/agent_engine.pytests/unit/hr/performance/test_models.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/hr/performance/models.pytests/unit/tools/test_invocation_record.pytests/unit/communication/delegation/test_service.pytests/unit/tools/test_invocation_tracker.pytests/unit/api/conftest.pysrc/synthorg/communication/delegation/models.pytests/unit/api/controllers/test_activities.pysrc/synthorg/observability/events/delegation.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/tools/invocation_tracker.pytests/unit/tools/test_invocation_bridge.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/hr/activity.pysrc/synthorg/observability/events/tool.pytests/unit/communication/delegation/test_record_store.pysrc/synthorg/tools/invocation_record.pysrc/synthorg/communication/delegation/record_store.pysrc/synthorg/api/state.pytests/unit/hr/test_activity.py
src/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.py: All public functions must have type hints. Type checking enforced by mypy in strict mode.
Google-style docstrings required on all public classes and functions, enforced by ruff D rules
Use Pydantic v2 conventions:BaseModel,model_validator,computed_field,ConfigDict. Use@computed_fieldfor derived values instead of storing redundant fields. UseNotBlankStrfor all identifier/name fields.
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g., multiple tool invocations, parallel agent calls) over barecreate_task
Line length must be 88 characters (enforced by ruff)
Functions must be less than 50 lines, files less than 800 lines
Handle errors explicitly, never silently swallow them. All error paths must log at WARNING or ERROR with context before raising.
Never useimport logging/logging.getLogger()/print()in application code (exceptobservability/setup.pyandobservability/sinks.pywhich may use stdlib logging and stderr print for bootstrap)
Always useloggeras the variable name (not_logger, notlog)
Always use event name constants from domain-specific modules undersynthorg.observability.events(e.g.,API_REQUEST_STARTEDfromevents.api). Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT
Use structured logging: alwayslogger.info(EVENT, key=value)- never use printf-style formatting likelogger.info("msg %s", val)
All state transitions must log at INFO level
DEBUG logging is appropriate for object creation, internal flow, and entry/exit of key functions
Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names:example-provider,example-large-001,example-medium-001, etc. Vendor names only in: (1) Operations design page, (2).claude/skill files, (3) third-party import paths.
Files:
src/synthorg/tools/invoker.pysrc/synthorg/api/app.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/hr/performance/models.pysrc/synthorg/communication/delegation/models.pysrc/synthorg/observability/events/delegation.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/tools/invocation_tracker.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/hr/activity.pysrc/synthorg/observability/events/tool.pysrc/synthorg/tools/invocation_record.pysrc/synthorg/communication/delegation/record_store.pysrc/synthorg/api/state.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Every module with business logic MUST import:
from synthorg.observability import get_loggerand instantiatelogger = get_logger(__name__)
Files:
src/synthorg/tools/invoker.pysrc/synthorg/api/app.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/communication/delegation/service.pysrc/synthorg/hr/performance/models.pysrc/synthorg/communication/delegation/models.pysrc/synthorg/observability/events/delegation.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/tools/invocation_tracker.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/hr/activity.pysrc/synthorg/observability/events/tool.pysrc/synthorg/tools/invocation_record.pysrc/synthorg/communication/delegation/record_store.pysrc/synthorg/api/state.py
src/synthorg/api/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/api/**/*.py: REST API defined with Litestar. Errors must use RFC 9457 format insrc/synthorg/api/
Litestar API must include setup wizard, auth/, auto-wiring, and lifecycle management
Files:
src/synthorg/api/app.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/api/state.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Tests must usetest-provider,test-small-001, etc. instead of real vendor names
Use@pytest.mark.parametrizefor testing similar cases
Mark all tests with appropriate pytest markers:@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slow
Use Hypothesis for property-based testing in Python with@givenand@settingsdecorators. Run dev profile with 1000 examples viaHYPOTHESIS_PROFILE=devenvironment variable.
Never skip, dismiss, or ignore flaky tests. For timing-sensitive tests, mocktime.monotonic()andasyncio.sleep()to make them deterministic. For tasks blocking indefinitely, useasyncio.Event().wait()instead ofasyncio.sleep(large_number).
Files:
tests/unit/hr/performance/test_models.pytests/unit/tools/test_invocation_record.pytests/unit/communication/delegation/test_service.pytests/unit/tools/test_invocation_tracker.pytests/unit/api/conftest.pytests/unit/api/controllers/test_activities.pytests/unit/tools/test_invocation_bridge.pytests/unit/communication/delegation/test_record_store.pytests/unit/hr/test_activity.py
🧠 Learnings (66)
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
CLAUDE.mdsrc/synthorg/api/app.pysrc/synthorg/engine/agent_engine.pytests/unit/api/conftest.pysrc/synthorg/api/controllers/activities.pysrc/synthorg/api/state.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Settings: Runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge, ConfigResolver (typed composed reads for controllers), validation, registry, change notifications via message bus. Per-namespace setting definitions in definitions/ submodule (api, company, providers, memory, budget, security, coordination, observability, backup).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Documentation source in `docs/` (Markdown, built with Zensical). Design spec in `docs/design/` (7 pages: index, agents, organization, communication, engine, memory, operations). Architecture in `docs/architecture/` (overview, tech-stack, decision log). Roadmap in `docs/roadmap/`. Security in `docs/security.md`. Licensing in `docs/licensing.md`. Reference in `docs/reference/`. REST API reference in `docs/rest-api.md`. Library reference in `docs/api/` (auto-generated from docstrings). Custom templates in `docs/overrides/`. Config in `mkdocs.yml`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Documentation structure: `docs/design/` (10 pages), `docs/architecture/`, `docs/roadmap/`, `docs/security.md`, `docs/licensing.md`, `docs/reference/`, `docs/rest-api.md` + generated API reference
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: 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:
CLAUDE.md
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to docs/** : Docs source in docs/ (Markdown, built with Zensical); design spec in docs/design/ (7 pages: index, agents, organization, communication, engine, memory, operations)
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-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:
CLAUDE.mdsrc/synthorg/tools/invoker.pysrc/synthorg/engine/agent_engine.pysrc/synthorg/api/state.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)
Applied to files:
CLAUDE.mdsrc/synthorg/api/app.py
📚 Learning: 2026-03-15T21:20:09.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:20:09.993Z
Learning: Applies to web/src/components/** : Vue components organized by feature (agents/, approvals/, budget/, common/, dashboard/, layout/, messages/, org-chart/, tasks/).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to web/src/**/*.{ts,tsx} : React 19 + shadcn/ui + Tailwind CSS for web dashboard. TypeScript required in `web/src/`.
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:
docs/design/agents.md
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/hr/**/*.py : HR package (hr/): hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)
Applied to files:
docs/design/agents.mdtests/unit/api/conftest.pysrc/synthorg/api/controllers/activities.pytests/unit/hr/test_activity.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/security/**/*.py : Security module includes SecOps agent, rule engine (soft-allow/hard-deny), audit log, output scanner, risk classifier, autonomy levels (4 strategies), timeout policies.
Applied to files:
src/synthorg/tools/invoker.py
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to src/synthorg/api/**/*.py : Litestar API must include setup wizard, auth/, auto-wiring, and lifecycle management
Applied to files:
src/synthorg/api/app.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/communication/**/*.py : Communication package (communication/): message bus, dispatcher, messenger, channels, delegation, loop prevention, conflict resolution; meeting/ subpackage for meeting protocol (round-robin, position papers, structured phases), scheduler (frequency, participant resolver), orchestrator
Applied to files:
src/synthorg/api/app.pytests/unit/communication/delegation/test_service.pysrc/synthorg/api/state.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/backup/**/*.py : Backup package (backup/): scheduled/manual/lifecycle backups of persistence DB, agent memory, company config. BackupService orchestrator, BackupScheduler (periodic asyncio task), RetentionManager (count + age pruning), tar.gz compression, SHA-256 checksums, manifest tracking, validated restore with atomic rollback and safety backup. handlers/ subpackage: ComponentHandler protocol + concrete handlers (PersistenceComponentHandler, MemoryComponentHandler, ConfigComponentHandler)
Applied to files:
src/synthorg/api/app.pysrc/synthorg/api/state.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/providers/**/*.py : Providers: LLM provider abstraction (LiteLLM adapter), auth types (api_key/oauth/custom_header/none), presets (PROVIDER_PRESETS), runtime CRUD (ProviderManagementService with asyncio.Lock serialization), hot-reload via AppState swap.
Applied to files:
src/synthorg/api/app.pysrc/synthorg/engine/agent_engine.pytests/unit/api/conftest.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/engine/**/*.py : Engine package (engine/): agent orchestration, parallel execution, task decomposition, routing, TaskEngine (centralized single-writer), task lifecycle/recovery/shutdown, workspace isolation, coordination (4 dispatchers: SAS/centralized/decentralized/context-dependent, wave execution), approval gates (escalation detection, context parking/resume), stagnation detection (ToolRepetitionDetector, corrective prompt injection), AgentRuntimeState (execution status), context budget management, conversation compaction (oldest-turns summarizer)
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Prefer `asyncio.TaskGroup` for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare `create_task`. Existing code is being migrated incrementally.
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST import: `from synthorg.observability import get_logger` and instantiate `logger = get_logger(__name__)`
Applied to files:
src/synthorg/engine/agent_engine.pysrc/synthorg/api/state.py
📚 Learning: 2026-03-16T19:13:36.562Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:13:36.562Z
Learning: Applies to src/synthorg/providers/**/*.py : RetryConfig and RateLimiterConfig are set per-provider in ProviderConfig.
Applied to files:
src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import `from synthorg.observability import get_logger` and define `logger = get_logger(__name__)`
Applied to files:
src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import logger via `from synthorg.observability import get_logger` and initialize with `logger = get_logger(__name__)`
Applied to files:
src/synthorg/engine/agent_engine.py
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to tests/**/*.py : Never skip, dismiss, or ignore flaky tests. For timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic. For tasks blocking indefinitely, use `asyncio.Event().wait()` instead of `asyncio.sleep(large_number)`.
Applied to files:
tests/unit/hr/performance/test_models.pytests/unit/api/controllers/test_activities.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to tests/**/*.py : NEVER skip, dismiss, or ignore flaky tests — always fix them fully and fundamentally. For timing-sensitive tests, mock `time.monotonic()` and `asyncio.sleep()` to make them deterministic instead of widening timing margins
Applied to files:
tests/unit/hr/performance/test_models.pytests/unit/api/controllers/test_activities.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to tests/**/*.py : Test markers: `pytest.mark.unit`, `pytest.mark.integration`, `pytest.mark.e2e`, `pytest.mark.slow`. Coverage: 80% minimum. Async: `asyncio_mode = 'auto'` — no manual `pytest.mark.asyncio` needed. Timeout: 30 seconds per test. Parallelism: `pytest-xdist` via `-n auto` — ALWAYS include `-n auto` when running pytest, never run tests sequentially.
Applied to files:
tests/unit/hr/performance/test_models.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/hr/performance/test_models.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/communication/delegation/service.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to **/*.py : Handle errors explicitly; never silently swallow exceptions
Applied to files:
src/synthorg/communication/delegation/service.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to **/*.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/communication/delegation/service.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to **/*.py : Handle errors explicitly, never silently swallow exceptions
Applied to files:
src/synthorg/communication/delegation/service.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to **/*.py : Handle errors explicitly—never silently swallow exceptions.
Applied to files:
src/synthorg/communication/delegation/service.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to **/*.py : Use PEP 758 except syntax: `except A, B:` (no parentheses) — enforced by ruff on Python 3.14
Applied to files:
src/synthorg/communication/delegation/service.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (no parentheses) per PEP 758 - ruff enforces this on Python 3.14
Applied to files:
src/synthorg/communication/delegation/service.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-15T16:55:07.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T16:55:07.730Z
Learning: Applies to **/*.py : Use PEP 758 except syntax: use `except A, B:` (no parentheses) — ruff enforces this on Python 3.14.
Applied to files:
src/synthorg/communication/delegation/service.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions.
Applied to files:
src/synthorg/communication/delegation/service.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, and key function entry/exit
Applied to files:
src/synthorg/communication/delegation/service.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. For derived values use `computed_field` instead of storing + validating redundant fields. Use `NotBlankStr` (from `core.types`) for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.
Applied to files:
src/synthorg/hr/performance/models.pysrc/synthorg/communication/delegation/models.pysrc/synthorg/tools/invocation_record.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/communication/delegation/test_service.py
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to tests/**/*.py : Use `pytest.mark.parametrize` for testing similar cases
Applied to files:
tests/unit/communication/delegation/test_service.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to tests/**/*.py : Parametrize: Prefer pytest.mark.parametrize for testing similar cases.
Applied to files:
tests/unit/communication/delegation/test_service.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/api/conftest.pysrc/synthorg/api/controllers/activities.pytests/unit/hr/test_activity.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Applied to files:
src/synthorg/communication/delegation/models.pysrc/synthorg/tools/invocation_record.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state
Applied to files:
src/synthorg/communication/delegation/models.pysrc/synthorg/tools/invocation_record.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Applied to files:
src/synthorg/communication/delegation/models.pysrc/synthorg/tools/invocation_record.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 BaseModel, model_validator, computed_field, ConfigDict.
Applied to files:
src/synthorg/communication/delegation/models.pysrc/synthorg/tools/invocation_record.py
📚 Learning: 2026-03-15T18:42:17.990Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:42:17.990Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`
Applied to files:
src/synthorg/communication/delegation/models.pysrc/synthorg/tools/invocation_record.py
📚 Learning: 2026-03-16T23:05:29.577Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T23:05:29.577Z
Learning: Applies to **/*.py : For dict/list fields in frozen Pydantic models, rely on frozen=True for field reassignment prevention and copy.deepcopy() at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to **/*.py : Config vs runtime state: use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state. Never mix static config fields with mutable runtime fields in one model.
Applied to files:
src/synthorg/communication/delegation/models.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/communication/delegation/models.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to **/*.py : Config vs runtime state: frozen Pydantic models for config/identity; separate mutable-via-copy models (using model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Applied to files:
src/synthorg/communication/delegation/models.py
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
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/communication/delegation/models.py
📚 Learning: 2026-03-16T20:14:00.937Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T20:14:00.937Z
Learning: Applies to **/*.py : Validate: at system boundaries (user input, external APIs, config files).
Applied to files:
src/synthorg/communication/delegation/models.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/delegation.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/observability/events/tool.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/delegation.pysrc/synthorg/observability/events/tool.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/delegation.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/observability/events/tool.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/delegation.pysrc/synthorg/observability/events/tool.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/delegation.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/observability/events/tool.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls
Applied to files:
src/synthorg/observability/events/delegation.pysrc/synthorg/tools/invocation_bridge.pysrc/synthorg/observability/events/tool.py
📚 Learning: 2026-03-17T18:52:05.142Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T18:52:05.142Z
Learning: Applies to **/*.py : Async concurrency: prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over bare create_task.
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to src/**/*.py : 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`
Applied to files:
src/synthorg/api/controllers/activities.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.
Applied to files:
src/synthorg/api/controllers/activities.pytests/unit/hr/test_activity.py
📚 Learning: 2026-03-26T13:22:36.844Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T13:22:36.844Z
Learning: Applies to src/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`
Applied to files:
src/synthorg/observability/events/tool.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings
Applied to files:
src/synthorg/observability/events/tool.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use event name constants from domain-specific modules under `ai_company.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`). Import directly: `from ai_company.observability.events.<domain> import EVENT_CONSTANT`.
Applied to files:
src/synthorg/observability/events/tool.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/hr/**/*.py : HR engine must provide: hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, trend detection), promotion/demotion
Applied to files:
tests/unit/hr/test_activity.py
🔇 Additional comments (24)
CLAUDE.md (1)
122-122: LGTM! Documentation accurately reflects the new capability.The addition of "invocation tracking" to the tools package description correctly documents the new
ToolInvocationTracker,ToolInvocationRecord, and related infrastructure introduced in this PR.src/synthorg/communication/delegation/models.py (1)
22-22: Verify the non-finite guard on nestedTaskmodels.
allow_inf_nan=Falseis model-local in Pydantic, so the new configs on Line 22 and Line 59 only harden floats declared onDelegationRequest/DelegationResultthemselves. Ifsynthorg.core.task.Taskstill allows non-finite floats,taskanddelegated_taskremain open; Line 112 adds no effective validation today becauseDelegationRecordhas no float fields.#!/bin/bash set -euo pipefail TASK_FILE="$(fd '^task\.py$' src | head -n1)" echo "Inspecting $TASK_FILE" rg -n -C2 'class Task|model_config|budget_limit' "$TASK_FILE" python -m pip install --quiet 'pydantic==2.12.5' python - <<'PY' from pydantic import BaseModel, ConfigDict, ValidationError class Task(BaseModel): budget_limit: float class Wrapper(BaseModel): model_config = ConfigDict(frozen=True, allow_inf_nan=False) task: Task for value in (float("inf"), float("nan")): try: Wrapper.model_validate({"task": {"budget_limit": value}}) except ValidationError as exc: print("REJECTED", value, exc.errors()[0]["msg"]) else: print("ACCEPTED", value) PYAlso applies to: 59-59, 112-112
tests/unit/communication/delegation/test_service.py (1)
448-517: LGTM! Well-structured record-store integration tests.The new
TestDelegationServiceRecordStoreclass properly exercises the constructor wiring (addressing past review feedback), uses parametrization for the fatal error cases (MemoryError,RecursionError), and follows the test guidelines. The graceful degradation test forRuntimeErrorcorrectly verifies that delegation succeeds despite storage failures.docs/design/agents.md (1)
308-308: LGTM! Documentation accurately reflects expanded activity timeline.The updated description correctly lists all the activity sources now merged into the timeline: lifecycle events, task metrics, cost records, tool invocations, and delegation records.
src/synthorg/engine/agent_engine.py (1)
131-131: LGTM! Clean dependency injection for tool invocation tracking.The
tool_invocation_trackerparameter is properly:
- Imported in the
TYPE_CHECKINGblock (addressing past review feedback)- Documented in the
Argsdocstring (addressing past review feedback)- Stored on
self._tool_invocation_tracker- Passed through to
ToolInvokerin_make_tool_invokerAlso applies to: 200-204, 232-232, 298-298, 1196-1196
src/synthorg/tools/invoker.py (1)
408-410: LGTM! Clean integration of tool invocation recording.The recording is correctly placed after the result is built but before returning, ensuring all tool invocations (successful or error) are captured. The
record_tool_invocationbridge handles errors gracefully (best-effort recording), so this won't disrupt tool execution.src/synthorg/api/app.py (1)
429-430: LGTM! Proper dependency injection for new activity sources.The new
tool_invocation_trackeranddelegation_record_storeparameters follow the established pattern: optional withNonedefaults for testing flexibility, documented in the docstring, and correctly passed toAppState.Also applies to: 564-565
tests/unit/hr/performance/test_models.py (1)
119-162: LGTM! Comprehensive temporal validation tests.The
started_atfield tests properly cover:
- Valid case:
started_atbeforecompleted_at(line 119-134)- Invalid cases: parametrized test for
started_atafter and equal tocompleted_at(lines 136-158)- None allowed: confirms optional nature (lines 160-162)
Past review feedback about module-level imports and parametrization has been addressed.
src/synthorg/hr/performance/models.py (1)
53-56: LGTM! Well-implemented temporal validation forstarted_at.The implementation correctly:
- Makes
started_atoptional withNonedefault for backward compatibility- Uses Pydantic's
@model_validator(mode="after")for cross-field validation- Enforces strict ordering (
<not<=) via the>=rejection condition- Provides informative error messages with ISO timestamps for debugging
Also applies to: 74-83
src/synthorg/observability/events/tool.py (1)
58-62: LGTM! Properly organized event constants for invocation tracking.The new constants follow the established naming conventions (
TOOL_*prefix,tool.invocation.*dotted values) and are correctly typed withFinal[str]. The section comment keeps the file well-organized.src/synthorg/tools/invocation_bridge.py (1)
21-25: Failed tool attempts still bypass recording.
src/synthorg/tools/invoker.py:354-413only callsrecord_tool_invocation()on the post-_build_result()path. Lookup, permission, validation, security, execution, and parking failures still return before this helper runs, so many failed tool calls never producetool_usedevents. Please move the recording into a singlefinallyin_invoke_single()or record on each early-return path.src/synthorg/tools/invocation_tracker.py (1)
24-33: Persisttool_usedrecords outside process memory.This tracker is still backed only by the in-process
_recordslist, so restarts and multi-worker deployments will lose or fragment tool activity. That means the newtool_usedfeed is not actually durable/queryable at the system level yet; please back it with a shared store or repository before shipping.src/synthorg/api/state.py (4)
15-17: LGTM - New imports align with feature requirements.The imports for
DelegationRecordStoreandToolInvocationTrackerare correctly placed and use# noqa: TC001consistently with other type-only imports in this file.Also applies to: 42-42
73-73: LGTM - Service wiring follows established patterns.The new
_delegation_record_storeand_tool_invocation_trackerslots, constructor parameters, and assignments follow the exact pattern used for other optional services. The constructor's keyword-only args withNonedefaults enable graceful degradation.Also applies to: 87-87, 113-114, 135-136
193-196: LGTM - Adds missinghas_cost_trackerfor consistency.This completes the
has_*/ accessor pair pattern forcost_tracker, which was previously missing the presence check. Controllers can now check availability before accessing.
376-400: LGTM - New service accessors follow established conventions.The
has_tool_invocation_tracker/tool_invocation_trackerandhas_delegation_record_store/delegation_record_storeproperty pairs:
- Follow the existing
has_*+ accessor pattern- Use
_require_servicefor consistent 503 behavior with logging- Have proper docstrings consistent with the class style
tests/unit/hr/test_activity.py (8)
7-8: LGTM - New imports for test data model dependencies.Imports for
CostRecord,DelegationRecord, andToolInvocationRecordare correctly added to support the new test helper factories.Also applies to: 14-14
40-62: LGTM - Updated helper supportsstarted_atfor task_started tests.The addition of the optional
started_atparameter withNonedefault maintains backward compatibility while enabling tests for the newtask_startedevent generation.
65-125: LGTM - Well-structured test helper factories.The new helpers
_make_cost_record,_make_tool_invocation, and_make_delegation_record:
- Follow the existing factory pattern with sensible defaults
- Use guideline-compliant test vendor names (
test-provider,test-medium-001)- Have consistent parameter ordering and typing
243-250: LGTM - Important backward compatibility test.This test validates that callers using positional-only arguments continue to work after the signature changes to support keyword-only new parameters.
256-319: LGTM - Comprehensive task_started event coverage.The test class covers:
- Event generation when
started_atis present- No event when
started_atisNone- Both events from same record with correct timestamps
- Merge ordering with lifecycle events
325-372: LGTM - cost_incurred tests validate description format and field population.Good coverage of the description format (
"test-medium-001","500+100 tokens","0.0025 USD") and merge behavior with empty inputs.
378-471: LGTM - Thorough tool_used tests with security consideration.The test suite covers success/failure states and importantly verifies that error messages are not leaked in descriptions (line 421:
assert "Permission denied" not in evt.description). Thetask_id=Noneedge case is also properly tested.
477-576: LGTM - Delegation and all-types merge tests.The delegation tests correctly validate:
- Dual perspective (
delegation_sentvsdelegation_received) from the same record- Proper
agent_idassignment (delegator for sent, delegatee for received)related_idspopulationThe
test_merge_all_event_typesis an excellent integration test verifying all 7 events merge with correct descending timestamp order.
| except MemoryError, RecursionError: | ||
| raise | ||
| except ServiceUnavailableError: | ||
| raise |
There was a problem hiding this comment.
Log these source-specific failures before re-raising them.
These fatal/service-unavailable branches bubble out without a warning/error log that identifies which fetcher failed, so the request loses source context right where it is known. Please emit a structured log before each re-raise, or centralize that behavior in a helper. As per coding guidelines "Handle errors explicitly, never silently swallow them. All error paths must log at WARNING or ERROR with context before raising."
Also applies to: 209-212, 238-241, 286-289
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/api/controllers/activities.py` around lines 180 - 183, The
except blocks that catch MemoryError, RecursionError and ServiceUnavailableError
should log the failure with source/fetcher context before re-raising; update
each catch (the blocks handling MemoryError, RecursionError and
ServiceUnavailableError in activities.py and the similar blocks at the other
locations) to call a structured logger (e.g., logger.error or logger.warning)
including the source/fetcher identifier and include exception details
(exc_info=True) immediately prior to re-raising, or implement a small helper
like log_and_reraise(exc, source_name, fetcher_name) and call that from each
except block to avoid duplication.
Fetch delegator and delegatee records independently so a failure in one does not discard the successful result of the other. Extracted _safe_delegation_query helper to reduce complexity. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🤖 I have created a release *beep* *boop* --- #MAJOR CHANGES; We got a somewhat working webui :) ## [0.5.0](v0.4.9...v0.5.0) (2026-03-30) ### Features * add analytics trends and budget forecast API endpoints ([#798](#798)) ([16b61f5](16b61f5)) * add department policies to default templates ([#852](#852)) ([7a41548](7a41548)) * add remaining activity event types (task_started, tool_used, delegation, cost_incurred) ([#832](#832)) ([4252fac](4252fac)) * agent performance, activity, and history API endpoints ([#811](#811)) ([9b75c1d](9b75c1d)) * Agent Profiles and Detail pages (biography, career, performance) ([#874](#874)) ([62d7880](62d7880)) * app shell, Storybook, and CI/CD pipeline ([#819](#819)) ([d4dde90](d4dde90)) * Approvals page with risk grouping, urgency indicators, batch actions ([#889](#889)) ([4e9673d](4e9673d)) * Budget Panel page (P&L dashboard, breakdown charts, forecast) ([#890](#890)) ([b63b0f1](b63b0f1)) * build infrastructure layer (API client, auth, WebSocket) ([#815](#815)) ([9f01d3e](9f01d3e)) * CLI global options infrastructure, UI modes, exit codes, env vars ([#891](#891)) ([fef4fc5](fef4fc5)) * CodeMirror editor and theme preferences toggle ([#905](#905), [#807](#807)) ([#909](#909)) ([41fbedc](41fbedc)) * Company page (department/agent management) ([#888](#888)) ([cfb88b0](cfb88b0)) * comprehensive hint coverage across all CLI commands ([#900](#900)) ([937974e](937974e)) * config system extensions, per-command flags for init/start/stop/status/logs ([#895](#895)) ([32f83fe](32f83fe)) * configurable currency system replacing hardcoded USD ([#854](#854)) ([b372551](b372551)) * Dashboard page (metric cards, activity feed, budget burn) ([#861](#861)) ([7d519d5](7d519d5)) * department health, provider status, and activity feed endpoints ([#818](#818)) ([6d5f196](6d5f196)) * design tokens and core UI components ([#833](#833)) ([ed887f2](ed887f2)) * extend approval, meeting, and budget API responses ([#834](#834)) ([31472bf](31472bf)) * frontend polish -- real-time UX, accessibility, responsive, performance ([#790](#790), [#792](#792), [#791](#791), [#793](#793)) ([#917](#917)) ([f04a537](f04a537)) * implement human roles and access control levels ([#856](#856)) ([d6d8a06](d6d8a06)) * implement semantic conflict detection in workspace merge ([#860](#860)) ([d97283b](d97283b)) * interaction components and animation patterns ([#853](#853)) ([82d4b01](82d4b01)) * Login page + first-run bootstrap + Company page ([#789](#789), [#888](#888)) ([#896](#896)) ([8758e8d](8758e8d)) * Meetings page with timeline viz, token bars, contribution formatting ([#788](#788)) ([#904](#904)) ([b207f46](b207f46)) * Messages page with threading, channel badges, sender indicators ([#787](#787)) ([#903](#903)) ([28293ad](28293ad)) * Org Chart force-directed view and drag-drop reassignment ([#872](#872), [#873](#873)) ([#912](#912)) ([a68a938](a68a938)) * Org Chart page (living nodes, status, CRUD, department health) ([#870](#870)) ([0acbdae](0acbdae)) * per-command flags for remaining commands, auto-behavior wiring, help/discoverability ([#897](#897)) ([3f7afa2](3f7afa2)) * Providers page with backend rework -- health, CRUD, subscription auth ([#893](#893)) ([9f8dd98](9f8dd98)) * scaffold React + Vite + TypeScript + Tailwind project ([#799](#799)) ([bd151aa](bd151aa)) * Settings page with search, dependency indicators, grouped rendering ([#784](#784)) ([#902](#902)) ([a7b9870](a7b9870)) * Setup Wizard rebuild with template comparison, cost estimator, theme customization ([#879](#879)) ([ae8b50b](ae8b50b)) * setup wizard UX -- template filters, card metadata, provider form reuse ([#910](#910)) ([7f04676](7f04676)) * setup wizard UX overhaul -- mode choice, step reorder, provider fixes ([#907](#907)) ([ee964c4](ee964c4)) * structured ModelRequirement in template agent configs ([#795](#795)) ([7433548](7433548)) * Task Board page (rich Kanban, filtering, dependency viz) ([#871](#871)) ([04a19b0](04a19b0)) ### Bug Fixes * align frontend types with backend and debounce WS refetches ([#916](#916)) ([134c11b](134c11b)) * auto-cleanup targets newly pulled images instead of old ones ([#884](#884)) ([50e6591](50e6591)) * correct wipe backup-skip flow and harden error handling ([#808](#808)) ([c05860f](c05860f)) * improve provider setup in wizard, subscription auth, dashboard bugs ([#914](#914)) ([87bf8e6](87bf8e6)) * improve update channel detection and add config get command ([#814](#814)) ([6b137f0](6b137f0)) * resolve all ESLint warnings, add zero-warnings enforcement ([#899](#899)) ([079b46a](079b46a)) * subscription auth uses api_key, base URL optional for cloud providers ([#915](#915)) ([f0098dd](f0098dd)) ### Refactoring * semantic analyzer cleanup -- shared filtering, concurrency, extraction ([#908](#908)) ([81372bf](81372bf)) ### Documentation * brand identity and UX design system from [#765](#765) exploration ([#804](#804)) ([389a9f4](389a9f4)) * page structure and information architecture for v0.5.0 dashboard ([#809](#809)) ([f8d6d4a](f8d6d4a)) * write UX design guidelines with WCAG-verified color system ([#816](#816)) ([4a4594e](4a4594e)) ### Tests * add unit tests for agent hooks and page components ([#875](#875)) ([#901](#901)) ([1d81546](1d81546)) ### CI/CD * bump actions/deploy-pages from 4.0.5 to 5.0.0 in the major group ([#831](#831)) ([01c19de](01c19de)) * bump astral-sh/setup-uv from 7.6.0 to 8.0.0 in /.github/actions/setup-python-uv in the all group ([#920](#920)) ([5f6ba54](5f6ba54)) * bump codecov/codecov-action from 5.5.3 to 6.0.0 in the major group ([#868](#868)) ([f22a181](f22a181)) * bump github/codeql-action from 4.34.1 to 4.35.0 in the all group ([#883](#883)) ([87a4890](87a4890)) * bump sigstore/cosign-installer from 4.1.0 to 4.1.1 in the minor-and-patch group ([#830](#830)) ([7a69050](7a69050)) * bump the all group with 3 updates ([#923](#923)) ([ff27c8e](ff27c8e)) * bump wrangler from 4.76.0 to 4.77.0 in /.github in the minor-and-patch group ([#822](#822)) ([07d43eb](07d43eb)) * bump wrangler from 4.77.0 to 4.78.0 in /.github in the all group ([#882](#882)) ([f84118d](f84118d)) ### Maintenance * add design system enforcement hook and component inventory ([#846](#846)) ([15abc43](15abc43)) * add dev-only auth bypass for frontend testing ([#885](#885)) ([6cdcd8a](6cdcd8a)) * add pre-push rebase check hook ([#855](#855)) ([b637a04](b637a04)) * backend hardening -- eviction/size-caps and model validation ([#911](#911)) ([81253d9](81253d9)) * bump axios from 1.13.6 to 1.14.0 in /web in the all group across 1 directory ([#922](#922)) ([b1b0232](b1b0232)) * bump brace-expansion from 5.0.4 to 5.0.5 in /web ([#862](#862)) ([ba4a565](ba4a565)) * bump eslint-plugin-react-refresh from 0.4.26 to 0.5.2 in /web ([#801](#801)) ([7574bb5](7574bb5)) * bump faker from 40.11.0 to 40.11.1 in the minor-and-patch group ([#803](#803)) ([14d322e](14d322e)) * bump https://github.com/astral-sh/ruff-pre-commit from v0.15.7 to 0.15.8 ([#864](#864)) ([f52901e](f52901e)) * bump nginxinc/nginx-unprivileged from `6582a34` to `f99cc61` in /docker/web in the all group ([#919](#919)) ([df85e4f](df85e4f)) * bump nginxinc/nginx-unprivileged from `ccbac1a` to `6582a34` in /docker/web ([#800](#800)) ([f4e9450](f4e9450)) * bump node from `44bcbf4` to `71be405` in /docker/sandbox ([#827](#827)) ([91bec67](91bec67)) * bump node from `5209bca` to `cf38e1f` in /docker/web ([#863](#863)) ([66d6043](66d6043)) * bump picomatch in /site ([#842](#842)) ([5f20bcc](5f20bcc)) * bump recharts 2->3 and @types/node 22->25 in /web ([#802](#802)) ([a908800](a908800)) * Bump requests from 2.32.5 to 2.33.0 ([#843](#843)) ([41daf69](41daf69)) * bump smol-toml from 1.6.0 to 1.6.1 in /site ([#826](#826)) ([3e5dbe4](3e5dbe4)) * bump the all group with 3 updates ([#921](#921)) ([7bace0b](7bace0b)) * bump the minor-and-patch group across 1 directory with 2 updates ([#829](#829)) ([93e611f](93e611f)) * bump the minor-and-patch group across 1 directory with 3 updates ([#841](#841)) ([7010c8e](7010c8e)) * bump the minor-and-patch group across 1 directory with 3 updates ([#869](#869)) ([548cee5](548cee5)) * bump the minor-and-patch group in /site with 2 updates ([#865](#865)) ([9558101](9558101)) * bump the minor-and-patch group with 2 updates ([#867](#867)) ([4830706](4830706)) * consolidate Dependabot groups to 1 PR per ecosystem ([06d2556](06d2556)) * consolidate Dependabot groups to 1 PR per ecosystem ([#881](#881)) ([06d2556](06d2556)) * improve worktree skill with full dep sync and status enhancements ([#906](#906)) ([772c625](772c625)) * remove Vue remnants and document framework decision ([#851](#851)) ([bf2adf6](bf2adf6)) * update web dependencies and fix brace-expansion CVE ([#880](#880)) ([a7a0ed6](a7a0ed6)) * upgrade to Storybook 10 and TypeScript 6 ([#845](#845)) ([52d95f2](52d95f2)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary
task_started,tool_used,delegation_sent,delegation_received,cost_incurredhr/activity.py,merge_activity_timelineintegration (backward-compatible keyword-only args), controller wiring with graceful degradation, and unit testsToolInvocationRecordmodel,ToolInvocationTrackerstore,DelegationRecordStorewith dual-perspective querying,invocation_bridge.pyfor invoker integrationasyncio.TaskGroupfor parallel async fetchesTest plan
uv run python -m pytest tests/ -n auto -m unit)Closes #812
🤖 Generated with Claude Code