diff --git a/src/main/claude/statusline-script.test.ts b/src/main/claude/statusline-script.test.ts index 690d206044a..09fc0854ef5 100644 --- a/src/main/claude/statusline-script.test.ts +++ b/src/main/claude/statusline-script.test.ts @@ -148,8 +148,14 @@ describe('statusline curl throttle (posix)', () => { expect(durationIndex).toBeLessThan(intervalIndex) expect(intervalIndex).toBeLessThan(curlIndex) // Fail-open shape: non-numeric date output or stamp content must never suppress the post. - expect(script).toContain('case "$orca_statusline_now" in \'\'|*[!0-9]*) orca_statusline_now=') - expect(script).toContain('case "$orca_statusline_last" in \'\'|*[!0-9]*) orca_statusline_last=') + // Why: the allow-list (not a mere digits check) matters — leading-zero values like 008 are + // invalid octal inside $(( )) and abort the whole script under dash, wedging the stamp. + expect(script).toContain( + 'case "$orca_statusline_now" in 0|[1-9]|[1-9][0-9]*) ;; *) orca_statusline_now=' + ) + expect(script).toContain( + 'case "$orca_statusline_last" in 0|[1-9]|[1-9][0-9]*) ;; *) orca_statusline_last=' + ) }) }) @@ -262,6 +268,16 @@ describe.skipIf(process.platform === 'win32')('statusline curl throttle (posix b expect(readFileSync(stampPathFor(dir), 'utf8')).toMatch(/^[0-9]+$/) }) + it('fails open and repairs a leading-zero stamp instead of dying in arithmetic', async () => { + const { scriptPath, dir, curlLog } = makeHarness() + // Why: 008 is all-digits but invalid octal inside $(( )) — under dash the old digits-only + // check made the script abort before rewriting the stamp, wedging this pane's feed dark. + writeFileSync(stampPathFor(dir), '008') + await runScript(scriptPath, dir, rateLimitPayload(1_000)) + expect(lineCount(curlLog)).toBe(1) + expect(readFileSync(stampPathFor(dir), 'utf8')).toBe('1') + }) + it('uses the clock fallback when the payload omits session duration', async () => { const { scriptPath, dir, curlLog, dateLog } = makeHarness() const payload = '{"rate_limits":{"five_hour":{"used_percentage":12}}}' diff --git a/src/main/claude/statusline-script.ts b/src/main/claude/statusline-script.ts index 341b93330f9..f1ff016afd3 100644 --- a/src/main/claude/statusline-script.ts +++ b/src/main/claude/statusline-script.ts @@ -126,11 +126,14 @@ export function getManagedStatusLineScript(target: 'local' | 'posix' = 'local'): 'if [ -z "$orca_statusline_now" ]; then', ' orca_statusline_now=$(date +%s 2>/dev/null) || orca_statusline_now=', 'fi', - 'case "$orca_statusline_now" in \'\'|*[!0-9]*) orca_statusline_now= ;; esac', + // Why: leading zeros read as octal inside $(( )), and a bad constant (008) is FATAL in dash — + // the script would die before rewriting the stamp, wedging the pane dark. Allow-list canonical + // decimals so any malformed value fails open to posting instead. + 'case "$orca_statusline_now" in 0|[1-9]|[1-9][0-9]*) ;; *) orca_statusline_now= ;; esac', 'if [ -n "$orca_statusline_now" ] && [ -f "$orca_statusline_stamp" ]; then', ' orca_statusline_last=', ' IFS= read -r orca_statusline_last <"$orca_statusline_stamp" 2>/dev/null || :', - ' case "$orca_statusline_last" in \'\'|*[!0-9]*) orca_statusline_last= ;; esac', + ' case "$orca_statusline_last" in 0|[1-9]|[1-9][0-9]*) ;; *) orca_statusline_last= ;; esac', ' if [ "${#orca_statusline_last}" -gt 15 ]; then orca_statusline_last=; fi', ' if [ -n "$orca_statusline_last" ]; then', ' orca_statusline_elapsed=$((orca_statusline_now - orca_statusline_last))',