Skip to content

Commit 9ca461a

Browse files
committed
fix(runner): make RunnerContext.get_env() read live from os.environ
RunnerContext.__post_init__ snapshots os.environ once at construction. After that, get_env() reads from the frozen dict. Multiple endpoints mutate os.environ at runtime (POST /workflow, POST /repos/add, auth refresh), but the context is never recreated — mark_dirty() resets the bridge while reusing the same stale context. Store explicit constructor overrides separately in _overrides and make get_env() check overrides first, then fall through to live os.environ. self.environment remains populated for backward compat. Made-with: Cursor
1 parent 744c0d4 commit 9ca461a

2 files changed

Lines changed: 46 additions & 3 deletions

File tree

components/runners/ambient-runner/ambient_runner/platform/context.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,15 @@ class RunnerContext:
2828
metadata: Dict[str, Any] = field(default_factory=dict)
2929

3030
def __post_init__(self) -> None:
31-
"""Merge environment variables (explicit overrides win)."""
31+
"""Store explicit overrides for precedence in get_env(); keep environment populated for backward compatibility."""
32+
self._overrides = dict(self.environment)
3233
self.environment = {**os.environ, **self.environment}
3334

3435
def get_env(self, key: str, default: Optional[str] = None) -> Optional[str]:
35-
"""Get an environment variable value."""
36-
return self.environment.get(key, default)
36+
"""Get an environment variable, with explicit overrides winning. Reads live from os.environ for non-overridden keys."""
37+
if key in self._overrides:
38+
return self._overrides[key]
39+
return os.environ.get(key, default)
3740

3841
def set_metadata(self, key: str, value: Any) -> None:
3942
"""Set a metadata value."""
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""Tests for RunnerContext."""
2+
3+
import os
4+
5+
import pytest
6+
7+
from ambient_runner.platform.context import RunnerContext
8+
9+
10+
class TestRunnerContextGetEnv:
11+
"""Verify get_env() reads live from os.environ and respects explicit overrides."""
12+
13+
def test_get_env_sees_os_environ_mutations_after_creation(self):
14+
"""get_env() must return current os.environ value when key is mutated at runtime."""
15+
key = "_RUNNER_CONTEXT_TEST_MUTATION_"
16+
try:
17+
if key in os.environ:
18+
del os.environ[key]
19+
ctx = RunnerContext(session_id="s1", workspace_path="/tmp")
20+
assert ctx.get_env(key) is None
21+
os.environ[key] = "runtime-mutated"
22+
assert ctx.get_env(key) == "runtime-mutated"
23+
finally:
24+
os.environ.pop(key, None)
25+
26+
def test_explicit_overrides_win_over_os_environ(self):
27+
"""Explicit overrides passed at construction must take precedence over os.environ."""
28+
key = "_RUNNER_CONTEXT_TEST_OVERRIDE_"
29+
try:
30+
os.environ[key] = "from-os-environ"
31+
ctx = RunnerContext(
32+
session_id="s1",
33+
workspace_path="/tmp",
34+
environment={key: "from-constructor"},
35+
)
36+
assert ctx.get_env(key) == "from-constructor"
37+
os.environ[key] = "mutated-after-creation"
38+
assert ctx.get_env(key) == "from-constructor"
39+
finally:
40+
os.environ.pop(key, None)

0 commit comments

Comments
 (0)