--- phase: 16-api-migration plan: 03 type: execute wave: 1 depends_on: [] files_modified: [n8n-update.json] autonomous: true must_haves: truths: - "User can update a single container and sees 'updated: old_version -> new_version' message" - "User sees 'already up to date' when no update is available" - "User sees error message when update fails (pull error, container not found)" - "Update success/failure messages sent via both text and inline keyboard response modes" artifacts: - path: "n8n-update.json" provides: "Single container update via Unraid GraphQL updateContainer mutation" contains: "updateContainer" key_links: - from: "n8n-update.json mutation node" to: "Unraid GraphQL API" via: "POST updateContainer mutation" pattern: "updateContainer" - from: "n8n-update.json" to: "Telegram response nodes" via: "Format Update Success/No Update/Error Code nodes" pattern: "Format.*Result|Format.*Update|Format.*Error" --- Replace the 5-step Docker update flow in n8n-update.json with a single Unraid GraphQL `updateContainer` mutation. Purpose: The most complex workflow migration. Docker requires 5 sequential steps (inspect→stop→remove→create→start+cleanup) to update a container. Unraid's `updateContainer` mutation does all this atomically. This dramatically simplifies the workflow from 34 nodes to ~15-18 nodes. Output: n8n-update.json with single `updateContainer` mutation replacing the 5-step Docker flow, 60-second timeout for large image pulls, and identical user-facing messages (success, no-update, error). @/home/luc/.claude/get-shit-done/workflows/execute-plan.md @/home/luc/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/16-api-migration/16-RESEARCH.md @.planning/phases/15-infrastructure-foundation/15-01-SUMMARY.md @.planning/phases/15-infrastructure-foundation/15-02-SUMMARY.md @n8n-update.json @n8n-workflow.json (for Phase 15 utility node code — Container ID Registry, GraphQL Response Normalizer, Error Handler) @ARCHITECTURE.md Task 1: Replace container lookup and 5-step Docker update with single GraphQL mutation n8n-update.json Completely restructure n8n-update.json to replace the 5-step Docker update flow with a single `updateContainer` GraphQL mutation. The current 34-node workflow has these stages: **Current flow (to be replaced):** 1. Container lookup: Has Container ID? → Get All Containers → Resolve Container ID 2. Image inspection: Inspect Container → Parse Container Config 3. Image pull: Pull Image (Execute Command via docker pull) → Check Pull Response → Check Pull Success 4. Digest comparison: Inspect New Image → Compare Digests → Check If Update Needed 5. Container recreation: Stop → Remove → Build Create Body → Create → Parse Create Response → Start 6. Messaging: Format Success/No Update/Error → Check Response Mode → Send Inline/Text → Return **New flow (replacement):** **Stage 1: Container lookup** (similar to Plan 02 pattern) - Keep "When executed by another workflow" trigger (unchanged) - Keep "Has Container ID?" IF node (unchanged) - Replace "Get All Containers" with GraphQL query: POST `={{ $env.UNRAID_HOST }}/graphql`, body `{"query": "query { docker { containers { id names state image imageId } } }"}`, timeout 15000ms - Add GraphQL Response Normalizer after query - Add Container ID Registry update after normalizer - Update "Resolve Container ID" to also output `unraidId` and `currentImageId` (from `imageId` field in normalized response for later comparison) **Stage 2: Pre-update state capture** (new Code node) - "Capture Pre-Update State" Code node: Extracts `unraidId`, `containerName`, `currentImageId`, `currentImage` from resolved container data. Passes through `chatId`, `messageId`, `responseMode`, `correlationId`. **Stage 3: Update mutation** (replaces stages 3-5 above) - "Build Update Mutation" Code node: Constructs GraphQL mutation body: ```javascript const data = $input.item.json; return { json: { ...data, query: `mutation { docker { updateContainer(id: "${data.unraidId}") { id state image imageId } } }` }}; ``` - "Update Container" HTTP Request node: - POST `={{ $env.UNRAID_HOST }}/graphql` - Body: from $json (query field) - Headers: `Content-Type: application/json`, `x-api-key: ={{ $env.UNRAID_API_KEY }}` - **Timeout: 60000ms (60 seconds)** — container updates pull images which can take 30+ seconds for large images - Error handling: `continueRegularOutput` - "Handle Update Response" Code node (replaces Compare Digests + Check If Update Needed): ```javascript const response = $input.item.json; const prevData = $('Capture Pre-Update State').item.json; // Check for GraphQL errors if (response.errors) { const error = response.errors[0]; return { json: { success: false, error: true, errorMessage: error.message, ...prevData } }; } // Extract updated container from response const updated = response.data?.docker?.updateContainer; if (!updated) { return { json: { success: false, error: true, errorMessage: 'No response from update mutation', ...prevData } }; } // Compare imageId to determine if update happened const newImageId = updated.imageId || ''; const oldImageId = prevData.currentImageId || ''; const wasUpdated = (newImageId !== oldImageId); return { json: { success: true, needsUpdate: wasUpdated, // matches existing Check If Update Needed output field name updated: wasUpdated, containerName: prevData.containerName, currentVersion: prevData.currentImage, newVersion: updated.image, currentImageId: oldImageId, newImageId: newImageId, chatId: prevData.chatId, messageId: prevData.messageId, responseMode: prevData.responseMode, correlationId: prevData.correlationId }}; ``` **Stage 4: Route result** (simplified) - "Check If Updated" IF node: Checks `$json.needsUpdate === true` - True → "Format Update Success" (existing node — may need minor field name adjustments) - False → "Format No Update Needed" (existing node — may need minor field name adjustments) - Error path: from "Handle Update Response" error output → "Format Pull Error" (reuse existing error formatting) **Stage 5: Messaging** (preserve existing) - Keep ALL existing messaging nodes unchanged: - "Format Update Success" / "Check Response Mode (Success)" / "Send Inline Success" / "Send Text Success" / "Return Success" - "Format No Update Needed" / "Check Response Mode (No Update)" / "Send Inline No Update" / "Send Text No Update" / "Return No Update" - "Format Pull Error" / "Check Response Mode (Error)" / "Send Inline Error" / "Send Text Error" / "Return Error" - These 15 messaging nodes stay exactly as they are. The "Handle Update Response" Code node formats its output to match what these nodes expect. **Nodes to REMOVE** (no longer needed — Docker-specific operations replaced by single mutation): - "Inspect Container" (HTTP Request) - "Parse Container Config" (Code) - "Pull Image" (Execute Command) - "Check Pull Response" (Code) - "Check Pull Success" (IF) - "Inspect New Image" (HTTP Request) - "Compare Digests" (Code) - "Check If Update Needed" (IF) - "Stop Container" (HTTP Request) - "Remove Container" (HTTP Request) - "Build Create Body" (Code) - "Create Container" (HTTP Request) - "Parse Create Response" (Code) - "Start Container" (HTTP Request) - "Remove Old Image (Success)" (HTTP Request) That's 15 nodes removed, replaced by ~4 new nodes (Normalizer, Registry Update, Build Mutation, Handle Response). Plus updated query and resolve nodes. Net reduction from 34 to ~19 nodes. **Adjust "Format Update Success"** Code node if needed: It currently references `$('Parse Create Response').item.json` for container data. Update to reference `$('Handle Update Response').item.json` instead. The output fields (`containerName`, `currentVersion`, `newVersion`, `chatId`, `messageId`, `responseMode`, `correlationId`) must match what Format Update Success expects. **Adjust "Format No Update Needed"** similarly: Currently references `$('Check If Update Needed').item.json`. Update reference to `$('Handle Update Response').item.json`. **Adjust "Format Pull Error"** similarly: Currently references `$('Check Pull Success').item.json`. Update reference to `$('Handle Update Response').item.json`. Field mapping: `errorMessage` stays as-is. **CRITICAL: Update Container ID Registry after mutation** — Container updates recreate containers with new IDs. After successful update, the old PrefixedID is invalid. Add registry cache refresh in the success path. However, since we can't easily query the full container list mid-update, rely on the mutation response (which includes the new `id`) and do a targeted registry update for just the updated container. Load n8n-update.json with python3 and verify: 1. Zero "docker-socket-proxy" references 2. Zero "Execute Command" nodes (docker pull removed) 3. Single "updateContainer" mutation HTTP Request node exists with 60000ms timeout 4. Container lookup uses GraphQL query with normalizer 5. Handle Update Response properly routes to existing Format Success/No Update/Error nodes 6. All 15 messaging nodes (Format/Check/Send/Return) are present 7. Node count reduced from 34 to ~19 8. All connections valid (no references to deleted nodes) 9. Push to n8n via API and verify HTTP 200 n8n-update.json uses single updateContainer GraphQL mutation instead of 5-step Docker flow. 60-second timeout accommodates large image pulls. Format Success/No Update/Error messaging nodes preserved (with updated node references). Container ID Registry refreshed after update. Workflow reduced from 34 to ~19 nodes. Pushed to n8n successfully. 1. Zero "docker-socket-proxy" references in n8n-update.json 2. Zero "Execute Command" nodes (no docker pull) 3. Single updateContainer mutation with 60s timeout 4. ImageId comparison determines if update happened (not image digest) 5. All 3 response paths work: success, no-update, error 6. Format Result Code nodes reference correct upstream nodes 7. Push to n8n with HTTP 200 - n8n-update.json has zero Docker socket proxy references - Single GraphQL mutation replaces 5-step Docker flow - 60-second timeout for update mutation (accommodates large image pulls) - Success/no-update/error messaging identical to user - Container ID Registry refreshed after successful update - Node count reduced by ~15 nodes - Workflow valid and pushed to n8n After completion, create `.planning/phases/16-api-migration/16-03-SUMMARY.md`