-
Notifications
You must be signed in to change notification settings - Fork 3
Domains Safety
The safety domain owns every layer of Clio's tool-call safety stack: action classification, damage-control rule matching, path-policy enforcement, project-policy validation, autonomy-level mapping, NDJSON audit recording, the finish-contract completion gate, and the scientific validation contract loader. It composes these into a single SafetyContract that the tool registry, the worker subprocess, the ACP delegation mediator, and the Claude SDK tool-safety bridge all consume through one interface.
The domain module entry point is SafetyDomainModule in src/domains/safety/index.ts, which exposes createSafetyBundle from src/domains/safety/extension.ts. The bundle returns a DomainBundle<SafetyContract> containing the lifecycle extension (bus subscriptions, audit writer, policy engine) and the contract object that callers depend on.
flowchart TD
A["Tool call arrives"] --> B["SafetyContract.evaluate(call, posture)"]
B --> C["SafetyPolicyEngine.evaluate"]
C --> D["Classify + damage-control match"]
D --> E{"Write-root confinement?"}
E -->|"escape"| F["block: write-root"]
E -->|"pass"| G{"Git destructive / block rule?"}
G -->|"yes"| H["block"]
G -->|"no"| I{"Project policy valid?"}
I -->|"invalid + execute"| J["block: project-policy-invalid"]
I -->|"valid"| K["Path policy + trust/skill checks"]
K --> L{"Path block?"}
L -->|"yes"| M["block: path-policy"]
L -->|"no"| N{"Authored ask rule?"}
N -->|"yes"| O{"Confirmed?"}
O -->|"yes"| P["allow"]
O -->|"no"| Q["ask"]
N -->|"no"| R{"System modify?"}
R -->|"yes"| S{"Yolo/confirmed?"}
S -->|"yes"| P
S -->|"no"| Q
R -->|"no"| T["Bash net evaluation"]
T --> U["Autonomy mapping: mapAutonomy"]
U --> V["Final disposition: allow/ask/deny"]
The SafetyContract interface in src/domains/safety/contract.ts declares the full public surface:
-
classify(call)— pure classification, no audit, no bus events. -
evaluate(call, posture?)— full evaluation: classify + damage-control match + decision; writes the safety-net audit row and emitsBusChannels.SafetyBlockedon a block. -
observeLoop(key, now?)— loop-detection verdict. -
scopes— readonly specs forreadonly,workspace, andconfirmed. -
isSubset(worker, orchestrator)— scope containment check for dispatch admission. -
policy.metadata(posture?)— immutable safety policy metadata for receipts and audit replay. -
audit.recordToolCall(input)— the registry's hook for autonomy-level final dispositions. -
audit.recordCompletionContract(input)— the finish-contract's hook for turn_end decisions.
Key symbols by file:
| File | Key symbols |
|---|---|
src/domains/safety/policy-engine.ts |
createSafetyPolicyEngine, SafetyPolicyDecision, SafetyPolicyEngine, SafetyPolicyEngineOptions, BUILTIN_ALLOWLIST, TEST_RUNNER_COMMANDS, PROJECT_SCRIPT_COMMANDS, WRITE_ROOT_REFUSED_TOOLS, evaluateBashPolicy, evaluateWriteRoots, matchesRepositoryCommand
|
src/domains/safety/action-classifier.ts |
classify, ActionClass, Classification, ClassifierCall, GIT_DESTRUCTIVE_PATTERNS, SYSTEM_MODIFY_PATTERNS, SYSTEM_WRITE_ROOT_PREFIXES, SYSTEM_WRITE_EXEMPT_PREFIXES, normalizeCallPaths
|
src/domains/safety/autonomy.ts |
mapAutonomy, AutonomyLevel, AutonomyDisposition, autonomyFromUserInput, AUTONOMY_EXPOSURES
|
src/domains/safety/audit.ts |
openAuditWriter, AuditRecord, ToolCallAuditRecord, buildAuditRecord, buildCompletionContractAuditRecord, AUDIT_FLUSH_INTERVAL_MS
|
src/domains/safety/finish-contract.ts |
assessFinishContract, FinishContractAssessment, FinishContractInput, recentEntries, typedValidationSummary, DEFAULT_RECENT_ENTRY_LIMIT
|
src/domains/safety/finish-contract-registration.ts |
createFinishContractRegistration, FINISH_CONTRACT_REGISTRATION_ID, HIGH_RIGOR_REVALIDATION_MESSAGE
|
src/domains/safety/project-policy.ts |
loadProjectSafetyPolicy, LoadedProjectSafetyPolicy, ProjectCommandPolicy
|
src/domains/safety/validation-contract.ts |
loadValidationContract, parseValidationContractText, describeValidationContract, ValidationContractLoadResult, VALIDATION_CONTRACT_CAPS, VALIDATION_CONTRACT_YAML_PATHS
|
src/domains/safety/path-policy.ts |
compilePathPolicy, evaluatePathPolicy, CompiledPathPolicy, PathPolicyDecision
|
src/domains/safety/default-path-policy.ts |
DEFAULT_DAMAGE_CONTROL_PATH_POLICY, OPERATOR_PATH_POLICY, mergePathPolicyInputs
|
src/domains/safety/damage-control.ts |
match, DamageControlRule, DamageControlRuleset, DamageControlMatch
|
src/domains/safety/rule-pack-loader.ts |
getCachedDefaultRulePacks, RulePacks, PackId
|
src/domains/safety/protected-artifacts.ts |
extractCommandWriteTargets, extractCommandDeleteTargets, detectValidationCommand, toolMutationPaths, scanShellLike, scanShellLikeDeep
|
src/domains/safety/protected-artifacts-registration.ts |
createProtectedArtifactsRegistration, ProtectedArtifactsRegistration
|
src/domains/safety/scope.ts |
READONLY_SCOPE, WORKSPACE_SCOPE, CONFIRMED_SCOPE, isSubset
|
src/domains/safety/loop-detector.ts |
createLoopState, observe, LoopDetectorState, LoopVerdict
|
src/domains/safety/run-effects.ts |
RunEffects, createRunEffectsRecorder
|
src/domains/safety/rigor.ts |
Rigor, resolveRigor, rigorResolution, parseRigorOverride
|
The safety domain separates two orthogonal axes of control:
-
The safety net (level-independent): hard blocks and confirm rails that apply at every autonomy level. This axis owns damage-control rule matching (
damage-control.ts), path-policy enforcement (path-policy.ts), project-policy validation (project-policy.ts), trust and skill authority checks, and the write-root confinement for worker runs. -
The autonomy axis (level-dependent): an ordered dial (
default→yolo) that controls which action classes trigger the approval flow versus run immediately versus auto-deny. This axis runs after the safety net; a net block is final, and the mapping applies only to level-dependent rows that passed the net.
The finish contract and the validation contract are separate subsystems that ride on the same audit ledger and share the same mutation vocabulary (extractCommandWriteTargets, detectValidationCommand, toolMutationPaths from protected-artifacts.ts).
The primary caller is src/tools/registry.ts:602. The admit function calls deps.safety.evaluate(call, posture), where posture is derived from the autonomy level: a one-shot grant sets "confirmed", yolo level sets "yolo", and default sets undefined.
registry.admit(call)
→ deps.safety.evaluate(call, posture)
→ activePolicyEngine().evaluate(call, posture) // extension.ts:277
→ createSafetyPolicyEngine().evaluate(call, posture) // policy-engine.ts:269
createSafetyPolicyEngine in src/domains/safety/policy-engine.ts:269 returns an object with evaluate, readablePath, and metadata. The evaluate method runs through the following stages in order, each of which can short-circuit to a final block, ask, or allow:
-
Normalize and classify.
normalizeCallPathsstrips leading@and folds Unicode spaces in path arguments;classifymaps(tool, args)to anActionClass(read,write,execute,dispatch,system_modify,git_destructive, orunknown). -
Damage-control scan.
damageControlScans(call)extracts the command string(s) to scan; for content-bearing tools (Write,Edit,Artifact,Dispatch,Tasks) it scans only the destination path, while for bash and verify it scans the full command plusshellCommandSegmentsandnormalizedGitCommands.matchSourcedRulematches the scanned strings against the base rule pack fromdamage-control-rules.yaml. -
Write-root containment.
evaluateWriteRootschecks whether a write-class tool's target escapes the permittedwriteRoots. Under active write-root confinement, execute-class tools (bash,verify,run_script) anddispatchare blocked outright (isWriteConfinementEscape), because they can mutate the filesystem outside the roots. -
Git destructive and block rules. If the classification or the damage-control match is
git_destructive, or the match hasblock: true, the call is a hard block. An explicitask: truedamage-control rule (askRule) is deferred to a later stage. -
Project policy validity. If
.clio-coder/safety.yamlis invalid and the tool is an execution tool (bash,verify), the call is blocked with reasonproject-policy-invalid. -
Trust and skill authority.
invokesTrustMutationdetectsconfig trustcommands.skillMutationReasonchecks whether the command mutates Clio's settings, workspace trust records, or skill roots. -
Path policy.
evaluateProjectPathPolicycompiles and evaluates the merged path policy (defaults + project + operator) against the call's targets. A block here uses reason codes likepath-policy:zeroAccessPathsorpath-policy:readOnlyPaths. -
Bash zero-access read scan.
evaluateBashZeroAccessReadscans bash command tokens for zero-access paths (e.g.,cat .env), blocking them. The only carve-out isgrep -sq "^NAME=" <file>(exit-code-only presence check). -
Library confirm. Managed library changes (
invokesClioSkillMutation) are confirmed byconfirmedposture or admitted byyolo, otherwise they ask in default. -
Authored ask rail. If
askRulematched a damage-control rule withask: trueandblockis not true, the confirmed posture allows; otherwise it asks. -
System modify confirm.
system_modifyaction class asks in default, allows in yolo and confirmed. -
Bash net evaluation.
evaluateBashPolicy(policy-engine.ts:805) is the bash-specific net: empty commands, cwd escapes, hidden content (variable substitution, command substitution), project policy commands,&&chains, test runners, project scripts, built-in allowlist, and sequencing operators. It returns anexecRecognitiontag ("recognized"or"unrecognized") that the autonomy mapping uses. -
Read scope escape. For read-class tools,
readScopeEscapechecks whether the path resolves outside the workspace and outside Clio's own readable roots. The net passes; thereadScope: "outside-workspace"flag lets the autonomy mapping ask in default.
After the safety net passes, src/tools/registry.ts calls mapAutonomy from src/domains/safety/autonomy.ts. The function takes level (default or yolo), actionClass, and options (executeRecognized, dispatchPlanScale, exposure, readOutsideWorkspace). The mapping:
-
git_destructive→"deny"(defensive; the net blocks it first). -
readwithreadOutsideWorkspaceat default →"ask"; at yolo →"allow". -
read→"allow". -
write→"allow"at both levels. -
dispatchwithdispatchPlanScaleat default →"ask"; at yolo →"allow". -
executewithexecuteRecognized !== false→"allow"; unrecognized at default →"ask", at yolo →"allow". -
unknown→"ask"at default,"allow"at yolo. -
system_modify→"ask"at default,"allow"at yolo.
The registry then records the final disposition through safety.audit.recordToolCall.
src/engine/worker-tools.ts:55 createWorkerSafety(options) builds a worker-local SafetyContract that owns its own policy engine (with writeRoots confinement), per-run loop-detector state, and protected artifact state. The worker does not share the orchestrator's loop detector or audit writer.
-
src/engine/acp/tool-mediator.ts:592callssafety.evaluatefor delegated tool calls. -
src/engine/claude/tool-safety.ts:238callssafety.classifyandsafety.evaluatefor the Claude SDK tool-safety bridge.
assessFinishContract in src/domains/safety/finish-contract.ts:112 is a pure function that reads only ledger receipts. It looks at the recent window (capped at DEFAULT_RECENT_ENTRY_LIMIT = 80 entries, bounded by the last user message) and returns a discriminated union:
ok/no_mutation — no successful mutation receipt in the window
ok/validation_evidence — validation evidence present
ok/explicit_limitation — successful limitation receipt present
engage/unvalidated_mutation — mutation without evidence or limitation
The decision order is:
-
mutatingReceipts(window)— paths from successful write/edit/artifact receipts and bash write/delete targets. -
collectValidationEvidence(window, acceptanceChecks, workspaceRoot)— validation command runs (detectValidationCommand), verify check runs, passed dispatch receipts, protected-artifact records. -
collectLimitationEvidence(window)— successfullimitationtool calls (non-errortool_result). - If rigor is
"high", it additionally requires that every check inactiveAcceptance.verificationhas passing evidence or a limitation receipt naming the exact check id.
createFinishContractRegistration in src/domains/safety/finish-contract-registration.ts:67 packages the assessor as a middleware hook registration for turn_end. On a settled stop turn with non-empty final text, it reads session entries, resolves rigor, and calls assessFinishContract. On engage:
- At normal rigor: injects a
warnreminder. - At high rigor: if verification tools are available, emits
request_continuationwithHIGH_RIGOR_REVALIDATION_MESSAGE; otherwise injects a warning.
Every decision (ok or engage) is written to the audit ledger via recordDecision, which calls options.recordDecision (wired in production to safety.audit.recordCompletionContract).
loadProjectSafetyPolicy in src/domains/safety/project-policy.ts reads .clio-coder/safety.yaml from the workspace (searching upward). It validates the YAML against a strict schema:
- Root keys:
version,commands,tasks,disableDefaultPathPolicy,zeroAccessPaths,readOnlyPaths,noWritePaths,noDeletePaths. -
versionmust be1. - Command entries have
id,command,cwd,timeoutMs,maxOutputBytes,actionClass,shellOperators,env,requireConfirmation,rationale,owner,comment. - Path entries must be relative to the policy root, cannot be absolute, and cannot escape with
...
An invalid policy returns valid: false with errors; the policy engine treats this as a fail-closed condition for execution tools.
compilePathPolicy in src/domains/safety/path-policy.ts compiles a PathPolicyInput into sorted entries. The policy engine merges three layers:
-
DEFAULT_DAMAGE_CONTROL_PATH_POLICYfromdefault-path-policy.ts— zero-access paths (.env,~/.ssh/,*.pem,credentials.yaml, etc.), read-only paths (.clio-coder/safety.yaml,node_modules/,dist/, etc.), no-write paths (foreign agent dirs), and no-delete paths (CLAUDE.md,LICENSE,README.md, etc.). - The project policy's path entries (gated on validity).
-
OPERATOR_PATH_POLICY— operator authority that survives every project-local exemption.
evaluatePathPolicy resolves each target path (chasing symlinks via canonicalizeRawPath) and checks it against entries sorted by kind (zeroAccessPaths → readOnlyPaths → noWritePaths → noDeletePaths). The first matching entry that blocks the operation wins.
getCachedDefaultRulePacks in src/domains/safety/rule-pack-loader.ts loads damage-control-rules.yaml (schema v2) from the package root. The v2 schema stores rules in a named base pack. Rules are compiled by compileDamageControlRule in rule-compiler.ts into DamageControlRule objects with a RegExp pattern, an action class, and a block flag. The rule pack is cached in module scope.
rigorResolution in src/domains/safety/rigor.ts resolves the effective rigor for a turn. Rigor is orthogonal to the autonomy permission levels: autonomy says what an agent may touch; rigor says what evidence "done" requires.
The resolution order is:
-
Explicit override (
"high"or"normal"): always wins. Sourced from theCLIO_CODER_RIGORenv var viaparseRigorOverride. -
Validation contract: if the workspace's scientific-validation contract parses under the version-1 schema, rigor is
high. The contract path is recorded in the resolution. -
Markdown advisory: if only
VALIDATION.mdexists (no YAML), rigor staysnormalwith a diagnostic noting the advisory prose. -
Invalid contract: if the YAML contract exists but fails to parse, rigor stays
normalwith a diagnostic. -
Default:
normalwith source"none".
This means the evidence bar is derived from what the repo actually declares, not from a filename or a global toggle. A contract that fails to parse is diagnosed and leaves rigor at normal.
loadValidationContract in src/domains/safety/validation-contract.ts reads the workspace's scientific validation contract from .clio-coder/validation.yaml (or validation.yml). It is a strict version-1 loader:
-
VALIDATION_CONTRACT_CAPSdefines hard limits:fileBytes: 256 KB,textBytes: 4096,notesBytes: 16 KB,artifacts: 256,validators: 128,modules: 64,mapEntries: 256,mapKeyBytes: 256. - The schema requires
version: 1at the root, with optionaltask,runtime,artifacts,validators, andnotes. -
runtime.kindmust be one of"local","slurm","mpi","other". -
artifactsentries requirepath, with optionalformat,expected_dimensions,expected_attributes,numerical_tolerances, andpreserve. -
numerical_tolerances.ulpis capped atMAX_ULP_TOLERANCEfromtools/verify/numeric.ts.
The loader never throws; every failure is a { ok: false, path, reason } value. A VALIDATION.md without YAML is reported present and advisory.
openAuditWriter in src/domains/safety/audit.ts creates an NDJSON writer that rotates files on local-date rollover (YYYY-MM-DD.jsonl). Rows are written with writeSync; durability fsync happens on flush(), close(), rotation, and a 5-second background interval (AUDIT_FLUSH_INTERVAL_MS). Write errors are logged to stderr with [clio-coder:audit] prefix and never thrown.
The writer is a discriminated union over kind:
-
tool_call— emitted bysafety.evaluate()for every classified tool call. -
permission— one-shot tool/action confirmation requests and resolutions. -
abort—BusChannels.RunAbortedevents. -
session_park/session_resume— session lifecycle events. -
agent_status_change— alarmable agent-status transitions. -
completion_contract— finish-contract decisions.
Strings are truncated at MAX_STRING_LEN = 200; keys matching REDACT_KEY_RE (password, token, secret, key, auth, credential) are redacted.
classify in src/domains/safety/action-classifier.ts is a pure function with no I/O and no state. It maps (tool, args) to an ActionClass:
-
Read class:
read,grep,find,ls,evidence,web_read,web_fetch(GET/HEAD),git,code_nav,self_compact,context,clio_docs,clio_library,data,gateway,monitor,ask_user,credential_present,tasks,ledger,panes,limitation,decide,consult,vision. -
Write class:
write,edit,artifact,configure_clio, andweb_fetchwith outward arguments. -
Execute class:
bash,verify,run_script. -
Dispatch class:
dispatch,steer, andpaneswithaction: "handoff". -
Harness extensions: classified as
execute.
For bash calls, the classifier additionally:
- Matches
GIT_DESTRUCTIVE_PATTERNS(force push, reset --hard, checkout -- ., branch -D). - Matches
SYSTEM_MODIFY_PATTERNS(sudo, rm -rf /, apt install, brew install, systemctl, chmod, chown). - Scans write targets in the command (
bashPathReasons) for system-root or outside-cwd paths, escalating tosystem_modify.
For write-class tools, writePathClass checks the target against SYSTEM_WRITE_ROOT_PREFIXES (/etc, /usr, /var, /bin, /sbin, /run, /private/etc, /private/var) with exemptions for SYSTEM_WRITE_EXEMPT_PREFIXES (/var/tmp, /var/folders, /private/var/tmp, /private/var/folders). Paths outside the workspace (~-prefixed or unresolvable) also escalate to system_modify.
Rules are added to the base pack in damage-control-rules.yaml (schema v2). The rule pack loader compiles them at load time. The policy engine matches them in rule order (first match wins). A rule with ask: true and block not true becomes a confirm rail; a rule with block: true is a hard block.
Test runners are added to TEST_RUNNER_COMMANDS in src/domains/safety/policy-engine.ts:169. Project scripts are added to PROJECT_SCRIPT_COMMANDS at line 192. Both are regex lists. The test-runner-vocabulary.test.ts test enforces that every TEST_RUNNER_COMMANDS id maps to a detectValidationCommand label; the test fails if they drift.
Project-authored path entries go in .clio-coder/safety.yaml under zeroAccessPaths, readOnlyPaths, noWritePaths, or noDeletePaths. Built-in defaults are in DEFAULT_DAMAGE_CONTROL_PATH_POLICY in src/domains/safety/default-path-policy.ts. Operator-level entries are in OPERATOR_PATH_POLICY.
The AutonomyMappingOptions interface in src/domains/safety/autonomy.ts is the extension point. The registry passes executeRecognized, dispatchPlanScale, exposure, and readOutsideWorkspace. New options are added to this interface and passed by the registry's admit function.
The AuditRecord discriminated union in src/domains/safety/audit.ts is extended by adding a new kind and a corresponding builder function. The write method serializes any AuditRecord to JSON.
This is the comprehensive safety gate boundary test. It creates a SafetyPolicyEngine in an isolated scratch environment and tests:
- Hard-blocking zero-access paths (
read .env,write credentials.yaml,bash ": > .env") before confirmation or ask rails. - The interaction between
mapAutonomyand the policy engine:executionDispositionreturns"allow","ask", or"block"depending on the level and the decision. - Write-root confinement: a worker safety with
writeRootsconfines writes to the declared roots. - Symlink escape classification: writes through escaping symlinks classify as
system_modify. - Project policy command recognition: a valid
.clio-coder/safety.yamlallows declared commands.
This test enforces that TEST_RUNNER_COMMANDS and PROJECT_SCRIPT_COMMANDS stay in sync with detectValidationCommand labels. It asserts:
- Every
VALIDATION_COMMAND_LABELSkey has a correspondingTEST_RUNNER_COMMANDSentry. - Every test runner spelling is recognized by the policy engine as
allowwithexecRecognition: "recognized". - Project scripts are
allowwithexecRecognition: "unrecognized", which maps toaskat default andallowat yolo. - Command substitution and unsafe destinations keep their safety rails (
node --test $(cat f)is not allowed). -
cd build && ctestis recognized as a chain (bash-recognized-chain).
This test verifies that the policy engine and classifier handle symlink escapes correctly:
- A write through a dangling escaping link classifies as
system_modifywith reasonwrite-path-outside-cwd. - The registry's end-to-end flow refuses the write and leaves nothing outside the root.
- A read through an escaping link into a zero-access path is blocked by the path policy.
- A link loop at the target or in a parent component fails closed.
-
bash-cwd-escapeblocks a bash call with a cwd that is a link loop.
This test verifies the finish contract's decision order:
- A mutation plus a successful
limitationreceipt settlesok/explicit_limitation. - An errored
limitationcall leaves no receipt, so the contract engages. - Prose like "not verified" without a limitation receipt does not satisfy the contract.
- A limitation receipt from before the last user message is ignored.
- Validation evidence takes precedence over a limitation receipt.
- A successful native Node test receipt settles the contract; a failed test does not.
This test verifies the validation contract loader:
- The documented example contract (a NetCDF climate output contract with slurm runtime, 4 nodes, 64 ranks, and 2 artifacts) parses under the version-1 schema.
- Cap violations (file size, text length, map entries) produce diagnostics.
- A
VALIDATION.mdwithout YAML is reported present and advisory. -
parseValidationContractTextrejects unknown fields, invalid versions, and missing required fields.
-
Rule order is precedence. In
matchSourcedRule(policy-engine.ts), the outer loop stays over the rules and each one is offered every scan candidate. A rule fires on the first candidate it matches, which can only add matches, never reorder or drop one. Reordering rules indamage-control-rules.yamlchanges behavior. -
The autonomy mapping never overrides a block. A
blockfrom the safety net is final at every level. ThemapAutonomyfunction inautonomy.tsonly applies to level-dependent rows after the net passed. -
Write-root confinement is lexical.
evaluateWriteRootsusespath.resolveandpathBoundaryCoverswithout chasing symlinks. A symlink inside a root that points outside is not detected here; the classifier'swritePathClassand the path policy'scanonicalizeRawPathcatch those separately. -
The finish contract reads only ledger receipts. The assistant's prose never enters the decision. A limitation counts only as a
limitationtool_call paired with a non-error tool_result inside the same window the mutation scan uses. -
The validation contract loader never throws. Every failure is a
{ ok: false, path, reason }value. Rigor resolution, verifier authoring, doctor, and the startup hint all read the contract through this module. -
The audit writer never propagates errors. Write errors are logged to stderr and swallowed. Safety must not kill the admission hot path.
-
The test runner vocabulary test is a synchronization guard. If you add a new test runner command to
TEST_RUNNER_COMMANDS, you must also add a correspondingValidationCommandLabeltodetectValidationCommandinprotected-artifacts.ts, or the test will fail. -
The damage-control rule pack is cached.
getCachedDefaultRulePacksstores the parsed rules in module scope. Changes todamage-control-rules.yamlrequire a restart to take effect. -
The project policy is fail-closed for execution tools. If
.clio-coder/safety.yamlis invalid,loadProjectSafetyPolicyreturnsvalid: false, and the policy engine blocksbashandverifycalls with reasonproject-policy-invalid. -
The path policy merges three layers in a specific order.
DEFAULT_DAMAGE_CONTROL_PATH_POLICY→ project policy →OPERATOR_PATH_POLICY. The operator layer survives every project-local exemption. -
The read-scope escape check only runs in non-confirmed posture.
readScopeEscapeis called only whenposture !== "confirmed". A confirmed one-shot grant bypasses the read-scope ask. -
The
&&chain recognition is limited to 6 segments.CHAIN_MAX_SEGMENTS = 6inpolicy-engine.tscaps the number of&&-separated segments. Chains longer than this are unrecognized. -
The bash zero-access read scan has one carve-out.
grep -qorgrep -sqwith a^NAME=-shaped pattern and a single file argument is allowed. This is the safe protocol the credentials skill teaches for exit-code-only presence checks.
Source and generation metadata
title: "Domains safety"
summary: "The safety domain classifies tool calls into action classes, evaluates them against damage-control rules, path policies, and project policy, maps them to autonomy dispositions, and audits every decision to a daily-rotated NDJSON ledger."
sources:
- "src/domains/safety/index.ts"
- "src/domains/safety/extension.ts"
- "src/domains/safety/contract.ts"
- "src/domains/safety/policy-engine.ts"
- "src/domains/safety/action-classifier.ts"
- "src/domains/safety/autonomy.ts"
- "src/domains/safety/audit.ts"
- "src/domains/safety/finish-contract.ts"
- "src/domains/safety/finish-contract-registration.ts"
- "src/domains/safety/project-policy.ts"
- "src/domains/safety/validation-contract.ts"
- "src/domains/safety/protected-artifacts.ts"
- "src/domains/safety/path-policy.ts"
- "src/domains/safety/default-path-policy.ts"
- "src/domains/safety/damage-control.ts"
- "src/domains/safety/rule-pack-loader.ts"
- "src/domains/safety/scope.ts"
- "src/domains/safety/loop-detector.ts"
- "src/domains/safety/run-effects.ts"
- "src/domains/safety/rigor.ts"
- "src/domains/safety/protected-artifacts-registration.ts"
tests:
- "tests/contracts/safety-gates.test.ts"
- "tests/contracts/test-runner-vocabulary.test.ts"
- "tests/contracts/symlink-escape.test.ts"
- "tests/contracts/finish-contract-limitation.test.ts"
- "tests/extended/validation-contract.test.ts"
invariants:
- "A safety-net block is final at every autonomy level; the autonomy mapping never overrides a block."
- "The finish contract engages only when the recent window contains a successful mutation receipt and no validation evidence and no successful limitation receipt."
- "The validation contract loader returns a discriminated union on every path; it never throws."
- "The audit writer never propagates write errors to the caller; safety must not kill the admission hot path."
- "Write-root confinement is lexical and does not chase symlinks, so a symlink inside a root that points outside is not detected by the write-root check alone."
validate:
- "pnpm run test:file -- tests/contracts/safety-gates.test.ts"
- "pnpm run test:file -- tests/contracts/test-runner-vocabulary.test.ts"
- "pnpm run test:file -- tests/contracts/symlink-escape.test.ts"
- "pnpm run test:file -- tests/contracts/finish-contract-limitation.test.ts"
- "pnpm run test:file -- tests/extended/validation-contract.test.ts"Clio Coder · Repository · Website · Documentation
Wiki v0.1 · Developing implementation reference · Source snapshot: 657dce13d. Authored architecture documents define the product contracts.
- Clio Coder GUI Client
- apps / clio-coder-gui
- Apps clio coder gui server
- Apps clio coder gui tests
- apps
- Architecture
- Command-line surfaces
- Core
- Domains agents
- Config Domain
- Context Domain
- Dispatch domain
- Domains evidence
- Domains extensions
- Domains gateway
- domains
- Domains interop
- Domains lifecycle
- Domains memory
- Middleware Domain
- Domains mux
- Domains observability
- Domains plugins
- Prompt Compiler
- Domains providers
- Domains quota
- Domains resources
- Domains safety
- Domains scheduling
- Domains session
- Vendored Tool Registry and Resolution
- Engine
- Engine acp
- Engine apis
- engine
- Entry point
- Interactive
- interactive
- Interactive overlays
- Interactive renderers
- clio-coder wiki
- Scripts
- Contract tests
- Tests extended
- tests
- Tools
- Tools data
- tools
- Tools verify
- Worker runtime