{
  "name": "TaskManager Tool: complete_task",
  "nodes": [
    {
      "parameters": {},
      "id": "c4a0b004-0001-4000-8000-000000000001",
      "name": "Execute Workflow Trigger",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "typeVersion": 1.1,
      "position": [
        200,
        400
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// ============================================================\n// INPUT VALIDATION \u2014 Verify task_id is valid integer\n// ============================================================\n\nconst input = $input.first().json;\nconst taskId = parseInt(input.task_id);\n\nif (isNaN(taskId) || taskId <= 0) {\n  return [{\n    json: {\n      success: false,\n      error: 'INVARIANT_VIOLATION: task_id must be a positive integer, got: ' + input.task_id,\n      timestamp: new Date().toISOString()\n    }\n  }];\n}\n\nreturn [{ json: { task_id: taskId, validated: true } }];"
      },
      "id": "c4a0b004-0001-4000-8000-000000000002",
      "name": "Validate Input",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        420,
        400
      ]
    },
    {
      "parameters": {
        "operation": "read",
        "tableId": "DATA_TABLE_ID_TASKS",
        "returnAll": true,
        "options": {}
      },
      "id": "c4a0b004-0001-4000-8000-000000000003",
      "name": "Data Table: Read All Tasks",
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1,
      "position": [
        640,
        400
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// ============================================================\n// TASK LOOKUP + STATUS CHECK\n// Find target task, verify it exists and isn't already done.\n// ============================================================\n\nconst taskId = $('Validate Input').first().json.task_id;\nconst allTasks = $input.all().map(item => item.json);\n\n// Find target task\nconst targetTask = allTasks.find(t => parseInt(t.id) === taskId);\n\nif (!targetTask) {\n  return [{\n    json: {\n      success: false,\n      error: 'NOT_FOUND: Task #' + taskId + ' does not exist in the database.',\n      available_tasks: allTasks\n        .filter(t => t.status !== 'done')\n        .map(t => ({ id: t.id, title: t.title, status: t.status }))\n        .slice(0, 10),\n      timestamp: new Date().toISOString()\n    }\n  }];\n}\n\nif (targetTask.status === 'done') {\n  return [{\n    json: {\n      success: false,\n      error: 'IDEMPOTENCY_CHECK: Task #' + taskId + ' (\"' + targetTask.title + '\") is already marked as done (completed at ' + targetTask.completed_at + '). No action taken.',\n      task: targetTask,\n      timestamp: new Date().toISOString()\n    }\n  }];\n}\n\n// Task is valid and not yet complete\nreturn [{\n  json: {\n    success: true,\n    target_task: targetTask,\n    all_tasks: allTasks,\n    proceed: true\n  }\n}];"
      },
      "id": "c4a0b004-0001-4000-8000-000000000004",
      "name": "Lookup & Status Check",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        860,
        400
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "proceed-check",
              "leftValue": "={{ $json.proceed }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "c4a0b004-0001-4000-8000-000000000005",
      "name": "Proceed Gate",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1080,
        400
      ]
    },
    {
      "parameters": {
        "mode": "raw",
        "jsonOutput": "={\n  \"id\": {{ $json.target_task.id }},\n  \"status\": \"done\",\n  \"completed_at\": \"{{ new Date().toISOString() }}\"\n}",
        "options": {}
      },
      "id": "c4a0b004-0001-4000-8000-000000000006",
      "name": "Prepare Update",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1300,
        300
      ]
    },
    {
      "parameters": {
        "operation": "update",
        "tableId": "DATA_TABLE_ID_TASKS",
        "columns": {
          "mappingMode": "autoMapInputData"
        },
        "options": {}
      },
      "id": "c4a0b004-0001-4000-8000-000000000007",
      "name": "Data Table: Mark Done",
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1,
      "position": [
        1520,
        300
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// ============================================================\n// DAG TRAVERSAL ENGINE\n// After completing a task, traverse the dependency graph\n// to find tasks that are now unblocked.\n// ============================================================\n\nconst lookupData = $('Lookup & Status Check').first().json;\nconst completedTask = lookupData.target_task;\nconst completedId = String(completedTask.id);\nconst allTasks = lookupData.all_tasks;\nconst now = new Date();\n\n// --- Find newly unblocked tasks ---\nconst unblockedTasks = [];\n\nfor (const task of allTasks) {\n  if (task.status === 'done' || task.status === 'cancelled') continue;\n  if (!task.depends_on || task.depends_on === '') continue;\n\n  const depIds = task.depends_on.split(',').map(d => d.trim());\n  \n  // Only check tasks that depend on the completed task\n  if (!depIds.includes(completedId)) continue;\n\n  // Check if ALL dependencies are now satisfied\n  const allDepsSatisfied = depIds.every(depId => {\n    if (depId === completedId) return true; // just completed\n    const depTask = allTasks.find(t => String(t.id) === depId);\n    return depTask && depTask.status === 'done';\n  });\n\n  if (allDepsSatisfied) {\n    unblockedTasks.push({\n      id: task.id,\n      title: task.title,\n      priority: task.priority,\n      category: task.category\n    });\n  }\n}\n\n// --- Compute completion metrics ---\nconst createdAt = new Date(completedTask.created_at);\nconst completedAt = now;\nconst elapsedMs = completedAt - createdAt;\nconst elapsedHours = Math.round(elapsedMs / (1000 * 60 * 60) * 10) / 10;\nconst elapsedDays = Math.round(elapsedMs / (1000 * 60 * 60 * 24) * 10) / 10;\n\nlet elapsedHuman;\nif (elapsedDays >= 1) {\n  elapsedHuman = elapsedDays + ' days';\n} else {\n  elapsedHuman = elapsedHours + ' hours';\n}\n\n// --- Count completed tasks for productivity metric ---\nconst doneTasks = allTasks.filter(t => t.status === 'done').length + 1; // +1 for this one\nconst totalTasks = allTasks.length;\n\n// --- Check recurrence ---\nlet recurrenceNote = null;\nif (completedTask.recurrence_rule && completedTask.recurrence_rule !== 'none') {\n  recurrenceNote = 'This is a recurring task (' + completedTask.recurrence_rule + '). A new instance should be created.';\n}\n\n// --- Build response ---\nconst response = {\n  success: true,\n  operation: 'task_completed',\n  completed_task: {\n    id: completedTask.id,\n    title: completedTask.title,\n    category: completedTask.category,\n    priority_was: completedTask.priority,\n    eisenhower_was: completedTask.eisenhower_quadrant,\n    created_at: completedTask.created_at,\n    completed_at: completedAt.toISOString()\n  },\n  metrics: {\n    elapsed_human: elapsedHuman,\n    elapsed_hours: elapsedHours,\n    tasks_done_total: doneTasks,\n    tasks_remaining: totalTasks - doneTasks,\n    completion_rate: Math.round((doneTasks / totalTasks) * 100) + '%'\n  },\n  dependency_graph: {\n    unblocked_tasks: unblockedTasks,\n    unblocked_count: unblockedTasks.length\n  },\n  recurrence: recurrenceNote,\n  timestamp: completedAt.toISOString()\n};\n\nreturn [{ json: response }];"
      },
      "id": "c4a0b004-0001-4000-8000-000000000008",
      "name": "DAG Traversal & Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1740,
        300
      ]
    }
  ],
  "connections": {
    "Execute Workflow Trigger": {
      "main": [
        [
          {
            "node": "Validate Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Input": {
      "main": [
        [
          {
            "node": "Data Table: Read All Tasks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data Table: Read All Tasks": {
      "main": [
        [
          {
            "node": "Lookup & Status Check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Lookup & Status Check": {
      "main": [
        [
          {
            "node": "Proceed Gate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Proceed Gate": {
      "main": [
        [
          {
            "node": "Prepare Update",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Prepare Update": {
      "main": [
        [
          {
            "node": "Data Table: Mark Done",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data Table: Mark Done": {
      "main": [
        [
          {
            "node": "DAG Traversal & Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "tags": [
    {
      "name": "Tool",
      "id": "4"
    },
    {
      "name": "Task Manager",
      "id": "2"
    }
  ]
}