Skip to content

feat: build infrastructure layer (API client, auth, WebSocket)#815

Merged
Aureliolo merged 14 commits intomainfrom
feat/769-infrastructure-layer
Mar 24, 2026
Merged

feat: build infrastructure layer (API client, auth, WebSocket)#815
Aureliolo merged 14 commits intomainfrom
feat/769-infrastructure-layer

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

  • Port non-UI infrastructure from the Vue dashboard to React/Zustand equivalents
  • Axios API client with JWT interceptor, 401 redirect, RFC 9457 error handling, and ApiResponse envelope unwrapping
  • TypeScript types for all backend DTOs (1100+ lines) with readonly array fields, including new analytics trends/forecast, backup, coordination, and discovery-policy types
  • 18 typed endpoint modules by domain (14 ported + 4 new) with barrel export and explicit return types
  • Auth Zustand store: token lifecycle, login/setup flow, session expiry, localStorage persistence, HMR-safe timer cleanup
  • WebSocket Zustand store: first-message ticket auth (keeps ticket out of URLs/logs), exponential backoff reconnection (1s-30s, 20 max), channel subscriptions with handler deduplication, auto-re-subscribe, message size gating (byte-length via TextEncoder), runtime WsEvent type guard with array payload rejection
  • Backend: backward-compatible first-message WS auth protocol alongside legacy query-param auth, with auth message size limits
  • 11 empty domain store shells (one per domain, Zustand conventions)
  • 5 React hooks: useAuth (role-based canWrite, memoized), useWebSocket (component lifecycle subscriptions with .catch() on async setup), useOptimisticUpdate (with rollback), usePolling (setTimeout-based, run-generation guards, error state), useLoginLockout (ref-based, no stale closures)
  • 4 utility modules: error extraction (5xx leak prevention, suppressed message logging), date/currency formatters, sanitizeForLog (BIDI override stripping), app-wide constants with task transition graph
  • 183 tests across 19 files including property-based tests (fast-check) and 30 backend WS tests

Test plan

  • npm --prefix web run type-check -- zero errors
  • npm --prefix web run lint -- zero errors
  • npm --prefix web run test -- 183/183 tests pass
  • npm --prefix web run build -- production build succeeds
  • uv run ruff check src/ tests/ -- clean
  • uv run mypy src/ tests/ -- clean (1196 files)
  • uv run python -m pytest tests/ -n auto -- 10,537/10,537 pass
  • Pre-reviewed by 9 agents (code-reviewer, frontend-reviewer, api-contract-drift, silent-failure-hunter, conventions-enforcer, docs-consistency, security-reviewer, test-quality-reviewer, issue-resolution-verifier), 15 findings addressed

Closes #769

🤖 Generated with Claude Code

Aureliolo and others added 11 commits March 24, 2026 16:27
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>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 24, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 12a63e6.
Ensure 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 Files

None

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6a5539df-1ac6-4438-9bdc-fc7ed1610f87

📥 Commits

Reviewing files that changed from the base of the PR and between a52adbe and 12a63e6.

📒 Files selected for processing (1)
  • web/src/stores/websocket.ts
📜 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)
  • GitHub Check: Build Web
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Backend
  • GitHub Check: Test (Python 3.14)
  • 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-query for server state, @xyflow/react for workflow visualization
NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT) in web dashboard code -- use generic names like example-provider or test-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-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).
🪛 GitHub Check: CodeQL
web/src/stores/websocket.ts

[warning] 217-217: Log injection
Log entry depends on a user-provided value.

🔇 Additional comments (10)
web/src/stores/websocket.ts (10)

215-219: Log injection mitigated via sanitization.

The static analyzer flags msg.error as user-controlled data reaching console.error. The code already applies sanitizeForLog(msg.error, 200) which should strip BIDI overrides and truncate the value, mitigating common log injection vectors.

Verify that sanitizeForLog provides sufficient protection:

#!/bin/bash
# Description: Check sanitizeForLog implementation for log injection mitigation

# Find and display the sanitizeForLog implementation
ast-grep --pattern 'function sanitizeForLog($$$) {
  $$$
}'

# Also check if it's an exported const arrow function
rg -n -A 20 'export.*sanitizeForLog' web/src/utils/logging.ts

1-26: LGTM!

Imports are well-organized and subscriptionKey correctly produces a stable deduplication key by sorting both channels and filter keys.


28-40: LGTM!

Module-scoped internals are correctly structured outside the store for non-renderable state. The HMR-safe pattern is properly implemented with disconnect() resetting all these variables.


43-54: LGTM!

Store interface is well-typed with appropriate use of readonly for the subscribedChannels array.


56-104: LGTM!

Helper functions are well-implemented. VALID_WS_CHANNELS is now correctly derived from the exported WS_CHANNELS constant, ensuring runtime validation stays in sync with the type. The dispatchEvent function properly isolates handler errors with individual try/catch blocks.


108-128: LGTM!

Reconnection logic correctly implements capped exponential backoff. Using getState().connect() avoids stale closure issues in the timer callback.


130-185: LGTM!

The connection flow is well-implemented with proper generation guards to prevent stale connection attempts from affecting state. The brief connected: true flash before auth confirmation is documented (lines 171-174) and is an acceptable tradeoff for first-message auth security.


233-258: LGTM!

Close handling correctly distinguishes auth failures (4001/4003) from transient errors. The onerror logging includes only connection metadata without user-controlled values.


260-293: LGTM!

The connect() method now correctly checks for an existing connectPromise first (before socket state), ensuring callers awaiting concurrent connect() calls receive a consistent promise. The disconnect() method provides thorough cleanup with generation increment to invalidate in-flight connections.


295-351: LGTM!

Subscription management correctly deduplicates requests and safely handles the offline/online transition. The backward iteration pattern in unsubscribe() allows safe in-place removal. Handler registration follows a clean Map-of-Sets pattern.


Walkthrough

Updates add a React frontend infrastructure layer and extensive tests plus a backend WebSocket auth/control flow refactor. Frontend: new Axios API client (apiClient) with interceptors, a comprehensive web/src/api/types.ts, ~18 endpoint modules under web/src/api/endpoints, new Zustand stores (including auth and full websocket stores), React hooks (useAuth, useLoginLockout, useOptimisticUpdate, usePolling, useWebSocket), and utilities (constants, errors, format, logging). Many unit and property tests were added across hooks, stores, utils, and the API client. Backend: WebSocket handler now supports query-param and first-message auth paths, stricter validation, message-size gating, and improved logging; public APIs unchanged.

Suggested labels

autorelease: tagged

@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 24, 2026 16:17 — with GitHub Actions Inactive
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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

  • Infrastructure Porting: Ported non-UI infrastructure components from the Vue dashboard to React/Zustand equivalents, establishing a new foundation for the frontend.
  • API Client & Authentication: Implemented an Axios API client featuring a JWT interceptor, automatic 401 redirect handling, RFC 9457 error handling, and ApiResponse envelope unwrapping. The authentication Zustand store manages token lifecycle, login/setup flows, session expiry, and localStorage persistence.
  • WebSocket Enhancements: Developed a robust WebSocket Zustand store with first-message ticket authentication (keeping tickets out of URLs/logs), exponential backoff reconnection, channel subscriptions with handler deduplication, auto-re-subscribe, and message size gating.
  • Comprehensive TypeScript Types: Introduced over 1100 lines of TypeScript types for all backend DTOs, including new analytics trends/forecast, backup, coordination, and discovery-policy types, ensuring strong type safety across the API layer.
  • New React Hooks: Added five new React hooks: useAuth (role-based permissions), useWebSocket (component lifecycle subscriptions), useOptimisticUpdate (with rollback), usePolling (setTimeout-based), and useLoginLockout (ref-based).
  • Utility Modules: Created four new utility modules for error extraction (preventing 5xx leaks), date/currency formatters, log sanitization (stripping BIDI overrides), and app-wide constants including a task transition graph.
  • Extensive Testing: Included 183 tests across 19 files, featuring property-based tests (fast-check) and 30 backend WebSocket tests, ensuring high coverage and reliability for the new infrastructure.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 24, 2026

Codecov Report

❌ Patch coverage is 59.78261% with 37 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.26%. Comparing base (9b75c1d) to head (12a63e6).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/synthorg/api/controllers/ws.py 59.78% 31 Missing and 6 partials ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6b137f0 and 82b5fa3.

📒 Files selected for processing (63)
  • CLAUDE.md
  • src/synthorg/api/controllers/ws.py
  • tests/unit/api/controllers/test_ws.py
  • web/src/__tests__/api/client.property.test.ts
  • web/src/__tests__/api/client.test.ts
  • web/src/__tests__/hooks/useAuth.test.ts
  • web/src/__tests__/hooks/useLoginLockout.test.ts
  • web/src/__tests__/hooks/useOptimisticUpdate.test.ts
  • web/src/__tests__/hooks/usePolling.test.ts
  • web/src/__tests__/hooks/useWebSocket.test.ts
  • web/src/__tests__/stores/auth.test.ts
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/__tests__/utils/constants.property.test.ts
  • web/src/__tests__/utils/constants.test.ts
  • web/src/__tests__/utils/errors.property.test.ts
  • web/src/__tests__/utils/errors.test.ts
  • web/src/__tests__/utils/format.property.test.ts
  • web/src/__tests__/utils/format.test.ts
  • web/src/__tests__/utils/logging.property.test.ts
  • web/src/__tests__/utils/logging.test.ts
  • web/src/api/client.ts
  • web/src/api/endpoints/agents.ts
  • web/src/api/endpoints/analytics.ts
  • web/src/api/endpoints/approvals.ts
  • web/src/api/endpoints/artifacts.ts
  • web/src/api/endpoints/auth.ts
  • web/src/api/endpoints/backup.ts
  • web/src/api/endpoints/budget.ts
  • web/src/api/endpoints/collaboration.ts
  • web/src/api/endpoints/company.ts
  • web/src/api/endpoints/coordination.ts
  • web/src/api/endpoints/health.ts
  • web/src/api/endpoints/index.ts
  • web/src/api/endpoints/meetings.ts
  • web/src/api/endpoints/messages.ts
  • web/src/api/endpoints/projects.ts
  • web/src/api/endpoints/providers.ts
  • web/src/api/endpoints/settings.ts
  • web/src/api/endpoints/setup.ts
  • web/src/api/endpoints/tasks.ts
  • web/src/api/types.ts
  • web/src/hooks/useAuth.ts
  • web/src/hooks/useLoginLockout.ts
  • web/src/hooks/useOptimisticUpdate.ts
  • web/src/hooks/usePolling.ts
  • web/src/hooks/useWebSocket.ts
  • web/src/stores/agents.ts
  • web/src/stores/analytics.ts
  • web/src/stores/approvals.ts
  • web/src/stores/auth.ts
  • web/src/stores/budget.ts
  • web/src/stores/company.ts
  • web/src/stores/meetings.ts
  • web/src/stores/messages.ts
  • web/src/stores/providers.ts
  • web/src/stores/settings.ts
  • web/src/stores/setup.ts
  • web/src/stores/tasks.ts
  • web/src/stores/websocket.ts
  • web/src/utils/constants.ts
  • web/src/utils/errors.ts
  • web/src/utils/format.ts
  • web/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-query for server state, @xyflow/react for workflow visualization
NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT) in web dashboard code -- use generic names like example-provider or test-provider

Files:

  • web/src/__tests__/utils/errors.property.test.ts
  • web/src/api/endpoints/health.ts
  • web/src/stores/analytics.ts
  • web/src/__tests__/utils/constants.property.test.ts
  • web/src/stores/setup.ts
  • web/src/api/endpoints/projects.ts
  • web/src/__tests__/utils/format.property.test.ts
  • web/src/stores/budget.ts
  • web/src/api/endpoints/artifacts.ts
  • web/src/stores/providers.ts
  • web/src/stores/agents.ts
  • web/src/stores/meetings.ts
  • web/src/stores/messages.ts
  • web/src/stores/settings.ts
  • web/src/stores/company.ts
  • web/src/stores/tasks.ts
  • web/src/__tests__/utils/logging.property.test.ts
  • web/src/api/endpoints/coordination.ts
  • web/src/stores/approvals.ts
  • web/src/hooks/useAuth.ts
  • web/src/api/endpoints/messages.ts
  • web/src/__tests__/utils/logging.test.ts
  • web/src/utils/logging.ts
  • web/src/__tests__/api/client.property.test.ts
  • web/src/api/endpoints/company.ts
  • web/src/api/endpoints/meetings.ts
  • web/src/__tests__/hooks/useLoginLockout.test.ts
  • web/src/api/endpoints/budget.ts
  • web/src/api/endpoints/auth.ts
  • web/src/hooks/useLoginLockout.ts
  • web/src/__tests__/hooks/useOptimisticUpdate.test.ts
  • web/src/__tests__/hooks/useAuth.test.ts
  • web/src/api/endpoints/analytics.ts
  • web/src/__tests__/utils/constants.test.ts
  • web/src/api/endpoints/backup.ts
  • web/src/hooks/usePolling.ts
  • web/src/__tests__/utils/errors.test.ts
  • web/src/api/endpoints/agents.ts
  • web/src/__tests__/stores/auth.test.ts
  • web/src/utils/errors.ts
  • web/src/api/endpoints/approvals.ts
  • web/src/api/endpoints/index.ts
  • web/src/utils/format.ts
  • web/src/hooks/useOptimisticUpdate.ts
  • web/src/api/endpoints/settings.ts
  • web/src/__tests__/utils/format.test.ts
  • web/src/__tests__/api/client.test.ts
  • web/src/api/endpoints/tasks.ts
  • web/src/api/endpoints/collaboration.ts
  • web/src/hooks/useWebSocket.ts
  • web/src/api/endpoints/providers.ts
  • web/src/stores/websocket.ts
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/utils/constants.ts
  • web/src/api/client.ts
  • web/src/api/endpoints/setup.ts
  • web/src/stores/auth.ts
  • web/src/__tests__/hooks/usePolling.test.ts
  • web/src/api/types.ts
  • web/src/__tests__/hooks/useWebSocket.test.ts
web/src/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Vitest for unit tests with --detect-async-leaks flag; use fast-check for property-based testing with fc.assert and fc.property

Files:

  • web/src/__tests__/utils/errors.property.test.ts
  • web/src/__tests__/utils/constants.property.test.ts
  • web/src/__tests__/utils/format.property.test.ts
  • web/src/__tests__/utils/logging.property.test.ts
  • web/src/__tests__/utils/logging.test.ts
  • web/src/__tests__/api/client.property.test.ts
  • web/src/__tests__/hooks/useLoginLockout.test.ts
  • web/src/__tests__/hooks/useOptimisticUpdate.test.ts
  • web/src/__tests__/hooks/useAuth.test.ts
  • web/src/__tests__/utils/constants.test.ts
  • web/src/__tests__/utils/errors.test.ts
  • web/src/__tests__/stores/auth.test.ts
  • web/src/__tests__/utils/format.test.ts
  • web/src/__tests__/api/client.test.ts
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/__tests__/hooks/usePolling.test.ts
  • web/src/__tests__/hooks/useWebSocket.test.ts
**/*.{py,pyi}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{py,pyi}: Do NOT use from __future__ import annotations -- Python 3.14+ supports PEP 649 native lazy annotations
Use except 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, use copy.deepcopy() at construction and MappingProxyType wrapping for read-only enforcement
For frozen Pydantic models with dict/list fields, use copy.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 with model_copy(update=...) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model
Use @computed_field for derived values instead of storing redundant fields; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual validators
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (multiple tool invocations, parallel agent calls) over bare create_task
Never implement retry logic in driver subclasses or calling code -- all provider calls go through BaseCompletionProvider which 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.py
  • src/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.py
  • src/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/small as aliases
For property-based testing, use Hypothesis with @given decorator and @settings; run with HYPOTHESIS_PROFILE=dev uv run pytest tests/ -m unit -n auto -k properties for 1000 examples
ALWAYS run pytest with -n auto for parallelism via pytest-xdist; never run tests sequentially
For timing-sensitive tests, mock time.monotonic() and asyncio.sleep() to make them deterministic; for tasks that must block indefinitely, use asyncio.Event().wait() instead of large asyncio.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_logger then logger = get_logger(__name__) -- never use import logging or print()
Always use event name constants from synthorg.observability.events.<domain> modules (e.g. API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool) instead of string literals
Always use structured logging with kwargs: logger.info(EVENT, key=value) -- never use format strings like logger.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.ts
  • web/src/__tests__/utils/constants.property.test.ts
  • web/src/__tests__/utils/format.property.test.ts
  • web/src/__tests__/utils/logging.property.test.ts
  • web/src/__tests__/utils/logging.test.ts
  • web/src/__tests__/api/client.property.test.ts
  • CLAUDE.md
  • web/src/__tests__/hooks/useLoginLockout.test.ts
  • web/src/__tests__/hooks/useAuth.test.ts
  • web/src/__tests__/utils/constants.test.ts
  • web/src/__tests__/utils/errors.test.ts
  • web/src/__tests__/stores/auth.test.ts
  • web/src/__tests__/utils/format.test.ts
  • web/src/__tests__/api/client.test.ts
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/__tests__/hooks/usePolling.test.ts
  • web/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.ts
  • web/src/__tests__/utils/constants.property.test.ts
  • web/src/__tests__/utils/format.property.test.ts
  • web/src/__tests__/utils/logging.property.test.ts
  • web/src/__tests__/utils/logging.test.ts
  • web/src/__tests__/api/client.property.test.ts
  • web/src/__tests__/hooks/useLoginLockout.test.ts
  • web/src/__tests__/hooks/useOptimisticUpdate.test.ts
  • web/src/__tests__/hooks/useAuth.test.ts
  • web/src/__tests__/utils/constants.test.ts
  • web/src/__tests__/utils/errors.test.ts
  • web/src/__tests__/stores/auth.test.ts
  • web/src/__tests__/utils/format.test.ts
  • web/src/__tests__/api/client.test.ts
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/__tests__/hooks/usePolling.test.ts
  • web/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.ts
  • web/src/stores/approvals.ts
  • CLAUDE.md
  • web/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.md
  • web/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.md
  • web/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.py
  • src/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.ts
  • 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/endpoints/settings.ts
  • web/src/utils/constants.ts
  • 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/endpoints/settings.ts
  • web/src/utils/constants.ts
  • web/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.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Warning

Gemini is experiencing higher than usual traffic and was unable to create the review. Please try again in a few hours by commenting /gemini review.

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>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 82b5fa3 and a88e341.

📒 Files selected for processing (15)
  • .github/workflows/dependency-review.yml
  • CLAUDE.md
  • src/synthorg/api/controllers/ws.py
  • tests/unit/api/controllers/test_ws.py
  • web/src/__tests__/api/client.test.ts
  • web/src/__tests__/hooks/useAuth.test.ts
  • web/src/__tests__/utils/logging.property.test.ts
  • web/src/api/endpoints/analytics.ts
  • web/src/api/types.ts
  • web/src/hooks/useOptimisticUpdate.ts
  • web/src/hooks/usePolling.ts
  • web/src/stores/websocket.ts
  • web/src/utils/errors.ts
  • web/src/utils/format.ts
  • web/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 use from __future__ import annotations -- Python 3.14+ supports PEP 649 native lazy annotations
Use except 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, use copy.deepcopy() at construction and MappingProxyType wrapping for read-only enforcement
For frozen Pydantic models with dict/list fields, use copy.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 with model_copy(update=...) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model
Use @computed_field for derived values instead of storing redundant fields; use NotBlankStr from core.types for all identifier/name fields (including optional and tuple variants) instead of manual validators
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new code (multiple tool invocations, parallel agent calls) over bare create_task
Never implement retry logic in driver subclasses or calling code -- all provider calls go through BaseCompletionProvider which 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.py
  • src/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.py
  • src/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/small as aliases
For property-based testing, use Hypothesis with @given decorator and @settings; run with HYPOTHESIS_PROFILE=dev uv run pytest tests/ -m unit -n auto -k properties for 1000 examples
ALWAYS run pytest with -n auto for parallelism via pytest-xdist; never run tests sequentially
For timing-sensitive tests, mock time.monotonic() and asyncio.sleep() to make them deterministic; for tasks that must block indefinitely, use asyncio.Event().wait() instead of large asyncio.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-query for server state, @xyflow/react for workflow visualization
NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT) in web dashboard code -- use generic names like example-provider or test-provider

Files:

  • web/src/__tests__/utils/logging.property.test.ts
  • web/src/hooks/usePolling.ts
  • web/src/api/endpoints/analytics.ts
  • web/src/__tests__/hooks/useAuth.test.ts
  • web/src/utils/logging.ts
  • web/src/hooks/useOptimisticUpdate.ts
  • web/src/utils/format.ts
  • web/src/utils/errors.ts
  • web/src/__tests__/api/client.test.ts
  • web/src/stores/websocket.ts
  • web/src/api/types.ts
web/src/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Vitest for unit tests with --detect-async-leaks flag; use fast-check for property-based testing with fc.assert and fc.property

Files:

  • web/src/__tests__/utils/logging.property.test.ts
  • web/src/__tests__/hooks/useAuth.test.ts
  • web/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_logger then logger = get_logger(__name__) -- never use import logging or print()
Always use event name constants from synthorg.observability.events.<domain> modules (e.g. API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool) instead of string literals
Always use structured logging with kwargs: logger.info(EVENT, key=value) -- never use format strings like logger.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.ts
  • web/src/__tests__/hooks/useAuth.test.ts
  • CLAUDE.md
  • web/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.ts
  • web/src/__tests__/hooks/useAuth.test.ts
  • web/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_ROLES constant is correctly imported from the shared constants module, addressing the earlier review feedback about preventing test drift.


14-28: LGTM!

The resetStore helper 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 canWrite test suite correctly uses the shared WRITE_ROLES constant and derives readOnlyRoles dynamically. The allRoles array is typed as readonly 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 mustChangePassword behavior, including the fallback path when user is null but _mustChangePasswordFallback is 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_detail can be None even on failure, and PaginatedResponse always has pagination and data (defaulting to empty tuple). The current types advertise guarantees the backend doesn't make.

See past review for suggested fix:

  • ApiResponse error branch: error_detail: ErrorDetail | null
  • PaginatedResponse error branch: data: T[]; pagination: PaginationMeta

997-1004: agent_count is not in the backend response.

src/synthorg/api/controllers/setup_models.py doesn't define agent_count on SetupCompanyResponse. This field will deserialize as undefined. Derive it from agents.length instead.

 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, and DepartmentName correctly mirror the backend StrEnum values from src/synthorg/core/enums.py and src/synthorg/api/guards.py.


754-819: LGTM! WebSocket types are well-defined.

The WS_CHANNELS const array, WsChannel type, WsEventType union, and message interfaces provide type-safe WebSocket communication. The as const assertion 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 maxLen values with sensible defaults
  • Error objects (extracting stack/message)
  • Unstringifiable values with a safe fallback

The use of codePointAt(0) with for...of iteration 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 robustness

Good use of fc.assert and fc.property per 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)
  • formatUptime correctly handles non-finite/negative inputs
  • Edge cases like zero uptime (0m) and future dates are handled appropriately
web/src/__tests__/api/client.test.ts (2)

176-182: Fragile but acceptable test approach for interceptor verification.

Accessing Axios's internal handlers array 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:

  • ApiRequestError construction 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_auth helper 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_ws function properly:

  • Uses is not None check (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_accepted flag 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_filters function logs a warning with context (channel, payload type) when encountering non-dict payloads, then returns false to 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:

  1. Invalid query-param ticket (pre-accept rejection)
  2. Invalid first-message ticket (post-accept rejection)
  3. Non-auth first message (wrong action type)

Each test correctly verifies the _WS_CLOSE_AUTH_FAILED close 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, but sanitizeForLog(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_CHANNELS from WS_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 horizonDayshorizon_days snake_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 ErrorDetail extraction 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
  • finally block 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>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
web/src/stores/websocket.ts (1)

70-80: 🧹 Nitpick | 🔵 Trivial

Consider using a type predicate to avoid double casting.

isWsEvent returns boolean, requiring an awkward msg as unknown as WsEvent cast 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

📥 Commits

Reviewing files that changed from the base of the PR and between a88e341 and a52adbe.

📒 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-query for server state, @xyflow/react for workflow visualization
NEVER use real vendor names (Anthropic, OpenAI, Claude, GPT) in web dashboard code -- use generic names like example-provider or test-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.error is user-controlled. The use of sanitizeForLog(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 subscriptionKey ensures stable deduplication regardless of channel order.


315-337: LGTM!

The backward iteration with splice correctly handles index shifting. Channels are removed from both activeSubscriptions and pendingSubscriptions before attempting the send, ensuring consistent state even if the send fails.

Comment on lines +158 to +181
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))
}
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 24, 2026 19:24 — with GitHub Actions Inactive
@Aureliolo Aureliolo merged commit 9f01d3e into main Mar 24, 2026
32 checks passed
@Aureliolo Aureliolo deleted the feat/769-infrastructure-layer branch March 24, 2026 19:29
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 24, 2026 19:29 — with GitHub Actions Inactive
Aureliolo added a commit that referenced this pull request Mar 30, 2026
🤖 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-&gt;3 and @types/node 22-&gt;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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: build infrastructure layer (API client, auth, WebSocket)

2 participants