UNPKG

meld

Version:

Meld: A template language for LLM prompts

597 lines (496 loc) 18 kB
# StateService Audit Notes ## Core Responsibilities (from ARCHITECTURE.md and PIPELINE.md) 1. Maintain both original and transformed node trees 2. Handle state inheritance correctly 3. Support immutable state operations 4. Manage transformation state through cloning/merging ## Critical State Operations ### 1. Node Management - Original nodes array - Transformed nodes array (optional) - Node addition/removal - Content appending - Transformation tracking ### 2. State Lifecycle ```typescript // Key operations that must preserve transformation state: createChildState() mergeChildState() clone() ``` ### 3. Transformation State - Service level: `_transformationEnabled` flag - State level: `transformedNodes` array - Factory level: Transformation state copying ## StateFactory Implementation Findings ### 1. State Creation ```typescript createState(options?: StateNodeOptions): StateNode { // Creates new maps from parent if available const state: StateNode = { variables: { text: new Map(options?.parentState?.variables.text ?? []), data: new Map(options?.parentState?.variables.data ?? []), path: new Map(options?.parentState?.variables.path ?? []) }, commands: new Map(options?.parentState?.commands ?? []), imports: new Set(options?.parentState?.imports ?? []), nodes: [...(options?.parentState?.nodes ?? [])], transformedNodes: options?.parentState?.transformedNodes ? [...options.parentState.transformedNodes] : undefined, filePath: options?.filePath ?? options?.parentState?.filePath, parentState: options?.parentState }; // ... } ``` Key Observations: - All maps and collections are properly cloned - Parent state is preserved in new state - Transformation nodes are conditionally copied - Immutability is maintained through new Map/Set creation ### 2. State Merging ```typescript mergeStates(parent: StateNode, child: StateNode): StateNode { // Creates new maps with parent as base const text = new Map(parent.variables.text); const data = new Map(parent.variables.data); const path = new Map(parent.variables.path); const commands = new Map(parent.commands); // Child values override parent values for (const [key, value] of child.variables.text) { text.set(key, value); } // ... similar for other maps ... // Nodes are appended, not merged nodes: [...parent.nodes, ...child.nodes], // Transformed nodes from child take precedence transformedNodes: child.transformedNodes !== undefined ? [...child.transformedNodes] : parent.transformedNodes !== undefined ? [...parent.transformedNodes] : undefined, } ``` Key Observations: - Child state takes precedence over parent - Collections are properly cloned - Nodes are appended rather than replaced - Transformed nodes follow child-first precedence ## Potential Issues Found ### 1. State Inheritance Chain Verified Behavior: - Parent state is properly referenced in child states - Maps and collections are properly cloned during inheritance - Child states can override parent values - Transformation state is preserved in child creation Potential Issues: - No validation of parent/child relationship consistency - No checks for circular parent references - Transformation flags not explicitly copied in child creation - No validation of transformed nodes matching original nodes ### 2. State Cloning Depth Current clone implementation has proper deep cloning: - Creates new state without parent reference - Properly clones all collections - Preserves transformation state - Copies service-level flags ### 3. State Factory Operations Verified operations: ```typescript createState() - ✓ Proper cloning, parent handling createChildState() - ✓ Inherits parent state correctly mergeStates() - ✓ Child precedence, proper cloning updateState() - ✓ Immutable updates, proper state copying ``` ## Investigation Plan 1. **State Creation Flow** - ✓ Transformation state initialization verified - ✓ Parent state handling in factory verified - ⚠️ Need to verify transformation flag inheritance 2. **State Modification** - ✓ Methods properly clone state - ✓ Immutability maintained - ⚠️ Need to verify transformation consistency across operations 3. **State Inheritance** - ✓ Parent/child relationship mapped - ⚠️ Need to verify transformation state in complex scenarios - ⚠️ Need to test circular reference handling ## Next Steps 1. Create test cases for: - Circular parent references - Deep transformation state inheritance - Complex state merging scenarios - Transformation flag consistency 2. Add validation for: - Parent/child relationship consistency - Transformed node validity - Circular reference detection 3. Document transformation state lifecycle: - When transformation flags should be inherited - How transformed nodes should be validated - Best practices for state merging ## Questions to Answer 1. **State Initialization** - When should transformation be enabled? - Should child states inherit transformation settings? - How should transformed nodes be initialized? 2. **State Operations** - What's the correct order for copying state? - How should transformed nodes be merged? - When should transformation state be preserved? 3. **Factory Behavior** - Should factory be transformation-aware? - How should it handle parent state? - What's the correct state copying depth? ## Test Coverage Needed 1. **State Inheritance** - Parent/child transformation inheritance - Transformation state in merged states - Cloned state consistency 2. **State Operations** - Node transformation tracking - State immutability with transformations - Error handling in transformation operations 3. **Edge Cases** - Partial transformations - Nested state inheritance - Complex state merging ## Test Coverage Analysis ### Existing Test Coverage 1. Basic State Operations - ✓ State creation and initialization - ✓ Variable management (text, data, path) - ✓ Command handling - ✓ Import management - ✓ Node operations 2. Transformation State - ✓ Basic transformation enabling/disabling - ✓ Node transformation tracking - ✓ Transformation state preservation in cloning - ✓ Immutability checks with transformations 3. State Inheritance - ✓ Basic parent/child state creation - ✓ Simple state merging - ✓ Variable inheritance and overriding ### Test Coverage Gaps 1. Complex Transformation Scenarios ```typescript // Need tests for: - Nested child states with transformations enabled/disabled at different levels - Merging states with conflicting transformed nodes - Transformation state preservation across multiple inheritance levels ``` 2. Edge Cases ```typescript // Missing tests for: - Circular parent references - Invalid state merges - Incomplete or corrupted state ``` 3. State Validation ```typescript // Need validation tests for: - Node array consistency - Parent/child relationship integrity - Transformation state validity ``` ### Required Test Cases 1. **Transformation Inheritance** ```typescript it('should handle transformation flags in nested states', () => { const parent = new StateService(); parent.enableTransformation(true); const child = parent.createChildState(); const grandchild = child.createChildState(); // Verify transformation state inheritance expect(child.isTransformationEnabled()).toBe(true); expect(grandchild.isTransformationEnabled()).toBe(true); }); it('should merge transformed nodes correctly in complex hierarchies', () => { const parent = new StateService(); const child1 = parent.createChildState(); const child2 = parent.createChildState(); // Setup different transformation states parent.enableTransformation(true); child1.enableTransformation(true); child2.enableTransformation(false); // Verify correct merging behavior }); ``` 2. **State Validation** ```typescript it('should detect circular parent references', () => { const parent = new StateService(); const child = parent.createChildState(); // Attempt to create circular reference expect(() => parent.mergeChildState(child)) .toThrow('Circular parent reference detected'); }); it('should validate transformed nodes match originals', () => { const service = new StateService(); service.enableTransformation(true); // Add original node const original = createTestNode('original'); service.addNode(original); // Attempt invalid transformation const invalid = createTestNode('invalid'); expect(() => service.transformNode(invalid, invalid)) .toThrow('Cannot transform node: original node not found'); }); ``` 3. **Complex State Operations** ```typescript it('should handle deep cloning with transformations', () => { const original = new StateService(); original.enableTransformation(true); // Setup complex state const node1 = createTestNode('node1'); const node2 = createTestNode('node2'); original.addNode(node1); original.addNode(node2); // Transform nodes const transformed1 = createTestNode('transformed1'); original.transformNode(node1, transformed1); // Clone and verify const cloned = original.clone(); expect(cloned.getTransformedNodes()).toEqual(original.getTransformedNodes()); expect(cloned.isTransformationEnabled()).toBe(true); }); it('should preserve transformation state in complex merges', () => { const parent = new StateService(); const child1 = parent.createChildState(); const child2 = parent.createChildState(); // Setup different states parent.enableTransformation(true); child1.enableTransformation(true); child2.enableTransformation(false); // Add and transform nodes const parentNode = createTestNode('parent'); const child1Node = createTestNode('child1'); const child2Node = createTestNode('child2'); parent.addNode(parentNode); child1.addNode(child1Node); child2.addNode(child2Node); // Transform some nodes const transformed = createTestNode('transformed'); child1.transformNode(child1Node, transformed); // Merge and verify parent.mergeChildState(child1); parent.mergeChildState(child2); // Verify correct node ordering and transformation state }); ``` ### Implementation Recommendations 1. Add Validation Layer ```typescript class StateValidator { static validateParentReference(state: StateNode): void { // Check for circular references let current = state; const visited = new Set<StateNode>(); while (current.parentState) { if (visited.has(current.parentState)) { throw new Error('Circular parent reference detected'); } visited.add(current); current = current.parentState; } } static validateTransformedNodes(state: StateNode): void { if (!state.transformedNodes) return; if (state.transformedNodes.length !== state.nodes.length) { throw new Error('Transformed nodes array length mismatch'); } // Add additional validation as needed } } ``` 2. Enhance State Factory ```typescript class StateFactory { createState(options?: StateNodeOptions): StateNode { const state = // ... existing creation code ... // Add validation StateValidator.validateParentReference(state); StateValidator.validateTransformedNodes(state); return state; } } ``` 3. Improve Error Handling ```typescript class StateService { mergeChildState(childState: IStateService): void { this.checkMutable(); const child = childState as StateService; try { // Validate before merge StateValidator.validateParentReference(child.currentState); StateValidator.validateTransformedNodes(child.currentState); this.currentState = this.stateFactory.mergeStates( this.currentState, child.currentState ); } catch (error) { logger.error('State merge failed', { error }); throw new Error(`Invalid state merge: ${error.message}`); } } } ``` ## Next Steps 1. Implement test cases in priority order: - Transformation inheritance tests - State validation tests - Complex merge tests 2. Add validation layer: - Create StateValidator class - Add validation to factory operations - Enhance error handling 3. Document best practices: - When to enable/disable transformations - How to handle complex state merges - Validation requirements ## Mock Implementation Analysis ### 1. Mock Service Variants 1. Legacy InterpreterState (`tests/mocks/state.ts`): ```typescript export class InterpreterState { private nodes: MeldNode[] = []; private textVars: Map<string, string> = new Map(); private dataVars: Map<string, any> = new Map(); private commands: Map<string, string> = new Map(); private imports: Set<string> = new Set(); // Missing transformation state completely } ``` Issues: - No transformation support - Doesn't implement full IStateService interface - Used in older tests that may need updating 2. Factory Mock (`testFactories.ts`): ```typescript export function createMockStateService(): IStateService { const mockService = { // Has all interface methods but behavior doesn't match real service getTransformedNodes: vi.fn(), setTransformedNodes: vi.fn(), transformNode: vi.fn(), isTransformationEnabled: vi.fn(), enableTransformation: vi.fn(), // ... }; // Default implementations don't match real service behavior mockService.isTransformationEnabled.mockImplementation(() => false); mockService.getTransformedNodes.mockImplementation(() => []); } ``` Issues: - Transformation state not properly preserved - Clone operation doesn't copy all state - Child state creation doesn't inherit parent state 3. Real Service in TestContext: ```typescript // In TestContext.ts const state = new StateService(); state.setCurrentFilePath('test.meld'); state.enableTransformation(true); // Different default than mock ``` Issues: - Inconsistent with mock defaults - May hide problems in tests using mocks ### 2. Behavioral Mismatches 1. State Inheritance: ```typescript // Real Service createChildState(): IStateService { const child = new StateService(this); // Inherits transformation state and nodes return child; } // Mock Service createChildState: vi.fn().mockImplementation(() => createMockStateService()) // Creates fresh mock without inheritance ``` 2. Transformation State: ```typescript // Real Service enableTransformation(enable: boolean): void { if (enable) { this.updateState({ transformedNodes: [...this.currentState.nodes] }, 'enableTransformation'); } this._transformationEnabled = enable; } // Mock Service enableTransformation: vi.fn() // No state initialization or preservation ``` 3. State Cloning: ```typescript // Real Service - Full state preservation clone(): IStateService { const cloned = new StateService(); cloned.currentState = this.stateFactory.createState({...}); cloned._transformationEnabled = this._transformationEnabled; return cloned; } // Mock Service - Partial state copying clone: vi.fn().mockImplementation(() => { const newMock = createMockStateService(); newMock.getNodes.mockImplementation(() => [...mockService.getNodes()]); return newMock; }) ``` ### 3. Test Impact Analysis 1. Affected Test Types: - Unit tests using legacy InterpreterState - Integration tests mixing real and mock services - API tests with transformation expectations - Output service tests requiring transformed nodes 2. Failure Patterns: - Missing transformation state in child states - Inconsistent node arrays after cloning - Lost state during service interactions - Transformation flags not preserved 3. Risk Areas: - Directive processing with transformation - State inheritance chains - Complex state merging operations - Cross-service interactions ### 4. Required Alignments 1. Interface Compliance: ```typescript interface IStateService { // All mocks must implement these transformation methods getTransformedNodes(): MeldNode[]; setTransformedNodes(nodes: MeldNode[]): void; transformNode(original: MeldNode, transformed: MeldNode): void; isTransformationEnabled(): boolean; enableTransformation(enable: boolean): void; } ``` 2. Behavioral Consistency: - State inheritance in child states - Transformation state preservation - Node array management - Cloning and merging operations 3. Default Settings: - Transformation enabled/disabled state - Initial node arrays - Parent/child relationships - State immutability ### 5. Investigation Needed 1. Test Coverage: - Which tests use which mock variants? - Are transformation tests comprehensive? - Do integration tests verify state preservation? 2. State Lifecycle: - How is transformation state propagated? - When should state be preserved vs reset? - What are the valid state transitions? 3. Mock Migration: - Can we deprecate legacy mocks? - How to update affected tests? - What's the migration timeline?