{
  "name": "TaskManager Tool: add_task",
  "nodes": [
    {
      "parameters": {},
      "id": "c3a0b003-0001-4000-8000-000000000001",
      "name": "Execute Workflow Trigger",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "typeVersion": 1.1,
      "position": [
        200,
        400
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// ============================================================\n// VALIDATION LAYER \u2014 Contract enforcement\n// Every field is validated before touching the database.\n// Returns structured error objects, never crashes.\n// ============================================================\n\nconst input = $input.first().json;\nconst errors = [];\nconst warnings = [];\n\n// --- Required field validation ---\nif (!input.title || input.title.trim().length === 0) {\n  errors.push('INVARIANT_VIOLATION: title is required and cannot be empty');\n}\nif (input.title && input.title.trim().length > 120) {\n  errors.push('CONSTRAINT: title must be <= 120 characters, got ' + input.title.length);\n}\n\n// --- Importance score: must be in [0.0, 1.0] ---\nconst importance = parseFloat(input.importance_score);\nif (isNaN(importance) || importance < 0 || importance > 1) {\n  errors.push('RANGE_VIOLATION: importance_score must be float in [0.0, 1.0], got: ' + input.importance_score);\n}\n\n// --- Category: must be in allowed set ---\nconst VALID_CATEGORIES = ['work', 'personal', 'health', 'learning', 'finance', 'admin'];\nconst category = (input.category || '').toLowerCase().trim();\nif (!VALID_CATEGORIES.includes(category)) {\n  warnings.push('Category \"' + input.category + '\" not in allowed set, defaulting to \"personal\"');\n}\n\n// --- Context: must be valid GTD context ---\nconst VALID_CONTEXTS = ['@home', '@computer', '@phone', '@errands'];\nconst context = (input.context || '').toLowerCase().trim();\nif (context && !VALID_CONTEXTS.includes(context)) {\n  warnings.push('Context \"' + input.context + '\" not recognized, defaulting to \"@computer\"');\n}\n\n// --- Deadline: validate ISO 8601 if provided ---\nlet deadline = input.deadline;\nif (deadline && deadline !== 'null' && deadline !== '') {\n  const d = new Date(deadline);\n  if (isNaN(d.getTime())) {\n    errors.push('FORMAT_VIOLATION: deadline must be valid ISO 8601, got: ' + deadline);\n  }\n} else {\n  deadline = null;\n}\n\n// --- Estimated minutes: must be positive integer ---\nconst estMinutes = parseInt(input.estimated_minutes);\nif (isNaN(estMinutes) || estMinutes < 0) {\n  warnings.push('estimated_minutes invalid, defaulting to 15');\n}\n\n// --- Recurrence rule validation ---\nconst VALID_RECURRENCE_PREFIXES = ['daily', 'weekly', 'monthly', 'none'];\nconst recurrence = (input.recurrence_rule || 'none').toLowerCase().trim();\nconst recurrencePrefix = recurrence.split(':')[0];\nif (!VALID_RECURRENCE_PREFIXES.includes(recurrencePrefix)) {\n  warnings.push('recurrence_rule \"' + recurrence + '\" not recognized, defaulting to \"none\"');\n}\n\n// --- Depends_on: validate format (comma-separated integers) ---\nconst dependsOn = (input.depends_on || '').trim();\nif (dependsOn && dependsOn !== '') {\n  const depIds = dependsOn.split(',').map(d => d.trim());\n  for (const depId of depIds) {\n    if (isNaN(parseInt(depId))) {\n      errors.push('REFERENCE_VIOLATION: depends_on contains non-integer: \"' + depId + '\"');\n    }\n  }\n}\n\n// --- If errors exist, return failure ---\nif (errors.length > 0) {\n  return [{\n    json: {\n      success: false,\n      errors: errors,\n      warnings: warnings,\n      validation_timestamp: new Date().toISOString()\n    }\n  }];\n}\n\n// --- Sanitize and normalize ---\nconst sanitized = {\n  title: input.title.trim().charAt(0).toUpperCase() + input.title.trim().slice(1),\n  description: (input.description || '').trim(),\n  deadline: deadline,\n  category: VALID_CATEGORIES.includes(category) ? category : 'personal',\n  importance_score: importance,\n  estimated_minutes: isNaN(estMinutes) || estMinutes < 0 ? 15 : estMinutes,\n  context: VALID_CONTEXTS.includes(context) ? context : '@computer',\n  recurrence_rule: VALID_RECURRENCE_PREFIXES.includes(recurrencePrefix) ? recurrence : 'none',\n  depends_on: dependsOn,\n  tags: (input.tags || '').trim(),\n  raw_input: (input.raw_input || '').trim(),\n  status: 'todo',\n  created_at: new Date().toISOString(),\n  completed_at: '',\n  validation_passed: true,\n  warnings: warnings\n};\n\nreturn [{ json: sanitized }];"
      },
      "id": "c3a0b003-0001-4000-8000-000000000002",
      "name": "Validation Layer",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        420,
        400
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "validation-check",
              "leftValue": "={{ $json.validation_passed }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "c3a0b003-0001-4000-8000-000000000003",
      "name": "Validation Gate",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        640,
        400
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// ============================================================\n// EISENHOWER COMPUTATION ENGINE\n// Computes urgency from deadline, derives quadrant and priority.\n// This is the mathematical core of the system.\n// ============================================================\n\nconst MAX_HORIZON_HOURS = 336; // 2-week planning horizon\nconst now = new Date();\nconst task = $input.first().json;\n\n// --- Compute urgency_score as f(deadline, now) ---\nlet urgencyScore = 0.3; // default for no-deadline\nif (task.deadline && task.deadline !== 'null') {\n  const deadline = new Date(task.deadline);\n  const hoursRemaining = (deadline - now) / (1000 * 60 * 60);\n  urgencyScore = Math.max(0, Math.min(1, 1 - (hoursRemaining / MAX_HORIZON_HOURS)));\n}\n\n// --- Eisenhower Matrix Derivation ---\nconst u = urgencyScore;\nconst imp = task.importance_score;\nlet quadrant, priority, actionLabel;\n\nif (u >= 0.6 && imp >= 0.6) {\n  quadrant = 'Q1_do_first';\n  priority = 1;\n  actionLabel = 'Urgent + Important -> Act immediately';\n} else if (u < 0.6 && imp >= 0.6) {\n  quadrant = 'Q2_schedule';\n  priority = 2;\n  actionLabel = 'Not Urgent + Important -> Schedule for focused time';\n} else if (u >= 0.6 && imp < 0.6) {\n  quadrant = 'Q3_delegate';\n  priority = 3;\n  actionLabel = 'Urgent + Not Important -> Delegate or batch';\n} else {\n  quadrant = 'Q4_eliminate';\n  priority = 4;\n  actionLabel = 'Not Urgent + Not Important -> Consider dropping';\n}\n\nconst enriched = {\n  ...task,\n  urgency_score: Math.round(urgencyScore * 100) / 100,\n  eisenhower_quadrant: quadrant,\n  priority: priority,\n  action_label: actionLabel\n};\n\n// Remove internal fields before DB write\ndelete enriched.validation_passed;\ndelete enriched.warnings;\n\nreturn [{ json: enriched }];"
      },
      "id": "c3a0b003-0001-4000-8000-000000000004",
      "name": "Eisenhower Compute",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        860,
        300
      ]
    },
    {
      "parameters": {
        "operation": "create",
        "tableId": "DATA_TABLE_ID_TASKS",
        "columns": {
          "mappingMode": "autoMapInputData"
        },
        "options": {}
      },
      "id": "c3a0b003-0001-4000-8000-000000000005",
      "name": "Data Table: Insert Task",
      "type": "n8n-nodes-base.dataTable",
      "typeVersion": 1,
      "position": [
        1080,
        300
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// ============================================================\n// RESPONSE BUILDER \u2014 Structured output for the agent\n// ============================================================\n\nconst task = $input.first().json;\n\nconst response = {\n  success: true,\n  operation: 'task_created',\n  task: {\n    id: task.id,\n    title: task.title,\n    description: task.description,\n    status: task.status,\n    priority: task.priority,\n    eisenhower_quadrant: task.eisenhower_quadrant,\n    urgency_score: task.urgency_score,\n    importance_score: task.importance_score,\n    category: task.category,\n    deadline: task.deadline,\n    estimated_minutes: task.estimated_minutes,\n    context: task.context,\n    recurrence_rule: task.recurrence_rule,\n    depends_on: task.depends_on,\n    tags: task.tags,\n    created_at: task.created_at\n  },\n  metadata: {\n    action_recommendation: task.action_label || 'No recommendation',\n    is_recurring: task.recurrence_rule && task.recurrence_rule !== 'none',\n    has_dependencies: task.depends_on && task.depends_on !== '',\n    creation_timestamp: new Date().toISOString()\n  }\n};\n\nreturn [{ json: response }];"
      },
      "id": "c3a0b003-0001-4000-8000-000000000006",
      "name": "Build Response",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1300,
        300
      ]
    }
  ],
  "connections": {
    "Execute Workflow Trigger": {
      "main": [
        [
          {
            "node": "Validation Layer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validation Layer": {
      "main": [
        [
          {
            "node": "Validation Gate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validation Gate": {
      "main": [
        [
          {
            "node": "Eisenhower Compute",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Eisenhower Compute": {
      "main": [
        [
          {
            "node": "Data Table: Insert Task",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Data Table: Insert Task": {
      "main": [
        [
          {
            "node": "Build Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "tags": [
    {
      "name": "Tool",
      "id": "4"
    },
    {
      "name": "Task Manager",
      "id": "2"
    }
  ]
}