|
| 1 | +# Case Study: Issue #1096 - Logs Upload Failed |
| 2 | + |
| 3 | +## Problem Statement |
| 4 | + |
| 5 | +When the hive-mind system attempted to upload a solution draft log to GitHub via `gh-upload-log`, it failed with: |
| 6 | + |
| 7 | +``` |
| 8 | +Error: File does not exist: "/tmp/solution-draft-log-pr-1768003849690.txt" --public --verbose |
| 9 | +``` |
| 10 | + |
| 11 | +The error message shows that the CLI flags (`--public --verbose`) were incorrectly included as part of the filename argument, causing `gh-upload-log` to look for a non-existent file with that combined string as its name. |
| 12 | + |
| 13 | +## Timeline of Events |
| 14 | + |
| 15 | +| Timestamp | Event | |
| 16 | +| ------------------- | ------------------------------------------------------------ | |
| 17 | +| 2026-01-09 23:52:55 | solve.mjs started processing issue #1083 | |
| 18 | +| 2026-01-09 23:53:34 | PR #1090 created as draft | |
| 19 | +| 2026-01-10 00:10:48 | Solution completed, attempting to upload log | |
| 20 | +| 2026-01-10 00:10:49 | Log too long (1,174,742 chars), GitHub limit is 65,536 chars | |
| 21 | +| 2026-01-10 00:10:49 | Sanitization completed (5 secrets masked) | |
| 22 | +| 2026-01-10 00:10:51 | **gh-upload-log FAILED** with argument parsing error | |
| 23 | +| 2026-01-10 00:10:51 | Fallback to truncated comment initiated | |
| 24 | +| 2026-01-10 00:10:54 | Truncated log uploaded successfully (1156KB) | |
| 25 | +| 2026-01-10 00:10:54 | Process reported as successful (but log was truncated) | |
| 26 | + |
| 27 | +## Root Cause Analysis |
| 28 | + |
| 29 | +### Primary Root Cause: command-stream Template Literal Argument Handling |
| 30 | + |
| 31 | +The bug is in `src/log-upload.lib.mjs` at line 52: |
| 32 | + |
| 33 | +```javascript |
| 34 | +// BUGGY CODE: |
| 35 | +const commandArgs = [`"${logFile}"`, publicFlag]; |
| 36 | +if (verbose) { |
| 37 | + commandArgs.push('--verbose'); |
| 38 | +} |
| 39 | +const uploadResult = await $`gh-upload-log ${commandArgs.join(' ')}`; |
| 40 | +``` |
| 41 | + |
| 42 | +**The Problem:** When using `command-stream`'s `$` template tag, a single template interpolation (`${commandArgs.join(' ')}`) is treated as a **single argument**, regardless of internal spaces. This means: |
| 43 | + |
| 44 | +1. `commandArgs.join(' ')` produces: `"/tmp/file.txt" --public --verbose` |
| 45 | +2. The entire string becomes the **first positional argument** to `gh-upload-log` |
| 46 | +3. `gh-upload-log` interprets this as the file path, leading to the error |
| 47 | + |
| 48 | +### Evidence from Logs |
| 49 | + |
| 50 | +The displayed command showed (line 7869): |
| 51 | + |
| 52 | +``` |
| 53 | +Running: gh-upload-log "/tmp/solution-draft-log-pr-1768003849690.txt" --public --description "..." --verbose |
| 54 | +``` |
| 55 | + |
| 56 | +But the actual error showed (line 7872): |
| 57 | + |
| 58 | +``` |
| 59 | +Error: File does not exist: "/tmp/solution-draft-log-pr-1768003849690.txt" --public --verbose |
| 60 | +``` |
| 61 | + |
| 62 | +Note: The `--description` flag was missing from the error because it was never included in `commandArgs` (only in the display command). |
| 63 | + |
| 64 | +### Secondary Issue: Description Flag Not Passed |
| 65 | + |
| 66 | +The code builds a display command including `--description` (line 41): |
| 67 | + |
| 68 | +```javascript |
| 69 | +const command = `gh-upload-log "${logFile}" ${publicFlag} ${descFlag} ${verboseFlag}`; |
| 70 | +``` |
| 71 | + |
| 72 | +But the actually executed command (line 52) uses `commandArgs` which doesn't include `descFlag`: |
| 73 | + |
| 74 | +```javascript |
| 75 | +const commandArgs = [`"${logFile}"`, publicFlag]; |
| 76 | +``` |
| 77 | + |
| 78 | +This means the description is never actually sent to `gh-upload-log`. |
| 79 | + |
| 80 | +## Solution |
| 81 | + |
| 82 | +### Fix 1: Use Separate Template Interpolations |
| 83 | + |
| 84 | +Replace the single joined interpolation with individual interpolations: |
| 85 | + |
| 86 | +```javascript |
| 87 | +// FIXED CODE: |
| 88 | +const publicFlag = isPublic ? '--public' : '--private'; |
| 89 | +const verboseFlag = verbose ? '--verbose' : ''; |
| 90 | + |
| 91 | +const uploadResult = await $`gh-upload-log ${logFile} ${publicFlag} ${verboseFlag}`; |
| 92 | +``` |
| 93 | + |
| 94 | +Each `${}` interpolation is properly handled as a separate argument by command-stream. |
| 95 | + |
| 96 | +### Fix 2: Include Description Flag |
| 97 | + |
| 98 | +```javascript |
| 99 | +const descFlag = description ? ['--description', description] : []; |
| 100 | +// Then use spread operator or multiple interpolations |
| 101 | +``` |
| 102 | + |
| 103 | +## Testing |
| 104 | + |
| 105 | +### Reproduction Test |
| 106 | + |
| 107 | +```javascript |
| 108 | +// This reproduces the bug: |
| 109 | +const commandArgs = [`"${logFile}"`, '--public', '--verbose']; |
| 110 | +const uploadResult = await $`gh-upload-log ${commandArgs.join(' ')}`; |
| 111 | +// Result: gh-upload-log receives entire string as file path |
| 112 | +``` |
| 113 | + |
| 114 | +### Verification Test |
| 115 | + |
| 116 | +```javascript |
| 117 | +// This works correctly: |
| 118 | +const uploadResult = await $`gh-upload-log ${logFile} --public --verbose`; |
| 119 | +// Result: arguments are properly parsed |
| 120 | +``` |
| 121 | + |
| 122 | +## Files in This Case Study |
| 123 | + |
| 124 | +- `README.md` - This document |
| 125 | +- `full-log.txt` - Complete log from the failed execution (7891 lines) |
| 126 | + |
| 127 | +## Related Links |
| 128 | + |
| 129 | +- Original Issue: https://github.com/link-assistant/hive-mind/issues/1096 |
| 130 | +- Pull Request: https://github.com/link-assistant/hive-mind/pull/1097 |
| 131 | +- gh-upload-log Tool: https://github.com/link-foundation/gh-upload-log |
| 132 | +- Previous Case Study: Issue #587 (large file upload limitations) |
| 133 | + |
| 134 | +## Lessons Learned |
| 135 | + |
| 136 | +1. **Template literals in shell commands require careful handling**: Each `${}` interpolation in command-stream is treated as a single argument. Don't join multiple arguments into one string. |
| 137 | + |
| 138 | +2. **Display vs execution divergence is dangerous**: The code displayed one command but executed a different one, making debugging harder. |
| 139 | + |
| 140 | +3. **Log truncation should be considered a failure**: The "successful" completion masked the actual upload failure. Truncated logs lose critical debugging information. |
| 141 | + |
| 142 | +4. **Tests should verify actual command execution**: The existing tests verified argument building logic but didn't test actual command execution with command-stream. |
0 commit comments