Problem
Issue #1923 fixed the non-interactive device-login hang for story validation by scoping PDD_NO_INTERACTIVE inside run_user_story_tests, which protects pdd detect --stories, pdd change, agentic change, and drift. However, two sibling story-validation paths call detect_change directly WITHOUT that guard and can still reach the interactive GitHub device-flow and block forever in a non-TTY agent/CI shell:
run_user_story_fix (used by pdd fix <story>) calls detect_change directly before it later delegates to the protected runner.
- The story prompt-link caching path (used by story link/add flows) calls
detect_change directly.
Why this is bad
These are the same failure mode as #1923 in adjacent commands; an agent running pdd fix <story> in a non-interactive shell with cloud credentials present but no cached JWT can still hang on a device code.
Evidence
Found during adversarial review of PR #1959. The guard lives in run_user_story_tests; the two direct detect_change call sites bypass it.
Expected behavior
Every story-validation path (including pdd fix <story> and story prompt-link caching) must be non-interactive-safe in a non-TTY shell — no device-login flow starts; missing credentials fail closed with actionable guidance.
Proposed solution
Relocate the non-interactive scoping down into detect_change itself (or a small shared context manager reused by all detect_change call sites in story validation), so the guard is truly shared rather than living only in run_user_story_tests. This would also cover standard pdd detect prompt-change mode. Red/green: pdd fix <story> in a non-TTY shell with cloud creds and no cached JWT must not start device auth and must exit non-zero with guidance.
References
Problem
Issue #1923 fixed the non-interactive device-login hang for story validation by scoping
PDD_NO_INTERACTIVEinsiderun_user_story_tests, which protectspdd detect --stories,pdd change, agentic change, and drift. However, two sibling story-validation paths calldetect_changedirectly WITHOUT that guard and can still reach the interactive GitHub device-flow and block forever in a non-TTY agent/CI shell:run_user_story_fix(used bypdd fix <story>) callsdetect_changedirectly before it later delegates to the protected runner.detect_changedirectly.Why this is bad
These are the same failure mode as #1923 in adjacent commands; an agent running
pdd fix <story>in a non-interactive shell with cloud credentials present but no cached JWT can still hang on a device code.Evidence
Found during adversarial review of PR #1959. The guard lives in
run_user_story_tests; the two directdetect_changecall sites bypass it.Expected behavior
Every story-validation path (including
pdd fix <story>and story prompt-link caching) must be non-interactive-safe in a non-TTY shell — no device-login flow starts; missing credentials fail closed with actionable guidance.Proposed solution
Relocate the non-interactive scoping down into
detect_changeitself (or a small shared context manager reused by alldetect_changecall sites in story validation), so the guard is truly shared rather than living only inrun_user_story_tests. This would also cover standardpdd detectprompt-change mode. Red/green:pdd fix <story>in a non-TTY shell with cloud creds and no cached JWT must not start device auth and must exit non-zero with guidance.References