Profiles
AXIOM judges two things, with two policies:
- the gate judges one tool call at a time (a write, a shell command) inside the agent’s
hook, in about 100 ms — its policy is the
gatesection; check/applyjudge a whole change set (a manifest) with typed predicates — their policy is a checkProfile, thecheckssection.
Since 3.0 both live in one file, .axiom/axiom.json. The 2.x files
(.axiom/gate-profile.json, .axiom/profiles/<name>.json) are still read. Terms used below
are defined in the glossary.
.axiom/axiom.json
Section titled “.axiom/axiom.json”Since 3.0. Schema: AxiomConfigFileSchema in packages/schema/src/config.ts; loader:
packages/mcp/src/profile-v3.ts. The file is a loose object: unknown top-level keys produce
one warning naming them and are ignored, so a file written for a newer 3.x still works on an
older 3.x. The file never participates in a digest.
| Key | Type | Meaning |
|---|---|---|
$schema |
string | optional editor hint, e.g. https://dragoscv.github.io/axiom/schemas/v3/AxiomConfig.schema.json |
version |
positive int | policy format major; this release understands 3. A higher value is refused with ERR_PROFILE_VERSION — never half-understood, never fail-open |
extends |
string[], ≤ 32 entries |
layers merged under this file, in order (see below) |
allowNpmPacks |
boolean | allow extends to name npm packages outside @codai/; honoured only in the root file, not in an extended layer |
gate |
object | the gate policy, validated by GateProfileSchema after merging — see Gate keys |
checks |
object | the check profile: the Profile fields without apiVersion/kind; name defaults to axiom |
A checks section that sets neither checks nor extends extends the built-in default
profile, so a gate-only file is never weaker than having no file. The checks profile answers
a request for its own name and for default (the repository policy is the repository’s
default).
extends resolution
Section titled “extends resolution”Entries are resolved in order; later entries win, and the file itself is applied last.
| Entry form | Resolved as |
|---|---|
./base.json, ../shared/x.json (or an absolute path) |
a file relative to the declaring file. Its realpath must stay inside the repository root or ~/.axiom, so a symlink cannot bring a file in from elsewhere |
axiom:default, axiom:strict, axiom:permissive |
a built-in pack (table below) |
@codai/axiom-pack-<name> |
an npm package resolved from the repository root. It must already be in node_modules — AXIOM never installs anything — and ship axiom-pack.json, exposed through exports["./axiom-pack.json"] or named by package.json#axiomPack (which must stay inside the package). Packages from any other scope need allowNpmPacks: true |
An extended file is itself an axiom.json-shaped document and may have its own extends.
Built-in packs (BUILTIN_PACKS):
| Pack | checks |
gate |
|---|---|---|
axiom:default |
extends the built-in default profile |
deny: the built-in deny list; steer: "hint" |
axiom:strict |
extends the built-in strict profile |
deny: the built-in deny list + .github/workflows/**; steer: "hint"; outside: "deny"; noSecrets: true; pii: true; commands: { pack: "house" } |
axiom:permissive |
extends the built-in permissive profile |
deny: [".git/**", ".axiom/**"]; outside: "warn" |
The built-in deny list is .git/**, .axiom/**, **/*.lock, pnpm-lock.yaml, .env,
.env.*, **/node_modules/**.
Merge rules
Section titled “Merge rules”- Objects deep-merge; scalars are replaced by the later layer.
deny,ask,hardandallowarrays are unioned at every depth: a pack or a later layer can add entries but never drop a deny.- A
checkslist of{ id, … }entries merges byid: a later entry with the same id replaces the earlier one, new ids are appended. - Every other array is replaced.
- A cycle, or nesting deeper than 8 levels (the root file is level 0), is
ERR_INVALID_PROFILE. So are invalid JSON, a schema violation, an unreadable or missing extends file, a file outside the allowed directories, an unknownaxiom:pack and an npm pack that is not installed.
Example
Section titled “Example”{ "$schema": "https://dragoscv.github.io/axiom/schemas/v3/AxiomConfig.schema.json", "version": 3, "extends": ["axiom:strict", "./team-policy.json"], "gate": { "mode": "enforce", "ask": ["infra/**", "packages/db/drizzle/**"], "maxBytes": 262144, "record": true, "commands": { "pack": "house", "deny": [ { "id": "repo.noPrismaPush", "argv": ["prisma", "db", "push"], "reason": "schema changes go through migrations", "fix": "pnpm db:generate" } ] }, "hard": ["house.forcePush", "house.resetHard", "house.cleanForce", "house.worktreeRemoveForce"], "sessions": { "foreignWrite": "ask", "budget": { "maxDeletes": 20 } } }, "checks": { "extends": "strict", "checks": [ { "id": "path.allow", "predicate": "path.allow", "params": { "globs": ["apps/**", "packages/**", "docs/**"] } }, { "id": "content.noSecrets", "predicate": "content.noSecrets", "params": { "pii": true } } ] }}With axiom:strict first, the effective gate.deny is the strict list plus anything
team-policy.json and this file add; hard is the union of all three layers.
Moving from the 2.x files: axiom setup migrate --profile (old verb
axiom migrate --profile) folds .axiom/gate-profile.json and .axiom/profiles/<name>.json
into a new .axiom/axiom.json; see MIGRATION.md.
Where the gate finds its policy
Section titled “Where the gate finds its policy”loadGateProfileForRoot and loadGateProfile in packages/mcp/src/gate.ts. The first hit
wins; layers are never merged across these locations:
--profile <file>— always wins. A missing file isERR_INVALID_PROFILE, not a fallback.<root>/.axiom/axiom.json— itsgatesection, withextendsresolved.<root>/.axiom/gate-profile.json— the 2.x file, still read.~/.axiom/gate-profile.json— a per-user fallback.- The built-in default (
DEFAULT_GATE_PROFILE).
Slot inheritance. When <root> is a linked git worktree (an agent slot: its .git is a
file whose gitdir back-links to it, and commondir names the main clone’s .git) and the
slot has no axiom.json, the slot inherits the main clone’s .axiom/gate-profile.json if its
own copy is missing or older (by mtime). A slot whose own profile is newer keeps it. A policy
change in the main clone therefore reaches every slot without copying files.
flowchart TD
accTitle: Gate policy lookup
accDescr: The gate takes the first policy it finds, from the profile flag through axiom.json and the legacy files to the built-in default, with worktree slots inheriting a newer main clone profile.
A{--profile given} -->|yes| P[that file]
A -->|no| B{root .axiom/axiom.json}
B -->|exists| C[gate section with extends merged]
B -->|absent| W{root is a linked worktree}
W -->|yes and main clone gate-profile.json is newer or own is missing| M[main clone .axiom/gate-profile.json]
W -->|no, or own copy is newer| L{root .axiom/gate-profile.json}
L -->|exists| G[legacy gate profile]
L -->|absent| H{home .axiom/gate-profile.json}
H -->|exists| U[per-user gate profile]
H -->|absent| D[built-in default]
Check profiles are looked up separately: a named profile resolves from
<root>/.axiom/profiles/<name>.json, then the built-ins (see Discovery).
A profile that cannot be used — invalid JSON, a schema violation, a bad extends — is
ERR_INVALID_PROFILE; a version above 3 is ERR_PROFILE_VERSION. Neither is covered by the
gate’s --fail-open: an unusable policy never turns the gate off. Unknown keys are not an
error: the gate logs one warning naming them and ignores them.
Gate keys
Section titled “Gate keys”GateProfileSchema in packages/mcp/src/gate.ts. The gate is flat on purpose: it has ~100 ms
and sees one tool call at a time. Defaults are the schema defaults, which apply to any explicit
policy (a gate section or a gate profile file). The built-in default, used only when no file
exists, differs in two places: it carries the built-in deny list and steer: "hint".
| Key | Type | Default | Meaning | More |
|---|---|---|---|---|
version |
positive int | — | policy format major; above 3 → ERR_PROFILE_VERSION |
|
deny |
string[] (globs) |
[] (built-in: the deny list above) |
any target matching a glob (picomatch, dot: true) → deny path.deny; applies to write tools and shell operands |
|
ask |
string[] (globs) |
[] |
a match hands the decision to the human (path.ask); every deny wins over an ask; formats without an ask prompt get a deny |
human-confirm.md |
allow |
string[] (globs) |
— | when present, every target must match one glob, else path.allow |
|
noSecrets |
boolean | true |
run content.noSecrets on supplied content |
checks.md |
pii |
boolean | false |
also scan for personal data (cnp, email, phoneRo, card) |
The pii knob |
noSecretsExclude |
string[] (globs) |
— | paths where the PII-class findings (email, phoneRo, cnp, card, credentialAssignment) are ignored, e.g. test fixtures; real keys are still reported everywhere |
|
maxBytes |
positive int | — | run content.maxBytes on supplied content |
|
steer |
"off" | "hint" | "require" |
"off" (built-in: "hint") |
hint: a once-per-session hint pointing the agent at axiom_write; require: also deny direct writes matching steerGlobs (steer.requireApply) |
|
steerGlobs |
string[] (globs) |
[] |
targets a direct write may not touch under steer: "require" |
|
record |
boolean | false |
append every judged decision to .axiom/decisions.jsonl |
stats.md |
commands |
object | — | shell command rules — see commands |
command-rules.md |
sessions |
object | — | cross-session and blind-overwrite rules, write budgets — see sessions |
sessions.md |
undo |
boolean | false |
capture the pre-image of every allowed write into the CAS for axiom journal undo --session |
undo.md |
coordination |
object | — | file-protocol marks, leases and queue-required commands — see coordination |
coordination.md |
git |
object | — | git-state rules read from .git without spawning git — see git |
|
shadow |
boolean | false |
never deny; record what the gate would have decided (and the shell command) to .axiom/decisions.jsonl for a replay before a cutover |
|
mode |
"enforce" | "warn" |
"enforce" |
see Modes, hard codes and overrides | |
hard |
string[] |
[] |
codes that always deny in every mode and cannot be overridden | |
override |
boolean | false |
let the agent pass a non-hard deny by stating a reason | |
outside |
"deny" | "warn" | "allow" |
"deny" |
targets outside the root (and outside every worktree of the same repository): ERR_CONTAINMENT, allow with a warning, or allow (still a warning for operating-system directories) |
Contract, wiring and shell scanning: hooks.md.
What axiom setup init writes
Section titled “What axiom setup init writes”GATE_PROFILE in packages/mcp/src/init.ts, written to .axiom/gate-profile.json:
{ "version": 3, "deny": [".git/**", ".axiom/**", ".github/**", "pnpm-lock.yaml", "**/*.lock", ".env", ".env.*", "**/.env", "**/.env.*", "**/node_modules/**", "**/.next/**", "**/dist/**"], "noSecrets": true, "pii": false, "maxBytes": 262144, "steer": "hint", "record": true, "mode": "enforce", "commands": { "pack": "house" }, "hard": ["house.forcePush", "house.resetHard", "house.cleanForce", "house.worktreeRemoveForce"]}The house command pack is on, and the four irreversible git operations deny in every mode and cannot be overridden.
commands
Section titled “commands”CommandsConfigSchema in packages/mcp/src/command-rules.ts (strict object).
| Key | Type | Default | Meaning |
|---|---|---|---|
deny |
CommandRule[] |
[] |
your own rules, evaluated against every simple command the shell line runs (including nested bash -c, pwsh -Command, heredocs fed to a shell) |
pack |
"none" | "house" |
"none" |
house adds the built-in rules below |
A CommandRule (strict object; pattern or argv is required):
| Field | Type | Meaning |
|---|---|---|
id |
string | verdict code; default command.deny |
pattern |
regex source (≤ 500 chars) | matches the command text |
argv |
string[] (≥ 1) |
matches the leading argv tokens |
unless |
regex source | a command matching it is exempt |
reason |
string | shown to the agent |
fix |
string | the command to run instead |
decision |
"deny" | "ask" |
default deny |
caseSensitive |
boolean | regexes are case-insensitive by default |
Every matching rule is evaluated; if any match is a hard code, that one decides.
House pack (HOUSE_PACK):
| Id | Refuses |
|---|---|
house.gitAddAll |
git add -A / --all / . |
house.gitCommitAll |
git commit -a / -am / --all |
house.forcePush |
git push --force, -f, --force-with-lease |
house.pushDelete |
git push --delete / -d / <remote> :<ref> |
house.resetHard |
git reset --hard |
house.cleanForce |
git clean -f |
house.checkoutDiscard |
git checkout -- <path> / git checkout . |
house.restore |
git restore |
house.stashFlagsAfterDashDash |
git stash push … -- … -m (flags after -- become paths) |
house.stashDrop |
git stash drop / clear |
house.stashBare |
git stash pop / apply / drop without stash@{N} |
house.sqlDestructive |
dropping or truncating a table, database or schema |
house.sqlDeleteAll |
a DELETE FROM <table> with no WHERE |
house.rmRoot |
rm -rf / or ~ |
house.removeItemRoot |
Remove-Item -Recurse -Force at a drive root |
house.commitNoPathspec |
git commit without -- <paths> (exempt: agentq.ps1, commit-tree, --amend, --allow-empty) |
house.worktreeRemoveForce |
git worktree remove --force / -f |
house.worktreeRawRemove |
raw git worktree remove / move outside worktree.ps1 / agentq.ps1 / deploy-clean.ps1 |
house.worktreeRawAdd |
raw git worktree add outside those wrappers |
house.playwrightLocal |
local playwright test runs (exempt with PW_ALLOW_LOCAL=1) |
house.buildUnqueued |
pnpm build / typecheck, turbo build, next build, tsc --noEmit outside run-build.ps1 / agentq.ps1 |
house.undecodable |
a nested command AXIOM cannot read (undecodable -EncodedCommand, nesting deeper than 3) — decision ask |
command.deployNeedsCleanTree (HOUSE_DEPLOY_RULE) is not in the pack: the gate adds it only
when a git-state fact reports uncommitted changes. It refuses deploy and release commands
(gcloud run deploy, vercel --prod, docker build|push, terraform apply, npm publish, …)
outside deploy-clean.ps1.
sessions
Section titled “sessions”SessionsConfigSchema in packages/mcp/src/session-guard.ts (strict object). Details:
sessions.md.
| Key | Type | Default | Meaning |
|---|---|---|---|
foreignWrite |
"off" | "ask" | "deny" |
"off" |
a write to a file another agent session wrote within windowMin → session.foreignWrite |
windowMin |
int 1–1440 | 15 |
the foreign-write window in minutes |
blindOverwrite |
"off" | "ask" | "deny" |
"off" |
a whole-file overwrite of a file this session never read → blind.overwrite |
trackReads |
boolean | false |
record reads (needed by blindOverwrite) |
staleScript |
boolean | true |
refuse running a script file another session changed since this session last saw it → blind.staleScript |
budget |
object | — | per-session write budget → sessions.budget |
budget.maxFiles |
int ≥ 1 | — | distinct files written within the window |
budget.maxDeletes |
int ≥ 0 | — | deletes within the window |
budget.windowMin |
int 1–10080 | 60 |
budget window in minutes |
budget.decision |
"ask" | "deny" |
"ask" |
verdict when the budget is exceeded |
GitRulesSchema in packages/mcp/src/gitfacts.ts (strict object). Command classes are
build, install, commit, deploy.
| Key | Type | Default | Meaning |
|---|---|---|---|
cleanTreeFor |
class[] | [] |
commands of these classes need a clean tree (index = HEAD, worktree = index) → git.dirtyTree |
untracked |
boolean | false |
also count untracked files |
requirePathspec |
boolean | false |
refuse git commit without -- <path> → git.noPathspec |
branch |
{ is, for, reason? } |
— | commands of classes for run only on a branch matching is (exact or * glob) → git.branch |
A git state that cannot be read fails closed with git.dirtyTree.
coordination
Section titled “coordination”CoordinationSchema in packages/mcp/src/coord.ts (strict object). Details:
coordination.md.
| Key | Type | Default | Meaning |
|---|---|---|---|
marksDir |
string | — | directory of mark files (*.json); ~ and ${env:NAME} expand → coord.frozen |
leasesDir |
string | — | directory of lease files (<repo>/<slot>.json) → coord.leased |
slotsRoot |
string | — | root of worktree slots (<slotsRoot>/<repo>/<slot>) for leases without a path |
exemptVia |
regex[] | ["agentq\\.ps1"] |
a command matching any is exempt from marks, leases and the queue rule |
queueRequired |
{ class, via, fix }[] |
[] |
commands of class must match via (the sanctioned wrapper) → coord.queueRequired |
repo |
string | — | repository name; default the lowercased leaf of the git common dir’s parent |
Modes, hard codes and overrides
Section titled “Modes, hard codes and overrides”mode. enforce denies. warn turns a deny into an allow that carries the reason to the
agent as a warning, journaled with relaxed: "warn". Codes listed in hard still deny, and
ask globs still ask.
hard. Entries are an exact code, a prefix.* (every code starting with prefix.), or
*. A hard code denies in every mode and cannot be overridden. git.destructiveTree (a shell
command whose target is the whole tree, such as git clean or git reset --hard) is hard
whenever house.resetHard or
house.cleanForce is hard. Every matching command rule is evaluated, and a hard match wins over
an earlier relaxable one — evaluation order never decides.
override: true. The agent may pass a non-hard deny by stating why. The gate reads the
reason from exactly two places:
axiom-override: <reason>in one of the tool input’s prose fields —explanation,goal,description,reason,justification. The reason runs to the end of the line.- A trailing shell comment
# axiom-override: <reason>, the last thing in the command, outside quotes and heredoc bodies (bash and PowerShell readings must agree).
It is never read from a command body, from apply_patch text, from file content or from a
quoted string. A reason needs ≥ 3 words containing letters and ≥ 12 letters in total, and at
most 300 characters. The trailing comment is stripped before the command is scanned for write
targets. The decision is journaled with relaxed: "override" and override_reason.
pnpm build # axiom-override: release candidate build agreed with the owneroutside. deny (default) refuses a target outside the root with ERR_CONTAINMENT;
warn allows it with a warning; allow allows it, warning only for operating-system
directories. A target in a sibling worktree of the same repository is judged against that
worktree’s root, not as outside.
Check profile schema
Section titled “Check profile schema”packages/schema/src/profile.ts → ProfileSchema, served as axiom://schema/Profile and
axiom schema Profile. Every object is strict. A profile is the operator’s statement of what a
change set must satisfy: a named list of typed predicates plus limits and provider switches.
check and apply run the resolved profile’s checks and the manifest’s own checks[];
anything other than verdict: pass blocks apply. Predicate semantics and every params shape
live in checks.md.
flowchart LR accTitle: Check profile resolution accDescr: Built-in and file profiles resolve through their extends chain, merge by check id, and the plan checks merge last before the checks run. B[built-ins<br/>default · strict · permissive] --> R[loadProfile name] D[<root>/.axiom/profiles/name.json] --> R R -->|extends chain, parent first| M[merge by check id<br/>child replaces parent] P[Plan.checks / manifest.checks] -->|merged last| M M --> E[effective check list<br/>+ limits + facts] E --> C[runChecks]
| Field | Type | Constraint |
|---|---|---|
apiVersion |
literal | "axiom.codai.ro/v3"; "axiom.dev/v2" still parses |
kind |
literal | "Profile" |
name |
string | ^[a-z0-9][a-z0-9._-]{0,63}$; for a file profile it must equal the file stem |
extends |
profile name | optional; resolved parent-first; cycle or missing parent → ERR_INVALID_PROFILE |
checks |
CheckRef[] |
{ id, predicate, params, severity? } — see plan-format.md |
limits |
{ maxArtifacts?, maxTotalBytes?, maxBlobBytes? } |
positive ints; merged shallowly, child over parent |
facts |
{ allowRepo: bool = true, allowGuards: bool = false } |
allowRepo: false skips every repo.* predicate and hides repo from expr.cel/expr.cedar; allowGuards: true is one of the two switches guard.external needs (the other is the process flag --allow-guards) |
Violations of the schema, a bad name, or an unreadable file are ERR_INVALID_PROFILE with
details.issues.
Built-in profiles
Section titled “Built-in profiles”Defined in packages/checks/src/profile.ts (BUILTIN_PROFILE_INPUTS), served as
axiom://profile/default, …/strict, …/permissive. Side by side:
| Check id | Predicate | default |
strict (extends default) |
permissive |
|---|---|---|---|---|
path.reservedNames |
path.reservedNames |
{} |
inherited | {} |
content.noSecrets |
content.noSecrets |
{} (secrets only, pii: false) |
inherited | — |
manifest.maxArtifacts |
manifest.maxArtifacts |
{ "max": 2000 } |
inherited | — |
manifest.maxTotalBytes |
manifest.maxTotalBytes |
{ "max": 67108864 } (64 MiB) |
inherited | — |
repo.noOverwriteOf |
repo.noOverwriteOf |
{ "globs": [".git/**", ".axiom/**", "**/*.lock", "pnpm-lock.yaml", ".env*"] } |
inherited | — |
manifest.noDeletes |
manifest.noDeletes |
— | {} |
— |
deps.max |
deps.max |
— | { "max": 50 } |
— |
path.deny |
path.deny |
— | { "globs": ["**/node_modules/**"] } |
— |
limits |
{ maxArtifacts: 2000, maxTotalBytes: 67108864 } |
inherited | none | |
facts |
{ allowRepo: true, allowGuards: false } |
same | same |
default is what check/apply use when a Plan names no profile. It protects the repository’s
own control files from an agent overwrite (.git, .axiom, lockfiles, .env*), scans for
credentials, and caps set size; it does not restrict where an agent may write — that is a
repository decision, expressed with path.allow/path.deny. permissive runs only the
path-sanity check and exists for scratch repos and tests.
The pii knob
Section titled “The pii knob”content.noSecrets always scans for secrets (credentialAssignment, awsKey, githubToken,
privateKey, jwt, slackToken). Personal-data patterns (cnp, email, phoneRo, card)
are opt-in with "pii": true: replaying 30 OSS repositories through an all-on default
rejected 110 of 3240 real files on maintainer e-mails alone. Turn it on where personal data in a
source tree is a policy question:
{ "id": "content.noSecrets", "predicate": "content.noSecrets", "params": { "pii": true } }Because checks merge by id, that one line in a child profile re-parameterises the default
check rather than adding a second scan. The gate has the same knob (pii, plus
noSecretsExclude). Precision rules and the corpus numbers:
checks.md § content.noSecrets.
extends and merging for check profiles
Section titled “extends and merging for check profiles”loadProfile(name, { searchDirs }) resolves:
- Lookup — each search directory in order for
<name>.json, then the built-ins. First hit wins; files are not merged with a same-named built-in. - Parents first — the
extendschain is loaded recursively; a cycle or a missing parent isERR_INVALID_PROFILE. - Checks merge by
id— a child entry replaces the parent’s entry with the same id (change params, severity or even the predicate), and appends otherwise. There is no “remove”; to silence an inherited check, override it with the same id and"severity": "info". limitsandfactsmerge shallowly, child over parent.- The Plan’s own
checks[]are merged last with the same rule — a Plan can tighten or re-parameterise, and the result is whatCheckReport.profilenames.
{ "apiVersion": "axiom.codai.ro/v3", "kind": "Profile", "name": "web", "extends": "strict", "checks": [ { "id": "path.allow", "predicate": "path.allow", "params": { "globs": ["apps/web/**", "packages/ui/**", "docs/**"] } }, { "id": "content.noSecrets", "predicate": "content.noSecrets", "params": { "pii": true, "allowPaths": ["**/*.test.ts"] } }, { "id": "ripple.schema", "predicate": "repo.requireCompanion", "params": { "rules": [ { "when": "packages/db/src/schema/*.ts", "expect": [ { "name": "migration", "match": "packages/db/drizzle/*.sql", "mustChange": true } ] } ] } } ]}Discovery
Section titled “Discovery”The CLI (--profile <name>) and the MCP server (profile argument) resolve a named check
profile from <root>/.axiom/profiles/<name>.json, then the built-ins. The file name is the
profile name; name inside must match. This is the location check/apply read today (see
the note at the top of this page about the checks section).
Commit the policy files — they are the repository’s policy, reviewed like CI configuration —
and keep the rest of .axiom/ ignored:
.axiom/*!.axiom/axiom.json!.axiom/profiles/!.axiom/gate-profile.jsonA Plan selects its profile with "profile": "web"; the CLI/MCP --profile/profile overrides
it; apply defaults to the manifest’s profile. The default profile’s repo.noOverwriteOf
refuses a manifest that rewrites .axiom/**, and apply refuses any artifact under .axiom/
(ERR_PATH_RESERVED_DIR), so an agent cannot edit the policy it is judged by through AXIOM.
Guards need two switches
Section titled “Guards need two switches”guard.external (your own scripts/check-*.mjs as a predicate) is disabled twice by default:
the profile must set facts.allowGuards: true and the process must be started with
--allow-guards (plus --guard-allowlist <abs> for absolute commands). Neither switch is
reachable from an MCP tool; a profile that asks for guards on a process without the flag yields
verdict: error (ERR_FACT_DISABLED) — fail closed, never skipped. Long suites belong in
axiom_check_start tasks (mcp-tools.md § Tasks). Worked example:
integration/brivio.md.
Resources and introspection
Section titled “Resources and introspection”axiom://profile/<name>— the built-in profiles as JSON.axiom schema Profile/axiom://schema/Profile— the JSON Schema.CheckReport.profile— the resolved name the report was judged with;factsDigestlets it be replayed.
See also
- Checks — every predicate and its params
- Command rules — writing
commands.denyrules - Plan format —
Profileamong the other wire types - Hooks — the gate policy in context
- Error codes — every gate rule code
- Trust model — why policy is operator-owned and fails closed