# Dev Debug Canvas Nested Groups

> This skill should be used when debugging Vue Flow canvas issues involving nested nodes, parent-child relationships, position coordinate systems, and containment detection. Triggers on nested group problems, section nodes jumping, parent-child synchronization bugs, position persistence issues, and coordinate transformation errors in Vue Flow implementations.

- Skill: `majiayu000/dev-debug-canvas-nested-groups` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds add majiayu000/dev-debug-canvas-nested-groups`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/dev-debug-canvas-nested-groups/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/dev-debug-canvas-nested-groups

---


# Vue Flow Nested Nodes Debugging Skill

## Overview

Debug Vue Flow nested nodes, parent-child relationships, position coordinate systems, and containment detection issues. This skill provides systematic diagnostic procedures, code patterns, and utilities for troubleshooting complex canvas hierarchy problems.

## When to Use This Skill

Use this skill when encountering:
- Nested groups not moving together when parent is dragged
- Section nodes jumping to unexpected positions
- Parent-child relationships not being recognized
- Tasks inside groups not being detected correctly
- Position values appearing incorrect after operations
- Groups resetting or losing their nested structure
- Coordinate mismatches between UI and stored data

## Quick Diagnostic

Before deep debugging, run these quick checks:

```typescript
// 1. Check if parentNode is properly set
const node = getNode('task-123')
console.log('Parent:', node?.parentNode) // Should be group ID or undefined

// 2. Verify coordinate types
console.log('Position (relative):', node?.position)
console.log('ComputedPosition (absolute):', node?.computedPosition)

// 3. Check extent mode
console.log('Extent:', node?.extent) // 'parent' for constrained children
```

## Core Concept: Coordinate Systems

**CRITICAL**: Vue Flow uses TWO coordinate systems that must not be confused:

| Property | Type | When Used |
|----------|------|-----------|
| `node.position` | Relative to parent | Child nodes with `parentNode` set |
| `node.position` | Absolute canvas coords | Root nodes (no parent) |
| `node.computedPosition` | Always absolute | Use for canvas operations |

**Golden Rule**: Store relative positions for children, use `computedPosition` for calculations.

## Debugging Workflow

### Step 1: Identify the Problem Type

Consult the troubleshooting tree in `references/troubleshooting-tree.md`:

- **Position Issues**: Node jumping, incorrect placement, reset positions
- **Hierarchy Issues**: Parent-child not recognized, nesting lost
- **Movement Issues**: Children not moving with parent, group drag problems
- **Containment Issues**: Tasks not detected inside groups

### Step 2: Enable Debug Logging

Add targeted logging based on problem type:

```typescript
// For position debugging
watch(() => node.position, (newPos, oldPos) => {
  console.log(`[POSITION] ${node.id}: ${JSON.stringify(oldPos)} → ${JSON.stringify(newPos)}`)
}, { deep: true })

// For hierarchy debugging
watch(() => node.parentNode, (newParent, oldParent) => {
  console.log(`[HIERARCHY] ${node.id}: parent ${oldParent} → ${newParent}`)
})
```

### Step 3: Use Diagnostic Utilities

Load the debugging utilities from `scripts/vueFlowDebug.ts`:

```typescript
import { logNodeHierarchy, validateParentChild, dumpCanvasState } from './vueFlowDebug'

// Dump full canvas state
dumpCanvasState(getNodes.value, getEdges.value)

// Validate all parent-child relationships
validateParentChild(getNodes.value)

// Log hierarchy tree
logNodeHierarchy(getNodes.value)
```

### Step 4: Apply Fix Patterns

Consult `references/code-patterns.md` for proven solutions:

- **Reparenting**: Correct way to move node between parents
- **Position Conversion**: Converting between coordinate systems
- **Containment Detection**: Proper center-point-in-bounds check

## Common Bugs and Solutions

### Bug 1: Child Not Moving with Parent

**Symptom**: Dragging parent leaves children behind

**Cause**: `parentNode` property not set or position stored as absolute

**Fix**:
```typescript
// Ensure parentNode is set
updateNode(childId, { parentNode: parentId })

// Convert to relative position
const parent = getNode(parentId)
const relativePos = {
  x: child.computedPosition.x - parent.computedPosition.x,
  y: child.computedPosition.y - parent.computedPosition.y
}
updateNode(childId, { position: relativePos })
```

### Bug 2: Node Jumping When Set as Child

**Symptom**: Node teleports when assigned parent

**Cause**: Absolute position interpreted as relative after parent set

**Fix**: Convert position BEFORE setting parentNode:
```typescript
const parent = getNode(parentId)
const child = getNode(childId)

// Step 1: Calculate relative position first
const relativePos = {
  x: child.computedPosition.x - parent.computedPosition.x,
  y: child.computedPosition.y - parent.computedPosition.y
}

// Step 2: Update both in same call
updateNode(childId, {
  parentNode: parentId,
  position: relativePos
})
```

### Bug 3: Containment Detection Fails

**Symptom**: Tasks inside group not recognized

**Cause**: Comparing relative position against absolute bounds

**Fix**: Always use `computedPosition` for containment:
```typescript
function isInsideGroup(task: Node, group: Node): boolean {
  // WRONG: task.position (may be relative)
  // RIGHT: task.computedPosition (always absolute)

  const taskCenter = {
    x: task.computedPosition.x + (task.dimensions?.width ?? 200) / 2,
    y: task.computedPosition.y + (task.dimensions?.height ?? 100) / 2
  }

  return (
    taskCenter.x >= group.computedPosition.x &&
    taskCenter.x <= group.computedPosition.x + (group.dimensions?.width ?? 400) &&
    taskCenter.y >= group.computedPosition.y &&
    taskCenter.y <= group.computedPosition.y + (group.dimensions?.height ?? 300)
  )
}
```

### Bug 4: Position Persistence Issues

**Symptom**: Positions reset on page reload

**Cause**: Storing `computedPosition` instead of `position` for children

**Fix**: Store the correct coordinate type:
```typescript
function saveNodePosition(node: Node) {
  // For children: store relative position
  // For root nodes: store absolute position
  // node.position is ALWAYS what should be stored

  await saveToDatabase({
    id: node.id,
    position: node.position,  // NOT computedPosition
    parentNode: node.parentNode
  })
}
```

### Bug 5: Stale Parent Reference

**Symptom**: "Stale parentGroupId" warnings, child outside parent bounds

**Cause**: Parent moved/deleted but child still references it

**Fix**: Validate and repair on load:
```typescript
function validateHierarchy(nodes: Node[]) {
  const nodeMap = new Map(nodes.map(n => [n.id, n]))

  for (const node of nodes) {
    if (node.parentNode && !nodeMap.has(node.parentNode)) {
      console.warn(`Orphan node ${node.id} - parent ${node.parentNode} missing`)
      // Convert to root node
      node.parentNode = undefined
      // Position is already absolute-equivalent, keep as-is
    }
  }
}
```

## Resources

### references/
- `coordinate-system.md` - Deep dive into Vue Flow coordinate systems
- `troubleshooting-tree.md` - Systematic problem diagnosis flowchart
- `code-patterns.md` - Proven code patterns for common operations
- `e2e-test-patterns.md` - Playwright E2E test patterns for nested groups and parent movement

### scripts/
- `vueFlowDebug.ts` - TypeScript debugging utilities for Vue Flow

## Verification Checklist

After applying fixes, verify:

- [ ] Dragging parent moves all children together
- [ ] Positions persist correctly across page reloads
- [ ] New tasks dropped in group are detected as children
- [ ] Nested groups maintain hierarchy when moved
- [ ] No position jumping on any operation
- [ ] Console shows no stale parent warnings

