Skip to content

Backend

backend

Abstract base class for sandbox backends.

HostSetupRefusedError()

Bases: ValueError

allow_host_setup was requested in a CI job, where it is never allowed.

Source code in src/agentic_ci/backend.py
def __init__(self) -> None:
    super().__init__(
        "--allow-host-setup is refused in CI (CI, GITLAB_CI or GITHUB_ACTIONS is set); "
        "use a sandbox profile's setup steps instead"
    )

Backend(workdir='.', image=None, *, harness, allow_host_setup=False)

Bases: ABC

Base class for sandbox backends.

Subclasses implement setup() and run() to provide different execution environments (OpenShell sandbox, Podman container, etc.).

Source code in src/agentic_ci/backend.py
def __init__(
    self, workdir=".", image=None, *, harness: Harness, allow_host_setup: bool = False
):
    if allow_host_setup and running_in_ci():
        raise HostSetupRefusedError()
    self.workdir = os.path.abspath(workdir)
    self.image = image
    self.harness = harness
    self.verdict_path: Path | None = None
    self.output_file: Path | None = None
    # Host directory for run records (the skill session's ``_run``);
    # None writes none.
    self.run_dir: Path | None = None
    # False skips a sandbox profile's validate commands after the next
    # run (a classifier run, which changes no code). Backends without
    # profile steps ignore it.
    self.validate_after_run = True
    # The skill the agent runs, if known; backends export its installed
    # directory as CLAUDE_SKILL_DIR for harnesses that do not set it.
    self.skill_name: str | None = None
    self._host_git: GitControlSnapshot | None = None
    # Deprecated: run the repo's .agentic-ci/config.yml setup steps on
    # this host (see _run_setup_steps). Never true in CI.
    self.allow_host_setup = allow_host_setup
    self._host_setup_warned = False

setup(otel_port=None) abstractmethod

Prepare the backend. Idempotent.

Source code in src/agentic_ci/backend.py
@abstractmethod
def setup(self, otel_port: int | None = None):
    """Prepare the backend. Idempotent."""

stop() abstractmethod

Tear down the sandbox environment.

Source code in src/agentic_ci/backend.py
@abstractmethod
def stop(self):
    """Tear down the sandbox environment."""

run(prompt, model, streaming=True, otel_port=None, otel_rate_file=None, extra_args=None, traceparent=None, effort=None) abstractmethod

Execute the agent with the given prompt. Returns the exit code.

effort is the resolved reasoning effort (None when no effort flag is passed). It is exported to the agent as AGENT_REASONING_EFFORT next to AGENT_MODEL; the matching CLI flags come from the caller through extra_args.

Source code in src/agentic_ci/backend.py
@abstractmethod
def run(
    self,
    prompt,
    model,
    streaming=True,
    otel_port=None,
    otel_rate_file=None,
    extra_args=None,
    traceparent=None,
    effort=None,
) -> int:
    """Execute the agent with the given prompt. Returns the exit code.

    *effort* is the resolved reasoning effort (``None`` when no effort
    flag is passed). It is exported to the agent as
    ``AGENT_REASONING_EFFORT`` next to ``AGENT_MODEL``; the matching CLI
    flags come from the caller through *extra_args*.
    """

running_in_ci(env=None)

Whether env (default os.environ) marks a CI job.

A marker counts when it is set to a non-empty value other than 0 or false (any case).

Source code in src/agentic_ci/backend.py
def running_in_ci(env: Mapping[str, str] | None = None) -> bool:
    """Whether *env* (default ``os.environ``) marks a CI job.

    A marker counts when it is set to a non-empty value other than ``0`` or
    ``false`` (any case).
    """
    env = os.environ if env is None else env
    for name in CI_ENV_MARKERS:
        value = env.get(name, "")
        if value and value.lower() not in ("0", "false"):
            return True
    return False