Files
unraid-docker-manager/.planning/phases/10-workflow-modularization/10-07-PLAN.md
T
Lucas Berger 5a6b6e3e85 docs(10): create UAT gap closure plan 10-07
Phase 10 UAT revealed 5 gaps:
- Text update race condition (parallel message send)
- Batch update/action data chain broken ($json vs Build Progress Message)
- Logs fuzzy matching missing
- Logs refresh "message not modified" error

10-07-PLAN.md addresses all gaps in 5 tasks.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 15:54:02 -05:00

12 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, gap_closure, must_haves
phase plan type wave depends_on files_modified autonomous gap_closure must_haves
10-workflow-modularization 07 remediation 6
10-06
n8n-workflow.json
true true
truths artifacts key_links
Single text update sends only one result message (no race condition)
Batch update executes via Container Update sub-workflow
Batch actions execute via Container Actions sub-workflow
Container logs text command works with fuzzy matching
Refresh Logs button handles 'message not modified' gracefully
path provides contains
n8n-workflow.json Fixed main workflow with all UAT gaps closed $('Build Progress Message').item.json
from to via
Prepare Text Update Input Execute Text Update Sequential connection (not parallel with Send Text Update Started)
from to via
Prepare Batch Update Input Execute Batch Update Uses $('Build Progress Message').item.json.container
from to via
Prepare Batch Action Input Execute Batch Action Sub-workflow Uses $('Build Progress Message').item.json
Close 5 UAT gaps discovered during user testing of Phase 10 modularization.

Purpose: UAT revealed race conditions, broken data chains, missing fuzzy matching, and unhandled Telegram API errors that prevent the modularized workflow from functioning correctly.

Output: Fully functional modularized workflow with all user-reported issues fixed.

<execution_context> @/home/luc/.claude/get-shit-done/workflows/execute-plan.md @/home/luc/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/phases/10-workflow-modularization/10-06-SUMMARY.md @n8n-workflow.json @n8n-container-logs.json Task 1: Fix text update race condition (remove parallel message) n8n-workflow.json **Problem:** User reported two messages sent for single text update - "already up to date" AND "Updating..." in race condition. Root cause: `Prepare Text Update Input` connects in PARALLEL to both `Send Text Update Started` AND `Execute Text Update`. The sub-workflow also sends a result message.

Current connection (lines 6616-6629):

"Prepare Text Update Input": {
  "main": [
    [
      { "node": "Send Text Update Started", ... },
      { "node": "Execute Text Update", ... }
    ]
  ]
}

Both nodes are in the same array = parallel execution.

Fix: Remove Send Text Update Started from the connection array. The sub-workflow handles all messaging, so the "Updating..." message is redundant.

Steps:

  1. Find the "Prepare Text Update Input" entry in the connections object
  2. Remove the object { "node": "Send Text Update Started", "type": "main", "index": 0 } from the array
  3. Keep only { "node": "Execute Text Update", "type": "main", "index": 0 }

Result connection:

"Prepare Text Update Input": {
  "main": [
    [
      { "node": "Execute Text Update", "type": "main", "index": 0 }
    ]
  ]
}
- Grep for "Prepare Text Update Input" in connections shows only Execute Text Update - No reference to Send Text Update Started in parallel Text update sends only one message (from sub-workflow) Task 2: Fix batch update data chain (use Build Progress Message reference) n8n-workflow.json **Problem:** User reported "Cannot read properties of undefined (reading 'id')" in Prepare Batch Update Input. Root cause: The node uses `$json.container` but receives Telegram API response from `Edit Progress Message`, not the batch data.

Current code (node id: caeae5d6-f9ec-4aa3-83d3-198b6b55be65, lines 4688-4699):

const data = $json;
const container = data.container;

Data flow:

  • Build Progress Message outputs: { container: {...}, chatId, progressMessageId, action, ... }
  • Edit Progress Message sends Telegram API call, outputs Telegram response (no container data)
  • Prepare Batch Update Input receives Telegram response (wrong data!)

Fix: Change to reference Build Progress Message directly instead of relying on $json:

// Prepare input for Container Update sub-workflow
const data = $('Build Progress Message').item.json;
const container = data.container;

// Extract container info
const containerId = container.id || container.Id || '';
const containerName = container.name || container.Name || '';

return {
  json: {
    containerId: containerId,
    containerName: containerName,
    chatId: data.chatId,
    messageId: data.progressMessageId || 0,
    responseMode: "inline"
  }
};

Steps:

  1. Find node with id "caeae5d6-f9ec-4aa3-83d3-198b6b55be65" (Prepare Batch Update Input)
  2. Update the jsCode parameter to use $('Build Progress Message').item.json instead of $json
  3. Keep all other logic the same
  • Grep for "Prepare Batch Update Input" shows $('Build Progress Message').item.json
  • No const data = $json in that node Batch update correctly reads container data from Build Progress Message
Task 3: Fix batch action data chain (use Build Progress Message reference) n8n-workflow.json **Problem:** Same as Task 2 - "Cannot read properties of undefined (reading 'id')" in Prepare Batch Action Input. Uses `$json.container` but receives Telegram response.

Current code (node id: 958f19ef-249b-42ca-8a29-ecb91548f1dd, lines 4732-4743):

const data = $json;
const container = data.container;
const action = data.action;

Fix: Change to reference Build Progress Message directly:

// Prepare input for Container Actions sub-workflow
const data = $('Build Progress Message').item.json;
const container = data.container;
const action = data.action;

// Extract container info
const containerId = container.id || container.Id || '';
const containerName = container.name || container.Name || '';

return {
  json: {
    containerId: containerId,
    containerName: containerName,
    action: action,
    chatId: data.chatId,
    messageId: data.progressMessageId || 0,
    responseMode: "inline"
  }
};

Steps:

  1. Find node with id "958f19ef-249b-42ca-8a29-ecb91548f1dd" (Prepare Batch Action Input)
  2. Update the jsCode parameter to use $('Build Progress Message').item.json instead of $json
  3. Keep all other logic the same
  • Grep for "Prepare Batch Action Input" shows $('Build Progress Message').item.json
  • No const data = $json in that node Batch action correctly reads container data from Build Progress Message
Task 4: Add fuzzy matching to logs text command and fix Send Logs Response chatId n8n-workflow.json **Problem:** Two issues with logs text command: 1. Fuzzy matching not working - exact name required (unlike update/start/stop) 2. Send Logs Response uses `$json.chatId` but sub-workflow returns `{success, message, containerName}` without chatId

Current flow:

Parse Logs Command -> Prepare Text Logs Input -> Execute Text Logs -> Send Logs Response

Issue 1 - No fuzzy matching: The logs flow passes containerQuery directly to sub-workflow which does exact match only. Other commands (update/start/stop) have Docker query + fuzzy matching nodes BEFORE calling sub-workflow.

Issue 2 - Missing chatId: Send Logs Response (node id: telegram-send-logs) uses:

"chatId": "={{ $json.chatId }}"

But $json is the sub-workflow output which doesn't include chatId.

Fix for Send Logs Response: Change chatId to reference the input node that HAS chatId:

"chatId": "={{ $('Prepare Text Logs Input').item.json.chatId }}"

Note on fuzzy matching: Adding full fuzzy matching infrastructure (Docker query + Match Container node + Check Match Count switch) would require significant changes. For now, the sub-workflow already does name lookup via Docker API. The issue is it requires EXACT normalized name.

Alternative simpler fix: Update the logs sub-workflow's "Find Container" node to use .includes() instead of exact === match. This provides fuzzy matching without restructuring main workflow.

Steps:

  1. In n8n-workflow.json, find node id "telegram-send-logs" (Send Logs Response)

  2. Change chatId from ={{ $json.chatId }} to ={{ $('Prepare Text Logs Input').item.json.chatId }}

  3. In n8n-container-logs.json, find "Find Container" node (id: 52dd705b-dd3b-4fdc-8484-276845857ad0)

  4. Change the filter logic from exact match to includes:

    • FROM: normalizeName(c.Names[0]) === containerName
    • TO: normalizeName(c.Names[0]).includes(containerName)
  5. Add handling for multiple matches (throw error suggesting which containers match)

  • Send Logs Response uses $('Prepare Text Logs Input').item.json.chatId
  • n8n-container-logs.json Find Container uses .includes() for matching Logs command works with fuzzy matching and Send Logs Response has valid chatId
Task 5: Handle "message not modified" error in logs refresh n8n-workflow.json **Problem:** User reported "message is not modified" error when refreshing logs that haven't changed. Telegram API rejects editMessageText when content is identical.

Current flow:

Prepare Inline Logs Input -> Execute Inline Logs -> Format Inline Logs Result -> Send Logs Result

Send Logs Result (node id: http-send-logs-result): Uses httpRequest to call editMessageText API.

Fix options: A. Add timestamp to force content difference (changes UX - shows timestamp) B. Add error handling node after Send Logs Result to catch this specific error C. Use Telegram node with "Continue On Fail" and handle error in next node

Recommended: Option A - Add timestamp to logs display

This is the cleanest solution because:

  • Logs refresh SHOULD show when they were fetched
  • Always succeeds (no error handling needed)
  • Better UX - user knows logs are fresh

Implementation in Format Inline Logs Result (node id: b1800598-1ff6-4da3-8506-4e4e8127f902):

Update the jsCode to add timestamp to the message:

// Format logs result for inline keyboard display
const result = $json;
const data = $('Prepare Inline Logs Input').item.json;

const containerName = result.containerName;

// Add timestamp to prevent "message not modified" error on refresh
const timestamp = new Date().toLocaleTimeString('en-US', {
  hour: '2-digit',
  minute: '2-digit',
  second: '2-digit',
  hour12: false
});

// Build inline keyboard
const keyboard = [
  [
    { text: '\ud83d\udd04 Refresh Logs', callback_data: `action:logs:${containerName}` },
    { text: '\u2b06\ufe0f Update', callback_data: `action:update:${containerName}` }
  ],
  [
    { text: '\u25c0\ufe0f Back to List', callback_data: 'list:0' }
  ]
];

// Append timestamp to message
const messageWithTimestamp = result.message + `\n\n<i>Updated: ${timestamp}</i>`;

return {
  json: {
    chatId: data.chatId,
    messageId: data.messageId,
    text: messageWithTimestamp,
    reply_markup: { inline_keyboard: keyboard }
  }
};

Steps:

  1. Find node with id "b1800598-1ff6-4da3-8506-4e4e8127f902" (Format Inline Logs Result)
  2. Update jsCode to add timestamp generation
  3. Append timestamp to the message text before returning
  • Format Inline Logs Result includes timestamp generation code
  • Output text includes "Updated: HH:MM:SS" suffix Logs refresh always succeeds (timestamp ensures content is different)
1. `grep -A5 '"Prepare Text Update Input"' n8n-workflow.json` shows only Execute Text Update connection 2. `grep 'Build Progress Message' n8n-workflow.json | grep -c 'Prepare Batch'` returns 2 (update + action) 3. `grep 'Prepare Text Logs Input' n8n-workflow.json` shows chatId reference in Send Logs Response 4. `grep 'includes' n8n-container-logs.json` shows fuzzy matching in Find Container 5. `grep 'timestamp' n8n-workflow.json` shows timestamp in Format Inline Logs Result

<success_criteria>

  • Text update sends single message (no race condition)
  • Batch update and batch actions execute without "undefined" errors
  • Logs command works with partial container names
  • Logs refresh never fails with "message not modified" error </success_criteria>
After completion, create `.planning/phases/10-workflow-modularization/10-07-SUMMARY.md`