Skip to content

feat(web,setup): WP-6 frontend + UX polish#1941

Merged
Aureliolo merged 33 commits into
mainfrom
feat/wp6-frontend-ux-polish
May 16, 2026
Merged

feat(web,setup): WP-6 frontend + UX polish#1941
Aureliolo merged 33 commits into
mainfrom
feat/wp6-frontend-ux-polish

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

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

  • Setup wizard agents step is sticky-complete with revalidation glyph; forward-only completion sync prevents the backward demotion from a re-edit of an earlier step. (AgentsStep.tsx, setup-wizard/navigation.ts, setup-wizard/providers.ts, _hooks.ts)

Data freshness indicators

  • usePolling exposes isRefetching, skips when document.hidden, and accepts a per-page skipIfFresh predicate. Wired into Dashboard / Approvals / Budget / Providers / Scaling / Settings.
  • WS-offline banner added to Connections / CustomRules / Meta / OrgChart / OrgEdit via the new ws-connection-banner component.

Frontend error handling

  • company store mutations follow the sentinel-return contract (12 CRUD ops; null for entity-returning, false for boolean-returning).
  • usePolling first-tick error now sets error state.
  • Fire-and-forget WS-driven fetches in budget / company / analytics stores have error recovery.
  • useEmptyStateProps standardises empty-state derivation.

Tablet responsiveness

  • BudgetPage gains 4 → 3 → 2 and 3 → 2 → 1 grid breakpoints so 768-1023px renders cleanly.
  • Hardcoded max-w-lg modals migrated to responsive Drawer / md:max-w-2xl widths (CredentialsRotateDialog, TaskCreateDialog, AddManualModelDialog).

Dashboard UX gaps

  • Pagination on AgentsPage / ProjectsPage / ClientListPage / ProvidersPage.
  • Search / filter on RequestQueuePage and BudgetPage.
  • Breadcrumbs on Artifact / Client / Meeting / Provider detail pages.
  • Bulk-selection UI on AgentsPage / ClientListPage / ProvidersPage.
  • WorkflowExecutionsPage cancel already has the ConfirmDialog (verified, pre-existing).
  • FineTuningPage already exposes Deploy / Rollback / Cancel / Delete via CheckpointTable + PipelineControlPanel (verified, pre-existing).

Missing backend feature UI surfaces

  • SettingsSinksPage Delete + Reset.
  • PersonalitiesAdminPage CRUD (list + detail + new endpoint client + MSW handler).
  • AdminAuditLogPage consuming /security/audit (new endpoint client + MSW handler).

Error message UX

  • getErrorMessage / getCrudErrorTitle differentiate 429 (with Retry-After), 503 transient vs sustained, and 409 duplicate / version-conflict / generic-conflict.
  • Pydantic validation message leakage replaced with actionable copy.

Onboarding wizard reliability

  • Setup completion atomicity: setup_complete=true is persisted ONLY after post_setup_reinit() returns clean; the whole check/validate/reinit/persist sequence is serialised under COMPLETE_LOCK. Agent mutation checks (create_agent, update_agent_model, update_agent_name, randomize_agent_name) re-check setup_complete inside AGENT_LOCK.
  • auto_select_embedder propagates failures via SetupCompleteResponse.embedder_failure_reason. CompleteStep holds 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.
  • Backend validation that persisted agent configs reference real provider+model pairs before allowing completion.

WS/SSE robustness

  • api.sse_revalidate_max_failures exposed as a setting (defaults to 3, bounded 1-20).
  • WS event-type enum allowlist via sanitizeWsEnum for defense in depth.
  • WS_MAX_OUTBOUND_MESSAGE_SIZE exported as a frontend constant.
  • ±5% jitter on the heartbeat.

Storybook

  • LazyCodeMirrorEditor story with onChange callback demonstrating the editable variant.

Pre-PR review (this commit)

/pre-pr-review ran 13 review agents and produced 14 valid findings (3 Critical, 2 Major, 8 Medium, 1 Minor). All 14 addressed in 6e3525492:

  • SetupCompleteResponse gains a @model_validator(mode="after") enforcing embedder_selected ↔ embedder_failure_reason consistency.
  • auto_select_embedder() logs MEMORY_EMBEDDER_AUTO_SELECTED AFTER the persistence writes succeed, binds the broad-except path so error_type + safe_error_description land on the warning, and surfaces its inverted contract via a new EmbedderSelectResult type alias.
  • CompleteStep surfaces completionWarning inline with a "Continue to dashboard" button.
  • DepartmentsTab drag overlay uses the shadow-card-hover Tailwind class.
  • CategoryBreakdown bar transition uses the --so-transition-slow design token.
  • CLAUDE.md API startup lifecycle section names the implementation module.
  • docs/design/page-structure.md gains a Setup Wizard step / route table.
  • docs/reference/web-design-system.md documents the WsConnectionBanner component.
  • web/CLAUDE.md sentinel-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/ and uv 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 lint clean (zero warnings).
  • npm --prefix web run type-check clean.
  • 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.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 16, 2026

Dependency Review

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

Scanned Files

None

@Aureliolo Aureliolo temporarily deployed to cloudflare-preview May 16, 2026 17:08 — with GitHub Actions Inactive
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 16, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro

Run ID: 38a771ec-5cdc-49d9-80c5-14b6f3ce4528

📥 Commits

Reviewing files that changed from the base of the PR and between b7ab105 and 3768913.

📒 Files selected for processing (1)
  • web/src/__tests__/stores/websocket.test.ts
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Lighthouse Dashboard
  • GitHub Check: Lighthouse Site
  • 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 (1)
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Reuse web/src/components/ui/ components and design tokens only in web dashboard design system

Always use createLogger from @/lib/logger; never bare console.warn, console.error, or console.debug in application code. Variable name must always be log (e.g., const log = createLogger('module-name')). Only logger.ts itself may use bare console methods.

Pass dynamic/untrusted values as separate arguments to logger methods (not interpolated into the message string) so they go through sanitizeArg. Attacker-controlled fields inside structured objects must be wrapped in sanitizeForLog() before embedding.

Callers MUST NOT wrap store mutation calls in try / catch; the store owns the error UX.

Import ErrorCode and ErrorCategory from @/api/types/errors (re-exported from generated web/src/api/types/error-codes.gen.ts). Discriminate on ErrorCode.<NAME>, never on raw integer literals.

Files:

  • web/src/__tests__/stores/websocket.test.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg

Timestamp: 2026-05-16T20:11:02.594Z
Learning: Read design spec from `docs/design/` page before implementing; deviations require approval
Learnt from: CR
Repo: Aureliolo/synthorg

Timestamp: 2026-05-16T20:11:02.594Z
Learning: Present every plan for accept/deny before coding
Learnt from: CR
Repo: Aureliolo/synthorg

Timestamp: 2026-05-16T20:11:02.594Z
Learning: No region/currency/locale privileged; use metric units; use British English
Learnt from: CR
Repo: Aureliolo/synthorg

Timestamp: 2026-05-16T20:11:02.594Z
Learning: Every convention PR ships its enforcement gate
Learnt from: CR
Repo: Aureliolo/synthorg

Timestamp: 2026-05-16T20:11:02.594Z
Learning: Commits: `<type>: <description>` (feat/fix/refactor/docs/test/chore/perf/ci); commitizen-enforced
Learnt from: CR
Repo: Aureliolo/synthorg

Timestamp: 2026-05-16T20:11:02.594Z
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-16T20:11:02.594Z
Learning: Branches: `<type>/<slug>` from main
Learnt from: CR
Repo: Aureliolo/synthorg

Timestamp: 2026-05-16T20:11:02.594Z
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-16T20:11:02.594Z
Learning: After every squash merge → `/post-merge-cleanup`
Learnt from: CR
Repo: Aureliolo/synthorg

Timestamp: 2026-05-16T20:11:02.594Z
Learning: CLI is Docker-only (init/start/stop/status); features go in dashboard + REST API
Learnt from: CR
Repo: Aureliolo/synthorg

Timestamp: 2026-05-16T20:11:37.222Z
Learning: The client-server 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. Bump the protocol version on both sides together for breaking payload changes.
Learnt from: CR
Repo: Aureliolo/synthorg

Timestamp: 2026-05-16T20:11:37.222Z
Learning: Use `eslint-react/eslint-plugin` v5+ via the `recommended-type-checked` preset with `parserOptions.projectService: true`. Enforce error-level rules: `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` (with `checksVoidReturn: { attributes: false }`).
Learnt from: CR
Repo: Aureliolo/synthorg

Timestamp: 2026-05-16T20:11:37.222Z
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 (3)
web/src/__tests__/stores/websocket.test.ts (3)

788-791: Past review comment addressed.

Line 791 now uses WS_HEARTBEAT_MAX_DELAY_MS instead of the hardcoded 1.1 multiplier, resolving the previous nitpick about magic constants. The shared constant will track any future jitter bound changes automatically.


690-710: LGTM!

Also applies to: 735-742, 750-760


9-9: No action needed—WS_HEARTBEAT_JITTER_MAX is correctly exported from web/src/utils/constants.ts with the value 1.05, matching the ±5% jitter specification. The import at line 9 and usage at lines 21–23 are valid.


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.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 16, 2026

Merging this PR will not alter performance

✅ 54 untouched benchmarks


Comparing feat/wp6-frontend-ux-polish (03baf13) with main (484c1d3)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (69a1649) during the generation of this report, so 484c1d3 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

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

Choose a reason for hiding this comment

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

critical

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

Choose a reason for hiding this comment

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

critical

Invalid syntax for catching multiple exceptions. Use a tuple to catch both MemoryError and RecursionError.

    except (MemoryError, RecursionError):

@@ -111,11 +131,13 @@ async def post_setup_reinit(app_state: AppState) -> None:
)
except MemoryError, RecursionError:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

critical

Invalid syntax for catching multiple exceptions. Use a tuple to catch both MemoryError and RecursionError.

        except (MemoryError, RecursionError):

@@ -400,8 +423,24 @@ async def auto_select_embedder(
)
except MemoryError, RecursionError:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

critical

Invalid syntax for catching multiple exceptions. Use a tuple to catch both MemoryError and RecursionError.

    except (MemoryError, RecursionError):

provider_preset_name=provider_preset_name,
has_gpu=has_gpu,
)
except MemoryError, RecursionError:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

critical

Invalid syntax for catching multiple exceptions. Use a tuple to catch both MemoryError and RecursionError.

    except (MemoryError, RecursionError):

Comment thread web/src/pages/AdminAuditLogPage.tsx Outdated
Comment on lines +30 to +36
type VerdictFilter = '' | 'APPROVED' | 'DENIED'

const VERDICT_OPTIONS: ReadonlyArray<{ value: VerdictFilter; label: string }> = [
{ value: '', label: 'Any verdict' },
{ value: 'APPROVED', label: 'Approved' },
{ value: 'DENIED', label: 'Denied' },
]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

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]

Comment thread web/src/pages/AdminAuditLogPage.tsx Outdated
{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}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The timestamp is rendered as a raw ISO string. It should be formatted for the user's locale to improve readability and maintain consistency with other dashboard pages.

Suggested change
{entry.timestamp}
{new Date(entry.timestamp).toLocaleString()}

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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 win

Optimistic rollback broken: sentinel contract mismatch.

onReorderAgents now returns Promise<boolean> per the updated contract (line 38), but this handler still uses try/catch. The store returns false on failure instead of throwing, so the catch block never executes and rollback() 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

📥 Commits

Reviewing files that changed from the base of the PR and between 3b1f4c0 and 6e35254.

📒 Files selected for processing (112)
  • CLAUDE.md
  • docs/design/page-structure.md
  • docs/reference/web-design-system.md
  • src/synthorg/api/controllers/events.py
  • src/synthorg/api/controllers/setup/agent_helpers.py
  • src/synthorg/api/controllers/setup_agents.py
  • src/synthorg/api/controllers/setup_controller.py
  • src/synthorg/api/controllers/setup_models.py
  • src/synthorg/settings/definitions/api.py
  • tests/unit/api/controllers/test_setup.py
  • web/CLAUDE.md
  • web/src/__tests__/hooks/usePolling.test.ts
  • web/src/__tests__/pages/ApprovalsPage.test.tsx
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/src/__tests__/pages/ProvidersPage.test.tsx
  • web/src/__tests__/pages/SettingsPage.test.tsx
  • web/src/__tests__/pages/setup/WizardProgress.test.tsx
  • web/src/__tests__/stores/company.test.ts
  • web/src/__tests__/stores/fine-tuning.test.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/__tests__/utils/errors.test.ts
  • web/src/api/endpoints/audit.ts
  • web/src/api/endpoints/personalities.ts
  • web/src/api/endpoints/setup.ts
  • web/src/api/types/openapi.gen.ts
  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/src/components/ui/list-header.tsx
  • web/src/components/ui/ws-connection-banner.stories.tsx
  • web/src/components/ui/ws-connection-banner.tsx
  • web/src/hooks/useApprovalsData.ts
  • web/src/hooks/useBudgetData.ts
  • web/src/hooks/useDashboardData.ts
  • web/src/hooks/useOrgEditData.ts
  • web/src/hooks/usePolling.ts
  • web/src/hooks/useProvidersData.ts
  • web/src/hooks/useScalingData.ts
  • web/src/hooks/useSettingsData.ts
  • web/src/mocks/handlers/audit.ts
  • web/src/mocks/handlers/index.ts
  • web/src/mocks/handlers/personalities.ts
  • web/src/mocks/handlers/setup.ts
  • web/src/pages/AdminAuditLogPage.tsx
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/pages/BudgetPage.tsx
  • web/src/pages/ClientListPage.tsx
  • web/src/pages/ConnectionsPage.tsx
  • web/src/pages/CustomRulesPage.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/MeetingsPage.tsx
  • web/src/pages/MetaPage.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/PersonalitiesAdminPage.tsx
  • web/src/pages/ProjectsPage.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/RequestQueuePage.tsx
  • web/src/pages/ScalingPage.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/SettingsSinksPage.tsx
  • web/src/pages/agents/AgentGridView.tsx
  • web/src/pages/budget/AgentSpendingTable.tsx
  • web/src/pages/budget/CategoryBreakdown.tsx
  • web/src/pages/org-edit/AgentCreateDialog.tsx
  • web/src/pages/org-edit/AgentEditDrawer.stories.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/org-edit/AgentsTab.stories.tsx
  • web/src/pages/org-edit/AgentsTab.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.tsx
  • web/src/pages/org-edit/TeamEditDialog.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/pages/org-edit/YamlEditorPanel.stories.tsx
  • web/src/pages/org-edit/YamlEditorPanel.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/pages/providers/AddManualModelDialog.tsx
  • web/src/pages/providers/CredentialsRotateDialog.tsx
  • web/src/pages/providers/ProviderGridView.tsx
  • web/src/pages/settings/sinks/SinkCard.tsx
  • web/src/pages/setup/AgentsStep.tsx
  • web/src/pages/setup/CompleteStep.tsx
  • web/src/pages/setup/WizardProgress.tsx
  • web/src/pages/setup/WizardShell.tsx
  • web/src/pages/setup/_hooks.ts
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/router/index.tsx
  • web/src/router/routes.ts
  • web/src/stores/artifacts.ts
  • web/src/stores/company.ts
  • web/src/stores/escalations.ts
  • web/src/stores/fine-tuning.ts
  • web/src/stores/projects.ts
  • web/src/stores/setup-wizard/completion.ts
  • web/src/stores/setup-wizard/navigation.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/stores/setup-wizard/types.ts
  • web/src/stores/sinks.ts
  • web/src/stores/subworkflows.ts
  • web/src/stores/users.ts
  • web/src/stores/websocket.ts
  • web/src/stores/workflows.ts
  • web/src/utils/constants.ts
  • web/src/utils/errors.ts
  • web/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.md

Always use createLogger from @/lib/logger for logging; never use bare console.warn, console.error, or console.debug in application code

Variable 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 sanitizeArg

Wrap attacker-controlled fields inside structured objects with sanitizeForLog() before embedding in logs

Callers 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 explicitly

Use sanitizeWsString() and sanitizeWsEnum() from web/src/utils/ws-sanitize.ts for 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 EnumType are forbidden

Import ErrorCode and ErrorCategory from @/api/types/errors (re-exported from the generated web/src/api/types/error-codes.gen.ts). Discriminate on ErrorCode.<NAME>, never on raw integer literals

Icon helpers: NEVER write getXIcon(value): LucideIcon factories called inside JSX bodies. Export a <XIcon value={...} /> wrapper that does the lookup via createElement inside the wrapper body. Wrapper components live in their own file

Viewport-size reads: use useViewportSize() from @/hooks/useViewportSize. NEVER read window.innerWidth / window.innerHeight directly in a render body or useMemo

Detect fetch() in effects without AbortController cleanup using @eslint-react/web-api-no-leaked-fetch

Use @eslint-react/no-leaked-conditional-rendering to catch the {count && <Foo />} bug...

Files:

  • web/src/__tests__/pages/ApprovalsPage.test.tsx
  • web/src/mocks/handlers/setup.ts
  • web/src/pages/CustomRulesPage.tsx
  • web/src/pages/ScalingPage.tsx
  • web/src/stores/subworkflows.ts
  • web/src/mocks/handlers/audit.ts
  • web/src/stores/workflows.ts
  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/pages/ConnectionsPage.tsx
  • web/src/api/endpoints/setup.ts
  • web/src/components/ui/ws-connection-banner.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/__tests__/pages/ProvidersPage.test.tsx
  • web/src/hooks/useApprovalsData.ts
  • web/src/pages/providers/CredentialsRotateDialog.tsx
  • web/src/stores/users.ts
  • web/src/pages/org-edit/YamlEditorPanel.stories.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/src/router/routes.ts
  • web/src/hooks/useBudgetData.ts
  • web/src/router/index.tsx
  • web/src/utils/constants.ts
  • web/src/hooks/useScalingData.ts
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/src/stores/escalations.ts
  • web/src/components/ui/list-header.tsx
  • web/src/pages/budget/CategoryBreakdown.tsx
  • web/src/pages/MetaPage.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/__tests__/pages/SettingsPage.test.tsx
  • web/src/pages/setup/WizardShell.tsx
  • web/src/hooks/useDashboardData.ts
  • web/src/pages/ProjectsPage.tsx
  • web/src/stores/projects.ts
  • web/src/pages/org-edit/AgentsTab.stories.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/pages/org-edit/AgentsTab.tsx
  • web/src/mocks/handlers/personalities.ts
  • web/src/pages/org-edit/YamlEditorPanel.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/api/types/openapi.gen.ts
  • web/src/hooks/useSettingsData.ts
  • web/src/api/endpoints/personalities.ts
  • web/src/pages/budget/AgentSpendingTable.tsx
  • web/src/api/endpoints/audit.ts
  • web/src/pages/org-edit/DepartmentsTab.tsx
  • web/src/pages/providers/AddManualModelDialog.tsx
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/__tests__/hooks/usePolling.test.ts
  • web/src/pages/MeetingsPage.tsx
  • web/src/pages/setup/AgentsStep.tsx
  • web/src/hooks/useProvidersData.ts
  • web/src/pages/SettingsSinksPage.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/stores/fine-tuning.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/pages/PersonalitiesAdminPage.tsx
  • web/src/pages/setup/WizardProgress.tsx
  • web/src/pages/providers/ProviderGridView.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/pages/agents/AgentGridView.tsx
  • web/src/stores/sinks.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/__tests__/stores/fine-tuning.test.ts
  • web/src/components/ui/ws-connection-banner.stories.tsx
  • web/src/utils/mutation-result.ts
  • web/src/pages/AdminAuditLogPage.tsx
  • web/src/pages/setup/_hooks.ts
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/org-edit/AgentEditDrawer.stories.tsx
  • web/src/pages/BudgetPage.tsx
  • web/src/pages/RequestQueuePage.tsx
  • web/src/__tests__/utils/errors.test.ts
  • web/src/stores/websocket.ts
  • web/src/stores/setup-wizard/navigation.ts
  • web/src/mocks/handlers/index.ts
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/org-edit/AgentCreateDialog.tsx
  • web/src/stores/setup-wizard/types.ts
  • web/src/__tests__/pages/setup/WizardProgress.test.tsx
  • web/src/pages/setup/CompleteStep.tsx
  • web/src/stores/setup-wizard/completion.ts
  • web/src/pages/org-edit/TeamEditDialog.tsx
  • web/src/__tests__/stores/company.test.ts
  • web/src/pages/settings/sinks/SinkCard.tsx
  • web/src/hooks/usePolling.ts
  • web/src/pages/ClientListPage.tsx
  • web/src/utils/errors.ts
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/stores/artifacts.ts
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/stores/company.ts
  • web/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, never vi.mock('@/api/endpoints/*')

Every unit test runs under web/test-infra/active-handle-tracker.ts which 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 global afterEach

Files:

  • web/src/__tests__/pages/ApprovalsPage.test.tsx
  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/__tests__/pages/ProvidersPage.test.tsx
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/src/__tests__/pages/SettingsPage.test.tsx
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/__tests__/hooks/usePolling.test.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/__tests__/stores/fine-tuning.test.ts
  • web/src/__tests__/utils/errors.test.ts
  • web/src/__tests__/pages/setup/WizardProgress.test.tsx
  • web/src/__tests__/stores/company.test.ts
web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

Use @eslint-react/eslint-plugin v5+ via the recommended-type-checked preset 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.tsx
  • web/src/mocks/handlers/setup.ts
  • web/src/pages/CustomRulesPage.tsx
  • web/src/pages/ScalingPage.tsx
  • web/src/stores/subworkflows.ts
  • web/src/mocks/handlers/audit.ts
  • web/src/stores/workflows.ts
  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/pages/ConnectionsPage.tsx
  • web/src/api/endpoints/setup.ts
  • web/src/components/ui/ws-connection-banner.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/__tests__/pages/ProvidersPage.test.tsx
  • web/src/hooks/useApprovalsData.ts
  • web/src/pages/providers/CredentialsRotateDialog.tsx
  • web/src/stores/users.ts
  • web/src/pages/org-edit/YamlEditorPanel.stories.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/src/router/routes.ts
  • web/src/hooks/useBudgetData.ts
  • web/src/router/index.tsx
  • web/src/utils/constants.ts
  • web/src/hooks/useScalingData.ts
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/src/stores/escalations.ts
  • web/src/components/ui/list-header.tsx
  • web/src/pages/budget/CategoryBreakdown.tsx
  • web/src/pages/MetaPage.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/__tests__/pages/SettingsPage.test.tsx
  • web/src/pages/setup/WizardShell.tsx
  • web/src/hooks/useDashboardData.ts
  • web/src/pages/ProjectsPage.tsx
  • web/src/stores/projects.ts
  • web/src/pages/org-edit/AgentsTab.stories.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/pages/org-edit/AgentsTab.tsx
  • web/src/mocks/handlers/personalities.ts
  • web/src/pages/org-edit/YamlEditorPanel.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/api/types/openapi.gen.ts
  • web/src/hooks/useSettingsData.ts
  • web/src/api/endpoints/personalities.ts
  • web/src/pages/budget/AgentSpendingTable.tsx
  • web/src/api/endpoints/audit.ts
  • web/src/pages/org-edit/DepartmentsTab.tsx
  • web/src/pages/providers/AddManualModelDialog.tsx
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/__tests__/hooks/usePolling.test.ts
  • web/src/pages/MeetingsPage.tsx
  • web/src/pages/setup/AgentsStep.tsx
  • web/src/hooks/useProvidersData.ts
  • web/src/pages/SettingsSinksPage.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/stores/fine-tuning.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/pages/PersonalitiesAdminPage.tsx
  • web/src/pages/setup/WizardProgress.tsx
  • web/src/pages/providers/ProviderGridView.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/pages/agents/AgentGridView.tsx
  • web/src/stores/sinks.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/__tests__/stores/fine-tuning.test.ts
  • web/src/components/ui/ws-connection-banner.stories.tsx
  • web/src/utils/mutation-result.ts
  • web/src/pages/AdminAuditLogPage.tsx
  • web/src/pages/setup/_hooks.ts
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/org-edit/AgentEditDrawer.stories.tsx
  • web/src/pages/BudgetPage.tsx
  • web/src/pages/RequestQueuePage.tsx
  • web/src/__tests__/utils/errors.test.ts
  • web/src/stores/websocket.ts
  • web/src/stores/setup-wizard/navigation.ts
  • web/src/mocks/handlers/index.ts
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/org-edit/AgentCreateDialog.tsx
  • web/src/stores/setup-wizard/types.ts
  • web/src/__tests__/pages/setup/WizardProgress.test.tsx
  • web/src/pages/setup/CompleteStep.tsx
  • web/src/stores/setup-wizard/completion.ts
  • web/src/pages/org-edit/TeamEditDialog.tsx
  • web/src/__tests__/stores/company.test.ts
  • web/src/pages/settings/sinks/SinkCard.tsx
  • web/src/hooks/usePolling.ts
  • web/src/pages/ClientListPage.tsx
  • web/src/utils/errors.ts
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/stores/artifacts.ts
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/stores/company.ts
  • web/src/hooks/useOrgEditData.ts
web/src/mocks/handlers/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

MSW handlers in web/src/mocks/handlers/ must mirror web/src/api/endpoints/*.ts 1: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.ts
  • web/src/mocks/handlers/audit.ts
  • web/src/mocks/handlers/personalities.ts
  • web/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.ts pattern: wrap API calls in try/catch, emit success toast on success, emit error toast with getCrudErrorTitle() on failure, return sentinel values (null for entity mutations, false for boolean/void mutations)

List reads (fetch*) must set error: string | null on the store instead of toasting

Files:

  • web/src/stores/subworkflows.ts
  • web/src/stores/workflows.ts
  • web/src/stores/users.ts
  • web/src/stores/escalations.ts
  • web/src/stores/projects.ts
  • web/src/stores/fine-tuning.ts
  • web/src/stores/sinks.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/stores/websocket.ts
  • web/src/stores/setup-wizard/navigation.ts
  • web/src/stores/setup-wizard/types.ts
  • web/src/stores/setup-wizard/completion.ts
  • web/src/stores/artifacts.ts
  • web/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 keep nextCursor + hasMore in state (not offset arithmetic) and early-return when !hasMore || !nextCursor. Display counts come from data.length

Files:

  • web/src/api/endpoints/setup.ts
  • web/src/api/types/openapi.gen.ts
  • web/src/api/endpoints/personalities.ts
  • web/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/motion presets, helpers in @/utils/format, and DEFAULT_CURRENCY from @/utils/currencies

Component Props interface name must be <ComponentName>Props and be exported from the same file (e.g. AgentCardProps in agent-card.tsx)

Base UI primitives must compose Portal + Backdrop + Popup explicitly, use the render prop for polymorphism, and rely on animation state attributes (data-[open], data-[closed]) rather than the older data-[state=open] form

Files:

  • web/src/components/ui/ws-connection-banner.tsx
  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/src/components/ui/list-header.tsx
  • web/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.tsx covering every meaningful state (default, hover, loading, error, empty, disabled where applicable)

Files:

  • web/src/components/ui/ws-connection-banner.tsx
  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/src/components/ui/list-header.tsx
  • web/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.md
  • docs/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.md
  • docs/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 with uv 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. No os.environ.get outside startup; pre-init Cat-2 reads use settings.bootstrap_resolver.resolve_init_value per configuration-precedence.md

No 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 binding

Type 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>Error pattern inheriting from DomainError, never Exception/RuntimeError directly. Enforced by check_domain_error_hierarchy.py

Pydantic v2: frozen + extra="forbid" on API DTOs (Request/Response/Snapshot/Result/Envelope/Status/Info/Summary suffixes); use @computed_field for derived fields; NotBlankStr for identifiers

Args models at every system boundary; parse_typed() for every external dict ingestion. Enforced by check_boundary_typed.py

Immutability: use model_copy(update=...) or copy.deepcopy(); deepcopy at system boundaries

Async patterns: use asyncio.TaskGroup for fan-out/fan-in; helpers must catch Exception and re-raise MemoryError/RecursionError

Clock seam: parameter clock: Clock | None = None; tests inject FakeClock. Lifecycle: services own _lifecycle_lock; timed-out stops mark unrestartable

Untrusted content (SEC-1): use wrap_untrusted() from engine.prompt_safety; use HTMLParseGuard for HTML processing

Logging: from synthorg.observability import get_logger; variable always logger. Never import logging / print() in app code. Event names from...

Files:

  • src/synthorg/api/controllers/setup_models.py
  • src/synthorg/api/controllers/setup_agents.py
  • src/synthorg/settings/definitions/api.py
  • src/synthorg/api/controllers/events.py
  • src/synthorg/api/controllers/setup_controller.py
  • src/synthorg/api/controllers/setup/agent_helpers.py
src/synthorg/api/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

API startup lifecycle: construction phase (create_app body) wires synchronous services; on_startup (_build_lifecycle.on_startup) wires services needing connected persistence. Construction ordering: agent_registry before auto_wire_meetings; tunnel_provider wired unconditionally. On-startup ordering: SettingsService auto-wire before WorkflowExecutionObserver; OntologyService after persistence.connect() via _wire_ontology_service

Files:

  • src/synthorg/api/controllers/setup_models.py
  • src/synthorg/api/controllers/setup_agents.py
  • src/synthorg/api/controllers/events.py
  • src/synthorg/api/controllers/setup_controller.py
  • src/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.py
  • src/synthorg/api/controllers/setup_agents.py
  • src/synthorg/settings/definitions/api.py
  • src/synthorg/api/controllers/events.py
  • src/synthorg/api/controllers/setup_controller.py
  • src/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% min

Windows testing: unit tests use WindowsSelectorEventLoopPolicy (3.14 IOCP teardown race); subprocess tests override back

Test doubles: use FakeClock for Clock seam, mock_of[T](**overrides) for typed-boundary substitutions, SimpleNamespace for attribute-bags. Bare MagicMock at typed boundaries blocked by check_mock_spec.py (zero-tolerance). FakeClock and mock_of imported from tests._shared

Hypothesis testing: 10 deterministic CI examples; failures are real bugs (fix + add @example(...))

Flaky tests: NEVER skip/xfail; fix fundamentally. Use asyncio.Event().wait() not sleep(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() in src/synthorg/api/controllers/setup/agent_helpers.py propagates failures; settings_svc.set("api", "setup_complete", "true") only after clean reinit. Whole sequence serialized under COMPLETE_LOCK to prevent concurrent /setup/complete race 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.py
  • src/synthorg/api/controllers/setup_agents.py
  • src/synthorg/settings/definitions/api.py
  • src/synthorg/api/controllers/events.py
  • tests/unit/api/controllers/test_setup.py
  • src/synthorg/api/controllers/setup_controller.py
  • src/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!

Comment thread src/synthorg/api/controllers/setup_controller.py Outdated
Comment thread src/synthorg/api/controllers/setup_controller.py Outdated
Comment thread web/src/__tests__/pages/OrgEditPage.test.tsx Outdated
Comment thread web/src/api/endpoints/personalities.ts Outdated
Comment thread web/src/components/ui/ws-connection-banner.tsx
Comment thread web/src/pages/RequestQueuePage.tsx
Comment thread web/src/router/index.tsx Outdated
Comment thread web/src/stores/sinks.ts Outdated
Comment thread web/src/stores/websocket.ts Outdated
Comment thread web/src/utils/errors.ts Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented May 16, 2026

Codecov Report

❌ Patch coverage is 70.90909% with 32 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.20%. Comparing base (484c1d3) to head (03baf13).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/synthorg/api/controllers/setup_agents.py 40.00% 8 Missing and 4 partials ⚠️
src/synthorg/api/controllers/events.py 33.33% 9 Missing and 1 partial ⚠️
src/synthorg/api/controllers/setup_controller.py 91.22% 5 Missing ⚠️
src/synthorg/api/controllers/setup_models.py 72.72% 2 Missing and 1 partial ⚠️
...rc/synthorg/api/controllers/setup/agent_helpers.py 66.66% 2 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

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

@Aureliolo Aureliolo force-pushed the feat/wp6-frontend-ux-polish branch from 6e35254 to 2d353a2 Compare May 16, 2026 18:24
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview May 16, 2026 18:26 — with GitHub Actions Inactive
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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 win

Fix the off-by-one in the revalidation failure ceiling.

The docstrings here and on _user_revocation_reason() say the stream should tolerate api.sse_revalidate_max_failures consecutive transient failures before revoking, but Line 399 disconnects on the Nth failure because it uses >=. With the default 3, that drops the stream on failures 1/2/3 instead of after 1/2/3 have 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 win

Use 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 to successFor<typeof ...>(...).

As per coding guidelines: “MSW handlers in web/src/mocks/handlers/ must mirror web/src/api/endpoints/*.ts 1: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

📥 Commits

Reviewing files that changed from the base of the PR and between 6e35254 and 2d353a2.

📒 Files selected for processing (112)
  • CLAUDE.md
  • docs/design/page-structure.md
  • docs/reference/web-design-system.md
  • src/synthorg/api/controllers/events.py
  • src/synthorg/api/controllers/setup/agent_helpers.py
  • src/synthorg/api/controllers/setup_agents.py
  • src/synthorg/api/controllers/setup_controller.py
  • src/synthorg/api/controllers/setup_models.py
  • src/synthorg/settings/definitions/api.py
  • tests/unit/api/controllers/test_setup.py
  • web/CLAUDE.md
  • web/src/__tests__/hooks/usePolling.test.ts
  • web/src/__tests__/pages/ApprovalsPage.test.tsx
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/src/__tests__/pages/ProvidersPage.test.tsx
  • web/src/__tests__/pages/SettingsPage.test.tsx
  • web/src/__tests__/pages/setup/WizardProgress.test.tsx
  • web/src/__tests__/stores/company.test.ts
  • web/src/__tests__/stores/fine-tuning.test.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/__tests__/utils/errors.test.ts
  • web/src/api/endpoints/audit.ts
  • web/src/api/endpoints/personalities.ts
  • web/src/api/endpoints/setup.ts
  • web/src/api/types/openapi.gen.ts
  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/src/components/ui/list-header.tsx
  • web/src/components/ui/ws-connection-banner.stories.tsx
  • web/src/components/ui/ws-connection-banner.tsx
  • web/src/hooks/useApprovalsData.ts
  • web/src/hooks/useBudgetData.ts
  • web/src/hooks/useDashboardData.ts
  • web/src/hooks/useOrgEditData.ts
  • web/src/hooks/usePolling.ts
  • web/src/hooks/useProvidersData.ts
  • web/src/hooks/useScalingData.ts
  • web/src/hooks/useSettingsData.ts
  • web/src/mocks/handlers/audit.ts
  • web/src/mocks/handlers/index.ts
  • web/src/mocks/handlers/personalities.ts
  • web/src/mocks/handlers/setup.ts
  • web/src/pages/AdminAuditLogPage.tsx
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/pages/BudgetPage.tsx
  • web/src/pages/ClientListPage.tsx
  • web/src/pages/ConnectionsPage.tsx
  • web/src/pages/CustomRulesPage.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/MeetingsPage.tsx
  • web/src/pages/MetaPage.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/PersonalitiesAdminPage.tsx
  • web/src/pages/ProjectsPage.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/RequestQueuePage.tsx
  • web/src/pages/ScalingPage.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/SettingsSinksPage.tsx
  • web/src/pages/agents/AgentGridView.tsx
  • web/src/pages/budget/AgentSpendingTable.tsx
  • web/src/pages/budget/CategoryBreakdown.tsx
  • web/src/pages/org-edit/AgentCreateDialog.tsx
  • web/src/pages/org-edit/AgentEditDrawer.stories.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/org-edit/AgentsTab.stories.tsx
  • web/src/pages/org-edit/AgentsTab.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.tsx
  • web/src/pages/org-edit/TeamEditDialog.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/pages/org-edit/YamlEditorPanel.stories.tsx
  • web/src/pages/org-edit/YamlEditorPanel.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/pages/providers/AddManualModelDialog.tsx
  • web/src/pages/providers/CredentialsRotateDialog.tsx
  • web/src/pages/providers/ProviderGridView.tsx
  • web/src/pages/settings/sinks/SinkCard.tsx
  • web/src/pages/setup/AgentsStep.tsx
  • web/src/pages/setup/CompleteStep.tsx
  • web/src/pages/setup/WizardProgress.tsx
  • web/src/pages/setup/WizardShell.tsx
  • web/src/pages/setup/_hooks.ts
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/router/index.tsx
  • web/src/router/routes.ts
  • web/src/stores/artifacts.ts
  • web/src/stores/company.ts
  • web/src/stores/escalations.ts
  • web/src/stores/fine-tuning.ts
  • web/src/stores/projects.ts
  • web/src/stores/setup-wizard/completion.ts
  • web/src/stores/setup-wizard/navigation.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/stores/setup-wizard/types.ts
  • web/src/stores/sinks.ts
  • web/src/stores/subworkflows.ts
  • web/src/stores/users.ts
  • web/src/stores/websocket.ts
  • web/src/stores/workflows.ts
  • web/src/utils/constants.ts
  • web/src/utils/errors.ts
  • web/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 createLogger from @/lib/logger for logging; never use bare console.warn, console.error, or console.debug in application code

Logger 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 sanitizeArg

Wrap attacker-controlled fields inside structured objects with sanitizeForLog() before embedding in log messages

Any new store that schedules timers or attaches event listeners must expose an equivalent cleanup hook and register it in the global afterEach in web/src/test-setup.tsx

Any new WebSocket payload handler must route untrusted strings through sanitizeWsString() or sanitizeWsEnum(); raw (sanitizeWsString(x, n) ?? '') as EnumType casts are forbidden

Import ErrorCode and ErrorCategory from @/api/types/errors (re-exported from web/src/api/types/error-codes.gen.ts) and discriminate on ErrorCode.<NAME>, never on raw integer literals

Import DTOs via the barrel import: import type { AgentConfig } from '@/api/types'

NEVER write getXIcon(value): LucideIcon factories called inside JSX bodies; export a <XIcon value={...} /> wrapper that does the lookup via createElement inside the wrapper body

Use useViewportSize() from @/hooks/useViewportSize to read viewport size; NEVER read window.innerWidth / window.innerHeight directly in a render body or useMemo

@eslint-react/no-leaked-conditional-rendering: use {value != null && value !== false && <jsx>} for ReactNode | undefined props; for compound truthiness use Boolean(...); avoid the {count && <Foo />} bug where 0 renders verbatim

@typescript-eslint/no-floating-promises: forbids unawaited promises so async work cannot survive the test that scheduled it and trip the active-handle gate

Hoist window, document, localStorage, and other global API reads outside of render; place them in `...

Files:

  • web/src/pages/providers/AddManualModelDialog.tsx
  • web/src/pages/budget/CategoryBreakdown.tsx
  • web/src/pages/MetaPage.tsx
  • web/src/pages/org-edit/AgentEditDrawer.stories.tsx
  • web/src/pages/CustomRulesPage.tsx
  • web/src/hooks/useApprovalsData.ts
  • web/src/stores/workflows.ts
  • web/src/stores/subworkflows.ts
  • web/src/pages/org-edit/YamlEditorPanel.stories.tsx
  • web/src/mocks/handlers/audit.ts
  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/mocks/handlers/setup.ts
  • web/src/stores/fine-tuning.ts
  • web/src/pages/org-edit/AgentsTab.stories.tsx
  • web/src/__tests__/pages/ApprovalsPage.test.tsx
  • web/src/router/routes.ts
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/ConnectionsPage.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/MeetingsPage.tsx
  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/components/ui/list-header.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/stores/users.ts
  • web/src/hooks/useProvidersData.ts
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/BudgetPage.tsx
  • web/src/__tests__/pages/ProvidersPage.test.tsx
  • web/src/api/types/openapi.gen.ts
  • web/src/stores/artifacts.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/stores/projects.ts
  • web/src/__tests__/stores/fine-tuning.test.ts
  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/src/pages/ProjectsPage.tsx
  • web/src/pages/agents/AgentGridView.tsx
  • web/src/hooks/useScalingData.ts
  • web/src/pages/providers/CredentialsRotateDialog.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/router/index.tsx
  • web/src/stores/escalations.ts
  • web/src/pages/budget/AgentSpendingTable.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/setup/AgentsStep.tsx
  • web/src/__tests__/pages/SettingsPage.test.tsx
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/src/hooks/useSettingsData.ts
  • web/src/pages/org-edit/YamlEditorPanel.tsx
  • web/src/pages/setup/WizardProgress.tsx
  • web/src/pages/setup/_hooks.ts
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/api/endpoints/setup.ts
  • web/src/mocks/handlers/personalities.ts
  • web/src/pages/settings/sinks/SinkCard.tsx
  • web/src/__tests__/pages/setup/WizardProgress.test.tsx
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/api/endpoints/audit.ts
  • web/src/components/ui/ws-connection-banner.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/pages/providers/ProviderGridView.tsx
  • web/src/utils/mutation-result.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/pages/org-edit/AgentCreateDialog.tsx
  • web/src/pages/SettingsSinksPage.tsx
  • web/src/stores/setup-wizard/completion.ts
  • web/src/hooks/useBudgetData.ts
  • web/src/utils/constants.ts
  • web/src/stores/websocket.ts
  • web/src/hooks/usePolling.ts
  • web/src/__tests__/utils/errors.test.ts
  • web/src/utils/errors.ts
  • web/src/pages/RequestQueuePage.tsx
  • web/src/stores/setup-wizard/types.ts
  • web/src/pages/setup/WizardShell.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/__tests__/stores/company.test.ts
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/api/endpoints/personalities.ts
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/AdminAuditLogPage.tsx
  • web/src/components/ui/ws-connection-banner.stories.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/hooks/useOrgEditData.ts
  • web/src/pages/ScalingPage.tsx
  • web/src/stores/setup-wizard/navigation.ts
  • web/src/mocks/handlers/index.ts
  • web/src/pages/PersonalitiesAdminPage.tsx
  • web/src/pages/ClientListPage.tsx
  • web/src/pages/org-edit/DepartmentsTab.tsx
  • web/src/stores/sinks.ts
  • web/src/hooks/useDashboardData.ts
  • web/src/pages/org-edit/AgentsTab.tsx
  • web/src/__tests__/hooks/usePolling.test.ts
  • web/src/pages/setup/CompleteStep.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/AgentsPage.tsx
  • web/src/stores/company.ts
  • web/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.ts pattern: wrap API calls in try/catch, success path updates state + emits success toast, failure path logs + emits error toast + returns a sentinel on failure

Mutation sentinel shape mirrors the mutation's return type: null for entity-returning mutations, false for boolean-returning mutations; use false for all void/boolean-returning mutations (delete, reorder, update)

Optimistic mutations must capture previous state synchronously and restore it in the catch block on failure

Use getCrudErrorTitle(err, fallback) from @/utils/errors on every error toast to provide distinct titles for duplicate-resource / version-conflict / generic-conflict 409s

Callers MUST NOT wrap store mutation calls in try/catch; the store owns the error UX

Files:

  • web/src/stores/workflows.ts
  • web/src/stores/subworkflows.ts
  • web/src/stores/fine-tuning.ts
  • web/src/stores/users.ts
  • web/src/stores/artifacts.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/stores/projects.ts
  • web/src/__tests__/stores/fine-tuning.test.ts
  • web/src/stores/escalations.ts
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/stores/setup-wizard/completion.ts
  • web/src/stores/websocket.ts
  • web/src/stores/setup-wizard/types.ts
  • web/src/__tests__/stores/company.test.ts
  • web/src/stores/setup-wizard/navigation.ts
  • web/src/stores/sinks.ts
  • web/src/stores/company.ts
web/src/mocks/handlers/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

MSW handlers in web/src/mocks/handlers/ must mirror web/src/api/endpoints/*.ts 1:1 with a default happy-path handler for every exported endpoint

Use typed envelope helpers (successFor, paginatedFor, voidSuccess) to keep MSW handlers in lockstep with endpoint return types

Test setup boots MSW with onUnhandledRequest: 'error'; tests override per-case via server.use(...) instead of mocking endpoints

Files:

  • web/src/mocks/handlers/audit.ts
  • web/src/mocks/handlers/setup.ts
  • web/src/mocks/handlers/personalities.ts
  • web/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 use server.use(...) to override per-case

Files:

  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/__tests__/pages/ApprovalsPage.test.tsx
  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/__tests__/pages/ProvidersPage.test.tsx
  • web/src/__tests__/stores/fine-tuning.test.ts
  • web/src/__tests__/pages/SettingsPage.test.tsx
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/__tests__/pages/setup/WizardProgress.test.tsx
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/__tests__/utils/errors.test.ts
  • web/src/__tests__/stores/company.test.ts
  • web/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.ts which fails any test that leaks an event-loop-holding resource; zero tolerance, no ceiling, no buffer

Files:

  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/__tests__/pages/ApprovalsPage.test.tsx
  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/__tests__/pages/ProvidersPage.test.tsx
  • web/src/__tests__/stores/fine-tuning.test.ts
  • web/src/__tests__/pages/SettingsPage.test.tsx
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/__tests__/pages/setup/WizardProgress.test.tsx
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/__tests__/utils/errors.test.ts
  • web/src/__tests__/stores/company.test.ts
  • web/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 in web/CLAUDE.md

Files:

  • web/src/components/ui/list-header.tsx
  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/src/components/ui/ws-connection-banner.tsx
  • web/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/codes

Use design tokens, @/lib/motion presets, helpers from @/utils/format, and DEFAULT_CURRENCY from @/utils/currencies instead of hardcoding values

The Props interface name must be <ComponentName>Props and must be exported from the same file (e.g., AgentCardProps in agent-card.tsx)

Base UI primitives must compose Portal + Backdrop + Popup explicitly, use the render prop for polymorphism, and rely on animation state attributes (data-[open], data-[closed]) rather than data-[state=open]

The local <Slot> helper is reserved for <Button asChild> only; use Base UI's native render prop for other polymorphic components

Files:

  • web/src/components/ui/list-header.tsx
  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/src/components/ui/ws-connection-banner.tsx
  • web/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.md

Numerics in README and public docs sourced from data/runtime_stats.yaml via <!--RS:NAME--> markers

Files:

  • docs/reference/web-design-system.md
  • CLAUDE.md
  • docs/design/page-structure.md
  • web/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.md
  • CLAUDE.md
  • src/synthorg/api/controllers/setup_models.py
  • src/synthorg/api/controllers/setup_agents.py
  • docs/design/page-structure.md
  • tests/unit/api/controllers/test_setup.py
  • src/synthorg/api/controllers/setup/agent_helpers.py
  • src/synthorg/settings/definitions/api.py
  • web/CLAUDE.md
  • src/synthorg/api/controllers/setup_controller.py
  • src/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.tsx covering every meaningful state (default, hover, loading, error, empty, disabled where applicable)

Files:

  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/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. No os.environ.get outside startup; pre-init Cat-2 reads use settings.bootstrap_resolver.resolve_init_value

No 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 by scripts/check_no_magic_numbers.py

Errors: <Domain><Condition>Error from DomainError; never inherit Exception/RuntimeError/etc directly. Enforced by check_domain_error_hierarchy.py

Pydantic v2 frozen + extra="forbid" on API DTOs (Request/Response/Snapshot/Result/Envelope/Status/Info/Summary suffixes); @computed_field for derived; NotBlankStr for identifiers

Args models at every system boundary; parse_typed() for every external dict ingestion. Enforced by check_boundary_typed.py

Immutability: use model_copy(update=...) or copy.deepcopy(); deepcopy at system boundaries

Async: use asyncio.TaskGroup for fan-out/fan-in; helpers catch Exception (re-raise MemoryError/RecursionError)

Clock seam: use clock: Clock | None = None; tests inject FakeClock. Lifecycle: services own _lifecycle_lock; timed-out stops mark unrestartable

Untrusted content (SEC-1): use wrap_untrusted() from engine.prompt_safety; use HTMLParseGuard for HTML

Repository CRUD: save(entity), get(id), delete(id) -> bool, list_items(...), query(...) returning tuples

Datetime in persistence: use parse_iso_utc / format_iso_utc from persistence._shared (reject naive); use normalize_utc for already-typed

Logging: use from synthorg.observability import get_logger; variable always logger. Never import logging / print() in app c...

Files:

  • src/synthorg/api/controllers/setup_models.py
  • src/synthorg/api/controllers/setup_agents.py
  • src/synthorg/api/controllers/setup/agent_helpers.py
  • src/synthorg/settings/definitions/api.py
  • src/synthorg/api/controllers/setup_controller.py
  • src/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.py

No from __future__ import annotations (Python 3.14 has PEP 649). Use PEP 758 except: except A, B: requires parens when binding

Type 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.py
  • src/synthorg/api/controllers/setup_agents.py
  • tests/unit/api/controllers/test_setup.py
  • src/synthorg/api/controllers/setup/agent_helpers.py
  • src/synthorg/settings/definitions/api.py
  • src/synthorg/api/controllers/setup_controller.py
  • src/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.py
  • src/synthorg/api/controllers/setup_agents.py
  • src/synthorg/api/controllers/setup/agent_helpers.py
  • src/synthorg/settings/definitions/api.py
  • src/synthorg/api/controllers/setup_controller.py
  • src/synthorg/api/controllers/events.py
web/**/api/endpoints/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

List read endpoints must set error: string | null on the store instead of toasting; use cursor-based pagination via PaginationMeta with nextCursor + hasMore in state, early-returning when !hasMore || !nextCursor

Display counts must come from data.length; the wire envelope no longer carries total field

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.ts
  • web/src/api/endpoints/audit.ts
  • web/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 with 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
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=loadfile auto-applied via pyproject addopts (loadfile prevents Python 3.14+Windows ProactorEventLoop leak)

Windows: unit tests use WindowsSelectorEventLoopPolicy (3.14 IOCP teardown race). Subprocess tests override back

Test doubles: use FakeClock for Clock seam, mock_of[T](**overrides) for typed-boundary substitutions, SimpleNamespace for attribute-bags. Bare MagicMock at typed boundaries is blocked by scripts/check_mock_spec.py (zero-tolerance, no baseline)

FakeClock and mock_of import from tests._shared; inject via clock= and the helper's spec subscript

Hypothesis: 10 deterministic CI examples; failures are real bugs (fix + add @example(...))

Flaky tests: NEVER skip/xfail; fix fundamentally. Use asyncio.Event().wait() not sleep(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.py
  • src/synthorg/api/controllers/setup_agents.py
  • tests/unit/api/controllers/test_setup.py
  • src/synthorg/api/controllers/setup/agent_helpers.py
  • src/synthorg/settings/definitions/api.py
  • src/synthorg/api/controllers/setup_controller.py
  • src/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

Comment thread src/synthorg/api/controllers/setup_models.py Outdated
Comment thread tests/unit/api/controllers/test_setup.py Outdated
Comment thread web/src/api/endpoints/audit.ts Outdated
Comment thread web/src/api/endpoints/setup.ts Outdated
Comment thread web/src/pages/AdminAuditLogPage.tsx Outdated
Comment thread web/src/pages/org-edit/DepartmentCreateDialog.tsx Outdated
Comment thread web/src/pages/ProvidersPage.tsx Outdated
Comment thread web/src/stores/users.ts Outdated
Comment thread web/src/utils/errors.ts
Aureliolo added 20 commits May 16, 2026 20:52
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.
Aureliolo added 8 commits May 16, 2026 20:52
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)
@Aureliolo Aureliolo force-pushed the feat/wp6-frontend-ux-polish branch from 2d353a2 to 32c97e5 Compare May 16, 2026 18:57
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview May 16, 2026 18:59 — with GitHub Actions Inactive
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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 value

Consider adding success toasts to reorder mutations for consistency.

Other mutations (create, update, delete) show success toasts, but reorderDepartments (lines 290-310) and reorderTeams (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

📥 Commits

Reviewing files that changed from the base of the PR and between 2d353a2 and 32c97e5.

📒 Files selected for processing (116)
  • CLAUDE.md
  • docs/design/page-structure.md
  • docs/reference/web-design-system.md
  • src/synthorg/api/controllers/events.py
  • src/synthorg/api/controllers/setup/agent_helpers.py
  • src/synthorg/api/controllers/setup_agents.py
  • src/synthorg/api/controllers/setup_controller.py
  • src/synthorg/api/controllers/setup_models.py
  • src/synthorg/settings/definitions/api.py
  • tests/unit/api/controllers/test_setup.py
  • tests/unit/api/controllers/test_setup_dtos.py
  • tests/unit/api/controllers/test_sse_revalidate.py
  • web/CLAUDE.md
  • web/src/__tests__/hooks/usePolling.test.ts
  • web/src/__tests__/pages/ApprovalsPage.test.tsx
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/src/__tests__/pages/ProvidersPage.test.tsx
  • web/src/__tests__/pages/SettingsPage.test.tsx
  • web/src/__tests__/pages/setup/WizardProgress.test.tsx
  • web/src/__tests__/stores/company.test.ts
  • web/src/__tests__/stores/fine-tuning.test.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/__tests__/utils/errors.test.ts
  • web/src/api/endpoints/audit.ts
  • web/src/api/endpoints/personalities.ts
  • web/src/api/endpoints/setup.ts
  • web/src/api/types/openapi.gen.ts
  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/src/components/ui/list-header.tsx
  • web/src/components/ui/ws-connection-banner.stories.tsx
  • web/src/components/ui/ws-connection-banner.tsx
  • web/src/hooks/useApprovalsData.ts
  • web/src/hooks/useBudgetData.ts
  • web/src/hooks/useDashboardData.ts
  • web/src/hooks/useOrgEditData.ts
  • web/src/hooks/usePolling.ts
  • web/src/hooks/useProvidersData.ts
  • web/src/hooks/useScalingData.ts
  • web/src/hooks/useSettingsData.ts
  • web/src/mocks/handlers/audit.ts
  • web/src/mocks/handlers/index.ts
  • web/src/mocks/handlers/personalities.ts
  • web/src/mocks/handlers/setup.ts
  • web/src/pages/AdminAuditLogPage.tsx
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/pages/BudgetPage.tsx
  • web/src/pages/ClientListPage.tsx
  • web/src/pages/ConnectionsPage.tsx
  • web/src/pages/CustomRulesPage.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/MeetingsPage.tsx
  • web/src/pages/MetaPage.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/PersonalitiesAdminPage.tsx
  • web/src/pages/ProjectsPage.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/RequestQueuePage.tsx
  • web/src/pages/ScalingPage.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/SettingsSinksPage.tsx
  • web/src/pages/agents/AgentGridView.tsx
  • web/src/pages/budget/AgentSpendingTable.tsx
  • web/src/pages/budget/CategoryBreakdown.tsx
  • web/src/pages/org-edit/AgentCreateDialog.tsx
  • web/src/pages/org-edit/AgentEditDrawer.stories.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/org-edit/AgentsTab.stories.tsx
  • web/src/pages/org-edit/AgentsTab.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.tsx
  • web/src/pages/org-edit/TeamEditDialog.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/pages/org-edit/YamlEditorPanel.stories.tsx
  • web/src/pages/org-edit/YamlEditorPanel.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/pages/providers/AddManualModelDialog.tsx
  • web/src/pages/providers/CredentialsRotateDialog.tsx
  • web/src/pages/providers/ProviderGridView.tsx
  • web/src/pages/settings/sinks/SinkCard.tsx
  • web/src/pages/setup/AgentsStep.tsx
  • web/src/pages/setup/CompleteStep.tsx
  • web/src/pages/setup/WizardProgress.tsx
  • web/src/pages/setup/WizardShell.tsx
  • web/src/pages/setup/_hooks.ts
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/router/index.tsx
  • web/src/router/routes.ts
  • web/src/stores/artifacts.ts
  • web/src/stores/company.ts
  • web/src/stores/escalations.ts
  • web/src/stores/fine-tuning.ts
  • web/src/stores/projects.ts
  • web/src/stores/providers/crud-actions.ts
  • web/src/stores/providers/types.ts
  • web/src/stores/setup-wizard/completion.ts
  • web/src/stores/setup-wizard/navigation.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/stores/setup-wizard/types.ts
  • web/src/stores/sinks.ts
  • web/src/stores/subworkflows.ts
  • web/src/stores/users.ts
  • web/src/stores/websocket.ts
  • web/src/stores/workflows.ts
  • web/src/utils/constants.ts
  • web/src/utils/errors.ts
  • web/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; see web/CLAUDE.md

Files:

  • web/src/pages/budget/CategoryBreakdown.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/router/routes.ts
  • web/src/__tests__/pages/ProvidersPage.test.tsx
  • web/src/pages/setup/WizardShell.tsx
  • web/src/__tests__/pages/ApprovalsPage.test.tsx
  • web/src/__tests__/stores/fine-tuning.test.ts
  • web/src/stores/providers/types.ts
  • web/src/pages/org-edit/AgentsTab.stories.tsx
  • web/src/stores/subworkflows.ts
  • web/src/hooks/useProvidersData.ts
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/router/index.tsx
  • web/src/pages/CustomRulesPage.tsx
  • web/src/stores/workflows.ts
  • web/src/pages/providers/CredentialsRotateDialog.tsx
  • web/src/pages/MetaPage.tsx
  • web/src/api/types/openapi.gen.ts
  • web/src/stores/users.ts
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/hooks/useApprovalsData.ts
  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/ProjectsPage.tsx
  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/hooks/useScalingData.ts
  • web/src/stores/escalations.ts
  • web/src/stores/artifacts.ts
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/ConnectionsPage.tsx
  • web/src/pages/org-edit/YamlEditorPanel.stories.tsx
  • web/src/stores/projects.ts
  • web/src/mocks/handlers/audit.ts
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/hooks/useBudgetData.ts
  • web/src/pages/org-edit/YamlEditorPanel.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/providers/AddManualModelDialog.tsx
  • web/src/api/endpoints/setup.ts
  • web/src/pages/org-edit/AgentEditDrawer.stories.tsx
  • web/src/stores/providers/crud-actions.ts
  • web/src/__tests__/utils/errors.test.ts
  • web/src/pages/ScalingPage.tsx
  • web/src/stores/fine-tuning.ts
  • web/src/pages/settings/sinks/SinkCard.tsx
  • web/src/hooks/useSettingsData.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/__tests__/pages/SettingsPage.test.tsx
  • web/src/stores/setup-wizard/types.ts
  • web/src/components/ui/list-header.tsx
  • web/src/api/endpoints/audit.ts
  • web/src/utils/constants.ts
  • web/src/pages/setup/_hooks.ts
  • web/src/__tests__/pages/setup/WizardProgress.test.tsx
  • web/src/pages/setup/AgentsStep.tsx
  • web/src/hooks/useOrgEditData.ts
  • web/src/__tests__/stores/company.test.ts
  • web/src/mocks/handlers/setup.ts
  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/stores/setup-wizard/completion.ts
  • web/src/pages/budget/AgentSpendingTable.tsx
  • web/src/pages/providers/ProviderGridView.tsx
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/SettingsSinksPage.tsx
  • web/src/pages/org-edit/TeamEditDialog.tsx
  • web/src/pages/agents/AgentGridView.tsx
  • web/src/__tests__/hooks/usePolling.test.ts
  • web/src/stores/sinks.ts
  • web/src/pages/MeetingsPage.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/components/ui/ws-connection-banner.tsx
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/src/pages/RequestQueuePage.tsx
  • web/src/mocks/handlers/index.ts
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/BudgetPage.tsx
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/hooks/useDashboardData.ts
  • web/src/pages/OrgEditPage.tsx
  • web/src/components/ui/ws-connection-banner.stories.tsx
  • web/src/pages/org-edit/AgentsTab.tsx
  • web/src/api/endpoints/personalities.ts
  • web/src/pages/setup/WizardProgress.tsx
  • web/src/pages/setup/CompleteStep.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/stores/websocket.ts
  • web/src/pages/ClientListPage.tsx
  • web/src/mocks/handlers/personalities.ts
  • web/src/hooks/usePolling.ts
  • web/src/utils/mutation-result.ts
  • web/src/stores/setup-wizard/navigation.ts
  • web/src/utils/errors.ts
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/org-edit/AgentCreateDialog.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/pages/org-edit/DepartmentsTab.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/AdminAuditLogPage.tsx
  • web/src/stores/company.ts
  • web/src/pages/PersonalitiesAdminPage.tsx
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

Always use createLogger from @/lib/logger; never bare console.warn/console.error/console.debug in application code. Variable name must always be log (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 ErrorCode and ErrorCategory from @/api/types/errors (re-exported from web/src/api/types/error-codes.gen.ts). Discriminate on ErrorCode.<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 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. Fix every violation before proceeding.

Files:

  • web/src/pages/budget/CategoryBreakdown.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/router/routes.ts
  • web/src/__tests__/pages/ProvidersPage.test.tsx
  • web/src/pages/setup/WizardShell.tsx
  • web/src/__tests__/pages/ApprovalsPage.test.tsx
  • web/src/__tests__/stores/fine-tuning.test.ts
  • web/src/stores/providers/types.ts
  • web/src/pages/org-edit/AgentsTab.stories.tsx
  • web/src/stores/subworkflows.ts
  • web/src/hooks/useProvidersData.ts
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/router/index.tsx
  • web/src/pages/CustomRulesPage.tsx
  • web/src/stores/workflows.ts
  • web/src/pages/providers/CredentialsRotateDialog.tsx
  • web/src/pages/MetaPage.tsx
  • web/src/api/types/openapi.gen.ts
  • web/src/stores/users.ts
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/hooks/useApprovalsData.ts
  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/ProjectsPage.tsx
  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/hooks/useScalingData.ts
  • web/src/stores/escalations.ts
  • web/src/stores/artifacts.ts
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/ConnectionsPage.tsx
  • web/src/pages/org-edit/YamlEditorPanel.stories.tsx
  • web/src/stores/projects.ts
  • web/src/mocks/handlers/audit.ts
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/hooks/useBudgetData.ts
  • web/src/pages/org-edit/YamlEditorPanel.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/providers/AddManualModelDialog.tsx
  • web/src/api/endpoints/setup.ts
  • web/src/pages/org-edit/AgentEditDrawer.stories.tsx
  • web/src/stores/providers/crud-actions.ts
  • web/src/__tests__/utils/errors.test.ts
  • web/src/pages/ScalingPage.tsx
  • web/src/stores/fine-tuning.ts
  • web/src/pages/settings/sinks/SinkCard.tsx
  • web/src/hooks/useSettingsData.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/__tests__/pages/SettingsPage.test.tsx
  • web/src/stores/setup-wizard/types.ts
  • web/src/components/ui/list-header.tsx
  • web/src/api/endpoints/audit.ts
  • web/src/utils/constants.ts
  • web/src/pages/setup/_hooks.ts
  • web/src/__tests__/pages/setup/WizardProgress.test.tsx
  • web/src/pages/setup/AgentsStep.tsx
  • web/src/hooks/useOrgEditData.ts
  • web/src/__tests__/stores/company.test.ts
  • web/src/mocks/handlers/setup.ts
  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/stores/setup-wizard/completion.ts
  • web/src/pages/budget/AgentSpendingTable.tsx
  • web/src/pages/providers/ProviderGridView.tsx
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/SettingsSinksPage.tsx
  • web/src/pages/org-edit/TeamEditDialog.tsx
  • web/src/pages/agents/AgentGridView.tsx
  • web/src/__tests__/hooks/usePolling.test.ts
  • web/src/stores/sinks.ts
  • web/src/pages/MeetingsPage.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/components/ui/ws-connection-banner.tsx
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/src/pages/RequestQueuePage.tsx
  • web/src/mocks/handlers/index.ts
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/BudgetPage.tsx
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/hooks/useDashboardData.ts
  • web/src/pages/OrgEditPage.tsx
  • web/src/components/ui/ws-connection-banner.stories.tsx
  • web/src/pages/org-edit/AgentsTab.tsx
  • web/src/api/endpoints/personalities.ts
  • web/src/pages/setup/WizardProgress.tsx
  • web/src/pages/setup/CompleteStep.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/stores/websocket.ts
  • web/src/pages/ClientListPage.tsx
  • web/src/mocks/handlers/personalities.ts
  • web/src/hooks/usePolling.ts
  • web/src/utils/mutation-result.ts
  • web/src/stores/setup-wizard/navigation.ts
  • web/src/utils/errors.ts
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/org-edit/AgentCreateDialog.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/pages/org-edit/DepartmentsTab.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/AdminAuditLogPage.tsx
  • web/src/stores/company.ts
  • web/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/motion presets, helpers in @/utils/format, and DEFAULT_CURRENCY from @/utils/currencies.

Files:

  • web/src/pages/budget/CategoryBreakdown.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/pages/setup/WizardShell.tsx
  • web/src/pages/org-edit/AgentsTab.stories.tsx
  • web/src/hooks/useProvidersData.ts
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/pages/CustomRulesPage.tsx
  • web/src/pages/providers/CredentialsRotateDialog.tsx
  • web/src/pages/MetaPage.tsx
  • web/src/hooks/useApprovalsData.ts
  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/ProjectsPage.tsx
  • web/src/hooks/useScalingData.ts
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/ConnectionsPage.tsx
  • web/src/pages/org-edit/YamlEditorPanel.stories.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/hooks/useBudgetData.ts
  • web/src/pages/org-edit/YamlEditorPanel.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/providers/AddManualModelDialog.tsx
  • web/src/pages/org-edit/AgentEditDrawer.stories.tsx
  • web/src/pages/ScalingPage.tsx
  • web/src/pages/settings/sinks/SinkCard.tsx
  • web/src/hooks/useSettingsData.ts
  • web/src/components/ui/list-header.tsx
  • web/src/pages/setup/_hooks.ts
  • web/src/pages/setup/AgentsStep.tsx
  • web/src/hooks/useOrgEditData.ts
  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/budget/AgentSpendingTable.tsx
  • web/src/pages/providers/ProviderGridView.tsx
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/SettingsSinksPage.tsx
  • web/src/pages/org-edit/TeamEditDialog.tsx
  • web/src/pages/agents/AgentGridView.tsx
  • web/src/pages/MeetingsPage.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/components/ui/ws-connection-banner.tsx
  • web/src/pages/RequestQueuePage.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/BudgetPage.tsx
  • web/src/hooks/useDashboardData.ts
  • web/src/pages/OrgEditPage.tsx
  • web/src/components/ui/ws-connection-banner.stories.tsx
  • web/src/pages/org-edit/AgentsTab.tsx
  • web/src/pages/setup/WizardProgress.tsx
  • web/src/pages/setup/CompleteStep.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/ClientListPage.tsx
  • web/src/hooks/usePolling.ts
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/org-edit/AgentCreateDialog.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/pages/org-edit/DepartmentsTab.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/AdminAuditLogPage.tsx
  • web/src/pages/PersonalitiesAdminPage.tsx
web/src/{components,pages,hooks}/**/*.tsx

📄 CodeRabbit inference engine (web/CLAUDE.md)

Use useViewportSize() from @/hooks/useViewportSize for viewport-size reads. NEVER read window.innerWidth / window.innerHeight directly in a render body or useMemo.

Files:

  • web/src/pages/budget/CategoryBreakdown.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/pages/setup/WizardShell.tsx
  • web/src/pages/org-edit/AgentsTab.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/pages/CustomRulesPage.tsx
  • web/src/pages/providers/CredentialsRotateDialog.tsx
  • web/src/pages/MetaPage.tsx
  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/ProjectsPage.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/ConnectionsPage.tsx
  • web/src/pages/org-edit/YamlEditorPanel.stories.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/org-edit/YamlEditorPanel.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/providers/AddManualModelDialog.tsx
  • web/src/pages/org-edit/AgentEditDrawer.stories.tsx
  • web/src/pages/ScalingPage.tsx
  • web/src/pages/settings/sinks/SinkCard.tsx
  • web/src/components/ui/list-header.tsx
  • web/src/pages/setup/AgentsStep.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/budget/AgentSpendingTable.tsx
  • web/src/pages/providers/ProviderGridView.tsx
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/SettingsSinksPage.tsx
  • web/src/pages/org-edit/TeamEditDialog.tsx
  • web/src/pages/agents/AgentGridView.tsx
  • web/src/pages/MeetingsPage.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/components/ui/ws-connection-banner.tsx
  • web/src/pages/RequestQueuePage.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/BudgetPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/components/ui/ws-connection-banner.stories.tsx
  • web/src/pages/org-edit/AgentsTab.tsx
  • web/src/pages/setup/WizardProgress.tsx
  • web/src/pages/setup/CompleteStep.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/ClientListPage.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/org-edit/AgentCreateDialog.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/pages/org-edit/DepartmentsTab.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/AdminAuditLogPage.tsx
  • web/src/pages/PersonalitiesAdminPage.tsx
web/src/{components,pages}/**/*.tsx

📄 CodeRabbit inference engine (web/CLAUDE.md)

For ReactNode | undefined props use {value != null && value !== false && <jsx>}; for compound truthiness use Boolean(...); avoid the {count && <Foo />} bug where 0 renders verbatim.

Files:

  • web/src/pages/budget/CategoryBreakdown.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/pages/setup/WizardShell.tsx
  • web/src/pages/org-edit/AgentsTab.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/pages/CustomRulesPage.tsx
  • web/src/pages/providers/CredentialsRotateDialog.tsx
  • web/src/pages/MetaPage.tsx
  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/src/pages/DashboardPage.tsx
  • web/src/pages/ProjectsPage.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/ConnectionsPage.tsx
  • web/src/pages/org-edit/YamlEditorPanel.stories.tsx
  • web/src/pages/ProviderDetailPage.tsx
  • web/src/pages/org-edit/YamlEditorPanel.tsx
  • web/src/pages/SettingsPage.tsx
  • web/src/pages/providers/AddManualModelDialog.tsx
  • web/src/pages/org-edit/AgentEditDrawer.stories.tsx
  • web/src/pages/ScalingPage.tsx
  • web/src/pages/settings/sinks/SinkCard.tsx
  • web/src/components/ui/list-header.tsx
  • web/src/pages/setup/AgentsStep.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/budget/AgentSpendingTable.tsx
  • web/src/pages/providers/ProviderGridView.tsx
  • web/src/pages/AgentsPage.tsx
  • web/src/pages/SettingsSinksPage.tsx
  • web/src/pages/org-edit/TeamEditDialog.tsx
  • web/src/pages/agents/AgentGridView.tsx
  • web/src/pages/MeetingsPage.tsx
  • web/src/pages/ApprovalsPage.tsx
  • web/src/components/ui/ws-connection-banner.tsx
  • web/src/pages/RequestQueuePage.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/BudgetPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/components/ui/ws-connection-banner.stories.tsx
  • web/src/pages/org-edit/AgentsTab.tsx
  • web/src/pages/setup/WizardProgress.tsx
  • web/src/pages/setup/CompleteStep.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/ClientListPage.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/org-edit/AgentCreateDialog.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/pages/org-edit/DepartmentsTab.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/AdminAuditLogPage.tsx
  • web/src/pages/PersonalitiesAdminPage.tsx
web/src/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

test-setup.tsx boots with onUnhandledRequest: 'error'; tests override per-case via server.use(...), never vi.mock('@/api/endpoints/*').

Every unit test runs under web/test-infra/active-handle-tracker.ts, which hooks Node's async_hooks and fails any test that leaks an event-loop-holding resource. Zero tolerance, no ceiling, no buffer.

Files:

  • web/src/__tests__/pages/ProvidersPage.test.tsx
  • web/src/__tests__/pages/ApprovalsPage.test.tsx
  • web/src/__tests__/stores/fine-tuning.test.ts
  • web/src/__tests__/stores/websocket.test.ts
  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/__tests__/utils/errors.test.ts
  • web/src/__tests__/pages/SettingsPage.test.tsx
  • web/src/__tests__/pages/setup/WizardProgress.test.tsx
  • web/src/__tests__/stores/company.test.ts
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/__tests__/hooks/usePolling.test.ts
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/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: null for entity-returning mutations, false for boolean-returning mutations. Every void / boolean-returning mutation uses false regardless of operation type.

List reads (fetch*) set error: string | null on 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 afterEach in web/src/test-setup.tsx.

Files:

  • web/src/stores/providers/types.ts
  • web/src/stores/subworkflows.ts
  • web/src/stores/workflows.ts
  • web/src/stores/users.ts
  • web/src/stores/escalations.ts
  • web/src/stores/artifacts.ts
  • web/src/stores/projects.ts
  • web/src/stores/providers/crud-actions.ts
  • web/src/stores/fine-tuning.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/stores/setup-wizard/types.ts
  • web/src/stores/setup-wizard/completion.ts
  • web/src/stores/sinks.ts
  • web/src/stores/websocket.ts
  • web/src/stores/setup-wizard/navigation.ts
  • web/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 keep nextCursor + hasMore in state (not offset arithmetic) and early-return when !hasMore || !nextCursor. Display counts come from data.length; the wire envelope no longer carries total.

Files:

  • web/src/stores/providers/types.ts
  • web/src/stores/subworkflows.ts
  • web/src/stores/workflows.ts
  • web/src/stores/users.ts
  • web/src/stores/escalations.ts
  • web/src/stores/artifacts.ts
  • web/src/stores/projects.ts
  • web/src/api/endpoints/setup.ts
  • web/src/stores/providers/crud-actions.ts
  • web/src/stores/fine-tuning.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/stores/setup-wizard/types.ts
  • web/src/api/endpoints/audit.ts
  • web/src/stores/setup-wizard/completion.ts
  • web/src/stores/sinks.ts
  • web/src/api/endpoints/personalities.ts
  • web/src/stores/websocket.ts
  • web/src/stores/setup-wizard/navigation.ts
  • web/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 with uv run python scripts/generate_dto_types_ts.py; drift enforced at pre-push by scripts/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 native render prop for polymorphism; the local <Slot> helper is reserved for <Button asChild> only.

The Props interface name is <ComponentName>Props and must be exported from the same file (e.g., AgentCardProps in agent-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.tsx
  • web/src/components/ui/list-header.tsx
  • web/src/components/ui/ws-connection-banner.tsx
  • web/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.tsx covering all states (default, hover, loading, error, empty, disabled where applicable).

Base UI primitives compose Portal + Backdrop + Popup explicitly, use the render prop for polymorphism, and rely on animation state attributes (data-[open], data-[closed]) rather than data-[state=open] form.

Files:

  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/src/components/ui/list-header.tsx
  • web/src/components/ui/ws-connection-banner.tsx
  • web/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-docs is now separate; imports moved to storybook/test and storybook/actions.

Files:

  • web/src/components/ui/lazy-code-mirror-editor.stories.tsx
  • web/src/components/ui/ws-connection-banner.stories.tsx
web/src/mocks/handlers/**/*.ts

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/mocks/handlers/ must mirror web/src/api/endpoints/*.ts 1: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.ts
  • web/src/mocks/handlers/setup.ts
  • web/src/mocks/handlers/index.ts
  • web/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.ts
  • web/src/api/endpoints/audit.ts
  • web/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.ts pattern: wrap the API call in try / 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/errors on 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.yaml via <!--RS:NAME--> markers per data/README.md

Files:

  • docs/design/page-structure.md
  • docs/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.ts and MUST stay in lockstep with src/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.py
  • src/synthorg/api/controllers/setup_models.py
  • src/synthorg/api/controllers/setup_controller.py
  • src/synthorg/api/controllers/setup_agents.py
  • src/synthorg/api/controllers/setup/agent_helpers.py
  • src/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 use settings.bootstrap_resolver.resolve_init_value; no os.environ.get outside startup per configuration-precedence.md

No 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 by scripts/check_no_magic_numbers.py

Comments explain WHY only; no reviewer citations, issue back-refs, or migration framing; enforced by check_no_review_origin_in_code.py and check_no_migration_framing.py

No from __future__ import annotations (Python 3.14 has PEP 649); use PEP 758 style except except A, B: requires parens when binding

Type 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>Error pattern inheriting from DomainError; never inherit Exception/RuntimeError directly; enforced by check_domain_error_hierarchy.py

Pydantic v2 frozen + extra='forbid' on API DTOs (Request/Response/Snapshot/Result/Envelope/Status/Info/Summary suffixes); use @computed_field for derived; use NotBlankStr for identifiers

Args models at every system boundary; parse_typed() for every external dict ingestion; enforced by check_boundary_typed.py

Use immutability patterns: model_copy(update=...) or copy.deepcopy(); deepcopy at system boundaries

Async code uses asyncio.TaskGroup for fan-out/fan-in; helpers catch Exception and re-raise MemoryError/RecursionError

Clock seam pattern: inject clock: Clock | None = None; tests inject FakeClock; lifecycle services own _lifecycle_lock; timed-out stops mark unrestartable

Untrusted content (SEC-1): use wrap_untrusted() from engine.prompt_safety; use `H...

Files:

  • src/synthorg/settings/definitions/api.py
  • src/synthorg/api/controllers/setup_models.py
  • src/synthorg/api/controllers/setup_controller.py
  • src/synthorg/api/controllers/setup_agents.py
  • src/synthorg/api/controllers/setup/agent_helpers.py
  • src/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.py
  • tests/unit/api/controllers/test_setup_dtos.py
  • tests/unit/api/controllers/test_sse_revalidate.py
  • src/synthorg/api/controllers/setup_models.py
  • src/synthorg/api/controllers/setup_controller.py
  • src/synthorg/api/controllers/setup_agents.py
  • src/synthorg/api/controllers/setup/agent_helpers.py
  • tests/unit/api/controllers/test_setup.py
  • src/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.py
  • src/synthorg/api/controllers/setup_models.py
  • src/synthorg/api/controllers/setup_controller.py
  • src/synthorg/api/controllers/setup_agents.py
  • src/synthorg/api/controllers/setup/agent_helpers.py
  • src/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 minimum

Windows: unit tests use WindowsSelectorEventLoopPolicy; subprocess tests override back

Test doubles: use ladder from conventions.md; FakeClock for Clock seam, mock_of[T](**overrides) for typed-boundary substitutions, SimpleNamespace for attribute-bags; bare MagicMock at typed boundaries is blocked by check_mock_spec.py

FakeClock and mock_of imported from tests._shared; inject via clock= parameter and helper's spec subscript

Hypothesis: 10 deterministic CI examples; failures are real bugs (fix + add @example(...))

Flaky tests: NEVER skip/xfail; fix fundamentally; use asyncio.Event().wait() not sleep(large)

Files:

  • tests/unit/api/controllers/test_setup_dtos.py
  • tests/unit/api/controllers/test_sse_revalidate.py
  • 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_dtos.py
  • tests/unit/api/controllers/test_sse_revalidate.py
  • tests/unit/api/controllers/test_setup.py
src/synthorg/api/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

API startup lifecycle: construction phase (create_app body) wires synchronous services; on_startup phase wires services needing persistence backend; invariants: agent_registry before auto_wire_meetings; tunnel_provider always wired; on-startup: SettingsService before WorkflowExecutionObserver; OntologyService after persistence.connect()

Files:

  • src/synthorg/api/controllers/setup_models.py
  • src/synthorg/api/controllers/setup_controller.py
  • src/synthorg/api/controllers/setup_agents.py
  • src/synthorg/api/controllers/setup/agent_helpers.py
  • src/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 under COMPLETE_LOCK to 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.md
  • docs/design/page-structure.md
  • web/CLAUDE.md
  • 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:

  • CLAUDE.md
  • docs/design/page-structure.md
  • web/CLAUDE.md
  • 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/design/page-structure.md
  • 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/design/page-structure.md
  • docs/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.py
  • tests/unit/api/controllers/test_setup_dtos.py
  • tests/unit/api/controllers/test_sse_revalidate.py
  • src/synthorg/api/controllers/setup_models.py
  • src/synthorg/api/controllers/setup_controller.py
  • src/synthorg/api/controllers/setup_agents.py
  • src/synthorg/api/controllers/setup/agent_helpers.py
  • tests/unit/api/controllers/test_setup.py
  • src/synthorg/api/controllers/events.py

Comment thread docs/reference/web-design-system.md Outdated
Comment thread web/src/api/endpoints/personalities.ts Outdated
Comment thread web/src/mocks/handlers/audit.ts Outdated
Comment thread web/src/pages/org-edit/AgentCreateDialog.tsx
Comment thread web/src/pages/org-edit/AgentEditDrawer.tsx
Comment thread web/src/pages/PersonalitiesAdminPage.tsx Outdated
Comment thread web/src/pages/PersonalitiesAdminPage.tsx
Comment thread web/src/pages/setup/CompleteStep.tsx
Comment thread web/src/stores/providers/crud-actions.ts Outdated
Comment thread web/src/stores/providers/types.ts Outdated
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)
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview May 16, 2026 19:22 — with GitHub Actions Inactive
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against 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

📥 Commits

Reviewing files that changed from the base of the PR and between 32c97e5 and eb5aa0c.

📒 Files selected for processing (10)
  • docs/reference/web-design-system.md
  • web/src/api/endpoints/personalities.ts
  • web/src/mocks/handlers/audit.ts
  • web/src/pages/PersonalitiesAdminPage.tsx
  • web/src/pages/org-edit/AgentCreateDialog.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/stores/providers/crud-actions.ts
  • web/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 in web/CLAUDE.md

Always 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 a total field in the wire envelope

Use useViewportSize() from @/hooks/useViewportSize for viewport-size reads; NEVER read window.innerWidth / window.innerHeight directly in render bodies or useMemo

Use @typescript-eslint/no-floating-promises to forbid unawaited promises so async work cannot survive the test and trip the active-handle gate

Use @typescript-eslint/no-misused-promises (with checksVoidReturn: { attributes: false }) to forbid passing async functions where callsite ignores the promise; React 19 async event handlers stay allowed

Files:

  • web/src/stores/providers/types.ts
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/mocks/handlers/audit.ts
  • web/src/pages/org-edit/AgentCreateDialog.tsx
  • web/src/pages/PersonalitiesAdminPage.tsx
  • web/src/stores/providers/crud-actions.ts
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/api/endpoints/personalities.ts
web/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

Use createLogger from @/lib/logger for all logging; never use bare console.warn, console.error, or console.debug in application code

Pass dynamic/untrusted values as separate arguments to logger, not interpolated into the message string, so they go through sanitizeArg

Wrap attacker-controlled fields inside structured objects in sanitizeForLog() before embedding in logs

Use @eslint-react/web-api-no-leaked-fetch to detect fetch() in effects without AbortController cleanup

Files:

  • web/src/stores/providers/types.ts
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/mocks/handlers/audit.ts
  • web/src/pages/org-edit/AgentCreateDialog.tsx
  • web/src/pages/PersonalitiesAdminPage.tsx
  • web/src/stores/providers/crud-actions.ts
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/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.ts

Store mutation actions must return a sentinel on failure: null for entity-returning mutations, false for boolean-returning mutations

Use getCrudErrorTitle(err, fallback) from @/utils/errors on every error toast for distinct titles on 409 conflicts

List reads (fetch*) must set error: string | null on 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 afterEach in web/src/test-setup.tsx

sanitizeWsEnum<T>(value, allowlist, fallback, { field }) validates unknown WS enum values against an allowlist, emits a structured ws.enum.unknown warning, and returns the supplied fallback

Files:

  • web/src/stores/providers/types.ts
  • web/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 keep nextCursor + hasMore in state and early-return when !hasMore || !nextCursor

Files:

  • web/src/stores/providers/types.ts
  • web/src/stores/providers/crud-actions.ts
  • web/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.ts
  • web/src/stores/providers/crud-actions.ts
web/src/{stores,utils}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

sanitizeWsString() and sanitizeWsEnum() 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.ts
  • web/src/stores/providers/crud-actions.ts
web/src/{stores,components,api}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

Import ErrorCode and ErrorCategory from @/api/types/errors and discriminate on ErrorCode.<NAME>, never on raw integer literals

Import DTOs via the barrel import: import type { AgentConfig } from '@/api/types'

Files:

  • web/src/stores/providers/types.ts
  • web/src/stores/providers/crud-actions.ts
  • web/src/api/endpoints/personalities.ts
docs/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Numerics in README and public docs sourced from data/runtime_stats.yaml via <!--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 mirror web/src/api/endpoints/*.ts 1:1 with a default happy-path 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.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!

Comment thread web/src/stores/providers/crud-actions.ts
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
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview May 16, 2026 19:41 — with GitHub Actions Inactive
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against 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

📥 Commits

Reviewing files that changed from the base of the PR and between eb5aa0c and c5ef146.

📒 Files selected for processing (5)
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/pages/setup/CompleteStep.tsx
  • web/src/pages/setup/SkipWizardForm.tsx
  • web/src/stores/providers/crud-actions.ts
  • web/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 in web/CLAUDE.md

Files:

  • web/src/stores/providers/crud-actions.ts
  • web/src/pages/setup/SkipWizardForm.tsx
  • web/src/pages/setup/CompleteStep.tsx
  • web/src/stores/setup-wizard/completion.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

Always use createLogger from @/lib/logger; never use bare console.warn, console.error, or console.debug in application code

Use camelCase for variable name: always log when 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 sanitizeArg

Wrap attacker-controlled fields inside structured objects with sanitizeForLog() before embedding in logs

Files:

  • web/src/stores/providers/crud-actions.ts
  • web/src/pages/setup/SkipWizardForm.tsx
  • web/src/pages/setup/CompleteStep.tsx
  • web/src/stores/setup-wizard/completion.ts
  • web/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.ts pattern: wrap API calls in try/catch, success path updates state and emits success toast, failure path logs and emits error toast and returns a sentinel on failure

Sentinel shapes for mutations mirror the mutation's return type: null for entity-returning mutations, false for boolean-returning mutations; void/boolean-returning mutations use false regardless of operation type

Use getCrudErrorTitle(err, fallback) from @/utils/errors on every error toast to distinguish between duplicate-resource, version-conflict, generic-conflict 409s, and other error types

List reads (fetch*) must set error: string | null on the store instead of toasting

Cursor pagination (MANDATORY): list endpoints must use opaque cursor-based paging via PaginationMeta. Stores must keep nextCursor and hasMore in state (not offset arithmetic) and early-return when !hasMore || !nextCursor

Any new store that schedules timers or attaches event listeners must expose an equivalent cleanup hook and register it in the global afterEach defined in web/src/test-setup.tsx

WS 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 structured ws.enum.unknown warning on unknown values and returns the supplied fallback

Any new WS payload handler ingesting untrusted strings MUST route through sanitizeWsString() or sanitizeWsEnum(); raw casts like (sanitizeWsString(x, n) ?? '') as EnumType are forbidden

Files:

  • web/src/stores/providers/crud-actions.ts
  • web/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 UX

Display counts for paginated lists come from data.length; the wire envelope no longer carries total

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

Import ErrorCode and ErrorCategory from @/api/types/errors (re-exported from web/src/api/types/error-codes.gen.ts); discriminate on ErrorCode.<NAME>, never on raw integer literals

NEVER hardcode hex colors, font-family declarations, pixel spacing, Motion transition durations, BCP 47 locale literals, or currency symbols/codes; use design tokens, @/lib/motion presets, @/utils/format helpers, and DEFAULT_CURRENCY

@typescript-eslint/no-floating-promises forbids unawaited promises so async work cannot survive the test that scheduled it; all promises must be awaited

@typescript-eslint/no-misused-promises with checksVoidReturn: { attributes: false } forbids passing async functions where callsites ignore the returned promise; React 19 async event handlers stay allowed via attributes: false exemption

Files:

  • web/src/stores/providers/crud-actions.ts
  • web/src/pages/setup/SkipWizardForm.tsx
  • web/src/pages/setup/CompleteStep.tsx
  • web/src/stores/setup-wizard/completion.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
web/src/**/*.tsx

📄 CodeRabbit inference engine (web/CLAUDE.md)

Use useViewportSize() from @/hooks/useViewportSize for reading viewport size; NEVER read window.innerWidth / window.innerHeight directly in render bodies or useMemo

For ReactNode | undefined props use {value != null && value !== false && <jsx>}; for compound truthiness use Boolean(...); never use the bug pattern {count && <Foo />} which renders 0 verbatim

Files:

  • web/src/pages/setup/SkipWizardForm.tsx
  • web/src/pages/setup/CompleteStep.tsx
web/src/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

test-setup.tsx boots MSW with onUnhandledRequest: 'error'; tests override per-case via server.use(...), never vi.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

Comment thread web/src/pages/setup/SkipWizardForm.tsx
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
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview May 16, 2026 19:58 — with GitHub Actions Inactive
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against 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

📥 Commits

Reviewing files that changed from the base of the PR and between c5ef146 and b7ab105.

📒 Files selected for processing (2)
  • web/src/__tests__/stores/websocket.test.ts
  • web/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.md for web-specific conventions.

Files:

  • web/src/pages/setup/SkipWizardForm.tsx
  • web/src/__tests__/stores/websocket.test.ts
web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

Always use createLogger from @/lib/logger; never use bare console.warn, console.error, or console.debug in application code. Variable name must be const log = createLogger('module-name'). Only logger.ts itself 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 through sanitizeArg. Attacker-controlled fields inside structured objects must be wrapped in sanitizeForLog() before embedding.

Error-code constants are mandatory: import ErrorCode and ErrorCategory from @/api/types/errors (re-exported from generated web/src/api/types/error-codes.gen.ts). Discriminate on ErrorCode.<NAME>, never on raw integer literals. Drift is enforced at pre-push by scripts/check_error_codes_ts_in_sync.py.

NEVER read window.innerWidth / window.innerHeight directly in a render body or useMemo. Use useViewportSize() from @/hooks/useViewportSize instead. This is enforced by react-x/globals and prevents stale reads across resizes.

Use @eslint-react/eslint-plugin v5+ via the recommended-type-checked preset (requires parserOptions.projectService: true in web/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 use Boolean(...).

Enable @typescript-eslint/no-floating-promises to forbid unawaited promises so async work cannot survive the test that scheduled it and trip the active-handle gate.

Enable @typescript-eslint/no-misused-promises with checksVoidReturn: { attributes: false } to forbid passing async functions where the callsite ignores the promise. React 19 async event handlers stay allowed via the attributes: false exemption, paired with the global e...

Files:

  • web/src/pages/setup/SkipWizardForm.tsx
  • web/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.ts pattern: 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: null for entity-returning mutations, false for boolean-returning mutations. Use getCrudErrorTitle(err, fallback) on every error toast. Callers MUST NOT wrap store mutation calls in try/catch.

List reads (fetch*) must set error: string | null on the store instead of toasting.

Cursor pagination is mandatory: list endpoints must use opaque cursor-based paging via PaginationMeta. Stores must keep nextCursor + hasMore in state (not offset arithmetic) and early-return when !hasMore || !nextCursor. Display counts come from data.length; the wire envelope no longer carries total.

Test teardown is mandatory: web/src/test-setup.tsx registers a global afterEach that calls useToastStore.getState().dismissAll(), cancelPendingPersist() (notifications store), and useThemeStore.getState().teardown(). Any new store that schedules timers or attaches event listeners must expose an equivalent cleanup hook and register it in the global afterEach. The websocket store is a deliberate exception (file-local resetStore() in its test file).

Active-handle gate is mandatory: every unit test runs under web/test-infra/active-handle-tracker.ts, which hooks Node's async_hooks and 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 global afterEach; otherwise the gate fails.

WS payload sanitization is mandatory: use sanitizeWsString() and sanitizeWsEnum() from web/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

Comment thread web/src/__tests__/stores/websocket.test.ts Outdated
- 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
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview May 16, 2026 20:11 — with GitHub Actions Inactive
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview May 16, 2026 20:21 — with GitHub Actions Inactive
@Aureliolo Aureliolo merged commit d9ca76d into main May 16, 2026
68 of 69 checks passed
@Aureliolo Aureliolo deleted the feat/wp6-frontend-ux-polish branch May 16, 2026 20:22
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview May 16, 2026 20:22 — with GitHub Actions Inactive
Aureliolo pushed a commit that referenced this pull request May 17, 2026
<!-- 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WP-6: Frontend + UX Polish

1 participant