feat: generalize versioning to VersionSnapshot[T] for all entity types#1155
feat: generalize versioning to VersionSnapshot[T] for all entity types#1155
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🧰 Additional context used📓 Path-based instructions (3)**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
src/synthorg/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
src/**/*.py📄 CodeRabbit inference engine (CLAUDE.md)
Files:
⚙️ CodeRabbit configuration file
Files:
🧠 Learnings (1)📓 Common learnings🔇 Additional comments (4)
WalkthroughThe PR replaces bespoke workflow-versioning with a generic VersionSnapshot[T]/VersionRepository[T] approach: it removes the WorkflowDefinitionVersion model and its repository/protocol, introduces generic SQLiteVersionRepository[T] usage for workflow snapshots, updates the SQLite schema to entity-keyed snapshot tables (content_hash + snapshot) and adds tables for evaluation_config, budget_config, company, and role, switches snapshot persistence to VersioningService.snapshot_if_changed(), adds controllers for the new versioned entities, and adds a migration check for legacy workflow version tables. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Preview DeploymentURL: https://pr-1155.synthorg-pr-preview.pages.dev Built from commit |
Dependency ReviewThe following issues were 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. License Issuesweb/package-lock.json
OpenSSF Scorecard
Scanned Files
|
There was a problem hiding this comment.
Code Review
This pull request introduces a generic versioning system for several entities, including BudgetConfig, Company, and EvaluationConfig, by replacing the specific WorkflowDefinitionVersion model with a generic VersionSnapshot. It implements a unified SQLiteVersionRepository to handle these snapshots and adds corresponding API controllers for version history. My feedback suggests refactoring the nearly identical version controllers into a generic base class or factory to reduce code duplication and recommends adding a missing unit test for the Company model's serialization round-trip.
| class BudgetConfigVersionController(Controller): | ||
| """Version history for budget configuration.""" | ||
|
|
||
| path = "/budget/config" | ||
| tags = ("budget",) | ||
|
|
||
| @get("/versions", guards=[require_read_access]) | ||
| async def list_versions( | ||
| self, | ||
| state: State, | ||
| offset: PaginationOffset = 0, | ||
| limit: PaginationLimit = 20, | ||
| ) -> Response[PaginatedResponse[SnapshotT]]: | ||
| """List version history for budget configuration.""" | ||
| repo = state.app_state.persistence.budget_config_versions | ||
| versions = await repo.list_versions( | ||
| _ENTITY_ID, | ||
| limit=limit, | ||
| offset=offset, | ||
| ) | ||
| total = await repo.count_versions(_ENTITY_ID) | ||
| meta = PaginationMeta(total=total, offset=offset, limit=limit) | ||
| return Response( | ||
| content=PaginatedResponse[SnapshotT]( | ||
| data=versions, | ||
| pagination=meta, | ||
| ), | ||
| ) | ||
|
|
||
| @get( | ||
| "/versions/{version_num:int}", | ||
| guards=[require_read_access], | ||
| ) | ||
| async def get_version( | ||
| self, | ||
| state: State, | ||
| version_num: Annotated[int, Parameter(ge=1)], | ||
| ) -> Response[ApiResponse[SnapshotT]]: | ||
| """Get a specific budget config version snapshot.""" | ||
| repo = state.app_state.persistence.budget_config_versions | ||
| version = await repo.get_version(_ENTITY_ID, version_num) | ||
| if version is None: | ||
| return Response( | ||
| content=ApiResponse[SnapshotT]( | ||
| error=f"Version {version_num} not found", | ||
| ), | ||
| status_code=404, | ||
| ) | ||
| return Response( | ||
| content=ApiResponse[SnapshotT](data=version), | ||
| ) |
There was a problem hiding this comment.
This controller is nearly identical to CompanyVersionController and EvaluationConfigVersionController. To improve maintainability and reduce code duplication, consider creating a generic base controller or a factory function to generate these simple, read-only version history controllers.
A factory could look something like this:
def create_version_controller(
entity_name: str,
path: str,
tags: tuple[str, ...],
repo_name: str,
model_type: type,
entity_id: str = "default",
) -> type[Controller]:
SnapshotT = VersionSnapshot[model_type]
class GenericVersionController(Controller):
# ... implementation ...
GenericVersionController.__name__ = f"{model_type.__name__}VersionController"
return GenericVersionControllerThis would allow you to define each controller in just a few lines.
| loaded = await repo.get_version("backend-developer", 1) | ||
| assert loaded is not None | ||
| assert loaded.snapshot.name == "backend-developer" | ||
| assert loaded.snapshot.department == "engineering" |
There was a problem hiding this comment.
This test file validates round-trip serialization for most of the newly versioned entities, which is great. However, a test for the Company model seems to be missing.
Given that Company is also part of this versioning generalization, adding a test_company_roundtrip would ensure its serialization is also correctly handled by the generic SQLiteVersionRepository.
You could add a test like this:
# ── Company round-trip ──────────────────────────────────────────
@pytest.mark.unit
async def test_company_roundtrip() -> None:
"""Company survives serialization with defaults."""
from synthorg.core.company import Company
company = Company(name="Test Corp")
async with aiosqlite.connect(":memory:") as db:
repo = await _make_repo(
db,
"company_versions",
Company,
)
snap = _make_snapshot("default", company)
inserted = await repo.save_version(snap)
assert inserted is True
loaded = await repo.get_version("default", 1)
assert loaded is not None
assert loaded.snapshot.name == company.nameThere was a problem hiding this comment.
Pull request overview
This PR generalizes the workflow versioning implementation to the shared VersionSnapshot[T] / VersionRepository[T] infrastructure and expands the same version-history capability to additional entity types (BudgetConfig, EvaluationConfig, Company, Role), including new SQLite tables and API endpoints for config version history.
Changes:
- Replace bespoke workflow version model/repo with generic
VersionSnapshot[WorkflowDefinition]backed bySQLiteVersionRepository. - Add SQLite schema + persistence backend wiring for
evaluation_config_versions,budget_config_versions,company_versions, androle_versions. - Add new API controllers + tests for BudgetConfig/Company/EvaluationConfig version history endpoints; update workflow version API/tests to the new snapshot shape.
Reviewed changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/persistence/test_protocol.py | Updates PersistenceBackend conformance fake to include new version-repo properties. |
| tests/unit/persistence/sqlite/test_workflow_version_repo.py | Removes tests for deleted bespoke SQLiteWorkflowVersionRepository. |
| tests/unit/persistence/sqlite/test_migrations.py | Updates expected tables/indexes for new generic workflow version schema + new entity version tables. |
| tests/unit/persistence/sqlite/test_entity_version_repos.py | Adds round-trip tests for generic SQLiteVersionRepository instantiations across entity types. |
| tests/unit/engine/workflow/test_version.py | Removes tests for deleted WorkflowDefinitionVersion model. |
| tests/unit/engine/workflow/test_diff.py | Migrates diff tests to wrap workflow definitions in VersionSnapshot. |
| tests/unit/api/fakes_workflow.py | Updates fake workflow version repository to implement VersionRepository[WorkflowDefinition]. |
| tests/unit/api/fakes_backend.py | Adds fake persistence properties for the newly versioned entity repositories. |
| tests/unit/api/controllers/test_workflow_versions.py | Updates assertions to the new { snapshot: ... } response shape. |
| tests/unit/api/controllers/test_config_version_controllers.py | Adds tests for list/get version history endpoints for budget/company/evaluation configs. |
| src/synthorg/persistence/workflow_version_repo.py | Deletes bespoke workflow version repo protocol. |
| src/synthorg/persistence/sqlite/workflow_version_repo.py | Deletes bespoke SQLite workflow version repo implementation. |
| src/synthorg/persistence/sqlite/schema.sql | Migrates workflow versions table to generic snapshot format + adds new version tables and indexes. |
| src/synthorg/persistence/sqlite/backend.py | Wires SQLiteVersionRepository[T] instances for workflow + 4 additional entity types. |
| src/synthorg/persistence/protocol.py | Extends PersistenceBackend protocol with new version repository properties and generic workflow type. |
| src/synthorg/engine/workflow/version.py | Deletes bespoke workflow version snapshot model. |
| src/synthorg/engine/workflow/diff.py | Updates compute_diff() to accept VersionSnapshot[WorkflowDefinition] and unwrap at the top level. |
| src/synthorg/api/state.py | Injects budget_config_versions repo into OrgMutationService. |
| src/synthorg/api/services/org_mutations.py | Adds best-effort budget config snapshotting when budget-related company fields change. |
| src/synthorg/api/controllers/workflows.py | Switches workflow snapshot writes to VersioningService.snapshot_if_changed() and updates delete call. |
| src/synthorg/api/controllers/workflow_versions.py | Migrates workflow version APIs to VersionSnapshot responses + uses VersioningService for rollback snapshotting. |
| src/synthorg/api/controllers/evaluation_config_versions.py | New controller providing list/get for evaluation config versions. |
| src/synthorg/api/controllers/company_versions.py | New controller providing list/get for company versions. |
| src/synthorg/api/controllers/budget_config_versions.py | New controller providing list/get for budget config versions. |
| src/synthorg/api/controllers/_workflow_helpers.py | Removes obsolete build_version_snapshot() helper. |
| src/synthorg/api/controllers/init.py | Registers the 3 new config version controllers. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| """Tests for entity-specific version repository instantiations. | ||
|
|
||
| Validates that WorkflowDefinition, BudgetConfig, EvaluationConfig, | ||
| Company, and Role models survive JSON round-trip serialization through | ||
| the generic SQLiteVersionRepository. | ||
| """ |
There was a problem hiding this comment.
The module docstring claims this test suite validates JSON round-trip for Company, but there is currently no company round-trip test in this file. Either add a Company round-trip test or update the docstring to match what’s actually covered, so the test intent stays accurate.
| @pytest.mark.unit | ||
| async def test_content_hash_dedup() -> None: | ||
| """Saving the same content twice returns False on second insert.""" | ||
| from synthorg.budget.config import BudgetConfig | ||
|
|
||
| config = BudgetConfig() | ||
|
|
||
| async with aiosqlite.connect(":memory:") as db: | ||
| repo = await _make_repo( | ||
| db, | ||
| "budget_versions", | ||
| BudgetConfig, | ||
| ) | ||
| snap1 = _make_snapshot("default", config, version=1) | ||
| assert await repo.save_version(snap1) is True | ||
|
|
||
| # Same content, different version number. | ||
| snap2 = _make_snapshot("default", config, version=2) | ||
| assert await repo.save_version(snap2) is True | ||
|
|
||
| # Same version number (duplicate PK) -- rejected. | ||
| assert await repo.save_version(snap1) is False | ||
|
|
||
| count = await repo.count_versions("default") | ||
| assert count == 2 |
There was a problem hiding this comment.
test_content_hash_dedup doesn’t actually test content-hash deduplication: it inserts the same content under different version numbers and expects both inserts to succeed. If the goal is to test “skip no-change saves”, this should exercise VersioningService.snapshot_if_changed() (or otherwise assert a save is skipped when the latest content hash matches). Otherwise, rename the test/docstring to reflect that it’s only testing PK idempotency / duplicate version handling.
| CREATE TABLE IF NOT EXISTS workflow_definition_versions ( | ||
| definition_id TEXT NOT NULL CHECK(length(definition_id) > 0), | ||
| entity_id TEXT NOT NULL CHECK(length(entity_id) > 0), | ||
| version INTEGER NOT NULL CHECK(version >= 1), | ||
| name TEXT NOT NULL CHECK(length(name) > 0), | ||
| description TEXT NOT NULL DEFAULT '', | ||
| workflow_type TEXT NOT NULL CHECK(workflow_type IN ( | ||
| 'sequential_pipeline', 'parallel_execution', 'kanban', 'agile_kanban' | ||
| )), | ||
| nodes TEXT NOT NULL, | ||
| edges TEXT NOT NULL, | ||
| created_by TEXT NOT NULL CHECK(length(created_by) > 0), | ||
| content_hash TEXT NOT NULL CHECK(length(content_hash) > 0), | ||
| snapshot TEXT NOT NULL CHECK(length(snapshot) > 0), | ||
| saved_by TEXT NOT NULL CHECK(length(saved_by) > 0), | ||
| saved_at TEXT NOT NULL, | ||
| PRIMARY KEY (definition_id, version), | ||
| FOREIGN KEY (definition_id) | ||
| REFERENCES workflow_definitions(id) | ||
| ON DELETE CASCADE | ||
| saved_at TEXT NOT NULL CHECK( | ||
| saved_at LIKE '%+00:00' OR saved_at LIKE '%Z' | ||
| ), | ||
| PRIMARY KEY (entity_id, version) | ||
| ); |
There was a problem hiding this comment.
workflow_definition_versions no longer has a foreign key back to workflow_definitions(id). That means deleting a workflow definition outside the explicit controller path can leave orphaned version rows, and DB-level referential integrity is lost. Consider restoring an FOREIGN KEY (entity_id) REFERENCES workflow_definitions(id) ON DELETE CASCADE constraint for this table (even if other version tables remain FK-less).
| new_etag = await self._company_snapshot_etag() | ||
| if "budget_monthly" in updated: | ||
| await self._snapshot_budget_config(saved_by="api") |
There was a problem hiding this comment.
Budget config version snapshots are being attributed to saved_by="api", even though this mutation is triggered by an authenticated HTTP request. If these snapshots are meant to provide an audit trail, consider threading the caller identity into OrgMutationService.update_company() (e.g., pass saved_by=get_auth_user_id(request) from the controller) so the version history records the real actor.
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/synthorg/api/controllers/workflow_versions.py (1)
364-376:⚠️ Potential issue | 🟡 MinorSnapshot failure is logged but not communicated to the client.
When the version snapshot fails after a successful rollback, the error is logged but the client receives a 200 OK response without any indication that the snapshot wasn't persisted. This could lead to confusion when the version history doesn't reflect the rollback.
Consider either:
- Including a warning in the response payload indicating partial success
- Documenting this behavior clearly in the API contract
The current approach handles the error explicitly (logging), so it's not a silent swallow, but the client has no visibility into this partial failure state.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/controllers/workflow_versions.py` around lines 364 - 376, The rollback currently calls VersioningService.snapshot_if_changed and catches PersistenceError (svc, snapshot_if_changed, PersistenceError) but only logs via logger.exception(WORKFLOW_VERSION_SNAPSHOT_FAILED) so the client still gets a 200 with no indication of snapshot failure; update the controller handling around svc.snapshot_if_changed (and the rolled_back/updater flow) to surface the partial-failure to the client by adding a warning flag/message in the response payload (e.g., include snapshot_status: "failed" or warning: "snapshot persistence failed") or alternatively return a 207/appropriate error code, and ensure the logged message is preserved; adjust response construction to include the new warning field when PersistenceError is caught.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/synthorg/api/controllers/budget_config_versions.py`:
- Around line 34-78: Add DEBUG logging in list_versions and WARNING logging in
get_version: define new observability event constants (e.g.,
BUDGET_CONFIG_VERSION_LISTED and BUDGET_CONFIG_VERSION_NOT_FOUND) in
synthorg.observability.events, then in budget_config_versions.list_versions log
a debug event with context (entity id _ENTITY_ID, offset, limit, total) before
returning, and in get_version log a warning with context (entity id _ENTITY_ID,
version_num) when version is None before returning the 404 response; use the
same logging facility used elsewhere (match style in workflow_versions) and
attach the event constants to the log calls.
In `@src/synthorg/persistence/sqlite/backend.py`:
- Around line 276-331: The repeated serialize_snapshot/deserialize_snapshot
lambdas across SQLiteVersionRepository instantiations (e.g., _workflow_versions,
_identity_versions, _evaluation_config_versions, _budget_config_versions,
_company_versions, _role_versions) should be extracted into a small
factory/helper to avoid duplication; add a generic helper (e.g.,
_json_serializer and _make_version_repo) that accepts the db, table_name and
model class and returns a SQLiteVersionRepository with serialize_snapshot using
model.model_dump(mode="json") and deserialize_snapshot using
model_cls.model_validate(json.loads(s)), then replace each direct
SQLiteVersionRepository(...) call with a call to that helper.
In `@src/synthorg/persistence/sqlite/schema.sql`:
- Around line 437-454: The schema change for table workflow_definition_versions
renames definition_id→entity_id, drops several columns and adds
content_hash/snapshot which breaks existing DBs because migrations.py only
supports _add_column_if_missing for a small allowlist; fix by adding an explicit
migration in src/synthorg/persistence/sqlite/migrations.py that handles the
workflow_definition_versions transformation: detect legacy schema (presence of
definition_id and old columns), create a new temp table with the new schema
(entity_id, version, content_hash, snapshot, saved_by, saved_at), copy/transform
data from old columns into the temp table (map definition_id→entity_id,
compute/NULL new columns), drop the old table and rename the temp table to
workflow_definition_versions, and add this migration to the migration sequence;
alternatively, if opting not to auto-migrate, add clear upgrade documentation
and a manual SQL migration script mentioning the exact column changes
(definition_id, name, description, workflow_type, nodes, edges, created_by →
removed; content_hash, snapshot → added) and ensure _add_column_if_missing
remains unchanged.
In `@tests/unit/api/controllers/test_config_version_controllers.py`:
- Around line 133-176: Add tests mirroring TestBudgetConfigVersions: implement
test_list_versions_with_data and test_list_versions_paginated for company
snapshots so Company endpoints have equivalent coverage. Use
fake_persistence.company_versions (as in test_get_version) to save multiple
snapshots via _snap("name", company), then call
test_client.get("/api/v1/company/versions", headers=make_auth_headers("ceo")) to
assert the returned list contains the saved versions and that pagination
parameters (limit/offset or page) behave as expected; name the tests
test_list_versions_with_data and test_list_versions_paginated to match existing
patterns.
- Around line 182-224: Add a new test method named test_list_versions_with_data
to the TestEvaluationConfigVersions class that persists multiple
EvaluationConfig snapshots via
fake_persistence.evaluation_config_versions.save_version (use _snap to create
versions), then calls the list endpoint "/api/v1/evaluation/config/versions"
with make_auth_headers("ceo") and asserts status_code 200 and that the returned
data contains the saved snapshots in the expected order and with correct version
numbers; mirror the structure of Company’s list_versions_with_data test to
verify ordering and contents.
In `@tests/unit/persistence/sqlite/test_entity_version_repos.py`:
- Around line 1-6: Docstring mentions Company but no test exists; add a unit
test named test_company_roundtrip that mirrors the existing round-trip tests:
import Company from synthorg.core.company, construct Company(name="Test Corp"),
create an in-memory aiosqlite DB, call _make_repo(db, "company_versions",
Company), build a snapshot with _make_snapshot("default", company), call
repo.save_version(snap) and assert True, then repo.get_version("default", 1) and
assert the loaded snapshot.name == "Test Corp"; alternatively, if you prefer not
to add a test, update the module docstring to remove "Company" from the
validated models list.
---
Outside diff comments:
In `@src/synthorg/api/controllers/workflow_versions.py`:
- Around line 364-376: The rollback currently calls
VersioningService.snapshot_if_changed and catches PersistenceError (svc,
snapshot_if_changed, PersistenceError) but only logs via
logger.exception(WORKFLOW_VERSION_SNAPSHOT_FAILED) so the client still gets a
200 with no indication of snapshot failure; update the controller handling
around svc.snapshot_if_changed (and the rolled_back/updater flow) to surface the
partial-failure to the client by adding a warning flag/message in the response
payload (e.g., include snapshot_status: "failed" or warning: "snapshot
persistence failed") or alternatively return a 207/appropriate error code, and
ensure the logged message is preserved; adjust response construction to include
the new warning field when PersistenceError is caught.
🪄 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: a423bb04-964b-42d4-80d2-bed204d49cd6
📒 Files selected for processing (26)
src/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/_workflow_helpers.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/state.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/engine/workflow/version.pysrc/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/persistence/sqlite/schema.sqlsrc/synthorg/persistence/sqlite/workflow_version_repo.pysrc/synthorg/persistence/workflow_version_repo.pytests/unit/api/controllers/test_config_version_controllers.pytests/unit/api/controllers/test_workflow_versions.pytests/unit/api/fakes_backend.pytests/unit/api/fakes_workflow.pytests/unit/engine/workflow/test_diff.pytests/unit/engine/workflow/test_version.pytests/unit/persistence/sqlite/test_entity_version_repos.pytests/unit/persistence/sqlite/test_migrations.pytests/unit/persistence/sqlite/test_workflow_version_repo.pytests/unit/persistence/test_protocol.py
💤 Files with no reviewable changes (6)
- tests/unit/engine/workflow/test_version.py
- src/synthorg/engine/workflow/version.py
- src/synthorg/persistence/workflow_version_repo.py
- src/synthorg/persistence/sqlite/workflow_version_repo.py
- tests/unit/persistence/sqlite/test_workflow_version_repo.py
- src/synthorg/api/controllers/_workflow_helpers.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). (10)
- GitHub Check: Deploy Preview
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Build Sandbox
- GitHub Check: Test (Python 3.14)
- GitHub Check: Dependency Review
- GitHub Check: Agent
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Analyze (python)
- GitHub Check: Analyze (go)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Nofrom __future__ import annotations-- Python 3.14 has PEP 649 native lazy annotations
Useexcept A, B:(no parentheses) -- PEP 758 except syntax, enforced by ruff on Python 3.14
All public functions must have type hints. Use mypy strict mode.
Google style docstrings are required on public classes and functions. Enforced by ruff D rules.
Use immutability patterns: create new objects, never mutate existing ones. For non-Pydantic internal collections, usecopy.deepcopy()at construction +MappingProxyTypewrapping.
Use frozen Pydantic models for config/identity; separate mutable-via-copy models for runtime state. Never mix static config fields with mutable runtime fields.
Use Pydantic v2 (BaseModel,model_validator,computed_field,ConfigDict). Includeallow_inf_nan=Falsein allConfigDictdeclarations.
Use@computed_fieldfor derived values instead of storing + validating redundant fields
UseNotBlankStr(fromcore.types) for all identifier/name fields, including optional and tuple variants, instead of manual whitespace validators
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code
Line length must be 88 characters. Enforced by ruff.
Functions must be less than 50 lines, files must be less than 800 lines
Handle errors explicitly, never silently swallow exceptions
Validate at system boundaries (user input, external APIs, config files)
Always use variable namelogger(not_logger, notlog) for logger instances
Files:
tests/unit/api/fakes_backend.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/workflows.pytests/unit/api/controllers/test_workflow_versions.pytests/unit/engine/workflow/test_diff.pytests/unit/persistence/sqlite/test_migrations.pytests/unit/persistence/test_protocol.pytests/unit/persistence/sqlite/test_entity_version_repos.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/persistence/protocol.pytests/unit/api/controllers/test_config_version_controllers.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/evaluation_config_versions.pytests/unit/api/fakes_workflow.pysrc/synthorg/api/controllers/workflow_versions.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Use pytest markers:@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slow
Useasyncio_mode = 'auto'for async tests -- no manual@pytest.mark.asyncioneeded
Global test timeout is 30 seconds per test in pyproject.toml. Non-default overrides liketimeout(60)are allowed, but do not add per-filepytest.mark.timeout(30)markers.
Prefer@pytest.mark.parametrizefor testing similar cases
Use Hypothesis for property-based testing with@given+@settingsdecorators
Never skip, dismiss, or ignore flaky tests. For timing-sensitive tests, mocktime.monotonic()andasyncio.sleep(). For indefinite blocks, useasyncio.Event().wait()instead ofasyncio.sleep(large_number).
Files:
tests/unit/api/fakes_backend.pytests/unit/api/controllers/test_workflow_versions.pytests/unit/engine/workflow/test_diff.pytests/unit/persistence/sqlite/test_migrations.pytests/unit/persistence/test_protocol.pytests/unit/persistence/sqlite/test_entity_version_repos.pytests/unit/api/controllers/test_config_version_controllers.pytests/unit/api/fakes_workflow.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/api/fakes_backend.pytests/unit/api/controllers/test_workflow_versions.pytests/unit/engine/workflow/test_diff.pytests/unit/persistence/sqlite/test_migrations.pytests/unit/persistence/test_protocol.pytests/unit/persistence/sqlite/test_entity_version_repos.pytests/unit/api/controllers/test_config_version_controllers.pytests/unit/api/fakes_workflow.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Every module with business logic MUST importfrom synthorg.observability import get_loggerand setlogger = get_logger(__name__)
Never useimport loggingorlogging.getLogger()orprint()in application code. Exceptions: observability/setup.py, observability/sinks.py, observability/syslog_handler.py, observability/http_handler.py, observability/otlp_handler.py may use stdlib logging and stderr print for handler construction.
Event names must always use constants from domain-specific modules undersynthorg.observability.events. Import directly from specific domain modules.
Use structured kwargs for logging: alwayslogger.info(EVENT, key=value)-- neverlogger.info('msg %s', val)
All error paths must log at WARNING or ERROR with context before raising
All state transitions must log at INFO level
DEBUG logging should be used for object creation, internal flow, and entry/exit of key functions
RetryConfig and RateLimiterConfig are set per-provider inProviderConfig
Files:
src/synthorg/api/state.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/persistence/protocol.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/controllers/workflow_versions.py
src/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Never use real vendor names (Anthropic, OpenAI, Claude, GPT) in project-owned code, docstrings, comments, tests, or config examples. Use generic names:
example-provider,example-large-001,example-medium-001,test-provider,test-small-001, etc.
Files:
src/synthorg/api/state.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/persistence/protocol.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/controllers/workflow_versions.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/api/state.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/persistence/protocol.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/controllers/workflow_versions.py
🧠 Learnings (19)
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (via `model_copy(update=...)`) for runtime state that evolves
Applied to files:
src/synthorg/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 : Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state
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/api/**/*.py : REST API: Litestar framework, controllers with guards, channels for WebSocket, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. RFC 9457 structured errors (ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation).
Applied to files:
src/synthorg/api/controllers/workflows.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/workflow_versions.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/api/**/*.py : API package (api/): Litestar REST + WebSocket with controllers, guards, channels, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint, provider management endpoint (CRUD + test + presets), backup endpoint, RFC 9457 structured errors, AppState hot-reload slots, service auto-wiring (Phase 1 at construction, Phase 2 on startup), lifecycle helpers
Applied to files:
src/synthorg/api/controllers/workflows.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/api/controllers/workflow_versions.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/persistence/**/*.py : Persistence uses pluggable PersistenceBackend protocol. SQLite is the initial backend. Settings use SettingsRepository (namespaced settings CRUD).
Applied to files:
tests/unit/persistence/sqlite/test_entity_version_repos.pysrc/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/backend.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/services/org_mutations.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/api/services/org_mutations.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Persistence backend: pluggable PersistenceBackend protocol in `src/synthorg/persistence/`, SQLite initial, SettingsRepository (namespaced settings CRUD).
Applied to files:
src/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/backend.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/persistence/protocol.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/persistence/sqlite/backend.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/memory/**/*.py : Memory package (memory/): pluggable MemoryBackend protocol, backends/ (Mem0 adapter), retrieval pipeline (ranking, RRF fusion, injection, formatting, non-inferable filtering), shared org memory (org/), consolidation/archival (density-aware: DensityClassifier, AbstractiveSummarizer, ExtractivePreserver, DualModeConsolidationStrategy)
Applied to files:
src/synthorg/persistence/protocol.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/persistence/protocol.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/budget_config_versions.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/persistence/protocol.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:
src/synthorg/persistence/protocol.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/persistence/protocol.pysrc/synthorg/api/controllers/budget_config_versions.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/persistence/protocol.py
📚 Learning: 2026-04-02T07:18:02.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T07:18:02.381Z
Learning: Applies to {pyproject.toml,src/synthorg/__init__.py} : Update version in `pyproject.toml` (`[tool.commitizen].version`) and `src/synthorg/__init__.py` (`__version__`)
Applied to files:
src/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/workflow_versions.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to src/**/*.py : Use event name constants from domain-specific modules under ai_company.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.) — import directly
Applied to files:
src/synthorg/persistence/sqlite/backend.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/synthorg/api/**/*.py : Litestar API must include setup wizard, auth/, auto-wiring, and lifecycle management
Applied to files:
src/synthorg/api/controllers/workflow_versions.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/api/**/*.py : Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
Applied to files:
src/synthorg/api/controllers/workflow_versions.py
🔇 Additional comments (24)
src/synthorg/engine/workflow/diff.py (1)
167-206: LGTM!The migration to
VersionSnapshot[WorkflowDefinition]is well-implemented. The function correctly:
- Compares
entity_idfor cross-definition validation- Extracts the
snapshotattribute for actual diff computation- Sets
definition_idfromold.entity_idtests/unit/api/fakes_backend.py (1)
236-239: LGTM!The new version repositories are correctly added following the existing pattern. The
FakeVersionRepositoryclass is appropriately generic to handle all snapshot types.Also applies to: 375-390
src/synthorg/api/state.py (1)
200-212: LGTM!The
budget_config_versionsparameter is correctly wired toOrgMutationServicein both initialization paths with proper null-safety handling.Also applies to: 842-850
src/synthorg/api/controllers/workflows.py (2)
421-435: LGTM!The migration to
VersioningService.snapshot_if_changed()is correctly implemented, replacing the manualbuild_version_snapshot()+save_version()pattern. The broaderPersistenceErrorcatch aligns with the PR objective.
614-617: LGTM!The method name is correctly updated from
delete_versions_for_definitiontodelete_versions_for_entity, matching theVersionRepositoryprotocol.tests/unit/persistence/sqlite/test_migrations.py (1)
45-48: LGTM!The expected tables and indexes are correctly updated to match the new schema with:
- Four new version tables for
evaluation_config,budget_config,company, androle- Renamed workflow version indexes (
idx_wdv_entity_saved,idx_wdv_content_hash)- Consistent index naming pattern across all version tables
Also applies to: 108-109, 124-131
tests/unit/api/controllers/test_workflow_versions.py (1)
146-147: LGTM!The test assertions are correctly updated to access the
namefield through the nestedsnapshotobject, reflecting the newVersionSnapshot[WorkflowDefinition]response structure.Also applies to: 194-194, 284-284
tests/unit/engine/workflow/test_diff.py (1)
56-89: LGTM!The
_verhelper is well-refactored to buildVersionSnapshot[WorkflowDefinition]objects. The backward-compatible handling ofdefinition_idoverride (mapped toentity_id) and the proper synchronization betweenentity_idandWorkflowDefinition.idensures existing tests continue to work correctly.src/synthorg/api/controllers/__init__.py (1)
15-17: LGTM!The three new version controllers are correctly imported, registered in
ALL_CONTROLLERS, and exported via__all__. Per the app wiring (context snippets), adding toALL_CONTROLLERSis sufficient for automatic route registration.Also applies to: 23-25, 31-33, 98-100, 117-117, 122-122, 127-127
src/synthorg/api/services/org_mutations.py (2)
88-113: Versioning helper correctly implements best-effort semantics.The
_snapshot_budget_configmethod appropriately:
- Returns early when versioning is disabled (line 99-100)
- Uses broad exception handling to avoid blocking mutations (line 108)
- Logs failures with structured context (lines 109-113)
The placement inside
_org_lock(lines 284-285) correctly addresses the race condition mentioned in the PR objectives.
77-86: Optional dependency injection pattern is well-implemented.The use of
VersionRepository[BudgetConfig] | Nonewith conditionalVersioningServiceinitialization follows the project's optional-feature pattern cleanly.tests/unit/persistence/test_protocol.py (1)
555-591: Protocol conformance stubs follow established pattern.The new version repository properties (
evaluation_config_versions,budget_config_versions,company_versions,role_versions) correctly mirror the existing stub pattern for@runtime_checkableprotocol compliance testing.src/synthorg/persistence/protocol.py (1)
263-295: Generic version repository protocol properties are well-defined.The new
evaluation_config_versions,budget_config_versions,company_versions, androle_versionsproperties follow the established pattern fromidentity_versionsand provide proper type hints withVersionRepository[T].src/synthorg/api/controllers/evaluation_config_versions.py (2)
34-78: Controller implementation follows established patterns.The endpoint implementations are correct:
- Proper pagination with
PaginationMeta- Appropriate 404 handling for missing versions
Parameter(ge=1)constraint on version numbers- Read access guard applied consistently
17-20: 🧹 Nitpick | 🔵 TrivialLogger imported but unused.
The
loggeris initialized but never used in this controller. Consider adding DEBUG logging for endpoint entry/exit or INFO for version lookups, or remove the unused import if no logging is needed.⛔ Skipped due to learnings
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 : Variable name for logger: always `logger` (not `_logger`, not `log`)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__)`Learnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-03-17T06:43:14.114Z Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger`.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__)`Learnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-03-15T18:28:13.207Z Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger` (not `_logger`, not `log`).Learnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-04-08T15:24:49.467Z Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST import `from synthorg.observability import get_logger` and set `logger = get_logger(__name__)`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 : Every module with business logic must have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`Learnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-03-20T11:18:48.128Z Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have `from synthorg.observability import get_logger` followed by `logger = get_logger(__name__)`.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 : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`Learnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-03-31T21:07:37.470Z Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`tests/unit/api/controllers/test_config_version_controllers.py (1)
23-35: Generic helper function is well-designed.The
_snap[T: BaseModel]helper using PEP 695 syntax provides clean test data creation with proper content hash computation.tests/unit/persistence/sqlite/test_entity_version_repos.py (1)
36-67: Generic test helpers are well-implemented.The
_make_snapshotand_make_repohelpers provide clean abstractions for creating test fixtures with proper type parameterization.src/synthorg/api/controllers/company_versions.py (2)
28-78: Controller implementation is correct and consistent.The
CompanyVersionControllercorrectly implements the version history endpoints with proper pagination, 404 handling, and read access guards.
17-20: 🧹 Nitpick | 🔵 TrivialLogger imported but unused.
Same as
evaluation_config_versions.py- the logger is initialized but never used. Consider adding logging or removing the unused import.⛔ Skipped due to learnings
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 : Variable name for logger: always `logger` (not `_logger`, not `log`)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__)`Learnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-03-17T06:43:14.114Z Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger`.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__)`Learnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-03-15T18:28:13.207Z Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger` (not `_logger`, not `log`).Learnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-04-08T15:24:49.467Z Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST import `from synthorg.observability import get_logger` and set `logger = get_logger(__name__)`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 : Every module with business logic must have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`Learnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-03-20T11:18:48.128Z Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have `from synthorg.observability import get_logger` followed by `logger = get_logger(__name__)`.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 : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`Learnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-03-31T21:07:37.470Z Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`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 : Never use `import logging`, `logging.getLogger()`, or `print()` in application code; use the injected logger insteadLearnt 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 : Never use `import logging`, `logging.getLogger()`, or `print()` in application code — use the synthorg logger insteadLearnt 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 : Never use `import logging` / `logging.getLogger()` / `print()` in application codeLearnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-04-01T12:36:58.294Z Learning: Applies to src/synthorg/**/*.py : Never use `import logging` / `logging.getLogger()` / `print()` in application code; exceptions: `observability/setup.py` and `observability/sinks.py` may use stdlib logging and `print(..., file=sys.stderr)` for bootstrap codeLearnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-04-01T15:36:39.993Z Learning: Applies to src/**/*.py : Never use `import logging` / `logging.getLogger()` / `print()` in application code (exception: `observability/setup.py` and `observability/sinks.py` may use stdlib `logging` and `print(..., file=sys.stderr)` for bootstrap)Learnt from: CR Repo: Aureliolo/synthorg PR: 0 File: CLAUDE.md:0-0 Timestamp: 2026-04-08T15:24:49.467Z Learning: Applies to src/synthorg/**/*.py : Never use `import logging` or `logging.getLogger()` or `print()` in application code. Exceptions: observability/setup.py, observability/sinks.py, observability/syslog_handler.py, observability/http_handler.py, observability/otlp_handler.py may use stdlib logging and stderr print for handler construction.src/synthorg/persistence/sqlite/backend.py (2)
667-721: New version repository properties follow established pattern.The property accessors for
evaluation_config_versions,budget_config_versions,company_versions, androle_versionscorrectly use_require_connectedand include proper docstrings withRaisessections.
143-154: Instance variable declarations are properly typed.The new nullable repository references use correct generic type annotations (e.g.,
SQLiteVersionRepository[BudgetConfig] | None).src/synthorg/api/controllers/workflow_versions.py (1)
181-189: LGTM!The rollback construction correctly restores content from the target snapshot while preserving identity fields (
id,created_by,created_at) from the existing definition and properly bumping the version number. This aligns well with the migration toVersionSnapshot[T].tests/unit/api/fakes_workflow.py (1)
112-186: LGTM!The
FakeWorkflowVersionRepositorycorrectly implements theVersionRepository[WorkflowDefinition]protocol with proper semantics:
save_versionreturnsboolindicating insert vs. duplicate (consistent withFakeVersionRepositoryinfakes_backend.py)- New methods
get_latest_versionandget_by_content_hashsupport conflict resolution inVersioningService- Method renamed from
delete_versions_for_definitiontodelete_versions_for_entitymatching the protocol- Immutability maintained via
copy.deepcopy()on all returnssrc/synthorg/persistence/sqlite/schema.sql (1)
597-671: LGTM on new version tables.The four new versioning tables (
evaluation_config_versions,budget_config_versions,company_versions,role_versions) follow a consistent pattern with:
- Proper
IF NOT EXISTSguards for idempotent creation- Consistent column structure matching the generic
VersionSnapshot[T]model- Appropriate indexes for entity listing and content-hash lookups
- UTC timestamp validation via CHECK constraints
This design enables the generic
SQLiteVersionRepository[T]to work uniformly across all entity types.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1155 +/- ##
========================================
Coverage 88.88% 88.89%
========================================
Files 857 858 +1
Lines 50440 50574 +134
Branches 5072 5081 +9
========================================
+ Hits 44835 44958 +123
- Misses 4643 4650 +7
- Partials 962 966 +4 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/design/engine.md`:
- Around line 230-231: The design doc's schema description for workflow
versioning is incomplete: update the paragraph referencing
VersionSnapshot[WorkflowDefinition] and VersioningService[WorkflowDefinition] to
reflect the actual generic version table schema used (e.g.,
workflow_definition_versions) by adding the missing saved_by and saved_at
columns alongside entity_id, version, content_hash, and snapshot; preserve the
note about content-hash deduplication and INSERT OR IGNORE conflict retry but
explicitly document saved_by and saved_at semantics (who and when) to eliminate
the design/implementation drift.
In `@src/synthorg/api/services/org_mutations.py`:
- Around line 91-114: The _snapshot_budget_config method currently only catches
PersistenceError but calls self._resolver.get_budget_config which can raise
SettingNotFoundError or other resolver exceptions; update the try/except to
either catch SettingNotFoundError in addition to PersistenceError or broaden to
a general Exception to preserve the "best-effort" behavior, and when logging
VERSION_SNAPSHOT_FAILED include the caught exception info (keep existing
entity_type/entity_id context) so resolver failures don't crash the mutation
unless you intend them to—adjust the docstring if you choose to let resolver
errors propagate.
In `@src/synthorg/engine/workflow/diff.py`:
- Around line 186-203: Add guard checks that the snapshots themselves belong to
the same entity before computing diffs: after confirming old.entity_id ==
new.entity_id, verify old.snapshot.id == new.snapshot.id (and/or any
snapshot-specific identifier you use) and log a warning via
WORKFLOW_DEF_INVALID_REQUEST with old_snapshot_id and new_snapshot_id if they
differ, then raise ValueError to abort (similar to the existing entity_id
check). Update the block containing old.entity_id/new.entity_id, the uses of
old.snapshot and new.snapshot, and ensure WorkflowDiff construction remains
unchanged when snapshots pass the check.
In `@src/synthorg/persistence/sqlite/schema.sql`:
- Around line 437-461: The comment in schema.sql references a non-existent
migration doc; either add the missing migration file
docs/migrations/workflow-version-schema.md with clear manual migration steps for
the breaking change that converts the old flattened workflow fields to the
VersionSnapshot format (affecting workflow_definition_versions table/entity_id,
version, content_hash, snapshot, saved_by, saved_at), or remove/update the
inline comment in src/synthorg/persistence/sqlite/schema.sql (the block above
CREATE TABLE workflow_definition_versions and the note about FK removal) to no
longer point to the missing file and instead provide a concise alternative
pointer (e.g., to an existing migration guide or an inline brief migration
note).
In `@tests/unit/api/controllers/test_config_version_controllers.py`:
- Around line 1-5: The module docstring currently lists
BudgetConfigVersionController, CompanyVersionController, and
EvaluationConfigVersionController but omits RoleVersionController; update the
top-of-file docstring in
tests/unit/api/controllers/test_config_version_controllers.py to include
RoleVersionController so it accurately reflects all controllers covered by the
tests (mention RoleVersionController alongside the existing three controller
names).
🪄 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: 22f7994c-9b22-482a-b706-6213f451fc9c
📒 Files selected for processing (18)
CLAUDE.mddocs/design/engine.mdsrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/api/controllers/company.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/observability/events/versioning.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/persistence/sqlite/schema.sqlsrc/synthorg/persistence/sqlite/version_repo.pytests/unit/api/controllers/test_config_version_controllers.pytests/unit/persistence/sqlite/test_entity_version_repos.pyweb/src/api/types.tsweb/src/pages/workflow-editor/VersionHistoryPanel.tsx
📜 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: Dashboard Test
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Web
- GitHub Check: Build Backend
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (8)
web/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx,js,jsx}: Always usecreateLoggerfrom@/lib/logger-- never bareconsole.warn/console.error/console.debugin application code
Logger variable name must always beconst log(e.g.const log = createLogger('module-name'))
Pass dynamic/untrusted values as separate arguments to logger methods (not interpolated into the message string) so they go throughsanitizeArg
Attacker-controlled fields inside structured objects must be wrapped insanitizeForLog()before embedding in log calls
Files:
web/src/pages/workflow-editor/VersionHistoryPanel.tsxweb/src/api/types.ts
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground,bg-card,text-accent,text-success,bg-danger, etc.) or CSS variables (var(--so-*)) for colors; NEVER hardcode hex values in.tsx/.tsfiles
Usefont-sansorfont-mono(Geist tokens) for typography; NEVER setfontFamilydirectly in.tsx/.tsfiles
Use density-aware tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind spacing; NEVER hardcode pixel values for layout spacing in components
Use token variables (var(--so-shadow-card-hover),border-border,border-bright) for shadows and borders; NEVER hardcode values in.tsx/.tsfiles
Use@/lib/motionpresets for Framer Motion transition durations; NEVER hardcode transition durations
CSS side-effect imports in TypeScript 6 require type declarations -- add/// <reference types="vite/client" />at the top of files with CSS importsALWAYS reuse existing components from
web/src/components/ui/before creating new onesNEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions — use design tokens and
@/lib/motionpresets
Files:
web/src/pages/workflow-editor/VersionHistoryPanel.tsxweb/src/api/types.ts
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
No
from __future__ import annotations— Python 3.14 has PEP 649 native lazy annotationsUse PEP 758 except syntax:
except A, B:(no parentheses) — ruff enforces this on Python 3.14All public functions and classes must have type hints. Use mypy strict mode
Use Google style docstrings, required on all public classes and functions — enforced by ruff D rules
Create new objects for immutability, never mutate existing ones. For non-Pydantic internal collections (registries,
BaseTool), usecopy.deepcopy()at construction +MappingProxyTypewrapping for read-only enforcementFor
dict/listfields in frozen Pydantic models, rely onfrozen=Truefor field reassignment prevention and usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence)Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using
model_copy(update=...)) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one modelUse Pydantic v2 (
BaseModel,model_validator,computed_field,ConfigDict). In allConfigDictdeclarations, useallow_inf_nan=Falseto rejectNaN/Infin numeric fields at validation timeUse
@computed_fieldfor derived values instead of storing + validating redundant fields (e.g.TokenUsage.total_tokens)Use
NotBlankStr(fromcore.types) for all identifier/name fields — including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants — instead of manual whitespace validatorsPrefer
asyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over barecreate_taskKeep line length to 88 characters — ruff enforces this
Functions must be < 50 lines, files < 800 lines
Handle errors explicitly, never silently swallow them
Validate at system boundaries (user input, external APIs, c...
Files:
src/synthorg/observability/events/versioning.pysrc/synthorg/api/controllers/company.pysrc/synthorg/persistence/sqlite/version_repo.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/controllers/company_versions.pytests/unit/persistence/sqlite/test_entity_version_repos.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/engine/workflow/diff.pytests/unit/api/controllers/test_config_version_controllers.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/persistence/sqlite/backend.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Every module with business logic MUST have:
from synthorg.observability import get_loggerthenlogger = get_logger(__name__)Logging variable name must always be
logger(not_logger, notlog)Use event name constants from domain-specific modules under
synthorg.observability.events(e.g.,API_REQUEST_STARTEDfromevents.api,TOOL_INVOKE_STARTfromevents.tool, etc.). Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANTAlways use structured kwargs in logging:
logger.info(EVENT, key=value)— neverlogger.info("msg %s", val)All error paths must log at WARNING or ERROR with context before raising
All state transitions must log at INFO
Use DEBUG logging for object creation, internal flow, and entry/exit of key functions
All provider calls go through
BaseCompletionProviderwhich applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code
RetryConfigandRateLimiterConfigare set per-provider inProviderConfigRetryable errors (
is_retryable=True):RateLimitError,ProviderTimeoutError,ProviderConnectionError,ProviderInternalError. Non-retryable errors raise immediately without retry.RetryExhaustedErrorsignals all retries failed — the engine layer catches this to trigger fallback chainsRate limiter respects
RateLimitError.retry_afterfrom providers — automatically pauses future requestsVendor-agnostic everywhere: NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, or config examples. Use generic names:
example-provider,example-large-001,example-medium-001,example-small-001,large/medium/smallaliases
Files:
src/synthorg/observability/events/versioning.pysrc/synthorg/api/controllers/company.pysrc/synthorg/persistence/sqlite/version_repo.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/persistence/sqlite/backend.py
src/**/*.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/observability/events/versioning.pysrc/synthorg/api/controllers/company.pysrc/synthorg/persistence/sqlite/version_repo.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/persistence/sqlite/backend.py
docs/design/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Read the relevant
docs/design/page before implementing any feature or planning any issue. The design spec is the starting point for architecture, data models, and behaviorWhen approved deviations occur, update the relevant
docs/design/page to reflect the new reality
Files:
docs/design/engine.md
docs/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Vendor names may appear in: (1) Operations design page provider list (
docs/design/operations.md), (2) third-party import paths/module names in docstrings (e.g.litellm.types.llms.openai), (3) provider presets (src/synthorg/providers/presets.py) which are user-facing runtime data
Files:
docs/design/engine.md
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Every module with business logic MUST have:
from synthorg.observability import get_loggerthenlogger = get_logger(__name__)Logging variable name must always be
logger(not_logger, notlog)Use markers:
@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slow80% coverage minimum — enforced in CI
Use
asyncio_mode = "auto"for async tests — no manual@pytest.mark.asyncioneeded30 second timeout per test (global in
pyproject.toml) — do not add per-filepytest.mark.timeout(30)markers; non-default overrides liketimeout(60)are allowedUse parallelism with
pytest-xdistvia-n 8— ALWAYS include-n 8when running pytest locally, never run tests sequentiallyPrefer
@pytest.mark.parametrizefor testing similar casesVendor-agnostic 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/smallaliases. Usetest-provider,test-small-001, etc. in testsUse Hypothesis for property-based testing with
@given+@settings. Hypothesis profiles:ci(deterministic, 10 examples +derandomize=True),dev(1000 examples),fuzz(10,000 examples, no deadline),extreme(500,000 examples, no deadline). Control viaHYPOTHESIS_PROFILEenv varWhen Hypothesis finds a failure, it is a real bug — fix the underlying bug and add an explicit
@example(...)decorator to the test so the case is permanently covered in CI. Do NOT just rerun and move onNEVER skip, dismiss, or ignore flaky tests — always fix them fully and fundamentally. For timing-sensitive tests, mock
time.monotonic()andasyncio.sleep()to make them deterministic. For tasks that must block indefinitely until cancelled, useasyncio.Event().wait()instead ofasyncio.sleep(large_number)
Files:
tests/unit/persistence/sqlite/test_entity_version_repos.pytests/unit/api/controllers/test_config_version_controllers.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/persistence/sqlite/test_entity_version_repos.pytests/unit/api/controllers/test_config_version_controllers.py
🧠 Learnings (27)
📓 Common learnings
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: If implementation deviates from the spec (better approach found, scope evolved, etc.), alert the user and explain why — user decides whether to proceed or update the spec. Do NOT silently diverge
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: When a spec topic is referenced, read the relevant `docs/design/` page before coding
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: Every implementation plan must be presented to the user for accept/deny before coding starts
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: At every phase of planning and implementation, be critical and actively look for ways to improve the design in the spirit of robustness, correctness, simplicity, and future-proofing. Surface improvements as suggestions, not silent changes
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: Prioritize issues by dependency order, not priority labels — unblocked dependencies come first
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: Never use `import logging` / `logging.getLogger()` / `print()` in application code. Exception: `observability/setup.py`, `observability/sinks.py`, `observability/syslog_handler.py`, `observability/http_handler.py`, and `observability/otlp_handler.py` may use stdlib `logging` and `print(..., file=sys.stderr)` for handler construction, bootstrap, and error reporting code
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: Pure data models, enums, and re-exports do NOT need logging
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: See `web/CLAUDE.md` for the full component inventory, design token rules, and post-training references. A PostToolUse hook (`scripts/check_web_design_system.py`) enforces design system rules on every Edit/Write to `web/src/`
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: NEVER use `cd` in Bash commands — the working directory is already set to the project root. Use absolute paths or run commands directly
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: Exception to no-cd rule: `bash -c "cd <dir> && <cmd>"` is safe (runs in a child process, no cwd side effects). Use this for tools without a `-C` flag
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: NEVER use Bash to write or modify files — use the Write or Edit tools. Do not use `cat >`, `cat << EOF`, `echo >`, `echo >>`, `sed -i`, `python -c "open(...).write(...)", or `tee` to create or modify files
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: Commits must use format: `<type>: <description>` with types: feat, fix, refactor, docs, test, chore, perf, ci. Enforced by commitizen (commit-msg hook)
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: Signed commits are required on `main` via branch protection — all commits must be GPG/SSH signed
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: Use branch naming: `<type>/<slug>` from main
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: Pre-commit hooks enforce: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint, golangci-lint + go vet, no-em-dashes, no-redundant-timeout, eslint-web
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: Hookify rules (committed in `.claude/hookify.*.md`): block-pr-create (blocks direct `gh pr create`), enforce-parallel-tests (enforces `-n 8` with pytest), no-cd-prefix (blocks `cd` prefix in Bash commands), no-local-coverage (blocks `--cov` locally)
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: Pre-push hooks: mypy type-check (affected modules only) + pytest unit tests (affected modules only) + golangci-lint + go vet + go test + eslint-web. Foundational module changes (core, config, observability) or conftest changes trigger full runs
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: Use `gh issue list` via Bash (not MCP tools) for GitHub issue queries — MCP `list_issues` has unreliable field data
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: Merge strategy: squash merge — PR body becomes the squash commit message on main. Trailers (e.g. `Release-As`, `Closes `#N``) must be in the PR body to land in the final commit
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: Preserve existing `Closes `#NNN`` references in PR issue references — never remove unless explicitly asked
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: After finishing an issue implementation: create a feature branch (`<type>/<slug>`), commit, and push — do NOT create a PR automatically. Do NOT leave work uncommitted on main
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: NEVER create a PR directly — `gh pr create` is blocked by hookify. ALWAYS use `/pre-pr-review` to create PRs — it runs automated checks + review agents + fixes before creating the PR
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: For trivial/docs-only changes: `/pre-pr-review quick` skips agents but still runs automated checks
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: After the PR exists, use `/aurelio-review-pr` to handle external reviewer feedback
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T18:29:49.081Z
Learning: Fix everything valid when review agents find issues — including pre-existing issues in surrounding code, suggestions, and findings adjacent to the PR's changes. No deferring, no "out of scope" skipping
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/api/**/*.py : API package (api/): Litestar REST + WebSocket with controllers, guards, channels, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint, provider management endpoint (CRUD + test + presets), backup endpoint, RFC 9457 structured errors, AppState hot-reload slots, service auto-wiring (Phase 1 at construction, Phase 2 on startup), lifecycle helpers
Applied to files:
src/synthorg/api/controllers/company.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/role_versions.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/api/**/*.py : REST API: Litestar framework, controllers with guards, channels for WebSocket, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. RFC 9457 structured errors (ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation).
Applied to files:
src/synthorg/api/controllers/company.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/controllers/company_versions.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/controllers/company.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/persistence/sqlite/backend.py
📚 Learning: 2026-04-06T16:35:12.934Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-06T16:35:12.934Z
Learning: All project conventions, commands, and standards are defined in CLAUDE.md - refer to it for project structure, package layout, code conventions, quick commands, git workflow, testing standards, design specifications, logging, resilience, and security patterns
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions. Pure data models, enums, and re-exports do NOT need logging.
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-04-02T12:07:44.443Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T12:07:44.443Z
Learning: Applies to src/synthorg/**/*.py : Always use structured logging: `logger.info(EVENT, key=value)` — never `logger.info('msg %s', val)`
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions.
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/budget_config_versions.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 : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger` (not `_logger`, not `log`).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Use structured logging: always `logger.info(EVENT, key=value)` — never `logger.info("msg %s", val)`
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Structured kwargs in logging: always `logger.info(EVENT, key=value)` — never `logger.info('msg %s', val)`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, and key function entry/exit
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-31T20:07:03.035Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T20:07:03.035Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising; all state transitions must log at INFO; DEBUG for object creation, internal flow, entry/exit of key functions
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/budget_config_versions.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/services/org_mutations.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/api/services/org_mutations.pysrc/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately.
Applied to files:
src/synthorg/api/services/org_mutations.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/persistence/**/*.py : Persistence uses pluggable PersistenceBackend protocol. SQLite is the initial backend. Settings use SettingsRepository (namespaced settings CRUD).
Applied to files:
tests/unit/persistence/sqlite/test_entity_version_repos.pysrc/synthorg/persistence/sqlite/backend.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/budget_config_versions.pysrc/synthorg/api/controllers/__init__.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : All state transitions must log at INFO.
Applied to files:
src/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : All error paths must log at WARNING or ERROR with context before raising.
Applied to files:
src/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use DEBUG logging for object creation, internal flow, and entry/exit of key functions.
Applied to files:
src/synthorg/api/controllers/budget_config_versions.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/controllers/__init__.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Persistence backend: pluggable PersistenceBackend protocol in `src/synthorg/persistence/`, SQLite initial, SettingsRepository (namespaced settings CRUD).
Applied to files:
src/synthorg/persistence/sqlite/backend.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/persistence/sqlite/backend.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/persistence/sqlite/backend.py
🔇 Additional comments (29)
src/synthorg/observability/events/versioning.py (1)
35-36: Good addition of a dedicated not-found event constant.This keeps missing-version logs consistent with the versioning event namespace.
As per coding guidelines: "Event names: always use constants from the domain-specific module under
synthorg.observability.events."src/synthorg/persistence/sqlite/version_repo.py (1)
187-188: Docstring improvement is accurate and useful.Explicitly documenting
QueryErrorhere improves the repository contract for callers.CLAUDE.md (1)
93-93: LGTM — event-convention docs stay aligned with the new versioning events.web/src/api/types.ts (1)
1782-1793: Type contract update looks correct for the snapshot-based API shape.web/src/pages/workflow-editor/VersionHistoryPanel.tsx (1)
27-27: Correct field-path update for the new version payload shape.src/synthorg/engine/workflow/diff.py (1)
283-284: Helper type updates are clean and consistent withVersionSnapshotusage.Also applies to: 382-383, 422-423
src/synthorg/api/controllers/company.py (1)
100-104: Good propagation of actor identity into mutation versioning.Passing
saved_byfrom request context improves historical attribution quality for company snapshots.src/synthorg/api/controllers/__init__.py (1)
15-17: LGTM!The new version controller imports and registrations are correctly structured. The controllers are properly added to both
ALL_CONTROLLERSfor route registration and__all__for public API export. The alphabetical ordering in__all__is maintained.Also applies to: 23-25, 31-33, 46-46, 99-102, 119-119, 124-124, 129-129, 140-140
src/synthorg/api/services/org_mutations.py (1)
234-290: LGTM - Budget snapshot correctly placed inside org lock.The
_snapshot_budget_configcall at lines 287-288 is correctly positioned inside the_org_lockcontext, ensuring atomicity between the budget update and version snapshot creation. Thesaved_byparameter threading from the public API is well-designed for audit attribution.src/synthorg/api/controllers/company_versions.py (3)
39-64: LGTM - List versions endpoint is well-structured.The pagination implementation correctly uses
asyncio.gatherfor concurrent fetch/count operations, constructs properPaginationMeta, and logs appropriately at DEBUG level for successful listings.
66-93: LGTM - Get single version endpoint handles 404 correctly.The endpoint properly validates
version_num >= 1viaParameter(ge=1), logs warnings for missing versions, and returns a structured 404 response with an error message.
33-37: No action needed.CompanyVersionControllerandCompanyControllerdo not have conflicting routes: CompanyController exposesGET /company,PATCH /company,POST /company/reorder-departments, andGET /company/departments, while CompanyVersionController exposesGET /company/versionsandGET /company/versions/{version_num}. These paths are completely distinct and coexist without collision.> Likely an incorrect or invalid review comment.src/synthorg/api/controllers/evaluation_config_versions.py (1)
1-93: LGTM!The
EvaluationConfigVersionControllerfollows the same well-structured pattern as other version controllers. The unique path/evaluation/configavoids conflicts, pagination and 404 handling are correctly implemented, and logging uses proper event constants.tests/unit/persistence/sqlite/test_entity_version_repos.py (2)
1-6: LGTM - Company round-trip test now included.The test suite now comprehensively covers all five entity types mentioned in the docstring (WorkflowDefinition, BudgetConfig, EvaluationConfig, Company, Role). The
test_company_roundtriptest validates the full save/retrieve cycle.Also applies to: 187-202
208-232: LGTM - Duplicate PK idempotency test validates INSERT OR IGNORE.The
test_duplicate_pk_idempotencytest correctly verifies that:
- Initial insert returns
True- Different version number allows insertion
- Duplicate
(entity_id, version)returnsFalse(ignored)- Final count is accurate
This confirms the
INSERT OR IGNOREsemantics for concurrent-write safety.src/synthorg/persistence/sqlite/backend.py (2)
278-317: LGTM - Factory helper eliminates duplication.The
_ver_repofactory cleanly encapsulates theSQLiteVersionRepositoryinstantiation pattern with consistent JSON serialization/deserialization viamodel_dump(mode="json")andmodel_validate(). This addresses the previous review suggestion about reducing repetition.
653-707: All version repository table names correctly match the schema definitions. Each_ver_repo()call explicitly passes the table name as the first argument, which aligns with the correspondingCREATE TABLEstatements in schema.sql:
evaluation_config_versions✓budget_config_versions✓company_versions✓role_versions✓No action needed.
src/synthorg/api/controllers/role_versions.py (2)
30-62: LGTM - Per-role versioning correctly uses role_name as entity_id.Unlike singleton config controllers that use
"default"as entity_id, this controller correctly usesrole_nameas the entity identifier, supporting the per-role granularity design decision mentioned in the PR objectives. The list endpoint properly passesrole_nameto bothlist_versionsandcount_versions.
64-92: LGTM - 404 error message includes role context.The error message at line 86 helpfully includes
role_namein the response (f"Version {version_num} not found for role {role_name!r}"), providing better debugging context than generic version-not-found messages.tests/unit/api/controllers/test_config_version_controllers.py (3)
135-220: LGTM - Company version tests now include comprehensive coverage.The
TestCompanyVersionsclass now includestest_list_versions_with_dataandtest_list_versions_paginated, addressing the previous review comment about missing coverage parity withTestBudgetConfigVersions.
226-290: LGTM - EvaluationConfig version tests include list with data.The
TestEvaluationConfigVersionsclass now includestest_list_versions_with_data, addressing the previous review comment.
296-377: LGTM - Role version tests verify per-role entity_id pattern.The
TestRoleVersionsclass correctly tests the per-role versioning pattern whererole_name(e.g.,"backend-dev") is used as the entity identifier. The tests properly use_snap("backend-dev", role, ...)to match this design.src/synthorg/api/controllers/budget_config_versions.py (3)
1-31: LGTM! Imports, logging setup, and module constants are well-structured.The file correctly:
- Uses
get_logger(__name__)per guidelines- Imports event constants from the versioning events module
- Defines a clear type alias for the snapshot type
- Uses a descriptive constant for the singleton entity ID
39-64: LGTM! Thelist_versionsendpoint is correctly implemented.Good use of
asyncio.gatherfor concurrent fetching of versions and count. The DEBUG logging with structured kwargs follows guidelines. Pagination parameters and response wrapping are consistent with the generic versioning pattern.
66-93: LGTM! Theget_versionendpoint correctly handles the not-found case.The WARNING log before returning 404 satisfies the guideline that all error paths must log at WARNING or ERROR with context. Parameter validation with
ge=1prevents invalid version numbers at the API boundary.src/synthorg/persistence/sqlite/schema.sql (4)
604-621: LGTM! Theevaluation_config_versionstable follows the established pattern.Table structure, CHECK constraints, and indexes are consistent with the generic
VersionSnapshot[T]infrastructure.
623-640: LGTM! Thebudget_config_versionstable is correctly defined.Structurally identical to other version tables with proper constraints and indexes.
642-659: LGTM! Thecompany_versionstable follows the versioning pattern.Consistent with the other new version tables.
661-678: LGTM! Therole_versionstable completes the versioning infrastructure.All five version tables now use the same generic schema pattern, enabling uniform
SQLiteVersionRepository[T]usage across entity types.
| async def _snapshot_budget_config(self, saved_by: str) -> None: | ||
| """Snapshot the current BudgetConfig if content changed. | ||
|
|
||
| Best-effort: versioning failures are logged but do not | ||
| block the mutation. | ||
|
|
||
| Args: | ||
| saved_by: Actor who triggered the mutation. | ||
| """ | ||
| if self._budget_versioning is None: | ||
| return | ||
| try: | ||
| budget = await self._resolver.get_budget_config() | ||
| await self._budget_versioning.snapshot_if_changed( | ||
| entity_id="default", | ||
| snapshot=budget, | ||
| saved_by=saved_by, | ||
| ) | ||
| except PersistenceError: | ||
| logger.exception( | ||
| VERSION_SNAPSHOT_FAILED, | ||
| entity_type="BudgetConfig", | ||
| entity_id="default", | ||
| ) |
There was a problem hiding this comment.
Best-effort versioning may not handle all failure modes.
The _snapshot_budget_config method only catches PersistenceError, but self._resolver.get_budget_config() at line 103 may raise SettingNotFoundError or other resolver exceptions. If this is truly best-effort (as documented), consider broadening the exception handling:
🛡️ Proposed fix to catch all versioning errors
try:
budget = await self._resolver.get_budget_config()
await self._budget_versioning.snapshot_if_changed(
entity_id="default",
snapshot=budget,
saved_by=saved_by,
)
- except PersistenceError:
+ except (PersistenceError, Exception) as exc:
logger.exception(
VERSION_SNAPSHOT_FAILED,
entity_type="BudgetConfig",
entity_id="default",
)Alternatively, if resolver failures should propagate to indicate a more serious configuration issue, document this explicitly in the docstring.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/api/services/org_mutations.py` around lines 91 - 114, The
_snapshot_budget_config method currently only catches PersistenceError but calls
self._resolver.get_budget_config which can raise SettingNotFoundError or other
resolver exceptions; update the try/except to either catch SettingNotFoundError
in addition to PersistenceError or broaden to a general Exception to preserve
the "best-effort" behavior, and when logging VERSION_SNAPSHOT_FAILED include the
caught exception info (keep existing entity_type/entity_id context) so resolver
failures don't crash the mutation unless you intend them to—adjust the docstring
if you choose to let resolver errors propagate.
b7018c8 to
914a4b3
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 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/persistence/sqlite/schema.sql`:
- Around line 437-469: The comment warns that the new CREATE TABLE IF NOT EXISTS
for workflow_definition_versions will silently do nothing if an old-schema table
exists; add an explicit safety/migration check and guidance: implement a startup
or migration script that checks for the legacy table name/columns (e.g.,
existence of _wdv_old or columns like definition_id, workflow_type), refuse to
apply the new schema or emit a clear error/warning and instructions if legacy
data is detected, and document or provide an automated migration path that
computes content_hash and populates the new workflow_definition_versions (and
then creates/validates idx_wdv_entity_saved and idx_wdv_content_hash) so
operators are prevented from accidentally running with incompatible schemas.
In `@tests/unit/api/controllers/test_config_version_controllers.py`:
- Around line 226-290: Add a pagination unit test for
TestEvaluationConfigVersions mirroring the pattern used in
TestBudgetConfigVersions/TestCompanyVersions: create multiple versions via
fake_persistence.evaluation_config_versions using await
repo.save_version(_snap(...)) (use EvaluationConfig instances), then call the
GET endpoint "/api/v1/evaluation/config/versions" with pagination query params
(e.g., limit and cursor/next) and assert the response status is 200, the "data"
length equals the page size, ordering is newest-first (versions descending), and
that the response includes pagination metadata (cursor/next) consistent with
other tests; name the test test_list_versions_paginated and place it alongside
the other methods in class TestEvaluationConfigVersions.
In `@tests/unit/api/fakes_workflow.py`:
- Around line 136-137: Update the type annotations for all entity_id parameters
in the fake workflow to use NotBlankStr instead of str: change signatures in the
fake functions/methods including get_latest_version, get_by_content_hash,
list_versions, count_versions, delete_versions_for_entity (and the earlier
occurrence at the initial constructor/signature around the provided diff) so
they match the protocol’s NotBlankStr type and improve IDE/type-checking
support; ensure any imports for NotBlankStr are added if missing.
In `@tests/unit/persistence/sqlite/test_entity_version_repos.py`:
- Around line 238-260: In test_role_roundtrip, replace the hardcoded string
assertion comparing loaded.snapshot.department to "engineering" with the enum
constant to ensure type safety: update the assertion in test_role_roundtrip (the
block that creates Role, saves via repo.save_version and loads via
repo.get_version) to assert loaded.snapshot.department ==
DepartmentName.ENGINEERING so the test uses the DepartmentName enum rather than
a raw string.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: c45a773a-7503-40e5-b3e0-5f2ece2dbd14
📒 Files selected for processing (34)
CLAUDE.mddocs/design/engine.mdsrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/_workflow_helpers.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/api/controllers/company.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/state.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/engine/workflow/version.pysrc/synthorg/observability/events/versioning.pysrc/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/persistence/sqlite/schema.sqlsrc/synthorg/persistence/sqlite/version_repo.pysrc/synthorg/persistence/sqlite/workflow_version_repo.pysrc/synthorg/persistence/workflow_version_repo.pytests/unit/api/controllers/test_config_version_controllers.pytests/unit/api/controllers/test_workflow_versions.pytests/unit/api/fakes_backend.pytests/unit/api/fakes_workflow.pytests/unit/engine/workflow/test_diff.pytests/unit/engine/workflow/test_version.pytests/unit/persistence/sqlite/test_entity_version_repos.pytests/unit/persistence/sqlite/test_migrations.pytests/unit/persistence/sqlite/test_workflow_version_repo.pytests/unit/persistence/test_protocol.pyweb/src/api/types.tsweb/src/pages/workflow-editor/VersionHistoryPanel.tsx
💤 Files with no reviewable changes (6)
- src/synthorg/api/controllers/_workflow_helpers.py
- tests/unit/persistence/sqlite/test_workflow_version_repo.py
- src/synthorg/engine/workflow/version.py
- src/synthorg/persistence/workflow_version_repo.py
- src/synthorg/persistence/sqlite/workflow_version_repo.py
- tests/unit/engine/workflow/test_version.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (7)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
No
from __future__ import annotations-- Python 3.14 has PEP 649 native lazy annotations.Use PEP 758 except syntax: use
except A, B:(no parentheses) -- ruff enforces this on Python 3.14.Type hints required on all public functions; enforce mypy strict mode.
Docstrings required on public classes and functions in Google style; enforced by ruff D rules.
Create new objects, never mutate existing ones. For non-Pydantic internal collections (registries,
BaseTool), usecopy.deepcopy()at construction +MappingProxyTypewrapping for read-only enforcement. Fordict/listfields in frozen Pydantic models, rely onfrozen=Truefor field reassignment prevention andcopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).Distinguish config vs runtime state: use frozen Pydantic models for config/identity; use separate mutable-via-copy models (using
model_copy(update=...)) for runtime state that evolves (e.g. agent execution state, task progress). Never mix static config fields with mutable runtime fields in one model.Use Pydantic v2 with
BaseModel,model_validator,computed_field, andConfigDict. Useallow_inf_nan=Falsein allConfigDictdeclarations to rejectNaN/Infin numeric fields at validation time.Use
@computed_fieldfor derived values instead of storing + validating redundant fields (e.g.TokenUsage.total_tokens).Use
NotBlankStr(fromcore.types) for all identifier/name fields -- including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants -- instead of manual whitespace validators.Prefer
asyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over barecreate_task. Existing code is being migrated incrementally.Line length: 88 characters (enforced by ruff).
Functions must be < 50 lines; files must be < 800...
Files:
src/synthorg/api/controllers/company.pysrc/synthorg/observability/events/versioning.pysrc/synthorg/api/state.pytests/unit/api/controllers/test_workflow_versions.pysrc/synthorg/persistence/sqlite/version_repo.pytests/unit/persistence/sqlite/test_migrations.pytests/unit/api/fakes_backend.pytests/unit/engine/workflow/test_diff.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/persistence/protocol.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/services/org_mutations.pytests/unit/api/controllers/test_config_version_controllers.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/api/controllers/budget_config_versions.pytests/unit/persistence/sqlite/test_entity_version_repos.pysrc/synthorg/persistence/sqlite/backend.pytests/unit/persistence/test_protocol.pytests/unit/api/fakes_workflow.pysrc/synthorg/api/controllers/__init__.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Every module with business logic MUST have:
from synthorg.observability import get_loggerthenlogger = get_logger(__name__).Never use
import logging/logging.getLogger()/print()in application code. Exception:observability/setup.py,observability/sinks.py,observability/syslog_handler.py,observability/http_handler.py, andobservability/otlp_handler.pymay use stdlibloggingandprint(..., file=sys.stderr)for handler construction, bootstrap, and error reporting code that runs before or during logging system configuration.Use constants from domain-specific modules under
synthorg.observability.eventsfor event names (e.g.,API_REQUEST_STARTEDfromevents.api,TOOL_INVOKE_STARTfromevents.tool). Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT. Never use hardcoded strings for event names.Use structured logging: always
logger.info(EVENT, key=value)-- neverlogger.info("msg %s", val).All error paths must log at WARNING or ERROR with context before raising.
All state transitions must log at INFO level.
Use DEBUG logging for object creation, internal flow, entry/exit of key functions.
All provider calls go through
BaseCompletionProviderwhich applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code -- it's handled by the base class.
RetryConfigandRateLimiterConfigare set per-provider inProviderConfig.Retryable errors (
is_retryable=True):RateLimitError,ProviderTimeoutError,ProviderConnectionError,ProviderInternalError. Non-retryable errors raise immediately without retry.RetryExhaustedErrorsignals that all retries failed -- the engine layer catches this to trigger fallback chains. Rate limiter respectsRateLimitError.retry_afterfrom providers -- automatically pauses future requests.Never use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, o...
Files:
src/synthorg/api/controllers/company.pysrc/synthorg/observability/events/versioning.pysrc/synthorg/api/state.pysrc/synthorg/persistence/sqlite/version_repo.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/persistence/protocol.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/__init__.py
src/**/*.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/api/controllers/company.pysrc/synthorg/observability/events/versioning.pysrc/synthorg/api/state.pysrc/synthorg/persistence/sqlite/version_repo.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/persistence/protocol.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/__init__.py
web/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx,js,jsx}: Always usecreateLoggerfrom@/lib/logger-- never bareconsole.warn/console.error/console.debugin application code
Logger variable name must always beconst log(e.g.const log = createLogger('module-name'))
Pass dynamic/untrusted values as separate arguments to logger methods (not interpolated into the message string) so they go throughsanitizeArg
Attacker-controlled fields inside structured objects must be wrapped insanitizeForLog()before embedding in log calls
Files:
web/src/pages/workflow-editor/VersionHistoryPanel.tsxweb/src/api/types.ts
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground,bg-card,text-accent,text-success,bg-danger, etc.) or CSS variables (var(--so-*)) for colors; NEVER hardcode hex values in.tsx/.tsfiles
Usefont-sansorfont-mono(Geist tokens) for typography; NEVER setfontFamilydirectly in.tsx/.tsfiles
Use density-aware tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind spacing; NEVER hardcode pixel values for layout spacing in components
Use token variables (var(--so-shadow-card-hover),border-border,border-bright) for shadows and borders; NEVER hardcode values in.tsx/.tsfiles
Use@/lib/motionpresets for Framer Motion transition durations; NEVER hardcode transition durations
CSS side-effect imports in TypeScript 6 require type declarations -- add/// <reference types="vite/client" />at the top of files with CSS importsAlways reuse existing components from
web/src/components/ui/before creating new ones. Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions -- use design tokens and@/lib/motionpresets. A PostToolUse hook (scripts/check_web_design_system.py) enforces these rules on every Edit/Write toweb/src/.Follow the full component inventory, design token rules, and post-training references in
web/CLAUDE.md(TS6, Storybook 10).
Files:
web/src/pages/workflow-editor/VersionHistoryPanel.tsxweb/src/api/types.ts
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Always read the relevant
docs/design/page before implementing any feature or planning any issue. The design spec is the starting point for architecture, data models, and behavior. When a spec topic is referenced, read the relevantdocs/design/page before coding.
Files:
CLAUDE.mddocs/design/engine.md
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Mark tests with
@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, or@pytest.mark.slow.Maintain 80% code coverage minimum (enforced in CI).
Use
asyncio_mode = "auto"for async tests -- no manual@pytest.mark.asyncioneeded.Timeout is 30 seconds per test (global in
pyproject.toml) -- do not add per-filepytest.mark.timeout(30)markers. Non-default overrides liketimeout(60)are allowed.Always use
-n 8with pytest for parallelism viapytest-xdist, never run tests sequentially. CI uses-n auto(fewer cores on runners).Prefer
@pytest.mark.parametrizefor testing similar cases.Use
test-provider,test-small-001, etc. in tests instead of real vendor names.Use Hypothesis (
@given+@settings) for property-based testing in Python. Hypothesis profiles:ci(deterministic,max_examples=10+derandomize=True),dev(1000 examples),fuzz(10,000 examples, no deadline),extreme(500,000 examples, no deadline). Control viaHYPOTHESIS_PROFILEenv var. Failing examples are persisted to~/.synthorg/hypothesis-examples/. When Hypothesis finds a failure, fix the underlying bug and add an@example(...)decorator to permanently cover the case in CI.
Files:
tests/unit/api/controllers/test_workflow_versions.pytests/unit/persistence/sqlite/test_migrations.pytests/unit/api/fakes_backend.pytests/unit/engine/workflow/test_diff.pytests/unit/api/controllers/test_config_version_controllers.pytests/unit/persistence/sqlite/test_entity_version_repos.pytests/unit/persistence/test_protocol.pytests/unit/api/fakes_workflow.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/api/controllers/test_workflow_versions.pytests/unit/persistence/sqlite/test_migrations.pytests/unit/api/fakes_backend.pytests/unit/engine/workflow/test_diff.pytests/unit/api/controllers/test_config_version_controllers.pytests/unit/persistence/sqlite/test_entity_version_repos.pytests/unit/persistence/test_protocol.pytests/unit/api/fakes_workflow.py
🧠 Learnings (44)
📓 Common learnings
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:36:20.083Z
Learning: If implementation deviates from the spec (better approach found, scope evolved, etc.), alert the user and explain why -- user decides whether to proceed or update the spec. Do NOT silently diverge -- every deviation needs explicit user approval.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:36:20.083Z
Learning: When approved deviations occur, update the relevant `docs/design/` page to reflect the new reality.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:36:20.083Z
Learning: Every implementation plan must be presented to the user for accept/deny before coding starts. At every phase of planning and implementation, be critical -- actively look for ways to improve the design in the spirit of what we're building (robustness, correctness, simplicity, future-proofing where it's free).
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:36:20.083Z
Learning: Prioritize issues by dependency order, not priority labels -- unblocked dependencies come first.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:36:20.083Z
Learning: Pure data models, enums, and re-exports do NOT need logging.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:36:20.083Z
Learning: 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. For tasks that must block indefinitely until cancelled, use `asyncio.Event().wait()` instead of `asyncio.sleep(large_number)` -- it is cancellation-safe and carries no timing assumptions.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:36:20.083Z
Learning: Commits: `<type>: <description>` format (types: feat, fix, refactor, docs, test, chore, perf, ci). Enforced by commitizen (commit-msg hook). Signed commits required on `main` via branch protection -- all commits must be GPG/SSH signed.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:36:20.083Z
Learning: Branch naming: `<type>/<slug>` from main.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:36:20.083Z
Learning: Pre-commit hooks enforce: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint (Dockerfile linting), golangci-lint + go vet (CLI, conditional on `cli/**/*.go`), no-em-dashes, no-redundant-timeout, eslint-web (web dashboard, zero warnings, conditional on `web/src/**/*.{ts,tsx}`).
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:36:20.083Z
Learning: Pre-push hooks: mypy type-check (affected modules only) + pytest unit tests (affected modules only) + golangci-lint + go vet + go test (CLI, conditional on `cli/**/*.go`) + eslint-web (web dashboard) (fast gate before push, skipped in pre-commit.ci -- dedicated CI jobs already run these). Foundational module changes (core, config, observability) or conftest changes trigger full runs.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:36:20.083Z
Learning: Never create a PR directly with `gh pr create` -- it is blocked by hookify. Always use `/pre-pr-review` to create PRs -- it runs automated checks + review agents + fixes before creating the PR. For trivial/docs-only changes: `/pre-pr-review quick` skips agents but still runs automated checks.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:36:20.083Z
Learning: After finishing an issue implementation, always create a feature branch (`<type>/<slug>`), commit, and push -- do NOT create a PR automatically. Do NOT leave work uncommitted on main -- branch, commit, push immediately after finishing.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:36:20.083Z
Learning: Fix everything valid found during review -- never skip. When review agents find valid issues (including pre-existing issues in surrounding code, suggestions, and findings adjacent to the PR's changes), fix them all. No deferring, no 'out of scope' skipping.
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/api/**/*.py : API package (api/): Litestar REST + WebSocket with controllers, guards, channels, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint, provider management endpoint (CRUD + test + presets), backup endpoint, RFC 9457 structured errors, AppState hot-reload slots, service auto-wiring (Phase 1 at construction, Phase 2 on startup), lifecycle helpers
Applied to files:
src/synthorg/api/controllers/company.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/api/controllers/__init__.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/api/**/*.py : REST API: Litestar framework, controllers with guards, channels for WebSocket, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. RFC 9457 structured errors (ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation).
Applied to files:
src/synthorg/api/controllers/company.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/controllers/workflow_versions.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/controllers/company.pysrc/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/__init__.py
📚 Learning: 2026-04-06T16:35:12.934Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-06T16:35:12.934Z
Learning: All project conventions, commands, and standards are defined in CLAUDE.md - refer to it for project structure, package layout, code conventions, quick commands, git workflow, testing standards, design specifications, logging, resilience, and security patterns
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-31T16:09:24.320Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T16:09:24.320Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from `synthorg.observability.events.<domain>` modules (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly and use in structured logging
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions. Pure data models, enums, and re-exports do NOT need logging.
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-04-02T12:07:44.443Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T12:07:44.443Z
Learning: Applies to src/synthorg/**/*.py : Always use structured logging: `logger.info(EVENT, key=value)` — never `logger.info('msg %s', val)`
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions.
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/budget_config_versions.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 : Use structured logging: always `logger.info(EVENT, key=value)` — never `logger.info("msg %s", val)`
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Structured kwargs in logging: always `logger.info(EVENT, key=value)` — never `logger.info('msg %s', val)`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger` (not `_logger`, not `log`).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, and key function entry/exit
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-31T20:07:03.035Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T20:07:03.035Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising; all state transitions must log at INFO; DEBUG for object creation, internal flow, entry/exit of key functions
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (via `model_copy(update=...)`) for runtime state that evolves
Applied to files:
src/synthorg/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 : Use frozen Pydantic models for config/identity; separate mutable-via-copy models (using `model_copy(update=...)`) for runtime state
Applied to files:
src/synthorg/api/state.py
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Update the relevant `docs/design/` page to reflect new reality when approved deviations from spec occur
Applied to files:
docs/design/engine.md
📚 Learning: 2026-03-18T08:23:08.912Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T08:23:08.912Z
Learning: When approved deviations occur, update the relevant `docs/design/` page to reflect the new reality.
Applied to files:
docs/design/engine.md
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: Applies to docs/design/*.md : Update the relevant `docs/design/` page when approved deviations occur to reflect the new reality
Applied to files:
docs/design/engine.md
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to docs/design/**/*.md : Design specification pages in `docs/design/` must be consulted before implementing features (7 pages: index, agents, organization, communication, engine, memory, operations)
Applied to files:
docs/design/engine.md
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/persistence/**/*.py : Persistence uses pluggable PersistenceBackend protocol. SQLite is the initial backend. Settings use SettingsRepository (namespaced settings CRUD).
Applied to files:
src/synthorg/persistence/protocol.pytests/unit/persistence/sqlite/test_entity_version_repos.pysrc/synthorg/persistence/sqlite/backend.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Persistence backend: pluggable PersistenceBackend protocol in `src/synthorg/persistence/`, SQLite initial, SettingsRepository (namespaced settings CRUD).
Applied to files:
src/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/backend.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/persistence/protocol.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/api/controllers/__init__.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/persistence/protocol.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:
src/synthorg/persistence/protocol.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/persistence/protocol.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/persistence/protocol.py
📚 Learning: 2026-04-08T20:20:05.318Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-08T20:20:05.318Z
Learning: Applies to src/synthorg/**/*.py : If implementation deviates from the design spec (better approach found, scope evolved, etc.), alert the user and explain why before proceeding; do NOT silently diverge
Applied to files:
src/synthorg/api/services/org_mutations.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/services/org_mutations.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/api/services/org_mutations.pysrc/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately.
Applied to files:
src/synthorg/api/services/org_mutations.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/synthorg/api/**/*.py : Litestar API must include setup wizard, auth/, auto-wiring, and lifecycle management
Applied to files:
src/synthorg/api/controllers/workflow_versions.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/api/**/*.py : Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
Applied to files:
src/synthorg/api/controllers/workflow_versions.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : All state transitions must log at INFO.
Applied to files:
src/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : All error paths must log at WARNING or ERROR with context before raising.
Applied to files:
src/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use DEBUG logging for object creation, internal flow, and entry/exit of key functions.
Applied to files:
src/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : All state transitions must log at INFO level
Applied to files:
src/synthorg/api/controllers/budget_config_versions.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/persistence/sqlite/backend.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/persistence/sqlite/backend.py
📚 Learning: 2026-04-02T07:18:02.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T07:18:02.381Z
Learning: Applies to {pyproject.toml,src/synthorg/__init__.py} : Update version in `pyproject.toml` (`[tool.commitizen].version`) and `src/synthorg/__init__.py` (`__version__`)
Applied to files:
src/synthorg/api/controllers/__init__.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/controllers/__init__.py
🔇 Additional comments (35)
src/synthorg/observability/events/versioning.py (1)
35-36: Version-not-found event constant is correctly introduced.Line 35 and Line 36 follow the established versioning event naming and documentation pattern.
src/synthorg/persistence/sqlite/version_repo.py (1)
186-188:save_version()error contract is now accurately documented.Line 187 and Line 188 correctly describe the
QueryErrorraise conditions.src/synthorg/api/controllers/company.py (1)
14-14: Actor attribution is correctly propagated in company updates.Line 103 now forwards authenticated user identity, which keeps version snapshots attributable to the caller.
Also applies to: 103-103
web/src/pages/workflow-editor/VersionHistoryPanel.tsx (1)
27-27: Version title binding is aligned with the updated API payload.Line 27 correctly reads from
v.snapshot.name.CLAUDE.md (1)
93-93: Logging conventions doc is updated consistently with new versioning events.Line 93 now includes
VERSION_LISTEDandVERSION_NOT_FOUND, keeping guidance current.src/synthorg/api/state.py (1)
204-208: OrgMutationService wiring is now consistent across both construction paths.Passing
budget_config_versionsin both locations avoids startup/deferred-init behavior drift.Also applies to: 845-849
docs/design/engine.md (1)
228-231: Version-history design section now matches the implemented generic snapshot model.The updated schema and behavior notes are accurate and complete for the new flow.
tests/unit/api/controllers/test_workflow_versions.py (1)
146-147: Assertions are correctly updated to the nested snapshot response format.These changes align test expectations with the VersionSnapshot-based API payload.
Also applies to: 194-194, 284-284
src/synthorg/engine/workflow/diff.py (2)
195-206: LGTM - Snapshot ID consistency check correctly implemented.This addresses the past review feedback by adding a secondary validation that
old.snapshot.id == new.snapshot.id, preventing diffs when the payload's internal ID doesn't match the entity context. The error message is descriptive and includes both snapshot IDs for debugging.
167-221: LGTM - compute_diff correctly migrated to VersionSnapshot.The function signature and implementation properly:
- Accept
VersionSnapshot[WorkflowDefinition]for both parameters- Validate entity-level and snapshot-level ID consistency
- Extract
old.snapshot/new.snapshotfor the inner diff operations- Populate
WorkflowDiff.definition_idfromold.entity_idsrc/synthorg/api/controllers/workflows.py (2)
421-435: LGTM - Correct migration to VersioningService.The snapshot creation now correctly routes through
VersioningService.snapshot_if_changed(), which handles content-hash deduplication and concurrent-write conflict resolution. Ignoring the return value is intentional per the design - versioning is fire-and-forget, and failures are logged but don't block the primary workflow creation.
614-617: LGTM - Correct method rename for generic version repository.The method rename from
delete_versions_for_definitiontodelete_versions_for_entityaligns with the genericVersionRepository[T]interface that now handles multiple entity types.tests/unit/persistence/sqlite/test_migrations.py (1)
45-48: LGTM - Schema expectations correctly updated.The new version tables and their corresponding indexes follow the established pattern from the existing
workflow_definition_versionsandagent_identity_versionstables. Each table has the expected(entity_id, saved_at DESC)and(entity_id, content_hash)indexes for efficient version listing and content-hash deduplication.Also applies to: 108-109, 124-131
tests/unit/engine/workflow/test_diff.py (1)
56-89: LGTM - Test helper correctly migrated to VersionSnapshot.The
_ver()helper properly constructsVersionSnapshot[WorkflowDefinition]by:
- Building the inner
WorkflowDefinitionwith correct field mapping- Computing
content_hashviacompute_content_hash()- Ensuring
definition.idmatchesentity_idto satisfy the new snapshot consistency check incompute_diff()web/src/api/types.ts (1)
1781-1796: LGTM - Type correctly aligned with backend VersionSnapshot model.The restructured type properly separates:
- Version metadata at top level:
entity_id,version,content_hash,saved_by,saved_at- Entity payload in nested
snapshot: workflow-specific fields (id,name,description, etc.)This matches the backend
VersionSnapshot[WorkflowDefinition]structure and the frontend usage inVersionHistoryPanel.tsxwhich correctly accessesv.snapshot.name.tests/unit/api/fakes_backend.py (1)
236-239: LGTM - Fake backend correctly extended for new version repositories.The additions follow the established pattern from
identity_versions, ensuring test coverage for the newEvaluationConfig,BudgetConfig,Company, andRoleversioning surfaces.Also applies to: 375-389
src/synthorg/api/controllers/__init__.py (1)
99-102: LGTM - New version controllers correctly registered.The four new version history controllers are properly:
- Imported from their respective modules
- Added to
ALL_CONTROLLERStuple for Litestar registration- Included in
__all__for public exportAlso applies to: 119-119, 124-124, 129-129, 140-140
src/synthorg/persistence/protocol.py (1)
263-295: LGTM - Protocol correctly extended with generic version repositories.The migration from
WorkflowVersionRepositorytoVersionRepository[WorkflowDefinition]and the addition of four new typed version repository properties follow the established pattern. SQLiteBackend correctly implements all five version repository properties with proper return type annotations (SQLiteVersionRepository[T]) and docstrings.src/synthorg/api/services/org_mutations.py (2)
91-114: LGTM! Exception handling correctly addresses both failure modes.The
_snapshot_budget_confighelper now catches bothPersistenceError(which includesQueryErrorper the exception hierarchy) andSettingNotFoundErrorfrom the resolver. This implements proper best-effort semantics where versioning failures are logged but don't block mutations.
287-288: Budget snapshot trigger looks correct.The conditional snapshot is appropriately placed inside the lock after the budget value is persisted, ensuring consistency between the settings write and the version snapshot.
src/synthorg/api/controllers/role_versions.py (1)
1-92: LGTM! Controller follows the established version-history pattern.The
RoleVersionControllercorrectly implements per-role versioning with:
- Proper guards (
require_read_access)- Concurrent fetching via
asyncio.gatherfor list endpoint- Appropriate logging at debug/warning levels
- Parameter validation (
ge=1) for version numberstests/unit/persistence/test_protocol.py (1)
577-591: LGTM! Fake backend extended with new version repository properties.The new properties follow the established pattern of returning
object()to satisfy the@runtime_checkableprotocol conformance test, consistent with other version repository properties.src/synthorg/api/controllers/evaluation_config_versions.py (1)
1-93: LGTM! Consistent implementation of evaluation config version history.The controller follows the established pattern with proper logging, guards, pagination, and error handling matching the other version controllers in this PR.
src/synthorg/api/controllers/company_versions.py (1)
1-93: LGTM! Consistent implementation of company version history.The controller correctly implements the version-history pattern with singleton
"default"entity ID for single-tenant company structure versioning.tests/unit/api/controllers/test_config_version_controllers.py (1)
25-37: LGTM! Clean helper function using modern Python generics.The
_snaphelper uses Python 3.12+ generic syntax[T: BaseModel]and correctly constructs deterministicVersionSnapshotinstances for testing.src/synthorg/api/controllers/budget_config_versions.py (1)
1-93: LGTM! Budget config version controller with proper logging.The controller correctly implements version history endpoints with structured logging using
VERSION_LISTEDandVERSION_NOT_FOUNDevent constants, addressing the previous review feedback.src/synthorg/api/controllers/workflow_versions.py (2)
181-189: LGTM! Clean model restoration from snapshot.Using
target.snapshot.model_copy(update={...})elegantly restores the workflow content while preserving identity fields (id,created_by,created_at) and bumping the version.
364-376: Content-hash deduplication for rollback is intentional design.Per
docs/design/engine.md, the versioning system uses content-addressable snapshots whereVersioningService.snapshot_if_changed()intentionally skips creating new records when content is unchanged. Rolling back to content matching the current version produces no new version record—this is documented as the intended behavior, not a gap. The rollback action remains logged at INFO level; version history tracks unique content states, not mutation actions.src/synthorg/persistence/sqlite/backend.py (2)
277-317: LGTM! Factory helper pattern cleanly eliminates duplication.The
_ver_repohelper addresses the previous review feedback by consolidating the serialize/deserialize boilerplate. Each lambda correctly captures its ownmodel_clsfrom the function parameter scope.
653-707: Property accessors follow established patterns.The new version repository properties correctly use
_require_connected()and maintain consistency with existing accessors. Documentation follows the same style.tests/unit/persistence/sqlite/test_entity_version_repos.py (3)
21-33: Test schema template is sufficient for unit testing.The template correctly mirrors production column structure, constraints, and primary key. The omitted indexes (
idx_*_entity_saved,idx_*_content_hash) don't affect correctness—they're query optimizations. Unit tests validating repository logic don't require indexes.
73-127: Comprehensive workflow round-trip test.Test validates nested structures (nodes, edges) survive JSON serialization and content hash is preserved. Good coverage of WorkflowDefinition complexity.
208-232: Idempotency test correctly validates INSERT OR IGNORE semantics.Verifies that duplicate
(entity_id, version)returnsFalsewhile same content with different version succeeds. Good edge case coverage.tests/unit/api/fakes_workflow.py (1)
112-186: Protocol implementation is complete and correct.All seven
VersionRepository[WorkflowDefinition]protocol methods are implemented. The storage migration from(definition_id, version)to(entity_id, version)keys aligns with the new generic snapshot model. Proper use ofcopy.deepcopy()maintains immutability guarantees.src/synthorg/persistence/sqlite/schema.sql (1)
612-686: New version tables follow consistent schema pattern.All four tables (evaluation_config_versions, budget_config_versions, company_versions, role_versions) correctly replicate the established
VersionSnapshot[T]schema with:
- Composite primary key
(entity_id, version)- Non-empty CHECK constraints on text fields
- UTC timestamp validation on
saved_at- Appropriate indexes for query patterns
CREATE TABLE IF NOT EXISTSensures idempotent creation on both fresh installs and upgrades.
| -- ── Workflow Definition Versions ───────────────────────────── | ||
| -- Breaking change from v0.6.x: migrated from bespoke flattened | ||
| -- columns (definition_id, name, description, workflow_type, nodes, | ||
| -- edges, created_by) to generic VersionSnapshot format (entity_id, | ||
| -- content_hash, snapshot JSON, saved_by, saved_at). | ||
| -- Manual migration for existing databases: | ||
| -- 1. ALTER TABLE workflow_definition_versions RENAME TO _wdv_old; | ||
| -- 2. Create the new table (below) and indexes. | ||
| -- 3. INSERT INTO workflow_definition_versions (entity_id, version, | ||
| -- content_hash, snapshot, saved_by, saved_at) | ||
| -- SELECT definition_id, version, | ||
| -- '<compute-sha256-of-snapshot>', json_object(...old columns...), | ||
| -- created_by, saved_at FROM _wdv_old; | ||
| -- 4. DROP TABLE _wdv_old; | ||
| -- FK to workflow_definitions intentionally dropped for generic | ||
| -- VersionSnapshot[T] pattern consistency across all entity types. | ||
|
|
||
| CREATE TABLE IF NOT EXISTS workflow_definition_versions ( | ||
| definition_id TEXT NOT NULL CHECK(length(definition_id) > 0), | ||
| entity_id TEXT NOT NULL CHECK(length(entity_id) > 0), | ||
| version INTEGER NOT NULL CHECK(version >= 1), | ||
| name TEXT NOT NULL CHECK(length(name) > 0), | ||
| description TEXT NOT NULL DEFAULT '', | ||
| workflow_type TEXT NOT NULL CHECK(workflow_type IN ( | ||
| 'sequential_pipeline', 'parallel_execution', 'kanban', 'agile_kanban' | ||
| )), | ||
| nodes TEXT NOT NULL, | ||
| edges TEXT NOT NULL, | ||
| created_by TEXT NOT NULL CHECK(length(created_by) > 0), | ||
| content_hash TEXT NOT NULL CHECK(length(content_hash) > 0), | ||
| snapshot TEXT NOT NULL CHECK(length(snapshot) > 0), | ||
| saved_by TEXT NOT NULL CHECK(length(saved_by) > 0), | ||
| saved_at TEXT NOT NULL, | ||
| PRIMARY KEY (definition_id, version), | ||
| FOREIGN KEY (definition_id) | ||
| REFERENCES workflow_definitions(id) | ||
| ON DELETE CASCADE | ||
| saved_at TEXT NOT NULL CHECK( | ||
| saved_at LIKE '%+00:00' OR saved_at LIKE '%Z' | ||
| ), | ||
| PRIMARY KEY (entity_id, version) | ||
| ); | ||
|
|
||
| CREATE INDEX IF NOT EXISTS idx_wdv_definition_saved | ||
| ON workflow_definition_versions(definition_id, saved_at DESC); | ||
| CREATE INDEX IF NOT EXISTS idx_wdv_entity_saved | ||
| ON workflow_definition_versions(entity_id, saved_at DESC); | ||
| CREATE INDEX IF NOT EXISTS idx_wdv_content_hash | ||
| ON workflow_definition_versions(entity_id, content_hash); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Migration guidance is clear but manual.
The inline comment (lines 438-451) documents the breaking schema change and provides manual migration steps. This aligns with the file header stating "When data stability is declared, adopt Atlas for declarative migrations."
For production deployments, ensure operators are aware that existing workflow_definition_versions data requires manual transformation before this schema can be applied—CREATE TABLE IF NOT EXISTS will silently skip creation if the old-schema table exists.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/persistence/sqlite/schema.sql` around lines 437 - 469, The
comment warns that the new CREATE TABLE IF NOT EXISTS for
workflow_definition_versions will silently do nothing if an old-schema table
exists; add an explicit safety/migration check and guidance: implement a startup
or migration script that checks for the legacy table name/columns (e.g.,
existence of _wdv_old or columns like definition_id, workflow_type), refuse to
apply the new schema or emit a clear error/warning and instructions if legacy
data is detected, and document or provide an automated migration path that
computes content_hash and populates the new workflow_definition_versions (and
then creates/validates idx_wdv_entity_saved and idx_wdv_content_hash) so
operators are prevented from accidentally running with incompatible schemas.
914a4b3 to
b04e59a
Compare
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/synthorg/api/controllers/workflows.py (1)
421-435: 🧹 Nitpick | 🔵 TrivialConsider caching the VersioningService instance.
The
VersioningServiceis instantiated fresh increate_from_blueprint,create_workflow, andupdate_workflow. While functionally correct, you could consider caching it onAppStateor creating a helper to reduce object allocation overhead. This is a minor optimization opportunity.♻️ Optional: Extract service creation to a helper or cache on AppState
# Option 1: Helper function at module level def _get_versioning_service( state: State, ) -> VersioningService[WorkflowDefinition]: return VersioningService(state.app_state.persistence.workflow_versions) # Option 2: Cache on AppState during app startup🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/controllers/workflows.py` around lines 421 - 435, Several call sites (create_from_blueprint, create_workflow, update_workflow) repeatedly instantiate VersioningService just to call snapshot_if_changed; extract and reuse a single VersioningService instance to avoid repeated allocations by either (a) adding a module-level helper function _get_versioning_service(state: State) -> VersioningService[WorkflowDefinition] that returns VersioningService(state.app_state.persistence.workflow_versions) and replace the direct instantiations with calls to _get_versioning_service, or (b) add a cached attribute on AppState (e.g., app_state.versioning_service or a factory stored at startup) and use that shared VersioningService in those functions; ensure calls to snapshot_if_changed remain unchanged and that the chosen cache is initialized once during app startup to preserve behavior.
🤖 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/services/org_mutations.py`:
- Around line 103-108: The snapshot is using ConfigResolver.get_budget_config()
which reads from the settings namespace `budget.total_monthly`, but
update_company() writes to `company.total_monthly`, so snapshotting via the
resolver can miss the just-written change; modify the _snapshot_budget_config()
call in src/synthorg/api/services/org_mutations.py (the block that calls
self._resolver.get_budget_config() and
self._budget_versioning.snapshot_if_changed) to build the BudgetConfig from the
updated company value and pass that constructed snapshot directly to
self._budget_versioning.snapshot_if_changed (or persist the change through the
same namespace ConfigResolver.get_budget_config() reads) so the snapshot
reflects the exact value written by update_company(); target symbols:
update_company(), _snapshot_budget_config(), ConfigResolver.get_budget_config(),
and budget_versioning.snapshot_if_changed.
In `@src/synthorg/api/state.py`:
- Around line 204-208: The OrgMutationService is only receiving
budget_config_versions and never the new company_versions dependency, so it
never emits company snapshots; update the OrgMutationService constructors (both
instantiation sites that pass budget_config_versions) to accept and pass a
company_versions repository parameter, store it on the service, and then call
the repository to write a company snapshot whenever company-level mutations
occur (e.g., in update_company and any other methods that currently call
_write_departments or _snapshot_budget_config). Ensure the stored field name
matches company_versions and invoke the appropriate write/snapshot method on
that repository alongside existing budget snapshot calls so company version
history is emitted.
In `@src/synthorg/persistence/sqlite/migrations.py`:
- Around line 74-75: The current call to _check_legacy_workflow_versions
silently logs and lets apply_schema() succeed even when the old
workflow_definition_versions schema exists; change this to fail fast: have
_check_legacy_workflow_versions raise a MigrationError (or re-raise a clear
exception) when it detects the pre-generic layout so startup stops rather than
letting SQLiteVersionRepository later fail, and remove the broad except that
hides real PRAGMA/DB errors—only handle the explicit "table does not exist" case
(or let unexpected sqlite exceptions propagate) so real PRAGMA failures surface
instead of being masked.
In `@tests/unit/api/fakes_workflow.py`:
- Around line 153-161: The fake get_by_content_hash implementation returns the
first insertion-order match from self._versions instead of the newest version
like SQLiteVersionRepository.get_by_content_hash; change get_by_content_hash to
collect all snapshots in self._versions whose entity_id and content_hash match
and return a deepcopy of the one with the highest version (e.g., max by the
VersionSnapshot.version attribute) so unit tests mirror the production ordering
and dedup/rollback behavior.
- Around line 163-175: The test fake list_versions function in
tests/unit/api/fakes_workflow.py currently slices when given negative
pagination, hiding invalid-pagination bugs; update the async function
list_versions to validate its parameters and raise ValueError if limit < 0 or
offset < 0 (same behavior as the production repo), before computing matching
from self._versions and returning the tuple of deep-copied
VersionSnapshot[WorkflowDefinition] results.
In `@web/src/api/types.ts`:
- Around line 1782-1797: The WorkflowDefinitionVersionSummary currently
duplicates a generic version envelope; extract a reusable generic alias like
VersionSummary<TSnapshot> (matching backend VersionSnapshot[T]) and replace
WorkflowDefinitionVersionSummary with VersionSummary<WorkflowDefinitionSnapshot>
where WorkflowDefinitionSnapshot is a narrow Pick of WorkflowDefinition fields
(id, name, description, workflow_type, nodes, edges, created_by) to avoid
duplicated envelope shape; update imports/exports and replace the inline
snapshot object type in WorkflowDefinitionVersionSummary with the new snapshot
type and the generic VersionSummary alias so other DTOs can reuse it.
---
Outside diff comments:
In `@src/synthorg/api/controllers/workflows.py`:
- Around line 421-435: Several call sites (create_from_blueprint,
create_workflow, update_workflow) repeatedly instantiate VersioningService just
to call snapshot_if_changed; extract and reuse a single VersioningService
instance to avoid repeated allocations by either (a) adding a module-level
helper function _get_versioning_service(state: State) ->
VersioningService[WorkflowDefinition] that returns
VersioningService(state.app_state.persistence.workflow_versions) and replace the
direct instantiations with calls to _get_versioning_service, or (b) add a cached
attribute on AppState (e.g., app_state.versioning_service or a factory stored at
startup) and use that shared VersioningService in those functions; ensure calls
to snapshot_if_changed remain unchanged and that the chosen cache is initialized
once during app startup to preserve behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 15d4e55f-69a4-42f9-9e7e-f12c5f849836
📒 Files selected for processing (35)
CLAUDE.mddocs/design/engine.mdsrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/_workflow_helpers.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/api/controllers/company.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/state.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/engine/workflow/version.pysrc/synthorg/observability/events/versioning.pysrc/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/persistence/sqlite/migrations.pysrc/synthorg/persistence/sqlite/schema.sqlsrc/synthorg/persistence/sqlite/version_repo.pysrc/synthorg/persistence/sqlite/workflow_version_repo.pysrc/synthorg/persistence/workflow_version_repo.pytests/unit/api/controllers/test_config_version_controllers.pytests/unit/api/controllers/test_workflow_versions.pytests/unit/api/fakes_backend.pytests/unit/api/fakes_workflow.pytests/unit/engine/workflow/test_diff.pytests/unit/engine/workflow/test_version.pytests/unit/persistence/sqlite/test_entity_version_repos.pytests/unit/persistence/sqlite/test_migrations.pytests/unit/persistence/sqlite/test_workflow_version_repo.pytests/unit/persistence/test_protocol.pyweb/src/api/types.tsweb/src/pages/workflow-editor/VersionHistoryPanel.tsx
💤 Files with no reviewable changes (6)
- tests/unit/engine/workflow/test_version.py
- src/synthorg/api/controllers/_workflow_helpers.py
- tests/unit/persistence/sqlite/test_workflow_version_repo.py
- src/synthorg/engine/workflow/version.py
- src/synthorg/persistence/sqlite/workflow_version_repo.py
- src/synthorg/persistence/workflow_version_repo.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Test (Python 3.14)
- GitHub Check: Dashboard Test
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
No
from __future__ import annotations— Python 3.14 has PEP 649 native lazy annotations.Use PEP 758 except syntax: use
except A, B:(no parentheses) — ruff enforces this on Python 3.14.Type hints: all public functions must have type hints. Use mypy strict mode.
Docstrings: Google style, required on public classes and functions. Enforced by ruff D rules.
Immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections, use
copy.deepcopy()at construction andMappingProxyTypewrapping for read-only enforcement.For
dict/listfields in frozen Pydantic models, rely onfrozen=Truefor field reassignment prevention andcopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).Config vs runtime state: use frozen Pydantic models for config/identity. Use separate mutable-via-copy models for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Models: Use Pydantic v2 (
BaseModel,model_validator,computed_field,ConfigDict). Useallow_inf_nan=Falsein allConfigDictdeclarations. Use@computed_fieldfor derived values instead of storing redundant fields. UseNotBlankStrfor all identifier/name fields.Async concurrency: prefer
asyncio.TaskGroupfor fan-out/fan-in parallel operations (e.g., multiple tool invocations, parallel agent calls). Prefer structured concurrency over barecreate_task.Line length: 88 characters (enforced by ruff).
Functions: less than 50 lines. Files: less than 800 lines.
Errors: handle explicitly, never silently swallow.
Validate at system boundaries (user input, external APIs, config files).
Variable name for logger: always
logger(not_logger, notlog).
Files:
src/synthorg/observability/events/versioning.pysrc/synthorg/persistence/sqlite/version_repo.pysrc/synthorg/api/controllers/company.pysrc/synthorg/api/state.pytests/unit/persistence/sqlite/test_migrations.pysrc/synthorg/api/controllers/workflows.pytests/unit/api/controllers/test_workflow_versions.pytests/unit/api/fakes_backend.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/persistence/sqlite/migrations.pytests/unit/persistence/test_protocol.pysrc/synthorg/persistence/protocol.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/api/controllers/company_versions.pytests/unit/api/controllers/test_config_version_controllers.pysrc/synthorg/api/controllers/budget_config_versions.pytests/unit/persistence/sqlite/test_entity_version_repos.pytests/unit/engine/workflow/test_diff.pytests/unit/api/fakes_workflow.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Every module with business logic must have:
from synthorg.observability import get_loggerthenlogger = get_logger(__name__).Never use
import logging/logging.getLogger()/print()in application code. Exception:observability/setup.py,observability/sinks.py,observability/syslog_handler.py,observability/http_handler.py, andobservability/otlp_handler.pymay use stdlibloggingandprint(..., file=sys.stderr)for handler construction.Event names: always use constants from domain-specific modules under
synthorg.observability.events. Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT.Structured kwargs for logging: always
logger.info(EVENT, key=value)— neverlogger.info("msg %s", val).All error paths must log at WARNING or ERROR with context before raising.
All state transitions must log at INFO.
DEBUG for object creation, internal flow, entry/exit of key functions. Pure data models, enums, and re-exports do not need logging.
Vendor-agnostic 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/smallas aliases.
Files:
src/synthorg/observability/events/versioning.pysrc/synthorg/persistence/sqlite/version_repo.pysrc/synthorg/api/controllers/company.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/persistence/sqlite/migrations.pysrc/synthorg/persistence/protocol.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/budget_config_versions.py
src/**/*.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/observability/events/versioning.pysrc/synthorg/persistence/sqlite/version_repo.pysrc/synthorg/api/controllers/company.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/persistence/sqlite/migrations.pysrc/synthorg/persistence/protocol.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/budget_config_versions.py
web/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx,js,jsx}: Always usecreateLoggerfrom@/lib/logger-- never bareconsole.warn/console.error/console.debugin application code
Logger variable name must always beconst log(e.g.const log = createLogger('module-name'))
Pass dynamic/untrusted values as separate arguments to logger methods (not interpolated into the message string) so they go throughsanitizeArg
Attacker-controlled fields inside structured objects must be wrapped insanitizeForLog()before embedding in log calls
Files:
web/src/pages/workflow-editor/VersionHistoryPanel.tsxweb/src/api/types.ts
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground,bg-card,text-accent,text-success,bg-danger, etc.) or CSS variables (var(--so-*)) for colors; NEVER hardcode hex values in.tsx/.tsfiles
Usefont-sansorfont-mono(Geist tokens) for typography; NEVER setfontFamilydirectly in.tsx/.tsfiles
Use density-aware tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind spacing; NEVER hardcode pixel values for layout spacing in components
Use token variables (var(--so-shadow-card-hover),border-border,border-bright) for shadows and borders; NEVER hardcode values in.tsx/.tsfiles
Use@/lib/motionpresets for Framer Motion transition durations; NEVER hardcode transition durations
CSS side-effect imports in TypeScript 6 require type declarations -- add/// <reference types="vite/client" />at the top of files with CSS importsAlways reuse existing components from
web/src/components/ui/before creating new ones in the web dashboard.
Files:
web/src/pages/workflow-editor/VersionHistoryPanel.tsxweb/src/api/types.ts
web/src/**/*.{ts,tsx,css}
📄 CodeRabbit inference engine (CLAUDE.md)
Never hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions in the web dashboard — use design tokens and
@/lib/motionpresets.
Files:
web/src/pages/workflow-editor/VersionHistoryPanel.tsxweb/src/api/types.ts
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Test markers:
@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slow.Async in tests:
asyncio_mode = "auto"— no manual@pytest.mark.asyncioneeded.Test timeout: 30 seconds per test (global in
pyproject.toml). Do not add per-filepytest.mark.timeout(30)markers; non-default overrides liketimeout(60)are allowed.Parametrize: Prefer
@pytest.mark.parametrizefor testing similar cases.Tests must use
test-provider,test-small-001, etc. instead of vendor-specific names.Property-based testing in Python: use Hypothesis (
@given+@settings). Profiles configured intests/conftest.py:ci(deterministic,max_examples=10+derandomize=True),dev(1000 examples),fuzz(10,000 examples, no deadline),extreme(500,000 examples, no deadline). Controlled viaHYPOTHESIS_PROFILEenv var.Never skip, dismiss, or ignore flaky tests — always fix them fully and fundamentally. For timing-sensitive tests, mock
time.monotonic()andasyncio.sleep(). For tasks that must block indefinitely, useasyncio.Event().wait()instead ofasyncio.sleep(large_number).
Files:
tests/unit/persistence/sqlite/test_migrations.pytests/unit/api/controllers/test_workflow_versions.pytests/unit/api/fakes_backend.pytests/unit/persistence/test_protocol.pytests/unit/api/controllers/test_config_version_controllers.pytests/unit/persistence/sqlite/test_entity_version_repos.pytests/unit/engine/workflow/test_diff.pytests/unit/api/fakes_workflow.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/persistence/sqlite/test_migrations.pytests/unit/api/controllers/test_workflow_versions.pytests/unit/api/fakes_backend.pytests/unit/persistence/test_protocol.pytests/unit/api/controllers/test_config_version_controllers.pytests/unit/persistence/sqlite/test_entity_version_repos.pytests/unit/engine/workflow/test_diff.pytests/unit/api/fakes_workflow.py
🧠 Learnings (47)
📓 Common learnings
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: Always read the relevant `docs/design/` page before implementing any feature or planning any issue. DESIGN_SPEC.md is a pointer file linking to the 12 design pages.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: When implementation deviates from the design spec, alert the user and explain why. Do not silently diverge. Every deviation needs explicit user approval.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: When approved deviations occur, update the relevant `docs/design/` page to reflect the new reality.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: Every implementation plan must be presented to the user for accept/deny before coding starts.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: At every phase of planning and implementation, be critical and actively look for ways to improve the design. Surface improvements as suggestions, not silent changes.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: Prioritize issues by dependency order, not priority labels. Unblocked dependencies come first.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: Coverage: 80% minimum (enforced in CI).
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: Parallelism: `pytest-xdist` via `-n 8` — always include `-n 8` when running pytest locally, never run tests sequentially. CI uses `-n auto`.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: CI runs 10 deterministic examples per property test (`derandomize=True`). Random fuzzing runs locally: `HYPOTHESIS_PROFILE=dev` (1000 examples) or `HYPOTHESIS_PROFILE=fuzz` (10,000 examples, no deadline).
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: When Hypothesis finds a failure, it is a real bug. Add an explicit `example(...)` decorator to the test so the case is permanently covered in CI. Do not just rerun and move on.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: Commits: `<type>: <description>` — types: feat, fix, refactor, docs, test, chore, perf, ci. Enforced by commitizen (commit-msg hook).
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: Signed commits: required on `main` via branch protection — all commits must be GPG/SSH signed.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: Branches: `<type>/<slug>` from main.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: Pre-commit hooks: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint, golangci-lint + go vet (CLI), no-em-dashes, no-redundant-timeout, eslint-web (web dashboard).
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: Hookify rules (committed in `.claude/hookify.*.md`): `block-pr-create` blocks direct `gh pr create`, `enforce-parallel-tests` enforces `-n 8` with pytest, `no-cd-prefix` blocks `cd` prefix in Bash commands, `no-local-coverage` blocks `--cov` flags locally.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: Pre-push hooks: mypy type-check (affected modules) + pytest unit tests (affected modules) + golangci-lint + go vet + go test (CLI) + eslint-web (web dashboard). Foundational module changes (core, config, observability) or conftest changes trigger full runs.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: Pre-commit.ci: autoupdate disabled. Dependabot owns hook version bumps via `pre-commit` ecosystem.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: GitHub issue queries: use `gh issue list` via Bash — MCP `list_issues` has unreliable field data.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: Merge strategy: squash merge — PR body becomes the squash commit message on main. Trailers (e.g. `Release-As`, `Closes `#N``) must be in the PR body to land in the final commit.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: PR issue references: preserve existing `Closes `#NNN`` references — never remove unless explicitly asked.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: After finishing an issue implementation, always create a feature branch (`<type>/<slug>`), commit, and push — do not create a PR automatically. Do not leave work uncommitted on main.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: Never create a PR directly — `gh pr create` is blocked by hookify. Always use `/pre-pr-review` to create PRs. For trivial/docs-only changes: `/pre-pr-review quick` skips agents but still runs automated checks.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: Never use `cd` in Bash commands — the working directory is already set to the project root. Use absolute paths or run commands directly. Exception: `bash -c "cd <dir> && <cmd>"` is safe (runs in a child process). Use this for tools without a `-C` flag.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: Never use Bash to write or modify files — use the Write or Edit tools. Do not use `cat >`, `cat << EOF`, `echo >`, `echo >>`, `sed -i`, `python -c "open(...).write(...)"`, or `tee` to create or modify files.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: A PostToolUse hook (`scripts/check_web_design_system.py`) enforces web design system rules on every Edit/Write to `web/src/`.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: See `web/CLAUDE.md` for web dashboard commands, design system, and component inventory. See `cli/CLAUDE.md` for CLI commands, flags, and reference. Key rule: use `go -C cli` (never `cd cli`).
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T20:53:52.443Z
Learning: Fix everything valid found during review — never skip or defer. When review agents find valid issues (including pre-existing issues in surrounding code, suggestions, and findings adjacent to the PR's changes), fix them all.
📚 Learning: 2026-04-06T16:35:12.934Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-06T16:35:12.934Z
Learning: All project conventions, commands, and standards are defined in CLAUDE.md - refer to it for project structure, package layout, code conventions, quick commands, git workflow, testing standards, design specifications, logging, resilience, and security patterns
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions. Pure data models, enums, and re-exports do NOT need logging.
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-04-02T12:07:44.443Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T12:07:44.443Z
Learning: Applies to src/synthorg/**/*.py : Always use structured logging: `logger.info(EVENT, key=value)` — never `logger.info('msg %s', val)`
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions.
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/budget_config_versions.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:
CLAUDE.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Use structured logging: always `logger.info(EVENT, key=value)` — never `logger.info("msg %s", val)`
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Structured kwargs in logging: always `logger.info(EVENT, key=value)` — never `logger.info('msg %s', val)`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, and key function entry/exit
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/budget_config_versions.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 : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger` (not `_logger`, not `log`).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-31T20:07:03.035Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T20:07:03.035Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising; all state transitions must log at INFO; DEBUG for object creation, internal flow, entry/exit of key functions
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/api/**/*.py : API package (api/): Litestar REST + WebSocket with controllers, guards, channels, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint, provider management endpoint (CRUD + test + presets), backup endpoint, RFC 9457 structured errors, AppState hot-reload slots, service auto-wiring (Phase 1 at construction, Phase 2 on startup), lifecycle helpers
Applied to files:
src/synthorg/api/controllers/company.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/api/**/*.py : REST API: Litestar framework, controllers with guards, channels for WebSocket, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. RFC 9457 structured errors (ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation).
Applied to files:
src/synthorg/api/controllers/company.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/api/controllers/company_versions.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/controllers/company.pysrc/synthorg/persistence/protocol.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/company_versions.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (via `model_copy(update=...)`) for runtime state that evolves
Applied to files:
src/synthorg/api/state.py
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Update the relevant `docs/design/` page to reflect new reality when approved deviations from spec occur
Applied to files:
docs/design/engine.md
📚 Learning: 2026-03-18T08:23:08.912Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T08:23:08.912Z
Learning: When approved deviations occur, update the relevant `docs/design/` page to reflect the new reality.
Applied to files:
docs/design/engine.md
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: Applies to docs/design/*.md : Update the relevant `docs/design/` page when approved deviations occur to reflect the new reality
Applied to files:
docs/design/engine.md
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to docs/design/**/*.md : Design specification pages in `docs/design/` must be consulted before implementing features (7 pages: index, agents, organization, communication, engine, memory, operations)
Applied to files:
docs/design/engine.md
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/persistence/**/*.py : Persistence uses pluggable PersistenceBackend protocol. SQLite is the initial backend. Settings use SettingsRepository (namespaced settings CRUD).
Applied to files:
src/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/backend.pytests/unit/persistence/sqlite/test_entity_version_repos.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Persistence backend: pluggable PersistenceBackend protocol in `src/synthorg/persistence/`, SQLite initial, SettingsRepository (namespaced settings CRUD).
Applied to files:
src/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/backend.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/persistence/protocol.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/budget_config_versions.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/persistence/protocol.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:
src/synthorg/persistence/protocol.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/persistence/protocol.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/persistence/protocol.py
📚 Learning: 2026-04-08T20:20:05.318Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-08T20:20:05.318Z
Learning: Applies to src/synthorg/**/*.py : If implementation deviates from the design spec (better approach found, scope evolved, etc.), alert the user and explain why before proceeding; do NOT silently diverge
Applied to files:
src/synthorg/api/services/org_mutations.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/services/org_mutations.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/api/services/org_mutations.pysrc/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately.
Applied to files:
src/synthorg/api/services/org_mutations.py
📚 Learning: 2026-04-02T07:18:02.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T07:18:02.381Z
Learning: Applies to {pyproject.toml,src/synthorg/__init__.py} : Update version in `pyproject.toml` (`[tool.commitizen].version`) and `src/synthorg/__init__.py` (`__version__`)
Applied to files:
src/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/workflow_versions.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/controllers/__init__.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/synthorg/api/**/*.py : Litestar API must include setup wizard, auth/, auto-wiring, and lifecycle management
Applied to files:
src/synthorg/api/controllers/workflow_versions.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/api/**/*.py : Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
Applied to files:
src/synthorg/api/controllers/workflow_versions.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/persistence/sqlite/backend.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/persistence/sqlite/backend.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : All state transitions must log at INFO.
Applied to files:
src/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : All error paths must log at WARNING or ERROR with context before raising.
Applied to files:
src/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use DEBUG logging for object creation, internal flow, and entry/exit of key functions.
Applied to files:
src/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : All state transitions must log at INFO level
Applied to files:
src/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use `NotBlankStr` from `core.types` for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Applied to files:
tests/unit/api/fakes_workflow.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:
tests/unit/api/fakes_workflow.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 with adopted conventions: use computed_field for derived values instead of storing + validating redundant fields; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.
Applied to files:
tests/unit/api/fakes_workflow.py
📚 Learning: 2026-03-31T20:07:03.035Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T20:07:03.035Z
Learning: Applies to **/*.py : Use `NotBlankStr` (from `core.types`) for all identifier/name fields, including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants, instead of manual whitespace validators
Applied to files:
tests/unit/api/fakes_workflow.py
📚 Learning: 2026-04-08T20:20:05.318Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-08T20:20:05.318Z
Learning: Applies to **/*.py : Use `NotBlankStr` from `core.types` for all identifier/name fields (including optional `NotBlankStr | None` and tuple variants `tuple[NotBlankStr, ...]`) instead of manual whitespace validators
Applied to files:
tests/unit/api/fakes_workflow.py
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
.github/workflows/docker.yml (1)
225-237:⚠️ Potential issue | 🔴 CriticalEnable
pipefailbefore pipingcosignintotee.At lines 230, 432, and 634, the pipeline
if ! cosign ... | tee /tmp/cosign-out; thenonly capturescosign's exit code ifpipefailis enabled. These steps do not setshell: bash; GitHub Actions defaults tobash -e {0}(errexit enabled, pipefail disabled). Ifcosign signfails but writes output,teeexits 0, causing the pipeline to succeed and bypassing the error handler. This allows unsigned backend, web, and sandbox images to be published without detection.Suggested fix
Apply the same change to all three
Sign imagesteps:run: | + set -o pipefail if [ -z "$DIGEST" ]; then echo "::error::Push step did not produce a digest -- cannot sign image" exit 1 fi if ! cosign sign --yes ghcr.io/aureliolo/synthorg-backend@${DIGEST} 2>&1 | tee /tmp/cosign-out; then if grep -q 'createLogEntryConflict' /tmp/cosign-out; then echo "::notice::Image already signed in transparency log -- skipping" else cat /tmp/cosign-out exit 1 fi fiAlso applies to: lines 427–439 (web), 629–641 (sandbox).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/docker.yml around lines 225 - 237, The cosign signing pipeline (the command using DIGEST, cosign sign and piping to tee writing /tmp/cosign-out) can hide cosign failures because pipefail isn’t enabled; before running the pipeline in each "Sign image" step (backend, web, sandbox) enable pipefail (e.g., run "set -o pipefail" or prefix the shell command to enable pipefail) so the pipeline returns cosign’s exit code instead of tee’s, ensuring the if ! ... | tee /tmp/cosign-out check correctly detects failures.src/synthorg/api/services/org_mutations.py (1)
334-366:⚠️ Potential issue | 🟠 MajorDon't hardcode
saved_by="api"for department-driven snapshots.These writes now populate
company_versions, but every department mutation records the actor as"api". That drops the authenticated user from the audit trail even though the controllers already have the request andupdate_company()threads through a realsaved_byvalue. Please add asaved_byparameter to these methods and pass it through to_snapshot_company().Suggested direction
async def create_department( self, data: CreateDepartmentRequest, + *, + saved_by: str = "api", ) -> Department: @@ - await self._snapshot_company(saved_by="api") + await self._snapshot_company(saved_by=saved_by) async def update_department( self, name: str, data: UpdateDepartmentRequest, *, if_match: str | None = None, + saved_by: str = "api", ) -> Department: @@ - await self._snapshot_company(saved_by="api") + await self._snapshot_company(saved_by=saved_by)Apply the same pattern to
delete_department()andreorder_departments(), then have the controllers pass the authenticated user ID the same wayupdate_company()already does.Also applies to: 374-425, 433-503
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/services/org_mutations.py` around lines 334 - 366, The department mutation methods (create_department, delete_department, reorder_departments) currently call _snapshot_company(saved_by="api") and must accept a saved_by parameter so the authenticated user is recorded; update each method signature to add saved_by: str (or Optional[str]) and propagate that value into the call to _snapshot_company(saved_by=saved_by), and ensure callers/controllers that invoke these methods pass the authenticated user ID (same pattern used by update_company); update any internal calls to _write_departments/_read_departments/_check_budget_sum as needed to thread the new saved_by through without changing behavior otherwise.
♻️ Duplicate comments (1)
src/synthorg/api/services/org_mutations.py (1)
111-123:⚠️ Potential issue | 🟠 MajorCatch
ValueErrorhere too.
ConfigResolver.get_budget_config()also documentsValueErrorfor parsing/threshold failures. Sinceupdate_company()calls this after persisting the setting write, that uncaught path turns a documented best-effort snapshot into a post-write failure.Suggested fix
- except PersistenceError, SettingNotFoundError: + except PersistenceError, SettingNotFoundError, ValueError: logger.exception( VERSION_SNAPSHOT_FAILED, entity_type="BudgetConfig", entity_id="default", )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/services/org_mutations.py` around lines 111 - 123, The except block in update_company's budget snapshot currently only catches PersistenceError and SettingNotFoundError; include ValueError as well so parsing/threshold failures from ConfigResolver.get_budget_config() are handled and don't bubble up; change the handler to catch (PersistenceError, SettingNotFoundError, ValueError) around the snapshot/get_budget_config() call and keep the existing logger.exception(VERSION_SNAPSHOT_FAILED, entity_type="BudgetConfig", entity_id="default") behavior.
🤖 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/workflows.py`:
- Around line 626-629: The call to
version_repo.delete_versions_for_entity(workflow_id) can raise PersistenceError
and should not cause the whole request to fail after the primary workflow was
deleted; wrap that call in a try/except catching PersistenceError (import if
needed), log a warning via the existing logger (e.g., state.app_state.logger or
process logger) that includes workflow_id and the exception, and continue
returning success—ensure the except block does not re-raise so the successful
primary delete is not masked.
- Around line 76-91: The module-level cache _WF_VERSIONING_CACHE used by
_wf_versioning(state: State) is fragile and unbounded; replace it by attaching
the VersioningService directly to state.app_state so the service lifetime
follows the app. In _wf_versioning, read repo =
state.app_state.persistence.workflow_versions and if state.app_state does not
yet have a _wf_versioning_svc attribute, create
state.app_state._wf_versioning_svc = VersioningService(repo) and return that; do
not use id(repo) or the module dict so you avoid stale services and unbounded
growth.
In `@src/synthorg/persistence/sqlite/migrations.py`:
- Around line 83-93: The docstring for the legacy-schema check that mentions
"Warn if workflow_definition_versions has the legacy schema" is outdated; update
it to state that the function logs an error and raises MigrationError when it
detects the legacy layout of workflow_definition_versions (pre-v0.7 flattened
columns like definition_id/name/workflow_type) instead of the VersionSnapshot
format (entity_id/content_hash/snapshot), and include guidance that operators
must migrate data before access because the check will now fail-fast by raising
MigrationError.
- Around line 94-98: The PRAGMA query block starting at "cursor = await
db.execute(...)" can raise SQLite errors that bypass migration logging; wrap the
db.execute/fetchall call in a try/except that catches database errors
(sqlite3.Error or the DB client's exception) around the "cursor = await
db.execute(...)" and "await cursor.fetchall()" operations, log the error at
WARNING/ERROR with context (including the PRAGMA and migration name/version) and
re-raise a MigrationError (use the existing MigrationError class) so callers
only see MigrationError; update references to "cursor" and "columns = {row[1]
for row in await cursor.fetchall()}" to occur inside the try block.
---
Outside diff comments:
In @.github/workflows/docker.yml:
- Around line 225-237: The cosign signing pipeline (the command using DIGEST,
cosign sign and piping to tee writing /tmp/cosign-out) can hide cosign failures
because pipefail isn’t enabled; before running the pipeline in each "Sign image"
step (backend, web, sandbox) enable pipefail (e.g., run "set -o pipefail" or
prefix the shell command to enable pipefail) so the pipeline returns cosign’s
exit code instead of tee’s, ensuring the if ! ... | tee /tmp/cosign-out check
correctly detects failures.
In `@src/synthorg/api/services/org_mutations.py`:
- Around line 334-366: The department mutation methods (create_department,
delete_department, reorder_departments) currently call
_snapshot_company(saved_by="api") and must accept a saved_by parameter so the
authenticated user is recorded; update each method signature to add saved_by:
str (or Optional[str]) and propagate that value into the call to
_snapshot_company(saved_by=saved_by), and ensure callers/controllers that invoke
these methods pass the authenticated user ID (same pattern used by
update_company); update any internal calls to
_write_departments/_read_departments/_check_budget_sum as needed to thread the
new saved_by through without changing behavior otherwise.
---
Duplicate comments:
In `@src/synthorg/api/services/org_mutations.py`:
- Around line 111-123: The except block in update_company's budget snapshot
currently only catches PersistenceError and SettingNotFoundError; include
ValueError as well so parsing/threshold failures from
ConfigResolver.get_budget_config() are handled and don't bubble up; change the
handler to catch (PersistenceError, SettingNotFoundError, ValueError) around the
snapshot/get_budget_config() call and keep the existing
logger.exception(VERSION_SNAPSHOT_FAILED, entity_type="BudgetConfig",
entity_id="default") behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: f9cc5767-5c4b-4467-89e8-4f6736fb551f
⛔ Files ignored due to path filters (1)
web/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (8)
.github/workflows/ci.yml.github/workflows/docker.ymlsrc/synthorg/api/controllers/workflows.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/state.pysrc/synthorg/persistence/sqlite/migrations.pytests/unit/api/fakes_workflow.pyweb/src/api/types.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Dashboard Test
- GitHub Check: Test (Python 3.14)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
No
from __future__ import annotations—Python 3.14 has PEP 649Use
except A, B:(no parentheses) for exception syntax—PEP 758, enforced by ruff on Python 3.14Type hints: all public functions, mypy strict mode
Docstrings: Google style, required on public classes/functions (enforced by ruff D rules)
Create new objects, never mutate existing ones. For non-Pydantic internal collections (registries, BaseTool), use
copy.deepcopy()at construction +MappingProxyTypewrapping for read-only enforcement.For
dict/listfields in frozen Pydantic models, rely onfrozen=Truefor field reassignment prevention andcopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence).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.Use Pydantic v2 (
BaseModel,model_validator,computed_field,ConfigDict). Useallow_inf_nan=Falsein allConfigDictdeclarations to rejectNaN/Infin numeric fields at validation time.Use
@computed_fieldfor derived values instead of storing + validating redundant fields (e.g.TokenUsage.total_tokens)Use
NotBlankStr(fromcore.types) for all identifier/name fields—including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants—instead of manual whitespace validators.Prefer
asyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over barecreate_task.Line length: 88 characters (ruff)
Functions: < 50 lines, files < 800 lines
Handle errors explicitly, never silently swallow
Validate at system boundaries (user input, external APIs, config files)
Variable name: always
logger(not_logger, notlog)Python: 3.14+ (PEP 6...
Files:
src/synthorg/api/state.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/persistence/sqlite/migrations.pysrc/synthorg/api/services/org_mutations.pytests/unit/api/fakes_workflow.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Every module with business logic MUST have:
from synthorg.observability import get_loggerthenlogger = get_logger(__name__)Never use
import logging/logging.getLogger()/print()in application code. Exception:observability/setup.py,observability/sinks.py,observability/syslog_handler.py,observability/http_handler.py, andobservability/otlp_handler.pymay use stdlibloggingandprint(..., file=sys.stderr)for handler construction, bootstrap, and error reporting.Event names: always use constants from domain-specific modules under
synthorg.observability.events. Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANTStructured kwargs: always
logger.info(EVENT, key=value)—neverlogger.info("msg %s", val)All error paths must log at WARNING or ERROR with context before raising
All state transitions must log at INFO
DEBUG logging for object creation, internal flow, entry/exit of key functions
All provider calls go through
BaseCompletionProviderwhich applies retry + rate limiting automatically. Never implement retry logic in driver subclasses or calling code—it's handled by the base class.
RetryConfigandRateLimiterConfigare set per-provider inProviderConfigRetryable errors (
is_retryable=True):RateLimitError,ProviderTimeoutError,ProviderConnectionError,ProviderInternalError. Non-retryable errors raise immediately without retry.
RetryExhaustedErrorsignals that all retries failed—the engine layer catches this to trigger fallback chainsRate limiter respects
RateLimitError.retry_afterfrom providers—automatically pauses future requestsVendor-agnostic 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/smallas aliases.
Files:
src/synthorg/api/state.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/persistence/sqlite/migrations.pysrc/synthorg/api/services/org_mutations.py
src/**/*.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/api/state.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/persistence/sqlite/migrations.pysrc/synthorg/api/services/org_mutations.py
web/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx,js,jsx}: Always usecreateLoggerfrom@/lib/logger-- never bareconsole.warn/console.error/console.debugin application code
Logger variable name must always beconst log(e.g.const log = createLogger('module-name'))
Pass dynamic/untrusted values as separate arguments to logger methods (not interpolated into the message string) so they go throughsanitizeArg
Attacker-controlled fields inside structured objects must be wrapped insanitizeForLog()before embedding in log calls
Files:
web/src/api/types.ts
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground,bg-card,text-accent,text-success,bg-danger, etc.) or CSS variables (var(--so-*)) for colors; NEVER hardcode hex values in.tsx/.tsfiles
Usefont-sansorfont-mono(Geist tokens) for typography; NEVER setfontFamilydirectly in.tsx/.tsfiles
Use density-aware tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind spacing; NEVER hardcode pixel values for layout spacing in components
Use token variables (var(--so-shadow-card-hover),border-border,border-bright) for shadows and borders; NEVER hardcode values in.tsx/.tsfiles
Use@/lib/motionpresets for Framer Motion transition durations; NEVER hardcode transition durations
CSS side-effect imports in TypeScript 6 require type declarations -- add/// <reference types="vite/client" />at the top of files with CSS importsWeb Dashboard Design System (MANDATORY): See
web/CLAUDE.mdfor full component inventory. ALWAYS reuse existing components fromweb/src/components/ui/before creating new ones.NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions in web code—use design tokens and
@/lib/motionpresets
Files:
web/src/api/types.ts
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Markers:
@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e,@pytest.mark.slowTimeout: 30 seconds per test (global in
pyproject.toml—do not add per-filepytest.mark.timeout(30)markers; non-default overrides liketimeout(60)are allowed)Parametrize: Prefer
@pytest.mark.parametrizefor testing similar casesTests must use
test-provider,test-small-001, etc. instead of real vendor namesPython uses Hypothesis (
@given+@settings) for property-based testing. Hypothesis profiles configured intests/conftest.py:ci(deterministic,max_examples=10+derandomize=True),dev(1000 examples),fuzz(10,000 examples, no deadline),extreme(500,000 examples, no deadline).NEVER skip, dismiss, or ignore flaky tests—always fix them fully and fundamentally. For timing-sensitive tests, mock
time.monotonic()andasyncio.sleep()to make them deterministic. For tasks that must block indefinitely, useasyncio.Event().wait()instead ofasyncio.sleep(large_number).
Files:
tests/unit/api/fakes_workflow.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/api/fakes_workflow.py
🧠 Learnings (33)
📓 Common learnings
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: Always read the relevant `docs/design/` page before implementing any feature or planning any issue. DESIGN_SPEC.md is a pointer file linking to the 12 design pages.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: When implementation deviates from the design spec, alert the user and explain why—user decides whether to proceed or update the spec. Do NOT silently diverge—every deviation needs explicit user approval.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: Every implementation plan must be presented to the user for accept/deny before coding starts.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: At every phase of planning and implementation, be critical—actively look for ways to improve the design in the spirit of what we're building (robustness, correctness, simplicity, future-proofing where it's free).
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: Surface improvements as suggestions, not silent changes—user decides.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: Prioritize issues by dependency order, not priority labels—unblocked dependencies come first.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: Coverage: 80% minimum (enforced in CI)
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: Parallelism: `pytest-xdist` via `-n 8`—ALWAYS include `-n 8` when running pytest locally, never run tests sequentially. CI uses `-n auto`.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: When Hypothesis finds a failure, it is a real bug—the shrunk example is saved but is not replayed automatically. Do NOT just rerun and move on. Fix the underlying bug and add an explicit `example(...)` decorator permanently.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: Commits: `<type>: <description>`—types: feat, fix, refactor, docs, test, chore, perf, ci. Enforced by commitizen (commit-msg hook)
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: Signed commits: required on `main` via branch protection—all commits must be GPG/SSH signed
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: Branches: `<type>/<slug>` from main
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: Pre-commit hooks: trailing-whitespace, end-of-file-fixer, check-yaml, check-toml, check-json, check-merge-conflict, check-added-large-files, no-commit-to-branch (main), ruff check+format, gitleaks, hadolint, golangci-lint + go vet (conditional on cli), no-em-dashes, no-redundant-timeout, eslint-web (conditional on web)
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: Hookify rules (committed in `.claude/hookify.*.md`): `block-pr-create` (blocks direct `gh pr create`), `enforce-parallel-tests` (enforces `-n 8` with pytest), `no-cd-prefix` (blocks `cd` prefix in Bash commands), `no-local-coverage` (blocks `--cov` flags locally)
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: Pre-push hooks: mypy type-check (affected modules) + pytest unit tests (affected modules) + golangci-lint + go vet + go test (CLI) + eslint-web (dashboard) (fast gate before push, skipped in CI). Foundational module changes (core, config, observability) or conftest changes trigger full runs.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: GitHub issue queries: use `gh issue list` via Bash (not MCP tools)—MCP `list_issues` has unreliable field data
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: Merge strategy: squash merge—PR body becomes the squash commit message on main. Trailers (e.g. `Release-As`, `Closes `#N``) must be in the PR body to land in the final commit.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: Preserve existing `Closes `#NNN`` references in PR issue references—never remove unless explicitly asked
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: After finishing an issue implementation: always create a feature branch (`<type>/<slug>`), commit, and push—do NOT create a PR automatically. Do NOT leave work uncommitted on main—branch, commit, push immediately after finishing.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: NEVER create a PR directly—`gh pr create` is blocked by hookify. ALWAYS use `/pre-pr-review` to create PRs—it runs automated checks + review agents + fixes before creating the PR.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: For trivial/docs-only changes: `/pre-pr-review quick` skips agents but still runs automated checks
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: After the PR exists, use `/aurelio-review-pr` to handle external reviewer feedback
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: When review agents find valid issues (including pre-existing issues, suggestions, and findings adjacent to the PR's changes), fix them all. No deferring, no "out of scope" skipping.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: NEVER use `cd` in Bash commands—the working directory is already set to the project root. Use absolute paths or run commands directly. Exception: `bash -c "cd <dir> && <cmd>"` is safe. Use this for tools without a `-C` flag.
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: NEVER use Bash to write or modify files—use the Write or Edit tools. Do not use `cat >`, `cat << EOF`, `echo >`, `echo >>`, `sed -i`, `python -c "open(...).write(...)", or `tee` (read-only uses like piping to stdout are fine).
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: A PostToolUse hook (`scripts/check_web_design_system.py`) enforces design system rules on every Edit/Write to `web/src/`
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T21:54:57.367Z
Learning: Layout: `src/synthorg/` (src layout), `tests/` (unit/integration/e2e), `web/` (React 19 dashboard), `cli/` (Go CLI binary)
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.stories.{ts,tsx} : Storybook 10: Use storybook/test (not storybook/test) and storybook/actions (not storybook/addon-actions) import paths
Applied to files:
.github/workflows/ci.yml
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/*.yml : Dependabot: daily updates for uv + github-actions + npm + pre-commit + docker + gomod, grouped minor/patch, no auto-merge. Use `/review-dep-pr` to review Dependabot PRs before merging.
Applied to files:
.github/workflows/ci.yml
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: import from `storybook/test` (not `storybook/test`), `storybook/actions` (not `storybook/addon-actions`)
Applied to files:
.github/workflows/ci.yml
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to .github/workflows/pages.yml : Pages job: version extraction from `pyproject.toml`, OpenAPI export, Astro + Zensical docs build (with version banner), GitHub Pages deploy on push to main
Applied to files:
.github/workflows/ci.yml
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/cli.yml : CLI workflow: Go lint (golangci-lint + go vet) + test (-race -coverprofile) + build (cross-compile: linux/darwin/windows × amd64/arm64) + govulncheck + fuzz testing (main-only, 30s/target, continue-on-error, matrix over 4 packages). cli-pass gate includes fuzz as informational. GoReleaser release on v* tags. Cosign keyless signing of checksums.txt. SLSA L3 provenance attestations. Sigstore bundle (.sigstore.json) attached. Post-release appends checksums/verification/provenance to draft release notes.
Applied to files:
.github/workflows/ci.yml.github/workflows/docker.yml
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/docker.yml : Docker workflow: builds backend + web + sandbox images, pushes to GHCR, signs with cosign. SLSA L3 provenance attestations via actions/attest-build-provenance. Scans: Trivy (CRITICAL = hard fail, HIGH = warn) + Grype (critical cutoff) + CIS Docker Benchmark v1.6.0 compliance (informational). CVE triage via .github/.trivyignore.yaml and .github/.grype.yaml. Images only pushed after scans pass. Triggers on push to main and version tags (v*).
Applied to files:
.github/workflows/ci.yml.github/workflows/docker.yml
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: CLI workflow (`.github/workflows/cli.yml`) runs Go lint (golangci-lint + go vet) + test (race, coverage) + build (cross-compile matrix) + vulnerability check (govulncheck) + fuzz testing. Cross-compiles for linux/darwin/windows × amd64/arm64. GoReleaser release on v* tags with cosign keyless signing and SLSA L3 attestations.
Applied to files:
.github/workflows/ci.yml.github/workflows/docker.yml
📚 Learning: 2026-03-15T12:00:18.113Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T12:00:18.113Z
Learning: Applies to .github/workflows/docker.yml : CI Docker: build → scan → push to GHCR + cosign sign + SLSA L3 provenance via attest-build-provenance (images only pushed after Trivy/Grype scans pass).
Applied to files:
.github/workflows/ci.yml.github/workflows/docker.yml
📚 Learning: 2026-04-02T08:47:46.313Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:47:46.313Z
Learning: Pre-push hooks enforce: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional) + eslint-web (web dashboard, conditional) — fast gate before push, skipped in pre-commit.ci
Applied to files:
.github/workflows/ci.yml
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Docker workflow (`docker.yml`): build + Trivy/Grype scan + push to GHCR + cosign sign + SLSA L3 provenance. CVE triage: `.github/.trivyignore.yaml`, `.github/.grype.yaml`
Applied to files:
.github/workflows/ci.yml.github/workflows/docker.yml
📚 Learning: 2026-03-31T14:28:28.895Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:28:28.895Z
Learning: Applies to docker/**/Dockerfile* : Docker images: backend (Chainguard distroless, non-root), web (nginx-unprivileged, SPA + API proxy), sandbox (Python + Node.js, non-root)
Applied to files:
.github/workflows/docker.yml
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Dependabot: auto-updates Docker image digests and versions daily.
Applied to files:
.github/workflows/docker.yml
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Applies to Dockerfile* : Backend images must use Chainguard distroless with non-root user; web image must use nginx-unprivileged with non-root user; sandbox image must use Python + Node.js with non-root user. All Docker files in `docker/` -- Dockerfiles, compose, `.env.example`. Single root `.dockerignore`. All images build with `context: .`
Applied to files:
.github/workflows/docker.yml
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to docker/{Dockerfile*,compose.yml} : Docker: Backend uses 3-stage build (builder → setup → distroless runtime), Chainguard Python, non-root (UID 65532), CIS-hardened. Web uses nginxinc/nginx-unprivileged, Vue 3 SPA with PrimeVue + Tailwind CSS, SPA routing, API/WebSocket proxy to backend.
Applied to files:
.github/workflows/docker.yml
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/finalize-release.yml : Finalize Release workflow: publishes draft releases created by Release Please. Triggers on workflow_run completion of Docker and CLI workflows. Verifies both workflows succeeded for the associated tag before publishing. Extracts CLI checksums, cosign verification, and container verification data from HTML comments, assembles into combined Verification section. Guards against PR-triggered runs. Handles TOCTOU races. Immutable releases enabled—once published, release assets and body cannot be modified.
Applied to files:
.github/workflows/docker.yml
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: CLI verifies cosign signatures + SLSA provenance at pull time; bypass with `--skip-verify`
Applied to files:
.github/workflows/docker.yml
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: CLI workflow (`cli.yml`): Go lint/test/build (cross-compile) + govulncheck + fuzz. GoReleaser release on `v*` tags with cosign signing + SLSA provenance
Applied to files:
.github/workflows/docker.yml
📚 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 docker/Dockerfile.sandbox : Docker sandbox: `synthorg-sandbox` — Python 3.14 + Node.js + git, non-root (UID 10001), agent code execution sandbox
Applied to files:
.github/workflows/docker.yml
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/api/**/*.py : REST API: Litestar framework, controllers with guards, channels for WebSocket, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. RFC 9457 structured errors (ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation).
Applied to files:
src/synthorg/api/controllers/workflows.pysrc/synthorg/api/services/org_mutations.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/api/**/*.py : API package (api/): Litestar REST + WebSocket with controllers, guards, channels, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint, provider management endpoint (CRUD + test + presets), backup endpoint, RFC 9457 structured errors, AppState hot-reload slots, service auto-wiring (Phase 1 at construction, Phase 2 on startup), lifecycle helpers
Applied to files:
src/synthorg/api/controllers/workflows.pysrc/synthorg/api/services/org_mutations.py
📚 Learning: 2026-04-08T20:20:05.318Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-08T20:20:05.318Z
Learning: Applies to src/synthorg/**/*.py : If implementation deviates from the design spec (better approach found, scope evolved, etc.), alert the user and explain why before proceeding; do NOT silently diverge
Applied to files:
src/synthorg/persistence/sqlite/migrations.pysrc/synthorg/api/services/org_mutations.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/services/org_mutations.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/services/org_mutations.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/services/org_mutations.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/services/org_mutations.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/services/org_mutations.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/api/services/org_mutations.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately.
Applied to files:
src/synthorg/api/services/org_mutations.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use `NotBlankStr` from `core.types` for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Applied to files:
tests/unit/api/fakes_workflow.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:
tests/unit/api/fakes_workflow.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 with adopted conventions: use computed_field for derived values instead of storing + validating redundant fields; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.
Applied to files:
tests/unit/api/fakes_workflow.py
📚 Learning: 2026-03-31T20:07:03.035Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T20:07:03.035Z
Learning: Applies to **/*.py : Use `NotBlankStr` (from `core.types`) for all identifier/name fields, including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants, instead of manual whitespace validators
Applied to files:
tests/unit/api/fakes_workflow.py
🔇 Additional comments (11)
web/src/api/types.ts (1)
1782-1803: Versioning type generalization is correctly modeled.
VersionSummary<TSnapshot>andWorkflowDefinitionVersionSummary = VersionSummary<WorkflowDefinitionSnapshot>align with the backendVersionSnapshot[T]envelope and remove duplicated workflow-specific envelope typing..github/workflows/ci.yml (1)
329-345: Good reliability improvement for Storybook CI failures.This is a solid change: Line 329 adds explicit Node crash diagnostics/heap tuning, and Lines 339-345 capture crash reports only on failure without introducing flaky follow-up failures.
tests/unit/api/fakes_workflow.py (5)
9-14: LGTM!The
TYPE_CHECKINGimports forNotBlankStrandVersionSnapshotare the correct pattern for Python 3.14's PEP 649 lazy annotations.
113-123: LGTM!The class correctly adopts the new
VersionSnapshot[WorkflowDefinition]model with(entity_id, version)composite keys. The docstring accurately documents protocol conformance via structural typing.
125-133: LGTM!The
save_versionimplementation correctly returnsboolwith INSERT OR IGNORE semantics. This is critical forVersioningService.snapshot_if_changed()to detect concurrent-write conflicts and trigger_resolve_conflict().
135-166: LGTM!The getter methods correctly implement the
VersionRepositoryprotocol:
get_versionuses direct key lookupget_latest_versionreturns the highest version number for the entityget_by_content_hashreturns the newest version among matching hashes (aligning with productionORDER BY version DESC)All methods now use
NotBlankStrtype hints and return deep copies.
168-198: LGTM!The list/count/delete methods are well-implemented:
list_versionsnow validates non-negative pagination parameters, matching production behaviorcount_versionscorrectly counts by entitydelete_versions_for_entityproperly filters composite keys by entity_idsrc/synthorg/persistence/sqlite/migrations.py (1)
74-76: Good fail-fast placement for legacy schema detection.Running the legacy-shape check before
PERSISTENCE_MIGRATION_COMPLETEDis the right ordering and prevents booting with an incompatible workflow versions table.src/synthorg/api/state.py (1)
200-215: Good catch wiringcompany_versionsthrough both initialization paths.This closes the deferred-settings case too, so
OrgMutationServicegets the same versioning dependencies whether it is built in__init__()or later inset_settings_service().Also applies to: 845-858
src/synthorg/api/controllers/workflows.py (2)
439-451: LGTM on snapshot error handling migration.The refactored code correctly catches
PersistenceErroras documented byVersioningService.snapshot_if_changed(), logs withlogger.exception()(preserving stack trace), and allows the primary operation to succeed even if snapshotting fails. The pattern is consistently applied across all three mutation endpoints.Also applies to: 526-538, 587-599
14-14: Imports are correctly updated for the refactored versioning pattern.The removed
build_version_snapshotimport is replaced byVersioningService, and the exception types align with the new error handling.Also applies to: 67-75
| _WF_VERSIONING_CACHE: dict[int, VersioningService[WorkflowDefinition]] = {} | ||
|
|
||
|
|
||
| def _wf_versioning(state: State) -> VersioningService[WorkflowDefinition]: | ||
| """Return a VersioningService for workflow definitions. | ||
|
|
||
| Uses the persistence backend's identity to avoid repeated | ||
| allocations across controller methods. | ||
| """ | ||
| repo = state.app_state.persistence.workflow_versions | ||
| key = id(repo) | ||
| svc = _WF_VERSIONING_CACHE.get(key) | ||
| if svc is None: | ||
| svc = VersioningService(repo) | ||
| _WF_VERSIONING_CACHE[key] = svc | ||
| return svc |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how tests handle app_state/persistence to assess impact
rg -n -A3 'app_state\.persistence' tests/Repository: Aureliolo/synthorg
Length of output: 23711
🏁 Script executed:
# Find conftest.py to understand test fixtures and app lifecycle
find tests -name "conftest.py" -type fRepository: Aureliolo/synthorg
Length of output: 1716
🏁 Script executed:
# Search for TestClient instantiation patterns to understand if app is recreated per test
rg -n "TestClient|FastAPI\(\)" tests/ | head -30Repository: Aureliolo/synthorg
Length of output: 2792
🏁 Script executed:
# Check if the module-level cache persists across test runs
# Look for any test isolation or app recreation patterns
rg -n "app_state|persistence" tests/ -B2 -A2 | grep -E "(fixture|setup|teardown|conftest)" | head -20Repository: Aureliolo/synthorg
Length of output: 1679
🏁 Script executed:
# Check the main conftest to understand app fixture lifecycle
cat tests/unit/api/conftest.pyRepository: Aureliolo/synthorg
Length of output: 13744
🏁 Script executed:
# Check test_workflows.py conftest specifically
cat tests/unit/api/controllers/conftest.pyRepository: Aureliolo/synthorg
Length of output: 1054
🏁 Script executed:
# Look for fixture scope to understand if app is recreated per test
rg -n "@pytest.fixture" tests/unit/api/conftest.py tests/unit/api/controllers/conftest.py -A1Repository: Aureliolo/synthorg
Length of output: 2477
🏁 Script executed:
# Check if repos are actually garbage-collected between tests
# by looking at whether app instances have references that keep repos alive
rg -n "with TestClient" tests/unit/api/controllers/test_workflows.py -A5 -B2Repository: Aureliolo/synthorg
Length of output: 44
🏁 Script executed:
# Check the actual repo object lifecycle by examining FakePersistenceBackend
rg -n "class FakePersistenceBackend" tests/ -A20Repository: Aureliolo/synthorg
Length of output: 1947
🏁 Script executed:
# Check if test_workflows.py actually uses the _wf_versioning function
# to understand if module cache issues would surface
rg -n "_wf_versioning" tests/unit/api/controllers/test_workflows.pyRepository: Aureliolo/synthorg
Length of output: 44
🏁 Script executed:
# Look at the actual test execution to see if multiple tests create repos
rg -n "def test_" tests/unit/api/controllers/test_workflows.py | head -20Repository: Aureliolo/synthorg
Length of output: 1584
🏁 Script executed:
# Check if _wf_versioning is actually called in the workflow controller methods
rg -n "_wf_versioning" src/synthorg/api/controllers/workflows.pyRepository: Aureliolo/synthorg
Length of output: 259
🏁 Script executed:
# Look at how the function is used in the actual route handlers
rg -n "def create_workflow\|def update_workflow\|def delete_workflow" src/synthorg/api/controllers/workflows.py -A10 | head -50Repository: Aureliolo/synthorg
Length of output: 44
🏁 Script executed:
# Check if there are any actual test failures or issues related to _wf_versioning
rg -n "VersioningService" tests/unit/api/controllers/test_workflows.pyRepository: Aureliolo/synthorg
Length of output: 44
🏁 Script executed:
# Look at the actual repository object to understand if id() reuse is a real concern
cat src/synthorg/api/controllers/workflows.py | head -100Repository: Aureliolo/synthorg
Length of output: 3385
🏁 Script executed:
# Look for any tests that explicitly test version conflict behavior
rg -n "version_conflict\|VersionConflictError" tests/unit/api/controllers/test_workflows.py -B3 -A3Repository: Aureliolo/synthorg
Length of output: 44
🏁 Script executed:
# Check if the tests actually exercise the _wf_versioning cache across multiple test runs
# by looking at test methods that use snapshot_if_changed
rg -n "snapshot_if_changed" tests/unit/api/controllers/ -lRepository: Aureliolo/synthorg
Length of output: 44
🏁 Script executed:
# Check integration tests to see if they create multiple app instances
rg -n "TestClient\|create_app" tests/integration/ | head -20Repository: Aureliolo/synthorg
Length of output: 44
🏁 Script executed:
# Verify if FakePersistenceBackend repo objects would have stable ids across tests
# by checking if the test fixture creates fresh instances per test
rg -n "fake_persistence" tests/unit/api/conftest.py -A5 -B2Repository: Aureliolo/synthorg
Length of output: 1525
🏁 Script executed:
# Check if this pattern appears in production code or is test-only
rg -n "id\(" src/synthorg/ --type py | rg -v test | head -10Repository: Aureliolo/synthorg
Length of output: 1010
🏁 Script executed:
# Verify if persistence backend is actually a singleton or could be recreated
rg -n "persistence.*=.*Persistence\|create_persistence" src/synthorg/ --type py -B2 -A2Repository: Aureliolo/synthorg
Length of output: 44
Module-level cache using id() as key is an architectural anti-pattern; attach the service to state.app_state instead.
The _WF_VERSIONING_CACHE grows unbounded and couples cache ownership to module initialization rather than app lifetime. Using id(repo) is fragile: if the persistence backend is ever recreated during app operation, the cache returns a stale VersioningService bound to the old repo. More importantly, there's no cleanup mechanism—the cache accumulates entries indefinitely.
Attach the service directly to state.app_state alongside the repo to tie lifetimes together and clarify ownership:
def _wf_versioning(state: State) -> VersioningService[WorkflowDefinition]:
"""Return a VersioningService for workflow definitions."""
if not hasattr(state.app_state, "_wf_versioning_svc"):
repo = state.app_state.persistence.workflow_versions
state.app_state._wf_versioning_svc = VersioningService(repo)
return state.app_state._wf_versioning_svc🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/synthorg/api/controllers/workflows.py` around lines 76 - 91, The
module-level cache _WF_VERSIONING_CACHE used by _wf_versioning(state: State) is
fragile and unbounded; replace it by attaching the VersioningService directly to
state.app_state so the service lifetime follows the app. In _wf_versioning, read
repo = state.app_state.persistence.workflow_versions and if state.app_state does
not yet have a _wf_versioning_svc attribute, create
state.app_state._wf_versioning_svc = VersioningService(repo) and return that; do
not use id(repo) or the module dict so you avoid stale services and unbounded
growth.
…ype hints, enum assertion
…e cache, VersionSummary generic, dep bump
…, department saved_by threading
37df199 to
f264d94
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/synthorg/api/services/org_mutations.py (1)
526-580:⚠️ Potential issue | 🟡 MinorAgent mutations do not snapshot the company structure.
The
create_agent,update_agent, anddelete_agentmethods modify the org configuration but do not call_snapshot_company(). This means agent additions, changes, and deletions are not captured in the company version history, unlike department mutations which do trigger company snapshots.If agents are considered part of the company structure for versioning purposes, consider adding
_snapshot_company(saved_by=...)to these methods. If agents are intentionally excluded from company versioning, this is fine but should be documented.Also applies to: 630-677, 679-714
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/services/org_mutations.py` around lines 526 - 580, The agent mutations (create_agent, update_agent, delete_agent) currently modify agents but do not record a company snapshot; update each of these functions to call self._snapshot_company(saved_by=...) immediately after successfully writing agents (i.e., after await self._write_agents(...) and before/after releasing the org lock), passing an appropriate actor identifier (use data.saved_by if the request carries it or the existing request/user context on the service) so agent changes are captured in the company version history via _snapshot_company.src/synthorg/api/controllers/workflow_versions.py (1)
364-376:⚠️ Potential issue | 🟡 MinorSnapshot failure is silently swallowed after successful definition save.
If
repo.save(rolled_back)succeeds butsvc.snapshot_if_changed()fails, the rollback completes and returns 200 with the rolled-back definition, but no version snapshot is persisted. This creates an inconsistency where the workflow state exists but its version history is incomplete.Consider whether this best-effort behavior is intentional. If version history is critical for auditing rollbacks, this should either:
- Be wrapped in a transaction so both succeed or fail together
- Return a warning in the response indicating partial success
- Be documented explicitly in the endpoint's behavior
If best-effort is acceptable (similar to
_snapshot_budget_configinorg_mutations.py), the current implementation is fine but could benefit from a comment explaining the trade-off.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/api/controllers/workflow_versions.py` around lines 364 - 376, The code currently saves the rolled-back definition (repo.save(rolled_back)) and then best-effort calls VersioningService.snapshot_if_changed (svc.snapshot_if_changed) swallowing PersistenceError; to fix, make snapshot persistence part of the same atomic operation or explicitly surface partial failure: either (A) wrap repo.save and svc.snapshot_if_changed in a transaction so both succeed or both rollback (use your DB/ORM transaction helper around the save + snapshot call and re-raise on failure), or (B) if best-effort is intended, add a clear comment and change the controller response to include a warning field when the snapshot step raises PersistenceError (log WORKFLOW_VERSION_SNAPSHOT_FAILED as now but also append a warning to the HTTP response payload so callers know version history may be incomplete). Ensure references to VersioningService, snapshot_if_changed, PersistenceError, repo.save and WORKFLOW_VERSION_SNAPSHOT_FAILED are updated accordingly.
♻️ Duplicate comments (1)
src/synthorg/engine/workflow/diff.py (1)
195-206:⚠️ Potential issue | 🟠 MajorValidate each snapshot against its own
entity_id, not just against the other snapshot.Line 195 only rejects differing
snapshot.idvalues. If both persisted payloads carry the same wrongsnapshot.id,compute_diff()still returns a diff fordefinition_id=old.entity_id, which can attribute changes to the wrong workflow. Checkold.snapshot.id == old.entity_idandnew.snapshot.id == new.entity_idbefore diffing.🛡️ Proposed fix
- if old.snapshot.id != new.snapshot.id: + if ( + old.snapshot.id != old.entity_id + or new.snapshot.id != new.entity_id + or old.snapshot.id != new.snapshot.id + ): msg = ( - "Snapshot definition IDs do not match entity_id " - f"(old={old.snapshot.id!r}, new={new.snapshot.id!r})" + "Version snapshot payload/entity mismatch " + f"(old_entity_id={old.entity_id!r}, old_snapshot_id={old.snapshot.id!r}, " + f"new_entity_id={new.entity_id!r}, new_snapshot_id={new.snapshot.id!r})" ) logger.warning( WORKFLOW_DEF_INVALID_REQUEST, + old_definition_id=old.entity_id, + new_definition_id=new.entity_id, old_snapshot_id=old.snapshot.id, new_snapshot_id=new.snapshot.id, reason=msg, )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/synthorg/engine/workflow/diff.py` around lines 195 - 206, The current check only compares old.snapshot.id vs new.snapshot.id; instead validate each snapshot matches its declared entity id before diffing by asserting old.snapshot.id == old.entity_id and new.snapshot.id == new.entity_id (e.g., in the compute_diff flow surrounding the existing block), logging WORKFLOW_DEF_INVALID_REQUEST with old_snapshot_id/new_snapshot_id and a clear reason for each mismatch and raising ValueError when either snapshot.id does not equal its entity_id; ensure these checks run prior to any diff computation that uses definition_id=old.entity_id.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/docker.yml:
- Around line 230-238: The Rekor conflict branch currently skips on
'createLogEntryConflict' without confirming the image is actually signed; update
the conflict-handling branch that greps for 'createLogEntryConflict' (the block
around the cosign sign invocation and /tmp/cosign-out) to run a cosign verify
for the same image digest (${DIGEST}) before treating it as success: call cosign
verify with certificate identity/issuer constraints (e.g. --certificate-identity
"<OIDC_SUBJECT>" --certificate-issuer "<OIDC_ISSUER>") against
ghcr.io/aureliolo/synthorg-backend@${DIGEST} and fail the step if verify returns
non-zero, otherwise print a notice and continue; apply this same change to the
other two sign steps that use the same pattern (grep 'createLogEntryConflict' on
/tmp/cosign-out).
---
Outside diff comments:
In `@src/synthorg/api/controllers/workflow_versions.py`:
- Around line 364-376: The code currently saves the rolled-back definition
(repo.save(rolled_back)) and then best-effort calls
VersioningService.snapshot_if_changed (svc.snapshot_if_changed) swallowing
PersistenceError; to fix, make snapshot persistence part of the same atomic
operation or explicitly surface partial failure: either (A) wrap repo.save and
svc.snapshot_if_changed in a transaction so both succeed or both rollback (use
your DB/ORM transaction helper around the save + snapshot call and re-raise on
failure), or (B) if best-effort is intended, add a clear comment and change the
controller response to include a warning field when the snapshot step raises
PersistenceError (log WORKFLOW_VERSION_SNAPSHOT_FAILED as now but also append a
warning to the HTTP response payload so callers know version history may be
incomplete). Ensure references to VersioningService, snapshot_if_changed,
PersistenceError, repo.save and WORKFLOW_VERSION_SNAPSHOT_FAILED are updated
accordingly.
In `@src/synthorg/api/services/org_mutations.py`:
- Around line 526-580: The agent mutations (create_agent, update_agent,
delete_agent) currently modify agents but do not record a company snapshot;
update each of these functions to call self._snapshot_company(saved_by=...)
immediately after successfully writing agents (i.e., after await
self._write_agents(...) and before/after releasing the org lock), passing an
appropriate actor identifier (use data.saved_by if the request carries it or the
existing request/user context on the service) so agent changes are captured in
the company version history via _snapshot_company.
---
Duplicate comments:
In `@src/synthorg/engine/workflow/diff.py`:
- Around line 195-206: The current check only compares old.snapshot.id vs
new.snapshot.id; instead validate each snapshot matches its declared entity id
before diffing by asserting old.snapshot.id == old.entity_id and new.snapshot.id
== new.entity_id (e.g., in the compute_diff flow surrounding the existing
block), logging WORKFLOW_DEF_INVALID_REQUEST with
old_snapshot_id/new_snapshot_id and a clear reason for each mismatch and raising
ValueError when either snapshot.id does not equal its entity_id; ensure these
checks run prior to any diff computation that uses definition_id=old.entity_id.
🪄 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: 1d77978d-290d-41a7-a0b1-f1a55f3b0ee9
⛔ Files ignored due to path filters (1)
web/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (38)
.github/workflows/ci.yml.github/workflows/docker.ymlCLAUDE.mddocs/design/engine.mdsrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/_workflow_helpers.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/api/controllers/company.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/api/services/org_mutations.pysrc/synthorg/api/state.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/engine/workflow/version.pysrc/synthorg/observability/events/versioning.pysrc/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/persistence/sqlite/migrations.pysrc/synthorg/persistence/sqlite/schema.sqlsrc/synthorg/persistence/sqlite/version_repo.pysrc/synthorg/persistence/sqlite/workflow_version_repo.pysrc/synthorg/persistence/workflow_version_repo.pytests/unit/api/controllers/test_config_version_controllers.pytests/unit/api/controllers/test_workflow_versions.pytests/unit/api/fakes_backend.pytests/unit/api/fakes_workflow.pytests/unit/engine/workflow/test_diff.pytests/unit/engine/workflow/test_version.pytests/unit/persistence/sqlite/test_entity_version_repos.pytests/unit/persistence/sqlite/test_migrations.pytests/unit/persistence/sqlite/test_workflow_version_repo.pytests/unit/persistence/test_protocol.pyweb/src/api/types.tsweb/src/pages/workflow-editor/VersionHistoryPanel.tsx
💤 Files with no reviewable changes (6)
- src/synthorg/api/controllers/_workflow_helpers.py
- tests/unit/engine/workflow/test_version.py
- src/synthorg/engine/workflow/version.py
- tests/unit/persistence/sqlite/test_workflow_version_repo.py
- src/synthorg/persistence/workflow_version_repo.py
- src/synthorg/persistence/sqlite/workflow_version_repo.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (7)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Always read the relevant
docs/design/page before implementing any feature or planning any issue. The design spec is the starting point for architecture, data models, and behaviorIf implementation deviates from the spec (better approach found, scope evolved, etc.), alert the user and explain why before proceeding. Do NOT silently diverge -- every deviation needs explicit user approval
Every implementation plan must be presented to the user for accept/deny before coding starts
Line length: 88 characters (enforced by ruff)
Files:
src/synthorg/observability/events/versioning.pysrc/synthorg/persistence/sqlite/version_repo.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/company.pysrc/synthorg/api/controllers/departments.pytests/unit/engine/workflow/test_diff.pytests/unit/api/controllers/test_workflow_versions.pysrc/synthorg/api/controllers/workflows.pytests/unit/api/fakes_backend.pysrc/synthorg/engine/workflow/diff.pytests/unit/persistence/test_protocol.pysrc/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/migrations.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/budget_config_versions.pytests/unit/persistence/sqlite/test_migrations.pytests/unit/api/controllers/test_config_version_controllers.pytests/unit/persistence/sqlite/test_entity_version_repos.pysrc/synthorg/api/services/org_mutations.pytests/unit/api/fakes_workflow.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Do NOT import from
__future__ import annotations-- Python 3.14 has PEP 649 native lazy annotationsUse PEP 758 except syntax: use
except A, B:(no parentheses) -- ruff enforces this on Python 3.14Type hints required on all public functions, enforced with mypy strict mode
Docstrings must use Google style format and are required on all public classes and functions (enforced by ruff D rules)
Create new objects for immutability, never mutate existing ones. For non-Pydantic internal collections (registries,
BaseTool), usecopy.deepcopy()at construction +MappingProxyTypewrapping for read-only enforcementFor
dict/listfields in frozen Pydantic models, usefrozen=Truefor field reassignment prevention andcopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, serializing for persistence)Use frozen Pydantic models for config and identity; use separate mutable-via-copy models (using
model_copy(update=...)) for runtime state that evolves (e.g. agent execution state, task progress). Never mix static config fields with mutable runtime fields in one modelUse Pydantic v2 with:
BaseModel,model_validator,computed_field,ConfigDict. Useallow_inf_nan=Falsein allConfigDictdeclarations to rejectNaN/Infin numeric fields at validation timeUse
@computed_fieldfor derived values instead of storing + validating redundant fields (e.g.TokenUsage.total_tokens)Use
NotBlankStrfromcore.typesfor all identifier/name fields -- including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants -- instead of manual whitespace validatorsPrefer
asyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (e.g. multiple tool invocations, parallel agent calls). Prefer structured concurrency over barecreate_taskKeep functions under 50 lines and files under 800 lines
Handle errors explicitly, never silently swallow exceptions
Validate at system...
Files:
src/synthorg/observability/events/versioning.pysrc/synthorg/persistence/sqlite/version_repo.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/company.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/migrations.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/api/services/org_mutations.py
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
Never use
cdin Bash commands -- the working directory is already set to the project root. Use absolute paths or run commands directly. Exception:bash -c "cd <dir> && <cmd>"is safe (runs in a child process). Use this for tools without a-CflagNever use Bash to write or modify files -- use the Write or Edit tools instead. Do not use
cat >,cat << EOF,echo >,echo >>,sed -i,python -c "open(...).write(...)", ortee` to create or modify files
Files:
src/synthorg/observability/events/versioning.pyweb/src/pages/workflow-editor/VersionHistoryPanel.tsxsrc/synthorg/persistence/sqlite/version_repo.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/company.pysrc/synthorg/api/controllers/departments.pytests/unit/engine/workflow/test_diff.pytests/unit/api/controllers/test_workflow_versions.pydocs/design/engine.mdsrc/synthorg/api/controllers/workflows.pytests/unit/api/fakes_backend.pysrc/synthorg/engine/workflow/diff.pytests/unit/persistence/test_protocol.pyCLAUDE.mdsrc/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/migrations.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/budget_config_versions.pytests/unit/persistence/sqlite/test_migrations.pytests/unit/api/controllers/test_config_version_controllers.pyweb/src/api/types.tstests/unit/persistence/sqlite/test_entity_version_repos.pysrc/synthorg/api/services/org_mutations.pytests/unit/api/fakes_workflow.pysrc/synthorg/persistence/sqlite/schema.sql
src/**/*.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/observability/events/versioning.pysrc/synthorg/persistence/sqlite/version_repo.pysrc/synthorg/api/state.pysrc/synthorg/api/controllers/company.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/engine/workflow/diff.pysrc/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/migrations.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/api/services/org_mutations.py
web/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx,js,jsx}: Always usecreateLoggerfrom@/lib/logger-- never bareconsole.warn/console.error/console.debugin application code
Logger variable name must always beconst log(e.g.const log = createLogger('module-name'))
Pass dynamic/untrusted values as separate arguments to logger methods (not interpolated into the message string) so they go throughsanitizeArg
Attacker-controlled fields inside structured objects must be wrapped insanitizeForLog()before embedding in log calls
Files:
web/src/pages/workflow-editor/VersionHistoryPanel.tsxweb/src/api/types.ts
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground,bg-card,text-accent,text-success,bg-danger, etc.) or CSS variables (var(--so-*)) for colors; NEVER hardcode hex values in.tsx/.tsfiles
Usefont-sansorfont-mono(Geist tokens) for typography; NEVER setfontFamilydirectly in.tsx/.tsfiles
Use density-aware tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind spacing; NEVER hardcode pixel values for layout spacing in components
Use token variables (var(--so-shadow-card-hover),border-border,border-bright) for shadows and borders; NEVER hardcode values in.tsx/.tsfiles
Use@/lib/motionpresets for Framer Motion transition durations; NEVER hardcode transition durations
CSS side-effect imports in TypeScript 6 require type declarations -- add/// <reference types="vite/client" />at the top of files with CSS importsAlways reuse existing components from
web/src/components/ui/before creating new onesNever hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions -- use design tokens and
@/lib/motionpresets
Files:
web/src/pages/workflow-editor/VersionHistoryPanel.tsxweb/src/api/types.ts
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Mark tests with:
@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, or@pytest.mark.slowCoverage minimum 80% (enforced in CI)
Use
asyncio_mode = "auto"-- no manual@pytest.mark.asyncioneededGlobal timeout: 30 seconds per test (set in
pyproject.toml). Do not add per-filepytest.mark.timeout(30)markers; only use non-default overrides liketimeout(60)when explicitly neededPrefer
@pytest.mark.parametrizefor testing similar casesNever use real vendor names (Anthropic, OpenAI, Claude, GPT, etc.) in project-owned code, docstrings, comments, tests, or config examples. Use generic names:
example-provider,example-large-001,example-medium-001,example-small-001,large/medium/smallas aliases. Tests must usetest-provider,test-small-001, etc.When Hypothesis finds a failure, it is a real bug. Add an explicit
@example(...)decorator to permanently cover the case in CI. Do NOT just rerun and move onNever skip, dismiss, or ignore flaky tests -- always fix them fully and fundamentally. For timing-sensitive tests, mock
time.monotonic()andasyncio.sleep()to make them deterministic. For tasks that must block indefinitely, useasyncio.Event().wait()instead ofasyncio.sleep(large_number)Always include
-n 8when running pytest locally, never run tests sequentially. CI uses-n auto. This is enforced by hookify enforce-parallel-tests rule
Files:
tests/unit/engine/workflow/test_diff.pytests/unit/api/controllers/test_workflow_versions.pytests/unit/api/fakes_backend.pytests/unit/persistence/test_protocol.pytests/unit/persistence/sqlite/test_migrations.pytests/unit/api/controllers/test_config_version_controllers.pytests/unit/persistence/sqlite/test_entity_version_repos.pytests/unit/api/fakes_workflow.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/engine/workflow/test_diff.pytests/unit/api/controllers/test_workflow_versions.pytests/unit/api/fakes_backend.pytests/unit/persistence/test_protocol.pytests/unit/persistence/sqlite/test_migrations.pytests/unit/api/controllers/test_config_version_controllers.pytests/unit/persistence/sqlite/test_entity_version_repos.pytests/unit/api/fakes_workflow.py
🧠 Learnings (64)
📓 Common learnings
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T22:41:30.687Z
Learning: Every implementation plan must be presented to the user for accept/deny before coding starts. At every phase of planning and implementation, be critical and actively look for ways to improve the design
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T22:41:30.687Z
Learning: When approved deviations from the spec occur, update the relevant `docs/design/` page to reflect the new reality
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T22:41:30.687Z
Learning: Commits must follow format: `<type>: <description>` with types: feat, fix, refactor, docs, test, chore, perf, ci. Enforced by commitizen (commit-msg hook)
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T22:41:30.687Z
Learning: Signed commits required on `main` via branch protection -- all commits must be GPG/SSH signed
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T22:41:30.687Z
Learning: Use branches named `<type>/<slug>` from main (e.g., `feat/add-agent-routing`)
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T22:41:30.687Z
Learning: Never create PRs directly with `gh pr create` -- use `/pre-pr-review` to create PRs. For trivial/docs-only changes, use `/pre-pr-review quick`. This is enforced by hookify block-pr-create rule
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T22:41:30.687Z
Learning: After finishing implementation, always create a feature branch (`<type>/<slug>`), commit, and push -- do NOT create a PR automatically or leave work uncommitted on main
Learnt from: CR
URL:
File: CLAUDE.md:undefined-undefined
Timestamp: 2026-04-08T22:41:30.687Z
Learning: When review agents find valid issues (including pre-existing issues in surrounding code, suggestions, and findings adjacent to changes), fix them all. No deferring, no 'out of scope' skipping
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.stories.{ts,tsx} : Storybook 10: Use storybook/test (not storybook/test) and storybook/actions (not storybook/addon-actions) import paths
Applied to files:
.github/workflows/ci.yml
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/*.yml : Dependabot: daily updates for uv + github-actions + npm + pre-commit + docker + gomod, grouped minor/patch, no auto-merge. Use `/review-dep-pr` to review Dependabot PRs before merging.
Applied to files:
.github/workflows/ci.yml
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/cli.yml : CLI workflow: Go lint (golangci-lint + go vet) + test (-race -coverprofile) + build (cross-compile: linux/darwin/windows × amd64/arm64) + govulncheck + fuzz testing (main-only, 30s/target, continue-on-error, matrix over 4 packages). cli-pass gate includes fuzz as informational. GoReleaser release on v* tags. Cosign keyless signing of checksums.txt. SLSA L3 provenance attestations. Sigstore bundle (.sigstore.json) attached. Post-release appends checksums/verification/provenance to draft release notes.
Applied to files:
.github/workflows/ci.yml.github/workflows/docker.yml
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/docker.yml : Docker workflow: builds backend + web + sandbox images, pushes to GHCR, signs with cosign. SLSA L3 provenance attestations via actions/attest-build-provenance. Scans: Trivy (CRITICAL = hard fail, HIGH = warn) + Grype (critical cutoff) + CIS Docker Benchmark v1.6.0 compliance (informational). CVE triage via .github/.trivyignore.yaml and .github/.grype.yaml. Images only pushed after scans pass. Triggers on push to main and version tags (v*).
Applied to files:
.github/workflows/ci.yml.github/workflows/docker.yml
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to .github/workflows/pages.yml : Pages job: version extraction from `pyproject.toml`, OpenAPI export, Astro + Zensical docs build (with version banner), GitHub Pages deploy on push to main
Applied to files:
.github/workflows/ci.yml
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: CLI workflow (`.github/workflows/cli.yml`) runs Go lint (golangci-lint + go vet) + test (race, coverage) + build (cross-compile matrix) + vulnerability check (govulncheck) + fuzz testing. Cross-compiles for linux/darwin/windows × amd64/arm64. GoReleaser release on v* tags with cosign keyless signing and SLSA L3 attestations.
Applied to files:
.github/workflows/ci.yml.github/workflows/docker.yml
📚 Learning: 2026-03-15T12:00:18.113Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T12:00:18.113Z
Learning: Applies to .github/workflows/docker.yml : CI Docker: build → scan → push to GHCR + cosign sign + SLSA L3 provenance via attest-build-provenance (images only pushed after Trivy/Grype scans pass).
Applied to files:
.github/workflows/ci.yml.github/workflows/docker.yml
📚 Learning: 2026-04-02T08:47:46.313Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T08:47:46.313Z
Learning: Pre-push hooks enforce: mypy type-check + pytest unit tests + golangci-lint + go vet + go test (CLI, conditional) + eslint-web (web dashboard, conditional) — fast gate before push, skipped in pre-commit.ci
Applied to files:
.github/workflows/ci.yml
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Docker workflow (`docker.yml`): build + Trivy/Grype scan + push to GHCR + cosign sign + SLSA L3 provenance. CVE triage: `.github/.trivyignore.yaml`, `.github/.grype.yaml`
Applied to files:
.github/workflows/ci.yml.github/workflows/docker.yml
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: import from `storybook/test` (not `storybook/test`), `storybook/actions` (not `storybook/addon-actions`)
Applied to files:
.github/workflows/ci.yml
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use frozen Pydantic models for config/identity; use separate mutable-via-copy models (via `model_copy(update=...)`) for runtime state that evolves
Applied to files:
src/synthorg/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/api/**/*.py : API package (api/): Litestar REST + WebSocket with controllers, guards, channels, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint, provider management endpoint (CRUD + test + presets), backup endpoint, RFC 9457 structured errors, AppState hot-reload slots, service auto-wiring (Phase 1 at construction, Phase 2 on startup), lifecycle helpers
Applied to files:
src/synthorg/api/controllers/company.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/api/controllers/evaluation_config_versions.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/role_versions.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/api/services/org_mutations.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/api/**/*.py : REST API: Litestar framework, controllers with guards, channels for WebSocket, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint. RFC 9457 structured errors (ErrorCategory, ErrorCode, ErrorDetail, ProblemDetail, CATEGORY_TITLES, category_title, category_type_uri, content negotiation).
Applied to files:
src/synthorg/api/controllers/company.pysrc/synthorg/api/controllers/departments.pysrc/synthorg/api/controllers/workflows.pysrc/synthorg/api/controllers/workflow_versions.pysrc/synthorg/api/controllers/company_versions.pysrc/synthorg/api/services/org_mutations.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/controllers/company.pysrc/synthorg/persistence/protocol.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/persistence/sqlite/backend.pysrc/synthorg/api/services/org_mutations.py
📚 Learning: 2026-03-31T14:28:28.895Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:28:28.895Z
Learning: Applies to docker/**/Dockerfile* : Docker images: backend (Chainguard distroless, non-root), web (nginx-unprivileged, SPA + API proxy), sandbox (Python + Node.js, non-root)
Applied to files:
.github/workflows/docker.yml
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Applies to Dockerfile* : Backend images must use Chainguard distroless with non-root user; web image must use nginx-unprivileged with non-root user; sandbox image must use Python + Node.js with non-root user. All Docker files in `docker/` -- Dockerfiles, compose, `.env.example`. Single root `.dockerignore`. All images build with `context: .`
Applied to files:
.github/workflows/docker.yml
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/finalize-release.yml : Finalize Release workflow: publishes draft releases created by Release Please. Triggers on workflow_run completion of Docker and CLI workflows. Verifies both workflows succeeded for the associated tag before publishing. Extracts CLI checksums, cosign verification, and container verification data from HTML comments, assembles into combined Verification section. Guards against PR-triggered runs. Handles TOCTOU races. Immutable releases enabled—once published, release assets and body cannot be modified.
Applied to files:
.github/workflows/docker.yml
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Dependabot: auto-updates Docker image digests and versions daily.
Applied to files:
.github/workflows/docker.yml
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: CLI verifies cosign signatures + SLSA provenance at pull time; bypass with `--skip-verify`
Applied to files:
.github/workflows/docker.yml
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: CLI workflow (`cli.yml`): Go lint/test/build (cross-compile) + govulncheck + fuzz. GoReleaser release on `v*` tags with cosign signing + SLSA provenance
Applied to files:
.github/workflows/docker.yml
📚 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 docker/Dockerfile.sandbox : Docker sandbox: `synthorg-sandbox` — Python 3.14 + Node.js + git, non-root (UID 10001), agent code execution sandbox
Applied to files:
.github/workflows/docker.yml
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Update the relevant `docs/design/` page to reflect new reality when approved deviations from spec occur
Applied to files:
docs/design/engine.md
📚 Learning: 2026-03-18T08:23:08.912Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T08:23:08.912Z
Learning: When approved deviations occur, update the relevant `docs/design/` page to reflect the new reality.
Applied to files:
docs/design/engine.md
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: Applies to docs/design/*.md : Update the relevant `docs/design/` page when approved deviations occur to reflect the new reality
Applied to files:
docs/design/engine.md
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to docs/design/**/*.md : Design specification pages in `docs/design/` must be consulted before implementing features (7 pages: index, agents, organization, communication, engine, memory, operations)
Applied to files:
docs/design/engine.md
📚 Learning: 2026-04-06T16:35:12.934Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-06T16:35:12.934Z
Learning: All project conventions, commands, and standards are defined in CLAUDE.md - refer to it for project structure, package layout, code conventions, quick commands, git workflow, testing standards, design specifications, logging, resilience, and security patterns
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions. Pure data models, enums, and re-exports do NOT need logging.
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-04-02T12:07:44.443Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T12:07:44.443Z
Learning: Applies to src/synthorg/**/*.py : Always use structured logging: `logger.info(EVENT, key=value)` — never `logger.info('msg %s', val)`
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions.
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/budget_config_versions.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:
CLAUDE.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Use structured logging: always `logger.info(EVENT, key=value)` — never `logger.info("msg %s", val)`
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Structured kwargs in logging: always `logger.info(EVENT, key=value)` — never `logger.info('msg %s', val)`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, and key function entry/exit
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/budget_config_versions.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 : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use `import logging` / `logging.getLogger()` / `print()` in application code. Variable name: always `logger` (not `_logger`, not `log`).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-31T20:07:03.035Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T20:07:03.035Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising; all state transitions must log at INFO; DEBUG for object creation, internal flow, entry/exit of key functions
Applied to files:
CLAUDE.mdsrc/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/persistence/**/*.py : Persistence uses pluggable PersistenceBackend protocol. SQLite is the initial backend. Settings use SettingsRepository (namespaced settings CRUD).
Applied to files:
src/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/backend.pytests/unit/persistence/sqlite/test_entity_version_repos.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Persistence backend: pluggable PersistenceBackend protocol in `src/synthorg/persistence/`, SQLite initial, SettingsRepository (namespaced settings CRUD).
Applied to files:
src/synthorg/persistence/protocol.pysrc/synthorg/persistence/sqlite/backend.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/memory/**/*.py : Use MemoryBackend protocol with pluggable backends (Mem0 adapter available at backends/mem0/) for persistent agent memory
Applied to files:
src/synthorg/persistence/protocol.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/memory/**/*.py : Memory package (memory/): pluggable MemoryBackend protocol, backends/ (Mem0 adapter), retrieval pipeline (ranking, RRF fusion, injection, formatting, non-inferable filtering), shared org memory (org/), consolidation/archival (density-aware: DensityClassifier, AbstractiveSummarizer, ExtractivePreserver, DualModeConsolidationStrategy)
Applied to files:
src/synthorg/persistence/protocol.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/persistence/protocol.pysrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/budget_config_versions.pysrc/synthorg/api/services/org_mutations.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/persistence/protocol.pysrc/synthorg/api/services/org_mutations.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:
src/synthorg/persistence/protocol.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/persistence/protocol.pysrc/synthorg/api/services/org_mutations.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/persistence/protocol.py
📚 Learning: 2026-04-08T20:20:05.318Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-08T20:20:05.318Z
Learning: Applies to src/synthorg/**/*.py : If implementation deviates from the design spec (better approach found, scope evolved, etc.), alert the user and explain why before proceeding; do NOT silently diverge
Applied to files:
src/synthorg/persistence/sqlite/migrations.pysrc/synthorg/api/services/org_mutations.py
📚 Learning: 2026-04-02T07:18:02.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T07:18:02.381Z
Learning: Applies to {pyproject.toml,src/synthorg/__init__.py} : Update version in `pyproject.toml` (`[tool.commitizen].version`) and `src/synthorg/__init__.py` (`__version__`)
Applied to files:
src/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/workflow_versions.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/controllers/__init__.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/persistence/sqlite/backend.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/persistence/sqlite/backend.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/synthorg/api/**/*.py : Litestar API must include setup wizard, auth/, auto-wiring, and lifecycle management
Applied to files:
src/synthorg/api/controllers/workflow_versions.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/api/**/*.py : Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
Applied to files:
src/synthorg/api/controllers/workflow_versions.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : All state transitions must log at INFO.
Applied to files:
src/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : All error paths must log at WARNING or ERROR with context before raising.
Applied to files:
src/synthorg/api/controllers/budget_config_versions.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use DEBUG logging for object creation, internal flow, and entry/exit of key functions.
Applied to files:
src/synthorg/api/controllers/budget_config_versions.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/api/controllers/budget_config_versions.pysrc/synthorg/api/services/org_mutations.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : All state transitions must log at INFO level
Applied to files:
src/synthorg/api/controllers/budget_config_versions.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/services/org_mutations.py
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to src/synthorg/**/*.py : `RetryConfig` and `RateLimiterConfig` are set per-provider in `ProviderConfig`. Retryable errors: `RateLimitError`, `ProviderTimeoutError`, `ProviderConnectionError`, `ProviderInternalError`. Non-retryable errors raise immediately.
Applied to files:
src/synthorg/api/services/org_mutations.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Use `NotBlankStr` from `core.types` for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators
Applied to files:
tests/unit/api/fakes_workflow.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:
tests/unit/api/fakes_workflow.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 with adopted conventions: use computed_field for derived values instead of storing + validating redundant fields; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual whitespace validators.
Applied to files:
tests/unit/api/fakes_workflow.py
📚 Learning: 2026-03-31T20:07:03.035Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T20:07:03.035Z
Learning: Applies to **/*.py : Use `NotBlankStr` (from `core.types`) for all identifier/name fields, including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants, instead of manual whitespace validators
Applied to files:
tests/unit/api/fakes_workflow.py
| set -o pipefail | ||
| if ! cosign sign --yes ghcr.io/aureliolo/synthorg-backend@${DIGEST} 2>&1 | tee /tmp/cosign-out; then | ||
| if grep -q 'createLogEntryConflict' /tmp/cosign-out; then | ||
| echo "::notice::Image already signed in transparency log -- skipping" | ||
| else | ||
| cat /tmp/cosign-out | ||
| exit 1 | ||
| fi | ||
| fi |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify each "Sign image" block suppresses createLogEntryConflict
# and whether it performs a cosign verify before continuing.
python - <<'PY'
from pathlib import Path
import re
text = Path(".github/workflows/docker.yml").read_text()
blocks = re.finditer(
r'- name: Sign image.*?\n\s+run: \|\n(.*?)(?=\n\s+- name: |\n\s*$)',
text,
flags=re.S
)
for i, m in enumerate(blocks, 1):
b = m.group(1)
print(f"Block {i}:")
print(" has createLogEntryConflict handling:", "createLogEntryConflict" in b)
print(" has cosign verify:", "cosign verify" in b)
print()
PYRepository: Aureliolo/synthorg
Length of output: 304
Add signature verification to the Rekor conflict-handling branch.
All three "Sign image" steps (backend, web, sandbox) suppress cosign sign failures when Rekor reports a createLogEntryConflict. However, the conflict branch does not verify that the digest is actually signed. A failed signing attempt could pass silently. Add cosign verify with OIDC identity and issuer constraints before continuing.
🔧 Suggested hardening pattern (apply to all three sign steps)
set -o pipefail
- if ! cosign sign --yes ghcr.io/aureliolo/synthorg-backend@${DIGEST} 2>&1 | tee /tmp/cosign-out; then
- if grep -q 'createLogEntryConflict' /tmp/cosign-out; then
- echo "::notice::Image already signed in transparency log -- skipping"
+ COSIGN_OUT="$(mktemp)"
+ trap 'rm -f "$COSIGN_OUT"' EXIT
+ if ! cosign sign --yes ghcr.io/aureliolo/synthorg-backend@${DIGEST} 2>&1 | tee "$COSIGN_OUT"; then
+ if grep -q 'createLogEntryConflict' "$COSIGN_OUT"; then
+ echo "::notice::Rekor conflict detected; verifying existing signature"
+ cosign verify \
+ --certificate-identity-regexp='github\.com/Aureliolo/synthorg' \
+ --certificate-oidc-issuer='https://token.actions.githubusercontent.com' \
+ ghcr.io/aureliolo/synthorg-backend@${DIGEST} >/dev/null
else
- cat /tmp/cosign-out
+ cat "$COSIGN_OUT"
exit 1
fi
fiApplies to lines 230–238, 433–441, 636–644.
📝 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.
| set -o pipefail | |
| if ! cosign sign --yes ghcr.io/aureliolo/synthorg-backend@${DIGEST} 2>&1 | tee /tmp/cosign-out; then | |
| if grep -q 'createLogEntryConflict' /tmp/cosign-out; then | |
| echo "::notice::Image already signed in transparency log -- skipping" | |
| else | |
| cat /tmp/cosign-out | |
| exit 1 | |
| fi | |
| fi | |
| set -o pipefail | |
| COSIGN_OUT="$(mktemp)" | |
| trap 'rm -f "$COSIGN_OUT"' EXIT | |
| if ! cosign sign --yes ghcr.io/aureliolo/synthorg-backend@${DIGEST} 2>&1 | tee "$COSIGN_OUT"; then | |
| if grep -q 'createLogEntryConflict' "$COSIGN_OUT"; then | |
| echo "::notice::Rekor conflict detected; verifying existing signature" | |
| cosign verify \ | |
| --certificate-identity-regexp='github\.com/Aureliolo/synthorg' \ | |
| --certificate-oidc-issuer='https://token.actions.githubusercontent.com' \ | |
| ghcr.io/aureliolo/synthorg-backend@${DIGEST} >/dev/null | |
| else | |
| cat "$COSIGN_OUT" | |
| exit 1 | |
| fi | |
| fi |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/docker.yml around lines 230 - 238, The Rekor conflict
branch currently skips on 'createLogEntryConflict' without confirming the image
is actually signed; update the conflict-handling branch that greps for
'createLogEntryConflict' (the block around the cosign sign invocation and
/tmp/cosign-out) to run a cosign verify for the same image digest (${DIGEST})
before treating it as success: call cosign verify with certificate
identity/issuer constraints (e.g. --certificate-identity "<OIDC_SUBJECT>"
--certificate-issuer "<OIDC_ISSUER>") against
ghcr.io/aureliolo/synthorg-backend@${DIGEST} and fail the step if verify returns
non-zero, otherwise print a notice and continue; apply this same change to the
other two sign steps that use the same pattern (grep 'createLogEntryConflict' on
/tmp/cosign-out).
|
@CodeRabbit review |
✅ Actions performedReview triggered.
|
Summary
Migrates bespoke
WorkflowDefinitionVersionto genericVersionSnapshot[T]and extends versioning infrastructure to 5 entity types total.#1131: Migrate workflow versioning to generic VersionSnapshot[WorkflowDefinition]
WorkflowDefinitionVersionmodel,WorkflowVersionRepositoryprotocol,SQLiteWorkflowVersionRepository(entity_id, version, content_hash, snapshot)formatcompute_diff()to acceptVersionSnapshot[WorkflowDefinition], unwrapping at top levelbuild_version_snapshot()withVersioningService.snapshot_if_changed()at all 4 mutation sites#1132: Add versioning to EvaluationConfig, BudgetConfig, CompanyStructure
SQLiteVersionRepositoryinstances with propertiesOrgMutationService._snapshot_budget_config()called inside_org_lockafter budget mutations#1133: Add versioning to Role Catalog definitions
role_versionstableSQLiteVersionRepository[Role]wiredTest plan
test_entity_version_repos.py: round-trip serialization for all 5 entity types + content-hash deduptest_config_version_controllers.py: 11 tests covering list/get/pagination/404 for all 3 new controllerstest_diff.py,test_workflow_versions.py,test_migrations.py,test_protocol.py, fakesReview coverage
Pre-reviewed by 13 agents, 3 findings addressed:
_snapshot_budget_config()inside_org_lockCloses #1131
Closes #1132
Closes #1133