feat: build infrastructure layer (API client, auth, WebSocket)#815
feat: build infrastructure layer (API client, auth, WebSocket)#815
Conversation
Port non-UI infrastructure from the Vue dashboard to React/Zustand: - Axios API client with JWT interceptor, 401 redirect, RFC 9457 error handling, and ApiResponse envelope unwrapping - TypeScript types for all backend DTOs (1134 lines), including new analytics trends/forecast, backup, coordination, and discovery-policy - 18 endpoint modules by domain (14 ported + 4 new) with barrel export - Auth Zustand store: token lifecycle, login/setup flow, session expiry, localStorage persistence, HMR-safe timer cleanup - WebSocket Zustand store: ticket-based auth, exponential backoff reconnection (1s-30s, 20 max), channel subscriptions with handler deduplication, auto-re-subscribe on reconnect, message size gating - 11 empty domain store shells (one per domain, Zustand conventions) - 5 React hooks: useAuth (role-based canWrite), useWebSocket (component lifecycle subscriptions), useOptimisticUpdate (with rollback), usePolling (setTimeout-based, no overlap), useLoginLockout (client-side brute-force UX hint) - 4 utility modules: error extraction, date/currency formatters, sanitizeForLog, app-wide constants with task transition graph - 170 tests across 19 files including property-based tests (fast-check) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-reviewed by 7 agents (docs-consistency, frontend-reviewer, api-contract-drift, type-design, security-reviewer, test-quality, issue-resolution-verifier), 21 findings addressed: Types (api/types.ts): - Add 5 missing OverviewMetrics fields (budget_remaining_usd, etc.) - Remove extra requested_by from CreateApprovalRequest - Fix type widening: department -> DepartmentName, topology -> CoordinationTopology, level -> SeniorityLevel - Remove dead ProblemDetail type - Add readonly to 40+ response array fields API client (api/client.ts): - Replace unsafe `as string` cast with `??` on env var - unwrapPaginated now throws ApiRequestError (not Error) - Export PaginatedResult<T> type for explicit return types - Add explicit return types to all 9 list* endpoint functions - Add unwrapVoid to 5 delete/void endpoints Hooks: - Fix stale closure in useLoginLockout.recordFailure (use ref) - Gate lockout interval on lockedUntil (no re-renders when unlocked) WebSocket (stores/websocket.ts + backend ws.py): - Add isWsEvent type guard (replaces unsafe double cast) - Add isWsChannelArray validation on ack messages - First-message auth: send ticket as first WS message instead of URL query param (keeps ticket out of browser history and proxy logs) - Backend: add _read_auth_message + _authenticate_ws helpers, backward compatible with query-param auth Tests: - Fix conditional assertion antipattern in interceptor test - Add 401 response interceptor tests - Add setup() method test - Add fetchUser error path tests (401 + non-401) - Add WebSocket reconnect exhaustion test - Add WebSocket ticket 401 handling test - Add first-message auth test Docs: - Update CLAUDE.md Package Structure with new web/src/ directories Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adapt existing test_ws_rejects_bad_ticket to test query-param path only, add two new tests for first-message auth: bad ticket and wrong action. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nd GHAS Critical fixes: - unwrapVoid handles 204 No Content (all DELETE endpoints were broken) - doConnect throws on ticket failure (callers now see WS errors) - WS callbacks bound to concrete socket instance (stale onclose fix) - onclose inspects close codes (4001/4003 skip reconnect) Major WebSocket fixes: - unsubscribe handles partial channel overlaps in replay state - Message size check uses byte length (TextEncoder) not UTF-16 units - Log injection: truncate attacker-controlled msg.error - isWsChannelArray validates against known WsChannel values - Remove redundant try/catch around dispatchEvent Major Python fixes: - Drop unreachable TypeError from _read_auth_message except clause - Fix incorrect _READ_ROLES == frozenset(HumanRole) comment - Extract _setup_connection to bring ws_handler under 50 lines - Use correct event name for channels_plugin resolution failure - Add logging for oversized messages and auth-stage disconnects Major frontend fixes: - fetchUser throws on 401 (callers detect auth failure) - usePolling: no render-phase throw, run-generation prevents stale loops - useWebSocket: disposedRef check before handler wiring, cleanup race fix - useWebSocket: unmount only removes handlers (no global unsubscribe) - useAuth: useMemo prevents unnecessary re-renders - sanitizeForLog preserves Error stack traces - useMustChangePassword reads from store state, not localStorage selector - Add 4 missing coordination WsEventType members - Readonly consistency for constants and DTO types - Fix Vue composable reference in React codebase - Fix CSP header file reference and JSDoc syntax - 401 interceptor: document fire-and-forget trade-off Test fixes: - auth.test: resetStore cleans up expiry timer via logout() - auth.test: token expiry uses beforeEach/afterEach for fake timers - websocket.test: precise assertions, subscription queuing verified - client.test: idiomatic error assertion pattern - usePolling.test: restart-while-inflight regression test - logging.test: actual Unicode preservation test - format.test: negative currency assertion - constants.property.test: accurate test name Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- auth.ts: don't clear fresh token on transient fetchUser errors - ws.py: avoid double data.encode() in message size check - client.test.ts: add test for unwrapVoid 204 No Content handling Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…zeForLog Property test (fc.anything()) generates Error-like objects where both .stack and .message are undefined (e.g. Object.create(Error.prototype)). Add fallback String(value) to prevent TypeError on undefined iteration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ws.py: enforce _MAX_WS_MESSAGE_BYTES on auth messages (defense in depth) - websocket.ts: sanitize event.reason through sanitizeForLog in onclose - websocket.ts: reject array payloads in isWsEvent validation - useWebSocket.ts: return early on subscribe failure (skip handler wiring) - auth.ts: sync _mustChangePasswordFallback in changePassword - types.ts: accept readonly model arrays in provider request DTOs - useAuth.test.ts: reset _mustChangePasswordFallback in resetStore - websocket.test.ts: clarify ASCII byte-length assumption in size test Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ws.py: safely extract action from non-dict msg in auth logger - useWebSocket.ts: clear stale setupError on re-setup - types.ts: readonly models in CreateFromPresetRequest Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Source fixes: - client.ts: log error in 401 import catch, fix "short-lived" comment - websocket.ts: add diagnostic context to onerror handler - ws.py: upgrade WARNING to ERROR for plugin/subscribe failures - ws.py: log non-dict payload drops in _on_event - logging.ts: strip Unicode BIDI override chars in sanitizeForLog - usePolling.ts: add .catch() to fire-and-forget immediate() - useWebSocket.ts: add .catch() to fire-and-forget setup() - errors.ts: log suppressed long/JSON-shaped error messages - constants.ts: document WS_MAX_MESSAGE_SIZE vs backend cap - CLAUDE.md: fix components/ description accuracy Test additions: - websocket.test: isWsEvent rejection cases (missing fields, array payload, null payload) - usePolling.test: error state surfacing and clearing - client.test: unwrap rejects success:true with data:null Pre-reviewed by 9 agents, 15 findings addressed Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure that dependencies are being submitted on PR branches. Re-running this action after a short time may resolve the issue. See the documentation for more information and troubleshooting advice. Scanned FilesNone |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📜 Recent review details⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
🧰 Additional context used📓 Path-based instructions (1)web/src/**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (1)📓 Common learnings🪛 GitHub Check: CodeQLweb/src/stores/websocket.ts[warning] 217-217: Log injection 🔇 Additional comments (10)
WalkthroughUpdates add a React frontend infrastructure layer and extensive tests plus a backend WebSocket auth/control flow refactor. Frontend: new Axios API client ( Suggested labels
|
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 establishes the foundational infrastructure layer for the new React/Zustand frontend, migrating core API communication, authentication, and real-time WebSocket capabilities from the previous Vue dashboard. It introduces a structured and type-safe approach to interacting with the backend, enhancing both developer experience and application robustness. The changes lay the groundwork for future UI development by providing a complete and tested set of tools for data fetching, state management, and event handling. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #815 +/- ##
==========================================
- Coverage 92.33% 92.26% -0.08%
==========================================
Files 578 578
Lines 30051 30120 +69
Branches 2901 2910 +9
==========================================
+ Hits 27748 27789 +41
- Misses 1820 1842 +22
- Partials 483 489 +6 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 14
🤖 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/ws.py`:
- Around line 103-155: _split _read_auth_message() because it exceeds 50 lines
and repeats the same warning/close/return None pattern; create a small helper
(e.g., _reject_auth(socket: WebSocket, log_reason: str, close_reason: str, code:
int = _WS_CLOSE_AUTH_FAILED)) that calls logger.warning(API_WS_TICKET_INVALID,
reason=log_reason, ...) and awaits socket.close(code=code, reason=close_reason)
then returns None, and replace each repeated logger.warning/await
socket.close/return None block inside _read_auth_message() with a single call to
this helper (keep existing constants like API_WS_TICKET_INVALID,
_WS_CLOSE_AUTH_FAILED and preserve action truncation logic when logging action).
- Around line 309-319: The code treats ?ticket= as first-message auth and can
fall through to _auth_from_first_message; update the branch so that when
socket.query_params.get("ticket") is present you call await
_validate_ticket(socket) and immediately return (user, False) if valid or return
None if invalid — do not call socket.accept() or proceed to
_auth_from_first_message; adjust the logic around ticket_param,
_validate_ticket, socket.accept, and _auth_from_first_message to ensure
query-param ticket auth is handled and rejected before any accept().
- Around line 366-382: The subscriber is created via await
channels_plugin.subscribe(list(ALL_CHANNELS)) before socket.accept(), which can
leak the subscriber if accept() fails; move the socket.accept() call so it runs
before allocating the subscriber (i.e., call await socket.accept() prior to
await channels_plugin.subscribe(...)) or wrap subscribe() in a
try/except/finally that ensures channels_plugin.unsubscribe(subscriber) is
called on accept failure; update the logic around subscribe, socket.accept, and
the ws_handler cleanup to guarantee unsubscribe is called if accept or any
subsequent step raises (refer to channels_plugin.subscribe, socket.accept, and
the ws_handler cleanup/unsubscribe path).
In `@web/src/__tests__/hooks/useAuth.test.ts`:
- Around line 45-70: Replace the hardcoded writeRoles/readOnlyRoles in the test
with the canonical role constants: import WRITE_ROLES from the module that
defines the roles (the same module that exports useAuth) and use WRITE_ROLES in
place of the local writeRoles; for readOnlyRoles either import READ_ONLY_ROLES
if that constant exists or derive it by importing the full roles list (e.g.,
ALL_ROLES or Role enum) and filtering out WRITE_ROLES. Keep the test logic using
useAuth, useAuthStore, and asserting result.current.canWrite unchanged, just
source the role arrays from the shared constants to avoid test drift.
In `@web/src/__tests__/utils/logging.property.test.ts`:
- Around line 14-25: The test for sanitizeForLog currently only forbids ASCII
control characters; extend it to also assert that result characters' code points
are not any of the known bidirectional control code points filtered by
sanitizeForLog (e.g., 0x200E, 0x200F, 0x202A, 0x202B, 0x202C, 0x202D, 0x202E).
Locate the test using sanitizeForLog in logging.property.test.ts and, inside the
property loop that inspects each character code, add a check that the code is
not equal to any value in that bidi list (or assert !bidiSet.has(code)) so the
test fails if sanitizeForLog allows those bidi control characters through.
In `@web/src/api/endpoints/analytics.ts`:
- Around line 9-23: The getTrends implementation is inconsistent with
getForecast in how query params are built; update getTrends (function getTrends)
to construct its params object conditionally (e.g., only include period and
metric keys when they are defined) instead of always passing { period, metric }
so both endpoints follow the same pattern of omitting undefined params (mirror
the conditional params construction used in getForecast).
In `@web/src/api/endpoints/projects.ts`:
- Around line 4-8: The TODO notes that listProjects currently returns
PaginatedResult<Record<string, unknown>> because Project isn't defined yet;
create a tracking issue to add a concrete Project interface in types.ts once the
backend DTO stabilizes and then update the listProjects signature and related
usages (listProjects, PaginatedResult, PaginationParams, unwrapPaginated) to use
PaginatedResult<Project> and PaginatedResponse<Project>, ensuring type-safe
consumption across the codebase; include the expected DTO fields or a link to
the backend contract in the issue to avoid ambiguity.
In `@web/src/api/types.ts`:
- Around line 1001-1007: The TypeScript interface SetupCompanyResponse currently
includes a synthetic agent_count property that isn't provided by the backend
(remove agent_count from the interface); update the SetupCompanyResponse
definition to drop the agent_count field and ensure callers derive the count
from the readonly agents array (e.g. use agents.length) and adjust any code
referencing SetupCompanyResponse.agent_count to read from agents instead so
types remain accurate and deserialization won't produce undefined.
- Around line 127-141: The TypeScript response unions misrepresent the backend
contract: make ApiResponse's error_detail nullable (ErrorDetail | null) in the
failure branch and update PaginatedResponse's failure branch to carry pagination
and a data array (data: T[]; pagination: PaginationMeta) instead of nulls so it
matches the backend behavior (backend may return error_detail=null and still
include pagination with an empty data tuple); adjust the PaginatedResponse
success/failure shapes accordingly and keep ErrorDetail and PaginationMeta types
as-is so existing guards in client.ts continue to work.
In `@web/src/hooks/useOptimisticUpdate.ts`:
- Around line 11-16: The docs for useOptimisticUpdate incorrectly claim a null
return only means server error or in-flight no-op; update the comment block to
also mention that a null is returned when applyOptimistic() (the
optimistic-prepare step) throws and the optimistic state was rolled back. Edit
the comment near the top of useOptimisticUpdate (the block that currently
describes the two null cases) to add a third bullet like "optimistic-prepare
failure: applyOptimistic() threw and the optimistic state was reverted," and
ensure callers are advised to check error to distinguish these cases.
In `@web/src/hooks/usePolling.ts`:
- Line 2: The current usePolling hook sets UI error state using sanitizeForLog
(e.g., calls to setError(sanitizeForLog(...))) which can surface stack/internal
details; update the error handling so setError receives a user-safe message (use
getErrorMessage or equivalent) and keep sanitizeForLog only for logging to
console or processLogger; locate the setError(...) calls in usePolling (the
error branches around the polling/error handlers) and replace the argument with
getErrorMessage(error) while passing sanitizeForLog(error) to your logger
(console.error or processLogger.error) for diagnostics.
In `@web/src/stores/websocket.ts`:
- Around line 57-60: VALID_WS_CHANNELS is hardcoded and can drift from the
WsChannel type; instead, export a single source of truth array (e.g.,
WS_CHANNELS) from types.ts and derive both the WsChannel type and the runtime
set from it, then replace the hardcoded VALID_WS_CHANNELS with a Set constructed
from the imported WS_CHANNELS; update websocket.ts to import WS_CHANNELS (and
keep the existing VALID_WS_CHANNELS symbol name) and remove the manual list so
runtime validation always reflects the WsChannel type.
In `@web/src/utils/format.ts`:
- Around line 41-55: formatCurrency and formatNumber currently pass NaN/Infinity
through to Intl.NumberFormat; add a defensive check using Number.isFinite(value)
and return the same fallback used by the date helpers (e.g., an empty string)
when the value is not finite. Update both functions (formatCurrency and
formatNumber) to early-return the fallback for non-finite inputs before calling
Intl.NumberFormat so the UI never renders invalid numeric strings.
In `@web/src/utils/logging.ts`:
- Around line 10-19: sanitizeForLog currently allows C1 controls (0x80–0x9F) and
treats non-positive maxLen incorrectly; update the function to return ''
immediately when maxLen <= 0, and change the character-acceptance condition in
the loop (inside sanitizeForLog where it tests code >= 0x20 ...) to also reject
codes in the 0x80–0x9F range (e.g., add !(code >= 0x80 && code <= 0x9f)) while
keeping the existing isBidiControl(code) check and the existing maxLen
truncation behavior on result.length.
🪄 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: af0eebe9-5716-4999-a22e-019ed36e24ef
📒 Files selected for processing (63)
CLAUDE.mdsrc/synthorg/api/controllers/ws.pytests/unit/api/controllers/test_ws.pyweb/src/__tests__/api/client.property.test.tsweb/src/__tests__/api/client.test.tsweb/src/__tests__/hooks/useAuth.test.tsweb/src/__tests__/hooks/useLoginLockout.test.tsweb/src/__tests__/hooks/useOptimisticUpdate.test.tsweb/src/__tests__/hooks/usePolling.test.tsweb/src/__tests__/hooks/useWebSocket.test.tsweb/src/__tests__/stores/auth.test.tsweb/src/__tests__/stores/websocket.test.tsweb/src/__tests__/utils/constants.property.test.tsweb/src/__tests__/utils/constants.test.tsweb/src/__tests__/utils/errors.property.test.tsweb/src/__tests__/utils/errors.test.tsweb/src/__tests__/utils/format.property.test.tsweb/src/__tests__/utils/format.test.tsweb/src/__tests__/utils/logging.property.test.tsweb/src/__tests__/utils/logging.test.tsweb/src/api/client.tsweb/src/api/endpoints/agents.tsweb/src/api/endpoints/analytics.tsweb/src/api/endpoints/approvals.tsweb/src/api/endpoints/artifacts.tsweb/src/api/endpoints/auth.tsweb/src/api/endpoints/backup.tsweb/src/api/endpoints/budget.tsweb/src/api/endpoints/collaboration.tsweb/src/api/endpoints/company.tsweb/src/api/endpoints/coordination.tsweb/src/api/endpoints/health.tsweb/src/api/endpoints/index.tsweb/src/api/endpoints/meetings.tsweb/src/api/endpoints/messages.tsweb/src/api/endpoints/projects.tsweb/src/api/endpoints/providers.tsweb/src/api/endpoints/settings.tsweb/src/api/endpoints/setup.tsweb/src/api/endpoints/tasks.tsweb/src/api/types.tsweb/src/hooks/useAuth.tsweb/src/hooks/useLoginLockout.tsweb/src/hooks/useOptimisticUpdate.tsweb/src/hooks/usePolling.tsweb/src/hooks/useWebSocket.tsweb/src/stores/agents.tsweb/src/stores/analytics.tsweb/src/stores/approvals.tsweb/src/stores/auth.tsweb/src/stores/budget.tsweb/src/stores/company.tsweb/src/stores/meetings.tsweb/src/stores/messages.tsweb/src/stores/providers.tsweb/src/stores/settings.tsweb/src/stores/setup.tsweb/src/stores/tasks.tsweb/src/stores/websocket.tsweb/src/utils/constants.tsweb/src/utils/errors.tsweb/src/utils/format.tsweb/src/utils/logging.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). (4)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Backend
- GitHub Check: Build Sandbox
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
web/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx,js,jsx}: React 19 dashboard with shadcn/ui components, Tailwind CSS 4, Zustand for state,@tanstack/react-queryfor server state,@xyflow/reactfor workflow visualization
NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT) in web dashboard code -- use generic names likeexample-providerortest-provider
Files:
web/src/__tests__/utils/errors.property.test.tsweb/src/api/endpoints/health.tsweb/src/stores/analytics.tsweb/src/__tests__/utils/constants.property.test.tsweb/src/stores/setup.tsweb/src/api/endpoints/projects.tsweb/src/__tests__/utils/format.property.test.tsweb/src/stores/budget.tsweb/src/api/endpoints/artifacts.tsweb/src/stores/providers.tsweb/src/stores/agents.tsweb/src/stores/meetings.tsweb/src/stores/messages.tsweb/src/stores/settings.tsweb/src/stores/company.tsweb/src/stores/tasks.tsweb/src/__tests__/utils/logging.property.test.tsweb/src/api/endpoints/coordination.tsweb/src/stores/approvals.tsweb/src/hooks/useAuth.tsweb/src/api/endpoints/messages.tsweb/src/__tests__/utils/logging.test.tsweb/src/utils/logging.tsweb/src/__tests__/api/client.property.test.tsweb/src/api/endpoints/company.tsweb/src/api/endpoints/meetings.tsweb/src/__tests__/hooks/useLoginLockout.test.tsweb/src/api/endpoints/budget.tsweb/src/api/endpoints/auth.tsweb/src/hooks/useLoginLockout.tsweb/src/__tests__/hooks/useOptimisticUpdate.test.tsweb/src/__tests__/hooks/useAuth.test.tsweb/src/api/endpoints/analytics.tsweb/src/__tests__/utils/constants.test.tsweb/src/api/endpoints/backup.tsweb/src/hooks/usePolling.tsweb/src/__tests__/utils/errors.test.tsweb/src/api/endpoints/agents.tsweb/src/__tests__/stores/auth.test.tsweb/src/utils/errors.tsweb/src/api/endpoints/approvals.tsweb/src/api/endpoints/index.tsweb/src/utils/format.tsweb/src/hooks/useOptimisticUpdate.tsweb/src/api/endpoints/settings.tsweb/src/__tests__/utils/format.test.tsweb/src/__tests__/api/client.test.tsweb/src/api/endpoints/tasks.tsweb/src/api/endpoints/collaboration.tsweb/src/hooks/useWebSocket.tsweb/src/api/endpoints/providers.tsweb/src/stores/websocket.tsweb/src/__tests__/stores/websocket.test.tsweb/src/utils/constants.tsweb/src/api/client.tsweb/src/api/endpoints/setup.tsweb/src/stores/auth.tsweb/src/__tests__/hooks/usePolling.test.tsweb/src/api/types.tsweb/src/__tests__/hooks/useWebSocket.test.ts
web/src/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use Vitest for unit tests with
--detect-async-leaksflag; use fast-check for property-based testing withfc.assertandfc.property
Files:
web/src/__tests__/utils/errors.property.test.tsweb/src/__tests__/utils/constants.property.test.tsweb/src/__tests__/utils/format.property.test.tsweb/src/__tests__/utils/logging.property.test.tsweb/src/__tests__/utils/logging.test.tsweb/src/__tests__/api/client.property.test.tsweb/src/__tests__/hooks/useLoginLockout.test.tsweb/src/__tests__/hooks/useOptimisticUpdate.test.tsweb/src/__tests__/hooks/useAuth.test.tsweb/src/__tests__/utils/constants.test.tsweb/src/__tests__/utils/errors.test.tsweb/src/__tests__/stores/auth.test.tsweb/src/__tests__/utils/format.test.tsweb/src/__tests__/api/client.test.tsweb/src/__tests__/stores/websocket.test.tsweb/src/__tests__/hooks/usePolling.test.tsweb/src/__tests__/hooks/useWebSocket.test.ts
**/*.{py,pyi}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{py,pyi}: Do NOT usefrom __future__ import annotations-- Python 3.14+ supports PEP 649 native lazy annotations
Useexcept A, B:syntax without parentheses for exception handling (PEP 758) -- ruff enforces this on Python 3.14
All public functions and classes must have type hints and Google-style docstrings, enforced by ruff D rules and mypy strict mode
Functions must be under 50 lines, files must be under 800 lines
Use immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections, usecopy.deepcopy()at construction andMappingProxyTypewrapping for read-only enforcement
For frozen Pydantic models with dict/list fields, usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization)
Use Pydantic v2 with frozen models for config/identity; use mutable-via-copy models withmodel_copy(update=...)for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model
Use@computed_fieldfor derived values instead of storing redundant fields; useNotBlankStrfromcore.typesfor all identifier/name fields (including optional and tuple variants) instead of manual validators
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (multiple tool invocations, parallel agent calls) over barecreate_task
Never implement retry logic in driver subclasses or calling code -- all provider calls go throughBaseCompletionProviderwhich applies retry and rate limiting automatically
Handle all errors explicitly -- never silently swallow exceptions
Validate input at system boundaries: user input, external APIs, and config files
Files:
tests/unit/api/controllers/test_ws.pysrc/synthorg/api/controllers/ws.py
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Line length: 88 characters (enforced by ruff)
Files:
tests/unit/api/controllers/test_ws.pysrc/synthorg/api/controllers/ws.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Mark all tests with pytest markers:@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, or@pytest.mark.slow
NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT) in tests or project-owned code -- use generic names:example-provider,test-provider,test-small-001,large/medium/smallas aliases
For property-based testing, use Hypothesis with@givendecorator and@settings; run withHYPOTHESIS_PROFILE=dev uv run pytest tests/ -m unit -n auto -k propertiesfor 1000 examples
ALWAYS run pytest with-n autofor parallelism via pytest-xdist; never run tests sequentially
For timing-sensitive tests, mocktime.monotonic()andasyncio.sleep()to make them deterministic; for tasks that must block indefinitely, useasyncio.Event().wait()instead of largeasyncio.sleep()calls
Maintain minimum 80% code coverage (enforced in CI). NEVER skip, dismiss, or ignore flaky tests -- always fix them fully and fundamentally
Files:
tests/unit/api/controllers/test_ws.py
src/synthorg/**/*.{py,pyi}
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.{py,pyi}: Every module with business logic MUST have:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)-- never useimport loggingorprint()
Always use event name constants fromsynthorg.observability.events.<domain>modules (e.g.API_REQUEST_STARTEDfromevents.api,TOOL_INVOKE_STARTfromevents.tool) instead of string literals
Always use structured logging with kwargs:logger.info(EVENT, key=value)-- never use format strings likelogger.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 and internal flow
Files:
src/synthorg/api/controllers/ws.py
🧠 Learnings (21)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T11:40:22.911Z
Learning: Applies to web/src/**/*.{ts,tsx,js,jsx} : React 19 dashboard with shadcn/ui components, Tailwind CSS 4, Zustand for state, tanstack/react-query for server state, xyflow/react for workflow visualization
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.
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
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).
Applied to files:
web/src/__tests__/utils/errors.property.test.tsweb/src/__tests__/utils/constants.property.test.tsweb/src/__tests__/utils/format.property.test.tsweb/src/__tests__/utils/logging.property.test.tsweb/src/__tests__/utils/logging.test.tsweb/src/__tests__/api/client.property.test.tsCLAUDE.mdweb/src/__tests__/hooks/useLoginLockout.test.tsweb/src/__tests__/hooks/useAuth.test.tsweb/src/__tests__/utils/constants.test.tsweb/src/__tests__/utils/errors.test.tsweb/src/__tests__/stores/auth.test.tsweb/src/__tests__/utils/format.test.tsweb/src/__tests__/api/client.test.tsweb/src/__tests__/stores/websocket.test.tsweb/src/__tests__/hooks/usePolling.test.tsweb/src/__tests__/hooks/useWebSocket.test.ts
📚 Learning: 2026-03-24T11:40:22.911Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T11:40:22.911Z
Learning: Applies to web/src/**/*.test.{ts,tsx} : Use Vitest for unit tests with `--detect-async-leaks` flag; use fast-check for property-based testing with `fc.assert` and `fc.property`
Applied to files:
web/src/__tests__/utils/errors.property.test.tsweb/src/__tests__/utils/constants.property.test.tsweb/src/__tests__/utils/format.property.test.tsweb/src/__tests__/utils/logging.property.test.tsweb/src/__tests__/utils/logging.test.tsweb/src/__tests__/api/client.property.test.tsweb/src/__tests__/hooks/useLoginLockout.test.tsweb/src/__tests__/hooks/useOptimisticUpdate.test.tsweb/src/__tests__/hooks/useAuth.test.tsweb/src/__tests__/utils/constants.test.tsweb/src/__tests__/utils/errors.test.tsweb/src/__tests__/stores/auth.test.tsweb/src/__tests__/utils/format.test.tsweb/src/__tests__/api/client.test.tsweb/src/__tests__/stores/websocket.test.tsweb/src/__tests__/hooks/usePolling.test.tsweb/src/__tests__/hooks/useWebSocket.test.ts
📚 Learning: 2026-03-24T11:40:22.911Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T11:40:22.911Z
Learning: Applies to web/src/**/*.{ts,tsx,js,jsx} : React 19 dashboard with shadcn/ui components, Tailwind CSS 4, Zustand for state, tanstack/react-query for server state, xyflow/react for workflow visualization
Applied to files:
web/src/stores/tasks.tsweb/src/stores/approvals.tsCLAUDE.mdweb/src/stores/auth.ts
📚 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 web/** : Web dashboard: Node.js 20+, dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, fast-check, ESLint, vue-tsc).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T21:20:09.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:20:09.993Z
Learning: Applies to web/src/components/** : Vue components organized by feature (agents/, approvals/, budget/, common/, dashboard/, layout/, messages/, org-chart/, tasks/).
Applied to files:
CLAUDE.mdweb/src/api/endpoints/index.ts
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
CLAUDE.mdweb/src/api/endpoints/index.ts
📚 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:
CLAUDE.md
📚 Learning: 2026-03-15T20:45:14.430Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T20:45:14.430Z
Learning: Shell commands: for Go CLI work, cd cli is an exception because Go tooling requires working directory to be the module root. Go commands require `cd cli` for other work, never use `cd`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Documentation source in `docs/` (Markdown, built with Zensical). Design spec in `docs/design/` (7 pages: index, agents, organization, communication, engine, memory, operations). Architecture in `docs/architecture/` (overview, tech-stack, decision log). Roadmap in `docs/roadmap/`. Security in `docs/security.md`. Licensing in `docs/licensing.md`. Reference in `docs/reference/`. REST API reference in `docs/rest-api.md`. Library reference in `docs/api/` (auto-generated from docstrings). Custom templates in `docs/overrides/`. Config in `mkdocs.yml`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to docs/** : Docs source in docs/ (Markdown, built with Zensical); design spec in docs/design/ (7 pages: index, agents, organization, communication, engine, memory, operations)
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-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 web/package.json : Web dashboard Node.js 20+; dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, ESLint, vue-tsc)
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/api/**/*.py : Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
Applied to files:
tests/unit/api/controllers/test_ws.pysrc/synthorg/api/controllers/ws.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:
web/src/api/endpoints/budget.ts
📚 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:
web/src/api/endpoints/budget.ts
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/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:
web/src/api/endpoints/auth.tssrc/synthorg/api/controllers/ws.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Settings: Runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge, ConfigResolver (typed composed reads for controllers), validation, registry, change notifications via message bus. Per-namespace setting definitions in definitions/ submodule (api, company, providers, memory, budget, security, coordination, observability, backup).
Applied to files:
web/src/api/endpoints/settings.tsweb/src/utils/constants.tsweb/src/api/types.ts
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)
Applied to files:
web/src/api/endpoints/settings.tsweb/src/utils/constants.tsweb/src/api/types.ts
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/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/ws.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:
web/src/api/endpoints/providers.ts
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings use runtime-editable persistence with precedence: DB > env > YAML > code defaults. 8 namespaces with Fernet encryption for sensitive values.
Applied to files:
web/src/utils/constants.ts
🧬 Code graph analysis (53)
web/src/__tests__/utils/errors.property.test.ts (1)
web/src/utils/errors.ts (1)
getErrorMessage(17-70)
web/src/api/endpoints/health.ts (2)
web/src/api/types.ts (2)
HealthStatus(642-648)ApiResponse(128-130)web/src/api/client.ts (2)
apiClient(12-16)unwrap(83-93)
web/src/stores/analytics.ts (1)
src/synthorg/api/auth/ticket_store.py (1)
create(84-115)
web/src/__tests__/utils/constants.property.test.ts (2)
web/src/utils/constants.ts (3)
TASK_STATUS_ORDER(24-34)VALID_TRANSITIONS(40-50)TERMINAL_STATUSES(37-37)web/src/api/types.ts (1)
TaskStatus(5-14)
web/src/stores/setup.ts (1)
src/synthorg/api/auth/ticket_store.py (1)
create(84-115)
web/src/api/endpoints/projects.ts (2)
web/src/api/types.ts (2)
PaginationParams(936-939)PaginatedResponse(139-141)web/src/api/client.ts (3)
PaginatedResult(113-118)apiClient(12-16)unwrapPaginated(124-144)
web/src/__tests__/utils/format.property.test.ts (1)
web/src/utils/format.ts (4)
formatCurrency(41-48)formatUptime(60-70)formatLabel(75-80)formatDate(6-17)
web/src/stores/budget.ts (1)
src/synthorg/api/auth/ticket_store.py (1)
create(84-115)
web/src/api/endpoints/artifacts.ts (2)
web/src/api/types.ts (2)
PaginationParams(936-939)PaginatedResponse(139-141)web/src/api/client.ts (2)
PaginatedResult(113-118)unwrapPaginated(124-144)
web/src/stores/providers.ts (1)
src/synthorg/api/auth/ticket_store.py (1)
create(84-115)
web/src/stores/agents.ts (1)
src/synthorg/api/auth/ticket_store.py (1)
create(84-115)
web/src/stores/meetings.ts (1)
src/synthorg/api/auth/ticket_store.py (1)
create(84-115)
web/src/stores/company.ts (1)
src/synthorg/api/auth/ticket_store.py (1)
create(84-115)
web/src/stores/tasks.ts (1)
src/synthorg/api/auth/ticket_store.py (1)
create(84-115)
web/src/__tests__/utils/logging.property.test.ts (1)
web/src/utils/logging.ts (1)
sanitizeForLog(10-21)
web/src/api/endpoints/coordination.ts (2)
web/src/api/types.ts (3)
CoordinateTaskRequest(872-877)CoordinationResultResponse(886-894)ApiResponse(128-130)web/src/api/client.ts (2)
apiClient(12-16)unwrap(83-93)
web/src/hooks/useAuth.ts (3)
web/src/api/types.ts (2)
UserInfoResponse(172-177)HumanRole(46-52)web/src/stores/auth.ts (4)
useIsAuthenticated(208-208)useAuthStore(61-204)useUserRole(210-210)useMustChangePassword(212-215)web/src/utils/constants.ts (1)
WRITE_ROLES(53-53)
web/src/api/endpoints/messages.ts (2)
web/src/api/types.ts (5)
PaginationParams(936-939)Message(619-630)PaginatedResponse(139-141)Channel(634-638)ApiResponse(128-130)web/src/api/client.ts (4)
PaginatedResult(113-118)apiClient(12-16)unwrapPaginated(124-144)unwrap(83-93)
web/src/__tests__/utils/logging.test.ts (1)
web/src/utils/logging.ts (1)
sanitizeForLog(10-21)
web/src/__tests__/api/client.property.test.ts (2)
web/src/api/types.ts (2)
ApiResponse(128-130)PaginatedResponse(139-141)web/src/api/client.ts (3)
unwrap(83-93)ApiRequestError(69-77)unwrapPaginated(124-144)
web/src/api/endpoints/company.ts (2)
web/src/api/types.ts (5)
CompanyConfig(467-471)ApiResponse(128-130)PaginationParams(936-939)Department(456-460)PaginatedResponse(139-141)web/src/api/client.ts (4)
apiClient(12-16)unwrap(83-93)PaginatedResult(113-118)unwrapPaginated(124-144)
web/src/api/endpoints/meetings.ts (2)
web/src/api/types.ts (5)
MeetingFilters(742-747)MeetingRecord(732-740)PaginatedResponse(139-141)ApiResponse(128-130)TriggerMeetingRequest(749-752)web/src/api/client.ts (3)
PaginatedResult(113-118)unwrapPaginated(124-144)unwrap(83-93)
web/src/__tests__/hooks/useLoginLockout.test.ts (2)
web/src/hooks/useLoginLockout.ts (1)
useLoginLockout(9-60)web/src/utils/constants.ts (2)
LOGIN_MAX_ATTEMPTS(20-20)LOGIN_LOCKOUT_MS(21-21)
web/src/api/endpoints/budget.ts (2)
web/src/api/types.ts (6)
BudgetConfig(394-401)ApiResponse(128-130)PaginationParams(936-939)CostRecord(369-379)PaginatedResponse(139-141)AgentSpending(403-406)web/src/api/client.ts (4)
apiClient(12-16)unwrap(83-93)PaginatedResult(113-118)unwrapPaginated(124-144)
web/src/api/endpoints/auth.ts (4)
web/src/stores/auth.ts (3)
setup(150-152)login(146-148)changePassword(180-198)src/synthorg/api/auth/controller.py (8)
setup(268-335)SetupRequest(68-85)TokenResponse(125-138)login(343-393)LoginRequest(88-99)ChangePasswordRequest(102-119)UserInfoResponse(141-156)WsTicketResponse(159-170)web/src/api/types.ts (7)
SetupRequest(151-151)TokenResponse(161-165)ApiResponse(128-130)LoginRequest(154-154)ChangePasswordRequest(156-159)UserInfoResponse(172-177)WsTicketResponse(167-170)web/src/api/client.ts (2)
apiClient(12-16)unwrap(83-93)
web/src/hooks/useLoginLockout.ts (2)
web/src/utils/errors.ts (1)
isAxiosError(9-11)web/src/utils/constants.ts (2)
LOGIN_MAX_ATTEMPTS(20-20)LOGIN_LOCKOUT_MS(21-21)
web/src/__tests__/hooks/useOptimisticUpdate.test.ts (1)
web/src/hooks/useOptimisticUpdate.ts (1)
useOptimisticUpdate(17-72)
web/src/__tests__/hooks/useAuth.test.ts (3)
web/src/stores/auth.ts (1)
useAuthStore(61-204)web/src/hooks/useAuth.ts (1)
useAuth(10-31)web/src/api/types.ts (1)
UserInfoResponse(172-177)
web/src/api/endpoints/analytics.ts (2)
web/src/api/types.ts (6)
OverviewMetrics(410-420)ApiResponse(128-130)TrendPeriod(422-422)TrendMetric(424-424)TrendsResponse(433-438)ForecastResponse(445-452)web/src/api/client.ts (2)
apiClient(12-16)unwrap(83-93)
web/src/__tests__/utils/constants.test.ts (2)
web/src/utils/constants.ts (15)
WS_RECONNECT_BASE_DELAY(7-7)WS_RECONNECT_MAX_DELAY(8-8)WS_MAX_RECONNECT_ATTEMPTS(9-9)WS_MAX_MESSAGE_SIZE(11-11)DEFAULT_PAGE_SIZE(15-15)MAX_PAGE_SIZE(16-16)LOGIN_MAX_ATTEMPTS(20-20)LOGIN_LOCKOUT_MS(21-21)MIN_PASSWORD_LENGTH(18-18)WRITE_ROLES(53-53)VALID_TRANSITIONS(40-50)TASK_STATUS_ORDER(24-34)TERMINAL_STATUSES(37-37)NAMESPACE_ORDER(62-70)NAMESPACE_DISPLAY_NAMES(73-83)web/src/api/types.ts (1)
TaskStatus(5-14)
web/src/api/endpoints/backup.ts (2)
web/src/api/types.ts (5)
BackupManifest(902-910)ApiResponse(128-130)BackupInfo(912-919)RestoreRequest(921-925)RestoreResponse(927-932)web/src/api/client.ts (3)
apiClient(12-16)unwrap(83-93)unwrapVoid(99-110)
web/src/hooks/usePolling.ts (1)
web/src/utils/logging.ts (1)
sanitizeForLog(10-21)
web/src/__tests__/utils/errors.test.ts (2)
web/src/utils/errors.ts (3)
isAxiosError(9-11)getErrorMessage(17-70)getErrorDetail(77-83)web/src/api/types.ts (1)
ErrorDetail(114-123)
web/src/api/endpoints/agents.ts (2)
web/src/api/types.ts (6)
PaginationParams(936-939)AgentConfig(351-365)PaginatedResponse(139-141)ApiResponse(128-130)AutonomyLevelResponse(652-656)AutonomyLevelRequest(658-660)web/src/api/client.ts (4)
PaginatedResult(113-118)apiClient(12-16)unwrapPaginated(124-144)unwrap(83-93)
web/src/__tests__/stores/auth.test.ts (2)
web/src/api/types.ts (2)
TokenResponse(161-165)UserInfoResponse(172-177)web/src/stores/auth.ts (1)
useAuthStore(61-204)
web/src/utils/errors.ts (2)
web/src/api/types.ts (1)
ErrorDetail(114-123)src/synthorg/api/dto.py (1)
ErrorDetail(53-87)
web/src/api/endpoints/approvals.ts (2)
web/src/api/types.ts (7)
ApprovalFilters(295-301)ApprovalItem(260-275)PaginatedResponse(139-141)ApiResponse(128-130)CreateApprovalRequest(277-285)ApproveRequest(287-289)RejectRequest(291-293)web/src/api/client.ts (3)
PaginatedResult(113-118)unwrapPaginated(124-144)unwrap(83-93)
web/src/api/endpoints/index.ts (2)
web/src/stores/auth.ts (1)
setup(150-152)web/src/api/endpoints/auth.ts (1)
setup(12-15)
web/src/hooks/useOptimisticUpdate.ts (1)
web/src/utils/errors.ts (1)
getErrorMessage(17-70)
web/src/api/endpoints/settings.ts (3)
web/src/api/types.ts (5)
SettingDefinition(1064-1079)ApiResponse(128-130)SettingNamespace(1047-1056)SettingEntry(1081-1086)UpdateSettingRequest(1089-1091)web/src/api/client.ts (3)
apiClient(12-16)unwrap(83-93)unwrapVoid(99-110)src/synthorg/api/controllers/settings.py (1)
UpdateSettingRequest(32-41)
web/src/__tests__/utils/format.test.ts (1)
web/src/utils/format.ts (6)
formatDate(6-17)formatRelativeTime(22-36)formatCurrency(41-48)formatNumber(53-55)formatUptime(60-70)formatLabel(75-80)
web/src/__tests__/api/client.test.ts (2)
web/src/api/types.ts (3)
ErrorDetail(114-123)ApiResponse(128-130)PaginatedResponse(139-141)web/src/api/client.ts (5)
ApiRequestError(69-77)unwrap(83-93)unwrapVoid(99-110)unwrapPaginated(124-144)apiClient(12-16)
web/src/api/endpoints/tasks.ts (2)
web/src/api/types.ts (8)
TaskFilters(250-256)Task(191-217)PaginatedResponse(139-141)ApiResponse(128-130)CreateTaskRequest(219-229)UpdateTaskRequest(231-238)TransitionTaskRequest(240-244)CancelTaskRequest(246-248)web/src/api/client.ts (5)
PaginatedResult(113-118)apiClient(12-16)unwrapPaginated(124-144)unwrap(83-93)unwrapVoid(99-110)
web/src/hooks/useWebSocket.ts (4)
web/src/api/types.ts (3)
WsChannel(756-763)WsEventHandler(823-823)WsSubscriptionFilters(799-799)web/src/stores/auth.ts (2)
useAuthStore(61-204)setup(150-152)web/src/stores/websocket.ts (1)
useWebSocketStore(109-344)web/src/utils/logging.ts (1)
sanitizeForLog(10-21)
web/src/api/endpoints/providers.ts (2)
web/src/api/types.ts (14)
ProviderConfig(489-501)ApiResponse(128-130)ProviderModelConfig(477-484)CreateProviderRequest(503-516)UpdateProviderRequest(518-531)TestConnectionRequest(533-535)TestConnectionResponse(537-542)ProviderPreset(544-553)CreateFromPresetRequest(561-567)ProbePresetResponse(555-559)DiscoverModelsResponse(569-572)DiscoveryPolicyResponse(574-578)AddAllowlistEntryRequest(580-582)RemoveAllowlistEntryRequest(584-586)web/src/api/client.ts (3)
apiClient(12-16)unwrap(83-93)unwrapVoid(99-110)
web/src/stores/websocket.ts (4)
web/src/api/types.ts (4)
WsChannel(756-763)WsEventHandler(823-823)WsSubscriptionFilters(799-799)WsEvent(791-796)web/src/utils/logging.ts (1)
sanitizeForLog(10-21)web/src/utils/constants.ts (4)
WS_MAX_RECONNECT_ATTEMPTS(9-9)WS_RECONNECT_BASE_DELAY(7-7)WS_RECONNECT_MAX_DELAY(8-8)WS_MAX_MESSAGE_SIZE(11-11)web/src/api/endpoints/auth.ts (1)
getWsTicket(32-35)
web/src/__tests__/stores/websocket.test.ts (2)
web/src/stores/websocket.ts (1)
useWebSocketStore(109-344)web/src/api/types.ts (1)
WsEvent(791-796)
web/src/api/client.ts (2)
web/src/stores/auth.ts (1)
useAuthStore(61-204)web/src/api/types.ts (3)
ErrorDetail(114-123)ApiResponse(128-130)PaginatedResponse(139-141)
web/src/api/endpoints/setup.ts (3)
web/src/api/types.ts (14)
SetupStatusResponse(943-951)ApiResponse(128-130)TemplateInfoResponse(964-971)SetupCompanyRequest(973-977)SetupCompanyResponse(1001-1008)SetupAgentRequest(979-988)SetupAgentResponse(1010-1016)SetupAgentSummary(990-999)SetupAgentsListResponse(1027-1030)UpdateAgentModelRequest(1018-1021)UpdateAgentNameRequest(1023-1025)AvailableLocalesResponse(1040-1043)SetupNameLocalesResponse(1036-1038)SetupNameLocalesRequest(1032-1034)src/synthorg/api/controllers/setup_models.py (13)
SetupStatusResponse(12-33)TemplateInfoResponse(36-62)SetupCompanyRequest(65-78)SetupCompanyResponse(107-132)SetupAgentRequest(135-176)SetupAgentResponse(179-196)SetupAgentSummary(81-104)SetupAgentsListResponse(225-241)UpdateAgentModelRequest(199-210)UpdateAgentNameRequest(213-222)AvailableLocalesResponse(269-280)SetupNameLocalesResponse(257-266)SetupNameLocalesRequest(244-254)web/src/api/client.ts (2)
apiClient(12-16)unwrap(83-93)
web/src/stores/auth.ts (2)
web/src/api/types.ts (2)
UserInfoResponse(172-177)HumanRole(46-52)web/src/utils/errors.ts (2)
isAxiosError(9-11)getErrorMessage(17-70)
web/src/__tests__/hooks/usePolling.test.ts (1)
web/src/hooks/usePolling.ts (1)
usePolling(12-87)
web/src/api/types.ts (10)
src/synthorg/core/enums.py (21)
TaskStatus(226-252)TaskType(255-263)Priority(266-272)Complexity(275-281)ApprovalStatus(462-468)ApprovalRiskLevel(471-477)SeniorityLevel(6-21)AgentStatus(67-73)AutonomyLevel(487-497)DepartmentName(212-223)ProjectStatus(292-299)RiskTolerance(76-81)CreativityLevel(84-89)DecisionMakingStyle(339-345)CollaborationPreference(348-353)CommunicationVerbosity(356-361)ConflictApproach(364-371)TaskStructure(374-383)CoordinationTopology(386-397)ToolAccessLevel(302-319)MemoryLevel(92-98)src/synthorg/api/guards.py (1)
HumanRole(18-26)src/synthorg/api/errors.py (2)
ErrorCategory(15-28)ErrorCode(31-69)src/synthorg/api/dto.py (12)
ErrorDetail(53-87)ApiResponse(130-158)PaginationMeta(161-174)PaginatedResponse(177-210)CreateTaskRequest(216-241)UpdateTaskRequest(244-269)TransitionTaskRequest(272-289)CreateProviderRequest(523-559)UpdateProviderRequest(562-603)CoordinateTaskRequest(388-427)CoordinationPhaseResponse(430-456)CoordinationResultResponse(459-487)src/synthorg/providers/enums.py (1)
AuthType(6-12)src/synthorg/providers/presets.py (1)
ProviderPreset(16-40)src/synthorg/api/controllers/health.py (1)
HealthStatus(31-54)src/synthorg/hr/performance/models.py (1)
CollaborationScoreResult(160-186)src/synthorg/api/controllers/collaboration.py (2)
SetOverrideRequest(39-60)OverrideResponse(63-82)src/synthorg/api/controllers/setup_models.py (3)
SetupCompanyRequest(65-78)SetupAgentRequest(135-176)SetupNameLocalesRequest(244-254)
web/src/__tests__/hooks/useWebSocket.test.ts (2)
web/src/stores/websocket.ts (1)
useWebSocketStore(109-344)web/src/hooks/useWebSocket.ts (1)
useWebSocket(45-126)
🪛 GitHub Check: CodeQL
web/src/stores/websocket.ts
[warning] 214-214: Log injection
Log entry depends on a user-provided value.
|
Warning Gemini is experiencing higher than usual traffic and was unable to create the review. Please try again in a few hours by commenting |
Backend (ws.py): - Fix ?ticket= empty string bypass (use `is not None`) - Reorder accept before subscribe to prevent subscriber leak - Extract _reject_auth helper to reduce _read_auth_message length - Extract _matches_filters helper to reduce _on_event length - Change _on_event send failure log level from WARNING to ERROR - Fix wrong event name API_WS_SEND_FAILED for subscribe failure - Rename accepted param to already_accepted for clarity Frontend: - Use getErrorMessage (not sanitizeForLog) for UI error state in usePolling - Fix sanitizeForLog: strip C1 controls, handle non-positive maxLen, use codePointAt for supplementary Unicode, catch unstringifiable objects - Add NaN/Infinity guards to formatCurrency/formatNumber - Add 4xx fallback in getErrorMessage for uncommon status codes (405, 410, etc.) - Add console.warn for messages failing isWsEvent validation - Derive VALID_WS_CHANNELS from WS_CHANNELS const array (single source of truth) - Add readonly to WsChannel arrays in WS message types - Update useOptimisticUpdate docs for 3rd null-return case - Consistent conditional params in analytics.ts getTrends Tests: - Import WRITE_ROLES constant in useAuth test (prevent drift) - Extend sanitizeForLog property test with BIDI + C1 control checks - Use idiomatic expect().toThrow in client.test.ts - Simplify first-message WS auth test structure Docs & CI: - Add useLoginLockout to CLAUDE.md hooks description - Enable retry-on-snapshot-warnings in dependency-review workflow Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/stores/websocket.ts`:
- Around line 263-271: The connect() function can return undefined when
socket.readyState === WebSocket.CONNECTING, so change the check order to prefer
an existing connectPromise: inside connect(), check if connectPromise exists
first and return it, then check for socket.readyState === WebSocket.OPEN (and
return), and only after those checks proceed to create a new connectPromise via
doConnect(...). Reference symbols: function connect(), variable connectPromise,
socket.readyState, WebSocket.CONNECTING and doConnect(generation).
🪄 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: f84366d5-8608-4d53-940c-542a14c1592f
📒 Files selected for processing (15)
.github/workflows/dependency-review.ymlCLAUDE.mdsrc/synthorg/api/controllers/ws.pytests/unit/api/controllers/test_ws.pyweb/src/__tests__/api/client.test.tsweb/src/__tests__/hooks/useAuth.test.tsweb/src/__tests__/utils/logging.property.test.tsweb/src/api/endpoints/analytics.tsweb/src/api/types.tsweb/src/hooks/useOptimisticUpdate.tsweb/src/hooks/usePolling.tsweb/src/stores/websocket.tsweb/src/utils/errors.tsweb/src/utils/format.tsweb/src/utils/logging.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: Test (Python 3.14)
- GitHub Check: Build Backend
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{py,pyi}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{py,pyi}: Do NOT usefrom __future__ import annotations-- Python 3.14+ supports PEP 649 native lazy annotations
Useexcept A, B:syntax without parentheses for exception handling (PEP 758) -- ruff enforces this on Python 3.14
All public functions and classes must have type hints and Google-style docstrings, enforced by ruff D rules and mypy strict mode
Functions must be under 50 lines, files must be under 800 lines
Use immutability: create new objects, never mutate existing ones. For non-Pydantic internal collections, usecopy.deepcopy()at construction andMappingProxyTypewrapping for read-only enforcement
For frozen Pydantic models with dict/list fields, usecopy.deepcopy()at system boundaries (tool execution, LLM provider serialization, inter-agent delegation, persistence serialization)
Use Pydantic v2 with frozen models for config/identity; use mutable-via-copy models withmodel_copy(update=...)for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model
Use@computed_fieldfor derived values instead of storing redundant fields; useNotBlankStrfromcore.typesfor all identifier/name fields (including optional and tuple variants) instead of manual validators
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new code (multiple tool invocations, parallel agent calls) over barecreate_task
Never implement retry logic in driver subclasses or calling code -- all provider calls go throughBaseCompletionProviderwhich applies retry and rate limiting automatically
Handle all errors explicitly -- never silently swallow exceptions
Validate input at system boundaries: user input, external APIs, and config files
Files:
tests/unit/api/controllers/test_ws.pysrc/synthorg/api/controllers/ws.py
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Line length: 88 characters (enforced by ruff)
Files:
tests/unit/api/controllers/test_ws.pysrc/synthorg/api/controllers/ws.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
tests/**/*.py: Mark all tests with pytest markers:@pytest.mark.unit,@pytest.mark.integration,@pytest.mark.e2e, or@pytest.mark.slow
NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT) in tests or project-owned code -- use generic names:example-provider,test-provider,test-small-001,large/medium/smallas aliases
For property-based testing, use Hypothesis with@givendecorator and@settings; run withHYPOTHESIS_PROFILE=dev uv run pytest tests/ -m unit -n auto -k propertiesfor 1000 examples
ALWAYS run pytest with-n autofor parallelism via pytest-xdist; never run tests sequentially
For timing-sensitive tests, mocktime.monotonic()andasyncio.sleep()to make them deterministic; for tasks that must block indefinitely, useasyncio.Event().wait()instead of largeasyncio.sleep()calls
Maintain minimum 80% code coverage (enforced in CI). NEVER skip, dismiss, or ignore flaky tests -- always fix them fully and fundamentally
Files:
tests/unit/api/controllers/test_ws.py
web/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx,js,jsx}: React 19 dashboard with shadcn/ui components, Tailwind CSS 4, Zustand for state,@tanstack/react-queryfor server state,@xyflow/reactfor workflow visualization
NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT) in web dashboard code -- use generic names likeexample-providerortest-provider
Files:
web/src/__tests__/utils/logging.property.test.tsweb/src/hooks/usePolling.tsweb/src/api/endpoints/analytics.tsweb/src/__tests__/hooks/useAuth.test.tsweb/src/utils/logging.tsweb/src/hooks/useOptimisticUpdate.tsweb/src/utils/format.tsweb/src/utils/errors.tsweb/src/__tests__/api/client.test.tsweb/src/stores/websocket.tsweb/src/api/types.ts
web/src/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use Vitest for unit tests with
--detect-async-leaksflag; use fast-check for property-based testing withfc.assertandfc.property
Files:
web/src/__tests__/utils/logging.property.test.tsweb/src/__tests__/hooks/useAuth.test.tsweb/src/__tests__/api/client.test.ts
src/synthorg/**/*.{py,pyi}
📄 CodeRabbit inference engine (CLAUDE.md)
src/synthorg/**/*.{py,pyi}: Every module with business logic MUST have:from synthorg.observability import get_loggerthenlogger = get_logger(__name__)-- never useimport loggingorprint()
Always use event name constants fromsynthorg.observability.events.<domain>modules (e.g.API_REQUEST_STARTEDfromevents.api,TOOL_INVOKE_STARTfromevents.tool) instead of string literals
Always use structured logging with kwargs:logger.info(EVENT, key=value)-- never use format strings likelogger.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 and internal flow
Files:
src/synthorg/api/controllers/ws.py
🧠 Learnings (21)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T11:40:22.911Z
Learning: Applies to web/src/**/*.{ts,tsx,js,jsx} : React 19 dashboard with shadcn/ui components, Tailwind CSS 4, Zustand for state, tanstack/react-query for server state, xyflow/react for workflow visualization
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.
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
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to web/** : Web dashboard: Node.js 20+, dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, fast-check, ESLint, vue-tsc).
📚 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/dependency-review.yml
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).
Applied to files:
web/src/__tests__/utils/logging.property.test.tsweb/src/__tests__/hooks/useAuth.test.tsCLAUDE.mdweb/src/__tests__/api/client.test.ts
📚 Learning: 2026-03-24T11:40:22.911Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T11:40:22.911Z
Learning: Applies to web/src/**/*.test.{ts,tsx} : Use Vitest for unit tests with `--detect-async-leaks` flag; use fast-check for property-based testing with `fc.assert` and `fc.property`
Applied to files:
web/src/__tests__/utils/logging.property.test.tsweb/src/__tests__/hooks/useAuth.test.tsweb/src/__tests__/api/client.test.ts
📚 Learning: 2026-03-15T21:20:09.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:20:09.993Z
Learning: Applies to web/src/components/** : Vue components organized by feature (agents/, approvals/, budget/, common/, dashboard/, layout/, messages/, org-chart/, tasks/).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-24T11:40:22.911Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T11:40:22.911Z
Learning: Applies to web/src/**/*.{ts,tsx,js,jsx} : React 19 dashboard with shadcn/ui components, Tailwind CSS 4, Zustand for state, tanstack/react-query for server state, xyflow/react for workflow visualization
Applied to files:
CLAUDE.md
📚 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 web/** : Web dashboard: Node.js 20+, dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, fast-check, ESLint, vue-tsc).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to web/package.json : Web dashboard Node.js 20+; dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, ESLint, vue-tsc)
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
CLAUDE.md
📚 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:
CLAUDE.md
📚 Learning: 2026-03-15T20:45:14.430Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T20:45:14.430Z
Learning: Shell commands: for Go CLI work, cd cli is an exception because Go tooling requires working directory to be the module root. Go commands require `cd cli` for other work, never use `cd`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to docs/** : Docs source in docs/ (Markdown, built with Zensical); design spec in docs/design/ (7 pages: index, agents, organization, communication, engine, memory, operations)
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Documentation source in `docs/` (Markdown, built with Zensical). Design spec in `docs/design/` (7 pages: index, agents, organization, communication, engine, memory, operations). Architecture in `docs/architecture/` (overview, tech-stack, decision log). Roadmap in `docs/roadmap/`. Security in `docs/security.md`. Licensing in `docs/licensing.md`. Reference in `docs/reference/`. REST API reference in `docs/rest-api.md`. Library reference in `docs/api/` (auto-generated from docstrings). Custom templates in `docs/overrides/`. Config in `mkdocs.yml`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-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 cli/**/*.go : Go CLI (Go 1.26+) uses Cobra for commands, charmbracelet/huh for interactive CLI, charmbracelet/lipgloss for styled output. Cross-platform builds (linux/darwin/windows × amd64/arm64). GoReleaser for releases with cosign keyless signing of checksums.txt. SLSA L3 provenance attestations via actions/attest-build-provenance.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/api/**/*.py : Use Litestar for REST + WebSocket API. Controllers, guards, channels, JWT + API key + WS ticket auth, RFC 9457 structured errors.
Applied to files:
src/synthorg/api/controllers/ws.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/ws.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/ws.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/api/**/*.py : Authentication uses JWT + API key. Approval gate integration for high-risk operations.
Applied to files:
src/synthorg/api/controllers/ws.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Settings: Runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge, ConfigResolver (typed composed reads for controllers), validation, registry, change notifications via message bus. Per-namespace setting definitions in definitions/ submodule (api, company, providers, memory, budget, security, coordination, observability, backup).
Applied to files:
web/src/api/types.ts
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings package (settings/): runtime-editable settings persistence (DB > env > YAML > code defaults), typed definitions (9 namespaces), Fernet encryption for sensitive values, config bridge (JSON serialization for Pydantic/collections), ConfigResolver (typed accessors), validation, registry, change notifications via message bus, SettingsSubscriber protocol, SettingsChangeDispatcher (polls `#settings` channel, routes to subscribers, restart_required filtering)
Applied to files:
web/src/api/types.ts
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/settings/**/*.py : Settings use runtime-editable persistence with precedence: DB > env > YAML > code defaults. 8 namespaces with Fernet encryption for sensitive values.
Applied to files:
web/src/api/types.ts
🧬 Code graph analysis (11)
tests/unit/api/controllers/test_ws.py (1)
tests/unit/api/conftest.py (1)
test_client(217-252)
web/src/__tests__/utils/logging.property.test.ts (1)
web/src/utils/logging.ts (1)
sanitizeForLog(10-31)
web/src/hooks/usePolling.ts (2)
web/src/utils/errors.ts (1)
getErrorMessage(17-75)web/src/utils/logging.ts (1)
sanitizeForLog(10-31)
web/src/api/endpoints/analytics.ts (2)
web/src/api/types.ts (6)
OverviewMetrics(410-420)ApiResponse(128-130)TrendPeriod(422-422)TrendMetric(424-424)TrendsResponse(433-438)ForecastResponse(445-452)web/src/api/client.ts (2)
apiClient(12-16)unwrap(83-93)
web/src/__tests__/hooks/useAuth.test.ts (4)
web/src/stores/auth.ts (1)
useAuthStore(61-204)web/src/hooks/useAuth.ts (1)
useAuth(10-31)web/src/api/types.ts (2)
UserInfoResponse(172-177)HumanRole(46-52)web/src/utils/constants.ts (1)
WRITE_ROLES(53-53)
web/src/hooks/useOptimisticUpdate.ts (1)
web/src/utils/errors.ts (1)
getErrorMessage(17-75)
web/src/utils/errors.ts (1)
web/src/api/types.ts (1)
ErrorDetail(114-123)
web/src/__tests__/api/client.test.ts (2)
web/src/api/types.ts (3)
ErrorDetail(114-123)ApiResponse(128-130)PaginatedResponse(139-141)web/src/api/client.ts (5)
ApiRequestError(69-77)unwrap(83-93)unwrapVoid(99-110)unwrapPaginated(124-144)apiClient(12-16)
web/src/stores/websocket.ts (4)
web/src/api/types.ts (5)
WsChannel(759-759)WsEventHandler(819-819)WsSubscriptionFilters(795-795)WS_CHANNELS(757-757)WsEvent(787-792)web/src/utils/logging.ts (1)
sanitizeForLog(10-31)web/src/utils/constants.ts (4)
WS_MAX_RECONNECT_ATTEMPTS(9-9)WS_RECONNECT_BASE_DELAY(7-7)WS_RECONNECT_MAX_DELAY(8-8)WS_MAX_MESSAGE_SIZE(11-11)web/src/api/endpoints/auth.ts (1)
getWsTicket(32-35)
src/synthorg/api/controllers/ws.py (3)
src/synthorg/api/auth/models.py (1)
AuthenticatedUser(69-88)src/synthorg/api/auth/ticket_store.py (1)
validate_and_consume(117-150)web/src/stores/websocket.ts (1)
subscribe(293-313)
web/src/api/types.ts (9)
src/synthorg/core/enums.py (10)
TaskStatus(226-252)TaskType(255-263)Priority(266-272)Complexity(275-281)ApprovalStatus(462-468)ApprovalRiskLevel(471-477)SeniorityLevel(6-21)AgentStatus(67-73)AutonomyLevel(487-497)DepartmentName(212-223)src/synthorg/api/guards.py (1)
HumanRole(18-26)src/synthorg/api/errors.py (2)
ErrorCategory(15-28)ErrorCode(31-69)src/synthorg/api/dto.py (4)
ErrorDetail(53-87)ApiResponse(130-158)PaginationMeta(161-174)PaginatedResponse(177-210)src/synthorg/api/auth/controller.py (5)
SetupRequest(68-85)LoginRequest(88-99)ChangePasswordRequest(102-119)TokenResponse(125-138)UserInfoResponse(141-156)src/synthorg/config/schema.py (3)
AgentConfig(316-369)ProviderModelConfig(44-83)ProviderConfig(86-240)src/synthorg/providers/enums.py (1)
AuthType(6-12)src/synthorg/providers/presets.py (1)
ProviderPreset(16-40)src/synthorg/hr/performance/models.py (2)
CollaborationScoreResult(160-186)LlmCalibrationRecord(189-245)
🪛 GitHub Check: CodeQL
web/src/stores/websocket.ts
[warning] 213-213: Log injection
Log entry depends on a user-provided value.
🔇 Additional comments (27)
.github/workflows/dependency-review.yml (1)
25-25: Good resilience improvement for transient snapshot warnings.Enabling retries here should reduce flaky dependency-review failures without relaxing your severity/license gates.
web/src/__tests__/hooks/useAuth.test.ts (5)
1-12: LGTM!Imports are well-organized and the
WRITE_ROLESconstant is correctly imported from the shared constants module, addressing the earlier review feedback about preventing test drift.
14-28: LGTM!The
resetStorehelper provides proper test isolation by clearing both localStorage and Zustand state. The unauthenticated state test correctly verifies all expected default values.
30-44: LGTM!The authenticated state test properly verifies user propagation and derived values (
userRole,mustChangePassword) from the store state.
46-72: LGTM!The
canWritetest suite correctly uses the sharedWRITE_ROLESconstant and derivesreadOnlyRolesdynamically. TheallRolesarray is typed asreadonly HumanRole[], so TypeScript will flag any invalid role values. The parameterized tests provide comprehensive coverage for all role permutations.
74-87: LGTM!Good coverage of the
mustChangePasswordbehavior, including the fallback path when user is null but_mustChangePasswordFallbackis set (which handles the page refresh scenario mentioned in the store implementation).web/src/api/types.ts (4)
128-141: Response envelope types don't match backend contract.Per the backend
dto.py,error_detailcan beNoneeven on failure, andPaginatedResponsealways haspaginationanddata(defaulting to empty tuple). The current types advertise guarantees the backend doesn't make.See past review for suggested fix:
ApiResponseerror branch:error_detail: ErrorDetail | nullPaginatedResponseerror branch:data: T[]; pagination: PaginationMeta
997-1004:agent_countis not in the backend response.
src/synthorg/api/controllers/setup_models.pydoesn't defineagent_countonSetupCompanyResponse. This field will deserialize asundefined. Derive it fromagents.lengthinstead.export interface SetupCompanyResponse { company_name: string description: string | null template_applied: string | null department_count: number - agent_count: number readonly agents: readonly SetupAgentSummary[] }
1-91: LGTM! Enum types align with backend.The string union types for
TaskStatus,TaskType,Priority,Complexity,ApprovalStatus,SeniorityLevel,AgentStatus,AutonomyLevel,HumanRole, andDepartmentNamecorrectly mirror the backendStrEnumvalues fromsrc/synthorg/core/enums.pyandsrc/synthorg/api/guards.py.
754-819: LGTM! WebSocket types are well-defined.The
WS_CHANNELSconst array,WsChanneltype,WsEventTypeunion, and message interfaces provide type-safe WebSocket communication. Theas constassertion ensures channel names are literal types.CLAUDE.md (1)
123-130: LGTM!The documentation updates accurately reflect the new infrastructure layer additions. The directory descriptions for
api/,hooks/,stores/,utils/, and__tests__/align well with the PR objectives and the actual modules being added.web/src/utils/logging.ts (1)
1-31: LGTM!The sanitization logic correctly handles:
- C0 controls (
< 0x20), DEL (0x7f), and C1 controls (0x80-0x9f)- BIDI override/direction control characters across multiple Unicode ranges
- Non-finite
maxLenvalues with sensible defaults- Error objects (extracting stack/message)
- Unstringifiable values with a safe fallback
The use of
codePointAt(0)withfor...ofiteration correctly handles characters outside the BMP.web/src/__tests__/utils/logging.property.test.ts (1)
1-44: LGTM!The property-based tests provide excellent coverage:
- Length bounds are verified across a wide range of inputs
- Control character assertions correctly mirror the implementation's filtering logic
- The BIDI control Set exactly matches the ranges in
isBidiControl- Type stability test with
fc.anything()validates robustnessGood use of
fc.assertandfc.propertyper coding guidelines.web/src/utils/format.ts (1)
1-82: LGTM!The formatting utilities are well-implemented with comprehensive defensive handling:
- Date formatters gracefully handle null/undefined/invalid inputs
- Numeric formatters guard against
NaN/Infinity(addressing past review)formatUptimecorrectly handles non-finite/negative inputs- Edge cases like zero uptime (
0m) and future dates are handled appropriatelyweb/src/__tests__/api/client.test.ts (2)
176-182: Fragile but acceptable test approach for interceptor verification.Accessing Axios's internal
handlersarray is necessary to unit-test interceptor behavior in isolation. The defensive throw on line 180 will surface breakage if Axios internals change. Consider adding a comment noting this is Axios-version-dependent.
1-246: LGTM!Comprehensive test coverage for the API client layer:
ApiRequestErrorconstruction and inheritance- All three unwrap helpers with success, error, and edge cases (null body, non-object, missing pagination, non-array data)
- 204 No Content handling for
unwrapVoid- Request interceptor auth token injection
- Response interceptor 401 handling with localStorage cleanup
src/synthorg/api/controllers/ws.py (4)
103-113: Clean helper extraction for auth rejection.The
_reject_authhelper consolidates the repeated warning/close/return pattern, improving maintainability while keeping the function well under the 50-line limit.
315-336: Auth flow correctly implements both paths.The
_authenticate_wsfunction properly:
- Uses
is not Nonecheck (line 325) to handle empty?ticket=as query-param auth rather than falling through to first-message auth (addressing past review)- Accepts the socket before reading on the first-message path (line 332), which is required to read messages
- Returns the
already_acceptedflag to prevent double-accept in_setup_connection
380-395: Accept-before-subscribe ordering is correct.The connection is now accepted (line 382) before subscribing to channels (line 385), preventing subscriber leaks if accept fails. This addresses the past review finding about subscriber cleanup.
246-261: Good defensive validation for event payloads.The
_matches_filtersfunction logs a warning with context (channel, payload type) when encountering non-dict payloads, then returnsfalseto filter them out. This provides observability for unexpected backend events without crashing.tests/unit/api/controllers/test_ws.py (1)
290-337: LGTM! Comprehensive auth rejection tests.The three new tests cover the key authentication failure scenarios:
- Invalid query-param ticket (pre-accept rejection)
- Invalid first-message ticket (post-accept rejection)
- Non-auth first message (wrong action type)
Each test correctly verifies the
_WS_CLOSE_AUTH_FAILEDclose code, ensuring consistent client-facing behavior across all auth failure modes.web/src/stores/websocket.ts (2)
211-215: CodeQL log injection warning is mitigated by sanitization.The static analysis flags user-controlled data reaching
console.error, butsanitizeForLog(msg.error, 200)provides appropriate defense:
- Replaces control characters (newlines, terminal escapes) with spaces
- Strips BIDI override characters that could visually reorder output
- Truncates to 200 characters
This is the standard mitigation pattern for log injection. The warning can be considered a false positive in context.
1-350: Well-architected WebSocket store.The implementation correctly handles:
- Ticket-first auth (line 164): Keeps credentials out of URLs/logs
- Generation-based stale connection prevention (lines 149, 160, 233)
- Exponential backoff with auth-failure circuit breaker (lines 116-128, 238-242)
- Subscription deduplication via stable keys and defensive array copies
- Per-handler error isolation (lines 95-103) prevents one handler from breaking others
- Message size gating before JSON parsing (line 185)
The derivation of
VALID_WS_CHANNELSfromWS_CHANNELS(line 59) correctly addresses the type/runtime sync concern from prior review.web/src/api/endpoints/analytics.ts (1)
1-24: LGTM!Clean implementation of analytics endpoints. The conditional params pattern (lines 14, 21) is consistent across both parameterized functions, and the
horizonDays→horizon_dayssnake_case conversion correctly matches the backend API convention.web/src/utils/errors.ts (1)
1-88: LGTM!Solid error handling utilities with appropriate security measures:
- 5xx responses return generic messages to prevent leaking server internals
- 4xx responses surface backend validation messages when available
- Non-Axios errors are filtered for length and JSON-shaped content to avoid surfacing unexpected internal details
The structured
ErrorDetailextraction enables callers to access retry information and machine-readable error codes when needed.web/src/hooks/usePolling.ts (1)
1-88: LGTM!Well-designed polling hook with several defensive measures:
- Generation counter (
runIdRef) correctly invalidates stale in-flight runs after stop/start cycles- Non-overlapping execution via setTimeout-based scheduling (next tick only scheduled after current completes)
- Proper separation of user-facing errors (
getErrorMessage) from log diagnostics (sanitizeForLog)- Minimum interval validation prevents accidental tight loops
web/src/hooks/useOptimisticUpdate.ts (1)
1-73: LGTM!Clean optimistic update pattern with comprehensive error handling:
- Concurrency guard prevents overlapping executions
- Three failure paths are documented and handled correctly
- Rollback failure appends a helpful "UI may be out of sync" message to guide users
finallyblock ensures pending state is always cleared regardless of outcome
Reorder guards so callers awaiting connect() during CONNECTING state receive the in-flight promise instead of undefined. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
web/src/stores/websocket.ts (1)
70-80: 🧹 Nitpick | 🔵 TrivialConsider using a type predicate to avoid double casting.
isWsEventreturnsboolean, requiring an awkwardmsg as unknown as WsEventcast on line 220. Changing to a type predicate allows TypeScript to narrow the type automatically.♻️ Proposed fix using type predicate
-/** Runtime validation that a parsed message conforms to the WsEvent shape. */ -function isWsEvent(msg: Record<string, unknown>): boolean { +/** Runtime type guard that a parsed message conforms to the WsEvent shape. */ +function isWsEvent(msg: Record<string, unknown>): msg is WsEvent { return ( typeof msg.event_type === 'string' && typeof msg.channel === 'string' && typeof msg.timestamp === 'string' && typeof msg.payload === 'object' && msg.payload !== null && !Array.isArray(msg.payload) ) }Then on line 220:
if (isWsEvent(msg)) { - // isWsEvent validates all required WsEvent fields at runtime; - // dispatchEvent has internal per-handler try/catch - dispatchEvent(msg as unknown as WsEvent) + dispatchEvent(msg) } else {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/stores/websocket.ts` around lines 70 - 80, Change the isWsEvent helper to be a TypeScript type predicate so the compiler can narrow types without manual casts: update its signature from returning boolean to "msg is WsEvent" and keep the same runtime checks inside; then remove the manual "msg as unknown as WsEvent" double-cast at the call site so the variable is treated as a WsEvent automatically. Ensure you import/declare the WsEvent type used by the predicate and update any callers (e.g., where msg is validated before usage) to rely on the narrowed type instead of casting.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/stores/websocket.ts`:
- Around line 158-181: The code sets connected: true inside the
thisSocket.onopen handler before server-side auth is confirmed, which can
briefly show the UI as connected even if auth later fails; to fix, move or delay
the set({ connected: true }) update until after a successful auth
acknowledgement from the server (i.e., handle the server "auth success" message
in your message handler and call set({ connected: true }) there), or
alternatively document the current behavior and add logic in the onclose handler
to clear connected on auth-failure close codes (4001/4003); refer to
thisSocket.onopen, the set({ connected: true }) call, and the onclose/auth
failure close codes when making the change.
---
Duplicate comments:
In `@web/src/stores/websocket.ts`:
- Around line 70-80: Change the isWsEvent helper to be a TypeScript type
predicate so the compiler can narrow types without manual casts: update its
signature from returning boolean to "msg is WsEvent" and keep the same runtime
checks inside; then remove the manual "msg as unknown as WsEvent" double-cast at
the call site so the variable is treated as a WsEvent automatically. Ensure you
import/declare the WsEvent type used by the predicate and update any callers
(e.g., where msg is validated before usage) to rely on the narrowed type instead
of casting.
🪄 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: 5e78b5c5-05e5-4752-97bb-3b1279acb13c
📒 Files selected for processing (1)
web/src/stores/websocket.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Build Sandbox
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (1)
web/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
web/src/**/*.{ts,tsx,js,jsx}: React 19 dashboard with shadcn/ui components, Tailwind CSS 4, Zustand for state,@tanstack/react-queryfor server state,@xyflow/reactfor workflow visualization
NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT) in web dashboard code -- use generic names likeexample-providerortest-provider
Files:
web/src/stores/websocket.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-24T11:40:22.911Z
Learning: Applies to web/src/**/*.{ts,tsx,js,jsx} : React 19 dashboard with shadcn/ui components, Tailwind CSS 4, Zustand for state, tanstack/react-query for server state, xyflow/react for workflow visualization
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to web/** : Web dashboard: Node.js 20+, dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, fast-check, ESLint, vue-tsc).
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
🧬 Code graph analysis (1)
web/src/stores/websocket.ts (4)
web/src/api/types.ts (5)
WsChannel(759-759)WsEventHandler(819-819)WsSubscriptionFilters(795-795)WS_CHANNELS(757-757)WsEvent(787-792)web/src/utils/logging.ts (1)
sanitizeForLog(10-31)web/src/utils/constants.ts (4)
WS_MAX_RECONNECT_ATTEMPTS(9-9)WS_RECONNECT_BASE_DELAY(7-7)WS_RECONNECT_MAX_DELAY(8-8)WS_MAX_MESSAGE_SIZE(11-11)web/src/api/endpoints/auth.ts (1)
getWsTicket(32-35)
🔇 Additional comments (4)
web/src/stores/websocket.ts (4)
211-215: Log injection mitigation is in place.The SAST tool flagged this line for log injection since
msg.erroris user-controlled. The use ofsanitizeForLog(msg.error, 200)properly mitigates this by stripping control characters, BIDI overrides, and truncating to 200 characters. The existing mitigation is adequate.
93-104: LGTM!Good error isolation pattern — each handler is wrapped in try/catch so a failing handler doesn't break event dispatch to other handlers.
293-313: LGTM!The subscription deduplication logic is correct. Active subscriptions are tracked for reconnect replay, and pending subscriptions handle the disconnected case. The use of
subscriptionKeyensures stable deduplication regardless of channel order.
315-337: LGTM!The backward iteration with
splicecorrectly handles index shifting. Channels are removed from bothactiveSubscriptionsandpendingSubscriptionsbefore attempting the send, ensuring consistent state even if the send fails.
| thisSocket.onopen = () => { | ||
| // Guard: if a newer connection replaced us, bail out | ||
| if (socket !== thisSocket) return | ||
|
|
||
| // Send auth ticket as first message (keeps ticket out of URL/logs/history) | ||
| try { | ||
| thisSocket.send(JSON.stringify({ action: 'auth', ticket })) | ||
| } catch (err) { | ||
| console.error('WebSocket auth send failed:', sanitizeForLog(err)) | ||
| thisSocket.close() | ||
| return | ||
| } | ||
|
|
||
| set({ connected: true }) | ||
| reconnectAttempts = 0 | ||
| pendingSubscriptions = [] | ||
| for (const sub of activeSubscriptions) { | ||
| try { | ||
| thisSocket.send(JSON.stringify({ action: 'subscribe', channels: sub.channels, filters: sub.filters })) | ||
| } catch (err) { | ||
| console.error('WebSocket subscribe send failed (will retry on reconnect):', sanitizeForLog(err)) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
connected: true is set before auth acknowledgment.
Line 171 sets connected: true in onopen, but the server hasn't yet acknowledged the auth ticket. If auth fails (close codes 4001/4003), the UI briefly shows connected before onclose corrects it.
This is likely acceptable given the corrective onclose behavior, but consider documenting this timing or deferring connected: true until after receiving a successful server response if precise state is important.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/stores/websocket.ts` around lines 158 - 181, The code sets connected:
true inside the thisSocket.onopen handler before server-side auth is confirmed,
which can briefly show the UI as connected even if auth later fails; to fix,
move or delay the set({ connected: true }) update until after a successful auth
acknowledgement from the server (i.e., handle the server "auth success" message
in your message handler and call set({ connected: true }) there), or
alternatively document the current behavior and add logic in the onclose handler
to clear connected on auth-failure close codes (4001/4003); refer to
thisSocket.onopen, the set({ connected: true }) call, and the onclose/auth
failure close codes when making the change.
- Change isWsEvent return type to type predicate, removing manual cast - Document the brief connected=true flash inherent to first-message auth Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🤖 I have created a release *beep* *boop* --- #MAJOR CHANGES; We got a somewhat working webui :) ## [0.5.0](v0.4.9...v0.5.0) (2026-03-30) ### Features * add analytics trends and budget forecast API endpoints ([#798](#798)) ([16b61f5](16b61f5)) * add department policies to default templates ([#852](#852)) ([7a41548](7a41548)) * add remaining activity event types (task_started, tool_used, delegation, cost_incurred) ([#832](#832)) ([4252fac](4252fac)) * agent performance, activity, and history API endpoints ([#811](#811)) ([9b75c1d](9b75c1d)) * Agent Profiles and Detail pages (biography, career, performance) ([#874](#874)) ([62d7880](62d7880)) * app shell, Storybook, and CI/CD pipeline ([#819](#819)) ([d4dde90](d4dde90)) * Approvals page with risk grouping, urgency indicators, batch actions ([#889](#889)) ([4e9673d](4e9673d)) * Budget Panel page (P&L dashboard, breakdown charts, forecast) ([#890](#890)) ([b63b0f1](b63b0f1)) * build infrastructure layer (API client, auth, WebSocket) ([#815](#815)) ([9f01d3e](9f01d3e)) * CLI global options infrastructure, UI modes, exit codes, env vars ([#891](#891)) ([fef4fc5](fef4fc5)) * CodeMirror editor and theme preferences toggle ([#905](#905), [#807](#807)) ([#909](#909)) ([41fbedc](41fbedc)) * Company page (department/agent management) ([#888](#888)) ([cfb88b0](cfb88b0)) * comprehensive hint coverage across all CLI commands ([#900](#900)) ([937974e](937974e)) * config system extensions, per-command flags for init/start/stop/status/logs ([#895](#895)) ([32f83fe](32f83fe)) * configurable currency system replacing hardcoded USD ([#854](#854)) ([b372551](b372551)) * Dashboard page (metric cards, activity feed, budget burn) ([#861](#861)) ([7d519d5](7d519d5)) * department health, provider status, and activity feed endpoints ([#818](#818)) ([6d5f196](6d5f196)) * design tokens and core UI components ([#833](#833)) ([ed887f2](ed887f2)) * extend approval, meeting, and budget API responses ([#834](#834)) ([31472bf](31472bf)) * frontend polish -- real-time UX, accessibility, responsive, performance ([#790](#790), [#792](#792), [#791](#791), [#793](#793)) ([#917](#917)) ([f04a537](f04a537)) * implement human roles and access control levels ([#856](#856)) ([d6d8a06](d6d8a06)) * implement semantic conflict detection in workspace merge ([#860](#860)) ([d97283b](d97283b)) * interaction components and animation patterns ([#853](#853)) ([82d4b01](82d4b01)) * Login page + first-run bootstrap + Company page ([#789](#789), [#888](#888)) ([#896](#896)) ([8758e8d](8758e8d)) * Meetings page with timeline viz, token bars, contribution formatting ([#788](#788)) ([#904](#904)) ([b207f46](b207f46)) * Messages page with threading, channel badges, sender indicators ([#787](#787)) ([#903](#903)) ([28293ad](28293ad)) * Org Chart force-directed view and drag-drop reassignment ([#872](#872), [#873](#873)) ([#912](#912)) ([a68a938](a68a938)) * Org Chart page (living nodes, status, CRUD, department health) ([#870](#870)) ([0acbdae](0acbdae)) * per-command flags for remaining commands, auto-behavior wiring, help/discoverability ([#897](#897)) ([3f7afa2](3f7afa2)) * Providers page with backend rework -- health, CRUD, subscription auth ([#893](#893)) ([9f8dd98](9f8dd98)) * scaffold React + Vite + TypeScript + Tailwind project ([#799](#799)) ([bd151aa](bd151aa)) * Settings page with search, dependency indicators, grouped rendering ([#784](#784)) ([#902](#902)) ([a7b9870](a7b9870)) * Setup Wizard rebuild with template comparison, cost estimator, theme customization ([#879](#879)) ([ae8b50b](ae8b50b)) * setup wizard UX -- template filters, card metadata, provider form reuse ([#910](#910)) ([7f04676](7f04676)) * setup wizard UX overhaul -- mode choice, step reorder, provider fixes ([#907](#907)) ([ee964c4](ee964c4)) * structured ModelRequirement in template agent configs ([#795](#795)) ([7433548](7433548)) * Task Board page (rich Kanban, filtering, dependency viz) ([#871](#871)) ([04a19b0](04a19b0)) ### Bug Fixes * align frontend types with backend and debounce WS refetches ([#916](#916)) ([134c11b](134c11b)) * auto-cleanup targets newly pulled images instead of old ones ([#884](#884)) ([50e6591](50e6591)) * correct wipe backup-skip flow and harden error handling ([#808](#808)) ([c05860f](c05860f)) * improve provider setup in wizard, subscription auth, dashboard bugs ([#914](#914)) ([87bf8e6](87bf8e6)) * improve update channel detection and add config get command ([#814](#814)) ([6b137f0](6b137f0)) * resolve all ESLint warnings, add zero-warnings enforcement ([#899](#899)) ([079b46a](079b46a)) * subscription auth uses api_key, base URL optional for cloud providers ([#915](#915)) ([f0098dd](f0098dd)) ### Refactoring * semantic analyzer cleanup -- shared filtering, concurrency, extraction ([#908](#908)) ([81372bf](81372bf)) ### Documentation * brand identity and UX design system from [#765](#765) exploration ([#804](#804)) ([389a9f4](389a9f4)) * page structure and information architecture for v0.5.0 dashboard ([#809](#809)) ([f8d6d4a](f8d6d4a)) * write UX design guidelines with WCAG-verified color system ([#816](#816)) ([4a4594e](4a4594e)) ### Tests * add unit tests for agent hooks and page components ([#875](#875)) ([#901](#901)) ([1d81546](1d81546)) ### CI/CD * bump actions/deploy-pages from 4.0.5 to 5.0.0 in the major group ([#831](#831)) ([01c19de](01c19de)) * bump astral-sh/setup-uv from 7.6.0 to 8.0.0 in /.github/actions/setup-python-uv in the all group ([#920](#920)) ([5f6ba54](5f6ba54)) * bump codecov/codecov-action from 5.5.3 to 6.0.0 in the major group ([#868](#868)) ([f22a181](f22a181)) * bump github/codeql-action from 4.34.1 to 4.35.0 in the all group ([#883](#883)) ([87a4890](87a4890)) * bump sigstore/cosign-installer from 4.1.0 to 4.1.1 in the minor-and-patch group ([#830](#830)) ([7a69050](7a69050)) * bump the all group with 3 updates ([#923](#923)) ([ff27c8e](ff27c8e)) * bump wrangler from 4.76.0 to 4.77.0 in /.github in the minor-and-patch group ([#822](#822)) ([07d43eb](07d43eb)) * bump wrangler from 4.77.0 to 4.78.0 in /.github in the all group ([#882](#882)) ([f84118d](f84118d)) ### Maintenance * add design system enforcement hook and component inventory ([#846](#846)) ([15abc43](15abc43)) * add dev-only auth bypass for frontend testing ([#885](#885)) ([6cdcd8a](6cdcd8a)) * add pre-push rebase check hook ([#855](#855)) ([b637a04](b637a04)) * backend hardening -- eviction/size-caps and model validation ([#911](#911)) ([81253d9](81253d9)) * bump axios from 1.13.6 to 1.14.0 in /web in the all group across 1 directory ([#922](#922)) ([b1b0232](b1b0232)) * bump brace-expansion from 5.0.4 to 5.0.5 in /web ([#862](#862)) ([ba4a565](ba4a565)) * bump eslint-plugin-react-refresh from 0.4.26 to 0.5.2 in /web ([#801](#801)) ([7574bb5](7574bb5)) * bump faker from 40.11.0 to 40.11.1 in the minor-and-patch group ([#803](#803)) ([14d322e](14d322e)) * bump https://github.com/astral-sh/ruff-pre-commit from v0.15.7 to 0.15.8 ([#864](#864)) ([f52901e](f52901e)) * bump nginxinc/nginx-unprivileged from `6582a34` to `f99cc61` in /docker/web in the all group ([#919](#919)) ([df85e4f](df85e4f)) * bump nginxinc/nginx-unprivileged from `ccbac1a` to `6582a34` in /docker/web ([#800](#800)) ([f4e9450](f4e9450)) * bump node from `44bcbf4` to `71be405` in /docker/sandbox ([#827](#827)) ([91bec67](91bec67)) * bump node from `5209bca` to `cf38e1f` in /docker/web ([#863](#863)) ([66d6043](66d6043)) * bump picomatch in /site ([#842](#842)) ([5f20bcc](5f20bcc)) * bump recharts 2->3 and @types/node 22->25 in /web ([#802](#802)) ([a908800](a908800)) * Bump requests from 2.32.5 to 2.33.0 ([#843](#843)) ([41daf69](41daf69)) * bump smol-toml from 1.6.0 to 1.6.1 in /site ([#826](#826)) ([3e5dbe4](3e5dbe4)) * bump the all group with 3 updates ([#921](#921)) ([7bace0b](7bace0b)) * bump the minor-and-patch group across 1 directory with 2 updates ([#829](#829)) ([93e611f](93e611f)) * bump the minor-and-patch group across 1 directory with 3 updates ([#841](#841)) ([7010c8e](7010c8e)) * bump the minor-and-patch group across 1 directory with 3 updates ([#869](#869)) ([548cee5](548cee5)) * bump the minor-and-patch group in /site with 2 updates ([#865](#865)) ([9558101](9558101)) * bump the minor-and-patch group with 2 updates ([#867](#867)) ([4830706](4830706)) * consolidate Dependabot groups to 1 PR per ecosystem ([06d2556](06d2556)) * consolidate Dependabot groups to 1 PR per ecosystem ([#881](#881)) ([06d2556](06d2556)) * improve worktree skill with full dep sync and status enhancements ([#906](#906)) ([772c625](772c625)) * remove Vue remnants and document framework decision ([#851](#851)) ([bf2adf6](bf2adf6)) * update web dependencies and fix brace-expansion CVE ([#880](#880)) ([a7a0ed6](a7a0ed6)) * upgrade to Storybook 10 and TypeScript 6 ([#845](#845)) ([52d95f2](52d95f2)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary
Test plan
npm --prefix web run type-check-- zero errorsnpm --prefix web run lint-- zero errorsnpm --prefix web run test-- 183/183 tests passnpm --prefix web run build-- production build succeedsuv run ruff check src/ tests/-- cleanuv run mypy src/ tests/-- clean (1196 files)uv run python -m pytest tests/ -n auto-- 10,537/10,537 passCloses #769
🤖 Generated with Claude Code