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.