fix: comprehensive audit fixes for dashboard and backend

Backend:
- Emit WebSocket events from REST delete/promote/demote handlers
- Emit DreamStarted/ConsolidationStarted from MCP tool dispatch
- Add path validation in backup_to() for defense-in-depth

Dashboard:
- Fix ConnectionDiscovered field names (source_id/target_id)
- Fix $effect → onMount in settings (prevents infinite loop)
- Fix $derived → $derived.by in RetentionCurve
- Fix field name mismatches in settings (nodesProcessed, etc.)
- Fix nested <button> → <span role="button"> in memories
- Fix unhandled Promise rejection in stats consolidation
- Add missing EVENT_TYPE_COLORS entries
- Add Three.js resource disposal and event listener cleanup
- Eliminate duplicate root page, redirect to /graph
- Update nav links and keyboard shortcuts to /graph

All 734+ tests passing, 22MB binary, zero build warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sam Valladares 2026-02-22 15:50:47 -06:00
parent 22831af509
commit ec2af6e71b
220 changed files with 347 additions and 443 deletions

View file

@ -69,6 +69,19 @@
onDestroy(() => {
cancelAnimationFrame(animationId);
window.removeEventListener('resize', onResize);
container?.removeEventListener('pointermove', onPointerMove);
container?.removeEventListener('click', onClick);
// Dispose Three.js resources to prevent GPU memory leaks
scene?.traverse((obj: THREE.Object3D) => {
if (obj instanceof THREE.Mesh || obj instanceof THREE.InstancedMesh) {
obj.geometry?.dispose();
if (Array.isArray(obj.material)) {
obj.material.forEach((m: THREE.Material) => m.dispose());
} else if (obj.material) {
(obj.material as THREE.Material).dispose();
}
}
});
renderer?.dispose();
composer?.dispose();
});
@ -554,9 +567,9 @@
break;
}
case 'ConnectionDiscovered': {
const data = event.data as { source?: string; target?: string };
const srcPos = data.source ? nodePositions.get(data.source) : null;
const tgtPos = data.target ? nodePositions.get(data.target) : null;
const data = event.data as { source_id?: string; target_id?: string };
const srcPos = data.source_id ? nodePositions.get(data.source_id) : null;
const tgtPos = data.target_id ? nodePositions.get(data.target_id) : null;
if (srcPos && tgtPos) {
createConnectionFlash(srcPos, tgtPos, new THREE.Color(0xf59e0b));
}