Governed-tool behaviour and compile-time validating __using__ macro.
Host developers declare a governed tool with:
use Cairnloop.Tool,
risk_tier: :read_only,
title: "Lookup Order",
description: "Retrieve an order by ID."The macro validates risk_tier and approval_mode enum values at compile time
(raises CompileError on bad values), derives a fail-closed approval_mode from
risk_tier when omitted, and generates a __tool_spec__/0 returning a frozen
%Cairnloop.Tool.Spec{} pure data struct.
A tool that declares no policy is denied by default — authorize/2 returns
{:error, :no_policy_defined} unless overridden.
Callbacks
Required (host must implement):
run/3— executes the tool; NOT called in Phase 13changeset/2— validates typed input via Ecto embedded schema (D-04)scope/0— returns list of required scope atoms
Optional:
authorize/2— dynamic policy callback; defaults to{:error, :no_policy_defined}(deny-by-default, D-16)custom_ui/0— custom LiveView UI module; defaults tonilpreview/1— human-readable consequence summary; no default (Phase 14 seam)
Summary
Callbacks
Dynamic policy callback. Returns :ok to permit or {:error, reason} to deny.
Default implementation returns {:error, :no_policy_defined} (deny-by-default, D-16).
Returns an Ecto changeset for the tool's inputs. This is the typed-input seam (D-04). Host must implement; no default is injected.
Optional callback to provide a custom UI module (e.g. a LiveView module) that should be rendered instead of the auto-generated form.
Optional callback returning a human-readable consequence summary string. No default implementation — Phase 14 seam.
Executes the tool logic with the populated struct.
Returns {:ok, result} or {:error, reason}.
Returns the list of scope atoms this tool requires to be present in the actor context. Used by the registry visibility filter and the Governance validation pipeline.
Functions
Derives the fail-closed approval_mode from a risk_tier value (D-11).
Types
Callbacks
Dynamic policy callback. Returns :ok to permit or {:error, reason} to deny.
Default implementation returns {:error, :no_policy_defined} (deny-by-default, D-16).
@callback changeset(tool :: struct(), attrs :: map()) :: Ecto.Changeset.t()
Returns an Ecto changeset for the tool's inputs. This is the typed-input seam (D-04). Host must implement; no default is injected.
@callback custom_ui() :: module() | nil
Optional callback to provide a custom UI module (e.g. a LiveView module) that should be rendered instead of the auto-generated form.
Optional callback returning a human-readable consequence summary string. No default implementation — Phase 14 seam.
Executes the tool logic with the populated struct.
Returns {:ok, result} or {:error, reason}.
NOT called in Phase 13 — execution is deferred to Phase 16.
@callback scope() :: [atom()]
Returns the list of scope atoms this tool requires to be present in the actor context. Used by the registry visibility filter and the Governance validation pipeline.
Functions
Derives the fail-closed approval_mode from a risk_tier value (D-11).
Called at macro-expansion time (not runtime) so this is a plain def, not a macro.
read_only -> :auto
low_write -> :requires_approval
high_write -> :requires_approval
destructive -> :always_block
unknown/nil -> :always_block (fail-closed default)