Skip to content

Python API

The root surface is the public import contract. The narrative contracts, runtime defaults and profile limits take precedence over overbroad legacy docstrings about atomicity, read-only behavior or isolation.

axm_config

axm-config.

Non-sensitive runtime config under ~/.axm (env>file>default)

PATHS_NAMESPACE = 'paths' module-attribute

__all__ = ['PATHS_NAMESPACE', 'ConfigError', 'ExecutionPolicyOverride', 'NamespaceStore', 'ProfileIsolation', 'ProfileIsolationTool', 'UnsafeHomeError', 'axm_home', 'axm_home_path', 'current_profile', 'delete', 'delete_execution_policy', 'get', 'get_bool', 'get_execution_policy', 'get_file', 'get_int', 'get_path', 'get_str', 'inference_base_url', 'inference_model', 'inference_origin', 'is_isolated', 'list_execution_policies', 'load', 'profile_config_path', 'profile_env', 'profile_isolation', 'profile_root', 'profile_root_for', 'protocols_dir', 'quality_dir', 'resolve_safe', 'service_port', 'sessions_root', 'set_', 'set_execution_policy', 'tickets_db', 'validate_segment', 'warden_autostart', 'warden_binary_path', 'warden_log_path', 'warden_max_concurrent', 'warden_mode', 'warden_park_threshold', 'warden_socket'] module-attribute

ConfigError

Bases: RuntimeError

Raised when a required config value cannot be resolved.

ExecutionPolicyOverride

Bases: _PolicyBase

Typed per-ticket-type execution override.

NamespaceStore

Read/write namespace sections of a single ~/.axm/config.toml.

Each namespace maps to a top-level (or nested, for a dotted namespace) TOML table within one config.toml. Reads of an absent or malformed file/section return {} rather than raising, so a consumer can rely on the store at import time without a pre-existing ~/.axm directory. Writes are atomic, preserve every other section, and leave the file 0600. A namespace node may be both a leaf (its own scalar/array keys) and a prefix (nested child namespaces such as [git.default] under [git]): those child sub-tables are re-attached on every write, so setting a key under the parent never erases them. Legacy ~/.axm/<ns>.toml files are folded in on first write.

delete(ns, key)

Remove key from ns, rewriting atomically (no-op if absent).

Read-modify-write mirroring :meth:write over the whole config.toml: the key is popped from the (legacy-folded) section. If the section becomes empty it is dropped; if the file then holds no section it is unlinked. A missing key (after folding) is a silent no-op, but a pending legacy fold is still applied.

namespaces()

Return every namespace path present in config.toml or legacy.

A leaf table (a mapping whose values are all scalars/arrays, i.e. an actual namespace section) yields its dotted path. Legacy ~/.axm/<ns>.toml files contribute their stem. Used by the doctor to enumerate "all known" namespaces when none is requested.

read(ns)

Return the section for ns, or {} if absent/corrupt.

The [ns] section of config.toml is returned as a flat mapping of that namespace's own keys: a dotted namespace maps to a nested table, and any nested sub-table is a child namespace, not a key, so it is excluded from the result. If the section is absent but a legacy ~/.axm/<ns>.toml exists, the legacy contents are returned so the value stays visible before the fold. A missing file or a malformed TOML payload both degrade to {}. Raises :class:~axm_config.resolver.UnsafeHomeError (a :class:ConfigError) only when ~/.axm cannot be used safely (HOME inside a git repo).

read_exact(ns)

Return exactly one stored section without compatibility overlays.

replace_section(ns, section)

Atomically replace a namespace's own leaf while keeping children.

write(ns, key, value)

Set key to value in ns, preserving every other section.

Read-modify-write of the whole config.toml: the full mapping is loaded, the ns section folded with any legacy file and updated, and the result serialised to a same-directory temp file atomically moved into place via :func:os.replace; the file is chmod 0600. The legacy ~/.axm/<ns>.toml (if any) is removed after the fold.

ProfileIsolation

Bases: BaseModel

Resolved state paths and their isolation verdict for one profile.

ProfileIsolationTool

Resolve isolated state paths for an explicit or active profile.

name property

Unique tool identifier.

execute(*, profile=None)

Return resolved profile paths and their isolation verdict.

UnsafeHomeError

Bases: ConfigError

Raised when ~/.axm cannot be used safely (e.g. a HOME in a git repo).

A :class:ConfigError subclass so every consumer surface that already catches :class:ConfigError (the CLI, :func:load) degrades cleanly instead of leaking the raw ValueError from :func:axm_config.home.resolve_safe. The security refusal itself is intentional; only its type is narrowed here so callers can handle it.

axm_home()

Return the resolved ~/.axm directory, creating it 0700 if absent.

Idempotent: a pre-existing directory with looser permissions is tightened back to 0700. Permission calls degrade gracefully on non-POSIX systems.

axm_home_path()

Return the resolved ~/.axm path without touching the filesystem.

Pure computation, the read-only counterpart of :func:axm_home: it never creates the directory nor tightens its permissions, so merely describing or resolving a location (a report, a path lookup) no longer materialises the home. Every code path that actually persists state keeps calling :func:axm_home.

current_profile()

Return the active, lexically validated state profile.

delete(namespace, key)

Remove key from the [namespace] section of config.toml (no-op if absent).

namespace and key are validated first. Deleting an absent key (or a namespace with no file) is a silent no-op — it never raises. After removal the key resolves through the lower layers again (env, then default).

delete_execution_policy(ticket_type)

Idempotently delete one policy leaf while preserving descendants.

get(namespace, key, *, default=None)

Return the resolved value for key in namespace.

Precedence is env > file > default. An env value is returned as the raw str from the environment; file values keep their TOML-parsed type.

get_bool(key, default, *, namespace=PATHS_NAMESPACE)

Resolve a configured boolean while preserving an untouched default.

get_execution_policy(ticket_type)

Resolve one targeted policy, raising ConfigError for malformed values.

get_file(namespace, key, *, default=None)

Return a persisted value without consulting environment overrides.

get_int(key, default, *, namespace=PATHS_NAMESPACE, env_aliases=())

Resolve a configured integer while preserving an untouched default.

get_path(key, default, *, namespace=PATHS_NAMESPACE, profile=None)

Resolve key in [paths] as a normalised :class:~pathlib.Path.

default is the caller's existing constant. In production it is returned unchanged when nothing is configured. For the state paths registered in _PROFILE_RELATIVE_PATHS, a non-production profile replaces that fallback with a path rooted below :func:profile_root.

A configured value (env or file) is expanded (~), resolved to an absolute path, and refused via :func:resolve_safe if it sits inside a git checkout. Raises :class:ConfigError on a non-string/non-path value or an in-repo path, so a bad config fails loudly at the boundary rather than writing runtime state somewhere unintended.

get_str(key, default, *, namespace=PATHS_NAMESPACE)

Resolve a configured value as text while preserving an untouched default.

inference_base_url()

Return the configured inference engine address unchanged.

inference_model()

Return the configured inference model identifier unchanged.

inference_origin()

Return the configured inference provider origin.

is_isolated(root, paths)

Return whether every named path is contained by the root.

list_execution_policies()

Return valid policies in lexical order, skipping malformed leaves.

load(namespace, model)

Build model from namespace, resolving each field by name.

Every field of model is resolved via :func:get (the field name is the config key). Unresolved fields are omitted so pydantic applies the field default; a required field that stays unresolved raises :class:ConfigError instead of a raw ValidationError.

profile_config_path()

Return the config store path for the active profile.

profile_env()

Return the environment overlay that propagates the active profile.

profile_isolation(profile=None)

Resolve a profile's state paths without creating filesystem entries.

profile_root()

Return the isolated state root, or None for production.

profile_root_for(profile)

Return the state root of profile, or None for the default one.

The named counterpart of :func:profile_root: it answers for an arbitrary profile instead of the active one, so a caller can ask what a profile would use without mutating AXM_PROFILE. Pure computation -- the root hangs below :func:axm_home_path, so no directory is created, and the deliberate asymmetry is preserved: AXM_HOME only selects the config.toml that is read, never this convention.

protocols_dir(*, default=None, profile=None)

The legacy YAML protocol directory read by the engine and briefings.

quality_dir(*, default=None, profile=None)

The quality-trace directory written by axm-audit / axm-init.

resolve_safe(target)

Resolve target and refuse any path sitting inside a git repo.

Walks the resolved path and its ancestors looking for a .git marker (a source checkout). Raises :class:ValueError rather than returning an in-repo path; returns the resolved path otherwise.

service_port(service, *, profile=None)

The TCP port service listens on for the active state profile.

A listening point is a profile-owned resource exactly like the state roots above, so the decision belongs here instead of being pushed onto every caller as "configure a port or I refuse to start".

Production is frozen: with nothing configured the adopted default is returned unchanged, so an existing installation reconfigures nothing. Under a non-production profile the fallback is derived from the profile name, so two installations on one machine do not fight over a port. Either way the value is only the default handed to :func:get_int, which keeps the resolver's env > file > default precedence intact.

A service may also carry historical environment variables, registered in _SERVICE_PORT_ENV_ALIASES: they are consulted after the derived name and before any configured value, so an installation still exporting the legacy variable keeps binding the port it always bound.

profile names the profile the question is asked for, exactly like :func:get_path and the state roots above: None means the active profile, and naming another one is a pure read -- it neither consults nor mutates the process-wide profile selection.

Raises :class:ConfigError naming service when it is not registered, before any resolution is attempted.

sessions_root(*, default=None, profile=None)

The loom sessions root -- where runs write manifests, traces, artifacts.

Declared identically in axm-loom, axm-knowledge and axm-orison (whose docstrings already say they mirror loom); this is the seam those three delegate to. In production, default overrides the built-in ~/axm/sessions for callers retaining their migration constant; an active non-production profile instead owns the unconfigured state root.

set_(namespace, key, value)

Persist key = value in the [namespace] section of config.toml.

namespace and key are validated against the safe-segment pattern first (path-traversal guard). A value of None is routed to :func:delete — TOML cannot encode None, so deleting the key is the well-defined contract rather than a raw TypeError. Otherwise delegates to :meth:NamespaceStore.write (atomic, 0600, other keys preserved).

set_execution_policy(ticket_type, *, backend=None, model=None, analysis_enabled=None)

Atomically replace or clear one complete ticket-type policy leaf.

tickets_db(*, default=None, profile=None)

Return the ticket database path for the active state profile.

validate_segment(value, *, kind='segment')

Return value if it is a safe config segment, else raise ConfigError.

A segment is a namespace or a key: the single entry-point guard against path traversal and env-name ambiguity. It must be a non-empty str matching its kind's pattern — no path separators, no .. traversal, no NUL byte — so it can never widen the on-disk ~/.axm/<ns>.toml path. Both patterns are lowercase-only (no upper-case): the env-name surface upper-cases the segments, so accepting both "Demo" and "demo" would let two distinct namespaces fold to the same AXM_DEMO_* prefix — forbidding upper-case makes that collision unrepresentable. The patterns differ by kind: a "namespace" (:data:_NAMESPACE_RE) is lowercase-alphanumeric segments joined by dots — no _ and no - — whereas a "key" (:data:_KEY_RE) is lowercase-alphanumeric segments joined by single _ (no ./-, no leading/trailing _, no doubled __) so the derived env name stays POSIX-valid and the ns/key boundary is unambiguous: only the namespace's dot-fold yields __, the key can never forge one, and the lone single _ separates the folded namespace from the key. Any other kind falls back to the namespace pattern. Shared with every public boundary (and reused by the env-name surface) so validation is declared exactly once.

warden_autostart(*, default=None)

Return whether consumers should start the warden automatically.

warden_binary_path(*, default=None)

Return the configured or interpreter-relative warden executable path.

warden_log_path(*, default=None, profile=None)

Return the configured or AXM-home-relative warden log path.

warden_max_concurrent(*, default=None)

Return the strictly positive warden concurrency limit.

warden_mode(*, default=None)

Return the configured warden execution mode.

warden_park_threshold(*, default=None)

Return the minimum number of failed generations before parking.

warden_socket(*, default=None, profile=None)

The warden control-plane socket bound by axm-warden serve.

Note the precedence a consumer must preserve. Callers layer an explicit argument on top of this (--socket on the CLI, the socket= kwarg on the tools), which outranks everything here; this function covers only the env > file > default tail below it. Collapsing the explicit argument into the config lookup would silently change behaviour, so callers keep their own if socket is not None: return socket guard and delegate the rest.

Registered provenance tool

This class is registered under axm.tools but is not exported by the root.

ConfigDoctorTool

Report config-key provenance (env/file/default), read-only.

Satisfies the :class:~axm.tools.base.AXMTool protocol structurally. The tool is diagnostic: it never mutates any config layer, it only reports which layer would win per key.

name property

Unique tool identifier.

execute(*, namespace=None)

Return the provenance report for namespace (or all known).

On success, data is the {"<ns>.<key>": {layer, present}} mapping from :func:config_doctor_data and text is the shared one-line-per-key rendering from :func:render_doctor_report (so the MCP text and the CLI doctor output cannot drift). Any failure is shaped into ToolResult(success=False, error=...) at the MCP boundary.

Documentation build

This static directive page is built both standalone and in the workspace nav. The workspace generator additionally emits module pages under reference/axm_config/; it does not generate this package's former reference/api/ target. This page needs only the Python mkdocstrings handler and the configured local source path, without relying on that generator.