ADR-0020: Allowlists¶
Status: Accepted, amended by ADR-0029 and ADR-0034 Date: 2026-10-12 Deciders: Alex Nodeland
Context¶
artifactr checks a rule's writes to artifacts and proposals against their write policies and the thread's mode, but not where a rule acts, whether it creates threads, or whether its message starts a turn. stackr RFC-0002 makes each rule's allowlist the limit on those, "required rather than optional", defaulting to proposals and notices in the chain's own thread, and D7 makes starting a turn a per-rule permission.
Decision¶
Allowlistis a frozen model with four fields:direct, the artifact kinds a rule creates and edits without proposing;threads,chainorany;create_threads; andturns. artifactr's write policy and the thread's mode still apply to a direct write.- Every rule has one.
Outbound(allowlists=...)maps rule names to allowlists; a rule with none gets the default, which allows proposals and notices only. - A chain's threads are the threads of the bridged events that made the rule fire, and those its run creates. A write names a thread or takes the first of them; with
threads="chain", any other thread is refused. A run with none must create one (if allowed) or name one (withthreads="any"). sendalso needs a runner:Outbound(runner=...), artifactr'sRunner, which starts the turn. Without one no rule may start turns.- A refusal is
NotAllowed, a permanentRunFailurewith the reasonnot_allowed. An agent's tools turn it into a retry for the model, which can do something else.
Options considered¶
| Option | For | Against |
|---|---|---|
| A mapping from rule names, with a restrictive default (chosen) | Plain data, reviewed with the rules; a rule nobody listed can do the least | Stored rules need their allowlist from somewhere: phase 5 decides where |
| A callback per write | Anything can be checked | The limit moves into code no one reviews with the rule |
| A permissive default | Nothing to configure | A new rule could start turns, or act in any thread, by omission |
Consequences¶
- Easier: what each rule may do beyond proposals and notices is one line beside it.
- Harder: phase 5's chat rules need an allowlist of their own, which the install rule will set.