fix(tui-gateway): secondary side workers and branch builds honour their own terminal backend; launch env-only SSH/Docker policy survives multiplexing - #110207
Merged
Conversation
…own terminal scope Under multiplexing tools/terminal_tool.py no longer bridges a profile's terminal.* into os.environ while a home override is bound (#108440), so any secondary-profile entrypoint that binds only the home (+ secrets) leaves terminal_tool on the launch process's ambient TERMINAL_*: a secondary with `terminal.backend: docker` ran its prompt.background / prompt.btw / preview.restart side agents, its eager resume and its session.branch build on the launch `local` backend. _spawn_side_agent and _profile_build_scope now enter _session_profile_runtime_scope, the same home -> secrets -> terminal composition a prompt turn binds (and gateway/run.py::_profile_runtime_scope mirrors). The terminal scope is installed for the whole worker/build lifetime and reset on success and on exception; a malformed secondary config still yields the fail-closed refusal scope. No ambient os.environ write is restored. Refs #108440 review (andrexibiza), #107442 (ehz0ah).
…olicy once multiplexing is active 606903b made launch-profile turns bind a file-backed terminal scope as soon as any secondary home is served, so a poisoned ambient bridge can never be the launch turn's authority. That scope is rebuilt from defaults + <home>/.env + config.yaml only, which drops the launch process's legitimate env-only policy: TERMINAL_ENV=ssh TERMINAL_SSH_HOST=example.test with a `{}` config.yaml became backend=local, ssh_host='' the moment a second profile was served. tui_gateway/launch_terminal_policy.py freezes the process TERMINAL_* once, in _profile_home right before the first secondary home is registered as served — the last moment ambient env is provably the launch profile's own. build_profile_terminal_scope takes that snapshot as a trusted env_overlay sitting where the process env sits in the standalone bridge (explicit YAML keys still win). Launch turns overlay the snapshot; ambient os.environ is never re-read after activation, so a later secondary write is still rejected, and the scope is reset after the turn as before. Refs #108440 review (andrexibiza), #107442 (ehz0ah).
…l scope and launch env-only policy Two per finding, proven red on origin/main b9271bc: secondary side worker and branch build run their own docker backend (scope reset on success and exception, os.environ untouched); launch turns keep env-only SSH policy after activation and ignore ambient TERMINAL_* written afterwards.
૮ >ﻌ< ა ci reviewran on e9ca8c9 — test(tui-gateway): invariants for secondary side-worker/buil
|
This was referenced Sep 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Under
gateway.multiplex_profiles, a secondary profile'sprompt.background/prompt.btw/preview.restartside agents and its resume/branch agent builds now run on that profile's own terminal backend, and launch-profile turns keep the process's env-onlyTERMINAL_*policy (systemd /op run/ launcher-bridged SSH or Docker) instead of collapsing tolocalonce a second profile is served.Two P1 findings from the andrexibiza review of #108440, both re-verified RED on
origin/mainb9271bc with the review's own probes.Changes
tui_gateway/methods_prompt.py::_spawn_side_agentandmethods_session.py::_profile_build_scopeenter_session_profile_runtime_scope— the same home → secrets → terminal composition a prompt turn binds (mirror ofgateway/run.py::_profile_runtime_scope). The terminal scope covers the whole worker/build lifetime, resets on success and on exception, and a malformed secondary config still installs the fail-closed refusal scope. No ambientos.environwrite is restored.tui_gateway/launch_terminal_policy.py(new, ~45 LOC): freezes the processTERMINAL_*once, inserver._profile_homeright before the first secondary home is registered as served — the last moment ambient env is provably the launch profile's own. Never re-read from ambient state afterwards.tools/terminal_scope.py::build_profile_terminal_scope(..., env_overlay=): optional trusted overlay sitting where the process env sits in the standalone bridge (defaults ←.env← overlay ← explicitconfig.yamlkeys).prompt_turn._prepare_turn_inputpasses the launch snapshot for launch turns once multiplexing is active.tests/tui_gateway/test_profile_terminal_scope_entrypoints.py— 2 per finding, red on base (side worker + branch build pickdocker, scope reset on success/exception,os.environuntouched; launch turn keeps env-only SSH after activation and ignores ambientTERMINAL_*written afterwards).Validation
Review probes (
contract_probes.py, fresh process each,env -i), secondaryterminal.backend: docker, launchTERMINAL_ENV=local/ env-onlyTERMINAL_ENV=ssh TERMINAL_SSH_HOST=example.testwith{}config:origin/mainb9271bc)terminal_side(prompt.background worker){"scope_bound": false, "backend": "local"}✗{"scope_bound": true, "backend": "docker"}✓terminal_build(session.branch build){"scope_bound": false, "backend": "local"}✗{"scope_bound": true, "backend": "docker"}✓terminal_launch(launch turn, secondary served){"scope_bound": true, "backend": "local", "ssh_host": ""}✗{"scope_bound": true, "backend": "ssh", "ssh_host": "example.test"}✓docker, scopeNoneafter each,os.environ["TERMINAL_ENV"]stilllocal✓config.yamlinside_profile_build_scope_get_env_config()raisesTerminalPolicyUnavailable, reset after ✓scripts/run_tests.sh tests/tui_gateway/ tests/tools/test_terminal_*→ 154 files, 1938 passed, 0 failed.ruff,check-windows-footguns --all,check_compat_pointers,git diff --checkclean.Root cause
#108440 correctly stopped
terminal_toolfrom bridging a routed profile'sterminal.*intoos.environ, which turned every "home-only" secondary entrypoint into an ambient-launch-backend leak, and 606903b's launch-turn scope rebuilt policy from files alone, so the launch process's env-only policy had nothing to be rebuilt from.Refs #108440 review (andrexibiza), #107442 (ehz0ah).
Infographic