Skip to content

wavecli: Fix refresh consent gate misdetecting non-TTY stdin - #1005

Merged
Roasbeef merged 1 commit into
mainfrom
fix/refresh-consent-tty-detection
Jul 20, 2026
Merged

wavecli: Fix refresh consent gate misdetecting non-TTY stdin#1005
Roasbeef merged 1 commit into
mainfrom
fix/refresh-consent-tty-detection

Conversation

@Roasbeef

Copy link
Copy Markdown
Member

In this PR, we fix a bug found while manually testing #987 on the arktest
regtest harness: the refresh consent gate treats /dev/null and other
non-terminal character devices as an interactive terminal, so a real
refresh on that stdin prints a y/N prompt and then dies on EOF instead
of refusing cleanly.

The bug

#987 gates a real ark vtxos refresh on explicit consent: an interactive
TTY prompts, --yes skips the prompt for scripted use, and
non-interactive stdin must refuse with an actionable INVALID_ARGS error
directing the caller to --yes or --dry_run, so an agent is never
blocked on a y/N it cannot answer. The refusal is driven by
defaultStdinIsTTY, which reported any character device as a terminal.

But /dev/null and a closed descriptor -- the most common
non-interactive stdin shapes for agents, CI, and systemd units -- are
character devices that are not terminals. On those, the gate printed the
prompt and then failed with read confirmation: EOF (EXECUTION_FAILED,
exit 1), the exact hang-then-fail the gate exists to prevent. Only a pipe
(echo | wavecli) was refused cleanly.

Observed on the live daemon:

stdin before after
pipe INVALID_ARGS, exit 2 INVALID_ARGS, exit 2
/dev/null prompt then EOF, exit 1 INVALID_ARGS, exit 2
closed (0<&-) prompt then EOF, exit 1 INVALID_ARGS, exit 2

The fix

We detect a real terminal with term.IsTerminal instead of the
character-device heuristic. Every non-terminal stdin now refuses with the
actionable message; the custom-reader path (embedded harnesses that drive
the prompt themselves) is unchanged. The same detector backs the
leave --all, recovery escalate, and send confirmations, so all four
consent gates are fixed at once.

defaultStdinIsTTY had no direct test -- the suite stubs the
stdinIsTTY indirection, which is how the heuristic shipped. A
regression test now exercises the real function against /dev/null, a
pipe, and a closed descriptor (and fails on the old char-device check).

Validation

  • go test -race -run TestDefaultStdinIsTTY ./cmd/wavecli/waveclicommands/
  • Full refresh/leave/confirm suite under -race
  • Live re-test against the arktest daemon (table above)
  • make fmt-changed-check, make lint-changed-local (0 issues),
    make commitmsg-lint

Found while manually testing #987.

In this commit, we fix the refresh consent gate treating /dev/null and
other non-terminal character devices as an interactive terminal. The
gate's defaultStdinIsTTY reported any character device as a TTY, but
/dev/null and a closed descriptor -- the most common non-interactive
stdin shapes for agents, CI, and systemd units -- are character devices
that are not terminals. On those, the gate printed a y/N prompt that
immediately read EOF and failed with EXECUTION_FAILED, the exact
hang-then-fail #986 set out to prevent; only a pipe stdin was refused
cleanly with the actionable INVALID_ARGS message.

We now detect a real terminal with term.IsTerminal instead of the
character-device heuristic, so every non-terminal stdin refuses with the
message directing the caller to --yes or --dry_run. The custom-reader
path (embedded harnesses that drive the prompt themselves) is unchanged.

defaultStdinIsTTY had no direct test -- the suite stubs the stdinIsTTY
indirection -- which is how the heuristic shipped; a regression test now
exercises the real function against /dev/null, a pipe, and a closed
descriptor. The same detection backs the leave --all, recovery escalate,
and send confirmations, so all four gates are fixed.
@Roasbeef Roasbeef added the backport-v0.1.x-branch Backport this merged PR to v0.1.x-branch label Jul 20, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request replaces the character-device check for os.Stdin with a proper terminal check using term.IsTerminal to prevent non-interactive inputs (like /dev/null or closed descriptors) from being incorrectly treated as interactive terminals. Additionally, a comprehensive suite of unit tests has been added to verify this behavior under various conditions. I have no feedback to provide as the changes are well-implemented and include thorough test coverage.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@Roasbeef
Roasbeef merged commit 979a578 into main Jul 20, 2026
21 checks passed
@github-actions

Copy link
Copy Markdown

Successfully created backport PR for v0.1.x-branch:

Roasbeef added a commit that referenced this pull request Jul 20, 2026
…ranch

[v0.1.x-branch] Backport #1005: wavecli: Fix refresh consent gate misdetecting non-TTY stdin
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-v0.1.x-branch Backport this merged PR to v0.1.x-branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant