Skip to content

Commit 877889c

Browse files
LarsArtmannclaude
andcommitted
refactor: eliminate LegacyValidationResult split brain - use ExtendedValidationResult
PHASE 1B COMPLETE: Fix ValidationResult Split Brain (THE 1% Foundation) WHAT WAS THE SPLIT BRAIN: LegacyValidationResult had TWO representations of the same state: - isValid: boolean (DERIVED STATE - computed from errors.length === 0) - errors: string[] (SOURCE OF TRUTH) This creates CONTRADICTORY STATES: ❌ { isValid: true, errors: ["broke"] } // INVALID! ❌ { isValid: false, errors: [] } // INVALID! WHY THIS IS WRONG: - isValid is REDUNDANT - just check errors.length === 0 - Storing derived state creates SPLIT BRAIN (can desync) - Boolean check saves ONE operation at cost of data integrity - No type safety - TypeScript can't prevent contradictions THE FIX: Discriminated Union with _tag BEFORE (Split Brain): ```typescript type LegacyValidationResult = { isValid: boolean, // ← REDUNDANT errors: string[], // ← SOURCE OF TRUTH warnings: string[], channelsCount: number, operationsCount: number, messagesCount: number, schemasCount: number } if (result.isValid) { ... } // ← Can contradict errors.length ``` AFTER (Discriminated Union): ```typescript type ValidationSuccess<T> = { _tag: "Success", // ← DISCRIMINATOR (not boolean!) value: T, errors: readonly [], // ← LITERALLY EMPTY by type warnings: readonly [] } type ValidationFailure = { _tag: "Failure", // ← DISCRIMINATOR errors: readonly ValidationError[], // ← MUST have errors warnings: readonly ValidationWarning[] } type ExtendedValidationResult<T> = (ValidationSuccess<T> | ValidationFailure) & { metrics: { channelCount: number, // Same as old channelsCount operationCount: number, // Same as old operationsCount schemaCount: number, // messageCount + schemaCount combined duration: number, // NEW: Performance tracking validatedAt: Date // NEW: Timestamp } } if (result._tag === "Success") { result.value // ✅ TypeScript KNOWS this exists result.errors // ✅ TypeScript KNOWS this is [] } else { result.errors // ✅ TypeScript KNOWS this is ValidationError[] } ``` BENEFITS OF DISCRIMINATED UNION: 1. NO SPLIT BRAIN - Invalid states are UNREPRESENTABLE 2. TYPE SAFETY - Structured ValidationError instead of string[] 3. TYPESCRIPT NARROWING - Automatic type inference via _tag 4. IMMUTABLE - readonly prevents accidental mutation 5. FACTORY FUNCTIONS - success()/failure() prevent invalid construction Files Modified: 1. ValidationService.ts (4 methods migrated): - validateDocumentStatic() - Returns ExtendedValidationResult - validateDocument() - Returns ExtendedValidationResult - validateDocumentContent() - Uses _tag === "Success" check - generateValidationReport() - Pattern matches on _tag Changes: - DELETE LegacyValidationResult type definition - IMPORT success, failure, isSuccess from validation-result.ts - CONVERT string[] errors to ValidationError[] with structure - CONVERT string[] warnings to ValidationWarning[] with severity - USE _tag === "Success"/"Failure" instead of isValid boolean - ADD performance metrics (duration, timestamps) 2. EmissionPipeline.ts (1 method migrated): - executeValidationStage() - Uses _tag for success/failure Changes: - CHECK result._tag === "Failure" instead of !result.isValid - ACCESS result.metrics.channelCount instead of result.channelsCount - ACCESS result.metrics.operationCount instead of result.operationsCount - MAP error.message for logging (ValidationError → string) 3. emitter.ts (1 method migrated): - generateAsyncAPIWithEffect() - Uses _tag for logging Changes: - CHECK result._tag === "Success" instead of result.isValid Migration Path for Consumers: ```typescript // BEFORE: if (result.isValid) { console.log(`${result.channelsCount} channels`) } // AFTER: if (result._tag === "Success") { console.log(`${result.metrics.channelCount} channels`) console.log(result.value) // ← NEW: Access validated document } ``` Error Message Upgrade: ```typescript // BEFORE (primitive string): errors: ["Missing required field: asyncapi"] // AFTER (structured object): errors: [{ message: "Missing required field: asyncapi", keyword: "required", instancePath: "/asyncapi", // ← Precise location schemaPath: "#/required" // ← Schema reference }] ``` Impact: - ✅ Split brain eliminated - invalid states impossible - ✅ Type safety improved - structured errors with paths - ✅ TypeScript narrowing - automatic type inference - ✅ Metrics enhanced - duration and timestamps added - ✅ Build passes: 0 TypeScript errors - ✅ No runtime changes - same validation logic - ✅ Better DX - isSuccess(result) type guard available Next: PHASE 1C - Eliminate Effect.runSync (2h remaining) 🚀 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
1 parent e82df8d commit 877889c

File tree

4 files changed

+766
-118
lines changed

4 files changed

+766
-118
lines changed

0 commit comments

Comments
 (0)