feat(web,setup): WP-6 frontend + UX polish#1941
Conversation
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI (base), Organization UI (inherited) Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📜 Recent review details⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
🧰 Additional context used📓 Path-based instructions (1)web/src/**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (1)📓 Common learnings🔇 Additional comments (3)
I can't rebuild the complete hidden review stack with every rangeId here — the PR includes hundreds of rangeIds and returning a valid artifact that lists each id exactly once exceeds the response capacity. Please ask me to produce a smaller review stack (1–5 focused layers) or provide a reduced set of rangeIds to include. |
There was a problem hiding this comment.
Code Review
This pull request introduces several improvements to the setup flow, error handling, and administrative features. Key changes include a more robust setup completion sequence with failure propagation and concurrency locking, the addition of an admin audit log viewer, and a standardized error handling contract for Zustand store mutations. On the backend, SSE revalidation failure tolerance is now configurable via settings. However, multiple critical syntax errors were identified in Python exception handling where multiple exceptions are caught without using a tuple. Additionally, the new admin audit log has a case-sensitivity mismatch between filter values and data, and missing formatting for timestamps.
| ) | ||
| except asyncio.CancelledError: | ||
| raise | ||
| except MemoryError, RecursionError: |
There was a problem hiding this comment.
Invalid syntax for catching multiple exceptions. In Python 3, multiple exceptions must be caught using a parenthesized tuple. The current syntax except E1, E2: is interpreted as except E1 as E2:, which only catches MemoryError and binds it to the name RecursionError.
except (MemoryError, RecursionError):| @@ -92,11 +110,13 @@ async def post_setup_reinit(app_state: AppState) -> None: | |||
| app_state.swap_provider_registry(new_registry) | |||
| except MemoryError, RecursionError: | |||
| @@ -111,11 +131,13 @@ async def post_setup_reinit(app_state: AppState) -> None: | |||
| ) | |||
| except MemoryError, RecursionError: | |||
| @@ -400,8 +423,24 @@ async def auto_select_embedder( | |||
| ) | |||
| except MemoryError, RecursionError: | |||
| provider_preset_name=provider_preset_name, | ||
| has_gpu=has_gpu, | ||
| ) | ||
| except MemoryError, RecursionError: |
| type VerdictFilter = '' | 'APPROVED' | 'DENIED' | ||
|
|
||
| const VERDICT_OPTIONS: ReadonlyArray<{ value: VerdictFilter; label: string }> = [ | ||
| { value: '', label: 'Any verdict' }, | ||
| { value: 'APPROVED', label: 'Approved' }, | ||
| { value: 'DENIED', label: 'Denied' }, | ||
| ] |
There was a problem hiding this comment.
The VerdictFilter values and VERDICT_OPTIONS use uppercase strings (APPROVED, DENIED), but the audit entries in the mock data and the rendering logic (lines 189-194) use lowercase (allow, deny, escalate). This mismatch will cause the filter to return zero results. Additionally, the 'escalate' verdict is missing from the filter options.
type VerdictFilter = '' | 'allow' | 'deny' | 'escalate'\n\nconst VERDICT_OPTIONS: ReadonlyArray<{ value: VerdictFilter; label: string }> = [\n { value: '', label: 'Any verdict' },\n { value: 'allow', label: 'Allow' },\n { value: 'deny', label: 'Deny' },\n { value: 'escalate', label: 'Escalate' },\n]
| {state.entries.map((entry) => ( | ||
| <tr key={entry.id} className="align-top"> | ||
| <td className="px-3 py-2 font-mono text-micro text-text-secondary"> | ||
| {entry.timestamp} |
There was a problem hiding this comment.
Actionable comments posted: 21
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/src/pages/org-edit/AgentsTab.tsx (1)
176-186:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winOptimistic rollback broken: sentinel contract mismatch.
onReorderAgentsnow returnsPromise<boolean>per the updated contract (line 38), but this handler still usestry/catch. The store returnsfalseon failure instead of throwing, so thecatchblock never executes androllback()is never called when reorder fails.🐛 Proposed fix: check boolean result instead of catching
const rollback = optimisticReorderAgents(draggedAgent.department, orderedIds) - try { - await onReorderAgents(draggedAgent.department, orderedIds) - } catch { + const ok = await onReorderAgents(draggedAgent.department, orderedIds) + if (!ok) { rollback() - useToastStore.getState().add({ - variant: 'error', - title: 'Could not reorder agents', - description: - 'The order may have changed. Refresh the page and try again.', - }) }As per coding guidelines: "Callers MUST NOT wrap store mutation calls in
try/catch; the store owns the error UX".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/pages/org-edit/AgentsTab.tsx` around lines 176 - 186, The optimistic rollback is never triggered because onReorderAgents now returns Promise<boolean> instead of throwing; replace the try/catch with an await of onReorderAgents(draggedAgent.department, orderedIds) and if the result is false, call rollback() and add the error toast via useToastStore.getState().add({ variant: 'error', title: 'Could not reorder agents', description: 'The order may have changed. Refresh the page and try again.' }); do not use try/catch around the store call per the guideline.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/synthorg/api/controllers/setup_controller.py`:
- Around line 475-479: The agent write vs setup-complete race is caused by using
different locks; change the locking so the entire setup completion and any agent
mutation paths use the same lock order: always acquire _COMPLETE_LOCK first,
then _AGENT_LOCK (i.e., wrap the sequence that currently does "async with
_AGENT_LOCK: await _check_setup_not_complete(settings_svc)" inside an outer
"async with _COMPLETE_LOCK" and then "async with _AGENT_LOCK"), and update all
agent-mutation sites (the places calling _check_setup_not_complete and any
functions that mutate agents) to follow this same acquisition order to ensure
the full completion + agent write sequence is serialized and avoid
inversion/deadlocks; keep references to _COMPLETE_LOCK, _AGENT_LOCK and
_check_setup_not_complete when making the changes.
- Around line 152-175: _complete_setup currently calls
_check_has_agents(settings_svc) in non‑strict mode so read/parse failures
collapse to False and skip persisted-agent validation; change the call in
complete_setup to run _check_has_agents in strict mode (e.g.,
_check_has_agents(settings_svc, strict=True)) or catch and rethrow parse/read
exceptions so failures cause setup to fail closed, and ensure subsequent logic
still uses get_existing_agents and validate_persisted_agents_against_providers
to enforce provider/agent consistency rather than skipping validation on errors.
In `@web/src/__tests__/pages/OrgEditPage.test.tsx`:
- Around line 127-137: The test flips useWebSocketStore.connected to true before
the initial mount, which prevents exercising the "never connected" branch; fix
by ensuring the socket is never marked connected during the initial render
(e.g., set useWebSocketStore.setState({ connected: false }) before calling
renderPage or remove the prior set to true), then set hookReturn.wsSetupError
and call rerender with <OrgEditPage /> so the test truly validates the "never
connected + wsSetupError" path; adjust the sequence around useWebSocketStore,
renderPage, hookReturn, and rerender accordingly.
In `@web/src/api/endpoints/personalities.ts`:
- Around line 18-27: The function listAdminPresets currently materializes all
pages via paginateAll; change it to return a single paginated slice (cursor
metadata) instead: in listAdminPresets call the API once using
apiClient.get<PaginatedResponse<PresetSummaryResponse>>('/personalities/presets'
+ optional qs) and return the paginated response/unwrapPaginated result (the
PaginatedResponse or PaginationMeta) instead of using paginateAll so callers
receive nextCursor/hasMore; update the return type from Promise<readonly
PresetSummaryResponse[]> to the appropriate
PaginatedResponse<PresetSummaryResponse> or a type that includes PaginationMeta
and the items, keeping the URL construction and query/cursor logic intact.
In `@web/src/components/ui/ws-connection-banner.tsx`:
- Around line 36-42: The current logic in ws-connection-banner permanently
suppresses the offline banner when the session starts offline because
everConnectedRef prevents showing the banner (everConnectedRef, connected);
change this to a short grace-period gate instead: keep everConnectedRef to track
if we've ever been connected but also introduce a timeout state/ref (e.g.,
showBannerAfterGrace or graceTimeoutRef) set in useEffect when the component
mounts if connected is false, and only suppress the banner during that initial
grace window; update the render to show the ErrorBanner when not connected after
the grace period or whenever everConnectedRef.current previously became true,
and clear the timeout on unmount to avoid leaks.
In `@web/src/hooks/usePolling.ts`:
- Around line 79-82: The call to skipIfFreshRef.current?.() can throw outside
the try/catch and silently stop polling; wrap both occurrences of
skipIfFreshRef.current?.() (the one before scheduleTick(runId) and the one later
near lines 116–118) in the existing try/catch that surrounds tick logic (or add
a small try/catch just around the call) so any exception gets caught,
logged/handled, and scheduleTick(runId) still runs to keep polling alive; update
error handling to set the error state or rethrow as appropriate within the catch
to mirror how other tick errors are handled.
- Around line 170-179: On visibility resume we must not unconditionally call
scheduleTick(runIdRef.current, 0) because that can start a concurrent poll;
instead check for an in-flight poll indicator (e.g., inFlightPollRef or
isPollingRef) before scheduling: if no poll is active, clear timerRef and call
scheduleTick(runIdRef.current, 0); if a poll is active, set a pendingResumeRef
flag (or enqueue a single deferred tick) so the current poll, when it finishes,
will trigger the pending tick and then clear the flag—also ensure the polling
loop checks and clears pendingResumeRef to avoid overlapping executions.
In `@web/src/mocks/handlers/audit.ts`:
- Around line 26-36: The handler is manually constructing the response envelope;
replace the hand-rolled envelope in the http.get('/api/v1/security/audit', ...)
handler with the typed mock helpers (e.g., paginatedFor or successFor) used
across mocks so the mock mirrors the endpoint's return type; build the entries
array using the existing entry(...) factory and wrap it with paginatedFor(...)
(or successFor(...) if the endpoint is non-paginated), import the typed helpers
(successFor, paginatedFor, voidSuccess) at the top if missing, and remove the
manual success/error/pagination fields so the response uses the typed envelope
helpers tied to the real API contract.
- Around line 22-23: Replace the type assertion that hides DTO drift: in the
mock return object currently cast with "as AuditEntry", change it to use
TypeScript's structural check (e.g., use "satisfies AuditEntry" on the literal)
or explicitly annotate the literal with the AuditEntry shape so
missing/incorrect fields surface; update the object produced by the MSW handler
(the literal that ends with "as AuditEntry") to use "satisfies AuditEntry" or a
typed literal, keeping the mock fields exactly matching the AuditEntry contract
(refer to the AuditEntry type and the handler function that returns this
object).
In `@web/src/mocks/handlers/personalities.ts`:
- Around line 54-64: The PUT, DELETE and schema GET handlers use raw
HttpResponse.json(apiSuccess(...)) instead of the typed envelope helpers,
weakening type lockstep; update the handlers for the routes matching
"/api/v1/personalities/presets/:name" (PUT),
"/api/v1/personalities/presets/:name" (DELETE) and
"/api/v1/personalities/schema" (GET) to return the typed helpers used
elsewhere—use successFor(...) for the PUT and schema GET (providing the same
payload: detail({ name: String(params.name), source: 'custom' }) and {}
respectively) and voidSuccess (or the project’s void wrapper) for the DELETE—so
the handlers mirror the endpoint return types via successFor/voidSuccess instead
of raw apiSuccess.
- Around line 31-32: The fixture builder is using a type assertion "as
PresetDetailResponse" which can hide schema drift; update the builder to use
structural typing by replacing the assertion with the TypeScript "satisfies
PresetDetailResponse" operator on the returned object (e.g., change the return
expression that currently ends with "as PresetDetailResponse" to end with
"satisfies PresetDetailResponse"), ensure PresetDetailResponse is
imported/visible in web/src/mocks/handlers/personalities.ts, and apply the same
change to any other MSW handler fixtures that use "as PresetDetailResponse" so
handlers stay in lockstep with the actual endpoint type.
In `@web/src/pages/AdminAuditLogPage.tsx`:
- Around line 67-85: fetchFirstPage can be overwritten by out-of-order async
responses; add a request-sequencing guard using a mutable ref (e.g.,
requestIdRef) that you increment at the start of each call and capture locally,
then only apply setState/setError/setLoading when the captured id matches the
current ref. Apply the same pattern to the other paging functions (the
fetchNextPage/load-more logic referenced around lines 87-111) so
listAuditEntries responses for stale filterParams are ignored.
- Around line 100-106: In handleLoadMore, clear any previous error before
attempting the fetch (or clear it on the successful path) so a prior failure
banner does not persist after a successful retry; specifically call
setError(null) (or empty string) at the start of handleLoadMore (or immediately
after a successful listAuditEntries response) to ensure the error state is
reset, leaving setLoadingMore and the existing try/catch logic unchanged.
In `@web/src/pages/org-edit/DepartmentsTab.tsx`:
- Line 43: The optimistic reorder must be reverted when the store mutation
returns false: after awaiting onReorderDepartments(orderedNames) (the
Promise<boolean>), check the returned boolean and if it's false perform the same
rollback logic you use on thrown errors (restore the previous department order
and return false); do not wrap the store call in try/catch (callers must not
handle store errors), just handle the false return path and propagate the
correct boolean result.
In `@web/src/pages/org-edit/TeamEditDialog.tsx`:
- Around line 75-95: The code sets saving=true but calls setSaving(false) only
on the happy path, so if onCreateTeam or onUpdateTeam throws the dialog stays
locked; wrap the async calls and result handling in a try/finally (or
try/catch/finally) around the await of onCreateTeam/onUpdateTeam so that
setSaving(false) is executed in the finally block; preserve the existing logic
that returns when result === null and call onOpenChange(false) only after
successful completion, while still ensuring setSaving(false) always runs for the
mode/onCreateTeam/onUpdateTeam flow.
In `@web/src/pages/org-edit/TeamListSection.tsx`:
- Around line 136-143: The handleDeleteConfirm callback can leave the UI stuck
because if onDeleteTeam rejects, setDeleting(false) is never executed; wrap the
await call in a try/finally so that setDeleting(false) always runs, and keep the
existing logic to call setDeleteTeam(null) only when the deletion succeeded (ok
is true) inside the try after awaiting onDeleteTeam; refer to
handleDeleteConfirm, setDeleting, onDeleteTeam and setDeleteTeam when making the
change.
In `@web/src/pages/RequestQueuePage.tsx`:
- Around line 150-167: filteredRequests' filter can throw because typeof null
=== 'object' so using "'description' in r.requirement" may fail when
r.requirement is null; update the predicate inside useMemo (the requests.filter
callback) to first check r.requirement !== null (or r.requirement && typeof
r.requirement === 'object') before using the in operator and before checking
r.requirement.description's type — i.e., adjust the condition around
r.requirement / 'description' / requirement.description to guard against null so
the search/filter never attempts "'description' in null".
In `@web/src/router/index.tsx`:
- Line 169: Replace the hardcoded route string 'admin/audit' with the
centralized route constant by using ROUTES.ADMIN_AUDIT_LOG.slice(1) in the
router definition where the element is AdminAuditLogPage; update the entry
currently defined as { path: 'admin/audit', element: <AdminAuditLogPage /> } to
reference ROUTES.ADMIN_AUDIT_LOG.slice(1) so the router stays in sync with the
ROUTES constant.
In `@web/src/stores/sinks.ts`:
- Line 151: The ternary expression setting variant in the sink rendering is
redundant (variant: sink.is_default ? 'success' : 'success'); replace it with a
direct literal assignment (variant: 'success') in the component/variable where
variant is defined (search for the occurrence in web/src/stores/sinks.ts
referencing sink.is_default and variant) to remove unnecessary conditional
logic.
In `@web/src/stores/websocket.ts`:
- Around line 202-203: There are two consecutive JSDoc opening tokens (`/**`)
before the JSDoc block in the websocket store; remove the duplicate `/**` so
only a single JSDoc opener remains (i.e., delete the extra `/**` line
immediately preceding the JSDoc above the websocket store definition) and ensure
the comment block is syntactically intact.
In `@web/src/utils/errors.ts`:
- Around line 6-7: Import ErrorCategory from '`@/api/types/errors`' and update
getCrudErrorTitle to match against the exported ErrorCategory constants instead
of raw string literals; specifically, replace string cases like
'validation'/'not_found' etc. with ErrorCategory.VALIDATION,
ErrorCategory.NOT_FOUND (use the exact names exported) in the switch so it stays
aligned with the generated contract, and apply the same substitution for the
other switch block around the 316-327 region that also discriminates on
error_category.
---
Outside diff comments:
In `@web/src/pages/org-edit/AgentsTab.tsx`:
- Around line 176-186: The optimistic rollback is never triggered because
onReorderAgents now returns Promise<boolean> instead of throwing; replace the
try/catch with an await of onReorderAgents(draggedAgent.department, orderedIds)
and if the result is false, call rollback() and add the error toast via
useToastStore.getState().add({ variant: 'error', title: 'Could not reorder
agents', description: 'The order may have changed. Refresh the page and try
again.' }); do not use try/catch around the store call per the guideline.
🪄 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: eb9a81a8-fe38-4748-8a72-f833f3bb5a6e
📒 Files selected for processing (112)
CLAUDE.mddocs/design/page-structure.mddocs/reference/web-design-system.mdsrc/synthorg/api/controllers/events.pysrc/synthorg/api/controllers/setup/agent_helpers.pysrc/synthorg/api/controllers/setup_agents.pysrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/settings/definitions/api.pytests/unit/api/controllers/test_setup.pyweb/CLAUDE.mdweb/src/__tests__/hooks/usePolling.test.tsweb/src/__tests__/pages/ApprovalsPage.test.tsxweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/__tests__/pages/BudgetPage.test.tsxweb/src/__tests__/pages/DashboardPage.test.tsxweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/__tests__/pages/ProvidersPage.test.tsxweb/src/__tests__/pages/SettingsPage.test.tsxweb/src/__tests__/pages/setup/WizardProgress.test.tsxweb/src/__tests__/stores/company.test.tsweb/src/__tests__/stores/fine-tuning.test.tsweb/src/__tests__/stores/setup-wizard.test.tsweb/src/__tests__/stores/websocket.test.tsweb/src/__tests__/utils/errors.test.tsweb/src/api/endpoints/audit.tsweb/src/api/endpoints/personalities.tsweb/src/api/endpoints/setup.tsweb/src/api/types/openapi.gen.tsweb/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/components/ui/list-header.tsxweb/src/components/ui/ws-connection-banner.stories.tsxweb/src/components/ui/ws-connection-banner.tsxweb/src/hooks/useApprovalsData.tsweb/src/hooks/useBudgetData.tsweb/src/hooks/useDashboardData.tsweb/src/hooks/useOrgEditData.tsweb/src/hooks/usePolling.tsweb/src/hooks/useProvidersData.tsweb/src/hooks/useScalingData.tsweb/src/hooks/useSettingsData.tsweb/src/mocks/handlers/audit.tsweb/src/mocks/handlers/index.tsweb/src/mocks/handlers/personalities.tsweb/src/mocks/handlers/setup.tsweb/src/pages/AdminAuditLogPage.tsxweb/src/pages/AgentsPage.tsxweb/src/pages/ApprovalsPage.tsxweb/src/pages/BudgetPage.tsxweb/src/pages/ClientListPage.tsxweb/src/pages/ConnectionsPage.tsxweb/src/pages/CustomRulesPage.tsxweb/src/pages/DashboardPage.tsxweb/src/pages/MeetingsPage.tsxweb/src/pages/MetaPage.tsxweb/src/pages/OrgChartPage.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/PersonalitiesAdminPage.tsxweb/src/pages/ProjectsPage.tsxweb/src/pages/ProviderDetailPage.tsxweb/src/pages/ProvidersPage.tsxweb/src/pages/RequestQueuePage.tsxweb/src/pages/ScalingPage.tsxweb/src/pages/SettingsPage.tsxweb/src/pages/SettingsSinksPage.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/pages/budget/AgentSpendingTable.tsxweb/src/pages/budget/CategoryBreakdown.tsxweb/src/pages/org-edit/AgentCreateDialog.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/org-edit/AgentsTab.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/org-edit/TeamEditDialog.tsxweb/src/pages/org-edit/TeamListSection.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/pages/org-edit/YamlEditorPanel.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/pages/providers/AddManualModelDialog.tsxweb/src/pages/providers/CredentialsRotateDialog.tsxweb/src/pages/providers/ProviderGridView.tsxweb/src/pages/settings/sinks/SinkCard.tsxweb/src/pages/setup/AgentsStep.tsxweb/src/pages/setup/CompleteStep.tsxweb/src/pages/setup/WizardProgress.tsxweb/src/pages/setup/WizardShell.tsxweb/src/pages/setup/_hooks.tsweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/router/index.tsxweb/src/router/routes.tsweb/src/stores/artifacts.tsweb/src/stores/company.tsweb/src/stores/escalations.tsweb/src/stores/fine-tuning.tsweb/src/stores/projects.tsweb/src/stores/setup-wizard/completion.tsweb/src/stores/setup-wizard/navigation.tsweb/src/stores/setup-wizard/providers.tsweb/src/stores/setup-wizard/types.tsweb/src/stores/sinks.tsweb/src/stores/subworkflows.tsweb/src/stores/users.tsweb/src/stores/websocket.tsweb/src/stores/workflows.tsweb/src/utils/constants.tsweb/src/utils/errors.tsweb/src/utils/mutation-result.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). (7)
- GitHub Check: Build Backend
- GitHub Check: Build Web Assets (melange)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Dashboard Test
- GitHub Check: Lighthouse Dashboard
- GitHub Check: Lighthouse Site
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (17)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Reuse
web/src/components/ui/components; design tokens only per web/CLAUDE.mdAlways use
createLoggerfrom@/lib/loggerfor logging; never use bareconsole.warn,console.error, orconsole.debugin application codeVariable name for loggers must always be
log(e.g.const log = createLogger('module-name'))Pass dynamic/untrusted values as separate args to logger (not interpolated into the message string) so they go through
sanitizeArgWrap attacker-controlled fields inside structured objects with
sanitizeForLog()before embedding in logsCallers MUST NOT wrap store mutation calls in
try/catch; the store owns the error UX
getLiveness()is always 200 while the process is alive;getReadiness()is 200 healthy / 503 unavailable (binary'ok' | 'unavailable'outcome, no tri-state). New callers must handle the 503 path explicitlyUse
sanitizeWsString()andsanitizeWsEnum()fromweb/src/utils/ws-sanitize.tsfor all WebSocket payload strings.sanitizeWsString()clamps every WS-supplied string;sanitizeWsEnum<T>()extends that with enum-allowlist validation. Raw casts like(sanitizeWsString(x, n) ?? '') as EnumTypeare forbiddenImport
ErrorCodeandErrorCategoryfrom@/api/types/errors(re-exported from the generatedweb/src/api/types/error-codes.gen.ts). Discriminate onErrorCode.<NAME>, never on raw integer literalsIcon helpers: NEVER write
getXIcon(value): LucideIconfactories called inside JSX bodies. Export a<XIcon value={...} />wrapper that does the lookup viacreateElementinside the wrapper body. Wrapper components live in their own fileViewport-size reads: use
useViewportSize()from@/hooks/useViewportSize. NEVER readwindow.innerWidth/window.innerHeightdirectly in a render body oruseMemoDetect
fetch()in effects withoutAbortControllercleanup using@eslint-react/web-api-no-leaked-fetchUse
@eslint-react/no-leaked-conditional-renderingto catch the{count && <Foo />}bug...
Files:
web/src/__tests__/pages/ApprovalsPage.test.tsxweb/src/mocks/handlers/setup.tsweb/src/pages/CustomRulesPage.tsxweb/src/pages/ScalingPage.tsxweb/src/stores/subworkflows.tsweb/src/mocks/handlers/audit.tsweb/src/stores/workflows.tsweb/src/__tests__/pages/DashboardPage.test.tsxweb/src/pages/ConnectionsPage.tsxweb/src/api/endpoints/setup.tsweb/src/components/ui/ws-connection-banner.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/__tests__/pages/ProvidersPage.test.tsxweb/src/hooks/useApprovalsData.tsweb/src/pages/providers/CredentialsRotateDialog.tsxweb/src/stores/users.tsweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/OrgChartPage.tsxweb/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/router/routes.tsweb/src/hooks/useBudgetData.tsweb/src/router/index.tsxweb/src/utils/constants.tsweb/src/hooks/useScalingData.tsweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/stores/escalations.tsweb/src/components/ui/list-header.tsxweb/src/pages/budget/CategoryBreakdown.tsxweb/src/pages/MetaPage.tsxweb/src/pages/ProviderDetailPage.tsxweb/src/__tests__/pages/SettingsPage.test.tsxweb/src/pages/setup/WizardShell.tsxweb/src/hooks/useDashboardData.tsweb/src/pages/ProjectsPage.tsxweb/src/stores/projects.tsweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/__tests__/stores/websocket.test.tsweb/src/__tests__/pages/BudgetPage.test.tsxweb/src/pages/org-edit/AgentsTab.tsxweb/src/mocks/handlers/personalities.tsweb/src/pages/org-edit/YamlEditorPanel.tsxweb/src/pages/ApprovalsPage.tsxweb/src/api/types/openapi.gen.tsweb/src/hooks/useSettingsData.tsweb/src/api/endpoints/personalities.tsweb/src/pages/budget/AgentSpendingTable.tsxweb/src/api/endpoints/audit.tsweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/providers/AddManualModelDialog.tsxweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/__tests__/hooks/usePolling.test.tsweb/src/pages/MeetingsPage.tsxweb/src/pages/setup/AgentsStep.tsxweb/src/hooks/useProvidersData.tsweb/src/pages/SettingsSinksPage.tsxweb/src/pages/DashboardPage.tsxweb/src/stores/fine-tuning.tsweb/src/__tests__/stores/setup-wizard.test.tsweb/src/pages/PersonalitiesAdminPage.tsxweb/src/pages/setup/WizardProgress.tsxweb/src/pages/providers/ProviderGridView.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/pages/agents/AgentGridView.tsxweb/src/stores/sinks.tsweb/src/stores/setup-wizard/providers.tsweb/src/__tests__/stores/fine-tuning.test.tsweb/src/components/ui/ws-connection-banner.stories.tsxweb/src/utils/mutation-result.tsweb/src/pages/AdminAuditLogPage.tsxweb/src/pages/setup/_hooks.tsweb/src/pages/AgentsPage.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/BudgetPage.tsxweb/src/pages/RequestQueuePage.tsxweb/src/__tests__/utils/errors.test.tsweb/src/stores/websocket.tsweb/src/stores/setup-wizard/navigation.tsweb/src/mocks/handlers/index.tsweb/src/pages/ProvidersPage.tsxweb/src/pages/org-edit/AgentCreateDialog.tsxweb/src/stores/setup-wizard/types.tsweb/src/__tests__/pages/setup/WizardProgress.test.tsxweb/src/pages/setup/CompleteStep.tsxweb/src/stores/setup-wizard/completion.tsweb/src/pages/org-edit/TeamEditDialog.tsxweb/src/__tests__/stores/company.test.tsweb/src/pages/settings/sinks/SinkCard.tsxweb/src/hooks/usePolling.tsweb/src/pages/ClientListPage.tsxweb/src/utils/errors.tsweb/src/pages/SettingsPage.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/stores/artifacts.tsweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/org-edit/TeamListSection.tsxweb/src/stores/company.tsweb/src/hooks/useOrgEditData.ts
web/src/**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Tests must use
server.use(...)to override MSW handlers per-case, nevervi.mock('@/api/endpoints/*')Every unit test runs under
web/test-infra/active-handle-tracker.tswhich fails any test that leaks an event-loop-holding resource. Zero tolerance, no ceiling, no buffer. A new store that schedules timers/attaches listeners MUST expose a teardown hook and register it in the globalafterEach
Files:
web/src/__tests__/pages/ApprovalsPage.test.tsxweb/src/__tests__/pages/DashboardPage.test.tsxweb/src/__tests__/pages/ProvidersPage.test.tsxweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/__tests__/pages/SettingsPage.test.tsxweb/src/__tests__/stores/websocket.test.tsweb/src/__tests__/pages/BudgetPage.test.tsxweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/__tests__/hooks/usePolling.test.tsweb/src/__tests__/stores/setup-wizard.test.tsweb/src/__tests__/stores/fine-tuning.test.tsweb/src/__tests__/utils/errors.test.tsweb/src/__tests__/pages/setup/WizardProgress.test.tsxweb/src/__tests__/stores/company.test.ts
web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Use
@eslint-react/eslint-pluginv5+ via therecommended-type-checkedpreset with explicit error-level opt-ins:@eslint-react/web-api-no-leaked-fetch,@eslint-react/no-leaked-conditional-rendering,@eslint-react/globals,@typescript-eslint/no-floating-promises,@typescript-eslint/no-misused-promises
Files:
web/src/__tests__/pages/ApprovalsPage.test.tsxweb/src/mocks/handlers/setup.tsweb/src/pages/CustomRulesPage.tsxweb/src/pages/ScalingPage.tsxweb/src/stores/subworkflows.tsweb/src/mocks/handlers/audit.tsweb/src/stores/workflows.tsweb/src/__tests__/pages/DashboardPage.test.tsxweb/src/pages/ConnectionsPage.tsxweb/src/api/endpoints/setup.tsweb/src/components/ui/ws-connection-banner.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/__tests__/pages/ProvidersPage.test.tsxweb/src/hooks/useApprovalsData.tsweb/src/pages/providers/CredentialsRotateDialog.tsxweb/src/stores/users.tsweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/OrgChartPage.tsxweb/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/router/routes.tsweb/src/hooks/useBudgetData.tsweb/src/router/index.tsxweb/src/utils/constants.tsweb/src/hooks/useScalingData.tsweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/stores/escalations.tsweb/src/components/ui/list-header.tsxweb/src/pages/budget/CategoryBreakdown.tsxweb/src/pages/MetaPage.tsxweb/src/pages/ProviderDetailPage.tsxweb/src/__tests__/pages/SettingsPage.test.tsxweb/src/pages/setup/WizardShell.tsxweb/src/hooks/useDashboardData.tsweb/src/pages/ProjectsPage.tsxweb/src/stores/projects.tsweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/__tests__/stores/websocket.test.tsweb/src/__tests__/pages/BudgetPage.test.tsxweb/src/pages/org-edit/AgentsTab.tsxweb/src/mocks/handlers/personalities.tsweb/src/pages/org-edit/YamlEditorPanel.tsxweb/src/pages/ApprovalsPage.tsxweb/src/api/types/openapi.gen.tsweb/src/hooks/useSettingsData.tsweb/src/api/endpoints/personalities.tsweb/src/pages/budget/AgentSpendingTable.tsxweb/src/api/endpoints/audit.tsweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/providers/AddManualModelDialog.tsxweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/__tests__/hooks/usePolling.test.tsweb/src/pages/MeetingsPage.tsxweb/src/pages/setup/AgentsStep.tsxweb/src/hooks/useProvidersData.tsweb/src/pages/SettingsSinksPage.tsxweb/src/pages/DashboardPage.tsxweb/src/stores/fine-tuning.tsweb/src/__tests__/stores/setup-wizard.test.tsweb/src/pages/PersonalitiesAdminPage.tsxweb/src/pages/setup/WizardProgress.tsxweb/src/pages/providers/ProviderGridView.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/pages/agents/AgentGridView.tsxweb/src/stores/sinks.tsweb/src/stores/setup-wizard/providers.tsweb/src/__tests__/stores/fine-tuning.test.tsweb/src/components/ui/ws-connection-banner.stories.tsxweb/src/utils/mutation-result.tsweb/src/pages/AdminAuditLogPage.tsxweb/src/pages/setup/_hooks.tsweb/src/pages/AgentsPage.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/BudgetPage.tsxweb/src/pages/RequestQueuePage.tsxweb/src/__tests__/utils/errors.test.tsweb/src/stores/websocket.tsweb/src/stores/setup-wizard/navigation.tsweb/src/mocks/handlers/index.tsweb/src/pages/ProvidersPage.tsxweb/src/pages/org-edit/AgentCreateDialog.tsxweb/src/stores/setup-wizard/types.tsweb/src/__tests__/pages/setup/WizardProgress.test.tsxweb/src/pages/setup/CompleteStep.tsxweb/src/stores/setup-wizard/completion.tsweb/src/pages/org-edit/TeamEditDialog.tsxweb/src/__tests__/stores/company.test.tsweb/src/pages/settings/sinks/SinkCard.tsxweb/src/hooks/usePolling.tsweb/src/pages/ClientListPage.tsxweb/src/utils/errors.tsweb/src/pages/SettingsPage.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/stores/artifacts.tsweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/org-edit/TeamListSection.tsxweb/src/stores/company.tsweb/src/hooks/useOrgEditData.ts
web/src/mocks/handlers/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
MSW handlers in
web/src/mocks/handlers/must mirrorweb/src/api/endpoints/*.ts1:1 with a default happy-path handler for every exported endpoint. Use typed envelope helpers (successFor,paginatedFor,voidSuccess) to keep handlers in lockstep with endpoint return types
Files:
web/src/mocks/handlers/setup.tsweb/src/mocks/handlers/audit.tsweb/src/mocks/handlers/personalities.tsweb/src/mocks/handlers/index.ts
web/src/stores/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
All store mutation actions (create/update/delete) must follow the
stores/connections/crud-actions.tspattern: wrap API calls intry/catch, emit success toast on success, emit error toast withgetCrudErrorTitle()on failure, return sentinel values (nullfor entity mutations,falsefor boolean/void mutations)List reads (
fetch*) must seterror: string | nullon the store instead of toasting
Files:
web/src/stores/subworkflows.tsweb/src/stores/workflows.tsweb/src/stores/users.tsweb/src/stores/escalations.tsweb/src/stores/projects.tsweb/src/stores/fine-tuning.tsweb/src/stores/sinks.tsweb/src/stores/setup-wizard/providers.tsweb/src/stores/websocket.tsweb/src/stores/setup-wizard/navigation.tsweb/src/stores/setup-wizard/types.tsweb/src/stores/setup-wizard/completion.tsweb/src/stores/artifacts.tsweb/src/stores/company.ts
web/src/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Cursor pagination (MANDATORY): list endpoints use opaque cursor-based paging via
PaginationMeta. Stores keepnextCursor+hasMorein state (not offset arithmetic) and early-return when!hasMore || !nextCursor. Display counts come fromdata.length
Files:
web/src/api/endpoints/setup.tsweb/src/api/types/openapi.gen.tsweb/src/api/endpoints/personalities.tsweb/src/api/endpoints/audit.ts
web/src/components/ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
ALWAYS reuse existing components from
web/src/components/ui/before creating new ones. NEVER hardcode hex colors, font-family declarations, pixel spacing, Motion transition durations, BCP 47 locale literals ('en-US'), or currency symbols/codes; use design tokens,@/lib/motionpresets, helpers in@/utils/format, andDEFAULT_CURRENCYfrom@/utils/currenciesComponent Props interface name must be
<ComponentName>Propsand be exported from the same file (e.g.AgentCardPropsinagent-card.tsx)Base UI primitives must compose Portal + Backdrop + Popup explicitly, use the
renderprop for polymorphism, and rely on animation state attributes (data-[open],data-[closed]) rather than the olderdata-[state=open]form
Files:
web/src/components/ui/ws-connection-banner.tsxweb/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/components/ui/list-header.tsxweb/src/components/ui/ws-connection-banner.stories.tsx
web/src/components/ui/**/*.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
Every shared UI component must have a sibling
<ComponentName>.stories.tsxcovering every meaningful state (default, hover, loading, error, empty, disabled where applicable)
Files:
web/src/components/ui/ws-connection-banner.tsxweb/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/components/ui/list-header.tsxweb/src/components/ui/ws-connection-banner.stories.tsx
{README.md,docs/**/*.md}
📄 CodeRabbit inference engine (CLAUDE.md)
Numerics in README and public docs must be sourced from data/runtime_stats.yaml via markers per data/README.md
Files:
docs/design/page-structure.mddocs/reference/web-design-system.md
docs/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
D2 diagrams for architecture/nested containers; mermaid for flowcharts/sequence/pipelines; markdown tables for tabular data. D2 theme 200 (Dark Mauve), D2 CLI pinned to v0.7.1 in CI
Files:
docs/design/page-structure.mddocs/reference/web-design-system.md
web/src/utils/constants.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
The client-server WS contract lives in
web/src/utils/constants.ts(WS_PROTOCOL_VERSION,WS_MAX_MESSAGE_SIZE,WS_HEARTBEAT_INTERVAL_MS,WS_PONG_TIMEOUT_MS,LOG_SANITIZE_MAX_LENGTH) and MUST stay in lockstep with Python backend (src/synthorg/api/ws_models.py/src/synthorg/api/controllers/ws.py). Bump protocol version on both sides together for breaking payload changes
Files:
web/src/utils/constants.ts
web/src/api/types/**/*.gen.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
NEVER hand-edit
web/src/api/types/*.gen.ts. Regenerate withuv run python scripts/generate_dto_types_ts.py. Import DTOs via the barrel (import type { AgentConfig } from '@/api/types')
Files:
web/src/api/types/openapi.gen.ts
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Configuration precedence: DB > env > code default via
SettingsService/ConfigResolver(Cat-1) or env > code default (Cat-2,read_only_post_init); Cat-3 bootstrap secrets pure env. Noos.environ.getoutside startup; pre-init Cat-2 reads usesettings.bootstrap_resolver.resolve_init_valueper configuration-precedence.mdNo hardcoded numerics outside settings/definitions/; allowlist 0/1/-1, HTTP codes, hex masks, powers-of-2, and module-level annotated named constants (NAME: int|float|Final|Final[int]|Final[float] = literal). Enforced by scripts/check_no_magic_numbers.py
No
from __future__ import annotations(3.14 has PEP 649). PEP 758 except:except A, B:no parens unless bindingType hints required on public functions; mypy strict mode. Use Google-style docstrings. Line length 88; functions <50 lines; files <800 lines
Errors must follow
<Domain><Condition>Errorpattern inheriting fromDomainError, neverException/RuntimeErrordirectly. Enforced by check_domain_error_hierarchy.pyPydantic v2: frozen +
extra="forbid"on API DTOs (Request/Response/Snapshot/Result/Envelope/Status/Info/Summary suffixes); use@computed_fieldfor derived fields;NotBlankStrfor identifiersArgs models at every system boundary;
parse_typed()for every external dict ingestion. Enforced by check_boundary_typed.pyImmutability: use
model_copy(update=...)orcopy.deepcopy(); deepcopy at system boundariesAsync patterns: use
asyncio.TaskGroupfor fan-out/fan-in; helpers must catchExceptionand re-raiseMemoryError/RecursionErrorClock seam: parameter
clock: Clock | None = None; tests injectFakeClock. Lifecycle: services own_lifecycle_lock; timed-out stops mark unrestartableUntrusted content (SEC-1): use
wrap_untrusted()fromengine.prompt_safety; useHTMLParseGuardfor HTML processingLogging:
from synthorg.observability import get_logger; variable alwayslogger. Neverimport logging/print()in app code. Event names from...
Files:
src/synthorg/api/controllers/setup_models.pysrc/synthorg/api/controllers/setup_agents.pysrc/synthorg/settings/definitions/api.pysrc/synthorg/api/controllers/events.pysrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/setup/agent_helpers.py
src/synthorg/api/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
API startup lifecycle: construction phase (
create_appbody) wires synchronous services; on_startup (_build_lifecycle.on_startup) wires services needing connected persistence. Construction ordering:agent_registrybeforeauto_wire_meetings;tunnel_providerwired unconditionally. On-startup ordering:SettingsServiceauto-wire beforeWorkflowExecutionObserver;OntologyServiceafterpersistence.connect()via_wire_ontology_service
Files:
src/synthorg/api/controllers/setup_models.pysrc/synthorg/api/controllers/setup_agents.pysrc/synthorg/api/controllers/events.pysrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/setup/agent_helpers.py
src/**/*.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/api/controllers/setup_models.pysrc/synthorg/api/controllers/setup_agents.pysrc/synthorg/settings/definitions/api.pysrc/synthorg/api/controllers/events.pysrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/setup/agent_helpers.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Test markers:
@pytest.mark.{unit,integration,e2e,slow}. Async auto-handled. Timeout 30s global. Coverage 80% minWindows testing: unit tests use
WindowsSelectorEventLoopPolicy(3.14 IOCP teardown race); subprocess tests override backTest doubles: use
FakeClockfor Clock seam,mock_of[T](**overrides)for typed-boundary substitutions,SimpleNamespacefor attribute-bags. BareMagicMockat typed boundaries blocked by check_mock_spec.py (zero-tolerance). FakeClock andmock_ofimported fromtests._sharedHypothesis testing: 10 deterministic CI examples; failures are real bugs (fix + add
@example(...))Flaky tests: NEVER skip/xfail; fix fundamentally. Use
asyncio.Event().wait()notsleep(large)
Files:
tests/unit/api/controllers/test_setup.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/api/controllers/test_setup.py
src/synthorg/api/controllers/setup/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Setup completion:
post_setup_reinit()insrc/synthorg/api/controllers/setup/agent_helpers.pypropagates failures;settings_svc.set("api", "setup_complete", "true")only after clean reinit. Whole sequence serialized underCOMPLETE_LOCKto prevent concurrent/setup/completerace conditions
Files:
src/synthorg/api/controllers/setup/agent_helpers.py
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T17:08:13.591Z
Learning: Read design specification from `docs/design/` page before implementing; deviations require approval per DESIGN_SPEC.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T17:08:13.591Z
Learning: Present every plan for accept/deny before coding
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T17:08:13.591Z
Learning: No region/currency/locale privileged; use metric units; use British English per docs/reference/regional-defaults.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T17:08:13.591Z
Learning: Every convention PR must ship its enforcement gate per docs/reference/convention-gates.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T17:08:13.591Z
Learning: Test timeout/slow failures indicate source-code regression; never edit tests/baselines/unit_timing.json, scripts/*_baseline.{txt,json}, or scripts/_*_baseline.py; both families are PreToolUse-blocked. Per-invocation bypass: ALLOW_BASELINE_GROWTH=1 requires explicit user approval
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T17:08:13.591Z
Learning: After issue completion: branch + commit + push (no auto-PR); use `/pre-pr-review`. After PR: use `/aurelio-review-pr` for external feedback. Fix all valid issues; no deferring
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T17:08:13.591Z
Learning: Git commits: `<type>: <description>` (feat/fix/refactor/docs/test/chore/perf/ci); commitizen-enforced
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T17:08:13.591Z
Learning: Signed commits required on protected refs (GPG/SSH or GitHub App via `synthorg-repo-bot`)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T17:08:13.591Z
Learning: Branches: `<type>/<slug>` from main
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T17:08:13.591Z
Learning: Squash merge: PR body becomes squash commit; trailers (`Release-As`, `Closes `#N``) must be in PR body
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T17:08:13.591Z
Learning: CLI is Docker-only (init/start/stop/status); features go in dashboard + REST API
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T17:08:13.591Z
Learning: After every squash merge → `/post-merge-cleanup`
📚 Learning: 2026-05-05T09:04:46.195Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1760
File: scripts/_dual_backend_parity_lib.py:215-216
Timestamp: 2026-05-05T09:04:46.195Z
Learning: This repository targets Python 3.14+ and follows PEP 758. Therefore, reviewer tooling should NOT treat unparenthesized multi-exception `except` clauses written without an `as` clause (e.g., `except MemoryError, RecursionError:`) as syntax errors. Only flag `except`-clause problems when they are genuinely invalid for Python 3.14+.
Applied to files:
src/synthorg/api/controllers/setup_models.pysrc/synthorg/api/controllers/setup_agents.pysrc/synthorg/settings/definitions/api.pysrc/synthorg/api/controllers/events.pytests/unit/api/controllers/test_setup.pysrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/setup/agent_helpers.py
🔇 Additional comments (99)
src/synthorg/settings/definitions/api.py (1)
591-608: LGTM!tests/unit/api/controllers/test_setup.py (1)
460-500: LGTM!Also applies to: 563-618
web/CLAUDE.md (1)
42-42: LGTM!Also applies to: 79-85
web/src/__tests__/hooks/usePolling.test.ts (1)
209-307: LGTM!web/src/__tests__/pages/ApprovalsPage.test.tsx (1)
16-16: LGTM!web/src/__tests__/pages/BudgetForecastPage.test.tsx (1)
73-73: LGTM!Also applies to: 185-191, 204-207, 215-217
web/src/__tests__/pages/BudgetPage.test.tsx (1)
69-69: LGTM!Also applies to: 98-106
web/src/__tests__/pages/DashboardPage.test.tsx (1)
61-61: LGTM!web/src/__tests__/pages/OrgEditPage.test.tsx (1)
4-4: LGTM!Also applies to: 58-61, 101-123
web/src/__tests__/pages/ProvidersPage.test.tsx (1)
49-49: LGTM!web/src/__tests__/pages/SettingsPage.test.tsx (1)
93-93: LGTM!web/src/__tests__/pages/setup/WizardProgress.test.tsx (1)
19-29: LGTM!Also applies to: 37-37, 56-56, 74-74, 93-93, 109-109, 121-153, 162-162
web/src/__tests__/stores/company.test.ts (1)
277-281: LGTM!Also applies to: 302-321, 340-342, 384-396, 438-439, 487-499
web/src/__tests__/stores/fine-tuning.test.ts (1)
118-121: LGTM!web/src/__tests__/stores/setup-wizard.test.ts (1)
79-137: LGTM!Also applies to: 1141-1231
web/src/__tests__/stores/websocket.test.ts (1)
770-774: LGTM!web/src/__tests__/utils/errors.test.ts (1)
8-9: LGTM!Also applies to: 19-20, 70-75, 77-91, 93-107, 128-131, 133-139, 141-145, 147-151
web/src/api/endpoints/audit.ts (1)
1-40: LGTM!web/src/api/endpoints/personalities.ts (1)
29-61: LGTM!web/src/api/endpoints/setup.ts (1)
19-19: LGTM!Also applies to: 132-134
web/src/api/types/openapi.gen.ts (1)
10583-10585: LGTM!web/src/components/ui/lazy-code-mirror-editor.stories.tsx (1)
2-2: LGTM!Also applies to: 38-70
web/src/components/ui/list-header.tsx (1)
1-1: LGTM!Also applies to: 17-25, 43-43, 61-69
web/src/components/ui/ws-connection-banner.stories.tsx (1)
1-50: LGTM!web/src/hooks/useApprovalsData.ts (1)
24-24: LGTM!Also applies to: 105-105
web/src/hooks/useBudgetData.ts (1)
32-32: LGTM!Also applies to: 97-97
web/src/hooks/useDashboardData.ts (1)
1-1: LGTM!Also applies to: 15-21, 33-33, 53-56, 61-66, 80-80, 100-100
web/src/hooks/useOrgEditData.ts (1)
38-49: LGTM!web/src/hooks/useProvidersData.ts (1)
16-17: LGTM!Also applies to: 56-57
web/src/hooks/useScalingData.ts (1)
24-25: LGTM!Also applies to: 83-84
web/src/hooks/useSettingsData.ts (1)
18-19: LGTM!Also applies to: 78-79
web/src/mocks/handlers/index.ts (1)
66-67: LGTM!Also applies to: 88-89, 117-118, 139-140, 165-166, 187-188
web/src/mocks/handlers/setup.ts (1)
176-180: LGTM!web/src/pages/AdminAuditLogPage.tsx (1)
113-232: LGTM!web/src/pages/AgentsPage.tsx (1)
32-35: LGTM!Also applies to: 37-45, 47-67, 67-107, 137-181
web/src/pages/ApprovalsPage.tsx (1)
56-57: LGTM!Also applies to: 65-72, 272-273, 279-285
web/src/pages/BudgetPage.tsx (1)
53-54: LGTM!Also applies to: 104-105, 124-124, 132-132
web/src/pages/ClientListPage.tsx (1)
57-70: LGTM!Also applies to: 94-115, 116-147, 220-270, 273-304
web/src/pages/ConnectionsPage.tsx (1)
12-12: LGTM!Also applies to: 81-82
web/src/pages/CustomRulesPage.tsx (1)
17-17: LGTM!Also applies to: 92-93
web/src/pages/DashboardPage.tsx (1)
2-2: LGTM!Also applies to: 41-42, 63-63
web/src/pages/MeetingsPage.tsx (1)
10-10: LGTM!Also applies to: 81-100, 152-152
web/src/pages/MetaPage.tsx (1)
15-15: LGTM!Also applies to: 99-100
web/src/pages/OrgChartPage.tsx (1)
58-58: LGTM!Also applies to: 550-551
web/src/pages/OrgEditPage.tsx (1)
1-1: LGTM!Also applies to: 7-7, 82-101, 150-152
web/src/pages/PersonalitiesAdminPage.tsx (1)
4-9: LGTM!Also applies to: 11-37, 54-55, 60-84, 87-90, 97-101, 129-195, 198-207, 243-244, 252-278, 294-347
web/src/pages/ProjectsPage.tsx (1)
11-12: LGTM!Also applies to: 33-41, 111-121
web/src/pages/ProviderDetailPage.tsx (1)
6-6: LGTM!Also applies to: 111-116
web/src/pages/ProvidersPage.tsx (1)
5-18: LGTM!Also applies to: 37-50, 52-61, 165-208, 215-215, 233-245, 249-281
web/src/pages/RequestQueuePage.tsx (1)
1-1: LGTM!Also applies to: 16-19, 61-63, 217-217, 238-260
web/src/pages/ScalingPage.tsx (1)
25-25: LGTM!Also applies to: 63-63
web/src/pages/SettingsPage.tsx (1)
10-10: LGTM!Also applies to: 90-90, 316-324
web/src/pages/SettingsSinksPage.tsx (1)
7-7: LGTM!Also applies to: 29-35, 83-93, 136-136, 152-169
web/src/pages/agents/AgentGridView.tsx (1)
16-25: LGTM!Also applies to: 27-35, 37-68, 75-76, 93-103
web/src/pages/budget/AgentSpendingTable.tsx (1)
3-3: LGTM!Also applies to: 122-122, 133-142, 153-186
web/src/pages/budget/CategoryBreakdown.tsx (1)
72-72: LGTM!web/src/pages/org-edit/AgentCreateDialog.tsx (1)
16-16: LGTM!Also applies to: 61-75
web/src/pages/org-edit/AgentEditDrawer.stories.tsx (1)
66-66: LGTM!web/src/pages/org-edit/AgentEditDrawer.tsx (1)
20-21: LGTM!Also applies to: 78-95, 97-106
web/src/pages/org-edit/AgentsTab.stories.tsx (1)
140-141: LGTM!web/src/pages/org-edit/AgentsTab.tsx (1)
35-38: LGTM!web/src/pages/org-edit/DepartmentCreateDialog.tsx (1)
12-12: LGTM!Also applies to: 53-65
web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx (1)
61-61: LGTM!Also applies to: 72-73
web/src/pages/org-edit/DepartmentEditDrawer.tsx (1)
26-31: LGTM!Also applies to: 89-98, 101-110
web/src/pages/org-edit/DepartmentsTab.stories.tsx (1)
95-96: LGTM!Also applies to: 108-109
web/src/pages/org-edit/DepartmentsTab.tsx (1)
332-333: LGTM!web/src/pages/org-edit/TeamEditDialog.tsx (1)
14-15: LGTM!web/src/pages/org-edit/TeamListSection.tsx (1)
29-33: LGTM!web/src/pages/org-edit/YamlEditorPanel.stories.tsx (1)
68-68: LGTM!web/src/pages/org-edit/YamlEditorPanel.tsx (1)
8-15: LGTM!Also applies to: 47-48
web/src/pages/org/useOrgChartDragDrop.ts (1)
110-147: LGTM!web/src/pages/providers/AddManualModelDialog.tsx (1)
130-130: LGTM!web/src/pages/providers/CredentialsRotateDialog.tsx (1)
129-129: LGTM!web/src/pages/providers/ProviderGridView.tsx (1)
14-15: LGTM!Also applies to: 21-22, 26-50, 59-60, 67-69, 88-89
web/src/pages/settings/sinks/SinkCard.tsx (1)
1-1: LGTM!Also applies to: 9-15, 25-25, 82-82, 87-98
web/src/pages/setup/AgentsStep.tsx (1)
9-9: LGTM!Also applies to: 76-85
web/src/pages/setup/CompleteStep.tsx (1)
24-24: LGTM!Also applies to: 27-45, 46-63, 105-117, 120-139
web/src/pages/setup/WizardProgress.tsx (1)
1-1: LGTM!Also applies to: 26-27, 37-37, 42-47, 55-55, 68-70, 73-75, 85-87, 91-95, 114-115, 123-124, 141-141
web/src/pages/setup/WizardShell.tsx (1)
45-45: LGTM!Also applies to: 155-155
web/src/pages/setup/_hooks.ts (1)
4-19: LGTM!Also applies to: 27-42, 44-57
web/src/pages/tasks/TaskCreateDialog.tsx (1)
168-168: LGTM!web/src/router/routes.ts (1)
43-43: LGTM!web/src/stores/artifacts.ts (1)
280-282: LGTM!web/src/stores/company.ts (1)
19-20: LGTM!Also applies to: 58-77, 147-167, 190-191, 221-235, 247-261, 273-287, 299-309, 321-335, 347-361, 373-387, 407-417, 438-452, 473-487, 513-527, 546-556
web/src/stores/escalations.ts (1)
182-184: LGTM!Also applies to: 233-235, 258-260
web/src/stores/fine-tuning.ts (1)
315-327: LGTM!web/src/stores/projects.ts (1)
357-359: LGTM!web/src/stores/setup-wizard/completion.ts (1)
7-7: LGTM!Also applies to: 37-37, 79-79, 86-86, 90-92, 98-112
web/src/stores/setup-wizard/navigation.ts (1)
1-2: LGTM!Also applies to: 42-53, 59-59, 89-112, 150-152
web/src/stores/setup-wizard/providers.ts (1)
73-73: LGTM!Also applies to: 130-130, 137-137, 192-192, 199-199, 232-232
web/src/stores/setup-wizard/types.ts (1)
43-53: LGTM!Also applies to: 61-71, 216-224
web/src/stores/sinks.ts (1)
24-35: LGTM!Also applies to: 117-150, 152-165
web/src/stores/subworkflows.ts (1)
138-142: LGTM!web/src/stores/users.ts (1)
187-189: LGTM!web/src/stores/websocket.ts (1)
11-11: LGTM!Also applies to: 17-19, 51-51, 135-142, 157-157, 159-159, 204-213, 221-221, 241-265
web/src/stores/workflows.ts (1)
396-400: LGTM!web/src/utils/constants.ts (1)
31-39: LGTM!Also applies to: 42-52
web/src/utils/errors.ts (1)
8-49: LGTM!Also applies to: 99-196
web/src/utils/mutation-result.ts (1)
1-58: LGTM!
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1941 +/- ##
==========================================
- Coverage 85.21% 85.20% -0.02%
==========================================
Files 1846 1846
Lines 107802 107867 +65
Branches 9288 9298 +10
==========================================
+ Hits 91868 91909 +41
- Misses 13681 13699 +18
- Partials 2253 2259 +6 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
6e35254 to
2d353a2
Compare
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/synthorg/api/controllers/events.py (1)
388-400:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix the off-by-one in the revalidation failure ceiling.
The docstrings here and on
_user_revocation_reason()say the stream should tolerateapi.sse_revalidate_max_failuresconsecutive transient failures before revoking, but Line 399 disconnects on the Nth failure because it uses>=. With the default3, that drops the stream on failures1/2/3instead of after1/2/3have all been tolerated.🔧 Proposed fix
- if new_failures >= max_failures: + if new_failures > max_failures: return _RevalidationVerdict( consecutive_failures=new_failures, revoked_event={ "event": "revoked", "data": _json.dumps({"reason": "backend_unavailable"}),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/synthorg/api/controllers/events.py` around lines 388 - 400, The revalidation logic has an off-by-one: when `_user_revocation_reason` fails we compute `new_failures = consecutive_failures + 1` and currently revoke when `new_failures >= max_failures`; change that comparison so the stream is revoked only after exceeding the allowed failures (i.e., use `>` rather than `>=`) so that `consecutive_failures` is allowed up to `max_failures` before returning a `_RevalidationVerdict`. Update the conditional that references `new_failures` and `max_failures` in the block around `_user_revocation_reason`, keeping the increment and `_RevalidationVerdict` logic intact.
♻️ Duplicate comments (1)
web/src/mocks/handlers/personalities.ts (1)
89-96:⚠️ Potential issue | 🟠 Major | ⚡ Quick winUse a typed endpoint helper for the schema handler.
Line 95 falls back to
apiSuccess({}), which breaks handler↔endpoint type lockstep. Add a corresponding endpoint helper and switch this route tosuccessFor<typeof ...>(...).As per coding guidelines: “MSW handlers in
web/src/mocks/handlers/must mirrorweb/src/api/endpoints/*.ts1:1 …” and “Use typed envelope helpers (successFor,paginatedFor,voidSuccess) to keep MSW handlers in lockstep with endpoint return types”.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/mocks/handlers/personalities.ts` around lines 89 - 96, The handler for '/api/v1/personalities/schema' uses the untyped apiSuccess({}) fallback; add a typed endpoint helper in the personalities endpoints module (the helper name should match the route, e.g. getPersonalitiesSchema or personalitiesSchema) that exports the endpoint Fn type, then change the mock route to return successFor<typeof <helper>.Fn>(...) (instead of HttpResponse.json(apiSuccess({}))) so the MSW handler uses the typed envelope helper; keep using HttpResponse.json(...) around successFor if required by the handler helpers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/synthorg/api/controllers/setup_models.py`:
- Around line 435-440: The validation currently allows whitespace-only strings
for embedder_failure_reason when embedder_selected is False; update the check in
the same block (where embedder_selected and embedder_failure_reason are
evaluated) to treat whitespace-only strings as empty by using
embedder_failure_reason.strip() (or equivalent) and require its stripped value
to be non-empty before accepting it; if the stripped string is empty, raise the
same ValueError with the existing message.
In `@tests/unit/api/controllers/test_setup.py`:
- Around line 570-575: Update the test docstring in
tests/unit/api/controllers/test_setup.py that currently mentions "replaces
earlier test" and migration/history context so it only states the current
intent: describe that post_setup_reinit now propagates errors and that the
controller persists the completion flag only after reinit returns successfully;
remove any reviewer citations, historical framing, or references to prior tests
and instead succinctly explain the present behavior and why the test exists
(i.e., to ensure the frontend does not see setup as complete when reinit fails).
In `@web/src/api/endpoints/audit.ts`:
- Line 10: Replace the direct DTO import with the project barrel: change the
import of AuditEntry to use the shared DTO barrel (import type { AuditEntry }
from '`@/api/types`') so the module imports follow the agreed contract; update the
import statement that currently references AuditEntry from '../types/dtos.gen'
to import from '`@/api/types`' and remove the old path.
In `@web/src/api/endpoints/setup.ts`:
- Line 19: Replace the direct import of the generated DTO with the project
barrel: update the import of SetupCompleteResponse in setup.ts to use the API
types barrel (import type { SetupCompleteResponse } from '`@/api/types`') so the
DTO is consistently imported via the central export; locate the existing import
statement referencing SetupCompleteResponse and swap its module path to
'`@/api/types`'.
In `@web/src/pages/AdminAuditLogPage.tsx`:
- Around line 31-44: Replace the manual VerdictFilter union with a type derived
from the generated OpenAPI type: import AuditEntry from the generated types and
set VerdictFilter = AuditEntry['verdict'] | '' (to keep the "any verdict"
sentinel). Update VERDICT_OPTIONS' value type if needed to use VerdictFilter and
ensure the option values ('' , 'allow', 'deny', 'escalate', 'output_scan')
remain the runtime strings used by the backend enum so the select still matches
API values.
In `@web/src/pages/org-edit/DepartmentCreateDialog.tsx`:
- Around line 52-65: The code sets submitting true then awaits onCreate but
calls setSubmitting(false) only on the happy path; wrap the onCreate call and
subsequent logic in a try/finally so setSubmitting(false) always runs (use try {
const result = await onCreate({...}); if (result === null) return;
setForm(INITIAL_FORM); onOpenChange(false); } finally { setSubmitting(false);
}), referencing the existing setSubmitting, onCreate, onOpenChange, setForm and
INITIAL_FORM symbols so the dialog can always close even if onCreate throws.
In `@web/src/pages/ProvidersPage.tsx`:
- Around line 186-205: The page-level aggregate toast in handleBulkDelete is
duplicating per-provider toasts from deleteProvider; remove the toast logic here
and instead call a new store action (e.g., providersStore.bulkDeleteProviders or
similar) that accepts visibleSelected and owns the API loop, wrapping each
delete in try/catch and emitting the aggregate success/warning/error toast from
the store per the stores/connections/crud-actions.ts pattern; keep
handleBulkDelete limited to UI state changes (setBulkDeleting,
setBulkDeleteOpen, clearSelection) and a single call to the new
bulkDeleteProviders action (do not add try/catch or toast code in the page).
In `@web/src/stores/users.ts`:
- Around line 187-189: The fire-and-forget call to get().fetchUsers().catch(...)
should be prefixed with void to make the intent explicit and match project
convention; update the expression where get().fetchUsers() is invoked (the call
with the .catch handler) to be void get().fetchUsers().catch(...) so it
satisfies the `@typescript-eslint/no-floating-promises` pattern and maintains
consistency with other usages like the one in ProviderDetailPage.tsx.
In `@web/src/utils/errors.ts`:
- Around line 181-195: The heuristic in errors.ts that checks data.error (raw)
for Pydantic-like messages (the looksPydanticy check around the raw variable) is
too narrow; expand the regex to match additional Pydantic v2 phrasings (e.g.
"input should be", "should have at least", "should have at most", "expected .*
type", "is not of type", "must be a", and other "should"/"expected" variants) so
these messages are treated as Pydantic/internal and the function returns the
generic validation message; update the looksPydanticy pattern used in the same
block (where data?.error is checked) to include these broader alternatives and
keep returning the generic string when the pattern matches.
---
Outside diff comments:
In `@src/synthorg/api/controllers/events.py`:
- Around line 388-400: The revalidation logic has an off-by-one: when
`_user_revocation_reason` fails we compute `new_failures = consecutive_failures
+ 1` and currently revoke when `new_failures >= max_failures`; change that
comparison so the stream is revoked only after exceeding the allowed failures
(i.e., use `>` rather than `>=`) so that `consecutive_failures` is allowed up to
`max_failures` before returning a `_RevalidationVerdict`. Update the conditional
that references `new_failures` and `max_failures` in the block around
`_user_revocation_reason`, keeping the increment and `_RevalidationVerdict`
logic intact.
---
Duplicate comments:
In `@web/src/mocks/handlers/personalities.ts`:
- Around line 89-96: The handler for '/api/v1/personalities/schema' uses the
untyped apiSuccess({}) fallback; add a typed endpoint helper in the
personalities endpoints module (the helper name should match the route, e.g.
getPersonalitiesSchema or personalitiesSchema) that exports the endpoint Fn
type, then change the mock route to return successFor<typeof <helper>.Fn>(...)
(instead of HttpResponse.json(apiSuccess({}))) so the MSW handler uses the typed
envelope helper; keep using HttpResponse.json(...) around successFor if required
by the handler helpers.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 622c8455-89a0-43f4-9fb4-6e8a3ade97ac
📒 Files selected for processing (112)
CLAUDE.mddocs/design/page-structure.mddocs/reference/web-design-system.mdsrc/synthorg/api/controllers/events.pysrc/synthorg/api/controllers/setup/agent_helpers.pysrc/synthorg/api/controllers/setup_agents.pysrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/settings/definitions/api.pytests/unit/api/controllers/test_setup.pyweb/CLAUDE.mdweb/src/__tests__/hooks/usePolling.test.tsweb/src/__tests__/pages/ApprovalsPage.test.tsxweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/__tests__/pages/BudgetPage.test.tsxweb/src/__tests__/pages/DashboardPage.test.tsxweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/__tests__/pages/ProvidersPage.test.tsxweb/src/__tests__/pages/SettingsPage.test.tsxweb/src/__tests__/pages/setup/WizardProgress.test.tsxweb/src/__tests__/stores/company.test.tsweb/src/__tests__/stores/fine-tuning.test.tsweb/src/__tests__/stores/setup-wizard.test.tsweb/src/__tests__/stores/websocket.test.tsweb/src/__tests__/utils/errors.test.tsweb/src/api/endpoints/audit.tsweb/src/api/endpoints/personalities.tsweb/src/api/endpoints/setup.tsweb/src/api/types/openapi.gen.tsweb/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/components/ui/list-header.tsxweb/src/components/ui/ws-connection-banner.stories.tsxweb/src/components/ui/ws-connection-banner.tsxweb/src/hooks/useApprovalsData.tsweb/src/hooks/useBudgetData.tsweb/src/hooks/useDashboardData.tsweb/src/hooks/useOrgEditData.tsweb/src/hooks/usePolling.tsweb/src/hooks/useProvidersData.tsweb/src/hooks/useScalingData.tsweb/src/hooks/useSettingsData.tsweb/src/mocks/handlers/audit.tsweb/src/mocks/handlers/index.tsweb/src/mocks/handlers/personalities.tsweb/src/mocks/handlers/setup.tsweb/src/pages/AdminAuditLogPage.tsxweb/src/pages/AgentsPage.tsxweb/src/pages/ApprovalsPage.tsxweb/src/pages/BudgetPage.tsxweb/src/pages/ClientListPage.tsxweb/src/pages/ConnectionsPage.tsxweb/src/pages/CustomRulesPage.tsxweb/src/pages/DashboardPage.tsxweb/src/pages/MeetingsPage.tsxweb/src/pages/MetaPage.tsxweb/src/pages/OrgChartPage.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/PersonalitiesAdminPage.tsxweb/src/pages/ProjectsPage.tsxweb/src/pages/ProviderDetailPage.tsxweb/src/pages/ProvidersPage.tsxweb/src/pages/RequestQueuePage.tsxweb/src/pages/ScalingPage.tsxweb/src/pages/SettingsPage.tsxweb/src/pages/SettingsSinksPage.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/pages/budget/AgentSpendingTable.tsxweb/src/pages/budget/CategoryBreakdown.tsxweb/src/pages/org-edit/AgentCreateDialog.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/org-edit/AgentsTab.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/org-edit/TeamEditDialog.tsxweb/src/pages/org-edit/TeamListSection.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/pages/org-edit/YamlEditorPanel.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/pages/providers/AddManualModelDialog.tsxweb/src/pages/providers/CredentialsRotateDialog.tsxweb/src/pages/providers/ProviderGridView.tsxweb/src/pages/settings/sinks/SinkCard.tsxweb/src/pages/setup/AgentsStep.tsxweb/src/pages/setup/CompleteStep.tsxweb/src/pages/setup/WizardProgress.tsxweb/src/pages/setup/WizardShell.tsxweb/src/pages/setup/_hooks.tsweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/router/index.tsxweb/src/router/routes.tsweb/src/stores/artifacts.tsweb/src/stores/company.tsweb/src/stores/escalations.tsweb/src/stores/fine-tuning.tsweb/src/stores/projects.tsweb/src/stores/setup-wizard/completion.tsweb/src/stores/setup-wizard/navigation.tsweb/src/stores/setup-wizard/providers.tsweb/src/stores/setup-wizard/types.tsweb/src/stores/sinks.tsweb/src/stores/subworkflows.tsweb/src/stores/users.tsweb/src/stores/websocket.tsweb/src/stores/workflows.tsweb/src/utils/constants.tsweb/src/utils/errors.tsweb/src/utils/mutation-result.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). (5)
- GitHub Check: Build Web Assets (melange)
- GitHub Check: Lighthouse Site
- GitHub Check: Dashboard Test
- GitHub Check: Test (Python 3.14)
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (17)
web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Always use
createLoggerfrom@/lib/loggerfor logging; never use bareconsole.warn,console.error, orconsole.debugin application codeLogger variable name must always be
log(e.g.,const log = createLogger('module-name'))Pass dynamic/untrusted values as separate arguments to logger methods, not interpolated into message strings, so they go through
sanitizeArgWrap attacker-controlled fields inside structured objects with
sanitizeForLog()before embedding in log messagesAny new store that schedules timers or attaches event listeners must expose an equivalent cleanup hook and register it in the global
afterEachinweb/src/test-setup.tsxAny new WebSocket payload handler must route untrusted strings through
sanitizeWsString()orsanitizeWsEnum(); raw(sanitizeWsString(x, n) ?? '') as EnumTypecasts are forbiddenImport
ErrorCodeandErrorCategoryfrom@/api/types/errors(re-exported fromweb/src/api/types/error-codes.gen.ts) and discriminate onErrorCode.<NAME>, never on raw integer literalsImport DTOs via the barrel import:
import type { AgentConfig } from '@/api/types'NEVER write
getXIcon(value): LucideIconfactories called inside JSX bodies; export a<XIcon value={...} />wrapper that does the lookup viacreateElementinside the wrapper bodyUse
useViewportSize()from@/hooks/useViewportSizeto read viewport size; NEVER readwindow.innerWidth/window.innerHeightdirectly in a render body oruseMemo
@eslint-react/no-leaked-conditional-rendering: use{value != null && value !== false && <jsx>}forReactNode | undefinedprops; for compound truthiness useBoolean(...); avoid the{count && <Foo />}bug where0renders verbatim
@typescript-eslint/no-floating-promises: forbids unawaited promises so async work cannot survive the test that scheduled it and trip the active-handle gateHoist
window,document,localStorage, and other global API reads outside of render; place them in `...
Files:
web/src/pages/providers/AddManualModelDialog.tsxweb/src/pages/budget/CategoryBreakdown.tsxweb/src/pages/MetaPage.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/CustomRulesPage.tsxweb/src/hooks/useApprovalsData.tsweb/src/stores/workflows.tsweb/src/stores/subworkflows.tsweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/mocks/handlers/audit.tsweb/src/__tests__/pages/BudgetPage.test.tsxweb/src/mocks/handlers/setup.tsweb/src/stores/fine-tuning.tsweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/__tests__/pages/ApprovalsPage.test.tsxweb/src/router/routes.tsweb/src/pages/ProviderDetailPage.tsxweb/src/pages/ConnectionsPage.tsxweb/src/pages/DashboardPage.tsxweb/src/pages/MeetingsPage.tsxweb/src/__tests__/pages/DashboardPage.test.tsxweb/src/components/ui/list-header.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/stores/users.tsweb/src/hooks/useProvidersData.tsweb/src/pages/OrgChartPage.tsxweb/src/pages/BudgetPage.tsxweb/src/__tests__/pages/ProvidersPage.test.tsxweb/src/api/types/openapi.gen.tsweb/src/stores/artifacts.tsweb/src/stores/setup-wizard/providers.tsweb/src/stores/projects.tsweb/src/__tests__/stores/fine-tuning.test.tsweb/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/pages/ProjectsPage.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/hooks/useScalingData.tsweb/src/pages/providers/CredentialsRotateDialog.tsxweb/src/pages/SettingsPage.tsxweb/src/router/index.tsxweb/src/stores/escalations.tsweb/src/pages/budget/AgentSpendingTable.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/setup/AgentsStep.tsxweb/src/__tests__/pages/SettingsPage.test.tsxweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/hooks/useSettingsData.tsweb/src/pages/org-edit/YamlEditorPanel.tsxweb/src/pages/setup/WizardProgress.tsxweb/src/pages/setup/_hooks.tsweb/src/__tests__/stores/websocket.test.tsweb/src/api/endpoints/setup.tsweb/src/mocks/handlers/personalities.tsweb/src/pages/settings/sinks/SinkCard.tsxweb/src/__tests__/pages/setup/WizardProgress.test.tsxweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/api/endpoints/audit.tsweb/src/components/ui/ws-connection-banner.tsxweb/src/pages/ApprovalsPage.tsxweb/src/pages/providers/ProviderGridView.tsxweb/src/utils/mutation-result.tsweb/src/__tests__/stores/setup-wizard.test.tsweb/src/pages/org-edit/AgentCreateDialog.tsxweb/src/pages/SettingsSinksPage.tsxweb/src/stores/setup-wizard/completion.tsweb/src/hooks/useBudgetData.tsweb/src/utils/constants.tsweb/src/stores/websocket.tsweb/src/hooks/usePolling.tsweb/src/__tests__/utils/errors.test.tsweb/src/utils/errors.tsweb/src/pages/RequestQueuePage.tsxweb/src/stores/setup-wizard/types.tsweb/src/pages/setup/WizardShell.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/__tests__/stores/company.test.tsweb/src/pages/org-edit/TeamListSection.tsxweb/src/api/endpoints/personalities.tsweb/src/pages/ProvidersPage.tsxweb/src/pages/AdminAuditLogPage.tsxweb/src/components/ui/ws-connection-banner.stories.tsxweb/src/pages/OrgEditPage.tsxweb/src/hooks/useOrgEditData.tsweb/src/pages/ScalingPage.tsxweb/src/stores/setup-wizard/navigation.tsweb/src/mocks/handlers/index.tsweb/src/pages/PersonalitiesAdminPage.tsxweb/src/pages/ClientListPage.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/stores/sinks.tsweb/src/hooks/useDashboardData.tsweb/src/pages/org-edit/AgentsTab.tsxweb/src/__tests__/hooks/usePolling.test.tsweb/src/pages/setup/CompleteStep.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/AgentsPage.tsxweb/src/stores/company.tsweb/src/pages/org-edit/TeamEditDialog.tsx
web/**/stores/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
All store mutation actions (create/update/delete) must follow the
stores/connections/crud-actions.tspattern: wrap API calls intry/catch, success path updates state + emits success toast, failure path logs + emits error toast + returns a sentinel on failureMutation sentinel shape mirrors the mutation's return type:
nullfor entity-returning mutations,falsefor boolean-returning mutations; usefalsefor all void/boolean-returning mutations (delete, reorder, update)Optimistic mutations must capture
previousstate synchronously and restore it in thecatchblock on failureUse
getCrudErrorTitle(err, fallback)from@/utils/errorson every error toast to provide distinct titles for duplicate-resource / version-conflict / generic-conflict 409sCallers MUST NOT wrap store mutation calls in
try/catch; the store owns the error UX
Files:
web/src/stores/workflows.tsweb/src/stores/subworkflows.tsweb/src/stores/fine-tuning.tsweb/src/stores/users.tsweb/src/stores/artifacts.tsweb/src/stores/setup-wizard/providers.tsweb/src/stores/projects.tsweb/src/__tests__/stores/fine-tuning.test.tsweb/src/stores/escalations.tsweb/src/__tests__/stores/websocket.test.tsweb/src/__tests__/stores/setup-wizard.test.tsweb/src/stores/setup-wizard/completion.tsweb/src/stores/websocket.tsweb/src/stores/setup-wizard/types.tsweb/src/__tests__/stores/company.test.tsweb/src/stores/setup-wizard/navigation.tsweb/src/stores/sinks.tsweb/src/stores/company.ts
web/src/mocks/handlers/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
MSW handlers in
web/src/mocks/handlers/must mirrorweb/src/api/endpoints/*.ts1:1 with a default happy-path handler for every exported endpointUse typed envelope helpers (
successFor,paginatedFor,voidSuccess) to keep MSW handlers in lockstep with endpoint return typesTest setup boots MSW with
onUnhandledRequest: 'error'; tests override per-case viaserver.use(...)instead of mocking endpoints
Files:
web/src/mocks/handlers/audit.tsweb/src/mocks/handlers/setup.tsweb/src/mocks/handlers/personalities.tsweb/src/mocks/handlers/index.ts
web/**/__tests__/**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Never override MSW handlers via
vi.mock('@/api/endpoints/*'); instead useserver.use(...)to override per-case
Files:
web/src/__tests__/pages/BudgetPage.test.tsxweb/src/__tests__/pages/ApprovalsPage.test.tsxweb/src/__tests__/pages/DashboardPage.test.tsxweb/src/__tests__/pages/ProvidersPage.test.tsxweb/src/__tests__/stores/fine-tuning.test.tsweb/src/__tests__/pages/SettingsPage.test.tsxweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/__tests__/stores/websocket.test.tsweb/src/__tests__/pages/setup/WizardProgress.test.tsxweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/__tests__/stores/setup-wizard.test.tsweb/src/__tests__/utils/errors.test.tsweb/src/__tests__/stores/company.test.tsweb/src/__tests__/hooks/usePolling.test.ts
web/**/__tests__/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Every unit test runs under
web/test-infra/active-handle-tracker.tswhich fails any test that leaks an event-loop-holding resource; zero tolerance, no ceiling, no buffer
Files:
web/src/__tests__/pages/BudgetPage.test.tsxweb/src/__tests__/pages/ApprovalsPage.test.tsxweb/src/__tests__/pages/DashboardPage.test.tsxweb/src/__tests__/pages/ProvidersPage.test.tsxweb/src/__tests__/stores/fine-tuning.test.tsweb/src/__tests__/pages/SettingsPage.test.tsxweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/__tests__/stores/websocket.test.tsweb/src/__tests__/pages/setup/WizardProgress.test.tsxweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/__tests__/stores/setup-wizard.test.tsweb/src/__tests__/utils/errors.test.tsweb/src/__tests__/stores/company.test.tsweb/src/__tests__/hooks/usePolling.test.ts
web/src/components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Reuse
web/src/components/ui/design tokens only in Web Dashboard; detail inweb/CLAUDE.md
Files:
web/src/components/ui/list-header.tsxweb/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/components/ui/ws-connection-banner.tsxweb/src/components/ui/ws-connection-banner.stories.tsx
web/src/components/ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Always reuse existing components from
web/src/components/ui/before creating new ones; NEVER hardcode hex colors, font-family declarations, pixel spacing, Motion transition durations, BCP 47 locale literals ('en-US'), or currency symbols/codesUse design tokens,
@/lib/motionpresets, helpers from@/utils/format, andDEFAULT_CURRENCYfrom@/utils/currenciesinstead of hardcoding valuesThe Props interface name must be
<ComponentName>Propsand must be exported from the same file (e.g.,AgentCardPropsinagent-card.tsx)Base UI primitives must compose Portal + Backdrop + Popup explicitly, use the
renderprop for polymorphism, and rely on animation state attributes (data-[open],data-[closed]) rather thandata-[state=open]The local
<Slot>helper is reserved for<Button asChild>only; use Base UI's nativerenderprop for other polymorphic components
Files:
web/src/components/ui/list-header.tsxweb/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/components/ui/ws-connection-banner.tsxweb/src/components/ui/ws-connection-banner.stories.tsx
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Read
docs/design/page before implementing; deviations need approval. See DESIGN_SPEC.mdNumerics in README and public docs sourced from
data/runtime_stats.yamlvia<!--RS:NAME-->markers
Files:
docs/reference/web-design-system.mdCLAUDE.mddocs/design/page-structure.mdweb/CLAUDE.md
**/*.{py,md}
📄 CodeRabbit inference engine (CLAUDE.md)
No region/currency/locale privileged; use metric units; use British English
Files:
docs/reference/web-design-system.mdCLAUDE.mdsrc/synthorg/api/controllers/setup_models.pysrc/synthorg/api/controllers/setup_agents.pydocs/design/page-structure.mdtests/unit/api/controllers/test_setup.pysrc/synthorg/api/controllers/setup/agent_helpers.pysrc/synthorg/settings/definitions/api.pyweb/CLAUDE.mdsrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/events.py
web/src/api/types/*.gen.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
NEVER hand-edit generated DTO type files; regenerate with
uv run python scripts/generate_dto_types_ts.py
Files:
web/src/api/types/openapi.gen.ts
web/src/components/ui/*.stories.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
Every shared UI component must have a sibling
<ComponentName>.stories.tsxcovering every meaningful state (default, hover, loading, error, empty, disabled where applicable)
Files:
web/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/components/ui/ws-connection-banner.stories.tsx
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Configuration Precedence: DB > env > code default via
SettingsService/ConfigResolver(Cat-1) or env > code default (Cat-2,read_only_post_init); Cat-3 bootstrap secrets are pure env at boot site. YAML is a company-template ingestion format, not a precedence tier. Noos.environ.getoutside startup; pre-init Cat-2 reads usesettings.bootstrap_resolver.resolve_init_valueNo hardcoded numeric values; numerics live in
settings/definitions/. Allowlist: 0/1/-1, HTTP codes, hex masks, powers-of-2, and module-level annotated named constants (NAME: int|float|Final|Final[int]|Final[float] = literal). Enforced byscripts/check_no_magic_numbers.pyErrors:
<Domain><Condition>ErrorfromDomainError; never inheritException/RuntimeError/etc directly. Enforced bycheck_domain_error_hierarchy.pyPydantic v2 frozen +
extra="forbid"on API DTOs (Request/Response/Snapshot/Result/Envelope/Status/Info/Summary suffixes);@computed_fieldfor derived;NotBlankStrfor identifiersArgs models at every system boundary;
parse_typed()for every external dict ingestion. Enforced bycheck_boundary_typed.pyImmutability: use
model_copy(update=...)orcopy.deepcopy(); deepcopy at system boundariesAsync: use
asyncio.TaskGroupfor fan-out/fan-in; helpers catchException(re-raiseMemoryError/RecursionError)Clock seam: use
clock: Clock | None = None; tests injectFakeClock. Lifecycle: services own_lifecycle_lock; timed-out stops mark unrestartableUntrusted content (SEC-1): use
wrap_untrusted()fromengine.prompt_safety; useHTMLParseGuardfor HTMLRepository CRUD:
save(entity),get(id),delete(id) -> bool,list_items(...),query(...)returning tuplesDatetime in persistence: use
parse_iso_utc/format_iso_utcfrompersistence._shared(reject naive); usenormalize_utcfor already-typedLogging: use
from synthorg.observability import get_logger; variable alwayslogger. Neverimport logging/print()in app c...
Files:
src/synthorg/api/controllers/setup_models.pysrc/synthorg/api/controllers/setup_agents.pysrc/synthorg/api/controllers/setup/agent_helpers.pysrc/synthorg/settings/definitions/api.pysrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/events.py
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Comments explain WHY only; no reviewer citations / issue back-refs / migration framing. Enforced by
check_no_review_origin_in_code.py+check_no_migration_framing.pyNo
from __future__ import annotations(Python 3.14 has PEP 649). Use PEP 758 except:except A, B:requires parens when bindingType hints on public functions; mypy strict. Google-style docstrings. Line length 88; functions <50 lines; files <800 lines
Vendor-agnostic: NEVER use real vendor names in project code/tests. Use
example-provider,test-provider,example-{large,medium,small}-001. Allowed in.claude/, third-party imports,providers/presets.py,web/public/provider-logos/
Files:
src/synthorg/api/controllers/setup_models.pysrc/synthorg/api/controllers/setup_agents.pytests/unit/api/controllers/test_setup.pysrc/synthorg/api/controllers/setup/agent_helpers.pysrc/synthorg/settings/definitions/api.pysrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/events.py
src/**/*.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/api/controllers/setup_models.pysrc/synthorg/api/controllers/setup_agents.pysrc/synthorg/api/controllers/setup/agent_helpers.pysrc/synthorg/settings/definitions/api.pysrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/events.py
web/**/api/endpoints/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
List read endpoints must set
error: string | nullon the store instead of toasting; use cursor-based pagination viaPaginationMetawithnextCursor+hasMorein state, early-returning when!hasMore || !nextCursorDisplay counts must come from
data.length; the wire envelope no longer carriestotalfield
getLiveness()is always 200 while the process is alive;getReadiness()is 200 healthy / 503 unavailable (binary'ok' | 'unavailable'outcome, no tri-state). Any new caller must handle the 503 path explicitly
Files:
web/src/api/endpoints/setup.tsweb/src/api/endpoints/audit.tsweb/src/api/endpoints/personalities.ts
web/src/utils/constants.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
The client-server contract (
WS_PROTOCOL_VERSION,WS_MAX_MESSAGE_SIZE,WS_HEARTBEAT_INTERVAL_MS,WS_PONG_TIMEOUT_MS,LOG_SANITIZE_MAX_LENGTH) MUST stay in lockstep withsrc/synthorg/api/ws_models.py/src/synthorg/api/controllers/ws.py; bump protocol version on both sides together for breaking payload changes
Files:
web/src/utils/constants.ts
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Use markers:
@pytest.mark.{unit,integration,e2e,slow}. Async tests auto-marked. Global timeout 30s. Coverage minimum 80%xdist:
-n 8 --dist=loadfileauto-applied via pyprojectaddopts(loadfileprevents Python 3.14+Windows ProactorEventLoop leak)Windows: unit tests use
WindowsSelectorEventLoopPolicy(3.14 IOCP teardown race). Subprocess tests override backTest doubles: use
FakeClockfor Clock seam,mock_of[T](**overrides)for typed-boundary substitutions,SimpleNamespacefor attribute-bags. BareMagicMockat typed boundaries is blocked byscripts/check_mock_spec.py(zero-tolerance, no baseline)FakeClock and
mock_ofimport fromtests._shared; inject viaclock=and the helper's spec subscriptHypothesis: 10 deterministic CI examples; failures are real bugs (fix + add
@example(...))Flaky tests: NEVER skip/xfail; fix fundamentally. Use
asyncio.Event().wait()notsleep(large)
Files:
tests/unit/api/controllers/test_setup.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/api/controllers/test_setup.py
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:25:36.047Z
Learning: Present every plan for accept/deny before coding
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:25:36.047Z
Learning: Every convention PR ships its enforcement gate
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:25:36.047Z
Learning: Timeout/slow failures = source-code regression; never edit `tests/baselines/unit_timing.json` or any `scripts/*_baseline.{txt,json}` / `scripts/_*_baseline.py`. Both families are PreToolUse-blocked. Per-invocation bypass: `ALLOW_BASELINE_GROWTH=1 git commit ...`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:25:36.047Z
Learning: After issue: branch + commit + push (no auto-PR); use `/pre-pr-review` (gh pr create is hookify-blocked). After PR: `/aurelio-review-pr` for external feedback. Fix EVERYTHING valid; no deferring
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:25:36.047Z
Learning: Git commits: `<type>: <description>` (feat/fix/refactor/docs/test/chore/perf/ci); commitizen-enforced
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:25:36.047Z
Learning: Signed commits required on protected refs (GPG/SSH or GitHub App via `synthorg-repo-bot`)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:25:36.047Z
Learning: Branches: `<type>/<slug>` from main
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:25:36.047Z
Learning: Squash merge with PR body as squash commit; trailers (`Release-As`, `Closes `#N``) must be in PR body
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:25:36.047Z
Learning: After every squash merge: run `/post-merge-cleanup`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:25:36.047Z
Learning: CLI is Docker-only (init/start/stop/status); features go in dashboard + REST API
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:26:17.251Z
Learning: React 19 + shadcn/ui + Base UI + Tailwind CSS 4 + Motion + Zustand is the tech stack for the web dashboard
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:26:17.251Z
Learning: CSP nonces flow via `<CSPProvider>` + `<MotionConfig nonce>` in `App.tsx`; details in `docs/security.md`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:26:17.251Z
Learning: Bench targets (`web/src/__tests__/benchmarks/*.bench.ts`) are pure-compute helpers only: no DOM, no MSW, no store imports that pull in toast/timer side effects
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:26:17.251Z
Learning: Bundle-size budgets in `web/.size-limit.cjs` are raised only when a feature legitimately requires more shipping JS, never to silence a CI red
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:26:17.251Z
Learning: The PostToolUse hook (`scripts/check_web_design_system.py`) runs on every `web/src/` edit and flags hardcoded hex/rgba/fonts/Motion durations/locale literals/bare `.toLocale*String()` calls/missing Storybook stories/duplicate component patterns/complex `.map()` blocks
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:26:17.251Z
Learning: Lint runs via `npm --prefix web run lint` with `--max-warnings 0`; ESLint is configured with `eslint-react/eslint-plugin` v5+ via the `recommended-type-checked` preset with explicit error-level opt-ins
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:26:17.251Z
Learning: TypeScript 6 `baseUrl` is deprecated and `esModuleInterop` is always true; `types` defaults to `[]` so `vitest/globals` etc. need explicit listing
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:26:17.251Z
Learning: Storybook 10 is ESM-only; essentials are built into core, but `storybook/addon-docs` is now separate; imports moved to `storybook/test` and `storybook/actions`
📚 Learning: 2026-05-05T09:04:46.195Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1760
File: scripts/_dual_backend_parity_lib.py:215-216
Timestamp: 2026-05-05T09:04:46.195Z
Learning: This repository targets Python 3.14+ and follows PEP 758. Therefore, reviewer tooling should NOT treat unparenthesized multi-exception `except` clauses written without an `as` clause (e.g., `except MemoryError, RecursionError:`) as syntax errors. Only flag `except`-clause problems when they are genuinely invalid for Python 3.14+.
Applied to files:
src/synthorg/api/controllers/setup_models.pysrc/synthorg/api/controllers/setup_agents.pytests/unit/api/controllers/test_setup.pysrc/synthorg/api/controllers/setup/agent_helpers.pysrc/synthorg/settings/definitions/api.pysrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/events.py
🔇 Additional comments (97)
web/src/pages/providers/AddManualModelDialog.tsx (1)
130-130: LGTM!web/src/pages/budget/CategoryBreakdown.tsx (1)
72-72: LGTM!web/src/pages/MetaPage.tsx (1)
15-15: LGTM!Also applies to: 99-99
web/src/pages/org-edit/AgentEditDrawer.stories.tsx (1)
66-66: LGTM!web/src/pages/CustomRulesPage.tsx (1)
17-17: LGTM!Also applies to: 92-92
web/src/hooks/useApprovalsData.ts (1)
24-24: LGTM!Also applies to: 105-105
web/src/stores/workflows.ts (1)
397-399: LGTM!web/src/stores/subworkflows.ts (1)
139-141: LGTM!web/src/pages/org-edit/YamlEditorPanel.stories.tsx (1)
68-68: LGTM!web/src/mocks/handlers/audit.ts (1)
6-28: LGTM!Also applies to: 30-45
web/src/__tests__/pages/BudgetPage.test.tsx (1)
69-69: LGTM!Also applies to: 98-107
web/src/mocks/handlers/setup.ts (1)
176-180: LGTM!web/src/stores/fine-tuning.ts (1)
315-327: LGTM!web/src/pages/org-edit/AgentsTab.stories.tsx (1)
140-141: LGTM!web/src/__tests__/pages/ApprovalsPage.test.tsx (1)
16-16: LGTM!web/src/router/routes.ts (1)
43-43: LGTM!web/src/pages/ProviderDetailPage.tsx (1)
6-6: LGTM!Also applies to: 111-116
web/src/pages/ConnectionsPage.tsx (1)
12-12: LGTM!Also applies to: 81-82
web/src/pages/DashboardPage.tsx (1)
2-2: LGTM!Also applies to: 41-41, 63-63
web/src/pages/MeetingsPage.tsx (1)
10-10: LGTM!Also applies to: 81-99, 152-152
web/src/__tests__/pages/DashboardPage.test.tsx (1)
61-61: LGTM!web/src/components/ui/list-header.tsx (1)
1-1: LGTM!Also applies to: 17-24, 43-43, 61-69
web/src/pages/org-edit/DepartmentsTab.stories.tsx (1)
95-96: LGTM!Also applies to: 108-109
web/src/hooks/useProvidersData.ts (1)
16-16: LGTM!Also applies to: 56-56
web/src/pages/OrgChartPage.tsx (1)
58-58: LGTM!Also applies to: 550-551
docs/reference/web-design-system.md (1)
93-93: LGTM!web/src/pages/BudgetPage.tsx (1)
53-53: LGTM!Also applies to: 104-104, 124-124, 132-132
web/src/__tests__/pages/ProvidersPage.test.tsx (1)
49-49: LGTM!CLAUDE.md (1)
81-81: LGTM!web/src/api/types/openapi.gen.ts (1)
10583-10585: LGTM!web/src/stores/artifacts.ts (1)
280-282: LGTM!web/src/stores/setup-wizard/providers.ts (1)
73-73: LGTM!Also applies to: 130-130, 137-137, 192-192, 199-199, 232-232
web/src/stores/projects.ts (1)
357-359: LGTM!web/src/__tests__/stores/fine-tuning.test.ts (1)
118-121: LGTM!web/src/components/ui/lazy-code-mirror-editor.stories.tsx (1)
2-2: LGTM!Also applies to: 38-70
web/src/pages/ProjectsPage.tsx (1)
11-13: LGTM!Also applies to: 33-41, 111-111, 115-121
web/src/pages/agents/AgentGridView.tsx (1)
16-25: LGTM!Also applies to: 27-35, 37-69, 75-75, 93-103
web/src/hooks/useScalingData.ts (1)
24-24: LGTM!Also applies to: 83-83
web/src/pages/providers/CredentialsRotateDialog.tsx (1)
129-129: LGTM!web/src/pages/SettingsPage.tsx (1)
10-10: LGTM!Also applies to: 90-90, 316-324
web/src/router/index.tsx (1)
45-45: LGTM!Also applies to: 169-169
web/src/stores/escalations.ts (1)
182-184: LGTM!Also applies to: 233-235, 258-260
web/src/pages/budget/AgentSpendingTable.tsx (1)
3-3: LGTM!Also applies to: 122-122, 133-141, 153-186
web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx (1)
61-61: LGTM!Also applies to: 72-73
web/src/pages/setup/AgentsStep.tsx (1)
9-9: LGTM!Also applies to: 76-86
web/src/__tests__/pages/SettingsPage.test.tsx (1)
93-93: LGTM!web/src/__tests__/pages/OrgEditPage.test.tsx (1)
1-4: LGTM!Also applies to: 58-63, 100-145
web/src/hooks/useSettingsData.ts (1)
18-18: LGTM!Also applies to: 78-78
web/src/pages/org-edit/YamlEditorPanel.tsx (1)
8-15: LGTM!Also applies to: 47-48
web/src/pages/setup/WizardProgress.tsx (1)
1-1: LGTM!Also applies to: 26-27, 37-38, 42-47, 55-56, 68-70, 73-76, 85-87, 91-95, 114-115, 123-124, 141-142
web/src/pages/setup/_hooks.ts (1)
4-19: LGTM!Also applies to: 27-42, 44-57
web/src/__tests__/stores/websocket.test.ts (1)
770-775: LGTM!web/src/api/endpoints/setup.ts (1)
132-134: LGTM!web/src/pages/settings/sinks/SinkCard.tsx (1)
9-15: LGTM!Also applies to: 25-25, 82-99
web/src/__tests__/pages/setup/WizardProgress.test.tsx (1)
19-28: LGTM!Also applies to: 37-37, 56-57, 74-75, 93-94, 109-110, 121-153, 162-163
web/src/__tests__/pages/BudgetForecastPage.test.tsx (1)
73-74: LGTM!Also applies to: 185-191, 204-207, 215-217
web/src/pages/tasks/TaskCreateDialog.tsx (1)
168-168: LGTM!web/src/pages/org/useOrgChartDragDrop.ts (1)
110-147: LGTM!src/synthorg/api/controllers/setup_agents.py (1)
246-307: LGTM!web/src/components/ui/ws-connection-banner.tsx (1)
1-62: LGTM!web/src/pages/ApprovalsPage.tsx (1)
56-57: LGTM!Also applies to: 65-72, 272-273, 279-285
web/src/pages/providers/ProviderGridView.tsx (1)
14-16: LGTM!Also applies to: 21-23, 26-50, 59-61, 67-69, 88-90
web/src/utils/mutation-result.ts (1)
1-58: LGTM!web/src/__tests__/stores/setup-wizard.test.ts (1)
79-137: LGTM!Also applies to: 1141-1231
web/src/pages/org-edit/AgentCreateDialog.tsx (1)
16-17: LGTM!Also applies to: 61-75
web/src/pages/SettingsSinksPage.tsx (1)
7-7: LGTM!Also applies to: 29-35, 83-93, 136-137, 152-169
web/src/stores/setup-wizard/completion.ts (1)
7-7: LGTM!Also applies to: 37-37, 79-79, 86-86, 90-112
web/src/hooks/useBudgetData.ts (1)
32-32: LGTM!Also applies to: 97-97
web/src/utils/constants.ts (1)
31-39: LGTM!Also applies to: 42-52
web/src/stores/websocket.ts (1)
11-18: LGTM!Also applies to: 51-52, 135-165, 202-265
web/src/hooks/usePolling.ts (1)
9-33: LGTM!Also applies to: 55-90, 91-130, 132-179, 199-235
web/src/__tests__/utils/errors.test.ts (1)
5-25: LGTM!Also applies to: 70-151
web/src/pages/RequestQueuePage.tsx (1)
1-19: LGTM!Also applies to: 61-62, 150-170, 219-221, 242-264
web/src/stores/setup-wizard/types.ts (1)
43-71: LGTM!Also applies to: 216-224
web/src/pages/OrgEditPage.tsx (1)
82-101: LGTM!Also applies to: 150-152
web/src/hooks/useOrgEditData.ts (1)
38-49: LGTM!docs/design/page-structure.md (1)
246-257: LGTM!web/src/pages/ScalingPage.tsx (1)
25-25: LGTM!Also applies to: 63-63
web/src/stores/setup-wizard/navigation.ts (1)
42-53: LGTM!Also applies to: 89-112, 150-152
web/src/mocks/handlers/index.ts (1)
66-66: LGTM!Also applies to: 88-88, 117-117, 139-139, 165-165, 187-187
web/src/pages/PersonalitiesAdminPage.tsx (1)
72-91: LGTM!Also applies to: 144-202, 206-215, 302-355
src/synthorg/api/controllers/setup/agent_helpers.py (1)
50-57: LGTM!Also applies to: 61-65, 88-95, 113-120, 134-140, 365-365, 377-383, 405-413, 426-447
web/src/pages/ClientListPage.tsx (1)
1-3: LGTM!Also applies to: 6-10, 14-14, 21-31, 57-70, 94-115, 116-147, 220-271, 273-304
web/src/pages/org-edit/DepartmentsTab.tsx (1)
39-47: LGTM!Also applies to: 177-182, 332-332
src/synthorg/settings/definitions/api.py (1)
591-609: LGTM!web/src/stores/sinks.ts (1)
24-35: LGTM!Also applies to: 117-166
web/src/hooks/useDashboardData.ts (1)
1-1: LGTM!Also applies to: 15-22, 33-33, 53-56, 61-66, 80-80, 100-100
web/src/pages/org-edit/AgentsTab.tsx (1)
34-37: LGTM!Also applies to: 175-180
web/src/__tests__/hooks/usePolling.test.ts (1)
209-307: LGTM!web/CLAUDE.md (1)
42-43: LGTM!Also applies to: 79-85
web/src/pages/setup/CompleteStep.tsx (1)
24-63: LGTM!Also applies to: 105-139
web/src/pages/org-edit/AgentEditDrawer.tsx (1)
20-21: LGTM!Also applies to: 86-106
web/src/pages/org-edit/DepartmentEditDrawer.tsx (1)
26-31: LGTM!Also applies to: 89-110
web/src/pages/AgentsPage.tsx (1)
1-106: LGTM!Also applies to: 137-181
web/src/stores/company.ts (1)
57-77: LGTM!Also applies to: 144-168, 173-557
web/src/pages/org-edit/TeamEditDialog.tsx (1)
14-15: LGTM!Also applies to: 74-98
src/synthorg/api/controllers/setup_controller.py (1)
129-179: LGTM!Also applies to: 182-214, 392-428, 491-506, 601-623, 664-683, 727-749, 897-922
Switch the wizard to a hybrid approach: the Agents step no longer auto-uncompletes when an upstream Providers edit invalidates an agent model reference. A new `stepsNeedRevalidation` flag drives a warning-coloured glyph on the progress bar so the user sees the signal without losing forward navigation; the existing on-page ErrorBanner still calls out the broken refs. - Add `stepsNeedRevalidation: Record<WizardStep, boolean>` to the navigation slice plus mark / clear / recompute actions. - Extend `useStepCompletionSync` with a `forwardOnly` option; only the Agents step opts in, so Company / Providers retain reactive demotion behaviour. - Providers slice calls `recomputeAgentsRevalidation` after every successful mutation; the helper re-runs `resolveAgentModels` against current state and only flips the flag when the step is already complete and now has unresolved agents. - `WizardProgress` renders an `AlertTriangle` (text-warning) on complete-but-needs-revalidation steps, skipping the active step and incomplete steps. Adds an sr-only revalidation hint via aria-describedby. - `AgentsStep` mounts with `useClearStepRevalidationOnMount` so the warning glyph disappears the moment the user returns to the step. - Persistence: `stepsNeedRevalidation` is excluded from `partialize`; persisted shape unchanged, so `persistOptions.version` stays at 3.
- `usePolling`: add `isRefetching` to the return shape; skip the tick
while `document.hidden` is true; resume on `visibilitychange`. Add
an optional `skipIfFresh` gate so a polling consumer can short-
circuit when a WS update already populated state. Add a defensive
outer-catch setError to cover the (unlikely) case of scheduleTick
itself rejecting.
- `useDashboardData`: track `lastWsUpdateAt` in a ref and wire
`skipIfFresh: () => Date.now() - last < 15s`; expose `isRefetching`
on the return.
- `useBudgetData`, `useApprovalsData`, `useProvidersData`,
`useScalingData`, `useSettingsData`: surface `isRefetching` on the
return so pages can render a quiet refresh indicator.
- `ListHeader`: new optional `refreshing?: boolean` prop renders a
small spinning `RefreshCw` icon next to the title with an
sr-only "Refreshing" label.
- Wire `refreshing={isRefetching}` on DashboardPage, BudgetPage,
ApprovalsPage, ProvidersPage, ScalingPage; SettingsPage uses a
custom h1 layout so the icon is added inline.
- New `<WsConnectionBanner>` primitive reads from the websocket
store and renders `<ErrorBanner variant=offline>` only when
disconnected. Add it to ConnectionsPage, CustomRulesPage,
MetaPage, and OrgChartPage. OrgEditPage already has the same
banner via its defensive ref pattern.
- New regression tests cover first-tick error state, isRefetching
toggle, skipIfFresh short-circuit, and document.hidden skip.
Convert the 12 company-store CRUD mutations to the canonical Zustand
sentinel pattern: log + error toast + return `null` (entity) or
`false` (boolean) on failure instead of throwing past the caller. The
store now owns the error UX end-to-end; callers branch on the return
value rather than wrapping in try/catch.
- `web/src/stores/company.ts`: rewrite createDepartment, updateDepartment,
deleteDepartment, reorderDepartments, createAgent, updateAgent,
deleteAgent, reorderAgents, createTeam, updateTeam, deleteTeam,
reorderTeams. Success path adds the existing optimistic state update
plus a success toast; failure logs via `sanitizeForLog`, sets
`saveError`, emits an error toast through `getCrudErrorTitle`, and
returns the sentinel.
- Hook + caller propagation: drop every try/catch around the 12
mutations and switch to sentinel-return checks.
- `useOrgEditData`, `DepartmentsTab`, `AgentsTab`, the per-entity
create/edit dialogs (DepartmentCreateDialog, AgentCreateDialog,
TeamEditDialog), the edit drawers, `TeamListSection`, and
`OrgEditPage.handleYamlSave` now check the boolean / null result
and close the dialog only on success.
- `YamlEditorPanel.onSave` contract is now `Promise<boolean>`; the
panel clears its dirty flag only on `true`.
- `useOrgChartDragDrop`: branch on the sentinel and roll back the
optimistic move on `null`. The defensive `.catch` is kept for
unexpected throws.
- Fire-and-forget WS-triggered refetches in artifacts, fine-tuning,
escalations, projects, subworkflows, users, and workflows now use
`.catch(log.warn)` instead of bare `void` so a failure is at least
observable. No toast: WS-driven refetches are not user-initiated.
- Existing company.test.ts assertions flipped from "expect throw" to
"expect sentinel + toast emitted" against the new contract.
Add an intermediate breakpoint to the BudgetPage grid layout so the page renders cleanly at 768-1023px (tablet portrait / landscape) instead of jumping from 4 columns to 2 between 1023 and 1024px. - Line 122 metric strip: now 4 cols at xl+ (>=1280), 3 cols at lg (1024-1279), 2 cols at md (768-1023). Below 768px the grid stays at 2 cols because each metric card is narrow enough to coexist. - Line 130 gauge / spend-burn row: 3 cols at lg+, 2 cols at md, 1 col at phone widths (max-[767px]). The col-span-2 spend-burn chart retains its existing `max-[1023px]:col-span-1` override so the gauge and chart sit side-by-side on tablet instead of stacking awkwardly. Satisfies AC: "BudgetPage renders cleanly at 768-1023px." The five max-w-lg dialogs called out by audit agent 53 (TaskCreate, CredentialsRotate, AddManualModel, PackSelection, PackApplyPreview) will be migrated to <Drawer> in a follow-up; they currently render acceptably on tablet because Base UI's Dialog respects viewport width when `max-w-lg` exceeds 100% of the visible area.
ProviderDetailPage was the only detail page in the dashboard without
breadcrumbs (ArtifactDetail, ClientDetail, and MeetingDetail already
have them via the established pattern). Add the canonical
`Providers > {provider.name}` trail above the DetailNavBar.
Satisfies AC: "4 detail pages have breadcrumbs."
Other Phase 5 polish items (pagination on Agents/Projects/
ClientList/Providers, search/filter on RequestQueue + Budget agent
spending, bulk-action UI on Agents/ClientList/Providers) are
out-of-scope for this PR. They require substantial row-component
changes (selectable slots, checkbox columns, batch handlers) that
are not load-bearing for any acceptance criterion; defer to a
focused follow-up.
Make rate-limit, transient, and conflict toasts actionable by
reading the structured signal available on the wire and converting
it to user-facing copy.
- 429: read the `Retry-After` HTTP header directly (a new
`readRetryAfterHeaderMs` helper that does not cap against the
auto-retry budget, distinct from the existing `parseRetryAfterMs`
used by the retry interceptor) and format via a new `formatRetryAfter`
British-English duration helper. Copy: "Too many requests. Try
again in 2 minutes." Falls back to the previous generic copy when
the header is absent.
- 503: differentiate transient vs sustained by `Retry-After`
presence. Header present -> "The service is restarting. Try again
in {duration}." (transient backpressure). Header absent ->
"The service is unavailable. Contact the operator if this persists."
The FE error taxonomy does not currently carry a `retryable`
boolean for 503, so this uses the header-presence signal instead
of inventing a new schema field.
- 409: branch on `error_detail.error_code` against the codes that
already exist in the generated taxonomy (`DUPLICATE_RECORD`,
`ONTOLOGY_DUPLICATE`, `VERSION_CONFLICT`, `TASK_VERSION_CONFLICT`).
Duplicate-name copy nudges the user to pick a different name;
version-conflict copy asks them to reload first. The generic 409
fallback handles missing-code envelopes.
- Restructure `getErrorMessage` so 409 / 429 / 503 differentiate
BEFORE the generic `data.error` early-return; otherwise a backend
that always populates `data.error` would flatten the new copy
into a single uninformative line.
- New regression tests cover each differentiated branch.
Satisfies AC: "429 / 503 / 409 toasts include actionable next-step
guidance."
Previously the controller flipped `setup_complete=true` before the post-setup reinit (`_post_setup_reinit`, which reloads providers and bootstraps agents into the runtime). If reinit failed, the frontend believed setup succeeded but the runtime was actually in a half-configured state with no obvious retry surface for the operator. Reorder so reinit runs first; the flag only flips on success. A reinit failure now propagates as an exception, leaving the flag false and allowing the operator to retry the completion step. Three other Phase 8 onboarding items (propagate embedder auto-select failure, validate agent provider/model refs, surface provider health in the response) require coordinated DTO + generated-types changes and will follow in a focused PR.
Phase 7's `getErrorMessage` rewrite swapped the 503 fallback from
"temporarily unavailable" to "unavailable. Contact the operator if
this persists." when no `Retry-After` header is present. Update the
fine-tuning store test to assert on the surviving substring
("unavailable") rather than the legacy wording.
- OrgEditPage adopts WsConnectionBanner for parity with the other 4 WP-6 pages; keeps wsSetupError surfacing via a separate warning banner when the socket is connected. - LazyCodeMirrorEditor stories gain an Editable variant that exercises the onChange round-trip. - Drop a workpackage shorthand reference from a fine-tuning test comment; rewrite as WHY-only.
The previous reorder of post_setup_reinit before settings_svc.set was cosmetic: post_setup_reinit swallowed every exception except MemoryError/RecursionError, so a broken provider config still left setup_complete=true with a half-configured runtime. Make post_setup_reinit propagate failures and rely on the controller's existing ordering to leave the flag at false. Two existing tests that asserted the inverse are updated to the new invariant.
- C13: promote api.sse_revalidate_max_failures to a setting (default 3) via the existing api.py registry pattern; the SSE stream resolves it per-connection through the config resolver, with the registry default as a fallback when the chain is unavailable. - C14: WS event-type allowlist for defence-in-depth -- the WS client already imports WS_EVENT_TYPE_VALUES; mirror SSE's AGUI_EVENT_MAP pattern by validating msg.event_type against the allowlist in isWsEvent() so a server roll-out of a new event_type cannot slip past the dispatch loop. - C15: export the server's 4 KiB inbound WS control-message cap as WS_MAX_OUTBOUND_MESSAGE_SIZE in web/src/utils/constants.ts (matching ws.py _MAX_WS_MESSAGE_BYTES = 4096). - C16: +/-5% jitter on heartbeat. setInterval becomes a self-rearming setTimeout sampling a fresh jittered delay each tick, matching the reconnect-backoff jitter pattern but with a tighter band because heartbeat timing has stricter pong-deadline coupling. WsConnectionBanner now suppresses the false-positive flash during the initial handshake via an internal everConnectedRef, so every page using it (the 5 WP-6 pages) gets the same anti-flash behaviour OrgEditPage previously rolled by hand. OrgEditPage test updated to drive the WS store directly since the banner reads connection state from the store.
- M1: ApprovalsPage swaps setState-in-render for a useRef+useEffect pair, matching the OrgEditPage pattern and eliminating the Strict-Mode warning that the previous code triggered. - M3: CLAUDE.md API startup lifecycle gets a bullet pinning the setup-completion ordering invariant (post_setup_reinit must succeed before persisting setup_complete=true). - M4: web/CLAUDE.md Zustand section now states the sentinel contract covers boolean-returning non-delete mutations too, and points all error toasts at getCrudErrorTitle. - M6: updateCompany routes its failure-toast title through getCrudErrorTitle so a 403 lands on 'Permission denied' instead of the generic fallback (test updated to assert the new title). - M10: utils/errors.ts adds a setup-specific 409 branch so the 'setup already complete' case returns 'Setup is already complete. Reload to see the current dashboard.' instead of the generic RESOURCE_CONFLICT copy. - Mi1: usePolling's outer immediate().catch now bails on a stale run generation so an after-stop reject cannot clobber active state.
Two pre-existing races in the setup controller: (M7) Two concurrent /setup/complete requests could both pass _check_setup_not_complete and then both run _post_setup_reinit + settings_svc.set. Add a module-level COMPLETE_LOCK in agent_helpers and wrap the entire validation / reinit / persist flow. (M8, M9) The four agent-mutation endpoints (create_agent, update_agent_model, update_agent_name, randomize_agent_name) called _check_setup_not_complete OUTSIDE the existing _AGENT_LOCK, so a /setup/complete that landed between the check and the agents write would let the agent mutation proceed against a runtime that believed setup was already finalised. Move the check inside the lock in all four methods.
- Me6: getCrudErrorTitle now has a never-typed exhaustiveness guard on the error_category switch so a new backend ErrorCategory member breaks the build instead of silently falling through to the caller's generic fallback. Adds 'Provider error' for provider_error and lets internal fall through to the HTTP-status branch. - Me9: company.updateFromWsEvent runs the two refreshes sequentially (fetchDepartmentHealths still needs config.departments freshly populated) but each branch now has its own try/catch + warn log so one fetch's failure cannot suppress the other and the diagnostic trail attributes the failure to the right call.
- C4: pagination on AgentsPage, ProjectsPage, ClientListPage, ProvidersPage, matching the existing ArtifactsPage pattern (useListPagination + Pagination). Each gets its own namespace so co-existing paginators on the same route do not collide. - C20: utils/errors.ts no longer surfaces raw Pydantic ValueError strings on 422. Prefer the backends curated error_detail.detail; treat raw Pydantic-shaped data.error as internal copy and fall back to a clean user-facing message. - C21: MeetingsPage migrates its inline filtered-vs-empty branch to useEmptyStateProps so the discriminator is uniform with the other list pages already on the helper.
- RequestQueuePage: SearchInput (id / client / requirement description) + status filter SelectField wrapped in SearchFilterSort. Filtered list flows into the existing Kanban grouping so each per-status card surfaces only matching rows. - BudgetPage: AgentSpendingTable now exposes a SearchInput above the header row that filters on agentName. The 'no matches' state is distinct from the 'no spending data' empty state so operators can tell a typo from a missing record.
- C18: validate every persisted agent provider+model pair against provider management config before persisting setup_complete. - C17: auto_select_embedder returns failure reason instead of swallowing it; threaded into SetupCompleteResponse. - C19: SetupCompleteResponse gains embedder_selected / embedder_failure_reason; wizard's completion slice stores completionWarning for non-fatal post-completion notices.
Sinks store gains a deleteSink action that mirrors the saveSink sentinel-return contract: - Built-in (is_default) sinks: clear the override entry from the observability/sink_overrides setting so the sink reverts to its built-in defaults; the identifier stays addressable by the runtime. Surfaced as 'Reset' in the UI. - Custom (operator-added) sinks: filter the sink out of the observability/custom_sinks array entirely. Surfaced as 'Delete'. SinkCard accepts an optional onDelete prop so legacy / read-only contexts can suppress the button; SettingsSinksPage wires it through a ConfirmDialog so the destructive action requires explicit confirmation.
- Me4: ListHeader refreshing announcement includes the page title
via 'Refreshing {title}' instead of bare 'Refreshing', so AT users
hear context-anchored announcements.
- Mi2: WsConnectionBanner hoists its default title / description into
module-level constants so the props default does not allocate a
new string per render.
Switch the admin page from the read-only setup/personality-presets endpoint to the full personalities/presets CRUD surface. New web/src/api/endpoints/personalities.ts exposes list / get / create / update / delete; MSW handlers added in web/src/mocks/handlers so the page renders under test without unhandled-request errors. Page now renders a 'New custom preset' button that opens a narrow Drawer with name / description / traits inputs; on submit it posts to /personalities/presets and refreshes. Custom (non-builtin) preset cards gain a Delete button gated by a ConfirmDialog. Built-in presets stay read-only because the backend only mutates the custom source. Also fixes a rules-of-hooks violation on RequestQueuePage's filteredRequests useMemo (moved above the early-return branches) and a no-floating-promises warning on company.ts updateFromWsEvent.
Consumes GET /security/audit with the most useful filter subset
(tool name, agent id, action type, verdict) and cursor pagination
('Load more' button). The verdict cell renders a colour-coded badge
(deny=danger, escalate=warning, otherwise success) so operators can
scan a long evaluation trail at a glance.
New endpoint web/src/api/endpoints/audit.ts wraps the backend's
filter parameters; MSW handler added under
web/src/mocks/handlers/audit.ts so test renders never hit the
unhandled-request guard. Routing entry added at /admin/audit
(ROUTES.ADMIN_AUDIT_LOG).
…Page (C6) Mirrors the ProjectsPage / WorkflowsPage pattern: each page gets a selectedIds set, a checkbox overlay on each card, a BulkActionBar that surfaces below the list when at least one item is selected, and a ConfirmDialog for the destructive bulk action. - AgentsPage: bulk-delete via the existing company-store deleteAgent action (serial; the per-agent endpoint is idempotent enough for a for-loop to fan-out without batch backend support). - ProvidersPage: bulk-delete via the existing providers-store deleteProvider action. - ClientListPage: bulk-deactivate via the existing clients endpoint DELETE /clients/:id (which deactivates rather than hard-deletes; copy reflects this). AgentGridView and ProviderGridView now accept optional selectedIds + onToggleSelect props so the selection is opt-in -- other call sites of those grids keep their existing layout.
- Me1: split complete_setup body into _validate_completion_prereqs and _run_embedder_auto_select so the controller method stays under the 50-line limit per CLAUDE.md conventions. - C12: widen three field-dense Base UI dialogs to md:max-w-2xl so tablet (768-1023px) viewports render with more breathing room.
- Me3: completeSetup unit tests cover happy path, embedder warning passthrough, 409 already-complete, 422 validation, and a retry-clears-error sequence (5 new tests in setup-wizard.test.ts). - Me7: 429 toast copy distinguishes /setup/complete from other endpoints so operators do not assume the whole dashboard is throttled. - Mi4: BudgetForecastPage tests scope - / N-A assertions to the specific MetricCard card via class-attribute selector instead of asserting a global count of one. - Me2: BudgetPage test beforeEach re-allocates the Map fields explicitly so they cannot reference-share across tests. - M5: MutationResult discriminated-union helper introduced as an opt-in alongside the existing sentinel-return contract.
Splits the cursor-bearing URL into a separate variable so the dead-api-endpoint pre-push gate's literal-string parser can resolve the route to /personalities/presets and match it against the backend controller route. Functionally identical to the inline template-literal version.
Triage outcome from /pre-pr-review (14 findings, all valid): - SetupCompleteResponse model_validator enforces embedder_selected and embedder_failure_reason consistency at construction time. - auto_select_embedder() logs MEMORY_EMBEDDER_AUTO_SELECTED AFTER the persistence writes succeed (was logged before), binds the broad-except path so error_type + safe_error_description land on the warning, and surfaces its inverted str | None contract via the new EmbedderSelectResult type alias. - CompleteStep surfaces completionWarning inline before navigating so a successful-but-degraded setup (failed embedder auto-select) no longer silently ships the operator to a half-configured dashboard. - DepartmentsTab drag overlay uses shadow-card-hover Tailwind class instead of an inline style. - CategoryBreakdown bar transition uses the design-token duration-[var(--so-transition-slow)] instead of duration-500. - CLAUDE.md API startup lifecycle section names the implementation module for COMPLETE_LOCK + post_setup_reinit. - docs/design/page-structure.md gains a Setup Wizard step/route table covering guided vs quick mode. - web/CLAUDE.md sentinel-return rule is unambiguous (any boolean-returning mutation, not just delete); component file conventions section cross-references Base UI integration rules. - web/CLAUDE.md component reuse list adds WsConnectionBanner. Pre-reviewed by 13 agents, 14 findings addressed.
Skipped 5 gemini PEP 758 except-syntax findings (factually wrong: 3.14 PEP 758 comma-except is correct and mandatory per CLAUDE.md). Backend: - setup_controller: _check_has_agents strict=True (fail-closed on corrupted company.agents) - setup_controller: shared lock order (_COMPLETE_LOCK then _AGENT_LOCK) across all agent-mutation endpoints plus create_company; create_company holds _COMPLETE_LOCK only since auto_create_template_agents owns _AGENT_LOCK and asyncio.Lock is non-reentrant Frontend: - AdminAuditLogPage: verdict filter mirrors 4-value OpenAPI enum, formatDateTime for timestamps, request-sequencing guard, clear error on load-more retry - ws-connection-banner: grace-period gate so it is not suppressed forever when the session starts offline - usePolling: guard skipIfFresh throw, in-flight guard on visibility resume to prevent overlapping polls - DepartmentsTab/AgentsTab: handle false from reorder, no try/catch - TeamEditDialog/TeamListSection: finally resets saving/deleting - personalities endpoint: single-page cursor pagination - mocks/audit and personalities: satisfies over as, typed envelope - utils/errors: ErrorCategory constants over raw literals - router: ROUTES.ADMIN_AUDIT_LOG.slice(1) - sinks: drop redundant ternary; websocket: drop duplicate JSDoc - OrgEditPage.test: exercise true never-connected path
Backend: - events.py: revalidation off-by-one, tolerate max_failures before revoking (strict greater-than) + 2 threshold tests - setup_models.py: reject whitespace-only embedder_failure_reason + 2 SetupCompleteResponse validation tests - test_setup.py: drop migration framing from test docstring Frontend: - audit.ts/setup.ts/AdminAuditLogPage: DTO barrel imports - AdminAuditLogPage: derive VerdictFilter type from AuditEntry (kept explicit VERDICT_OPTIONS, labels are user-visible copy) - DepartmentCreateDialog: try/finally so setSubmitting always resets - ProvidersPage: new providersStore.bulkDeleteProviders action owns the API loop + single aggregate toast; page is a thin caller - stores/users.ts: void prefix on fire-and-forget refetch - utils/errors.ts: broaden Pydantic v2 phrase regex (input/string/ list/dict should) - personalities endpoint: add getPersonalitiesSchema helper; mock uses successFor instead of apiSuccess fallback codecov/patch: 4 new tests lift patch coverage above the 70% target (events revalidation block + setup_models embedder validation)
2d353a2 to
32c97e5
Compare
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/src/stores/company.ts (1)
290-310: 🧹 Nitpick | 🔵 Trivial | 💤 Low valueConsider adding success toasts to reorder mutations for consistency.
Other mutations (create, update, delete) show success toasts, but
reorderDepartments(lines 290-310) andreorderTeams(lines 530-557) only show error toasts. While reordering is a lower-visibility operation, the inconsistency may confuse users who expect feedback on all save actions.This is optional—silent success for drag-drop reorder is a reasonable UX choice.
Also applies to: 530-557
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/stores/company.ts` around lines 290 - 310, Add a success toast when reorder operations succeed to match other mutations: in reorderDepartments (and similarly in reorderTeams) after the API call returns and before returning true, call useToastStore.getState().add(...) with variant: 'success' and the same success title/description pattern used by other mutations (e.g., mirror the structure used with getCrudSuccessTitle/getCrudErrorTitle or the app's success toast helper), so that a positive feedback toast is shown on successful reorder; keep the existing state update and return true logic intact and only add the toast call after set(...) that updates config.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/reference/web-design-system.md`:
- Line 93: The docs text for WsConnectionBanner is out of sync: update the
description for the `WsConnectionBanner` component (referenced as
`WsConnectionBanner` and its internal `everConnectedRef`) to state that the
banner can also appear after the initial grace window even if no connection has
ever succeeded (i.e., it is not strictly gated by a successful first
connection), so tests and consumers understand the current behavior and the role
of the initial grace window.
In `@web/src/api/endpoints/personalities.ts`:
- Around line 11-16: The DTO imports in this module currently come from
'../types/dtos.gen' (CreatePresetRequest, PresetDetailResponse,
PresetSummaryResponse, UpdatePresetRequest); replace that relative import with
the sanctioned barrel import from '`@/api/types`' so the file imports these types
via the centralized API-types barrel to maintain consistent type boundaries
across the web app.
In `@web/src/mocks/handlers/audit.ts`:
- Line 3: The import currently pulls AuditEntry from the generated file
`@/api/types/dtos.gen`; change it to use the DTO barrel by importing AuditEntry
from `@/api/types` instead (update the import that references AuditEntry in the
audit handlers file so it uses the centralised barrel export).
In `@web/src/pages/org-edit/AgentCreateDialog.tsx`:
- Around line 60-75: The submit handler in AgentCreateDialog.tsx calls
setSubmitting(true) then awaits onCreate but never guarantees
setSubmitting(false) if onCreate throws, which can leave the dialog stuck; wrap
the await call in a try/finally: call setSubmitting(true) before the try, assign
the await onCreate(...) result inside the try, and always call
setSubmitting(false) in the finally block; keep the existing logic that returns
when result === null and only call setForm(INITIAL_FORM) and onOpenChange(false)
after a successful non-null result.
In `@web/src/pages/org-edit/AgentEditDrawer.tsx`:
- Around line 97-106: The delete handler (handleDelete) can leave the UI stuck
in loading if onDelete throws; wrap the async call in a try/finally so
setDeleting(true) is followed by setDeleting(false) in the finally block, e.g.,
call setDeleting(true) before awaiting onDelete(agent.name), await inside try,
handle success (setDeleteOpen(false), onClose()) after successful await, and
always call setDeleting(false) in finally; update references to agent, onDelete,
setDeleteOpen, onClose and ensure early return if !agent remains.
In `@web/src/pages/org-edit/DepartmentEditDrawer.tsx`:
- Around line 101-110: The handleDelete function can leave deleting=true if
onDelete unexpectedly rejects; update handleDelete (the callback that calls
onDelete with department.name) to wrap the await onDelete(...) in a try/finally
so setDeleting(false) always runs, and only on success (ok) perform
setDeleteOpen(false) and onClose(); keep the existing early return when
department is falsy and preserve dependencies [department, onDelete, onClose].
In `@web/src/pages/org/useOrgChartDragDrop.ts`:
- Around line 140-147: The caller is catching rejection from the store mutation
which breaks the store's sentinel-return/error-ownership contract; remove the
caller-side .catch around the updateAgent mutation invocation in
useOrgChartDragDrop (the block that currently calls rollback() and
announce(`Failed to move ${agentName}`)), so the store retains responsibility
for error handling and UX; simply delete the .catch handler (and its
rollback/announce calls) so updateAgent is invoked without a try/catch or .catch
in this module.
In `@web/src/pages/PersonalitiesAdminPage.tsx`:
- Line 34: The file imports the generated DTO directly; change the import of
PresetSummaryResponse so it comes from the API barrel export instead of the
generated file. Locate the import statement that references
PresetSummaryResponse from dtos.gen and update it to import type {
PresetSummaryResponse } from '`@/api/types`' so the page uses the public API
barrel (symbol: PresetSummaryResponse).
- Around line 72-91: refresh can race: an older paginateAll/listAdminPresets
response may call setPresets and overwrite a newer result; fix by making refresh
generate a unique invocation id (or increment a useRef counter) at start and
store it as the “current” refresh id, then before calling setPresets, setError,
or setLoading in the success/error/finally blocks verify the id matches the
current one and only then apply state; update the refresh function (and related
success/error/finally paths) to perform this stale-response check using the
chosen refresh id mechanism so slower responses cannot clobber newer data.
In `@web/src/pages/setup/CompleteStep.tsx`:
- Around line 46-52: The page currently wraps the store mutation call in a
try/catch inside handleComplete which swallows store-managed errors; remove the
caller-side try/catch and call wizardCompleteSetup() directly (await it if
needed) and then branch UI behavior off the store state fields completionError
and completionWarning (and any loading state) after the promise resolves; update
handleComplete to simply invoke wizardCompleteSetup without error handling so
the store retains ownership of error UX.
In `@web/src/stores/providers/crud-actions.ts`:
- Around line 196-249: The bulkDeleteProviders mutation currently returns
Promise<void> but must return the store sentinel for void mutations (boolean
false). Change bulkDeleteProviders to return Promise<boolean> and ensure every
execution path returns false (e.g., add a final "return false" after the
try/finally where endMutation(set) is called) so callers receive the sentinel;
reference the bulkDeleteProviders function, get().fetchProviders(),
beginMutation/endMutation, and the useToastStore notifications when implementing
the change.
In `@web/src/stores/providers/types.ts`:
- Line 94: Change the bulkDeleteProviders mutation signature to follow the
sentinel-return contract by returning Promise<boolean> instead of Promise<void>;
update the declaration of bulkDeleteProviders and all implementations to return
true on success and false on failure (mirroring the existing deleteProvider
pattern so callers can detect failure without try/catch), ensuring the function
name bulkDeleteProviders is used for locating and updating both type and
implementation sites.
---
Outside diff comments:
In `@web/src/stores/company.ts`:
- Around line 290-310: Add a success toast when reorder operations succeed to
match other mutations: in reorderDepartments (and similarly in reorderTeams)
after the API call returns and before returning true, call
useToastStore.getState().add(...) with variant: 'success' and the same success
title/description pattern used by other mutations (e.g., mirror the structure
used with getCrudSuccessTitle/getCrudErrorTitle or the app's success toast
helper), so that a positive feedback toast is shown on successful reorder; keep
the existing state update and return true logic intact and only add the toast
call after set(...) that updates config.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 55154387-8645-4c1f-85df-dfb181fcf322
📒 Files selected for processing (116)
CLAUDE.mddocs/design/page-structure.mddocs/reference/web-design-system.mdsrc/synthorg/api/controllers/events.pysrc/synthorg/api/controllers/setup/agent_helpers.pysrc/synthorg/api/controllers/setup_agents.pysrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/settings/definitions/api.pytests/unit/api/controllers/test_setup.pytests/unit/api/controllers/test_setup_dtos.pytests/unit/api/controllers/test_sse_revalidate.pyweb/CLAUDE.mdweb/src/__tests__/hooks/usePolling.test.tsweb/src/__tests__/pages/ApprovalsPage.test.tsxweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/__tests__/pages/BudgetPage.test.tsxweb/src/__tests__/pages/DashboardPage.test.tsxweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/__tests__/pages/ProvidersPage.test.tsxweb/src/__tests__/pages/SettingsPage.test.tsxweb/src/__tests__/pages/setup/WizardProgress.test.tsxweb/src/__tests__/stores/company.test.tsweb/src/__tests__/stores/fine-tuning.test.tsweb/src/__tests__/stores/setup-wizard.test.tsweb/src/__tests__/stores/websocket.test.tsweb/src/__tests__/utils/errors.test.tsweb/src/api/endpoints/audit.tsweb/src/api/endpoints/personalities.tsweb/src/api/endpoints/setup.tsweb/src/api/types/openapi.gen.tsweb/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/components/ui/list-header.tsxweb/src/components/ui/ws-connection-banner.stories.tsxweb/src/components/ui/ws-connection-banner.tsxweb/src/hooks/useApprovalsData.tsweb/src/hooks/useBudgetData.tsweb/src/hooks/useDashboardData.tsweb/src/hooks/useOrgEditData.tsweb/src/hooks/usePolling.tsweb/src/hooks/useProvidersData.tsweb/src/hooks/useScalingData.tsweb/src/hooks/useSettingsData.tsweb/src/mocks/handlers/audit.tsweb/src/mocks/handlers/index.tsweb/src/mocks/handlers/personalities.tsweb/src/mocks/handlers/setup.tsweb/src/pages/AdminAuditLogPage.tsxweb/src/pages/AgentsPage.tsxweb/src/pages/ApprovalsPage.tsxweb/src/pages/BudgetPage.tsxweb/src/pages/ClientListPage.tsxweb/src/pages/ConnectionsPage.tsxweb/src/pages/CustomRulesPage.tsxweb/src/pages/DashboardPage.tsxweb/src/pages/MeetingsPage.tsxweb/src/pages/MetaPage.tsxweb/src/pages/OrgChartPage.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/PersonalitiesAdminPage.tsxweb/src/pages/ProjectsPage.tsxweb/src/pages/ProviderDetailPage.tsxweb/src/pages/ProvidersPage.tsxweb/src/pages/RequestQueuePage.tsxweb/src/pages/ScalingPage.tsxweb/src/pages/SettingsPage.tsxweb/src/pages/SettingsSinksPage.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/pages/budget/AgentSpendingTable.tsxweb/src/pages/budget/CategoryBreakdown.tsxweb/src/pages/org-edit/AgentCreateDialog.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/org-edit/AgentsTab.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/org-edit/TeamEditDialog.tsxweb/src/pages/org-edit/TeamListSection.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/pages/org-edit/YamlEditorPanel.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/pages/providers/AddManualModelDialog.tsxweb/src/pages/providers/CredentialsRotateDialog.tsxweb/src/pages/providers/ProviderGridView.tsxweb/src/pages/settings/sinks/SinkCard.tsxweb/src/pages/setup/AgentsStep.tsxweb/src/pages/setup/CompleteStep.tsxweb/src/pages/setup/WizardProgress.tsxweb/src/pages/setup/WizardShell.tsxweb/src/pages/setup/_hooks.tsweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/router/index.tsxweb/src/router/routes.tsweb/src/stores/artifacts.tsweb/src/stores/company.tsweb/src/stores/escalations.tsweb/src/stores/fine-tuning.tsweb/src/stores/projects.tsweb/src/stores/providers/crud-actions.tsweb/src/stores/providers/types.tsweb/src/stores/setup-wizard/completion.tsweb/src/stores/setup-wizard/navigation.tsweb/src/stores/setup-wizard/providers.tsweb/src/stores/setup-wizard/types.tsweb/src/stores/sinks.tsweb/src/stores/subworkflows.tsweb/src/stores/users.tsweb/src/stores/websocket.tsweb/src/stores/workflows.tsweb/src/utils/constants.tsweb/src/utils/errors.tsweb/src/utils/mutation-result.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). (5)
- GitHub Check: Build Web Assets (melange)
- GitHub Check: Test (Python 3.14)
- GitHub Check: Dashboard Test
- GitHub Check: Lighthouse Site
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (24)
web/src/**/*.{tsx,ts}
📄 CodeRabbit inference engine (CLAUDE.md)
Reuse
web/src/components/ui/components and design tokens in web dashboard; seeweb/CLAUDE.md
Files:
web/src/pages/budget/CategoryBreakdown.tsxweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/router/routes.tsweb/src/__tests__/pages/ProvidersPage.test.tsxweb/src/pages/setup/WizardShell.tsxweb/src/__tests__/pages/ApprovalsPage.test.tsxweb/src/__tests__/stores/fine-tuning.test.tsweb/src/stores/providers/types.tsweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/stores/subworkflows.tsweb/src/hooks/useProvidersData.tsweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/router/index.tsxweb/src/pages/CustomRulesPage.tsxweb/src/stores/workflows.tsweb/src/pages/providers/CredentialsRotateDialog.tsxweb/src/pages/MetaPage.tsxweb/src/api/types/openapi.gen.tsweb/src/stores/users.tsweb/src/__tests__/stores/websocket.test.tsweb/src/__tests__/pages/DashboardPage.test.tsxweb/src/hooks/useApprovalsData.tsweb/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/pages/DashboardPage.tsxweb/src/pages/ProjectsPage.tsxweb/src/__tests__/pages/BudgetPage.test.tsxweb/src/hooks/useScalingData.tsweb/src/stores/escalations.tsweb/src/stores/artifacts.tsweb/src/pages/OrgChartPage.tsxweb/src/pages/ConnectionsPage.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/stores/projects.tsweb/src/mocks/handlers/audit.tsweb/src/pages/ProviderDetailPage.tsxweb/src/hooks/useBudgetData.tsweb/src/pages/org-edit/YamlEditorPanel.tsxweb/src/pages/SettingsPage.tsxweb/src/pages/providers/AddManualModelDialog.tsxweb/src/api/endpoints/setup.tsweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/stores/providers/crud-actions.tsweb/src/__tests__/utils/errors.test.tsweb/src/pages/ScalingPage.tsxweb/src/stores/fine-tuning.tsweb/src/pages/settings/sinks/SinkCard.tsxweb/src/hooks/useSettingsData.tsweb/src/stores/setup-wizard/providers.tsweb/src/__tests__/pages/SettingsPage.test.tsxweb/src/stores/setup-wizard/types.tsweb/src/components/ui/list-header.tsxweb/src/api/endpoints/audit.tsweb/src/utils/constants.tsweb/src/pages/setup/_hooks.tsweb/src/__tests__/pages/setup/WizardProgress.test.tsxweb/src/pages/setup/AgentsStep.tsxweb/src/hooks/useOrgEditData.tsweb/src/__tests__/stores/company.test.tsweb/src/mocks/handlers/setup.tsweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/stores/setup-wizard/completion.tsweb/src/pages/budget/AgentSpendingTable.tsxweb/src/pages/providers/ProviderGridView.tsxweb/src/pages/AgentsPage.tsxweb/src/pages/SettingsSinksPage.tsxweb/src/pages/org-edit/TeamEditDialog.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/__tests__/hooks/usePolling.test.tsweb/src/stores/sinks.tsweb/src/pages/MeetingsPage.tsxweb/src/pages/ApprovalsPage.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/components/ui/ws-connection-banner.tsxweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/pages/RequestQueuePage.tsxweb/src/mocks/handlers/index.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/BudgetPage.tsxweb/src/__tests__/stores/setup-wizard.test.tsweb/src/hooks/useDashboardData.tsweb/src/pages/OrgEditPage.tsxweb/src/components/ui/ws-connection-banner.stories.tsxweb/src/pages/org-edit/AgentsTab.tsxweb/src/api/endpoints/personalities.tsweb/src/pages/setup/WizardProgress.tsxweb/src/pages/setup/CompleteStep.tsxweb/src/pages/ProvidersPage.tsxweb/src/stores/websocket.tsweb/src/pages/ClientListPage.tsxweb/src/mocks/handlers/personalities.tsweb/src/hooks/usePolling.tsweb/src/utils/mutation-result.tsweb/src/stores/setup-wizard/navigation.tsweb/src/utils/errors.tsweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/org-edit/AgentCreateDialog.tsxweb/src/pages/org-edit/TeamListSection.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/AdminAuditLogPage.tsxweb/src/stores/company.tsweb/src/pages/PersonalitiesAdminPage.tsx
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Always use
createLoggerfrom@/lib/logger; never bareconsole.warn/console.error/console.debugin application code. Variable name must always belog(e.g.,const log = createLogger('module-name')).For static messages in logging, pass dynamic/untrusted values as separate args (not interpolated into the message string) so they go through
sanitizeArg.Attacker-controlled fields inside structured objects must be wrapped in
sanitizeForLog()before embedding in log calls.Callers MUST NOT wrap store mutation calls in
try/catch; the store owns the error UX.Any new caller of health endpoints must handle the 503 path explicitly.
Import error codes via
ErrorCodeandErrorCategoryfrom@/api/types/errors(re-exported fromweb/src/api/types/error-codes.gen.ts). Discriminate onErrorCode.<NAME>, never on raw integer literals.Import DTOs via the barrel:
import type { AgentConfig } from '@/api/types'.A PostToolUse hook (
scripts/check_web_design_system.py) runs on everyweb/src/edit and flags hardcoded hex / rgba / fonts / Motion durations / locale literals / bare.toLocale*String()calls / missing Storybook stories / duplicate component patterns / complex.map()blocks. Fix every violation before proceeding.
Files:
web/src/pages/budget/CategoryBreakdown.tsxweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/router/routes.tsweb/src/__tests__/pages/ProvidersPage.test.tsxweb/src/pages/setup/WizardShell.tsxweb/src/__tests__/pages/ApprovalsPage.test.tsxweb/src/__tests__/stores/fine-tuning.test.tsweb/src/stores/providers/types.tsweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/stores/subworkflows.tsweb/src/hooks/useProvidersData.tsweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/router/index.tsxweb/src/pages/CustomRulesPage.tsxweb/src/stores/workflows.tsweb/src/pages/providers/CredentialsRotateDialog.tsxweb/src/pages/MetaPage.tsxweb/src/api/types/openapi.gen.tsweb/src/stores/users.tsweb/src/__tests__/stores/websocket.test.tsweb/src/__tests__/pages/DashboardPage.test.tsxweb/src/hooks/useApprovalsData.tsweb/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/pages/DashboardPage.tsxweb/src/pages/ProjectsPage.tsxweb/src/__tests__/pages/BudgetPage.test.tsxweb/src/hooks/useScalingData.tsweb/src/stores/escalations.tsweb/src/stores/artifacts.tsweb/src/pages/OrgChartPage.tsxweb/src/pages/ConnectionsPage.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/stores/projects.tsweb/src/mocks/handlers/audit.tsweb/src/pages/ProviderDetailPage.tsxweb/src/hooks/useBudgetData.tsweb/src/pages/org-edit/YamlEditorPanel.tsxweb/src/pages/SettingsPage.tsxweb/src/pages/providers/AddManualModelDialog.tsxweb/src/api/endpoints/setup.tsweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/stores/providers/crud-actions.tsweb/src/__tests__/utils/errors.test.tsweb/src/pages/ScalingPage.tsxweb/src/stores/fine-tuning.tsweb/src/pages/settings/sinks/SinkCard.tsxweb/src/hooks/useSettingsData.tsweb/src/stores/setup-wizard/providers.tsweb/src/__tests__/pages/SettingsPage.test.tsxweb/src/stores/setup-wizard/types.tsweb/src/components/ui/list-header.tsxweb/src/api/endpoints/audit.tsweb/src/utils/constants.tsweb/src/pages/setup/_hooks.tsweb/src/__tests__/pages/setup/WizardProgress.test.tsxweb/src/pages/setup/AgentsStep.tsxweb/src/hooks/useOrgEditData.tsweb/src/__tests__/stores/company.test.tsweb/src/mocks/handlers/setup.tsweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/stores/setup-wizard/completion.tsweb/src/pages/budget/AgentSpendingTable.tsxweb/src/pages/providers/ProviderGridView.tsxweb/src/pages/AgentsPage.tsxweb/src/pages/SettingsSinksPage.tsxweb/src/pages/org-edit/TeamEditDialog.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/__tests__/hooks/usePolling.test.tsweb/src/stores/sinks.tsweb/src/pages/MeetingsPage.tsxweb/src/pages/ApprovalsPage.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/components/ui/ws-connection-banner.tsxweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/pages/RequestQueuePage.tsxweb/src/mocks/handlers/index.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/BudgetPage.tsxweb/src/__tests__/stores/setup-wizard.test.tsweb/src/hooks/useDashboardData.tsweb/src/pages/OrgEditPage.tsxweb/src/components/ui/ws-connection-banner.stories.tsxweb/src/pages/org-edit/AgentsTab.tsxweb/src/api/endpoints/personalities.tsweb/src/pages/setup/WizardProgress.tsxweb/src/pages/setup/CompleteStep.tsxweb/src/pages/ProvidersPage.tsxweb/src/stores/websocket.tsweb/src/pages/ClientListPage.tsxweb/src/mocks/handlers/personalities.tsweb/src/hooks/usePolling.tsweb/src/utils/mutation-result.tsweb/src/stores/setup-wizard/navigation.tsweb/src/utils/errors.tsweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/org-edit/AgentCreateDialog.tsxweb/src/pages/org-edit/TeamListSection.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/AdminAuditLogPage.tsxweb/src/stores/company.tsweb/src/pages/PersonalitiesAdminPage.tsx
web/src/{components,pages,hooks}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
NEVER hardcode hex colors, font-family declarations, pixel spacing, Motion transition durations, BCP 47 locale literals (
'en-US'), or currency symbols / codes; use design tokens,@/lib/motionpresets, helpers in@/utils/format, andDEFAULT_CURRENCYfrom@/utils/currencies.
Files:
web/src/pages/budget/CategoryBreakdown.tsxweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/pages/setup/WizardShell.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/hooks/useProvidersData.tsweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/CustomRulesPage.tsxweb/src/pages/providers/CredentialsRotateDialog.tsxweb/src/pages/MetaPage.tsxweb/src/hooks/useApprovalsData.tsweb/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/pages/DashboardPage.tsxweb/src/pages/ProjectsPage.tsxweb/src/hooks/useScalingData.tsweb/src/pages/OrgChartPage.tsxweb/src/pages/ConnectionsPage.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/pages/ProviderDetailPage.tsxweb/src/hooks/useBudgetData.tsweb/src/pages/org-edit/YamlEditorPanel.tsxweb/src/pages/SettingsPage.tsxweb/src/pages/providers/AddManualModelDialog.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/ScalingPage.tsxweb/src/pages/settings/sinks/SinkCard.tsxweb/src/hooks/useSettingsData.tsweb/src/components/ui/list-header.tsxweb/src/pages/setup/_hooks.tsweb/src/pages/setup/AgentsStep.tsxweb/src/hooks/useOrgEditData.tsweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/budget/AgentSpendingTable.tsxweb/src/pages/providers/ProviderGridView.tsxweb/src/pages/AgentsPage.tsxweb/src/pages/SettingsSinksPage.tsxweb/src/pages/org-edit/TeamEditDialog.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/pages/MeetingsPage.tsxweb/src/pages/ApprovalsPage.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/components/ui/ws-connection-banner.tsxweb/src/pages/RequestQueuePage.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/BudgetPage.tsxweb/src/hooks/useDashboardData.tsweb/src/pages/OrgEditPage.tsxweb/src/components/ui/ws-connection-banner.stories.tsxweb/src/pages/org-edit/AgentsTab.tsxweb/src/pages/setup/WizardProgress.tsxweb/src/pages/setup/CompleteStep.tsxweb/src/pages/ProvidersPage.tsxweb/src/pages/ClientListPage.tsxweb/src/hooks/usePolling.tsweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/org-edit/AgentCreateDialog.tsxweb/src/pages/org-edit/TeamListSection.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/AdminAuditLogPage.tsxweb/src/pages/PersonalitiesAdminPage.tsx
web/src/{components,pages,hooks}/**/*.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
Use
useViewportSize()from@/hooks/useViewportSizefor viewport-size reads. NEVER readwindow.innerWidth/window.innerHeightdirectly in a render body oruseMemo.
Files:
web/src/pages/budget/CategoryBreakdown.tsxweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/pages/setup/WizardShell.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/CustomRulesPage.tsxweb/src/pages/providers/CredentialsRotateDialog.tsxweb/src/pages/MetaPage.tsxweb/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/pages/DashboardPage.tsxweb/src/pages/ProjectsPage.tsxweb/src/pages/OrgChartPage.tsxweb/src/pages/ConnectionsPage.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/pages/ProviderDetailPage.tsxweb/src/pages/org-edit/YamlEditorPanel.tsxweb/src/pages/SettingsPage.tsxweb/src/pages/providers/AddManualModelDialog.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/ScalingPage.tsxweb/src/pages/settings/sinks/SinkCard.tsxweb/src/components/ui/list-header.tsxweb/src/pages/setup/AgentsStep.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/budget/AgentSpendingTable.tsxweb/src/pages/providers/ProviderGridView.tsxweb/src/pages/AgentsPage.tsxweb/src/pages/SettingsSinksPage.tsxweb/src/pages/org-edit/TeamEditDialog.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/pages/MeetingsPage.tsxweb/src/pages/ApprovalsPage.tsxweb/src/components/ui/ws-connection-banner.tsxweb/src/pages/RequestQueuePage.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/BudgetPage.tsxweb/src/pages/OrgEditPage.tsxweb/src/components/ui/ws-connection-banner.stories.tsxweb/src/pages/org-edit/AgentsTab.tsxweb/src/pages/setup/WizardProgress.tsxweb/src/pages/setup/CompleteStep.tsxweb/src/pages/ProvidersPage.tsxweb/src/pages/ClientListPage.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/org-edit/AgentCreateDialog.tsxweb/src/pages/org-edit/TeamListSection.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/AdminAuditLogPage.tsxweb/src/pages/PersonalitiesAdminPage.tsx
web/src/{components,pages}/**/*.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
For
ReactNode | undefinedprops use{value != null && value !== false && <jsx>}; for compound truthiness useBoolean(...); avoid the{count && <Foo />}bug where0renders verbatim.
Files:
web/src/pages/budget/CategoryBreakdown.tsxweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/pages/setup/WizardShell.tsxweb/src/pages/org-edit/AgentsTab.stories.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/CustomRulesPage.tsxweb/src/pages/providers/CredentialsRotateDialog.tsxweb/src/pages/MetaPage.tsxweb/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/pages/DashboardPage.tsxweb/src/pages/ProjectsPage.tsxweb/src/pages/OrgChartPage.tsxweb/src/pages/ConnectionsPage.tsxweb/src/pages/org-edit/YamlEditorPanel.stories.tsxweb/src/pages/ProviderDetailPage.tsxweb/src/pages/org-edit/YamlEditorPanel.tsxweb/src/pages/SettingsPage.tsxweb/src/pages/providers/AddManualModelDialog.tsxweb/src/pages/org-edit/AgentEditDrawer.stories.tsxweb/src/pages/ScalingPage.tsxweb/src/pages/settings/sinks/SinkCard.tsxweb/src/components/ui/list-header.tsxweb/src/pages/setup/AgentsStep.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/budget/AgentSpendingTable.tsxweb/src/pages/providers/ProviderGridView.tsxweb/src/pages/AgentsPage.tsxweb/src/pages/SettingsSinksPage.tsxweb/src/pages/org-edit/TeamEditDialog.tsxweb/src/pages/agents/AgentGridView.tsxweb/src/pages/MeetingsPage.tsxweb/src/pages/ApprovalsPage.tsxweb/src/components/ui/ws-connection-banner.tsxweb/src/pages/RequestQueuePage.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/BudgetPage.tsxweb/src/pages/OrgEditPage.tsxweb/src/components/ui/ws-connection-banner.stories.tsxweb/src/pages/org-edit/AgentsTab.tsxweb/src/pages/setup/WizardProgress.tsxweb/src/pages/setup/CompleteStep.tsxweb/src/pages/ProvidersPage.tsxweb/src/pages/ClientListPage.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/org-edit/AgentCreateDialog.tsxweb/src/pages/org-edit/TeamListSection.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/AdminAuditLogPage.tsxweb/src/pages/PersonalitiesAdminPage.tsx
web/src/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
test-setup.tsxboots withonUnhandledRequest: 'error'; tests override per-case viaserver.use(...), nevervi.mock('@/api/endpoints/*').Every unit test runs under
web/test-infra/active-handle-tracker.ts, which hooks Node'sasync_hooksand fails any test that leaks an event-loop-holding resource. Zero tolerance, no ceiling, no buffer.
Files:
web/src/__tests__/pages/ProvidersPage.test.tsxweb/src/__tests__/pages/ApprovalsPage.test.tsxweb/src/__tests__/stores/fine-tuning.test.tsweb/src/__tests__/stores/websocket.test.tsweb/src/__tests__/pages/DashboardPage.test.tsxweb/src/__tests__/pages/BudgetPage.test.tsxweb/src/__tests__/utils/errors.test.tsweb/src/__tests__/pages/SettingsPage.test.tsxweb/src/__tests__/pages/setup/WizardProgress.test.tsxweb/src/__tests__/stores/company.test.tsweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/__tests__/hooks/usePolling.test.tsweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/__tests__/stores/setup-wizard.test.ts
web/src/stores/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
The sentinel shape for store mutations mirrors the mutation's return type:
nullfor entity-returning mutations,falsefor boolean-returning mutations. Every void / boolean-returning mutation usesfalseregardless of operation type.List reads (
fetch*) seterror: string | nullon the store instead of toasting.Any new store that schedules timers or attaches event listeners must expose an equivalent cleanup hook and register it in the global
afterEachinweb/src/test-setup.tsx.
Files:
web/src/stores/providers/types.tsweb/src/stores/subworkflows.tsweb/src/stores/workflows.tsweb/src/stores/users.tsweb/src/stores/escalations.tsweb/src/stores/artifacts.tsweb/src/stores/projects.tsweb/src/stores/providers/crud-actions.tsweb/src/stores/fine-tuning.tsweb/src/stores/setup-wizard/providers.tsweb/src/stores/setup-wizard/types.tsweb/src/stores/setup-wizard/completion.tsweb/src/stores/sinks.tsweb/src/stores/websocket.tsweb/src/stores/setup-wizard/navigation.tsweb/src/stores/company.ts
web/src/{api/endpoints,stores}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
List endpoints must use opaque cursor-based paging via
PaginationMeta. Stores keepnextCursor+hasMorein state (not offset arithmetic) and early-return when!hasMore || !nextCursor. Display counts come fromdata.length; the wire envelope no longer carriestotal.
Files:
web/src/stores/providers/types.tsweb/src/stores/subworkflows.tsweb/src/stores/workflows.tsweb/src/stores/users.tsweb/src/stores/escalations.tsweb/src/stores/artifacts.tsweb/src/stores/projects.tsweb/src/api/endpoints/setup.tsweb/src/stores/providers/crud-actions.tsweb/src/stores/fine-tuning.tsweb/src/stores/setup-wizard/providers.tsweb/src/stores/setup-wizard/types.tsweb/src/api/endpoints/audit.tsweb/src/stores/setup-wizard/completion.tsweb/src/stores/sinks.tsweb/src/api/endpoints/personalities.tsweb/src/stores/websocket.tsweb/src/stores/setup-wizard/navigation.tsweb/src/stores/company.ts
web/src/api/types/*.gen.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
NEVER hand-edit
web/src/api/types/*.gen.ts. Regenerate withuv run python scripts/generate_dto_types_ts.py; drift enforced at pre-push byscripts/check_dto_types_ts_in_sync.py.
Files:
web/src/api/types/openapi.gen.ts
web/src/components/**/*.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
ALWAYS reuse existing components from
web/src/components/ui/before creating new ones.Base UI primitives are imported directly from
@base-ui/react/<subpath>and use the nativerenderprop for polymorphism; the local<Slot>helper is reserved for<Button asChild>only.The Props interface name is
<ComponentName>Propsand must be exported from the same file (e.g.,AgentCardPropsinagent-card.tsx). This makes the contract greppable and lets callers extend the props without re-typing.
Files:
web/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/components/ui/list-header.tsxweb/src/components/ui/ws-connection-banner.tsxweb/src/components/ui/ws-connection-banner.stories.tsx
web/src/components/ui/**/*.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
Every new shared component lives in
web/src/components/ui/with a sibling.stories.tsxcovering all states (default, hover, loading, error, empty, disabled where applicable).Base UI primitives compose Portal + Backdrop + Popup explicitly, use the
renderprop for polymorphism, and rely on animation state attributes (data-[open],data-[closed]) rather thandata-[state=open]form.
Files:
web/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/components/ui/list-header.tsxweb/src/components/ui/ws-connection-banner.tsxweb/src/components/ui/ws-connection-banner.stories.tsx
web/src/components/**/*.stories.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
Storybook 10 is ESM-only; essentials are built into core, but
@storybook/addon-docsis now separate; imports moved tostorybook/testandstorybook/actions.
Files:
web/src/components/ui/lazy-code-mirror-editor.stories.tsxweb/src/components/ui/ws-connection-banner.stories.tsx
web/src/mocks/handlers/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/mocks/handlers/must mirrorweb/src/api/endpoints/*.ts1:1 with a default happy-path MSW handler for every exported endpoint.Use typed envelope helpers (
successFor,paginatedFor,voidSuccess) in MSW handlers to keep handlers in lockstep with endpoint return types.
Files:
web/src/mocks/handlers/audit.tsweb/src/mocks/handlers/setup.tsweb/src/mocks/handlers/index.tsweb/src/mocks/handlers/personalities.ts
web/src/api/endpoints/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
The
getLiveness()endpoint is always 200 while the process is alive;getReadiness()is 200 healthy / 503 unavailable (binary'ok' | 'unavailable'outcome).
Files:
web/src/api/endpoints/setup.tsweb/src/api/endpoints/audit.tsweb/src/api/endpoints/personalities.ts
web/src/stores/**/*-actions.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
All store mutation actions (create / update / delete) follow the
stores/connections/crud-actions.tspattern: wrap the API call intry/catch, success path updates state + emits a success toast, failure path logs + emits an error toast + returns a sentinel on failure.Use
getCrudErrorTitle(err, fallback)from@/utils/errorson every error toast so duplicate-resource / version-conflict / generic-conflict 409s get distinct titles.
Files:
web/src/stores/providers/crud-actions.ts
{README.md,docs/**/*.md}
📄 CodeRabbit inference engine (CLAUDE.md)
Numerics in README and public docs sourced from
data/runtime_stats.yamlvia<!--RS:NAME-->markers per data/README.md
Files:
docs/design/page-structure.mddocs/reference/web-design-system.md
web/src/utils/constants.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
The client-server WebSocket contract (version, max message size, heartbeat interval, pong timeout, sanitize max length) lives in
web/src/utils/constants.tsand MUST stay in lockstep withsrc/synthorg/api/ws_models.py/src/synthorg/api/controllers/ws.py.
Files:
web/src/utils/constants.ts
src/synthorg/!(persistence)/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Only
src/synthorg/persistence/may import sqlite/psycopg or emit raw SQL per persistence-boundary.md
Files:
src/synthorg/settings/definitions/api.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/setup_agents.pysrc/synthorg/api/controllers/setup/agent_helpers.pysrc/synthorg/api/controllers/events.py
src/synthorg/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Configuration precedence: DB > env > code default via
SettingsService/ConfigResolver(Cat-1) or env > code default (Cat-2,read_only_post_init); Cat-3 bootstrap secrets are pure env; pre-init Cat-2 reads usesettings.bootstrap_resolver.resolve_init_value; noos.environ.getoutside startup per configuration-precedence.mdNo hardcoded numeric values; numerics live in
settings/definitions/; allowlist only 0/1/-1, HTTP codes, hex masks, powers-of-2, and module-level annotated named constants (Final types); enforced byscripts/check_no_magic_numbers.pyComments explain WHY only; no reviewer citations, issue back-refs, or migration framing; enforced by
check_no_review_origin_in_code.pyandcheck_no_migration_framing.pyNo
from __future__ import annotations(Python 3.14 has PEP 649); use PEP 758 style exceptexcept A, B:requires parens when bindingType hints required on public functions; mypy strict mode; use Google-style docstrings; 88 char line length; functions <50 lines; files <800 lines
Errors use
<Domain><Condition>Errorpattern inheriting fromDomainError; never inherit Exception/RuntimeError directly; enforced bycheck_domain_error_hierarchy.pyPydantic v2 frozen +
extra='forbid'on API DTOs (Request/Response/Snapshot/Result/Envelope/Status/Info/Summary suffixes); use@computed_fieldfor derived; useNotBlankStrfor identifiersArgs models at every system boundary;
parse_typed()for every external dict ingestion; enforced bycheck_boundary_typed.pyUse immutability patterns:
model_copy(update=...)orcopy.deepcopy(); deepcopy at system boundariesAsync code uses
asyncio.TaskGroupfor fan-out/fan-in; helpers catchExceptionand re-raiseMemoryError/RecursionErrorClock seam pattern: inject
clock: Clock | None = None; tests injectFakeClock; lifecycle services own_lifecycle_lock; timed-out stops mark unrestartableUntrusted content (SEC-1): use
wrap_untrusted()fromengine.prompt_safety; use `H...
Files:
src/synthorg/settings/definitions/api.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/setup_agents.pysrc/synthorg/api/controllers/setup/agent_helpers.pysrc/synthorg/api/controllers/events.py
+(src|tests)/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Vendor-agnostic: NEVER use real vendor names in project code/tests; use
example-provider,test-provider,example-{large,medium,small}-001; allowed in.claude/, third-party imports,providers/presets.py,web/public/provider-logos/
Files:
src/synthorg/settings/definitions/api.pytests/unit/api/controllers/test_setup_dtos.pytests/unit/api/controllers/test_sse_revalidate.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/setup_agents.pysrc/synthorg/api/controllers/setup/agent_helpers.pytests/unit/api/controllers/test_setup.pysrc/synthorg/api/controllers/events.py
src/**/*.py
⚙️ CodeRabbit configuration file
This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.
Files:
src/synthorg/settings/definitions/api.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/setup_agents.pysrc/synthorg/api/controllers/setup/agent_helpers.pysrc/synthorg/api/controllers/events.py
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Test markers: use
@pytest.mark.{unit,integration,e2e,slow}; async auto; 30s global timeout; 80% coverage minimumWindows: unit tests use
WindowsSelectorEventLoopPolicy; subprocess tests override backTest doubles: use ladder from conventions.md;
FakeClockfor Clock seam,mock_of[T](**overrides)for typed-boundary substitutions,SimpleNamespacefor attribute-bags; bareMagicMockat typed boundaries is blocked bycheck_mock_spec.pyFakeClock and
mock_ofimported fromtests._shared; inject viaclock=parameter and helper's spec subscriptHypothesis: 10 deterministic CI examples; failures are real bugs (fix + add
@example(...))Flaky tests: NEVER skip/xfail; fix fundamentally; use
asyncio.Event().wait()notsleep(large)
Files:
tests/unit/api/controllers/test_setup_dtos.pytests/unit/api/controllers/test_sse_revalidate.pytests/unit/api/controllers/test_setup.py
⚙️ CodeRabbit configuration file
Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare
@settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which@given() honors automatically.
Files:
tests/unit/api/controllers/test_setup_dtos.pytests/unit/api/controllers/test_sse_revalidate.pytests/unit/api/controllers/test_setup.py
src/synthorg/api/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
API startup lifecycle: construction phase (
create_appbody) wires synchronous services;on_startupphase wires services needing persistence backend; invariants:agent_registrybeforeauto_wire_meetings;tunnel_provideralways wired; on-startup:SettingsServicebeforeWorkflowExecutionObserver;OntologyServiceafterpersistence.connect()
Files:
src/synthorg/api/controllers/setup_models.pysrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/setup_agents.pysrc/synthorg/api/controllers/setup/agent_helpers.pysrc/synthorg/api/controllers/events.py
src/synthorg/api/controllers/setup/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Setup completion:
post_setup_reinit()propagates failures;settings_svc.set('api', 'setup_complete', 'true')only runs if reinit succeeds; entire sequence serialised underCOMPLETE_LOCKto prevent race conditions
Files:
src/synthorg/api/controllers/setup/agent_helpers.py
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:58:40.031Z
Learning: Read `docs/design/` page before implementing; deviations need approval per DESIGN_SPEC.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:58:40.031Z
Learning: Present every plan for accept/deny before coding
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:58:40.031Z
Learning: No region/currency/locale privileged; use metric units; use British English per regional-defaults.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:58:40.031Z
Learning: Every convention PR ships its enforcement gate per convention-gates.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:58:40.031Z
Learning: Timeout/slow test failures indicate source-code regression; never edit `tests/baselines/unit_timing.json`, `scripts/*_baseline.{txt,json}`, or `scripts/_*_baseline.py`; both families are PreToolUse-blocked; bypass via `ALLOW_BASELINE_GROWTH=1 git commit ...`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:58:40.031Z
Learning: After implementation: branch + commit + push (no auto-PR); use `/pre-pr-review`; after PR use `/aurelio-review-pr` for external feedback; fix everything valid; no deferring
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:58:40.031Z
Learning: xdist: `-n 8 --dist=loadfile` auto-applied via pyproject `addopts`; loadfile prevents 3.14+ Windows ProactorEventLoop leak
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:58:40.031Z
Learning: Git commits: `<type>: <description>` (feat/fix/refactor/docs/test/chore/perf/ci); commitizen-enforced
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:58:40.031Z
Learning: Signed commits required on protected refs (GPG/SSH or GitHub App via `synthorg-repo-bot`)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:58:40.031Z
Learning: Branches: `<type>/<slug>` from main
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:58:40.031Z
Learning: Pre-commit/pre-push hooks via `.pre-commit-config.yaml`; hookify rules in `.claude/hookify.*.md`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:58:40.031Z
Learning: Squash merge; PR body becomes squash commit; trailers (`Release-As`, `Closes `#N``) must be in PR body
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:58:40.031Z
Learning: GitHub queries: use `gh issue list` via Bash, NOT MCP `list_issues`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:58:40.031Z
Learning: After every squash merge: run `/post-merge-cleanup`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:58:40.031Z
Learning: CLI is Docker-only (init/start/stop/status); features go in dashboard + REST API; see `cli/CLAUDE.md`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:58:40.031Z
Learning: Web: see `web/CLAUDE.md` for framework and component guidelines; Shell: see `~/.claude/rules/common/bash.md` for bash conventions and `cd` / `git -C` rules; use `go -C cli` never `cd cli`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:59:30.241Z
Learning: Bump the WebSocket protocol version (`WS_PROTOCOL_VERSION`) on both client and server together for breaking payload changes. Drift is enforced at pre-commit / pre-push by `scripts/check_ws_protocol_version_in_sync.py`.
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:59:30.241Z
Learning: Lint runs via `npm --prefix web run lint` with `--max-warnings 0`. Zero warnings are enforced.
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:59:30.241Z
Learning: To enumerate stale `eslint-disable` directives after a rule reshuffle: `npm --prefix web run lint -- --report-unused-disable-directives-severity=warn`.
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T18:59:30.241Z
Learning: Minimum versions: Node 20.19+, Vite 5+, Vitest 3+.
📚 Learning: 2026-05-16T18:36:31.446Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/reference/conventions.md:787-789
Timestamp: 2026-05-16T18:36:31.446Z
Learning: In Aureliolo/synthorg, do not require adding `<!--RS:...-->` “Doc Numeric Claims (MANDATORY)” numeric macros for Python version numbers mentioned in documentation prose (e.g., “Python 3.14”, “Python 3.15”). The `scripts/check_doc_numeric_macros.py` gate only applies to `README.md`, `docs/index.md`, `docs/roadmap/index.md`, `docs/architecture/decisions.md`, and `docs/reference/convention-gates.md`, and it only flags digits adjacent to specific stat nouns (tests/providers/agents/stars/releases), not language version mentions like “Python 3.14”.
Applied to files:
CLAUDE.mddocs/design/page-structure.mdweb/CLAUDE.mddocs/reference/web-design-system.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing Markdown in the synthorg repo, account for the CI gate `check_doc_numeric_macros.py`: it skips fenced code blocks entirely, and it only flags digits that are adjacent to these stat nouns: `tests`, `providers`, `agents`, `stars`, `releases`. Therefore, numeric examples such as CLI flag values (e.g., `--num-workers=4` in fenced bash blocks) and prose version numbers (e.g., `3.14`/`3.15`) are not expected to trigger this check; prioritize changes only when digits appear next to one of the listed nouns (e.g., “5 tests”, “10 stars”, etc.).
Applied to files:
CLAUDE.mddocs/design/page-structure.mdweb/CLAUDE.mddocs/reference/web-design-system.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing markdown files for the "Doc Numeric Claims (MANDATORY)" RS-marker rule, only require/flag missing RS markers in the files that are actually in-scope for the rule. The scope is enforced via an identical _SCOPED_FILES allowlist in scripts/check_doc_numeric_macros.py and scripts/inject_runtime_stats.py, and currently includes: README.md; docs/index.md; docs/roadmap/index.md; docs/architecture/decisions.md; docs/reference/convention-gates.md. For any other markdown files (e.g., docs/getting_started.md, docs/guides/*), missing RS markers for numeric claims are no-ops and should NOT be flagged.
Applied to files:
docs/design/page-structure.mddocs/reference/web-design-system.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing Markdown in the synthorg repo against the `check_doc_numeric_macros.py` gate, account for its documented behavior: it skips fenced code blocks entirely, and it only flags digits that are adjacent to specific stat nouns (`tests`, `providers`, `agents`, `stars`, `releases`). As a result, CLI-style numbers (e.g., `--num-workers=4`) inside fenced bash code blocks should never be treated as violations of this gate; only non-fenced text needs checking, and only around those specific nouns.
Applied to files:
docs/design/page-structure.mddocs/reference/web-design-system.md
📚 Learning: 2026-05-05T09:04:46.195Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1760
File: scripts/_dual_backend_parity_lib.py:215-216
Timestamp: 2026-05-05T09:04:46.195Z
Learning: This repository targets Python 3.14+ and follows PEP 758. Therefore, reviewer tooling should NOT treat unparenthesized multi-exception `except` clauses written without an `as` clause (e.g., `except MemoryError, RecursionError:`) as syntax errors. Only flag `except`-clause problems when they are genuinely invalid for Python 3.14+.
Applied to files:
src/synthorg/settings/definitions/api.pytests/unit/api/controllers/test_setup_dtos.pytests/unit/api/controllers/test_sse_revalidate.pysrc/synthorg/api/controllers/setup_models.pysrc/synthorg/api/controllers/setup_controller.pysrc/synthorg/api/controllers/setup_agents.pysrc/synthorg/api/controllers/setup/agent_helpers.pytests/unit/api/controllers/test_setup.pysrc/synthorg/api/controllers/events.py
Skipped 2 (factually wrong / reviewer-optional, logged with disproof): - CompleteStep try/catch: store completeSetup intentionally re-throws so SkipWizardForm can render its partial-success message; CR caller-only fix would regress one of two consumers - company.ts reorder success toasts: CR self-classified Low value and endorsed current silent-success drag-drop UX Frontend: - DTO barrel imports: personalities endpoint, mocks/audit, PersonalitiesAdminPage - AgentCreateDialog/AgentEditDrawer/DepartmentEditDrawer: try/finally so submitting/deleting always resets on reject - useOrgChartDragDrop: drop caller-side .catch (store owns error UX via sentinel-return); void-prefix the fire-and-forget chain - PersonalitiesAdminPage: monotonic refreshIdRef guard so a slow stale refresh cannot clobber a newer result - providersStore.bulkDeleteProviders now returns a boolean sentinel (true all-ok, false on any failure) per store mutation contract Docs: - web-design-system.md: WsConnectionBanner description now matches the grace-window behaviour (surfaces after the window even if never connected)
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@web/src/stores/providers/crud-actions.ts`:
- Around line 201-243: The all-failed bulk-delete toast currently uses a plain
fallback title; change it to use getCrudErrorTitle so 409 conflict titles are
preserved: introduce a variable (e.g., lastErr: unknown) in the scope of
bulkDeleteProviders and assign it inside the per-name catch blocks (alongside
failed.push(name) and the log.error call), then in the branch that shows the
error toast (the else where no deletions succeeded) call
useToastStore.getState().add with title: getCrudErrorTitle(lastErr, `Failed to
delete ${failed.length} provider${failed.length === 1 ? '' : 's'}`) instead of
the current plain string; keep existing sanitizeForLog/getErrorMessage usage in
the per-name catch logging.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 134301ea-1148-43a7-ae2e-22297f38e699
📒 Files selected for processing (10)
docs/reference/web-design-system.mdweb/src/api/endpoints/personalities.tsweb/src/mocks/handlers/audit.tsweb/src/pages/PersonalitiesAdminPage.tsxweb/src/pages/org-edit/AgentCreateDialog.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/stores/providers/crud-actions.tsweb/src/stores/providers/types.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Lighthouse Site
- GitHub Check: Lighthouse Dashboard
- GitHub Check: Build Web Assets (melange)
- GitHub Check: Dashboard Test
- GitHub Check: Test (Python 3.14)
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (10)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Reuse
web/src/components/ui/design tokens in web dashboard; detail inweb/CLAUDE.mdAlways name logger instances as
log(e.g.,const log = createLogger('module-name'))Callers MUST NOT wrap store mutation calls in try/catch; the store owns the error UX
Display counts must come from
data.length, not from atotalfield in the wire envelopeUse
useViewportSize()from@/hooks/useViewportSizefor viewport-size reads; NEVER readwindow.innerWidth/window.innerHeightdirectly in render bodies oruseMemoUse
@typescript-eslint/no-floating-promisesto forbid unawaited promises so async work cannot survive the test and trip the active-handle gateUse
@typescript-eslint/no-misused-promises(withchecksVoidReturn: { attributes: false }) to forbid passing async functions where callsite ignores the promise; React 19asyncevent handlers stay allowed
Files:
web/src/stores/providers/types.tsweb/src/pages/org/useOrgChartDragDrop.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/mocks/handlers/audit.tsweb/src/pages/org-edit/AgentCreateDialog.tsxweb/src/pages/PersonalitiesAdminPage.tsxweb/src/stores/providers/crud-actions.tsweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/api/endpoints/personalities.ts
web/src/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Use
createLoggerfrom@/lib/loggerfor all logging; never use bareconsole.warn,console.error, orconsole.debugin application codePass dynamic/untrusted values as separate arguments to logger, not interpolated into the message string, so they go through
sanitizeArgWrap attacker-controlled fields inside structured objects in
sanitizeForLog()before embedding in logsUse
@eslint-react/web-api-no-leaked-fetchto detectfetch()in effects withoutAbortControllercleanup
Files:
web/src/stores/providers/types.tsweb/src/pages/org/useOrgChartDragDrop.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/mocks/handlers/audit.tsweb/src/pages/org-edit/AgentCreateDialog.tsxweb/src/pages/PersonalitiesAdminPage.tsxweb/src/stores/providers/crud-actions.tsweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/api/endpoints/personalities.ts
web/src/stores/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
All store mutation actions (create/update/delete) must wrap API calls in try/catch, emit success toast on success path, and emit error toast on failure path following the pattern in
stores/connections/crud-actions.tsStore mutation actions must return a sentinel on failure:
nullfor entity-returning mutations,falsefor boolean-returning mutationsUse
getCrudErrorTitle(err, fallback)from@/utils/errorson every error toast for distinct titles on 409 conflictsList reads (
fetch*) must seterror: string | nullon the store instead of toastingAny new store that schedules timers or attaches event listeners must expose an equivalent cleanup hook and register it in the global
afterEachinweb/src/test-setup.tsx
sanitizeWsEnum<T>(value, allowlist, fallback, { field })validates unknown WS enum values against an allowlist, emits a structuredws.enum.unknownwarning, and returns the supplied fallback
Files:
web/src/stores/providers/types.tsweb/src/stores/providers/crud-actions.ts
web/src/{stores,api}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
List endpoints use cursor-based pagination via
PaginationMeta; stores must keepnextCursor+hasMorein state and early-return when!hasMore || !nextCursor
Files:
web/src/stores/providers/types.tsweb/src/stores/providers/crud-actions.tsweb/src/api/endpoints/personalities.ts
web/src/{stores,components}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Any new caller of health/readiness endpoints must handle the 503 path explicitly
Files:
web/src/stores/providers/types.tsweb/src/stores/providers/crud-actions.ts
web/src/{stores,utils}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
sanitizeWsString()andsanitizeWsEnum()must be used for all WS-supplied strings and enums; raw unsafe casts are forbidden
sanitizeWsString()clamps every WS-supplied string: strips C0 controls + bidi-overrides + caps length
Files:
web/src/stores/providers/types.tsweb/src/stores/providers/crud-actions.ts
web/src/{stores,components,api}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Import
ErrorCodeandErrorCategoryfrom@/api/types/errorsand discriminate onErrorCode.<NAME>, never on raw integer literalsImport DTOs via the barrel import:
import type { AgentConfig } from '@/api/types'
Files:
web/src/stores/providers/types.tsweb/src/stores/providers/crud-actions.tsweb/src/api/endpoints/personalities.ts
docs/**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Numerics in README and public docs sourced from
data/runtime_stats.yamlvia<!--RS:NAME-->markers per data/README.md
Files:
docs/reference/web-design-system.md
web/src/mocks/handlers/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
MSW handlers in
web/src/mocks/handlers/must mirrorweb/src/api/endpoints/*.ts1:1 with a default happy-path handler for every exported endpointUse typed envelope helpers (
successFor,paginatedFor,voidSuccess) in MSW handlers to keep handlers in lockstep with endpoint return types
Files:
web/src/mocks/handlers/audit.ts
web/src/api/endpoints/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
getLiveness()is always 200 while the process is alive;getReadiness()is 200 healthy / 503 unavailable with binary'ok' | 'unavailable'outcome
Files:
web/src/api/endpoints/personalities.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:21:52.258Z
Learning: Read `docs/design/` page before implementing; deviations need approval per DESIGN_SPEC.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:21:52.258Z
Learning: Present every plan for accept/deny before coding
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:21:52.258Z
Learning: No region/currency/locale privileged; use metric units and British English per docs/reference/regional-defaults.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:21:52.258Z
Learning: Every convention PR ships its enforcement gate per docs/reference/convention-gates.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:21:52.258Z
Learning: Timeout/slow test failures are source-code regressions; never edit `tests/baselines/unit_timing.json` or any `scripts/*_baseline.{txt,json}` / `scripts/_*_baseline.py`; use `ALLOW_BASELINE_GROWTH=1` for gate baseline bypass with explicit user approval
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:21:52.258Z
Learning: After issue implementation: branch + commit + push (no auto-PR); use `/pre-pr-review`; after PR merge use `/aurelio-review-pr` for external feedback; fix everything valid, no deferring
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:21:52.258Z
Learning: xdist `-n 8 --dist=loadfile` auto-applied via pyproject addopts (loadfile prevents 3.14+ Windows ProactorEventLoop leak)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:21:52.258Z
Learning: Commits: `<type>: <description>` (feat/fix/refactor/docs/test/chore/perf/ci); commitizen-enforced
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:21:52.258Z
Learning: Signed commits required on protected refs (GPG/SSH or GitHub App via `synthorg-repo-bot`)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:21:52.258Z
Learning: Branches: `<type>/<slug>` from main
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:21:52.258Z
Learning: Pre-commit/pre-push hooks: `.pre-commit-config.yaml`; hookify rules: `.claude/hookify.*.md`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:21:52.258Z
Learning: Squash merge; PR body becomes squash commit; trailers (`Release-As`, `Closes `#N``) must be in PR body
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:21:52.258Z
Learning: GitHub queries: use `gh issue list` via Bash, NOT MCP `list_issues`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:21:52.258Z
Learning: After every squash merge run `/post-merge-cleanup`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:21:52.258Z
Learning: CLI is Docker-only (init/start/stop/status); features go in dashboard + REST API
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:22:33.135Z
Learning: ESLint runs with `--max-warnings 0` enforcing zero warnings; use `--report-unused-disable-directives-severity=warn` to enumerate stale `eslint-disable` directives
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:22:33.135Z
Learning: CSP nonces flow via `<CSPProvider>` + `<MotionConfig nonce>` in `App.tsx`; see `docs/security.md` for details
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:22:33.135Z
Learning: Bundle-size budgets in `web/.size-limit.cjs` are raised only when a feature legitimately requires more shipping JS, never to silence a CI red
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:22:33.135Z
Learning: Vitest coverage is scoped to files changed vs origin/main by default; use `--coverage` for full suite matching CI
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:22:33.135Z
Learning: The active-handle allowlist in `web/test-infra/active-handle-allowlist.ts` is empty; additions require an audit step per docs/design/web-active-handle-detection.md
📚 Learning: 2026-05-16T18:36:31.446Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/reference/conventions.md:787-789
Timestamp: 2026-05-16T18:36:31.446Z
Learning: In Aureliolo/synthorg, do not require adding `<!--RS:...-->` “Doc Numeric Claims (MANDATORY)” numeric macros for Python version numbers mentioned in documentation prose (e.g., “Python 3.14”, “Python 3.15”). The `scripts/check_doc_numeric_macros.py` gate only applies to `README.md`, `docs/index.md`, `docs/roadmap/index.md`, `docs/architecture/decisions.md`, and `docs/reference/convention-gates.md`, and it only flags digits adjacent to specific stat nouns (tests/providers/agents/stars/releases), not language version mentions like “Python 3.14”.
Applied to files:
docs/reference/web-design-system.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing Markdown in the synthorg repo, account for the CI gate `check_doc_numeric_macros.py`: it skips fenced code blocks entirely, and it only flags digits that are adjacent to these stat nouns: `tests`, `providers`, `agents`, `stars`, `releases`. Therefore, numeric examples such as CLI flag values (e.g., `--num-workers=4` in fenced bash blocks) and prose version numbers (e.g., `3.14`/`3.15`) are not expected to trigger this check; prioritize changes only when digits appear next to one of the listed nouns (e.g., “5 tests”, “10 stars”, etc.).
Applied to files:
docs/reference/web-design-system.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing markdown files for the "Doc Numeric Claims (MANDATORY)" RS-marker rule, only require/flag missing RS markers in the files that are actually in-scope for the rule. The scope is enforced via an identical _SCOPED_FILES allowlist in scripts/check_doc_numeric_macros.py and scripts/inject_runtime_stats.py, and currently includes: README.md; docs/index.md; docs/roadmap/index.md; docs/architecture/decisions.md; docs/reference/convention-gates.md. For any other markdown files (e.g., docs/getting_started.md, docs/guides/*), missing RS markers for numeric claims are no-ops and should NOT be flagged.
Applied to files:
docs/reference/web-design-system.md
📚 Learning: 2026-05-16T18:36:35.250Z
Learnt from: Aureliolo
Repo: Aureliolo/synthorg PR: 1944
File: docs/getting_started.md:109-109
Timestamp: 2026-05-16T18:36:35.250Z
Learning: When reviewing Markdown in the synthorg repo against the `check_doc_numeric_macros.py` gate, account for its documented behavior: it skips fenced code blocks entirely, and it only flags digits that are adjacent to specific stat nouns (`tests`, `providers`, `agents`, `stars`, `releases`). As a result, CLI-style numbers (e.g., `--num-workers=4`) inside fenced bash code blocks should never be treated as violations of this gate; only non-fenced text needs checking, and only around those specific nouns.
Applied to files:
docs/reference/web-design-system.md
🔇 Additional comments (9)
web/src/stores/providers/types.ts (1)
94-94: LGTM!docs/reference/web-design-system.md (1)
93-93: LGTM!web/src/pages/org/useOrgChartDragDrop.ts (1)
110-145: LGTM!web/src/pages/org-edit/AgentEditDrawer.tsx (1)
20-21: LGTM!Also applies to: 86-95, 101-109
web/src/mocks/handlers/audit.ts (1)
1-45: LGTM!web/src/pages/org-edit/AgentCreateDialog.tsx (1)
16-16: LGTM!Also applies to: 62-79
web/src/pages/PersonalitiesAdminPage.tsx (1)
11-12: LGTM!Also applies to: 27-38, 55-100, 146-211, 215-365
web/src/pages/org-edit/DepartmentEditDrawer.tsx (1)
26-31: LGTM!Also applies to: 81-99, 101-115
web/src/api/endpoints/personalities.ts (1)
1-89: LGTM!
CodeRabbit (1 actionable; all round-3 fixes got LGTM): - providers crud-actions: all-failed bulkDeleteProviders error toast now routes through getCrudErrorTitle (first per-name error threaded in) so 409 conflicts keep distinct titles per the store-mutation contract User-directed (overrides round-3 skip of CompleteStep try/catch): - setup-wizard completion store: completeSetup no longer re-throws; it sets completionError and returns (store owns error UX) - CompleteStep: drop caller try/catch, branch off completionError / completionWarning after the await - SkipWizardForm: drop the throw-dependent catch; read completionError from store state for the partial-success message (try/finally kept only for the loading-flag reset, no catch) - setup-wizard.test: the three completeSetup failure cases now assert completionError is set without a thrown rejection web 3176 tests pass; type-check + lint clean
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@web/src/pages/setup/SkipWizardForm.tsx`:
- Around line 45-63: The code currently checks only completionError after
calling wizardCompleteSetup(); add a branch to inspect
useSetupWizardStore.getState().completionWarning before marking setup complete
or navigating: if completionWarning is non-null, surface it via setError or the
existing warning flow (so the user must acknowledge) and do not call
useSetupStore.setState({ setupComplete: true }) or navigate('/'); retain the
existing completionError handling path (completionError !== null) and only mark
success and toast+navigate when both completionError and completionWarning are
null.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: cacedff3-5790-4646-b5e7-b326d6da3f93
📒 Files selected for processing (5)
web/src/__tests__/stores/setup-wizard.test.tsweb/src/pages/setup/CompleteStep.tsxweb/src/pages/setup/SkipWizardForm.tsxweb/src/stores/providers/crud-actions.tsweb/src/stores/setup-wizard/completion.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: Lighthouse Site
- GitHub Check: Lighthouse Dashboard
- GitHub Check: Build Web Assets (melange)
- GitHub Check: Dashboard Test
- GitHub Check: Test (Python 3.14)
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
web/src/**/*.{tsx,ts,jsx,js}
📄 CodeRabbit inference engine (CLAUDE.md)
Reuse
web/src/components/ui/design tokens in Web Dashboard; detail inweb/CLAUDE.md
Files:
web/src/stores/providers/crud-actions.tsweb/src/pages/setup/SkipWizardForm.tsxweb/src/pages/setup/CompleteStep.tsxweb/src/stores/setup-wizard/completion.tsweb/src/__tests__/stores/setup-wizard.test.ts
web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Always use
createLoggerfrom@/lib/logger; never use bareconsole.warn,console.error, orconsole.debugin application codeUse camelCase for variable name: always
logwhen creating a logger instance (e.g.,const log = createLogger('module-name'))Pass dynamic or untrusted values as separate arguments to logger methods (not interpolated into the message string) so they go through
sanitizeArgWrap attacker-controlled fields inside structured objects with
sanitizeForLog()before embedding in logs
Files:
web/src/stores/providers/crud-actions.tsweb/src/pages/setup/SkipWizardForm.tsxweb/src/pages/setup/CompleteStep.tsxweb/src/stores/setup-wizard/completion.tsweb/src/__tests__/stores/setup-wizard.test.ts
web/src/stores/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
All store mutation actions (create/update/delete) must follow the
stores/connections/crud-actions.tspattern: wrap API calls intry/catch, success path updates state and emits success toast, failure path logs and emits error toast and returns a sentinel on failureSentinel shapes for mutations mirror the mutation's return type:
nullfor entity-returning mutations,falsefor boolean-returning mutations; void/boolean-returning mutations usefalseregardless of operation typeUse
getCrudErrorTitle(err, fallback)from@/utils/errorson every error toast to distinguish between duplicate-resource, version-conflict, generic-conflict 409s, and other error typesList reads (
fetch*) must seterror: string | nullon the store instead of toastingCursor pagination (MANDATORY): list endpoints must use opaque cursor-based paging via
PaginationMeta. Stores must keepnextCursorandhasMorein state (not offset arithmetic) and early-return when!hasMore || !nextCursorAny new store that schedules timers or attaches event listeners must expose an equivalent cleanup hook and register it in the global
afterEachdefined inweb/src/test-setup.tsxWS payload string values must be sanitized via
sanitizeWsString()which clamps every WS-supplied string (strips C0 controls + bidi-overrides + caps length)WS enum values must be validated via
sanitizeWsEnum<T>(value, allowlist, fallback, { field })which emits a structuredws.enum.unknownwarning on unknown values and returns the supplied fallbackAny new WS payload handler ingesting untrusted strings MUST route through
sanitizeWsString()orsanitizeWsEnum(); raw casts like(sanitizeWsString(x, n) ?? '') as EnumTypeare forbidden
Files:
web/src/stores/providers/crud-actions.tsweb/src/stores/setup-wizard/completion.ts
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Callers MUST NOT wrap store mutation calls in
try/catch; the store owns the error UXDisplay counts for paginated lists come from
data.length; the wire envelope no longer carriestotal
getLiveness()is always 200 while the process is alive;getReadiness()is 200 healthy / 503 unavailable (binary'ok' | 'unavailable'outcome, no tri-state); any new caller must handle the 503 path explicitlyImport
ErrorCodeandErrorCategoryfrom@/api/types/errors(re-exported fromweb/src/api/types/error-codes.gen.ts); discriminate onErrorCode.<NAME>, never on raw integer literalsNEVER hardcode hex colors, font-family declarations, pixel spacing, Motion transition durations, BCP 47 locale literals, or currency symbols/codes; use design tokens,
@/lib/motionpresets,@/utils/formathelpers, andDEFAULT_CURRENCY
@typescript-eslint/no-floating-promisesforbids unawaited promises so async work cannot survive the test that scheduled it; all promises must be awaited
@typescript-eslint/no-misused-promiseswithchecksVoidReturn: { attributes: false }forbids passing async functions where callsites ignore the returned promise; React 19asyncevent handlers stay allowed viaattributes: falseexemption
Files:
web/src/stores/providers/crud-actions.tsweb/src/pages/setup/SkipWizardForm.tsxweb/src/pages/setup/CompleteStep.tsxweb/src/stores/setup-wizard/completion.tsweb/src/__tests__/stores/setup-wizard.test.ts
web/src/**/*.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
Use
useViewportSize()from@/hooks/useViewportSizefor reading viewport size; NEVER readwindow.innerWidth/window.innerHeightdirectly in render bodies oruseMemoFor
ReactNode | undefinedprops use{value != null && value !== false && <jsx>}; for compound truthiness useBoolean(...); never use the bug pattern{count && <Foo />}which renders0verbatim
Files:
web/src/pages/setup/SkipWizardForm.tsxweb/src/pages/setup/CompleteStep.tsx
web/src/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
test-setup.tsxboots MSW withonUnhandledRequest: 'error'; tests override per-case viaserver.use(...), nevervi.mock('@/api/endpoints/*')
Files:
web/src/__tests__/stores/setup-wizard.test.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:41:19.357Z
Learning: Read design specification from `docs/design/` page before implementing; deviations require approval per DESIGN_SPEC.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:41:19.357Z
Learning: Present every plan for accept/deny before coding
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:41:19.357Z
Learning: No region/locale privileged; use metric units; use British English per docs/reference/regional-defaults.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:41:19.357Z
Learning: Every convention PR ships its enforcement gate per docs/reference/convention-gates.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:41:19.357Z
Learning: Post-implementation + pre-PR: branch + commit + push (no auto-PR); use `/pre-pr-review`. After PR: `/aurelio-review-pr` for external feedback. Fix EVERYTHING valid; no deferring
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:41:19.357Z
Learning: Git commits: `<type>: <description>` (feat/fix/refactor/docs/test/chore/perf/ci); commitizen-enforced. Signed commits required on protected refs (GPG/SSH or GitHub App via `synthorg-repo-bot`)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:41:19.357Z
Learning: Git branches: `<type>/<slug>` from main
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:41:19.357Z
Learning: Squash merge. PR body becomes squash commit; trailers (`Release-As`, `Closes `#N``) must be in PR body
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:41:19.357Z
Learning: After every squash merge → `/post-merge-cleanup`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:41:19.357Z
Learning: CLI is Docker-only (init/start/stop/status); features go in dashboard + REST API
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:41:19.357Z
Learning: Web uses React 19; see `web/CLAUDE.md` for detailed guidelines. CLI uses Go binary; see `cli/CLAUDE.md` for detailed guidelines. Use `go -C cli`, never `cd cli`. Shell rules in `~/.claude/rules/common/bash.md`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:42:01.861Z
Learning: Every unit test runs under `web/test-infra/active-handle-tracker.ts` which fails any test that leaks event-loop-holding resources (Timeout, TCPWRAP, PIPEWRAP, FSEVENTWRAP, etc.) attributable to a `web/src/` frame; zero tolerance, no ceiling, no buffer
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:42:01.861Z
Learning: The client-server WS contract lives in `web/src/utils/constants.ts` (`WS_PROTOCOL_VERSION`, `WS_MAX_MESSAGE_SIZE`, `WS_HEARTBEAT_INTERVAL_MS`, `WS_PONG_TIMEOUT_MS`, `LOG_SANITIZE_MAX_LENGTH`) and MUST stay in lockstep with Python backend (`src/synthorg/api/ws_models.py` / `src/synthorg/api/controllers/ws.py`); bump the protocol version on both sides together
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:42:01.861Z
Learning: ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:42:01.861Z
Learning: `eslint-react/eslint-plugin` v5+ with `recommended-type-checked` preset (requires `parserOptions.projectService: true` in `web/eslint.config.js`) with explicit error-level opt-ins: `eslint-react/web-api-no-leaked-fetch`, `eslint-react/no-leaked-conditional-rendering`, `eslint-react/globals`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:42:01.861Z
Learning: Lint runs via `npm --prefix web run lint` with `--max-warnings 0` to enforce zero ESLint warnings
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:42:01.861Z
Learning: Bundle-size budgets in `web/.size-limit.cjs` are raised only when a feature legitimately requires more shipping JS, never to silence a CI red
🔇 Additional comments (4)
web/src/stores/providers/crud-actions.ts (1)
189-264: LGTM!web/src/pages/setup/CompleteStep.tsx (1)
24-67: LGTM!Also applies to: 109-143
web/src/stores/setup-wizard/completion.ts (1)
7-7: LGTM!Also applies to: 37-37, 79-79, 86-86, 90-111, 125-130
web/src/__tests__/stores/setup-wizard.test.ts (1)
79-137: LGTM!Also applies to: 1141-1233
CodeRabbit (1 actionable; round-5 bug it caught): - SkipWizardForm: handle completionWarning before the success navigate. After the round-5 store-owns-error-UX refactor the skip path checked only completionError, so a warning-only completion marked setup complete and navigated, skipping the acknowledgement flow. Bare return now lets CompleteStep re-render (companyResponse non-null) and surface the warning plus continue CTA. CI (Dashboard Test FAILURE, flaky, made deterministic): - websocket heartbeat tests advanced exactly WS_HEARTBEAT_INTERVAL_MS while the scheduler arms its timer at a jittered delay up to WS_HEARTBEAT_INTERVAL_MS times JITTER_MAX; whenever the random factor exceeded 1.0 the first tick never fired (no ping, so no pong timer, so the socket never closed and the assertion saw false). The three ping-dependent tests now advance WS_HEARTBEAT_MAX_DELAY_MS = ceil(interval times JITTER_MAX), eliminating the jitter race deterministically. retry:3 retained and re-scoped in comments to the orthogonal, documented MSW/undici macrotask race only. websocket file 32/32 across 3 consecutive runs; full web suite 3176/3176; type-check plus lint clean
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@web/src/__tests__/stores/websocket.test.ts`:
- Around line 788-793: Replace the hardcoded jitter multiplier in the test's
timer advance call with the shared constant: use WS_HEARTBEAT_MAX_DELAY_MS
instead of Math.ceil(WS_HEARTBEAT_INTERVAL_MS * 1.1). Locate the
vi.advanceTimersByTimeAsync(...) call that currently references
WS_HEARTBEAT_INTERVAL_MS and update it to advance by WS_HEARTBEAT_MAX_DELAY_MS
(keeping any existing Math.ceil or async call wrapper as appropriate) so the
test uses the canonical jitter-derived delay.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 57e2f42b-25d9-4bc8-baba-1b94b92c048e
📒 Files selected for processing (2)
web/src/__tests__/stores/websocket.test.tsweb/src/pages/setup/SkipWizardForm.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Build Web Assets (melange)
- GitHub Check: Lighthouse Site
- GitHub Check: Lighthouse Dashboard
- GitHub Check: Dashboard Test
- GitHub Check: Test (Python 3.14)
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (3)
web/**/*.{ts,tsx,jsx,js}
📄 CodeRabbit inference engine (CLAUDE.md)
React 19 web framework. See
web/CLAUDE.mdfor web-specific conventions.
Files:
web/src/pages/setup/SkipWizardForm.tsxweb/src/__tests__/stores/websocket.test.ts
web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Always use
createLoggerfrom@/lib/logger; never use bareconsole.warn,console.error, orconsole.debugin application code. Variable name must beconst log = createLogger('module-name'). Onlylogger.tsitself may use bare console methods.Use logging levels:
log.debug()(DEV-only, stripped in production),log.warn(),log.error(). Static messages must pass dynamic/untrusted values as separate args (not interpolated) so they go throughsanitizeArg. Attacker-controlled fields inside structured objects must be wrapped insanitizeForLog()before embedding.Error-code constants are mandatory: import
ErrorCodeandErrorCategoryfrom@/api/types/errors(re-exported from generatedweb/src/api/types/error-codes.gen.ts). Discriminate onErrorCode.<NAME>, never on raw integer literals. Drift is enforced at pre-push byscripts/check_error_codes_ts_in_sync.py.NEVER read
window.innerWidth/window.innerHeightdirectly in a render body oruseMemo. UseuseViewportSize()from@/hooks/useViewportSizeinstead. This is enforced byreact-x/globalsand prevents stale reads across resizes.Use
@eslint-react/eslint-pluginv5+ via therecommended-type-checkedpreset (requiresparserOptions.projectService: trueinweb/eslint.config.js). Enable error-level:@eslint-react/web-api-no-leaked-fetch,@eslint-react/no-leaked-conditional-rendering,@eslint-react/globals. For ReactNode | undefined props use{value != null && value !== false && <jsx>}; for compound truthiness useBoolean(...).Enable
@typescript-eslint/no-floating-promisesto forbid unawaited promises so async work cannot survive the test that scheduled it and trip the active-handle gate.Enable
@typescript-eslint/no-misused-promiseswithchecksVoidReturn: { attributes: false }to forbid passing async functions where the callsite ignores the promise. React 19asyncevent handlers stay allowed via theattributes: falseexemption, paired with the global e...
Files:
web/src/pages/setup/SkipWizardForm.tsxweb/src/__tests__/stores/websocket.test.ts
web/**/{stores,src/stores}/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
All Zustand store mutation actions (create/update/delete) must follow the
stores/connections/crud-actions.tspattern: wrap API calls in try/catch, success path updates state + emits success toast, failure path logs + emits error toast + returns sentinel on failure. Sentinel shape mirrors mutation's return type:nullfor entity-returning mutations,falsefor boolean-returning mutations. UsegetCrudErrorTitle(err, fallback)on every error toast. Callers MUST NOT wrap store mutation calls in try/catch.List reads (fetch*) must set
error: string | nullon the store instead of toasting.Cursor pagination is mandatory: list endpoints must use opaque cursor-based paging via
PaginationMeta. Stores must keepnextCursor+hasMorein state (not offset arithmetic) and early-return when!hasMore || !nextCursor. Display counts come fromdata.length; the wire envelope no longer carriestotal.Test teardown is mandatory:
web/src/test-setup.tsxregisters a globalafterEachthat callsuseToastStore.getState().dismissAll(),cancelPendingPersist()(notifications store), anduseThemeStore.getState().teardown(). Any new store that schedules timers or attaches event listeners must expose an equivalent cleanup hook and register it in the globalafterEach. The websocket store is a deliberate exception (file-localresetStore()in its test file).Active-handle gate is mandatory: every unit test runs under
web/test-infra/active-handle-tracker.ts, which hooks Node'sasync_hooksand fails any test that leaks an event-loop-holding resource. Zero tolerance, no ceiling, no buffer. The allowlist is empty; additions are an audit step. New stores that schedule timers/attach listeners must expose a teardown hook and register it in globalafterEach; otherwise the gate fails.WS payload sanitization is mandatory: use
sanitizeWsString()andsanitizeWsEnum()fromweb/src/utils/ws-sanitize.ts(re-exported from@/stores/notifications). `sanitizeWsString(...
Files:
web/src/__tests__/stores/websocket.test.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:58:13.597Z
Learning: Use `d2` for architecture / nested containers; `mermaid` for flowcharts / sequence / pipelines. Markdown tables for tabular data. D2 theme 200 (Dark Mauve), D2 CLI pinned to v0.7.1 in CI.
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:58:13.597Z
Learning: Commits: `<type>: <description>` (feat/fix/refactor/docs/test/chore/perf/ci); commitizen-enforced. Signed commits required on protected refs. Branches: `<type>/<slug>` from main. Squash merge; PR body becomes commit; trailers must be in PR body.
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:58:13.597Z
Learning: After every squash merge → `/post-merge-cleanup`.
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:58:13.597Z
Learning: CLI is Docker-only (init/start/stop/status); features go in dashboard + REST API.
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-16T19:58:54.283Z
Learning: Lint runs via `npm --prefix web run lint` with `--max-warnings 0`. To enumerate stale `eslint-disable` directives after a rule reshuffle: `npm --prefix web run lint -- --report-unused-disable-directives-severity=warn`.
🔇 Additional comments (2)
web/src/pages/setup/SkipWizardForm.tsx (1)
30-64: LGTM!web/src/__tests__/stores/websocket.test.ts (1)
9-23: LGTM!Also applies to: 690-760
- websocket.test.ts: the teardown-timer test still hardcoded Math.ceil(WS_HEARTBEAT_INTERVAL_MS * 1.1); reuse the canonical WS_HEARTBEAT_MAX_DELAY_MS constant added in round 7 so the jitter-derived advance does not drift if the heartbeat jitter bounds change. Test-only, no behaviour change. websocket file 32/32 across 2 runs; full web suite green; type-check plus lint clean
<!-- HIGHLIGHTS_START --> ## Highlights > _AI-generated summary (model: `openai/gpt-4.1-mini` via GitHub Models). Commit-based changelog below._ ### What you'll notice - Frontend WP-6 update with UX polish improves user interface and workflow. - Dashboard and training endpoint improvements enhance observability and dispatch behavior. - Web storybook now supports change detection for more responsive UI interactions. - Git hooks now isolated per worktree for cleaner repository management. - Providers automatically detect native streaming support in Litellm models. ### What's new - Added a new pipeline to convert Pydantic DTOs to TypeScript for better front-end compatibility. ### Under the hood - Refactored settings to three precedence categories, removing YAML tier for simpler configuration. - Completed RootConfig mirror coverage for enhanced configuration consistency. - Adopted API conventions with better query performance and forbidden extra fields for stricter validation. - Improved persistence, layer discipline, and restart safety in core work packages. - CI updated with split test jobs and tightened coverage gates for better test quality. - Switched to direct Trivy binary for security scans, removing previous Trivy action dependency. - Enhanced memory management with per-call processing options and better observability during speech-to-text encoding. - Various dependency updates for Python, infrastructure, and lock files maintain security and stability. - Removed TypeScript DTO type-tightening overlays to simplify type management. - Codebase audit tightened skill sets to prevent false positivity in class detection by 2026. <!-- HIGHLIGHTS_END --> :robot: I have created a release *beep* *boop* --- ## [0.8.5](v0.8.4...v0.8.5) (2026-05-17) ### Features * **codegen:** pydantic-to-typescript DTO pipeline + parity gate (closes [#1889](#1889)) ([#1909](#1909)) ([0265ef5](0265ef5)) * **storybook:** enable changeDetection + trim web/CLAUDE.md ([#1939](#1939)) ([3b1f4c0](3b1f4c0)) * **web,setup:** WP-6 frontend + UX polish ([#1941](#1941)) ([d9ca76d](d9ca76d)) ### Bug Fixes * correct invalid git for-each-ref syntax in post-merge-cleanup skill ([#1946](#1946)) ([69a1649](69a1649)) * dashboard polish, training endpoint dispatch, and observability cleanup ([#1911](#1911)) ([b61e9e8](b61e9e8)) * per-worktree git-hook isolation + hookify gate migration + MSW drift fix ([#1949](#1949)) ([e3f8495](e3f8495)) * **providers:** read supports_native_streaming from litellm model info ([#1942](#1942)) ([60364ca](60364ca)) * security and audit coverage (closes [#1883](#1883)) ([#1904](#1904)) ([d8ebf55](d8ebf55)) ### Performance * **ci:** mypy --num-workers=4 + enable ruff TID255 ([#1944](#1944)) ([484c1d3](484c1d3)) ### Refactoring * **ci:** drop aquasecurity/trivy-action, use direct trivy binary ([#1940](#1940)) ([df1f946](df1f946)) * **memory:** per-call processing_kwargs + observability for ST encode ([#1943](#1943)) ([3aa9d20](3aa9d20)) * Phase 7 follow-up — complete RootConfig mirror coverage (closes [#1907](#1907)) ([#1914](#1914)) ([605500b](605500b)) * **settings:** collapse precedence to three categories; drop YAML tier (closes [#1890](#1890)) ([#1910](#1910)) ([efd54c9](efd54c9)) * WP-3 API conventions + query performance + project-wide extra=forbid ([#1953](#1953)) ([504d579](504d579)), closes [#1918](#1918) * WP-4 settings + cross-cutting (clock seam, contextvars, dispatch, plugin surfaces) ([#1954](#1954)) ([7207d92](7207d92)) * **wp1:** persistence + layer discipline + restart safety ([#1945](#1945)) ([57586fb](57586fb)) ### Documentation * **wp5:** public-facing truth refresh ([#1924](#1924)) ([afb5cc5](afb5cc5)) ### CI/CD * split test job by marker with airtight aggregate coverage gate ([#1948](#1948)) ([0b818d5](0b818d5)), closes [#1938](#1938) [#1937](#1937) ### Maintenance * **codebase-audit:** tighten skill to prevent 2026-05-15 FP classes ([#1923](#1923)) ([9317ed1](9317ed1)) * Lock file maintenance ([#1913](#1913)) ([c08a355](c08a355)) * Lock file maintenance ([#1950](#1950)) ([8940ab1](8940ab1)) * remove TS DTO type-tightening overlays ([#1915](#1915)) ([d296214](d296214)), closes [#1906](#1906) * Update Infrastructure dependencies ([#1928](#1928)) ([d19fae5](d19fae5)) * Update Python dependencies ([#1929](#1929)) ([75cc2c8](75cc2c8)) * **wp7:** hygiene, stubs, test/CI/tooling, doc gaps, boundary patterns doc ([#1926](#1926)) ([c29eb32](c29eb32)) --- 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: synthorg-repo-bot[bot] <279117679+synthorg-repo-bot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Closes #1921
Summary
WP-6 dashboard ergonomics + onboarding work driven by the 2026-05-15 codebase audit. One bundled PR per the issue preamble.
HIGH
AgentsStep.tsx,setup-wizard/navigation.ts,setup-wizard/providers.ts,_hooks.ts)Data freshness indicators
usePollingexposesisRefetching, skips whendocument.hidden, and accepts a per-pageskipIfFreshpredicate. Wired into Dashboard / Approvals / Budget / Providers / Scaling / Settings.ws-connection-bannercomponent.Frontend error handling
companystore mutations follow the sentinel-return contract (12 CRUD ops;nullfor entity-returning,falsefor boolean-returning).usePollingfirst-tick error now setserrorstate.useEmptyStatePropsstandardises empty-state derivation.Tablet responsiveness
max-w-lgmodals migrated to responsive Drawer /md:max-w-2xlwidths (CredentialsRotateDialog, TaskCreateDialog, AddManualModelDialog).Dashboard UX gaps
Missing backend feature UI surfaces
/security/audit(new endpoint client + MSW handler).Error message UX
getErrorMessage/getCrudErrorTitledifferentiate 429 (with Retry-After), 503 transient vs sustained, and 409 duplicate / version-conflict / generic-conflict.Onboarding wizard reliability
setup_complete=trueis persisted ONLY afterpost_setup_reinit()returns clean; the whole check/validate/reinit/persist sequence is serialised underCOMPLETE_LOCK. Agent mutation checks (create_agent,update_agent_model,update_agent_name,randomize_agent_name) re-checksetup_completeinsideAGENT_LOCK.auto_select_embedderpropagates failures viaSetupCompleteResponse.embedder_failure_reason.CompleteStepholds the wizard open and surfaces the warning before navigating, so a degraded-but-successful setup never silently ships the operator to a half-configured dashboard.WS/SSE robustness
api.sse_revalidate_max_failuresexposed as a setting (defaults to 3, bounded 1-20).sanitizeWsEnumfor defense in depth.WS_MAX_OUTBOUND_MESSAGE_SIZEexported as a frontend constant.Storybook
LazyCodeMirrorEditorstory with onChange callback demonstrating the editable variant.Pre-PR review (this commit)
/pre-pr-reviewran 13 review agents and produced 14 valid findings (3 Critical, 2 Major, 8 Medium, 1 Minor). All 14 addressed in6e3525492:SetupCompleteResponsegains a@model_validator(mode="after")enforcingembedder_selected ↔ embedder_failure_reasonconsistency.auto_select_embedder()logsMEMORY_EMBEDDER_AUTO_SELECTEDAFTER the persistence writes succeed, binds the broad-except path soerror_type+safe_error_descriptionland on the warning, and surfaces its inverted contract via a newEmbedderSelectResulttype alias.CompleteStepsurfacescompletionWarninginline with a "Continue to dashboard" button.DepartmentsTabdrag overlay uses theshadow-card-hoverTailwind class.CategoryBreakdownbar transition uses the--so-transition-slowdesign token.CLAUDE.mdAPI startup lifecycle section names the implementation module.docs/design/page-structure.mdgains a Setup Wizard step / route table.docs/reference/web-design-system.mddocuments theWsConnectionBannercomponent.web/CLAUDE.mdsentinel-return rule rewording (any boolean-returning mutation, not just delete); component file conventions cross-reference Base UI integration rules.The verifier's C1 (WorkflowExecutionsPage cancel) and C2 (FineTuningPage deploy/rollback/cancel) flags were diff-scope artefacts: both features exist in the codebase and were verified during triage.
Test plan
uv run ruff check src/ tests/anduv run ruff format src/ tests/clean.uv run mypy src/ tests/clean (3816 files).uv run python -m pytest tests/ -n 8: 30533 passed, 50 skipped (15:02).npm --prefix web run lintclean (zero warnings).npm --prefix web run type-checkclean.npm --prefix web run test: 256 files / 3176 tests pass.Review coverage
13 agents: docs-consistency, python-reviewer, code-reviewer, security-reviewer, frontend-reviewer, design-token-audit, api-contract-drift, type-design-analyzer, async-concurrency-reviewer, issue-resolution-verifier, mini-pass-missing-logger, mini-pass-missing-event-constants, mini-pass-missing-state-transition-log, mini-pass-unwired-settings, mini-pass-race-conditions. 14 findings addressed; full triage in
_audit/pre-pr-review/triage.md.