-
Notifications
You must be signed in to change notification settings - Fork 0
Codex Backend Architectural Immunity — Capability Consumption, Symmetric Env, and NotImplementedError Arch Tests #3395
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7b0d8ce
baf4772
d985f3b
ecc7dd2
eeacbfc
704c3b0
45dad77
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| """Architectural invariant: skill and food-truck builders must set the same required base env vars.""" # noqa: E501 | ||
|
|
||
| from pathlib import Path | ||
|
|
||
| import pytest | ||
|
|
||
| pytestmark = [pytest.mark.layer("arch"), pytest.mark.small] | ||
|
|
||
| _REQUIRED_IN_BOTH: frozenset[str] = frozenset( | ||
| { | ||
| "AUTOSKILLIT_HEADLESS", | ||
| "AUTOSKILLIT_SESSION_TYPE", | ||
| "MAX_MCP_OUTPUT_TOKENS", | ||
| } | ||
| ) | ||
|
|
||
|
|
||
| @pytest.fixture(autouse=True) | ||
| def _clean_env(monkeypatch: pytest.MonkeyPatch) -> None: | ||
| monkeypatch.delenv("AUTOSKILLIT_CAMPAIGN_ID", raising=False) | ||
| monkeypatch.delenv("AUTOSKILLIT_KITCHEN_SESSION_ID", raising=False) | ||
| monkeypatch.delenv("AUTOSKILLIT_AGENT_BACKEND", raising=False) | ||
|
|
||
|
|
||
| def test_skill_and_food_truck_share_required_env_vars() -> None: | ||
| """build_skill_session_cmd and build_food_truck_cmd must both set the required base env vars.""" # noqa: E501 | ||
| from autoskillit.core.types._type_plugin_source import DirectInstall | ||
| 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() | ||
| skill_spec = backend.build_skill_session_cmd( | ||
| "/autoskillit:investigate", "/repo", completion_marker="DONE" | ||
| ) | ||
| food_truck_spec = backend.build_food_truck_cmd( | ||
| orchestrator_prompt="test prompt", | ||
| plugin_source=DirectInstall(plugin_dir=Path("/plugins")), | ||
| cwd="/repo", | ||
| completion_marker="DONE", | ||
| ) | ||
| for var in _REQUIRED_IN_BOTH: | ||
| assert var in skill_spec.env, f"{name}: {var} missing from build_skill_session_cmd env" | ||
| assert var in food_truck_spec.env, ( | ||
| f"{name}: {var} missing from build_food_truck_cmd env" | ||
| ) | ||
|
|
||
|
|
||
| def test_agent_backend_env_var_in_food_truck(monkeypatch: pytest.MonkeyPatch) -> None: | ||
| """AUTOSKILLIT_AGENT_BACKEND must appear in build_food_truck_cmd env for every backend.""" | ||
| from autoskillit.core.types._type_plugin_source import DirectInstall | ||
| from autoskillit.execution.backends import BACKEND_REGISTRY | ||
|
|
||
| # Ensure clean environment - remove any residual AGENT_BACKEND_ENV_VAR from host | ||
| monkeypatch.delenv("AUTOSKILLIT_AGENT_BACKEND", raising=False) | ||
|
|
||
| assert BACKEND_REGISTRY, "BACKEND_REGISTRY is empty — test provides no coverage" | ||
| for name, cls in BACKEND_REGISTRY.items(): | ||
| backend = cls() | ||
| food_truck_spec = backend.build_food_truck_cmd( | ||
| orchestrator_prompt="test prompt", | ||
| plugin_source=DirectInstall(plugin_dir=Path("/plugins")), | ||
| cwd="/repo", | ||
| completion_marker="DONE", | ||
| ) | ||
| assert "AUTOSKILLIT_AGENT_BACKEND" in food_truck_spec.env, ( | ||
| f"{name}: AUTOSKILLIT_AGENT_BACKEND missing from build_food_truck_cmd env" | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| """Architectural invariant: no registered backend may raise NotImplementedError.""" | ||
|
|
||
| import ast | ||
| import inspect | ||
|
|
||
| import pytest | ||
|
|
||
| pytestmark = [pytest.mark.layer("arch"), pytest.mark.small] | ||
|
|
||
|
|
||
| def test_no_not_implemented_error_in_registered_backends() -> None: | ||
| """No method on any BACKEND_REGISTRY class should raise NotImplementedError. | ||
|
|
||
| CodingAgentBackend is a typing.Protocol, not an ABC — there are no legitimate | ||
| abstract stubs. Any raise NotImplementedError in a registered backend is a bug. | ||
| """ | ||
| from autoskillit.execution.backends import BACKEND_REGISTRY | ||
|
|
||
| violations: list[str] = [] | ||
| for name, cls in BACKEND_REGISTRY.items(): | ||
| source = inspect.getsource(cls) | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Observation from local review round 0: [warning] defense: inspect.getsource(cls) can raise OSError (source unavailable, C extension, etc.) with no error context identifying which backend triggered it. Wrap in try/except and include name in the error to preserve context. Evidence: Marginal improvement in test diagnostics for arch test code. Human judgment needed on whether defensive wrapping in test code is warranted.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Valid observation — flagged for design decision. The question is whether defensive try/except with backend name context is warranted in test code for inspect.getsource(). Human judgment needed on complexity vs. diagnostic value tradeoff. |
||
| tree = ast.parse(source) | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Observation from local review round 0: [warning] defense: ast.parse(source) can raise SyntaxError with no indication of which backend's source failed to parse. Wrap in try/except and include name in the error message. Evidence: SyntaxError in well-maintained Python backends is unlikely but possible. Adding try/except with name improves triage. Human decision on whether worth adding complexity to test code.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Valid observation — flagged for design decision. SyntaxError wrapping in ast.parse() for well-maintained Python backends is unlikely but possible. Human decision on whether the added complexity is worth the diagnostic improvement. |
||
| for node in ast.walk(tree): | ||
| if not isinstance(node, ast.Raise): | ||
| continue | ||
| exc = node.exc | ||
| if ( | ||
|
Trecek marked this conversation as resolved.
|
||
| isinstance(exc, ast.Call) | ||
| and isinstance(exc.func, ast.Name) | ||
| and exc.func.id == "NotImplementedError" | ||
| ) or (isinstance(exc, ast.Name) and exc.id == "NotImplementedError"): | ||
| violations.append(f"{name}:{node.lineno}") | ||
|
|
||
| assert not violations, ( | ||
| f"Registered backends must not raise NotImplementedError " | ||
| f"(CodingAgentBackend is a Protocol, not an ABC): {violations}" | ||
| ) | ||
Uh oh!
There was an error while loading. Please reload this page.