feat(codegen): pydantic-to-typescript DTO pipeline + parity gate (closes #1889)#1909
Conversation
Dependency ReviewThe following issues were found:
OpenSSF ScorecardScorecard details
Scanned Files
|
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (152)
WalkthroughAdds a hermetic Python generator and unit tests that emit three deterministic TypeScript artifacts (openapi.gen.ts, dtos.gen.ts, enum-values.gen.ts); a wrapper drift-check script; pre-commit and CI wiring (dashboard-build step and local hook); and bulk frontend migration to consume generated DTOs/enums. Also updates provider creation/sync plumbing, Storybook fixtures, mocks, tests, and multiple UI paths for nullish safety and shape changes. Suggested labels
|
There was a problem hiding this comment.
Code Review
This pull request introduces an automated pipeline for generating TypeScript DTOs and enums from backend Pydantic models using OpenAPI, adding generation and validation scripts to the CI workflow. The frontend type system is refactored to use these generated sources, with existing type files updated to act as refined shims. Feedback identifies a missing autonomy_level field in the AgentConfig shim, redundant and local imports in the generator script, and a recommendation to remove trailing newlines from docstrings to ensure consistent documentation rendering.
| 'department' | 'level' | 'personality' | 'model' | 'memory' | 'tools' | 'authority' | 'autonomy_level' | ||
| > & { | ||
| id?: string | ||
| name: string | ||
| role: string | ||
| status?: AgentStatus | ||
| department: DepartmentName | ||
| level: SeniorityLevel | ||
| status?: AgentStatus | ||
| personality: Record<string, unknown> | ||
| personality_preset?: string | null | ||
| strategic_output_mode?: StrategicOutputMode | null |
There was a problem hiding this comment.
The field autonomy_level is omitted from WireAgentConfig but not re-added to the AgentConfig type, which means it will be missing from the frontend model despite being introduced to the system in this PR. Given that AutonomyLevel is imported at line 7, it should be included here. Additionally, for consistency with other shims in this PR (like tasks.ts), fields that are redefined in the intersection (like strategic_output_mode, personality_preset, and tier) should be added to the Omit list to avoid potential type conflicts.
| 'department' | 'level' | 'personality' | 'model' | 'memory' | 'tools' | 'authority' | 'autonomy_level' | |
| > & { | |
| id?: string | |
| name: string | |
| role: string | |
| status?: AgentStatus | |
| department: DepartmentName | |
| level: SeniorityLevel | |
| status?: AgentStatus | |
| personality: Record<string, unknown> | |
| personality_preset?: string | null | |
| strategic_output_mode?: StrategicOutputMode | null | |
| 'department' | 'level' | 'personality' | 'model' | 'memory' | 'tools' | 'authority' | 'autonomy_level' | 'strategic_output_mode' | 'personality_preset' | 'tier' | 'model_requirement' | |
| > & { | |
| id?: string | |
| status?: AgentStatus | |
| department: DepartmentName | |
| level: SeniorityLevel | |
| personality: Record<string, unknown> | |
| personality_preset?: string | null | |
| strategic_output_mode?: StrategicOutputMode | null | |
| autonomy_level: AutonomyLevel | null |
| up automatically. Conflicts (two distinct classes sharing a name) | ||
| drop the entry to avoid a silent wrong mapping. | ||
| """ | ||
| import sys |
| with the class's own docstring (or removing it when the class has | ||
| none) makes the export byte-stable. | ||
| """ | ||
| import inspect |
| continue | ||
| docstring = inspect.getdoc(cls) | ||
| if docstring: | ||
| defn["description"] = f"{docstring}\n" |
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
web/src/stores/setup-wizard/providers.ts (1)
90-139:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd mandatory success/error toast emission in this mutation action.
createProviderFromPresetupdates state and returns sentinels, but it still does not emit a success toast on success or an error toast on failure. This breaks the store mutation contract used across the app.Suggested patch
async createProviderFromPreset(presetName, name, apiKey, baseUrl) { set({ providersError: null, providersWarning: null }) @@ try { const provider = await createFromPreset({ @@ set((s) => ({ providers: { ...s.providers, [name]: provider } })) + useToastStore.getState().add({ + variant: 'success', + title: 'Provider created', + description: `Provider '${name}' was added successfully.`, + }) @@ } catch (err) { const msg = getErrorMessage(err) log.error('createProviderFromPreset failed:', msg) set({ providersError: msg }) + useToastStore.getState().add({ + variant: 'error', + title: 'Failed to create provider', + description: msg, + }) return { ok: false, error: msg } } },As per coding guidelines: “All store mutation actions (create/update/delete) must wrap the API call in try/catch, with success path updating state + emitting a success toast, failure path logging + emitting an error toast + returning a sentinel.”
🤖 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/setup-wizard/providers.ts` around lines 90 - 139, The createProviderFromPreset mutation currently updates state and returns sentinels but omits emitting toasts; update createProviderFromPreset so the success path (before returning { ok: true } or when returning { ok: true, warning }) calls the app toast success emitter (e.g., toast.success or showToastSuccess) with a concise message including the provider name, and the failure path inside the outer catch logs and sets providersError but must also call the error toast emitter (e.g., toast.error or showToastError) with the error message (use getErrorMessage(err)) before returning { ok: false, error: msg }; keep the existing log.error/log.warn and providersWarning/providersError updates intact. Ensure you add these toast calls in the same scopes as set() and returns within createProviderFromPreset so all mutation paths emit the appropriate toast.web/src/stores/meetings.ts (1)
319-340:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAllow optional WS fields in the shape guard so these new fallbacks can execute.
Line 319 and Line 340 add nullish defaults, but
isMeetingShapecurrently rejects payloads whentoken_usage_by_participantorminutesisundefined, so these events are dropped beforesanitizeMeetingruns.Suggested fix
function isMeetingMinutesShape(value: unknown): boolean { - if (value === null) return true + if (value === null || value === undefined) return true if (typeof value !== 'object' || Array.isArray(value)) return false const m = value as Record<string, unknown> ... } function isMeetingShape( c: Record<string, unknown>, ): c is Record<string, unknown> & MeetingResponse { return ( typeof c.meeting_id === 'string' && typeof c.status === 'string' && typeof c.meeting_type_name === 'string' && typeof c.protocol_type === 'string' && Number.isFinite(c.token_budget) && typeof c.token_budget === 'number' && c.token_budget >= 0 && Array.isArray(c.contribution_rank) && c.contribution_rank.every((entry) => typeof entry === 'string') && - isTokenUsageMap(c.token_usage_by_participant) && - isMeetingMinutesShape(c.minutes) && + (c.token_usage_by_participant === undefined || isTokenUsageMap(c.token_usage_by_participant)) && + isMeetingMinutesShape(c.minutes) && (c.error_message === null || typeof c.error_message === 'string') && (c.meeting_duration_seconds === null || (typeof c.meeting_duration_seconds === 'number' && Number.isFinite(c.meeting_duration_seconds) && c.meeting_duration_seconds >= 0)) ) }🤖 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/meetings.ts` around lines 319 - 340, The shape guard is rejecting payloads when optional fields like token_usage_by_participant or minutes are undefined so sanitizeMeeting's nullish defaults never run; update isMeetingShape to allow these properties to be optional (accept undefined) — specifically permit token_usage_by_participant to be absent or undefined and minutes to be absent/undefined in the shape check so sanitizeMeeting (and its calls to sanitizeWsString, sanitizeMeetingMinutes, etc.) can execute its fallbacks and sanitizers on incoming events.
♻️ Duplicate comments (1)
web/src/pages/org-edit/DepartmentCreateDialog.tsx (1)
62-62:⚠️ Potential issue | 🟡 MinorSame
autonomy_levelconcern as inAgentEditDrawer.tsx.This file has the same pattern of setting
autonomy_level: nullwithout UI to modify it. See the verification comment onweb/src/pages/org-edit/AgentEditDrawer.tsxline 93.🤖 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/DepartmentCreateDialog.tsx` at line 62, DepartmentCreateDialog currently initializes department data with autonomy_level: null but provides no UI to set it (same issue as AgentEditDrawer), so either remove autonomy_level from the initial payload or add a controlled form input to edit it; update the initial state in DepartmentCreateDialog (the object where autonomy_level is set) to match the change you made in AgentEditDrawer.tsx (either delete the autonomy_level property or wire it to a new form field and handlers that validate/convert values before submit in the submit/save handler).
🤖 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 `@CLAUDE.md`:
- Line 45: Replace the hard-coded "43" in CLAUDE.md with the runtime-stats
marker so the gate count is sourced from data/runtime_stats.yaml; specifically
update the line that currently reads "-
[docs/reference/convention-gates.md](docs/reference/convention-gates.md): gate
inventory (43 enforcement gates + meta-gate + PreToolUse hooks)" to use the
marker <!--RS:convention_gates--> (i.e., "... (<!--RS:convention_gates-->
enforcement gates + meta-gate + PreToolUse hooks)") so the document reflects the
actual count (45) from the runtime_stats source.
In `@web/src/__tests__/stores/setup-wizard.test.ts`:
- Around line 928-933: The test fixtures for custom providers use an empty
driver string which no longer matches production; update the fixtures that
construct provider objects (e.g., the object literal with keys driver,
auth_type, base_url, tos_accepted, models) to set driver: 'litellm' instead of
driver: '' so tests mirror the real create flow; apply the same change to the
other fixture instance referenced (the second occurrence around the later block)
to keep both fixtures consistent.
In `@web/src/hooks/useConnectionsData.ts`:
- Around line 45-46: The filter logic in useConnectionsData.ts currently checks
health via healthMap[conn.name]?.status ?? conn.health_status which omits
null/undefined statuses when healthFilter === 'unknown'; update the filtering to
use the same fallback used for sorting (e.g., const connHealth =
healthMap[conn.name]?.status ?? conn.health_status ?? 'unknown') and compare
connHealth to healthFilter (including the 'unknown' string) so connections with
missing status are included when filtering for 'unknown' (refer to aHealth,
bHealth, healthMap, healthFilter, conn.name, conn.health_status).
In `@web/src/pages/org/useOrgChartDragDrop.ts`:
- Around line 99-101: Validate newDept using the isDepartmentName() type guard
before calling optimisticReassignAgent instead of casting; if
isDepartmentName(newDept) is false, skip the optimisticReassignAgent call (and
avoid updating state) or handle the error path, otherwise call
optimisticReassignAgent(newDept) and then updateAgent(agentName, { department:
newDept, autonomy_level: null, level: null }); reference
optimisticReassignAgent, updateAgent, isDepartmentName, and newDept to locate
the change.
In `@web/src/pages/OrgEditPage.tsx`:
- Around line 95-97: The mapping for autonomy_level currently coerces every
non-string to null which can inadvertently clear the field; update the logic in
OrgEditPage.tsx where autonomy_level is built (using parsed.autonomy_level and
UpdateCompanyRequest['autonomy_level']) so that you preserve undefined unless
the YAML explicitly sets a value: if parsed.autonomy_level is undefined keep
undefined, if it is null set null, if it's a string cast to the expected type,
otherwise leave undefined; reference autonomy_level, parsed, and
UpdateCompanyRequest to locate and change the conditional.
In `@web/src/pages/settings/sinks/SinkFormDrawer.tsx`:
- Around line 36-39: The state initialization for level and rotationStrategy
currently force-casts incoming sink values (sink?.level and
sink?.rotation?.strategy) into LogLevel and the 'builtin'|'external'|'none'
union; replace these casts with allowlist normalization: implement small mapper
functions (or inline switch/lookup) that check sink?.level against valid
LogLevel values and return the matched value or 'INFO' default, and check
sink?.rotation?.strategy against 'builtin'|'external'|'none' and return matched
value or 'none' default (referencing state variables level, setLevel,
rotationStrategy, setRotationStrategy and the DTO fields SinkInfoResponse.level
and SinkRotationResponse.strategy to locate usage). Ensure unknown backend
strings are not propagated by defaulting to the safe value.
In `@web/src/pages/tasks/TaskDetailPanel.tsx`:
- Line 146: The update calls are unintentionally clearing task.priority by
including "priority: null" in partial updates; remove "priority: null" from the
onUpdate calls and only send the specific field being edited plus
expected_version (e.g., change await onUpdate(task.id, { title: value,
expected_version: task.version, priority: null }) to await onUpdate(task.id, {
title: value, expected_version: task.version }) and make equivalent removals for
the description and assignee update calls so only the edited property
(description or assignee_id/assignee) and expected_version are sent.
In `@web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts`:
- Around line 200-201: The "Save As New" flow currently overwrites the
duplicated workflow's I/O by setting inputs: [] and outputs: []; update the
save-as-new handler (the callback in useWorkflowEditorCallbacks that performs
the duplication—look for the "Save As New" or saveAsNewWorkflow/onSaveAsNew
callback) to copy the original workflow's inputs and outputs instead of forcing
empty arrays: pass sourceWorkflow.inputs and sourceWorkflow.outputs (or the
editor state's inputs/outputs) into the new workflow payload so existing
declarations are preserved.
In `@web/src/pages/workflows/WorkflowCard.tsx`:
- Line 62: The workflow-type StatPill currently renders with
formatLabel(workflow.workflow_type ?? ''), which can produce an empty pill;
update the rendering logic in WorkflowCard so it either supplies a readable
fallback (e.g., "Unknown" or "Unspecified") to formatLabel or conditionally
omits the <StatPill> when workflow.workflow_type is null/undefined/empty; target
the StatPill usage and the formatLabel call in the component to implement the
conditional render or fallback label.
---
Outside diff comments:
In `@web/src/stores/meetings.ts`:
- Around line 319-340: The shape guard is rejecting payloads when optional
fields like token_usage_by_participant or minutes are undefined so
sanitizeMeeting's nullish defaults never run; update isMeetingShape to allow
these properties to be optional (accept undefined) — specifically permit
token_usage_by_participant to be absent or undefined and minutes to be
absent/undefined in the shape check so sanitizeMeeting (and its calls to
sanitizeWsString, sanitizeMeetingMinutes, etc.) can execute its fallbacks and
sanitizers on incoming events.
In `@web/src/stores/setup-wizard/providers.ts`:
- Around line 90-139: The createProviderFromPreset mutation currently updates
state and returns sentinels but omits emitting toasts; update
createProviderFromPreset so the success path (before returning { ok: true } or
when returning { ok: true, warning }) calls the app toast success emitter (e.g.,
toast.success or showToastSuccess) with a concise message including the provider
name, and the failure path inside the outer catch logs and sets providersError
but must also call the error toast emitter (e.g., toast.error or showToastError)
with the error message (use getErrorMessage(err)) before returning { ok: false,
error: msg }; keep the existing log.error/log.warn and
providersWarning/providersError updates intact. Ensure you add these toast calls
in the same scopes as set() and returns within createProviderFromPreset so all
mutation paths emit the appropriate toast.
---
Duplicate comments:
In `@web/src/pages/org-edit/DepartmentCreateDialog.tsx`:
- Line 62: DepartmentCreateDialog currently initializes department data with
autonomy_level: null but provides no UI to set it (same issue as
AgentEditDrawer), so either remove autonomy_level from the initial payload or
add a controlled form input to edit it; update the initial state in
DepartmentCreateDialog (the object where autonomy_level is set) to match the
change you made in AgentEditDrawer.tsx (either delete the autonomy_level
property or wire it to a new form field and handlers that validate/convert
values before submit in the submit/save handler).
🪄 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: 4ddbac8b-d6bd-4521-b66f-df353c44ad23
⛔ Files ignored due to path filters (1)
web/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (152)
.github/workflows/ci.yml.pre-commit-config.yamlCLAUDE.mddata/runtime_stats.yamldocs/reference/convention-gates.mdscripts/check_dto_types_ts_in_sync.pyscripts/convention_gate_map.yamlscripts/generate_dto_types_ts.pytests/unit/scripts/fixtures/__init__.pytests/unit/scripts/fixtures/dto_codegen_fixture_schema.pytests/unit/scripts/test_check_dto_types_ts_in_sync.pytests/unit/scripts/test_generate_dto_types_ts.pyweb/CLAUDE.mdweb/package.jsonweb/src/__tests__/_infra/active-handle-reporter.test.tsweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/__tests__/pages/BudgetPage.test.tsxweb/src/__tests__/pages/DashboardPage.test.tsxweb/src/__tests__/pages/ProviderDetailPage.test.tsxweb/src/__tests__/pages/approvals/ApprovalDetailDrawer.test.tsxweb/src/__tests__/pages/budget/ThresholdAlerts.test.tsxweb/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsxweb/src/__tests__/pages/org-edit/DepartmentCreateDialog.test.tsxweb/src/__tests__/stores/agents.test.tsweb/src/__tests__/stores/analytics.test.tsweb/src/__tests__/stores/artifacts.test.tsweb/src/__tests__/stores/budget.test.tsweb/src/__tests__/stores/company.test.tsweb/src/__tests__/stores/connections.test.tsweb/src/__tests__/stores/projects.test.tsweb/src/__tests__/stores/setup-wizard.test.tsweb/src/__tests__/stores/tasks.test.tsweb/src/__tests__/stores/workflows.test.tsweb/src/__tests__/utils/budget.property.test.tsweb/src/__tests__/utils/budget.test.tsweb/src/__tests__/utils/dashboard.property.test.tsweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/utils/tasks.property.test.tsweb/src/api/endpoints/activities.tsweb/src/api/endpoints/providers.tsweb/src/api/types/agents.tsweb/src/api/types/analytics.tsweb/src/api/types/approvals.tsweb/src/api/types/artifacts.tsweb/src/api/types/auth.tsweb/src/api/types/backup.tsweb/src/api/types/budget.tsweb/src/api/types/capabilities.tsweb/src/api/types/ceremony-policy.tsweb/src/api/types/collaboration.tsweb/src/api/types/coordination.tsweb/src/api/types/dtos.gen.tsweb/src/api/types/enum-values.gen.tsweb/src/api/types/enums.tsweb/src/api/types/errors.tsweb/src/api/types/escalations.tsweb/src/api/types/index.tsweb/src/api/types/integrations.tsweb/src/api/types/meetings.tsweb/src/api/types/messages.tsweb/src/api/types/openapi.gen.tsweb/src/api/types/org.tsweb/src/api/types/projects.tsweb/src/api/types/providers.tsweb/src/api/types/security.tsweb/src/api/types/settings.tsweb/src/api/types/setup.tsweb/src/api/types/system.tsweb/src/api/types/tasks.tsweb/src/api/types/templates.tsweb/src/api/types/workflows.tsweb/src/hooks/useArtifactsData.tsweb/src/hooks/useConnectionsData.tsweb/src/hooks/useProjectsData.tsweb/src/mocks/handlers/budget.tsweb/src/mocks/handlers/company.tsweb/src/mocks/handlers/connections.tsweb/src/mocks/handlers/escalations.tsweb/src/mocks/handlers/providers.tsweb/src/mocks/handlers/workflows.tsweb/src/pages/BudgetForecastPage.tsxweb/src/pages/EscalationQueuePage.stories.tsxweb/src/pages/EscalationQueuePage.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/ProvidersPage.tsxweb/src/pages/WorkflowsPage.tsxweb/src/pages/artifacts/ArtifactCard.tsxweb/src/pages/artifacts/ArtifactContentPreview.tsxweb/src/pages/artifacts/ArtifactCreateDialog.tsxweb/src/pages/artifacts/ArtifactMetadata.tsxweb/src/pages/budget/BudgetPage.stories.tsxweb/src/pages/budget/SpendBurnChart.tsxweb/src/pages/budget/ThresholdAlerts.stories.tsxweb/src/pages/connections/ConnectionCard.stories.tsxweb/src/pages/connections/ConnectionCard.tsxweb/src/pages/connections/ConnectionFormModal.stories.tsxweb/src/pages/connections/ConnectionFormModal.tsxweb/src/pages/dashboard/BudgetBurnChart.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/pages/escalations/EscalationDetailDrawer.stories.tsxweb/src/pages/mcp-catalog/McpInstallWizard.stories.tsxweb/src/pages/meetings/MeetingActionItems.tsxweb/src/pages/meetings/MeetingCard.tsxweb/src/pages/meetings/MeetingDetailHeader.tsxweb/src/pages/meetings/MeetingTokenBreakdown.tsxweb/src/pages/oauth-apps/OauthAppCard.stories.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/org-edit/PackSelectionDialog.tsxweb/src/pages/org/build-org-tree.tsweb/src/pages/org/drop-target.tsweb/src/pages/org/useOrgChartDragDrop.tsweb/src/pages/projects/ProjectCard.tsxweb/src/pages/projects/ProjectCreateDrawer.tsxweb/src/pages/projects/ProjectHeader.tsxweb/src/pages/projects/ProjectTeamSection.tsxweb/src/pages/providers/AuditLogDrawer.tsxweb/src/pages/providers/ModelConfigDrawer.stories.tsxweb/src/pages/providers/PresetOverrideDrawer.tsxweb/src/pages/providers/ProviderFormModal.tsxweb/src/pages/providers/ProviderHealthMetrics.tsxweb/src/pages/providers/ProviderModelList.stories.tsxweb/src/pages/providers/TestConnectionResult.tsxweb/src/pages/settings/ceremony-policy/DepartmentOverridesPanel.tsxweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/setup/TemplateCard.tsxweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/pages/tasks/TaskDetailHeader.tsxweb/src/pages/tasks/TaskDetailMetadata.tsxweb/src/pages/tasks/TaskDetailPanel.stories.tsxweb/src/pages/tasks/TaskDetailPanel.tsxweb/src/pages/workflow-editor/VersionDiffViewer.tsxweb/src/pages/workflow-editor/node-config-schemas.tsweb/src/pages/workflow-editor/useWorkflowEditorCallbacks.tsweb/src/pages/workflows/WorkflowCard.tsxweb/src/pages/workflows/WorkflowCreateDrawer.tsxweb/src/pages/workflows/WorkflowTableView.tsxweb/src/stores/approvals.tsweb/src/stores/meetings.tsweb/src/stores/providers/crud-actions.tsweb/src/stores/setup-wizard/providers.tsweb/src/stores/tasks.tsweb/src/stores/workflow-editor/clipboard.tsweb/src/stores/workflow-editor/persistence.tsweb/src/stores/workflow-editor/validation.tsweb/src/stores/workflow-editor/yaml.tsweb/src/utils/agents.tsweb/src/utils/meetings.tsweb/src/utils/messages.tsweb/src/utils/setup-validation.tsweb/test-infra/active-handle-tracker.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: Test (Python 3.14)
- GitHub Check: Dashboard Test
- GitHub Check: Build Web Assets (melange)
- GitHub Check: Lighthouse Site
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (12)
web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Web: follow design system and components in
web/CLAUDE.md; use React 19Wrap the app in
<CSPProvider nonce={getCspNonce()}>+<MotionConfig nonce>so every inline<style>tag injected by Base UI and Motion carries the per-request CSP nonceAlways use
createLoggerfrom@/lib/logger; never use bareconsole.warn,console.error, orconsole.debugin application codeUse variable name
logfor logger instances (e.g.,const log = createLogger('module-name'))Use
useEmptyStateProps({ filteredCount, totalCount, filterActive, empty, filtered })from@/hooks/use-empty-state-propsto discriminate between empty-state variants instead of duplicating the logic
Files:
web/src/__tests__/pages/DashboardPage.test.tsxweb/src/__tests__/pages/ProviderDetailPage.test.tsxweb/src/pages/meetings/MeetingDetailHeader.tsxweb/src/hooks/useConnectionsData.tsweb/src/__tests__/pages/budget/ThresholdAlerts.test.tsxweb/src/hooks/useArtifactsData.tsweb/src/pages/mcp-catalog/McpInstallWizard.stories.tsxweb/src/pages/providers/AuditLogDrawer.tsxweb/src/pages/connections/ConnectionCard.tsxweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/pages/budget/SpendBurnChart.tsxweb/src/pages/projects/ProjectCreateDrawer.tsxweb/src/pages/org/drop-target.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/pages/org-edit/DepartmentCreateDialog.test.tsxweb/src/pages/meetings/MeetingCard.tsxweb/src/pages/EscalationQueuePage.stories.tsxweb/src/pages/oauth-apps/OauthAppCard.stories.tsxweb/src/pages/connections/ConnectionFormModal.tsxweb/src/pages/meetings/MeetingTokenBreakdown.tsxweb/src/pages/workflows/WorkflowTableView.tsxweb/src/pages/tasks/TaskDetailHeader.tsxweb/src/pages/connections/ConnectionCard.stories.tsxweb/src/stores/workflow-editor/clipboard.tsweb/src/__tests__/utils/budget.property.test.tsweb/src/api/endpoints/activities.tsweb/src/stores/workflow-editor/yaml.tsweb/src/pages/artifacts/ArtifactContentPreview.tsxweb/src/pages/projects/ProjectHeader.tsxweb/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsxweb/src/pages/artifacts/ArtifactMetadata.tsxweb/src/pages/dashboard/BudgetBurnChart.tsxweb/src/pages/settings/ceremony-policy/DepartmentOverridesPanel.tsxweb/src/pages/artifacts/ArtifactCard.tsxweb/src/mocks/handlers/company.tsweb/src/api/endpoints/providers.tsweb/src/hooks/useProjectsData.tsweb/src/pages/budget/BudgetPage.stories.tsxweb/src/mocks/handlers/escalations.tsweb/src/pages/providers/ModelConfigDrawer.stories.tsxweb/src/__tests__/pages/BudgetPage.test.tsxweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/pages/workflows/WorkflowCard.tsxweb/src/mocks/handlers/workflows.tsweb/src/pages/connections/ConnectionFormModal.stories.tsxweb/src/__tests__/stores/analytics.test.tsweb/src/pages/ProvidersPage.tsxweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/__tests__/stores/projects.test.tsweb/src/pages/providers/TestConnectionResult.tsxweb/src/pages/meetings/MeetingActionItems.tsxweb/src/stores/providers/crud-actions.tsweb/src/pages/projects/ProjectTeamSection.tsxweb/src/pages/escalations/EscalationDetailDrawer.stories.tsxweb/src/pages/budget/ThresholdAlerts.stories.tsxweb/src/pages/tasks/TaskDetailPanel.stories.tsxweb/src/api/types/index.tsweb/src/api/types/errors.tsweb/src/__tests__/utils/tasks.property.test.tsweb/src/api/types/coordination.tsweb/src/pages/providers/ProviderHealthMetrics.tsxweb/src/pages/BudgetForecastPage.tsxweb/src/__tests__/stores/budget.test.tsweb/src/pages/org-edit/PackSelectionDialog.tsxweb/src/pages/setup/TemplateCard.tsxweb/src/pages/workflow-editor/node-config-schemas.tsweb/src/pages/tasks/TaskDetailPanel.tsxweb/src/pages/tasks/TaskDetailMetadata.tsxweb/src/pages/artifacts/ArtifactCreateDialog.tsxweb/src/mocks/handlers/providers.tsweb/src/stores/meetings.tsweb/src/pages/projects/ProjectCard.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/pages/workflow-editor/useWorkflowEditorCallbacks.tsweb/src/__tests__/stores/workflows.test.tsweb/src/__tests__/stores/tasks.test.tsweb/src/pages/EscalationQueuePage.tsxweb/src/utils/messages.tsweb/src/pages/WorkflowsPage.tsxweb/src/api/types/templates.tsweb/src/pages/OrgEditPage.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/stores/approvals.tsweb/src/stores/workflow-editor/persistence.tsweb/src/api/types/capabilities.tsweb/src/stores/workflow-editor/validation.tsweb/src/stores/tasks.tsweb/src/api/types/backup.tsweb/src/utils/agents.tsweb/src/pages/providers/ProviderModelList.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/__tests__/utils/budget.test.tsweb/src/__tests__/utils/dashboard.property.test.tsweb/src/__tests__/stores/connections.test.tsweb/src/api/types/system.tsweb/src/stores/setup-wizard/providers.tsweb/src/api/types/projects.tsweb/src/pages/org/build-org-tree.tsweb/src/api/types/collaboration.tsweb/src/api/types/dtos.gen.tsweb/src/pages/org/useOrgChartDragDrop.tsweb/src/api/types/artifacts.tsweb/src/pages/workflows/WorkflowCreateDrawer.tsxweb/src/pages/providers/ProviderFormModal.tsxweb/src/__tests__/_infra/active-handle-reporter.test.tsweb/src/mocks/handlers/budget.tsweb/src/__tests__/pages/approvals/ApprovalDetailDrawer.test.tsxweb/src/api/types/auth.tsweb/src/utils/meetings.tsweb/src/api/types/analytics.tsweb/src/api/types/ceremony-policy.tsweb/src/__tests__/stores/artifacts.test.tsweb/src/api/types/messages.tsweb/src/api/types/meetings.tsweb/src/__tests__/stores/company.test.tsweb/src/pages/providers/PresetOverrideDrawer.tsxweb/src/api/types/settings.tsweb/src/utils/setup-validation.tsweb/src/api/types/budget.tsweb/src/__tests__/stores/agents.test.tsweb/src/api/types/security.tsweb/src/mocks/handlers/connections.tsweb/src/__tests__/stores/setup-wizard.test.tsweb/src/pages/workflow-editor/VersionDiffViewer.tsxweb/src/api/types/setup.tsweb/src/api/types/enum-values.gen.tsweb/src/api/types/org.tsweb/src/api/types/approvals.tsweb/src/api/types/integrations.tsweb/src/api/types/tasks.tsweb/test-infra/active-handle-tracker.tsweb/src/api/types/agents.tsweb/src/api/types/workflows.tsweb/src/api/types/enums.tsweb/src/api/types/escalations.tsweb/src/api/types/providers.ts
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Import and discriminate on
ErrorCodeandErrorCategoryfrom@/api/types/errors; never discriminate on raw integer error-code literalsNever hand-edit
*.gen.tsfiles; regenerate generated DTO types withuv run python scripts/generate_dto_types_ts.pywhen OpenAPI schema changesRoute WebSocket string payloads through
sanitizeWsString()and enums throughsanitizeWsEnum<T>(value, allowlist, fallback, { field })from@/stores/notificationsor@/utils/ws-sanitize.ts; raw(sanitizeWsString(x, n) ?? '') as EnumTypecasts are forbiddenESLint rule: enable
@eslint-react/web-api-no-leaked-fetchto detectfetch()in effects withoutAbortControllercleanupESLint rule: enable
@eslint-react/no-leaked-conditional-renderingto catch the{count && <Foo />}bug; use{value != null && value !== false && <jsx>}forReactNode | undefinedprops andBoolean(...)for compound truthinessESLint rule: enable
@eslint-react/globalsto restrictwindow/document/localStorageinside render bodies; hoist offenders intouseCallbackevent handlers,useEffect, oruseSyncExternalStore-backed hooksESLint rule: enable
@typescript-eslint/no-floating-promisesto forbid unawaited promises so async work cannot survive the test and trip the active-handle gateESLint rule: enable
@typescript-eslint/no-misused-promises(withchecksVoidReturn: { attributes: false }) to forbid passing async functions where callsites ignore the returned promise; React 19asyncevent handlers stay allowed
Files:
web/src/__tests__/pages/DashboardPage.test.tsxweb/src/__tests__/pages/ProviderDetailPage.test.tsxweb/src/pages/meetings/MeetingDetailHeader.tsxweb/src/hooks/useConnectionsData.tsweb/src/__tests__/pages/budget/ThresholdAlerts.test.tsxweb/src/hooks/useArtifactsData.tsweb/src/pages/mcp-catalog/McpInstallWizard.stories.tsxweb/src/pages/providers/AuditLogDrawer.tsxweb/src/pages/connections/ConnectionCard.tsxweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/pages/budget/SpendBurnChart.tsxweb/src/pages/projects/ProjectCreateDrawer.tsxweb/src/pages/org/drop-target.tsweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/pages/org-edit/DepartmentCreateDialog.test.tsxweb/src/pages/meetings/MeetingCard.tsxweb/src/pages/EscalationQueuePage.stories.tsxweb/src/pages/oauth-apps/OauthAppCard.stories.tsxweb/src/pages/connections/ConnectionFormModal.tsxweb/src/pages/meetings/MeetingTokenBreakdown.tsxweb/src/pages/workflows/WorkflowTableView.tsxweb/src/pages/tasks/TaskDetailHeader.tsxweb/src/pages/connections/ConnectionCard.stories.tsxweb/src/stores/workflow-editor/clipboard.tsweb/src/__tests__/utils/budget.property.test.tsweb/src/api/endpoints/activities.tsweb/src/stores/workflow-editor/yaml.tsweb/src/pages/artifacts/ArtifactContentPreview.tsxweb/src/pages/projects/ProjectHeader.tsxweb/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsxweb/src/pages/artifacts/ArtifactMetadata.tsxweb/src/pages/dashboard/BudgetBurnChart.tsxweb/src/pages/settings/ceremony-policy/DepartmentOverridesPanel.tsxweb/src/pages/artifacts/ArtifactCard.tsxweb/src/mocks/handlers/company.tsweb/src/api/endpoints/providers.tsweb/src/hooks/useProjectsData.tsweb/src/pages/budget/BudgetPage.stories.tsxweb/src/mocks/handlers/escalations.tsweb/src/pages/providers/ModelConfigDrawer.stories.tsxweb/src/__tests__/pages/BudgetPage.test.tsxweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/pages/workflows/WorkflowCard.tsxweb/src/mocks/handlers/workflows.tsweb/src/pages/connections/ConnectionFormModal.stories.tsxweb/src/__tests__/stores/analytics.test.tsweb/src/pages/ProvidersPage.tsxweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/__tests__/stores/projects.test.tsweb/src/pages/providers/TestConnectionResult.tsxweb/src/pages/meetings/MeetingActionItems.tsxweb/src/stores/providers/crud-actions.tsweb/src/pages/projects/ProjectTeamSection.tsxweb/src/pages/escalations/EscalationDetailDrawer.stories.tsxweb/src/pages/budget/ThresholdAlerts.stories.tsxweb/src/pages/tasks/TaskDetailPanel.stories.tsxweb/src/api/types/index.tsweb/src/api/types/errors.tsweb/src/__tests__/utils/tasks.property.test.tsweb/src/api/types/coordination.tsweb/src/pages/providers/ProviderHealthMetrics.tsxweb/src/pages/BudgetForecastPage.tsxweb/src/__tests__/stores/budget.test.tsweb/src/pages/org-edit/PackSelectionDialog.tsxweb/src/pages/setup/TemplateCard.tsxweb/src/pages/workflow-editor/node-config-schemas.tsweb/src/pages/tasks/TaskDetailPanel.tsxweb/src/pages/tasks/TaskDetailMetadata.tsxweb/src/pages/artifacts/ArtifactCreateDialog.tsxweb/src/mocks/handlers/providers.tsweb/src/stores/meetings.tsweb/src/pages/projects/ProjectCard.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/pages/workflow-editor/useWorkflowEditorCallbacks.tsweb/src/__tests__/stores/workflows.test.tsweb/src/__tests__/stores/tasks.test.tsweb/src/pages/EscalationQueuePage.tsxweb/src/utils/messages.tsweb/src/pages/WorkflowsPage.tsxweb/src/api/types/templates.tsweb/src/pages/OrgEditPage.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/stores/approvals.tsweb/src/stores/workflow-editor/persistence.tsweb/src/api/types/capabilities.tsweb/src/stores/workflow-editor/validation.tsweb/src/stores/tasks.tsweb/src/api/types/backup.tsweb/src/utils/agents.tsweb/src/pages/providers/ProviderModelList.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/__tests__/utils/budget.test.tsweb/src/__tests__/utils/dashboard.property.test.tsweb/src/__tests__/stores/connections.test.tsweb/src/api/types/system.tsweb/src/stores/setup-wizard/providers.tsweb/src/api/types/projects.tsweb/src/pages/org/build-org-tree.tsweb/src/api/types/collaboration.tsweb/src/api/types/dtos.gen.tsweb/src/pages/org/useOrgChartDragDrop.tsweb/src/api/types/artifacts.tsweb/src/pages/workflows/WorkflowCreateDrawer.tsxweb/src/pages/providers/ProviderFormModal.tsxweb/src/__tests__/_infra/active-handle-reporter.test.tsweb/src/mocks/handlers/budget.tsweb/src/__tests__/pages/approvals/ApprovalDetailDrawer.test.tsxweb/src/api/types/auth.tsweb/src/utils/meetings.tsweb/src/api/types/analytics.tsweb/src/api/types/ceremony-policy.tsweb/src/__tests__/stores/artifacts.test.tsweb/src/api/types/messages.tsweb/src/api/types/meetings.tsweb/src/__tests__/stores/company.test.tsweb/src/pages/providers/PresetOverrideDrawer.tsxweb/src/api/types/settings.tsweb/src/utils/setup-validation.tsweb/src/api/types/budget.tsweb/src/__tests__/stores/agents.test.tsweb/src/api/types/security.tsweb/src/mocks/handlers/connections.tsweb/src/__tests__/stores/setup-wizard.test.tsweb/src/pages/workflow-editor/VersionDiffViewer.tsxweb/src/api/types/setup.tsweb/src/api/types/enum-values.gen.tsweb/src/api/types/org.tsweb/src/api/types/approvals.tsweb/src/api/types/integrations.tsweb/src/api/types/tasks.tsweb/src/api/types/agents.tsweb/src/api/types/workflows.tsweb/src/api/types/enums.tsweb/src/api/types/escalations.tsweb/src/api/types/providers.ts
web/**/*.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
Export icon wrappers as
<XIcon value={...} {...lucideProps} />components instead ofgetXIcon(value): LucideIconfactories that return component references; the wrapper performs the lookup viacreateElementto avoid PascalCase JSX bindings in the render bodyUse
useViewportSize()from@/hooks/useViewportSizefor viewport size reads; never readwindow.innerWidth/window.innerHeightdirectly inside component render bodies oruseMemo
Files:
web/src/__tests__/pages/DashboardPage.test.tsxweb/src/__tests__/pages/ProviderDetailPage.test.tsxweb/src/pages/meetings/MeetingDetailHeader.tsxweb/src/__tests__/pages/budget/ThresholdAlerts.test.tsxweb/src/pages/mcp-catalog/McpInstallWizard.stories.tsxweb/src/pages/providers/AuditLogDrawer.tsxweb/src/pages/connections/ConnectionCard.tsxweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/pages/budget/SpendBurnChart.tsxweb/src/pages/projects/ProjectCreateDrawer.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/__tests__/pages/org-edit/DepartmentCreateDialog.test.tsxweb/src/pages/meetings/MeetingCard.tsxweb/src/pages/EscalationQueuePage.stories.tsxweb/src/pages/oauth-apps/OauthAppCard.stories.tsxweb/src/pages/connections/ConnectionFormModal.tsxweb/src/pages/meetings/MeetingTokenBreakdown.tsxweb/src/pages/workflows/WorkflowTableView.tsxweb/src/pages/tasks/TaskDetailHeader.tsxweb/src/pages/connections/ConnectionCard.stories.tsxweb/src/pages/artifacts/ArtifactContentPreview.tsxweb/src/pages/projects/ProjectHeader.tsxweb/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsxweb/src/pages/artifacts/ArtifactMetadata.tsxweb/src/pages/dashboard/BudgetBurnChart.tsxweb/src/pages/settings/ceremony-policy/DepartmentOverridesPanel.tsxweb/src/pages/artifacts/ArtifactCard.tsxweb/src/pages/budget/BudgetPage.stories.tsxweb/src/pages/providers/ModelConfigDrawer.stories.tsxweb/src/__tests__/pages/BudgetPage.test.tsxweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/pages/workflows/WorkflowCard.tsxweb/src/pages/connections/ConnectionFormModal.stories.tsxweb/src/pages/ProvidersPage.tsxweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/providers/TestConnectionResult.tsxweb/src/pages/meetings/MeetingActionItems.tsxweb/src/pages/projects/ProjectTeamSection.tsxweb/src/pages/escalations/EscalationDetailDrawer.stories.tsxweb/src/pages/budget/ThresholdAlerts.stories.tsxweb/src/pages/tasks/TaskDetailPanel.stories.tsxweb/src/pages/providers/ProviderHealthMetrics.tsxweb/src/pages/BudgetForecastPage.tsxweb/src/pages/org-edit/PackSelectionDialog.tsxweb/src/pages/setup/TemplateCard.tsxweb/src/pages/tasks/TaskDetailPanel.tsxweb/src/pages/tasks/TaskDetailMetadata.tsxweb/src/pages/artifacts/ArtifactCreateDialog.tsxweb/src/pages/projects/ProjectCard.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/pages/EscalationQueuePage.tsxweb/src/pages/WorkflowsPage.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/providers/ProviderModelList.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/workflows/WorkflowCreateDrawer.tsxweb/src/pages/providers/ProviderFormModal.tsxweb/src/__tests__/pages/approvals/ApprovalDetailDrawer.test.tsxweb/src/pages/providers/PresetOverrideDrawer.tsxweb/src/pages/workflow-editor/VersionDiffViewer.tsx
web/src/**/*.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
Use
<StatusBadge>(withrole="img"and aria-label by default;decorativefor adjacent-labeled,announcefor live WS updates) instead of hardcoding status dotsUse
<MetricCard>/<Sparkline>/<ProgressGauge>/<TokenUsageBar>for KPI displays instead of creating inline custom componentsUse
<SectionCard>for titled wrapper with icon and action slot; use<AgentCard>,<DeptHealthBar>for domain-specific card displaysUse form field components:
<InputField>/<SelectField>/<SliderField>/<ToggleField>/<SegmentedControl>/<TagInput>/<SearchInput>instead of bare input elementsUse
<Drawer width="compact|narrow|default|wide">for slide-in panels; do NOT add inlinew-[40vw]overridesUse
<Skeleton>family /<EmptyState>/<ErrorBoundary>/<ErrorBanner>/<ProgressIndicator>for loading / empty / error states instead of custom implementationsUse
<ListHeader>/<SearchFilterSort>/<Pagination>/<BulkActionBar>/<MetadataGrid>/<Breadcrumbs>/<Collapsible>for list-page primitivesImport
STATUS_COLORSfamily from@/styles/status-colorsfor status/role/risk/urgency badge styling instead of defining inlineRecord<EnumValue, string>constantsUse
<ConfirmDialog>/<Toast>(Zustand-backed queue, NOT Base UI's Toast) for confirmations and notificationsUse
<CommandPalette>/<KeyboardShortcutHint>/<CommandCheatsheet>for command-palette and shortcut UI instead of custom implementationsUse
<AnimatedPresence>/<StaggerGroup>/<LiveRegion>(debounced ARIA live for WS updates) for animation and accessibility instead of custom implementations
Files:
web/src/__tests__/pages/DashboardPage.test.tsxweb/src/__tests__/pages/ProviderDetailPage.test.tsxweb/src/pages/meetings/MeetingDetailHeader.tsxweb/src/__tests__/pages/budget/ThresholdAlerts.test.tsxweb/src/pages/mcp-catalog/McpInstallWizard.stories.tsxweb/src/pages/providers/AuditLogDrawer.tsxweb/src/pages/connections/ConnectionCard.tsxweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/pages/budget/SpendBurnChart.tsxweb/src/pages/projects/ProjectCreateDrawer.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/__tests__/pages/org-edit/DepartmentCreateDialog.test.tsxweb/src/pages/meetings/MeetingCard.tsxweb/src/pages/EscalationQueuePage.stories.tsxweb/src/pages/oauth-apps/OauthAppCard.stories.tsxweb/src/pages/connections/ConnectionFormModal.tsxweb/src/pages/meetings/MeetingTokenBreakdown.tsxweb/src/pages/workflows/WorkflowTableView.tsxweb/src/pages/tasks/TaskDetailHeader.tsxweb/src/pages/connections/ConnectionCard.stories.tsxweb/src/pages/artifacts/ArtifactContentPreview.tsxweb/src/pages/projects/ProjectHeader.tsxweb/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsxweb/src/pages/artifacts/ArtifactMetadata.tsxweb/src/pages/dashboard/BudgetBurnChart.tsxweb/src/pages/settings/ceremony-policy/DepartmentOverridesPanel.tsxweb/src/pages/artifacts/ArtifactCard.tsxweb/src/pages/budget/BudgetPage.stories.tsxweb/src/pages/providers/ModelConfigDrawer.stories.tsxweb/src/__tests__/pages/BudgetPage.test.tsxweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/pages/workflows/WorkflowCard.tsxweb/src/pages/connections/ConnectionFormModal.stories.tsxweb/src/pages/ProvidersPage.tsxweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/providers/TestConnectionResult.tsxweb/src/pages/meetings/MeetingActionItems.tsxweb/src/pages/projects/ProjectTeamSection.tsxweb/src/pages/escalations/EscalationDetailDrawer.stories.tsxweb/src/pages/budget/ThresholdAlerts.stories.tsxweb/src/pages/tasks/TaskDetailPanel.stories.tsxweb/src/pages/providers/ProviderHealthMetrics.tsxweb/src/pages/BudgetForecastPage.tsxweb/src/pages/org-edit/PackSelectionDialog.tsxweb/src/pages/setup/TemplateCard.tsxweb/src/pages/tasks/TaskDetailPanel.tsxweb/src/pages/tasks/TaskDetailMetadata.tsxweb/src/pages/artifacts/ArtifactCreateDialog.tsxweb/src/pages/projects/ProjectCard.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/pages/EscalationQueuePage.tsxweb/src/pages/WorkflowsPage.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/providers/ProviderModelList.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/workflows/WorkflowCreateDrawer.tsxweb/src/pages/providers/ProviderFormModal.tsxweb/src/__tests__/pages/approvals/ApprovalDetailDrawer.test.tsxweb/src/pages/providers/PresetOverrideDrawer.tsxweb/src/pages/workflow-editor/VersionDiffViewer.tsx
web/src/pages/**/*.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
Page root container should use
space-y-section-gapfor spacing (the majority pattern;flex flex-col gap-section-gapis equivalent but discouraged)Place
<ErrorBanner>immediately after<ListHeader>, before any filter/pagination rowAim for 2 or 3 breadcrumb levels max in visible trails; if natural depth exceeds 3, route the user to a flatter parent or use
<Breadcrumbs maxItems={...} />to collapse middle nodes into the ellipsis
Files:
web/src/pages/meetings/MeetingDetailHeader.tsxweb/src/pages/mcp-catalog/McpInstallWizard.stories.tsxweb/src/pages/providers/AuditLogDrawer.tsxweb/src/pages/connections/ConnectionCard.tsxweb/src/pages/budget/SpendBurnChart.tsxweb/src/pages/projects/ProjectCreateDrawer.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/meetings/MeetingCard.tsxweb/src/pages/EscalationQueuePage.stories.tsxweb/src/pages/oauth-apps/OauthAppCard.stories.tsxweb/src/pages/connections/ConnectionFormModal.tsxweb/src/pages/meetings/MeetingTokenBreakdown.tsxweb/src/pages/workflows/WorkflowTableView.tsxweb/src/pages/tasks/TaskDetailHeader.tsxweb/src/pages/connections/ConnectionCard.stories.tsxweb/src/pages/artifacts/ArtifactContentPreview.tsxweb/src/pages/projects/ProjectHeader.tsxweb/src/pages/artifacts/ArtifactMetadata.tsxweb/src/pages/dashboard/BudgetBurnChart.tsxweb/src/pages/settings/ceremony-policy/DepartmentOverridesPanel.tsxweb/src/pages/artifacts/ArtifactCard.tsxweb/src/pages/budget/BudgetPage.stories.tsxweb/src/pages/providers/ModelConfigDrawer.stories.tsxweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/pages/workflows/WorkflowCard.tsxweb/src/pages/connections/ConnectionFormModal.stories.tsxweb/src/pages/ProvidersPage.tsxweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/providers/TestConnectionResult.tsxweb/src/pages/meetings/MeetingActionItems.tsxweb/src/pages/projects/ProjectTeamSection.tsxweb/src/pages/escalations/EscalationDetailDrawer.stories.tsxweb/src/pages/budget/ThresholdAlerts.stories.tsxweb/src/pages/tasks/TaskDetailPanel.stories.tsxweb/src/pages/providers/ProviderHealthMetrics.tsxweb/src/pages/BudgetForecastPage.tsxweb/src/pages/org-edit/PackSelectionDialog.tsxweb/src/pages/setup/TemplateCard.tsxweb/src/pages/tasks/TaskDetailPanel.tsxweb/src/pages/tasks/TaskDetailMetadata.tsxweb/src/pages/artifacts/ArtifactCreateDialog.tsxweb/src/pages/projects/ProjectCard.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/pages/EscalationQueuePage.tsxweb/src/pages/WorkflowsPage.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/providers/ProviderModelList.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/workflows/WorkflowCreateDrawer.tsxweb/src/pages/providers/ProviderFormModal.tsxweb/src/pages/providers/PresetOverrideDrawer.tsxweb/src/pages/workflow-editor/VersionDiffViewer.tsx
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Read design spec from
docs/design/before implementing; deviations require approval per DESIGN_SPEC.mdUse
from synthorg.observability import get_loggerwith variable namelogger; never useimport loggingorprint()in app codeEvent names must come from
observability.events.<domain>constants; use structured kwargs likelogger.info(EVENT, key=value)Never use
error=str(exc)or interpolate{exc}in logs; useerror_type=type(exc).__name__anderror=safe_error_description(exc). Never useexc_info=True. For OTel spans, usespan.set_attribute()instead ofspan.record_exception()Error classes must be named
<Domain><Condition>Errorand inherit fromDomainError, never directly fromException,RuntimeError, or other base exceptionsNumeric values must live in
settings/definitions/; only allow hardcoded 0/1/-1, HTTP codes, hex masks, powers-of-2, and module-level annotated named constants of formNAME: int|float|Final|Final[int]|Final[float] = literalType hints required on public functions; strict mypy checking. Google-style docstrings. Line length 88; functions under 50 lines; files under 800 lines
Use Pydantic v2 with
frozen=Trueandextra='forbid'on API DTOs (Request/Response/Snapshot/Result/Envelope/Status/Info/Summary suffixes); use@computed_fieldfor derived data; useNotBlankStrfor identifiersUse args models at every system boundary; call
parse_typed()for every external dict ingestion. Enforced bycheck_boundary_typed.pyUse immutability via
model_copy(update=...)for Pydantic models orcopy.deepcopy()for other objects; use deepcopy at system boundariesUse
asyncio.TaskGroupfor fan-out/fan-in async operations; helper functions must catchExceptionand re-raiseMemoryErrorandRecursionErrorImplement Clock seam pattern with
clock: Clock | None = Noneparameter; tests must injectFakeClockServices must own
_lifecycle_lock; timed-out stops must mark services as unrestartableFor...
Files:
scripts/check_dto_types_ts_in_sync.pytests/unit/scripts/fixtures/__init__.pytests/unit/scripts/fixtures/dto_codegen_fixture_schema.pytests/unit/scripts/test_check_dto_types_ts_in_sync.pytests/unit/scripts/test_generate_dto_types_ts.pyscripts/generate_dto_types_ts.py
web/src/stores/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
All store mutation actions (create/update/delete) must wrap the API call in
try/catch, with success path updating state + emitting a success toast, failure path logging + emitting an error toast + returning a sentinel (nullfor entity returns,falsefor delete); callers MUST NOT wrap store mutation calls intry/catchStore list reads (
fetch*) must seterror: string | nullon the store instead of toastingImplement cursor-based pagination (not offset) via
PaginationMeta, keepingnextCursor+hasMorein state (not offset arithmetic) and early-returning when!hasMore || !nextCursor
Files:
web/src/stores/workflow-editor/clipboard.tsweb/src/stores/workflow-editor/yaml.tsweb/src/stores/providers/crud-actions.tsweb/src/stores/meetings.tsweb/src/stores/approvals.tsweb/src/stores/workflow-editor/persistence.tsweb/src/stores/workflow-editor/validation.tsweb/src/stores/tasks.tsweb/src/stores/setup-wizard/providers.ts
tests/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Use markers
@pytest.mark.{unit,integration,e2e,slow}. Async tests use auto mode. Global timeout 30s. Minimum coverage 80%Use xdist with
-n 8 --dist=loadfilefor parallel test execution;loadfiledistribution prevents Python 3.14+ Windows ProactorEventLoop leakOn Windows, unit tests must use
WindowsSelectorEventLoopPolicy; subprocess tests must override back to defaultUse test doubles:
FakeClockfor Clock seam,mock_of[T](**overrides)for typed-boundary substitutions,SimpleNamespacefor attribute bags. Never use bareMagicMockat typed boundaries. Enforced byscripts/check_mock_spec.pyImport
FakeClockandmock_offromtests._shared; inject viaclock=parameter and helper's spec subscriptNever use real vendor names in project code/tests; use
example-provider,test-provider,example-{large,medium,small}-001. Real vendors allowed only in.claude/, third-party imports,providers/presets.py, andweb/public/provider-logos/Hypothesis: use 10 deterministic CI examples; treat failures as real bugs (fix + add
@example(...)). Never skip/xfail flaky tests; fix fundamentally. Useasyncio.Event().wait()instead ofsleep(large)
Files:
tests/unit/scripts/fixtures/__init__.pytests/unit/scripts/fixtures/dto_codegen_fixture_schema.pytests/unit/scripts/test_check_dto_types_ts_in_sync.pytests/unit/scripts/test_generate_dto_types_ts.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/scripts/fixtures/__init__.pytests/unit/scripts/fixtures/dto_codegen_fixture_schema.pytests/unit/scripts/test_check_dto_types_ts_in_sync.pytests/unit/scripts/test_generate_dto_types_ts.py
**/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Numerics in README and public docs must be sourced from
data/runtime_stats.yamlvia<!--RS:NAME-->markers (Doc Numeric Claims MANDATORY)
Files:
CLAUDE.mdweb/CLAUDE.mddocs/reference/convention-gates.md
web/src/mocks/handlers/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
Mirror
web/src/api/endpoints/*.ts1:1 with a default happy-path MSW handler for every exported endpoint, using typed envelope helpers (successFor,paginatedFor,voidSuccess)
Files:
web/src/mocks/handlers/company.tsweb/src/mocks/handlers/escalations.tsweb/src/mocks/handlers/workflows.tsweb/src/mocks/handlers/providers.tsweb/src/mocks/handlers/budget.tsweb/src/mocks/handlers/connections.ts
web/src/api/types/dtos.gen.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
This file is auto-generated and contains the named-alias layer with
<Name>Envelope/<Name>Pageshortcuts; never hand-edit
Files:
web/src/api/types/dtos.gen.ts
web/src/api/types/enum-values.gen.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
This file is auto-generated and contains runtime
*_VALUEStuples plus derived string-union types; never hand-edit
Files:
web/src/api/types/enum-values.gen.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T18:29:23.923Z
Learning: Every plan for implementation must be presented for accept/deny before coding (Planning MANDATORY)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T18:29:23.923Z
Learning: Every convention PR must ship its enforcement gate (Convention Rollout MANDATORY)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T18:29:23.923Z
Learning: Configuration precedence: DB > env > YAML > code default via `SettingsService`/`ConfigResolver`; never use `os.environ.get` outside startup
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T18:29:23.923Z
Learning: Never edit test baseline files (`tests/baselines/unit_timing.json`, `scripts/*_baseline.{txt,json}`, `scripts/_*_baseline.py`); use `ALLOW_BASELINE_GROWTH=1 git commit` for explicit approval. Test regression = source-code regression (Test Regression MANDATORY)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T18:29:23.923Z
Learning: After issue completion: create branch, commit, push (no auto-PR); use `/pre-pr-review` for checks. After PR: use `/aurelio-review-pr` for external feedback. Fix all valid issues; no deferring (Post-Implementation + Pre-PR Review MANDATORY)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T18:29:23.923Z
Learning: Use d2 (with theme 200 Dark Mauve, CLI v0.7.1) for architecture/nested containers; use mermaid for flowcharts/sequence/pipelines; use Markdown tables for tabular data
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T18:29:23.923Z
Learning: Git commits: `<type>: <description>` format (feat/fix/refactor/docs/test/chore/perf/ci); commitizen-enforced. Signed commits required on protected refs. Branches: `<type>/<slug>` from main. Squash merge with PR body as commit message; trailers must be in PR body
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T18:29:23.923Z
Learning: After every squash merge, run `/post-merge-cleanup`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T18:29:23.923Z
Learning: CLI is Docker-only (init/start/stop/status); features go in dashboard + REST API
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T18:30:21.733Z
Learning: All store mutation actions must use the pattern from `stores/connections/crud-actions.ts`: wrap API calls in `try`/`catch`, emit success toasts on success, emit error toasts on failure, and return sentinels; optimistic mutations must capture `previous` synchronously and restore in `catch`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T18:30:21.733Z
Learning: `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
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T18:30:21.733Z
Learning: Every test teardown must call `useToastStore.getState().dismissAll()`, `cancelPendingPersist()` (notifications store), and `useThemeStore.getState().teardown()` via the global `afterEach` in `web/src/test-setup.tsx`; any new store that schedules timers or attaches event listeners must expose an equivalent cleanup hook
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T18:30:21.733Z
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; a new store that schedules timers must expose a teardown hook and register it in the global `afterEach`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T18:30:21.733Z
Learning: When the WebSocket handshake fails twice in a row with close code 1006 before `auth_ok` arrives (canonical signature of a reverse proxy that does not forward WS upgrades), switch transport to read-only SSE feed against `/api/v1/events/stream` via `web/src/api/sse/client.ts`, mapping AG-UI projected event types onto the dashboard's internal `WsEvent` envelope
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T18:30:21.733Z
Learning: ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T18:30:21.733Z
Learning: Prefer interface for defining object shapes in TypeScript (per TypeScript best practices)
📚 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:
scripts/check_dto_types_ts_in_sync.pytests/unit/scripts/fixtures/__init__.pytests/unit/scripts/fixtures/dto_codegen_fixture_schema.pytests/unit/scripts/test_check_dto_types_ts_in_sync.pytests/unit/scripts/test_generate_dto_types_ts.pyscripts/generate_dto_types_ts.py
🪛 LanguageTool
web/CLAUDE.md
[grammar] ~72-~72: Ensure spelling is correct
Context: .../ <Name>Page shortcuts for Litestar's monomorphised generics), and `web/src/api/types/enum-...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1909 +/- ##
=======================================
Coverage 85.05% 85.06%
=======================================
Files 1834 1834
Lines 107149 107149
Branches 9241 9241
=======================================
+ Hits 91140 91148 +8
+ Misses 13776 13771 -5
+ Partials 2233 2230 -3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 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 `@scripts/generate_dto_types_ts.py`:
- Around line 203-219: The subprocess.run invocation that invokes
openapi-typescript (the call that assigns to result) needs a timeout to avoid
indefinite hangs: add a timeout parameter (e.g. timeout=TIMEOUT_SECONDS) or make
it configurable via an env var/constant (e.g. OPENAPI_TYPESCRIPT_TIMEOUT), wrap
the subprocess.run call in a try/except that catches subprocess.TimeoutExpired
and logs/errors out (use the existing logging/exit pattern used elsewhere) so
the generator fails fast instead of hanging; reference the subprocess.run call,
the result variable, and subprocess.TimeoutExpired in your changes.
In `@web/src/api/types/agents.ts`:
- Around line 35-38: Replace the hardcoded union with a type alias derived from
the generated DTO: import the generated WireAgentConfig type and set AgentTier
to the tier property type (handling the optional field), e.g. use AgentTier =
NonNullable<WireAgentConfig['tier']> or AgentTier =
Exclude<WireAgentConfig['tier'], undefined>; update the file to import
WireAgentConfig from the generated barrel and replace the literal union with
that derived type.
In `@web/src/pages/org/useOrgChartDragDrop.ts`:
- Around line 105-108: The current drag handler always clears autonomy_level and
level by passing nulls to useCompanyStore.getState().updateAgent; instead, read
the existing agent from the store (e.g. const current =
useCompanyStore.getState().agents?.[agentName] or a getAgent helper) and pass
through current.autonomy_level and current.level when doing the department-only
reassignment (use the same newDeptName and keep
optimisticReassignAgent(agentName, newDeptName) as-is), or omit those fields
entirely from the update payload so they are not overwritten with null.
In `@web/src/pages/settings/sinks/SinkFormDrawer.tsx`:
- Line 191: The onChange currently casts the input with "v as RotationStrategy"
which bypasses validation; replace that cast by calling the normalization helper
toRotationStrategy(v) and pass its result into setRotationStrategy so the
component uses the same allowlist validation used at initialization; update the
onChange handler that sets rotation strategy (the setRotationStrategy call) to
use toRotationStrategy instead of a direct type cast, referencing
RotationStrategy for types.
In `@web/src/pages/tasks/TaskDetailPanel.tsx`:
- Around line 146-150: Create a small helper in TaskDetailPanel.tsx (e.g.,
sendTaskUpdate or buildUpdatePayload) that centralises the required wire
envelope for UpdateTaskRequest so callers only pass the changing fields; the
helper should call onUpdate(task.id, { ...patch, expected_version: task.version,
priority: task.priority }). Replace the inline calls that currently do await
onUpdate(task.id, { title: value, expected_version: task.version, priority:
task.priority }) (and the similar calls near the other handlers) to use this
helper so expected_version and priority are always set consistently.
In `@web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts`:
- Around line 200-216: Replace the ad-hoc inline types with the generated DTO
types: import the appropriate request DTO (e.g., CreateWorkflowRequest or
WorkflowCreateRequest) and the IO DTO (e.g., IORequest or WorkflowIO) from the
generated types and use them instead of the inline type in the toIORequest
signature; update the toIORequest return type to that IO DTO and map
inputs/outputs to that DTO. Also replace the broad casts on nodes and edges
(nodeData as readonly Record<string, unknown>[], edgeData as readonly
Record<string, unknown>[]) with the generated node/edge DTO types (e.g.,
WorkflowNodeDTO[] and WorkflowEdgeDTO[]) or the request’s expected shapes before
passing them into useWorkflowsStore.getState().createWorkflow, and ensure the
createWorkflow payload uses the imported
CreateWorkflowRequest/WorkflowCreateRequest type for compile-time schema parity.
In `@web/src/stores/tasks.ts`:
- Line 155: isTaskShape currently rejects undefined for assigned_to, deadline,
and parent_task_id so sanitizeTask's "?? null" fallbacks never run; update the
WS shape guard (isTaskShape) to accept those three fields as optional/undefined
(rather than requiring non-undefined) so that sanitizeTask (and calls to
sanitizeNullable for assigned_to, deadline, parent_task_id) can normalise
omitted fields to null; ensure you update the corresponding guard checks that
reference assigned_to, deadline, and parent_task_id (also applied to the similar
checks around lines 172-174) to allow undefined values.
🪄 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: 8f218a33-ab33-464c-bef0-24e0e997339a
📒 Files selected for processing (17)
CLAUDE.mdscripts/generate_dto_types_ts.pyweb/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsxweb/src/__tests__/stores/setup-wizard.test.tsweb/src/api/types/agents.tsweb/src/hooks/useConnectionsData.tsweb/src/pages/OrgEditPage.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/tasks/TaskDetailPanel.tsxweb/src/pages/workflow-editor/useWorkflowEditorCallbacks.tsweb/src/pages/workflows/WorkflowCard.tsxweb/src/stores/meetings.tsweb/src/stores/setup-wizard/providers.tsweb/src/stores/tasks.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 (9)
web/src/**/*.{tsx,ts}
📄 CodeRabbit inference engine (CLAUDE.md)
Reuse
web/src/components/ui/and design tokens in Web Dashboard; follow design system detailed inweb/CLAUDE.md
Files:
web/src/pages/workflows/WorkflowCard.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/hooks/useConnectionsData.tsweb/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/tasks/TaskDetailPanel.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/workflow-editor/useWorkflowEditorCallbacks.tsweb/src/stores/meetings.tsweb/src/stores/setup-wizard/providers.tsweb/src/api/types/agents.tsweb/src/__tests__/stores/setup-wizard.test.tsweb/src/stores/tasks.ts
web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Always use
createLoggerfrom@/lib/logger; never bareconsole.warn/console.error/console.debugin application code. Variable name must always belog(e.g.const log = createLogger('module-name')). Onlylogger.tsitself may use bare console methods.Use
log.debug()(DEV-only, stripped in production),log.warn(),log.error()logging levels. Pass dynamic/untrusted values as separate args (not interpolated into the message string) so they go throughsanitizeArg. Wrap attacker-controlled fields inside structured objects insanitizeForLog()before embedding.Error-code constants (MANDATORY): import
ErrorCodeandErrorCategoryfrom@/api/types/errors(re-exported from the generatedweb/src/api/types/error-codes.gen.ts). Discriminate onErrorCode.<NAME>, never on raw integer literals.Generated DTO types (MANDATORY): every Pydantic DTO mirrored on the wire is mechanically generated into three committed files:
web/src/api/types/openapi.gen.ts,web/src/api/types/dtos.gen.ts, andweb/src/api/types/enum-values.gen.ts. NEVER hand-edit a*.gen.tsfile. Regenerate withuv run python scripts/generate_dto_types_ts.py. Consumer code imports DTOs via the barrel (import type { AgentConfig } from '@/api/types') or directly from the generated module.
@eslint-react/eslint-pluginv5+ via therecommended-type-checkedpreset (requiresparserOptions.projectService: true, configured inweb/eslint.config.js). 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(withchecksVoidReturn: { attributes: false }).
Files:
web/src/pages/workflows/WorkflowCard.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/hooks/useConnectionsData.tsweb/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/tasks/TaskDetailPanel.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/workflow-editor/useWorkflowEditorCallbacks.tsweb/src/stores/meetings.tsweb/src/stores/setup-wizard/providers.tsweb/src/api/types/agents.tsweb/src/__tests__/stores/setup-wizard.test.tsweb/src/stores/tasks.ts
web/**/{pages,components}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
ALWAYS reuse existing components from
web/src/components/ui/before creating new ones. NEVER hardcode hex colors, font-family declarations, pixel spacing, Motion transition durations, BCP 47 locale literals ('en-US'), or currency symbols / codes; use design tokens,@/lib/motionpresets, the helpers in@/utils/format, andDEFAULT_CURRENCYfrom@/utils/currencies.Status dots ->
<StatusBadge>(defaults torole="img"with aria-label;decorativefor adjacent-labeled,announcefor live WS updates).KPI displays ->
<MetricCard>/<Sparkline>/<ProgressGauge>/<TokenUsageBar>.Cards ->
<SectionCard>(titled wrapper with icon and action slot);<AgentCard>,<DeptHealthBar>for domain-specific.Form fields ->
<InputField>/<SelectField>/<SliderField>/<ToggleField>/<SegmentedControl>/<TagInput>/<SearchInput>.Slide-in panels ->
<Drawer width="compact|narrow|default|wide">(Base UI; do NOT add inlinew-[40vw]overrides).Loading / empty / error states ->
<Skeleton>family /<EmptyState>/<ErrorBoundary>/<ErrorBanner>/<ProgressIndicator>.Status / role / risk / urgency badge classes ->
STATUS_COLORSfamily from@/styles/status-colors(typedRecord<EnumValue, string>lookups; no inlineRecord<EnumValue, string>constants per page).Confirmation / toasts ->
<ConfirmDialog>/<Toast>(Zustand-backed queue, NOT Base UI's Toast).Cmd+K / shortcuts ->
<CommandPalette>/<KeyboardShortcutHint>/<CommandCheatsheet>.Animation ->
<AnimatedPresence>/<StaggerGroup>/<LiveRegion>(debounced ARIA live for WS updates).
Files:
web/src/pages/workflows/WorkflowCard.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/tasks/TaskDetailPanel.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts
web/**/pages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
List-page primitives ->
<ListHeader>/<SearchFilterSort>/<Pagination>/<BulkActionBar>/<MetadataGrid>/<Breadcrumbs>/<Collapsible>. Page conventions: root container usesspace-y-section-gap;<ErrorBanner>lands immediately after<ListHeader>, before any filter / pagination row.Breadcrumb depth: aim for 2 or 3 levels max in visible trails. When natural depth exceeds 3, route the user to a flatter parent or rely on
<Breadcrumbs maxItems={...} />to collapse middle nodes.<Breadcrumbs items={[]}>returnsnullso unconditional render at the top of a page is safe.Empty-state derivation ->
useEmptyStateProps({ filteredCount, totalCount, filterActive, empty, filtered })from@/hooks/use-empty-state-propsreturnsEmptyStateProps | nullso the page branches on a single value instead of duplicating the "no data ever" / "no data after filter" discriminator.
Files:
web/src/pages/workflows/WorkflowCard.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/tasks/TaskDetailPanel.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts
web/**/*.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
Viewport-size reads ->
useViewportSize()from@/hooks/useViewportSize(useSyncExternalStoreoverwindowresize). Never readwindow.innerWidth/window.innerHeightdirectly inside a component render body oruseMemo.
Files:
web/src/pages/workflows/WorkflowCard.tsxweb/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/tasks/TaskDetailPanel.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/OrgEditPage.tsx
web/**/{pages,components,styles,utils}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
A PostToolUse hook (
scripts/check_web_design_system.py) runs on everyweb/src/edit and flags hardcoded hex / rgba / fonts / Motion durations / locale literals / bare.toLocale*String()calls / missing Storybook stories / duplicate component patterns / complex.map()blocks. Fix every violation before proceeding.
Files:
web/src/pages/workflows/WorkflowCard.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsxweb/src/pages/org-edit/AgentEditDrawer.tsxweb/src/pages/tasks/TaskDetailPanel.tsxweb/src/pages/org-edit/DepartmentCreateDialog.tsxweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts
web/**/stores/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
List reads (
fetch*) seterror: string | nullon the store instead of toasting.Cursor pagination (MANDATORY): list endpoints use opaque cursor-based paging via
PaginationMeta. Stores keepnextCursor+hasMorein state (not offset arithmetic) and early-return when!hasMore || !nextCursor. Display counts come fromdata.length; the wire envelope no longer carriestotal.Test teardown (MANDATORY):
web/src/test-setup.tsxregisters a globalafterEachthat callsuseToastStore.getState().dismissAll(),cancelPendingPersist()(notifications store), anduseThemeStore.getState().teardown(). Any new store that schedules timers or attaches event listeners must expose an equivalent cleanup hook and register it in the globalafterEach. The websocket store is a deliberate exception (file-localresetStore()in its test file).Active-handle gate (MANDATORY): every unit test runs under
web/test-infra/active-handle-tracker.ts, which hooks Node'sasync_hooksand fails any test that leaks an event-loop-holding resource. A new store that schedules timers / attaches listeners MUST expose a teardown hook and register it in the globalafterEach; otherwise the gate fails the first test that triggers the schedule.WS payload sanitization (MANDATORY):
sanitizeWsString()andsanitizeWsEnum()live inweb/src/utils/ws-sanitize.ts.sanitizeWsString()clamps every WS-supplied string (strips C0 controls + bidi-overrides + caps length).sanitizeWsEnum<T>(value, allowlist, fallback, { field })extends that with enum-allowlist validation. Any new WS payload handler that ingests untrusted strings MUST route through one of these; raw(sanitizeWsString(x, n) ?? '') as EnumTypecasts are forbidden.WS wire protocol (MANDATORY): 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 `src/synthor...
Files:
web/src/stores/meetings.tsweb/src/stores/setup-wizard/providers.tsweb/src/__tests__/stores/setup-wizard.test.tsweb/src/stores/tasks.ts
web/**/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Health / readiness endpoints (MANDATORY):
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/types/agents.ts
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Configuration precedence: DB > env > YAML > code default via
SettingsService/ConfigResolver; noos.environ.getoutside startupNo
from __future__ import annotations(Python 3.14 has PEP 649). Use PEP 758 exceptexcept A, B:no parens unless binding
Files:
scripts/generate_dto_types_ts.py
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T19:35:02.554Z
Learning: Read design spec from `docs/design/` before implementing; deviations need approval per DESIGN_SPEC.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T19:35:02.554Z
Learning: Present every plan for accept/deny before coding
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T19:35:02.554Z
Learning: No region/currency/locale privileged; use metric units; use British English per regional defaults documentation
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T19:35:02.554Z
Learning: Only `src/synthorg/persistence/` may import sqlite/psycopg or emit raw SQL; enforce persistence boundary
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T19:35:02.554Z
Learning: Every convention PR ships its enforcement gate per convention-gates.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T19:35:02.554Z
Learning: Test regression: timeout/slow failures are source-code regression; never edit `tests/baselines/unit_timing.json` or any `scripts/*_baseline.{txt,json}` / `scripts/_*_baseline.py`. Both families PreToolUse-blocked. Per-invocation bypass: `ALLOW_BASELINE_GROWTH=1 git commit ...` requires explicit user approval
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T19:35:02.554Z
Learning: After issue: branch + commit + push without 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-14T19:35:02.554Z
Learning: xdist `-n 8 --dist=loadfile` auto-applied via pyproject `addopts` to prevent Windows ProactorEventLoop leak
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T19:35:02.554Z
Learning: Git commits: `<type>: <description>` (feat/fix/refactor/docs/test/chore/perf/ci); commitizen-enforced
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T19:35:02.554Z
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-14T19:35:02.554Z
Learning: Git branches: `<type>/<slug>` from main
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T19:35:02.554Z
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-14T19:35:02.554Z
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-14T19:35:02.554Z
Learning: GitHub queries: use `gh issue list` via Bash, NOT MCP `list_issues`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T19:35:02.554Z
Learning: After every squash merge → `/post-merge-cleanup`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T19:35:02.554Z
Learning: CLI is Docker-only (init/start/stop/status); features go in dashboard + REST API
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T19:36:01.831Z
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`.
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T19:36:01.831Z
Learning: `web/src/` follows the standard split: `api/` (Axios client + endpoint domains), `components/` (`ui/` primitives + `layout/`), `hooks/`, `lib/`, `mocks/` (MSW), `pages/`, `router/`, `stores/` (Zustand), `styles/` (design tokens), `utils/`, `__tests__/`. Stores over ~600 lines are sliced into packages with one of two aggregation patterns (package-internal `index.ts` or sibling `.ts` aggregator).
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T19:36:01.831Z
Learning: `web/e2e/` holds the Playwright suite: `factories/`, `fixtures/` (`mock-api.ts`, `websocket-harness.ts`), `flows/`, `helpers/`, `visual/`.
📚 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:
scripts/generate_dto_types_ts.py
🔇 Additional comments (23)
CLAUDE.md (1)
45-45: LGTM!web/src/pages/workflows/WorkflowCard.tsx (1)
62-62: LGTM!web/src/hooks/useConnectionsData.ts (1)
43-46: LGTM!Also applies to: 86-86
web/src/stores/meetings.ts (1)
124-124: LGTM!Also applies to: 204-205, 211-214, 321-321, 342-342
web/src/stores/setup-wizard/providers.ts (3)
29-38: LGTM!
55-55: LGTM!Also applies to: 90-105
120-158: LGTM!web/src/__tests__/stores/setup-wizard.test.ts (2)
723-724: LGTM!Also applies to: 782-783, 806-807
928-932: LGTM!Also applies to: 952-955
web/src/api/types/agents.ts (1)
3-34: LGTM!Also applies to: 40-69
scripts/generate_dto_types_ts.py (7)
38-77: LGTM!
80-96: LGTM!
99-159: LGTM!
162-180: LGTM!
226-343: LGTM!
346-398: LGTM!
401-472: LGTM!web/src/pages/org-edit/AgentEditDrawer.tsx (1)
93-97: LGTM!web/src/pages/OrgEditPage.tsx (1)
95-102: LGTM!web/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsx (1)
65-65: LGTM!web/src/pages/org-edit/DepartmentCreateDialog.tsx (1)
62-65: LGTM!web/src/pages/org/useOrgChartDragDrop.ts (1)
6-6: LGTM!Also applies to: 99-103
web/src/pages/settings/sinks/SinkFormDrawer.tsx (1)
24-56: LGTM!
| // UpdateTaskRequest.priority is required on the wire | ||
| // (no ``?``); pass the current task.priority so the | ||
| // title edit doesn't reset it. The original ``null`` | ||
| // here was clearing priority on every title save. | ||
| await onUpdate(task.id, { title: value, expected_version: task.version, priority: task.priority }) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
Centralise the inline-edit update envelope.
These three handlers now have to remember the same expected_version + priority contract. A tiny helper keeps that wire requirement in one place and makes it much harder to reintroduce the earlier priority-clearing bug on the next inline edit.
♻️ Proposed refactor
+ const buildInlineUpdate = useCallback(
+ (patch: Omit<UpdateTaskRequest, 'expected_version' | 'priority'>): UpdateTaskRequest => ({
+ ...patch,
+ expected_version: task.version,
+ priority: task.priority,
+ }),
+ [task.version, task.priority],
+ )
+
@@
- await onUpdate(task.id, { title: value, expected_version: task.version, priority: task.priority })
+ await onUpdate(task.id, buildInlineUpdate({ title: value }))
@@
- await onUpdate(task.id, { description: value, expected_version: task.version, priority: task.priority })
+ await onUpdate(task.id, buildInlineUpdate({ description: value }))
@@
- await onUpdate(task.id, { assigned_to: value.trim() || undefined, expected_version: task.version, priority: task.priority })
+ await onUpdate(task.id, buildInlineUpdate({ assigned_to: value.trim() || undefined }))Also applies to: 162-163, 199-200
🤖 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/tasks/TaskDetailPanel.tsx` around lines 146 - 150, Create a
small helper in TaskDetailPanel.tsx (e.g., sendTaskUpdate or buildUpdatePayload)
that centralises the required wire envelope for UpdateTaskRequest so callers
only pass the changing fields; the helper should call onUpdate(task.id, {
...patch, expected_version: task.version, priority: task.priority }). Replace
the inline calls that currently do await onUpdate(task.id, { title: value,
expected_version: task.version, priority: task.priority }) (and the similar
calls near the other handlers) to use this helper so expected_version and
priority are always set consistently.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/generate_dto_types_ts.py`:
- Around line 388-389: The comparison uses path.read_text() which normalizes
line endings, letting CRLF-vs-LF differences slip through; change the check in
the block around path.read_text/read_text() so it reads the file as bytes (use
path.read_bytes()) and compare to expected.encode("utf-8") instead of comparing
decoded text, ensuring _write()'s LF-only output is validated exactly; update
the variable usage where actual is set and the if (actual != expected)
comparison to operate on bytes.
In `@web/src/stores/tasks.ts`:
- Line 155: The code normalises omitted nullable IDs (e.g., assigned_to) to null
via sanitizeNullable which causes the identifier-mutation rejection to see a
change (undefined → null) and drop valid WS frames; to fix, ensure the mutation
gate compares values after the same normalization instead of raw values (or stop
converting undefined→null at the source). Concretely, either (A) stop mapping
omitted ID fields to null in the assignment spots (symbols: assigned_to, and
similar calls around lines 172-174 and 285-296) so original undefined is
preserved, or (B) change the mutation-check logic to compare
sanitizeNullable(oldValue) vs sanitizeNullable(newValue) (i.e., normalize both
sides before equality check) so undefined→null is not treated as a mutation;
apply the same consistent approach across the referenced fields and the
identifier-mutation rejection code path.
🪄 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: d6d0602c-b199-4799-a21a-994533b59dac
📒 Files selected for processing (10)
scripts/generate_dto_types_ts.pysrc/synthorg/api/openapi.pyweb/src/api/types/agents.tsweb/src/api/types/openapi.gen.tsweb/src/pages/OrgEditPage.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/setup/MiniOrgChart.tsxweb/src/pages/workflow-editor/useWorkflowEditorCallbacks.tsweb/src/stores/tasks.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: Lighthouse Site
- GitHub Check: Dashboard Test
- GitHub Check: Lighthouse Dashboard
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (8)
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Reuse
web/src/components/ui/design tokens; no custom design systems in React 19 componentsExport wrapper component instead of getXIcon(value): LucideIcon factories that return a component reference; do the lookup inside the wrapper via createElement to avoid PascalCase JSX binding
Use useViewportSize() from
@/hooks/useViewportSizefor viewport-size reads; never read window.innerWidth / window.innerHeight directly inside a component render body or useMemo
Files:
web/src/pages/OrgEditPage.tsxweb/src/pages/setup/MiniOrgChart.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/workflow-editor/useWorkflowEditorCallbacks.tsweb/src/stores/tasks.tsweb/src/api/types/agents.ts
!(providers/presets.py|web/public/provider-logos/**|.claude/**)
📄 CodeRabbit inference engine (CLAUDE.md)
Vendor-agnostic naming: NEVER use real vendor names in project code/tests. Use example-provider, test-provider, example-{large,medium,small}-001. Real names allowed only in .claude/, third-party imports, providers/presets.py, and web/public/provider-logos/
Files:
web/src/pages/OrgEditPage.tsxsrc/synthorg/api/openapi.pyweb/src/pages/setup/MiniOrgChart.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/workflow-editor/useWorkflowEditorCallbacks.tsweb/src/stores/tasks.tsweb/src/api/types/agents.tsscripts/generate_dto_types_ts.py
web/**/*.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
Wrap the app in
<CSPProvider nonce={getCspNonce()}>+<MotionConfig nonce>so every inline<style>tag injected by Base UI and Motion carries the per-request CSP nonce
Files:
web/src/pages/OrgEditPage.tsxweb/src/pages/setup/MiniOrgChart.tsxweb/src/pages/settings/sinks/SinkFormDrawer.tsx
web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Always use
createLoggerfrom@/lib/logger; never bareconsole.warn/console.error/console.debugin application codeUse variable name
logfor logger instances (e.g.const log = createLogger('module-name'))Pass dynamic/untrusted values as separate args to logger, not interpolated into the message string, so they go through
sanitizeArgWrap attacker-controlled fields inside structured objects in
sanitizeForLog()before embedding in logsAny new WS payload handler that ingests untrusted strings MUST route through sanitizeWsString() or sanitizeWsEnum(); raw casts like
(sanitizeWsString(x, n) ?? '') as EnumTypeare forbiddenImport ErrorCode and ErrorCategory from
@/api/types/errors; discriminate on ErrorCode., never on raw integer literalsNEVER hardcode hex colors, font-family declarations, pixel spacing, Motion transition durations, BCP 47 locale literals ('en-US'), or currency symbols/codes; use design tokens,
@/lib/motionpresets, helpers in@/utils/format, and DEFAULT_CURRENCY from@/utils/currenciesESLint errors:
@eslint-react/web-api-no-leaked-fetch(detect fetch() in effects without AbortController cleanup),@eslint-react/no-leaked-conditional-rendering(catch {count && } bug),@eslint-react/globals(restrict window/document/localStorage in render),@typescript-eslint/no-floating-promises(forbid unawaited promises),@typescript-eslint/no-misused-promiseswith checksVoidReturn.attributes=false (forbid async functions where callsite ignores promise, except React 19 async event handlers)
Files:
web/src/pages/OrgEditPage.tsxweb/src/pages/setup/MiniOrgChart.tsxweb/src/pages/org/useOrgChartDragDrop.tsweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/workflow-editor/useWorkflowEditorCallbacks.tsweb/src/stores/tasks.tsweb/src/api/types/agents.ts
src/synthorg/!(persistence)/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Only
src/synthorg/persistence/may import sqlite/psycopg or emit raw SQL; other modules must use repository layer
Files:
src/synthorg/api/openapi.py
src/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Use SettingsService/ConfigResolver for configuration precedence: DB > env > YAML > code default; never use
os.environ.get()outside startupNo hardcoded numeric values; place numerics in
settings/definitions/; whitelist 0/1/-1, HTTP codes, hex masks, powers-of-2, and module-level annotated named constantsComments should explain WHY only; never include reviewer citations, issue back-refs, or migration framing. Enforced by check_no_review_origin_in_code.py and check_no_migration_framing.py
Do not use
from __future__ import annotations(Python 3.14+ has PEP 649); use PEP 758 style for exception handlingType hints required on public functions; mypy strict mode; use Google-style docstrings; line length 88; functions <50 lines; files <800 lines
Error classes must follow
<Domain><Condition>Errornaming and inherit fromDomainError, never fromException/RuntimeErrordirectly. Enforced by check_domain_error_hierarchy.pyUse Pydantic v2 with
frozen=Trueandextra="forbid"on API DTOs (Request/Response/Snapshot/Result/Envelope/Status/Info/Summary suffixes); use@computed_fieldfor derived fields; useNotBlankStrfor identifiersUse args models at every system boundary; call
parse_typed()for every external dict ingestion. Enforced by check_boundary_typed.pyEnsure immutability using
model_copy(update=...)orcopy.deepcopy(); deepcopy at system boundariesUse
asyncio.TaskGroupfor fan-out/fan-in async patterns; helper functions should catchException(re-raiseMemoryError/RecursionError)Implement Clock seam:
clock: Clock | None = Noneparameter; tests injectFakeClock. Services own_lifecycle_lock; timed-out stops mark services unrestartableWrap untrusted content with
wrap_untrusted()fromengine.prompt_safety; useHTMLParseGuardfor HTML content (SEC-1)Import logger via
from synthorg.observability import get_logger; variable must be namedlogger. Never useimport loggingorprint()in app codeUse event ...
Files:
src/synthorg/api/openapi.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/openapi.py
web/src/stores/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
All store mutation actions (create / update / delete) must wrap API calls in try/catch, success path updates state + emits success toast, failure path logs + emits error toast + returns sentinel (null for entity returns, false for delete)
Capture
previousstate synchronously in optimistic mutations and restore in catch blockCallers MUST NOT wrap store mutation calls in try/catch; the store owns the error UX
Use cursor-based pagination via PaginationMeta with nextCursor + 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
Files:
web/src/stores/tasks.ts
web/src/api/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
Use
getLiveness()endpoint that is always 200 while the process is alive;getReadiness()endpoint that is 200 healthy / 503 unavailable (binary 'ok' | 'unavailable' outcome, no tri-state)Any new caller must handle the 503 path from getReadiness() explicitly
Files:
web/src/api/types/agents.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:02.181Z
Learning: Read `docs/design/` page before implementing; deviations need approval per DESIGN_SPEC.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:02.181Z
Learning: Present every plan for accept/deny before coding
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:02.181Z
Learning: No region/currency/locale privileged; use metric units; use British English in strings and documentation
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:02.181Z
Learning: Every convention PR ships its enforcement gate; register MANDATORY rules in convention-gates.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:02.181Z
Learning: Timeout/slow test failures indicate source-code regression; never edit test baseline files (tests/baselines/unit_timing.json, scripts/*_baseline.{txt,json}, scripts/_*_baseline.py); PreToolUse-blocked. Bypass requires ALLOW_BASELINE_GROWTH=1
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:02.181Z
Learning: After implementation: branch + commit + push (no auto-PR); use `/pre-pr-review` command. After PR: use `/aurelio-review-pr`. Fix all valid feedback before submitting; no deferring to future PRs
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:02.181Z
Learning: Commit format: <type>: <description> (feat/fix/refactor/docs/test/chore/perf/ci); enforced by commitizen. Sign commits on protected refs (GPG/SSH or GitHub App). Squash merge; PR body becomes commit; trailers (Release-As, Closes `#N`) must be in PR body
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:02.181Z
Learning: Branches: <type>/<slug> from main
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:02.181Z
Learning: Use pre-commit/pre-push hooks from .pre-commit-config.yaml; hookify rules in .claude/hookify.*.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:02.181Z
Learning: After every squash merge, run `/post-merge-cleanup` command
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:02.181Z
Learning: CLI is Docker-only (init/start/stop/status); new features go in dashboard + REST API, not CLI
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:02.181Z
Learning: Use `uv sync` for dependency management; use `uv sync --group docs` for docs toolchain (zensical + D2). All operations via uv, never raw pip
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:02.181Z
Learning: Use `go -C cli` command, never `cd cli`, for CLI operations
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:02.181Z
Learning: Refer to web-specific conventions in web/CLAUDE.md; refer to CLI-specific conventions in cli/CLAUDE.md; refer to Bash conventions in ~/.claude/rules/common/bash.md
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:02.181Z
Learning: GitHub queries: use `gh issue list` via Bash, NOT MCP list_issues tool
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:02.181Z
Learning: Go binary CLI in cli/ subdirectory; web dashboard in React 19 in web/ subdirectory; Python 3.14+ in src/synthorg/ (src layout)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:02.181Z
Learning: License: BUSL-1.1 → Apache 2.0 after Change Date
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:42.394Z
Learning: Always reuse existing components from web/src/components/ui/ before creating new ones
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:42.394Z
Learning: Use cursor-based pagination with PaginationMeta for list endpoints; stores keep nextCursor + hasMore in state (not offset arithmetic)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:42.394Z
Learning: When WebSocket handshake fails twice in a row with close code 1006 before auth_ok, switch transport to read-only SSE feed against /api/v1/events/stream via web/src/api/sse/client.ts; write-path features surface 'Connection limited' toast and direct operator to reload
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:42.394Z
Learning: Store slicing: stores over ~600 lines are sliced into packages with one of two aggregation patterns (package-internal index.ts or sibling .ts aggregator)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:42.394Z
Learning: Use Kanban grouping for status-flow domains where each row's column conveys lifecycle phase (Tasks, Requests); use flat scrollable list for queues without explicit phase semantics (Escalations, Approvals)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:42.394Z
Learning: Aim for 2 or 3 levels max in breadcrumb trails; 4+ level trail almost always reflects a routing mistake; use <Breadcrumbs maxItems={...} /> to collapse middle nodes when natural depth exceeds 3
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:42.394Z
Learning: Root list-page container uses space-y-section-gap; <ErrorBanner> lands immediately after <ListHeader>, before any filter / pagination row; pages with a one-line mission statement pass it via <ListHeader description="..." />
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:42.394Z
Learning: Adopted Base UI primitives (direct import): Dialog, AlertDialog, Popover, Tabs, Menu, Drawer, CSPProvider, merge-props
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:42.394Z
Learning: Not adopted Base UI primitives: Toast (use Zustand-backed queue), Meter (use ProgressGauge), Select (use native <select> for mobile picker UX), Combobox / Autocomplete / OTP Field / Tooltip
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:01:42.394Z
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-14T20:01:42.394Z
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/openapi.pyscripts/generate_dto_types_ts.py
🔇 Additional comments (8)
web/src/pages/settings/sinks/SinkFormDrawer.tsx (1)
24-37: LGTM!Also applies to: 51-56, 191-191
web/src/pages/setup/MiniOrgChart.tsx (1)
77-77: LGTM!Also applies to: 96-96
web/src/pages/org/useOrgChartDragDrop.ts (1)
6-6: LGTM!Also applies to: 99-114
web/src/pages/OrgEditPage.tsx (1)
95-103: LGTM!web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts (2)
222-223: AvoidRecord<string, unknown>[]casts at the workflow creation boundary.These casts bypass DTO-shape checking for
nodes/edgesexactly where parity matters most; please type these arrays to the concrete request DTOs and pass them without broad casts.
11-16: LGTM!Also applies to: 200-221
web/src/api/types/agents.ts (1)
3-68: LGTM!src/synthorg/api/openapi.py (1)
215-225: LGTM!
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/generate_dto_types_ts.py`:
- Around line 83-99: _change _hermetic_env_setdefaults so it does not
permanently mutate os.environ: capture the original values (or absence) of
SYNTHORG_DB_PATH, SYNTHORG_DATABASE_URL, and SYNTHORG_PAGINATION_CURSOR_SECRET,
apply the temporary overrides as now, and return a small restore callable (or
implement a contextmanager) that restores the originals when invoked; update
call sites that use _hermetic_env_setdefaults to call the returned restore (or
use the context manager) after the export completes — apply the same pattern for
the similar logic around the second occurrence referenced in the diff.
- Around line 345-351: The code currently injects raw enum member strings into
rendered_members (built from members) and writes them into the TS literal array
(export const {snake}_VALUES), which breaks if a member contains single quotes,
backslashes or newlines; fix by escaping each member before wrapping in single
quotes — create/use a small escape helper (e.g., escape for '\', '\'', '\n',
'\r', '\t') and apply it when building rendered_members in the same block where
snake and rendered_members are computed so that the f" '{value}'" becomes f"
'{escape(value)}'"; ensure all special characters are replaced with their JS/TS
escape sequences so generated TypeScript is always valid.
🪄 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: f3ad027e-231a-45f9-a405-c33e66726e93
📒 Files selected for processing (2)
scripts/generate_dto_types_ts.pyweb/src/api/types/openapi.gen.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: Test (Python 3.14)
- GitHub Check: Dashboard Test
- GitHub Check: Build Web Assets (melange)
- GitHub Check: Lighthouse Site
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{py,ts,tsx,go,yaml,yml,md}
📄 CodeRabbit inference engine (CLAUDE.md)
No region/locale privilege; use metric units; use British English per docs/reference/regional-defaults.md
Files:
scripts/generate_dto_types_ts.py
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Do not use
from __future__ import annotations(Python 3.14+ has PEP 649); use PEP 758 except forexcept A, B:syntax
Files:
scripts/generate_dto_types_ts.py
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:44:56.893Z
Learning: Read design specification from `docs/design/` before implementing; deviations require approval per [DESIGN_SPEC.md](docs/DESIGN_SPEC.md)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:44:56.893Z
Learning: Present every plan for accept/deny before coding
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:44:56.893Z
Learning: Every convention PR ships its enforcement gate per [docs/reference/convention-gates.md](docs/reference/convention-gates.md)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:44:56.893Z
Learning: After issue completion: branch + commit + push (no auto-PR); use `/pre-pr-review`; after PR creation use `/aurelio-review-pr` for external feedback; fix all valid feedback before merging
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:44:56.893Z
Learning: Commits: `<type>: <description>` (feat/fix/refactor/docs/test/chore/perf/ci); commitizen-enforced
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:44:56.893Z
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-14T20:44:56.893Z
Learning: Branches: `<type>/<slug>` from main; pre-commit/pre-push hooks in `.pre-commit-config.yaml`; hookify rules in `.claude/hookify.*.md`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:44:56.893Z
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-14T20:44:56.893Z
Learning: Run `/post-merge-cleanup` after every squash merge
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:44:56.893Z
Learning: CLI is Docker-only (init/start/stop/status); features go in dashboard + REST API
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:45:50.197Z
Learning: Lint runs via `npm --prefix web run lint` with `--max-warnings 0`. To enumerate stale `eslint-disable` directives: `npm --prefix web run lint -- --report-unused-disable-directives-severity=warn`.
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T20:45:50.197Z
Learning: Base UI Adopted components: Dialog, AlertDialog, Popover, Tabs, Menu, Drawer, CSPProvider, merge-props (direct import from `base-ui/react/<subpath>`). Not adopted: Toast, Meter, Select, Combobox, Autocomplete, OTP Field, Tooltip.
📚 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:
scripts/generate_dto_types_ts.py
🔇 Additional comments (1)
scripts/generate_dto_types_ts.py (1)
390-536: LGTM!
There was a problem hiding this comment.
♻️ Duplicate comments (1)
scripts/generate_dto_types_ts.py (1)
83-99:⚠️ Potential issue | 🟠 Major | ⚡ Quick winMake the export environment truly hermetic and restore it afterwards.
Lines 93-98 only apply defaults, so any pre-existing
SYNTHORG_DB_PATHorSYNTHORG_PAGINATION_CURSOR_SECRETstill changes the app boot. Thenexport_openapi_schema()leaves the mutated values inos.environfor the rest of the interpreter. That breaks the generator’s determinism contract and can leak:memory:/ the fake cursor secret into later tests or tooling that imports this module in-process.Suggested fix
+from contextlib import contextmanager + +_HERMETIC_ENV_KEYS: Final[tuple[str, ...]] = ( + "SYNTHORG_DB_PATH", + "SYNTHORG_DATABASE_URL", + "SYNTHORG_PAGINATION_CURSOR_SECRET", +) + + +@contextmanager +def _hermetic_env() -> object: + original = {key: os.environ.get(key) for key in _HERMETIC_ENV_KEYS} + try: + os.environ["SYNTHORG_DB_PATH"] = ":memory:" + os.environ.pop("SYNTHORG_DATABASE_URL", None) + os.environ["SYNTHORG_PAGINATION_CURSOR_SECRET"] = ( + "openapi-export-stable-cursor-secret-not-a-real-secret" + ) + yield + finally: + for key, value in original.items(): + if value is None: + os.environ.pop(key, None) + else: + os.environ[key] = value - -def _hermetic_env_setdefaults() -> None: - ... def export_openapi_schema() -> dict[str, Any]: """Boot the app and return the enriched OpenAPI schema dict. @@ - _hermetic_env_setdefaults() - # Defer imports so unit tests can patch this function without - # paying the app-boot cost. - from synthorg.api.app import create_app - from synthorg.api.openapi import inject_rfc9457_responses - - app = create_app() - schema = app.openapi_schema.to_schema() - schema = inject_rfc9457_responses(schema) - return _normalise_enum_descriptions(schema) + with _hermetic_env(): + # Defer imports so unit tests can patch this function without + # paying the app-boot cost. + from synthorg.api.app import create_app + from synthorg.api.openapi import inject_rfc9457_responses + + app = create_app() + schema = app.openapi_schema.to_schema() + schema = inject_rfc9457_responses(schema) + return _normalise_enum_descriptions(schema)Also applies to: 165-183
🤖 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 `@scripts/generate_dto_types_ts.py` around lines 83 - 99, The helper _hermetic_env_setdefaults currently mutates os.environ with defaults but does not restore previous values, and export_openapi_schema leaves those changes in-process; modify _hermetic_env_setdefaults (and the equivalent helper used around lines 165-183) to save the original presence and values of SYNTHORG_DB_PATH, SYNTHORG_DATABASE_URL and SYNTHORG_PAGINATION_CURSOR_SECRET, then set the hermetic values, and provide a paired restore step (or context manager) that, after export_openapi_schema runs, restores each variable to its original value or removes it if it did not exist; ensure export_openapi_schema is invoked while the hermetic env is active and that restoration happens even on error (use try/finally or a context manager) so the environment is truly temporary and deterministic.
🤖 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.
Duplicate comments:
In `@scripts/generate_dto_types_ts.py`:
- Around line 83-99: The helper _hermetic_env_setdefaults currently mutates
os.environ with defaults but does not restore previous values, and
export_openapi_schema leaves those changes in-process; modify
_hermetic_env_setdefaults (and the equivalent helper used around lines 165-183)
to save the original presence and values of SYNTHORG_DB_PATH,
SYNTHORG_DATABASE_URL and SYNTHORG_PAGINATION_CURSOR_SECRET, then set the
hermetic values, and provide a paired restore step (or context manager) that,
after export_openapi_schema runs, restores each variable to its original value
or removes it if it did not exist; ensure export_openapi_schema is invoked while
the hermetic env is active and that restoration happens even on error (use
try/finally or a context manager) so the environment is truly temporary and
deterministic.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 5a1ed954-4663-421e-b6b6-82d5a38af4f1
📒 Files selected for processing (2)
scripts/generate_dto_types_ts.pytests/unit/scripts/test_generate_dto_types_ts.py
📜 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 Backend
- GitHub Check: Build Web Assets (melange)
- GitHub Check: Dashboard Test
- GitHub Check: Test (Python 3.14)
- GitHub Check: Lighthouse Site
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{py,pyi}
📄 CodeRabbit inference engine (CLAUDE.md)
Read design specification in
docs/design/before implementing; deviations require approval (MANDATORY)No region/currency/locale privileged; use metric units; use British English (MANDATORY)
Every convention PR must ship its enforcement gate (MANDATORY)
Configuration precedence: DB > env > YAML > code default via
SettingsService/ConfigResolver; noos.environ.getoutside startup (MANDATORY)No hardcoded numeric values; place numerics in
settings/definitions/; allowlist: 0/1/-1, HTTP codes, hex masks, powers-of-2, module-level annotated named constants (MANDATORY). Enforced byscripts/check_no_magic_numbers.pyTimeout/slow test failures indicate source-code regression; never edit
tests/baselines/unit_timing.jsonorscripts/*_baseline.*files. Both are PreToolUse-blocked. Per-invocation bypass requiresALLOW_BASELINE_GROWTH=1and explicit user approval (MANDATORY)Comments should explain WHY only; no reviewer citations, issue back-refs, or migration framing. Enforced by
check_no_review_origin_in_code.py+check_no_migration_framing.pyNo
from __future__ import annotations(Python 3.14 has PEP 649); use PEP 758 except forexcept A, B:without parens unless bindingType hints required on public functions; mypy strict mode; Google-style docstrings; line length 88; functions <50 lines; files <800 lines
Errors must use
<Domain><Condition>Errorpattern inheriting fromDomainError; never inherit fromException/RuntimeErrordirectly. Enforced bycheck_domain_error_hierarchy.pyUse Pydantic v2 frozen models with
extra="forbid"on API DTOs (Request/Response/Snapshot/Result/Envelope/Status/Info/Summary suffixes); use@computed_fieldfor derived fields;NotBlankStrfor identifiersUse args models at every system boundary; apply
parse_typed()for every external dict ingestion. Enforced bycheck_boundary_typed.pyEnsure immutability: use
model_copy(update=...)orcopy.deepcopy(); deepcopy at system boundariesAsync patte...
Files:
tests/unit/scripts/test_generate_dto_types_ts.pyscripts/generate_dto_types_ts.py
tests/**/*.{py,pyi}
📄 CodeRabbit inference engine (CLAUDE.md)
Test markers:
@pytest.mark.{unit,integration,e2e,slow}. Asyncauto. Timeout 30s global. Coverage 80% min. xdist-n 8 --dist=loadfileauto-applied via pyprojectaddoptsWindows: unit tests use
WindowsSelectorEventLoopPolicy(3.14 IOCP teardown race); subprocess tests override backTest doubles:
FakeClockfor Clock seam,mock_of[T](**overrides)for typed-boundary substitutions,SimpleNamespacefor attribute-bags. BareMagicMockat typed boundary is blocked byscripts/check_mock_spec.py(zero-tolerance). Import fromtests._shared; inject viaclock=and helper subscriptHypothesis: 10 deterministic CI examples; failures are real bugs (fix + add
@example(...)). Never skip/xfail flaky tests; fix fundamentally. Useasyncio.Event().wait()notsleep(large)
Files:
tests/unit/scripts/test_generate_dto_types_ts.py
tests/**/*.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/scripts/test_generate_dto_types_ts.py
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T21:02:48.063Z
Learning: Present every plan for accept/deny before coding (MANDATORY)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T21:02:48.063Z
Learning: After issue: branch + commit + push (no auto-PR); use `/pre-pr-review`; after PR use `/aurelio-review-pr` for external feedback; fix all valid issues before merging (MANDATORY)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T21:02:48.063Z
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). Branches: `<type>/<slug>` from main. Squash merge; PR body becomes commit; trailers in PR body
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T21:02:48.063Z
Learning: After every squash merge → `/post-merge-cleanup`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T21:03:43.310Z
Learning: The client-server WebSocket 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
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T21:03:43.310Z
Learning: Bump the WebSocket protocol version on both client and server together for breaking payload changes
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T21:03:43.310Z
Learning: ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T21:03:43.310Z
Learning: Use Kanban grouping for status-flow domains where each row's column conveys lifecycle phase (Tasks, Requests); use a flat scrollable list for queues without explicit phase semantics (Escalations, Approvals)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T21:03:43.310Z
Learning: Aim for 2 or 3 levels max in visible breadcrumb trails; the dashboard's information architecture is intentionally flat
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T21:03:43.310Z
Learning: Enable `eslint-react/eslint-plugin` v5+ via the `recommended-type-checked` preset (requires `parserOptions.projectService: true` in `web/eslint.config.js`)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T21:03:43.310Z
Learning: Lint runs via `npm --prefix web run lint` with `--max-warnings 0`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T21:03:43.310Z
Learning: Base UI Dialog, AlertDialog, Popover, Tabs, Menu, Drawer, CSPProvider, merge-props are adopted (direct import from `base-ui/react/<subpath>`)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T21:03:43.310Z
Learning: Base UI Toast is NOT adopted; use Zustand-backed queue instead
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T21:03:43.310Z
Learning: Base UI Meter, Select, Combobox, Autocomplete, OTP Field, Tooltip are NOT currently adopted
📚 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:
tests/unit/scripts/test_generate_dto_types_ts.pyscripts/generate_dto_types_ts.py
c295273 to
1dfae9f
Compare
There was a problem hiding this comment.
Actionable comments posted: 17
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
web/src/api/endpoints/providers.ts (1)
405-410:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAvoid implicit destructive default in model sync.
Defaulting to
replace_existing: truechanges behaviour for every caller that omitsdata, which can unexpectedly replace existing model configs. Keep the endpoint default non-opinionated and require explicit replacement intent at call sites.Proposed fix
export async function syncProviderModels( name: string, - data: SyncModelsRequest = { replace_existing: true }, + data: SyncModelsRequest = {}, ): Promise<SyncModelsResponse> {🤖 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/api/endpoints/providers.ts` around lines 405 - 410, The endpoint currently defaults the SyncModelsRequest parameter to { replace_existing: true }, which implicitly causes destructive behavior; change the default to a non-opinionated value (e.g., undefined or { replace_existing: false }) so callers must explicitly opt into replacement, update the API call signature that uses the data parameter (the function using SyncModelsRequest / returning Promise<SyncModelsResponse> and the apiClient.post call to `/providers/${encodeURIComponent(name)}/models/sync`) to remove the destructive default, and adjust any call sites/tests that relied on the implicit replace_existing: true to pass replace_existing: true explicitly when they intend replacement.web/src/api/types/providers.ts (1)
42-56: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick winDerive
ProviderAuditEventTypefrom the generated DTO instead of hardcoding it.This is still a manual mirror of a wire field, so backend changes to
ProviderAuditEvent.event_typecan drift silently past the new generator pipeline. Alias it from the generated DTO field and keep this file as a true shim.Proposed fix
import type { CloudPreset as WireCloudPreset, LocalPreset as WireLocalPreset, + ProviderAuditEvent as WireProviderAuditEvent, ProviderResponse as WireProviderResponse, } from './dtos.gen' -/** Frontend-only union mirroring the inline string union on the wire - * ProviderAuditEvent.event_type. */ -export type ProviderAuditEventType = - | 'provider_created' - | 'provider_updated' - | 'provider_deleted' - | 'provider_credentials_rotated' - | 'provider_rate_limits_updated' - | 'preset_override_updated' - | 'model_added' - | 'model_removed' - | 'model_config_updated' - | 'model_pulled' - | 'models_synced' +/** Frontend alias derived from the generated wire field. */ +export type ProviderAuditEventType = WireProviderAuditEvent['event_type']As per coding guidelines, “Generated DTO types (MANDATORY): every Pydantic DTO mirrored on the wire is mechanically generated…”.
🤖 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/api/types/providers.ts` around lines 42 - 56, Replace the hardcoded union for ProviderAuditEventType with a type alias that references the generated DTO's event_type so it stays in sync with the backend; import the generated DTO type (the generated ProviderAuditEvent / ProviderAuditEventDto type produced by the generator) and make ProviderAuditEventType an alias of ThatGeneratedType['event_type'] instead of listing the string literals in this file.
🤖 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__/pages/ProviderDetailPage.test.tsx`:
- Line 45: Replace the hardcoded 'USD' in the test fixture object for id
'test-model' with the shared currency constant (e.g., DEFAULT_CURRENCY), add an
import for that constant at the top of ProviderDetailPage.test.tsx, and ensure
the fixture's currency property uses DEFAULT_CURRENCY so tests follow the shared
currency configuration rather than a privileged literal.
In `@web/src/__tests__/utils/budget.property.test.ts`:
- Line 39: The test arbitrary currently hardcodes a single currency via
currency: fc.constant('USD'), which introduces a privileged default; replace it
with a non‑privileged arbitrary such as currency:
fc.constantFrom('GBP','EUR','USD') or a similar fc.oneof/fc.constantFrom list so
the arbitrary can produce multiple common currencies (refer to the currency
property in this test file to locate the change) and update any associated
expectations that assume a single currency.
In `@web/src/__tests__/utils/budget.test.ts`:
- Line 28: The shared test fixture in web/src/__tests__/utils/budget.test.ts
currently hardcodes currency: 'USD', which embeds a regional default; change the
fixture to use the project's default currency constant (e.g., DEFAULT_CURRENCY
or PROJECT_DEFAULT_CURRENCY) or remove the hardcoded value and require tests to
pass currency explicitly when constructing the fixture, updating any references
to the fixture's currency property accordingly.
In `@web/src/api/types/budget.ts`:
- Around line 32-42: Replace the inline union type for the field call_category
with the shared generated type LLMCallCategory (like FinishReason is used
elsewhere): import and use LLMCallCategory instead of the hand-maintained
"'productive' | 'coordination' | 'system' | 'embedding' | null" so the frontend
type follows the generated LLM_CALL_CATEGORY_VALUES in enum-values.gen.ts and
avoids drift.
In `@web/src/api/types/ceremony-policy.ts`:
- Line 27: Replace the inline import type in the optional property declaration
(strategy?: import('./enum-values.gen').CeremonyStrategyType | null) with the
already re-exported top-level type CeremonyStrategyType; update the strategy
property's type to simply use CeremonyStrategyType so the file uses the existing
import/export instead of the inline import() syntax.
In `@web/src/mocks/handlers/providers.ts`:
- Line 257: The mock is hardcoding currency: 'USD'—replace this with the
imported DEFAULT_CURRENCY constant for consistency; locate the object in
web/src/mocks/handlers/providers.ts where currency: 'USD' appears (same pattern
as buildDepartmentHealth) and change it to currency: DEFAULT_CURRENCY, ensuring
DEFAULT_CURRENCY is already imported at the top of the file.
In `@web/src/pages/meetings/MeetingTokenBreakdown.tsx`:
- Around line 62-65: The code currently sets contributionRank =
meeting.contribution_rank ?? [] which hides valid data when contribution_rank is
missing; change the fallback to use the keys of the token usage map (e.g.,
Object.keys(tokenUsage) or similar) so contributionRank becomes
meeting.contribution_rank ?? Object.keys(tokenUsage || {}), then reuse that
contributionRank when building segments (the segments variable) and when
rendering participant rows so the bar segments and rows are populated from
tokenUsage when contribution_rank is omitted.
In `@web/src/pages/org-edit/DepartmentEditDrawer.tsx`:
- Line 94: The save payload in DepartmentEditDrawer is unconditionally setting
autonomy_level: null which can wipe persisted settings; update the submit/save
logic (e.g., the component's handleSubmit/onSave/saveDepartment function) to
only include autonomy_level in the payload when the form actually manages or has
a value for it (e.g., check the form state or a changedFields flag and omit
autonomy_level if undefined/null/unchanged) so the field is not sent as null on
every update.
In `@web/src/pages/providers/ModelConfigDrawer.stories.tsx`:
- Line 11: The fixture in ModelConfigDrawer.stories.tsx currently hardcodes
currency: 'USD'; replace this literal with the shared default currency constant
(e.g., DEFAULT_CURRENCY or SHARED_DEFAULT_CURRENCY) by importing that constant
from your central constants module and using it for the currency field in the
fixture object (update the import list and replace the 'currency' value
accordingly).
In `@web/src/pages/providers/ProviderModelList.stories.tsx`:
- Line 12: Replace hardcoded 'USD' currency literals in the story fixtures with
the shared DEFAULT_CURRENCY constant: import DEFAULT_CURRENCY from the project's
shared constants module and update each fixture object's currency property (the
currency fields in ProviderModelList.stories.tsx) to use DEFAULT_CURRENCY
instead of the string 'USD' (apply to all occurrences in this file).
In `@web/src/pages/providers/TestConnectionResult.tsx`:
- Line 30: The JSX contains a redundant nullish coalescing: inside the
null-guarded expression where result.latency_ms != null, remove the unreachable
"?? null" and pass result.latency_ms directly to formatLatency; update the
template literal in the TestConnectionResult JSX so it calls
formatLatency(result.latency_ms) instead of formatLatency(result.latency_ms ??
null).
In `@web/src/pages/tasks/TaskDetailHeader.tsx`:
- Around line 26-30: The update call in TaskDetailHeader is clearing task
priority because updateTask is being passed priority: null; change the call to
avoid mutating priority on title edits by removing the priority field (or pass
the current task.priority) when only updating title and expected_version—i.e.,
call useTasksStore.getState().updateTask(task.id, { title: value,
expected_version: task.version }) so priority is preserved.
In `@web/src/pages/workflows/WorkflowCreateDrawer.tsx`:
- Line 94: The code is unsafely casting form.workflowType to the union via
"workflow_type: form.workflowType as 'sequential_pipeline' |
'parallel_execution' | 'kanban' | 'agile_kanban'"; replace this with a runtime
check against the allowed WORKFLOW_TYPES collection (e.g., array or set) to
narrow the type before building the request payload: if
WORKFLOW_TYPES.includes(form.workflowType) use form.workflowType as the union,
otherwise handle the invalid case (reject, show validation error, or fallback to
a safe default). Update the construction that sets workflow_type (referencing
workflow_type and form.workflowType) to use the validated/narrowed value so
invalid strings are never sent.
In `@web/src/pages/workflows/WorkflowTableView.tsx`:
- Line 90: The workflow type cell currently uses {(w.workflow_type ??
'').replace(/_/g, ' ')} which yields an empty pill when workflow_type is
missing; change this to render a visible fallback label (e.g., "Unknown" or
"N/A") or conditionally render the badge only when workflow_type exists. Locate
the expression in WorkflowTableView.tsx (the use of w.workflow_type and
replace(/_/g, ' ')) and update it to return the replaced string when
w.workflow_type is present, otherwise return a clear fallback label so the table
never shows a blank pill.
In `@web/src/stores/providers/crud-actions.ts`:
- Line 322: The default parameter "data: SyncModelsRequest = { replace_existing:
true }" forces replacement and contradicts the function's "merge with persisted"
behavior; remove the hardcoded default so callers who omit data retain the merge
semantics (e.g., change the parameter to an optional "data?: SyncModelsRequest"
or no default value) and let callers explicitly pass { replace_existing: true }
when they intend replacement; update any call sites that relied on the old
default if they actually require replacement.
In `@web/src/stores/setup-wizard/providers.ts`:
- Around line 95-105: The shortcut flow looks up a preset (get().presets) and
forwards its auth_type (authType) to createFromPreset, but only supplies
apiKey/baseUrl so non-api_key types will fail; update this branch to detect
unsupported auth types (e.g., subscription, custom_header) and either call
createProviderFromPresetFull with the full preset data (so required credential
fields are provided) or throw a clear error before calling createFromPreset;
specifically change the logic around presetName/authType to route to
createProviderFromPresetFull for non-'api_key' auth types or return a
user-visible rejection.
In `@web/src/stores/workflow-editor/validation.ts`:
- Around line 60-65: The validation currently overwrites draft metadata by
unconditionally setting description, version, workflow_type, inputs, outputs,
and is_subworkflow to hardcoded defaults; instead, update the validation logic
that constructs the draft object (the block assigning description, version,
workflow_type, inputs, outputs, is_subworkflow) to preserve existing draft
values and only apply defaults when a field is truly undefined (use nullish
checks or conditionals), e.g. keep non-empty inputs/outputs and a true
is_subworkflow as-is and only default workflow_type/version/description when
missing.
---
Outside diff comments:
In `@web/src/api/endpoints/providers.ts`:
- Around line 405-410: The endpoint currently defaults the SyncModelsRequest
parameter to { replace_existing: true }, which implicitly causes destructive
behavior; change the default to a non-opinionated value (e.g., undefined or {
replace_existing: false }) so callers must explicitly opt into replacement,
update the API call signature that uses the data parameter (the function using
SyncModelsRequest / returning Promise<SyncModelsResponse> and the apiClient.post
call to `/providers/${encodeURIComponent(name)}/models/sync`) to remove the
destructive default, and adjust any call sites/tests that relied on the implicit
replace_existing: true to pass replace_existing: true explicitly when they
intend replacement.
In `@web/src/api/types/providers.ts`:
- Around line 42-56: Replace the hardcoded union for ProviderAuditEventType with
a type alias that references the generated DTO's event_type so it stays in sync
with the backend; import the generated DTO type (the generated
ProviderAuditEvent / ProviderAuditEventDto type produced by the generator) and
make ProviderAuditEventType an alias of ThatGeneratedType['event_type'] instead
of listing the string literals in this file.
🪄 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: 8eb26533-c483-435d-b40a-3a7e667d304c
⛔ Files ignored due to path filters (1)
web/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (150)
.github/workflows/ci.yml.pre-commit-config.yamlCLAUDE.mddata/runtime_stats.yamldocs/reference/convention-gates.mdscripts/check_dto_types_ts_in_sync.pyscripts/convention_gate_map.yamlscripts/generate_dto_types_ts.pysrc/synthorg/api/openapi.pytests/unit/scripts/fixtures/__init__.pytests/unit/scripts/fixtures/dto_codegen_fixture_schema.pytests/unit/scripts/test_check_dto_types_ts_in_sync.pytests/unit/scripts/test_generate_dto_types_ts.pyweb/CLAUDE.mdweb/package.jsonweb/src/__tests__/_infra/active-handle-reporter.test.tsweb/src/__tests__/pages/BudgetForecastPage.test.tsxweb/src/__tests__/pages/BudgetPage.test.tsxweb/src/__tests__/pages/DashboardPage.test.tsxweb/src/__tests__/pages/ProviderDetailPage.test.tsxweb/src/__tests__/pages/approvals/ApprovalDetailDrawer.test.tsxweb/src/__tests__/pages/budget/ThresholdAlerts.test.tsxweb/src/__tests__/pages/settings/SourceBadge.test.tsxweb/src/__tests__/stores/agents.test.tsweb/src/__tests__/stores/analytics.test.tsweb/src/__tests__/stores/artifacts.test.tsweb/src/__tests__/stores/budget.test.tsweb/src/__tests__/stores/company.test.tsweb/src/__tests__/stores/connections.test.tsweb/src/__tests__/stores/projects.test.tsweb/src/__tests__/stores/setup-wizard.test.tsweb/src/__tests__/stores/tasks.test.tsweb/src/__tests__/stores/workflows.test.tsweb/src/__tests__/utils/budget.property.test.tsweb/src/__tests__/utils/budget.test.tsweb/src/__tests__/utils/dashboard.property.test.tsweb/src/__tests__/utils/dashboard.test.tsweb/src/__tests__/utils/tasks.property.test.tsweb/src/api/endpoints/activities.tsweb/src/api/endpoints/providers.tsweb/src/api/types/agents.tsweb/src/api/types/analytics.tsweb/src/api/types/approvals.tsweb/src/api/types/artifacts.tsweb/src/api/types/auth.tsweb/src/api/types/backup.tsweb/src/api/types/budget.tsweb/src/api/types/capabilities.tsweb/src/api/types/ceremony-policy.tsweb/src/api/types/collaboration.tsweb/src/api/types/coordination.tsweb/src/api/types/dtos.gen.tsweb/src/api/types/enum-values.gen.tsweb/src/api/types/enums.tsweb/src/api/types/errors.tsweb/src/api/types/escalations.tsweb/src/api/types/index.tsweb/src/api/types/integrations.tsweb/src/api/types/meetings.tsweb/src/api/types/messages.tsweb/src/api/types/openapi.gen.tsweb/src/api/types/org.tsweb/src/api/types/projects.tsweb/src/api/types/providers.tsweb/src/api/types/security.tsweb/src/api/types/settings.tsweb/src/api/types/setup.tsweb/src/api/types/system.tsweb/src/api/types/tasks.tsweb/src/api/types/templates.tsweb/src/api/types/workflows.tsweb/src/hooks/useArtifactsData.tsweb/src/hooks/useConnectionsData.tsweb/src/hooks/useProjectsData.tsweb/src/mocks/handlers/budget.tsweb/src/mocks/handlers/company.tsweb/src/mocks/handlers/connections.tsweb/src/mocks/handlers/escalations.tsweb/src/mocks/handlers/providers.tsweb/src/mocks/handlers/workflows.tsweb/src/pages/BudgetForecastPage.tsxweb/src/pages/EscalationQueuePage.stories.tsxweb/src/pages/EscalationQueuePage.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/ProvidersPage.tsxweb/src/pages/WorkflowsPage.tsxweb/src/pages/artifacts/ArtifactCard.tsxweb/src/pages/artifacts/ArtifactContentPreview.tsxweb/src/pages/artifacts/ArtifactCreateDialog.tsxweb/src/pages/artifacts/ArtifactMetadata.tsxweb/src/pages/budget/BudgetPage.stories.tsxweb/src/pages/budget/SpendBurnChart.tsxweb/src/pages/budget/ThresholdAlerts.stories.tsxweb/src/pages/connections/ConnectionCard.stories.tsxweb/src/pages/connections/ConnectionCard.tsxweb/src/pages/connections/ConnectionFormModal.stories.tsxweb/src/pages/connections/ConnectionFormModal.tsxweb/src/pages/dashboard/BudgetBurnChart.tsxweb/src/pages/dashboard/DashboardPage.stories.tsxweb/src/pages/escalations/EscalationDetailDrawer.stories.tsxweb/src/pages/mcp-catalog/McpInstallWizard.stories.tsxweb/src/pages/meetings/MeetingActionItems.tsxweb/src/pages/meetings/MeetingCard.tsxweb/src/pages/meetings/MeetingDetailHeader.tsxweb/src/pages/meetings/MeetingTokenBreakdown.tsxweb/src/pages/oauth-apps/OauthAppCard.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/org-edit/PackSelectionDialog.tsxweb/src/pages/org/build-org-tree.tsweb/src/pages/org/drop-target.tsweb/src/pages/org/useOrgChartDragDrop.tsweb/src/pages/projects/ProjectCard.tsxweb/src/pages/projects/ProjectCreateDrawer.tsxweb/src/pages/projects/ProjectHeader.tsxweb/src/pages/projects/ProjectTeamSection.tsxweb/src/pages/providers/AuditLogDrawer.tsxweb/src/pages/providers/ModelConfigDrawer.stories.tsxweb/src/pages/providers/PresetOverrideDrawer.tsxweb/src/pages/providers/ProviderFormModal.tsxweb/src/pages/providers/ProviderHealthMetrics.tsxweb/src/pages/providers/ProviderModelList.stories.tsxweb/src/pages/providers/TestConnectionResult.tsxweb/src/pages/settings/ceremony-policy/DepartmentOverridesPanel.tsxweb/src/pages/settings/sinks/SinkFormDrawer.tsxweb/src/pages/setup/MiniOrgChart.tsxweb/src/pages/setup/TemplateCard.tsxweb/src/pages/tasks/TaskCreateDialog.tsxweb/src/pages/tasks/TaskDetailHeader.tsxweb/src/pages/tasks/TaskDetailMetadata.tsxweb/src/pages/tasks/TaskDetailPanel.stories.tsxweb/src/pages/workflow-editor/VersionDiffViewer.tsxweb/src/pages/workflow-editor/node-config-schemas.tsweb/src/pages/workflow-editor/useWorkflowEditorCallbacks.tsweb/src/pages/workflows/WorkflowCard.tsxweb/src/pages/workflows/WorkflowCreateDrawer.tsxweb/src/pages/workflows/WorkflowTableView.tsxweb/src/stores/approvals.tsweb/src/stores/meetings.tsweb/src/stores/providers/crud-actions.tsweb/src/stores/setup-wizard/providers.tsweb/src/stores/tasks.tsweb/src/stores/workflow-editor/clipboard.tsweb/src/stores/workflow-editor/persistence.tsweb/src/stores/workflow-editor/validation.tsweb/src/stores/workflow-editor/yaml.tsweb/src/utils/agents.tsweb/src/utils/meetings.tsweb/src/utils/messages.tsweb/src/utils/setup-validation.tsweb/test-infra/active-handle-tracker.ts
There was a problem hiding this comment.
♻️ Duplicate comments (1)
web/src/stores/setup-wizard/providers.ts (1)
115-122:⚠️ Potential issue | 🟠 Major | ⚡ Quick winConditionally include
api_keyonly forauth_type: 'api_key'.The shortcut now permits
auth_type: 'none', but the request still always sendsapi_key. That can break discriminated backend validation for the'none'variant.Suggested patch
const provider = await createFromPreset({ preset_name: presetName, name, - api_key: apiKey, + ...(authType === 'api_key' ? { api_key: apiKey } : {}), base_url: baseUrl, auth_type: authType, tos_accepted: false, })🤖 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/setup-wizard/providers.ts` around lines 115 - 122, The request payload to createFromPreset always includes api_key which breaks discriminated validation for auth_type 'none'; modify the code that builds the payload for createFromPreset (symbols: createFromPreset, preset_name, api_key, auth_type, name, base_url, tos_accepted) to only include the api_key field when authType === 'api_key' (otherwise omit it or leave it undefined) so the backend sees the correct variant; construct the object conditionally before calling createFromPreset or use a spread that adds api_key only when authType is 'api_key'.
🤖 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.
Duplicate comments:
In `@web/src/stores/setup-wizard/providers.ts`:
- Around line 115-122: The request payload to createFromPreset always includes
api_key which breaks discriminated validation for auth_type 'none'; modify the
code that builds the payload for createFromPreset (symbols: createFromPreset,
preset_name, api_key, auth_type, name, base_url, tos_accepted) to only include
the api_key field when authType === 'api_key' (otherwise omit it or leave it
undefined) so the backend sees the correct variant; construct the object
conditionally before calling createFromPreset or use a spread that adds api_key
only when authType is 'api_key'.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: c171f4d6-48c0-4a7d-8079-fcc731e7a2f7
📒 Files selected for processing (20)
web/src/__tests__/pages/ProviderDetailPage.test.tsxweb/src/__tests__/utils/budget.property.test.tsweb/src/__tests__/utils/budget.test.tsweb/src/api/endpoints/providers.tsweb/src/api/types/budget.tsweb/src/api/types/ceremony-policy.tsweb/src/api/types/providers.tsweb/src/mocks/handlers/providers.tsweb/src/pages/meetings/MeetingTokenBreakdown.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/providers/ModelConfigDrawer.stories.tsxweb/src/pages/providers/ProviderModelList.stories.tsxweb/src/pages/providers/TestConnectionResult.tsxweb/src/pages/tasks/TaskDetailHeader.tsxweb/src/pages/workflows/WorkflowCreateDrawer.tsxweb/src/pages/workflows/WorkflowTableView.tsxweb/src/stores/providers/crud-actions.tsweb/src/stores/providers/types.tsweb/src/stores/setup-wizard/providers.tsweb/src/stores/workflow-editor/validation.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: Test (Python 3.14)
- GitHub Check: Dashboard Test
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (13)
web/src/**/*.{tsx,ts}
📄 CodeRabbit inference engine (CLAUDE.md)
Reuse web dashboard design system from
web/src/components/ui/using design tokens only, perweb/CLAUDE.mdWeb Dashboard uses React 19; design system and diagrams detailed in
web/CLAUDE.md
Files:
web/src/__tests__/pages/ProviderDetailPage.test.tsxweb/src/pages/workflows/WorkflowTableView.tsxweb/src/api/endpoints/providers.tsweb/src/pages/meetings/MeetingTokenBreakdown.tsxweb/src/stores/providers/crud-actions.tsweb/src/pages/providers/TestConnectionResult.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/stores/providers/types.tsweb/src/pages/tasks/TaskDetailHeader.tsxweb/src/pages/workflows/WorkflowCreateDrawer.tsxweb/src/stores/workflow-editor/validation.tsweb/src/pages/providers/ProviderModelList.stories.tsxweb/src/pages/providers/ModelConfigDrawer.stories.tsxweb/src/__tests__/utils/budget.property.test.tsweb/src/mocks/handlers/providers.tsweb/src/__tests__/utils/budget.test.tsweb/src/api/types/ceremony-policy.tsweb/src/api/types/budget.tsweb/src/api/types/providers.tsweb/src/stores/setup-wizard/providers.ts
web/**/*.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
Always wrap the app in
<CSPProvider nonce={getCspNonce()}>+<MotionConfig nonce>so every inline<style>tag injected by Base UI and Motion carries the per-request CSP nonceUse Base UI's
renderprop as the polymorphism primitive throughout the dashboard; the local<Slot>helper incomponents/ui/slot.tsxuses@base-ui/react/merge-propsto support the<Button asChild>ergonomic (only component that uses this helper; all other primitives use Base UI's nativerenderprop directly)Always use
createLoggerfrom@/lib/logger; never use bareconsole.warn/console.error/console.debugin application code
Files:
web/src/__tests__/pages/ProviderDetailPage.test.tsxweb/src/pages/workflows/WorkflowTableView.tsxweb/src/pages/meetings/MeetingTokenBreakdown.tsxweb/src/pages/providers/TestConnectionResult.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/tasks/TaskDetailHeader.tsxweb/src/pages/workflows/WorkflowCreateDrawer.tsxweb/src/pages/providers/ProviderModelList.stories.tsxweb/src/pages/providers/ModelConfigDrawer.stories.tsx
web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Variable name for logger must always be
log(e.g.const log = createLogger('module-name'))For logging static messages, pass dynamic/untrusted values as separate args (not interpolated into the message string) so they go through
sanitizeArgAttacker-controlled fields inside structured objects must be wrapped in
sanitizeForLog()before embedding in log callsCallers MUST NOT wrap store mutation calls in
try/catch; the store owns the error UXError-code constants (MANDATORY): import
ErrorCodeandErrorCategoryfrom@/api/types/errors(re-exported from the generatedweb/src/api/types/error-codes.gen.ts). Discriminate onErrorCode.<NAME>, never on raw integer literalsGenerated DTO types (MANDATORY): every Pydantic DTO mirrored on the wire is mechanically generated from the OpenAPI schema into three committed files. NEVER hand-edit a
*.gen.tsfile. Regenerate withuv run python scripts/generate_dto_types_ts.py. Consumer code imports DTOs via the barrel (import type { AgentConfig } from '@/api/types') or directly from the generated module (import type { AgentConfig } from '@/api/types/dtos.gen')
Files:
web/src/__tests__/pages/ProviderDetailPage.test.tsxweb/src/pages/workflows/WorkflowTableView.tsxweb/src/api/endpoints/providers.tsweb/src/pages/meetings/MeetingTokenBreakdown.tsxweb/src/stores/providers/crud-actions.tsweb/src/pages/providers/TestConnectionResult.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/stores/providers/types.tsweb/src/pages/tasks/TaskDetailHeader.tsxweb/src/pages/workflows/WorkflowCreateDrawer.tsxweb/src/stores/workflow-editor/validation.tsweb/src/pages/providers/ProviderModelList.stories.tsxweb/src/pages/providers/ModelConfigDrawer.stories.tsxweb/src/__tests__/utils/budget.property.test.tsweb/src/mocks/handlers/providers.tsweb/src/__tests__/utils/budget.test.tsweb/src/api/types/ceremony-policy.tsweb/src/api/types/budget.tsweb/src/api/types/providers.tsweb/src/stores/setup-wizard/providers.ts
web/src/**/*.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
Status dots should use
<StatusBadge>(defaults torole="img"with aria-label;decorativefor adjacent-labeled,announcefor live WS updates), not hardcoded inlineKPI displays should use
<MetricCard>/<Sparkline>/<ProgressGauge>/<TokenUsageBar>, not hardcoded inlineCards should use
<SectionCard>(titled wrapper with icon and action slot) and domain-specific cards like<AgentCard>,<DeptHealthBar>, not hardcoded inlineForm fields should use
<InputField>/<SelectField>/<SliderField>/<ToggleField>/<SegmentedControl>/<TagInput>/<SearchInput>, not hardcoded inlineSlide-in panels should use
<Drawer width="compact|narrow|default|wide">(Base UI); do NOT add inlinew-[40vw]overridesLoading / empty / error states should use
<Skeleton>family /<EmptyState>/<ErrorBoundary>/<ErrorBanner>/<ProgressIndicator>, not hardcoded inlineBreadcrumb depth should aim for 2 or 3 levels max in visible trails. When natural depth exceeds 3, route the user to a flatter parent or rely on
<Breadcrumbs maxItems={...} />to collapse middle nodes into the ellipsis (defaultmaxItems=4).<Breadcrumbs items={[]}>returnsnullso unconditional render at the top of a page is safeEmpty-state derivation must use
useEmptyStateProps({ filteredCount, totalCount, filterActive, empty, filtered })from@/hooks/use-empty-state-propswhich returnsEmptyStateProps | nullso the page branches on a single valueStatus / role / risk / urgency badge classes must use
STATUS_COLORSfamily from@/styles/status-colors(typedRecord<EnumValue, string>lookups; no inlineRecord<EnumValue, string>constants per page)Confirmation / toasts must use
<ConfirmDialog>/<Toast>(Zustand-backed queue, NOT Base UI's Toast)Cmd+K / shortcuts must use
<CommandPalette>/<KeyboardShortcutHint>/<CommandCheatsheet>, not hardcoded inlineAnimation must use
<AnimatedPresence>/<StaggerGroup>/<LiveRegion>(de...
Files:
web/src/__tests__/pages/ProviderDetailPage.test.tsxweb/src/pages/workflows/WorkflowTableView.tsxweb/src/pages/meetings/MeetingTokenBreakdown.tsxweb/src/pages/providers/TestConnectionResult.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/tasks/TaskDetailHeader.tsxweb/src/pages/workflows/WorkflowCreateDrawer.tsxweb/src/pages/providers/ProviderModelList.stories.tsxweb/src/pages/providers/ModelConfigDrawer.stories.tsx
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (web/CLAUDE.md)
Viewport-size reads must use
useViewportSize()from@/hooks/useViewportSize(useSyncExternalStoreoverwindowresize). Never readwindow.innerWidth/window.innerHeightdirectly inside a component render body oruseMemoESLint (MANDATORY): use
@eslint-react/eslint-pluginv5+ via therecommended-type-checkedpreset with explicit error-level opt-ins:@eslint-react/web-api-no-leaked-fetch,@eslint-react/no-leaked-conditional-rendering,@eslint-react/globals,@typescript-eslint/no-floating-promises,@typescript-eslint/no-misused-promises(withchecksVoidReturn: { attributes: false })
Files:
web/src/__tests__/pages/ProviderDetailPage.test.tsxweb/src/pages/workflows/WorkflowTableView.tsxweb/src/api/endpoints/providers.tsweb/src/pages/meetings/MeetingTokenBreakdown.tsxweb/src/stores/providers/crud-actions.tsweb/src/pages/providers/TestConnectionResult.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/stores/providers/types.tsweb/src/pages/tasks/TaskDetailHeader.tsxweb/src/pages/workflows/WorkflowCreateDrawer.tsxweb/src/stores/workflow-editor/validation.tsweb/src/pages/providers/ProviderModelList.stories.tsxweb/src/pages/providers/ModelConfigDrawer.stories.tsxweb/src/__tests__/utils/budget.property.test.tsweb/src/mocks/handlers/providers.tsweb/src/__tests__/utils/budget.test.tsweb/src/api/types/ceremony-policy.tsweb/src/api/types/budget.tsweb/src/api/types/providers.tsweb/src/stores/setup-wizard/providers.ts
web/src/pages/**/*.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
List-page primitives should use
<ListHeader>/<SearchFilterSort>/<Pagination>/<BulkActionBar>/<MetadataGrid>/<Breadcrumbs>/<Collapsible>. Page root container should usespace-y-section-gap;<ErrorBanner>lands immediately after<ListHeader>, before any filter / pagination rowPages with a one-line mission statement must pass it via
<ListHeader description="..." />List layout choice: use Kanban grouping for status-flow domains where each row's column conveys lifecycle phase (Tasks, Requests); use a flat scrollable list for queues without explicit phase semantics (Escalations, Approvals)
Files:
web/src/pages/workflows/WorkflowTableView.tsxweb/src/pages/meetings/MeetingTokenBreakdown.tsxweb/src/pages/providers/TestConnectionResult.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/tasks/TaskDetailHeader.tsxweb/src/pages/workflows/WorkflowCreateDrawer.tsxweb/src/pages/providers/ProviderModelList.stories.tsxweb/src/pages/providers/ModelConfigDrawer.stories.tsx
web/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
Always use
createLoggerfrom@/lib/logger; never use bareconsole.warn/console.error/console.debugin application code
Files:
web/src/api/endpoints/providers.tsweb/src/stores/providers/crud-actions.tsweb/src/stores/providers/types.tsweb/src/stores/workflow-editor/validation.tsweb/src/__tests__/utils/budget.property.test.tsweb/src/mocks/handlers/providers.tsweb/src/__tests__/utils/budget.test.tsweb/src/api/types/ceremony-policy.tsweb/src/api/types/budget.tsweb/src/api/types/providers.tsweb/src/stores/setup-wizard/providers.ts
web/src/api/endpoints/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
Health / readiness endpoints (MANDATORY):
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/providers.ts
web/src/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
Any new store that schedules timers or attaches event listeners must expose a teardown hook for registration in the global
afterEachin test-setup.tsxAny new WS payload handler that ingests untrusted strings MUST route through
sanitizeWsString()orsanitizeWsEnum(); raw(sanitizeWsString(x, n) ?? '') as EnumTypecasts are forbidden
Files:
web/src/api/endpoints/providers.tsweb/src/stores/providers/crud-actions.tsweb/src/stores/providers/types.tsweb/src/stores/workflow-editor/validation.tsweb/src/__tests__/utils/budget.property.test.tsweb/src/mocks/handlers/providers.tsweb/src/__tests__/utils/budget.test.tsweb/src/api/types/ceremony-policy.tsweb/src/api/types/budget.tsweb/src/api/types/providers.tsweb/src/stores/setup-wizard/providers.ts
web/src/stores/**/crud-actions.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
All store mutation actions (create / update / delete) must follow the
stores/connections/crud-actions.tspattern: wrap the API call intry/catch, success path updates state + emits a success toast, failure path logs + emits an error toast + returns a sentinel (nullfor entity returns,falsefor delete). Optimistic mutations captureprevioussynchronously and restore incatch
Files:
web/src/stores/providers/crud-actions.ts
web/src/stores/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
List reads (
fetch*) must seterror: string | nullon the store instead of toastingCursor pagination (MANDATORY): list endpoints use opaque cursor-based paging via
PaginationMeta. Stores keepnextCursor+hasMorein state (not offset arithmetic) and early-return when!hasMore || !nextCursor. Display counts come fromdata.length; the wire envelope no longer carriestotal
Files:
web/src/stores/providers/crud-actions.tsweb/src/stores/providers/types.tsweb/src/stores/workflow-editor/validation.tsweb/src/stores/setup-wizard/providers.ts
web/src/**/*.test.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
Every unit test runs under
web/test-infra/active-handle-tracker.ts, which hooks Node'sasync_hooksand fails any test that leaks an event-loop-holding resource (Timeout,TCPWRAP,PIPEWRAP,FSEVENTWRAP, etc.) attributable to aweb/src/frame. Zero tolerance, no ceiling, no buffer
Files:
web/src/__tests__/utils/budget.property.test.tsweb/src/__tests__/utils/budget.test.ts
web/src/mocks/handlers/**/*.ts
📄 CodeRabbit inference engine (web/CLAUDE.md)
MSW handlers (MANDATORY):
web/src/mocks/handlers/mirrorsweb/src/api/endpoints/*.ts1:1 with a default happy-path handler for every exported endpoint.test-setup.tsxboots withonUnhandledRequest: 'error'; tests override per-case viaserver.use(...), nevervi.mock('@/api/endpoints/*')Typed envelope helpers (
successFor,paginatedFor,voidSuccess) must be used in MSW handlers to keep handlers in lockstep with endpoint return types
Files:
web/src/mocks/handlers/providers.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T22:01:16.007Z
Learning: Read design spec from `docs/design/` page before implementing; deviations need approval per [DESIGN_SPEC.md](docs/DESIGN_SPEC.md)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T22:01:16.007Z
Learning: Present every plan for accept/deny before coding
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T22:01:16.007Z
Learning: No region/currency/locale should be privileged; use metric units and British English throughout
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T22:01:16.007Z
Learning: Every convention PR must ship its enforcement gate per [docs/reference/convention-gates.md](docs/reference/convention-gates.md)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T22:01:16.007Z
Learning: After issue implementation: create branch, commit, push without auto-PR; use `/pre-pr-review` command; after PR use `/aurelio-review-pr` for external feedback; fix all valid feedback without deferring
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T22:01:16.007Z
Learning: Git commits: use `<type>: <description>` format (feat/fix/refactor/docs/test/chore/perf/ci); commitizen-enforced
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T22:01:16.007Z
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-14T22:01:16.007Z
Learning: Branches: use `<type>/<slug>` naming from main
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T22:01:16.007Z
Learning: Squash merge PRs; PR body becomes squash commit; trailers (`Release-As`, `Closes `#N``) must be in PR body
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T22:01:16.007Z
Learning: GitHub queries: use `gh issue list` via Bash, NOT MCP `list_issues`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T22:01:16.007Z
Learning: After every squash merge run `/post-merge-cleanup`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T22:01:16.007Z
Learning: CLI is Docker-only (init/start/stop/status); features must go in dashboard + REST API, never in CLI binary
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T22:01:16.007Z
Learning: Shell conventions: see `~/.claude/rules/common/bash.md` (canonical for `cd` / `git -C` / Bash file-write rules)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T22:01:16.007Z
Learning: License: BUSL-1.1 with conversion to Apache 2.0 after Change Date
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T22:02:18.062Z
Learning: CSP Nonce Infrastructure: `App.tsx` wraps the app in `<CSPProvider nonce={getCspNonce()}>` + `<MotionConfig nonce>` so every inline `<style>` tag injected by Base UI and Motion carries the per-request CSP nonce. See `docs/security.md` for the full flow
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T22:02:18.062Z
Learning: Package structure: `web/src/` follows the standard split: `api/` (Axios client + endpoint domains), `components/` (`ui/` primitives + `layout/`), `hooks/`, `lib/`, `mocks/` (MSW), `pages/`, `router/`, `stores/` (Zustand), `styles/` (design tokens), `utils/`, `__tests__/`. Stores over ~600 lines are sliced into packages with one of two aggregation patterns (package-internal `index.ts` or sibling `.ts` aggregator)
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T22:02:18.062Z
Learning: Design System (MANDATORY): 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, or currency symbols / codes; use design tokens, `@/lib/motion` presets, helpers in `@/utils/format`, and `DEFAULT_CURRENCY` from `@/utils/currencies`
Learnt from: CR
Repo: Aureliolo/synthorg
Timestamp: 2026-05-14T22:02:18.062Z
Learning: Base UI Adoption Decisions: Adopted (direct import from `base-ui/react/<subpath>`): Dialog, AlertDialog, Popover, Tabs, Menu, Drawer, CSPProvider, merge-props. Not adopted: Toast (Zustand-backed queue), Meter (covered by `<ProgressGauge>`), Select (native `<select>` for mobile picker UX), Combobox / Autocomplete / OTP Field / Tooltip (revisit when needed)
🔇 Additional comments (20)
web/src/pages/meetings/MeetingTokenBreakdown.tsx (1)
61-69: LGTM!Also applies to: 87-92
web/src/pages/workflows/WorkflowTableView.tsx (1)
90-90: LGTM!web/src/pages/providers/TestConnectionResult.tsx (1)
30-30: LGTM!web/src/pages/org-edit/DepartmentEditDrawer.tsx (1)
91-97: LGTM!web/src/pages/tasks/TaskDetailHeader.tsx (1)
26-28: LGTM!web/src/api/types/ceremony-policy.ts (1)
3-34: LGTM!web/src/api/types/budget.ts (1)
3-63: LGTM!web/src/pages/workflows/WorkflowCreateDrawer.tsx (1)
41-45: LGTM!Also applies to: 86-88, 93-97, 105-110
web/src/stores/workflow-editor/validation.ts (1)
60-69: LGTM!Also applies to: 82-82, 95-95
web/src/api/endpoints/providers.ts (1)
403-410: LGTM!web/src/stores/providers/crud-actions.ts (1)
320-327: LGTM!web/src/stores/providers/types.ts (1)
122-125: LGTM!web/src/api/types/providers.ts (1)
3-35: LGTM!Also applies to: 36-63, 64-66, 75-77, 82-109
web/src/stores/setup-wizard/providers.ts (1)
30-36: LGTM!Also applies to: 55-55, 92-113, 137-141, 153-157, 161-164, 170-174
web/src/__tests__/pages/ProviderDetailPage.test.tsx (1)
7-7: LGTM!Also applies to: 45-47
web/src/pages/providers/ProviderModelList.stories.tsx (1)
5-5: LGTM!Also applies to: 13-13, 26-26, 39-39, 74-74
web/src/pages/providers/ModelConfigDrawer.stories.tsx (1)
5-5: LGTM!Also applies to: 12-12
web/src/__tests__/utils/budget.property.test.ts (1)
10-10: LGTM!Also applies to: 40-40
web/src/mocks/handlers/providers.ts (1)
41-41: LGTM!Also applies to: 258-258
web/src/__tests__/utils/budget.test.ts (1)
15-15: LGTM!Also applies to: 29-29, 477-477
Pipeline mirrors the established error-codes generator/gate pair: - scripts/generate_dto_types_ts.py boots the Litestar app, exports the OpenAPI schema (matching scripts/export_openapi.py env contract), normalises non-deterministic enum descriptions via class-docstring lookup, then hands the schema to openapi-typescript (pinned 7.13.0 via web/package-lock.json) to render web/src/api/types/openapi.gen.ts. Two thin Python-side renderers emit web/src/api/types/dtos.gen.ts (named aliases over components.schemas, with ApiResponse and PaginatedResponse monomorphised names mapped to friendly Envelope/Page aliases) and web/src/api/types/enum-values.gen.ts (runtime VALUES tuples plus derived string-union types that the dashboard's select inputs and type guards depend on). - scripts/check_dto_types_ts_in_sync.py is a thin --check wrapper that the pre-push/CI gate will invoke (wiring added in a follow-up commit). - Determinism is enforced by re-execing under PYTHONHASHSEED=0 and by overriding enum schema descriptions with their Python class docstrings; without both, a Litestar schema-cache effect makes the SeniorityLevel description flip between runs. - The first generator run is committed alongside the script so the drift gate has something to compare against.
…i/types barrel - web/src/api/types/enums.ts now re-exports every wire-facing enum tuple and derived string-union type from the generated enum-values.gen.ts. The hand-maintained re-declarations (TASK_STATUS_VALUES, AGENT_STATUS_VALUES, etc.) and the 10 supposedly-not-in-OpenAPI enums (RiskTolerance, CreativityLevel, CollaborationPreference, CommunicationVerbosity, ConflictApproach, DecisionMakingStyle, TaskStructure, CoordinationTopology, ToolAccessLevel, MemoryLevel - they ARE in OpenAPI now) collapse into one re-export block. The isDepartmentName type guard stays in enums.ts because the generator does not emit helper functions. - web/src/api/types/index.ts is a new barrel that re-exports every API type from a single import surface: generated DTOs from dtos.gen.ts, runtime enums from enum-values.gen.ts, helper functions from enums.ts, the hand-maintained envelope generics from http.ts, ErrorCode/ErrorCategory/ErrorDetail from errors.ts, and the WS contract from websocket.ts. Collisions between generated and hand-maintained sources resolve in favour of hand-maintained (PaginationMeta, ErrorCode, ErrorCategory, ErrorDetail) so consumer call sites stay on the canonical types.
Adds the convention gate that fails any push leaving the three web/src/api/types/*.gen.ts files out of sync with the Pydantic DTOs reachable through the OpenAPI schema: - .pre-commit-config.yaml dto-types-ts-in-sync hook runs at both pre-commit and pre-push stages (matching the error-codes-ts-in-sync template) with a files: trigger covering every Pydantic source the schema reads (src/synthorg/api/, src/synthorg/core/), the generator/gate scripts, scripts/export_openapi.py, src/synthorg/api/openapi.py (RFC 9457 enricher), the three output files, and npm lock files. - .github/workflows/ci.yml dashboard-build job runs the gate immediately after npm ci and before the Vite build so DTO drift fails CI in seconds rather than after the full bundle compiles. The dashboard paths filter is broadened to include src/synthorg/api/**, src/synthorg/core/**, the new scripts, and the gate so a Python-only DTO edit still trips the dashboard job. - scripts/convention_gate_map.yaml registers the new web-claude-md::generated-dto-types entry; the meta-gate scripts/check_convention_gate_inventory.py passes. - web/CLAUDE.md gains the 'Generated DTO types (MANDATORY)' paragraph describing the codegen contract, the regenerate command, the gate, and how consumers import the types. - docs/reference/convention-gates.md inventory + count macro bumped to 45 (43 enforcement gates + meta-gate + ratchet hook); data/runtime_stats.yaml convention_gates count updated to 45.
Generator gains two openapi-typescript flags that surface real wire-truth divergences the hand-maintained types papered over: - --default-non-nullable=false: Pydantic fields with defaults are not in OpenAPI 'required'; treat them as optional in TS so request consumers can omit them (matching the wire) and response consumers defensively handle the case where an older server returns the default. - --alphabetize: sorts every object key, eliminating the last source of cross-platform order drift in the openapi.gen.ts output. Two domain files migrate to thin re-export shims: - web/src/api/types/capabilities.ts: aliases the generated CapabilitiesResponse as Capabilities (one-line shape match). - web/src/api/types/artifacts.ts: re-exports Artifact + CreateArtifactRequest from dtos.gen, keeps the frontend-only ArtifactFilters query-param interface in place. Consumer-side adjustments for the now-correct optional fields on Artifact: artifact.description, content_type, size_bytes default to '?? 0' / '?? '' in ArtifactCard, ArtifactContentPreview, ArtifactMetadata, useArtifactsData.
… fixes Migrate four more domain files to thin re-export shims: - errors.ts: re-exports ErrorCategory/ErrorCode from error-codes.gen and ErrorDetail from dtos.gen (no hand-edited shape remains). - projects.ts: re-exports Project + CreateProjectRequest from dtos.gen; keeps the frontend-only ProjectFilters query type. - templates.ts: re-exports PackInfoResponse, ApplyTemplatePack* DTOs and the generated RebalanceMode union + VALUES tuple. - coordination.ts: re-exports all three coordination DTOs from dtos.gen (no frontend-only types remained). Consumer-side defensive fixes for fields that openapi-typescript now correctly marks optional (Pydantic defaulted fields not in OpenAPI required[]): - ProjectCard, ProjectHeader, ProjectTeamSection: ?? '' / ?? 0 / ?? [] for project.description, budget, task_ids, team, status. - useProjectsData filter: defensive description fallback. - PackSelectionDialog: != null guard for result.scale_factor.
…y-policy Migrate six more domain files. Five become pure re-export shims; messages.ts stays mostly hand-maintained because the dashboard's Message contract is a flattened view of the wire's A2A-style 'parts'-based Message and a full migration requires rewriting every message consumer (deferred to a follow-up). - auth.ts: SetupRequest, LoginRequest, ChangePasswordRequest, UserInfoResponse, WsTicketResponse from dtos.gen; CookieSessionResponse aliased as AuthResponse; SessionResponse aliased as SessionInfo. TokenResponse stays as a deprecated frontend-only shape for any unmigrated callers. - backup.ts: BackupInfo, BackupManifest, RestoreRequest, RestoreResponse from dtos.gen; BackupComponent/BackupTrigger VALUES from enum-values.gen. - system.ts: LivenessStatus, ReadinessStatus, AutonomyLevel* from dtos.gen; ReadinessStatus also aliased as HealthStatus for the legacy import path; ReadinessOutcome and TelemetryStatus VALUES from enum-values.gen. - collaboration.ts: every DTO from dtos.gen. - ceremony-policy.ts: ActiveCeremonyStrategyResponse (aliased as ActiveCeremonyStrategy), ResolvedCeremonyPolicyResponse, ResolvedPolicyField from dtos.gen; CeremonyStrategyType VALUES from enum-values.gen. CeremonyPolicyConfig, VelocityCalcType and PolicyFieldSource stay hand-maintained because the wire validates them via model_validate on dict payloads (not surfaced in OpenAPI components.schemas). - messages.ts: kept hand-maintained Message/Attachment/ MessageMetadata/Channel; re-exports ChannelType, MessagePriority, MessageType VALUES from enum-values.gen; AttachmentType + ATTACHMENT_TYPE_VALUES remain hand-maintained as a frontend-only classification (wire uses DataPart/FilePart/UriPart). Consumer fix: utils/messages.ts MESSAGE_TYPE_LABELS now exhausts the wire MessageType union (adds 'dissent' and 'context_injection' which the hand-maintained tuple was missing).
Migrate four more domain files via re-export shims:
- meetings.ts: re-exports ActionItem, MeetingAgenda, MeetingAgendaItem,
MeetingContribution, MeetingMinutes, MeetingResponse,
TriggerMeetingRequest from dtos.gen plus MeetingPhase,
MeetingProtocolType, MeetingStatus VALUES. MeetingRecord derives
from MeetingResponse via Omit (the wire never returns
MeetingRecord alone). MeetingFilters stays as a frontend-only
query type.
- setup.ts: re-exports every wire-facing setup DTO (Setup*Response /
Setup*Request / Update*Request / TemplateInfoResponse /
PersonalityPresetInfoResponse aliased / TemplateVariableResponse
aliased / DiscoverModelsResponse / AvailableLocalesResponse) plus
SkillPattern VALUES. DiscoverModelsRequest stays hand-maintained
(inline body schema).
- settings.ts: re-exports SettingDefinition, SettingEntry,
SinkInfoResponse (aliased as SinkInfo), SinkRotationResponse
(aliased as SinkRotation), TestSinkConfigResponse (aliased as
TestSinkResult), UpdateSettingRequest from dtos.gen plus
SettingLevel/Namespace/Source/Type VALUES. LogLevel,
AgentConfigEntry, DepartmentTeam, DepartmentEntry stay
hand-maintained (embedded dict payloads not in OpenAPI).
- org.ts: re-exports every wire-facing org mutation DTO; redefines
Department as Partial-over-WireDepartment plus a frontend-only
display_name so test fixtures and stories can construct minimal
{name, teams} objects (the wire still serialises every defaulted
field, but the dashboard treats them as opt-in display fields).
CompanyConfig is a frontend aggregation (the wire's CompanyConfig
is a much narrower policy-config shape with no agents/departments).
TeamConfig, DepartmentReportingLine, UpdateDepartmentTeam,
UPDATE_DEPARTMENT_MAX_TEAMS, AgentModelSelector stay
hand-maintained as embedded-dict / form-helper shapes.
Consumer fixes:
- utils/meetings.ts MEETING_PHASE_* maps exhausted to the wire's
full union (adds 'premortem' and 'devil_advocate').
- stores/meetings.ts and pages/meetings/* defensive ?? {} / ?? null
for now-optional fields (token_usage_by_participant, minutes,
meeting_duration_seconds, ActionItem.priority).
- build-org-tree.ts widens dept-agent map keys to plain string;
drops synthetic display_name fallback; populates required
Department fields when constructing synthetic departments.
- DepartmentBounds drops the DepartmentName narrowing.
- DepartmentOverridesPanel uses dept.name directly (wire has no
display_name field).
- SinkFormDrawer casts loosely-typed sink fields to the strict
LogLevel / rotation strategy unions.
- TemplateCard guards undefined autonomy_level lookup.
- AgentEditDrawer, DepartmentCreateDialog, DepartmentEditDrawer,
useOrgChartDragDrop supply autonomy_level/level: null on
PATCH/POST payloads now that the wire marks them required.
- setup-validation.ts returns null fallbacks for optional
provider/modelId.
- mocks/handlers/company.ts populates required
policies.approval_chains: [].
- __tests__/stores/company.test.ts: fixture calls supply the
required autonomy_level/level/budget_percent fields on mutation
payloads.
- __tests__/stores/connections.test.ts and
mocks/handlers/connections.ts add the now-required secret_refs
field (and pass health_check_enabled on create).
Both files use the Omit-and-override pattern to redeclare defaulted
Pydantic fields as required (the wire emits them on every response)
plus a few runtime fields that arrive via WS payloads but not the
HTTP DTO.
- agents.ts: re-exports ActivityEvent (aliased AgentActivityEvent),
AgentPerformanceSummary, CareerEvent, RecommendedAction,
TrendResult, WindowMetrics from dtos.gen plus ActivityEventType,
LifecycleEventType (aliased CareerEventType),
StrategicOutputMode, TrendDirection VALUES. AgentConfig is
WireAgentConfig with id?, status?, hiring_date?, tier?,
model_requirement?, strict department/level/personality/model/
memory/tools/authority/autonomy_level. AgentTier stays as a
frontend-only inline string union.
- tasks.ts: re-exports AcceptanceCriterion, Cancel/Create/Update/
TransitionTaskRequest, ExpectedArtifact from dtos.gen. Task is
WireTask with status/priority/type/estimated_complexity/
coordination_topology required, task_structure nullable, all
collection fields readonly arrays plus optional cost/version/
created_at/updated_at runtime fields.
Consumer fixes:
- utils/agents.ts CAREER_COLOR_MAP exhausts the now-broader
LifecycleEventType union ('offboarded', 'status_changed' added);
formatCompletionTime / formatCostPerTask call sites supply
defensive null fallbacks for the optional perf fields.
- stores/tasks.ts sanitizeTask drops the now-incorrect
ExpectedArtifact.name accessor and uses .path instead; defensive
?? null / ?? false for newly-optional fields.
- pages/tasks/TaskCreateDialog supplies budget_limit: 0 default
instead of undefined.
- pages/tasks/TaskDetail{Header,Metadata,Panel}.tsx UpdateTask
payloads include priority: null (the wire marks priority required
and nullable).
- TaskDetailPanel.stories.tsx artifacts_expected: [{ path, type }].
- __tests__/stores/tasks.test.ts createTask payloads supply
budget_limit: 0; updateTask supplies priority: null.
- __tests__/stores/agents.test.ts activity event fixtures use
'task_completed' as const / 'hired' as const so the union widens
cleanly to LifecycleEventType.
- __tests__/utils/tasks.property.test.ts artifacts_expected /
acceptance_criteria arbitrary types use readonly to match the
generated shapes.
Both files use Required-wrapper or Omit-and-override to promote Pydantic-defaulted fields to required, plus keep frontend-only inline string unions hand-maintained. - budget.ts: CostRecord with project_id/call_category/etc all required (the wire emits defaults; the dashboard UI relies on the field being present). BudgetConfig, BudgetAlertConfig, AutoDowngradeConfig, AgentSpending, DailySummary, PeriodSummary wrapped Required. FinishReason VALUES re-exported. - security.ts: TrustSummary, PerformanceSummary, AgentHealthResponse wrapped Required. AuditEntry omits-and-overrides each defaulted field to required-nullable. MessageOverheadPayload, CoordinationMetricsPayload, CoordinationMetricsRecord, SecurityConfigExportResponse wrapped Required. ToolCategory VALUES re-exported. AuditVerdictStr stays as hand-maintained inline union. Consumer fixes for the new required wire fields: - mocks/handlers/budget.ts buildBudgetConfig adds pte_tracking_enabled: false. - Every BudgetConfig literal in tests and stories (ThresholdAlerts, BudgetForecastPage, BudgetPage, DashboardPage, store tests, utils tests/property tests, BudgetPage.stories, ThresholdAlerts.stories, DashboardPage.stories) adds the pte_tracking_enabled boolean. - Every CostRecord literal (test fixtures, property-test arbitrary, story factory) adds currency: USD / DEFAULT_CURRENCY (the wire CostRecord requires currency).
providers.ts re-exports every available DTO from dtos.gen
(AddAllowlistEntryRequest, AddModelRequest, CloudPreset,
CreateFromPresetRequest, CreateProviderRequest,
DiscoverModelsResponse, DiscoveryPolicyResponse, LocalModelParams,
LocalPreset, PresetOverride, PresetOverrideUpdateRequest,
ProbeLocalResponse, ProbePresetResponse, ProviderAuditActor,
ProviderAuditEvent, ProviderHealthSummary, ProviderModelConfig,
ProviderModelResponse, PullModelRequest, RateLimitsUpdateRequest,
RemoveAllowlistEntryRequest, SyncModelsRequest, SyncModelsResponse,
TestConnectionRequest, TestConnectionResponse,
UpdateModelConfigRequest, UpdateProviderRequest) plus
AuthType / ProviderHealthStatus VALUES. ProviderAuditEventType,
CredentialsRotateRequest, PullProgressEvent, RateLimitsConfig,
ProviderConfig and the ProviderPreset union stay hand-maintained
(inline / endpoint-only / dict payload shapes not surfaced as
named OpenAPI components).
Consumer fixes:
- ProviderModelResponse literals (test fixtures, stories, mock
handlers) add currency: USD (the wire requires currency).
- CreateFromPresetRequest call sites (setup-wizard slice,
ProvidersPage, store tests) supply auth_type + tos_accepted.
- CreateProviderRequest call sites (ProviderFormModal, store tests)
supply driver and models.
- SyncModelsRequest defaults now include replace_existing: true.
- AuditLogDrawer event.payload ?? {} defensive access.
- ProviderHealthMetrics, TestConnectionResult: ?? null fallbacks
for the now-optional avg_response_time_ms / latency_ms fields.
- PresetOverrideDrawer rebuilds the payload as a literal so the
readonly fields are populated at construction time.
- ProbeLocal response handling uses ?? {} to handle now-optional
results / errors maps.
workflows.ts re-exports every wire-facing workflow DTO from dtos.gen (ActivateWorkflowRequest, BlueprintInfoResponse aliased as BlueprintInfo, CreateFromBlueprintRequest, CreateSubworkflowRequest, CreateWorkflowDefinitionRequest, ParentReference, RollbackWorkflowRequest, SubworkflowSummary, UpdateWorkflowDefinitionRequest, WorkflowDefinition, WorkflowDiff, WorkflowExecution, WorkflowIODeclaration, WorkflowIODeclarationRequest, WorkflowValidationError, WorkflowValidationResult) plus WorkflowEdgeType, WorkflowExecutionStatus, WorkflowNodeExecutionStatus, WorkflowNodeType, WorkflowValueType VALUES. Hand-maintained WorkflowNodeData, WorkflowEdgeData, WorkflowNodeExecution stay as the frontend's structured view of the wire's embedded-dict node/edge payloads. VersionSummary, WorkflowDefinitionSnapshot, WorkflowDefinitionVersionSummary, NodeChange, EdgeChange, MetadataChange stay as frontend shapes (the wire emits them as embedded-dict payloads). isWorkflowNodeType / isWorkflowEdgeType type-guards stay; WORKFLOW_NODE_TYPES / WORKFLOW_EDGE_TYPES are aliased to the generated VALUES tuples for back-compat. Consumer fixes: - mocks/handlers/workflows.ts mock fixtures use the wire's WorkflowType enum members (sequential_pipeline) instead of 'default'. - node-config-schemas adds the missing 'verification' entry that the wire's WorkflowNodeType union now requires. - WorkflowCard, WorkflowTableView: ?? '' fallback for workflow_type (optional in WireWorkflowDefinition). - WorkflowCreateDrawer, WorkflowsPage, useWorkflowEditorCallbacks, persistence.ts, validation.ts createWorkflow / updateWorkflow payloads supply the now-required description, version, inputs, outputs, is_subworkflow fields and cast nodes/edges to the wire's readonly Record-array shape (the wire validates them via Pydantic on the server but exports them as dict arrays). - yaml.ts edge.type defensive 'sequential' fallback. - stores/workflows.test.ts createWorkflow fixtures supply the full required field set. - VersionDiffViewer re-builds NodeChange / EdgeChange to required old_value/new_value: the wire emits them optional + nullable but the dashboard expects required-nullable.
…ateDepartment payload widening The CreateDepartment dialog now supplies autonomy_level: null, the AgentEdit drawer's Save submits autonomy_level: null, and the ConnectionsCreate body includes auth_method + health_check_enabled to satisfy the wire's defaulted-but-required CreateConnectionRequest shape. Refresh the test fixture assertions to match the new explicit payloads.
The hand-maintained @deprecated TokenResponse type lived as a backstop for an earlier auth flow that returned the JWT in the response body. The current wire returns the JWT via the HttpOnly cookie and the dashboard reads only AuthResponse (re-exported from CookieSessionResponse). No call sites reference TokenResponse, so the shim can be deleted outright.
CI failure (Dashboard Build > DTO TypeScript drift gate): - regenerated openapi.gen.ts/dtos.gen.ts/enum-values.gen.ts. - scripts/generate_dto_types_ts.py: hoisted local sys/inspect/StrEnum imports to module top level (gemini #105/#145) and dropped trailing newline from enum description override (gemini #159). Reviewer findings: - agents.ts: add strategic_output_mode/personality_preset/tier to AgentConfig Omit list so the dashboard shim does not leak wire-side fields (gemini #60). - CLAUDE.md: replace hard-coded 43 with <!--RS:convention_gates--> marker so the gate count is sourced from data/runtime_stats.yaml (CR CLAUDE.md:45). - setup-wizard.test.ts: custom-provider fixtures use driver: litellm to match the production create flow (CR setup-wizard.test.ts:933/952). - useConnectionsData.ts: align filter fallback with sort path so connections with null/undefined health_status surface under the unknown filter (CR useConnectionsData.ts:46). - useOrgChartDragDrop.ts: validate newDept via isDepartmentName before optimisticReassignAgent so an invalid department string cannot corrupt optimistic state (CR useOrgChartDragDrop.ts:101). - SinkFormDrawer.tsx: allowlist-normalise sink level + rotation strategy through toLogLevel/toRotationStrategy helpers (CR SinkFormDrawer.tsx:36/39). - WorkflowCard.tsx: render unknown fallback instead of blank workflow-type pill (CR WorkflowCard.tsx:62). - useWorkflowEditorCallbacks.ts: preserve source workflow I/O in Save As New by mapping declarations to WorkflowIODeclarationRequest with default ?? null (CR useWorkflowEditorCallbacks.ts:200-201). - stores/tasks.ts: shape guard accepts artifacts_expected.path (not name) and optional/null met on acceptance_criteria; sanitize artifacts_expected.type via sanitizeWsEnum(ARTIFACT_TYPE_VALUES) (CR stores/tasks.ts:163). - stores/meetings.ts: relax isMeetingShape so absent token_usage_by_participant and undefined minutes do not drop the frame before sanitizeMeeting runs (CR meetings.ts outside-diff 319-340). - setup-wizard/providers.ts: emit success/error toasts from createProviderFromPreset success and catch paths so the mutation contract matches the rest of the wizard (CR providers.ts outside-diff 90-139). - TaskDetailPanel.tsx: pass task.priority (not null) on partial title/ description/assignee inline edits so the wire-required field is preserved instead of cleared (CR TaskDetailPanel.tsx:146/158/195). - AgentEditDrawer.tsx: pass agent.autonomy_level (not null) on update so a non-autonomy edit no longer silently resets the field; the wire field is required so omission was not an option. - DepartmentCreateDialog.tsx: restore explicit autonomy_level: null with an explanatory comment (CreateDepartmentRequest.autonomy_level is required on the wire; CR duplicate finding pointed at agent drawer fix, not a removal). - OrgEditPage.tsx: keep autonomy_level: null fallback with comment explaining the wire requires the field (UpdateCompanyRequest); cannot pass undefined as CR initially proposed. Local: ruff/mypy/pytest unit + npm lint/type-check/test (3145/3145) all green.
Root cause of CR5/CR7/CR13 "field marked required even with default=None": - _flatten_nullable_ref in src/synthorg/api/openapi.py was inlining the enum component schema (incl. its synthesised ``default: "<first-value>"``) into nullable enum fields. The enum's default then masqueraded as the field's default, and openapi-typescript marks any field with a default as required in the emitted TypeScript regardless of OpenAPI's required array. Strip ``default`` from the inlined enum schema so the field's own absence-of-default (Litestar drops Pydantic's ``default=None`` when wrapping in oneOf) survives. Effect on the generated schema: - UpdateCompanyRequest.autonomy_level: required -> optional - CreateDepartmentRequest.autonomy_level: required -> optional - UpdateAgentOrgRequest.autonomy_level + level: required -> optional - UpdateTaskRequest.priority: required -> optional Round-2 skipped findings now properly applied: - OrgEditPage.tsx: preserve ``undefined`` when YAML omits autonomy_level so non-autonomy edits don't silently clear the existing value; ``null`` only when YAML explicitly sets the key to null (CR5). - DepartmentCreateDialog.tsx: drop ``autonomy_level: null`` from the create payload entirely; backend default applies (CR13). - AgentEditDrawer.tsx: drop ``autonomy_level: null`` from the update payload entirely so non-autonomy edits don't reset it (duplicate reference in CR13). - TaskDetailPanel.tsx: drop ``priority: <value>`` from title/description/ assignee inline edits; the wire no longer requires it (CR7 root fix also obsoletes CR-N5's buildInlineUpdate helper suggestion). Round-3 CR review findings on round-2 push: - scripts/generate_dto_types_ts.py: add ``timeout=120`` to the ``subprocess.run`` call so an openapi-typescript hang fails fast instead of blocking the generator indefinitely (CR-N1). - web/src/api/types/agents.ts: derive AgentTier from ``NonNullable<WireAgentConfig['tier']>`` so the dashboard alias stays in lockstep with the generated wire type instead of drifting via a hardcoded literal union (CR-N2). - web/src/pages/org/useOrgChartDragDrop.ts: preserve the agent's existing autonomy_level and level on department-only drag moves instead of clearing both to ``null`` (CR-N3). - web/src/pages/settings/sinks/SinkFormDrawer.tsx: route the rotation strategy onChange through ``toRotationStrategy(v)`` so the allowlist validation applied at init also gates UI changes (CR-N4). - web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts: replace the inline shape with generated ``WorkflowIODeclaration`` -> ``WorkflowIODeclarationRequest`` types so the duplicate payload keeps compile-time schema parity (CR-N6). - web/src/stores/tasks.ts: accept ``undefined`` in isNullableString so a WS frame that simply omits assigned_to / deadline / parent_task_id reaches sanitizeTask's ``?? null`` fallback instead of being dropped by the shape guard (CR-N7). Required-companion adjustments: - web/src/pages/setup/MiniOrgChart.tsx: SetupAgentSummary.level is now optional on the wire; coerce ``undefined`` to ``null`` for the ``seniorityRank`` call and switch the leader check to a nullish comparison so the loose typing flows through unchanged. - Tests updated to match the new (smaller) payloads: AgentEditDrawer.test, DepartmentCreateDialog.test. CR-N5 (TaskDetailPanel buildInlineUpdate helper) skipped: factually obsolete after the backend schema fix; UpdateTaskRequest.priority is no longer required, so the inline edits no longer carry a wire envelope the helper would centralise. Local: ruff/mypy + 133 codegen/openapi unit tests + npm lint/type-check/ test (3145/3145) + DTO drift gate all green.
Without explicit encoding=utf-8, subprocess.run with text=True decodes via locale.getpreferredencoding() — cp1252 on default Windows installs. openapi-typescript emits UTF-8, so a § in a Pydantic StrEnum docstring (MemoryCategory, MemoryLevel) round-trips as § on Windows. Linux CI generates the correct § and the drift gate flags the divergence (out of sync: openapi.gen.ts). Pin the decode to utf-8 and regenerate.
render_enum_values wrote each StrEnum member straight into a single-quoted TS literal. None of the 476 current members carries a quote, backslash, or whitespace control character, but a future member that does would produce syntactically broken enum-values.gen.ts and silently break the dashboard build. Escape backslash, single quote, and newline / carriage-return / tab before emitting each literal.
Replace _hermetic_env_setdefaults with a _hermetic_env contextmanager that snapshots the original presence/value of SYNTHORG_DB_PATH, SYNTHORG_DATABASE_URL, and SYNTHORG_PAGINATION_CURSOR_SECRET on entry and restores on exit (success or error). export_openapi_schema now wraps its app boot in 'with _hermetic_env()' so any in-process caller's environment is no longer permanently mutated. Operator-pinned SYNTHORG_DB_PATH still wins (existing behaviour preserved). Three new unit tests cover absence-restore, operator-pin pass-through, and exception-restore.
Pulled efd54c9 (settings precedence refactor): SettingSource drops 'yaml' tier and SettingDefinition drops yaml_path. Regenerated dtos/openapi/enum-values to match the new wire shape, and updated SourceBadge.test.tsx to import the canonical SETTING_SOURCE_VALUES tuple from settings.ts (which now re-exports it from enum-values.gen) instead of the removed SETTING_SOURCES const.
Currency literals now route through DEFAULT_CURRENCY (regional defaults rule): ProviderDetailPage / budget / budget.property tests, mocks/handlers/providers, ModelConfigDrawer + ProviderModelList stories. Type aliases swapped to generated equivalents: budget.ts call_category uses LLMCallCategory, ceremony-policy.ts strategy uses top-level CeremonyStrategyType (drops inline import), providers.ts ProviderAuditEventType aliases WireProviderAuditEvent.event_type. Bug fixes: MeetingTokenBreakdown falls back to token-usage map keys when contribution_rank is omitted (avoids hiding valid data); DepartmentEditDrawer no longer sends autonomy_level: null on every save (was wiping the value); TaskDetailHeader title-only edit drops priority field (was clearing priority on rename); WorkflowTableView shows 'Unknown' fallback instead of empty pill; WorkflowCreateDrawer validates workflow_type against WORKFLOW_TYPES via type guard before sending; workflow-editor validation preserves draft description/version/inputs/outputs/is_subworkflow instead of overwriting with hardcoded defaults; setup-wizard providers shortcut rejects non-api_key auth presets with a clear message instead of silently failing server-side. Ergonomics: TestConnectionResult drops dead '?? null' inside null-guard; SyncModelsRequest no longer defaults to { replace_existing: true } at endpoint or store layer (callers must opt into replacement explicitly). 3146/3146 web tests pass; type-check + lint + DTO drift gate all green.
…61e9e8 createProviderFromPreset shortcut now uses a conditional spread to attach api_key only when authType === 'api_key'. Today's only caller passes apiKey=undefined for local providers (axios drops the key), so the bug is latent — but a non-empty string for an auth_type='none' preset would be rejected by CreateFromPresetRequest._check_api_key (_reject_blank_secret). Also regenerated *.gen.ts after rebasing onto main b61e9e8 (dashboard polish + training endpoint dispatch + observability cleanup): picked up the new InstalledEntry / PaginatedResponse_InstalledEntry_ schemas backing GET /integrations/mcp/catalog/installed. Type-check + lint + 3146 web tests + DTO drift gate all green.
9991010 to
a2b8753
Compare
… size-limit) Codegen migration ships runtime *_VALUES tuples for ~90 backend StrEnums (select options + type guards). Net growth ~20 KB after deleting the inline literal arrays the tuples replace; current 950.44 kB sits 438 B over the prior 950 KB ceiling, failing the size-limit budget check inside Dashboard Build. Bump to 1000 KB (~5% headroom over current) to absorb routine churn and ongoing codegen additions. Updated comment block documents the rebaseline rationale per web/CLAUDE.md.
<!-- 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>
- web/.size-limit.cjs: re-baseline Total app JS gzipped cap from 1000 KB to 1100 KB for EPIC #2066 D2a decomposition module- boundary overhead (~10 KB across 24 newly-extracted controller hooks / helpers; consistent with the 2026-05-15 #1909 codegen precedent). - web/src/pages/AgentDetailPage.tsx:106: optional chaining on agent.tools to guard the index access when tools is undefined. - web/src/pages/artifacts/useArtifactImagePreview.ts: drop the full artifact object from useEffect deps (primitives already cover the reads); change onPreviewBlobFailed to accept artifactId + contentType primitives so the artifact reference disappears from the effect body, avoiding redundant downloads when the parent passes a new artifact reference with identical fields. - web/src/pages/workflow-editor/workflow-to-yaml.ts:178: explicit undefined / null / empty-string check in copyIfPresent so valid falsy step config values (0, false) are no longer silently dropped from the generated YAML.
- web/.size-limit.cjs: re-baseline Total app JS gzipped cap from 1000 KB to 1100 KB for EPIC #2066 D2a decomposition module- boundary overhead (~10 KB across 24 newly-extracted controller hooks / helpers; consistent with the 2026-05-15 #1909 codegen precedent). - web/src/pages/AgentDetailPage.tsx:106: optional chaining on agent.tools to guard the index access when tools is undefined. - web/src/pages/artifacts/useArtifactImagePreview.ts: drop the full artifact object from useEffect deps (primitives already cover the reads); change onPreviewBlobFailed to accept artifactId + contentType primitives so the artifact reference disappears from the effect body, avoiding redundant downloads when the parent passes a new artifact reference with identical fields. - web/src/pages/workflow-editor/workflow-to-yaml.ts:178: explicit undefined / null / empty-string check in copyIfPresent so valid falsy step config values (0, false) are no longer silently dropped from the generated YAML.
- web/.size-limit.cjs: re-baseline Total app JS gzipped cap from 1000 KB to 1100 KB for EPIC #2066 D2a decomposition module- boundary overhead (~10 KB across 24 newly-extracted controller hooks / helpers; consistent with the 2026-05-15 #1909 codegen precedent). - web/src/pages/AgentDetailPage.tsx:106: optional chaining on agent.tools to guard the index access when tools is undefined. - web/src/pages/artifacts/useArtifactImagePreview.ts: drop the full artifact object from useEffect deps (primitives already cover the reads); change onPreviewBlobFailed to accept artifactId + contentType primitives so the artifact reference disappears from the effect body, avoiding redundant downloads when the parent passes a new artifact reference with identical fields. - web/src/pages/workflow-editor/workflow-to-yaml.ts:178: explicit undefined / null / empty-string check in copyIfPresent so valid falsy step config values (0, false) are no longer silently dropped from the generated YAML.
Closes #1889.
What
Adds a deterministic Pydantic-to-TypeScript codegen pipeline that owns every wire-facing DTO in the dashboard. The three committed outputs (
web/src/api/types/openapi.gen.ts,dtos.gen.ts,enum-values.gen.ts) are now the single source of truth for HTTP DTOs and runtime enum tuples. Each hand-edited file underweb/src/api/types/becomes a thin shim that re-exports from the generated modules and applies a small set of frontend-specific overrides (required-nullable promotion, strict enum narrowing, frontend-only query filters).How
scripts/generate_dto_types_ts.py: boots the Litestar app under hermetic env (in-memory SQLite + stable cursor secret), exports the enriched OpenAPI schema, shells out tonpx openapi-typescriptforopenapi.gen.ts, and rendersdtos.gen.ts(named alias layer overcomponents['schemas']withXEnvelope/XPageshortcuts for Litestar's monomorphised generics) andenum-values.gen.ts(runtime*_VALUEStuples + derived string-union types). Determinism is enforced byPYTHONHASHSEED=0re-exec and a schema-cache enum-description normaliser.scripts/check_dto_types_ts_in_sync.py: pre-push + CI wrapper that fails on byte drift between Pydantic source and the committed.gen.tsfiles..pre-commit-config.yaml: newdto-types-ts-in-synchook wired to the relevant Pydantic source paths and committed outputs; added to the pre-commit.ci skip list (matches the other Litestar + npm boot hooks)..github/workflows/ci.yml: dashboard path filter expanded to include the new generator/gate scripts and the three.gen.tsoutputs; stale.github/ci/*.maxfilter dropped (the directory was removed when the async-leak ceiling was replaced by per-test active-handle detection in fix(web): replace async-leak ceiling with per-test active-handle detection #1905).scripts/convention_gate_map.yaml+web/CLAUDE.md: registered the new MANDATORY rule with its enforcement gate.web/src/api/types/*.ts: every domain shim now re-exports fromdtos.gen/enum-values.gen; required-nullable fields use theOmit<X, K> & { K: T }pattern, defaulted booleans useRequired<>/Readonly<Required<>>.web/.github/ci/web-async-leaks.max) with per-test active-handle detection (web/test-infra/active-handle-*): a worker-sideasync_hookstracker emits NDJSON, the main-process reporter aggregates into a telemetry artifact, ESLint adds@typescript-eslint/no-floating-promises+no-misused-promisesto prevent the syntactic causes upstream.Pre-PR review
12 agents run, 16 findings triaged and addressed in one follow-up commit on top of the migration:
CLAUDE.md; hand-typedProviderConfigrebased on generatedProviderResponse; hand-typedNodeChange/EdgeChange/MetadataChangerebased ondtos.gen..github/ci/*.maxCI filter;driver: ''->'litellm'in custom-provider creation; preset'sauth_typenow sourced instead of hardcoded;Wire*re-exports dropped fromproviders.ts; analytics test fixture aligned with new wire shape.import()-> named imports inagents.ts/tasks.ts/workflows.ts; readonly consistency inbudget.ts; outdated comment removed; pre-commit.ci skip for the new gate; dashboard CI filter includes generated outputs; module-helper schema -> pytest fixture;MAX_DRAIN_ITERATIONSpinned +createTelemetryArtifactallowlist-suppression covered by tests.Issue #1889 acceptance verifier reports all four criteria RESOLVED.
Test plan
uv run ruff check src/ tests/(clean)uv run ruff format src/ tests/(clean)uv run mypy src/ tests/(3785 files, no issues)uv run python -m pytest tests/ -n 8(30289 passed, 50 skipped; the 2 xdist races on Postgres conformance tests pass on direct rerun)uv run python -m pytest tests/unit/scripts/ -n 0(1380 passed; gates the fixture refactor)uv run python scripts/check_dto_types_ts_in_sync.py(clean)npm --prefix web run lint(zero warnings)npm --prefix web run type-check(clean)npm --prefix web run test(3142 passed, no leaked handles)Reviewers
After landing locally, run
/aurelio-review-prto gather external review feedback (CodeRabbit etc.) and triage.