The policy.* family (8 verbs). Every verb enters through the one front door: it is capability-checked, permission-checked, scope-checked and audited before it runs (see Security & permissions). Each entry below lists its capability, risk class (which fixes the minimum autonomy level), the minimum caller permission, its availability tier, whether it is idempotent, and any outbound egress.
Createpolicy.create
| Property | Value |
|---|---|
| Capability | policy.author |
| Risk class | Destructive - autonomy L3 |
| Min. permission | Admin |
| Availability | Admin only (CLI/HTTP; never advertised over MCP) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
Authoring (agent-non-writable). Creates a DRAFT policy.
Input
| Field | Type | Required | Description |
|---|---|---|---|
key | string | yes | |
title | string | yes | |
kind | string | yes | e.g. ELIGIBILITY |
jurisdiction | string | no | |
body | object | no | the authored rules: {inputs, rules, fallback, output} |
Call POST /v/policy.create with a JSON body; returns JSON (or { error, code }). Not advertised over MCP (admin-only); reachable via the CLI and HTTP doors.
Editpolicy.edit
| Property | Value |
|---|---|
| Capability | policy.author |
| Risk class | Destructive - autonomy L3 |
| Min. permission | Admin |
| Availability | Admin only (CLI/HTTP; never advertised over MCP) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
Re-author a DRAFT body (agent-non-writable). A PUBLISHED policy is edited by editing the DRAFT body then re-publishing (which forward-mints a NEW Version - prior verdicts pinned to the old version never drift, R11).
Input { id, title?, body? } (fields ending in ? are optional)
Call POST /v/policy.edit with a JSON body; returns JSON (or { error, code }). Not advertised over MCP (admin-only); reachable via the CLI and HTTP doors.
Evaluatepolicy.evaluate
| Property | Value |
|---|---|
| Capability | policy.evaluate |
| Risk class | Sensitive - autonomy L2 |
| Min. permission | Member |
| Availability | Core - always on (MCP tier 1) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
{ policyId, subject:{type,id}, inputs?, pinVersion?, gateOnConfirm? }. SENSITIVE/L2.
- Resolve the PINNED Policy Version (the caller may pin an explicit
pinVersion; default = the policy’s current published version). Re-publishing later never drifts THIS evaluation (R11). - Gather the typed input Facts: from inline
inputs(testable, no-LLM) ELSE from thefacttable by (subject, field_key). A missing REQUIRED input → BLOCK (a structured error), never a default (INV-9 fail-closed + no-silent-default). - Evaluate the rule-set + override layer (AND-of-ranges + bump-up + group roll-up; RISK_SCORE country-weight map) - deterministically.
- Emit: (a) a result Fact (the verdict - the named INV-6 exception, materialised-for-citation),
(b) the
policy_evaluationcitation row (policy version + matched branch + inputs hash), and optionally (c) a Gate when the outcome needs human confirmation. The dispatch records exactly one citation AuditEvent (INV-8).
Input
| Field | Type | Required | Description |
|---|---|---|---|
policyId | string | yes | |
subject | object | yes | {type: “matter”, id: “mat_…”} |
inputs | object | no | the policy inputs, e.g. {criteriaMet: 4} |
Call POST /v/policy.evaluate with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool policy.evaluate.
Getpolicy.get
| Property | Value |
|---|---|
| Capability | policy.read |
| Risk class | Read - autonomy L0 |
| Min. permission | Member |
| Availability | Core - always on (MCP tier 1) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
| Render schema | Yes - drives an inline chat artefact and a GUI panel |
A read operation in the policy.* family. Called through the one governed front door: capability, permission tier, autonomy, scope and egress are all resolved before the handler runs, and the call is audited (INV-1, INV-8).
Call POST /v/policy.get with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool policy.get.
Listpolicy.list
| Property | Value |
|---|---|
| Capability | policy.read |
| Risk class | Read - autonomy L0 |
| Min. permission | Member |
| Availability | Core - always on (MCP tier 1) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
| Render schema | Yes - drives an inline chat artefact and a GUI panel |
A read operation in the policy.* family. Called through the one governed front door: capability, permission tier, autonomy, scope and egress are all resolved before the handler runs, and the call is audited (INV-1, INV-8).
Call POST /v/policy.list with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool policy.list.
Publishpolicy.publish
| Property | Value |
|---|---|
| Capability | policy.author |
| Risk class | Destructive - autonomy L3 |
| Min. permission | Admin |
| Availability | Admin only (CLI/HTTP; never advertised over MCP) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
Authoring (agent-non-writable). Validates the DRAFT body, forward-mints an
immutable version row (parent_type=‘OTHER’, parent_id=the policy id) snapshotting the body +
jurisdiction + kind, and flips DRAFT→PUBLISHED with current_version = the new Version number. An
evaluation pins THIS version; a later re-publish mints a HIGHER version and never drifts it (R11).
Input
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes |
Call POST /v/policy.publish with a JSON body; returns JSON (or { error, code }). Not advertised over MCP (admin-only); reachable via the CLI and HTTP doors.
Setpolicy.set
| Property | Value |
|---|---|
| Capability | policy.author |
| Risk class | Write - autonomy L1 |
| Min. permission | Owner |
| Availability | On demand (MCP tier 2) |
| Idempotent | Yes |
| Egress | None - in-box (pure Postgres, no outbound call) |
where key ∈ GATE_CATEGORIES | SENSITIVITY_DEFAULTS. WRITE/L1/OWNER.
This is the TENANT’S GOVERNANCE CONFIG, NOT an authored Policy rule-set (that is policy.create over
the policy table + the policy.evaluate engine). It upserts the ONE per-(workspace, policy_key)
config row on the workspace_policy table (migration 0034) - the SAME upsert shape as
bill.defaults.set / party.facet.set / file.policy.* (steering #1: reuse the config-row pattern,
never fork a bespoke per-knob column set). OWNER-only (process-level blast radius - a workspace’s
gate-category set governs every future gate decision; its sensitivity defaults govern every new
object’s classification floor). Agent-non-writable (INV-11) like the rest of the policy authoring
surface: an AGENT actor is fail-closed denied.
GATE_CATEGORIES is TIGHTEN-ONLY (catalogue L119: floor=substrate, ceiling=tenant): the body’s
categories array MUST include every substrate-mandated category - a tenant may ADD categories on
top of the floor but may NEVER drop one (the regulated floor is non-negotiable). A body that omits a
floor category is refused (Conflict), never silently accepted (INV-9 fail-closed, no silent-default).
Input { key, body } (fields ending in ? are optional)
Call POST /v/policy.set with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool policy.set.
Validatepolicy.validate
| Property | Value |
|---|---|
| Capability | policy.read |
| Risk class | Read - autonomy L0 |
| Min. permission | Member |
| Availability | Admin only (CLI/HTTP; never advertised over MCP) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
| Render schema | Yes - drives an inline chat artefact and a GUI panel |
(or { kind, body }). READ/L0. Validate the authored-definition without persisting.
Input { id } (fields ending in ? are optional)
Call POST /v/policy.validate with a JSON body; returns JSON (or { error, code }). Not advertised over MCP (admin-only); reachable via the CLI and HTTP doors.