feat: per-initiative operator-set autonomy mode#2579
Conversation
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI (base), Organization UI (inherited) Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📜 Recent review details⏰ Context from checks skipped due to timeout. (34)
🧰 Additional context used📓 Path-based instructions (6)web/src/**/*.{ts,tsx}📄 CodeRabbit inference engine (web/CLAUDE.md)
Files:
web/src/stores/**/*.{ts,tsx}📄 CodeRabbit inference engine (web/CLAUDE.md)
Files:
web/**/*.{js,jsx,ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.{py,js,jsx,ts,tsx,go}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
web/src/**/*.{test,spec}.{ts,tsx}📄 CodeRabbit inference engine (web/CLAUDE.md)
Files:
🧠 Learnings (9)📚 Learning: 2026-05-28T22:27:09.037ZApplied to files:
📚 Learning: 2026-05-28T22:27:14.761ZApplied to files:
📚 Learning: 2026-05-28T22:27:25.293ZApplied to files:
📚 Learning: 2026-05-29T15:33:54.576ZApplied to files:
📚 Learning: 2026-05-29T15:35:11.914ZApplied to files:
📚 Learning: 2026-05-31T00:00:32.734ZApplied to files:
📚 Learning: 2026-06-13T14:07:55.334ZApplied to files:
📚 Learning: 2026-06-25T14:59:52.946ZApplied to files:
📚 Learning: 2026-06-05T10:17:59.769ZApplied to files:
🔇 Additional comments (2)
WalkthroughAdds nullable project-level autonomy modes with persistence, four-level resolution, guarded API updates, audit events, WebSocket propagation, worker integration, and fail-closed lookup behavior. The web application adds an oversight-mode selector with CEO-confirmed full mode, optimistic request handling, and synchronized project state. Project deletion cascade logic is moved into a dedicated controller module for plan and task termination. Documentation and tests cover the new contracts and flows. Suggested labels: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request implements a granular autonomy management system, enabling operators to define oversight modes at the initiative level. By extending the existing autonomy resolution chain, the system now evaluates project-specific settings alongside agent, department, and company defaults. The implementation includes a robust API for updates, strict authorization for sensitive 'full' mode transitions, and comprehensive fail-closed safety checks to maintain security integrity even during transient persistence faults. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a four-level autonomy resolution chain by adding a per-initiative (project-level) operator-set autonomy mode override, resolving below per-agent overrides and above department/company defaults. It includes database schema migrations, API updates with a new PATCH endpoint, worker execution service integration, and frontend UI controls. The review feedback highlights a potential fail-open security risk where an absent project lookup falls back to defaults, suggesting instead to fail closed to LOCKED autonomy.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| error=safe_error_description(exc), | ||
| ) | ||
| return AutonomyLevel.LOCKED | ||
| return project.autonomy_mode if project is not None else None |
There was a problem hiding this comment.
If a task is associated with a project_id but the project cannot be found in the repository (i.e., project is None), returning None will cause the resolver to fall back to the department or company default. If the default is permissive (e.g., full or semi), this could lead to a fail-open scenario for an orphaned or anomalous task.
To align with the fail-closed philosophy, consider treating an absent project as a lookup failure and failing closed to AutonomyLevel.LOCKED. Note that if you apply this change, you will also need to update the corresponding test test_absent_project_returns_none in tests/unit/workers/test_execution_service.py to expect AutonomyLevel.LOCKED instead of None.
| return project.autonomy_mode if project is not None else None | |
| if project is None: | |
| logger.warning( | |
| WORKERS_EXECUTION_SERVICE_AUTONOMY_DEGRADED, | |
| project_id=project_id, | |
| reason="project_not_found", | |
| fail_closed_to=AutonomyLevel.LOCKED.value, | |
| ) | |
| return AutonomyLevel.LOCKED | |
| return project.autonomy_mode |
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/synthorg/api/controllers/_project_autonomy.py`:
- Around line 36-39: Update the mode field declaration in the project autonomy
request model to remain nullable while requiring callers to provide it
explicitly; remove its default=None value and preserve the existing description
and type.
In `@src/synthorg/api/controllers/projects.py`:
- Around line 231-235: Update the project mutation flow around expected_version
to require the client-provided version instead of falling back to
project.version. Reject omitted expected_version consistently, and update the
web caller to send the displayed project version with the request so stale
writes produce the intended conflict response.
- Around line 221-227: Update the project autonomy change flow around
guard_full_autonomy_optin to resolve the effective post-change mode when
data.mode is None, including inherited department or company settings, and pass
that resolved target to the guard. Perform this resolution before persisting the
change and ensure audit data reflects whether the effective mode is full, so
clearing an override cannot bypass CEO confirmation.
In `@tests/conformance/persistence/test_project_repository.py`:
- Around line 59-75: Update the mode parameter list in
test_autonomy_mode_set_round_trips to include AutonomyLevel.SEMI, add its
matching “semi” pytest ID, and preserve the existing real-backend round-trip
assertions for every autonomy tier.
In `@tests/unit/api/controllers/test_projects.py`:
- Around line 190-207: Extend test_set_autonomy_mode_version_conflict after the
rejected PATCH by fetching the project with the existing async_test_client and
authentication headers, then assert autonomy_mode remains unchanged and,
preferably, version is unchanged from its initial value. Keep the existing 409
and success assertions while verifying the failed version-guarded write did not
mutate the row.
In `@web/src/api/types/websocket/provider.ts`:
- Around line 35-38: Update WsProjectAutonomyModeChangedPayload so new_mode and
previous_mode use the Project['autonomy_mode'] union type, preserving their
optional and nullable behavior instead of accepting arbitrary strings.
In `@web/src/mocks/handlers/projects.ts`:
- Line 6: Change the setProjectAutonomyMode import in the mock handlers module
to a type-only import, preserving its existing typeof usage and removing it from
the runtime dependency graph.
In `@web/src/stores/projects/crud-actions.ts`:
- Line 271: Include the current project version as expected_version in the
setProjectAutonomyModeApi call within the autonomy-mode update flow, preserving
mode and confirm. Update the mock handler in web/src/mocks/handlers/projects.ts
(lines 66-76) and the related tests in web/src/__tests__/stores/projects.test.ts
(lines 347-433) to accept and verify the version-aware request.
In `@web/src/stores/projects/ws-handler.ts`:
- Around line 64-75: The project.autonomy_mode_changed handler should
distinguish a legitimate null clear from an invalid mode. In the event block
around sanitizeWsEnumOrNull and applyAutonomyModeChanged, preserve raw null as
null without enum validation, but return early when a non-null payload.new_mode
fails sanitization; only call applyAutonomyModeChanged for valid modes or an
explicit null.
🪄 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 (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 08efbd10-f55d-4897-8d3c-b2c560f0f8fb
📒 Files selected for processing (51)
docs/architecture/decisions.mddocs/design/page-structure.mddocs/design/security.mddocs/guides/security.mdsrc/synthorg/api/controllers/_project_autonomy.pysrc/synthorg/api/controllers/_project_cascade.pysrc/synthorg/api/controllers/projects.pysrc/synthorg/api/guards.pysrc/synthorg/api/rate_limits/policies.pysrc/synthorg/api/services/project_service.pysrc/synthorg/api/ws_models.pysrc/synthorg/api/ws_payloads/__init__.pysrc/synthorg/api/ws_payloads/_domain.pysrc/synthorg/core/project.pysrc/synthorg/observability/events/api.pysrc/synthorg/persistence/postgres/project_repo.pysrc/synthorg/persistence/postgres/revisions/20260715000000_project_autonomy_mode.sqlsrc/synthorg/persistence/postgres/schema.sqlsrc/synthorg/persistence/sqlite/project_repo.pysrc/synthorg/persistence/sqlite/revisions/20260715000000_project_autonomy_mode.sqlsrc/synthorg/persistence/sqlite/schema.sqlsrc/synthorg/security/autonomy/resolver.pysrc/synthorg/workers/execution_resume.pysrc/synthorg/workers/execution_service/_agent_engine.pysrc/synthorg/workers/execution_service/_autonomy.pysrc/synthorg/workers/runtime_builder.pytests/conformance/persistence/test_project_repository.pytests/unit/api/controllers/test_projects.pytests/unit/core/test_project.pytests/unit/security/autonomy/test_resolver.pytests/unit/workers/test_execution_service.pytests/unit/workers/test_runtime_builder.pyweb/src/__tests__/helpers/factories.tsweb/src/__tests__/pages/ProjectDetailPage.test.tsxweb/src/__tests__/pages/ProjectOversightSection.test.tsxweb/src/__tests__/stores/projects.test.tsweb/src/api/endpoints/projects.tsweb/src/api/types/backend-enums.gen.tsweb/src/api/types/dtos.gen.tsweb/src/api/types/openapi.gen.tsweb/src/api/types/projects.tsweb/src/api/types/websocket/core.tsweb/src/api/types/websocket/provider.tsweb/src/mocks/handlers/projects.tsweb/src/pages/ProjectDetailPage.tsxweb/src/pages/projects/ProjectOversightSection.tsxweb/src/stores/projects.tsweb/src/stores/projects/_state.tsweb/src/stores/projects/crud-actions.tsweb/src/stores/projects/types.tsweb/src/stores/projects/ws-handler.ts
| createProject, | ||
| getProject, | ||
| listProjects, | ||
| setProjectAutonomyMode, |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Import the endpoint symbol as type-only.
setProjectAutonomyMode is used only through typeof; keep it out of the mock handler’s runtime dependency graph.
Proposed fix
-import {
- setProjectAutonomyMode,
-} from '`@/api/endpoints/projects`'
+import type {
+ setProjectAutonomyMode,
+} from '`@/api/endpoints/projects`'As per coding guidelines, “MSW handlers … import endpoint types with import type only.”
📝 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.
| setProjectAutonomyMode, | |
| import type { | |
| setProjectAutonomyMode, | |
| } from '`@/api/endpoints/projects`' |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/src/mocks/handlers/projects.ts` at line 6, Change the
setProjectAutonomyMode import in the mock handlers module to a type-only import,
preserving its existing typeof usage and removing it from the runtime dependency
graph.
Source: Coding guidelines
Adds Project.autonomy_mode resolved through a four-level chain (agent > initiative > department > company) into the SecOps gate, a CEO-only confirmed opt-in for the gate-off full mode, a dedicated audit event, version-guarded writes, a live WS broadcast, and the project oversight-mode control. Pre-reviewed by 15 agents; all valid findings addressed.
398ba30 to
1b44a01
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2579 +/- ##
========================================
Coverage 89.08% 89.08%
========================================
Files 3283 3286 +3
Lines 168910 169014 +104
========================================
+ Hits 150467 150570 +103
- Misses 18443 18444 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/guides/security.md`:
- Line 241: Update the resolution-chain text adjacent to the department override
guidance to include the existing project-level override as the fallback after
clearing a department override, before the company default. Preserve the
documented precedence order and adjust only the fallback description.
In `@src/synthorg/api/controllers/_project_cascade.py`:
- Around line 45-47: Update the project teardown path and task/plan creation
paths to use a shared deleting or lock state for the project. Mark the project
as deleting before scanning children, have child creation reject or wait while
that state is active, and remove the state only after the project row and
cascade complete, preventing children from being created between the final scan
and deletion.
In `@web/src/pages/projects/ProjectOversightSection.tsx`:
- Around line 80-82: Update the full-mode confirmation handler in
ProjectOversightSection to return the setAutonomyMode result as a boolean,
mapping the store’s null failure sentinel to false so ConfirmDialog stays open
when the PATCH fails while successful updates close it normally. Add a
regression test covering the failed full-mode update and retryable dialog
behavior.
In `@web/src/stores/projects/_state.ts`:
- Line 3: Replace the module-wide _autonomyModeRequestToken with per-project
token storage, and update setAutonomyModeImpl to pass the project ID when
creating and validating request tokens. Ensure each project’s latest request
invalidates only earlier requests for that same project, while updates for
different projects remain independent.
🪄 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 (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: c42fe610-fcc0-465e-9224-53c86093d99e
📒 Files selected for processing (52)
docs/architecture/decisions.mddocs/design/page-structure.mddocs/design/security.mddocs/guides/security.mdsrc/synthorg/api/controllers/_project_autonomy.pysrc/synthorg/api/controllers/_project_cascade.pysrc/synthorg/api/controllers/projects.pysrc/synthorg/api/guards.pysrc/synthorg/api/rate_limits/policies.pysrc/synthorg/api/services/project_service.pysrc/synthorg/api/ws_models.pysrc/synthorg/api/ws_payloads/__init__.pysrc/synthorg/api/ws_payloads/_domain.pysrc/synthorg/core/project.pysrc/synthorg/observability/events/api.pysrc/synthorg/persistence/postgres/project_repo.pysrc/synthorg/persistence/postgres/revisions/20260715000000_project_autonomy_mode.sqlsrc/synthorg/persistence/postgres/schema.sqlsrc/synthorg/persistence/sqlite/project_repo.pysrc/synthorg/persistence/sqlite/revisions/20260715000000_project_autonomy_mode.sqlsrc/synthorg/persistence/sqlite/schema.sqlsrc/synthorg/persistence/state.pysrc/synthorg/security/autonomy/resolver.pysrc/synthorg/workers/execution_resume.pysrc/synthorg/workers/execution_service/_agent_engine.pysrc/synthorg/workers/execution_service/_autonomy.pysrc/synthorg/workers/runtime_builder.pytests/conformance/persistence/test_project_repository.pytests/unit/api/controllers/test_projects.pytests/unit/core/test_project.pytests/unit/security/autonomy/test_resolver.pytests/unit/workers/test_execution_service.pytests/unit/workers/test_runtime_builder.pyweb/src/__tests__/helpers/factories.tsweb/src/__tests__/pages/ProjectDetailPage.test.tsxweb/src/__tests__/pages/ProjectOversightSection.test.tsxweb/src/__tests__/stores/projects.test.tsweb/src/api/endpoints/projects.tsweb/src/api/types/backend-enums.gen.tsweb/src/api/types/dtos.gen.tsweb/src/api/types/openapi.gen.tsweb/src/api/types/projects.tsweb/src/api/types/websocket/core.tsweb/src/api/types/websocket/provider.tsweb/src/mocks/handlers/projects.tsweb/src/pages/ProjectDetailPage.tsxweb/src/pages/projects/ProjectOversightSection.tsxweb/src/stores/projects.tsweb/src/stores/projects/_state.tsweb/src/stores/projects/crud-actions.tsweb/src/stores/projects/types.tsweb/src/stores/projects/ws-handler.ts
| ### Set a department-level override | ||
|
|
||
| Resolution chain: per-agent > per-department > company default. To set a department-wide override: | ||
| Resolution chain: per-agent > per-initiative > per-department > company default. To set a department-wide override: |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Qualify the department-clear fallback.
After adding the per-initiative layer, clearing a department override does not always fall directly to the company default: an existing project override still takes precedence. Update the adjacent fallback text to describe the next less-specific configured level.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/guides/security.md` at line 241, Update the resolution-chain text
adjacent to the department override guidance to include the existing
project-level override as the fallback after clearing a department override,
before the company default. Preserve the documented precedence order and adjust
only the fallback description.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/synthorg/api/controllers/projects.py (1)
249-253: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick winRestore
expected_versionon the request DTO.
ProjectAutonomyModeRequestnow defines onlymodeandconfirm, but this handler still readsdata.expected_version. Withextra="forbid", supplied versions are rejected before the handler; omitted versions reach this line and raiseAttributeError, breaking every successful PATCH path.Proposed fix
class ProjectAutonomyModeRequest(BaseModel): mode: AutonomyLevel | None = Field(...) confirm: bool = False + expected_version: int | None = Field(default=None, ge=1)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/synthorg/api/controllers/projects.py` around lines 249 - 253, Restore the expected_version field on the ProjectAutonomyModeRequest DTO so the handler’s data.expected_version access is valid. Make it optional with the appropriate version type and preserve extra="forbid", allowing omitted values to fall back to project.version while accepting supplied versions.
♻️ Duplicate comments (1)
src/synthorg/api/controllers/projects.py (1)
225-240: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy liftResolve department inheritance before guarding the transition.
The fallback skips the documented department layer. If the department resolves to
FULLwhile the company default is gated, clearing an override is misclassified as gated and bypasses the CEO confirmation guard and WARNING audit.Resolve the effective previous/new modes through the same initiative → department → company machinery used at runtime.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/synthorg/api/controllers/projects.py` around lines 225 - 240, Update the transition setup in the project mode-change controller to resolve effective previous and new modes using the same initiative → department → company inheritance machinery used at runtime, rather than falling back directly to company_default. Ensure department-level FULL values are reflected before the CEO confirmation guard and WARNING audit, while preserving explicit override values and the existing AutonomyModeTransition flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@web/src/stores/projects/crud-actions.ts`:
- Around line 275-278: Update the request construction in the project autonomy
mutation to never create an unguarded payload when current is null. Fetch the
latest project/version before building ProjectAutonomyModeRequest, or fail the
mutation; every successful write must include expected_version while preserving
the existing guarded path.
In `@web/src/stores/projects/ws-handler.ts`:
- Around line 37-47: Update the WebSocket mode-update flow around
applyAutonomyModeChanged to synchronize the project’s version alongside
new_mode. Validate and apply the event’s updated version when available,
including the null-mode path, or refetch the project before updating local
state; do not leave the project with a stale version that causes subsequent
PATCH conflicts.
---
Outside diff comments:
In `@src/synthorg/api/controllers/projects.py`:
- Around line 249-253: Restore the expected_version field on the
ProjectAutonomyModeRequest DTO so the handler’s data.expected_version access is
valid. Make it optional with the appropriate version type and preserve
extra="forbid", allowing omitted values to fall back to project.version while
accepting supplied versions.
---
Duplicate comments:
In `@src/synthorg/api/controllers/projects.py`:
- Around line 225-240: Update the transition setup in the project mode-change
controller to resolve effective previous and new modes using the same initiative
→ department → company inheritance machinery used at runtime, rather than
falling back directly to company_default. Ensure department-level FULL values
are reflected before the CEO confirmation guard and WARNING audit, while
preserving explicit override values and the existing AutonomyModeTransition
flow.
🪄 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 (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 99e6dd81-909a-46b3-967c-1784f98c163a
📒 Files selected for processing (16)
docs/guides/security.mdsrc/synthorg/api/controllers/_project_autonomy.pysrc/synthorg/api/controllers/projects.pysrc/synthorg/workers/execution_service/_autonomy.pytests/conformance/persistence/test_project_repository.pytests/unit/api/controllers/test_project_autonomy.pytests/unit/api/controllers/test_projects.pytests/unit/workers/test_execution_service.pyweb/src/__tests__/pages/ProjectOversightSection.test.tsxweb/src/__tests__/stores/projects.test.tsweb/src/api/types/openapi.gen.tsweb/src/api/types/websocket/provider.tsweb/src/pages/projects/ProjectOversightSection.tsxweb/src/stores/projects/_state.tsweb/src/stores/projects/crud-actions.tsweb/src/stores/projects/ws-handler.ts
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/src/stores/projects/ws-handler.ts (1)
43-60: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winDo not apply autonomy changes without a valid
new_version.When
new_versionis missing or malformed,newVersionbecomesnull, but the handler still updatesautonomy_modewhile retaining the stale local version. The backend payload requires this revision, and the next guarded PATCH can therefore spuriously return 409.Drop or refetch the event before applying either a mode change or an override clear when
newVersion === null, and add regression coverage for invalid versions.Suggested guard
const newVersion = sanitizeWsVersion(payload.new_version) + if (newVersion === null) return + if (payload.new_mode === null) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/stores/projects/ws-handler.ts` around lines 43 - 60, Validate newVersion immediately after sanitizeWsVersion in the autonomy-change handler and return or trigger the existing refetch path when it is null. Ensure neither the null override branch nor the sanitized newMode branch calls applyAutonomyModeChanged without a valid version, and add regression coverage for missing or malformed new_version values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@web/src/stores/projects/ws-handler.ts`:
- Around line 43-60: Validate newVersion immediately after sanitizeWsVersion in
the autonomy-change handler and return or trigger the existing refetch path when
it is null. Ensure neither the null override branch nor the sanitized newMode
branch calls applyAutonomyModeChanged without a valid version, and add
regression coverage for missing or malformed new_version values.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: f99472d6-88dd-4dc3-a388-3b279ecd2559
📒 Files selected for processing (6)
data/runtime_stats.yamlsrc/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.pyweb/src/__tests__/stores/projects.test.tsweb/src/api/types/websocket/provider.tsweb/src/stores/projects/ws-handler.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (32)
- GitHub Check: Dashboard Test (shard 2/4)
- GitHub Check: Dashboard Type Check
- GitHub Check: Dashboard Test (shard 3/4)
- GitHub Check: Dashboard Test (shard 1/4)
- GitHub Check: Dashboard Test (shard 4/4)
- GitHub Check: Dashboard Build
- GitHub Check: Build Backend
- GitHub Check: CodSpeed Web benchmarks
- GitHub Check: CodSpeed Python benchmarks
- GitHub Check: Lighthouse Dashboard
- GitHub Check: Lighthouse Site
- GitHub Check: Test Integration (shard 3)
- GitHub Check: Test Integration (shard 1)
- GitHub Check: Test Conformance (SQLite)
- GitHub Check: Test Integration (shard 4)
- GitHub Check: Test Integration (shard 2)
- GitHub Check: Test Unit (shard 1)
- GitHub Check: Test Unit (shard 3)
- GitHub Check: Dashboard Lint
- GitHub Check: Dashboard E2E (Playwright)
- GitHub Check: Test Unit (shard 4)
- GitHub Check: Test Unit (shard 2)
- GitHub Check: Test E2E
- GitHub Check: Gates (pre-commit all-files + parity)
- GitHub Check: Runtime Stats Freshness Gate
- GitHub Check: Build Web Assets (melange)
- GitHub Check: Build Preview
- GitHub Check: CLI Test (windows-latest)
- GitHub Check: CLI Test (macos-latest)
- GitHub Check: pyright (advisory)
- GitHub Check: Analyze (python)
- GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (10)
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
**/*: Every convention pull request must ship its enforcement gate.
Commits must use<type>: <description>, branches must use<type>/<slug>, and changes must be squash-merged.
Files:
data/runtime_stats.yamlweb/src/api/types/websocket/provider.tssrc/synthorg/api/controllers/projects.pyweb/src/__tests__/stores/projects.test.tssrc/synthorg/api/ws_payloads/_domain.pyweb/src/stores/projects/ws-handler.ts
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx}: The dashboard is a pure API consumer: it must not persist domain or app state client-side, must hydrate from backend GETs on mount, and must write every change through the REST API immediately.
Do not uselocalStorage,sessionStorage,IndexedDB, orzustandpersistfor domain/app state (including setup-wizard state, theme/appearance, and user/org preferences); those values must live in backend settings and be rehydrated from GET/PUT.
Step/progress UI must be derived from backend state, not from a persisted client flag.
The only sanctioned client storage is non-domain transport/UX data: the auth-token cookie shim and the active CSRF token.
UsecreateLoggerfrom@/lib/loggerinstead of bareconsole.*in app code, and uselog.debug(),log.warn(), andlog.error()with dynamic or untrusted values passed as separate args or structured objects sanitized viasanitizeArg/sanitizeForLog().
Pages that need to filter, sort, search, or aggregate across an entire dataset must load the full set withpaginateAlland page it in the browser withuseListPaginationusing?{ns}Page/?{ns}SizeURL params.
Sanitize untrusted WebSocket strings withsanitizeWsString()/sanitizeWsEnum<T>(); never use raw casts, and do not usemakeEnumParser<T>at a WebSocket boundary.
ImportErrorCodeandErrorCategoryfrom@/api/types/errors, and discriminate onErrorCode.<NAME>rather than raw integers.
Reuse existing components fromweb/src/components/ui/before creating new ones; do not hardcode hex colors, fonts, pixel spacing, Motion durations, BCP 47 locale literals, or currency symbols.
Do not usegetXIcon(): LucideIconfactories in JSX; export a<XIcon value={...} />wrapper in its own file instead, and useuseViewportSize()rather than rawwindow.innerWidthin render.
Follow the strict ESLint/type-safety rules: fix types instead of suppressing them, respectexactOptionalPropertyTypesandnoUncheckedIndexedAccess...
Files:
web/src/api/types/websocket/provider.tsweb/src/__tests__/stores/projects.test.tsweb/src/stores/projects/ws-handler.ts
web/src/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Health and readiness callers must treat
getLiveness()as always 200,getReadiness()(/readyz) as 200/503 binary, andgetHealthDetail()(/health) as the full breakdown; new callers must handle 503 explicitly.
Files:
web/src/api/types/websocket/provider.ts
web/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/**/*.{js,jsx,ts,tsx}: In the React dashboard, reuse components fromweb/src/components/ui/and use design tokens only.
The frontend must be a pure API consumer: do not persist application or domain state in client storage; hydrate through GET requests and write changes through the API.
Files:
web/src/api/types/websocket/provider.tsweb/src/__tests__/stores/projects.test.tsweb/src/stores/projects/ws-handler.ts
**/*.{py,js,jsx,ts,tsx,go}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{py,js,jsx,ts,tsx,go}: Do not add AGPL/GPL (non-LGPL) dependencies; attribute permitted LGPL dependencies inNOTICE.
Never use real vendor names in project code or tests; use approved example provider names, except in documented allowlisted locations.
Files:
web/src/api/types/websocket/provider.tssrc/synthorg/api/controllers/projects.pyweb/src/__tests__/stores/projects.test.tssrc/synthorg/api/ws_payloads/_domain.pyweb/src/stores/projects/ws-handler.ts
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.py: Onlysrc/synthorg/persistence/may import SQLite/PostgreSQL libraries or emit raw SQL.
Use the prescribed configuration precedence: database over environment over code defaults for Cat-1, environment over code defaults for Cat-2, and pure environment configuration for Cat-3 bootstrap secrets. Avoidos.environ.getoutside approved startup contexts.
Everyrestart_required=Trueorread_only_post_init=Truesetting must have a per-line justification or be listed in the approved baseline; per-request and per-tick settings must be hot-reloadable.
Keep numeric configuration values insettings/definitions/; only approved numeric literals and annotated module-levelFinalconstants are exceptions.
EachErrorCodemust map to exactly oneDomainErrorsubclass, except for documented inheritance aliases andSHAREABLE_CODESfallbacks.
Do not add stubs undersrc/synthorg/: avoid bareNotImplementedError, empty bodies, stub classes/modules, andstub:sentinel data; implement fully or fail loudly.
Everycost_recording_scope()call must passpurpose=, using aPromptPurposeIdor explicitNone.
Prompt classes tagging LLM chokepoints with a non-Nonepurpose must exposemetadata -> ModelPinMetadatareturningpin_for(self._PURPOSE_ID); pins belong inllm/model_pins.py.
Respect the tiered module-size budgets declared by# module-kind:headers.
Use declarative import-layering contracts and preserve architectural boundaries, including the raw-SQL boundary, DTO boundary, and dependency inversion.
Comments must explain why only; do not include reviewer citations, issue back-references, or migration framing.
Keep type-only imports at module level and useTYPE_CHECKINGonly for genuine cycle breakers.
Use public-function type hints, Google-style docstrings, 88-character lines, and functions shorter than 50 lines.
Domain errors must follow<Domain><Condition>Errorand inherit fromDomainError, never directly fromExceptionor ...
Files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Do not use
from __future__ import annotations; use the specified Python 3.14 exception syntax.
Files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.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/projects.pysrc/synthorg/api/ws_payloads/_domain.py
web/src/**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{test,spec}.{ts,tsx}: Tests must override MSW behavior withserver.use(...)and must notvi.mock('@/api/endpoints/*').
Unit tests run under the active-handle tracker and must not leak event-loop resources fromweb/src/frames.
Files:
web/src/__tests__/stores/projects.test.ts
web/src/stores/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/stores/**/*.{ts,tsx}: Store create/update/delete mutations must follow the CRUD pattern: try/catch, success state update plus success toast, failure logging plus error toast plus sentinel return, with optimistic updates restoringpreviousstate on error and usinggetCrudErrorTitle(err, fallback).
Callers must not wrap store mutation calls in try/catch; the store owns the mutation error UX.
List reads should seterror: string | nullinstead of showing toast errors.
Cursor-paginated list endpoints must use opaquePaginationMetacursors, storenextCursorandhasMore(no offset arithmetic), early-return when!hasMore || !nextCursor, and derive counts fromdata.length.
Files:
web/src/stores/projects/ws-handler.ts
🧠 Learnings (35)
📚 Learning: 2026-06-11T17:01:48.351Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2321
File: .github/actions/start-postgres/action.yml:100-106
Timestamp: 2026-06-11T17:01:48.351Z
Learning: When using Docker CLI v29.5.3+ with `docker tag`, a digest-qualified reference is valid as the SOURCE but not as the TARGET. Specifically, `docker tag SOURCEsha256:<digest> TARGET:tag` should work, while the error like “refusing to create a tag with a digest reference” applies when the digest reference is used as the TARGET (e.g., `docker tag src imagesha256:<digest>`). A digest-qualified source resolves to a locally available image (typically one you’ve pulled) before tagging it with `TARGET:tag`.
Applied to files:
data/runtime_stats.yaml
📚 Learning: 2026-06-25T10:42:32.801Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2473
File: docs/design/self-improvement.md:0-0
Timestamp: 2026-06-25T10:42:32.801Z
Learning: When reviewing documentation or YAML-based assertions around MCP tool counts in Aureliolo/synthorg, treat “domain/published” tool counts and “full-registry” tool counts as intentionally different. The published/domain-modules count under `src/synthorg/meta/mcp/domains/` is 245 tools (245 builder call-sites), and `data/runtime_stats.yaml`’s `mcp_tools` should match 245. By contrast, `build_full_registry().tool_count` is expected to be 246 because the full registry additionally includes the `_demo` regression-guard tool defined in `src/synthorg/_demo/mcp.py` (outside the domain modules). Do not flag mismatches between these two scopes; they are expected by design.
Applied to files:
data/runtime_stats.yaml
📚 Learning: 2026-05-28T22:27:09.037Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2159
File: web/src/__tests__/stores/auth-sessions.test.ts:8-8
Timestamp: 2026-05-28T22:27:09.037Z
Learning: In the frontend, `SessionInfo` is a local type alias for `SessionResponse` defined only in `web/src/api/types/auth.ts` (exported as `SessionResponse as SessionInfo`). It is not re-exported via the `@/api/types` barrel (which only re-exports `dtos.gen` including `SessionResponse`). Therefore, all code (including React components, stores, and tests) that needs `SessionInfo` must import it from `@/api/types/auth` and not from `@/api/types`. (Import `SessionResponse` from `@/api/types` only when `SessionResponse` is what you need.)
Applied to files:
web/src/api/types/websocket/provider.tsweb/src/__tests__/stores/projects.test.tsweb/src/stores/projects/ws-handler.ts
📚 Learning: 2026-05-28T22:27:14.761Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2159
File: web/src/__tests__/stores/backups.test.ts:9-9
Timestamp: 2026-05-28T22:27:14.761Z
Learning: In the synthorg web package, follow the established convention for importing DTOs from the domain-specific sub-barrels under `@/api/types/<domain>`, e.g. `import type { BackupInfo } from '`@/api/types/backup`'`. These domain sub-barrels are curated and re-export from generated DTOs (e.g., `./dtos.gen`), so they are not considered “deep reach” imports. Do not recommend changing these imports to the top-level `@/api/types` barrel (e.g., `@/api/types`)—production code and tests consistently use the domain-specific sub-barrel paths (such as `stores/backups.ts`, `AdminBackupsPage.tsx`, and `mocks/handlers/backup.ts`).
Applied to files:
web/src/api/types/websocket/provider.tsweb/src/__tests__/stores/projects.test.tsweb/src/stores/projects/ws-handler.ts
📚 Learning: 2026-05-28T22:27:25.293Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2159
File: web/src/__tests__/stores/workflows-export.test.ts:7-7
Timestamp: 2026-05-28T22:27:25.293Z
Learning: When reviewing code in this repository, if the change touches or adds types related to workflows, ensure those imports come from the domain-specific curated barrel at `@/api/types/workflows` (e.g., `@/api/types/workflows`), not from the top-level `@/api/types` barrel. Do not recommend switching workflow-related type imports to the top-level barrel; this convention is the established pattern across workflow UI/components, workflow editor store code, related hooks, and workflow-related tests.
Applied to files:
web/src/api/types/websocket/provider.tsweb/src/__tests__/stores/projects.test.tsweb/src/stores/projects/ws-handler.ts
📚 Learning: 2026-05-29T15:33:54.576Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2163
File: web/src/pages/providers/provider-form-helpers.ts:1-9
Timestamp: 2026-05-29T15:33:54.576Z
Learning: In the synthorg web package (web/src), import provider-related DTO types (e.g., AuthType, CloudPreset, CreateFromPresetRequest, CreateProviderRequest, ProviderConfig, ProviderPreset, UpdateProviderRequest) from the domain sub-barrel `@/api/types/providers` and not from the top-level `@/api/types` barrel. These types are not re-exported from `@/api/types`; using the top-level barrel will cause compilation errors. Follow the established convention when updating provider-related code and imports.
Applied to files:
web/src/api/types/websocket/provider.tsweb/src/__tests__/stores/projects.test.tsweb/src/stores/projects/ws-handler.ts
📚 Learning: 2026-05-29T15:35:11.914Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2163
File: web/src/pages/setup/CompanyStep.tsx:18-18
Timestamp: 2026-05-29T15:35:11.914Z
Learning: In the synthorg web package, when using setup-related DTO types (e.g., `SetupAgentSummary`, `SetupCompanyResponse`), import them from the setup domain sub-barrel `@/api/types/setup` rather than from the top-level `@/api/types`. The setup DTOs are not re-exported from `@/api/types`, so importing from the top-level barrel will fail to compile. Follow the existing domain-sub-barrel convention used elsewhere (e.g., `@/api/types/backup`, `@/api/types/workflows`, `@/api/types/auth`) and do not recommend moving setup-domain type imports to `@/api/types`.
Applied to files:
web/src/api/types/websocket/provider.tsweb/src/__tests__/stores/projects.test.tsweb/src/stores/projects/ws-handler.ts
📚 Learning: 2026-05-31T00:00:32.734Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2172
File: web/src/pages/project-brain/BrainEntryList.tsx:12-45
Timestamp: 2026-05-31T00:00:32.734Z
Learning: In the synthorg web package, do not flag React components for ESLint `max-params` violations based on destructured prop fields. ESLint `max-params` counts the number of function parameters (arity), not properties inside a destructured object. For example, `function BrainEntryList({ entries, loading, hasMore, ... }: BrainEntryListProps)` has exactly 1 parameter (the props object) and should not be considered a `max-params: 5` violation, even if the props type/interface contains 10+ fields. Only flag when a function declares 6+ separate parameters.
Applied to files:
web/src/api/types/websocket/provider.tsweb/src/__tests__/stores/projects.test.tsweb/src/stores/projects/ws-handler.ts
📚 Learning: 2026-06-13T14:07:55.334Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2356
File: web/src/components/ui/ws-connection-banner.tsx:30-45
Timestamp: 2026-06-13T14:07:55.334Z
Learning: In the synthorg web package, treat leading underscores on function names as an intentional convention for module-local, non-exported helper/utility functions (e.g., `_resolveBannerKind`, `_handleConflict`, `_statusFallback`, `_hasRequiredCredentials`, `_decideRetryWait`, `_shouldKeepRetrying`). Do not flag these or suggest renaming to remove the underscore. This does not conflict with React hook linting rules (e.g., the rule that React hook/sub-hook helpers must start with `use` and must not use leading underscores); only apply that hook-specific guidance to actual hook helpers, not plain utility functions.
Applied to files:
web/src/api/types/websocket/provider.tsweb/src/__tests__/stores/projects.test.tsweb/src/stores/projects/ws-handler.ts
📚 Learning: 2026-06-25T14:59:52.946Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2476
File: web/CLAUDE.md:31-34
Timestamp: 2026-06-25T14:59:52.946Z
Learning: For the TypeScript/React web dashboard code under web/src (e.g., dashboard utilities/components), follow the web logging convention: import and use `createLogger` from `@/lib/logger`, and name the resulting logger variable `log`. Do not apply the Python backend logging helper pattern (`get_logger` from `src/synthorg/observability/_logger.py` and its `logger` variable) to web TypeScript files.
Applied to files:
web/src/api/types/websocket/provider.tsweb/src/__tests__/stores/projects.test.tsweb/src/stores/projects/ws-handler.ts
📚 Learning: 2026-06-05T10:17:59.769Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2231
File: web/src/pages/org/build-org-tree-types.ts:122-122
Timestamp: 2026-06-05T10:17:59.769Z
Learning: When indexing an exhaustive TypeScript `Record<K, V>` (e.g., `SENIORITY_RANK: Record<SeniorityLevel, number>`) using a value whose parameter/type is the same union key type (e.g., `seniorityOf(level: SeniorityLevel)`), TypeScript already guarantees the key is covered at build time. In this case, do NOT request a runtime `?? fallback` or suggest handling for an `UNKNOWN` key unless the codebase has an explicit/ documented runtime drift or “unrecognized value” contract (like the one handled in `computeCategoryBreakdown` in `web/src/utils/budget.ts`). Also, don’t suggest non-existent keys that are not part of the union type.
Applied to files:
web/src/api/types/websocket/provider.tsweb/src/__tests__/stores/projects.test.tsweb/src/stores/projects/ws-handler.ts
📚 Learning: 2026-05-05T09:04:46.195Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1760
File: scripts/_dual_backend_parity_lib.py:215-216
Timestamp: 2026-05-05T09:04:46.195Z
Learning: This repository targets Python 3.14+ and follows PEP 758. Therefore, reviewer tooling should NOT treat unparenthesized multi-exception `except` clauses written without an `as` clause (e.g., `except MemoryError, RecursionError:`) as syntax errors. Only flag `except`-clause problems when they are genuinely invalid for Python 3.14+.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-05-21T22:55:20.496Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2035
File: src/synthorg/meta/toolsmith/models.py:114-114
Timestamp: 2026-05-21T22:55:20.496Z
Learning: In this repo’s “magic number” review standard, the existing gate in `scripts/check_no_magic_numbers.py` intentionally does NOT flag numeric literals used as raw call-site arguments. So, do not flag numeric literals passed as keyword arguments to Pydantic `Field()` (e.g., `Field(ge=0, le=100)` / `Field(ge=1, le=50)`)—this is an established idiom. Only treat numeric literals as “magic numbers” when they occur in the locations the gate checks (module-level assignments and function/method parameter defaults).
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-05-29T08:50:58.380Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2160
File: src/synthorg/persistence/sqlite/escalation_repo.py:370-370
Timestamp: 2026-05-29T08:50:58.380Z
Learning: In this repo, Ruff flake8-unused-arguments (ARG002) already suppresses unused-argument warnings on parameters of methods decorated with `override` (from `typing`). Therefore, if you see `# noqa: ARG002` (or equivalent) on parameters of an `override`-decorated method, treat it as stale/unused and remove it. Do not recommend re-adding `# noqa: ARG002` in these cases, because Ruff will flag the redundant directive (RUF100) and fail the Ruff CI gate.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-06-09T09:22:47.752Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2283
File: src/synthorg/engine/evolution/config.py:278-280
Timestamp: 2026-06-09T09:22:47.752Z
Learning: This repository uses ruff rule TC006 (`runtime-cast-value`), which requires the quoted string-literal form for the type argument in `cast()` calls (e.g., `cast("object", value)` rather than `cast(object, value)`). During code review, do not suggest removing the quotes from the `cast(<type>, ...)` first argument; the unquoted form will be auto-reverted by ruff on commit, so the quoted form should be treated as lint-compliant.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-06-28T19:03:11.016Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2513
File: src/synthorg/persistence/secret_backends/env_var.py:39-42
Timestamp: 2026-06-28T19:03:11.016Z
Learning: In this repository’s Python code, when cleaning up `property` or `cached_property` docstrings (especially the `Returns:` section), avoid requesting that you inline an “obvious” literal return value into the docstring if the removed code block only restated the annotated return type. Repository convention is to keep property docstrings as one-line summaries and rely on the type annotation for the return type; don’t duplicate what’s already evident from the immediate `return` statement because it restates WHAT (not WHY) and increases doc-rot risk.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-07-05T11:05:42.525Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2539
File: scripts/check_mcp_capability_gap_documented.py:42-71
Timestamp: 2026-07-05T11:05:42.525Z
Learning: Because this repository targets Python 3.14+ (where PEP 649 deferred annotation evaluation is the default), reviewers should not treat TYPE_CHECKING-only imports as errors just because they’re referenced in live/runtime function annotations in the same module. In particular, if an import guarded by `if TYPE_CHECKING:` (e.g., `from collections.abc import Iterable`) is only needed for annotations, it is safe under PEP 649 since annotations are evaluated lazily on demand rather than eagerly at `def` time. Do flag/consider issues only if the module changes annotation behavior (e.g., uses `from __future__ import annotations`) or if the referenced name is used in non-annotation runtime code.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-05-21T22:55:09.289Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2035
File: src/synthorg/meta/toolsmith/config.py:29-30
Timestamp: 2026-05-21T22:55:09.289Z
Learning: For this repo’s Pydantic configuration idiom, do not treat numeric literals passed directly as arguments to `pydantic.Field(...)` as “magic numbers” during review. This includes call-site usages like `Field(default=0.2, ge=0.0, le=1.0)` (e.g., in config models such as `ToolAuthoringConfig`, `ToolValidationConfig`, `ToolsmithConfig`). Do not request extracting those `Field(...)` numeric arguments into named constants, since the repo’s `scripts/check_no_magic_numbers.py` intentionally excludes call-site `Field(...)` numerics and relies on `Field(...)` as the canonical way to express these constraints/defaults.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-06-28T17:04:20.222Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2511
File: src/synthorg/engine/quality/decomposers/llm.py:371-373
Timestamp: 2026-06-28T17:04:20.222Z
Learning: When reviewing calls to `cost_recording_scope(...)` in this repository, do not flag missing prompt-class attribution if the call explicitly passes `purpose=None` as an intentional opt-out path under the `check_cost_scope_purpose` gate. This is sanctioned for prompts that are intentionally outside the registered prompt-class inventory. In particular, if the code comments/documentation indicate the cost is charged to the calling evaluator (not to a registered system prompt class)—for example in `LLMCriteriaDecomposer._invoke_provider` (`src/synthorg/engine/quality/decomposers/llm.py`)—treat the lack of prompt-class attribution as expected and do not raise a review issue.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-07-11T21:02:36.875Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2569
File: src/synthorg/tools/sandbox/docker_sandbox_sidecar.py:55-55
Timestamp: 2026-07-11T21:02:36.875Z
Learning: In synthorg Python code review, treat stub/seam patterns as follows: (1) If a normal (non-interface) method body contains `raise NotImplementedError`, flag it—`scripts/check_no_stubs.py` bans these stubs. (2) Do NOT flag an `...` (Ellipsis) body as unsafe when the method is intentionally interface-only and is enforced to be unreachable unless overridden—specifically methods decorated with `abstractmethod` or `overload`, and methods defined on classes that are `Protocol` (i.e., the method is part of a typing interface). In these cases, the `...` body is the supported no-implementation marker and should be considered safe.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-05-30T18:10:10.435Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2171
File: src/synthorg/api/auth/controllers/sessions_mgmt.py:78-79
Timestamp: 2026-05-30T18:10:10.435Z
Learning: When reviewing Python code under src/synthorg/api, do not flag callsites that use `session_store_of(app_state)` as unsafe/bare field dereferences for missing-session-store scenarios. `session_store_of(app_state)` already routes through `require_service(slice.session_store, 'Session Store')`, which raises `ServiceUnavailableError` (HTTP 503) when the session store is not wired (e.g., JWT-only deployments). The existing 503 guard prevents the missing-store from becoming an unsafe access.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-05-31T18:00:32.445Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2180
File: src/synthorg/engine/intervention/models.py:182-203
Timestamp: 2026-05-31T18:00:32.445Z
Learning: In this repository, `NotBlankStr` is a Pydantic `Annotated[str, ...]` type alias (defined in `synthorg/core/types.py`). At runtime, calling `NotBlankStr(value)` acts like an identity/cast to `str(value)` and does not execute the `StringConstraints` or `AfterValidator(...)`. Therefore, during code review, do not treat `NotBlankStr(x)` used inside non-Pydantic model methods as a place that would raise `ValidationError`; it won’t. Similarly, when `tuple[NotBlankStr, ...]` values are involved, `NotBlankStr` erases to `str` at runtime, so membership tests/comparisons can be done with raw `str` values.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-06-10T12:09:37.293Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2295
File: src/synthorg/project_brain/service.py:77-80
Timestamp: 2026-06-10T12:09:37.293Z
Learning: Do not flag or recommend moving imports out of `if TYPE_CHECKING:` in `src/synthorg` when the imported class is a collaborator type that’s intentionally “concrete-faked” in tests (duck-typed stubs injected into code under constructor/typing annotations). This pattern is used to avoid runtime import side effects where runtime type enforcement (e.g., `typeguard`/`isinstance` checks against the concrete type) would cause the test fakes to be rejected. If the code relies only on annotations for those collaborators and tests provide duck-typed stubs, keep the import under `TYPE_CHECKING` rather than promoting it to a module-level runtime import.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-06-10T12:09:46.221Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2295
File: src/synthorg/settings/dispatcher.py:43-46
Timestamp: 2026-06-10T12:09:46.221Z
Learning: In this repository’s Python modules, do not treat `if TYPE_CHECKING:` imports as violations of any “hoist to module level” rule when they are deliberately used to support duck-typed test fakes (“concrete-faked collaborators”).
Concretely: if the code includes a comment/docstring indicating that tests drive the module using a duck-typed stub (e.g., “Concrete-faked collaborator: tests drive … with a duck-typed … stub, so a runtime import would make typeguard reject the fake”), then keep that collaborator import/type reference under `if TYPE_CHECKING:` rather than importing it at runtime. The intent is to avoid runtime typeguard validation rejecting the test fake; these guards are intentional and should not be flagged.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-06-20T14:08:03.848Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2423
File: src/synthorg/api/lifecycle.py:292-292
Timestamp: 2026-06-20T14:08:03.848Z
Learning: When reviewing calls to `provider_credential_catalog_of(app_state)` (from `src/synthorg/integrations/state.py`), treat it as a soft/optional accessor: it returns `None` when the provider credential catalog is not wired and does not raise. Do not flag such uses as potentially raising exceptions or as hard dependencies that would disable functionality if the catalog is absent. (By contrast, `connection_catalog_of(app_state)` is a hard accessor that raises via `require_service`.)
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-06-28T19:03:23.438Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2513
File: src/synthorg/persistence/secret_backends/sqlite_row_store.py:42-46
Timestamp: 2026-06-28T19:03:23.438Z
Learning: In Aureliolo/synthorg, when reviewing updates to Python `property` or `cached_property` docstrings, keep the docstring as a one-line summary if the removed `Returns:` block only restated the type/value shape already implied by the code.
Do not add back (or request) a fixed literal or value that’s already immediately obvious from the first `return` statement (e.g., if the `return` is `"encrypted_sqlite"`, don’t ask to restate that exact literal in the docstring). Prefer docstrings that explain WHAT and especially WHY/behavior, avoiding duplication that increases doc-rot risk.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-06-28T19:03:57.476Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2513
File: src/synthorg/persistence/secret_backends/postgres_row_store.py:65-69
Timestamp: 2026-06-28T19:03:57.476Z
Learning: For Python code in this repo using `property` and `cached_property`, ensure the docstring follows the repo’s one-line-summary convention documented in `docs/reference/convention-gates.md`. When the prior/removed docstring `Returns:` section only restated the return *type* (i.e., it repeats a fixed literal value like `Result of type ``NotBlankStr``.`), do not reintroduce that literal-value restatement in the docstring—treat it as WHAT-not-WHY duplication and a potential doc-rot hazard. Prefer describing the purpose/meaning (WHY) of the returned value rather than repeating the type/value wording.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-07-11T21:02:36.144Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2569
File: src/synthorg/integrations/connections/types/gitea.py:40-40
Timestamp: 2026-07-11T21:02:36.144Z
Learning: Follow the repository’s `scripts/check_no_stubs.py` rule: do not use a bare `raise NotImplementedError` in a method body. Ellipsis (`...`) is the mandated convention for interface-only seams—i.e., methods whose bodies are `...` when they are decorated with `abstractmethod`, `overload`, or are members defined inside a `Protocol`. For concrete implementations, raise a typed `DomainError` (not `NotImplementedError`) instead of stubbing.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-07-11T21:03:18.103Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2569
File: src/synthorg/persistence/postgres/backend_migration.py:45-48
Timestamp: 2026-07-11T21:03:18.103Z
Learning: For code under src/synthorg/**/*.py, follow the enforcement in scripts/check_no_stubs.py:
- Do not allow `raise NotImplementedError` anywhere in any function/method body (no exemptions for `abstractmethod` or `overload`).
- A function/method body that is exactly `...` or `pass` is only acceptable if it’s an interface-only declaration: the enclosing function is decorated with `abstractmethod`/`overload`, the enclosing class is a `Protocol`, or the definition appears inside `if TYPE_CHECKING:`.
- Therefore, `abstractmethod` methods must use `...` (or otherwise fail loudly by raising a typed `DomainError`), but must never use `raise NotImplementedError`.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-06-03T11:43:13.104Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2200
File: tests/unit/engine/artifacts/test_service.py:42-45
Timestamp: 2026-06-03T11:43:13.104Z
Learning: For the D7 protocol method `save_returning_outcome(artifact: Artifact) -> bool` defined in `src/synthorg/persistence/artifact_protocol.py`, any implementation—including fake/stub/test doubles—must use the exact same parameter name `artifact` (i.e., `save_returning_outcome(self, artifact=...)` / `save_returning_outcome(self, artifact: Artifact)`), not `entity`. This name must match for typeguard positional-or-keyword name conformance. Do not suggest renaming the protocol method’s parameter to `entity`.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-06-09T10:06:53.040Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2283
File: src/synthorg/engine/middleware/coordination_constraints.py:152-152
Timestamp: 2026-06-09T10:06:53.040Z
Learning: Use International/British English spellings throughout the repository (code comments, docstrings, and documentation). British spellings such as "Analyse" (not "Analyze"), "Behaviour" (not "Behavior"), and "Colour" (not "Color") are mandatory and should not be flagged as spelling errors or inconsistencies during code review. This repo’s style is enforced via `vale` (see `CLAUDE.md` → "Regional Defaults (MANDATORY)"). If an American spelling appears, prefer the corresponding British spelling.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-06-13T08:51:11.124Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2348
File: docs/guides/custom-mcp-server-dev.md:155-161
Timestamp: 2026-06-13T08:51:11.124Z
Learning: When reviewing Aureliolo/synthorg usage of `mcp_descriptor()` (from `src/synthorg/meta/mcp/feature_descriptors.py`), ensure all call sites pass the keyword argument `handlers=...` (type `Callable[[], Mapping[str, object]]`). `mcp_descriptor()` maps this `handlers` value internally to the descriptor’s `handlers_factory` field; `handlers_factory=...` is not a valid keyword and will raise `TypeError: unexpected keyword argument`. Also ensure documentation and code examples do not suggest renaming `handlers` to `handlers_factory`; always show `handlers=`.
Applied to files:
src/synthorg/api/controllers/projects.pysrc/synthorg/api/ws_payloads/_domain.py
📚 Learning: 2026-06-20T14:08:01.947Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2423
File: src/synthorg/api/controllers/meta.py:268-273
Timestamp: 2026-06-20T14:08:01.947Z
Learning: In Aureliolo/synthorg controller code, do not flag an O(N) pre-materialization/performance issue when cursor pagination is implemented using the canonical idiom: `collect_all(lambda limit, offset: repo.list_items(...)) + paginate_cursor(...)` (e.g., in `custom_rules.py`, `meta.py`). Treat this as intentional because `paginate_cursor` requires the fully ordered collection to compute truncation/`has_more`, and repository protocol methods explicitly forbid bespoke `count`/extra methods under ADR-0001 (`IdKeyedRepository` compose-only). Only apply this “do not flag” guidance when the pagination is built from `repo.list_items(...)` via `collect_all` and then passed into `paginate_cursor` as part of cursor-pagination logic for low-cardinality datasets.
Applied to files:
src/synthorg/api/controllers/projects.py
📚 Learning: 2026-06-22T15:30:01.837Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2459
File: src/synthorg/api/controllers/risk_overrides.py:37-41
Timestamp: 2026-06-22T15:30:01.837Z
Learning: When reviewing controller helper code in this project, reading the ASGI `request.scope` user (e.g., `request.scope.get("user")` / `request.scope["user"]`) should NOT be treated as untrusted input that requires `parse_typed()` / `check_boundary_typed()`.
In Aureliolo/synthorg, the project’s auth middleware populates `scope["user"]` with an already-constructed `AuthenticatedUser` Pydantic model, not arbitrary external dict data. The correct boundary check is to validate the type, e.g. `isinstance(actor, AuthenticatedUser)` (and raise `UnauthorizedError` when it fails), rather than adding `parse_typed()` around `scope["user"]`.
Applied to files:
src/synthorg/api/controllers/projects.py
📚 Learning: 2026-06-22T15:30:20.153Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2459
File: src/synthorg/api/controllers/risk_overrides.py:39-40
Timestamp: 2026-06-22T15:30:20.153Z
Learning: In controller-layer code, do NOT log WARNING/ERROR with context immediately before raising client-facing guard exceptions that are part of normal request handling.
Specifically:
- For actor/auth guard helpers (e.g., `_require_actor`-style helpers) that raise `UnauthorizedError` when no authenticated actor is present, skip the preceding `logger.warning()`/`logger.error()` call and raise directly.
- For controller handlers that raise `NotFoundError` for missing resources (e.g., when a service returns `None`), skip the preceding log and raise directly (4xx “not found” is routine).
Only apply the “log WARNING/ERROR with context before raising” guideline to service-layer failures and state transitions (i.e., unexpected/operational failures), not routine controller authorization/not-found guards. Use the established idiom shown by `src/synthorg/api/controllers/users/org_roles.py::_require_actor` and the pattern in this controller (`risk_overrides.py`).
Applied to files:
src/synthorg/api/controllers/projects.py
🔇 Additional comments (5)
src/synthorg/api/controllers/projects.py (1)
6-25: LGTM!Also applies to: 35-56, 176-281, 319-319
src/synthorg/api/ws_payloads/_domain.py (1)
97-119: LGTM!web/src/api/types/websocket/provider.ts (1)
3-3: LGTM!Also applies to: 36-42
web/src/__tests__/stores/projects.test.ts (1)
56-56: LGTM!Also applies to: 347-475, 477-546
data/runtime_stats.yaml (1)
2-8: LGTM!
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@web/src/__tests__/stores/projects.test.ts`:
- Around line 546-566: The test around updateFromWsEvent should cover both
missing and malformed new_version values, not only the missing-field case. Add
an invalid typed new_version scenario and assert that projects[0].version and
selectedProject.version both remain unchanged, alongside the existing
autonomy_mode assertions.
In `@web/src/stores/projects/ws-handler.ts`:
- Around line 22-25: Update the project.autonomy_mode_changed handling around
the patch helper to ignore events when newVersion is not greater than the
current version. Apply this version gate independently to both the project list
row and selectedProject, preserving each existing state update only for newer
WebSocket versions.
🪄 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 (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 446599bc-0395-455c-a941-987ef3318625
📒 Files selected for processing (2)
web/src/__tests__/stores/projects.test.tsweb/src/stores/projects/ws-handler.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (32)
- GitHub Check: Dashboard Test (shard 1/4)
- GitHub Check: Dashboard Test (shard 2/4)
- GitHub Check: Dashboard Test (shard 3/4)
- GitHub Check: Dashboard Test (shard 4/4)
- GitHub Check: Dashboard Type Check
- GitHub Check: Dashboard Build
- GitHub Check: Build Backend
- GitHub Check: CodSpeed Python benchmarks
- GitHub Check: CodSpeed Web benchmarks
- GitHub Check: Lighthouse Dashboard
- GitHub Check: Lighthouse Site
- GitHub Check: Test E2E
- GitHub Check: Test Integration (shard 3)
- GitHub Check: Test Integration (shard 4)
- GitHub Check: Dashboard E2E (Playwright)
- GitHub Check: Test Unit (shard 4)
- GitHub Check: Test Integration (shard 2)
- GitHub Check: Test Unit (shard 2)
- GitHub Check: Dashboard Lint
- GitHub Check: Test Unit (shard 3)
- GitHub Check: Test Unit (shard 1)
- GitHub Check: Runtime Stats Freshness Gate
- GitHub Check: Test Integration (shard 1)
- GitHub Check: Test Conformance (SQLite)
- GitHub Check: Gates (pre-commit all-files + parity)
- GitHub Check: Build Web Assets (melange)
- GitHub Check: Build Preview
- GitHub Check: pyright (advisory)
- GitHub Check: CLI Test (windows-latest)
- GitHub Check: CLI Test (macos-latest)
- GitHub Check: Analyze (python)
- GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (6)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{ts,tsx}: The dashboard is a pure API consumer: it must not persist domain or app state client-side, must hydrate from backend GETs on mount, and must write every change through the REST API immediately.
Do not uselocalStorage,sessionStorage,IndexedDB, orzustandpersistfor domain/app state (including setup-wizard state, theme/appearance, and user/org preferences); those values must live in backend settings and be rehydrated from GET/PUT.
Step/progress UI must be derived from backend state, not from a persisted client flag.
The only sanctioned client storage is non-domain transport/UX data: the auth-token cookie shim and the active CSRF token.
UsecreateLoggerfrom@/lib/loggerinstead of bareconsole.*in app code, and uselog.debug(),log.warn(), andlog.error()with dynamic or untrusted values passed as separate args or structured objects sanitized viasanitizeArg/sanitizeForLog().
Pages that need to filter, sort, search, or aggregate across an entire dataset must load the full set withpaginateAlland page it in the browser withuseListPaginationusing?{ns}Page/?{ns}SizeURL params.
Sanitize untrusted WebSocket strings withsanitizeWsString()/sanitizeWsEnum<T>(); never use raw casts, and do not usemakeEnumParser<T>at a WebSocket boundary.
ImportErrorCodeandErrorCategoryfrom@/api/types/errors, and discriminate onErrorCode.<NAME>rather than raw integers.
Reuse existing components fromweb/src/components/ui/before creating new ones; do not hardcode hex colors, fonts, pixel spacing, Motion durations, BCP 47 locale literals, or currency symbols.
Do not usegetXIcon(): LucideIconfactories in JSX; export a<XIcon value={...} />wrapper in its own file instead, and useuseViewportSize()rather than rawwindow.innerWidthin render.
Follow the strict ESLint/type-safety rules: fix types instead of suppressing them, respectexactOptionalPropertyTypesandnoUncheckedIndexedAccess...
Files:
web/src/stores/projects/ws-handler.tsweb/src/__tests__/stores/projects.test.ts
web/src/stores/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/stores/**/*.{ts,tsx}: Store create/update/delete mutations must follow the CRUD pattern: try/catch, success state update plus success toast, failure logging plus error toast plus sentinel return, with optimistic updates restoringpreviousstate on error and usinggetCrudErrorTitle(err, fallback).
Callers must not wrap store mutation calls in try/catch; the store owns the mutation error UX.
List reads should seterror: string | nullinstead of showing toast errors.
Cursor-paginated list endpoints must use opaquePaginationMetacursors, storenextCursorandhasMore(no offset arithmetic), early-return when!hasMore || !nextCursor, and derive counts fromdata.length.
Files:
web/src/stores/projects/ws-handler.ts
web/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/**/*.{js,jsx,ts,tsx}: In the React dashboard, reuse components fromweb/src/components/ui/and use design tokens only.
The frontend must be a pure API consumer: do not persist application or domain state in client storage; hydrate through GET requests and write changes through the API.
Files:
web/src/stores/projects/ws-handler.tsweb/src/__tests__/stores/projects.test.ts
**/*
📄 CodeRabbit inference engine (CLAUDE.md)
**/*: Every convention pull request must ship its enforcement gate.
Commits must use<type>: <description>, branches must use<type>/<slug>, and changes must be squash-merged.
Files:
web/src/stores/projects/ws-handler.tsweb/src/__tests__/stores/projects.test.ts
**/*.{py,js,jsx,ts,tsx,go}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{py,js,jsx,ts,tsx,go}: Do not add AGPL/GPL (non-LGPL) dependencies; attribute permitted LGPL dependencies inNOTICE.
Never use real vendor names in project code or tests; use approved example provider names, except in documented allowlisted locations.
Files:
web/src/stores/projects/ws-handler.tsweb/src/__tests__/stores/projects.test.ts
web/src/**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.{test,spec}.{ts,tsx}: Tests must override MSW behavior withserver.use(...)and must notvi.mock('@/api/endpoints/*').
Unit tests run under the active-handle tracker and must not leak event-loop resources fromweb/src/frames.
Files:
web/src/__tests__/stores/projects.test.ts
🧠 Learnings (9)
📚 Learning: 2026-05-28T22:27:09.037Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2159
File: web/src/__tests__/stores/auth-sessions.test.ts:8-8
Timestamp: 2026-05-28T22:27:09.037Z
Learning: In the frontend, `SessionInfo` is a local type alias for `SessionResponse` defined only in `web/src/api/types/auth.ts` (exported as `SessionResponse as SessionInfo`). It is not re-exported via the `@/api/types` barrel (which only re-exports `dtos.gen` including `SessionResponse`). Therefore, all code (including React components, stores, and tests) that needs `SessionInfo` must import it from `@/api/types/auth` and not from `@/api/types`. (Import `SessionResponse` from `@/api/types` only when `SessionResponse` is what you need.)
Applied to files:
web/src/stores/projects/ws-handler.tsweb/src/__tests__/stores/projects.test.ts
📚 Learning: 2026-05-28T22:27:14.761Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2159
File: web/src/__tests__/stores/backups.test.ts:9-9
Timestamp: 2026-05-28T22:27:14.761Z
Learning: In the synthorg web package, follow the established convention for importing DTOs from the domain-specific sub-barrels under `@/api/types/<domain>`, e.g. `import type { BackupInfo } from '`@/api/types/backup`'`. These domain sub-barrels are curated and re-export from generated DTOs (e.g., `./dtos.gen`), so they are not considered “deep reach” imports. Do not recommend changing these imports to the top-level `@/api/types` barrel (e.g., `@/api/types`)—production code and tests consistently use the domain-specific sub-barrel paths (such as `stores/backups.ts`, `AdminBackupsPage.tsx`, and `mocks/handlers/backup.ts`).
Applied to files:
web/src/stores/projects/ws-handler.tsweb/src/__tests__/stores/projects.test.ts
📚 Learning: 2026-05-28T22:27:25.293Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2159
File: web/src/__tests__/stores/workflows-export.test.ts:7-7
Timestamp: 2026-05-28T22:27:25.293Z
Learning: When reviewing code in this repository, if the change touches or adds types related to workflows, ensure those imports come from the domain-specific curated barrel at `@/api/types/workflows` (e.g., `@/api/types/workflows`), not from the top-level `@/api/types` barrel. Do not recommend switching workflow-related type imports to the top-level barrel; this convention is the established pattern across workflow UI/components, workflow editor store code, related hooks, and workflow-related tests.
Applied to files:
web/src/stores/projects/ws-handler.tsweb/src/__tests__/stores/projects.test.ts
📚 Learning: 2026-05-29T15:33:54.576Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2163
File: web/src/pages/providers/provider-form-helpers.ts:1-9
Timestamp: 2026-05-29T15:33:54.576Z
Learning: In the synthorg web package (web/src), import provider-related DTO types (e.g., AuthType, CloudPreset, CreateFromPresetRequest, CreateProviderRequest, ProviderConfig, ProviderPreset, UpdateProviderRequest) from the domain sub-barrel `@/api/types/providers` and not from the top-level `@/api/types` barrel. These types are not re-exported from `@/api/types`; using the top-level barrel will cause compilation errors. Follow the established convention when updating provider-related code and imports.
Applied to files:
web/src/stores/projects/ws-handler.tsweb/src/__tests__/stores/projects.test.ts
📚 Learning: 2026-05-29T15:35:11.914Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2163
File: web/src/pages/setup/CompanyStep.tsx:18-18
Timestamp: 2026-05-29T15:35:11.914Z
Learning: In the synthorg web package, when using setup-related DTO types (e.g., `SetupAgentSummary`, `SetupCompanyResponse`), import them from the setup domain sub-barrel `@/api/types/setup` rather than from the top-level `@/api/types`. The setup DTOs are not re-exported from `@/api/types`, so importing from the top-level barrel will fail to compile. Follow the existing domain-sub-barrel convention used elsewhere (e.g., `@/api/types/backup`, `@/api/types/workflows`, `@/api/types/auth`) and do not recommend moving setup-domain type imports to `@/api/types`.
Applied to files:
web/src/stores/projects/ws-handler.tsweb/src/__tests__/stores/projects.test.ts
📚 Learning: 2026-05-31T00:00:32.734Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2172
File: web/src/pages/project-brain/BrainEntryList.tsx:12-45
Timestamp: 2026-05-31T00:00:32.734Z
Learning: In the synthorg web package, do not flag React components for ESLint `max-params` violations based on destructured prop fields. ESLint `max-params` counts the number of function parameters (arity), not properties inside a destructured object. For example, `function BrainEntryList({ entries, loading, hasMore, ... }: BrainEntryListProps)` has exactly 1 parameter (the props object) and should not be considered a `max-params: 5` violation, even if the props type/interface contains 10+ fields. Only flag when a function declares 6+ separate parameters.
Applied to files:
web/src/stores/projects/ws-handler.tsweb/src/__tests__/stores/projects.test.ts
📚 Learning: 2026-06-13T14:07:55.334Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2356
File: web/src/components/ui/ws-connection-banner.tsx:30-45
Timestamp: 2026-06-13T14:07:55.334Z
Learning: In the synthorg web package, treat leading underscores on function names as an intentional convention for module-local, non-exported helper/utility functions (e.g., `_resolveBannerKind`, `_handleConflict`, `_statusFallback`, `_hasRequiredCredentials`, `_decideRetryWait`, `_shouldKeepRetrying`). Do not flag these or suggest renaming to remove the underscore. This does not conflict with React hook linting rules (e.g., the rule that React hook/sub-hook helpers must start with `use` and must not use leading underscores); only apply that hook-specific guidance to actual hook helpers, not plain utility functions.
Applied to files:
web/src/stores/projects/ws-handler.tsweb/src/__tests__/stores/projects.test.ts
📚 Learning: 2026-06-25T14:59:52.946Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2476
File: web/CLAUDE.md:31-34
Timestamp: 2026-06-25T14:59:52.946Z
Learning: For the TypeScript/React web dashboard code under web/src (e.g., dashboard utilities/components), follow the web logging convention: import and use `createLogger` from `@/lib/logger`, and name the resulting logger variable `log`. Do not apply the Python backend logging helper pattern (`get_logger` from `src/synthorg/observability/_logger.py` and its `logger` variable) to web TypeScript files.
Applied to files:
web/src/stores/projects/ws-handler.tsweb/src/__tests__/stores/projects.test.ts
📚 Learning: 2026-06-05T10:17:59.769Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 2231
File: web/src/pages/org/build-org-tree-types.ts:122-122
Timestamp: 2026-06-05T10:17:59.769Z
Learning: When indexing an exhaustive TypeScript `Record<K, V>` (e.g., `SENIORITY_RANK: Record<SeniorityLevel, number>`) using a value whose parameter/type is the same union key type (e.g., `seniorityOf(level: SeniorityLevel)`), TypeScript already guarantees the key is covered at build time. In this case, do NOT request a runtime `?? fallback` or suggest handling for an `UNKNOWN` key unless the codebase has an explicit/ documented runtime drift or “unrecognized value” contract (like the one handled in `computeCategoryBreakdown` in `web/src/utils/budget.ts`). Also, don’t suggest non-existent keys that are not part of the union type.
Applied to files:
web/src/stores/projects/ws-handler.tsweb/src/__tests__/stores/projects.test.ts
🔇 Additional comments (1)
web/src/stores/projects/ws-handler.ts (1)
36-61: LGTM!
Summary
Phase 2 of #2576: operators can set an oversight mode per initiative, and that mode tunes the per-action SecOps gate. This does not close #2576 (Phase 3, the build/test/review completion oracle, closes it).
Project.autonomy_mode(AutonomyLevel | None) resolved through a four-level chain (agent > initiative > department > company) into the existingAutonomyPreset/EffectiveAutonomygate machinery. No new gate code: the mode selects the preset the gate already consumes.locked), neverNone, so a transient persistence fault can never silently loosen an operator's override (corruption logged at ERROR).full(pass-through, gate disabled) requires the CEO role + an explicitconfirm=trueand is audited at WARNING with actor + from/to modes. Tightening/lateral transitions stay open to write-access roles.expected_versionthrough the service into the repository, surfacing a 409 on a concurrent write instead of clobbering.PATCH /projects/{id}/autonomy-mode(ProjectAutonomyModeRequest),projects.updaterate policy, dedicatedapi.project.autonomy_mode_changedaudit event, and aproject.autonomy_mode_changedWS broadcast so other dashboards reflect the change live.autonomy_modecolumn onprojects(both backends) with a closed-setCHECK, one migration per backend, schema-drift clean.ProjectOversightSection) with a CEO-gated, confirmed opt-in forfull, a disabled/stale-response-guarded store action, and the WS-handler + types wiring.Test plan
uv run ruff check . && uv run mypy src/ tests/(clean, 6184 files)pytestunit + conformance for the resolver, project model, projects controller (CEO-only guardrail, missing-confirm 422, non-CEO 403, 409 version conflict, gate-off WARNING audit), worker glue (fail-closed-to-locked, resolver threading), and dual-backendautonomy_moderound-trip incl.full(181 passed)npm --prefix web run lint | type-check | test(3891 passed, no leaked handles)Review coverage
Pre-reviewed by 15 agents (code / python / conventions / security / persistence / async-concurrency / silent-failure / type-design / api-contract / frontend / test-quality / issue-resolution / docs-consistency / comment-quality + ghost-wiring). Every valid finding addressed: the fail-open degrade and missing confirm+reason guardrail (both CRITICAL, security), the lost-update OCC bypass and audit-trail gap (HIGH, 5-6 agents), the frontend enum-parser + stale-response guard, and all doc/test findings. Two LOW items were resolved by user decision: the live WS broadcast is implemented; the SQLite
status-column CHECK was dropped because a table rebuild would cascade-delete child rows viaDROP TABLEunderforeign_keys=ON(theautonomy_modeCHECK ships on both backends).Relates to #2576.