Skip to content

fix(tools): send init_command through the same framing as user commands 🤖🤖🤖 - #201

Open
shoemoney wants to merge 1 commit into
NVIDIA-NeMo:mainfrom
shoemoney:fix/init-command-framing
Open

fix(tools): send init_command through the same framing as user commands 🤖🤖🤖#201
shoemoney wants to merge 1 commit into
NVIDIA-NeMo:mainfrom
shoemoney:fix/init-command-framing

Conversation

@shoemoney

Copy link
Copy Markdown

What does this PR do?

Routes the one-time init_command in BashSession.start() through _build_script, the same framing run() and stream() already use, and logs a warning when the init command exits nonzero.

On main, start() composes init_script as raw shell text with the control protocol lines appended (src/nooa/tools/_bash_session.py:213), while user commands go through _build_script (:289, :345), which base64-encodes the payload and redirects stdin from /dev/null. PR #149 introduced that framing to keep bash's parser away from the protocol, but the init path never got it. Two consequences:

  • An init_command with an unbalanced quote (echo "unclosed) makes bash read the protocol lines as string content, so the sentinel never arrives and start() blocks for its full 60s timeout.
  • An init_command that reads stdin (cat) consumes the protocol lines as its own input, same 60s wedge.

With the framing applied, both cases return promptly and the session stays usable. A nonzero init exit now also logs init_command ... exited N instead of passing silently.

Deliberately not changed: a failed init still does not abort start(), it only warns, matching how run() reports command failures in-band rather than raising. Whether a bad init should fail session creation is a design call for maintainers.

Related issues

None on file. Sibling precedent: PR #149 (merged 2026-08-18), which fixed this same bug class for user commands.

How tested

Four regression tests in tests/tools/test_bash_session_framing.py::TestInitCommandUsesTheSameFraming. With the source change reverted and the tests kept in place, the two wedge tests fail on timeout and the warning test fails on the missing log record (3 failed, 19 passed); the benign-init test passes both ways, since the old code handled well-formed inits. With the fix, 22 passed in the framing file and 302 passed across tests/tools/.

Checklist

  • Code follows the project style (uv run ruff check . and uv run ruff format --check . pass)
  • Tests added/updated and passing (uv run pytest)
  • Docs updated if behavior or public APIs changed (no public API change; the warning goes through the module logger)
  • New source files carry an SPDX license header (no new files)

Written in conjunction with my pair programmer Claude. 🤖🤖🤖

…ds 🤖🤖🤖

start() composes init_script as raw shell text with the control
protocol lines appended, while run() and stream() both go through
_build_script (base64 payload plus </dev/null), the framing PR NVIDIA-NeMo#149
introduced to keep bash's parser away from the protocol. An
init_command with an unbalanced quote swallows the protocol lines as
string content, and one that reads stdin (cat) consumes them as
input; either way start() blocks for its full 60s timeout and the
session is unusable. Route init_command through the same
_build_script framing, and log a warning when the init command exits
nonzero so a failed env setup is visible instead of silent.

Sibling precedent: PR NVIDIA-NeMo#149 (merged 2026-08-18), which fixed the same
bug class for user commands.

Written in conjunction with my pair programmer Claude.

Signed-off-by: Jeremy Schoemaker <jeremy@shoemoney.com>
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.

1 participant