refactor(convex): extract validators into separate files#37
Conversation
- Create dedicated validators.ts files for each model domain - Move Convex validators from types.ts to validators.ts files - Reduces code duplication and improves maintainability - Affected domains: approvals, conversations, customers, documents, email_providers, integrations, organizations, products, tone_of_voice, websites, wf_definitions, wf_executions, wf_step_defs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR systematically refactors the codebase to separate validator definitions from type definitions across multiple model modules. New Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
services/platform/convex/model/wf_executions/types.ts (1)
8-110: Document the manual types pattern and sync strategy.The manual TypeScript interfaces are maintained separately from runtime validators, creating a risk of drift. While this separation can provide better IDE support, it requires careful coordination.
Consider one of these approaches:
Document the pattern (quick fix): Add a comment explaining why types are manual and how to keep them synchronized with validators:
/** * MANUAL TYPES (these rely on Doc types, not validators) * * Note: These TypeScript interfaces are maintained separately from runtime * validators in ./validators.ts for better IDE support. When updating validators, * ensure corresponding type definitions are updated to match. */Use type inference (recommended): Import validators and derive types to ensure they stay in sync:
import type { Infer } from 'convex/values'; import { updateExecutionStatusArgsValidator, completeExecutionArgsValidator, // ... other validators } from './validators'; export type UpdateExecutionStatusArgs = Infer<typeof updateExecutionStatusArgsValidator>; export type CompleteExecutionArgs = Infer<typeof completeExecutionArgsValidator>;Based on learnings, approach #1 is acceptable for this codebase if properly documented.
services/platform/convex/model/integrations/types.ts (1)
73-73: Consider adding a comment explaining the Shopify API version.A brief comment noting when this version was chosen or a link to Shopify's versioning documentation would help future maintainers understand update implications.
- Use v.id('organizations') instead of v.string() for _id in organizationValidator
- Use workflowStatusValidator for status field in workflowUpdateValidator
- Use stepTypeValidator for stepType field in getStepsByTypeArgsValidator
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…alidators The conversation return validators in conversations.ts were missing the lastMessageAt field which is part of the schema (added for efficient sorting by last message time). This caused ReturnsValidationError when workflow actions returned conversation objects with this field populated. Affected validators: - getConversationById - getConversationByExternalMessageId - queryConversations - getConversation (RLS) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
ReactFlow's StoreUpdater component was detecting data changes on every render because callback functions were included in node.data and edge.data objects. Since function references change on each render, this triggered an infinite update loop. The fix: - Create AutomationCallbacksContext to pass callbacks via React context - Remove callback functions from node.data and edge.data objects - Use stable stepsKey for memoization instead of callback dependencies - Update AutomationStep to get callbacks from context 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update conversationPriorityValidator to use 'medium' instead of 'normal' to match frontend usage - Replace v.optional(v.string()) with v.optional(conversationPriorityValidator) in all conversation mutation/query definitions - Update interface types (CreateConversationArgs, UpdateConversationsArgs, QueryConversationsArgs) to use ConversationPriority type - Add 'urgent' to CONVERSATION_PRIORITY constant for consistency - Add ConversationPriority type to workflow action helpers - Update schema.ts comment to reflect correct priority values Closes issue identified in PR #37 review (CodeRabbit comment #2652223390)
- Update conversationPriorityValidator to use 'medium' instead of 'normal' to match frontend usage - Replace v.optional(v.string()) with v.optional(conversationPriorityValidator) in all conversation mutation/query definitions - Update interface types (CreateConversationArgs, UpdateConversationsArgs, QueryConversationsArgs) to use ConversationPriority type - Add 'urgent' to CONVERSATION_PRIORITY constant for consistency - Add ConversationPriority type to workflow action helpers - Update schema.ts comment to reflect correct priority values Closes issue identified in PR #37 review (CodeRabbit comment #2652223390)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
…— limits + analytics polish Closes round-5 findings #32, #33, #37, #38, #44. - `security:tts-audio-fetch` switches from fixed-window 120/min to token-bucket rate:120/min, capacity:240. A single ~200-chunk replay on a NAT/office IP no longer hits a minute-boundary 429 mid-playback; sustained traffic still settles at the same 120/min rate. - `gcOrgTtsChunks` cron moves from daily (`0 5 * * *`) to hourly (`0 * * * *`). Per-run caps are unchanged; a transient failure recovers in ~60 min instead of ~24 hours, and deployments with more orgs than `MAX_ORGS_PER_RUN` finish a full sweep within a day. - `getOrgUsageMetrics` topModels now excludes TTS rows. Mixing them with LLM rows on a single "Tokens" axis showed misleading "0 tokens" entries for TTS spend (TTS bills per character, transcription per minute, LLM per token). Mirrors transcription's existing exclusion. A kind-aware "Units" column is a follow-up. - `personalization-settings.tsx` capability-probe catch distinguishes transient ConvexError codes (RATE_LIMITED, CONTENTION) from genuine unavailability. On a transient error the page leaves `providerAvailable` as `null` (unknown) instead of flipping into the destructive "provider unavailable" banner. Helper isolated via the `isTransientProbeError` guard. - `fr.json` voice-output strings replace `touche` with `clique` for blocked / decode-error labels. `touche` reads as the noun "key (keyboard)" or the 2nd-person indicative of `toucher`; the rest of fr.json already uses `clique` consistently for UI click/tap actions.
…— limits + analytics polish Closes round-5 findings #32, #33, #37, #38, #44. - `security:tts-audio-fetch` switches from fixed-window 120/min to token-bucket rate:120/min, capacity:240. A single ~200-chunk replay on a NAT/office IP no longer hits a minute-boundary 429 mid-playback; sustained traffic still settles at the same 120/min rate. - `gcOrgTtsChunks` cron moves from daily (`0 5 * * *`) to hourly (`0 * * * *`). Per-run caps are unchanged; a transient failure recovers in ~60 min instead of ~24 hours, and deployments with more orgs than `MAX_ORGS_PER_RUN` finish a full sweep within a day. - `getOrgUsageMetrics` topModels now excludes TTS rows. Mixing them with LLM rows on a single "Tokens" axis showed misleading "0 tokens" entries for TTS spend (TTS bills per character, transcription per minute, LLM per token). Mirrors transcription's existing exclusion. A kind-aware "Units" column is a follow-up. - `personalization-settings.tsx` capability-probe catch distinguishes transient ConvexError codes (RATE_LIMITED, CONTENTION) from genuine unavailability. On a transient error the page leaves `providerAvailable` as `null` (unknown) instead of flipping into the destructive "provider unavailable" banner. Helper isolated via the `isTransientProbeError` guard. - `fr.json` voice-output strings replace `touche` with `clique` for blocked / decode-error labels. `touche` reads as the noun "key (keyboard)" or the 2nd-person indicative of `toucher`; the rest of fr.json already uses `clique` consistently for UI click/tap actions.
Summary
validators.tsfiles for each Convex model domaintypes.tsto separatevalidators.tsfilesAffected Domains
Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.