Skip to content

State & errors

The state machines that enforce the pipelines, the state store that tracks the installed version, and the error types.

.xdeploy state machine

xcore_agent.agent.state

TERMINAL_STATES = frozenset({DeploymentState.SUCCEEDED, DeploymentState.FAILED, DeploymentState.ROLLED_BACK}) module-attribute

TRANSITIONS: dict[DeploymentState, tuple[DeploymentState, ...]] = {DeploymentState.PENDING: (DeploymentState.AUTHENTICATING,), DeploymentState.AUTHENTICATING: (DeploymentState.REQUESTING_ARTIFACT, DeploymentState.FAILED), DeploymentState.REQUESTING_ARTIFACT: (DeploymentState.DOWNLOADING, DeploymentState.FAILED), DeploymentState.DOWNLOADING: (DeploymentState.VERIFYING_SIGNATURE, DeploymentState.FAILED), DeploymentState.VERIFYING_SIGNATURE: (DeploymentState.OBTAINING_KEY, DeploymentState.FAILED), DeploymentState.OBTAINING_KEY: (DeploymentState.DECRYPTING, DeploymentState.FAILED), DeploymentState.DECRYPTING: (DeploymentState.EXTRACTING, DeploymentState.FAILED), DeploymentState.EXTRACTING: (DeploymentState.VERIFYING_MANIFEST, DeploymentState.FAILED), DeploymentState.VERIFYING_MANIFEST: (DeploymentState.VALIDATING_PROJECT, DeploymentState.FAILED), DeploymentState.VALIDATING_PROJECT: (DeploymentState.RESOLVING_PLUGINS, DeploymentState.FAILED), DeploymentState.RESOLVING_PLUGINS: (DeploymentState.RESOLVING_SEQUENCE, DeploymentState.FAILED), DeploymentState.RESOLVING_SEQUENCE: (DeploymentState.INSTALLING, DeploymentState.FAILED), DeploymentState.INSTALLING: (DeploymentState.HEALTHCHECKING, DeploymentState.FAILED, DeploymentState.ROLLED_BACK), DeploymentState.HEALTHCHECKING: (DeploymentState.NOTIFYING, DeploymentState.FAILED, DeploymentState.ROLLED_BACK), DeploymentState.NOTIFYING: (DeploymentState.SUCCEEDED, DeploymentState.FAILED), DeploymentState.SUCCEEDED: (), DeploymentState.FAILED: (), DeploymentState.ROLLED_BACK: ()} module-attribute

DeploymentState

Bases: str, Enum

Source code in xcore_agent/agent/state.py
class DeploymentState(str, Enum):
    PENDING = "pending"
    AUTHENTICATING = "authenticating"
    REQUESTING_ARTIFACT = "requesting_artifact"
    DOWNLOADING = "downloading"
    VERIFYING_SIGNATURE = "verifying_signature"
    OBTAINING_KEY = "obtaining_key"
    DECRYPTING = "decrypting"
    EXTRACTING = "extracting"
    VERIFYING_MANIFEST = "verifying_manifest"
    VALIDATING_PROJECT = "validating_project"
    RESOLVING_PLUGINS = "resolving_plugins"
    RESOLVING_SEQUENCE = "resolving_sequence"
    INSTALLING = "installing"
    HEALTHCHECKING = "healthchecking"
    NOTIFYING = "notifying"
    SUCCEEDED = "succeeded"
    FAILED = "failed"
    ROLLED_BACK = "rolled_back"
PENDING = 'pending' class-attribute instance-attribute
AUTHENTICATING = 'authenticating' class-attribute instance-attribute
REQUESTING_ARTIFACT = 'requesting_artifact' class-attribute instance-attribute
DOWNLOADING = 'downloading' class-attribute instance-attribute
VERIFYING_SIGNATURE = 'verifying_signature' class-attribute instance-attribute
OBTAINING_KEY = 'obtaining_key' class-attribute instance-attribute
DECRYPTING = 'decrypting' class-attribute instance-attribute
EXTRACTING = 'extracting' class-attribute instance-attribute
VERIFYING_MANIFEST = 'verifying_manifest' class-attribute instance-attribute
VALIDATING_PROJECT = 'validating_project' class-attribute instance-attribute
RESOLVING_PLUGINS = 'resolving_plugins' class-attribute instance-attribute
RESOLVING_SEQUENCE = 'resolving_sequence' class-attribute instance-attribute
INSTALLING = 'installing' class-attribute instance-attribute
HEALTHCHECKING = 'healthchecking' class-attribute instance-attribute
NOTIFYING = 'notifying' class-attribute instance-attribute
SUCCEEDED = 'succeeded' class-attribute instance-attribute
FAILED = 'failed' class-attribute instance-attribute
ROLLED_BACK = 'rolled_back' class-attribute instance-attribute

Marketplace state machine

xcore_agent.agent.marketplace_state

State machine for MarketplaceDeploymentRunner — deliberately separate from state.DeploymentState. The real Marketplace flow has no auth exchange, no DEK/decrypt step, and loads its install plan from a local operator file instead of from inside the artifact, so it is a materially different sequence of security-relevant stages, not a subset of the .xdeploy one.

MARKETPLACE_TERMINAL_STATES = frozenset({MarketplaceDeploymentState.SUCCEEDED, MarketplaceDeploymentState.FAILED, MarketplaceDeploymentState.ROLLED_BACK}) module-attribute

MARKETPLACE_TRANSITIONS: dict[MarketplaceDeploymentState, tuple[MarketplaceDeploymentState, ...]] = {MarketplaceDeploymentState.PENDING: (MarketplaceDeploymentState.FETCHING,), MarketplaceDeploymentState.FETCHING: (MarketplaceDeploymentState.VERIFYING_SIGNATURE, MarketplaceDeploymentState.FAILED), MarketplaceDeploymentState.VERIFYING_SIGNATURE: (MarketplaceDeploymentState.EXTRACTING, MarketplaceDeploymentState.FAILED), MarketplaceDeploymentState.EXTRACTING: (MarketplaceDeploymentState.LOADING_PLAN, MarketplaceDeploymentState.FAILED), MarketplaceDeploymentState.LOADING_PLAN: (MarketplaceDeploymentState.RESOLVING_SEQUENCE, MarketplaceDeploymentState.FAILED), MarketplaceDeploymentState.RESOLVING_SEQUENCE: (MarketplaceDeploymentState.INSTALLING, MarketplaceDeploymentState.FAILED), MarketplaceDeploymentState.INSTALLING: (MarketplaceDeploymentState.HEALTHCHECKING, MarketplaceDeploymentState.FAILED, MarketplaceDeploymentState.ROLLED_BACK), MarketplaceDeploymentState.HEALTHCHECKING: (MarketplaceDeploymentState.SUCCEEDED, MarketplaceDeploymentState.FAILED, MarketplaceDeploymentState.ROLLED_BACK), MarketplaceDeploymentState.SUCCEEDED: (), MarketplaceDeploymentState.FAILED: (), MarketplaceDeploymentState.ROLLED_BACK: ()} module-attribute

MarketplaceDeploymentState

Bases: str, Enum

Source code in xcore_agent/agent/marketplace_state.py
class MarketplaceDeploymentState(str, Enum):
    PENDING = "pending"
    FETCHING = "fetching"
    VERIFYING_SIGNATURE = "verifying_signature"
    EXTRACTING = "extracting"
    LOADING_PLAN = "loading_plan"
    RESOLVING_SEQUENCE = "resolving_sequence"
    INSTALLING = "installing"
    HEALTHCHECKING = "healthchecking"
    SUCCEEDED = "succeeded"
    FAILED = "failed"
    ROLLED_BACK = "rolled_back"
PENDING = 'pending' class-attribute instance-attribute
FETCHING = 'fetching' class-attribute instance-attribute
VERIFYING_SIGNATURE = 'verifying_signature' class-attribute instance-attribute
EXTRACTING = 'extracting' class-attribute instance-attribute
LOADING_PLAN = 'loading_plan' class-attribute instance-attribute
RESOLVING_SEQUENCE = 'resolving_sequence' class-attribute instance-attribute
INSTALLING = 'installing' class-attribute instance-attribute
HEALTHCHECKING = 'healthchecking' class-attribute instance-attribute
SUCCEEDED = 'succeeded' class-attribute instance-attribute
FAILED = 'failed' class-attribute instance-attribute
ROLLED_BACK = 'rolled_back' class-attribute instance-attribute

State store

xcore_agent.agent.state_store

Tracks which project version is currently installed on this host.

Without this, the CI/CD watch loop (agent.watcher.Watcher) would have no way to tell "Hub says v1.2.3 is latest" from "v1.2.3 is already what's running here" and would redeploy on every single check.

InstalledState dataclass

Source code in xcore_agent/agent/state_store.py
@dataclass(frozen=True)
class InstalledState:
    project_id: str
    version: str
    installed_at: str
project_id: str instance-attribute
version: str instance-attribute
installed_at: str instance-attribute
__init__(project_id: str, version: str, installed_at: str) -> None

StateStore

Persists install state as <project_root>/.xcore/state.json — or, with namespace set, <project_root>/.xcore/state-<namespace>.json.

namespace matters as soon as more than one deployment shares a project_root (e.g. MarketplaceWatcher polling several independent slugs against the same host): without it, every watcher reads/writes the SAME file, so each poll clobbers the others' recorded version and every watcher but the last-written-one redeploys on every single check, mistaking a sibling's state for its own. Omit it (the default) for the single-project-per-root case (agent.watcher.Watcher), which never had this problem and keeps its existing state.json path unchanged.

Source code in xcore_agent/agent/state_store.py
class StateStore:
    """Persists install state as `<project_root>/.xcore/state.json` — or,
    with `namespace` set, `<project_root>/.xcore/state-<namespace>.json`.

    `namespace` matters as soon as more than one deployment shares a
    `project_root` (e.g. `MarketplaceWatcher` polling several independent
    slugs against the same host): without it, every watcher reads/writes
    the SAME file, so each poll clobbers the others' recorded version and
    every watcher but the last-written-one redeploys on every single check,
    mistaking a sibling's state for its own. Omit it (the default) for the
    single-project-per-root case (`agent.watcher.Watcher`), which never had
    this problem and keeps its existing `state.json` path unchanged."""

    def __init__(self, project_root: Path, *, namespace: str | None = None) -> None:
        filename = f"state-{namespace}.json" if namespace else "state.json"
        self._path = project_root / ".xcore" / filename

    def read(self) -> InstalledState | None:
        if not self._path.is_file():
            return None
        return InstalledState(**json.loads(self._path.read_text()))

    def write(self, *, project_id: str, version: str) -> InstalledState:
        state = InstalledState(
            project_id=project_id,
            version=version,
            installed_at=datetime.now(timezone.utc).isoformat(),
        )
        self._path.parent.mkdir(parents=True, exist_ok=True)
        self._path.write_text(json.dumps(asdict(state)))
        return state
__init__(project_root: Path, *, namespace: str | None = None) -> None
Source code in xcore_agent/agent/state_store.py
def __init__(self, project_root: Path, *, namespace: str | None = None) -> None:
    filename = f"state-{namespace}.json" if namespace else "state.json"
    self._path = project_root / ".xcore" / filename
read() -> InstalledState | None
Source code in xcore_agent/agent/state_store.py
def read(self) -> InstalledState | None:
    if not self._path.is_file():
        return None
    return InstalledState(**json.loads(self._path.read_text()))
write(*, project_id: str, version: str) -> InstalledState
Source code in xcore_agent/agent/state_store.py
def write(self, *, project_id: str, version: str) -> InstalledState:
    state = InstalledState(
        project_id=project_id,
        version=version,
        installed_at=datetime.now(timezone.utc).isoformat(),
    )
    self._path.parent.mkdir(parents=True, exist_ok=True)
    self._path.write_text(json.dumps(asdict(state)))
    return state

Errors

xcore_agent.agent.errors

DeploymentError

Bases: Exception

Base class for all fatal deployment pipeline errors.

Source code in xcore_agent/agent/errors.py
class DeploymentError(Exception):
    """Base class for all fatal deployment pipeline errors."""

AuthenticationError

Bases: DeploymentError

Authentication against XCore Hub failed.

Source code in xcore_agent/agent/errors.py
class AuthenticationError(DeploymentError):
    """Authentication against XCore Hub failed."""

ArtifactError

Bases: DeploymentError

Artifact could not be requested, downloaded, verified, or decrypted.

Source code in xcore_agent/agent/errors.py
class ArtifactError(DeploymentError):
    """Artifact could not be requested, downloaded, verified, or decrypted."""

PublishError

Bases: DeploymentError

A built artifact could not be uploaded to XCore Hub.

Source code in xcore_agent/agent/errors.py
class PublishError(DeploymentError):
    """A built artifact could not be uploaded to XCore Hub."""

InstallError

Bases: DeploymentError

A filesystem-level install step failed.

Source code in xcore_agent/agent/errors.py
class InstallError(DeploymentError):
    """A filesystem-level install step failed."""

HealthcheckError

Bases: DeploymentError

A plugin or project failed its post-install healthcheck.

Source code in xcore_agent/agent/errors.py
class HealthcheckError(DeploymentError):
    """A plugin or project failed its post-install healthcheck."""