Skip to content

fix: stop the daily KPI run hanging when no manual activities are given - #33

Merged
DamengRandom merged 1 commit into
masterfrom
fix/scan-manual-kpi-stdin-hang
Aug 2, 2026
Merged

fix: stop the daily KPI run hanging when no manual activities are given#33
DamengRandom merged 1 commit into
masterfrom
fix/scan-manual-kpi-stdin-hang

Conversation

@DamengRandom

Copy link
Copy Markdown
Owner

Closes #32

The bug

promptUser (src/tools/manual-kpi.tool.ts) only resolved from rl.question's answer callback. On a stdin that is already at EOF, readline emits close and that callback never fires, so the promise never settles.

Dispatching daily-kpi with its optional activities input left blank sets MANUAL_ACTIVITIES='' — falsy — so collectActivities takes the interactive readline path on a runner with no keyboard. src/agent/index.ts:132 awaits it, so the job stops there: no KPI record, and no alert either, since a hang is not an exception. Node then drains the event loop and exits 0, so the workflow goes green having saved nothing.

The change

Resolve on readline's close event with an empty answer — which the existing loop already treats as "done". The answer is resolved before rl.close(), so the first settle always wins and the interactive path is unchanged.

Observed vs reasoned

Observed (run from the repo root with npx tsx, stdin from /dev/null, MANUAL_ACTIVITIES=''):

Reasoned, not observed: that a real GitHub Actions runner gives the step a /dev/null stdin. I reproduced that shape locally rather than on a runner. The rest of the pipeline consequence (that runDailyJobs therefore never saves a record and never alerts) follows from src/agent/index.ts:132 and the try/catch at :41-47; I could not execute it here as it needs a database and an LLM key.

Tests

src/tools/manual-kpi.test.ts runs the tool in a child process — readline writes its prompt to stdout, which corrupts the test runner's own reporting channel if run in-process. This mirrors the existing src/fatal-error.test.ts pattern.

  • closed stdin ⇒ zero activities instead of a hang (fails without the fix)
  • an activity typed before stdin ends is still recorded
  • a blank line still ends input

Noted, out of scope

With a piped (non-TTY) stdin, only the first line is ever collected: each prompt builds a fresh readline interface, and closing the first one discards whatever it had already buffered from the pipe. That is pre-existing and unchanged by this PR — before it, the second prompt hung instead. Interactive TTY use and the MANUAL_ACTIVITIES path are unaffected.

Checks

pnpm test (79 pass), pnpm tsc, pnpm format:check all pass.


Generated by Claude Code

promptUser only settled from readline's answer callback, which never fires
when stdin is at EOF. Dispatching daily-kpi with the optional activities
input left blank sets MANUAL_ACTIVITIES='', taking the interactive path on
a runner that has no keyboard, so the job stalled and exited 0 without
saving a KPI record or raising an alert.

Closes #32

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CAM6ikRvg1YcErFvkFLMML
@DamengRandom
DamengRandom marked this pull request as ready for review August 2, 2026 10:30
@DamengRandom
DamengRandom merged commit 428ab16 into master Aug 2, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Daily KPI run hangs then exits 0 when the activities input is left blank — nothing is saved, no alert

2 participants