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>
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 |
|
|
true |
|
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.-
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 } }; } -
Add "isAction" output to "Route Callback" switch node:
- Rule:
isAction === true-> output "action" - This catches all action callbacks before routing by type
- Rule:
-
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)
- "start":
-
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)
- Create Code node "Prepare Start Action":
-
For dangerous actions (stop, update), route to confirmation builder (Task 2)
-
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)
- From container submenu, tap "Start" on a stopped container
- Verify: Container starts (check n8n execution or docker ps)
- Tap "Restart" on a running container
- Verify: Container restarts
- Tap "Logs"
- Verify: Logs returned (may be separate message for now)
- Tap "Stop" on running container
- 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)
-
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 } }; -
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 } }; -
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
-
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
-
Update "Parse Callback Data" to handle
confirm:andcancel: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 } }; } -
Add "isConfirm" output to "Route Callback":
- Rule:
isConfirm === true && !expired-> output "confirm" - The existing "expired" output handles expired confirmations
- Rule:
-
Create Switch node "Route Confirmed Action":
- Input: from Route Callback "confirm" output
- Outputs: "stop", "update" based on actionType
-
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)
-
Handle cancel callback:
- isCancel should route back to container submenu
- Reuse/extend existing cancel handling
- On cancel: fetch container details again -> show submenu
- Tap "Stop" on a running container
- Verify: Confirmation dialog appears with Yes/Cancel buttons
- Wait 35 seconds, then tap "Yes"
- Verify: Shows "Confirmation expired" message
- Tap "Stop" again, immediately tap "Yes"
- Verify: Container stops
- Start a container, tap "Stop", tap "Cancel"
- Verify: Returns to container submenu (not list)
- 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
<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>