Skip to content

feat(codegen): pydantic-to-typescript DTO pipeline + parity gate (closes #1889)#1909

Merged
Aureliolo merged 27 commits into
mainfrom
feat/pydantic-to-ts-codegen
May 14, 2026
Merged

feat(codegen): pydantic-to-typescript DTO pipeline + parity gate (closes #1889)#1909
Aureliolo merged 27 commits into
mainfrom
feat/pydantic-to-ts-codegen

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

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 under web/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 to npx openapi-typescript for openapi.gen.ts, and renders dtos.gen.ts (named alias layer over components['schemas'] with XEnvelope / XPage shortcuts for Litestar's monomorphised generics) and enum-values.gen.ts (runtime *_VALUES tuples + derived string-union types). Determinism is enforced by PYTHONHASHSEED=0 re-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.ts files.
  • .pre-commit-config.yaml: new dto-types-ts-in-sync hook 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.ts outputs; stale .github/ci/*.max filter 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 from dtos.gen / enum-values.gen; required-nullable fields use the Omit<X, K> & { K: T } pattern, defaulted booleans use Required<>/Readonly<Required<>>.
  • Replaces the async-leak ceiling (web/.github/ci/web-async-leaks.max) with per-test active-handle detection (web/test-infra/active-handle-*): a worker-side async_hooks tracker emits NDJSON, the main-process reporter aggregates into a telemetry artifact, ESLint adds @typescript-eslint/no-floating-promises + no-misused-promises to 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:

  • 3 CRITICAL: stale gate-count claim in CLAUDE.md; hand-typed ProviderConfig rebased on generated ProviderResponse; hand-typed NodeChange/EdgeChange/MetadataChange rebased on dtos.gen.
  • 5 MAJOR: stale .github/ci/*.max CI filter; driver: '' -> 'litellm' in custom-provider creation; preset's auth_type now sourced instead of hardcoded; Wire* re-exports dropped from providers.ts; analytics test fixture aligned with new wire shape.
  • 8 MEDIUM: inline type-union import() -> named imports in agents.ts/tasks.ts/workflows.ts; readonly consistency in budget.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_ITERATIONS pinned + createTelemetryArtifact allowlist-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-pr to gather external review feedback (CodeRabbit etc.) and triage.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 14, 2026

Dependency Review

The following issues were found:
  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ✅ 0 package(s) with unknown licenses.
  • ⚠️ 1 packages with OpenSSF Scorecard issues.
See the Details below.

OpenSSF Scorecard

Scorecard details
PackageVersionScoreDetails
npm/@redocly/ajv 8.11.2 🟢 5.8
Details
CheckScoreReason
Maintained🟢 88 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 8
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Security-Policy🟢 10security policy file detected
Code-Review🟢 8Found 25/30 approved changesets -- score normalized to 8
Packaging⚠️ -1packaging workflow not detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
CII-Best-Practices⚠️ 2badge detected: InProgress
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/@redocly/config 0.22.0 UnknownUnknown
npm/@redocly/openapi-core 1.34.14 UnknownUnknown
npm/brace-expansion 2.1.0 🟢 6.3
Details
CheckScoreReason
Code-Review⚠️ 2Found 7/24 approved changesets -- score normalized to 2
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
Packaging⚠️ -1packaging workflow not detected
Maintained🟢 1016 commit(s) and 3 issue activity found in the last 90 days -- score normalized to 10
Binary-Artifacts🟢 10no binaries found in the repo
Security-Policy🟢 10security policy file detected
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 9license file detected
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
Signed-Releases⚠️ -1no releases found
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/change-case 5.4.4 🟢 3.7
Details
CheckScoreReason
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Packaging⚠️ -1packaging workflow not detected
Security-Policy🟢 10security policy file detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Code-Review⚠️ 1Found 4/30 approved changesets -- score normalized to 1
Binary-Artifacts🟢 10no binaries found in the repo
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Pinned-Dependencies⚠️ 2dependency not pinned by hash detected -- score normalized to 2
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/colorette 1.4.0 🟢 3
Details
CheckScoreReason
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Code-Review⚠️ 2Found 8/30 approved changesets -- score normalized to 2
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 10no binaries found in the repo
Packaging⚠️ -1packaging workflow not detected
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/index-to-position 1.2.0 🟢 3.9
Details
CheckScoreReason
Packaging⚠️ -1packaging workflow not detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Code-Review🟢 4Found 8/18 approved changesets -- score normalized to 4
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Security-Policy🟢 10security policy file detected
Binary-Artifacts🟢 10no binaries found in the repo
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Signed-Releases⚠️ -1no releases found
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/js-levenshtein 1.1.6 ⚠️ 2
Details
CheckScoreReason
Token-Permissions⚠️ -1No tokens found
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Dangerous-Workflow⚠️ -1no workflows found
Code-Review⚠️ 0Found 2/30 approved changesets -- score normalized to 0
Pinned-Dependencies⚠️ -1no dependencies found
Packaging⚠️ -1packaging workflow not detected
Binary-Artifacts🟢 10no binaries found in the repo
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/json-schema-traverse 1.0.0 🟢 3
Details
CheckScoreReason
Code-Review⚠️ 2Found 5/22 approved changesets -- score normalized to 2
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Packaging⚠️ -1packaging workflow not detected
Binary-Artifacts🟢 10no binaries found in the repo
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/minimatch 5.1.9 🟢 6.2
Details
CheckScoreReason
Code-Review⚠️ 0Found 1/28 approved changesets -- score normalized to 0
Packaging⚠️ -1packaging workflow not detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Maintained🟢 1027 commit(s) and 10 issue activity found in the last 90 days -- score normalized to 10
Binary-Artifacts🟢 10no binaries found in the repo
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Security-Policy🟢 10security policy file detected
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/openapi-typescript 7.13.0 UnknownUnknown
npm/parse-json 8.3.0 🟢 4
Details
CheckScoreReason
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Packaging⚠️ -1packaging workflow not detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Security-Policy🟢 10security policy file detected
Code-Review🟢 5Found 15/30 approved changesets -- score normalized to 5
Binary-Artifacts🟢 10no binaries found in the repo
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Signed-Releases⚠️ -1no releases found
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/pluralize 8.0.0 🟢 3.9
Details
CheckScoreReason
Code-Review🟢 6Found 19/30 approved changesets -- score normalized to 6
Token-Permissions⚠️ -1No tokens found
Binary-Artifacts🟢 10no binaries found in the repo
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Dangerous-Workflow⚠️ -1no workflows found
Security-Policy🟢 10security policy file detected
Packaging⚠️ -1packaging workflow not detected
Pinned-Dependencies⚠️ -1no dependencies found
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/supports-color 10.2.2 🟢 3.9
Details
CheckScoreReason
Code-Review🟢 4Found 12/30 approved changesets -- score normalized to 4
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 10no binaries found in the repo
Packaging⚠️ -1packaging workflow not detected
Security-Policy🟢 10security policy file detected
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Signed-Releases⚠️ -1no releases found
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/type-fest 4.41.0 🟢 5.3
Details
CheckScoreReason
Code-Review🟢 8Found 25/30 approved changesets -- score normalized to 8
Maintained🟢 1030 commit(s) and 12 issue activity found in the last 90 days -- score normalized to 10
Security-Policy🟢 10security policy file detected
Packaging⚠️ -1packaging workflow not detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/uri-js-replace 1.0.1 UnknownUnknown
npm/yaml-ast-parser 0.0.43 🟢 4.8
Details
CheckScoreReason
Maintained⚠️ 00 commit(s) out of 30 and 0 issue activity out of 25 found in the last 90 days -- score normalized to 0
Code-Review⚠️ 01 out of last 25 changesets reviewed before merge -- score normalized to 0
CII-Best-Practices⚠️ 0no badge detected
Vulnerabilities🟢 10no vulnerabilities detected
License🟢 9license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection🟢 3branch protection is not maximal on development and all release branches
Token-Permissions🟢 10tokens are read-only in GitHub workflows
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Packaging⚠️ -1no published package detected
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
Pinned-Dependencies🟢 10all dependencies are pinned
Binary-Artifacts🟢 10no binaries found in the repo
Fuzzing⚠️ 0project is not fuzzed
Dependency-Update-Tool⚠️ 0no update tool detected
Security-Policy⚠️ 0security policy file not detected

Scanned Files

  • web/package-lock.json

@Aureliolo Aureliolo temporarily deployed to cloudflare-preview May 14, 2026 18:30 — with GitHub Actions Inactive
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 14, 2026

Review Change Stack

Warning

Rate limit exceeded

@Aureliolo has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 28 seconds before requesting another review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e86ccded-8b30-4212-8e72-8391f4fee1a7

📥 Commits

Reviewing files that changed from the base of the PR and between 9991010 and 17828dc.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (152)
  • .github/workflows/ci.yml
  • .pre-commit-config.yaml
  • CLAUDE.md
  • data/runtime_stats.yaml
  • docs/reference/convention-gates.md
  • scripts/check_dto_types_ts_in_sync.py
  • scripts/convention_gate_map.yaml
  • scripts/generate_dto_types_ts.py
  • src/synthorg/api/openapi.py
  • tests/unit/scripts/fixtures/__init__.py
  • tests/unit/scripts/fixtures/dto_codegen_fixture_schema.py
  • tests/unit/scripts/test_check_dto_types_ts_in_sync.py
  • tests/unit/scripts/test_generate_dto_types_ts.py
  • web/.size-limit.cjs
  • web/CLAUDE.md
  • web/package.json
  • web/src/__tests__/_infra/active-handle-reporter.test.ts
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/__tests__/pages/ProviderDetailPage.test.tsx
  • web/src/__tests__/pages/approvals/ApprovalDetailDrawer.test.tsx
  • web/src/__tests__/pages/budget/ThresholdAlerts.test.tsx
  • web/src/__tests__/pages/settings/SourceBadge.test.tsx
  • web/src/__tests__/stores/agents.test.ts
  • web/src/__tests__/stores/analytics.test.ts
  • web/src/__tests__/stores/artifacts.test.ts
  • web/src/__tests__/stores/budget.test.ts
  • web/src/__tests__/stores/company.test.ts
  • web/src/__tests__/stores/connections.test.ts
  • web/src/__tests__/stores/projects.test.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/__tests__/stores/tasks.test.ts
  • web/src/__tests__/stores/workflows.test.ts
  • web/src/__tests__/utils/budget.property.test.ts
  • web/src/__tests__/utils/budget.test.ts
  • web/src/__tests__/utils/dashboard.property.test.ts
  • web/src/__tests__/utils/dashboard.test.ts
  • web/src/__tests__/utils/tasks.property.test.ts
  • web/src/api/endpoints/activities.ts
  • web/src/api/endpoints/providers.ts
  • web/src/api/types/agents.ts
  • web/src/api/types/analytics.ts
  • web/src/api/types/approvals.ts
  • web/src/api/types/artifacts.ts
  • web/src/api/types/auth.ts
  • web/src/api/types/backup.ts
  • web/src/api/types/budget.ts
  • web/src/api/types/capabilities.ts
  • web/src/api/types/ceremony-policy.ts
  • web/src/api/types/collaboration.ts
  • web/src/api/types/coordination.ts
  • web/src/api/types/dtos.gen.ts
  • web/src/api/types/enum-values.gen.ts
  • web/src/api/types/enums.ts
  • web/src/api/types/errors.ts
  • web/src/api/types/escalations.ts
  • web/src/api/types/index.ts
  • web/src/api/types/integrations.ts
  • web/src/api/types/meetings.ts
  • web/src/api/types/messages.ts
  • web/src/api/types/openapi.gen.ts
  • web/src/api/types/org.ts
  • web/src/api/types/projects.ts
  • web/src/api/types/providers.ts
  • web/src/api/types/security.ts
  • web/src/api/types/settings.ts
  • web/src/api/types/setup.ts
  • web/src/api/types/system.ts
  • web/src/api/types/tasks.ts
  • web/src/api/types/templates.ts
  • web/src/api/types/workflows.ts
  • web/src/hooks/useArtifactsData.ts
  • web/src/hooks/useConnectionsData.ts
  • web/src/hooks/useProjectsData.ts
  • web/src/mocks/handlers/budget.ts
  • web/src/mocks/handlers/company.ts
  • web/src/mocks/handlers/connections.ts
  • web/src/mocks/handlers/escalations.ts
  • web/src/mocks/handlers/providers.ts
  • web/src/mocks/handlers/workflows.ts
  • web/src/pages/BudgetForecastPage.tsx
  • web/src/pages/EscalationQueuePage.stories.tsx
  • web/src/pages/EscalationQueuePage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/WorkflowsPage.tsx
  • web/src/pages/artifacts/ArtifactCard.tsx
  • web/src/pages/artifacts/ArtifactContentPreview.tsx
  • web/src/pages/artifacts/ArtifactCreateDialog.tsx
  • web/src/pages/artifacts/ArtifactMetadata.tsx
  • web/src/pages/budget/BudgetPage.stories.tsx
  • web/src/pages/budget/SpendBurnChart.tsx
  • web/src/pages/budget/ThresholdAlerts.stories.tsx
  • web/src/pages/connections/ConnectionCard.stories.tsx
  • web/src/pages/connections/ConnectionCard.tsx
  • web/src/pages/connections/ConnectionFormModal.stories.tsx
  • web/src/pages/connections/ConnectionFormModal.tsx
  • web/src/pages/dashboard/BudgetBurnChart.tsx
  • web/src/pages/dashboard/DashboardPage.stories.tsx
  • web/src/pages/escalations/EscalationDetailDrawer.stories.tsx
  • web/src/pages/mcp-catalog/McpInstallWizard.stories.tsx
  • web/src/pages/meetings/MeetingActionItems.tsx
  • web/src/pages/meetings/MeetingCard.tsx
  • web/src/pages/meetings/MeetingDetailHeader.tsx
  • web/src/pages/meetings/MeetingTokenBreakdown.tsx
  • web/src/pages/oauth-apps/OauthAppCard.stories.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/org-edit/PackSelectionDialog.tsx
  • web/src/pages/org/build-org-tree.ts
  • web/src/pages/org/drop-target.ts
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/pages/projects/ProjectCard.tsx
  • web/src/pages/projects/ProjectCreateDrawer.tsx
  • web/src/pages/projects/ProjectHeader.tsx
  • web/src/pages/projects/ProjectTeamSection.tsx
  • web/src/pages/providers/AuditLogDrawer.tsx
  • web/src/pages/providers/ModelConfigDrawer.stories.tsx
  • web/src/pages/providers/PresetOverrideDrawer.tsx
  • web/src/pages/providers/ProviderFormModal.tsx
  • web/src/pages/providers/ProviderHealthMetrics.tsx
  • web/src/pages/providers/ProviderModelList.stories.tsx
  • web/src/pages/providers/TestConnectionResult.tsx
  • web/src/pages/settings/ceremony-policy/DepartmentOverridesPanel.tsx
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/pages/setup/MiniOrgChart.tsx
  • web/src/pages/setup/TemplateCard.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/pages/tasks/TaskDetailHeader.tsx
  • web/src/pages/tasks/TaskDetailMetadata.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/workflow-editor/VersionDiffViewer.tsx
  • web/src/pages/workflow-editor/node-config-schemas.ts
  • web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts
  • web/src/pages/workflows/WorkflowCard.tsx
  • web/src/pages/workflows/WorkflowCreateDrawer.tsx
  • web/src/pages/workflows/WorkflowTableView.tsx
  • web/src/stores/approvals.ts
  • web/src/stores/meetings.ts
  • web/src/stores/providers/crud-actions.ts
  • web/src/stores/providers/types.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/stores/tasks.ts
  • web/src/stores/workflow-editor/clipboard.ts
  • web/src/stores/workflow-editor/persistence.ts
  • web/src/stores/workflow-editor/validation.ts
  • web/src/stores/workflow-editor/yaml.ts
  • web/src/utils/agents.ts
  • web/src/utils/meetings.ts
  • web/src/utils/messages.ts
  • web/src/utils/setup-validation.ts
  • web/test-infra/active-handle-tracker.ts

Walkthrough

Adds 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

scope:web

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 14, 2026

Merging this PR will not alter performance

✅ 54 untouched benchmarks


Comparing feat/pydantic-to-ts-codegen (17828dc) with main (b61e9e8)

Open in CodSpeed

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Code Review

This pull request introduces 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.

Comment thread web/src/api/types/agents.ts Outdated
Comment on lines 52 to 60
'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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The 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.

Suggested change
'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

Comment thread scripts/generate_dto_types_ts.py Outdated
up automatically. Conflicts (two distinct classes sharing a name)
drop the entry to avoid a silent wrong mapping.
"""
import sys
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The sys module is already imported at the top level (line 44). This local import is redundant.

Comment thread scripts/generate_dto_types_ts.py Outdated
with the class's own docstring (or removing it when the class has
none) makes the export byte-stable.
"""
import inspect
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

It is generally preferred to keep all imports at the top level of the module for better visibility and to avoid repeated import overhead in functions called within loops.

Comment thread scripts/generate_dto_types_ts.py Outdated
continue
docstring = inspect.getdoc(cls)
if docstring:
defn["description"] = f"{docstring}\n"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Adding a trailing newline to the description might affect how it's rendered in some OpenAPI documentation tools. It's generally better to keep the docstring verbatim.

Suggested change
defn["description"] = f"{docstring}\n"
defn["description"] = docstring

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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

Add mandatory success/error toast emission in this mutation action.

createProviderFromPreset updates 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 win

Allow optional WS fields in the shape guard so these new fallbacks can execute.

Line 319 and Line 340 add nullish defaults, but isMeetingShape currently rejects payloads when token_usage_by_participant or minutes is undefined, so these events are dropped before sanitizeMeeting runs.

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 | 🟡 Minor

Same autonomy_level concern as in AgentEditDrawer.tsx.

This file has the same pattern of setting autonomy_level: null without UI to modify it. See the verification comment on web/src/pages/org-edit/AgentEditDrawer.tsx line 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

📥 Commits

Reviewing files that changed from the base of the PR and between d8ebf55 and b51eaef.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (152)
  • .github/workflows/ci.yml
  • .pre-commit-config.yaml
  • CLAUDE.md
  • data/runtime_stats.yaml
  • docs/reference/convention-gates.md
  • scripts/check_dto_types_ts_in_sync.py
  • scripts/convention_gate_map.yaml
  • scripts/generate_dto_types_ts.py
  • tests/unit/scripts/fixtures/__init__.py
  • tests/unit/scripts/fixtures/dto_codegen_fixture_schema.py
  • tests/unit/scripts/test_check_dto_types_ts_in_sync.py
  • tests/unit/scripts/test_generate_dto_types_ts.py
  • web/CLAUDE.md
  • web/package.json
  • web/src/__tests__/_infra/active-handle-reporter.test.ts
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/__tests__/pages/ProviderDetailPage.test.tsx
  • web/src/__tests__/pages/approvals/ApprovalDetailDrawer.test.tsx
  • web/src/__tests__/pages/budget/ThresholdAlerts.test.tsx
  • web/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsx
  • web/src/__tests__/pages/org-edit/DepartmentCreateDialog.test.tsx
  • web/src/__tests__/stores/agents.test.ts
  • web/src/__tests__/stores/analytics.test.ts
  • web/src/__tests__/stores/artifacts.test.ts
  • web/src/__tests__/stores/budget.test.ts
  • web/src/__tests__/stores/company.test.ts
  • web/src/__tests__/stores/connections.test.ts
  • web/src/__tests__/stores/projects.test.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/__tests__/stores/tasks.test.ts
  • web/src/__tests__/stores/workflows.test.ts
  • web/src/__tests__/utils/budget.property.test.ts
  • web/src/__tests__/utils/budget.test.ts
  • web/src/__tests__/utils/dashboard.property.test.ts
  • web/src/__tests__/utils/dashboard.test.ts
  • web/src/__tests__/utils/tasks.property.test.ts
  • web/src/api/endpoints/activities.ts
  • web/src/api/endpoints/providers.ts
  • web/src/api/types/agents.ts
  • web/src/api/types/analytics.ts
  • web/src/api/types/approvals.ts
  • web/src/api/types/artifacts.ts
  • web/src/api/types/auth.ts
  • web/src/api/types/backup.ts
  • web/src/api/types/budget.ts
  • web/src/api/types/capabilities.ts
  • web/src/api/types/ceremony-policy.ts
  • web/src/api/types/collaboration.ts
  • web/src/api/types/coordination.ts
  • web/src/api/types/dtos.gen.ts
  • web/src/api/types/enum-values.gen.ts
  • web/src/api/types/enums.ts
  • web/src/api/types/errors.ts
  • web/src/api/types/escalations.ts
  • web/src/api/types/index.ts
  • web/src/api/types/integrations.ts
  • web/src/api/types/meetings.ts
  • web/src/api/types/messages.ts
  • web/src/api/types/openapi.gen.ts
  • web/src/api/types/org.ts
  • web/src/api/types/projects.ts
  • web/src/api/types/providers.ts
  • web/src/api/types/security.ts
  • web/src/api/types/settings.ts
  • web/src/api/types/setup.ts
  • web/src/api/types/system.ts
  • web/src/api/types/tasks.ts
  • web/src/api/types/templates.ts
  • web/src/api/types/workflows.ts
  • web/src/hooks/useArtifactsData.ts
  • web/src/hooks/useConnectionsData.ts
  • web/src/hooks/useProjectsData.ts
  • web/src/mocks/handlers/budget.ts
  • web/src/mocks/handlers/company.ts
  • web/src/mocks/handlers/connections.ts
  • web/src/mocks/handlers/escalations.ts
  • web/src/mocks/handlers/providers.ts
  • web/src/mocks/handlers/workflows.ts
  • web/src/pages/BudgetForecastPage.tsx
  • web/src/pages/EscalationQueuePage.stories.tsx
  • web/src/pages/EscalationQueuePage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/WorkflowsPage.tsx
  • web/src/pages/artifacts/ArtifactCard.tsx
  • web/src/pages/artifacts/ArtifactContentPreview.tsx
  • web/src/pages/artifacts/ArtifactCreateDialog.tsx
  • web/src/pages/artifacts/ArtifactMetadata.tsx
  • web/src/pages/budget/BudgetPage.stories.tsx
  • web/src/pages/budget/SpendBurnChart.tsx
  • web/src/pages/budget/ThresholdAlerts.stories.tsx
  • web/src/pages/connections/ConnectionCard.stories.tsx
  • web/src/pages/connections/ConnectionCard.tsx
  • web/src/pages/connections/ConnectionFormModal.stories.tsx
  • web/src/pages/connections/ConnectionFormModal.tsx
  • web/src/pages/dashboard/BudgetBurnChart.tsx
  • web/src/pages/dashboard/DashboardPage.stories.tsx
  • web/src/pages/escalations/EscalationDetailDrawer.stories.tsx
  • web/src/pages/mcp-catalog/McpInstallWizard.stories.tsx
  • web/src/pages/meetings/MeetingActionItems.tsx
  • web/src/pages/meetings/MeetingCard.tsx
  • web/src/pages/meetings/MeetingDetailHeader.tsx
  • web/src/pages/meetings/MeetingTokenBreakdown.tsx
  • web/src/pages/oauth-apps/OauthAppCard.stories.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/org-edit/PackSelectionDialog.tsx
  • web/src/pages/org/build-org-tree.ts
  • web/src/pages/org/drop-target.ts
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/pages/projects/ProjectCard.tsx
  • web/src/pages/projects/ProjectCreateDrawer.tsx
  • web/src/pages/projects/ProjectHeader.tsx
  • web/src/pages/projects/ProjectTeamSection.tsx
  • web/src/pages/providers/AuditLogDrawer.tsx
  • web/src/pages/providers/ModelConfigDrawer.stories.tsx
  • web/src/pages/providers/PresetOverrideDrawer.tsx
  • web/src/pages/providers/ProviderFormModal.tsx
  • web/src/pages/providers/ProviderHealthMetrics.tsx
  • web/src/pages/providers/ProviderModelList.stories.tsx
  • web/src/pages/providers/TestConnectionResult.tsx
  • web/src/pages/settings/ceremony-policy/DepartmentOverridesPanel.tsx
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/pages/setup/TemplateCard.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/pages/tasks/TaskDetailHeader.tsx
  • web/src/pages/tasks/TaskDetailMetadata.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/workflow-editor/VersionDiffViewer.tsx
  • web/src/pages/workflow-editor/node-config-schemas.ts
  • web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts
  • web/src/pages/workflows/WorkflowCard.tsx
  • web/src/pages/workflows/WorkflowCreateDrawer.tsx
  • web/src/pages/workflows/WorkflowTableView.tsx
  • web/src/stores/approvals.ts
  • web/src/stores/meetings.ts
  • web/src/stores/providers/crud-actions.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/stores/tasks.ts
  • web/src/stores/workflow-editor/clipboard.ts
  • web/src/stores/workflow-editor/persistence.ts
  • web/src/stores/workflow-editor/validation.ts
  • web/src/stores/workflow-editor/yaml.ts
  • web/src/utils/agents.ts
  • web/src/utils/meetings.ts
  • web/src/utils/messages.ts
  • web/src/utils/setup-validation.ts
  • web/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 19

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

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

Use variable name log for logger instances (e.g., const log = createLogger('module-name'))

Use useEmptyStateProps({ filteredCount, totalCount, filterActive, empty, filtered }) from @/hooks/use-empty-state-props to discriminate between empty-state variants instead of duplicating the logic

Files:

  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/__tests__/pages/ProviderDetailPage.test.tsx
  • web/src/pages/meetings/MeetingDetailHeader.tsx
  • web/src/hooks/useConnectionsData.ts
  • web/src/__tests__/pages/budget/ThresholdAlerts.test.tsx
  • web/src/hooks/useArtifactsData.ts
  • web/src/pages/mcp-catalog/McpInstallWizard.stories.tsx
  • web/src/pages/providers/AuditLogDrawer.tsx
  • web/src/pages/connections/ConnectionCard.tsx
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/pages/budget/SpendBurnChart.tsx
  • web/src/pages/projects/ProjectCreateDrawer.tsx
  • web/src/pages/org/drop-target.ts
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/__tests__/utils/dashboard.test.ts
  • web/src/__tests__/pages/org-edit/DepartmentCreateDialog.test.tsx
  • web/src/pages/meetings/MeetingCard.tsx
  • web/src/pages/EscalationQueuePage.stories.tsx
  • web/src/pages/oauth-apps/OauthAppCard.stories.tsx
  • web/src/pages/connections/ConnectionFormModal.tsx
  • web/src/pages/meetings/MeetingTokenBreakdown.tsx
  • web/src/pages/workflows/WorkflowTableView.tsx
  • web/src/pages/tasks/TaskDetailHeader.tsx
  • web/src/pages/connections/ConnectionCard.stories.tsx
  • web/src/stores/workflow-editor/clipboard.ts
  • web/src/__tests__/utils/budget.property.test.ts
  • web/src/api/endpoints/activities.ts
  • web/src/stores/workflow-editor/yaml.ts
  • web/src/pages/artifacts/ArtifactContentPreview.tsx
  • web/src/pages/projects/ProjectHeader.tsx
  • web/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsx
  • web/src/pages/artifacts/ArtifactMetadata.tsx
  • web/src/pages/dashboard/BudgetBurnChart.tsx
  • web/src/pages/settings/ceremony-policy/DepartmentOverridesPanel.tsx
  • web/src/pages/artifacts/ArtifactCard.tsx
  • web/src/mocks/handlers/company.ts
  • web/src/api/endpoints/providers.ts
  • web/src/hooks/useProjectsData.ts
  • web/src/pages/budget/BudgetPage.stories.tsx
  • web/src/mocks/handlers/escalations.ts
  • web/src/pages/providers/ModelConfigDrawer.stories.tsx
  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/pages/workflows/WorkflowCard.tsx
  • web/src/mocks/handlers/workflows.ts
  • web/src/pages/connections/ConnectionFormModal.stories.tsx
  • web/src/__tests__/stores/analytics.test.ts
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/__tests__/stores/projects.test.ts
  • web/src/pages/providers/TestConnectionResult.tsx
  • web/src/pages/meetings/MeetingActionItems.tsx
  • web/src/stores/providers/crud-actions.ts
  • web/src/pages/projects/ProjectTeamSection.tsx
  • web/src/pages/escalations/EscalationDetailDrawer.stories.tsx
  • web/src/pages/budget/ThresholdAlerts.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/api/types/index.ts
  • web/src/api/types/errors.ts
  • web/src/__tests__/utils/tasks.property.test.ts
  • web/src/api/types/coordination.ts
  • web/src/pages/providers/ProviderHealthMetrics.tsx
  • web/src/pages/BudgetForecastPage.tsx
  • web/src/__tests__/stores/budget.test.ts
  • web/src/pages/org-edit/PackSelectionDialog.tsx
  • web/src/pages/setup/TemplateCard.tsx
  • web/src/pages/workflow-editor/node-config-schemas.ts
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/tasks/TaskDetailMetadata.tsx
  • web/src/pages/artifacts/ArtifactCreateDialog.tsx
  • web/src/mocks/handlers/providers.ts
  • web/src/stores/meetings.ts
  • web/src/pages/projects/ProjectCard.tsx
  • web/src/pages/dashboard/DashboardPage.stories.tsx
  • web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts
  • web/src/__tests__/stores/workflows.test.ts
  • web/src/__tests__/stores/tasks.test.ts
  • web/src/pages/EscalationQueuePage.tsx
  • web/src/utils/messages.ts
  • web/src/pages/WorkflowsPage.tsx
  • web/src/api/types/templates.ts
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/stores/approvals.ts
  • web/src/stores/workflow-editor/persistence.ts
  • web/src/api/types/capabilities.ts
  • web/src/stores/workflow-editor/validation.ts
  • web/src/stores/tasks.ts
  • web/src/api/types/backup.ts
  • web/src/utils/agents.ts
  • web/src/pages/providers/ProviderModelList.stories.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/__tests__/utils/budget.test.ts
  • web/src/__tests__/utils/dashboard.property.test.ts
  • web/src/__tests__/stores/connections.test.ts
  • web/src/api/types/system.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/api/types/projects.ts
  • web/src/pages/org/build-org-tree.ts
  • web/src/api/types/collaboration.ts
  • web/src/api/types/dtos.gen.ts
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/api/types/artifacts.ts
  • web/src/pages/workflows/WorkflowCreateDrawer.tsx
  • web/src/pages/providers/ProviderFormModal.tsx
  • web/src/__tests__/_infra/active-handle-reporter.test.ts
  • web/src/mocks/handlers/budget.ts
  • web/src/__tests__/pages/approvals/ApprovalDetailDrawer.test.tsx
  • web/src/api/types/auth.ts
  • web/src/utils/meetings.ts
  • web/src/api/types/analytics.ts
  • web/src/api/types/ceremony-policy.ts
  • web/src/__tests__/stores/artifacts.test.ts
  • web/src/api/types/messages.ts
  • web/src/api/types/meetings.ts
  • web/src/__tests__/stores/company.test.ts
  • web/src/pages/providers/PresetOverrideDrawer.tsx
  • web/src/api/types/settings.ts
  • web/src/utils/setup-validation.ts
  • web/src/api/types/budget.ts
  • web/src/__tests__/stores/agents.test.ts
  • web/src/api/types/security.ts
  • web/src/mocks/handlers/connections.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/pages/workflow-editor/VersionDiffViewer.tsx
  • web/src/api/types/setup.ts
  • web/src/api/types/enum-values.gen.ts
  • web/src/api/types/org.ts
  • web/src/api/types/approvals.ts
  • web/src/api/types/integrations.ts
  • web/src/api/types/tasks.ts
  • web/test-infra/active-handle-tracker.ts
  • web/src/api/types/agents.ts
  • web/src/api/types/workflows.ts
  • web/src/api/types/enums.ts
  • web/src/api/types/escalations.ts
  • web/src/api/types/providers.ts
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

Import and discriminate on ErrorCode and ErrorCategory from @/api/types/errors; never discriminate on raw integer error-code literals

Never hand-edit *.gen.ts files; regenerate generated DTO types with uv run python scripts/generate_dto_types_ts.py when OpenAPI schema changes

Route WebSocket string payloads through sanitizeWsString() and enums through sanitizeWsEnum<T>(value, allowlist, fallback, { field }) from @/stores/notifications or @/utils/ws-sanitize.ts; raw (sanitizeWsString(x, n) ?? '') as EnumType casts are forbidden

ESLint rule: enable @eslint-react/web-api-no-leaked-fetch to detect fetch() in effects without AbortController cleanup

ESLint rule: enable @eslint-react/no-leaked-conditional-rendering to catch the {count && <Foo />} bug; use {value != null && value !== false && <jsx>} for ReactNode | undefined props and Boolean(...) for compound truthiness

ESLint rule: enable @eslint-react/globals to restrict window / document / localStorage inside render bodies; hoist offenders into useCallback event handlers, useEffect, or useSyncExternalStore-backed hooks

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

ESLint rule: enable @typescript-eslint/no-misused-promises (with checksVoidReturn: { attributes: false }) to forbid passing async functions where callsites ignore the returned promise; React 19 async event handlers stay allowed

Files:

  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/__tests__/pages/ProviderDetailPage.test.tsx
  • web/src/pages/meetings/MeetingDetailHeader.tsx
  • web/src/hooks/useConnectionsData.ts
  • web/src/__tests__/pages/budget/ThresholdAlerts.test.tsx
  • web/src/hooks/useArtifactsData.ts
  • web/src/pages/mcp-catalog/McpInstallWizard.stories.tsx
  • web/src/pages/providers/AuditLogDrawer.tsx
  • web/src/pages/connections/ConnectionCard.tsx
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/pages/budget/SpendBurnChart.tsx
  • web/src/pages/projects/ProjectCreateDrawer.tsx
  • web/src/pages/org/drop-target.ts
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/__tests__/utils/dashboard.test.ts
  • web/src/__tests__/pages/org-edit/DepartmentCreateDialog.test.tsx
  • web/src/pages/meetings/MeetingCard.tsx
  • web/src/pages/EscalationQueuePage.stories.tsx
  • web/src/pages/oauth-apps/OauthAppCard.stories.tsx
  • web/src/pages/connections/ConnectionFormModal.tsx
  • web/src/pages/meetings/MeetingTokenBreakdown.tsx
  • web/src/pages/workflows/WorkflowTableView.tsx
  • web/src/pages/tasks/TaskDetailHeader.tsx
  • web/src/pages/connections/ConnectionCard.stories.tsx
  • web/src/stores/workflow-editor/clipboard.ts
  • web/src/__tests__/utils/budget.property.test.ts
  • web/src/api/endpoints/activities.ts
  • web/src/stores/workflow-editor/yaml.ts
  • web/src/pages/artifacts/ArtifactContentPreview.tsx
  • web/src/pages/projects/ProjectHeader.tsx
  • web/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsx
  • web/src/pages/artifacts/ArtifactMetadata.tsx
  • web/src/pages/dashboard/BudgetBurnChart.tsx
  • web/src/pages/settings/ceremony-policy/DepartmentOverridesPanel.tsx
  • web/src/pages/artifacts/ArtifactCard.tsx
  • web/src/mocks/handlers/company.ts
  • web/src/api/endpoints/providers.ts
  • web/src/hooks/useProjectsData.ts
  • web/src/pages/budget/BudgetPage.stories.tsx
  • web/src/mocks/handlers/escalations.ts
  • web/src/pages/providers/ModelConfigDrawer.stories.tsx
  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/pages/workflows/WorkflowCard.tsx
  • web/src/mocks/handlers/workflows.ts
  • web/src/pages/connections/ConnectionFormModal.stories.tsx
  • web/src/__tests__/stores/analytics.test.ts
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/__tests__/stores/projects.test.ts
  • web/src/pages/providers/TestConnectionResult.tsx
  • web/src/pages/meetings/MeetingActionItems.tsx
  • web/src/stores/providers/crud-actions.ts
  • web/src/pages/projects/ProjectTeamSection.tsx
  • web/src/pages/escalations/EscalationDetailDrawer.stories.tsx
  • web/src/pages/budget/ThresholdAlerts.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/api/types/index.ts
  • web/src/api/types/errors.ts
  • web/src/__tests__/utils/tasks.property.test.ts
  • web/src/api/types/coordination.ts
  • web/src/pages/providers/ProviderHealthMetrics.tsx
  • web/src/pages/BudgetForecastPage.tsx
  • web/src/__tests__/stores/budget.test.ts
  • web/src/pages/org-edit/PackSelectionDialog.tsx
  • web/src/pages/setup/TemplateCard.tsx
  • web/src/pages/workflow-editor/node-config-schemas.ts
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/tasks/TaskDetailMetadata.tsx
  • web/src/pages/artifacts/ArtifactCreateDialog.tsx
  • web/src/mocks/handlers/providers.ts
  • web/src/stores/meetings.ts
  • web/src/pages/projects/ProjectCard.tsx
  • web/src/pages/dashboard/DashboardPage.stories.tsx
  • web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts
  • web/src/__tests__/stores/workflows.test.ts
  • web/src/__tests__/stores/tasks.test.ts
  • web/src/pages/EscalationQueuePage.tsx
  • web/src/utils/messages.ts
  • web/src/pages/WorkflowsPage.tsx
  • web/src/api/types/templates.ts
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/stores/approvals.ts
  • web/src/stores/workflow-editor/persistence.ts
  • web/src/api/types/capabilities.ts
  • web/src/stores/workflow-editor/validation.ts
  • web/src/stores/tasks.ts
  • web/src/api/types/backup.ts
  • web/src/utils/agents.ts
  • web/src/pages/providers/ProviderModelList.stories.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/__tests__/utils/budget.test.ts
  • web/src/__tests__/utils/dashboard.property.test.ts
  • web/src/__tests__/stores/connections.test.ts
  • web/src/api/types/system.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/api/types/projects.ts
  • web/src/pages/org/build-org-tree.ts
  • web/src/api/types/collaboration.ts
  • web/src/api/types/dtos.gen.ts
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/api/types/artifacts.ts
  • web/src/pages/workflows/WorkflowCreateDrawer.tsx
  • web/src/pages/providers/ProviderFormModal.tsx
  • web/src/__tests__/_infra/active-handle-reporter.test.ts
  • web/src/mocks/handlers/budget.ts
  • web/src/__tests__/pages/approvals/ApprovalDetailDrawer.test.tsx
  • web/src/api/types/auth.ts
  • web/src/utils/meetings.ts
  • web/src/api/types/analytics.ts
  • web/src/api/types/ceremony-policy.ts
  • web/src/__tests__/stores/artifacts.test.ts
  • web/src/api/types/messages.ts
  • web/src/api/types/meetings.ts
  • web/src/__tests__/stores/company.test.ts
  • web/src/pages/providers/PresetOverrideDrawer.tsx
  • web/src/api/types/settings.ts
  • web/src/utils/setup-validation.ts
  • web/src/api/types/budget.ts
  • web/src/__tests__/stores/agents.test.ts
  • web/src/api/types/security.ts
  • web/src/mocks/handlers/connections.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/pages/workflow-editor/VersionDiffViewer.tsx
  • web/src/api/types/setup.ts
  • web/src/api/types/enum-values.gen.ts
  • web/src/api/types/org.ts
  • web/src/api/types/approvals.ts
  • web/src/api/types/integrations.ts
  • web/src/api/types/tasks.ts
  • web/src/api/types/agents.ts
  • web/src/api/types/workflows.ts
  • web/src/api/types/enums.ts
  • web/src/api/types/escalations.ts
  • web/src/api/types/providers.ts
web/**/*.tsx

📄 CodeRabbit inference engine (web/CLAUDE.md)

Export icon wrappers as <XIcon value={...} {...lucideProps} /> components instead of getXIcon(value): LucideIcon factories that return component references; the wrapper performs the lookup via createElement to avoid PascalCase JSX bindings in the render body

Use useViewportSize() from @/hooks/useViewportSize for viewport size reads; never read window.innerWidth / window.innerHeight directly inside component render bodies or useMemo

Files:

  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/__tests__/pages/ProviderDetailPage.test.tsx
  • web/src/pages/meetings/MeetingDetailHeader.tsx
  • web/src/__tests__/pages/budget/ThresholdAlerts.test.tsx
  • web/src/pages/mcp-catalog/McpInstallWizard.stories.tsx
  • web/src/pages/providers/AuditLogDrawer.tsx
  • web/src/pages/connections/ConnectionCard.tsx
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/pages/budget/SpendBurnChart.tsx
  • web/src/pages/projects/ProjectCreateDrawer.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/__tests__/pages/org-edit/DepartmentCreateDialog.test.tsx
  • web/src/pages/meetings/MeetingCard.tsx
  • web/src/pages/EscalationQueuePage.stories.tsx
  • web/src/pages/oauth-apps/OauthAppCard.stories.tsx
  • web/src/pages/connections/ConnectionFormModal.tsx
  • web/src/pages/meetings/MeetingTokenBreakdown.tsx
  • web/src/pages/workflows/WorkflowTableView.tsx
  • web/src/pages/tasks/TaskDetailHeader.tsx
  • web/src/pages/connections/ConnectionCard.stories.tsx
  • web/src/pages/artifacts/ArtifactContentPreview.tsx
  • web/src/pages/projects/ProjectHeader.tsx
  • web/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsx
  • web/src/pages/artifacts/ArtifactMetadata.tsx
  • web/src/pages/dashboard/BudgetBurnChart.tsx
  • web/src/pages/settings/ceremony-policy/DepartmentOverridesPanel.tsx
  • web/src/pages/artifacts/ArtifactCard.tsx
  • web/src/pages/budget/BudgetPage.stories.tsx
  • web/src/pages/providers/ModelConfigDrawer.stories.tsx
  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/pages/workflows/WorkflowCard.tsx
  • web/src/pages/connections/ConnectionFormModal.stories.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/pages/providers/TestConnectionResult.tsx
  • web/src/pages/meetings/MeetingActionItems.tsx
  • web/src/pages/projects/ProjectTeamSection.tsx
  • web/src/pages/escalations/EscalationDetailDrawer.stories.tsx
  • web/src/pages/budget/ThresholdAlerts.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/providers/ProviderHealthMetrics.tsx
  • web/src/pages/BudgetForecastPage.tsx
  • web/src/pages/org-edit/PackSelectionDialog.tsx
  • web/src/pages/setup/TemplateCard.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/tasks/TaskDetailMetadata.tsx
  • web/src/pages/artifacts/ArtifactCreateDialog.tsx
  • web/src/pages/projects/ProjectCard.tsx
  • web/src/pages/dashboard/DashboardPage.stories.tsx
  • web/src/pages/EscalationQueuePage.tsx
  • web/src/pages/WorkflowsPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/providers/ProviderModelList.stories.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/workflows/WorkflowCreateDrawer.tsx
  • web/src/pages/providers/ProviderFormModal.tsx
  • web/src/__tests__/pages/approvals/ApprovalDetailDrawer.test.tsx
  • web/src/pages/providers/PresetOverrideDrawer.tsx
  • web/src/pages/workflow-editor/VersionDiffViewer.tsx
web/src/**/*.tsx

📄 CodeRabbit inference engine (web/CLAUDE.md)

Use <StatusBadge> (with role="img" and aria-label by default; decorative for adjacent-labeled, announce for live WS updates) instead of hardcoding status dots

Use <MetricCard> / <Sparkline> / <ProgressGauge> / <TokenUsageBar> for KPI displays instead of creating inline custom components

Use <SectionCard> for titled wrapper with icon and action slot; use <AgentCard>, <DeptHealthBar> for domain-specific card displays

Use form field components: <InputField> / <SelectField> / <SliderField> / <ToggleField> / <SegmentedControl> / <TagInput> / <SearchInput> instead of bare input elements

Use <Drawer width="compact|narrow|default|wide"> for slide-in panels; do NOT add inline w-[40vw] overrides

Use <Skeleton> family / <EmptyState> / <ErrorBoundary> / <ErrorBanner> / <ProgressIndicator> for loading / empty / error states instead of custom implementations

Use <ListHeader> / <SearchFilterSort> / <Pagination> / <BulkActionBar> / <MetadataGrid> / <Breadcrumbs> / <Collapsible> for list-page primitives

Import STATUS_COLORS family from @/styles/status-colors for status/role/risk/urgency badge styling instead of defining inline Record<EnumValue, string> constants

Use <ConfirmDialog> / <Toast> (Zustand-backed queue, NOT Base UI's Toast) for confirmations and notifications

Use <CommandPalette> / <KeyboardShortcutHint> / <CommandCheatsheet> for command-palette and shortcut UI instead of custom implementations

Use <AnimatedPresence> / <StaggerGroup> / <LiveRegion> (debounced ARIA live for WS updates) for animation and accessibility instead of custom implementations

Files:

  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/__tests__/pages/ProviderDetailPage.test.tsx
  • web/src/pages/meetings/MeetingDetailHeader.tsx
  • web/src/__tests__/pages/budget/ThresholdAlerts.test.tsx
  • web/src/pages/mcp-catalog/McpInstallWizard.stories.tsx
  • web/src/pages/providers/AuditLogDrawer.tsx
  • web/src/pages/connections/ConnectionCard.tsx
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/pages/budget/SpendBurnChart.tsx
  • web/src/pages/projects/ProjectCreateDrawer.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/__tests__/pages/org-edit/DepartmentCreateDialog.test.tsx
  • web/src/pages/meetings/MeetingCard.tsx
  • web/src/pages/EscalationQueuePage.stories.tsx
  • web/src/pages/oauth-apps/OauthAppCard.stories.tsx
  • web/src/pages/connections/ConnectionFormModal.tsx
  • web/src/pages/meetings/MeetingTokenBreakdown.tsx
  • web/src/pages/workflows/WorkflowTableView.tsx
  • web/src/pages/tasks/TaskDetailHeader.tsx
  • web/src/pages/connections/ConnectionCard.stories.tsx
  • web/src/pages/artifacts/ArtifactContentPreview.tsx
  • web/src/pages/projects/ProjectHeader.tsx
  • web/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsx
  • web/src/pages/artifacts/ArtifactMetadata.tsx
  • web/src/pages/dashboard/BudgetBurnChart.tsx
  • web/src/pages/settings/ceremony-policy/DepartmentOverridesPanel.tsx
  • web/src/pages/artifacts/ArtifactCard.tsx
  • web/src/pages/budget/BudgetPage.stories.tsx
  • web/src/pages/providers/ModelConfigDrawer.stories.tsx
  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/pages/workflows/WorkflowCard.tsx
  • web/src/pages/connections/ConnectionFormModal.stories.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/pages/providers/TestConnectionResult.tsx
  • web/src/pages/meetings/MeetingActionItems.tsx
  • web/src/pages/projects/ProjectTeamSection.tsx
  • web/src/pages/escalations/EscalationDetailDrawer.stories.tsx
  • web/src/pages/budget/ThresholdAlerts.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/providers/ProviderHealthMetrics.tsx
  • web/src/pages/BudgetForecastPage.tsx
  • web/src/pages/org-edit/PackSelectionDialog.tsx
  • web/src/pages/setup/TemplateCard.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/tasks/TaskDetailMetadata.tsx
  • web/src/pages/artifacts/ArtifactCreateDialog.tsx
  • web/src/pages/projects/ProjectCard.tsx
  • web/src/pages/dashboard/DashboardPage.stories.tsx
  • web/src/pages/EscalationQueuePage.tsx
  • web/src/pages/WorkflowsPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/providers/ProviderModelList.stories.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/workflows/WorkflowCreateDrawer.tsx
  • web/src/pages/providers/ProviderFormModal.tsx
  • web/src/__tests__/pages/approvals/ApprovalDetailDrawer.test.tsx
  • web/src/pages/providers/PresetOverrideDrawer.tsx
  • web/src/pages/workflow-editor/VersionDiffViewer.tsx
web/src/pages/**/*.tsx

📄 CodeRabbit inference engine (web/CLAUDE.md)

Page root container should use space-y-section-gap for spacing (the majority pattern; flex flex-col gap-section-gap is equivalent but discouraged)

Place <ErrorBanner> immediately after <ListHeader>, before any filter/pagination row

Aim 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.tsx
  • web/src/pages/mcp-catalog/McpInstallWizard.stories.tsx
  • web/src/pages/providers/AuditLogDrawer.tsx
  • web/src/pages/connections/ConnectionCard.tsx
  • web/src/pages/budget/SpendBurnChart.tsx
  • web/src/pages/projects/ProjectCreateDrawer.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/meetings/MeetingCard.tsx
  • web/src/pages/EscalationQueuePage.stories.tsx
  • web/src/pages/oauth-apps/OauthAppCard.stories.tsx
  • web/src/pages/connections/ConnectionFormModal.tsx
  • web/src/pages/meetings/MeetingTokenBreakdown.tsx
  • web/src/pages/workflows/WorkflowTableView.tsx
  • web/src/pages/tasks/TaskDetailHeader.tsx
  • web/src/pages/connections/ConnectionCard.stories.tsx
  • web/src/pages/artifacts/ArtifactContentPreview.tsx
  • web/src/pages/projects/ProjectHeader.tsx
  • web/src/pages/artifacts/ArtifactMetadata.tsx
  • web/src/pages/dashboard/BudgetBurnChart.tsx
  • web/src/pages/settings/ceremony-policy/DepartmentOverridesPanel.tsx
  • web/src/pages/artifacts/ArtifactCard.tsx
  • web/src/pages/budget/BudgetPage.stories.tsx
  • web/src/pages/providers/ModelConfigDrawer.stories.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/pages/workflows/WorkflowCard.tsx
  • web/src/pages/connections/ConnectionFormModal.stories.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/pages/providers/TestConnectionResult.tsx
  • web/src/pages/meetings/MeetingActionItems.tsx
  • web/src/pages/projects/ProjectTeamSection.tsx
  • web/src/pages/escalations/EscalationDetailDrawer.stories.tsx
  • web/src/pages/budget/ThresholdAlerts.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/providers/ProviderHealthMetrics.tsx
  • web/src/pages/BudgetForecastPage.tsx
  • web/src/pages/org-edit/PackSelectionDialog.tsx
  • web/src/pages/setup/TemplateCard.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/tasks/TaskDetailMetadata.tsx
  • web/src/pages/artifacts/ArtifactCreateDialog.tsx
  • web/src/pages/projects/ProjectCard.tsx
  • web/src/pages/dashboard/DashboardPage.stories.tsx
  • web/src/pages/EscalationQueuePage.tsx
  • web/src/pages/WorkflowsPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/providers/ProviderModelList.stories.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/workflows/WorkflowCreateDrawer.tsx
  • web/src/pages/providers/ProviderFormModal.tsx
  • web/src/pages/providers/PresetOverrideDrawer.tsx
  • web/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.md

Use from synthorg.observability import get_logger with variable name logger; never use import logging or print() in app code

Event names must come from observability.events.<domain> constants; use structured kwargs like logger.info(EVENT, key=value)

Never use error=str(exc) or interpolate {exc} in logs; use error_type=type(exc).__name__ and error=safe_error_description(exc). Never use exc_info=True. For OTel spans, use span.set_attribute() instead of span.record_exception()

Error classes must be named <Domain><Condition>Error and inherit from DomainError, never directly from Exception, RuntimeError, or other base exceptions

Numeric 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 form NAME: int|float|Final|Final[int]|Final[float] = literal

Type 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=True and extra='forbid' on API DTOs (Request/Response/Snapshot/Result/Envelope/Status/Info/Summary suffixes); use @computed_field for derived data; use NotBlankStr for identifiers

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

Use immutability via model_copy(update=...) for Pydantic models or copy.deepcopy() for other objects; use deepcopy at system boundaries

Use asyncio.TaskGroup for fan-out/fan-in async operations; helper functions must catch Exception and re-raise MemoryError and RecursionError

Implement Clock seam pattern with clock: Clock | None = None parameter; tests must inject FakeClock

Services must own _lifecycle_lock; timed-out stops must mark services as unrestartable

For...

Files:

  • scripts/check_dto_types_ts_in_sync.py
  • tests/unit/scripts/fixtures/__init__.py
  • tests/unit/scripts/fixtures/dto_codegen_fixture_schema.py
  • tests/unit/scripts/test_check_dto_types_ts_in_sync.py
  • tests/unit/scripts/test_generate_dto_types_ts.py
  • scripts/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 (null for entity returns, false for delete); callers MUST NOT wrap store mutation calls in try/catch

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

Implement cursor-based pagination (not offset) via PaginationMeta, keeping nextCursor + hasMore in state (not offset arithmetic) and early-returning when !hasMore || !nextCursor

Files:

  • web/src/stores/workflow-editor/clipboard.ts
  • web/src/stores/workflow-editor/yaml.ts
  • web/src/stores/providers/crud-actions.ts
  • web/src/stores/meetings.ts
  • web/src/stores/approvals.ts
  • web/src/stores/workflow-editor/persistence.ts
  • web/src/stores/workflow-editor/validation.ts
  • web/src/stores/tasks.ts
  • web/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=loadfile for parallel test execution; loadfile distribution prevents Python 3.14+ Windows ProactorEventLoop leak

On Windows, unit tests must use WindowsSelectorEventLoopPolicy; subprocess tests must override back to default

Use test doubles: FakeClock for Clock seam, mock_of[T](**overrides) for typed-boundary substitutions, SimpleNamespace for attribute bags. Never use bare MagicMock at typed boundaries. Enforced by scripts/check_mock_spec.py

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

Never 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, and web/public/provider-logos/

Hypothesis: use 10 deterministic CI examples; treat failures as real bugs (fix + add @example(...)). Never skip/xfail flaky tests; fix fundamentally. Use asyncio.Event().wait() instead of sleep(large)

Files:

  • tests/unit/scripts/fixtures/__init__.py
  • tests/unit/scripts/fixtures/dto_codegen_fixture_schema.py
  • tests/unit/scripts/test_check_dto_types_ts_in_sync.py
  • tests/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__.py
  • tests/unit/scripts/fixtures/dto_codegen_fixture_schema.py
  • tests/unit/scripts/test_check_dto_types_ts_in_sync.py
  • tests/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.yaml via <!--RS:NAME--> markers (Doc Numeric Claims MANDATORY)

Files:

  • CLAUDE.md
  • web/CLAUDE.md
  • docs/reference/convention-gates.md
web/src/mocks/handlers/**/*.ts

📄 CodeRabbit inference engine (web/CLAUDE.md)

Mirror web/src/api/endpoints/*.ts 1: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.ts
  • web/src/mocks/handlers/escalations.ts
  • web/src/mocks/handlers/workflows.ts
  • web/src/mocks/handlers/providers.ts
  • web/src/mocks/handlers/budget.ts
  • web/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>Page shortcuts; 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 *_VALUES tuples 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.py
  • tests/unit/scripts/fixtures/__init__.py
  • tests/unit/scripts/fixtures/dto_codegen_fixture_schema.py
  • tests/unit/scripts/test_check_dto_types_ts_in_sync.py
  • tests/unit/scripts/test_generate_dto_types_ts.py
  • scripts/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)

Comment thread CLAUDE.md Outdated
Comment thread web/src/__tests__/stores/setup-wizard.test.ts Outdated
Comment thread web/src/hooks/useConnectionsData.ts
Comment thread web/src/pages/org/useOrgChartDragDrop.ts Outdated
Comment thread web/src/pages/OrgEditPage.tsx Outdated
Comment thread web/src/pages/settings/sinks/SinkFormDrawer.tsx Outdated
Comment thread web/src/pages/tasks/TaskDetailPanel.tsx Outdated
Comment thread web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts Outdated
Comment thread web/src/pages/workflows/WorkflowCard.tsx Outdated
Comment thread web/src/stores/tasks.ts
@codecov
Copy link
Copy Markdown

codecov Bot commented May 14, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.06%. Comparing base (b61e9e8) to head (17828dc).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

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.
📢 Have feedback on the report? Share it here.

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

@Aureliolo Aureliolo temporarily deployed to cloudflare-preview May 14, 2026 19:35 — with GitHub Actions Inactive
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between b51eaef and 69ceca1.

📒 Files selected for processing (17)
  • CLAUDE.md
  • scripts/generate_dto_types_ts.py
  • web/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsx
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/api/types/agents.ts
  • web/src/hooks/useConnectionsData.ts
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts
  • web/src/pages/workflows/WorkflowCard.tsx
  • web/src/stores/meetings.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/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 in web/CLAUDE.md

Files:

  • web/src/pages/workflows/WorkflowCard.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/hooks/useConnectionsData.ts
  • web/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts
  • web/src/stores/meetings.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/api/types/agents.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/stores/tasks.ts
web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

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

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

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, and web/src/api/types/enum-values.gen.ts. NEVER hand-edit a *.gen.ts file. Regenerate with uv 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-plugin v5+ via the recommended-type-checked preset (requires parserOptions.projectService: true, configured in web/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 (with checksVoidReturn: { attributes: false }).

Files:

  • web/src/pages/workflows/WorkflowCard.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/hooks/useConnectionsData.ts
  • web/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts
  • web/src/stores/meetings.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/api/types/agents.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/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/motion presets, the helpers in @/utils/format, and DEFAULT_CURRENCY from @/utils/currencies.

Status dots -> <StatusBadge> (defaults to role="img" with aria-label; decorative for adjacent-labeled, announce for 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 inline w-[40vw] overrides).

Loading / empty / error states -> <Skeleton> family / <EmptyState> / <ErrorBoundary> / <ErrorBanner> / <ProgressIndicator>.

Status / role / risk / urgency badge classes -> STATUS_COLORS family from @/styles/status-colors (typed Record<EnumValue, string> lookups; no inline Record<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.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/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 uses space-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={[]}> returns null so unconditional render at the top of a page is safe.

Empty-state derivation -> useEmptyStateProps({ filteredCount, totalCount, filterActive, empty, filtered }) from @/hooks/use-empty-state-props returns EmptyStateProps | null so 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.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts
web/**/*.tsx

📄 CodeRabbit inference engine (web/CLAUDE.md)

Viewport-size reads -> useViewportSize() from @/hooks/useViewportSize (useSyncExternalStore over window resize). Never read window.innerWidth / window.innerHeight directly inside a component render body or useMemo.

Files:

  • web/src/pages/workflows/WorkflowCard.tsx
  • web/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/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 every web/src/ edit and flags hardcoded hex / rgba / fonts / Motion durations / locale literals / bare .toLocale*String() calls / missing Storybook stories / duplicate component patterns / complex .map() blocks. Fix every violation before proceeding.

Files:

  • web/src/pages/workflows/WorkflowCard.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/__tests__/pages/org-edit/AgentEditDrawer.test.tsx
  • web/src/pages/org-edit/AgentEditDrawer.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/org-edit/DepartmentCreateDialog.tsx
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts
web/**/stores/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

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

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

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

Active-handle gate (MANDATORY): every unit test runs under web/test-infra/active-handle-tracker.ts, which hooks Node's async_hooks and fails any test that leaks an event-loop-holding resource. A new store that schedules timers / attaches listeners MUST expose a teardown hook and register it in the global afterEach; otherwise the gate fails the first test that triggers the schedule.

WS payload sanitization (MANDATORY): sanitizeWsString() and sanitizeWsEnum() live in web/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 EnumType casts 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.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/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; no os.environ.get outside startup

No from __future__ import annotations (Python 3.14 has PEP 649). Use PEP 758 except except 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!

Comment thread scripts/generate_dto_types_ts.py
Comment thread web/src/api/types/agents.ts Outdated
Comment thread web/src/pages/org/useOrgChartDragDrop.ts Outdated
Comment thread web/src/pages/settings/sinks/SinkFormDrawer.tsx Outdated
Comment thread web/src/pages/tasks/TaskDetailPanel.tsx Outdated
Comment on lines +146 to +150
// 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 })
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 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.

Comment thread web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts Outdated
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview May 14, 2026 20:01 — with GitHub Actions Inactive
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between 69ceca1 and a646551.

📒 Files selected for processing (10)
  • scripts/generate_dto_types_ts.py
  • src/synthorg/api/openapi.py
  • web/src/api/types/agents.ts
  • web/src/api/types/openapi.gen.ts
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/pages/setup/MiniOrgChart.tsx
  • web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts
  • web/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 components

Export 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/useViewportSize for viewport-size reads; never read window.innerWidth / window.innerHeight directly inside a component render body or useMemo

Files:

  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/setup/MiniOrgChart.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts
  • web/src/stores/tasks.ts
  • web/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.tsx
  • src/synthorg/api/openapi.py
  • web/src/pages/setup/MiniOrgChart.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts
  • web/src/stores/tasks.ts
  • web/src/api/types/agents.ts
  • scripts/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.tsx
  • web/src/pages/setup/MiniOrgChart.tsx
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

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

Use variable name log for 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 sanitizeArg

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

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

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

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

ESLint 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-promises with checksVoidReturn.attributes=false (forbid async functions where callsite ignores promise, except React 19 async event handlers)

Files:

  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/setup/MiniOrgChart.tsx
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts
  • web/src/stores/tasks.ts
  • web/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 startup

No hardcoded numeric values; place numerics in settings/definitions/; whitelist 0/1/-1, HTTP codes, hex masks, powers-of-2, and module-level annotated named constants

Comments 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 handling

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

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

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

Ensure immutability using model_copy(update=...) or copy.deepcopy(); deepcopy at system boundaries

Use asyncio.TaskGroup for fan-out/fan-in async patterns; helper functions should catch Exception (re-raise MemoryError/RecursionError)

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

Wrap untrusted content with wrap_untrusted() from engine.prompt_safety; use HTMLParseGuard for HTML content (SEC-1)

Import logger via from synthorg.observability import get_logger; variable must be named logger. Never use import logging or print() in app code

Use 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 previous state synchronously in optimistic mutations and restore in catch block

Callers 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.py
  • scripts/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: Avoid Record<string, unknown>[] casts at the workflow creation boundary.

These casts bypass DTO-shape checking for nodes/edges exactly 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!

Comment thread scripts/generate_dto_types_ts.py Outdated
Comment thread web/src/stores/tasks.ts
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview May 14, 2026 20:38 — with GitHub Actions Inactive
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 14, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between 34fdaf6 and 72a2712.

📒 Files selected for processing (2)
  • scripts/generate_dto_types_ts.py
  • web/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 for except 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!

Comment thread scripts/generate_dto_types_ts.py Outdated
Comment thread scripts/generate_dto_types_ts.py
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview May 14, 2026 21:03 — with GitHub Actions Inactive
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
scripts/generate_dto_types_ts.py (1)

83-99: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make the export environment truly hermetic and restore it afterwards.

Lines 93-98 only apply defaults, so any pre-existing SYNTHORG_DB_PATH or SYNTHORG_PAGINATION_CURSOR_SECRET still changes the app boot. Then export_openapi_schema() leaves the mutated values in os.environ for 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

📥 Commits

Reviewing files that changed from the base of the PR and between 72a2712 and c295273.

📒 Files selected for processing (2)
  • scripts/generate_dto_types_ts.py
  • tests/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; no os.environ.get outside 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 by scripts/check_no_magic_numbers.py

Timeout/slow test failures indicate source-code regression; never edit tests/baselines/unit_timing.json or scripts/*_baseline.* files. Both are PreToolUse-blocked. Per-invocation bypass requires ALLOW_BASELINE_GROWTH=1 and 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.py

No from __future__ import annotations (Python 3.14 has PEP 649); use PEP 758 except for except A, B: without parens unless binding

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

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

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

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

Async patte...

Files:

  • tests/unit/scripts/test_generate_dto_types_ts.py
  • scripts/generate_dto_types_ts.py
tests/**/*.{py,pyi}

📄 CodeRabbit inference engine (CLAUDE.md)

Test markers: @pytest.mark.{unit,integration,e2e,slow}. Async auto. Timeout 30s global. Coverage 80% min. xdist -n 8 --dist=loadfile auto-applied via pyproject addopts

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

Test doubles: FakeClock for Clock seam, mock_of[T](**overrides) for typed-boundary substitutions, SimpleNamespace for attribute-bags. Bare MagicMock at typed boundary is blocked by scripts/check_mock_spec.py (zero-tolerance). Import from tests._shared; inject via clock= and helper subscript

Hypothesis: 10 deterministic CI examples; failures are real bugs (fix + add @example(...)). Never skip/xfail flaky tests; fix fundamentally. Use asyncio.Event().wait() not sleep(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.py
  • scripts/generate_dto_types_ts.py

@Aureliolo Aureliolo force-pushed the feat/pydantic-to-ts-codegen branch from c295273 to 1dfae9f Compare May 14, 2026 21:34
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview May 14, 2026 21:36 — with GitHub Actions Inactive
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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

Avoid implicit destructive default in model sync.

Defaulting to replace_existing: true changes behaviour for every caller that omits data, 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 win

Derive ProviderAuditEventType from the generated DTO instead of hardcoding it.

This is still a manual mirror of a wire field, so backend changes to ProviderAuditEvent.event_type can 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

📥 Commits

Reviewing files that changed from the base of the PR and between c295273 and 1dfae9f.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (150)
  • .github/workflows/ci.yml
  • .pre-commit-config.yaml
  • CLAUDE.md
  • data/runtime_stats.yaml
  • docs/reference/convention-gates.md
  • scripts/check_dto_types_ts_in_sync.py
  • scripts/convention_gate_map.yaml
  • scripts/generate_dto_types_ts.py
  • src/synthorg/api/openapi.py
  • tests/unit/scripts/fixtures/__init__.py
  • tests/unit/scripts/fixtures/dto_codegen_fixture_schema.py
  • tests/unit/scripts/test_check_dto_types_ts_in_sync.py
  • tests/unit/scripts/test_generate_dto_types_ts.py
  • web/CLAUDE.md
  • web/package.json
  • web/src/__tests__/_infra/active-handle-reporter.test.ts
  • web/src/__tests__/pages/BudgetForecastPage.test.tsx
  • web/src/__tests__/pages/BudgetPage.test.tsx
  • web/src/__tests__/pages/DashboardPage.test.tsx
  • web/src/__tests__/pages/ProviderDetailPage.test.tsx
  • web/src/__tests__/pages/approvals/ApprovalDetailDrawer.test.tsx
  • web/src/__tests__/pages/budget/ThresholdAlerts.test.tsx
  • web/src/__tests__/pages/settings/SourceBadge.test.tsx
  • web/src/__tests__/stores/agents.test.ts
  • web/src/__tests__/stores/analytics.test.ts
  • web/src/__tests__/stores/artifacts.test.ts
  • web/src/__tests__/stores/budget.test.ts
  • web/src/__tests__/stores/company.test.ts
  • web/src/__tests__/stores/connections.test.ts
  • web/src/__tests__/stores/projects.test.ts
  • web/src/__tests__/stores/setup-wizard.test.ts
  • web/src/__tests__/stores/tasks.test.ts
  • web/src/__tests__/stores/workflows.test.ts
  • web/src/__tests__/utils/budget.property.test.ts
  • web/src/__tests__/utils/budget.test.ts
  • web/src/__tests__/utils/dashboard.property.test.ts
  • web/src/__tests__/utils/dashboard.test.ts
  • web/src/__tests__/utils/tasks.property.test.ts
  • web/src/api/endpoints/activities.ts
  • web/src/api/endpoints/providers.ts
  • web/src/api/types/agents.ts
  • web/src/api/types/analytics.ts
  • web/src/api/types/approvals.ts
  • web/src/api/types/artifacts.ts
  • web/src/api/types/auth.ts
  • web/src/api/types/backup.ts
  • web/src/api/types/budget.ts
  • web/src/api/types/capabilities.ts
  • web/src/api/types/ceremony-policy.ts
  • web/src/api/types/collaboration.ts
  • web/src/api/types/coordination.ts
  • web/src/api/types/dtos.gen.ts
  • web/src/api/types/enum-values.gen.ts
  • web/src/api/types/enums.ts
  • web/src/api/types/errors.ts
  • web/src/api/types/escalations.ts
  • web/src/api/types/index.ts
  • web/src/api/types/integrations.ts
  • web/src/api/types/meetings.ts
  • web/src/api/types/messages.ts
  • web/src/api/types/openapi.gen.ts
  • web/src/api/types/org.ts
  • web/src/api/types/projects.ts
  • web/src/api/types/providers.ts
  • web/src/api/types/security.ts
  • web/src/api/types/settings.ts
  • web/src/api/types/setup.ts
  • web/src/api/types/system.ts
  • web/src/api/types/tasks.ts
  • web/src/api/types/templates.ts
  • web/src/api/types/workflows.ts
  • web/src/hooks/useArtifactsData.ts
  • web/src/hooks/useConnectionsData.ts
  • web/src/hooks/useProjectsData.ts
  • web/src/mocks/handlers/budget.ts
  • web/src/mocks/handlers/company.ts
  • web/src/mocks/handlers/connections.ts
  • web/src/mocks/handlers/escalations.ts
  • web/src/mocks/handlers/providers.ts
  • web/src/mocks/handlers/workflows.ts
  • web/src/pages/BudgetForecastPage.tsx
  • web/src/pages/EscalationQueuePage.stories.tsx
  • web/src/pages/EscalationQueuePage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/ProvidersPage.tsx
  • web/src/pages/WorkflowsPage.tsx
  • web/src/pages/artifacts/ArtifactCard.tsx
  • web/src/pages/artifacts/ArtifactContentPreview.tsx
  • web/src/pages/artifacts/ArtifactCreateDialog.tsx
  • web/src/pages/artifacts/ArtifactMetadata.tsx
  • web/src/pages/budget/BudgetPage.stories.tsx
  • web/src/pages/budget/SpendBurnChart.tsx
  • web/src/pages/budget/ThresholdAlerts.stories.tsx
  • web/src/pages/connections/ConnectionCard.stories.tsx
  • web/src/pages/connections/ConnectionCard.tsx
  • web/src/pages/connections/ConnectionFormModal.stories.tsx
  • web/src/pages/connections/ConnectionFormModal.tsx
  • web/src/pages/dashboard/BudgetBurnChart.tsx
  • web/src/pages/dashboard/DashboardPage.stories.tsx
  • web/src/pages/escalations/EscalationDetailDrawer.stories.tsx
  • web/src/pages/mcp-catalog/McpInstallWizard.stories.tsx
  • web/src/pages/meetings/MeetingActionItems.tsx
  • web/src/pages/meetings/MeetingCard.tsx
  • web/src/pages/meetings/MeetingDetailHeader.tsx
  • web/src/pages/meetings/MeetingTokenBreakdown.tsx
  • web/src/pages/oauth-apps/OauthAppCard.stories.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/org-edit/PackSelectionDialog.tsx
  • web/src/pages/org/build-org-tree.ts
  • web/src/pages/org/drop-target.ts
  • web/src/pages/org/useOrgChartDragDrop.ts
  • web/src/pages/projects/ProjectCard.tsx
  • web/src/pages/projects/ProjectCreateDrawer.tsx
  • web/src/pages/projects/ProjectHeader.tsx
  • web/src/pages/projects/ProjectTeamSection.tsx
  • web/src/pages/providers/AuditLogDrawer.tsx
  • web/src/pages/providers/ModelConfigDrawer.stories.tsx
  • web/src/pages/providers/PresetOverrideDrawer.tsx
  • web/src/pages/providers/ProviderFormModal.tsx
  • web/src/pages/providers/ProviderHealthMetrics.tsx
  • web/src/pages/providers/ProviderModelList.stories.tsx
  • web/src/pages/providers/TestConnectionResult.tsx
  • web/src/pages/settings/ceremony-policy/DepartmentOverridesPanel.tsx
  • web/src/pages/settings/sinks/SinkFormDrawer.tsx
  • web/src/pages/setup/MiniOrgChart.tsx
  • web/src/pages/setup/TemplateCard.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/pages/tasks/TaskDetailHeader.tsx
  • web/src/pages/tasks/TaskDetailMetadata.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/workflow-editor/VersionDiffViewer.tsx
  • web/src/pages/workflow-editor/node-config-schemas.ts
  • web/src/pages/workflow-editor/useWorkflowEditorCallbacks.ts
  • web/src/pages/workflows/WorkflowCard.tsx
  • web/src/pages/workflows/WorkflowCreateDrawer.tsx
  • web/src/pages/workflows/WorkflowTableView.tsx
  • web/src/stores/approvals.ts
  • web/src/stores/meetings.ts
  • web/src/stores/providers/crud-actions.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/src/stores/tasks.ts
  • web/src/stores/workflow-editor/clipboard.ts
  • web/src/stores/workflow-editor/persistence.ts
  • web/src/stores/workflow-editor/validation.ts
  • web/src/stores/workflow-editor/yaml.ts
  • web/src/utils/agents.ts
  • web/src/utils/meetings.ts
  • web/src/utils/messages.ts
  • web/src/utils/setup-validation.ts
  • web/test-infra/active-handle-tracker.ts

Comment thread web/src/__tests__/pages/ProviderDetailPage.test.tsx Outdated
Comment thread web/src/__tests__/utils/budget.property.test.ts Outdated
Comment thread web/src/__tests__/utils/budget.test.ts Outdated
Comment thread web/src/api/types/budget.ts
Comment thread web/src/api/types/ceremony-policy.ts Outdated
Comment thread web/src/pages/workflows/WorkflowCreateDrawer.tsx Outdated
Comment thread web/src/pages/workflows/WorkflowTableView.tsx Outdated
Comment thread web/src/stores/providers/crud-actions.ts Outdated
Comment thread web/src/stores/setup-wizard/providers.ts
Comment thread web/src/stores/workflow-editor/validation.ts Outdated
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview May 14, 2026 22:02 — with GitHub Actions Inactive
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
web/src/stores/setup-wizard/providers.ts (1)

115-122: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Conditionally include api_key only for auth_type: 'api_key'.

The shortcut now permits auth_type: 'none', but the request still always sends api_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

📥 Commits

Reviewing files that changed from the base of the PR and between 1dfae9f and 9991010.

📒 Files selected for processing (20)
  • web/src/__tests__/pages/ProviderDetailPage.test.tsx
  • web/src/__tests__/utils/budget.property.test.ts
  • web/src/__tests__/utils/budget.test.ts
  • web/src/api/endpoints/providers.ts
  • web/src/api/types/budget.ts
  • web/src/api/types/ceremony-policy.ts
  • web/src/api/types/providers.ts
  • web/src/mocks/handlers/providers.ts
  • web/src/pages/meetings/MeetingTokenBreakdown.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/providers/ModelConfigDrawer.stories.tsx
  • web/src/pages/providers/ProviderModelList.stories.tsx
  • web/src/pages/providers/TestConnectionResult.tsx
  • web/src/pages/tasks/TaskDetailHeader.tsx
  • web/src/pages/workflows/WorkflowCreateDrawer.tsx
  • web/src/pages/workflows/WorkflowTableView.tsx
  • web/src/stores/providers/crud-actions.ts
  • web/src/stores/providers/types.ts
  • web/src/stores/setup-wizard/providers.ts
  • web/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, per web/CLAUDE.md

Web Dashboard uses React 19; design system and diagrams detailed in web/CLAUDE.md

Files:

  • web/src/__tests__/pages/ProviderDetailPage.test.tsx
  • web/src/pages/workflows/WorkflowTableView.tsx
  • web/src/api/endpoints/providers.ts
  • web/src/pages/meetings/MeetingTokenBreakdown.tsx
  • web/src/stores/providers/crud-actions.ts
  • web/src/pages/providers/TestConnectionResult.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/stores/providers/types.ts
  • web/src/pages/tasks/TaskDetailHeader.tsx
  • web/src/pages/workflows/WorkflowCreateDrawer.tsx
  • web/src/stores/workflow-editor/validation.ts
  • web/src/pages/providers/ProviderModelList.stories.tsx
  • web/src/pages/providers/ModelConfigDrawer.stories.tsx
  • web/src/__tests__/utils/budget.property.test.ts
  • web/src/mocks/handlers/providers.ts
  • web/src/__tests__/utils/budget.test.ts
  • web/src/api/types/ceremony-policy.ts
  • web/src/api/types/budget.ts
  • web/src/api/types/providers.ts
  • web/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 nonce

Use Base UI's render prop as the polymorphism primitive throughout the dashboard; the local <Slot> helper in components/ui/slot.tsx uses @base-ui/react/merge-props to support the <Button asChild> ergonomic (only component that uses this helper; all other primitives use Base UI's native render prop directly)

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

Files:

  • web/src/__tests__/pages/ProviderDetailPage.test.tsx
  • web/src/pages/workflows/WorkflowTableView.tsx
  • web/src/pages/meetings/MeetingTokenBreakdown.tsx
  • web/src/pages/providers/TestConnectionResult.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/tasks/TaskDetailHeader.tsx
  • web/src/pages/workflows/WorkflowCreateDrawer.tsx
  • web/src/pages/providers/ProviderModelList.stories.tsx
  • web/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 sanitizeArg

Attacker-controlled fields inside structured objects must be wrapped in sanitizeForLog() before embedding in log calls

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

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

Generated 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.ts file. Regenerate with uv 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.tsx
  • web/src/pages/workflows/WorkflowTableView.tsx
  • web/src/api/endpoints/providers.ts
  • web/src/pages/meetings/MeetingTokenBreakdown.tsx
  • web/src/stores/providers/crud-actions.ts
  • web/src/pages/providers/TestConnectionResult.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/stores/providers/types.ts
  • web/src/pages/tasks/TaskDetailHeader.tsx
  • web/src/pages/workflows/WorkflowCreateDrawer.tsx
  • web/src/stores/workflow-editor/validation.ts
  • web/src/pages/providers/ProviderModelList.stories.tsx
  • web/src/pages/providers/ModelConfigDrawer.stories.tsx
  • web/src/__tests__/utils/budget.property.test.ts
  • web/src/mocks/handlers/providers.ts
  • web/src/__tests__/utils/budget.test.ts
  • web/src/api/types/ceremony-policy.ts
  • web/src/api/types/budget.ts
  • web/src/api/types/providers.ts
  • web/src/stores/setup-wizard/providers.ts
web/src/**/*.tsx

📄 CodeRabbit inference engine (web/CLAUDE.md)

Status dots should use <StatusBadge> (defaults to role="img" with aria-label; decorative for adjacent-labeled, announce for live WS updates), not hardcoded inline

KPI displays should use <MetricCard> / <Sparkline> / <ProgressGauge> / <TokenUsageBar>, not hardcoded inline

Cards should use <SectionCard> (titled wrapper with icon and action slot) and domain-specific cards like <AgentCard>, <DeptHealthBar>, not hardcoded inline

Form fields should use <InputField> / <SelectField> / <SliderField> / <ToggleField> / <SegmentedControl> / <TagInput> / <SearchInput>, not hardcoded inline

Slide-in panels should use <Drawer width="compact|narrow|default|wide"> (Base UI); do NOT add inline w-[40vw] overrides

Loading / empty / error states should use <Skeleton> family / <EmptyState> / <ErrorBoundary> / <ErrorBanner> / <ProgressIndicator>, not hardcoded inline

Breadcrumb 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 (default maxItems=4). <Breadcrumbs items={[]}> returns null so unconditional render at the top of a page is safe

Empty-state derivation must use useEmptyStateProps({ filteredCount, totalCount, filterActive, empty, filtered }) from @/hooks/use-empty-state-props which returns EmptyStateProps | null so the page branches on a single value

Status / role / risk / urgency badge classes must use STATUS_COLORS family from @/styles/status-colors (typed Record<EnumValue, string> lookups; no inline Record<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 inline

Animation must use <AnimatedPresence> / <StaggerGroup> / <LiveRegion> (de...

Files:

  • web/src/__tests__/pages/ProviderDetailPage.test.tsx
  • web/src/pages/workflows/WorkflowTableView.tsx
  • web/src/pages/meetings/MeetingTokenBreakdown.tsx
  • web/src/pages/providers/TestConnectionResult.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/tasks/TaskDetailHeader.tsx
  • web/src/pages/workflows/WorkflowCreateDrawer.tsx
  • web/src/pages/providers/ProviderModelList.stories.tsx
  • web/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 (useSyncExternalStore over window resize). Never read window.innerWidth / window.innerHeight directly inside a component render body or useMemo

ESLint (MANDATORY): use @eslint-react/eslint-plugin v5+ via the recommended-type-checked preset with explicit error-level opt-ins: @eslint-react/web-api-no-leaked-fetch, @eslint-react/no-leaked-conditional-rendering, @eslint-react/globals, @typescript-eslint/no-floating-promises, @typescript-eslint/no-misused-promises (with checksVoidReturn: { attributes: false })

Files:

  • web/src/__tests__/pages/ProviderDetailPage.test.tsx
  • web/src/pages/workflows/WorkflowTableView.tsx
  • web/src/api/endpoints/providers.ts
  • web/src/pages/meetings/MeetingTokenBreakdown.tsx
  • web/src/stores/providers/crud-actions.ts
  • web/src/pages/providers/TestConnectionResult.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/stores/providers/types.ts
  • web/src/pages/tasks/TaskDetailHeader.tsx
  • web/src/pages/workflows/WorkflowCreateDrawer.tsx
  • web/src/stores/workflow-editor/validation.ts
  • web/src/pages/providers/ProviderModelList.stories.tsx
  • web/src/pages/providers/ModelConfigDrawer.stories.tsx
  • web/src/__tests__/utils/budget.property.test.ts
  • web/src/mocks/handlers/providers.ts
  • web/src/__tests__/utils/budget.test.ts
  • web/src/api/types/ceremony-policy.ts
  • web/src/api/types/budget.ts
  • web/src/api/types/providers.ts
  • web/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 use space-y-section-gap; <ErrorBanner> lands immediately after <ListHeader>, before any filter / pagination row

Pages 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.tsx
  • web/src/pages/meetings/MeetingTokenBreakdown.tsx
  • web/src/pages/providers/TestConnectionResult.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/tasks/TaskDetailHeader.tsx
  • web/src/pages/workflows/WorkflowCreateDrawer.tsx
  • web/src/pages/providers/ProviderModelList.stories.tsx
  • web/src/pages/providers/ModelConfigDrawer.stories.tsx
web/**/*.ts

📄 CodeRabbit inference engine (web/CLAUDE.md)

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

Files:

  • web/src/api/endpoints/providers.ts
  • web/src/stores/providers/crud-actions.ts
  • web/src/stores/providers/types.ts
  • web/src/stores/workflow-editor/validation.ts
  • web/src/__tests__/utils/budget.property.test.ts
  • web/src/mocks/handlers/providers.ts
  • web/src/__tests__/utils/budget.test.ts
  • web/src/api/types/ceremony-policy.ts
  • web/src/api/types/budget.ts
  • web/src/api/types/providers.ts
  • web/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 afterEach in test-setup.tsx

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

Files:

  • web/src/api/endpoints/providers.ts
  • web/src/stores/providers/crud-actions.ts
  • web/src/stores/providers/types.ts
  • web/src/stores/workflow-editor/validation.ts
  • web/src/__tests__/utils/budget.property.test.ts
  • web/src/mocks/handlers/providers.ts
  • web/src/__tests__/utils/budget.test.ts
  • web/src/api/types/ceremony-policy.ts
  • web/src/api/types/budget.ts
  • web/src/api/types/providers.ts
  • web/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.ts pattern: wrap the API call in try / catch, success path updates state + emits a success toast, failure path logs + emits an error toast + returns a sentinel (null for entity returns, false for delete). Optimistic mutations capture previous synchronously and restore in catch

Files:

  • web/src/stores/providers/crud-actions.ts
web/src/stores/**/*.ts

📄 CodeRabbit inference engine (web/CLAUDE.md)

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

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

Files:

  • web/src/stores/providers/crud-actions.ts
  • web/src/stores/providers/types.ts
  • web/src/stores/workflow-editor/validation.ts
  • web/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's async_hooks and fails any test that leaks an event-loop-holding resource (Timeout, TCPWRAP, PIPEWRAP, FSEVENTWRAP, etc.) attributable to a web/src/ frame. Zero tolerance, no ceiling, no buffer

Files:

  • web/src/__tests__/utils/budget.property.test.ts
  • web/src/__tests__/utils/budget.test.ts
web/src/mocks/handlers/**/*.ts

📄 CodeRabbit inference engine (web/CLAUDE.md)

MSW handlers (MANDATORY): web/src/mocks/handlers/ mirrors web/src/api/endpoints/*.ts 1:1 with a default happy-path handler for every exported endpoint. test-setup.tsx boots with onUnhandledRequest: 'error'; tests override per-case via server.use(...), never vi.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

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 14, 2026
Aureliolo added 6 commits May 15, 2026 00:12
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).
Aureliolo added 17 commits May 15, 2026 00:12
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.
… 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.
@Aureliolo Aureliolo merged commit 0265ef5 into main May 14, 2026
64 of 65 checks passed
@Aureliolo Aureliolo deleted the feat/pydantic-to-ts-codegen branch May 14, 2026 22:29
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview May 14, 2026 22:29 — with GitHub Actions Inactive
Aureliolo pushed a commit that referenced this pull request May 17, 2026
<!-- HIGHLIGHTS_START -->
## Highlights

> _AI-generated summary (model: `openai/gpt-4.1-mini` via GitHub
Models). Commit-based changelog below._

### What you'll notice
- Frontend WP-6 update with UX polish improves user interface and
workflow.
- Dashboard and training endpoint improvements enhance observability and
dispatch behavior.
- Web storybook now supports change detection for more responsive UI
interactions.
- Git hooks now isolated per worktree for cleaner repository management.
- Providers automatically detect native streaming support in Litellm
models.

### What's new
- Added a new pipeline to convert Pydantic DTOs to TypeScript for better
front-end compatibility.

### Under the hood
- Refactored settings to three precedence categories, removing YAML tier
for simpler configuration.
- Completed RootConfig mirror coverage for enhanced configuration
consistency.
- Adopted API conventions with better query performance and forbidden
extra fields for stricter validation.
- Improved persistence, layer discipline, and restart safety in core
work packages.
- CI updated with split test jobs and tightened coverage gates for
better test quality.
- Switched to direct Trivy binary for security scans, removing previous
Trivy action dependency.
- Enhanced memory management with per-call processing options and better
observability during speech-to-text encoding.
- Various dependency updates for Python, infrastructure, and lock files
maintain security and stability.
- Removed TypeScript DTO type-tightening overlays to simplify type
management.
- Codebase audit tightened skill sets to prevent false positivity in
class detection by 2026.

<!-- HIGHLIGHTS_END -->

:robot: I have created a release *beep* *boop*
---


##
[0.8.5](v0.8.4...v0.8.5)
(2026-05-17)


### Features

* **codegen:** pydantic-to-typescript DTO pipeline + parity gate (closes
[#1889](#1889))
([#1909](#1909))
([0265ef5](0265ef5))
* **storybook:** enable changeDetection + trim web/CLAUDE.md
([#1939](#1939))
([3b1f4c0](3b1f4c0))
* **web,setup:** WP-6 frontend + UX polish
([#1941](#1941))
([d9ca76d](d9ca76d))


### Bug Fixes

* correct invalid git for-each-ref syntax in post-merge-cleanup skill
([#1946](#1946))
([69a1649](69a1649))
* dashboard polish, training endpoint dispatch, and observability
cleanup ([#1911](#1911))
([b61e9e8](b61e9e8))
* per-worktree git-hook isolation + hookify gate migration + MSW drift
fix ([#1949](#1949))
([e3f8495](e3f8495))
* **providers:** read supports_native_streaming from litellm model info
([#1942](#1942))
([60364ca](60364ca))
* security and audit coverage (closes
[#1883](#1883))
([#1904](#1904))
([d8ebf55](d8ebf55))


### Performance

* **ci:** mypy --num-workers=4 + enable ruff TID255
([#1944](#1944))
([484c1d3](484c1d3))


### Refactoring

* **ci:** drop aquasecurity/trivy-action, use direct trivy binary
([#1940](#1940))
([df1f946](df1f946))
* **memory:** per-call processing_kwargs + observability for ST encode
([#1943](#1943))
([3aa9d20](3aa9d20))
* Phase 7 follow-up — complete RootConfig mirror coverage (closes
[#1907](#1907))
([#1914](#1914))
([605500b](605500b))
* **settings:** collapse precedence to three categories; drop YAML tier
(closes [#1890](#1890))
([#1910](#1910))
([efd54c9](efd54c9))
* WP-3 API conventions + query performance + project-wide extra=forbid
([#1953](#1953))
([504d579](504d579)),
closes [#1918](#1918)
* WP-4 settings + cross-cutting (clock seam, contextvars, dispatch,
plugin surfaces)
([#1954](#1954))
([7207d92](7207d92))
* **wp1:** persistence + layer discipline + restart safety
([#1945](#1945))
([57586fb](57586fb))


### Documentation

* **wp5:** public-facing truth refresh
([#1924](#1924))
([afb5cc5](afb5cc5))


### CI/CD

* split test job by marker with airtight aggregate coverage gate
([#1948](#1948))
([0b818d5](0b818d5)),
closes [#1938](#1938)
[#1937](#1937)


### Maintenance

* **codebase-audit:** tighten skill to prevent 2026-05-15 FP classes
([#1923](#1923))
([9317ed1](9317ed1))
* Lock file maintenance
([#1913](#1913))
([c08a355](c08a355))
* Lock file maintenance
([#1950](#1950))
([8940ab1](8940ab1))
* remove TS DTO type-tightening overlays
([#1915](#1915))
([d296214](d296214)),
closes [#1906](#1906)
* Update Infrastructure dependencies
([#1928](#1928))
([d19fae5](d19fae5))
* Update Python dependencies
([#1929](#1929))
([75cc2c8](75cc2c8))
* **wp7:** hygiene, stubs, test/CI/tooling, doc gaps, boundary patterns
doc ([#1926](#1926))
([c29eb32](c29eb32))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Co-authored-by: synthorg-repo-bot[bot] <279117679+synthorg-repo-bot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Aureliolo added a commit that referenced this pull request May 28, 2026
- 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.
Aureliolo added a commit that referenced this pull request May 28, 2026
- 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.
Aureliolo added a commit that referenced this pull request May 28, 2026
- 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RFC: code-generate TypeScript DTOs from Pydantic. Decide AND directly implement

1 participant