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:
- Deleted blocks return after page refresh
- 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
- Test input fields appear to be read-only
- Values don't change when typing
- Changes don't persist
- Looks like mockup data
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:
- Angular only checks for changes when:
- Input signals change
- Events occur
- Observables emit
- Direct property mutation inside a signal does NOT trigger change detection
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)
- Purpose: Immediate, reactive UI state
- When Updated: Every user action (add, delete, move)
- Storage Key:
singularity:blocks:canvas
Saved Flows (GgStorage)
- Purpose: Persistent, named flows
- When Updated: Explicit saves + auto-save on critical changes
- Storage Key:
flow_{timestamp}
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:
- ✅ Delete block
- ✅ Add block
- ✅ Move block
- ✅ Edit block properties
- ✅ Add connection
- ✅ Delete connection
Why: Ensures changes persist across refreshes without requiring explicit "Save" clicks.
📚 Related Files
Modified Files
1. logic-builder.component.ts
- Method:
deleteBlock(blockId: string) - Change: Made async, added
await this.saveCurrentFlow() - Impact: Block deletions now persist across refreshes
2. flow-tester.component.ts
- Method:
updateInputValue(index, field, value)← NEW - Change: Added explicit signal update method
- Impact: Test inputs are now editable
3. flow-tester.component.html
- Change: Replaced
[(ngModel)]with[ngModel] + (ngModelChange) - Impact: Input changes trigger proper signal updates
✅ 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:
- Issue 1: Two storage layers not synced
- Issue 2: Signal state not properly updated
Both fixed with explicit, intentional state updates. ✅