The rules file
Rules live in.attesso/rules.json, versioned with your code. Every entry records the digest of the rules it was judged under, so a later rule change is visible against old decisions.
The file is validated before it is used. An unknown capability, an unknown trust level, a rule with no condition, or a non-integer ceiling is a hard error, not a silently ignored rule.
Evaluation order
Rules are evaluated in file order and the first match wins, so specific rules belong above broad ones. For each rule:- Capability filter. A rule only judges a proposal whose capability it denies. A
max_stepson asendrule is checked when a send is proposed, against the whole task history. - Step ceiling.
max_stepscompares the task’s history length. At or over the ceiling:DENY, codeSTEP_LIMIT,tainted_byis the step count. This is the rule that ends a runaway loop. - Repeat ceiling.
max_repeatscounts earlier entries with the same subject and the same argument fingerprint. At or over the ceiling:DENY, codeREPEAT_LIMIT,tainted_byis the first repeated step. Without an argument fingerprint a repeat cannot be identified, so the check is skipped rather than counting every call with the same subject. - Path match. The most recent earlier step that matches
trust,capability,subjectandsame_arguments, insidewithin_minutes, taints the path. On a match:DENY, codePATH_DENIED,tainted_bynames that step.
#2 DENY mail.send partner@example.com (PATH_DENIED by step #1 under no-send-after-untrusted-read).
Shipped profiles
init writes balanced. init --profile strict writes the conservative variant. An existing ledger can adopt either:
rules.json.bak, and the command prints the new digest.
balanced blocks the outbound boundary and duplicate fetches; it leaves local edits and commands alone. strict adds write and execute to the first rule, for an untrusted environment where a shell command itself can exfiltrate.
Ceilings for loops
The repeat rule above is narrow on purpose: one subject, same arguments, thirty minutes. A ceiling is the general form and belongs on the task:max_steps counts the task’s history, so it stops the afternoon, not one call. max_repeats needs the caller to supply an argument fingerprint; a harness that sends args_hash gets repeat detection, one that does not is left with max_steps.
Find rules from the ledger
suggest counts identical calls in the ledger and prints the rule its own counts justify:
explain opens one entry in full: the verdict, the rule, the step that tainted the path, and what to do about it.
PATH_DENIED names the step and the rule, STEP_LIMIT suggests splitting the task or raising the ceiling, REPEAT_LIMIT suggests changing the call, and a policy refusal points at the amount, payee or window instead.
Test a change before it blocks work
replay re-judges the whole ledger against a candidate policy and rules file, using the history that preceded each decision:
--max-newly-denied N turns a budget into an exit code, so CI can refuse a rule change that would have blocked real work.
Replay is a diagnostic: it uses the history the ledger holds and cannot prove that history is complete.
Where the policy ends and the rules begin
policy.jsondecides one action: action name, subjects, amount, currency, window. Its codes areACTION_MISMATCH,REQUIRED_ATTRIBUTE_MISSING,VALUE_NOT_ALLOWED,VALUE_MISMATCH,ABOVE_MAXIMUM,OUTSIDE_DATE_RANGE.rules.jsondecides what a task may not do after a step, plus the step and repeat ceilings. Its codes arePATH_DENIED,STEP_LIMIT,REPEAT_LIMIT.paths.jsondecides which files a file tool may touch. Its codes arePATH_SCOPE_DENIED,PATH_REQUIREDandSHELL_SCOPE_DENIED. See Scope file tools.
- Control. A paused or stopped task is refused with
TASK_PAUSEDorTASK_STOPPED, even in observe mode. - Tool pin. A changed or unpinned tool is refused with
TOOL_CHANGEDbefore any other rule is read. See MCP servers. - Path scope.
paths.jsonis checked next:PATH_SCOPE_DENIED,PATH_REQUIRED,SHELL_SCOPE_DENIED. See Scope file tools. - Policy.
policy.jsondecides the action itself:ACTION_MISMATCH,REQUIRED_ATTRIBUTE_MISSING,VALUE_NOT_ALLOWED,VALUE_MISMATCH,ABOVE_MAXIMUM,OUTSIDE_DATE_RANGE. - Path rules.
rules.jsonis only consulted when the policy allows:PATH_DENIED,STEP_LIMIT,REPEAT_LIMIT.

