Skip to content

Fix: REPL /remote command hang in scripted/non-TTY environment#2722

Merged
Devesh36 merged 4 commits into
Tracer-Cloud:mainfrom
PrakharJain345:issue/2707-remote-command-hang
Jun 5, 2026
Merged

Fix: REPL /remote command hang in scripted/non-TTY environment#2722
Devesh36 merged 4 commits into
Tracer-Cloud:mainfrom
PrakharJain345:issue/2707-remote-command-hang

Conversation

@PrakharJain345

@PrakharJain345 PrakharJain345 commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Fixes #2707

Describe the changes you have made in this PR -

This PR fixes the hang/freeze when running the /remote slash command without arguments in a non-TTY / scripted / test-harness environment:

  1. Modified _dispatch_one_turn in loop.py to accept the optional is_tty parameter and forward both confirm_fn and is_tty to dispatch_slash.
  2. Passed is_tty=False from _run_initial_input (scripted test path) down to the turn dispatcher.
  3. Updated is_interactive_env() check to handle the OPENSRE_INTERACTIVE environment variable override appropriately, preventing the child cli process from invoking _run_remote_interactive when stdin is not a TTY.
  4. Added the unit test test_dispatch_one_turn_passes_is_tty_and_confirm_fn_to_dispatch_slash to assert correct parameter forwarding.

Demo/Screenshot for feature changes and bug fixes -

Screen.Recording.2026-06-04.222019.mp4

Verification via unit tests:

tests/cli/test_remote.py::test_remote_health_requires_saved_or_explicit_url PASSED [  5%]
tests/cli/test_remote.py::test_remote_health_uses_saved_url_and_persists_normalized_url PASSED [ 10%]
tests/cli/test_remote.py::test_remote_health_renders_probe_health_output PASSED [ 15%]
tests/cli/test_remote.py::test_remote_trigger_persists_url_after_successful_run PASSED [ 21%]
tests/cli/test_remote.py::test_remote_health_reports_timeout_cleanly PASSED [ 26%]
tests/cli/test_remote.py::test_remote_health_json_output PASSED          [ 31%]
tests/cli/test_remote.py::test_remote_health_connect_error_is_actionable PASSED [ 36%]
tests/cli/test_remote.py::test_remote_group_passes_api_key_to_client PASSED [ 42%]
tests/cli/test_remote.py::test_remote_ops_status_uses_provider_and_persists_scope PASSED [ 47%]
tests/cli/test_remote.py::test_remote_ops_status_prints_json PASSED      [ 52%]
tests/cli/test_remote.py::test_remote_ops_logs_forwards_follow_and_lines PASSED [ 57%]
tests/cli/test_remote.py::test_remote_ops_logs_does_not_persist_scope_on_failure PASSED [ 63%]
tests/cli/test_remote.py::test_remote_ops_restart_cancelled_without_yes PASSED [ 68%]
tests/cli/test_remote.py::test_remote_ops_restart_yes_requests_redeploy PASSED [ 73%]
tests/cli/interactive_shell/test_loop.py::test_dispatch_one_turn_reports_slash_dispatch_error PASSED [ 78%]
tests/cli/interactive_shell/test_loop.py::test_dispatch_one_turn_typoed_bare_alias_dispatches_canonical_slash PASSED [ 84%]
tests/cli/interactive_shell/test_loop.py::test_dispatch_one_turn_routes_to_cli_help_for_help_questions PASSED [ 89%]
tests/cli/interactive_shell/test_loop.py::test_dispatch_one_turn_calls_on_exit_when_slash_returns_false PASSED [ 94%]
tests/cli/interactive_shell/test_loop.py::test_dispatch_one_turn_passes_is_tty_and_confirm_fn_to_dispatch_slash PASSED [100%]

Code Understanding and AI Usage

Did you use AI assistance (ChatGPT, Claude, Copilot, etc.) to write any part of this code?

  • No, I wrote all the code myself
  • Yes, I used AI assistance (continue below)

If you used AI assistance:

  • I have reviewed every single line of the AI-generated code
  • I can explain the purpose and logic of each function/component I added
  • I have tested edge cases and understand how the code handles them
  • I have modified the AI output to follow this project's coding standards and conventions

Explain your implementation approach:
In scripted/seeded runs (like _run_initial_input in tests), the environment isn't interactive and stdin is not a TTY. However, because is_tty=False was never propagated from the REPL turn runner (loop.py) to the slash command dispatcher (dispatch_slash), the OPENSRE_INTERACTIVE environment override (os.environ["OPENSRE_INTERACTIVE"] = "0") was never set. As a result, the /remote command invoked a child subprocess that inherited the interactive setting and attempted to display the interactive questionary picker, hanging the terminal.

We resolved this by propagating the is_tty parameter through _dispatch_one_turn and passing is_tty=False when calling it from _run_initial_input. We also added a unit test to verify that both is_tty and confirm_fn are forwarded correctly.


Checklist before requesting a review

  • I have added proper PR title and linked to the issue
  • I have performed a self-review of my code
  • I can explain the purpose of every function, class, and logic block I added
  • I understand why my changes work and have tested them thoroughly
  • I have considered potential edge cases and how my code handles them
  • If it is a core feature, I have added thorough tests
  • My code follows the project's style guidelines and conventions

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Greptile code review

This repo uses Greptile for automated review. Before merge, aim for Confidence Score: 5/5 with zero unresolved review threads — see CONTRIBUTING.md.

Run a review — add a PR comment with:

@greptile review

Give it ~5-10 minutes (sometimes longer) for results, then fix feedback and re-trigger until you reach Confidence Score: 5/5.

Optional: automate with the greploop skill.

@PrakharJain345 PrakharJain345 marked this pull request as draft June 3, 2026 16:53
@greptile-apps

greptile-apps Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a hang/freeze in the /remote slash command when it is invoked without arguments in a scripted or non-TTY environment. The root cause was that is_tty=False was never propagated from the scripted entry-point (run_initial_input) down to dispatch_slash, so the env-var override (OPENSRE_INTERACTIVE=0) needed to short-circuit the interactive questionary picker was never set.

  • Parameter propagation: is_tty is threaded through dispatch_one_turnexecute_routed_turndispatch_slash / execute_cli_actions_with_metrics / answer_cli_agent / run_new_alert, and run_initial_input hardwires is_tty=False.
  • Non-interactive guard in remote.py: The no-subcommand branch gains not sys.stdin.isatty(), not sys.stdout.isatty(), and not is_interactive_env() guards so the interactive picker is never reached in piped/scripted contexts.
  • dispatch_slash env isolation: When is_tty=False, dispatch_slash sets OPENSRE_INTERACTIVE=0 for the duration of the handler and restores the prior value in a finally block.

Confidence Score: 5/5

Safe to merge — the fix is a clean parameter propagation through an existing call chain with no behavioural changes to interactive paths.

The change is well-scoped: is_tty is threaded mechanically through every dispatch layer and run_initial_input correctly hardwires is_tty=False. The non-TTY guards added to remote.py are additive and do not affect the interactive code path. Unit tests verify both the parameter forwarding and the is_tty=False contract on the scripted entry-point. The only known fragility — the del os.environ in the finally block — has already been flagged in a prior review cycle and does not affect normal operation.

app/cli/interactive_shell/command_registry/init.py — the finally cleanup still uses del rather than os.environ.pop(None).

Important Files Changed

Filename Overview
app/cli/interactive_shell/command_registry/init.py Wraps all of dispatch_slash in a try/finally that sets OPENSRE_INTERACTIVE=0 when is_tty=False; the del os.environ in the finally path can raise KeyError if the key is removed inside the try block (already flagged in prior review cycle).
app/cli/interactive_shell/runtime/dispatch.py Adds is_tty to dispatch_one_turn and run_initial_input hardwires is_tty=False; straightforward and correct.
app/cli/interactive_shell/runtime/execution.py Propagates is_tty to all four dispatch branches (slash, cli_agent actions, cli_agent LLM, new_alert); clean mechanical change.
app/cli/commands/remote.py Adds sys.stdin.isatty(), sys.stdout.isatty(), and is_interactive_env() guards to prevent the interactive picker from being invoked in non-TTY contexts.
app/cli/support/context.py Adds is_interactive_env() which checks OPENSRE_INTERACTIVE=0 env var first, then falls back to the Click context interactive flag; correct and consistent.
app/cli/interactive_shell/chat/cli_agent.py Adds is_tty parameter to answer_cli_agent and forwards it to _execute_action_plan; all dispatch_slash and run_opensre_cli_command call sites correctly forward is_tty=is_tty.
tests/cli/interactive_shell/test_terminal_runtime.py Adds two well-formed unit tests verifying dispatch_one_turn forwards is_tty and confirm_fn, and that run_initial_input always dispatches with is_tty=False.
tests/cli/interactive_shell/test_terminal_runtime_routing.py Minor stub update to accept the new is_tty parameter; no logic changes.
tests/cli/test_main.py Relaxes the Click error-message assertion to be robust against minor phrasing differences across Click versions.
app/cli/interactive_shell/routing/handle_message_with_agent/orchestration/agent_actions.py Mechanical addition of is_tty parameter and forwarding to _execute_planned_actions; correct.

Sequence Diagram

sequenceDiagram
    participant RI as run_initial_input
    participant DOT as dispatch_one_turn
    participant ERT as execute_routed_turn
    participant DS as dispatch_slash
    participant ENV as os.environ
    participant RC as remote (Click cmd)

    RI->>DOT: "dispatch_one_turn('/remote', is_tty=False)"
    DOT->>ERT: "execute_routed_turn(..., is_tty=False)"
    ERT->>DS: "dispatch_slash('/remote', is_tty=False)"
    DS->>ENV: "set OPENSRE_INTERACTIVE=0"
    DS->>RC: cmd.handler(session, console, args)
    RC->>RC: ctx.invoked_subcommand is None?
    RC->>RC: not is_interactive_env() True
    RC-->>DS: raise OpenSREError No subcommand provided
    DS->>ENV: restore OPENSRE_INTERACTIVE (finally)
    DS-->>ERT: exception propagates / handled
Loading

Reviews (3): Last reviewed commit: "Relax Click option error assertion" | Re-trigger Greptile

Comment thread app/cli/interactive_shell/command_registry/__init__.py
Comment thread app/cli/commands/tests.py Outdated
@PrakharJain345 PrakharJain345 force-pushed the issue/2707-remote-command-hang branch from f8d1ac7 to ef82dd3 Compare June 3, 2026 17:58
@PrakharJain345 PrakharJain345 marked this pull request as ready for review June 3, 2026 17:59
@PrakharJain345

Copy link
Copy Markdown
Contributor Author

@greptileai review

@PrakharJain345 PrakharJain345 force-pushed the issue/2707-remote-command-hang branch from 9cec1e0 to 99419e6 Compare June 4, 2026 15:12
@Devesh36

Devesh36 commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

Can u provide demo , like any screenshot

@PrakharJain345

Copy link
Copy Markdown
Contributor Author

Hi @Devesh36 ,

I've updated the Pull Request description to include the demo proof.
Please take a look when you have a moment!

@Devesh36 Devesh36 merged commit b7e2396 into Tracer-Cloud:main Jun 5, 2026
14 checks passed
@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

🍕 @PrakharJain345's PR: crispy edges, no unnecessary toppings, delivered on time. Understood the assignment. 🔥


👋 Join us on Discord - OpenSRE : hang out, contribute, or hunt for features and issues. Everyone's welcome.

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.

[BUG] /remote slash command hangs REPL in scripted/non-TTY environment

2 participants