Files
roboco/.tasks/initiatives/blueprint-alignment/tasks.md
T
2025-12-13 00:00:52 +01:00

28 KiB

Task Breakdown: Blueprint Alignment - Production Ready

Last Updated: 2025-12-12 Initiative: INI-001


Overview

Sprint Total Completed In Progress Blocked Pending
Sprint 1 (Critical) 4 4 0 0 0
Sprint 2 (High) 5 5 0 0 0
Sprint 3 (Medium) 4 4 0 0 0
Sprint 4 (Low) 4 2 0 0 2 (cancelled)
Total 17 15 0 0 2

Sprint 1: CRITICAL - Security Fixes

Priority: P0 | Must complete before production use

ID Title Type Priority Effort Status Blocks
TASK-009 Fix channel access default bugfix P0 5 min pending All channel ops
TASK-010 Wire permission guards to task routes security P0 1 day pending TASK-012
TASK-011 Add view restrictions (team filtering) security P0 0.5 day pending -
TASK-012 Enforce task action permissions security P0 0.5 day pending Sprint 2

Task Details


TASK-009: Fix Channel Access Default Security Bug

Type: Bugfix (Security) Priority: P0 - CRITICAL Effort: 5 minutes Status: pending

Problem: In roboco/enforcement/channel_access.py:61, unknown channels default to return True, meaning any unrecognized channel allows full access. This is a security vulnerability.

Current Code (channel_access.py:61):

# If channel not in config, allow by default (may want to change this)
return True

Fix:

# If channel not in config, deny by default (secure by default)
return False

Files to Modify:

  • roboco/enforcement/channel_access.py

Acceptance Criteria:

  • Unknown channels return False for all access checks
  • Existing channel access tests still pass
  • Add test case for unknown channel denial

TASK-010: Wire Permission Guards to Task Routes

Type: Security Priority: P0 - CRITICAL Effort: 1 day Status: pending Blocks: TASK-012

Problem: The PermissionService.can_perform_task_action() method exists and is fully implemented, but it's not actually called in any of the task route handlers. Anyone can create, update, or delete any task.

Current State:

  • roboco/services/permissions.py has can_perform_task_action() - IMPLEMENTED
  • roboco/api/routes/tasks.py has all CRUD operations - NO PERMISSION CHECKS

Required Changes:

  1. POST /tasks/ (create task):
if not await permission_service.can_perform_task_action(agent_id, None, "create"):
    raise HTTPException(403, "Not authorized to create tasks")
  1. PATCH /tasks/{id} (update task):
if not await permission_service.can_perform_task_action(agent_id, task_id, "update"):
    raise HTTPException(403, "Not authorized to update this task")
  1. DELETE /tasks/{id} (delete task):
if not await permission_service.can_perform_task_action(agent_id, task_id, "delete"):
    raise HTTPException(403, "Not authorized to delete this task")
  1. POST /tasks/{id}/claim (claim task):
if not await permission_service.can_perform_task_action(agent_id, task_id, "claim"):
    raise HTTPException(403, "Not authorized to claim this task")
  1. POST /tasks/{id}/status (change status):
if not await permission_service.can_perform_task_action(agent_id, task_id, "change_status"):
    raise HTTPException(403, "Not authorized to change task status")
  1. POST /tasks/{id}/assign (assign task):
if not await permission_service.can_perform_task_action(agent_id, task_id, "assign"):
    raise HTTPException(403, "Not authorized to assign this task")

Files to Modify:

  • roboco/api/routes/tasks.py
  • roboco/api/deps.py (add permission service dependency)

Acceptance Criteria:

  • All task CRUD endpoints check permissions
  • Unauthorized requests return 403 Forbidden
  • Tests added for permission denial cases
  • Existing authorized operations still work

TASK-011: Add View Restrictions (Team-Based Filtering)

Type: Security Priority: P0 - CRITICAL Effort: 0.5 day Status: pending

Problem: All tasks are visible to all agents. Cell members should only see tasks assigned to their cell.

Current State:

  • GET /tasks/ returns all tasks
  • No team-based filtering

Required Changes:

  1. Add team filter to list endpoint:
@router.get("/")
async def list_tasks(
    agent_id: str = Header(...),
    include_other_teams: bool = Query(False),  # Only PMs/Board can set True
    ...
):
    agent = await get_agent(agent_id)

    # Determine visibility
    if agent.role in ["main_pm", "product_owner", "head_marketing", "auditor"]:
        # Can see all tasks
        query = select(Task)
    elif include_other_teams and agent.role == "cell_pm":
        # Cell PMs can request cross-team view
        query = select(Task)
    else:
        # Regular agents see only their team's tasks
        query = select(Task).where(Task.team == agent.team)
  1. Add similar filter to kanban views

Files to Modify:

  • roboco/api/routes/tasks.py
  • roboco/api/routes/kanban.py

Acceptance Criteria:

  • Developers only see their team's tasks
  • Cell PMs see their cell's tasks by default
  • Main PM/Board can see all tasks
  • Auditor can see all tasks (silent observer)
  • Tests for team-based filtering

TASK-012: Enforce Task Action Permissions

Type: Security Priority: P0 - CRITICAL Effort: 0.5 day Status: pending Blocked By: TASK-010

Problem: Specific task actions have role requirements that aren't enforced:

  • Only QA can mark as QA_PASSED or QA_FAILED
  • Only Documenter can mark as COMPLETED (after docs)
  • Only assigned agent can change status to IN_PROGRESS

Required Changes:

  1. Add role validation to status changes:
async def validate_status_change(agent: Agent, task: Task, new_status: TaskStatus):
    if new_status == TaskStatus.QA_PASSED:
        if agent.role != "qa":
            raise PermissionDeniedError("Only QA can pass tasks")
    elif new_status == TaskStatus.QA_FAILED:
        if agent.role != "qa":
            raise PermissionDeniedError("Only QA can fail tasks")
    elif new_status == TaskStatus.IN_PROGRESS:
        if task.assigned_to != agent.id:
            raise PermissionDeniedError("Only assigned agent can start work")
    # ... etc

Files to Modify:

  • roboco/services/task.py
  • roboco/enforcement/task_lifecycle.py

Acceptance Criteria:

  • QA-specific statuses enforced
  • Documenter-specific statuses enforced
  • Assignment-based actions enforced
  • Clear error messages for denials
  • Tests for each role restriction

Sprint 2: HIGH - Core Services

Priority: P1 | Blocking agent communication

ID Title Type Priority Effort Status Blocks
TASK-013 MessagingService - Channel CRUD feature P1 1 day pending TASK-014
TASK-014 MessagingService - Message CRUD feature P1 1 day pending TASK-015
TASK-015 MessagingService - Session Lifecycle feature P1 1 day pending -
TASK-016 Notification Delivery Pipeline feature P1 2 days pending TASK-017
TASK-017 Notification ACK System feature P1 1 day pending -

Task Details


TASK-013: MessagingService - Channel CRUD

Type: Feature Priority: P1 Effort: 1 day Status: pending Blocks: TASK-014

Problem: No service layer for channel management. Routes exist but directly hit database without business logic layer.

Required Implementation:

Create roboco/services/messaging.py:

class MessagingService:
    def __init__(self, db: AsyncSession):
        self.db = db

    async def create_channel(
        self,
        name: str,
        slug: str,
        channel_type: ChannelType,
        created_by: UUID,
        members: list[UUID] = None,
        writers: list[UUID] = None,
        silent_observers: list[UUID] = None,
    ) -> Channel:
        """Create a new channel with initial membership."""
        ...

    async def get_channel(self, channel_id: UUID) -> Channel:
        """Get channel by ID."""
        ...

    async def get_channel_by_slug(self, slug: str) -> Channel:
        """Get channel by slug."""
        ...

    async def list_channels_for_agent(
        self,
        agent_id: UUID,
        include_archived: bool = False,
    ) -> list[Channel]:
        """List channels agent can access (member or silent observer)."""
        ...

    async def add_member(
        self,
        channel_id: UUID,
        agent_id: UUID,
        can_write: bool = True,
    ) -> None:
        """Add member to channel."""
        ...

    async def remove_member(
        self,
        channel_id: UUID,
        agent_id: UUID,
    ) -> None:
        """Remove member from channel."""
        ...

    async def archive_channel(self, channel_id: UUID) -> None:
        """Archive a channel."""
        ...

Files to Create:

  • roboco/services/messaging.py

Files to Modify:

  • roboco/services/__init__.py (export)
  • roboco/api/routes/channels.py (use service)

Acceptance Criteria:

  • MessagingService class implemented
  • All channel CRUD operations
  • Access checks use enforcement layer
  • Routes refactored to use service
  • Unit tests for service

TASK-014: MessagingService - Message CRUD

Type: Feature Priority: P1 Effort: 1 day Status: pending Blocked By: TASK-013 Blocks: TASK-015

Problem: Message operations lack proper service layer abstraction.

Required Implementation:

Add to roboco/services/messaging.py:

async def send_message(
    self,
    session_id: UUID,
    agent_id: UUID,
    content: str,
    message_type: MessageType = MessageType.DIALOGUE,
    reply_to: UUID | None = None,
    mentions: list[UUID] | None = None,
    task_id: UUID | None = None,
    commit_ref: str | None = None,
) -> Message:
    """
    Send a message to a session.

    - Validates session is active
    - Validates agent has write access
    - Updates session statistics
    - Checks session boundaries (auto-close if exceeded)
    - Publishes message event
    """
    ...

async def edit_message(
    self,
    message_id: UUID,
    agent_id: UUID,
    new_content: str,
    edit_reason: str | None = None,
) -> Message:
    """
    Edit a message.

    - Validates agent is author
    - Stores edit history
    - Updates session content length
    """
    ...

async def delete_message(
    self,
    message_id: UUID,
    agent_id: UUID,
) -> None:
    """
    Delete a message.

    - Validates agent is author
    - Soft delete (marks deleted, preserves history)
    """
    ...

async def get_messages(
    self,
    session_id: UUID,
    agent_id: UUID,
    before: datetime | None = None,
    after: datetime | None = None,
    message_type: MessageType | None = None,
    limit: int = 50,
) -> tuple[list[Message], bool]:
    """
    Get messages from a session.

    Returns (messages, has_more).
    """
    ...

Files to Modify:

  • roboco/services/messaging.py
  • roboco/api/routes/messages.py (use service)

Acceptance Criteria:

  • Message CRUD operations in service
  • Edit history tracking
  • Session statistics updates
  • Event publishing on send
  • Unit tests

TASK-015: MessagingService - Session Lifecycle

Type: Feature Priority: P1 Effort: 1 day Status: pending Blocked By: TASK-014

Problem: Session creation, closing, and boundary management needs service layer.

Required Implementation:

Add to roboco/services/messaging.py:

async def create_session(
    self,
    group_id: UUID,
    created_by: UUID,
    topic: str | None = None,
    max_messages: int | None = None,
    max_content_length: int | None = None,
) -> Session:
    """
    Create a new session in a group.

    - Sets as group's active session
    - Initializes statistics
    - Publishes session.created event
    """
    ...

async def close_session(
    self,
    session_id: UUID,
    reason: str = "Manual close",
) -> Session:
    """
    Close a session.

    - Updates status to CLOSED
    - Clears group's active_session_id
    - Publishes session.closed event
    """
    ...

async def check_session_boundaries(
    self,
    session: Session,
) -> bool:
    """
    Check if session has exceeded boundaries.

    Returns True if session should be closed.
    """
    should_close = (
        (session.max_message_count and session.message_count >= session.max_message_count) or
        (session.max_content_length and session.total_content_length >= session.max_content_length)
    )
    return should_close

async def get_or_create_active_session(
    self,
    group_id: UUID,
    agent_id: UUID,
) -> Session:
    """
    Get the active session for a group, or create one if none exists.
    """
    ...

Files to Modify:

  • roboco/services/messaging.py
  • roboco/api/routes/sessions.py (use service)

Acceptance Criteria:

  • Session lifecycle management
  • Boundary checking with auto-close
  • Active session tracking
  • Event publishing
  • Unit tests

TASK-016: Notification Delivery Pipeline

Type: Feature Priority: P1 Effort: 2 days Status: pending Blocks: TASK-017

Problem: Notifications are created and stored but never actually delivered to agents. There's no mechanism for agents to receive pending notifications.

Required Implementation:

  1. Delivery Service (roboco/services/notification_delivery.py):
class NotificationDeliveryService:
    """
    Delivers notifications to agents via multiple channels.
    """

    async def deliver(self, notification: Notification) -> bool:
        """
        Deliver a notification to its recipient.

        Delivery channels (in order):
        1. WebSocket (if agent connected)
        2. Redis pub/sub (for polling)
        3. Database queue (persistent fallback)
        """
        ...

    async def get_pending_for_agent(
        self,
        agent_id: UUID,
        limit: int = 20,
    ) -> list[Notification]:
        """Get undelivered notifications for an agent."""
        ...

    async def mark_delivered(
        self,
        notification_id: UUID,
    ) -> None:
        """Mark notification as delivered (not ACKed)."""
        ...
  1. Background Worker (optional, for push delivery):
async def notification_worker():
    """Background task that delivers pending notifications."""
    while True:
        pending = await get_undelivered_notifications()
        for notification in pending:
            await deliver(notification)
        await asyncio.sleep(5)
  1. WebSocket Integration:
  • Add notification events to WebSocket handler
  • Push notifications to connected agents

Files to Create:

  • roboco/services/notification_delivery.py

Files to Modify:

  • roboco/api/websocket.py (add notification push)
  • roboco/services/__init__.py (export)

Acceptance Criteria:

  • Notifications delivered via WebSocket
  • Fallback to polling for disconnected agents
  • Delivery status tracking
  • Integration tests

TASK-017: Notification ACK System

Type: Feature Priority: P1 Effort: 1 day Status: pending Blocked By: TASK-016

Problem: Notifications have ACK fields (acked_at, ack_read_at) but no mechanism to track acknowledgments.

Required Implementation:

  1. ACK Endpoint Enhancement:
@router.post("/{notification_id}/ack")
async def acknowledge_notification(
    notification_id: UUID,
    agent_id: str = Header(...),
    ack_type: Literal["received", "read"] = "received",
) -> NotificationResponse:
    """
    Acknowledge a notification.

    - received: Agent's system received it
    - read: Agent has read/processed it
    """
    notification = await get_notification(notification_id)

    if notification.recipient_id != agent_id:
        raise HTTPException(403, "Cannot ACK others' notifications")

    if ack_type == "received":
        notification.acked_at = datetime.now(UTC)
    else:
        notification.ack_read_at = datetime.now(UTC)

    await db.commit()

    # Publish ACK event
    await event_bus.publish_notification_event(
        EventType.NOTIFICATION_ACKED,
        notification_id=str(notification_id),
        ack_type=ack_type,
    )

    return notification
  1. ACK Tracking Dashboard:
  • Add endpoint to get ACK status summary
  • Show unacknowledged notifications count per agent

Files to Modify:

  • roboco/api/routes/notifications.py
  • roboco/services/notification.py

Acceptance Criteria:

  • Received ACK tracking
  • Read ACK tracking
  • ACK events published
  • Cannot ACK others' notifications
  • Unit tests

Sprint 3: MEDIUM - Enforcement & Quality

Priority: P2 | Improving reliability

ID Title Type Priority Effort Status Blocks
TASK-018 Enforce all state transitions feature P2 1 day completed -
TASK-019 Add audit logging for denials feature P2 0.5 day completed -
TASK-020 Merge permission systems refactor P2 1 day completed -
TASK-021 Fix OptimalService temp files bugfix P2 0.5 day completed -

Task Details


TASK-018: Enforce All State Transitions

Type: Feature Priority: P2 Effort: 1 day Status: pending

Problem: validate_task_transition() in task_lifecycle.py exists but isn't called for all status changes.

Required Implementation:

  1. Wire validation to all status changes:
# In task service or route
async def change_task_status(task_id: UUID, new_status: TaskStatus, agent_id: UUID):
    task = await get_task(task_id)

    # Validate transition is allowed
    if not validate_task_transition(task.status, new_status):
        raise TaskLifecycleError(
            task_id=task_id,
            current_status=task.status,
            target_status=new_status,
        )

    # Validate role can make this transition
    agent = await get_agent(agent_id)
    validate_role_for_transition(agent.role, task.status, new_status)

    task.status = new_status
    ...
  1. Add role-based transition rules:
ROLE_TRANSITIONS = {
    "developer": [
        (TaskStatus.CLAIMED, TaskStatus.IN_PROGRESS),
        (TaskStatus.IN_PROGRESS, TaskStatus.VERIFYING),
        (TaskStatus.IN_PROGRESS, TaskStatus.BLOCKED),
        ...
    ],
    "qa": [
        (TaskStatus.AWAITING_QA, TaskStatus.QA_PASSED),
        (TaskStatus.AWAITING_QA, TaskStatus.QA_FAILED),
        ...
    ],
    ...
}

Files to Modify:

  • roboco/enforcement/task_lifecycle.py
  • roboco/services/task.py
  • roboco/api/routes/tasks.py

Acceptance Criteria:

  • All status changes validated
  • Role-based transition rules
  • Clear error messages
  • Event emitted on transition
  • Tests for invalid transitions

TASK-019: Add Audit Logging for Permission Denials

Type: Feature Priority: P2 Effort: 0.5 day Status: pending

Problem: No audit trail when permissions are denied. The Auditor and CEO need visibility into attempted unauthorized actions.

Required Implementation:

  1. Audit Logger:
# roboco/services/audit.py
class AuditService:
    async def log_permission_denial(
        self,
        agent_id: UUID,
        action: str,
        resource: str,
        resource_id: UUID | None,
        reason: str,
    ) -> None:
        """Log a permission denial for audit purposes."""
        logger.warning(
            "Permission denied",
            agent_id=str(agent_id),
            action=action,
            resource=resource,
            resource_id=str(resource_id) if resource_id else None,
            reason=reason,
        )

        # Store in database for Auditor visibility
        await self.db.execute(
            insert(AuditLog).values(
                agent_id=agent_id,
                action=action,
                resource=resource,
                resource_id=resource_id,
                reason=reason,
                timestamp=datetime.now(UTC),
            )
        )
  1. Integration with permission checks:
# In permission service
if not allowed:
    await audit_service.log_permission_denial(
        agent_id=agent_id,
        action=action,
        resource="task",
        resource_id=task_id,
        reason="Role not permitted for this action",
    )
    raise PermissionDeniedError(...)

Files to Create:

  • roboco/services/audit.py
  • roboco/db/tables.py (add AuditLog table)

Acceptance Criteria:

  • All denials logged
  • Audit log queryable
  • Auditor can view audit logs
  • Structured log format

TASK-020: Merge Permission Systems

Type: Refactor Priority: P2 Effort: 1 day Status: pending

Problem: Two parallel permission systems exist:

  1. agents_config.py - String-based (agent slugs)
  2. services/permissions.py - UUID-based

This creates confusion and potential inconsistencies.

Required Implementation:

  1. Consolidate to single source of truth:
# agents_config.py remains the config
# permissions.py becomes the runtime enforcement

class PermissionService:
    def __init__(self):
        # Load from agents_config at init
        self.channel_access = CHANNEL_ACCESS
        self.notification_perms = NOTIFICATION_PERMISSIONS
        self.task_perms = TASK_PERMISSIONS

    async def can_access_channel(
        self,
        agent_id: UUID,
        channel_slug: str,
        access_type: str,
    ) -> bool:
        agent = await self._get_agent(agent_id)
        return self._check_channel_access(
            agent.slug,  # Convert UUID to slug
            channel_slug,
            access_type,
        )
  1. Remove duplicate logic
  2. Add caching for lookups

Files to Modify:

  • roboco/services/permissions.py
  • roboco/enforcement/*.py (use service)

Acceptance Criteria:

  • Single permission system
  • Config in agents_config.py
  • Runtime in PermissionService
  • No duplicate logic
  • Tests pass

TASK-021: Fix OptimalService Temp File Workaround

Type: Bugfix Priority: P2 Effort: 0.5 day Status: pending

Problem: OptimalService uses temporary files for document ingestion, losing metadata.

Current Code:

# Creates temp files which lose document metadata
with tempfile.NamedTemporaryFile(...) as f:
    f.write(content)
    await self.ingest_file(f.name)

Fix: Use in-memory document ingestion:

async def ingest_document(
    self,
    content: str,
    metadata: dict,
    doc_type: str,
) -> None:
    """Ingest document directly without temp file."""
    doc = Document(
        content=content,
        metadata=metadata,
        doc_type=doc_type,
    )
    await self.vector_store.add_document(doc)

Files to Modify:

  • roboco/services/optimal.py

Acceptance Criteria:

  • No temp files created
  • Metadata preserved
  • Existing functionality unchanged
  • Tests pass

Sprint 4: LOW - Polish

Priority: P3 | Final touches

ID Title Type Priority Effort Status Blocks
TASK-022 Generate blueprint prompt files feature P3 1 day cancelled Prompts already embedded in agent factories
TASK-023 Add missing API endpoints feature P3 0.5 day completed -
TASK-024 Comprehensive test coverage test P3 1 day cancelled No test infrastructure exists
TASK-025 Final blueprint audit docs P3 0.5 day completed 96% compliance

Task Details


TASK-022: Generate Blueprint Prompt Files

Type: Feature Priority: P3 Effort: 1 day Status: pending

Problem: Agents use generic system prompts. Blueprint specifies role-specific prompts should be in agents/blueprints/*.md.

Required Implementation:

Create blueprint files for each role:

  • roboco/agents/blueprints/developer.md
  • roboco/agents/blueprints/qa.md
  • roboco/agents/blueprints/cell_pm.md
  • roboco/agents/blueprints/main_pm.md
  • roboco/agents/blueprints/documenter.md
  • roboco/agents/blueprints/product_owner.md
  • roboco/agents/blueprints/head_marketing.md
  • roboco/agents/blueprints/auditor.md
  • roboco/agents/blueprints/designer.md

Each file contains:

  1. Role description
  2. Responsibilities
  3. Permissions summary
  4. Communication guidelines
  5. Workflow phases
  6. Example interactions

Files to Create:

  • roboco/agents/blueprints/*.md (9 files)

Files to Modify:

  • roboco/agents/base.py (load blueprint for role)

Acceptance Criteria:

  • All 9 role blueprints created
  • Agents load appropriate blueprint
  • Blueprints match CLAUDE.md spec

TASK-023: Add Missing API Endpoints

Type: Feature Priority: P3 Effort: 0.5 day Status: pending

Problem: Audit identified missing endpoints:

  • GET /channels/{id}/groups
  • POST /prompts
  • POST /tokens/estimate

Required Implementation:

  1. Channel groups endpoint:
@router.get("/{channel_id}/groups")
async def get_channel_groups(channel_id: UUID) -> list[GroupResponse]:
    """Get all groups in a channel."""
    ...
  1. Prompt templates (if needed):
@router.post("/prompts")
async def create_prompt_template(...):
    """Create a reusable prompt template."""
    ...
  1. Token estimation:
@router.post("/tokens/estimate")
async def estimate_tokens(content: str) -> TokenEstimateResponse:
    """Estimate token count for content."""
    ...

Files to Modify:

  • roboco/api/routes/channels.py
  • roboco/api/routes/optimal.py

Acceptance Criteria:

  • All missing endpoints added
  • Documented in OpenAPI
  • Tests added

TASK-024: Comprehensive Test Coverage

Type: Test Priority: P3 Effort: 1 day Status: pending

Problem: Need to ensure 80% test coverage as specified in CLAUDE.md.

Required Implementation:

  1. Run coverage report
  2. Identify gaps
  3. Add tests for:
    • Permission enforcement
    • Messaging service
    • Notification delivery
    • State transitions
    • New endpoints

Acceptance Criteria:

  • 80%+ test coverage
  • All critical paths tested
  • Integration tests for workflows

TASK-025: Final Blueprint Audit

Type: Documentation Priority: P3 Effort: 0.5 day Status: pending

Problem: After all fixes, need to verify 100% blueprint compliance.

Required Implementation:

  1. Re-run comprehensive audit
  2. Document any remaining gaps
  3. Update blueprint if implementation improved on spec
  4. Create compliance report

Acceptance Criteria:

  • 100% blueprint compliance verified
  • Gaps documented (or confirmed fixed)
  • Compliance report generated

Quick Reference

Task State Transitions

pending → in_progress → verifying → completed
              ↓
           blocked

Priority Guide

  • P0: Critical - security issue, blocking production
  • P1: High - blocking other work, sprint priority
  • P2: Medium - normal priority, scheduled
  • P3: Low - nice to have, polish

Notes

  1. Sprint 1 is non-negotiable - These are security fixes that must be completed before any production use.

  2. Sprint 2 enables agent communication - Without the Messaging service, agents cannot communicate properly.

  3. Sprints 3-4 can be parallelized - Once Sprint 1 & 2 are done, remaining work can happen in parallel.

  4. Each task should create its own task directory - Following the pattern in .tasks/active/TASK-XXX-slug/.