Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/autoskillit/core/types/_type_constants_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@
CODEX_INTERACTIVE_REQUIRED_ENV: frozenset[str] = frozenset(
{
MCP_CLIENT_BACKEND_ENV_VAR,
AGENT_BACKEND_ENV_VAR,
}
)

Expand Down
4 changes: 4 additions & 0 deletions src/autoskillit/execution/backends/claude.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,8 @@ def build_interactive_cmd(
for t in tools:
builder.variadic_pair(ClaudeFlags.TOOLS, t)
merged: dict[str, str] = dict(SHARED_BASELINE_ENV)
merged[AGENT_BACKEND_ENV_VAR] = AGENT_BACKEND_CLAUDE_CODE
merged[AGENT_BACKEND_DYNACONF_ENV_VAR] = AGENT_BACKEND_CLAUDE_CODE
if env_extras:
merged.update(env_extras)
interactive_base = {
Expand Down Expand Up @@ -500,6 +502,8 @@ def build_resume_cmd(
case None:
pass
merged: dict[str, str] = dict(SHARED_BASELINE_ENV)
merged[AGENT_BACKEND_ENV_VAR] = AGENT_BACKEND_CLAUDE_CODE
merged[AGENT_BACKEND_DYNACONF_ENV_VAR] = AGENT_BACKEND_CLAUDE_CODE
if env_extras:
merged.update(env_extras)
env = dict(build_agent_env(base={}, extras=merged))
Expand Down
5 changes: 4 additions & 1 deletion src/autoskillit/execution/backends/codex.py
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,7 @@ def build_interactive_cmd(
"AUTOSKILLIT_HEADLESS": "",
"AUTOSKILLIT_HEADLESS_AUTO_GATE": "",
"AUTOSKILLIT_SESSION_TYPE": "",
AGENT_BACKEND_ENV_VAR: AGENT_BACKEND_CODEX,
AGENT_BACKEND_DYNACONF_ENV_VAR: AGENT_BACKEND_CODEX,
MCP_CLIENT_BACKEND_ENV_VAR: AGENT_BACKEND_CODEX,
FOOD_TRUCK_TOOL_TAGS_ENV_VAR: "",
Expand Down Expand Up @@ -926,7 +927,9 @@ def build_resume_cmd(
cmd.append(resume_session_id)
cmd.append(prompt)
filtered_base = {k: v for k, v in os.environ.items() if k not in _HEADLESS_EXCLUSIVE_VARS}
resume_extras = _codex_exec_extras(session_type="", include_session_baseline=True)
resume_extras = _codex_exec_extras(
session_type="", include_session_baseline=True, include_agent_backend_flat=True
)
if env_extras:
resume_extras.update(env_extras)
env = self.env_policy().build_env(
Expand Down
69 changes: 69 additions & 0 deletions tests/arch/test_env_symmetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,72 @@ def test_dynaconf_and_flat_backend_values_match() -> None:
food_truck_spec.env["AUTOSKILLIT_AGENT_BACKEND__BACKEND"]
== food_truck_spec.env["AUTOSKILLIT_AGENT_BACKEND"]
), f"{name}: nested and flat AGENT_BACKEND values differ in build_food_truck_cmd"


_ALL_GUARD_BUILDERS: list[str] = [
"build_skill_session_cmd",
"build_food_truck_cmd",
"build_interactive_cmd",
"build_resume_cmd",
]


def _call_builder(backend: object, builder_name: str) -> object:
from autoskillit.core.types._type_plugin_source import DirectInstall

if builder_name == "build_skill_session_cmd":
return backend.build_skill_session_cmd(
"/autoskillit:investigate", "/repo", completion_marker="DONE"
)
if builder_name == "build_food_truck_cmd":
return backend.build_food_truck_cmd(
orchestrator_prompt="test prompt",
plugin_source=DirectInstall(plugin_dir=Path("/plugins")),
cwd="/repo",
completion_marker="DONE",
)
if builder_name == "build_interactive_cmd":
return backend.build_interactive_cmd()
if builder_name == "build_resume_cmd":
return backend.build_resume_cmd(resume_session_id="test-session", prompt="continue")
msg = f"Unknown builder: {builder_name}"
raise ValueError(msg)


@pytest.mark.parametrize("builder_name", _ALL_GUARD_BUILDERS)
def test_agent_backend_flat_env_var_in_all_guard_launch_builders(builder_name: str) -> None:
"""AUTOSKILLIT_AGENT_BACKEND must appear in every builder's CmdSpec.env for every backend."""
from autoskillit.core.types._type_backend import CmdSpec
from autoskillit.execution.backends import BACKEND_REGISTRY

assert BACKEND_REGISTRY, "BACKEND_REGISTRY is empty β€” test provides no coverage"
for name, cls in BACKEND_REGISTRY.items():
backend = cls()
if builder_name == "build_resume_cmd" and not backend.capabilities.session_resume_capable:
continue
spec = _call_builder(backend, builder_name)
assert isinstance(spec, CmdSpec), f"{name}: {builder_name} did not return CmdSpec"
assert "AUTOSKILLIT_AGENT_BACKEND" in spec.env, (
f"{name}: AUTOSKILLIT_AGENT_BACKEND missing from {builder_name} env"
)


def test_agent_backend_flat_and_dynaconf_values_match_in_interactive_and_resume() -> None:
"""Nested and flat AGENT_BACKEND env vars must carry the same value in interactive and resume builders.""" # noqa: E501
from autoskillit.execution.backends import BACKEND_REGISTRY

assert BACKEND_REGISTRY, "BACKEND_REGISTRY is empty β€” test provides no coverage"
for name, cls in BACKEND_REGISTRY.items():
backend = cls()
interactive_spec = backend.build_interactive_cmd()
assert (
interactive_spec.env["AUTOSKILLIT_AGENT_BACKEND__BACKEND"]
== interactive_spec.env["AUTOSKILLIT_AGENT_BACKEND"]
), f"{name}: nested and flat AGENT_BACKEND values differ in build_interactive_cmd"
if not backend.capabilities.session_resume_capable:
continue
resume_spec = backend.build_resume_cmd(resume_session_id="test-session", prompt="continue")
assert (
resume_spec.env["AUTOSKILLIT_AGENT_BACKEND__BACKEND"]
== resume_spec.env["AUTOSKILLIT_AGENT_BACKEND"]
), f"{name}: nested and flat AGENT_BACKEND values differ in build_resume_cmd"
7 changes: 5 additions & 2 deletions tests/arch/test_subpackage_isolation.py
Original file line number Diff line number Diff line change
Expand Up @@ -982,7 +982,7 @@ def test_data_directories_are_not_python_packages() -> None:
"dispatch gate add defense-in-depth checks",
),
"execution/backends/codex.py": (
1062,
1066,
"REQ-CNST-010-E9: Codex backend β€” skill_sigil capability threading adds multi-line "
"keyword args to _ensure_skill_prefix call sites and _has_prefix guard; "
"write_guard_tool_names env injection adds 7 lines to _codex_exec_extras; "
Expand All @@ -995,7 +995,10 @@ def test_data_directories_are_not_python_packages() -> None:
"; session_log_path method added to CodexSessionLocator (+5 net lines)"
"; process_idle_timeout_ms field wired through build_skill_session_cmd and "
"build_food_truck_cmd CmdSpec constructors (+8 net lines)"
"; CapabilityNotSupportedError capability-gate in build_inspector_cmd (+1 net line)",
"; CapabilityNotSupportedError capability-gate in build_inspector_cmd (+1 net line); "
"AGENT_BACKEND_ENV_VAR injection in build_interactive_cmd merged_extras (+1 net line) "
"and build_resume_cmd _codex_exec_extras call expansion to multi-line (+2 net lines) "
"for T5-P4-A3-WP3 guard-hook backend dispatch",
),
}

Expand Down
Loading