Files
unraid-docker-manager/.planning/phases/08-inline-keyboard-infrastructure/08-02-PLAN.md
T
Lucas Berger ecd02a4b0e docs(08): create inline keyboard infrastructure plans
Phase 8: Inline Keyboard Infrastructure
- 3 plans in 3 waves (sequential dependency)
- Plan 01: Container list keyboard and submenu navigation
- Plan 02: Action execution and confirmation flow
- Plan 03: Progress feedback and completion messages

Covers KEY-01 through KEY-05 requirements.
Ready for execution.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 11:44:46 -05:00

11 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
phase plan type wave depends_on files_modified autonomous must_haves
08-inline-keyboard-infrastructure 02 execute 2
08-01
n8n-workflow.json
true
truths artifacts key_links
Tapping Start button starts a stopped container
Tapping Stop button shows confirmation dialog
Tapping Update button shows confirmation dialog
Tapping Restart button executes restart immediately
Confirming Stop/Update executes the action
Cancelling returns to container submenu
Confirmation expires after 30 seconds
path provides contains
n8n-workflow.json Action execution routing and confirmation flow Route Action Type
path provides contains
n8n-workflow.json Confirmation keyboard builder Build Confirmation
from to via pattern
Route Callback Route Action Type action: callback routing action:
from to via pattern
Route Action Type existing container ops start/stop/restart/update wiring containers/.*/start
Wire action buttons to container operations and add confirmation flow for dangerous actions.

Purpose: When users tap action buttons in the container submenu, the corresponding action executes. Stop and Update require confirmation (per user decision). Start and Restart execute immediately.

Output: Updated n8n-workflow.json with action routing, confirmation flow, and wiring to existing container operations.

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

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/08-inline-keyboard-infrastructure/08-CONTEXT.md @.planning/phases/08-inline-keyboard-infrastructure/08-RESEARCH.md @.planning/phases/08-inline-keyboard-infrastructure/08-01-SUMMARY.md @n8n-workflow.json Task 1: Route Action Callbacks to Container Operations n8n-workflow.json Add routing to handle `action:{type}:{name}` callbacks and wire to existing container operations.
  1. Update "Parse Callback Data" to recognize action: prefix:

    // Add to existing parsing logic
    if (data.startsWith('action:')) {
      const parts = data.split(':');
      return {
        json: {
          isAction: true,
          actionType: parts[1], // start, stop, restart, update, logs
          containerName: parts[2],
          queryId: callbackQuery.id,
          chatId: callbackQuery.message.chat.id,
          messageId: callbackQuery.message.message_id
        }
      };
    }
    
  2. Add "isAction" output to "Route Callback" switch node:

    • Rule: isAction === true -> output "action"
    • This catches all action callbacks before routing by type
  3. Create Switch node "Route Action Type":

    • Input: from Route Callback "action" output
    • Outputs:
      • "start": actionType === 'start'
      • "restart": actionType === 'restart'
      • "logs": actionType === 'logs'
      • "stop": actionType === 'stop' (needs confirmation)
      • "update": actionType === 'update' (needs confirmation)
  4. For immediate actions (start, restart, logs), wire to existing container operation nodes:

    Start flow:

    • Create Code node "Prepare Start Action":
      const { queryId, chatId, messageId, containerName } = $json;
      return {
        json: {
          queryId,
          chatId,
          messageId,
          containerName,
          // Format for existing Start Container node
          container: containerName
        }
      };
      
    • Answer callback query immediately
    • Wire to existing "Start Container" HTTP Request node
    • After start completes, show success message (handled in Plan 03)

    Restart flow:

    • Similar to start, wire to existing "Restart Container" node

    Logs flow:

    • Wire to existing "Get Logs" flow
    • Logs may need special handling (send as new message, not edit)
  5. For dangerous actions (stop, update), route to confirmation builder (Task 2)

  6. Wire flows - ensuring callback is answered FIRST:

    • Route Action Type (start) -> Answer Start Callback -> Prepare Start Action -> Start Container -> (completion handling in Plan 03)
    • Route Action Type (restart) -> Answer Restart Callback -> Prepare Restart Action -> Restart Container -> (completion handling)
    • Route Action Type (logs) -> Answer Logs Callback -> existing logs flow
    • Route Action Type (stop) -> Build Stop Confirmation (Task 2)
    • Route Action Type (update) -> Build Update Confirmation (Task 2)
    1. From container submenu, tap "Start" on a stopped container
    2. Verify: Container starts (check n8n execution or docker ps)
    3. Tap "Restart" on a running container
    4. Verify: Container restarts
    5. Tap "Logs"
    6. Verify: Logs returned (may be separate message for now)
    7. Tap "Stop" on running container
    8. Verify: Shows confirmation (not executed yet - Task 2)
    • Start button starts containers immediately
    • Restart button restarts containers immediately
    • Logs button triggers log retrieval
    • Stop/Update route to confirmation flow
    • All callbacks answered (no loading indicator)
Task 2: Add Confirmation Flow for Dangerous Actions n8n-workflow.json Add confirmation dialog for Stop and Update actions with 30-second timeout.
  1. Create Code node "Build Stop Confirmation":

    const { queryId, chatId, messageId, containerName } = $json;
    const timestamp = Math.floor(Date.now() / 1000); // Unix seconds
    
    const keyboard = {
      inline_keyboard: [
        [
          { text: '✅ Yes, Stop', callback_data: `confirm:stop:${containerName}:${timestamp}` },
          { text: '❌ Cancel', callback_data: `cancel:${containerName}` }
        ]
      ]
    };
    
    return {
      json: {
        queryId,
        chatId,
        messageId,
        text: `⚠️ <b>Stop ${containerName}?</b>\n\nThis will stop the container immediately.\n\n<i>Expires in 30 seconds</i>`,
        reply_markup: keyboard
      }
    };
    
  2. Create Code node "Build Update Confirmation":

    const { queryId, chatId, messageId, containerName } = $json;
    const timestamp = Math.floor(Date.now() / 1000);
    
    const keyboard = {
      inline_keyboard: [
        [
          { text: '✅ Yes, Update', callback_data: `confirm:update:${containerName}:${timestamp}` },
          { text: '❌ Cancel', callback_data: `cancel:${containerName}` }
        ]
      ]
    };
    
    return {
      json: {
        queryId,
        chatId,
        messageId,
        text: `⬆️ <b>Update ${containerName}?</b>\n\nThis will pull the latest image and recreate the container.\n\n<i>Expires in 30 seconds</i>`,
        reply_markup: keyboard
      }
    };
    
  3. Create HTTP Request nodes to answer callback and show confirmation:

    • "Answer Stop Callback" -> answerCallbackQuery
    • "Show Stop Confirmation" -> editMessageText with confirmation keyboard
    • Same pattern for Update
  4. Wire confirmation display:

    • Route Action Type (stop) -> Answer Stop Callback -> Build Stop Confirmation -> Show Stop Confirmation
    • Route Action Type (update) -> Answer Update Callback -> Build Update Confirmation -> Show Update Confirmation
  5. Update "Parse Callback Data" to handle confirm: and cancel: callbacks:

    // confirm:stop:plex:1738595200
    if (data.startsWith('confirm:')) {
      const parts = data.split(':');
      const timestamp = parseInt(parts[3]);
      const currentTime = Math.floor(Date.now() / 1000);
      const expired = (currentTime - timestamp) > 30;
    
      return {
        json: {
          isConfirm: true,
          expired: expired,
          actionType: parts[1], // stop or update
          containerName: parts[2],
          queryId: callbackQuery.id,
          chatId: callbackQuery.message.chat.id,
          messageId: callbackQuery.message.message_id
        }
      };
    }
    
    // cancel:plex
    if (data.startsWith('cancel:')) {
      const containerName = data.split(':')[1];
      return {
        json: {
          isCancel: true,
          containerName: containerName,
          queryId: callbackQuery.id,
          chatId: callbackQuery.message.chat.id,
          messageId: callbackQuery.message.message_id
        }
      };
    }
    
  6. Add "isConfirm" output to "Route Callback":

    • Rule: isConfirm === true && !expired -> output "confirm"
    • The existing "expired" output handles expired confirmations
  7. Create Switch node "Route Confirmed Action":

    • Input: from Route Callback "confirm" output
    • Outputs: "stop", "update" based on actionType
  8. Wire confirmed actions to actual operations:

    • Route Confirmed Action (stop) -> Answer Confirm Callback -> Stop Container -> (completion in Plan 03)
    • Route Confirmed Action (update) -> Answer Confirm Callback -> existing Update flow -> (completion)
  9. Handle cancel callback:

    • isCancel should route back to container submenu
    • Reuse/extend existing cancel handling
    • On cancel: fetch container details again -> show submenu
    1. Tap "Stop" on a running container
    2. Verify: Confirmation dialog appears with Yes/Cancel buttons
    3. Wait 35 seconds, then tap "Yes"
    4. Verify: Shows "Confirmation expired" message
    5. Tap "Stop" again, immediately tap "Yes"
    6. Verify: Container stops
    7. Start a container, tap "Stop", tap "Cancel"
    8. Verify: Returns to container submenu (not list)
    9. Test same flow for "Update" button
    • Stop shows confirmation dialog
    • Update shows confirmation dialog
    • Confirmation includes 30-second timeout warning
    • Tapping "Yes" within 30s executes action
    • Tapping "Yes" after 30s shows expired message
    • Tapping "Cancel" returns to container submenu
After completing all tasks: 1. Start button works immediately (no confirmation) 2. Restart button works immediately (no confirmation) 3. Stop button shows confirmation, then executes on confirm 4. Update button shows confirmation, then executes on confirm 5. Cancel returns to container submenu 6. Expired confirmations are rejected with message 7. Logs button retrieves container logs

<success_criteria>

  • KEY-02 requirement met: Action buttons perform operations
  • KEY-03 requirement met: Dangerous actions show confirmation
  • Actions wire correctly to existing container operation nodes
  • Confirmation timeout enforced at 30 seconds </success_criteria>
After completion, create `.planning/phases/08-inline-keyboard-infrastructure/08-02-SUMMARY.md`