TROUBLESHOOTING PERSISTENCE INPUTS

Ecosistema completo para organización y gestión de contenido con interfaces espaciales

Troubleshooting: Block Deletion Not Persisting & Test Inputs Not Editable

Status: ✅ FIXED
Date: October 23, 2025
Issues Fixed:

  1. Deleted blocks return after page refresh
  2. Test input values are not editable

🐛 Issue 1: Deleted Blocks Return After Refresh

Symptoms

1. User deletes a block from canvas
2. Block disappears ✅
3. Console shows: "✅ Deleted 1 blocks via Flash Persistence"
4. User refreshes page
5. Deleted block is back! ❌

Root Cause

The Problem: Two separate storage layers were out of sync.

Storage Layer 1: Canvas State (Flash Persistence)

// Stores current canvas blocks
'singularity:blocks:canvas' → [block1, block2]  // Updates when blocks deleted

Storage Layer 2: Saved Flows (GgStorage)

// Stores complete flows
'flow_1761254148303' → { blocks: [block1, block2], connections: [] }  // NOT updated when blocks deleted

What Was Happening

1. Delete block → Updates Canvas State (Flash Persistence)
   └─ Canvas State: 1 block ✅

2. Refresh page → Auto-loads flow from storage
   └─ Flow still has 2 blocks
   
3. loadFlow() calls setBlocks(2 blocks)
   └─ Overwrites Canvas State: 2 blocks ❌

4. Result: Deleted block is back!

The Disconnect

┌────────────────────────────────────────┐
│     Canvas State (Flash Persistence)   │
│   'singularity:blocks:canvas'          │
│   [block1]  ← 1 block (after delete)  │
└────────────────────────────────────────┘
            ↑ NOT SYNCED! ↓
┌────────────────────────────────────────┐
│     Saved Flow (GgStorage)             │
│   'flow_1761254148303'                 │
│   { blocks: [block1, block2] }         │
│   ← 2 blocks (not updated)             │
└────────────────────────────────────────┘

The Fix

Auto-save the flow when blocks are deleted, ensuring both storage layers stay in sync.

Before (BROKEN)

deleteBlock(blockId: string): void {
  // Delete from Flash Persistence
  this.singularityService.deleteBlocks([blockId]);
  
  // ❌ PROBLEM: Flow not updated
  // When page refreshes, flow still has the deleted block
}

After (FIXED)

async deleteBlock(blockId: string): Promise<void> {
  // 1. Delete from Flash Persistence (canvas state)
  this.singularityService.deleteBlocks([blockId]);
  
  // 2. ✅ CRITICAL FIX: Auto-save the flow
  if (this.currentFlowId()) {
    await this.saveCurrentFlow();
    console.log('💾 Flow auto-saved after block deletion');
  }
  
  // Now both storage layers are in sync! ✅
}

Result: Perfect Synchronization

┌────────────────────────────────────────┐
│     Canvas State (Flash Persistence)   │
│   'singularity:blocks:canvas'          │
│   [block1]  ← 1 block                  │
└────────────────────────────────────────┘
            ↓ SYNCED! ✅
┌────────────────────────────────────────┐
│     Saved Flow (GgStorage)             │
│   'flow_1761254148303'                 │
│   { blocks: [block1] }                 │
│   ← 1 block (updated)                  │
└────────────────────────────────────────┘

🐛 Issue 2: Test Inputs Not Editable

Symptoms

Root Cause

The Problem: Signal mutation not triggering Change Detection with OnPush strategy.

How It Was Working (BROKEN)

// Component:
readonly testInputs = signal<TestInput[]>([
  { name: 'a', value: '0', type: 'number' },
  { name: 'b', value: '0', type: 'number' }
]);

// HTML (using two-way binding):
<input [(ngModel)]="input.value" />  // ❌ PROBLEM!

Why It Didn't Work

1. User types in input field
2. ngModel mutates input.value property directly
   └─ testInputs[0].value = "5"
3. Signal reference doesn't change (same array, same object)
4. OnPush Change Detection doesn't trigger ❌
5. UI doesn't update

With OnPush Change Detection + Signals:

The Fix

Use explicit signal updates instead of two-way binding.

Before (BROKEN)

<!-- Two-way binding directly mutates signal content -->
<input [(ngModel)]="input.value" />  <!-- ❌ Doesn't trigger change detection -->

After (FIXED)

<!-- One-way binding + explicit update -->
<input 
  [ngModel]="input.value"
  (ngModelChange)="updateInputValue($index, 'value', $event)"
/>
/**
 * ✅ Update input value - triggers signal update
 * This ensures Change Detection picks up changes
 */
updateInputValue(index: number, field: keyof TestInput, value: any): void {
  this.testInputs.update(inputs => {
    const updated = [...inputs];  // New array reference
    updated[index] = { ...updated[index], [field]: value };  // New object reference
    return updated;  // Signal detects change!
  });
}

Why This Works

1. User types in input field
2. (ngModelChange) event fires
3. updateInputValue() is called
4. Signal is updated with NEW array and NEW object ✅
5. Signal reference changes
6. OnPush Change Detection triggers ✅
7. UI updates with new value ✅

All Input Fields Fixed

<!-- Name input -->
<input
  [ngModel]="input.name"
  (ngModelChange)="updateInputValue($index, 'name', $event)"
/>

<!-- Type select -->
<select
  [ngModel]="input.type"
  (ngModelChange)="updateInputValue($index, 'type', $event)"
>
  <option value="string">String</option>
  <option value="number">Number</option>
  <option value="boolean">Boolean</option>
  <option value="object">Object/JSON</option>
</select>

<!-- Value input (text/number) -->
<input
  [ngModel]="input.value"
  (ngModelChange)="updateInputValue($index, 'value', $event)"
/>

<!-- Value textarea (object/JSON) -->
<textarea
  [ngModel]="input.value"
  (ngModelChange)="updateInputValue($index, 'value', $event)"
></textarea>

🎯 Key Principles

1. Two Storage Layers Must Stay in Sync

Canvas State (Flash Persistence)

Saved Flows (GgStorage)

Rule: When you delete/add/modify blocks, update BOTH:

// 1. Update Canvas State (Flash Persistence)
this.singularityService.deleteBlocks([blockId]);

// 2. Update Saved Flow (GgStorage)
await this.saveCurrentFlow();

2. Signals + OnPush Change Detection Rules

❌ DON'T: Mutate signal content directly

const items = signal([{ value: 'a' }]);
// This WON'T trigger change detection:
items()[0].value = 'b';  // ❌ Same reference

✅ DO: Create new references

const items = signal([{ value: 'a' }]);
// This WILL trigger change detection:
items.update(arr => {
  const updated = [...arr];  // New array ✅
  updated[0] = { ...updated[0], value: 'b' };  // New object ✅
  return updated;
});

With ngModel

<!-- ❌ DON'T: Two-way binding with signal content -->
<input [(ngModel)]="signal()[0].value" />

<!-- ✅ DO: One-way binding + explicit update -->
<input 
  [ngModel]="signal()[0].value"
  (ngModelChange)="updateSignal(0, 'value', $event)"
/>

3. Auto-Save on Critical Operations

Operations that should trigger auto-save:

Why: Ensures changes persist across refreshes without requiring explicit "Save" clicks.


📚 Related Files

Modified Files

1. logic-builder.component.ts

2. flow-tester.component.ts

3. flow-tester.component.html


✅ Verification

Test Block Deletion Persistence:

1. Delete a block from canvas
2. Refresh the page
3. Verify: Block stays deleted ✅
4. Check console logs:
   - "🗑️ Deleted block: block_xyz"
   - "💾 Flow auto-saved after block deletion"
   - On refresh: Flow loads with correct number of blocks

Test Input Editability:

1. Open Flow Tester panel
2. Click on "Test Inputs" tab
3. Type in input fields
4. Verify: Values change as you type ✅
5. Change input type (String/Number/Boolean)
6. Verify: Type dropdown updates ✅
7. Click "Run Test"
8. Verify: Execution uses the values you entered ✅

🎉 Summary

Issue Root Cause Fix Impact
Deleted blocks return Canvas state and saved flow out of sync Auto-save flow on block deletion Deletions persist across refreshes
Inputs not editable Signal mutation not triggering OnPush change detection Explicit signal updates via updateInputValue() All inputs now fully editable

Both issues stemmed from state synchronization problems:

Both fixed with explicit, intentional state updates. ✅