7601e245a9
- Add Known Limitations section to DEPLOY-SUBWORKFLOWS.md - Document UNR-01: Unraid shows "apply update" after bot updates a container - Explain root cause: bot bypasses Unraid's XML template system - Provide workaround: click Apply Update in Unraid (instant, image cached) - Clarify "Check for Updates" does NOT clear badge, may create new ones - Mark UNR-01 as complete (documented limitation with workaround)
726 lines
37 KiB
Markdown
726 lines
37 KiB
Markdown
# Sub-workflow Deployment Guide
|
|
|
|
## Overview
|
|
|
|
Phase 10.1 (Aggressive Workflow Modularization) reduced the main workflow from 192 nodes to 168 nodes by extracting domain-specific functionality into sub-workflows.
|
|
|
|
**Final State:**
|
|
- Main workflow: 168 nodes (reduced by 24 nodes, -12.5%)
|
|
- Total sub-workflows: 7 (3 pre-existing + 4 new)
|
|
|
|
## Sub-workflows
|
|
|
|
| File | Purpose | Nodes | Status |
|
|
|------|---------|-------|--------|
|
|
| n8n-update.json | Container update operations | 34 | Deployed (ID: `7AvTzLtKXM2hZTio92_mC`) |
|
|
| n8n-actions.json | Container start/stop/restart | 11 | Deployed (ID: `fYSZS5PkH0VSEaT5`) |
|
|
| n8n-logs.json | Container log retrieval | 9 | Deployed (ID: `oE7aO2GhbksXDEIw`) |
|
|
| n8n-batch-ui.json | Batch selection UI | 16 | Deployed |
|
|
| n8n-status.json | Container list/status | 11 | Deployed (ID: `lqpg2CqesnKE2RJQ`) |
|
|
| n8n-confirmation.json | Confirmation dialogs | 16 | Deployed (ID: `fZ1hu8eiovkCk08G`) |
|
|
| n8n-matching.json | Container matching/disambiguation | 23 | Deployed (ID: `kL4BoI8ITSP9Oxek`) |
|
|
|
|
## Deployment Status
|
|
|
|
All sub-workflows have been deployed. The main workflow (`n8n-workflow.json`) contains all correct workflow IDs.
|
|
|
|
To redeploy after changes:
|
|
1. Import the modified sub-workflow JSON to n8n
|
|
2. Re-import `n8n-workflow.json` if main workflow changed
|
|
3. Activate the workflow
|
|
|
|
## Execute Workflow Node Mapping
|
|
|
|
| Node Name | Target Sub-workflow | Workflow ID |
|
|
|-----------|---------------------|-------------|
|
|
| Execute Text Update | n8n-update.json | `7AvTzLtKXM2hZTio92_mC` |
|
|
| Execute Callback Update | n8n-update.json | `7AvTzLtKXM2hZTio92_mC` |
|
|
| Execute Batch Update | n8n-update.json | `7AvTzLtKXM2hZTio92_mC` |
|
|
| Execute Container Action | n8n-actions.json | `fYSZS5PkH0VSEaT5` |
|
|
| Execute Inline Action | n8n-actions.json | `fYSZS5PkH0VSEaT5` |
|
|
| Execute Batch Action Sub-workflow | n8n-actions.json | `fYSZS5PkH0VSEaT5` |
|
|
| Execute Text Logs | n8n-logs.json | `oE7aO2GhbksXDEIw` |
|
|
| Execute Inline Logs | n8n-logs.json | `oE7aO2GhbksXDEIw` |
|
|
| Execute Batch UI | n8n-batch-ui.json | Deployed |
|
|
| Execute Container Status | n8n-status.json | `lqpg2CqesnKE2RJQ` |
|
|
| Execute Select Status | n8n-status.json | `lqpg2CqesnKE2RJQ` |
|
|
| Execute Paginate Status | n8n-status.json | `lqpg2CqesnKE2RJQ` |
|
|
| Execute Batch Cancel Status | n8n-status.json | `lqpg2CqesnKE2RJQ` |
|
|
| Execute Confirmation | n8n-confirmation.json | `fZ1hu8eiovkCk08G` |
|
|
| Execute Action Match | n8n-matching.json | `kL4BoI8ITSP9Oxek` |
|
|
| Execute Update Match | n8n-matching.json | `kL4BoI8ITSP9Oxek` |
|
|
| Execute Batch Match | n8n-matching.json | `kL4BoI8ITSP9Oxek` |
|
|
|
|
## Rollback
|
|
|
|
If issues are encountered, backup files are available:
|
|
|
|
```bash
|
|
# Restore to before batch UI extraction
|
|
cp n8n-workflow.json.backup-batch n8n-workflow.json
|
|
|
|
# Restore to before status extraction
|
|
cp n8n-workflow.json.backup-status n8n-workflow.json
|
|
|
|
# Restore to before confirmation extraction
|
|
cp n8n-workflow.json.backup-confirm n8n-workflow.json
|
|
```
|
|
|
|
Then re-import the restored `n8n-workflow.json` to n8n.
|
|
|
|
## Known Limitations
|
|
|
|
### Unraid Update Badges (UNR-01)
|
|
|
|
After the bot updates a container, Unraid's Docker tab may show "apply update" on the next update check. This is expected behavior.
|
|
|
|
**Why this happens:** The bot uses the Docker API directly to pull images and recreate containers. Unraid manages containers through its own XML template system and doesn't know the container was updated outside of its control.
|
|
|
|
**What to do:** Click "Apply Update" in Unraid's Docker tab. It completes instantly because the image is already pulled and cached — Unraid just recreates the container from its template to sync its internal tracking.
|
|
|
|
**Important:** "Check for Updates" in Unraid does NOT clear an existing badge. It may actually cause a new badge to appear if the bot updated a container that Unraid hadn't checked yet.
|
|
|
|
**Why not fix programmatically:** Clearing the badge would require calling Unraid's internal web API to "apply" the update through its template system. This adds significant complexity (Unraid web UI access, authentication, template parsing) for a cosmetic issue that takes one click to resolve.
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
```
|
|
n8n-workflow.json (168 nodes - orchestrator)
|
|
├── Telegram Trigger (1)
|
|
├── Auth + Error Handling
|
|
├── Keyword Routing (switch/if nodes)
|
|
│
|
|
├── Update Operations
|
|
│ ├── Execute Text Update ──────────┐
|
|
│ ├── Execute Callback Update ──────┼── n8n-update.json (34 nodes)
|
|
│ └── Execute Batch Update ─────────┘
|
|
│
|
|
├── Action Operations
|
|
│ ├── Execute Container Action ─────┐
|
|
│ ├── Execute Inline Action ────────┼── n8n-actions.json (11 nodes)
|
|
│ └── Execute Batch Action ─────────┘
|
|
│
|
|
├── Log Operations
|
|
│ ├── Execute Text Logs ────────────┐
|
|
│ └── Execute Inline Logs ──────────┴── n8n-logs.json (9 nodes)
|
|
│
|
|
├── Batch UI
|
|
│ └── Execute Batch UI ─────────────── n8n-batch-ui.json (16 nodes)
|
|
│ ├── Returns: keyboard/confirmation/execute/cancel/limit
|
|
│ └── Main routes response based on action
|
|
│
|
|
├── Container Status
|
|
│ ├── Execute Container Status ─────┐
|
|
│ ├── Execute Select Status ────────┼── n8n-status.json (11 nodes)
|
|
│ ├── Execute Paginate Status ──────┤ Returns: list/status/paginate
|
|
│ └── Execute Batch Cancel Status ──┘
|
|
│
|
|
└── Confirmation Dialogs
|
|
└── Execute Confirmation ─────────── n8n-confirmation.json (16 nodes)
|
|
├── Returns: show_stop/show_update/confirm_*/cancel/expired
|
|
└── Calls n8n-actions.json for confirmed stop actions
|
|
```
|
|
|
|
## Sub-workflow Input/Output Contracts
|
|
|
|
### n8n-batch-ui.json
|
|
|
|
**Input:**
|
|
- `chatId`, `messageId`, `queryId`
|
|
- `callbackData`, `action`, `batchPage`
|
|
- `selectedCsv`, `toggleName`
|
|
|
|
**Output:**
|
|
- `action`: keyboard | confirmation | execute | cancel | limit_reached
|
|
|
|
### n8n-status.json
|
|
|
|
**Input:**
|
|
- `chatId`, `messageId`, `queryId`
|
|
- `action`, `containerId`, `containerName`
|
|
- `page`, `searchTerm`
|
|
|
|
**Output:**
|
|
- `action`: list | status | paginate | status_direct
|
|
|
|
### n8n-confirmation.json
|
|
|
|
**Input:**
|
|
- `chatId`, `messageId`, `queryId`
|
|
- `action`, `containerId`, `containerName`
|
|
- `confirmAction`, `confirmationToken`
|
|
- `expired`, `responseMode`
|
|
|
|
**Output:**
|
|
- `action`: show_stop | show_update | confirm_stop_result | confirm_update | cancel | expired
|
|
|
|
## Testing Checklist
|
|
|
|
After deployment, verify:
|
|
|
|
- [ ] `/list` - Shows container list
|
|
- [ ] `/status <container>` - Shows container details
|
|
- [ ] `/stop <container>` - Shows confirmation dialog
|
|
- [ ] Confirm stop - Executes stop action
|
|
- [ ] Cancel stop - Returns to status view
|
|
- [ ] `/update <container>` - Shows confirmation dialog
|
|
- [ ] Confirm update - Executes update flow
|
|
- [ ] `/stop` (no args) - Shows batch selection UI
|
|
- [ ] Select multiple containers - Batch selection works
|
|
- [ ] Execute batch - All selected containers processed
|
|
- [ ] `/logs <container>` - Shows container logs
|
|
|
|
---
|
|
|
|
## Code Node Classification
|
|
|
|
Analysis of all 60 Code nodes in the main workflow (`n8n-workflow.json`), classifying each as orchestration infrastructure or domain logic.
|
|
|
|
### Classification Categories
|
|
|
|
| Category | Definition | Must Stay in Main? |
|
|
|----------|-----------|-------------------|
|
|
| `prepare-input` | Prepares input data for a sub-workflow Execute Workflow call or Execute Command | YES - glue between routing and sub-workflow |
|
|
| `route-result` | Processes sub-workflow or command return data for routing/display | YES - glue between sub-workflow and Telegram response |
|
|
| `parse-command` | Parses user text input into structured command data | YES - part of keyword routing infrastructure |
|
|
| `build-response` | Builds Telegram response text/keyboards from data | YES - tightly coupled to Telegram response nodes |
|
|
| `orchestration` | Batch loop control, state management, result aggregation | YES - main workflow orchestration logic |
|
|
| `domain-logic` | Pure domain computation that could theoretically live in sub-workflow | CANDIDATE - but assess extraction overhead |
|
|
|
|
### Code Node Classification Table
|
|
|
|
| Node Name | Category | Lines | Stays in Main? | Reason |
|
|
|-----------|----------|-------|---------------|--------|
|
|
| Build Action Command | build-response | 22 | YES | Builds curl command for Docker action execution |
|
|
| Build Batch Keyboard | build-response | 56 | YES | Builds batch confirmation keyboard for multiple matches |
|
|
| Build Batch Stop Confirmation | build-response | 36 | YES | Builds Telegram message for batch stop confirmation |
|
|
| Build Batch Stop Expired | build-response | 10 | YES | Builds expired confirmation message |
|
|
| Build Batch Summary | build-response | 62 | YES | Builds batch result summary message with success/failure counts |
|
|
| Build Callback Action | build-response | 24 | YES | Builds curl command for callback action execution |
|
|
| Build Cancel Return Submenu | domain-logic | 72 | CANDIDATE | Container name matching + submenu building (search + normalize + build) |
|
|
| Build Immediate Action Command | domain-logic | 43 | CANDIDATE | Container lookup by name + curl command building |
|
|
| Build Progress Message | build-response | 30 | YES | Builds progress message for batch loop iteration |
|
|
| Build Update All Confirmation | build-response | 35 | YES | Builds Telegram confirmation message for update-all |
|
|
| Check Available Updates | orchestration | 44 | YES | Filters containers by :latest tag, orchestrates update-all flow |
|
|
| Detect Batch Command | parse-command | 70 | YES | Detects batch commands (multiple container names) from text input |
|
|
| Find Container For Callback Update | prepare-input | 38 | YES | Resolves container name to ID for callback update sub-workflow |
|
|
| Format Immediate Result | route-result | 49 | YES | Formats action result into Telegram message with inline keyboard |
|
|
| Format Inline Logs Result | route-result | 36 | YES | Formats logs result with inline keyboard and refresh button |
|
|
| Get Update All Data | prepare-input | 18 | YES | Prepares data for update-all re-fetch |
|
|
| Handle Batch Action Result Sub | route-result | 33 | YES | Aggregates batch action sub-workflow result into loop state |
|
|
| Handle Batch Update Result | route-result | 33 | YES | Aggregates batch update sub-workflow result into loop state |
|
|
| Handle Cancel | route-result | 10 | YES | Prepares cancel callback query response data |
|
|
| Handle Expired | route-result | 10 | YES | Prepares expired callback query response data |
|
|
| Handle Inline Action Result | route-result | 36 | YES | Routes inline action result to Telegram edit with keyboard |
|
|
| Handle Text Action Result | route-result | 13 | YES | Routes text action result to Telegram send |
|
|
| Handle Update Multiple | route-result | 14 | YES | Builds error message for ambiguous update match |
|
|
| Initialize Batch State | orchestration | 43 | YES | Initializes batch execution loop state from multiple input sources |
|
|
| Parse Action Command | parse-command | 49 | YES | Parses /stop, /start, /restart text commands |
|
|
| Parse Action Result | route-result | 42 | YES | Parses curl HTTP status code into success/failure message |
|
|
| Parse Callback Data | parse-command | 441 | YES | Central callback data parser (all button clicks) |
|
|
| Parse Callback Result | route-result | 54 | YES | Parses callback action curl result with keyboard building |
|
|
| Parse Logs Command | parse-command | 45 | YES | Parses /logs text command with optional line count |
|
|
| Parse Update Command | parse-command | 25 | YES | Parses /update text command |
|
|
| Prepare Action Match Input | prepare-input | 17 | YES | Prepares input for matching sub-workflow (action commands) |
|
|
| Prepare Batch Action Input | prepare-input | 19 | YES | Prepares input for actions sub-workflow (batch loop) |
|
|
| Prepare Batch Cancel Return | prepare-input | 10 | YES | Prepares data for return to container list from batch cancel |
|
|
| Prepare Batch Cancel Return Input | prepare-input | 14 | YES | Prepares input for status sub-workflow from batch cancel |
|
|
| Prepare Batch Exec | orchestration | 26 | YES | Prepares batch exec data, normalizes container name formats |
|
|
| Prepare Batch Execution | prepare-input | 13 | YES | Transforms matching sub-workflow output to batch execution format |
|
|
| Prepare Batch Loop | orchestration | 42 | YES | Stores progress message ID, prepares first batch iteration |
|
|
| Prepare Batch Match Input | prepare-input | 21 | YES | Prepares input for matching sub-workflow (batch commands) |
|
|
| Prepare Batch Stop Exec | prepare-input | 16 | YES | Prepares batch stop data from callback confirmation |
|
|
| Prepare Batch UI Input | prepare-input | 27 | YES | Prepares input for batch UI sub-workflow |
|
|
| Prepare Batch Update Input | prepare-input | 17 | YES | Prepares input for update sub-workflow (batch loop) |
|
|
| Prepare Callback Update Input | prepare-input | 15 | YES | Prepares input for update sub-workflow (inline mode) |
|
|
| Prepare Cancel From Confirm | prepare-input | 10 | YES | Prepares cancel return data from confirmation result |
|
|
| Prepare Cancel Return | prepare-input | 9 | YES | Prepares cancel data from callback for container submenu |
|
|
| Prepare Confirm Input | prepare-input | 26 | YES | Prepares input for confirmation sub-workflow |
|
|
| Prepare Immediate Action | prepare-input | 18 | YES | Prepares inline keyboard action data for Docker execution |
|
|
| Prepare Inline Action Input | prepare-input | 41 | YES | Prepares input for actions sub-workflow (inline keyboard path) |
|
|
| Prepare Inline Logs Input | prepare-input | 12 | YES | Prepares input for logs sub-workflow (inline action) |
|
|
| Prepare Next Iteration | orchestration | 18 | YES | Advances batch loop counter, checks completion |
|
|
| Prepare Paginate Input | prepare-input | 14 | YES | Prepares input for status sub-workflow (pagination callback) |
|
|
| Prepare Select Status Input | prepare-input | 14 | YES | Prepares input for status sub-workflow (container select) |
|
|
| Prepare Show Stop Input | prepare-input | 13 | YES | Prepares input for confirmation sub-workflow (show stop) |
|
|
| Prepare Show Update Input | prepare-input | 13 | YES | Prepares input for confirmation sub-workflow (show update) |
|
|
| Prepare Status Input | prepare-input | 24 | YES | Prepares input for status sub-workflow (/status command) |
|
|
| Prepare Text Action Input | prepare-input | 18 | YES | Prepares input for actions sub-workflow (text command) |
|
|
| Prepare Text Logs Input | prepare-input | 23 | YES | Prepares input for logs sub-workflow (text command) |
|
|
| Prepare Text Update Input | prepare-input | 15 | YES | Prepares input for update sub-workflow (text mode) |
|
|
| Prepare Update All Batch | orchestration | 41 | YES | Prepares batch data for update-all from container list |
|
|
| Prepare Update Match Input | prepare-input | 16 | YES | Prepares input for matching sub-workflow (update commands) |
|
|
| Strip Status Keyboard | route-result | 9 | YES | Strips inline keyboard for text-mode status display |
|
|
|
|
### Classification Summary
|
|
|
|
```
|
|
Total Code nodes: 60
|
|
|
|
prepare-input: 27 (must stay - sub-workflow integration glue)
|
|
route-result: 12 (must stay - sub-workflow result routing)
|
|
parse-command: 5 (must stay - keyword routing infrastructure)
|
|
build-response: 8 (must stay - Telegram response building)
|
|
orchestration: 6 (must stay - batch loop + state management)
|
|
domain-logic: 2 (extraction candidates)
|
|
|
|
Extractable domain logic: 2 nodes (72 + 43 = 115 lines)
|
|
- Build Cancel Return Submenu (72 lines) — container name matching + submenu building
|
|
- Build Immediate Action Command (43 lines) — container lookup + curl command building
|
|
|
|
Extraction overhead per domain: ~3 nodes (Prepare Input + Execute Workflow + Route Result)
|
|
Net reduction potential: 2 extracted - 3 overhead = -1 node (net increase)
|
|
```
|
|
|
|
### Extraction Viability Assessment
|
|
|
|
The 2 domain-logic candidates (`Build Cancel Return Submenu` and `Build Immediate Action Command`) both perform container name matching followed by response building. However:
|
|
|
|
1. **Both are already partially handled by n8n-matching.json** -- the matching sub-workflow handles the primary matching paths. These two nodes handle edge cases (cancel return to submenu, and immediate inline actions like start/restart) where the matching has already been resolved by callback data.
|
|
|
|
2. **Extraction would be net-negative** -- extracting 2 nodes but adding 3 integration nodes (Prepare Input, Execute Workflow, Route Result) would increase the node count by 1.
|
|
|
|
3. **Complexity is low** -- both nodes are under 75 lines and perform straightforward container name normalization + lookup against Docker API output already fetched.
|
|
|
|
**Conclusion:** No further Code node extraction is viable. All 58 non-candidate nodes are demonstrably orchestration infrastructure (input preparation, result routing, command parsing, response building, or loop control). The 2 domain-logic candidates would yield a net-negative extraction.
|
|
|
|
---
|
|
|
|
## Sub-workflow Input/Output Contracts (Detailed)
|
|
|
|
Formal contracts for all 7 sub-workflows with field-level documentation. Each contract is verified against the Prepare Input Code nodes that feed the Execute Workflow calls.
|
|
|
|
### n8n-update.json (Container Update)
|
|
|
|
**Workflow ID:** `7AvTzLtKXM2hZTio92_mC`
|
|
**Called by:** 3 Execute Workflow nodes
|
|
|
|
#### Input Contract
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| containerId | string | yes* | Docker container ID (empty string if resolving by name) |
|
|
| containerName | string | yes | Container display name |
|
|
| chatId | number | yes | Telegram chat ID |
|
|
| messageId | number | yes | Telegram message ID (0 for text mode) |
|
|
| responseMode | string | yes | `"text"` or `"inline"` |
|
|
|
|
*containerId can be empty string -- the sub-workflow resolves by name via `Resolve Container ID` node.
|
|
|
|
#### Output Contract
|
|
|
|
| Exit Node | Fields | When |
|
|
|-----------|--------|------|
|
|
| Return Success | `success: true, updated: true, message, oldDigest, newDigest` | Image pulled, container recreated |
|
|
| Return No Update | `success: true, updated: false, message` | Image already up to date |
|
|
| Return Error | `success: false, updated: false, message` | Pull or creation failed |
|
|
|
|
#### Execute Workflow Node Mapping
|
|
|
|
| Execute Node | Source Node | Fields Produced |
|
|
|-------------|-------------|-----------------|
|
|
| Execute Text Update | Prepare Text Update Input | containerId, containerName, chatId, messageId (0), responseMode ("text") |
|
|
| Execute Callback Update | Find Container For Callback Update | containerId, containerName, chatId, messageId, responseMode ("inline") |
|
|
| Execute Batch Update | Prepare Batch Update Input | containerId, containerName, chatId, messageId, responseMode ("batch") |
|
|
|
|
**Verification:** All 3 source nodes produce all 5 required fields. Confirmed.
|
|
|
|
---
|
|
|
|
### n8n-actions.json (Container Actions)
|
|
|
|
**Workflow ID:** `fYSZS5PkH0VSEaT5`
|
|
**Called by:** 3 Execute Workflow nodes
|
|
|
|
#### Input Contract
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| containerId | string | yes | Docker container ID |
|
|
| containerName | string | yes | Container display name |
|
|
| action | string | yes | `"start"`, `"stop"`, or `"restart"` |
|
|
| chatId | number | yes | Telegram chat ID |
|
|
| messageId | number | yes | Telegram message ID (0 for text mode) |
|
|
| responseMode | string | yes | `"text"`, `"inline"`, or `"batch"` |
|
|
|
|
#### Output Contract
|
|
|
|
All 3 exit nodes return the same structure (per action type):
|
|
|
|
| Exit Node | Fields |
|
|
|-----------|--------|
|
|
| Format Start Result | `success, message, action, containerName, containerId, chatId, messageId, responseMode` |
|
|
| Format Stop Result | `success, message, action, containerName, containerId, chatId, messageId, responseMode` |
|
|
| Format Restart Result | `success, message, action, containerName, containerId, chatId, messageId, responseMode` |
|
|
|
|
#### Execute Workflow Node Mapping
|
|
|
|
| Execute Node | Source Node | Fields Produced |
|
|
|-------------|-------------|-----------------|
|
|
| Execute Container Action | Prepare Text Action Input | containerId, containerName, action, chatId, messageId, responseMode ("text") |
|
|
| Execute Inline Action | Prepare Inline Action Input | containerId, containerName, action, chatId, messageId, responseMode ("inline") |
|
|
| Execute Batch Action Sub-workflow | Prepare Batch Action Input | containerId, containerName, action, chatId, messageId, responseMode ("batch") |
|
|
|
|
**Verification:** All 3 source nodes produce all 6 required fields. Confirmed.
|
|
|
|
---
|
|
|
|
### n8n-logs.json (Container Logs)
|
|
|
|
**Workflow ID:** `oE7aO2GhbksXDEIw`
|
|
**Called by:** 2 Execute Workflow nodes
|
|
|
|
#### Input Contract
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| containerId | string | no | Docker container ID (optional, resolved by name if absent) |
|
|
| containerName | string | yes* | Container name for lookup |
|
|
| lineCount | number | no | Lines to retrieve (default: 50, max: 1000) |
|
|
| chatId | number | yes | Telegram chat ID |
|
|
| messageId | number | no | Telegram message ID (default: 0) |
|
|
| responseMode | string | no | `"text"` or `"inline"` (default: "text") |
|
|
|
|
*Either containerId or containerName required. Sub-workflow's `Parse Input` node validates this.
|
|
|
|
#### Output Contract
|
|
|
|
| Exit Node | Fields | When |
|
|
|-----------|--------|------|
|
|
| Format Logs | `success: true, message, containerName, lineCount` | Logs retrieved and formatted |
|
|
|
|
Note: Error cases (container not found, Docker error) throw exceptions which n8n handles as workflow errors.
|
|
|
|
#### Execute Workflow Node Mapping
|
|
|
|
| Execute Node | Source Node | Fields Produced |
|
|
|-------------|-------------|-----------------|
|
|
| Execute Text Logs | Prepare Text Logs Input | containerName, lineCount, chatId, messageId, responseMode ("text") |
|
|
| Execute Inline Logs | Prepare Inline Logs Input | containerName, lineCount (30), chatId, messageId, responseMode ("inline") |
|
|
|
|
**Verification:** Both source nodes produce required fields. `Prepare Text Logs Input` has error-path return (error, chatId, text) but this is handled before reaching Execute Workflow node. Confirmed.
|
|
|
|
---
|
|
|
|
### n8n-batch-ui.json (Batch Selection UI)
|
|
|
|
**Workflow ID:** `ZJhnGzJT26UUmW45`
|
|
**Called by:** 1 Execute Workflow node
|
|
|
|
#### Input Contract
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| chatId | number | yes | Telegram chat ID |
|
|
| messageId | number | yes | Telegram message ID |
|
|
| callbackData | string | yes | Raw callback data string |
|
|
| queryId | string | yes | Telegram callback query ID |
|
|
| action | string | yes | `"mode"`, `"toggle"`, `"nav"`, `"exec"`, `"clear"`, `"cancel"` |
|
|
| batchPage | number | no | Current page number (default: 0) |
|
|
| selectedCsv | string | no | Comma-separated selected container names |
|
|
| toggleName | string | no | Container name being toggled |
|
|
| batchAction | string | no | Action for batch execution (stop/restart/update) |
|
|
|
|
#### Output Contract
|
|
|
|
| Exit Node | action Value | Key Fields |
|
|
|-----------|-------------|------------|
|
|
| Build Batch Keyboard | `"keyboard"` | queryId, chatId, messageId, text, keyboard, selectedCsv, selectedCount, answerText |
|
|
| Rebuild Keyboard After Toggle | `"keyboard"` | queryId, chatId, messageId, text, keyboard, selectedCsv, answerText |
|
|
| Rebuild Keyboard For Nav | `"keyboard"` | queryId, chatId, messageId, text, keyboard, selectedCsv, answerText |
|
|
| Rebuild Keyboard After Clear | `"keyboard"` | queryId, chatId, messageId, text, keyboard, selectedCsv (""), answerText |
|
|
| Handle Exec | `"execute"` | queryId, chatId, messageId, batchAction, containerNames, selectedCsv, count, fromKeyboard, answerText |
|
|
| Handle Cancel | `"cancel"` | queryId, chatId, messageId, page, answerText |
|
|
|
|
All exit nodes include `success: true`.
|
|
|
|
#### Execute Workflow Node Mapping
|
|
|
|
| Execute Node | Source Node | Fields Produced |
|
|
|-------------|-------------|-----------------|
|
|
| Execute Batch UI | Prepare Batch UI Input | chatId, messageId, queryId, callbackData, action, batchAction, batchPage, selectedCsv, toggleName |
|
|
|
|
**Verification:** Source node produces all 9 input fields. Confirmed.
|
|
|
|
---
|
|
|
|
### n8n-status.json (Container Status/List)
|
|
|
|
**Workflow ID:** `lqpg2CqesnKE2RJQ`
|
|
**Called by:** 4 Execute Workflow nodes
|
|
|
|
#### Input Contract
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| chatId | number | yes | Telegram chat ID |
|
|
| messageId | number | yes | Telegram message ID |
|
|
| action | string | yes | `"list"`, `"status"`, `"paginate"` |
|
|
| containerId | string | no | Docker container ID (for status lookup) |
|
|
| containerName | string | no | Container name (for status lookup) |
|
|
| page | number | no | Page number for pagination (default: 0) |
|
|
| queryId | string | no | Telegram callback query ID |
|
|
| searchTerm | string | no | Container name search term |
|
|
|
|
#### Output Contract
|
|
|
|
| Exit Node | action Value | Key Fields |
|
|
|-----------|-------------|------------|
|
|
| Build Container List | `"list"` | chatId, messageId, text, reply_markup, totalContainers, currentPage, totalPages |
|
|
| Build Container Submenu | `"status"` | chatId, messageId, text, reply_markup, container (id, name, state, status, image) |
|
|
| Build Paginated List | `"paginate"` | chatId, messageId, text, reply_markup, totalContainers, currentPage, totalPages |
|
|
|
|
All exit nodes include `success: true`.
|
|
|
|
Note: The main workflow also routes `status_direct` results (when messageId > 0 goes to edit, messageId == 0 goes to text send via `Strip Status Keyboard`).
|
|
|
|
#### Execute Workflow Node Mapping
|
|
|
|
| Execute Node | Source Node | Fields Produced |
|
|
|-------------|-------------|-----------------|
|
|
| Execute Container Status | Prepare Status Input | chatId, messageId, action ("list"/"status"), containerId, containerName, page, queryId, searchTerm |
|
|
| Execute Select Status | Prepare Select Status Input | chatId, messageId, action ("status"), containerId, containerName, page, queryId, searchTerm |
|
|
| Execute Paginate Status | Prepare Paginate Input | chatId, messageId, action ("paginate"), containerId, containerName, page, queryId, searchTerm |
|
|
| Execute Batch Cancel Status | Prepare Batch Cancel Return Input | chatId, messageId, action ("paginate"), containerId, containerName, page, queryId, searchTerm |
|
|
|
|
**Verification:** All 4 source nodes produce all 8 fields. Confirmed.
|
|
|
|
---
|
|
|
|
### n8n-confirmation.json (Confirmation Dialogs)
|
|
|
|
**Workflow ID:** `fZ1hu8eiovkCk08G`
|
|
**Called by:** 1 Execute Workflow node (from 3 source nodes)
|
|
|
|
#### Input Contract
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| chatId | number | yes | Telegram chat ID |
|
|
| messageId | number | yes | Telegram message ID |
|
|
| action | string | yes | `"show_stop"`, `"show_update"`, `"confirm"`, `"cancel"`, `"expired"` |
|
|
| containerId | string | no | Docker container ID |
|
|
| containerName | string | yes | Container display name |
|
|
| confirmAction | string | no | `"stop"` or `"update"` (for confirm action) |
|
|
| confirmationToken | string | no | Timestamp token for expiry check |
|
|
| expired | boolean | no | Whether confirmation has expired |
|
|
| responseMode | string | no | `"inline"` (default) |
|
|
|
|
Note: This sub-workflow internally calls n8n-actions.json (ID: `fYSZS5PkH0VSEaT5`) for confirmed stop actions.
|
|
|
|
#### Output Contract
|
|
|
|
| Exit Node | action Value | Key Fields |
|
|
|-----------|-------------|------------|
|
|
| Build Stop Confirmation | `"show_stop"` | chatId, messageId, text, reply_markup |
|
|
| Build Update Confirmation | `"show_update"` | chatId, messageId, text, reply_markup |
|
|
| Format Stop Result | `"confirm_stop_result"` | success, chatId, messageId, text, reply_markup |
|
|
| Return Update Action | `"confirm_update"` | containerId, containerName, chatId, messageId, responseMode |
|
|
| Handle Cancel | `"cancel"` | containerName, chatId, messageId |
|
|
| Handle Expired | `"expired"` | chatId, messageId, text, reply_markup |
|
|
| Stop Expired Response | `"expired"` | chatId, messageId, text, reply_markup |
|
|
| Update Expired Response | `"expired"` | chatId, messageId, text, reply_markup |
|
|
|
|
#### Execute Workflow Node Mapping
|
|
|
|
| Execute Node | Source Node | Fields Produced |
|
|
|-------------|-------------|-----------------|
|
|
| Execute Confirmation | Prepare Confirm Input | chatId, messageId, action ("confirm"/"cancel"/"expired"), containerId, containerName, confirmAction, confirmationToken, expired, responseMode |
|
|
| Execute Confirmation | Prepare Show Stop Input | chatId, messageId, action ("show_stop"), containerId, containerName, responseMode |
|
|
| Execute Confirmation | Prepare Show Update Input | chatId, messageId, action ("show_update"), containerId, containerName, responseMode |
|
|
|
|
**Verification:** All 3 source nodes produce required fields for their respective action paths. Confirmed.
|
|
|
|
---
|
|
|
|
### n8n-matching.json (Container Matching/Disambiguation)
|
|
|
|
**Workflow ID:** `kL4BoI8ITSP9Oxek`
|
|
**Called by:** 3 Execute Workflow nodes
|
|
|
|
#### Input Contract
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| action | string | yes | `"match_action"`, `"match_update"`, or `"match_batch"` |
|
|
| containerList | string | yes | Raw Docker API JSON output (container list) |
|
|
| searchTerm | string | yes | Container name query to match |
|
|
| selectedContainers | string | no | Comma-separated container names for batch matching |
|
|
| chatId | number | yes | Telegram chat ID |
|
|
| messageId | number | yes | Telegram message ID |
|
|
|
|
#### Output Contract
|
|
|
|
**Action match path** (action: "match_action"):
|
|
|
|
| Exit Node | action Value | Key Fields |
|
|
|-----------|-------------|------------|
|
|
| Format Single Match Return | `"matched"` | containerId, containerName, matches, matchCount, actionType, containerQuery, chatId, allContainers |
|
|
| Format Multiple Match Return | `"multiple"` | matches, matchCount, actionType, containerQuery, chatId, allContainers |
|
|
| Format No Match Return | `"no_match"` | query, chatId |
|
|
| Format Error Return | `"error"` | errorMessage, chatId |
|
|
| Build Suggestion Keyboard | `"suggestion"` | chat_id, text, parse_mode, reply_markup |
|
|
|
|
**Update match path** (action: "match_update"):
|
|
|
|
| Exit Node | action Value | Key Fields |
|
|
|-----------|-------------|------------|
|
|
| Format Update Single Match Return | `"matched_update"` | containerId, containerName, matches, matchCount, containerQuery, chatId |
|
|
| Format Update Multiple Match Return | `"multiple_update"` | matches, matchCount, containerQuery, chatId |
|
|
| Format Update No Match Return | `"no_match_update"` | containerQuery, chatId |
|
|
| Format Update Error Return | `"error"` | errorMessage, chatId |
|
|
|
|
**Batch match path** (action: "match_batch"):
|
|
|
|
| Exit Node | action Value | Key Fields |
|
|
|-----------|-------------|------------|
|
|
| Format Batch Matched Return | `"batch_matched"` | matchedContainers, chatId, messageId, originalContainerNames |
|
|
| Build Disambiguation Message | `"disambiguation"` | chat_id, text, parse_mode, reply_markup |
|
|
| Build Not Found Message | `"not_found"` | chat_id, text, parse_mode, hasMatched (+ optional matchedContainers) |
|
|
|
|
#### Execute Workflow Node Mapping
|
|
|
|
| Execute Node | Source Node | Fields Produced |
|
|
|-------------|-------------|-----------------|
|
|
| Execute Action Match | Prepare Action Match Input | action ("match_action"), containerList, searchTerm, selectedContainers (""), chatId, messageId |
|
|
| Execute Update Match | Prepare Update Match Input | action ("match_update"), containerList, searchTerm, selectedContainers (""), chatId, messageId |
|
|
| Execute Batch Match | Prepare Batch Match Input | action ("match_batch"), containerList, searchTerm (""), selectedContainers, chatId, messageId |
|
|
|
|
**Verification:** All 3 source nodes produce all 6 input fields. Confirmed.
|
|
|
|
---
|
|
|
|
### Execute Workflow Node Summary
|
|
|
|
**Total Execute Workflow nodes:** 17
|
|
|
|
| Target Sub-workflow | Count | Execute Nodes |
|
|
|--------------------|-------|---------------|
|
|
| n8n-update.json | 3 | Execute Text Update, Execute Callback Update, Execute Batch Update |
|
|
| n8n-actions.json | 3 | Execute Container Action, Execute Inline Action, Execute Batch Action Sub-workflow |
|
|
| n8n-logs.json | 2 | Execute Text Logs, Execute Inline Logs |
|
|
| n8n-batch-ui.json | 1 | Execute Batch UI |
|
|
| n8n-status.json | 4 | Execute Container Status, Execute Select Status, Execute Paginate Status, Execute Batch Cancel Status |
|
|
| n8n-confirmation.json | 1 | Execute Confirmation (3 source nodes) |
|
|
| n8n-matching.json | 3 | Execute Action Match, Execute Update Match, Execute Batch Match |
|
|
|
|
**Contract verification result:** All 17 Execute Workflow nodes receive correctly structured input from their source Prepare Input nodes. No mismatches found.
|
|
|
|
---
|
|
|
|
## Node Count Analysis
|
|
|
|
### Current State
|
|
|
|
- **Main workflow:** 168 nodes (after all Phase 10.1 extractions)
|
|
- **7 sub-workflows:** 120 nodes total
|
|
- n8n-update.json: 34 nodes
|
|
- n8n-matching.json: 23 nodes
|
|
- n8n-batch-ui.json: 16 nodes
|
|
- n8n-confirmation.json: 16 nodes
|
|
- n8n-actions.json: 11 nodes
|
|
- n8n-status.json: 11 nodes
|
|
- n8n-logs.json: 9 nodes
|
|
|
|
### Main Workflow Node Breakdown (168 total)
|
|
|
|
| Category | Count | Description |
|
|
|----------|-------|-------------|
|
|
| Code nodes | 60 | Orchestration logic (see Classification above) |
|
|
| HTTP Request nodes | 40 | Docker API + Telegram API calls |
|
|
| Telegram nodes | 23 | User-facing response nodes (locked to main per design) |
|
|
| Execute Workflow nodes | 17 | Sub-workflow dispatch |
|
|
| Switch nodes | 13 | Routing logic (Keyword Router, Route Callback, etc.) |
|
|
| If nodes | 8 | Conditional routing (auth checks, batch completion, etc.) |
|
|
| Execute Command nodes | 6 | Docker CLI calls (list, execute) |
|
|
| Telegram Trigger | 1 | Entry point |
|
|
|
|
### Why 115-125 Target Is Not Achievable
|
|
|
|
Based on the Code node classification above, here is the structural breakdown of what must remain in the main workflow:
|
|
|
|
```
|
|
Locked infrastructure (cannot be extracted):
|
|
1 Telegram Trigger
|
|
8 If nodes (auth, batch complete, expired checks, status routing)
|
|
13 Switch nodes (keyword router, callback router, result routers)
|
|
── Total: 22 nodes
|
|
|
|
Telegram response nodes (locked per design decision):
|
|
23 Telegram nodes (user-facing messages — must stay in main)
|
|
── Total: 23 nodes
|
|
|
|
HTTP Request nodes (Telegram API + Docker API):
|
|
40 HTTP Request nodes (edit message, answer callback, send message, Docker queries)
|
|
── Total: 40 nodes
|
|
|
|
Execute Workflow dispatch:
|
|
17 Execute Workflow nodes (sub-workflow calls)
|
|
── Total: 17 nodes
|
|
|
|
Execute Command (Docker CLI):
|
|
6 Execute Command nodes (docker list/exec operations)
|
|
── Total: 6 nodes
|
|
|
|
Code nodes — orchestration infrastructure (must stay):
|
|
27 prepare-input (sub-workflow glue)
|
|
12 route-result (sub-workflow result routing)
|
|
5 parse-command (keyword routing)
|
|
8 build-response (Telegram message building)
|
|
6 orchestration (batch loop control)
|
|
── Total: 58 nodes
|
|
|
|
Code nodes — domain logic candidates:
|
|
2 domain-logic (Build Cancel Return Submenu, Build Immediate Action Command)
|
|
── Total: 2 nodes
|
|
```
|
|
|
|
### Revised Realistic Baseline
|
|
|
|
**Minimum viable main workflow:** 166 nodes (all categories that must stay)
|
|
- 22 infrastructure + 23 Telegram + 40 HTTP + 17 Execute Workflow + 6 Execute Command + 58 Code = 166
|
|
|
|
**Current:** 168 nodes
|
|
**Gap:** 2 nodes of domain logic where extraction overhead (~3 nodes per extraction: Prepare Input + Execute Workflow + Route Result) makes extraction net-negative
|
|
|
|
### Extraction History
|
|
|
|
| Phase | Extraction | Nodes Removed | Nodes Added | Net Change |
|
|
|-------|-----------|--------------|-------------|------------|
|
|
| 10-02 | Container Update | -13 (inline logic) | +6 (integration) | -7 |
|
|
| 10-03 | Container Actions | -6 (inline logic) | +5 (integration) | -1 |
|
|
| 10-05 | Batch Loops + Logs | -5 (inline logic) | +4 (integration) | -1 |
|
|
| 10.1-02 | Batch UI | -16 (inline logic) | +3 (integration) | -13 |
|
|
| 10.1-03 | Container Status | -11 (inline logic) | +10 (integration) | -1 |
|
|
| 10.1-04 | Confirmation Dialogs | -16 (inline logic) | +6 (integration) | -10 |
|
|
| 10.1-06 | Matching/Disambiguation | -12 (inline logic) | +12 (integration + fixes) | 0 |
|
|
| **Total** | **7 sub-workflows** | **-79** | **+46** | **-33** |
|
|
|
|
Starting from 192 nodes (post-Phase 10), reduced to 168 nodes in Phase 10.1 (-24 nodes, -12.5%).
|
|
Including Phase 10 reductions: 209 -> 168 nodes total (-41 nodes, -19.6%).
|
|
|
|
### Diminishing Returns Evidence
|
|
|
|
Each extraction adds integration overhead (Prepare Input, Execute Workflow, Route Result nodes). The extraction benefit decreases as remaining logic is more tightly coupled to the orchestration:
|
|
|
|
- **Batch UI extraction:** 16 nodes extracted, 3 overhead = 81% efficient
|
|
- **Confirmation extraction:** 16 nodes extracted, 6 overhead = 63% efficient
|
|
- **Status extraction:** 11 nodes extracted, 10 overhead = 9% efficient
|
|
- **Matching extraction:** 12 nodes extracted, 12 overhead = 0% efficient
|
|
|
|
Further extraction of the 2 remaining domain-logic nodes (115 total lines) would require 3 overhead nodes, yielding a **-50% efficiency** (net increase of 1 node).
|
|
|
|
### Conclusion
|
|
|
|
The 168-node main workflow is **2 nodes above the structural minimum** of 166. The 115-125 target was based on incomplete analysis of extraction overhead costs. With full evidence:
|
|
|
|
1. **58 Code nodes** are verified orchestration infrastructure (cannot be extracted)
|
|
2. **2 Code nodes** are domain logic but extraction is net-negative
|
|
3. **108 non-Code nodes** are locked infrastructure (Telegram, HTTP, Switch, If, Execute, Trigger)
|
|
4. **All 17 Execute Workflow nodes** pass correct input data to sub-workflows
|
|
5. **All 7 sub-workflow contracts** are formally documented and verified
|
|
|
|
The modularization objective is achieved: domain complexity is in sub-workflows, the main workflow is a routing/orchestration shell.
|