# Kanban Card Dependencies Specification

**Version:** 1.0  
**Date:** 2026-05-06  
**Author:** EClaw Development Team  
**Status:** Active

## Table of Contents

1. [Overview](#overview)
2. [System Architecture](#system-architecture)
3. [Database Schema](#database-schema)
4. [API Specification](#api-specification)
5. [User Interface](#user-interface)
6. [Algorithms](#algorithms)
7. [Performance Optimization](#performance-optimization)
8. [Security Considerations](#security-considerations)
9. [Error Handling](#error-handling)
10. [Migration Guide](#migration-guide)

---

## 1. Overview

### Purpose
The Kanban Card Dependencies system enables users to create bidirectional dependency relationships between kanban cards, providing sophisticated project management capabilities with deadlock detection and automatic status management.

### Key Features
- **Bidirectional Dependencies**: Cards can both depend on others and be depended upon
- **Cycle Detection**: Real-time prevention of dependency cycles using DFS algorithm
- **Visual Interface**: Drag-and-drop dependency creation with live validation
- **Automatic Status Updates**: Card statuses update automatically based on dependency states
- **Performance Optimized**: Multi-index database design for fast dependency queries
- **Nudge gating**: Cards blocked by a still-pending `blocks` dependency are skipped by the kanban-nudge cron's L1 / L2 / L3 escalation. See [`specs/kanban-nudge-spec.md` §3.5](specs/kanban-nudge-spec.md#dependency-aware-nudge-gating).

### Terminology
- **Source Card**: The card that depends on another
- **Target Card**: The card that is depended upon
- **Dependency Chain**: A sequence of dependent cards
- **Blocking Dependency**: A dependency where the target must complete before the source can proceed
- **Dependency Graph**: The complete network of all dependency relationships

---

## 2. System Architecture

### Components
```
┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│  Frontend UI    │    │   REST API       │    │   Database      │
│                 │    │                  │    │                 │
│ - Drag & Drop   │◄──►│ - Add Dependency │◄──►│ - Cards Table   │
│ - Visualization │    │ - Remove Dep     │    │ - Dependencies  │
│ - Validation    │    │ - Get Graph      │    │ - Triggers      │
└─────────────────┘    └──────────────────┘    └─────────────────┘
```

### Data Flow
1. User creates dependency via drag-drop interface
2. Frontend validates relationship locally
3. API receives request and performs cycle detection
4. Database triggers update card statuses automatically
5. Frontend receives updated data and refreshes visualization

---

## 3. Database Schema

> **Source of truth**: `kanban_card_dependencies` is the canonical record
> of which card depends on which. Description text, comment threads, or
> ad-hoc roadmap docs that say "H2 depends on H1" do NOT auto-register —
> the dependent card stays nudge-eligible until an explicit row exists
> (`POST /api/mission/card/:cardId/dependency`). Other subsystems (nudge cron, status
> rollups, UI badges) read this table; nothing parses prose.

### 3.1 Core Tables

#### kanban_card_dependencies
```sql
CREATE TABLE kanban_card_dependencies (
    id BIGSERIAL PRIMARY KEY,
    device_id VARCHAR(64) NOT NULL,
    card_id VARCHAR(48) NOT NULL REFERENCES kanban_cards(id) ON DELETE CASCADE,
    depends_on_card_id VARCHAR(48) NOT NULL REFERENCES kanban_cards(id) ON DELETE CASCADE,
    dependency_type VARCHAR(16) DEFAULT 'blocks',
    created_at TIMESTAMPTZ DEFAULT NOW(),
    created_by INTEGER NOT NULL DEFAULT 0,
    UNIQUE(device_id, card_id, depends_on_card_id)
);
```

#### Enhanced kanban_cards Columns
```sql
ALTER TABLE kanban_cards ADD COLUMN has_dependencies BOOLEAN DEFAULT FALSE;
ALTER TABLE kanban_cards ADD COLUMN dependency_status VARCHAR(16) DEFAULT 'ready';
```

### 3.2 Indexes
```sql
-- Core query optimization
CREATE INDEX idx_kanban_dependencies_card ON kanban_card_dependencies(device_id, card_id);
CREATE INDEX idx_kanban_dependencies_depends_on ON kanban_card_dependencies(device_id, depends_on_card_id);
CREATE INDEX idx_kanban_dependencies_type ON kanban_card_dependencies(device_id, dependency_type);

-- Card status optimization
CREATE INDEX idx_kanban_cards_has_dependencies ON kanban_cards(device_id, has_dependencies, dependency_status)
    WHERE has_dependencies = TRUE;
```

### 3.3 Database Functions

#### Cycle Detection Function
```sql
CREATE OR REPLACE FUNCTION detect_dependency_cycle(
    p_device_id VARCHAR(64),
    p_card_id VARCHAR(48),
    p_depends_on_card_id VARCHAR(48)
) RETURNS BOOLEAN AS $$
DECLARE
    visited_cards TEXT[] := ARRAY[]::TEXT[];
    current_card VARCHAR(48);
BEGIN
    -- Start DFS from the card that would depend on p_card_id
    current_card := p_depends_on_card_id;
    
    -- If adding this dependency would create immediate cycle
    IF current_card = p_card_id THEN
        RETURN TRUE;
    END IF;
    
    -- DFS to detect cycles
    WHILE current_card IS NOT NULL LOOP
        -- If we've visited this card before, we have a cycle
        IF current_card = ANY(visited_cards) THEN
            RETURN TRUE;
        END IF;
        
        -- Add current card to visited list
        visited_cards := visited_cards || current_card;
        
        -- Find next card in dependency chain
        SELECT depends_on_card_id INTO current_card
        FROM kanban_card_dependencies
        WHERE device_id = p_device_id
        AND card_id = current_card
        AND dependency_type = 'blocks'
        LIMIT 1;
        
        -- If we reach back to original card, cycle detected
        IF current_card = p_card_id THEN
            RETURN TRUE;
        END IF;
    END LOOP;
    
    RETURN FALSE;
END;
$$ LANGUAGE plpgsql;
```

### 3.4 Automatic Status Updates

#### Trigger Function
```sql
CREATE OR REPLACE FUNCTION update_dependency_status() RETURNS TRIGGER AS $$
BEGIN
    -- Update has_dependencies flag
    UPDATE kanban_cards SET has_dependencies = (
        SELECT COUNT(*) > 0
        FROM kanban_card_dependencies
        WHERE device_id = NEW.device_id AND card_id = NEW.card_id
    ) WHERE id = NEW.card_id AND device_id = NEW.device_id;
    
    -- Update dependency_status based on blocking dependencies
    UPDATE kanban_cards SET dependency_status = (
        CASE
            WHEN EXISTS (
                SELECT 1 FROM kanban_card_dependencies d
                JOIN kanban_cards dep_card ON d.depends_on_card_id = dep_card.id
                WHERE d.device_id = NEW.device_id
                AND d.card_id = NEW.card_id
                AND d.dependency_type = 'blocks'
                AND dep_card.status NOT IN ('done', 'archived')
            ) THEN 'waiting'
            ELSE 'ready'
        END
    ) WHERE id = NEW.card_id AND device_id = NEW.device_id;
    
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER tr_kanban_dependency_status_update
AFTER INSERT OR UPDATE OR DELETE ON kanban_card_dependencies
FOR EACH ROW EXECUTE FUNCTION update_dependency_status();
```

---

## 4. API Specification

### 4.1 Add Dependency

**Endpoint:** `POST /api/mission/card/:cardId/dependency`

**Request Body:**
```json
{
  "deviceId": "string",
  "entityId": "number",
  "botSecret": "string",
  "dependsOnCardId": "string",
  "dependencyType": "blocks" // optional, default: "blocks"
}
```

**Response:**
```json
{
  "success": true,
  "dependency": {
    "id": "number",
    "device_id": "string",
    "card_id": "string",
    "depends_on_card_id": "string",
    "dependency_type": "string",
    "created_at": "timestamp",
    "created_by": "number"
  },
  "message": "Dependency added: Card cardId depends on Card dependsOnCardId"
}
```

**Error Responses:**
- `401` - Authentication required
- `404` - One or both cards not found
- `400` - Cycle detected (`cycleDetected: true`)

### 4.2 Remove Dependency

**Endpoint:** `DELETE /api/mission/card/:cardId/dependency/:dependsOnCardId`

**Request Body:**
```json
{
  "deviceId": "string",
  "entityId": "number",
  "botSecret": "string"
}
```

### 4.3 Get Dependencies

**Endpoint:** `GET /api/mission/card/:cardId/dependencies`

**Response:**
```json
{
  "success": true,
  "cardId": "string",
  "dependsOn": [
    {
      "id": "number",
      "depends_on_card_id": "string",
      "depends_on_title": "string",
      "depends_on_status": "string",
      "dependency_type": "string"
    }
  ],
  "dependents": [
    {
      "id": "number",
      "card_id": "string",
      "dependent_title": "string",
      "dependent_status": "string"
    }
  ],
  "status": {
    "dependency_status": "ready|waiting|blocked",
    "has_dependencies": "boolean"
  },
  "summary": {
    "dependsOnCount": "number",
    "dependentsCount": "number",
    "blockedByCount": "number"
  }
}
```

### 4.4 Dependency Graph

**Endpoint:** `GET /api/mission/dependencies/graph`

**Query Parameters:**
- `deviceId` - Device identifier
- `entityId` - Entity identifier 
- `botSecret` - Authentication secret
- `rootCardId` - Optional root card for subgraph

**Response:**
```json
{
  "success": true,
  "graph": {
    "nodes": [
      {
        "id": "string",
        "title": "string",
        "status": "string",
        "dependencyStatus": "string",
        "hasDependencies": "boolean",
        "depth": "number"
      }
    ],
    "edges": [
      {
        "from": "string",
        "to": "string", 
        "type": "string",
        "id": "number"
      }
    ],
    "metadata": {
      "totalCards": "number",
      "totalDependencies": "number",
      "rootCardId": "string|null"
    }
  }
}
```

### 4.5 Validate Dependency

**Endpoint:** `POST /api/mission/dependencies/validate`

**Response:**
```json
{
  "success": true,
  "validation": {
    "cardId": "string",
    "dependsOnCardId": "string",
    "hasCycle": "boolean",
    "isValid": "boolean",
    "message": "string"
  }
}
```

---

## 5. User Interface

### 5.1 Dependency Manager Component

**File:** `public/kanban-dependency-manager.js`

**Key Features:**
- Drag and drop dependency creation
- Real-time cycle validation
- Visual dependency graph
- Interactive card selection
- Dependency removal interface

### 5.2 UI States

#### Loading State
```javascript
{
  loading: true,
  dependencies: [],
  availableCards: []
}
```

#### Error State
```javascript
{
  error: "Cycle detected - this would create a circular dependency",
  showError: true
}
```

#### Success State
```javascript
{
  dependencies: [...],
  message: "Dependency added successfully",
  showSuccess: true
}
```

### 5.3 Drag & Drop Implementation

```javascript
// Drop handler
async handleCardDrop(cardId, targetCardId) {
  // Validate before API call
  const validation = await this.validateDependency(cardId, targetCardId);
  if (!validation.isValid) {
    this.showError(validation.message);
    return;
  }
  
  // Create dependency
  await this.addDependency(cardId, targetCardId);
}
```

---

## 6. Algorithms

### 6.1 Cycle Detection Algorithm

**Type:** Depth-First Search (DFS)  
**Time Complexity:** O(V + E) where V = vertices (cards), E = edges (dependencies)  
**Space Complexity:** O(V) for visited array

**Algorithm:**
1. Start from the card that would depend on the new target
2. Maintain a visited array to track traversed cards
3. Follow dependency chains depth-first
4. If we revisit a card or reach the original source card, a cycle exists
5. Return true if cycle detected, false otherwise

### 6.2 Graph Traversal for Visualization

**Type:** Breadth-First Search (BFS) with depth tracking  
**Purpose:** Generate hierarchical dependency tree for visualization

```sql
WITH RECURSIVE dependency_tree AS (
    -- Start from root card
    SELECT
        id, title, status, dependency_status,
        ARRAY[id] as path,
        0 as depth
    FROM kanban_cards
    WHERE device_id = $1 AND id = $2
    
    UNION
    
    -- Find all cards that depend on cards in current tree
    SELECT
        c.id, c.title, c.status, c.dependency_status,
        dt.path || c.id as path,
        dt.depth + 1 as depth
    FROM dependency_tree dt
    JOIN kanban_card_dependencies d ON dt.id = d.depends_on_card_id
    JOIN kanban_cards c ON d.card_id = c.id
    WHERE c.device_id = $1
    AND NOT c.id = ANY(dt.path) -- Prevent infinite recursion
    AND dt.depth < 10 -- Limit depth
)
SELECT DISTINCT * FROM dependency_tree ORDER BY depth, title;
```

---

## 7. Performance Optimization

### 7.1 Database Optimization

**Index Strategy:**
```sql
-- Primary lookup patterns
CREATE INDEX idx_kanban_dependencies_card ON kanban_card_dependencies(device_id, card_id);
CREATE INDEX idx_kanban_dependencies_depends_on ON kanban_card_dependencies(device_id, depends_on_card_id);

-- Type-based queries
CREATE INDEX idx_kanban_dependencies_type ON kanban_card_dependencies(device_id, dependency_type);

-- Filtered index for cards with dependencies
CREATE INDEX idx_kanban_cards_has_dependencies ON kanban_cards(device_id, has_dependencies, dependency_status)
    WHERE has_dependencies = TRUE;
```

**Query Optimization:**
- Use prepared statements for common queries
- Batch dependency operations where possible
- Limit recursive depth to prevent infinite loops
- Cache frequently accessed dependency graphs

### 7.2 Frontend Optimization

**Rendering Optimization:**
- Virtual scrolling for large dependency lists
- Debounced dependency validation (300ms)
- Memoized graph calculations
- Progressive loading for large graphs

**Memory Management:**
```javascript
// Cleanup on component unmount
componentWillUnmount() {
  this.cleanupDependencyListeners();
  this.clearGraphCache();
}
```

---

## 8. Security Considerations

### 8.1 Access Control

**Device-Level Security:**
- All API endpoints require valid `deviceId`, `entityId`, and `botSecret`
- Dependencies can only be created within the same device scope
- Card access is filtered by device ownership

**Entity-Level Security:**
```javascript
// Validate entity has access to both cards
const cardsExist = await pool.query(`
    SELECT id FROM kanban_cards
    WHERE device_id = $1 AND id IN ($2, $3)
`, [deviceId, cardId, dependsOnCardId]);

if (cardsExist.rows.length !== 2) {
    return res.status(404).json({ error: 'One or both cards not found' });
}
```

### 8.2 Input Validation

**SQL Injection Prevention:**
- All queries use parameterized statements
- Input sanitization for card IDs and device IDs
- Type validation for dependency types

**Cross-Site Scripting (XSS) Prevention:**
- HTML escape all user-generated content
- Content Security Policy headers
- Input validation on both client and server

---

## 9. Error Handling

### 9.1 Error Categories

**Client Errors (4xx):**
- `400` - Bad Request (cycle detected, invalid dependency type)
- `401` - Unauthorized (missing or invalid auth)
- `404` - Not Found (card not found)
- `409` - Conflict (dependency already exists)

**Server Errors (5xx):**
- `500` - Internal Server Error (database connection, unexpected errors)

### 9.2 Error Response Format

```json
{
  "success": false,
  "error": "Human-readable error message",
  "errorCode": "DEPENDENCY_CYCLE_DETECTED",
  "details": {
    "cardId": "card_123",
    "dependsOnCardId": "card_456",
    "cycleDetected": true
  }
}
```

### 9.3 Frontend Error Handling

```javascript
try {
  await this.addDependency(cardId, targetCardId);
} catch (error) {
  if (error.cycleDetected) {
    this.showError('This dependency would create a cycle');
  } else {
    this.showError('Failed to add dependency');
  }
}
```

---

## 10. Migration Guide

### 10.1 Database Migration

**Run Migration Script:**
```bash
node apply_dependency_chain_migration.js
```

**Migration Steps:**
1. Create `kanban_card_dependencies` table
2. Add dependency columns to `kanban_cards`
3. Create indexes for performance
4. Install cycle detection function
5. Set up automatic status update triggers

### 10.2 Backward Compatibility

**Existing Cards:**
- All existing cards default to `dependency_status = 'ready'`
- No dependencies are created during migration
- Existing functionality remains unchanged

**API Compatibility:**
- New endpoints are additive (no breaking changes)
- Existing card endpoints include new dependency fields
- Optional parameters maintain backward compatibility

### 10.3 Testing Migration

```sql
-- Verify migration success
SELECT table_name, column_name, data_type
FROM information_schema.columns
WHERE table_name = 'kanban_card_dependencies'
ORDER BY column_name;

-- Test cycle detection function
SELECT detect_dependency_cycle('test-device', 'card1', 'card2') as result;
```

---

## Appendix

### A. Dependency Types

| Type | Description | Behavior |
|------|-------------|----------|
| `blocks` | Target must complete before source can start | Blocks source until target is done |
| `subtask` | Source is a subtask of target | Informational relationship |
| `related` | Cards are related but not blocking | No status impact |

### B. Status Mapping

| Card Status | Dependency Status | Meaning |
|-------------|------------------|---------|
| Any | `ready` | No blocking dependencies or all dependencies resolved |
| Any | `waiting` | Has blocking dependencies that are not complete |
| `blocked` | `blocked` | Explicitly blocked by user, overrides dependency status |

### C. Performance Benchmarks

**Dependency Creation:** < 50ms for simple dependencies  
**Cycle Detection:** < 100ms for graphs up to 1000 nodes  
**Graph Visualization:** < 200ms for 100-node subgraphs  
**Status Updates:** < 10ms via database triggers

---

*This specification document is maintained by the EClaw Development Team and updated as the system evolves.*