# Task Identifier Semantics

This document describes the dual-identifier system used for tasks in CodeFRAME, the `depends_on` field semantics, and recommendations for working with task references.

## Overview

CodeFRAME tasks use two distinct identifiers:

| Identifier | Type | Example | Stability | Uniqueness Scope |
|------------|------|---------|-----------|------------------|
| `id` | Integer (backend) / String (frontend) | `42`, `"42"` | Permanent | Database-level (global) |
| `task_number` | String (hierarchical) | `"1.5.3"` | May change | Project-level |

## Task Identifier Types

### `id` - Primary Key Identifier

The `id` field is the database primary key assigned when a task is created.

**Backend (Python):**
```python
# codeframe/core/models.py:139
@dataclass
class Task:
    id: Optional[int] = None  # Auto-generated by database
```

**Frontend (TypeScript):**
```typescript
// web-ui/src/types/api.ts:30
interface Task {
  id: string;  // Serialized as string in API responses
}
```

**Characteristics:**
- **Permanent**: Once assigned, never changes for the lifetime of the task
- **Globally unique**: No two tasks share the same `id` across the entire database
- **Auto-generated**: Assigned by SQLite AUTOINCREMENT on insert
- **Foreign key compatible**: Can be used for database relationships

### `task_number` - Human-Readable Identifier

The `task_number` field provides a hierarchical, human-readable identifier.

**Backend (Python):**
```python
# codeframe/core/models.py:142
@dataclass
class Task:
    task_number: str = ""  # e.g., "1.5.3"
```

**Frontend (TypeScript):**
```typescript
// web-ui/src/types/api.ts:31
interface Task {
  task_number: string;  // e.g., "1.5.3"
}
```

**Characteristics:**
- **Hierarchical**: Format is `{issue_number}.{sequence}` (e.g., "1.5.3" means issue 1.5, task 3)
- **Project-scoped**: Unique within a project, but may duplicate across projects
- **Human-readable**: Meaningful for logs, debugging, and UI display
- **May change**: If tasks are renumbered, reordered, or reorganized

**Format Breakdown:**
```
task_number: "1.5.3"
             │ │ └── Task sequence within issue (3rd task)
             │ └──── Issue sub-number (5th sub-issue)
             └────── Issue top-level number (1st issue)
```

## Dependency Reference Patterns

The `depends_on` field stores task dependencies with different representations in backend and frontend.

### Backend Storage Format

**Location:** `codeframe/core/models.py:148`

```python
@dataclass
class Task:
    depends_on: str = ""  # Previous task number (e.g., "1.5.2")
```

The backend stores `depends_on` as a string in one of three formats:

| Format | Example | Meaning |
|--------|---------|---------|
| Empty string | `""` | No dependencies |
| Single task_number | `"1.5.2"` | Depends on task with task_number "1.5.2" |
| JSON array | `"[1, 2, 3]"` | Depends on tasks with IDs 1, 2, and 3 |

### Frontend API Format

**Location:** `web-ui/src/types/api.ts:35`

```typescript
interface Task {
  depends_on: string[];  // Array of dependency references
}
```

The frontend API layer parses the backend string into a string array:

| API Value | Meaning |
|-----------|---------|
| `[]` | No dependencies |
| `["1.5.2", "1.5.3"]` | Depends on tasks with those task_numbers |
| `["42", "43"]` | Depends on tasks with those IDs |

### API Transformation

The API layer in the frontend transforms the backend format:

```typescript
// Empty string → empty array
"" → []

// Single value → single-element array
"1.5.2" → ["1.5.2"]

// JSON array → parsed array (as strings)
"[1, 2, 3]" → ["1", "2", "3"]
```

## Current Implementation: Tolerant Lookup

The frontend implements a **tolerant lookup** pattern that accepts both identifier types.

**Location:** `web-ui/src/components/TaskTreeView.tsx:85`

```typescript
// Check if task is blocked by dependencies
const isTaskBlocked = (task: Task, allTasks: Task[]): boolean => {
  if (!task.depends_on || task.depends_on.length === 0) return false;
  if (task.status === 'completed' || task.status === 'in_progress') return false;

  // Find dependency tasks and check if any are not completed
  return task.depends_on.some((depId) => {
    // Dual-lookup: supports both id and task_number for backward compatibility
    const depTask = allTasks.find((t) => t.id === depId || t.task_number === depId);
    return depTask && depTask.status !== 'completed';
  });
};
```

**Why Tolerant Lookup?**

1. **Backward compatibility**: Legacy data may use `task_number` references
2. **Gradual migration**: Systems can migrate from `task_number` to `id` without breaking
3. **Flexibility**: Both human-readable and stable references work equally
4. **No data migration required**: Existing `depends_on` values continue to function

The same dual-lookup pattern is also used in the dependency tooltip display (line 234-236).

## Trade-offs and Recommendations

### Comparison Table

| Aspect | `id` | `task_number` |
|--------|------|---------------|
| **Stability** | Permanent (never changes) | May change (renumbering, reorg) |
| **Human Readability** | Opaque (just a number) | Hierarchical, meaningful |
| **Database Integrity** | Foreign key compatible | String matching only |
| **Debugging** | Requires ID lookup | Self-documenting |
| **API Responses** | Always included | Always included |
| **Cross-project Safety** | Globally unique | Project-scoped only |

### Recommendations

**For Production Systems:**
- **Prefer `id`-based dependencies** for stability
- Dependencies won't break if tasks are renumbered
- Safer for long-running projects with evolving task structures

**For Debugging and Logs:**
- **Use `task_number`** for human-readable output
- Easier to understand task relationships at a glance
- Include in error messages and UI displays

**For API Consumers:**
- **Accept both formats** (as the frontend does)
- Document which format you're using
- Consider returning both in responses for flexibility

### Example: Safe Dependency Creation

```typescript
// Preferred: Use task ID for stability
const createDependency = (dependsOnTask: Task): string => {
  return dependsOnTask.id;  // Use stable ID
};

// Alternative: Use task_number for readability (less stable)
const createReadableDependency = (dependsOnTask: Task): string => {
  return dependsOnTask.task_number;  // Human-readable but may change
};
```

## Migration Guidance

### Migrating from task_number to id-based Dependencies

If your system currently uses `task_number` in `depends_on` and you want to migrate to `id`:

1. **No code changes required**: The tolerant lookup supports both formats
2. **Update new dependencies only**: Start using `id` for new dependency references
3. **Optional bulk migration**: If desired, update existing `depends_on` values:

```python
# Migration script example (optional)
async def migrate_dependencies_to_ids(db: Database):
    tasks = await db.get_all_tasks()
    task_number_to_id = {t.task_number: str(t.id) for t in tasks}

    for task in tasks:
        if task.depends_on and not task.depends_on.isdigit():
            # Convert task_number to id
            if task.depends_on in task_number_to_id:
                task.depends_on = task_number_to_id[task.depends_on]
                await db.update_task(task)
```

### Backward Compatibility Guarantee

The tolerant lookup pattern ensures:
- Existing `task_number`-based dependencies continue to work
- New `id`-based dependencies work immediately
- No breaking changes for API consumers
- Gradual migration is possible without coordination

## Visual Reference

```
┌─────────────────────────────────────────────────────────────────┐
│                        Task Object                               │
├─────────────────────────────────────────────────────────────────┤
│  id: 42                    │  task_number: "1.5.3"              │
│  ────────                  │  ─────────────────                 │
│  • Primary key             │  • Human-readable                  │
│  • Permanent               │  • May change                      │
│  • Globally unique         │  • Project-scoped                  │
│  • Database FK compatible  │  • Hierarchical format             │
├─────────────────────────────────────────────────────────────────┤
│                     depends_on: ["1.5.2"]                        │
│                     ─────────────────────                        │
│                                                                  │
│                     ┌─────────────────────┐                      │
│                     │   Tolerant Lookup   │                      │
│                     └──────────┬──────────┘                      │
│                                │                                 │
│              ┌─────────────────┴─────────────────┐               │
│              ▼                                   ▼               │
│   ┌──────────────────┐              ┌──────────────────┐         │
│   │  Match by id     │              │ Match by         │         │
│   │  (t.id === dep)  │              │ task_number      │         │
│   │                  │              │ (t.task_number   │         │
│   │  Stable          │              │  === dep)        │         │
│   │  Preferred       │              │                  │         │
│   └──────────────────┘              │  Human-readable  │         │
│           ▲                         │  Fallback        │         │
│           │                         └──────────────────┘         │
│           │                                  ▲                   │
│    Recommended                       Legacy Support              │
└─────────────────────────────────────────────────────────────────┘
```

## Related Files

| File | Description |
|------|-------------|
| `codeframe/core/models.py:131-157` | Backend Task dataclass definition |
| `web-ui/src/types/api.ts:29-40` | Frontend Task interface |
| `web-ui/src/components/TaskTreeView.tsx:78-88` | Tolerant lookup implementation |
| `web-ui/src/components/TaskTreeView.tsx:234-236` | Dependency tooltip lookup |

## Changelog

- **2025-12-05**: Initial documentation created to clarify dual-identifier system
