# Cruxible Docs — full documentation

> Cruxible is one shared truth for humans and AI agents: a typed state layer where every write is governed before it is accepted and every answer carries a receipt. Open source, Apache 2.0. Kit catalog: https://cruxible.ai/kits · Skills: https://cruxible.ai/skills


========================================================================
SOURCE: https://docs.cruxible.ai/
========================================================================

# Cruxible documentation

Cruxible is one shared truth for humans and AI agents: a typed
state layer where every write is governed before it's accepted and every
answer comes back with a receipt. These docs are built from the
repository's `docs/` directory at the documented commit — what you read
here is what the code ships.

## Start here

- **[Quickstart](quickstart.md)** — install, initialize a kit, run your
  first governed write and receipted query in a few minutes.
- **[Concepts](concepts.md)** — entities, relationships, proposals,
  reviews, receipts, workflows: the vocabulary everything else uses.
- **[Modeling state](modeling-state.md)** — how to turn a domain into a
  config: what to type, what to govern, what to leave out.
- **[For AI agents](for-ai-agents.md)** — the operating guide written
  for the agents themselves.

## Kits

Kits are installable domains — browse the full catalog with entity
types, queries, workflows, and guards at
[cruxible.ai/kits](https://cruxible.ai/kits), or read the
[walkthroughs](kit-walkthroughs.md) and the
[authoring guide](kit-authoring.md) here.

## Reference

[CLI](cli-reference.md) · [Config](config-reference.md) ·
[MCP tools](mcp-tools.md) · [Common providers](common-providers.md)


========================================================================
SOURCE: https://docs.cruxible.ai/quickstart/
========================================================================

# Quickstart

Get from install to a governed kit-backed state model in a few minutes.

The recommended `0.2` shape is a local Cruxible daemon, launched with
`cruxible server start`. The daemon
owns state; the CLI, MCP server, client SDK, GUI, and agent harness talk to it
through Cruxible surfaces.

This guide assumes a **fresh daemon** with no instance yet. If you already
have a daemon from the README's Get Started, it holds that instance — leave
it running and start a second daemon alongside it, on its own port and
state directory:

```bash
CRUXIBLE_SERVER_STATE_DIR="$HOME/.cruxible/server-quickstart" \
  cruxible server start --port 8101
```

Then use `http://127.0.0.1:8101` wherever the commands below say
`http://127.0.0.1:8100`. See
[Runtime Auth And Agent Roles](runtime-auth-and-agent-roles.md#one-daemon-one-instance-02)
for the model behind this.

## Prerequisites

- Python 3.11 or later
- [git](https://git-scm.com/) and [uv](https://docs.astral.sh/uv/)
- An MCP-capable AI agent if you want agent orchestration

## Install And Start The Daemon

```bash
pip install cruxible
```

The daemon ships in the default install, and the built-in kit aliases
(`init --kit agent-operation`, `--kit supply-chain-blast-radius`, ...)
resolve from digest-pinned release bundles — no checkout needed. To hack on
kits instead, clone the repo and run from the checkout
(`uv sync --all-extras`); a source tree's `kits/` always wins over the
published bundles.

Start the local daemon:

```bash
CRUXIBLE_SERVER_STATE_DIR="$HOME/.cruxible/server" cruxible server start
```

Without auth, this is sandbox mode: writes are attributed to a built-in
`operator` identity, visible as such in provenance. Turn auth on (below)
when agents join and identity should be credential-backed.

The daemon runs in the foreground — run the commands below from another activated
shell, or start it in the background.

Use a durable state directory such as `~/.cruxible/server` or
`/var/lib/cruxible`. Do not put long-lived daemon state under `/tmp`,
`/var/tmp`, or macOS private temp directories; Cruxible warns at startup when
the configured server state path resolves under a known volatile temp location.

The daemon binds locally by default. For a simple local hardening layer, start
it with:

```bash
CRUXIBLE_SERVER_AUTH=true \
CRUXIBLE_RUNTIME_BOOTSTRAP_SECRET=change-me-once \
CRUXIBLE_SERVER_STATE_DIR="$HOME/.cruxible/server" cruxible server start
```

Claim the bootstrap secret with `cruxible credential claim-bootstrap` to create
an admin runtime credential, then use that runtime credential as
`CRUXIBLE_SERVER_BEARER_TOKEN` for authenticated CLI or client calls. See
[Runtime Auth And Agent Roles](runtime-auth-and-agent-roles.md) for the full
bootstrap and agent-role flow.

Use `cruxible-client` in a separate agent environment when the agent should not
import the runtime directly:

```bash
pip install cruxible-client
```

## First Instance: The Supply-Chain Demo

Create an instance from two kits — the agent-operation base and the
supply-chain demo domain — and connect the CLI context so commands stop
needing per-call flags:

```bash
cruxible --server-url http://127.0.0.1:8100 init --kit agent-operation --kit supply-chain-blast-radius
cruxible context connect --server-url http://127.0.0.1:8100 --instance-id <instance-id>
```

Build the seeded world. Canonical workflows are preview-first: `run` executes
against a clone and returns an apply digest; `apply` re-verifies it against
the current config, lockfile, and head snapshot before committing:

```bash
cruxible run --workflow build_seed_state --save-preview seed.json
cruxible apply --preview-file seed.json
cruxible run --workflow ingest_incidents --save-preview incidents.json
cruxible apply --preview-file incidents.json
```

Incident-to-supplier impact is a governed relationship: nothing may write it
directly, not even a workflow. The proposal workflow bridges its output into
a candidate group, each member carrying the signals and evidence that
matched it:

```bash
cruxible propose --workflow propose_incident_impacts_supplier
cruxible group list --status pending_review
cruxible group get --group <group-id>
```

Review the thesis, member signals, and pending version, then resolve. The
`--expected-pending-version` flag pins your decision to the exact pending
state you reviewed — a group that changed underneath you refuses to resolve:

```bash
cruxible group resolve --group <group-id> --action approve \
  --rationale "Confirmed against supplier geography" \
  --expected-pending-version <pending-version>
```

Ask the questions those edges now answer:

```bash
cruxible query run open_incident_impacts --json
cruxible query run incident_impacted_suppliers --param incident_id=INC-TW-RAIL-2026-07 --json
```

Every query returns a receipt ID: the deterministic path from parameters to
traversed edges to rows. Render it with `cruxible explain --receipt
<receipt-id>`, or in MCP with `cruxible_receipt(instance_id, "<receipt-id>")`.

The approved supplier impacts unlock the next cascade:
`cruxible propose --workflow propose_incident_impacts_component` fills the
queue with component-level candidates, and once judged,
`single_source_components_for_incident` names exposed components with no
alternative supplier.

To consume a published reference state instead of a seeded demo (the KEV
vulnerability brain), see the [KEV Guide](kev-guide.md). To publish states
of your own, see [Publishing And Subscribing To States](publishing-states.md).

## Point An Agent At Cruxible

Bootstrap and canonical apply usually require an admin surface. Day-to-day
agent work should use `governed_write` unless the agent is explicitly acting as
an administrator.

**Claude Code / Cursor**:

```json
{
  "mcpServers": {
    "cruxible": {
      "command": "cruxible-mcp",
      "env": {
        "CRUXIBLE_MODE": "governed_write",
        "CRUXIBLE_SERVER_URL": "http://127.0.0.1:8100"
      }
    }
  }
}
```

**Codex**:

```toml
[mcp_servers.cruxible]
command = "cruxible-mcp"

[mcp_servers.cruxible.env]
CRUXIBLE_MODE = "governed_write"
CRUXIBLE_SERVER_URL = "http://127.0.0.1:8100"
```

If the agent should not have direct state access, keep
`CRUXIBLE_SERVER_STATE_DIR` outside the workspace and install only
`cruxible-client` in the agent environment. See
[Isolated Deployment](isolated-deployment.md) for stronger local separation.

## Build Your Own

Use kits for repeatable work:

- A **standalone kit** creates a state model by itself.
- An **overlay kit** extends a published reference state.
- Provider refs use `kit://...::callable`.
- Deterministic state loading should be workflow-based: parse source artifacts,
  shape/filter/join/dedupe rows, make graph objects, preview, then apply.
- Inference, matching, classification, and reviewable judgment should go
  through proposal workflows and candidate groups.

For hands-on kit creation, see [Kit Walkthroughs](kit-walkthroughs.md). For the
manifest and distribution rules, see [Kit Authoring And Distribution](kit-authoring.md).

## Next Steps

- [Concepts](concepts.md) - Architecture and vocabulary
- [KEV Guide](kev-guide.md) - Subscribe to the vulnerability reference and work the triage queue
- [Publishing And Subscribing To States](publishing-states.md) - Build, publish, and track reference states
- [Guide For AI Agents](for-ai-agents.md) - Agent operating recipes
- [Kit Walkthroughs](kit-walkthroughs.md) - Build and customize kits
- [Local State And Backups](local-state-and-backups.md) - SQLite and droplet operations
- [Config Reference](config-reference.md) - YAML schema
- [MCP Tools Reference](mcp-tools.md) - MCP surface
- [CLI Reference](cli-reference.md) - Terminal commands


========================================================================
SOURCE: https://docs.cruxible.ai/concepts/
========================================================================

# Concepts

Cruxible is a deterministic state runtime with receipts. It gives
agents and humans a shared, governed substrate for domain state that should
survive beyond one prompt, one chat, or one run.

## A State Model, Not Scratch Memory

A **state model** is the governed universe exposed to an agent: entity types,
relationships, workflows, named queries, review state, receipts, traces, and
outcomes.

Cruxible state is not private agent memory. Agent memory is prompt-local,
heuristic, and useful for continuity. Cruxible state is domain-centric,
explicit, reviewable, queryable, and intended to be operationally trusted.

Use Cruxible for:

- accepted facts and relationships
- governed judgments and review status
- deterministic workflow outputs
- reusable named queries and constraints
- receipts, traces, decision records, feedback, and outcomes

Use agent-local notes for temporary reasoning that should not become shared
truth.

## The Runtime Boundary

The recommended `0.2` deployment shape is a local Cruxible daemon, launched with
`cruxible server start`. The daemon owns state. Agents, CLI, client SDKs, and MCP
tools call into the daemon instead of editing graph state directly.

Permission modes are meaningful at that boundary:

| Mode | Purpose |
| --- | --- |
| `read_only` | Validate, inspect, query, and retrieve receipts |
| `governed_write` | Read-only plus receipt-persisting workflow runs, proposal workflows, and feedback |
| `graph_write` | Governed write plus raw graph mutation and group resolution |
| `admin` | Full lifecycle, including init, locks, canonical apply, ingest, and config mutation |

If an agent can import `cruxible_core`, read the daemon state directory, or
control the daemon runtime, these modes are advisory. For stronger local
separation, see [Isolated Deployment](isolated-deployment.md).

## Kits, Overlays, Clones, And Local State

A **kit** is a versioned bundle with `cruxible-kit.yaml`, `config.yaml`,
provider code, optional data, and a bundled `cruxible.lock.yaml`.

- A **standalone kit** can initialize a state model by itself.
- An **overlay kit** targets a published upstream state and adds local schema,
  workflows, data, and governed proposal surfaces.
- An **overlay** is a local instance tracking a published upstream state.
- A **clone** is a point-in-time copy from a snapshot.
- **Local state** is customer-owned seeded or runtime state in the overlay.

Example:

- `kev-reference` is a standalone kit that builds public Vendor, Product, and
  Vulnerability state from pinned KEV/NVD/EPSS artifacts.
- `kev-triage` is an overlay kit that targets `kev-reference` and adds customer
  assets, services, owners, controls, incidents, findings, remediation, and
  governed exposure workflows.

Kit distribution details live in [Kit Authoring And Distribution](kit-authoring.md).

## Config

The config is the schema and execution contract for a state model. It can
declare:

- entity types and typed properties
- relationships and edge properties
- named queries
- validation constraints
- artifacts and contracts
- providers and workflows
- governed relationship policies, feedback profiles, and outcome profiles

Use workflow-based loading for source artifacts. Providers parse external data,
dataflow steps shape it, and canonical apply steps write accepted graph state.

## Source Evidence

Source artifacts let agents attach governed proposal evidence to stable
document locations without putting the whole document into every proposal.
Register a local Markdown file with `cruxible source register`; Cruxible stores
the document hash, parser version, parsed chunks, and a source artifact ID.

Source-evidence locators use one of two shapes:

```yaml
source_evidence:
  - source_artifact_id: SRC-...
    chunk_id: CHK-...
```

or:

```yaml
source_evidence:
  - source_artifact_id: SRC-...
    heading_path: ["Compatibility Evidence"]
    block_selector: paragraph:1
```

Use `chunk_id` when copying a locator from the registration output. Use
`heading_path` plus `block_selector` when the source should remain readable in a
hand-authored proposal. `source_artifact_id` is always required, and one locator
form must be complete.

Retention controls whether Cruxible keeps only the parsed manifest or also a
deep copy of the source bytes:

- `manifest_only` stores chunk metadata, hashes, and the local path. Dereference
  rereads the local file and reports drift if the content no longer matches.
- `archive` stores the manifest plus source bytes in the runtime state DB.
  Dereference can use the archived copy even if the original local file moves or
  changes.

Direct relationship writes can attach `evidence_refs` or `source_evidence` so a
live edge has durable provenance. That is not the same as governed acceptance:
direct evidence-backed adds remain unreviewed relationship state. Use candidate
groups when a human or policy needs to approve the relationship judgment.

## Inline Queries

Named queries remain the canonical query contract for workflows, docs, and
repeatable operating procedures. Agents can also run bounded inline queries for
one-off filtering and candidate discovery. Inline query definitions use the same
shape as named queries plus a required `name`, persist receipts for auditability,
and are never written back into `config.named_queries`.

Promote an inline query into config once it becomes workflow-critical or
repeated enough that humans should review and name the surface.

## Workflows

Workflows are repeatable procedures declared in config.

Canonical workflows build or refresh accepted state. They preview first and
return an `apply_digest` and `head_snapshot_id`; applying the preview commits
only if those identities still match.

Proposal workflows produce candidate groups for governed review. They preserve
tri-state signals from relationship-local signal sources:

- `support`
- `unsure`
- `contradict`

Accepted proposal groups create reviewed edges. Rejected groups preserve the
decision without mutating the graph.

Direct writes remain available for explicit state updates. When a direct
relationship write overlaps a pending proposal member, Cruxible keeps the write
permissive but annotates the affected group's `analysis_state` with
`direct_write_conflicts` and a `direct_write_conflict_summary`. Reviewers can
use that metadata to see that live state changed after the group was proposed;
the group status is not changed automatically.

Use built-in step types for generic deterministic dataflow mechanics:
`shape_items`, `join_items`, `filter_items`, `dedupe_items`, graph object
construction, and canonical apply steps. Use providers for source adapters,
external services, model calls, and domain policy.

## The Entity Graph

Cruxible stores entities and relationships in a directed graph. Each node is an
entity with a type and typed properties. Each edge is a typed relationship with
declared properties plus system-managed review and provenance metadata.

Config-defined edge properties are domain data. Cruxible-managed relationship
metadata stores assertion review/lifecycle state and provenance separately from
domain properties; feedback and group resolution update that metadata rather
than writing domain fields.

Provenance uses a two-part vocabulary: `source` names the channel that wrote
the edge (`cli_batch_direct_write`, `http_api`, `mcp_add`, `group_resolve`, workflow apply
sources), and `source_ref` names the operation — a snake_case operation name
(`add_relationship`, `batch_direct_write`) or a structured ref for governed and
workflow writes (`group:<group_id>`, `workflow:<workflow>:<step>`) — never a
surface spelling, so command or tool renames cannot leak into stored provenance.
Provenance is historical record: values written by earlier versions are never
rewritten.

## Named Queries

Named queries are deterministic read surfaces over the graph. Each query has an
entry point, traversal steps, optional filters, and a return type. Every query
returns a receipt that explains the traversal path and evidence used.

Agents should use named queries as the stable read API for downstream work
instead of spelunking graph storage. Named queries package a stable primary
traversal and evidence path, and can attach bounded one-hop side context with
`include` when related facts such as owners, services, exceptions, controls, or
patch windows are part of the query contract. Use read tools for ad hoc context
that is not stable enough to belong in the named query surface.

## Receipts, Traces, And Decision Records

A **receipt** is a structured proof for a query, workflow run, canonical apply,
group resolution, feedback operation, or other state transition. It records the
operation and evidence chain.

An **execution trace** proves what provider ran: provider ref, version, runtime,
artifact hash, retained input/output payload evidence, status, error, and
timing. Full provider payload bodies are retained only when allowed by the
instance config's `runtime.trace_payloads` policy.

A **decision record** groups receipts, traces, and events around a higher-level
question so an agent or reviewer can reconstruct the decision history.

These are different proofs. Receipts explain how Cruxible decided or changed
state. Traces explain what executable provider produced evidence.

Entity change history is a receipt-derived read model. `entity history`
and the matching API/MCP surface show recorded property diffs from mutation
receipts. This is not a named query over live graph state: it only reports diffs
explicitly recorded on entity-write receipts, so receipts created before that
detail existed are treated as legacy gaps rather than inferred timeline events.

## Feedback And Outcomes

Feedback is edge-level review tied to a receipt:

| Action | Effect |
| --- | --- |
| `approve` | Mark the edge trusted by the reviewer source |
| `reject` | Exclude the edge from future query results |
| `correct` | Apply declared property corrections and approve |
| `flag` | Mark for review without changing behavior |

Outcomes record whether a result, proposal, or resolution was correct,
incorrect, partial, or unknown. Feedback and outcomes let Cruxible accumulate
accepted judgment state without relying on agent memory.

Query receipts with relationship or path results can be used as evidence for
edge feedback via `feedback from-query`: the user selects one relationship row
or one path segment, and Cruxible applies the normal feedback path to that
existing assertion. This is separate from group resolution. Use `group get` and
`group resolve` when the decision is about a candidate group thesis or member
set rather than one existing edge.

## Constraints And Evaluation

Constraints encode validation rules over relationships. `evaluate` checks
orphan entities, coverage gaps, constraint violations, governed support state,
candidate opportunities, and weakly reviewed co-members.

Evaluate findings are returned severity-first (`error`, then `warning`, then
`info`) while preserving original order within the same severity. CLI, MCP, and
HTTP callers can filter findings by severity and category before `max_findings`
is applied; summary counts remain full-state counts.

For governed relationships, `evaluate` distinguishes group-backed support from
direct evidence-backed support. Direct governed relationships with stored
evidence refs are not reported as missing group signal trails, while direct
governed relationships with no evidence refs remain weak and are flagged.
Free-text rationale alone is not evidence support.

Use repeated feedback and outcome patterns to decide when a domain rule should
become an explicit constraint or decision policy.

## Technology

Cruxible uses Pydantic for typed models, Polars for data operations, Click/Rich
for CLI, FastAPI for the daemon, and FastMCP for agent tools. Persistence is a
single per-instance SQLite `state.db` that holds graph state plus
receipts/traces/groups/feedback/outcomes/decisions/snapshots/source-artifacts; a
NetworkX `MultiDiGraph` is the in-memory representation of that graph state.


========================================================================
SOURCE: https://docs.cruxible.ai/modeling-state/
========================================================================

# Modeling State in Cruxible: Gates, Tags, and Flags

Cruxible holds durable, shared state that both people and AI agents work from.
When you decide what to put in that state, every piece of information you add
plays one of three roles. You don't label the role directly — it's defined by
**what Cruxible does with the information, and when.**

Getting these roles right is the heart of good modeling. It's also what keeps
the system trustworthy without making it rigid.

## The three moments

An agent working with Cruxible moves through three moments, and each role acts in
exactly one of them:

1. **Reading** — before it acts, an agent reads the current state into its
   working view. *What it sees here shapes what it does.*
2. **Saving** — when the agent makes a change, that change has to be saved back.
   *What's allowed to be saved is what's guaranteed.*
3. **Maintaining** — separately, on a regular cadence, you check the health of
   the state and tidy it up. *What gets surfaced here is what needs attention.*

The three roles map one-to-one onto these moments.

## The three roles

| Role | What it means | When it acts | How it's set up | Does it block? |
| --- | --- | --- | --- | --- |
| **Tag** | Helps you find, filter, and group things — changes what you *see* | Reading | named queries | No — purely informational |
| **Gate** | A rule Cruxible enforces — changes what's *allowed* | Saving | mutation guards | **Yes** — refuses the save if the rule isn't met |
| **Flag** | A health check that points out problems to fix later — changes what's *surfaced for cleanup* | Maintaining | quality checks, constraints (warning or error) | No — it reports, never blocks |

### Tags — what you see

A tag is a way of organizing. "Which area does this work belong to?" "Group these
by priority." Tags make state findable and let an agent pull the right things
into view. They never stop anyone from doing anything — they just shape what
shows up. *Most* of what you model is tags, and that's healthy.

### Gates — what's allowed

A gate is a rule the system actually enforces at the moment a change is saved.
For example: *a piece of work can't be marked "done" until an approved review is
attached to it.* If the rule isn't satisfied, the save is **refused** — not
warned about, refused. Gates are how Cruxible makes promises that hold no matter
who, or which agent, is doing the work. They are powerful, and they have a cost:
every gate is one more rule to satisfy, so it adds a little friction to every
change it touches.

### Flags — what needs attention

A flag is a health check that runs when you *review* the state, not when a change
is saved. For example: *every work item should be linked to a product area.* If
one isn't, the review surfaces it as something to clean up — but nothing was
blocked when that work item was created. Flags come in two strengths: a
**warning** (a gentle nudge) and an **error** (a louder "this really should be
fixed"). Either way, a flag points; it never stops.

## How to decide which one you need

When you add something to the model, ask one question: **what should happen if
this isn't right?**

- **Nothing should be allowed to proceed → make it a Gate.** Use a gate only when
  breaking the rule would cause real harm, because gates add friction to every
  change. Reserve them for the promises that genuinely matter.
- **It should be caught and fixed later, but not block work now → make it a
  Flag.** Good for hygiene and consistency you care about but don't need to
  enforce in the moment.
- **Nothing needs enforcing — you just want to find and group it → make it a
  Tag.** This is the default, and most things land here.

A useful rule of thumb: **a thing earns heavier treatment — its own type, its own
gates — only when you enforce a real rule over it.** If you only ever filter or
group by something, a simple tag is the right, low-cost choice. Promoting
everything to a gate makes the system rigid and tiring to use; leaving real
guarantees as mere tags makes it untrustworthy. The skill is matching the role to
the stakes.

## An example

Say you're tracking project work:

- *"Group work items by the product area they touch."* → **Tag.** It organizes;
  it enforces nothing.
- *"A work item can't be closed until an approved review is attached."* →
  **Gate.** Try to close one without a review and the save is refused.
- *"Every work item should be linked to a product area."* → **Flag.** If one
  isn't, your next health check lists it — but it was never blocked from being
  created.

Same three pieces of information, three different roles, because you want three
different things to happen when each one isn't right.

---

*Related, and a separate topic: this guide covers how state is **read and
enforced** once it's there. How state is allowed **in** — direct writes versus
governed proposals, evidence requirements, and review — is its own dimension,
covered in the resolution and governance docs.*


========================================================================
SOURCE: https://docs.cruxible.ai/for-ai-agents/
========================================================================

# Guide For AI Agents

This guide is the operating playbook for agents that use Cruxible. The
agent supplies interpretation and planning. Cruxible supplies deterministic
execution, governed state transitions, receipts, traces, review groups, and
query surfaces.

For `0.2`, prefer a local Cruxible daemon (`cruxible server start`). MCP should be
a structured adapter over the daemon, not the place where workflow policy lives.

## Runtime Boundary

Use this split when permissions matter:

- Daemon environment: `pip install "cruxible[mcp]"` (the daemon ships in the default install)
- Agent/client environment: `pip install cruxible-client`
- Agent access path: MCP or HTTP client
- State path: daemon-owned `CRUXIBLE_SERVER_STATE_DIR`, outside the agent
  workspace

Permission modes are enforced at the daemon boundary. If the agent can import
the `cruxible` runtime, read daemon state files, or control the daemon runtime, local
permission modes are advisory.

For auth bootstrapping, runtime credentials, and reviewer/writer identity
boundaries, see [Runtime Auth And Agent Roles](runtime-auth-and-agent-roles.md).

Recommended agent mode:

```bash
CRUXIBLE_MODE=governed_write
CRUXIBLE_SERVER_URL=http://127.0.0.1:8100
```

Use `admin` only for bootstrap, lock regeneration, canonical apply, and explicit
operator-approved maintenance.

## Two Governance Axes

Permission mode is one axis; direct-write policy is a second, **independent**
one. Do not confuse them — and note that the permission tiers are cumulative, so
a `graph_write` actor can both direct-add and propose. There is no permission
tier meaning "may propose but may not direct-add."

- **Permission mode** — `CRUXIBLE_MODE` (`read_only` ⊂ `governed_write` ⊂
  `graph_write` ⊂ `admin`), enforced at the daemon boundary.
- **Direct-write policy** — `refuse_direct_writes`. A type marked
  `write_policy: proposal_only` (per-type, via the instance
  `runtime.default_write_policy`, or via the daemon `CRUXIBLE_REFUSE_DIRECT_WRITES`
  env kill-switch) **refuses** direct graph-write verbs (`add_entity` /
  `add_relationship` / `batch_direct_write` / lifecycle write) with
  `DirectWriteRefusedError` (HTTP 403). This is a **hard** constraint independent
  of permission tier — even `admin` is refused.

  When you hit `DirectWriteRefusedError`, do not retry or escalate the
  permission mode. Route the write through the governed path instead:
  - relationships: `group propose` → resolve, or `add-relationship --pending` to
    stage an edge for review (pending writes are always allowed);
  - entities/relationships in bulk: a canonical `apply_entities` /
    `apply_relationships` workflow.

  See [Direct-Write Governance](config-reference.md#direct-write-governance-refuse_direct_writes)
  for the precedence table and the three knobs.

There is **no** `CRUXIBLE_AGENT_MODE` env var — if older docs or skills mention
it, they are stale. The real knobs are `CRUXIBLE_MODE` and the
`refuse_direct_writes` policy above.

## Core Responsibilities

The agent should:

- read the kit README, generated config views, and source artifacts
- edit config and provider code when authoring or customizing kits
- run validation, lock, workflow preview, proposal, and query tools
- explain receipts, traces, pending groups, and resolution choices to humans
- collect human decisions and apply them through Cruxible surfaces
- write prose properties (note bodies, descriptions, rationale) as Markdown —
  GFM renders in UIs, so use headings, lists, and tables for structure instead
  of ad-hoc separators; property descriptions in the schema say which fields
  render this way

The agent should not:

- write graph state by editing SQLite, snapshots, or graph files directly
- treat chat notes as accepted operational state
- bypass governed proposal workflows for relationship judgments
- use legacy `ingest` as the default path for new configs

## Standard Lifecycle

Use this lifecycle for existing kits:

```text
read kit docs
  -> validate config
  -> lock workflows after changes
  -> refresh canonical state by preview/apply
  -> run proposal workflows
  -> inspect pending groups
  -> resolve or defer proposals
  -> query accepted state
  -> inspect receipts/traces
```

Use this lifecycle for new or customized kits:

```text
inspect source data
  -> define config schema and contracts
  -> add providers only where source adaptation or domain policy is needed
  -> use common step types for generic row mechanics
  -> validate
  -> lock
  -> run workflow tests or focused previews
  -> regenerate generated docs/readme blocks
```

When authoring graph schemas, keep configs compact: entity and relationship
properties default to `type: string` and optional, `{}` is valid for optional
string fields, and `required: true` is the positive form for required non-ID
fields. Contract fields are different: they still need explicit `type`.
For operation-style kits, use the reusable axes in
[Kit Authoring And Distribution](kit-authoring.md#operation-style-relationship-axes)
before adding domain-specific variants: sequencing dependencies, impediment
blockers, composition roll-ups, lineage/follow-up, replacement, review gates,
and durable state notes should remain distinct relationships.

## Read-Visibility State (`--state`)

Reads are gated at one read-visibility state, set with the `--state` flag (CLI),
the `state` MCP/HTTP parameter, or the `relationship_state` query-config field
(default `live`). The SAME selector gates **entities** (by lifecycle) and
**relationships** (by review AND lifecycle), so one flag controls every surface:

| State | Entities (lifecycle) | Relationships (review + lifecycle) |
|-------|----------------------|------------------------------------|
| `live` (default) | Only `lifecycle.status == live` entities. | Active edges whose review state is neither `pending` nor `rejected`. |
| `accepted` | Resolves to `live` (entities have no review axis). | Active edges whose review status is `approved`. |
| `all` | Every entity, regardless of lifecycle. | Every stored edge, regardless of review/lifecycle. |
| `not-live` | Exactly the gated-out set: `retired`/`superseded` entities. | Edges hidden from live reads: review-`rejected` OR lifecycle closed/retracted/superseded. |
| `pending` | Resolves to `live`. | Active edges whose review status is `pending` (proposals awaiting review). |
| `reviewable` | Resolves to `live`. | `live` edges plus pending edges — triage/context in one evidence path. |

An explicit by-id `entity get` is **never gated**: it returns the entity and
shows its `lifecycle.status` even when hidden from live reads (recovery path).

`pending` and `reviewable` require `result_shape: path` or `relationship` and do
not allow `dedupe: entity` (they refine the relationship review axis). See
[Config Reference](config-reference.md) for the full query-field rules.

## Recipe: Validate And Lock After Edits

Use this after changing `config.yaml`, provider refs, provider code, artifacts,
contracts, workflows, or decision policies.

CLI:

```bash
cruxible --server-url http://127.0.0.1:8100 validate --config config.yaml
cruxible --server-url http://127.0.0.1:8100 --instance-id <instance-id> lock
```

MCP:

```text
cruxible_validate(config_path="config.yaml")
cruxible_lock_workflow(instance_id)
```

If locking fails, inspect the named provider, artifact, contract, or workflow
step in the error. Do not run workflows from an unlocked or stale config.

## Recipe: Refresh Canonical State

Canonical workflows mutate accepted state only after preview verification.

CLI:

```bash
cruxible --server-url http://127.0.0.1:8100 --instance-id <instance-id> run \
  --workflow build_local_state \
  --save-preview preview.json

cruxible --server-url http://127.0.0.1:8100 --instance-id <instance-id> apply \
  --preview-file preview.json
```

MCP:

```text
preview = cruxible_run_workflow(instance_id, "build_local_state")
cruxible_apply_workflow(
  instance_id,
  "build_local_state",
  expected_apply_digest=preview.apply_digest,
  expected_head_snapshot_id=preview.head_snapshot_id,
)
```

Before apply, summarize the changed entities/relationships, receipt ID, trace
IDs, and any warnings. If the source artifact changed unexpectedly, stop and ask
for operator confirmation.

## Recipe: Run A Proposal Workflow

Use proposal workflows for relationship state that needs review, evidence, or
classification. The workflow output is bridged into a candidate group.

CLI:

```bash
cruxible --server-url http://127.0.0.1:8100 --instance-id <instance-id> propose \
  --workflow propose_asset_exposure
```

MCP:

```text
cruxible_propose_workflow(instance_id, "propose_asset_exposure")
```

If no group is created, check the workflow output status first. Some proposal
workflows intentionally complete without creating a group when there are no
candidates; those return `status: no_candidates` and `group_created: false`.
Treat that as a terminal "nothing to review" outcome, not as a failed proposal.
For other no-group outcomes, inspect suppressed members and prerequisite state.
In KEV triage, for example, asset exposure proposals depend on accepted
asset-product mappings and public vulnerability-product reference state.

## Recipe: Inspect A Pending Group

Always inspect the group before resolving it.

CLI:

```bash
cruxible --server-url http://127.0.0.1:8100 --instance-id <instance-id> group list \
  --status pending_review
cruxible --server-url http://127.0.0.1:8100 --instance-id <instance-id> group get \
  --group <group-id>
```

MCP:

```text
cruxible_list_groups(instance_id, status="pending_review")
cruxible_get_group(instance_id, group_id)
```

Present:

- thesis and thesis facts
- relationship type and member count
- member-level signals: support, unsure, contradict
- review priority
- pending version
- source workflow receipt and trace IDs
- suppressed members or prior resolution history when present

## Recipe: Resolve Or Defer A Proposal

Resolve only from the pending version the reviewer saw.

CLI:

```bash
cruxible --server-url http://127.0.0.1:8100 --instance-id <instance-id> group resolve \
  --group <group-id> \
  --action approve \
  --expected-pending-version <pending-version> \
  --rationale "Reviewed evidence and accepted the proposal"
```

MCP:

```text
cruxible_resolve_group(
  instance_id,
  group_id,
  action="approve",
  expected_pending_version=pending_version,
  rationale="Reviewed evidence and accepted the proposal",
)
```

Use rejection when the proposal is wrong. Use no action when evidence is not
ready. Do not create accepted edges manually just to skip group review.

## Recipe: Debug Provider Failure

When a workflow fails:

1. Capture the workflow name, step ID, provider name, receipt ID if present,
   and trace IDs if present.
2. Inspect the provider declaration and contracts in the generated config view.
3. Check artifact names and hashes against the lock.
4. Re-run with the smallest input payload that reproduces the failure.
5. Fix the provider or config, then validate and lock again.

Useful commands:

```bash
cruxible config views --config config.yaml --runtime --view workflow-steps
cruxible config views --config config.yaml --runtime --view signal-policy-catalog
cruxible --server-url http://127.0.0.1:8100 --instance-id <instance-id> decision-record events \
  --trace <trace-id>
```

Receipts prove how a query or state transition was decided. Execution traces
prove what provider ran, with which provider version, artifact hash, inputs,
outputs, status, error, and timing.

## Recipe: Update Source Data Safely

When a source artifact changes:

1. Confirm the file path belongs to the kit or local workspace.
2. Validate the config.
3. Regenerate the workflow lock. Use `--force` only when intentionally accepting
   new live canonical artifact hashes.
4. Run the canonical workflow in preview mode.
5. Summarize the changed examples and receipt/trace evidence.
6. Apply only after the operator accepts the preview.
7. Run dependent proposal workflows and inspect new or refreshed groups.

Do not edit SQLite or graph snapshots to "fix" source state.

## Recipe: Regenerate Kit Docs

Generated kit README blocks are code-owned. After changing a kit config, refresh
the marked blocks:

```bash
cruxible config views --config kits/kev-triage/config.yaml --runtime \
  --update-readme kits/kev-triage/README.md
```

The generated docs are grounding material for the agent and reviewer. They are
not a substitute for MCP/CLI review actions.

## Modeling Guidance

Use Cruxible for shared operational truth:

- accepted facts and relationships
- governed judgments and review history
- deterministic workflow outputs
- receipts, traces, decision records, feedback, and outcomes

Keep temporary reasoning in the agent. Commit only state that future agents,
humans, or software should rely on.

Use providers for source adapters, external services, model calls, and
domain-specific policy. Use built-in step types for generic deterministic
mechanics such as shaping rows, joining item sets, filtering, deduping, building
graph objects, and applying canonical state.

## Handoff Checklist

Before handing work back to a human or another agent, report:

- active instance ID and kit
- current config/lock status
- workflows run and whether they previewed, applied, or proposed
- receipt IDs and trace IDs for meaningful operations
- pending groups requiring review
- accepted state changed
- rejected/deferred proposals and rationale
- next safe command to run


========================================================================
SOURCE: https://docs.cruxible.ai/kit-walkthroughs/
========================================================================

# Kit Walkthroughs

This page shows the two common authoring paths for `0.2`: create a standalone
kit from scratch, and customize an overlay kit on top of an existing reference
state.

For manifest rules and distribution behavior, see
[Kit Authoring And Distribution](kit-authoring.md).

## Walkthrough 1: Create A Standalone Kit

Use a standalone kit when the domain can initialize a state model by itself.

### 1. Create The Kit Directory

```text
my-risk-kit/
  cruxible-kit.yaml
  config.yaml
  cruxible.lock.yaml
  providers/
    risk_seed.py
  data/
    assets.csv
```

`cruxible.lock.yaml` is generated by `cruxible lock` (run in Step 4), not
hand-authored.

Minimal manifest:

```yaml
schema_version: cruxible.kit.v1
kit_id: my-risk-kit
version: 0.2.0
role: standalone
entry_config: config.yaml
provider_paths:
  - providers
copy_paths:
  - data
  - README.md
requires_extras: []
```

### 2. Define A Small State Model

Start with one entity, one relationship, and one query. Add contracts,
artifacts, providers, and workflows only after the graph shape is clear.
If the kit models work, reviews, operations, remediation, or investigations,
start from the operation-style relationship axes in
[Kit Authoring And Distribution](kit-authoring.md#operation-style-relationship-axes)
so dependencies, blockers, roll-ups, lineage, replacement, and review gates do
not collapse into one ambiguous relationship.

```yaml
name: my_risk_kit
version: "0.2.0"

entity_types:
  Asset:
    properties:
      asset_id: {primary_key: true}
      hostname: {}
  Owner:
    properties:
      owner_id: {primary_key: true}
      name: {}

relationships:
  - name: asset_owned_by
    from: Asset
    to: Owner
    cardinality: many_to_one

named_queries:
  asset_owner:
    mode: traversal
    entry_point: Asset
    returns: Owner
    result_shape: path
    traversal:
      - relationship: asset_owned_by
        direction: outgoing
```

For operational queries, keep the traversal focused on the primary question and
use `include` for bounded side context that should travel with each result row,
such as owners, services, controls, exceptions, or patch windows. Use
`required: false` only for optional follow-on traversal where a matched neighbor
should become the next `$result`. Use `mode: collection` only for entryless
entity or relationship collection queries; traversal queries must declare
`mode: traversal` and an `entry_point`.

### 3. Add A Provider Only For Source Adaptation

Provider refs should be kit-relative:

```yaml
providers:
  normalize_assets:
    kind: function
    contract_in: RawAssetRows
    contract_out: AssetOwnerRows
    ref: kit://providers/risk_seed.py::normalize_assets
    version: "1.0.0"
    deterministic: true
    runtime: python
```

Use built-in workflow step types for generic mechanics such as row shaping,
joins, filtering, dedupe, entity creation, relationship creation, and canonical
apply. Keep providers focused on messy source formats or domain policy.

### 4. Validate, Initialize, Lock, And Run

First define a canonical build workflow in the kit's `config.yaml`, then run it
by name (`<your_build_workflow>` below stands in for that name):

```bash
cruxible validate --config my-risk-kit/config.yaml
cruxible init --kit file://./my-risk-kit
cruxible lock
cruxible run --workflow <your_build_workflow> --save-preview preview.json
cruxible apply --preview-file preview.json
cruxible query run asset_owner --param asset_id=ASSET-1
```

Inspect the returned receipt:

```bash
cruxible explain --receipt <receipt-id> --format markdown
```

### 5. Refresh Generated Docs

```bash
cruxible config views --config my-risk-kit/config.yaml --runtime \
  --update-readme my-risk-kit/README.md
```

The generated blocks are structural truth. Keep authored prose outside
`CRUXIBLE:BEGIN` / `CRUXIBLE:END` markers.

## Walkthrough 2: Customize An Overlay Kit

Use an overlay kit when you want local state on top of a published reference
state. KEV triage is the canonical example.

### 1. Create The Overlay

```bash
cruxible state create-overlay \
  --state-ref kev-reference \
  --kit kev-triage \
  --root-dir "$PWD/kev-triage-workspace"
```

The resulting instance tracks the KEV reference state and materializes local
triage config, providers, source data, skills, and lock state.

If you are testing from a source checkout without published OCI reference
states, publish a local `kev-reference` release to `file://...` first and use
`--transport-ref file://...` instead of `--state-ref kev-reference`.

### 2. Add Local State

In your customized kit copy, add customer-owned source data under `source_data/`
or `data/`, then model it in the overlay config:

```yaml
entity_types:
  MaintenanceTeam:
    properties:
      team_id: {primary_key: true}
      name: {}

relationships:
  - name: asset_supported_by_team
    from: Asset
    to: MaintenanceTeam
    cardinality: many_to_one
```

### 3. Add A Proposal Workflow For Judgment

If the relationship is inferred, matched, classified, or reviewable, do not
write it directly as accepted state. Add a provider or workflow step that emits
proposal members, then use a `propose_relationship_group` workflow step so the
result enters pending review.

For KEV-style workflows, the path is:

```text
source artifact
  -> parse/shape/filter/join/dedupe
  -> domain evidence provider if needed
  -> make_candidates
  -> map_signals with support/unsure/contradict evidence
  -> propose_relationship_group
  -> candidate group
  -> human or agent-assisted resolution
```

### 4. Lock, Preview, Propose, And Resolve

```bash
cruxible lock
cruxible run --workflow build_local_state --save-preview preview.json
cruxible apply --preview-file preview.json
cruxible propose --workflow propose_asset_products
cruxible group list --status pending_review
cruxible group get --group <group-id>
cruxible group resolve \
  --group <group-id> \
  --action approve \
  --expected-pending-version <pending-version> \
  --rationale "Reviewed evidence and accepted the proposal"
```

### 5. Query The Accepted Result

```bash
cruxible query run vulnerability_asset_context \
  --param cve_id=CVE-2020-1472
```

Use the receipt to explain how public vulnerability-product state connected to
local asset-product state.

### 6. Ship The Customized Kit

For local testing, deploy the overlay from a `file://` ref:

```bash
cruxible state create-overlay \
  --state-ref kev-reference \
  --kit file://./my-custom-kit
```

For distribution, publish the versioned bundle as an OCI kit ref and update the
catalog or deployment configuration that resolves the kit alias. Before
publishing, refresh the bundled `cruxible.lock.yaml` directly from the kit root:

```bash
cruxible lock --kit-dir path/to/kit
```


========================================================================
SOURCE: https://docs.cruxible.ai/kit-authoring/
========================================================================

# Kit Authoring And Distribution

A Cruxible kit is a versioned bundle with a `cruxible-kit.yaml` manifest,
an entry config, provider code, optional data, and a bundled
`cruxible.lock.yaml`.

For runnable examples, see [Kit Walkthroughs](kit-walkthroughs.md).

## Operation-Style Relationship Axes

Kits that model work, reviews, investigations, remediation, agent operations,
or project execution should not collapse every relationship into one generic
`related_to` or `blocks` edge. Start with explicit axes so readiness, critical
path, review, and roll-up queries can keep their meanings separate.

Use these defaults when the kit has work-like entities:

| Axis | Relationship Shape | Meaning |
| --- | --- | --- |
| Sequencing | `work_item_depends_on_work_item` | Direction: from depends on to. The target must land, be decided, or stabilize first. |
| Impediment | `risk_blocks_work_item`, `open_question_blocks_work_item` | A durable unresolved threat or uncertainty blocks or materially delays work. |
| Resolution | `work_item_mitigates_risk`, `work_item_answers_open_question`, `decision_answers_open_question` | Work or a decision resolves the impediment without pretending the impediment was sequencing. |
| Composition | `work_item_part_of_work_item` | Child work is part of a larger scope. This is roll-up, not order. |
| Lineage | `work_item_spawned_from_work_item` | A follow-up came out of earlier work. This is provenance, not a prerequisite. |
| Replacement | `work_item_supersedes_work_item`, `decision_supersedes_decision` | The source replaces the target. This is not general lineage. |
| Review gate | `review_request_for_work_item` plus a mutation guard | A work item cannot move to a guarded lifecycle value until the review request is approved. |
| Interpretation history | `StateNote` plus typed note-about relationships | Durable corrections, field notes, implementation notes, and review notes without bloating current entity fields. |

Do not make these hidden Cruxible defaults. Put the relationships in the kit
config with domain-specific names when needed, because the ontology is the
contract agents and reviewers inspect.

Use direct relationships for deterministic placement and roll-up. Use governed
proposal policies for interpretive claims such as dependencies, blockers,
mitigations, answers, supersession, and decision impact. Documents, chats,
review reports, and source sections should be evidence references for those
claims, not modeled entities.

Minimal operation-axis scaffold:

```yaml
enums:
  work_status:
    values: [planned, active, blocked, watching, closed, deferred, superseded]
  work_priority:
    values: [critical, high, medium, low]
  note_kind:
    values: [correction, field_note, rationale_update, implementation_note, review_note]

entity_types:
  WorkItem:
    properties:
      work_item_id: {primary_key: true}
      title: {required: true}
      status: {enum_ref: work_status, required: true}
      priority: {enum_ref: work_priority}
      summary: {}
  Risk:
    properties:
      risk_id: {primary_key: true}
      title: {required: true}
      status: {}
  OpenQuestion:
    properties:
      question_id: {primary_key: true}
      title: {required: true}
      status: {}
  Decision:
    properties:
      decision_id: {primary_key: true}
      title: {required: true}
      status: {}
  ReviewRequest:
    properties:
      review_request_id: {primary_key: true}
      title: {required: true}
      status: {required: true}
      summary: {}
      review_notes: {}
  StateNote:
    properties:
      state_note_id: {primary_key: true}
      note_kind: {enum_ref: note_kind, required: true}
      noted_at: {required: true}
      body: {required: true}

relationships:
  - name: work_item_depends_on_work_item
    description: "Sequencing: from depends on to."
    from: WorkItem
    to: WorkItem
    proposal_policy:
      signals:
        source_evidence: {role: required, always_review_on_unsure: true}
        maintainer_judgment: {role: advisory, always_review_on_unsure: true}

  - name: risk_blocks_work_item
    description: "Impediment: a risk blocks or materially delays work."
    from: Risk
    to: WorkItem
    proposal_policy:
      signals:
        source_evidence: {role: required, always_review_on_unsure: true}
        maintainer_judgment: {role: advisory, always_review_on_unsure: true}

  - name: open_question_blocks_work_item
    description: "Impediment: an unresolved question blocks or delays work."
    from: OpenQuestion
    to: WorkItem
    proposal_policy:
      signals:
        source_evidence: {role: required, always_review_on_unsure: true}
        maintainer_judgment: {role: advisory, always_review_on_unsure: true}

  - name: work_item_part_of_work_item
    description: "Composition and roll-up, not sequencing."
    from: WorkItem
    to: WorkItem

  - name: work_item_spawned_from_work_item
    description: "Lineage and follow-up provenance, not sequencing."
    from: WorkItem
    to: WorkItem

  - name: work_item_supersedes_work_item
    description: "Replacement: source supersedes target."
    from: WorkItem
    to: WorkItem
    proposal_policy:
      signals:
        source_evidence: {role: required, always_review_on_unsure: true}
        maintainer_judgment: {role: advisory, always_review_on_unsure: true}

  - name: review_request_for_work_item
    from: ReviewRequest
    to: WorkItem

  - name: state_note_about_work_item
    from: StateNote
    to: WorkItem
```

Add named queries around the axes rather than broad text search: active work
queue, blocked work with blocker context, work-item change context, roll-up
context, lineage context, pending reviews, and recent state notes. Add quality
checks or mutation guards only where they protect real operating discipline,
such as one composition parent, review requests attached to work, and closing
work only after an approved review.

Minimal manifest:

```yaml
schema_version: cruxible.kit.v1
kit_id: kev-triage
version: 0.2.0
role: overlay
target_state: kev-reference
entry_config: config.yaml
provider_paths:
  - providers
copy_paths:
  - data
  - skills
  - README.md
requires_extras: []
```

Rules:

- `role` is `standalone` or `overlay`.
- `role: overlay` requires `target_state`.
- `role: standalone` must not set `target_state`.
- 0.2 supports one `entry_config` per kit.
- `requires_extras` is metadata only. Cruxible does not install kit
  dependencies automatically.

Provider refs use `kit://`:

```yaml
ref: kit://providers/reference.py::normalize_public_kev_reference
```

`kit://` paths are relative to the materialized kit root. Absolute paths,
`..`, symlinks, and paths outside declared `provider_paths` are rejected.
Python providers run in the current Cruxible Python environment and may import
stdlib, `cruxible_core`, installed Cruxible dependencies or extras, and files
under declared provider paths.

Bundle behavior:

- The bundle digest covers every non-junk regular file in sorted POSIX-relative
  order, including path and bytes.
- Junk such as `__pycache__/`, `*.pyc`, `.DS_Store`, `.ruff_cache/`, and
  `.pytest_cache/` is ignored.
- Symlinks are rejected.
- Bundles are cached under `CRUXIBLE_KIT_CACHE_DIR` or
  `${XDG_CACHE_HOME:-~/.cache}/cruxible/kits`.
- Cache installs are locked and atomic by bundle digest.
- Materialization copies the cached kit into the instance root.
- Kit bundles carry `cruxible.lock.yaml` at the kit root as a portable bundle
  artifact.
- Initialized Cruxible instances execute workflows from
  `.cruxible/cruxible.lock.yaml`; kit-backed initialization imports the bundled
  lock there when it matches the active config, or regenerates the instance-local
  lock when an active runtime config has been composed from the bundle.
- Runtime workflow execution does not fall back to arbitrary config-root locks.
- Consumers should not silently regenerate published bundled locks. Rebuild the
  kit lock before publishing or distributing a changed kit.

## Standing Views And Decision Surfaces

A domain kit's read surface has three tiers, and shipping all three is what
makes a kit feel alive rather than inert:

1. **At least one standing "requiring action" view** — a parameterless
   `mode: collection` query over the state that needs attention now (open
   incident impacts, postures requiring action, upcoming deadlines). This is
   the queue surface: it renders in UIs with no input and answers "what needs
   a decision today?" Order it by the fields a triager would sort by, using
   `ordered` enums.
2. **Parameterized decision queries** — traversal queries anchored on the
   entity a decision is about (`entry_point`), supplying the evidence a
   decision report pulls: scope, exposure, alternatives, obligations.
3. **Proposal workflows** — where interpretive claims enter as governed
   candidates. The queue view tells you which entity needs judgment; the
   decision queries justify it; the proposal/resolution flow records it.

Reference kits (published state that consumers subscribe to) ship **no**
standing views: the actionable views belong to the overlay that composes local
judgment over the reference, not to the reference itself.

Keep each query in its layer: a query touching only operating-layer types
(work items, reviews, actors) belongs in the operating-state kit; queries
joining domain types to operating types must live in the overlay, since only
the overlay can see both vocabularies.

## Seed Data Is A Worked Example

A kit's `data/` ships as a pinned workflow artifact (lock-covered,
digest-attested), and for demo kits it is fictional: a worked example so the
kit runs end to end in minutes, not a starter dataset to build on. Adopting a
kit for real use means **replacing the seed with your own exports** — the
ingest workflows are the interface, the seed files are just one example input:

- Point the ingest/refresh workflows at your own exports (or swap the pinned
  artifact for your data directory and re-lock).
- Do not run seed workflows (`build_seed_state` and friends) against an
  instance that already carries real state; they exist to demonstrate the
  pipeline shape.
- Real reference data (a public catalog snapshot) is the exception: that is
  kit content, not an example, and updates with the kit version.

## Published Kit Bundles

Installed distributions resolve built-in aliases such as `kev-reference` from
digest-pinned release bundles, so `cruxible init --kit <name>` works without a
source checkout. `scripts/build_kit_bundles.py` builds a deterministic
`dist/kits/<id>-<version>.tar.gz` for every `kits/<id>/` (byte-identical across
runs: sorted members, zeroed timestamps and ownership, normalized permissions)
and regenerates `src/cruxible_core/kit_distribution/manifest.json`, which ships
in the wheel and pins each bundle by tarball sha256 and by the extracted
directory digest — the same `compute_path_sha256` discipline kit locks use.
The tarballs are uploaded as release assets on the `v<version>` tag; the
regenerated manifest is committed before tagging, and CI asserts it matches a
fresh digest of every `kits/<id>` directory (like the kit lock freshness
check).

Alias resolution order:

1. Local source-checkout `kits/` directories always win (development).
2. Published release bundles from the packaged manifest: the tarball sha256 is
   verified before extraction, members are safe-extracted (files and
   directories only, no absolute paths, `..`, or links), the extracted tree
   must match the pinned directory digest, and the verified kit is installed
   atomically into the kit cache keyed by that digest. Any mismatch deletes
   the artifacts and refuses; a cache hit skips the network entirely.
3. Shipped `oci://` refs cover aliases absent from the packaged manifest.

An overlay kit's `target_state` base resolves through the same order, so a
fetched overlay composes over its fetched base with no local kits present.
`CRUXIBLE_KIT_MANIFEST_URL_BASE` overrides the manifest's release `base_url`
(pre-publish smoke runs against a local file server); asset names and digests
still come from the packaged manifest.

Refresh a bundled lock directly from the kit root before publishing:

```bash
cruxible lock --kit-dir path/to/kit
```

The lock pins the kit's own config layer only — its providers and artifacts,
with URIs kept relative to the kit root — so an overlay kit locks without its
`target_state` base present. Base-layer content is pinned by the base kit's
own lock. CI asserts every bundled kit's committed lock matches a fresh regen,
so run this after any config, provider, or seed-data change.

Vocabulary:

- Use **overlay** for a local instance tracking a published upstream state.
- Use **clone** for a point-in-time state copy from a snapshot.
- Use **local** for customer-owned seeded or runtime state.
- Do not use clone for kit distribution. Use pull, cache, materialize, or
  install.


========================================================================
SOURCE: https://docs.cruxible.ai/kev-guide/
========================================================================

# KEV: Vulnerability Triage On Hard State

The KEV pair turns CISA's Known Exploited Vulnerabilities catalog into a
governed triage brain over **your** assets. Two kits, two roles:

- **kev-reference** is the published layer: KEV + NVD CPE + EPSS, built into
  a typed graph (vulnerabilities, products, vendors, affected-by edges) and
  released as a versioned state you subscribe to. You do not build it.
- **kev-triage** is your layer: assets, services, owners, controls,
  exceptions, patch windows — and the governed judgments that connect them
  to the reference (which assets run which products, which exposures are
  material).

The output is a standing work queue —
`asset_vulnerability_postures_requiring_action` — where every row is a
judgment-admitted exposure with evidence, ordered by priority and KEV due
date, and every answer carries a receipt.

## 1. Subscribe to the reference

Prerequisite: the [oras](https://oras.land/docs/installation) CLI for the
OCI transport (`brew install oras` on macOS).

```bash
pip install cruxible
CRUXIBLE_SERVER_STATE_DIR="$HOME/.cruxible/kev" cruxible server start   # shell 1

# shell 2 — creates a local overlay instance tracking the published reference
cruxible --server-url http://127.0.0.1:8100 state create-overlay \
  --state-ref kev-reference \
  --kit kev-triage \
  --root-dir "$PWD/kev-workspace"
cruxible context connect --server-url http://127.0.0.1:8100 --instance-id <overlay-instance-id>
```

## 2. Load your inventory

The kit ships seed data as a worked example; real use replaces it with your
asset, service, owner, and control exports. Point an agent at the
[`kev-start`](https://github.com/cruxible-ai/cruxible/tree/main/kits/kev-triage/skills/kev-start)
skill to adapt the kit to your data, then build:

```bash
cruxible run --workflow build_local_state --save-preview kev-local.json
cruxible apply --preview-file kev-local.json
```

## 3. Judge the mappings

Asset-to-product mappings are governed: the workflow can only propose, and
the proposals wait for review with their matching signals and evidence:

```bash
cruxible propose --workflow propose_asset_products
cruxible group list --status pending_review
cruxible group get --group <group-id>
cruxible group resolve --group <group-id> --action approve \
  --rationale "Verified against the CMDB export" \
  --expected-pending-version <n>
```

With mappings approved, propose and review exposure postures the same way
(`propose_asset_exposure`), then work the queue:

```bash
cruxible query run asset_vulnerability_postures_requiring_action --json
```

## 4. Stay current

The reference republishes as new releases land. Updates are preview-first,
like everything else:

```bash
cruxible state status
cruxible state pull-preview
cruxible state pull-apply
```

Your local judgments, exceptions, and controls persist across pulls; only
the upstream reference layer moves.

## Offline demo without a published reference

To exercise both layers from the pinned files bundled with the kits, compose
the reference and triage kits into one disposable instance, then build each
layer in order:

```bash
cruxible init --kit kev-reference --kit kev-triage

cruxible run --workflow build_public_kev_reference \
  --save-preview kev-reference.json
cruxible apply --preview-file kev-reference.json

cruxible run --workflow build_local_state --save-preview kev-local.json
cruxible apply --preview-file kev-local.json

cruxible propose --workflow propose_asset_products
# Review and approve the pending asset_runs_product group, then:
cruxible propose --workflow propose_asset_exposure
# Review and approve the pending asset_vulnerability_posture group, then:
cruxible query run asset_vulnerability_postures_requiring_action --json
```

This route proves the ontology and governed triage flow without requiring an
OCI registry or network fetch. It is a demo/build path, not a subscription:
use the overlay flow above when the reference should advance independently.

## Building or publishing your own reference

`init --kit kev-reference` plus the `build_public_kev_reference` workflow
builds the reference locally from the kit's pinned data snapshot — for
offline use, demos, or publishing your own release. The
[Quickstart](quickstart.md) walks that path, including publishing to a
`file://` transport.


========================================================================
SOURCE: https://docs.cruxible.ai/cli-reference/
========================================================================

# CLI Reference

This is the full searchable reference for the `cruxible` command line. Walkthroughs and agent recipes live elsewhere; this file is intentionally detailed so an agent can look up command names, flags, side effects, and failure modes without shelling out to `--help` first.

## Runtime Model

- Use `--server-url` or `--server-socket` for daemon transport, and
  `--instance-id` or CLI context for daemon-backed instances.
- The CLI context commands remember transport and the active instance for shell
  users; MCP does not use CLI context.
- Commands that mutate governed state are blocked locally when the command requires a daemon surface.
- `init --kit` accepts standalone kits. Overlay kits are created with `state create-overlay --kit`.
- `run` rejects proposal workflows; use `propose` for workflows that return governed relationship proposals.
- `explain` and `export edges` are direct-local file/rendering utilities. Use receipts and list/query tools for daemon/MCP flows.

## Command Word Order

Operations on a specific named resource instance are noun-first:
`cruxible entity add`, `cruxible entity update`, `cruxible relationship add`,
and `cruxible relationship update`. Cross-resource collection and inspection
commands remain top-level where the target is not a single CRUD-style resource,
for example `list`, `inspect`, and `sample`.

There is no hard-delete/remove command. Cruxible state is receipted and
append-oriented; retire entities by setting ontology lifecycle fields such as
`status=closed` or `status=superseded`, and reject relationship assertions with
the feedback/review surfaces.

## Direct Write Shorthand

**Usage:**

```bash
cruxible entity add ENTITY_TYPE ENTITY_ID [--set FIELD=VALUE] [--set-json FIELD=JSON] [--dry-run] [--json]
cruxible entity update ENTITY_TYPE ENTITY_ID --set FIELD=VALUE [--set-json FIELD=JSON] [--dry-run] [--json]
cruxible relationship add REL_TYPE FROM_TYPE FROM_ID TO_TYPE TO_ID [--set FIELD=VALUE] [--set-json FIELD=JSON] [--dry-run] [--json]
cruxible relationship update REL_TYPE FROM_TYPE FROM_ID TO_TYPE TO_ID [--set FIELD=VALUE] [--set-json FIELD=JSON] [--dry-run] [--json]
```

**Purpose:** Ergonomic CLI shorthand for creating and updating entities and
relationships without hand-authoring a direct-write payload file.

**Field Assignment:**
- `--set FIELD=VALUE` stores `VALUE` as a string. Values such as `NO`, `no`,
  `1.20`, `0755`, and `null` are not coerced.
- `--set-json FIELD=JSON` stores an explicitly typed JSON value.
- `--props JSON` remains accepted on noun write commands for compatibility.
- Duplicate fields, blank field names, and malformed assignments are rejected.

**Relationship Evidence Options:**
- `--evidence-ref JSON`
- `--source-evidence JSON`
- `--evidence-rationale TEXT`

**Output And Side Effects:**
- Uses the same guarded direct-write path as `batch-direct-write`, with the same
  dry-run behavior, receipts, mutation guards, and group-interaction notices.
- `entity add` and `relationship add` fail if the target already exists.
- `entity update` and `relationship update` fail if the target does not exist.
- `--json` emits the same `BatchDirectWriteResult` envelope as
  `batch-direct-write`.
- Actor attribution remains credential-derived when daemon auth is enabled.

**Examples:**

```bash
cruxible entity add WorkItem wi-example --set title="Add write verbs" --set status=planned
cruxible entity update WorkItem wi-example --set status=closed
cruxible relationship add work_item_part_of_work_item WorkItem wi-child WorkItem wi-parent --set composition_basis="Same ergonomics slice"
cruxible relationship update work_item_part_of_work_item WorkItem wi-child WorkItem wi-parent --set composition_basis="Refined after review"
```

## cruxible config

**Usage:** `cruxible config [OPTIONS]`

**Purpose:** Edit, validate, and render the active config.

**Subcommands:**

- `cruxible config reload` - Validate the active config or repoint the instance to a new config file.
- `cruxible config views` - Render canonical Mermaid/Markdown views for a Cruxible config.
- `cruxible config expand` - Expand a compact authoring config to the explicit engine config.
- `cruxible config add-constraint` - Add a constraint rule to the config.
- `cruxible config add-decision-policy` - Add a decision policy to the config.

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, or rendered file output depending on the subcommand.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible config expand

**Usage:** `cruxible config expand [OPTIONS]`

**Purpose:** Expand a compact authoring config to the explicit engine config. The compact form is the single source of truth; the loader expands it on load, so the explicit output is for inspection/review (e.g. diffing the resolved graph), not a committed artifact.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--in` | yes | `` | file | Path to the compact authoring YAML to expand. |
| `--out` | no | stdout | file | Write the expanded explicit YAML here. |
| `--validate` / `--no-validate` | no | `validate` | flag | Validate the expanded config as a CoreConfig before writing. |

**Output And Side Effects:**
- Pure transform: reads the compact YAML and writes the expanded explicit YAML to `--out` (or stdout). No instance, daemon, or graph state is touched.

**Common Errors:**
- Malformed compact grammar (raises a compact-expansion error naming the construct).
- The expanded config fails CoreConfig validation (unless `--no-validate`).

## cruxible config add-constraint

**Usage:** `cruxible config add-constraint [OPTIONS]`

**Purpose:** Add a constraint rule to the config.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--name` | yes | `Sentinel.UNSET` | text | Constraint name. |
| `--rule` | yes | `Sentinel.UNSET` | text | Constraint rule expression. |
| `--severity` | no | `warning` | choice | Severity level (default: warning). |
| `--description` | no | `` | text | Optional description. |

**Output And Side Effects:**
- Config mutation. Adds a constraint rule to the active config and reports the constraint name plus any validation warnings. Server-mode only.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations.
- Local mutation disabled when not server-backed; run against a server-mode instance.

## cruxible config add-decision-policy

**Usage:** `cruxible config add-decision-policy [OPTIONS]`

**Purpose:** Add a decision policy to the config.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--name` | yes | `Sentinel.UNSET` | text | Decision policy name. |
| `--applies-to` | yes | `Sentinel.UNSET` | choice | Policy application surface. |
| `--relationship` | yes | `Sentinel.UNSET` | text | Relationship type. |
| `--effect` | yes | `Sentinel.UNSET` | choice | Policy effect. |
| `--query-name` | no | `` | text | Named query for query policies. |
| `--workflow-name` | no | `` | text | Workflow name for workflow policies. |
| `--match` | no | `{}` | text | JSON object for exact-match selectors. |
| `--description` | no | `` | text | Optional description. |
| `--rationale` | no | `` | text | Policy rationale. |
| `--expires-at` | no | `` | text | Optional ISO timestamp/date. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible entity

**Usage:** `cruxible entity [OPTIONS]`

**Purpose:** Entity reads and writes.

**Subcommands:**

- `cruxible entity add` - Create one entity.
- `cruxible entity get` - Look up a specific entity by type and ID.
- `cruxible entity history` - Inspect receipt-derived entity change history for one entity type or entity.
- `cruxible entity inspect` - Inspect an entity and its immediate neighbors.
- `cruxible entity update` - Update one existing entity.

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible entity add

**Usage:** `cruxible entity add [OPTIONS] [ENTITY_TYPE] [ENTITY_ID]`

**Purpose:** Create one entity using JSON properties or FIELD=VALUE assignments.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `ENTITY_TYPE` | no | `` | argument | Entity type. |
| `ENTITY_ID` | no | `` | argument | Entity ID. |
| `--type` | no | `` | text | Entity type, for compatibility with older noun command usage. |
| `--id` | no | `` | text | Entity ID, for compatibility with older noun command usage. |
| `--props` | no | `` | text | JSON object of properties. |
| `--set` | no | `` | text | String property assignment FIELD=VALUE. Repeat for multiple properties. |
| `--set-json` | no | `` | text | Typed JSON property assignment FIELD=JSON. Repeat for multiple properties. |
| `--dry-run` | no | `False` | boolean | Validate without mutating graph state. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Uses the same guarded direct-write path as `batch-direct-write`.
- Fails if the entity already exists.
- JSON output is a `BatchDirectWriteResult`.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible entity update

**Usage:** `cruxible entity update [OPTIONS] [ENTITY_TYPE] [ENTITY_ID]`

**Purpose:** Update one existing entity using JSON properties or FIELD=VALUE assignments.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `ENTITY_TYPE` | no | `` | argument | Entity type. |
| `ENTITY_ID` | no | `` | argument | Entity ID. |
| `--type` | no | `` | text | Entity type, for compatibility with older noun command usage. |
| `--id` | no | `` | text | Entity ID, for compatibility with older noun command usage. |
| `--props` | no | `` | text | JSON object of properties. |
| `--set` | no | `` | text | String property assignment FIELD=VALUE. Repeat for multiple properties. |
| `--set-json` | no | `` | text | Typed JSON property assignment FIELD=JSON. Repeat for multiple properties. |
| `--dry-run` | no | `False` | boolean | Validate without mutating graph state. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Uses the same guarded direct-write path as `batch-direct-write`.
- Fails if the entity does not already exist.
- Requires at least one `--props`, `--set`, or `--set-json` property update.
- JSON output is a `BatchDirectWriteResult`.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible entity get

**Usage:** `cruxible entity get [OPTIONS]`

**Purpose:** Look up a specific entity by type and ID.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--type` | yes | `Sentinel.UNSET` | text | Entity type. |
| `--id` | yes | `Sentinel.UNSET` | text | Entity ID. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.
- A by-id get is **not** subject to live-only lifecycle gating: it returns the
  entity even when its `lifecycle.status` is `retired`/`superseded`,
  and surfaces that status (in the `Lifecycle` table column and in the JSON
  `metadata.lifecycle.status`). This is the recovery/inspection path for an
  entity hidden from live `query`/`list` reads.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible entity history

**Usage:** `cruxible entity history [OPTIONS]`

**Purpose:** Inspect receipt-derived entity change history for one entity type or entity.

Noun-first read of an entity's history.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--type` | yes | `Sentinel.UNSET` | text | Entity type. |
| `--id` | no | `` | text | Optional entity ID. |
| `--limit` | no | `50` | integer range |  |
| `--offset` | no | `0` | integer range |  |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only. Shows property diffs recorded on mutation receipts for entity writes.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible entity inspect

**Usage:** `cruxible entity inspect [OPTIONS]`

**Purpose:** Inspect an entity and its immediate neighbors.

Noun-first read of a single entity with its neighbors.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--type` | yes | `Sentinel.UNSET` | text | Entity type. |
| `--id` | yes | `Sentinel.UNSET` | text | Entity ID. |
| `--direction` | no | `both` | choice | Neighbor traversal direction. |
| `--relationship` | no | `` | text | Optional relationship filter. |
| `--limit` | no | `` | integer range | Max neighbors to show. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible relationship

**Usage:** `cruxible relationship [OPTIONS]`

**Purpose:** Relationship reads and writes.

**Subcommands:**

- `cruxible relationship add` - Create one relationship.
- `cruxible relationship get` - Look up a specific relationship by its endpoints and type.
- `cruxible relationship lineage` - Inspect a relationship's stored provenance lineage.
- `cruxible relationship update` - Update one existing relationship.

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible relationship add

**Usage:** `cruxible relationship add [OPTIONS] [RELATIONSHIP_TYPE] [FROM_TYPE] [FROM_ID] [TO_TYPE] [TO_ID]`

**Purpose:** Create one relationship using JSON properties, FIELD=VALUE assignments, or evidence flags.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `RELATIONSHIP_TYPE` | no | `` | argument | Relationship type. |
| `FROM_TYPE` | no | `` | argument | Source entity type. |
| `FROM_ID` | no | `` | argument | Source entity ID. |
| `TO_TYPE` | no | `` | argument | Target entity type. |
| `TO_ID` | no | `` | argument | Target entity ID. |
| `--from-type` | no | `` | text | Source entity type, for compatibility with older noun command usage. |
| `--from-id` | no | `` | text | Source entity ID, for compatibility with older noun command usage. |
| `--relationship` | no | `` | text | Relationship type, for compatibility with older noun command usage. |
| `--to-type` | no | `` | text | Target entity type, for compatibility with older noun command usage. |
| `--to-id` | no | `` | text | Target entity ID, for compatibility with older noun command usage. |
| `--props` | no | `` | text | JSON object of edge properties. |
| `--set` | no | `` | text | String relationship property assignment FIELD=VALUE. |
| `--set-json` | no | `` | text | Typed JSON relationship property assignment FIELD=JSON. |
| `--evidence-ref` | no | `` | text | JSON evidence ref object. Repeat to attach multiple refs. |
| `--source-evidence` | no | `` | text | JSON source-evidence locator. Repeat to attach multiple locators. |
| `--evidence-rationale` | no | `` | text | Optional rationale for the attached relationship evidence. |
| `--dry-run` | no | `False` | boolean | Validate without mutating graph state. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Uses the same guarded direct-write path as `batch-direct-write`.
- Fails if the relationship tuple already exists.
- Evidence refs and source-evidence locators are persisted as relationship evidence metadata.
  Direct adds are not group-reviewed accepted relationships; use `group propose`
  and `group resolve --action approve` when review/acceptance state matters.
- JSON output is a `BatchDirectWriteResult`.

**Example:**

```bash
cruxible relationship add \
  roadmap_item_depends_on_roadmap_item \
  RoadmapItem ri-compact-workflow-trace-payloads \
  RoadmapItem ri-transactional-sqlite-state \
  --source-evidence '{"source_artifact_id":"SRC-...","chunk_id":"CHK-..."}' \
  --evidence-rationale "Extracted from the P0 section."
```

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible relationship update

**Usage:** `cruxible relationship update [OPTIONS] [RELATIONSHIP_TYPE] [FROM_TYPE] [FROM_ID] [TO_TYPE] [TO_ID]`

**Purpose:** Update one existing relationship using JSON properties, FIELD=VALUE assignments, or evidence flags.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `RELATIONSHIP_TYPE` | no | `` | argument | Relationship type. |
| `FROM_TYPE` | no | `` | argument | Source entity type. |
| `FROM_ID` | no | `` | argument | Source entity ID. |
| `TO_TYPE` | no | `` | argument | Target entity type. |
| `TO_ID` | no | `` | argument | Target entity ID. |
| `--from-type` | no | `` | text | Source entity type, for compatibility with older noun command usage. |
| `--from-id` | no | `` | text | Source entity ID, for compatibility with older noun command usage. |
| `--relationship` | no | `` | text | Relationship type, for compatibility with older noun command usage. |
| `--to-type` | no | `` | text | Target entity type, for compatibility with older noun command usage. |
| `--to-id` | no | `` | text | Target entity ID, for compatibility with older noun command usage. |
| `--props` | no | `` | text | JSON object of edge properties. |
| `--set` | no | `` | text | String relationship property assignment FIELD=VALUE. |
| `--set-json` | no | `` | text | Typed JSON relationship property assignment FIELD=JSON. |
| `--evidence-ref` | no | `` | text | JSON evidence ref object. Repeat to attach multiple refs. |
| `--source-evidence` | no | `` | text | JSON source-evidence locator. Repeat to attach multiple locators. |
| `--evidence-rationale` | no | `` | text | Optional rationale for the attached relationship evidence. |
| `--dry-run` | no | `False` | boolean | Validate without mutating graph state. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Uses the same guarded direct-write path as `batch-direct-write`.
- Fails if the relationship tuple does not already exist.
- Requires at least one property or evidence update.
- JSON output is a `BatchDirectWriteResult`.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible relationship get

**Usage:** `cruxible relationship get [OPTIONS]`

**Purpose:** Look up a specific relationship by its endpoints and type.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--from-type` | yes | `Sentinel.UNSET` | text | Source entity type. |
| `--from-id` | yes | `Sentinel.UNSET` | text | Source entity ID. |
| `--relationship` | yes | `Sentinel.UNSET` | text | Relationship type. |
| `--to-type` | yes | `Sentinel.UNSET` | text | Target entity type. |
| `--to-id` | yes | `Sentinel.UNSET` | text | Target entity ID. |
| `--edge-key` | no | `` | integer | Edge key (multi-edge disambiguation). |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible relationship lineage

**Usage:** `cruxible relationship lineage [OPTIONS]`

**Purpose:** Inspect a relationship's stored provenance lineage.

Noun-first read of a relationship's lineage.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--from-type` | yes | `Sentinel.UNSET` | text | Source entity type. |
| `--from-id` | yes | `Sentinel.UNSET` | text | Source entity ID. |
| `--relationship` | yes | `Sentinel.UNSET` | text | Relationship type. |
| `--to-type` | yes | `Sentinel.UNSET` | text | Target entity type. |
| `--to-id` | yes | `Sentinel.UNSET` | text | Target entity ID. |
| `--edge-key` | no | `` | integer | Edge key (multi-edge disambiguation). |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only. Returns the matching relationship, `_provenance`, linked proposal group/resolution when provenance points to a group, source workflow receipt ID, source trace IDs, and warnings for missing or non-group provenance.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for read operations.
- Ambiguous relationship tuple without `--edge-key`.

## cruxible batch-direct-write

**Usage:** `cruxible batch-direct-write --payload-file PATH [--dry-run] [--json]`

**Purpose:** Validate or apply one structured direct graph write payload containing
entities, relationships, and optional payload-local shared evidence.

**Payload Shape:**

```yaml
entities:
  - entity_type: RoadmapItem
    entity_id: ri-example
    properties:
      roadmap_item_id: ri-example
      title: Example roadmap item
relationships:
  - from_type: WorkItem
    from_id: wi-example
    relationship_type: work_item_implements_roadmap_item
    to_type: RoadmapItem
    to_id: ri-example
    shared_evidence_keys: [source_section]
    evidence_rationale: Extracted from the referenced section.
shared_evidence:
  source_section:
    source_evidence:
      - source_artifact_id: SRC-...
        chunk_id: mdchunk_...
```

**Output And Side Effects:**
- `--dry-run` validates entity properties, relationship endpoints/properties,
  evidence locators, duplicate IDs, and missing shared evidence keys without
  mutating graph state.
- Apply mode writes all valid entities and relationships through one mutation
  receipt and returns a compact summary. Direct writes are live/unreviewed
  state, not group-reviewed accepted state.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Payload file is not a JSON/YAML object.
- Unknown shared evidence key or invalid source-evidence locator.

## cruxible feedback analyze

**Usage:** `cruxible feedback analyze [OPTIONS]`

**Purpose:** Analyze structured feedback and print remediation suggestions.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--relationship` | yes | `Sentinel.UNSET` | text | Relationship type. |
| `--limit` | no | `200` | integer range | Rows to inspect. |
| `--min-support` | no | `5` | integer range | Minimum support for suggestions. |
| `--decision-surface-type` | no | `` | choice | Optional decision surface type filter. |
| `--decision-surface-name` | no | `` | text | Optional decision surface name filter. |
| `--pair` | no | `Sentinel.UNSET` | text | Explicit mismatch pair as FROM_PROP=TO_PROP. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible outcome analyze

**Usage:** `cruxible outcome analyze [OPTIONS]`

**Purpose:** Analyze structured outcomes and print trust/debugging suggestions.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--anchor-type` | yes | `Sentinel.UNSET` | choice | Outcome anchor type to analyze. |
| `--relationship` | no | `` | text | Relationship type. |
| `--workflow` | no | `` | text | Workflow name filter. |
| `--query` | no | `` | text | Query name filter. |
| `--surface-type` | no | `` | choice | Explicit surface type filter. |
| `--surface-name` | no | `` | text | Explicit surface name filter. |
| `--limit` | no | `200` | integer range | Rows to inspect. |
| `--min-support` | no | `5` | integer range | Minimum support for suggestions. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible apply

**Usage:** `cruxible apply [OPTIONS]`

**Purpose:** Commit a previously previewed canonical workflow after verifying the preview identity.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--workflow` | no | `` | text | Workflow name from config. |
| `--input` | no | `` | text | Inline JSON or YAML workflow input. |
| `--input-file` | no | `` | path | JSON or YAML file providing workflow input. |
| `--apply-digest` | no | `` | text | Preview apply digest from workflow run. |
| `--head-snapshot` | no | `` | text | Expected head snapshot ID from workflow preview. |
| `--preview-file` | no | `` | file | Read preview state from a file saved by run --save-preview. |
| `--from-last-preview` | no | `False` | boolean | Apply the latest stored preview for the workflow. Mutually exclusive with `--preview-file`/`--apply-digest`. |
| `--decision-record` | no | `` | text | Decision record ID for audit logging. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible clone

**Usage:** `cruxible clone [OPTIONS]`

**Purpose:** Create a new local instance from a chosen snapshot.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--snapshot` | yes | `Sentinel.UNSET` | text | Snapshot ID to clone from. |
| `--root-dir` | yes | `Sentinel.UNSET` | text | Root directory for the new cloned instance. |
| `--activate / --no-activate` | no | `True` | boolean | Make the cloned server instance the active CLI context instance. |

**Output And Side Effects:**
- On an auth-enabled daemon the clone is minted its own one-time ADMIN
  runtime credential (label `clone-admin`) and the plaintext token is
  printed exactly once — save it immediately; only its hash is stored and
  it is never shown again. Auth-disabled daemons mint nothing.
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible config views

**Usage:** `cruxible config views [OPTIONS]`

**Purpose:** Render canonical Mermaid/Markdown views for a Cruxible config.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--config` | yes | `Sentinel.UNSET` | file | Path to config YAML file. |
| `--view` | no | `all` | choice | View to render. 'all' emits the standard config-drafting diagrams. |
| `--bare` | no | `False` | boolean | Emit the raw selected view without Markdown wrapping. |
| `--update-readme` | no | `Sentinel.UNSET` | file | Replace matching CRUXIBLE marker blocks in a README. |
| `--runtime` | no | `False` | boolean | Compose extends overlays as a runtime composed view. This includes inherited ontology/query surfaces but strips upstream build-only workflows. |

**Output And Side Effects:**
- Produces documentation or file output; graph state is not changed.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible context

**Usage:** `cruxible context [OPTIONS]`

**Purpose:** Manage remembered governed server and instance context.

**Subcommands:**

- `cruxible context clear` - Clear remembered governed CLI context.
- `cruxible context connect` - Persist the current governed transport and optional instance.
- `cruxible context show` - Show the remembered CLI context.
- `cruxible context use` - Set the active governed instance ID.

**Output And Side Effects:**
- Mutates only the remembered CLI context file, not graph state.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible context clear

**Usage:** `cruxible context clear [OPTIONS]`

**Purpose:** Clear remembered governed CLI context.

**Output And Side Effects:**
- Mutates only the remembered CLI context file, not graph state.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible context connect

**Usage:** `cruxible context connect [OPTIONS]`

**Purpose:** Persist the current governed transport and optional instance.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--server-url` | no | `` | text | Remote Cruxible server base URL. |
| `--server-socket` | no | `` | text | Local Cruxible server Unix socket path. |
| `--instance-id` | no | `` | text | Opaque server-mode instance ID. Defaults to remembered CLI context. |

**Output And Side Effects:**
- Mutates only the remembered CLI context file, not graph state.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible context show

**Usage:** `cruxible context show [OPTIONS]`

**Purpose:** Show the remembered CLI context.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Mutates only the remembered CLI context file, not graph state.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible context use

**Usage:** `cruxible context use [OPTIONS]`

**Purpose:** Set the active governed instance ID.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `instance_id` | yes | `Sentinel.UNSET` | text | Positional argument. |

**Output And Side Effects:**
- Mutates only the remembered CLI context file, not graph state.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible credential

**Usage:** `cruxible credential [OPTIONS]`

**Purpose:** Manage runtime bearer credentials for a governed server instance.

**Subcommands:**

- `cruxible credential claim-bootstrap` - Exchange the one-time bootstrap secret for the first ADMIN runtime token.
- `cruxible credential mint` - Mint a new runtime bearer credential.
- `cruxible credential list` - List runtime bearer credentials for the active instance.
- `cruxible credential revoke` - Revoke a runtime bearer credential.
- `cruxible credential rotate` - Rotate a runtime bearer credential and print the replacement token once.
- `cruxible credential recover-admin` - Recover an ADMIN token by local filesystem ownership of server state.

**Output And Side Effects:**
- Server-mode only. Uses the remembered CLI context or `--instance-id` for the target instance.
- Credential creation and rotation print plaintext tokens once. Save them immediately; later list calls show metadata only.

**Common Errors:**
- Missing server transport or missing/stale `--instance-id`.
- Permission mode too low; runtime credential management requires ADMIN.
- The bootstrap secret was already claimed or does not match the server secret.

## cruxible credential claim-bootstrap

**Usage:** `cruxible credential claim-bootstrap [OPTIONS]`

**Purpose:** Exchange the one-time runtime bootstrap secret for the initial ADMIN runtime token.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--secret-file` | no | `CRUXIBLE_RUNTIME_BOOTSTRAP_SECRET` | file | File containing the runtime bootstrap secret. |

**Output And Side Effects:**
- Calls the existing runtime bootstrap claim route for the active instance.
- Prints the ADMIN token once with the save hint
  `Save it now, for example: export CRUXIBLE_SERVER_BEARER_TOKEN=<token>`.

**Common Errors:**
- Provide `--secret-file` or set `CRUXIBLE_RUNTIME_BOOTSTRAP_SECRET`.
- The bootstrap secret is invalid or has already been claimed.

## cruxible credential mint

**Usage:** `cruxible credential mint [OPTIONS]`

**Purpose:** Mint a new runtime bearer credential for the active server instance.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--label` | yes | `Sentinel.UNSET` | text | Human-readable credential label. |
| `--mode` | yes | `Sentinel.UNSET` | choice | Permission mode: `admin`, `graph_write`, `governed_write`, or `read_only`. |

**Output And Side Effects:**
- Creates an instance-scoped runtime credential and prints its plaintext token once.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low; credential minting requires ADMIN.

## cruxible credential list

**Usage:** `cruxible credential list [OPTIONS]`

**Purpose:** List runtime bearer credential metadata for the active server instance.

**Output And Side Effects:**
- Read-only metadata output. Plaintext tokens are never returned by list.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low; credential listing requires ADMIN.

## cruxible credential revoke

**Usage:** `cruxible credential revoke [OPTIONS] CREDENTIAL_ID`

**Purpose:** Revoke a runtime bearer credential.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `CREDENTIAL_ID` | yes | `Sentinel.UNSET` | argument | Runtime credential ID to revoke. |

**Output And Side Effects:**
- Revokes the credential for the active instance and prints updated metadata.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Credential ID not found for the active instance.

## cruxible credential rotate

**Usage:** `cruxible credential rotate [OPTIONS] CREDENTIAL_ID`

**Purpose:** Rotate a runtime bearer credential and print the replacement token once.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `CREDENTIAL_ID` | yes | `Sentinel.UNSET` | argument | Runtime credential ID to rotate. |

**Output And Side Effects:**
- Revokes the old credential, creates a replacement with the same permission mode, and prints the new plaintext token once.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Credential ID not found for the active instance.

## cruxible credential recover-admin

**Usage:** `cruxible credential recover-admin [OPTIONS]`

**Purpose:** Recover an ADMIN runtime token by local filesystem ownership of server state. Local-only: refuses to run with any resolved server connection, never contacts a daemon, and treats invoking-uid ownership of `--state-dir` and its `runtime_credentials.db` as the recovery authority.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--state-dir` | yes | `Sentinel.UNSET` | path | Server state directory containing `runtime_credentials.db`. Stop the daemon first; the lock check only refuses a writer caught mid-transaction and does not detect an idle running daemon. |
| `--instance-id` | no | `None` | text | Target instance ID when the credentials DB contains multiple instances. |
| `--label` | no | `recovered-admin` | text | Human-readable label for the recovered ADMIN credential. |
| `--json` | no | `False` | flag | Output as JSON. |

**Output And Side Effects:**
- Mints one new ADMIN credential (`created_by: local_recovery`), records a recovery audit event in the same transaction, and prints the plaintext token once. Existing credentials are not revoked automatically.

**Common Errors:**
- Refused in server mode: unset `--server-url`/`--server-socket` (and their environment variables) and run locally.
- State dir or credentials DB not owned by the invoking uid.
- Credentials DB locked by an active writer, or no ADMIN credential record exists for the target instance.

## cruxible decision-record

**Usage:** `cruxible decision-record [OPTIONS]`

**Purpose:** Manage decision records and their logged receipts.

**Subcommands:**

- `cruxible decision-record abandon` - Abandon an open decision record.
- `cruxible decision-record create` - Create an open decision record.
- `cruxible decision-record events` - List decision-record events.
- `cruxible decision-record finalize` - Finalize an open decision record.
- `cruxible decision-record get` - Fetch one decision record.
- `cruxible decision-record list` - List decision records.

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible decision-record abandon

**Usage:** `cruxible decision-record abandon [OPTIONS]`

**Purpose:** Abandon an open decision record.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--id` | yes | `Sentinel.UNSET` | text | Decision record ID. |
| `--reason` | no | `` | text | Reason for abandoning the record. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible decision-record create

**Usage:** `cruxible decision-record create [OPTIONS]`

**Purpose:** Create an open decision record.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--question` | yes | `Sentinel.UNSET` | text | Question or decision being evaluated. |
| `--subject-type` | no | `` | text | Optional subject type. |
| `--subject-id` | no | `` | text | Optional subject identifier. |
| `--opened-by` | no | `human` | choice |  |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible decision-record events

**Usage:** `cruxible decision-record events [OPTIONS]`

**Purpose:** List decision-record events.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--id` | no | `` | text | Decision record ID. |
| `--receipt` | no | `` | text | Receipt ID. |
| `--trace` | no | `` | text | Trace ID. |
| `--status` | no | `` | choice |  |
| `--limit` | no | `100` | integer range |  |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible decision-record finalize

**Usage:** `cruxible decision-record finalize [OPTIONS]`

**Purpose:** Finalize an open decision record.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--id` | yes | `Sentinel.UNSET` | text | Decision record ID. |
| `--final-decision` | yes | `Sentinel.UNSET` | text | Final decision text. |
| `--decision-class` | yes | `Sentinel.UNSET` | choice |  |
| `--rationale` | no | `` | text | Decision rationale. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible decision-record get

**Usage:** `cruxible decision-record get [OPTIONS]`

**Purpose:** Fetch one decision record.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--id` | yes | `Sentinel.UNSET` | text | Decision record ID. |
| `--events, --no-events` | no | `True` | boolean |  |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible decision-record list

**Usage:** `cruxible decision-record list [OPTIONS]`

**Purpose:** List decision records.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--status` | no | `` | choice |  |
| `--subject-type` | no | `` | text |  |
| `--subject-id` | no | `` | text |  |
| `--decision-class` | no | `` | choice |  |
| `--limit` | no | `100` | integer range |  |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible evaluate

**Usage:** `cruxible evaluate [OPTIONS]`

**Purpose:** Assess graph quality: orphans, gaps, violations, unreviewed co-members.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--limit` | no | `100` | integer | Max findings to show. |
| `--severity` | no |  | choice: `error`, `warning`, `info` | Only return findings at this severity. Repeatable. |
| `--category` | no |  | choice: `orphan_entity`, `coverage_gap`, `constraint_violation`, `governed_support_relationship`, `unreviewed_co_member`, `quality_check_failed` | Only return findings in this category. Repeatable. |
| `--json` | no | `False` | boolean | Output as JSON. |

Agent triage example: `cruxible evaluate --severity error --limit 1 --json`
checks whether any error-level finding exists without fetching lower-severity
noise. Summaries still reflect the full graph evaluation, not just the filtered
findings.

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible explain

**Usage:** `cruxible explain [OPTIONS]`

**Purpose:** Explain a query result using its receipt.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--receipt` | yes | `Sentinel.UNSET` | text | Receipt ID to explain. |
| `--format` | no | `markdown` | choice | Output format. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible export

**Usage:** `cruxible export [OPTIONS]`

**Purpose:** Export graph data to files.

**Subcommands:**

- `cruxible export edges` - Export all edges to CSV.

**Output And Side Effects:**
- Produces documentation or file output; graph state is not changed.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible export edges

**Usage:** `cruxible export edges [OPTIONS]`

**Purpose:** Export all edges to CSV.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--output, -o` | yes | `Sentinel.UNSET` | file | Output file path. |
| `--relationship` | no | `` | text | Filter by relationship type. |
| `--exclude-rejected` | no | `False` | boolean | Exclude edges with rejected review_status. |

**Output And Side Effects:**
- Produces documentation or file output; graph state is not changed.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible feedback

**Usage:** `cruxible feedback [OPTIONS]`

**Purpose:** Record, batch, analyze, and inspect edge feedback.

**Subcommands:**

- `cruxible feedback record` - Submit feedback on a specific edge by explicit relationship coordinates.
- `cruxible feedback from-query` - Submit edge feedback by selecting relationship evidence from a query receipt.
- `cruxible feedback batch` - Submit a batch of edge feedback with one top-level receipt.
- `cruxible feedback profile` - Display the configured feedback profile for one relationship type.
- `cruxible feedback analyze` - Analyze structured feedback and print remediation suggestions.

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible feedback record

**Usage:** `cruxible feedback record [OPTIONS]`

**Purpose:** Submit feedback on a specific edge by explicit relationship coordinates.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--receipt` | yes | `Sentinel.UNSET` | text | Receipt ID. |
| `--action` | yes | `Sentinel.UNSET` | choice | Feedback action. |
| `--from-type` | yes | `Sentinel.UNSET` | text | Source entity type. |
| `--from-id` | yes | `Sentinel.UNSET` | text | Source entity ID. |
| `--relationship` | yes | `Sentinel.UNSET` | text | Relationship type. |
| `--to-type` | yes | `Sentinel.UNSET` | text | Target entity type. |
| `--to-id` | yes | `Sentinel.UNSET` | text | Target entity ID. |
| `--edge-key` | no | `` | integer | Edge key (multi-edge disambiguation). |
| `--reason` | no | `` | text | Reason for feedback. |
| `--reason-code` | no | `` | text | Structured feedback reason code. |
| `--scope-hints` | no | `` | text | JSON object of structured scope hints. |
| `--corrections` | no | `` | text | JSON object of edge property corrections (for action=correct). |
| `--source` | no | `human` | choice | Who produced this feedback (default: human). |
| `--group-override` | no | `False` | boolean | Mark relationship assertion metadata as a group override (edge must exist). |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible feedback from-query

**Usage:** `cruxible feedback from-query [OPTIONS]`

**Purpose:** Submit edge-level feedback by selecting one relationship row or path segment from a query receipt.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--receipt` | yes | `Sentinel.UNSET` | text | Query receipt ID. |
| `--result-index` | yes | `Sentinel.UNSET` | integer | Zero-based index of the query result row to adjudicate. |
| `--action` | yes | `Sentinel.UNSET` | choice | Feedback action. |
| `--source` | no | `human` | choice | Who produced this feedback (default: human). |
| `--reason` | no | `` | text | Reason for feedback. |
| `--reason-code` | no | `` | text | Structured feedback reason code. |
| `--scope-hints` | no | `` | text | JSON object of structured scope hints. |
| `--corrections` | no | `` | text | JSON object of edge property corrections (for action=correct). |
| `--group-override` | no | `False` | boolean | Mark selected edge assertion metadata as a group override (edge must exist). |
| `--path-index` | no | `` | integer | Zero-based path segment index for path query rows. |
| `--path-alias` | no | `` | text | Traversal alias for the selected path segment. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Creates normal feedback records and feedback receipts through the existing edge-feedback path.
- Adjudicates one existing relationship assertion from query evidence. It does not resolve candidate groups.
- Use `cruxible group get --group <group_id>` and `cruxible group resolve --group <group_id> --action approve|reject --expected-pending-version <n>` when the decision is about a group thesis or member set.

**Common Errors:**
- The receipt is missing, is not a query receipt, or the result index is out of range.
- Entity-shaped query rows do not contain relationship evidence.
- Multi-hop path rows require exactly one of `--path-index` or `--path-alias`.
- The selected path alias is missing or duplicated, or the selected edge is no longer in the graph.

## cruxible feedback batch

**Usage:** `cruxible feedback batch [OPTIONS]`

**Purpose:** Submit a batch of edge feedback with one top-level receipt.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--items-file` | no | `` | path | JSON or YAML file with batch feedback items. |
| `--items` | no | `` | text | Inline JSON array of feedback items. |
| `--source` | no | `human` | choice | Who produced this feedback batch (default: human). |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible feedback profile

**Usage:** `cruxible feedback profile [OPTIONS]`

**Purpose:** Display the configured feedback profile for one relationship type.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--relationship` | yes | `Sentinel.UNSET` | text | Relationship type. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible group

**Usage:** `cruxible group [OPTIONS]`

**Purpose:** Manage candidate groups for batch edge review.

**Subcommands:**

- `cruxible group get` - Get details of a candidate group.
- `cruxible group list` - List candidate groups.
- `cruxible group propose` - Propose a candidate group of edges for batch review.
- `cruxible group resolutions` - List group resolutions.
- `cruxible group resolve` - Resolve a candidate group (approve or reject).
- `cruxible group status` - Show lifecycle status for a signature bucket.
- `cruxible group trust` - Update trust status on a resolution.

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible group get

**Usage:** `cruxible group get [OPTIONS]`

**Purpose:** Get details of a candidate group.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--group` | yes | `Sentinel.UNSET` | text | Group ID. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible group list

**Usage:** `cruxible group list [OPTIONS]`

**Purpose:** List candidate groups.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--relationship` | no | `` | text | Filter by relationship type. |
| `--status` | no | `` | choice | Filter by status. |
| `--limit` | no | `50` | integer | Max groups to show. |
| `--offset` | no | `0` | integer | Rows to skip. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible group propose

**Usage:** `cruxible group propose [OPTIONS]`

**Purpose:** Propose a candidate group of edges for batch review.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--relationship` | yes | `Sentinel.UNSET` | text | Relationship type for the group. |
| `--members-file` | no | `` | path | JSON file with member list. |
| `--members` | no | `` | text | Inline JSON array of members. |
| `--thesis` | no | `` | text | Human-readable thesis text. |
| `--thesis-facts` | no | `` | text | Optional JSON object used as agent-supplied direct proposal scope. |
| `--analysis-state` | no | `` | text | JSON object of opaque analysis state. |
| `--signal-source` | no | `()` | text | Deprecated and hidden; signal sources are derived from member signals. Optional, repeatable. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible group resolutions

**Usage:** `cruxible group resolutions [OPTIONS]`

**Purpose:** List group resolutions.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--relationship` | no | `` | text | Filter by relationship type. |
| `--action` | no | `` | choice | Filter by action. |
| `--limit` | no | `50` | integer | Max resolutions to show. |
| `--offset` | no | `0` | integer | Rows to skip. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible group resolve

**Usage:** `cruxible group resolve [OPTIONS]`

**Purpose:** Resolve a candidate group (approve or reject).

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--group` | yes | `Sentinel.UNSET` | text | Group ID to resolve. |
| `--action` | yes | `Sentinel.UNSET` | choice | Resolution action. |
| `--rationale` | no | `` | text | Rationale for this resolution. |
| `--source` | no | `human` | choice | Who resolved (default: human). |
| `--expected-pending-version` | yes | `Sentinel.UNSET` | integer | Pending version the reviewer saw when deciding. |
| `--stamp-existing` | no | `False` | boolean | On approve, bless each surviving pre-existing edge with this group's review status and provenance instead of skipping it. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible group status

**Usage:** `cruxible group status [OPTIONS]`

**Purpose:** Show lifecycle status for a signature bucket.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--group` | no | `` | text | Concrete group ID. |
| `--signature` | no | `` | text | Signature bucket ID. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible group trust

**Usage:** `cruxible group trust [OPTIONS]`

**Purpose:** Update trust status on a resolution.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--resolution` | yes | `Sentinel.UNSET` | text | Resolution ID. |
| `--status` | yes | `Sentinel.UNSET` | choice | Trust status to set. |
| `--reason` | no | `` | text | Reason for trust status change. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible init

**Usage:** `cruxible init [OPTIONS]`

**Purpose:** Initialize a new instance or governed server-backed workspace.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--config` | no | `` | text | Path to config YAML file. |
| `--kit` | no | `` | text | Kit alias or ref to materialize; repeatable. Order is composition order: a standalone base kit first, overlay kits after. |
| `--root-dir` | no | `` | text | Workspace root for config/artifact provenance (defaults to current directory). |
| `--data-dir` | no | `` | text | Directory for data files. |
| `--bootstrap` | no | `False` | boolean | Use hosted kit init authorized by the runtime bootstrap bearer. Requires `--kit`. |
| `--activate / --no-activate` | no | `True` | boolean | Make a new server instance the active CLI context instance. |

**Output And Side Effects:**
- Normal server init calls the governed instance lifecycle route. With `--bootstrap --kit`, calls the hosted runtime kit-init route so the one-time bootstrap bearer can create the first auth-enabled instance.
- New server instances are remembered in CLI context unless `--no-activate` is used.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Auth rejecting plain `init --kit`; run `cruxible init --kit <ref> --bootstrap` with `CRUXIBLE_SERVER_BEARER_TOKEN` set to the bootstrap secret, then run `cruxible credential claim-bootstrap`.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible inspect

**Usage:** `cruxible inspect [OPTIONS]`

**Purpose:** Inspect entities plus canonical read-only system views.

**Subcommands:**

- `cruxible inspect governance` - Show the canonical governance view for the current instance.
- `cruxible inspect ontology` - Show the canonical ontology view for the current instance config.
- `cruxible inspect overview` - Show the generated config overview built from canonical views.
- `cruxible inspect queries` - Show the canonical query view for the current instance config.
- `cruxible inspect trace` - Inspect a provider execution trace by ID.
- `cruxible inspect workflows` - Show the canonical workflow view for the current instance config.

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible inspect governance

**Usage:** `cruxible inspect governance [OPTIONS]`

**Purpose:** Show the canonical governance view for the current instance.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--format` | no | `markdown` | choice | Output format. |
| `--limit` | no | `200` | integer range | Max pending groups and resolutions to inspect. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible inspect trace

**Usage:** `cruxible inspect trace [OPTIONS] TRACE_ID`

**Purpose:** Inspect a provider execution trace by ID.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `trace_id` | yes | `Sentinel.UNSET` | text | Positional argument. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only. Returns the persisted provider execution trace, including provider metadata, retained input/output payload fields, payload digest/size metadata, status, timings, and error details when present. Payload fields follow the instance config's `runtime.trace_payloads` retention policy.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Trace ID not found.
- Permission mode too low for read operations.

## cruxible inspect ontology

**Usage:** `cruxible inspect ontology [OPTIONS]`

**Purpose:** Show the canonical ontology view for the current instance config.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--format` | no | `markdown` | choice | Output format. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible inspect overview

**Usage:** `cruxible inspect overview [OPTIONS]`

**Purpose:** Show the generated config overview built from canonical views.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--format` | no | `markdown` | choice | Output format. |
| `--limit` | no | `200` | integer range | Max pending groups and resolutions to inspect. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible inspect queries

**Usage:** `cruxible inspect queries [OPTIONS]`

**Purpose:** Show the canonical query view for the current instance config.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--format` | no | `markdown` | choice | Output format. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible inspect workflows

**Usage:** `cruxible inspect workflows [OPTIONS]`

**Purpose:** Show the canonical workflow view for the current instance config.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--format` | no | `markdown` | choice | Output format: `json`, `markdown`, `mermaid`, `mermaid-dependencies`, or `mermaid-steps`. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible lint

**Usage:** `cruxible lint [OPTIONS]`

**Purpose:** Run the aggregate read-only corpus lint pass.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--max-findings` | no | `100` | integer | Max graph findings to include. |
| `--analysis-limit` | no | `200` | integer | Rows to inspect for feedback and outcome analysis. |
| `--min-support` | no | `5` | integer | Minimum support for lint suggestions. |
| `--exclude-orphan-type` | no | `Sentinel.UNSET` | text | Entity type to exclude from orphan checks. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible list

**Usage:** `cruxible list [OPTIONS]`

**Purpose:** List entities, receipts, or feedback.

**Subcommands:**

- `cruxible list edges` - List edges in the graph.
- `cruxible list entities` - List entities of a given type.
- `cruxible list feedback` - List feedback records.
- `cruxible list outcomes` - List outcome records.
- `cruxible list receipts` - List receipt summaries.
- `cruxible list traces` - List provider execution trace summaries.

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible list edges

**Usage:** `cruxible list edges [OPTIONS]`

**Purpose:** List edges in the graph.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--relationship` | no | `` | text | Filter by relationship type. |
| `--where` | no | `` | text | Property predicate. Repeatable. Use `field=value`, `field~value`, or `field:in=a,b`. |
| `--limit` | no | `50` | integer | Max edges to show. |
| `--offset` | no | `0` | integer | Rows to skip. |
| `--state` | no | `` | choice | Read-visibility state: `live`, `accepted`, `all`, `not-live`, `pending`, or `reviewable`. Omit to return every stored edge (the inspection default); `not-live` surfaces rejected/closed edges, `live` hides them. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.
- `list edges` is a stored-relationship inspection surface. With no `--state`
  it returns every stored edge, including pending, rejected, or otherwise
  non-live ones. Pass `--state live` (or use named queries, which are logical
  reads) when you need live/reviewable truth rather than store inspection;
  `--state not-live` surfaces exactly the rejected/closed edges for recovery.
- Example: `cruxible list edges --relationship work_item_depends_on_work_item --where dependency_basis~schema --json`

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible list entities

**Usage:** `cruxible list entities [OPTIONS]`

**Purpose:** List entities of a given type.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--type` | yes | `Sentinel.UNSET` | text | Entity type to list. |
| `--field` | no | `` | text | Property field to include. Repeat to project compact entity payloads. |
| `--where` | no | `` | text | Property predicate. Repeatable. Use `field=value`, `field~value`, or `field:in=a,b`. |
| `--limit` | no | `50` | integer | Max entities to show. |
| `--offset` | no | `0` | integer | Rows to skip. |
| `--state` | no | `` | choice | Read-visibility state by entity lifecycle: `live` (default — hides retired/superseded entities), `all`, or `not-live` (only the gated-out set). Review-only values resolve to `live` (entities have no review axis). |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only. Defaults to `--state live`: retired/superseded entities
  (entity `lifecycle.status != live`) are hidden. Use `--state not-live` to find
  the gated-out set (recovery), or `--state all` for everything. Without
  `--field`, returns full entity records. With `--field`,
  returns the same list envelope but trims each entity's `properties` to the
  requested fields while always keeping `entity_type` and `entity_id`.
- `--where` filters configured entity properties after the caller has selected
  an entity type. It is bounded predicate filtering, not topic or semantic
  search. Examples:
  `cruxible list entities --type WorkItem --where status=active --field title --json`
  and
  `cruxible list entities --type WorkItem --where title~query --field status --json`.
- Field projection reduces payload size after the caller has already selected
  an entity type; it is not topic search.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible list feedback

**Usage:** `cruxible list feedback [OPTIONS]`

**Purpose:** List feedback records.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--receipt` | no | `` | text | Filter by receipt ID. |
| `--limit` | no | `50` | integer | Max records to show. |
| `--offset` | no | `0` | integer | Rows to skip. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible list outcomes

**Usage:** `cruxible list outcomes [OPTIONS]`

**Purpose:** List outcome records.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--receipt` | no | `` | text | Filter by receipt ID. |
| `--limit` | no | `50` | integer | Max records to show. |
| `--offset` | no | `0` | integer | Rows to skip. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible list receipts

**Usage:** `cruxible list receipts [OPTIONS]`

**Purpose:** List receipt summaries.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--query-name` | no | `` | text | Filter by query name. |
| `--operation-type` | no | `` | text | Filter by operation type. |
| `--limit` | no | `50` | integer | Max receipts to show. |
| `--offset` | no | `0` | integer | Rows to skip. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible list traces

**Usage:** `cruxible list traces [OPTIONS]`

**Purpose:** List provider execution trace summaries.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--workflow` | no | `` | text | Filter by workflow name. |
| `--provider` | no | `` | text | Filter by provider name. |
| `--limit` | no | `100` | integer range | Max traces to show. |
| `--offset` | no | `0` | integer range | Rows to skip. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only. Returns trace summary rows with trace ID, workflow, step, provider, runtime, and creation time.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for read operations.
- Invalid limit or offset.

## cruxible lock

**Usage:** `cruxible lock [OPTIONS]`

**Purpose:** Generate a workflow lock file for the current instance config, or for a bare kit directory with `--kit-dir`.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--force` | no | `False` | boolean | Accept live canonical artifact hashes when regenerating the lock. |
| `--kit-dir` | no | `` | directory | Build `<kit-dir>/cruxible.lock.yaml` from `<kit-dir>/config.yaml` without loading an instance or contacting a daemon. |

**Output And Side Effects:**
- Without `--kit-dir`, updates the active instance workflow lock through the local service layer or configured daemon.
- With `--kit-dir`, performs a pure local kit lock refresh: reads the kit's own config layer (no `target_state` composition — base-layer content is pinned by the base kit's own lock), writes `<kit-dir>/cruxible.lock.yaml` with artifact URIs preserved as written, and prints the lock digest. This is the canonical generation path for a committed kit lock; CI asserts regen-is-noop for every bundled kit.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- `--kit-dir` cannot be combined with explicit server transport flags or `--instance-id`, and the kit directory must contain `config.yaml`.
- Canonical artifact digest mismatches fail unless `--force` is used to accept the on-disk hash.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible outcome

**Usage:** `cruxible outcome [OPTIONS]`

**Purpose:** Record, analyze, and inspect decision outcomes.

**Subcommands:**

- `cruxible outcome record` - Record the outcome of a decision.
- `cruxible outcome profile` - Display the configured outcome profile for one anchor context.
- `cruxible outcome analyze` - Analyze structured outcomes and print trust/debugging suggestions.

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible outcome record

**Usage:** `cruxible outcome record [OPTIONS]`

**Purpose:** Record the outcome of a decision.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--receipt` | yes | `Sentinel.UNSET` | text | Receipt ID. |
| `--outcome` | yes | `Sentinel.UNSET` | choice | Outcome of the decision. |
| `--detail` | no | `` | text | JSON string with outcome details. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible outcome profile

**Usage:** `cruxible outcome profile [OPTIONS]`

**Purpose:** Display the configured outcome profile for one anchor context.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--anchor-type` | yes | `Sentinel.UNSET` | choice | Anchor type to resolve. |
| `--relationship` | no | `` | text | Relationship type. |
| `--workflow` | no | `` | text | Workflow name. |
| `--surface-type` | no | `` | choice | Receipt surface type. |
| `--surface-name` | no | `` | text | Receipt surface name. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible plan

**Usage:** `cruxible plan [OPTIONS]`

**Purpose:** Compile a workflow plan for the current instance.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--workflow` | yes | `Sentinel.UNSET` | text | Workflow name from config. |
| `--input` | no | `` | text | Inline JSON or YAML workflow input. |
| `--input-file` | no | `` | path | JSON or YAML file providing workflow input. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible propose

**Usage:** `cruxible propose [OPTIONS]`

**Purpose:** Execute a `type: proposal` workflow and bridge its output into a candidate group.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--workflow` | yes | `Sentinel.UNSET` | text | Workflow name from config. |
| `--input` | no | `` | text | Inline JSON or YAML workflow input. |
| `--input-file` | no | `` | path | JSON or YAML file providing workflow input. |
| `--decision-record` | no | `` | text | Decision record ID for audit logging. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible query

**Usage:** `cruxible query [OPTIONS]`

**Purpose:** Run, inspect, and discover named queries on this instance.

**Subcommands:**

- `cruxible query describe` - Describe one named query with required params and example IDs.
- `cruxible query inline` - Execute a bounded inline query definition for exploration.
- `cruxible query list` - List named queries with entry points and required params.
- `cruxible query run` - Execute a named query and display results plus the receipt.

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible query run

**Usage:** `cruxible query run [OPTIONS] QUERY_NAME`

**Purpose:** Execute a named query and display results plus the receipt.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `query_name` | yes | `Sentinel.UNSET` | text | Positional argument. |
| `--param` | no | `Sentinel.UNSET` | text | Query parameter as KEY=VALUE. |
| `--limit` | no | `` | integer range | Max results to display. |
| `--state` | no | `` | choice | Read-visibility state: `live` (default), `accepted`, `all`, `not-live`, `pending`, or `reviewable`. Gates entities by lifecycle and edges by review+lifecycle. Overriding a named query's configured state requires `allow_relationship_state_override: true`. |
| `--count` | no | `False` | boolean | Show only summary metadata. |
| `--decision-record` | no | `` | text | Decision record ID for audit logging. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible query inline

**Usage:** `cruxible query inline [OPTIONS]`

**Purpose:** Execute a bounded inline query definition without persisting it to config.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--definition-json` | no | `` | text | Inline query definition as a JSON object. |
| `--definition-file` | no | `` | path | Path to a JSON or YAML inline query definition. |
| `--param` | no | `Sentinel.UNSET` | text | Query parameter as KEY=VALUE. |
| `--limit` | no | `` | integer range | Max results to display. |
| `--state` | no | `` | choice | Read-visibility state: `live` (default), `accepted`, `all`, `not-live`, `pending`, or `reviewable`. Gates entities by lifecycle and edges by review+lifecycle. Overriding the inline definition's configured state requires `allow_relationship_state_override: true`. |
| `--count` | no | `False` | boolean | Show only summary metadata. |
| `--decision-record` | no | `` | text | Decision record ID for audit logging. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Example:**

```bash
cruxible query inline \
  --definition-json '{"name":"brake_parts","mode":"collection","returns":"Part","result_shape":"entity","where":{"result.properties.category":{"eq":"brakes"}}}' \
  --json
```

**Output And Side Effects:**
- Read-only graph access. Inline queries persist query receipts and optional
  decision events, but they do not modify or persist config.

**Common Errors:**
- Provide exactly one of `--definition-json` or `--definition-file`.
- Inline query definitions use the same shape as configured named queries plus
  required `name`; repeated or workflow-critical inline queries should be
  promoted into config as named queries.

## cruxible query describe

**Usage:** `cruxible query describe [OPTIONS]`

**Purpose:** Describe one named query with required params and example IDs.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--query` | yes | `Sentinel.UNSET` | text | Named query from config. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible query list

**Usage:** `cruxible query list [OPTIONS]`

**Purpose:** List named queries with entry points and required params.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible config reload

**Usage:** `cruxible config reload [OPTIONS]`

**Purpose:** Validate the active config or repoint the instance to a new config file.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--config` | no | `` | text | Optional new config path. |
| `--allow-orphans` | no | `False` | flag | Allow stored graph types absent from the incoming config. |

**Output And Side Effects:**
- Refuses the reload (before any file or pointer changes) if the incoming
  config no longer declares entity or relationship types present in the
  stored graph; the error lists each stranded type with its stored count.
  `--allow-orphans` proceeds anyway and the output carries the stranding
  report.
- Successful reloads print the type delta (entity/relationship types added
  and removed) so a reload is never a silent schema change. A reload whose
  *current* config is unreadable still works as the repair path; the delta
  is reported as unknown via a warning.
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible run

**Usage:** `cruxible run [OPTIONS]`

**Purpose:** Execute a workflow for the current instance. Canonical workflows run as previews and return an `apply_digest` plus `head_snapshot_id`; use `cruxible apply` to commit them. For `type: proposal` workflows, use `cruxible propose` instead.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--workflow` | yes | `Sentinel.UNSET` | text | Workflow name from config. |
| `--input` | no | `` | text | Inline JSON or YAML workflow input. |
| `--input-file` | no | `` | path | JSON or YAML file providing workflow input. |
| `--save-preview` | no | `` | file | Save preview state to a JSON file for use with apply --preview-file. |
| `--decision-record` | no | `` | text | Decision record ID for audit logging. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible sample

**Usage:** `cruxible sample [OPTIONS]`

**Purpose:** Show a sample of entities of a given type.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--type` | yes | `Sentinel.UNSET` | text | Entity type to sample. |
| `--field` | no | `` | text | Property field to include. Repeat to project compact entity payloads. |
| `--limit` | no | `5` | integer | Number of entities to show. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only. Without `--field`, returns full sampled entity records. With
  `--field`, trims each entity's `properties` to the requested fields while
  always keeping `entity_type` and `entity_id`.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible schema

**Usage:** `cruxible schema [OPTIONS]`

**Purpose:** Display the config schema for this instance.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible server

**Usage:** `cruxible server [OPTIONS]`

**Purpose:** Launch and inspect the Cruxible daemon.

**Subcommands:**

- `cruxible server start` - Launch the Cruxible daemon in the foreground (the only daemon launch path).
- `cruxible server status` - Report a running daemon's version, state dir, transport, and instances.
- `cruxible server info` - Show live daemon metadata such as auth mode and state dir.
- `cruxible server restart` - Re-exec the live daemon in place, preserving its port, state dir, and env.

**Client Vs Launch:**
- `start` LAUNCHES the daemon; it takes no `--server-url` and becomes the
  long-running daemon process.
- `status`, `info`, and `restart` are CLIENT RPCs against an already-running
  daemon: they need a transport (`--server-url` / `--server-socket` or the
  matching env vars) and fail with a clear message when no daemon is reachable.

**Output And Side Effects:**
- Command-specific output only.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible server start

**Usage:** `cruxible server start [OPTIONS]`

**Purpose:** Launch the Cruxible daemon in the foreground.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--host` | no | `CRUXIBLE_HOST` or `127.0.0.1` | text | Bind host. Ignored when `--socket` is set. |
| `--port` | no | `CRUXIBLE_PORT` or `8100` | integer | Bind port. Ignored when `--socket` is set. |
| `--state-dir` | no | `CRUXIBLE_SERVER_STATE_DIR` or `~/.cruxible/server` | text | Server-owned state directory. |
| `--socket` | no | `CRUXIBLE_SERVER_SOCKET` | text | Listen on this Unix socket path instead of host/port. |
| `--bootstrap-secret-file` | no | `` | file | Write an auto-generated runtime bootstrap secret to this file with mode 0600. |

**Output And Side Effects:**
- This process becomes the long-running daemon (it is not a client of an
  existing one, so it takes no `--server-url`). Flags override the matching
  environment variables (`CRUXIBLE_HOST`, `CRUXIBLE_PORT`,
  `CRUXIBLE_SERVER_STATE_DIR`, `CRUXIBLE_SERVER_SOCKET`); unset flags fall back to
  the env value or the built-in default. Use a durable `--state-dir`; Cruxible
  warns at startup when the state path resolves under a volatile temp location.
  Stop with Ctrl-C. `cruxible server start --help` prints help and exits without
  serving.
- When `CRUXIBLE_SERVER_AUTH=true` and no `CRUXIBLE_RUNTIME_BOOTSTRAP_SECRET`
  is set, generates a one-time bootstrap secret. Without
  `--bootstrap-secret-file`, prints it once with hosted-init and claim hints.
  With `--bootstrap-secret-file`, writes it to that path as 0600 and prints only
  the path plus hosted-init and claim hints.

**Common Errors:**
- Binding a non-loopback host without `CRUXIBLE_SERVER_AUTH=true` is refused.
- A state dir that previously required auth is refused unless auth is re-enabled.
- The daemon ships in the default install (`pip install cruxible`); no extra is required.

## cruxible server status

**Usage:** `cruxible server status [OPTIONS]`

**Purpose:** Report a running daemon's version, state dir, transport, and instances.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- A CLIENT command: queries an already-running daemon over the configured
  transport (`--server-url` / `--server-socket` or the matching env vars) and
  prints whether it is reachable, plus its version, state directory, configured
  transport, instance count, and auth status. With `--json`, returns the same
  fields plus `transport`.

**Common Errors:**
- No transport configured, or the daemon is down: fails with a clear message
  (no hang) pointing at `cruxible server start` / `--server-url`.
- Permission mode too low to read cross-tenant daemon metadata.

## cruxible server info

**Usage:** `cruxible server info [OPTIONS]`

**Purpose:** Show live daemon metadata such as auth mode and state dir.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Prints daemon version, server requirement, auth enabled/required status, state
  directory, and instance count. With `--json`, returns the same fields.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible server restart

**Usage:** `cruxible server restart [OPTIONS]`

**Purpose:** Re-exec the live daemon in place, preserving its port, state dir, and env.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--json` | no | `False` | boolean | Output as JSON. |
| `--no-wait` | no | `False` | boolean | Return immediately after scheduling the restart, without confirming the daemon is back. |
| `--timeout` | no | `30.0` | float | Seconds to wait for the restarted daemon to answer again. |

**Output And Side Effects:**
- Replaces the daemon's own process image (`os.execv`), preserving port, state
  directory, and environment, so it picks up code changes without losing its
  transport or instances. By default waits for the new image to answer and
  prints the confirmed version; `--no-wait` skips the wait. Requires ADMIN.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible snapshot

**Usage:** `cruxible snapshot [OPTIONS]`

**Purpose:** Manage immutable state snapshots.

**Subcommands:**

- `cruxible snapshot create` - Create an immutable full snapshot for the current instance.
- `cruxible snapshot list` - List snapshots for the current instance.

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible snapshot create

**Usage:** `cruxible snapshot create [OPTIONS]`

**Purpose:** Create an immutable full snapshot for the current instance.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--label` | no | `` | text | Optional human label for the snapshot. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible snapshot list

**Usage:** `cruxible snapshot list [OPTIONS]`

**Purpose:** List snapshots for the current instance.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--limit` | no | `` | integer | Max snapshots to show. |
| `--offset` | no | `0` | integer | Rows to skip. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible source

**Usage:** `cruxible source [OPTIONS] COMMAND [ARGS]...`

**Purpose:** Register local source documents and dereference source-backed
evidence locators.

**Subcommands:**

- `cruxible source list` - List registered source artifact summaries.
- `cruxible source get` - Read one registered source artifact's metadata and chunk map.
- `cruxible source register` - Parse and register a local Markdown source artifact.
- `cruxible source dereference` - Resolve a registered source-evidence locator back to source text.

**Output And Side Effects:**
- `source list`, `source get`, and `source dereference` are read-only.
- `source register` writes a source artifact manifest, parsed chunk metadata, and
  optional archived source bytes into the current instance.

**Common Errors:**
- Missing local instance or stale daemon `--instance-id`.
- Permission mode too low for governed write/read operations.
- Unsupported source kind, missing local source path, incomplete locator, or
  drifted source content hash.

## cruxible source list

**Usage:** `cruxible source list [OPTIONS]`

**Purpose:** List registered source artifact summaries for the current instance.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--limit` | no | `50` | integer | Max artifacts to show. |
| `--offset` | no | `0` | integer | Rows to skip. |
| `--json` | no | `False` | boolean | Output the full list contract payload as JSON. |

**Output And Side Effects:**
- Read-only. Human output renders a table with artifact id, kind, label,
  retention, chunk count, and registration timestamp, followed by total and
  truncated pagination status.
- With `--json`, emits the full `SourceArtifactListResult` contract payload.

**Common Errors:**
- Missing local instance or stale daemon `--instance-id`.
- Permission mode too low for source artifact reads.
- Invalid negative pagination values.

## cruxible source get

**Usage:** `cruxible source get [OPTIONS] ARTIFACT_ID`

**Purpose:** Read a registered source artifact's metadata and chunk map.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `ARTIFACT_ID` | yes | `Sentinel.UNSET` | text | Source artifact ID returned by `source register`. |
| `--chunks / --no-chunks` | no | `True` | boolean | Show or hide the chunk metadata table in human output. |
| `--json` | no | `False` | boolean | Output the full read contract payload as JSON, including chunk text when available. |

**Output And Side Effects:**
- Read-only. Human output renders an artifact header with id, kind, label,
  original URI, retention, and content availability; when content is unavailable,
  the reason is shown.
- By default, human output also renders a chunk table with chunk id, heading path,
  block type, and line range. It does not print full chunk text; use `--json`
  or `source dereference` when source body text is needed.
- With `--json`, emits the full `SourceArtifactReadResult` contract payload.

**Common Errors:**
- Missing local instance or stale daemon `--instance-id`.
- Permission mode too low for source artifact reads.
- Unknown source artifact ID.

## cruxible source register

**Usage:** `cruxible source register [OPTIONS]`

**Purpose:** Register a Markdown document as source-backed proposal evidence.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--path` | yes | `Sentinel.UNSET` | text | Local Markdown source path. Relative paths resolve from the current workspace. |
| `--id` | no | `` | text | Caller-supplied deterministic artifact id (3-64 chars of `[A-Za-z0-9._-]`, alphanumeric start); duplicates are refused. Omit for a generated `SRC-*` id. |
| `--kind` | no | `markdown` | choice | Source parser kind. |
| `--retention` | no | `manifest_only` | choice | Source retention mode: `manifest_only` or `archive`. |
| `--original-uri` | no | `` | text | Optional display/provenance URI to preserve in the manifest. |
| `--label` | no | `` | text | Optional display label. |
| `--json` | no | `False` | boolean | Output the registered artifact and chunk manifest as JSON. |

**Examples:**

```bash
cruxible source register \
  --path docs/vendor-evidence.md \
  --original-uri https://vendor.example/evidence.md \
  --label "Vendor evidence" \
  --json
```

```bash
cruxible source register \
  --path docs/vendor-evidence.md \
  --retention archive
```

**Output And Side Effects:**
- Persists a source artifact ID, document hash, parser version, byte count, and
  deterministic chunk IDs in `state.db`.
- With `manifest_only`, Cruxible stores the manifest and local path but not a
  deep copy of the source bytes.
- With `archive`, Cruxible also stores the source bytes so later dereference can
  use the archived body if the local file is missing or changed.

**Common Errors:**
- Missing source path, unsupported source kind, path outside the registered
  workspace in daemon mode, or unreadable source file.

## cruxible source dereference

**Usage:** `cruxible source dereference [OPTIONS]`

**Purpose:** Resolve a registered source-evidence locator back to source text.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--artifact` | yes | `Sentinel.UNSET` | text | Source artifact ID returned by `source register`. |
| `--chunk` | no | `` | text | Deterministic chunk ID from the registered manifest. |
| `--heading` | no | `` | text | Heading path segment. Repeat for nested headings. |
| `--block-selector` | no | `` | text | Block selector under the heading path, such as `paragraph:1`. |
| `--expected-content-hash` | no | `` | text | Optional expected chunk content hash for drift checks. |
| `--json` | no | `False` | boolean | Output dereference status, chunk metadata, and body as JSON. |

Source-evidence locators must use one of two forms:

- `--chunk <chunk-id>`
- `--heading <heading> [--heading <nested-heading> ...] --block-selector <selector>`

**Examples:**

```bash
cruxible source dereference \
  --artifact SRC-... \
  --chunk CHK-... \
  --json
```

```bash
cruxible source dereference \
  --artifact SRC-... \
  --heading "Compatibility Evidence" \
  --block-selector paragraph:1
```

**Output And Side Effects:**
- Read-only. Returns `available`, `drifted`, or `unavailable` plus source body
  when Cruxible can safely dereference the locator.
- `body_origin` is `archive` when archived bytes are used, or `local_path` when
  Cruxible rereads the registered local file.

**Common Errors:**
- Missing artifact, incomplete locator, unknown chunk, unavailable local source
  file for `manifest_only`, or content drift against the stored manifest/hash.

## cruxible stats

**Usage:** `cruxible stats [OPTIONS]`

**Purpose:** Display entity and relationship counts for this instance.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only output unless the command records an explicit receipt, feedback, outcome, or decision event.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible test

**Usage:** `cruxible test [OPTIONS]`

**Purpose:** Execute config-defined workflow tests for the current instance.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--name` | no | `` | text | Run only a named workflow test. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible validate

**Usage:** `cruxible validate [OPTIONS]`

**Purpose:** Validate a config YAML file without creating an instance.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--config` | yes | `Sentinel.UNSET` | text | Path to config YAML file. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible instance

**Usage:** `cruxible instance [OPTIONS]`

**Purpose:** Back up and restore exact Cruxible instances.

**Subcommands:**

- `cruxible instance backup` - Write a portable same-identity backup artifact for the current instance.
- `cruxible instance restore` - Restore a same-identity backup artifact.
- `cruxible instance relocate` - Move the current healthy instance to a new directory, preserving identity.

**Output And Side Effects:**
- Command-specific output only.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible instance backup

**Usage:** `cruxible instance backup [OPTIONS] ARTIFACT_PATH`

**Purpose:** Write a portable same-identity backup artifact for the current instance.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `ARTIFACT_PATH` | yes |  | path | Destination path for the backup artifact. |
| `--label` | no |  | text | Optional human label for the backup artifact. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Writes a portable same-identity backup artifact (including the authoritative
  state database) for the current instance. Requires ADMIN.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible instance restore

**Usage:** `cruxible instance restore [OPTIONS] ARTIFACT_PATH`

**Purpose:** Restore a same-identity backup artifact.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `ARTIFACT_PATH` | yes |  | path | Backup artifact to restore from. |
| `--at` | no |  | text | Restore target root directory. |
| `--activate / --no-activate` | no |  | boolean | Make the restored server instance the active CLI context instance. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Restores a daemon-backed instance from a same-identity backup artifact.
  Requires ADMIN.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible instance relocate

**Usage:** `cruxible instance relocate [OPTIONS]`

**Purpose:** Move the current healthy instance to a new directory, preserving identity.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--to` | yes |  | text | New root directory for the instance. |
| `--remove-source / --keep-source` | no | `keep-source` | boolean | Delete the old directory after a successful relocate (default: keep it). |
| `--activate / --no-activate` | no |  | boolean | Make the relocated server instance the active CLI context instance. |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Moves a healthy daemon-backed instance to a new directory while preserving
  its identity; the registry is repointed to the new location. Requires ADMIN.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible state

**Usage:** `cruxible state [OPTIONS]`

**Purpose:** Publish immutable states and manage pullable overlays.

**Subcommands:**

- `cruxible state create-overlay` - Create a new local overlay instance from a published state release.
- `cruxible state health` - Show read-only deterministic state-health maintenance signals.
- `cruxible state publish` - Publish the current root state-model instance as an immutable release bundle.
- `cruxible state pull-apply` - Apply a previewed upstream release into the current overlay.
- `cruxible state pull-preview` - Preview pulling a newer upstream release into the current overlay.
- `cruxible state status` - Show upstream tracking metadata for the current instance.

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible state create-overlay

**Usage:** `cruxible state create-overlay [OPTIONS]`

**Purpose:** Create a new local overlay instance from a published state release.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--transport-ref` | no | `Sentinel.UNSET` | text | Transport ref, e.g. file://... or oci://... |
| `--state-ref` | no | `Sentinel.UNSET` | text | State alias, e.g. kev-reference or kev-reference@2026-03-27. |
| `--kit` | no | `Sentinel.UNSET` | text | Apply a checked-in local overlay kit, e.g. kev-triage. |
| `--no-kit` | no | `False` | boolean | Skip automatic kit application and create a bare overlay. |
| `--root-dir` | no | `` | text | Workspace root for the new overlay (defaults to current directory in server mode). |
| `--activate / --no-activate` | no | `True` | boolean | Make the new server overlay the active CLI context instance. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible state health

**Usage:** `cruxible state health [OPTIONS]`

**Purpose:** Show read-only deterministic state-health maintenance signals.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--json` | no | `False` | boolean | Output as JSON. |

**Output And Side Effects:**
- Read-only. Aggregates deterministic maintenance signals (group counts/ages, edge provenance tally, source-artifact/provider-trace freshness, config-compatibility, and graph-integrity counts) into four sections. Reports raw metrics and binary deterministic facts only; no scoring, ranking, severity, or thresholds. Creates no receipts and mutates nothing.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible state publish

**Usage:** `cruxible state publish [OPTIONS]`

**Purpose:** Publish the current root state-model instance as an immutable release bundle.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--transport-ref` | yes | `Sentinel.UNSET` | text | Transport ref, e.g. file://... or oci://... |
| `--state-id` | yes | `Sentinel.UNSET` | text | Stable published state identifier. |
| `--release-id` | yes | `Sentinel.UNSET` | text | User-supplied release identifier. |
| `--compatibility` | no | `data_only` | choice | Compatibility classification for the published release. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible state pull-apply

**Usage:** `cruxible state pull-apply [OPTIONS]`

**Purpose:** Apply a previewed upstream release into the current overlay.

**Options And Arguments:**

| Name | Required | Default | Type | Description |
| --- | --- | --- | --- | --- |
| `--apply-digest` | yes | `Sentinel.UNSET` | text | Apply digest returned by pull-preview. |

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible state pull-preview

**Usage:** `cruxible state pull-preview [OPTIONS]`

**Purpose:** Preview pulling a newer upstream release into the current overlay.

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.

## cruxible state status

**Usage:** `cruxible state status [OPTIONS]`

**Purpose:** Show upstream tracking metadata for the current instance.

**Output And Side Effects:**
- Calls the service layer and may create receipts, traces, snapshots, config changes, groups, or graph mutations depending on the command.

**Common Errors:**
- Missing or stale `--instance-id` for daemon-backed commands.
- Permission mode too low for mutations or admin operations.
- Unknown config/workflow/query/entity names, or stale workflow locks where applicable.


========================================================================
SOURCE: https://docs.cruxible.ai/config-reference/
========================================================================

# Config Reference

Cruxible configs are YAML files that define a decision domain: entity
types, relationships, named queries, constraints, workflows, providers,
artifacts, quality checks, feedback profiles, outcome profiles, and decision
policies, plus mutation guards for configured state writes. AI agents generate
these configs; Core validates and executes against them.

## Top-Level Structure

```yaml
version: "1.0"
name: "my_domain"
description: "Optional description of this decision domain"
# extends: base-config.yaml  # release-backed overlay composition (see below)

entity_types: { ... }
relationships: [ ... ]
named_queries: { ... }
constraints: [ ... ]

# Governed workflow sections (all optional)
quality_checks: [ ... ]
feedback_profiles: { ... }
outcome_profiles: { ... }
mutation_guards: [ ... ]
decision_policies: [ ... ]
contracts: { ... }
artifacts: { ... }
providers: { ... }
workflows: { ... }
runtime:
  trace_payloads: preview
tests: [ ... ]
```

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `version` | string | no | `"1.0"` | Config schema version |
| `name` | string | **yes** | — | Unique name for this domain |
| `description` | string | no | `null` | Human-readable description |
| `extends` | string | no | `null` | Path to a base config for release-backed overlay composition (see [Config Composition](#config-composition)) |
| `cruxible_version` | string | no | `null` | Version of cruxible-core that produced this config (auto-stamped on save) |
| `entity_types` | dict | **yes**\* | — | Entity type definitions (\*optional when `extends` is set) |
| `relationships` | list | no | `[]` | Relationship definitions |
| `named_queries` | dict | no | `{}` | Declarative query definitions |
| `constraints` | list | no | `[]` | Validation rules |
| `quality_checks` | list | no | `[]` | Evaluate-time graph quality checks |
| `feedback_profiles` | dict | no | `{}` | Structured feedback vocabularies per relationship type |
| `outcome_profiles` | dict | no | `{}` | Structured outcome vocabularies for trust calibration |
| `mutation_guards` | list | no | `[]` | Reject direct graph mutations unless configured state-side conditions pass |
| `decision_policies` | list | no | `[]` | Action-side behavior rules for queries and workflows |
| `enums` | dict | no | `{}` | Shared enum vocabularies referenced by property schemas |
| `contracts` | dict | no | `{}` | Typed payload contracts for providers/workflows |
| `artifacts` | dict | no | `{}` | Pinned external artifacts referenced by providers |
| `providers` | dict | no | `{}` | Versioned executable leaves used by workflow steps |
| `workflows` | dict | no | `{}` | Declarative step-based execution plans |
| `runtime` | dict | no | `{trace_payloads: preview}` | Local runtime behavior options, including provider trace payload retention |
| `tests` | list | no | `[]` | Fixture-based workflow tests |

---

## Runtime Options

`runtime` controls local execution and audit-capture behavior that is not part
of the state model itself.

```yaml
runtime:
  trace_payloads: preview
  default_write_policy: direct
```

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `trace_payloads` | string | no | `"preview"` | Provider trace payload retention: `"full"`, `"preview"`, or `"metadata"` |
| `default_write_policy` | string | no | `"direct"` | Instance-wide default direct-write governance: `"direct"` or `"proposal_only"`. Applies to entity/relationship types whose own `write_policy` is unset. See [Direct-Write Governance](#direct-write-governance-refuse_direct_writes). |

Trace payload retention controls what is persisted in provider execution traces:

- `full` stores full provider `input_payload` and `output_payload` bodies inline
  in local SQLite trace rows.
- `preview` stores small payload bodies inline, but replaces large payloads with
  bounded deterministic previews plus digest/byte-count metadata.
- `metadata` stores no full provider payload bodies; trace payload fields contain
  omission placeholders plus digest/byte-count metadata.

Local SQLite does not provide cold storage or later hydration for omitted
payload bodies. Choose `full` only when local full-body provider provenance is
more important than trace database size.

---

## Direct-Write Governance (`refuse_direct_writes`)

The `CRUXIBLE_MODE` permission tiers (`read_only` ⊂ `governed_write` ⊂
`graph_write` ⊂ `admin`) are **cumulative**: a `graph_write` actor can both
direct-add a fact *and* propose one. The tiers therefore cannot express "this
domain is proposal-only" — a per-domain governance axis. `refuse_direct_writes`
adds that axis.

A type marked **`proposal_only`** refuses bare direct graph-write verbs
(`add_entity` / `add_relationship` / `batch_direct_write` / the typed lifecycle
write) and forces state in only through the governed proposal/workflow path. It
is a **hard constraint, independent of permission tier** — even `admin` is
refused. The refusal raises `DirectWriteRefusedError` (HTTP **403**,
`error_code: direct_write_refused`).

An entity type marked **`mint_only`** (entity types only — not relationships) is
stricter still: it is writable **only** by the internal `token_mint` source and
refuses *all* other sources, including the governed verbs `workflow_apply` /
`group_resolve`. Because a workflow `make_entities` step would later apply through
`workflow_apply` and bypass the chokepoint refusal, a config whose `make_entities`
targets a `mint_only` entity type is **rejected at config load** (fail-closed).
Use it for auth-managed identity types: auth-on daemons materialize them from runtime credentials; auth-off daemons materialize a declared local `operator` identity through the same internal `token_mint` source.

**Scope.** `refuse_direct_writes` governs how state is *created* — it forces the
direct-write verbs above through the proposal/workflow path. It does **not**
govern the `feedback` review channel: promoting an already-staged (`pending`)
edge to live, or correcting an existing edge, goes through `feedback` — a
separate path gated by **reviewer identity** (credential-backed when server auth
is on; attributed to the declared local `operator` when auth is off). So
`proposal_only` guarantees that
*creation* is governed; *promotion* of a staged edge is exactly as strong as the
feedback review-gate, no stronger.

Three knobs control it:

| Knob | Where | Values | Effect |
|------|-------|--------|--------|
| `write_policy` (per type) | `entity_types.<T>` / `relationships[]` | `direct` \| `proposal_only` \| `mint_only` (entity types only) \| unset | Per-type policy. Unset inherits the instance default. An explicit `direct` opts out of the instance default (but **not** the env kill-switch). `mint_only` (entity types only) is stricter than `proposal_only`: the type is writable **only** by the internal `token_mint` source and refuses all other sources, including the governed verbs `workflow_apply` / `group_resolve`. A config whose workflow `make_entities` step targets a `mint_only` entity type is rejected at load (fail-closed). |
| `default_write_policy` | `runtime` | `direct` (default) \| `proposal_only` | Instance-wide default for types whose own `write_policy` is unset. |
| `CRUXIBLE_REFUSE_DIRECT_WRITES` | process env (daemon) | truthy (`1`/`true`/`yes`/`on`) | Daemon-wide **kill-switch** for the direct-write verbs: forces `proposal_only` for every type *at the write chokepoint*, overriding every per-type opt-out and the default. (Chokepoint only — the feedback review/promotion path is separate; see Scope above.) |

**Effective policy (union — any path to `proposal_only` wins):** a write is
refused when the env kill-switch is set **OR** the type's explicit `write_policy`
is `proposal_only` **OR** the type's `write_policy` is unset and
`runtime.default_write_policy` is `proposal_only`.

| `CRUXIBLE_REFUSE_DIRECT_WRITES` | type `write_policy` | `default_write_policy` | Effective |
|---|---|---|---|
| unset | unset | `direct` | direct |
| unset | unset | `proposal_only` | proposal_only |
| unset | `direct` | `proposal_only` | **direct** (opts out) |
| unset | `proposal_only` | `direct` | proposal_only |
| set | `direct` | `direct` | **proposal_only** (env wins) |
| set | unset | `direct` | proposal_only |

**Always permitted, regardless of policy:**

- Relationship writes with **`pending: true`** — they stage an edge for review
  and are not live. (Entities have no pending path; a direct entity add of a
  `proposal_only` type is refused outright — add it through a canonical
  `apply_entities` workflow.)
- Governed verbs: proposal **group resolution** (`group propose` → resolve) and
  **canonical workflow apply** (`apply_entities` / `apply_relationships`).

The default (everything unset) is byte-identical to pre-`refuse_direct_writes`
behavior — all direct writes succeed.

---

## Config-Declared Write Tiers (`write_tier`)

`write_tier` is the tier-ladder complement of `write_policy`. Where
`write_policy` answers "may this type be direct-written **at all**?" (a hard,
tier-independent constraint), `write_tier` answers "**which permission tier**
may direct-write it?". By default the direct-write verbs (`add_entity` /
`add_relationship` / `batch_direct_write`) require `graph_write`; a type may
declare a lower requirement:

```yaml
entity_types:
  StateNote:
    write_tier: governed_write   # governed_write actors may direct-write notes

relationships:
  - state_note_about_work_item: StateNote -> WorkItem
    write_tier: governed_write   # ...and attach them, in the same payload
```

Semantics:

- **Allowed values:** `governed_write` or `graph_write` (an explicit
  restatement of the default). `read_only` is rejected — it is not a write
  tier. `admin` is rejected — a declared tier only ever *lowers* the
  requirement below `graph_write`; restricting writes harder than the tier
  ladder is `write_policy`'s job (`proposal_only` / `mint_only`).
- **Per-payload requirement = max over touched types.** A direct write whose
  payload touches only types declared at-or-below the caller's tier is
  permitted; any type without `write_tier` keeps requiring `graph_write`, so a
  mixed payload is gated at its strictest member. An empty payload keeps the
  classic `graph_write` requirement.
- **Relationship writes contribute only the relationship type.** The edge is
  the thing mutated; endpoint entities are referenced, not written — a
  governed-tier edge may attach to entities the caller could not write
  directly.
- **Creates and updates are one surface.** The tier declares who may
  direct-write the type, not which verb flavor they use.
- **Everything downstream is unchanged.** Mutation guards, `write_policy`
  refusals, and validation all run after the tier check. Declaring
  `write_tier` together with an explicit `proposal_only`/`mint_only`
  `write_policy` is rejected at config load (the declared tier could never
  take effect).

Core stays domain-agnostic: it never knows what a "note" is — kits decide
which types form their low-trust write surfaces.

---

## Config Composition

The `extends` field enables an **overlay pattern** for release-backed state publishing. A published upstream state model provides entity types, relationships, and workflows; a downstream overlay adds its own internal extensions without duplicating the base.

**How it works:** `cruxible_validate` detects `extends`, resolves the base path relative to the overlay file, composes in memory, and validates the composed result. The raw `load_config()` function still parses a single file — composition happens in the service/CLI layer. For inline `config_yaml` (no file path), `extends` must use an absolute path or validation will error.

At runtime, the release-backed overlay flow (`service_reload_config`) materializes the composed config to disk as the active config the instance uses.

```yaml
# overlay config — validated by composing with the base automatically
version: "1.0"
name: kev_triage
extends: ../kev-reference/config.yaml
description: >
  Overlay of the KEV reference state for internal vulnerability triage.

entity_types:
  Asset:
    description: Internal asset from CMDB.
    properties:
      asset_id: {primary_key: true}
      hostname: {indexed: true}

relationships:
  - name: asset_owned_by
    from: Asset
    to: Owner
```

**Composition rules (strict append-only):**

| Field category | Fields | Behavior |
|----------------|--------|----------|
| Metadata | `name`, `description` | Overlay overrides base |
| Runtime options | `runtime` | Overlay runtime options override base runtime options |
| Safe lists | `constraints`, `quality_checks`, `mutation_guards`, `tests`, `decision_policies` | Overlay appends to base |
| Relationships | `relationships` | Overlay can only add new names; redefining an upstream relationship raises `ConfigError` |
| Keyed maps | `entity_types`, `named_queries`, `enums`, `feedback_profiles`, `outcome_profiles`, `contracts`, `artifacts`, `providers`, `workflows` | Overlay can only add new keys; redefining an upstream key raises `ConfigError` |
| Other fields | everything else | Overlay can only set if not in base, or if equal to base value |

When `extends` is set, `entity_types` may be empty — the base provides them.

## entity_types

A dict keyed by type name. Each value defines the entity's properties.

```yaml
entity_types:
  Vehicle:
    description: "A specific vehicle (year + make + model + trim)"
    properties:
      vehicle_id: {primary_key: true}
      year:
        type: int
        indexed: true
      make: {indexed: true}
      model: {indexed: true}
      trim: {}
      engine: {}
```

### EntityTypeSchema

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `description` | string | no | `null` | Human-readable description of this entity type |
| `properties` | dict | **yes** | — | Property definitions (see below) |
| `constraints` | list[string] | no | `[]` | Constraint names that apply to this entity type |
| `write_policy` | string | no | `null` | `"direct"`, `"proposal_only"`, or `"mint_only"`. Governs direct entity adds for this type — see [Direct-Write Governance](#direct-write-governance-refuse_direct_writes). `"mint_only"` is stricter than `"proposal_only"`: writable only by the internal `token_mint` source, refusing all other sources including `workflow_apply` / `group_resolve` (a config wiring a `mint_only` type into a workflow `make_entities` step is rejected at load). `null` inherits `runtime.default_write_policy`. |
| `write_tier` | string | no | `null` | `"governed_write"` or `"graph_write"`. Minimum permission tier allowed to direct-write this type — see [Config-Declared Write Tiers](#config-declared-write-tiers-write_tier). `null` keeps the default `graph_write` requirement. Rejected together with an explicit `proposal_only`/`mint_only` `write_policy`. |

### PropertySchema

Each property within an entity type (or relationship) is defined with:

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `type` | string | no for graph properties; yes for contract fields | `"string"` for graph properties | Data type: `string`, `int`, `integer`, `float`, `number`, `bool`, `date`, `datetime`, `json` |
| `primary_key` | bool | no | `false` | Mark as the entity's unique identifier |
| `indexed` | bool | no | `false` | Enable fast lookups on this property |
| `optional` | bool | no | graph properties default to `true`; contract fields default to `false` | Allow null/missing values |
| `required` | bool | no | `null` | Positive alias for `optional: false`; reject conflicting `required`/`optional` values |
| `default` | any | no | `null` | Default value when not provided |
| `enum` | list[string] | no | `null` | Restrict to allowed values |
| `enum_ref` | string | no | `null` | Reference a shared enum defined in top-level `enums` |
| `description` | string | no | `null` | Human-readable description |
| `json_schema` | dict | no | `null` | JSON Schema documentation for `json`-typed properties; write-time validation only checks JSON serializability |

**Rules:**
- Exactly one property per entity type should have `primary_key: true`.
- `primary_key` goes on the property, not the entity type.
- Entity and relationship properties default to `type: string` and `optional: true`, so `field_name: {}` is valid shorthand.
- `primary_key: true` implies required and may not be combined with `optional: true`.
- Contract fields still require an explicit `type` and are required by default.
- Use `required: true` for non-primary-key graph properties that must be present.
- `enum` and `enum_ref` are mutually exclusive.
- `json_schema` is only allowed when `type: json`. Use it to document the expected structure of complex nested data (e.g., version range arrays).

---

## enums

Shared bounded vocabularies referenced by `enum_ref`. Use these when the same
allowed values appear across multiple entity, relationship, or contract fields.

```yaml
enums:
  asset_status:
    description: Lifecycle state for tracked assets.
    values: [active, retired, decommissioned]
  criticality:
    description: Shared rank from lowest to highest.
    values: [low, medium, high, critical]
    ordered: low_to_high

entity_types:
  Asset:
    properties:
      asset_id: {primary_key: true}
      status: {enum_ref: asset_status}
```

Only an enum declared with `ordered: low_to_high` (values listed lowest to
highest) may be referenced as an `order_by` `enum_ref` in a named query;
referencing an unordered enum there is a config validation error.

Enum values must be non-empty and unique. With `extends`, overlays may add new
enum names but may not redefine or extend upstream enum vocabularies.

> **Authoring note — domain `status` vs. entity lifecycle.** A domain `status`
> enum should model **progress / workflow** states (e.g. `planned`, `active`,
> `closed`). Entity **retirement / deletion** is a *different axis* — "does this
> entity still exist / is it live" — and is the canonical way to "delete" an
> entity. It lives in the **canonical core entity lifecycle**
> `lifecycle.status` (uniform across all entities the way relationship lifecycle
> already is), **not** a per-kit `status` value. When authoring a kit, keep
> retirement-flavored values (`retired`, `decommissioned`, `superseded`) **out**
> of your `status` enum — the `asset_status` example above mixes the two for
> illustration, but the canonical soft-delete is the entity lifecycle, not a
> status value. This keeps "where is this in its workflow" separate from "is this
> entity still live."

### Entity `lifecycle.status` (read visibility)

Every entity carries an optional, **typed** lifecycle state. It is the typed
`lifecycle` field of the `EntityMetadata` envelope (an `EntityLifecycleState`,
validated on write), the entity analogue of the relationship's
`RelationshipMetadata.assertion.lifecycle`. It serializes onto the stored entity
metadata under the `lifecycle` key:

```yaml
# Stored/serialized shape — NOT hand-authored; written via the typed channel.
metadata:
  lifecycle:
    status: live   # one of: live | superseded | retired (default live)
    reason: "replaced by WI-204"       # optional
    closed_at: "2026-06-23T00:00:00Z"  # optional (shared closed_at/closed_by audit pair)
```

The lifecycle shares its structure with the relationship lifecycle (same
`reason`, effective window, `closed_at`/`closed_by` audit pair, and supersession
links); only the `status` vocabulary differs (`live|superseded|retired`
for entities vs `active|inactive|superseded|retracted` for relationships).
`orphaned` is **not** an authorable entity lifecycle status — an orphaned entity
is a derived evaluate/health finding (surfaced as `integrity.orphan_entity_count`),
not a state you set.

- **Default is `live`.** An entity with no `lifecycle` metadata is treated as
  live, so existing data needs no migration to keep current behavior.
- Set it through the **typed lifecycle write channel** — `entity update
  --lifecycle-status retired [--lifecycle-reason "…"]`, or `batch-direct-write`
  with the typed `lifecycle` field on the entity input. The status is validated
  against the entity lifecycle vocabulary; lifecycle is a **typed field**, not a
  free-form metadata blob. A `lifecycle` key inside free-form `metadata` is **not**
  a way to set it — it is carried inertly as ordinary metadata (under the typed
  envelope's free-form slot) and never changes the entity's lifecycle. There is no
  reserved metadata key and no special retire verb.
- **Read gating is uniform.** Every read path (`query`, `list entities`,
  traversal/relationship reads, and the MCP/HTTP equivalents) defaults to
  **live-only**: a `retired`/`superseded` entity is hidden. The one
  exception is an explicit **by-id `entity get`**, which always returns the
  entity and shows its `lifecycle.status` (the recovery/inspection path).
- The `--state` selector (config field `relationship_state`) controls
  visibility: `live` (default), `not-live` (only the gated-out set), `all`
  (everything). For entities the review-only values (`accepted`/`pending`/
  `reviewable`) resolve to `live`, since an entity has no review axis.

`ordered: low_to_high` marks a shared enum as semantically ranked. The order of
`values` is the rank order from lowest to highest. Query `order_by` clauses can
reference ordered enums with `enum_ref` to sort by rank instead of lexical string
order; `direction: asc` means low-to-high and `direction: desc` means
high-to-low.

---

## relationships

A list of relationship definitions connecting entity types.

```yaml
relationships:
  # Deterministic relationship — no proposal policy needed
  - name: product_from_vendor
    description: Deterministic product-to-vendor mapping from CPE structure.
    from: Product
    to: Vendor

  # Governed judgment relationship — uses proposal_policy + signals
  - name: asset_affected_by_vulnerability
    description: Accepted judgment that an asset is actually affected.
    from: Asset
    to: Vulnerability
    properties:
      installed_version: {}
      affected_basis: {}
    proposal_policy:
      signals:
        product_version_evidence:
          role: required
          always_review_on_unsure: true
        scanner_evidence:
          role: advisory
```

### RelationshipSchema

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `name` | string | **yes** | — | Unique relationship name |
| `from` | string | **yes** | — | Source entity type name |
| `to` | string | **yes** | — | Target entity type name |
| `cardinality` | string | no | `"many_to_many"` | Cardinality constraint |
| `properties` | dict | no | `{}` | Edge property definitions (same schema as entity properties) |
| `description` | string | no | `null` | Human-readable description |
| `inverse` | string | no | `null` | Name for the reverse traversal direction |
| `is_hierarchy` | bool | no | `false` | Mark as a hierarchical relationship |
| `proposal_policy` | ProposalPolicyConfig | no | `null` | Governed proposal policy (see [proposal_policy](#proposal_policy)) |
| `proposal_identity` | string | no | `"thesis_signature"` | `"thesis_signature"` groups trust by proposal thesis; `"relationship_tuple"` groups trust by edge tuple and requires `proposal_policy` |
| `write_policy` | string | no | `null` | `"direct"` or `"proposal_only"`. Governs direct edge writes for this type — see [Direct-Write Governance](#direct-write-governance-refuse_direct_writes). `null` inherits `runtime.default_write_policy`. |
| `write_tier` | string | no | `null` | `"governed_write"` or `"graph_write"`. Minimum permission tier allowed to direct-write edges of this type — see [Config-Declared Write Tiers](#config-declared-write-tiers-write_tier). Endpoint entity types are not part of the check. `null` keeps the default `graph_write` requirement. Rejected together with an explicit `proposal_only` `write_policy`. |

**Notes:**
- `from` and `to` must reference entity type names defined in `entity_types`.
- Edge `properties` use the same `PropertySchema` as entity properties.
- `inverse` enables traversing the relationship in reverse by name.
- Relationships with `proposal_policy` are intended to be governed: edges should be created through the proposal/group resolution flow when they are inferred, classified, or otherwise judgment-bearing. Raw `add_relationship` calls remain available for explicit deterministic facts.

### proposal_policy

The `proposal_policy` block on a relationship defines how candidate group proposals are evaluated and auto-resolved. It connects relationship types to the governed proposal pipeline.

```yaml
proposal_policy:
  signals:
    product_version_evidence:
      role: required
      always_review_on_unsure: true
    scanner_evidence:
      role: advisory
  auto_resolve_when: all_support
  auto_resolve_requires_prior_trust: trusted_only
  max_group_size: 1000
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `signals` | dict[str, SignalPolicyConfig] | `{}` | Per-signal-source guardrails keyed by the labels emitted from workflow `map_signals` steps |
| `auto_resolve_when` | string | `"all_support"` | `"all_support"` or `"no_contradict"` — when to auto-resolve proposals |
| `auto_resolve_requires_prior_trust` | string | `"trusted_only"` | `"trusted_only"` or `"trusted_or_watch"` — trust level required for auto-resolution |
| `max_group_size` | int | `1000` | Maximum candidates per group proposal |

**SignalPolicyConfig** (per signal source within `proposal_policy.signals`):

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `role` | string | `"required"` | `"blocking"`, `"required"`, or `"advisory"` — how the signal affects resolution |
| `always_review_on_unsure` | bool | `false` | Force manual review when this signal source returns `unsure` |
| `note` | string | `""` | Human-readable note about this signal source's role |

**Role semantics:**
- `blocking`: A `contradict` signal from this source blocks auto-resolution entirely.
- `required`: The signal is factored into the auto-resolve decision; `unsure` may trigger review.
- `advisory`: The signal is recorded but does not affect auto-resolution.

---

## named_queries

A dict of declarative query definitions. Every query declares an explicit
`mode`: `traversal` starts from an entry entity and walks relationship steps;
`collection` enumerates one entity type or relationship type directly.

```yaml
named_queries:
  parts_for_vehicle:
    mode: traversal
    description: "Find all parts that fit a specific vehicle"
    entry_point: Vehicle
    traversal:
      - relationship: fits
        direction: incoming
        filter:
          verified: true
    returns: "list[Part]"

  compatible_replacements:
    mode: traversal
    description: "Find replacement parts that also fit the same vehicle"
    entry_point: Part
    traversal:
      - relationship: replaces
        direction: both
        filter:
          direction: [equivalent, upgrade]
      - relationship: fits
        direction: outgoing
        constraint: "target.vehicle_id == $vehicle_id"
    returns: "list[Part]"

  all_active_fitments:
    mode: collection
    description: "List live fitment relationships"
    result_shape: relationship
    returns: fits
    where:
      edge.properties.verified:
        eq: true
```

### NamedQuerySchema

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `mode` | string | **yes** | — | Query mode: `traversal` or `collection` |
| `description` | string | no | `null` | Human-readable description |
| `entry_point` | string | for traversal | — | Entity type to start a traversal query from; invalid for collection queries |
| `traversal` | list | for traversal | — | Non-empty sequence of traversal steps; invalid for collection queries |
| `returns` | string | **yes** | — | Description of the return type |
| `result_shape` | string | no | `"path"` | Output shape: `entity`, `path`, or `relationship` |
| `dedupe` | string | no | shape-dependent | Result dedupe mode: `entity`, `path`, or `none`. Entity queries default to `entity`; path and relationship queries default to `path`. |
| `relationship_state` | string | no | `"live"` | Read-visibility state: `live`, `accepted`, `all`, `not-live`, `pending`, or `reviewable`. Gates entities by lifecycle and edges by review+lifecycle (see Read visibility below). The runtime/CLI selector for this is the `--state` flag (`state` on MCP/HTTP). |
| `allow_relationship_state_override` | bool | no | `false` | Whether runtime callers may override the visibility state |
| `where` | dict | no | `null` | Top-level predicate map for collection queries; invalid for traversal queries |
| `select` | dict | no | `null` | Projection map from output field name to query reference or literal value. When present, user-facing rows return `{values}` while receipts preserve source evidence for audit and feedback. |
| `order_by` | list | no | `[]` | Deterministic ordering rules. Each item uses `by`, optional `direction` (`asc` or `desc`), optional `value_type` (`string`, `int`, `integer`, `float`, `number`, `bool`, `date`, or `datetime`), and optional ordered `enum_ref`. |
| `include` | dict | no | `{}` | One-hop side-context includes keyed by alias. Includes decorate each primary row without advancing traversal or fanning out primary rows. |
| `limit` | int | no | `null` | Query-level output cap applied after traversal, dedupe, path budgets, ordering, and before projection. Result metadata reports pre-limit `total_results`, effective `limit`, and `limit_truncated`. |
| `max_paths` | int | no | `null` | Traversal-time retained-path frontier budget. It caps retained path states for each traversal step, limiting memory and receipt growth. It is not a total candidate-evaluation budget. |
| `max_paths_per_result` | int | no | `null` | Post-traversal final retained-path-per-result cap applied after traversal/dedupe, before ordering and `limit`. It does not bound traversal work. |

Validation rules:
- `mode: collection` queries omit `entry_point`, `traversal`, `include`, `max_paths`, and `max_paths_per_result`.
- Collection queries support `result_shape: entity` with `returns` set to an entity type, or `result_shape: relationship` with `returns` set to a canonical relationship name. Reverse aliases are rejected so direction is unambiguous.
- `mode: traversal` queries require `entry_point` and at least one traversal step. Put filters on traversal steps, include blocks, or related predicates; top-level `where` is reserved for collection queries.

Collection `where` scopes:
- `result_shape: entity`: paths start with `result` — e.g. `result.properties.status: {in: [active]}`.
- `result_shape: relationship`: `edge.properties.<field>` filters the edge itself (field names are validated against the relationship's configured schema), and `source.` / `target.` filter the endpoint entities — e.g. `source.properties.status: {eq: open}` keeps only edges whose from-entity is an open incident. `entry`, `current`, and `candidate` are accepted aliases (entry and current resolve to the from-entity, candidate to the to-entity); prefer `source`/`target` for readability.
- Traversal queries that intentionally return mixed entity types can set `returns: AnyEntity`; this skips homogeneous entity-type validation for entity/path rows.
- `result_shape: entity` requires `dedupe: entity`.
- For a traversal query with `result_shape: entity`, a concrete entity `returns` type, and `max_depth` on the final step, the engine may traverse through intermediate entity types and collect only the declared return type when at least one relationship in that final step can reach it. This is read-time typed collection, not a virtual or materialized relationship.
- `result_shape: relationship` requires `dedupe: path` or `none`.
- `relationship_state: pending` requires `result_shape: path` or `relationship`, and does not allow `dedupe: entity`.
- `relationship_state: reviewable` requires traversal `result_shape: path` or collection `result_shape: relationship`, and does not allow `dedupe: entity`.
- `required: false` traversal steps are optional continuations, not independent context enrichment. They require `result_shape: path` or `relationship`.
- `result_shape: relationship` may use `required: false` optional-continuation steps only when the final returned relationship step is still required.
- `max_paths` and `max_paths_per_result` require `result_shape: path` or `relationship`, and must be positive integers when set.
- `max_paths` is the retained-path frontier safety control. Once reached for a traversal step, the engine stops retaining/enqueuing more path states and avoids recording traversal receipts for the skipped frontier. Candidates that fail filters before any path is retained can still be evaluated; use a future candidate/work budget if total edge evaluation needs a separate cap.
- `max_paths_per_result` is a result-time evidence cap. It trims retained paths per final result entity after traversal, when result identity is known. It is distinct from `limit`: `max_paths_per_result` controls evidence fanout per result, while `limit` controls how many ordered rows are returned.
- `order_by` runs after traversal, dedupe, and path budgets, before `limit`.
- `order_by.value_type` and `order_by.enum_ref` are mutually exclusive.
- `order_by.enum_ref` must reference a top-level enum with `ordered: low_to_high`.
- Path budget truncation is reported separately with `path_truncated`, `retained_path_count`, and `truncation_reasons`.
- `path_truncated` means traversal was cut short by a path budget before the engine could prove completeness. It does not guarantee that every skipped frontier item would have produced a returned row.
- `total_path_count` is populated only when traversal completes. If traversal-time `max_paths` cuts exploration short, `total_path_count` is `null` because the full possible path count was intentionally not computed.
- Missing projected property or metadata refs resolve to `null`; missing `$input.*` refs fail execution.
- Missing `$path.<alias>...` refs for a non-required traversal alias resolve to `null` when that step did not match. Unknown aliases still fail validation/execution.
- Missing order values sort last, with stable graph-identity tie-breakers added automatically.
- Query-level `limit` is part of the named query contract. Runtime/API caller limits are only a caller-facing response cap.
- Projected query receipts retain source path/relationship evidence. User-facing projected results intentionally omit that source payload by default.
- `include` aliases must not collide with traversal aliases. Include anchors support `$entry`, `$result`, `$path.<alias>.source`, and `$path.<alias>.target`.
- Includes are one-hop side context. They do not advance the traversal frontier and do not fan out primary rows.
- Traversal queries with `result_shape: entity` may use includes only with `select`, so include values are projected explicitly while raw entity rows remain unchanged.
- `include.required: true` filters out a primary row when that include has no matches. `required: false` retains the row with `exists: false`, `count: 0`, and empty `items`.
- `include.many: false` expects at most one match and fails execution if multiple matches are found. Use `many: true` for repeated side context.
- Include `limit` is per include per primary row. It sets that include's `truncated` flag and is separate from query `limit`, `max_paths`, and `max_paths_per_result`.
- Include `order_by` refs may use `$edge`, `$source`, `$target`, or `$input`.

Relationship state modes:
- `live` includes active relationships whose review state is neither `pending` nor `rejected`. This includes deterministic/unreviewed state and approved state.
- `accepted` includes active relationships whose review status is `approved`.
- `pending` includes active relationships whose review status is `pending`.
- `reviewable` includes `live` relationships plus pending relationships. Use this for triage/context queries where an agent should see both accepted state and still-reviewable proposals in one evidence path.

Projection refs:
- All shapes: `$input.<name>`, `$entry.entity_type`, `$entry.entity_id`, `$entry.properties.<name>`, `$entry.metadata.<path>`, `$result.entity_type`, `$result.entity_id`, `$result.properties.<name>`, `$result.metadata.<path>`.
- `result_shape: path`: `$path.<alias>.edge.*`, `$path.<alias>.source.*`, and `$path.<alias>.target.*`. Path refs require a traversal `as` alias.
- `result_shape: relationship`: `$relationship.*`, `$from_entity.*`, and `$to_entity.*`.
- Include refs: `$include.<alias>.exists`, `$include.<alias>.count`, `$include.<alias>.truncated`, `$include.<alias>.items`. Singular includes also support `$include.<alias>.edge.*`, `$include.<alias>.source.*`, and `$include.<alias>.target.*`; `many: true` includes require selecting `items`, `count`, or existence flags.

Projection and ordering example:

```yaml
named_queries:
  remediation_exposure_context:
    mode: traversal
    entry_point: Vulnerability
    returns: Asset
    result_shape: path
    dedupe: path
    traversal:
      - as: affected_product
        relationship: vulnerability_affects_product
        direction: outgoing
      - as: exposure
        relationship: asset_runs_product
        direction: incoming
    select:
      vulnerability_id: $entry.entity_id
      asset_id: $result.entity_id
      hostname: $result.properties.hostname
      exposure_edge_key: $path.exposure.edge.edge_key
      priority: $path.exposure.edge.properties.priority
      review_status: $path.exposure.edge.metadata.assertion.review.status
    order_by:
      - by: $result.properties.criticality
        direction: desc
        enum_ref: criticality
      - by: $path.exposure.edge.properties.priority
        direction: desc
        enum_ref: criticality
      - by: $result.entity_id
        direction: asc
    max_paths: 500
    max_paths_per_result: 20
    limit: 50
```

Include example:

```yaml
named_queries:
  vulnerability_asset_context:
    mode: traversal
    entry_point: Vulnerability
    returns: Asset
    result_shape: path
    relationship_state: reviewable
    traversal:
      - as: affected_product
        relationship: vulnerability_affects_product
        direction: outgoing
      - as: installed_product
        relationship: asset_runs_product
        direction: incoming
    include:
      exposure:
        from: $result
        relationship: asset_vulnerability_posture
        direction: outgoing
        many: true
        where:
          edge.properties.status:
            eq: exposed
      owner:
        from: $result
        relationship: asset_owned_by
        direction: outgoing
      services:
        from: $result
        relationship: service_depends_on_asset
        direction: incoming
        many: true
        limit: 10
      exceptions:
        from: $result
        relationship: asset_has_exception
        direction: outgoing
        many: true
        where:
          target.properties.status:
            in: [active, approved]
```

This returns the primary vulnerability-to-asset path once per row, with the
configured owner, service, exposure, and exception context attached under the
row's `includes` map. Include context can also be selected with
`$include.<alias>...` projection refs.

### TraversalStep

Each step in the traversal sequence:

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `relationship` | string or list[string] | **yes** | — | Relationship name(s) to traverse. A list fans out across listed types in declared order and merges results. Candidates within each relationship type are stable-sorted when path budgets apply. |
| `direction` | string | no | `"outgoing"` | `outgoing`, `incoming`, or `both` |
| `filter` | dict | no | `null` | Property filters on edges or target entities |
| `target_filter` | dict | no | `null` | Exact-match property filters on candidate entities |
| `where` | dict | no | `null` | Structured traversal predicates. Top-level paths must start with `edge`, `source`, `target`, `current`, `candidate`, or `entry`. |
| `where_related` | list | no | `[]` | Related-edge predicates; at least one matching related edge must exist for each item |
| `where_not_related` | list | no | `[]` | Related-edge predicates; no matching related edge may exist for any item |
| `constraint` | string | no | `null` | Constraint expression to apply during traversal |
| `constraint_value_type` | string | no | `null` | Optional typed constraint comparison: `string`, `int`, `integer`, `float`, `number`, `bool`, `date`, or `datetime` |
| `exclude_if_related` | list | no | `[]` | Legacy related-edge exclusion checks |
| `max_depth` | int | no | `1` | BFS depth for this step (1 = direct neighbors only). By default, results include entities from depth 1 through max_depth; final-step typed collection on entity-shaped traversal queries may traverse intermediates while emitting only the declared `returns` type. |
| `required` | bool | no | `true` | Optional continuation. When `false`, preserves the incoming path if no edge passes relationship state, filters, predicates, related predicates, constraints, and policies. Matching edges still continue to the matched neighbor, which becomes the current `$result`. |
| `as` | string | no | `null` | Alias for the traversed path segment in path/relationship outputs |

**Optional continuation semantics:**

`required: false` makes a traversal step optional, but it does not attach
independent neighbor context to the same result row. When a non-required step
matches, traversal continues to the matched neighbor and that neighbor becomes
the current `$result`. When no candidate passes relationship state, filters,
predicates, related predicates, constraints, and policies, the incoming path is
preserved and `$result` remains the prior current entity.

Use `required: false` for optional successor, replacement, or follow-on paths.
Do not use it when the desired shape is "return this same asset, but attach
owner/service/control facts as additional row context." Use `include` for that:
it attaches bounded one-hop side context to each primary result row without
changing the traversal result or fanning out rows. Use read tools for ad hoc
context that is not worth baking into the named query contract.

**Direction semantics:**
- `outgoing`: Follow edges from entry point (source -> target)
- `incoming`: Follow edges into entry point (target -> source)
- `both`: Follow edges in either direction

**Structured predicate example:**

```yaml
named_queries:
  pending_exposures:
    mode: traversal
    entry_point: Vulnerability
    returns: asset_vulnerability_posture
    result_shape: relationship
    relationship_state: pending
    allow_relationship_state_override: true
    traversal:
      - relationship: asset_vulnerability_posture
        direction: incoming
        as: exposure
        where:
          edge.metadata.assertion.lifecycle.status:
            eq: active
          target.properties.environment:
            eq: production
        where_not_related:
          - relationship: asset_remediated_vulnerability
            direction: outgoing
            edge:
              properties.verification_status:
                eq: verified
            target:
              entity_id:
                eq: $entry.entity_id
```

Supported structured predicate operators are `eq`, `ne`, `in`, `not_in`,
`lt`, `lte`, `gt`, `gte`, `exists`, `contains`, and `icontains`. `contains`
and `icontains` require string values; `icontains` compares case-insensitively.
Predicate values may reference:

- `$input.<field>`
- `$entry.<field>`
- `$current.<field>`
- `$candidate.<field>`
- `$edge.<field>`
- `$source.<field>`
- `$target.<field>`
- `$path.<alias>.edge.<field>`
- `$path.<alias>.source.<field>`
- `$path.<alias>.target.<field>`

Use `$path` references when filtering an include or predicate against an
already-retained traversal path. Unknown path aliases fail unless the alias
belongs to an absent `required: false` traversal segment, where the missing
path behaves like a missing value and ordinary predicates fail. `$path`
references target existing traversal aliases, not include aliases.

```yaml
include:
  remediations:
    from: $path.exposure.source
    relationship: asset_remediated_vulnerability
    direction: outgoing
    many: true
    where:
      target.entity_id:
        eq: $path.exposure.target.entity_id
```

---

## constraints

A list of validation rules evaluated during `cruxible_evaluate`. Constraints check **graph state** — they flag suspicious or invalid data already in the graph.

```yaml
constraints:
  - name: replacement_same_category
    rule: "replaces.FROM.category == replaces.TO.category"
    severity: warning
    description: "Replacement parts should be in the same category"
```

### ConstraintSchema

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `name` | string | **yes** | — | Unique constraint name |
| `rule` | string | **yes** | — | Rule expression (see syntax below) |
| `severity` | string | no | `"warning"` | `warning` or `error` |
| `description` | string | no | `null` | Human-readable description |

### Rule Syntax

Constraints compare properties across relationship endpoints:

```
RELATIONSHIP.FROM.property <op> RELATIONSHIP.TO.property
```

- `RELATIONSHIP`: The relationship name (e.g., `replaces`)
- `FROM`: The source entity's property
- `TO`: The target entity's property
- `<op>`: One of `==`, `!=`, `>`, `>=`, `<`, `<=`
- Identifiers may contain letters, digits, underscores, and hyphens

**Examples:**
- `replaces.FROM.category == replaces.TO.category` — flags any `replaces` edge where the source and target parts have different categories.
- `replaces.FROM.priority > replaces.TO.priority` — flags any `replaces` edge where the source priority does not exceed the target priority.

---

## quality_checks

Evaluate-time graph quality checks run during `cruxible_evaluate`. Six check kinds are available, distinguished by the `kind` field.

### 1. property

Check a top-level property on entities or relationships.

```yaml
quality_checks:
  - name: cve_id_format
    kind: property
    severity: error
    target: entity
    entity_type: Vulnerability
    property: cve_id
    rule: pattern
    pattern: "^CVE-\\d{4}-\\d{4,}$"
```

| Field | Type | Description |
|-------|------|-------------|
| `kind` | `"property"` | |
| `target` | `"entity"` or `"relationship"` | What to check |
| `entity_type` | string | Required when `target: entity` |
| `relationship_type` | string | Required when `target: relationship` |
| `property` | string | Property name to check |
| `rule` | string | `"required"`, `"non_empty"`, `"type"`, or `"pattern"` |
| `expected_type` | string | Required when `rule: type` |
| `pattern` | string | Regex pattern, required when `rule: pattern` |

### 2. json_content

Check JSON array-of-object content on a `json`-typed property.

```yaml
  - name: affected_versions_have_useful_keys
    kind: json_content
    severity: warning
    target: relationship
    relationship_type: vulnerability_affects_product
    property: affected_versions
    rule: required_nested_keys
    keys: [version_start_including, version_end_excluding, version_exact, fixed_version]
    match: any

  - name: no_empty_affected_version_objects
    kind: json_content
    severity: error
    target: relationship
    relationship_type: vulnerability_affects_product
    property: affected_versions
    rule: no_empty_objects_in_array
```

| Field | Type | Description |
|-------|------|-------------|
| `kind` | `"json_content"` | |
| `target` | `"entity"` or `"relationship"` | What to check |
| `entity_type` / `relationship_type` | string | Target type |
| `property` | string | JSON property name to check |
| `rule` | string | `"no_empty_objects_in_array"` or `"required_nested_keys"` |
| `keys` | list[string] | Required when `rule: required_nested_keys` — keys to look for |
| `match` | string | `"any"` or `"all"` — required when `rule: required_nested_keys` |

### 3. uniqueness

Check entity-property uniqueness, optionally across compound keys.

```yaml
  - name: unique_vendor_product_pair
    kind: uniqueness
    severity: error
    entity_type: Product
    properties: [vendor_name, product_name]
```

| Field | Type | Description |
|-------|------|-------------|
| `kind` | `"uniqueness"` | |
| `entity_type` | string | Entity type to check |
| `properties` | list[string] | One or more property names that must be unique together |

### 4. bounds

Check entity or relationship counts against a numeric range.

```yaml
  - name: minimum_products
    kind: bounds
    severity: warning
    target: entity_count
    entity_type: Product
    min_count: 10
```

| Field | Type | Description |
|-------|------|-------------|
| `kind` | `"bounds"` | |
| `target` | `"entity_count"` or `"relationship_count"` | What to count |
| `entity_type` / `relationship_type` | string | Target type |
| `min_count` | int | Optional lower bound |
| `max_count` | int | Optional upper bound (at least one of min/max required) |

### 5. cardinality

Check per-entity relationship counts in one direction.

```yaml
  - name: products_have_exactly_one_vendor
    kind: cardinality
    severity: error
    entity_type: Product
    relationship_type: product_from_vendor
    direction: outgoing
    min_count: 1
    max_count: 1
```

| Field | Type | Description |
|-------|------|-------------|
| `kind` | `"cardinality"` | |
| `entity_type` | string | Entity type to check |
| `relationship_type` | string | Relationship type to count |
| `direction` | `"incoming"` or `"outgoing"` | Edge direction relative to the entity |
| `min_count` | int | Optional lower bound |
| `max_count` | int | Optional upper bound (at least one of min/max required) |

### 6. relationship_property_consistency

Check that an entity property agrees with a related entity reached through a
specific relationship. Use this when configs intentionally keep denormalized
inspection fields but still need the canonical relationship to stay aligned.

```yaml
  - name: product_vendor_id_matches_vendor_edge
    kind: relationship_property_consistency
    severity: error
    entity_type: Product
    relationship_type: product_from_vendor
    direction: outgoing
    source_property: vendor_id
    target_property: vendor_id
    allow_missing_source: false

  - name: product_vendor_name_matches_vendor_edge
    kind: relationship_property_consistency
    severity: warning
    entity_type: Product
    relationship_type: product_from_vendor
    direction: outgoing
    source_property: vendor_name
    target_property: name
    allow_missing_source: true
```

| Field | Type | Description |
|-------|------|-------------|
| `kind` | `"relationship_property_consistency"` | |
| `entity_type` | string | Source entity type to check |
| `relationship_type` | string | Relationship connecting source to related entity |
| `direction` | `"incoming"` or `"outgoing"` | Edge direction relative to the source entity |
| `source_property` | string | Source entity property to compare |
| `target_property` | string | Related entity property to compare; omit or use `entity_id` to compare against the related entity id |
| `allow_missing_source` | bool | Skip rows where the source property is absent or empty |

**Common fields across all quality check kinds:**

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `name` | string | **yes** | — | Unique check name |
| `kind` | string | **yes** | — | Check kind discriminator |
| `description` | string | no | `null` | Human-readable description |
| `severity` | string | no | `"warning"` | `"warning"` or `"error"` |

---

## feedback_profiles

Structured feedback vocabularies scoped to a relationship type. Feedback profiles define the **reason codes** an agent or human can attach to feedback, and the **scope keys** that enable grouping and analysis. This is the foundation of Loop 1: feedback drives constraint and decision policy suggestions.

```yaml
feedback_profiles:
  fits:
    version: 2
    reason_codes:
      legacy_unsupported:
        description: "Legacy environment is unsupported"
        remediation_hint: decision_policy
        required_scope_keys: [category, make]
      fitment_mismatch:
        description: "Part category mismatches vehicle make"
        remediation_hint: constraint
        required_scope_keys: [category, make]
    scope_keys:
      category: FROM.category
      make: TO.make
```

### FeedbackProfileSchema

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `version` | int | `1` | Profile version — bump when reason codes or scope keys change semantically |
| `reason_codes` | dict[str, FeedbackReasonCodeSchema] | `{}` | Named reason codes agents can attach to feedback |
| `scope_keys` | dict[str, FeedbackPathRef] | `{}` | Named scope dimensions extracted from graph state at feedback time |

### FeedbackReasonCodeSchema

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `description` | string | **required** | What this reason code means |
| `remediation_hint` | string | `"unknown"` | `"constraint"`, `"decision_policy"`, `"quality_check"`, `"provider_fix"`, or `"unknown"` — guides `analyze_feedback` to produce the right kind of suggestion |
| `required_scope_keys` | list[string] | `[]` | Scope keys that must be present when this code is used |

### FeedbackPathRef

Scope key paths follow the pattern `(FROM|TO|EDGE).<property>`:
- `FROM.category` — extracts the `category` property from the source entity
- `TO.make` — extracts the `make` property from the target entity
- `EDGE.confidence` — extracts the `confidence` property from the edge

**How it works:** When an agent submits structured feedback with a `reason_code` and `scope_hints`, `analyze_feedback` groups matching feedback records and produces suggestions:
- Reason codes with `remediation_hint: constraint` produce constraint suggestions
- Reason codes with `remediation_hint: decision_policy` produce decision policy suggestions
- Other hints produce quality check or provider fix candidates

---

## outcome_profiles

Structured outcome vocabularies for trust calibration and debugging (Loop 2). Outcome profiles define the **outcome codes** and **scope keys** attached to recorded outcomes, scoped to either a resolution anchor (proposal outcomes) or a receipt anchor (query/workflow outcomes).

```yaml
outcome_profiles:
  fits_resolution:
    anchor_type: resolution
    relationship_type: fits
    version: 1
    outcome_codes:
      wrong_match:
        description: "The resolved match was incorrect"
        remediation_hint: trust_adjustment
        required_scope_keys: [category]
      stale_data:
        description: "Source data was outdated at resolution time"
        remediation_hint: provider_fix
    scope_keys:
      category: RESOLUTION.relationship_type

  parts_query:
    anchor_type: receipt
    surface_type: query
    surface_name: parts_for_vehicle
    version: 1
    outcome_codes:
      missing_results:
        description: "Expected results were not returned"
        remediation_hint: workflow_fix
    scope_keys:
      query: SURFACE.name
```

### OutcomeProfileSchema

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `anchor_type` | string | **required** | `"resolution"` or `"receipt"` |
| `version` | int | `1` | Profile version |
| `relationship_type` | string | `null` | Required for `anchor_type: resolution` |
| `workflow_name` | string | `null` | Optional for resolution anchors |
| `surface_type` | string | `null` | Required for `anchor_type: receipt` — `"query"`, `"workflow"`, or `"operation"`. Only `"query"` and `"workflow"` are validated and bound to a surface; `"operation"` is accepted but inert (no binding). |
| `surface_name` | string | `null` | Required for `anchor_type: receipt` |
| `outcome_codes` | dict[str, OutcomeCodeSchema] | `{}` | Named outcome codes |
| `scope_keys` | dict[str, OutcomePathRef] | `{}` | Named scope dimensions |

### OutcomeCodeSchema

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `description` | string | **required** | What this outcome code means |
| `remediation_hint` | string | `"unknown"` | `"trust_adjustment"`, `"require_review"`, `"decision_policy"`, `"provider_fix"`, `"workflow_fix"`, `"graph_fix"`, or `"unknown"` |
| `required_scope_keys` | list[string] | `[]` | Scope keys that must be present |

### OutcomePathRef

Scope key paths depend on anchor type. Valid fields per prefix:

**Resolution anchors:**

| Prefix | Valid fields |
|--------|-------------|
| `RESOLUTION` | `resolution_id`, `relationship_type`, `action`, `trust_status`, `resolved_by` |
| `GROUP` | `group_signature` |
| `WORKFLOW` | `name`, `receipt_id`, `trace_ids` |
| `THESIS` | _(any thesis_facts key)_ |

**Receipt anchors:**

| Prefix | Valid fields |
|--------|-------------|
| `RECEIPT` | `receipt_id`, `operation_type` |
| `SURFACE` | `type`, `name` |
| `TRACESET` | `trace_ids`, `provider_names`, `trace_count` |

**Validation:** Resolution profiles require `relationship_type` and must not set `surface_type`/`surface_name`. Receipt profiles require `surface_type` and `surface_name` and must not set `relationship_type`/`workflow_name`.

---

## mutation_guards

Mutation guards reject configured state writes when a configured state-side
condition does not pass. They are enforced on direct entity writes, batch direct
writes, and canonical workflow apply: entity-property guards on entity writes;
relationship-evidence guards (the evidence-requirement condition, scoped to a
relationship type) on relationship writes. Release-backed `state pull`
re-materialization is exempt, since it replays already-accepted upstream state
rather than authoring new writes.
They are appendable in overlay composition and are not allowed in `kind:
ontology` configs.

Guards run on *writes*, not on state reconciliation. The upstream pull-apply on a
release-backed overlay (`cruxible state pull`) re-materializes the new upstream
release plus the overlay's existing local state and is deliberately guard-exempt:
the local side is a re-materialization of state that already passed its guards
when authored (re-materializing an unchanged value is not a transition and never
fires a guard), the upstream side is governed/published state that this overlay
must not re-litigate, and there is no write actor at merge time for actor-identity
guards to evaluate. The one merge-time risk that is genuinely novel — local edges
dangling onto upstream entities removed in the new release — is enforced
separately as a blocking pull conflict before the merge is materialized.

Entity-property guards fire on any direct write that **results in** the guarded
property value — creating an entity with the value and changing an existing
entity to the value are both covered. Updates that re-assert the value an entity
already holds are not transitions and do not fire.

Relationship evidence guards fire on writes to the configured relationship type
and require the resulting relationship evidence to meet the configured floor.
Use them for observation-style relationships whose claims must cite
dereferenceable source material. Decision-style relationships should usually
declare no evidence floor and rely on ambient attribution: provenance, receipts,
actor context, and review history. Every guard field is load-bearing.

The `condition` is a **discriminated union** keyed by an explicit `condition.type`
field — one of `query`, `actor`, `co_write`, or `evidence`. The type is required;
guard shape is never inferred from which keys are present. The guard-level
`operation` / `effect` discriminator fields deliberately do not exist.

```yaml
mutation_guards:
  - name: work_item_closed_requires_review
    entity_type: WorkItem
    property: status
    new_value: closed
    condition:
      type: query
      query_name: approved_review_for_work_item
      params:
        work_item_id: "$entity.entity_id"
      min_count: 1
    message: "Work item cannot be closed until approved review exists."

  - name: review_request_approval_requires_authorized_actor
    entity_type: ReviewRequest
    property: status
    new_value: approved
    condition:
      type: actor
      allowed_actor_ids: [authorized-reviewer]
    message: "ReviewRequest approvals require an authorized actor."

  - name: work_item_closed_requires_co_written_review
    entity_type: WorkItem
    property: status
    new_value: closed
    condition:
      type: co_write
      requires:
        entity_type: ReviewRequest
        via_relationship: review_request_for_work_item
        kind: approval        # optional: filter the co-written entity's `kind` property
    message: "Closing requires a review created in the same write."

  - name: finding_support_requires_source_evidence
    relationship_type: finding_supports_work_item
    condition:
      type: evidence
      require_evidence: source_evidence
      min_count: 1
    message: "Observation claims require source evidence."
```

### MutationGuardSchema

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `name` | string | **yes** | — | Unique guard name |
| `entity_type` | string | entity guards | — | Entity type the write applies to |
| `property` | string | entity guards | — | Property that must be present in the incoming write |
| `new_value` | any or list | entity guards | — | Guarded resulting value(s) after config property normalization. A scalar guards one value; a list guards several (the guard fires when the write results in any listed value) |
| `relationship_type` | string | relationship evidence guards | — | Relationship type the write applies to |
| `condition` | discriminated union on `type` | **yes** | — | Condition that must pass (see types below) |
| `message` | string | no | `null` | Optional user-facing rejection detail |
| `where` | predicate map | no | `null` | Entity-property guards only: scopes the trigger so the guard fires only when the mutated entity matches (candidate scope) |
| `where_related` | list | no | `[]` | Entity-property guards only: related-edge predicates; the guard fires only when every listed edge exists (and matches its inner predicates) on the mutated entity |
| `where_not_related` | list | no | `[]` | Entity-property guards only: related-edge predicates; the guard fires only when no listed edge exists on the mutated entity |

The optional `where` predicate scopes *when* an entity-property guard fires. It
uses the same predicate vocabulary as query `where` (`eq`/`in`/`not_in`/`lt`/`gt`/…)
but is restricted to the `candidate` scope — both predicate paths and any
`$`-reference operands must start with `candidate.` and read only the mutated
(proposed) entity. The guard fires only when the proposed entity matches;
otherwise the write is allowed regardless of the condition. `where` is rejected
on relationship evidence guards.

Guards may also carry `where_related` / `where_not_related` (related-edge trigger
scoping, the same `RelatedPredicateSpec` shape as query traversal steps —
`relationship`/`direction` plus per-scope `edge`/`source`/`target`/`current`/
`candidate`/`entry` predicates). They are anchored on the mutated entity and
evaluated against the proposed graph at `live` visibility (the canonical visible
state; there is no per-guard visibility knob). The guard fires only when every
`where_related` edge exists (and matches its inner predicates) and no
`where_not_related` edge exists. Like `where`, they are entity-property guards
only and are rejected on relationship evidence guards.

The `condition.type` discriminator selects the condition variant:

| `type` | Condition | Applies to |
|--------|-----------|------------|
| `query` | NamedQueryResultCountGuardCondition | entity guards |
| `actor` | ActorIdentityGuardCondition | entity guards |
| `co_write` | CoWriteGuardCondition | entity guards |
| `evidence` | EvidenceRequirementGuardCondition | relationship guards |

### NamedQueryResultCountGuardCondition (`type: query`)

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `type` | `query` | **yes** | — | Condition discriminator |
| `query_name` | string | **yes** | — | Named query to execute against the proposed graph state |
| `params` | dict | no | `{}` | Query params; values may reference write context |
| `min_count` | int | conditional | `null` | Minimum result count; at least one of `min_count` or `max_count` is required |
| `max_count` | int | conditional | `null` | Maximum result count; at least one of `min_count` or `max_count` is required |

Supported param references:

- `$entity.entity_type`
- `$entity.entity_id`
- `$entity.properties.<name>`
- `$current.properties.<name>`
- `$new_value`
- `$old_value`

On entity creation there is no prior state: `$old_value` resolves to `null`,
and `$current.properties.<name>` cannot resolve, so a guard using `$current`
refs fails closed on creation. Prior-state refs therefore make a guard
transition-only in practice.

### ActorIdentityGuardCondition (`type: actor`)

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `type` | `actor` | **yes** | — | Condition discriminator |
| `allowed_actor_ids` | list[string] | **yes** | — | Actor ids allowed to perform the guarded transition |

Actor identity conditions compare the current write's
`GovernedActorContext.actor_id` to `allowed_actor_ids`. Missing actor context
fails the guard. This condition is useful for guarded approval transitions where
the authority comes from authenticated runtime credential identity or a Cloud
control-plane supplied actor context.

### CoWriteGuardCondition (`type: co_write`)

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `type` | `co_write` | **yes** | — | Condition discriminator |
| `requires` | CoWriteRequirement | **yes** | — | The entity that must be co-created in the same write |

#### CoWriteRequirement

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `entity_type` | string | **yes** | — | Entity type that THIS write must create |
| `via_relationship` | string | **yes** | — | Relationship that must link the created entity to the guarded `$entity` |
| `kind` | string | no | `null` | When set, the co-written entity's `kind` property must equal this value |

Co-write conditions pass only when the **current write delta** both creates an
entity of `requires.entity_type` (optionally `kind`-filtered) AND creates a
`requires.via_relationship` edge linking it to the guarded `$entity`. "Created in
THIS write" means present in the write delta — a stale pre-existing linked entity
or a pre-existing edge does not satisfy the requirement; the entity and its
linking edge must both be new in this write. The required edge may attach the
co-written entity to `$entity` in either direction; the relationship's configured
endpoints determine the valid direction. Because the entity and edge must arrive
together, co-write conditions are satisfiable through the batch direct-write path
(which writes entities and edges in one delta) but not through entity-only writes
or step-by-step workflow apply, where they fail closed.

### EvidenceRequirementGuardCondition (`type: evidence`)

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `type` | `evidence` | **yes** | — | Condition discriminator |
| `require_evidence` | `source_evidence` | **yes** | — | Require dereferenceable source-evidence refs resolved from registered source artifacts |
| `min_count` | int | no | `1` | Minimum number of source-evidence refs required; must be at least `1` |

Evidence requirement guards are relationship-scoped: they require
`relationship_type` and must not define `entity_type`, `property`, or
`new_value`. The guard counts resolved `source_evidence` locators, not free-text
`evidence_rationale` alone. Generic `evidence_refs` only satisfy the floor when
they are dereferenceable `source_artifact` refs with chunk identity and content
hash metadata, as produced by source artifact registration.

For batch direct writes, guards evaluate against the proposed batch graph, so
valid same-batch entities and relationships can satisfy the named query before
anything is committed.

Dry-run (and real) `added`/`updated` counts cover **explicit writes only** —
when derived relationships ship, derived-edge effects will be reported in a
separate additive field, never folded into the write counts. Guard conditions
already see query-time derived edges in dry-runs by construction, since guards
evaluate the named-query engine against the proposed graph.

---

## decision_policies

Action-side behavior rules applied during query execution or workflow proposal. Decision policies are the **action controls** that complement state-side constraints. While constraints flag bad data in the graph, decision policies change what queries return or what workflows propose.

```yaml
decision_policies:
  - name: suppress_legacy_honda_brakes
    description: "Don't return legacy brake parts for Honda vehicles"
    applies_to: query
    query_name: parts_for_vehicle
    relationship_type: fits
    effect: suppress
    match:
      from:
        category: brakes
      to:
        make: Honda
    rationale: "Legacy brake fitments for Honda are unreliable — see feedback batch 2026-03"

  - name: review_substitutes_plant_b
    description: "Require manual review for substitute proposals at Plant B"
    applies_to: workflow
    workflow_name: propose_substitutes
    relationship_type: safe_to_substitute
    effect: require_review
    match:
      context:
        scope_plant_id: PLANT-B
    expires_at: "2026-06-30"
```

### DecisionPolicySchema

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `name` | string | **yes** | — | Unique policy name |
| `description` | string | no | `null` | Human-readable description |
| `rationale` | string | no | `""` | Why this policy exists (reference to feedback, incident, etc.) |
| `applies_to` | string | **yes** | — | `"query"` or `"workflow"` |
| `query_name` | string | conditional | `null` | Required when `applies_to: query` |
| `workflow_name` | string | conditional | `null` | Required when `applies_to: workflow` |
| `relationship_type` | string | **yes** | — | Relationship type this policy applies to |
| `effect` | string | **yes** | — | `"suppress"` (query only) or `"require_review"` |
| `match` | DecisionPolicyMatch | no | `{}` | Exact-match selectors (see below) |
| `expires_at` | string | no | `null` | Optional expiry date (ISO 8601) |

### DecisionPolicyMatch

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `from` | dict | `{}` | Exact-match on source entity properties |
| `to` | dict | `{}` | Exact-match on target entity properties |
| `edge` | dict | `{}` | Exact-match on edge properties |
| `context` | dict | `{}` | Exact-match on workflow context (e.g., scope keys) |

**Validation:**
- Query policies require `query_name` and only support `effect: suppress`.
- Workflow policies require `workflow_name` and support both effects.

**Keep the distinction clean:**
- **Constraints** = suspicious or invalid graph state (evaluated by `cruxible_evaluate`)
- **Decision policies** = query/workflow behavior changes (enforced at execution time)

---

## contracts

Typed payload contracts for provider inputs and outputs. Contracts define the fields a provider expects to receive and the shape of what it returns.

Common plumbing contracts are built in and do not need to be declared:

- `cruxible.EmptyInput`: no input fields and no extras.
- `cruxible.JsonObject`: any JSON-serializable object payload.
- `cruxible.JsonItems`: `{items: <json>}`.
- `cruxible.ParsedTabularBundle`: `{artifact, tables, files, diagnostics}` from the common tabular parser.

```yaml
contracts:
  PublicKevRows:
    description: "Rows of joined KEV + NVD + EPSS data"
    fields:
      items:
        type: json
        json_schema:
          type: array
          items:
            type: object
            properties:
              cve_id: {type: string}
              vendor_id: {type: string}
              product_id: {type: string}
              cvss_score: {type: number}
```

### ContractSchema

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `description` | string | no | `null` | Human-readable description |
| `fields` | dict[str, PropertySchema] | **yes** | — | Field definitions. Contract fields must define `type` explicitly and are required by default. |
| `allow_extra` | bool | no | `false` | Allow undeclared JSON-serializable fields; used by `cruxible.JsonObject` |

---

## artifacts

Pinned external artifacts referenced by providers. Artifacts represent data bundles, models, or other resources that providers depend on. The `digest` hash enables reproducible builds — the workflow lock verifies the live artifact matches the hash at lock time.

```yaml
artifacts:
  public_kev_bundle:
    kind: directory
    uri: ./data
    digest: sha256:f884e5f8fad66c6bba54face97863137833ab26035d7a4cda333063d0ab224f9
```

### ProviderArtifactSchema

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `kind` | string | **yes** | — | Artifact kind (e.g., `directory`, `file`, `model`) |
| `uri` | string | **yes** | — | Location (relative path, URL, etc.) |
| `digest` | string | no | `null` | Content hash for reproducibility verification (`sha256:`-prefixed) |
| `metadata` | dict | no | `{}` | Arbitrary metadata |

---

## providers

Versioned executable leaves used by workflow steps. A provider is a callable that takes a typed input, produces a typed output, and generates an execution trace for the receipt chain.

```yaml
providers:
  parse_public_kev_bundle:
    kind: function
    description: >
      Parse the pinned public KEV artifact into generic tables.
    contract_in: cruxible.JsonObject
    contract_out: cruxible.ParsedTabularBundle
    ref: cruxible_core.providers.common.tabular.load_tabular_artifact_bundle
    version: "1.0.0"
    deterministic: true
    runtime: python
    artifact: public_kev_bundle

  normalize_public_kev_reference:
    kind: function
    description: >
      Normalize explicit KEV, EPSS, and NVD rows selected by workflow config.
    contract_in: PublicKevReferenceInput
    contract_out: cruxible.JsonItems
    ref: providers.normalize_public_kev_reference
    version: "1.0.0"
    deterministic: true
    runtime: python
```

### ProviderSchema

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `kind` | string | **yes** | — | `"function"`, `"model"`, or `"tool"` |
| `description` | string | no | `null` | What this provider does |
| `contract_in` | string or inline ContractSchema | **yes** | — | Input contract reference. May be config-defined, built-in (`cruxible.*`), or an inline contract object. |
| `contract_out` | string or inline ContractSchema | **yes** | — | Output contract reference. May be config-defined, built-in (`cruxible.*`), or an inline contract object. |
| `ref` | string | **yes** | — | Callable reference (e.g., `module.function_name`) |
| `version` | string | **yes** | — | Semantic version for lock-file reproducibility |
| `deterministic` | bool | no | `true` | Whether the provider produces identical output for identical input |
| `artifact` | string | no | `null` | Name of artifact this provider depends on (must exist in `artifacts`) |
| `runtime` | string | no | `"python"` | Execution runtime: `"python"`, `"http_json"`, or `"command"` |
| `side_effects` | bool | no | `false` | Whether the provider has side effects |
| `config` | dict | no | `{}` | Provider-specific configuration |

Only providers whose job is to load or parse a source artifact should declare
`artifact`. Domain transform providers should receive the least information
they need through explicit workflow input fields. Required table selection and
source-table mapping belong in workflow config:

```yaml
- id: raw_tables
  provider: parse_public_kev_bundle
  input:
    expected_tables:
      - known_exploited_vulnerabilities
  as: raw_tables

- id: rows
  provider: normalize_public_kev_reference
  input:
    kev_rows: $steps.raw_tables.tables.known_exploited_vulnerabilities.rows
  as: rows
```

---

## workflows

Declarative step-based execution plans. Workflows compose queries, providers, and graph mutations into reproducible pipelines. A workflow `type` declares whether it is `utility`, `canonical`, `proposal`, or `decision_support`.

```yaml
workflows:
  build_public_kev_reference:
    type: canonical
    description: >
      Build the canonical public KEV reference layer from bundled data.
    contract_in: cruxible.EmptyInput
    steps:
      - id: raw_tables
        provider: parse_public_kev_bundle
        input:
          expected_tables:
            - known_exploited_vulnerabilities
        as: raw_tables

      - id: rows
        provider: normalize_public_kev_reference
        input:
          kev_rows: $steps.raw_tables.tables.known_exploited_vulnerabilities.rows
        as: rows

      - id: vendors
        make_entities:
          entity_type: Vendor
          items: $steps.rows.items
          entity_id: $item.vendor_id
          properties:
            vendor_id: $item.vendor_id
            name: $item.vendor_name
        as: vendors

      - id: product_vendor
        make_relationships:
          relationship_type: product_from_vendor
          items: $steps.rows.items
          from_type: Product
          from_id: $item.product_id
          to_type: Vendor
          to_id: $item.vendor_id
        as: product_vendor

      - id: apply_vendors
        apply_entities:
          entities_from: vendors
        as: apply_vendors

      - id: apply_product_vendor
        apply_relationships:
          relationships_from: product_vendor
        as: apply_product_vendor
    returns: apply_product_vendor
```

### WorkflowSchema

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `description` | string | no | `null` | What this workflow does |
| `type` | `utility`, `canonical`, `proposal`, or `decision_support` | no | `utility` | Workflow contract for execution and agent-facing lifecycle |
| `contract_in` | string or inline ContractSchema | **yes** | — | Workflow input contract reference. May be config-defined, built-in (`cruxible.*`), or an inline contract object. |
| `contract_out` | string or inline ContractSchema | no | `null` | Optional final output contract reference. Validates only the payload selected by `returns` after all workflow steps have run. |
| `steps` | list[WorkflowStepSchema] | **yes** | — | Ordered list of steps |
| `returns` | string | **yes** | — | ID of the step whose output is the workflow result |

`contract_out` is an agent-facing boundary check over the assembled workflow
output. It does not validate every provider, query, transform, or intermediate
step output; those steps keep their own validation rules. Omit `contract_out` to
preserve the current unvalidated final-output behavior.

### Workflow Step Types

Each step must define exactly one of these operations:

| Step type | Purpose | Key fields |
|-----------|---------|------------|
| `provider` | Call a registered provider | `provider`, `input`, `as` |
| `query` | Run a named query or inline query definition | `query`, `params?`, `relationship_state?`, `as` |
| `assert` | Guard condition — fail the workflow if not met | `assert: {left, op, right, message}` |
| `assert_not_truncated` | Guard that read/query context was not truncated | `assert_not_truncated: {step}` |
| `assert_count` | Guard a read/result collection count | `assert_count: {step, count, op, value}` |
| `assert_exists` | Guard that one intermediate reference resolves to a present value | `assert_exists: {ref, message?}` |
| `shape_items` | Project, rename, require, and cast list-shaped rows | `shape_items: {items, include_input?, rename?, fields?, casts?, required?}`, `as` |
| `join_items` | Indexed inner join over two item sets | `join_items: {left_items, right_items, left_key, right_key, fields}`, `as` |
| `filter_items` | Filter rows with exact filters and comparisons | `filter_items: {items, where?, comparisons?}`, `as` |
| `aggregate_items` | Deterministically summarize rows with grouped measures | `aggregate_items: {items, group_by?, measures}`, `as` |
| `dedupe_items` | Deterministically deduplicate rows | `dedupe_items: {items, keys, strategy?, rank?}`, `as` |
| `make_entities` | Build an entity set from list data | `make_entities: {entity_type, items, entity_id, properties}`, `as` |
| `make_relationships` | Build a relationship set from list data | `make_relationships: {relationship_type, items, from_type, from_id, to_type, to_id, properties, evidence?}`, `as` |
| `apply_entities` | Apply a built entity set to graph state | `apply_entities: {entities_from}`, `as` |
| `apply_relationships` | Apply a built relationship set to graph state | `apply_relationships: {relationships_from}`, `as` |
| `apply_all` | Apply explicit entity sets, then relationship sets | `apply_all: {entities_from, relationships_from}`, `as` |
| `register_source_artifacts` | Register source artifacts from workflow row data (canonical only; content is workflow data, never files or URLs; identical-content re-runs noop, conflicting content errors) | `register_source_artifacts: {items, artifact_id, content, kind, label?, original_uri?, retention?}`, `as` |
| `make_candidates` | Build relationship candidates for governed proposals | `make_candidates: {relationship_type, items, from_type, from_id, to_type, to_id, properties, evidence?}`, `as` |
| `map_signals` | Convert provider output to tri-state signal-source evidence | `map_signals: {signal_source, items, from_id, to_id, evidence?, evidence_refs?, score/enum}`, `as` |
| `propose_relationship_group` | Assemble a governed group proposal from candidates + signals | `propose_relationship_group: {relationship_type, candidates_from, signals_from, on_empty?}`, `as` |

### Step Reference Syntax

Steps reference data from prior steps and the current item in list iterations:

| Reference | Meaning |
|-----------|---------|
| `$input` | Workflow input payload |
| `$steps.<step_id>` | Output of a prior step (by its `as` alias) |
| `$steps.<step_id>.<field>` | A specific field from a prior step's output |
| `$item` | Current item when iterating over a list (used inside `make_*` and `map_signals`) |
| `$item.<field>` | A specific field on the current item |

Use `evidence` on `make_candidates`/`make_relationships` and
`evidence_refs` on `map_signals` for provenance pointers that should follow a
proposal or deterministic relationship into `relationship.metadata.evidence`.
Keep relationship `properties` for domain facts such as basis, status, version,
or scope fields.

**Read-step outputs:**
- `query` returns `{results: [...], total_results, returned_results, ...}` using the same result rows and metadata as the query engine.
- `shape_items` returns `{items, input_count, output_count, dropped_count, drop_examples}`.
- `join_items` returns `{items, left_count, right_count, skipped_right_count, matched_left_count, output_count}`.
- `filter_items` returns `{items, input_count, output_count, filtered_count}`.
- `aggregate_items` returns `{items, input_count, group_count, output_count}`.
- `dedupe_items` returns `{items, input_count, output_count, duplicate_count, duplicate_examples}`.

Read steps also expose consistent completeness metadata: `total_results`,
`returned_results`, `limit`, `truncated`, `limit_truncated`, `path_truncated`,
and `truncation_reasons`. Query steps additionally expose `result_shape`,
`dedupe`, `relationship_state`, `policy_summary`, and the child query
`receipt_id`. Transform steps that consume read output preserve that metadata in
`source_metadata`.

Workflow graph reads are query-engine-backed. Reusable product/API surfaces
should normally be named queries. Workflow-local collection reads can use an
inline `mode: collection` query definition:

```yaml
- id: production_assets
  query:
    mode: collection
    result_shape: entity
    returns: Asset
    where:
      result.properties.environment:
        eq: production
    order_by:
      - by: $result.entity_id
        direction: asc
  as: production_assets

- id: accepted_asset_products
  query:
    mode: collection
    result_shape: relationship
    returns: asset_runs_product
    relationship_state: accepted
  as: asset_products
```

Collection queries omit `entry_point` and do not define
`traversal`. `result_shape: entity` enumerates entities of `returns`;
`result_shape: relationship` enumerates relationships of `returns` using the
query engine's relationship-state semantics. `result_shape: path` is invalid
for collection queries. Downstream steps consume query rows through
`$steps.<alias>.results`, not `items`.

Older workflow-specific `list_entities` and `list_relationships` read steps
are not supported. Moving collection reads into `query` keeps filtering,
ordering, relationship visibility, receipts, truncation metadata, and query
evidence in one engine instead of duplicating graph-read semantics in
workflow code.

### Guarding Partial Read Context

Agent-facing workflows should fail explicitly when a limited or path-budgeted
read would make the output incomplete. Use `assert_not_truncated` and
`assert_count` for common completeness checks:

```yaml
workflows:
  exposure_context:
    contract_in: ExposureInput
    contract_out: ExposureContext
    steps:
      - id: exposed_assets
        query: exposed_assets_for_vulnerability
        params:
          vulnerability_id: $input.vulnerability_id
        as: exposed_assets

      - id: require_complete_exposure_context
        assert_not_truncated:
          step: exposed_assets

      - id: require_some_exposures
        assert_count:
          step: exposed_assets
          count: returned_results
          op: gt
          value: 0

    returns: exposed_assets
```

The same pattern works after shaping or filtering because transforms preserve
read metadata:

```yaml
- id: shaped_exposures
  shape_items:
    items: $steps.exposed_assets.results
    fields:
      asset_id: $item.values.asset_id
      priority: $item.values.priority
  as: shaped_exposures

- id: require_complete_shaped_context
  assert_not_truncated:
    step: shaped_exposures
```

Use `assert_exists` for required intermediate context refs where a missing nested
path should produce an author-controlled message instead of a low-level
reference-resolution error:

```yaml
- id: require_first_asset_id
  assert_exists:
    ref: $steps.exposures.results[0].values.asset_id
    message: first exposure must include an asset id
```

`assert_count.count` supports `returned_results`, `total_results`, `items`, and
`results`. `assert_exists` treats `null` and empty strings as missing; `false`,
`0`, empty lists, and empty objects are present values. General `assert` remains
available for arbitrary comparisons and is equivalent to the longer explicit
forms of these common checks.

`contract_out` validates the final output shape selected by `returns`. Read
metadata guards validate whether the workflow had complete enough source
context to support that output.

### Dataflow Steps

Use dataflow steps for deterministic row mechanics that should be visible in
the workflow receipt rather than hidden inside a provider.

`shape_items` applies operations in this order: `rename -> fields -> casts ->
required`. Rename keys are top-level only. `fields` resolves against the
post-rename item and may overwrite projected keys. Casts are explicit and
support `str`, `int`, `float`, `bool`, and `json`; missing and `null` values are
left for `required` handling.

`join_items` currently supports `join_type: inner`. It indexes the right side by
the canonical JSON form of `right_key`, skips right rows with `null` keys, and
preserves left-row order with right-match order for one-to-many fanout.

`filter_items` uses exact-match/list-membership `where` rules plus comparison
predicates. `where` reads top-level item keys and may use literals or `$input.*`
refs only. Comparisons may use normal workflow refs, including `$item`,
`$input`, and prior `$steps`.

`aggregate_items` groups already-materialized rows and computes deterministic
summary rows. Omit `group_by` for one global aggregate row; global aggregates
return one row even when the input is empty, so downstream steps can rely on a
stable summary object. Supported measures are `count`, `count_where`,
`count_distinct`, `sum`, `min`, and `max`. `count_distinct` ignores `null`
values and uses canonical JSON identity for structured values. `sum`, `min`,
and `max` can declare `value_type` (`number`, `date`, `datetime`, etc.) to use
the shared typed comparison/coercion rules. Aggregation preserves source
truncation metadata when it summarizes read/query-derived rows.

Grouped count example:

```yaml
- id: exposure_counts
  aggregate_items:
    items: $steps.exposures.results
    group_by:
      priority: $item.values.priority
    measures:
      exposure_count:
        count: true
      affected_assets:
        count_distinct:
          value: $item.values.asset_id
      critical_count:
        count_where:
          left: $item.values.priority
          op: eq
          right: critical
  as: exposure_counts
```

Global count example:

```yaml
- id: exposure_total
  aggregate_items:
    items: $steps.exposures.results
    measures:
      exposure_count:
        count: true
  as: exposure_total
```

`dedupe_items` requires one or more keys and supports `first`, `last`, `max`,
and `min`. Ranked strategies require `rank`; missing ranks lose to present
ranks, and ties keep the earlier item.

### apply_all

`apply_all` is a canonical workflow step for reducing repetitive apply
boilerplate while keeping writes explicit. It applies entity sets first in the
listed order, then relationship sets in the listed order, reusing the same
validation and write semantics as `apply_entities` and `apply_relationships`.
It does not infer "all previous steps"; every source alias must be listed.

```yaml
- id: apply_local_state
  apply_all:
    entities_from:
      - assets
      - owners
      - controls
    relationships_from:
      - owned_by_edges
      - control_edges
  as: apply_local_state
```

The output contains `entity_results`, `relationship_results`, top-level
`create_count`, `update_count`, `noop_count`, and duplicate-input totals.

Common providers and step types have different jobs: step types are
engine-owned deterministic workflow/dataflow mechanics, while common providers
remain reusable but opaque adapters, external services, model calls, or
domain-policy modules.

### Governed Proposal Steps

For `type: proposal` workflows that produce governed proposals (fuzzy matching, judgment calls), the three-step pattern is:

1. **`make_candidates`** — build candidate (from, to) pairs with properties
2. **`map_signals`** — convert provider scores/enums to tri-state signals per signal source
3. **`propose_relationship_group`** — assemble candidates + signals into a group proposal

The group then enters the resolution lifecycle (auto-resolve or manual review) based on the relationship's `proposal_policy` config.

Workflow proposal signatures are Cruxible-generated. Config authors provide
`thesis_text` for human explanation and `analysis_state` for review/debug
context, but workflow `propose_relationship_group` steps do not author
`thesis_facts`. Cruxible builds the stored signature facts from executable
structure: workflow name, proposal step id, relationship shape, candidate
alias, actual consumed signal batches, the relationship proposal policy, and a
Cruxible-controlled proposal logic digest. `thesis_text`, `analysis_state`, and
`suggested_priority` are not hashed.

Direct agent-authored group proposals may provide optional caller
`thesis_facts` as signature scope. Cruxible stores that scope under
`agent_scope` in generated `thesis_facts`; generated top-level fields such as
`origin`, `relationship`, and `signals` remain Cruxible-owned. The origin is
marked `agent` with `evidence_mode: agent_supplied`, and signal-source facts
come from member signals supplied on the proposal. Agent-supplied facts cannot
impersonate workflow/provider-backed evidence; use a configured workflow when
evidence must be provider-backed.

`propose_relationship_group` is strict by default: if `candidates_from` resolves
to an empty candidate set, the workflow fails. Set `on_empty: complete` only when
"no candidates" is a valid terminal outcome for that workflow. In that case no
candidate group is created, the workflow succeeds with `status: no_candidates`,
and the workflow receipt records `group_created: false`.

```yaml
- id: proposal
  propose_relationship_group:
    relationship_type: asset_remediated_vulnerability
    candidates_from: candidates
    signals_from: [remediation_signals]
    on_empty: complete
    thesis_text: Close stale exposure edges
  as: proposal
```

**map_signals mapping modes** (exactly one required):

- `score`: Map a numeric value to signals using thresholds
  ```yaml
  score:
    path: similarity_score
    support_gte: 0.8
    unsure_gte: 0.5
  ```
  The `path` is a field name on each item — the executor prepends `$item.` automatically, so write `similarity_score` not `$item.similarity_score`. Values >= `support_gte` produce `support`, >= `unsure_gte` produce `unsure`, below produce `contradict`.

- `enum`: Map string values to signals using a lookup table
  ```yaml
  enum:
    path: verdict
    map:
      exact: support
      partial: unsure
      none: contradict
  ```

---

## tests

Fixture-based workflow tests defined in the config. These are run by `cruxible test` to verify workflow behavior.

```yaml
tests:
  - name: kev_reference_builds
    workflow: build_public_kev_reference
    input: {}
    expect:
      receipt_contains_provider: parse_public_kev_bundle
```

### WorkflowTestSchema

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `name` | string | **yes** | — | Test name |
| `workflow` | string | **yes** | — | Workflow to execute (must exist in `workflows`) |
| `input` | dict | no | `{}` | Input payload for the workflow |
| `expect` | WorkflowTestExpectSchema | no | `{}` | Assertions on the result |

### WorkflowTestExpectSchema

| Field | Type | Description |
|-------|------|-------------|
| `output_equals` | any | Exact match on the workflow output |
| `output_contains` | dict | Subset match on the workflow output |
| `receipt_contains_provider` | string or list[string] | Provider name(s) that must appear in the execution receipt |
| `error_contains` | string | Expected error substring (for negative tests) |

---

## Full Example

The KEV triage overlay config (`kits/kev-triage/config.yaml`) demonstrates a release-backed overlay that extends a reference layer with governed judgment relationships. **Note:** This config requires composition with its base (`kits/kev-reference/config.yaml`) before it can be validated or loaded — `Vulnerability`, `Product`, and other reference types are defined in the base, not here:

```yaml
version: "1.0"
name: kev_triage
extends: ../kev-reference/config.yaml
description: >
  Overlay of the KEV reference state for internal vulnerability triage.

entity_types:
  Asset:
    description: Internal asset from CMDB, cloud inventory, or endpoint tooling.
    properties:
      asset_id: {primary_key: true}
      hostname: {indexed: true}
      criticality: {}
      environment: {}
      internet_exposed: {type: bool}

  Owner:
    description: Team or person responsible for an asset.
    properties:
      owner_id: {primary_key: true}
      name: {}
      team: {}

relationships:
  - name: asset_owned_by
    description: Ownership mapping for assets.
    from: Asset
    to: Owner

  - name: asset_affected_by_vulnerability
    description: Accepted judgment that an asset is affected by a vulnerability.
    from: Asset
    to: Vulnerability
    properties:
      installed_version: {}
      affected_basis: {}
    proposal_policy:
      signals:
        product_version_evidence:
          role: required
          always_review_on_unsure: true
        scanner_evidence:
          role: advisory

named_queries:
  affected_assets_for_vulnerability:
    mode: traversal
    description: Find internal assets accepted as affected by a vulnerability.
    entry_point: Vulnerability
    returns: Asset
    traversal:
      - relationship: asset_affected_by_vulnerability
        direction: incoming

  owner_patch_queue:
    mode: traversal
    description: Find vulnerabilities affecting an owner's assets.
    entry_point: Owner
    returns: Vulnerability
    traversal:
      - relationship: asset_owned_by
        direction: incoming
      - relationship: asset_affected_by_vulnerability
        direction: outgoing

# Operational configs load local state through workflows with providers,
# dataflow steps, make_entities/make_relationships, and apply_* steps.

```

See also the reference layer config (`kits/kev-reference/config.yaml`) for a complete example with workflows, providers, contracts, artifacts, and quality checks. Relationship-level `proposal_policy.signals` defines governed proposal policy.


========================================================================
SOURCE: https://docs.cruxible.ai/mcp-tools/
========================================================================

# MCP Tools Reference

This is the full searchable reference for Cruxible MCP tools. MCP is a curated agent connector, not full CLI parity. The HTTP API/client remain the broader remote product surface; CLI keeps shell-only utilities such as `context`, `config views --update-readme`, `export edges`, and local receipt `explain`.

## Permission Modes

| Mode | Env value | Meaning |
| --- | --- | --- |
| READ_ONLY | `read_only` | Query, inspect, receipts, samples, evaluation, lint, snapshots listing. |
| GOVERNED_WRITE | `governed_write` | READ_ONLY plus workflow runs/tests, proposal workflows, feedback, outcomes, decision records, proposal groups, snapshot creation, and source artifact registration. |
| GRAPH_WRITE | `graph_write` | GOVERNED_WRITE plus raw graph mutation, canonical workflow apply, and group resolution/trust updates. |
| ADMIN | `admin` | Full lifecycle, config reload, locks, snapshots, clone, state publication/pull, ingest, constraints, and policies. |

`tools/list` advertises only tools allowed by the active `CRUXIBLE_MODE`; call-time permission checks still enforce the same tiers as a backstop.

## Tool Catalog Curation

Set `CRUXIBLE_MCP_PROFILE` to shrink the advertised catalog for focused clients:

| Profile | Meaning |
| --- | --- |
| `full` | Default. Advertise every tool allowed by the active permission mode. |
| `state_authoring` | Tools for creating, inspecting, querying, and directly loading state. |
| `review` | Tools for queries, receipts, feedback, outcomes, and proposal-group review. |

Set `CRUXIBLE_MCP_TOOLS` or `CRUXIBLE_MCP_TOOL_ALLOWLIST` to a comma-separated list of exact tool names for an explicit allowlist. Profile and allowlist curation are both intersected with `CRUXIBLE_MODE`.

## Tool Prompt Style

Tool descriptions are written for non-coding MCP clients. Each description starts with when to use the tool, uses kit-user vocabulary, and avoids implementation details that do not help with tool choice.

## cruxible_version

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to confirm which cruxible build this MCP server is running.

**Arguments:** none.

**Returns:** Returns a JSON object with dynamic keys.

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_server_info

**Permission:** `READ_ONLY`

**Purpose:** Use when you need live daemon details such as state directory, version, and how many instances are loaded.

**Arguments:** none.

**Returns:** Top-level fields: `server_required`, `state_dir`, `version`, `instance_count`, `auth_enabled`, `auth_required`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_init

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to create a governed instance from a config or reconnect to an existing instance after a daemon restart.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `root_dir` | yes | string |  |
| `config_path` | no | string | null |  |
| `config_yaml` | no | string | null |  |
| `data_dir` | no | string | null |  |
| `kits` | no | array | null | Ordered kit refs: standalone base first, overlay kits after. |

**Returns:** Top-level fields: `instance_id`, `status`, `warnings`

**Side Effects:** Creates a new instance or reloads an existing one.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_instance_backup

**Permission:** `ADMIN`

**Purpose:** Use when you need a portable same-identity backup of an instance, including its authoritative state database.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `artifact_path` | yes | string |  |
| `label` | no | string | null |  |

**Returns:** The backup artifact path and the instance identity it captured.

**Side Effects:** Writes a portable backup artifact to disk; does not mutate instance state.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_instance_restore

**Permission:** `ADMIN`

**Purpose:** Use when you need to restore a daemon-backed instance from a same-identity backup artifact.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `artifact_path` | yes | string |  |
| `root_dir` | no | string | null |  |

**Returns:** The restored instance id and status.

**Side Effects:** Creates an instance directory from the artifact and registers it with the daemon.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_instance_relocate

**Permission:** `ADMIN`

**Purpose:** Use when you need to move a healthy daemon-backed instance to a new directory while preserving its identity; the registry is repointed to the new location.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `to_dir` | yes | string |  |
| `remove_source` | no | boolean |  |

**Returns:** The instance id and its new on-disk location.

**Side Effects:** Moves the instance directory and repoints the registry to the new location.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_validate

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to check whether a Cruxible config is valid before creating or reloading an instance.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `config_path` | no | string | null |  |
| `config_yaml` | no | string | null |  |

**Returns:** Top-level fields: `valid`, `name`, `entity_types`, `relationships`, `named_queries`, `warnings`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_state_create_overlay

**Permission:** `ADMIN`

**Purpose:** Use when you need a local overlay instance based on a published upstream state release.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `root_dir` | yes | string |  |
| `transport_ref` | no | string | null |  |
| `state_ref` | no | string | null |  |
| `kit` | no | string | null |  |
| `no_kit` | no | boolean |  |

**Returns:** Top-level fields: `instance_id`, `manifest`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_lock_workflow

**Permission:** `ADMIN`

**Purpose:** Use when workflow inputs, providers, or artifacts changed and you need to refresh the workflow lock before running it.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `force` | no | boolean |  |

**Returns:** Top-level fields: `lock_path`, `config_digest`, `providers_locked`, `artifacts_locked`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_plan_workflow

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to preview the concrete steps a configured workflow would run without executing those steps.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `workflow_name` | yes | string |  |
| `input_payload` | no | object | null |  |

**Returns:** Top-level fields: `plan`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_run_workflow

**Permission:** `GOVERNED_WRITE`

**Purpose:** Use when you need to execute a configured workflow and receive its output, receipts, traces, and apply instructions if it is a preview.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `workflow_name` | yes | string |  |
| `input_payload` | no | object | null |  |
| `decision_record_id` | no | string | null |  |

**Returns:** Top-level fields: `workflow`, `output`, `receipt_id`, `mode`, `workflow_type`, `canonical`, `apply_digest`, `head_snapshot_id`, `committed_snapshot_id`, `apply_previews`, `query_receipt_ids`, `read_metadata`, `trace_ids`, `receipt`, `traces`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_apply_workflow

**Permission:** `GRAPH_WRITE`

**Purpose:** Use when a workflow preview returned an apply digest and you are ready to commit that exact workflow result.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `workflow_name` | yes | string |  |
| `expected_apply_digest` | yes | string |  |
| `expected_head_snapshot_id` | no | string | null |  |
| `input_payload` | no | object | null |  |
| `decision_record_id` | no | string | null |  |

**Returns:** Top-level fields: `workflow`, `output`, `receipt_id`, `mode`, `workflow_type`, `canonical`, `apply_digest`, `head_snapshot_id`, `committed_snapshot_id`, `apply_previews`, `query_receipt_ids`, `read_metadata`, `trace_ids`, `receipt`, `traces`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_test_workflow

**Permission:** `GOVERNED_WRITE`

**Purpose:** Use when you need to run workflow tests declared by the active config.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `name` | no | string | null |  |

**Returns:** Top-level fields: `total`, `passed`, `failed`, `cases`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_query

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to run a named query from the active config and receive matching items plus a receipt. First call cruxible_list_queries or cruxible_describe_query when you do not know the query name, required params, result shape, or examples. For traversal queries, params must include the entry_point primary-key field, such as {'vehicle_id': 'V-123'} when the entry point is Vehicle and its primary key is vehicle_id; cruxible_schema shows entity primary keys.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `query_name` | yes | string |  |
| `params` | no | object | null |  |
| `limit` | no | integer | null |  |
| `offset` | no | integer | Number of results to skip before the returned window. |
| `relationship_state` | no | string | null | Read-visibility state: one of `live`, `accepted`, `all`, `not-live`, `pending`, or `reviewable`. Gates entities by lifecycle and edges by review+lifecycle. |
| `decision_record_id` | no | string | null |  |

**Returns:** Top-level fields: `items`, `receipt_id`, `receipt`, `total`, `limit`, `offset`, `truncated`, `limit_truncated`, `path_truncated`, `truncation_reasons`, `max_paths`, `max_paths_per_result`, `total_path_count`, `retained_path_count`, `steps_executed`, `result_shape`, `dedupe`, `relationship_state`, `param_hints`, `policy_summary`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_query_inline

**Permission:** `READ_ONLY`

**Purpose:** Use when you need a one-off bounded graph query without adding it to the config. Inline definitions use the configured named-query JSON shape plus a required name; promote repeated or workflow-critical queries into config.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `definition` | yes | InlineQueryDefinition | Inline query definition object: same JSON shape as a configured named query (`mode`, `returns`, `traversal`, `where`, `select`, `order_by`, `include`, `limit`, `max_paths`, `max_paths_per_result`, ...) plus a required `name`. |
| `params` | no | object | null |  |
| `limit` | no | integer | null |  |
| `relationship_state` | no | string | null | Read-visibility state: one of `live`, `accepted`, `all`, `not-live`, `pending`, or `reviewable`. Gates entities by lifecycle and edges by review+lifecycle. |
| `decision_record_id` | no | string | null |  |

**Returns:** Top-level fields: `items`, `receipt_id`, `receipt`, `total`, `limit`, `offset`, `truncated`, `limit_truncated`, `path_truncated`, `truncation_reasons`, `max_paths`, `max_paths_per_result`, `total_path_count`, `retained_path_count`, `steps_executed`, `result_shape`, `dedupe`, `relationship_state`, `param_hints`, `policy_summary`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_list_queries

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to discover the named queries available in the active config.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `limit` | no | integer |  |
| `offset` | no | integer |  |

**Returns:** Top-level fields: `items`, `total`, `limit`, `offset`, `truncated`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_describe_query

**Permission:** `READ_ONLY`

**Purpose:** Use when you need the purpose, parameters, and result shape for one named query.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `query_name` | yes | string |  |

**Returns:** Top-level fields: `name`, `mode`, `entry_point`, `required_params`, `returns`, `result_shape`, `dedupe`, `relationship_state`, `allow_relationship_state_override`, `select`, `order_by`, `include`, `limit`, `max_paths`, `max_paths_per_result`, `description`, `example_ids`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_receipt

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to inspect the proof record for a previous query, write, workflow, feedback, or outcome.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `receipt_id` | yes | string |  |

**Returns:** Returns a JSON object with dynamic keys.

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_get_trace

**Permission:** `READ_ONLY`

**Purpose:** Use when you need the execution trace for one provider or workflow step.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string | Governed instance ID or local instance root. |
| `trace_id` | yes | string | Provider execution trace ID, usually returned by workflow run/apply/propose results. |

**Returns:** Returns the persisted trace with provider metadata, retained input/output payload fields, payload digest/size metadata, status, timings, and error details when present. Payload fields follow the instance config's `runtime.trace_payloads` retention policy.

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Trace ID not found.
- Permission mode too low for this tool.

## cruxible_list_traces

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to browse execution traces by workflow, provider, or page.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string | Governed instance ID or local instance root. |
| `workflow_name` | no | string | null | Filter by workflow name. |
| `provider_name` | no | string | null | Filter by provider name. |
| `limit` | no | integer | Maximum trace summaries to return. |
| `offset` | no | integer | Number of summaries to skip. |

**Returns:** Top-level fields: `items`, `total`, `limit`, `offset`, `truncated`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Invalid limit or offset.

## cruxible_feedback

**Permission:** `GOVERNED_WRITE`

**Purpose:** Use when a person or reviewer agent adjudicated one explicit relationship and you need to record support, rejection, flagging, or a correction. Use edge_key only to disambiguate multiple stored edges with the same relationship tuple; receipt_id is optional for explicit-coordinate feedback.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `receipt_id` | no | string |  |
| `action` | yes | enum: approve, reject, correct, flag |  |
| `source` | yes | enum: human, agent |  |
| `from_type` | yes | string |  |
| `from_id` | yes | string |  |
| `relationship_type` | yes | string |  |
| `to_type` | yes | string |  |
| `to_id` | yes | string |  |
| `edge_key` | no | integer | null |  |
| `reason` | no | string |  |
| `reason_code` | no | string | null |  |
| `scope_hints` | no | object | null |  |
| `corrections` | no | object | null |  |
| `group_override` | no | boolean |  |

**Returns:** Top-level fields: `feedback_id`, `applied`, `receipt_id`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_feedback_from_query

**Permission:** `GOVERNED_WRITE`

**Purpose:** Use when a query receipt and result index identify the relationship that needs feedback. This path requires receipt_id because the receipt/result selection is the target selector.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string | Governed instance ID or local instance root. |
| `receipt_id` | yes | string | Query receipt ID. |
| `result_index` | yes | integer | Zero-based query result row index. |
| `action` | yes | enum: approve, reject, correct, flag | Feedback action. |
| `source` | no | enum: human, agent | Who produced this feedback. |
| `reason` | no | string | Reason for feedback. |
| `reason_code` | no | string | Structured feedback reason code. |
| `scope_hints` | no | object | Structured feedback scope hints. |
| `corrections` | no | object | Edge property corrections for `action="correct"`. |
| `group_override` | no | boolean | Mark the selected edge assertion metadata as a group override. |
| `path_index` | no | integer | Zero-based path segment index for path rows. |
| `path_alias` | no | string | Traversal alias for the selected path segment. |

**Returns:** Top-level fields: `feedback_id`, `applied`, `receipt_id`

**Side Effects:** Creates normal feedback records and feedback receipts through the existing edge-feedback path.

**Common Errors:**
- Receipt is missing, not a query receipt, or result index is out of range.
- Entity-shaped query rows do not contain relationship evidence.
- Multi-hop path rows require exactly one of `path_index` or `path_alias`.
- Selected path alias is missing or duplicated, or selected edge is no longer in the graph.

## cruxible_feedback_batch

**Permission:** `GOVERNED_WRITE`

**Purpose:** Use when you need to record several relationship feedback decisions from the same review session.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `items` | yes | array |  |
| `source` | no | enum: human, agent |  |

**Returns:** Top-level fields: `feedback_ids`, `applied_count`, `total`, `receipt_id`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_outcome

**Permission:** `GOVERNED_WRITE`

**Purpose:** Use when you need to record what happened after a decision, query, workflow, or reviewed relationship.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `outcome` | yes | enum: correct, incorrect, partial, unknown |  |
| `receipt_id` | no | string | null |  |
| `anchor_type` | no | enum: resolution, receipt |  |
| `anchor_id` | no | string | null |  |
| `source` | no | enum: human, agent |  |
| `outcome_code` | no | string | null |  |
| `scope_hints` | no | object | null |  |
| `outcome_profile_key` | no | string | null |  |
| `detail` | no | object | null |  |

**Returns:** Top-level fields: `outcome_id`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_list

**Permission:** `READ_ONLY`

**Purpose:** Use when you need a paged list of entities, relationships, receipts, feedback, or outcomes with optional filters. Use resource_type='entities' with entity_type and optional fields to reduce payload size; use where for bounded property predicates such as {'status': {'eq': 'active'}}.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `resource_type` | yes | enum: entities, edges, receipts, feedback, outcomes |  |
| `entity_type` | no | string | null |  |
| `relationship_type` | no | string | null |  |
| `query_name` | no | string | null |  |
| `receipt_id` | no | string | null |  |
| `limit` | no | integer |  |
| `offset` | no | integer |  |
| `property_filter` | no | object | null |  |
| `where` | no | object | null | Bounded entity/edge property predicates such as `{"status": {"eq": "active"}}`, `{"title": {"contains": "query"}}`, or `{"status": {"in": ["active", "planned"]}}`. |
| `operation_type` | no | string | null |  |
| `fields` | no | array[string] | null | Entity property fields to include for `resource_type="entities"`. |
| `relationship_state` | no | string | null | Read-visibility state (`live`, `accepted`, `all`, `not-live`, `pending`, `reviewable`). For entities it gates by lifecycle (default `live`); for edges by review+lifecycle (default returns all stored edges). |

**Returns:** Top-level fields: `items`, `total`, `limit`, `offset`, `truncated`

**Side Effects:** Read-only.

For entity lists, `fields` is an opt-in projection that reduces payload size
after the caller has selected an entity type. It trims entity `properties` but
always keeps `entity_type` and `entity_id`; it is not topic search.
Use `where` for bounded property predicates on entity or edge lists, for
example `{"status": {"eq": "active"}}` or
`{"dependency_basis": {"contains": "schema"}}`. This is not semantic search.
For `resource_type="edges"`, this is a stored-relationship inspection surface:
it may return pending, rejected, or otherwise non-live stored edges. Named
queries are logical-state reads and apply `relationship_state` filtering, so
use `cruxible_query` when you need live/reviewable truth rather than store
inspection.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_evaluate

**Permission:** `READ_ONLY`

**Purpose:** Use when you need graph quality findings such as orphaned entities, coverage gaps, constraint issues, or candidate opportunities.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `max_findings` | no | integer |  |
| `exclude_orphan_types` | no | array or null |  |
| `severity_filter` | no | array | Optional list of `error`, `warning`, or `info` severities to return. |
| `category_filter` | no | array | Optional list of evaluate categories to return. |

**Returns:** Top-level fields: `entity_count`, `edge_count`, `findings`, `summary`, `constraint_summary`, `quality_summary`

Filtered calls still return full pre-filter `summary`, `constraint_summary`,
and `quality_summary` counts. Agent triage example: request
`severity_filter=["error"]` with `max_findings=1` to check whether any
error-level finding exists.

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_stats

**Permission:** `READ_ONLY`

**Purpose:** Use when you need quick counts of entity and relationship types in an instance.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |

**Returns:** Top-level fields: `entity_count`, `edge_count`, `entity_counts`, `relationship_counts`, `head_snapshot_id`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_lint

**Permission:** `READ_ONLY`

**Purpose:** Use when you need a combined quality report for config, graph state, feedback, and outcome coverage.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `max_findings` | no | integer |  |
| `analysis_limit` | no | integer |  |
| `min_support` | no | integer |  |
| `exclude_orphan_types` | no | array | null |  |

**Returns:** Top-level fields: `config_name`, `config_warnings`, `compatibility_warnings`, `evaluation`, `feedback_reports`, `outcome_reports`, `summary`, `has_issues`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_get_feedback_profile

**Permission:** `READ_ONLY`

**Purpose:** Use when you need the allowed feedback codes and guidance for a relationship type.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `relationship_type` | yes | string |  |

**Returns:** Top-level fields: `found`, `relationship_type`, `profile`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_analyze_feedback

**Permission:** `READ_ONLY`

**Purpose:** Use when you need patterns from recorded feedback, such as common corrections or recurring review issues.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `relationship_type` | yes | string |  |
| `limit` | no | integer |  |
| `min_support` | no | integer |  |
| `decision_surface_type` | no | string | null |  |
| `decision_surface_name` | no | string | null |  |
| `property_pairs` | no | array | null |  |

**Returns:** Top-level fields: `relationship_type`, `feedback_count`, `action_counts`, `source_counts`, `reason_code_counts`, `coded_groups`, `uncoded_feedback_count`, `uncoded_examples`, `constraint_suggestions`, `decision_policy_suggestions`, `quality_check_candidates`, `provider_fix_candidates`, `warnings`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_get_outcome_profile

**Permission:** `READ_ONLY`

**Purpose:** Use when you need the allowed outcome codes and guidance for a decision surface.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `anchor_type` | yes | enum: resolution, receipt |  |
| `relationship_type` | no | string | null |  |
| `workflow_name` | no | string | null |  |
| `surface_type` | no | string | null |  |
| `surface_name` | no | string | null |  |

**Returns:** Top-level fields: `found`, `profile_key`, `anchor_type`, `profile`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_analyze_outcomes

**Permission:** `READ_ONLY`

**Purpose:** Use when you need patterns from recorded outcomes for a query, workflow, relationship, or decision surface.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `anchor_type` | yes | enum: resolution, receipt |  |
| `relationship_type` | no | string | null |  |
| `workflow_name` | no | string | null |  |
| `query_name` | no | string | null |  |
| `surface_type` | no | string | null |  |
| `surface_name` | no | string | null |  |
| `limit` | no | integer |  |
| `min_support` | no | integer |  |

**Returns:** Top-level fields: `anchor_type`, `outcome_count`, `outcome_counts`, `outcome_code_counts`, `coded_groups`, `uncoded_outcome_count`, `uncoded_examples`, `trust_adjustment_suggestions`, `workflow_review_policy_suggestions`, `query_policy_suggestions`, `provider_fix_candidates`, `debug_packages`, `workflow_debug_packages`, `warnings`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_schema

**Permission:** `READ_ONLY`

**Purpose:** Use when you need the active entity types, relationships, queries, workflows, and governance settings.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |

**Returns:** Returns a JSON object with dynamic keys.

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_sample

**Permission:** `READ_ONLY`

**Purpose:** Use when you need example entities of one type before writing a query or review.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `entity_type` | yes | string |  |
| `limit` | no | integer |  |
| `fields` | no | array[string] | null | Entity property fields to include in sampled entities. |

**Returns:** Top-level fields: `items`, `total`, `limit`, `offset`, `truncated`, `entity_type`

**Side Effects:** Read-only.

`fields` is an opt-in projection for compact samples. It trims entity
`properties` but always keeps `entity_type` and `entity_id`.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_inspect_entity

**Permission:** `READ_ONLY`

**Purpose:** Use when you need one entity plus nearby relationships and related entities.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `entity_type` | yes | string |  |
| `entity_id` | yes | string |  |
| `direction` | no | string |  |
| `relationship_type` | no | string | null |  |
| `limit` | no | integer | null |  |

**Returns:** Top-level fields: `found`, `entity_type`, `entity_id`, `properties`, `metadata`, `neighbors`, `total_neighbors`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_inspect_entity_history

**Permission:** `READ_ONLY`

**Purpose:** Use when you need receipt-derived property changes for one entity type or entity.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `entity_type` | yes | string |  |
| `entity_id` | no | string | null |  |
| `limit` | no | integer |  |
| `offset` | no | integer |  |

**Returns:** Top-level fields: `entity_type`, `entity_id`, `items`, `total`, `legacy_entity_write_count`, `warnings`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_inspect_ontology

**Permission:** `READ_ONLY`

**Purpose:** Use when you need a compact overview of entity types, relationships, and rules.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |

**Returns:** Top-level fields: `view`, `payload`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_inspect_workflows

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to understand the workflows declared by the active config.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |

**Returns:** Top-level fields: `view`, `payload`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_inspect_queries

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to understand configured queries and their parameters.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |

**Returns:** Top-level fields: `view`, `payload`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_inspect_governance

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to review feedback, outcome, group, and policy settings.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `limit` | no | integer |  |

**Returns:** Top-level fields: `view`, `payload`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_inspect_overview

**Permission:** `READ_ONLY`

**Purpose:** Use when you need a single high-level summary of the instance.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `limit` | no | integer |  |

**Returns:** Top-level fields: `view`, `payload`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_add_relationship

**Permission:** `GRAPH_WRITE`

**Purpose:** Use when you need to add or update a small number of explicit relationships and the endpoint entities already exist. Set pending=true when the edge should enter relationship review state instead of immediately becoming live.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `relationships` | yes | array |  |
| `dry_run` | no | boolean | false | Validate (schema + mutation guards) without mutating graph state |

**Returns:** Top-level fields: `added`, `updated`, `pending_conflicts`, `updated_group_backed_edges`, `receipt_id`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_add_entity

**Permission:** `GRAPH_WRITE`

**Purpose:** Use when you need to add or update a small number of explicit entities.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `entities` | yes | array |  |
| `dry_run` | no | boolean | false | Validate (schema + mutation guards) without mutating graph state |

**Returns:** Top-level fields: `entities_added`, `entities_updated`, `receipt_id`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_batch_direct_write

**Permission:** `GRAPH_WRITE`

**Purpose:** Use when you need to validate or apply one coherent batch of explicit entities and relationships; set dry_run first.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `payload` | yes | BatchDirectWritePayload | Object with `entities` (entity inputs), `relationships` (relationship inputs, each optionally referencing `shared_evidence_keys`), and `shared_evidence` (map of key to shared evidence refs/source evidence). |
| `dry_run` | no | boolean | Validate the payload without mutating graph state. |

**Returns:** Top-level fields: `dry_run`, `valid`, `entities_added`, `entities_updated`, `relationships_added`, `relationships_updated`, `validation_errors`, `validation_warnings`, `evidence_sources_used`, `pending_conflicts`, `updated_group_backed_edges`, `receipt_id`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_add_constraint

**Permission:** `GOVERNED_WRITE`

**Purpose:** Use when you need to add a graph quality rule that future evaluations should check.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `name` | yes | string |  |
| `rule` | yes | string |  |
| `severity` | no | enum: warning, error |  |
| `description` | no | string | null |  |

**Returns:** Top-level fields: `name`, `added`, `config_updated`, `warnings`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_add_decision_policy

**Permission:** `GOVERNED_WRITE`

**Purpose:** Use when you need to record a policy that affects how a decision surface should be handled.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `name` | yes | string |  |
| `applies_to` | yes | enum: query, workflow |  |
| `relationship_type` | yes | string |  |
| `effect` | yes | enum: suppress, require_review |  |
| `match` | no | DecisionPolicyMatchInput | null |  |
| `description` | no | string | null |  |
| `rationale` | no | string |  |
| `query_name` | no | string | null |  |
| `workflow_name` | no | string | null |  |
| `expires_at` | no | string | null |  |

**Returns:** Top-level fields: `name`, `added`, `config_updated`, `warnings`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_reload_config

**Permission:** `ADMIN`

**Purpose:** Use when you need to replace or reload the active config for an instance.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `config_path` | no | string | null |  |
| `config_yaml` | no | string | null |  |
| `allow_orphans` | no | boolean | Allow stored graph types absent from the incoming config (default false: strandings refuse the reload with per-type counts). |

**Returns:** Top-level fields: `config_path`, `updated`, `warnings`, `type_delta`, `strandings`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_propose_workflow

**Permission:** `GOVERNED_WRITE`

**Purpose:** Use when a workflow proposes reviewable relationship changes instead of writing them directly.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `workflow_name` | yes | string |  |
| `input_payload` | no | object | null |  |
| `decision_record_id` | no | string | null |  |

**Returns:** Top-level fields: `workflow`, `output`, `receipt_id`, `mode`, `workflow_type`, `canonical`, `group_id`, `group_status`, `review_priority`, `suppressed`, `suppressed_members`, `query_receipt_ids`, `read_metadata`, `trace_ids`, `prior_resolution`, `policy_summary`, `receipt`, `traces`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_create_decision_record

**Permission:** `GOVERNED_WRITE`

**Purpose:** Use when you need to open a tracked decision before gathering evidence, running workflows, or recording outcomes.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `question` | yes | string |  |
| `subject_type` | no | string | null |  |
| `subject_id` | no | string | null |  |
| `opened_by` | no | string |  |

**Returns:** Top-level fields: `record`, `events`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_get_decision_record

**Permission:** `READ_ONLY`

**Purpose:** Use when you need the current state and optional event history for one decision.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `decision_record_id` | yes | string |  |
| `include_events` | no | boolean |  |

**Returns:** Top-level fields: `record`, `events`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_list_decision_records

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to find decision records by status, subject, class, or page.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `status` | no | string | null |  |
| `subject_type` | no | string | null |  |
| `subject_id` | no | string | null |  |
| `decision_class` | no | string | null |  |
| `limit` | no | integer |  |
| `offset` | no | integer |  |

**Returns:** Top-level fields: `items`, `total`, `limit`, `offset`, `truncated`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_list_decision_events

**Permission:** `READ_ONLY`

**Purpose:** Use when you need the event timeline for decisions, optionally filtered by receipt.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `decision_record_id` | no | string | null |  |
| `receipt_id` | no | string | null |  |
| `trace_id` | no | string | null |  |
| `status` | no | string | null |  |
| `limit` | no | integer |  |
| `offset` | no | integer |  |

**Returns:** Top-level fields: `items`, `total`, `limit`, `offset`, `truncated`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_finalize_decision_record

**Permission:** `GOVERNED_WRITE`

**Purpose:** Use when a tracked decision has a final answer and rationale.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `decision_record_id` | yes | string |  |
| `final_decision` | yes | string |  |
| `decision_class` | yes | enum: recommended, rejected, deferred, escalated |  |
| `rationale` | no | string |  |

**Returns:** Top-level fields: `record`, `events`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_abandon_decision_record

**Permission:** `GOVERNED_WRITE`

**Purpose:** Use when a tracked decision should be closed without a final decision.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `decision_record_id` | yes | string |  |
| `reason` | no | string |  |

**Returns:** Top-level fields: `record`, `events`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_propose_group

**Permission:** `GOVERNED_WRITE`

**Purpose:** Use when you need to create a review group for candidate relationship changes.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `relationship_type` | yes | string |  |
| `members` | yes | array |  |
| `thesis_text` | no | string |  |
| `thesis_facts` | no | object | null |  |
| `analysis_state` | no | object | null |  |
| `signal_sources_used` | no | array | null |  |
| `proposed_by` | no | enum: human, agent |  |
| `suggested_priority` | no | string | null |  |

**Returns:** Top-level fields: `group_id`, `signature`, `status`, `review_priority`, `member_count`, `prior_resolution`, `suppressed`, `suppressed_members`, `policy_summary`, `receipt_id`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_resolve_group

**Permission:** `GRAPH_WRITE`

**Purpose:** Use when a reviewer approves, rejects, or otherwise resolves a pending group.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `group_id` | yes | string |  |
| `action` | yes | enum: approve, reject |  |
| `expected_pending_version` | yes | integer |  |
| `rationale` | no | string |  |
| `resolved_by` | no | enum: human, agent |  |
| `stamp_existing` | no | boolean | On approve, bless each surviving pre-existing edge (member tuple already live) with this group's review status and provenance instead of skipping it. |

**Returns:** Top-level fields: `group_id`, `action`, `edges_created`, `edges_skipped`, `resolution_id`, `receipt_id`, `skipped_members` (per-member skip explanations: identity plus `skip_kind`, `reason`, `stamped`), `edges_stamped`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_update_trust_status

**Permission:** `GRAPH_WRITE`

**Purpose:** Use when you need to mark a prior group resolution as trusted, invalidated, or otherwise updated.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `resolution_id` | yes | string |  |
| `trust_status` | yes | enum: trusted, watch, invalidated |  |
| `reason` | no | string |  |

**Returns:** Top-level fields: `resolution_id`, `trust_status`, `receipt_id`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_get_group

**Permission:** `READ_ONLY`

**Purpose:** Use when you need the details and members for one candidate relationship group.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `group_id` | yes | string |  |

**Returns:** Top-level fields: `group`, `members`, `resolution`, `bucket_status`, `member_review`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_list_groups

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to find candidate relationship groups by type, status, or page.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `relationship_type` | no | string | null |  |
| `status` | no | enum: pending_review, auto_resolved, applying, resolved | null |  |
| `limit` | no | integer |  |
| `offset` | no | integer |  |

**Returns:** Top-level fields: `items`, `total`, `limit`, `offset`, `truncated`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_list_resolutions

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to review past group decisions by relationship type or action.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `relationship_type` | no | string | null |  |
| `action` | no | enum: approve, reject | null |  |
| `limit` | no | integer |  |
| `offset` | no | integer |  |

**Returns:** Top-level fields: `items`, `total`, `limit`, `offset`, `truncated`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_group_status

**Permission:** `READ_ONLY`

**Purpose:** Use when you need the latest status for a group or for a known group signature.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `group_id` | no | string | null |  |
| `signature` | no | string | null |  |

**Returns:** Top-level fields: `signature`, `relationship_type`, `thesis_text`, `thesis_facts`, `latest_trust_status`, `accepted_tuple_count`, `pending_delta_count`, `pending_group_id`, `pending_version`, `latest_approved_resolution_id`, `approved_history`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_state_publish

**Permission:** `ADMIN`

**Purpose:** Use when you need to publish the current instance state as an immutable release.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `transport_ref` | yes | string |  |
| `state_id` | yes | string |  |
| `release_id` | yes | string |  |
| `compatibility` | yes | enum: data_only, additive_schema, breaking |  |

**Returns:** Top-level fields: `manifest`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_create_snapshot

**Permission:** `GOVERNED_WRITE`

**Purpose:** Use when you need to mark the current state with a named snapshot.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `label` | no | string | null |  |

**Returns:** Top-level fields: `snapshot`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_list_snapshots

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to browse available snapshots for an instance.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `limit` | no | integer |  |
| `offset` | no | integer |  |

**Returns:** Top-level fields: `items`, `total`, `limit`, `offset`, `truncated`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_register_source_artifact

**Permission:** `GOVERNED_WRITE`

**Purpose:** Use when you need to register a source document so relationship evidence can cite stable chunks from it.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `source_path` | yes | string | Path to the local source document. |
| `source_artifact_id` | no | string | null | Caller-supplied artifact id so pinned evidence locators can reference it deterministically; server-generated when omitted. Must be 3-64 chars of `[A-Za-z0-9._-]` starting with an alphanumeric. Duplicate ids are refused by the service. |
| `source_kind` | no | enum: markdown | Only `markdown` is currently supported. |
| `source_retention` | no | enum: manifest_only, archive | `manifest_only` stores chunk hashes only; `archive` also stores the document content. |
| `original_uri` | no | string | null | Original document location for provenance. |
| `label` | no | string | null | Human-readable label for the artifact. |

**Returns:** Top-level fields: `source_artifact_id`, `source_kind`, `source_retention`, `original_uri`, `label`, `content_hash`, `byte_count`, `parser_version`, `archived`, `archive_content_hash`, `chunks`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Invalid or duplicate caller-supplied `source_artifact_id`.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_dereference_source_evidence

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to read back a registered source evidence chunk and verify its expected content hash.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `source_artifact_id` | yes | string | Artifact ID returned by `cruxible_register_source_artifact`. |
| `chunk_id` | no | string | null | Chunk ID locator. |
| `heading_path` | no | array | null | Heading-path locator (used with `block_selector`). |
| `block_selector` | no | string | null | Block selector within the heading path. |
| `expected_content_hash` | no | string | null | Expected chunk content hash for drift detection. |

**Returns:** Top-level fields: `status` (one of `available`, `drifted`, `unavailable`), `source_artifact_id`, `chunk_id`, `content_hash`, `expected_artifact_hash`, `current_artifact_hash`, `body_origin`, `body`, `reason`, `chunk`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_clone_snapshot

**Permission:** `ADMIN`

**Purpose:** Use when you need a new local instance created from an existing snapshot. On auth-enabled daemons the result carries a one-time admin_credential token for the new instance - save it immediately; it is never shown again.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `snapshot_id` | yes | string |  |
| `root_dir` | yes | string |  |

**Returns:** Top-level fields: `instance_id`, `snapshot`, `admin_credential` (auth-enabled daemons only: a one-time ADMIN token for the new instance — deliver it to the operator immediately; it is never shown again)

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_state_status

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to see whether an overlay is connected to an upstream state and whether pulls are available.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |

**Returns:** Top-level fields: `upstream`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_state_pull_preview

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to preview upstream state changes before applying them.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |

**Returns:** Top-level fields: `current_release_id`, `target_release_id`, `compatibility`, `apply_digest`, `warnings`, `conflicts`, `lock_changed`, `upstream_entity_delta`, `upstream_edge_delta`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_state_pull_apply

**Permission:** `GOVERNED_WRITE`

**Purpose:** Use when a pull preview returned an apply digest and you are ready to apply it.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `expected_apply_digest` | yes | string |  |

**Returns:** Top-level fields: `release_id`, `apply_digest`, `pre_pull_snapshot_id`

**Side Effects:** May create governed state, graph state, config changes, snapshots, or audit records according to its permission tier.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_get_entity

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to fetch one entity by type and ID.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `entity_type` | yes | string |  |
| `entity_id` | yes | string |  |

**Returns:** Top-level fields: `found`, `entity_type`, `entity_id`, `properties`, `metadata`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_get_relationship

**Permission:** `READ_ONLY`

**Purpose:** Use when you need to fetch one relationship by endpoints and relationship type.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string |  |
| `from_type` | yes | string |  |
| `from_id` | yes | string |  |
| `relationship_type` | yes | string |  |
| `to_type` | yes | string |  |
| `to_id` | yes | string |  |
| `edge_key` | no | integer | null |  |

**Returns:** Top-level fields: `found`, `from_type`, `from_id`, `relationship_type`, `to_type`, `to_id`, `edge_key`, `properties`, `metadata`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Missing config names, stale locks, invalid workflow/query/group identifiers, or invalid request shape where applicable.

## cruxible_relationship_lineage

**Permission:** `READ_ONLY`

**Purpose:** Use when you need the provenance, review state, feedback, and receipts for one relationship.

**Arguments:**

| Name | Required | Type | Description |
| --- | --- | --- | --- |
| `instance_id` | yes | string | Governed instance ID or local instance root. |
| `from_type` | yes | string | Source entity type. |
| `from_id` | yes | string | Source entity ID. |
| `relationship_type` | yes | string | Relationship type. |
| `to_type` | yes | string | Target entity type. |
| `to_id` | yes | string | Target entity ID. |
| `edge_key` | no | integer | null | Edge key for multi-edge disambiguation. |

**Returns:** Top-level fields: `found`, `relationship`, `provenance`, `group`, `resolution`, `source_workflow_receipt_id`, `source_trace_ids`, `warnings`

**Side Effects:** Read-only.

**Common Errors:**
- Unknown `instance_id` or missing daemon configuration.
- Permission mode too low for this tool.
- Ambiguous relationship tuple without `edge_key`.


========================================================================
SOURCE: https://docs.cruxible.ai/common-providers/
========================================================================

# Common Providers And Dataflow Steps

Cruxible has two reusable mechanisms that can look similar in configs but serve
different purposes.

## Built-In Step Types

Step types are engine-owned deterministic workflow mechanics. They are visible
to the compiler and executor, have stable semantics, and do not hide graph
writes or external side effects inside Python code.

Use built-in step types for generic row and state mechanics:

- `shape_items`: project rows, rename keys, require fields, and cast values
- `join_items`: indexed inner joins over two item sets
- `filter_items`: exact/list filters and comparison predicates
- `dedupe_items`: deterministic row deduplication
- graph construction steps that make entities, relationships, and proposal
  members
- canonical apply steps that preview and apply accepted state

These are the preferred building blocks for deterministic state loading in new
kits.

## Common Providers

Common providers are reusable Python providers under `cruxible_core.providers`
that remain opaque to the workflow engine. Configs still declare contracts,
provider entries, artifacts, and workflow steps explicitly.

Use common providers for reusable adapters and external source mechanics:

- parsing a pinned artifact into generic source rows
- converting documents into Markdown
- extracting tables from documents
- normalizing identifiers
- calling an external parser or model behind an explicit provider contract

Common plumbing contracts are built in. Use `cruxible.JsonObject` for flexible
provider options, `cruxible.ParsedTabularBundle` for tabular parser output, and
`cruxible.EmptyInput` when a workflow or provider takes no input.

Providers should return data to the workflow. They should not directly mutate
Cruxible graph state, SQLite state, snapshots, decision logs, or group stores.
State changes should go through workflow steps, proposal groups, feedback tools,
or canonical apply surfaces.

Provider functions still accept and return plain dictionaries at the workflow
boundary. For provider implementation code, prefer the small payload helpers in
`cruxible_core.provider.payloads` for common contracts:

```yaml
- id: raw_tables
  provider: parse_public_kev_bundle
  input:
    expected_tables:
      - known_exploited_vulnerabilities
  as: raw_tables

- id: rows
  provider: normalize_public_kev_reference
  input:
    kev_rows: $steps.raw_tables.tables.known_exploited_vulnerabilities.rows
  as: rows
```

```python
from cruxible_core.provider.payloads import JsonItems


def normalize_reference(input_payload, context):
    kev_rows = input_payload["kev_rows"]
    rows = [{"cve_id": row["cveID"]} for row in kev_rows]
    return JsonItems(items=rows).to_payload()
```

Config should own source-table selection and map parsed table rows to semantic
provider inputs. Common loader providers may parse artifacts and expose table
names, but kit/domain transform providers should not own filenames, required
table inventories, or parsed-bundle table names when workflow config can make
that mapping explicit. `ParsedTabularBundle.from_payload(...)` remains useful
inside generic loader/test helpers that operate on the parser contract.
`JsonItems.from_payload(...)` and `JsonItems(...).to_payload()` validate and
emit the standard `{items: [...]}` shape while preserving row order.
Use `EvidenceRef`, `evidence_ref(...)`, and `merge_evidence_refs(...)` for
generic provenance pointers. Workflow `make_candidates`, `map_signals`, and
`make_relationships` can route those pointers into relationship metadata so
governed relationship properties stay domain-specific. Keep ordinary source
parsing, CSV loading, text normalization, and source-specific scoring as local
provider code unless the logic becomes broadly reusable across kits.

## Domain Providers

Use kit-local providers for logic that is genuinely domain-specific or
customer-specific:

- source-specific normalization
- match scoring that depends on local inventory conventions
- policy interpretation
- classification using a kit-owned enum or taxonomy
- external system adapters whose input shape is not knowable in core

If provider logic becomes generic across multiple kits, consider moving it into
a common provider or a built-in step type. Promote conservatively: step types
should be deterministic, graph-side-effect free, and useful beyond one kit.

## Typical Workflow Shape

```text
pinned artifact
  -> common provider parses generic source shape
  -> built-in steps shape/filter/join/dedupe rows
  -> kit provider handles source-specific policy only when needed
  -> workflow creates entities, relationships, proposal members, or signals
  -> canonical apply or governed group resolution changes accepted state
```

## Initial Common Providers

- `load_tabular_artifact_bundle`: parse CSV, JSON, JSONL, NDJSON, and Excel
  files from a pinned artifact into provenance-rich generic tables.
- `source_diff`: compare previous and current parsed table bundles by
  configured keys.
- `document_to_markdown`: normalize text, Markdown, and simple HTML artifacts
  into Markdown.
- `pdf_to_markdown`: convert a PDF artifact to Markdown using a configured
  local or hosted parser.
- `extract_document_tables`: extract Markdown pipe tables into structured rows.
- `resolve_entities_by_alias`: match generic source records to existing
  entities using alias fields.
- `normalize_identifiers`: normalize common identifiers such as CVEs,
  GTIN/UPC/EAN, SKUs, slugs, dates, and CPE strings.

## Example Provider Snippet

```yaml
providers:
  parse_seed_bundle:
    kind: function
    description: Parse a pinned source artifact into generic tables.
    contract_in: cruxible.JsonObject
    contract_out: cruxible.ParsedTabularBundle
    ref: cruxible_core.providers.common.tabular.load_tabular_artifact_bundle
    version: "1.0.0"
    deterministic: true
    runtime: python
    artifact: seed_bundle
```

Follow this with dataflow steps such as `shape_items`, `join_items`,
`filter_items`, and `dedupe_items` before creating graph objects.


========================================================================
SOURCE: https://docs.cruxible.ai/local-state-and-backups/
========================================================================

# Local State And Backups

SQLite is an acceptable starting point for OSS, local daemon, and single-droplet
deployments. The important rule is that all state changes still go through
Cruxible surfaces: workflows, canonical apply, proposal groups, feedback,
queries, and receipts.

Do not treat SQLite as an application API. Treat it as the local persistence
backend.

## State Directory

In daemon mode, state lives under:

```text
${CRUXIBLE_SERVER_STATE_DIR:-~/.cruxible/server}
```

The daemon materializes governed instances under `instances/inst_*` and returns
opaque instance IDs to clients. Keep this directory outside the agent workspace
when the agent should not have direct state access.

Do not use `/tmp`, `/var/tmp`, or macOS private temp directories for long-lived
daemon state. Those paths may be cleaned by the operating system while the
daemon is still running. Cruxible emits a startup warning when the server state
directory or a registered instance location resolves under a known volatile temp
path.

Direct local runtime still creates a `.cruxible/` directory under the workspace.
That mode is convenient for development, but it is not the recommended agent
boundary.

## Daemon Logs

In daemon mode, structured server request logs are JSON lines written under the
server state directory by default:

```text
${CRUXIBLE_SERVER_STATE_DIR:-~/.cruxible/server}/logs/server.log
```

Set `CRUXIBLE_SERVER_LOG_PATH` to place that log somewhere else, for example
under a supervisor-managed log directory. The daemon rotates this file locally
and treats log sink failures as nonfatal; on the first write failure it emits a
best-effort warning to stderr so operators know request logs may be dropping.

## What Lives There

A Cruxible instance can include:

- active config and workflow lock
- kit metadata and materialized kit runtime files
- graph snapshots and current graph state
- query and workflow receipts
- provider execution traces
- candidate groups and group resolutions
- decision records and decision events
- feedback and outcomes

Exact file names may change across releases. Back up the instance directory as
a unit instead of cherry-picking one SQLite database.

## Backup Guidance

For a droplet or single VM:

1. Stop or quiesce the daemon before taking a filesystem-level backup when
   possible.
2. Back up the whole server state directory.
3. Back up the source kit or record the kit alias/ref and version used to
   materialize the instance.
4. Store any customer source artifacts that feed canonical workflows.
5. Store the Cruxible package version and command used to start the daemon.

If you need online backups, use SQLite-aware backup tooling or snapshot the
volume in a way that gives a consistent filesystem view.

## Snapshots Versus Backups

Cruxible snapshots are state snapshots. They are useful for cloning,
preview identity checks, and comparing graph state over time.

Backups are operational recovery artifacts. They should include the graph plus
the surrounding evidence stores: receipts, traces, groups, resolutions,
decision records, feedback, outcomes, locks, configs, and kit metadata.

Use snapshots to reason about state. Use backups to recover the deployment.

### Edge receipts across snapshots and clones

Each relationship records a `receipt_id` in its provenance pointing at the
receipt that authored it. The invariant is that a non-null `receipt_id` always
resolves to a receipt present in the same instance; a null `receipt_id` is
accepted, immutable history. Two cases produce a null `receipt_id`, and neither
is backfilled:

- **Legacy edges** created before per-edge receipts existed carry a null
  `receipt_id` because no receipt was ever written for them.
- **Cloned, snapshot-restored, and pulled-overlay edges** have their `receipt_id`
  cleared on materialization. A snapshot/clone/state-pull bundle is
  graph+config+lock with no receipts, so the original `receipt_id` would point at
  a receipt that lives only in the source instance. On materialization Cruxible
  nulls that dangling pointer and stamps `clone_origin: upstream-snapshot` on the
  provenance (preserving the original id under `cloned_receipt_id` for
  traceability), so the edge is honestly labeled as clone-origin rather than
  referencing a phantom receipt.

`cruxible instance backup` writes a portable same-identity backup artifact for
the active instance. The artifact includes the SQLite state database, active
config, instance metadata, optional workflow lock, and a manifest with content
digests. The service uses SQLite's backup API for the database copy instead of
copying a live database file byte-for-byte.

`cruxible instance restore` restores that artifact into a clean target and keeps
the original `instance_id`. This is different from `cruxible clone`, which
creates a new local instance from a graph snapshot. Restore is an admin
lifecycle operation and should only be used when the old instance is stopped or
unregistered, because the result is the same logical instance identity.

## Portability

State is not meant to be trapped in SQLite forever. The durable product
contract is the Cruxible state model and audited mutation surfaces, not direct
SQLite access.

When moving to a future managed backend, the data that must be portable is:

- accepted graph state and snapshots
- config and lock state
- receipts and provider traces
- candidate groups and resolutions
- decision records and events
- feedback and outcomes
- kit metadata and source artifact provenance

Until managed Postgres or cloud migration tooling exists, the practical
portability story is:

- keep source artifacts and kits versioned
- keep daemon state backed up as a unit
- use Cruxible export/query surfaces for inspection
- avoid customer code that depends on raw SQLite schemas

## Agent Isolation Notes

Local OSS isolation is a practical boundary, not a hard sandbox:

- run the daemon (`cruxible server start`) as the runtime owner
- keep `CRUXIBLE_SERVER_STATE_DIR` outside the repo and agent workspace
- install `cruxible-client` in the agent environment
- expose MCP or HTTP, not the state directory
- use `CRUXIBLE_MODE=governed_write` for normal agent workflows

If the agent can read the daemon state path, control the daemon process, or
import the runtime package with filesystem access, it can bypass the intended
state surfaces. Use a separate VM, host, or managed service when that boundary
must be strong.


========================================================================
SOURCE: https://docs.cruxible.ai/runtime-auth-and-agent-roles/
========================================================================

# Runtime Auth And Agent Roles

Cruxible can run as a local library, but agent workflows that rely on review
gates should run through an authenticated daemon. The daemon owns state and
credentials. Agents use scoped runtime credentials to read, write, propose, or
review state through Cruxible APIs.

## What Auth Protects

Review gates only matter if Cruxible can distinguish the actor doing the work
from the actor approving it. A writer agent must not be able to approve its own
work by sending a request body that claims to be the reviewer.

The core rule is:

> Authentication chooses the actor. Request payloads may carry correlation
> context, but they may not choose identity.

Use authenticated daemon mode when:

- multiple agents collaborate on the same state;
- one agent writes state and another reviews it;
- mutation guards depend on actor identity;
- the state directory should stay outside the agent workspace;
- a hosted or long-lived runtime is being exercised.

Unauthenticated local mode is suitable only for single-user scratch work.

## Bootstrap Flow

Start the daemon with auth enabled and a one-time bootstrap secret:

```bash
CRUXIBLE_SERVER_AUTH=true
CRUXIBLE_RUNTIME_BOOTSTRAP_SECRET=<one-time-secret>
CRUXIBLE_SERVER_STATE_DIR="$HOME/.cruxible/server" \
  cruxible server start
```

The first trusted operator claims the bootstrap secret for the target instance:

```text
POST /api/v1/{instance_id}/runtime/bootstrap/claim
{ "bootstrap_secret": "..." }
```

The daemon returns one plaintext `ADMIN` runtime credential token. Store it in
a secret manager or local operator-owned file outside the agent session. For
local dogfooding, use a file with restrictive permissions such as:

```bash
# ~/.cruxible/auth/agent-operation-admin.env
export CRUXIBLE_SERVER_URL=http://127.0.0.1:8100
export CRUXIBLE_INSTANCE_ID=inst_...
export CRUXIBLE_SERVER_BEARER_TOKEN=<admin-runtime-token>
```

The bootstrap secret cannot be claimed again.

Use the admin credential to create narrower credentials for agents and humans:

```text
POST /api/v1/{instance_id}/runtime/credentials
{
  "label": "writer-agent",
  "permission_mode": "graph_write"
}
```

Runtime credential tokens are stored server-side as hashes. Plaintext token
material is returned only when a credential is created, rotated, or bootstrap is
claimed.

## One Daemon, One Instance (0.2)

On an auth-enabled daemon, only the bootstrap bearer can create an instance.
Every runtime credential minted afterwards — including `ADMIN` — is scoped to
exactly one instance, and the bootstrap secret is consumed by its first
claim. The practical `0.2` model: a daemon whose bootstrap has been claimed
serves the instance it bootstrapped, and any `init` sent with an
instance-scoped credential fails with `InstanceScopeError` regardless of
permission mode. The bootstrap secret cannot help at that point; it is
already spent.

To create a second instance, stand up a second daemon with its own port,
state directory, and bootstrap file:

```bash
CRUXIBLE_SERVER_AUTH=true CRUXIBLE_SERVER_STATE_DIR="$HOME/.cruxible/server-2" \
  cruxible server start --port 8101 \
  --bootstrap-secret-file "$HOME/.cruxible/bootstrap-2.secret"
```

Then bootstrap it exactly as before, pointing at the new port:

```bash
export CRUXIBLE_SERVER_BEARER_TOKEN="$(cat "$HOME/.cruxible/bootstrap-2.secret")"
cruxible --server-url http://127.0.0.1:8101 init --kit <kit> --bootstrap
cruxible context connect --server-url http://127.0.0.1:8101 --instance-id <instance-id>
cruxible credential claim-bootstrap --secret-file "$HOME/.cruxible/bootstrap-2.secret"
```

Alternatively, restarting an existing auth-on daemon (same state directory,
auth still on) issues a fresh one-time bootstrap secret, which can authorize
one more `init --kit <kit> --bootstrap` plus claim on that daemon. Existing
instances and their credentials survive the restart untouched. Prefer the
second daemon when other agents are mid-session; a restart interrupts them.

## Credential Custody

Runtime credentials are bearer secrets. Any process that can read a token can
exercise that token's permissions. Treat them like passwords, API keys, or SSH
private keys:

- do not paste tokens into prompts, tickets, logs, or shared documents;
- do not put broad admin credentials in ordinary agent sessions;
- do not give one agent session both writer and reviewer tokens if independent
  review matters;
- revoke or rotate tokens when a session ends or a token may have leaked.

Cruxible enforces the identity and permission of the token presented on each
request. It cannot prevent a local process from using another token that the
operating system allows that process to read. Strong role separation therefore
requires credential custody outside Cruxible: separate OS users, shell sessions,
keychains, password managers, containers, VMs, or hosted user accounts.

## Agent Environment

Agents should not pass bearer tokens on every individual command. Start the
agent or MCP process with its role token in the environment:

```bash
export CRUXIBLE_SERVER_URL=http://127.0.0.1:8100
export CRUXIBLE_INSTANCE_ID=inst_...
export CRUXIBLE_SERVER_BEARER_TOKEN=<agent-runtime-token>
```

Then server-mode CLI, MCP, and client calls can reuse that credential without
printing it in prompts, shell history, or logs.

## Actor Identity

For auth-on runtime credentials, Cruxible derives actor identity from the credential:

- `actor_type`: `service_account`
- `actor_id`: runtime credential label
- `org_id`: instance ID
- `operation_id`: generated per request

If a request supplies `actor_context`, it must match the authenticated runtime
credential identity. Request payloads may preserve correlation context such as
`request_id`, but they cannot change `actor_type`, `actor_id`, or `org_id`.

This blocks the unsafe pattern:

```text
writer-agent token + actor_context.actor_id = "reviewer"
```

Mutation guards that check actor identity should use this credential-derived
actor context. With server auth disabled, hosted write routes instead use a
declared local operator context (`actor_type=human_user`, `actor_id=operator`,
`org_id=local`) so local sandbox writes remain attributed without credentials.

## Agent Role Pattern

For a review-gated agent workflow, create separate credentials for each role:

- `admin`: bootstrap, credential rotation, and operator maintenance
- `writer-agent`: normal graph writes and proposal creation
- `reviewer-agent`: review decisions and guarded approvals
- `human-reviewer`: optional human approval path

Keep the writer and reviewer credentials separate even on a local machine. If
one agent holds both tokens, Cruxible can no longer enforce that the reviewer is
independent from the writer.

Keep the admin credential separate from normal writer/reviewer agent sessions.
An admin token can mint, revoke, and rotate other runtime credentials, so
exposing it to an ordinary agent collapses the local role boundary.

Treat agent credentials as disposable. If a Codex or Claude session closes and
loses its token, use the stored operator/admin credential to mint a replacement
role credential and optionally revoke the old one. Do not restart the daemon
without auth to work around a lost agent token.

## Restart Discipline

For persistent agent-operated instances, treat auth as sticky operational
state. If a daemon has been started with auth for a state directory, restart it
with auth enabled for that same state directory.

Cruxible records this requirement in server state. Normal startup should fail
if that state directory has previously required auth but the daemon is started
without `CRUXIBLE_SERVER_AUTH=true`.

Do not restart a review-gated daemon without auth just because the process is
unresponsive. Restart scripts and supervisor configs should preserve:

- `CRUXIBLE_SERVER_AUTH=true`
- the same `CRUXIBLE_SERVER_STATE_DIR`
- the runtime credential store
- any bootstrap or secret-manager wiring needed for recovery

If you intentionally need unauthenticated scratch mode, use a separate state
directory.

## Local Boundary

Local auth is a product boundary, not a hardened OS sandbox. A local machine
owner can still intervene out of band by changing process environment, state
files, or databases. That is acceptable for local recovery.

The intended boundary is that normal Cruxible API calls preserve credential
identity and permission mode once auth is on. Stronger isolation requires a
separate user, VM, container boundary, or hosted runtime.

Local users remain responsible for token custody. If a single process can read
multiple role tokens, it can choose any of those roles. Cruxible will still
record which credential acted, reject request-body identity spoofing, and apply
permission checks, but it cannot make readable bearer secrets unusable.

## Recovering Access

If every admin runtime token for a local server state directory is lost, stop the
daemon before attempting recovery. Local recovery treats filesystem ownership of
the server state directory and its `runtime_credentials.db` as the root of trust.
It is not a network operation and does not weaken server auth.

Run recovery directly against the stopped daemon's state dir:

```bash
cruxible credential recover-admin --state-dir "$HOME/.cruxible/server"
```

The command verifies that the invoking uid owns both the state dir and
`runtime_credentials.db`, takes a SQLite `BEGIN IMMEDIATE` lock, mints one new
`ADMIN` credential, records a recovery audit event, and prints the plaintext
token once.

Stop the daemon yourself before running recovery. The lock check is
best-effort only: it refuses when another connection is mid-write, but a
running daemon that is idle holds no SQLite lock and will NOT be detected.
Recovery against a live daemon does not corrupt state (credentials are read
fresh on every request), but the operator — not the lock — is the guarantee
that nothing else is serving the state dir.

After recovery, restart the daemon with auth enabled and use the new admin token
to mint, rotate, or revoke credentials. Existing admin credentials are not
revoked automatically; if the old token should no longer work, revoke it after
you regain access.


========================================================================
SOURCE: https://docs.cruxible.ai/state-resolution-and-maintenance/
========================================================================

# State Resolution And Maintenance

This document is for adopters who have run the [Quickstart](quickstart.md) and
now need to trust Cruxible with real state. It answers two questions from the
runtime's actual behavior: when agents and pipelines disagree, what wins — and
what happens to your graph over time.

Vocabulary (candidate groups, signals, receipts, kits) is defined in
[Concepts](concepts.md). Policy syntax is in the
[Config Reference](config-reference.md). Nothing here repeats those documents.

## 1. How Proposal Conflicts Resolve

### Signature buckets

Every governed proposal lands in a **signature bucket**: a SHA-256 of the
relationship type plus canonical `thesis_facts` (`sigv1:...`). The signature
deliberately excludes `analysis_state`, so LLM rationale and other run-varying
context never split a bucket. Workflow-authored proposals hash the workflow
name, step, proposal logic digest, signal sources, and the relationship's
policy; direct agent proposals hash the relationship, the member-derived
signal sources, and the caller's scope facts. The bucket is the unit of
precedent: resolutions and trust are stored per `(relationship_type,
signature)`, not per edge.

### What gets suppressed at proposal time

Before a group is stored, each proposed member tuple is checked (for
`proposal_identity: relationship_tuple` relationships):

- tuple already live in the graph → suppressed, reason `existing_edge`;
- tuple already sitting in a `pending_review` or `applying` group → suppressed,
  reason `pending_proposal`, with the competing group's id in the result;
- tuple already approved earlier in this same signature bucket → suppressed as
  `existing_edge`.

If everything is suppressed, no group is created — the propose result comes
back `suppressed: true` with the per-tuple reasons. Duplicate work is refused
at the door, not merged later.

### Review priorities

Each stored group carries a mechanical `review_priority` derived from policy
signals and prior trust — `cruxible group list` sorts by it:

| Priority | Set when |
|---|---|
| `critical` | any member carries a `contradict` signal from a **blocking** source, or the bucket's prior resolution was **invalidated** |
| `review` | first contact (no prior confirmed approval for this signature); an `unsure` signal where the source sets `always_review_on_unsure` or has role `blocking`/`required`; a `support` signal with no evidence under `require_evidence_on_support`; prior resolution on `watch`; a decision policy with effect `require_review` matched; or a member tuple whose live edge has an active override or pending/rejected review state |
| `normal` | none of the above — a clean repeat of an already-reviewed thesis |

Signals from sources with role `advisory` are skipped entirely in this
derivation. Priority is advisory ordering for reviewers; it does not gate who
may resolve.

### Auto-resolve: earned, per bucket, never on first contact

A fresh group is stored as `auto_resolved` instead of `pending_review` only
when **all** of the following hold:

1. The bucket has a prior **confirmed approval** whose trust status satisfies
   `auto_resolve_requires_prior_trust` (`trusted_only` by default;
   `trusted_or_watch` optionally). No prior resolution — or an `invalidated`
   one — means no auto-resolve. **The first run of any thesis always goes to
   review.**
2. Current signals satisfy `auto_resolve_when`: `all_support` (every
   non-advisory signal is `support`) or `no_contradict` (no blocking
   `contradict`). An `unsure` under `always_review_on_unsure`, or an
   unevidenced `support` under `require_evidence_on_support`, disqualifies
   regardless of policy.
3. Nothing forces review: no matched `require_review` decision policy, and no
   member tuple with an active edge override.

Trust does not accumulate automatically. A first approval records the
resolution at `watch`. Promotion is an explicit act:

```bash
cruxible group resolutions                 # find the resolution ID
cruxible group trust --resolution <id> --status trusted \
  --reason "Spot-checked 20 members against source documents"
```

`group trust` also revokes: `--status invalidated` makes the next matching
proposal come back `critical` and permanently blocks auto-resolve until a
human re-approves the bucket (that re-approval resets trust to `watch`, not
`trusted`). Trust can only be set on the **latest confirmed approval** for a
signature — you cannot re-trust a superseded precedent. Trust changes never
touch existing edges; demoting a precedent and retracting a wrong edge are two
separate acts.

One honest limit: `auto_resolved` is a status, not an applied write. An
auto-resolved group has skipped human triage, but its edges are written only
when something calls `group resolve --action approve` (a `GRAPH_WRITE`
operation). Nothing in core applies auto-resolved groups on a timer.

### Re-proposing while a group is pending

Buckets converge instead of forking. If a proposal arrives for a signature
that already has a `pending_review` group, the pending group is **rewritten in
place**: members replaced (default) or merged (`pending_refresh_mode:
retain_missing`), metadata refreshed, priority re-derived, and
`pending_version` incremented. A rewrite never auto-resolves — auto-resolve is
evaluated only for fresh buckets. If the re-proposal has no surviving members,
the default mode clears the now-empty pending group (with a `group_clear`
receipt); `retain_missing` leaves it standing.

`pending_version` is the reviewer's concurrency guard: resolve requires
`--expected-pending-version`, and a mismatch fails with "Group changed during
review". You approve the exact member set you inspected, or nothing.

### Approval and rejection semantics

**Approve** validates every member against the current graph and config:
already-live tuples are skipped (reason `existing_edge` — pass
`stamp_existing` to instead bless the surviving edge with the group's review
state and provenance), invalid members are skipped with the validation detail,
and relationship evidence guards can abort the whole approval. Valid members
become edges through the governed `group_resolve` write path, stamped with the
group's evidence refs, source receipt/trace/step ids, and an
`assertion.review` of `approved/group`. The resolution is confirmed and the
group moves to `resolved`. If the process dies mid-apply the group is left
`applying`; re-running approve retries the same resolution (reject is refused
in that state).

**Reject** writes no edges. It records a confirmed `reject` resolution (with
your rationale and the group's full thesis and analysis state) and marks the
group `resolved`. Rejection is not a tombstone: it does not count as the prior
approval that auto-resolve looks for, so a re-proposal of the same thesis
opens a fresh bucket that again forces review. If you want a rejection to
*teach* the system, pair it with structured feedback (`cruxible feedback`) or
a decision policy so the same candidates get suppressed at proposal time.

## 2. Direct Writes Vs Governed Writes

### Permission tiers

The runtime enforces four cumulative tiers via `CRUXIBLE_MODE`
(`ADMIN ⊃ GRAPH_WRITE ⊃ GOVERNED_WRITE ⊃ READ_ONLY`):

| Tier | Can do |
|---|---|
| `read_only` | queries, receipts, traces, inspect, `group list`/`get`/`status`, state health, workflow planning |
| `governed_write` | propose groups, run/test/propose workflows, feedback and outcomes, decision records, snapshots, constraints and decision policies, state pulls |
| `graph_write` | `entity add`/`update`, `relationship add`, batch direct write, **canonical workflow apply**, **group resolve**, **group trust** |
| `admin` | config reload, locks, clones, backup/restore, state publish, overlays, credentials |

The split to notice: an agent at `governed_write` can *propose* anything but
*commit* nothing — resolving a group, applying a canonical preview, and
adjusting trust all sit at `graph_write`. When `CRUXIBLE_MODE` is unset the
local default is `admin` (deliberate, for local UX; set
`CRUXIBLE_DEFAULT_READ_ONLY=1` or an explicit mode to change it).

### Write policies are orthogonal to tiers

Per-type `write_policy` is a hard governance constraint that no tier
overrides, including `admin`:

- `proposal_only` — direct writes (`entity add`, `relationship add`, batch
  direct write, the typed lifecycle write) are refused with
  `direct_write_refused`; state enters only through the governed verbs
  (`workflow_apply`, `group_resolve`) or, for relationships, staged with
  `pending=true`. The `CRUXIBLE_REFUSE_DIRECT_WRITES` env kill-switch forces
  this instance-wide.
- `mint_only` — refuses **every** writer including the governed verbs; only
  the `token_mint` source may write.

### Mutation guards refuse with reasons and receipts

Config-defined mutation guards (actor identity, co-write requirements,
evidence floors, named-query result counts) run at the write chokepoints —
direct writes, workflow apply, and group approval alike. A refusal is a
`DataValidationError` whose errors name the guard and the offending write
(`Mutation guard '<name>' rejected write <type>:<id> <property>=<value>:
<message>`). Failed mutations still persist a receipt: the receipt records the
failed validation nodes and the error carries its `mutation_receipt_id`, so a
refusal is as auditable as a success.

### Auth-managed types

An entity type marked `auth_managed: true` + `write_policy: mint_only` (the
agent-operation kit's `Actor` is the canonical example) is materialized
through the internal `token_mint` source: auth-on daemons use runtime-
credential mints, while auth-off daemons create a declared local `operator`
identity. Config-declared workflows that target a `mint_only` type are
rejected at config load, and lifecycle updates are refused like any other
write. Facts *about* such an entity belong on notes attached to it, never on
the entity itself.

### Provenance on every edge

Every edge carries system-owned provenance: `source` (the operation),
`source_ref`, `created_at`/`last_modified_*`, actor context from either the
auth-on credential or the auth-off local operator,
and write-time `receipt_id`/`resolution_id` correlation. The `source_ref`
classes are how you read authority off an edge:

- `add_relationship` / `batch_direct_write` — direct-written;
- `group:<group_id>` — group-backed, with `resolution_id` linking to the
  approval;
- anything else (workflow apply refs, `clone_origin`-stamped snapshot/pull
  edges, legacy nulls) — "other".

Governed groups additionally record how their evidence was produced in their
signature facts: `evidence_mode: workflow_generated` (proposal built by a
locked workflow, carrying the workflow name, step, and proposal logic digest)
vs `agent_supplied` (an agent asserted the signals directly). The two modes
hash into different signatures, so agent-asserted judgments never inherit the
trust earned by a pipeline's judgments.

## 3. State Maintenance Over Time

### Lifecycle, not deletion

Entities are `live` / `superseded` / `retired`; relationships are `active` /
`inactive` / `superseded` / `retracted`. Non-live state is gated out of live
reads but stays fetchable by id, with `reason`, `closed_at`/`closed_by`, and
supersession links preserved. Lifecycle is set only through the typed channel:

```bash
cruxible entity update --type Matter --id M-104 \
  --lifecycle-status retired --lifecycle-reason "Matter closed 2026-06-30"
```

Hand-authored `metadata={"lifecycle": ...}` is inert free-form data — it can
never become the typed state. The lifecycle write is a direct-write verb, so a
`proposal_only` type refuses it too. Reserve deletion for bad imports and test
data; everything operational should retire, not vanish.

### Re-running deterministic ingest

Canonical ingest workflows are safe to re-run:

- **No-op upserts.** `apply_entities` / `apply_relationships` compare against
  current state; an upsert that changes nothing is counted as a `noop` — no
  write, no receipt write-node, no provenance churn. Re-running an unchanged
  ingest converges instead of rewriting.
- **Digest-pinned artifacts.** Canonical workflows require their file or
  directory artifacts to carry a `sha256:` digest. The digest is verified
  against disk when the lock is built and again when a plan compiles. If seed
  data changes underneath you, the run fails with the expected and actual
  hashes; `cruxible lock --force` is the explicit act of accepting the new
  content. Data cannot drift silently under a pinned workflow.
- **Preview/apply identity.** The `apply_digest` binds workflow name,
  normalized input, lock digest, head snapshot, and the previewed changes —
  apply refuses a preview that no longer matches what you inspected.

### Staleness is a kit-level idiom

Core has no decay or freshness engine — time-based maintenance is written as
kit workflows. The pattern is a **date sweep**: a canonical workflow that
queries current state, applies a deterministic date rule in a provider, and
writes back narrow status changes. The case-law kit's
`refresh_stale_deadlines` is the reference example: it closes deadlines that
lapsed or whose matter closed, and deliberately does *not* auto-close the work
items behind them — those close only through the review gate. If your domain
has a "stale after N days" rule, model it as a sweep workflow so the rule is
pinned, previewable, and receipted.

### State health

`cruxible state health` (also `GET /api/v1/{instance_id}/state/health`) is the
deterministic maintenance dashboard. It reports raw counts, ages, and binary
facts only — no scoring or severity; interpretation is left to you or your
agents. Five sections, plus `captured_at` and the current `head_snapshot_id`:

- **groups** — counts by status and the age span of the *unresolved* backlog
  (`pending_review` + `applying` only; an old pending group is a stale review
  queue, an old applying group is a stuck apply);
- **signals** — `unevidenced_support_by_source`: support signals sitting in
  pending review with no evidence, counted per source and scoped to sources
  that declare `require_evidence_on_support` — a per-source backlog of
  judgments asserted without proof;
- **provenance** — every live edge tallied as direct-write, group-backed, or
  other (watch the direct-write share on a domain you meant to govern);
- **freshness** — source-artifact and provider-trace counts and oldest ages,
  plus config/graph compatibility warnings;
- **integrity** — orphan entities, unused entity/relationship types, and
  whether the workflow configuration is locked.

## 4. Repair: When Accepted State Is Wrong

Wrong state that passed review is fixed in the open, not rewritten. The
sequence:

1. **Retire the wrong fact with a reason.**
   `cruxible relationship update ... --lifecycle-status retracted
   --lifecycle-reason "..."` (or `entity update --lifecycle-status retired`).
   The edge leaves live reads; its history, provenance, and receipts remain.
2. **Demote the precedent that admitted it.**
   `cruxible group trust --resolution <id> --status invalidated --reason "..."`
   so future matches of the same thesis re-review instead of auto-resolving.
   Skipping this step means the same pipeline can re-admit the same mistake.
3. **Re-propose the correction.** Propose the corrected members through the
   normal governed path. First contact with the corrected thesis forces review
   — that is the system working, not friction.
4. **Let quality checks catch the rest.** `cruxible evaluate` and `cruxible
   lint` report constraint violations, orphans, coverage gaps, and
   quality-check failures deterministically; `cruxible lint` additionally
   turns repeated rejection feedback and negative outcomes into concrete
   suggestions (constraints, decision policies, trust demotions).

For the audit trail while you work:

```bash
cruxible entity history --type Matter --id M-104   # receipt-derived change history
cruxible explain --receipt <receipt-id>            # render any receipt
cruxible group get --group <group-id>              # thesis, members, signals, resolution
```

Every mutation — including refused ones — has a receipt; every group-backed
edge links its `resolution_id`; every resolution stores the thesis and
analysis state it was judged on. If you cannot reconstruct why an edge exists,
that is a bug worth reporting, not a gap you should paper over.

## Summary: Who Wins

- **Pipelines and agents never overwrite each other silently.** Live edges and
  pending groups suppress overlapping proposals; pending buckets converge by
  rewrite with a version guard; direct writes to governed types are refused.
- **Review wins by default.** First contact, contradictions, unsure signals,
  and unevidenced support all force a human (or `graph_write` agent) decision.
- **Automation is earned per thesis** — a confirmed approval promoted to
  `trusted`, revocable in one command.
- **Time is handled by pinned workflows, not decay** — and `state health`
  tells you when the backlog, evidence debt, or provenance mix needs
  attention.


========================================================================
SOURCE: https://docs.cruxible.ai/publishing-states/
========================================================================

# Publishing And Subscribing To States

A reference state is a published, versioned release of an instance's state
that other instances subscribe to and track. Cruxible publishes the KEV
reference this way (consume it via the [KEV Guide](kev-guide.md)); this page
is the generic mechanism — for building a reference locally, publishing your
own releases, and subscribing an overlay instance to any published state.

Everything here runs against a local daemon from the
[Quickstart](quickstart.md) setup.

## Build A Reference State Locally

The KEV reference kit is the worked example: it builds the public reference
graph from the pinned CISA/NVD/EPSS snapshot in the kit's `data/`.

Initialize the standalone KEV reference kit. This materializes the kit bundle,
loads its config, and gives you an instance ID.

```bash
cruxible --server-url http://127.0.0.1:8100 init --kit kev-reference
```

Keep the returned `instance_id`; every server-backed command after init uses it.
Kit init installs the kit's pinned workflow lock automatically, so you can
preview the canonical reference refresh right away:

```bash
cruxible --server-url http://127.0.0.1:8100 --instance-id <instance-id> run \
  --workflow build_public_kev_reference \
  --save-preview kev-reference-preview.json
```

Canonical workflows preview state first. Apply the preview only after checking
the `apply_digest`, changed counts, receipt ID, and trace IDs:

```bash
cruxible --server-url http://127.0.0.1:8100 --instance-id <instance-id> apply \
  --preview-file kev-reference-preview.json
```

Run a query and inspect its receipt:

```bash
cruxible --server-url http://127.0.0.1:8100 --instance-id <instance-id> query run \
  vulnerability_products \
  --param cve_id=CVE-2020-1472
```

Every query returns a receipt ID. In MCP, fetch the full proof with
`cruxible_receipt(instance_id, "<receipt-id>")`. The CLI `explain` command
renders receipts in both server and direct-local modes.

## Publish, Then Subscribe An Overlay

An overlay kit composes local state and workflows over a published upstream.
The KEV triage kit is the worked example: it tracks the KEV reference and
adds local assets, services, controls, and governed proposal workflows.

One extra prerequisite for the `--state-ref` path: the
[oras](https://oras.land/docs/installation) CLI (`brew install oras` on
macOS). The state catalog resolves `--state-ref` aliases to OCI refs, and
the OCI transport shells out to `oras`. The `file://` path below needs no
extra tooling.

```bash
cruxible --server-url http://127.0.0.1:8100 state create-overlay \
  --state-ref kev-reference \
  --kit kev-triage \
  --root-dir "$PWD/kev-triage-workspace"
```

`--state-ref kev-reference` resolves through the published state catalog. In
a source checkout before published OCI reference states are available (or
without `oras`), publish the reference instance you built above to a local
`file://` transport and pass `--transport-ref` instead of `--state-ref`:

```bash
cruxible --server-url http://127.0.0.1:8100 --instance-id <instance-id> state publish \
  --transport-ref "file://$PWD/releases/kev-reference/v1" \
  --state-id kev-reference \
  --release-id v1

cruxible --server-url http://127.0.0.1:8100 state create-overlay \
  --transport-ref "file://$PWD/releases/kev-reference/v1" \
  --kit kev-triage \
  --root-dir "$PWD/kev-triage-workspace"
```

`file://` refs must be absolute paths, and publish refuses a target that
already exists — pick a new release directory per publish.

The command returns a new overlay `instance_id` and locks the overlay as part
of creation. Preview the local canonical state refresh and apply it:

```bash
cruxible --server-url http://127.0.0.1:8100 --instance-id <overlay-instance-id> run \
  --workflow build_local_state \
  --save-preview kev-local-preview.json
cruxible --server-url http://127.0.0.1:8100 --instance-id <overlay-instance-id> apply \
  --preview-file kev-local-preview.json
```

Run a governed proposal workflow and inspect the pending group:

```bash
cruxible --server-url http://127.0.0.1:8100 --instance-id <overlay-instance-id> propose \
  --workflow propose_asset_products

cruxible --server-url http://127.0.0.1:8100 --instance-id <overlay-instance-id> group list \
  --status pending_review
cruxible --server-url http://127.0.0.1:8100 --instance-id <overlay-instance-id> group get \
  --group <group-id>
```

Approve or reject only after reviewing the group thesis, member signals,
receipt, trace IDs, and pending version:

```bash
cruxible --server-url http://127.0.0.1:8100 --instance-id <overlay-instance-id> group resolve \
  --group <group-id> \
  --action approve \
  --expected-pending-version <pending-version> \
  --rationale "Reviewed source evidence and accepted the proposed mappings"
```



========================================================================
SOURCE: https://docs.cruxible.ai/isolated-deployment/
========================================================================

# Isolated Deployment

This guide is the advanced path for users who want a real runtime boundary between the agent and the Cruxible graph.

It is not the default onboarding flow. The normal local setup is still the fastest way to try Cruxible, but local same-user setups are a convenience mode, not a strong isolation boundary.

## What This Guide Is For

Use this guide if you want the agent to interact with Cruxible without being able to:

- import the `cruxible` runtime package directly
- read the graph files directly
- bypass daemon permission modes by reaching into the runtime

If that is not a requirement, stick with the standard [Quickstart](quickstart.md).

## What Creates a Real Boundary

At minimum, all of these need to be true:

- the `cruxible` runtime runs as a different principal or on a different host
- the graph state directory is readable only by that runtime principal
- the agent environment installs only `cruxible-client`
- the agent talks to Cruxible over HTTP or a Unix socket, not through shared filesystem access
- the agent cannot escalate into the runtime environment through `sudo`, Docker, SSH, or shared source checkout access

If the agent can read the instance files, import the runtime source, or control Docker on the runtime host, the boundary is not real.

## What Does Not Create a Real Boundary

These are useful for convenience, but they are not sufficient for graph isolation by themselves:

- separate `uv` environments only
- Docker on the same machine if the agent can run `docker`
- a named Docker volume if the agent can mount or inspect it
- keeping the full `cruxible` runtime installed in the agent environment
- a shared repo checkout visible to both the agent and the runtime
- running the agent and the daemon as the same Unix user

## Recommended Patterns

There are two practical ways to isolate the runtime:

- same machine, separate Unix user
- separate host or VM

The second is stronger. The first is often enough for local or internal setups.

## Option 1: Same Machine, Separate Unix User

This is the smallest setup that creates a meaningful local wall.

### 1. Create a dedicated runtime user

```bash
sudo useradd --system --create-home --home-dir /var/lib/cruxible cruxd
sudo mkdir -p /var/lib/cruxible
sudo chown -R cruxd:cruxd /var/lib/cruxible
sudo chmod 700 /var/lib/cruxible
```

Use a state directory the agent user cannot read. This example uses `/var/lib/cruxible`, but any directory owned only by the runtime user is fine.

### 2. Install the daemon runtime for that user

```bash
sudo -u cruxd python3 -m venv /opt/cruxible
sudo -u cruxd /opt/cruxible/bin/pip install cruxible
```

### 3. Start the server as the runtime user

```bash
sudo -u cruxd env \
  CRUXIBLE_SERVER_STATE_DIR=/var/lib/cruxible \
  CRUXIBLE_SERVER_AUTH=true \
  CRUXIBLE_RUNTIME_BOOTSTRAP_SECRET=change-me-once \
  CRUXIBLE_HOST=127.0.0.1 \
  CRUXIBLE_PORT=8100 \
  /opt/cruxible/bin/cruxible server start
```

Notes:

- `CRUXIBLE_HOST=127.0.0.1` keeps the daemon local to the machine.
- `CRUXIBLE_SERVER_AUTH=true` and `CRUXIBLE_RUNTIME_BOOTSTRAP_SECRET` enable
  runtime credential bootstrapping. The bootstrap secret is not the only way to
  satisfy the startup auth requirement: an already-stored runtime credential also
  satisfies it, so `CRUXIBLE_RUNTIME_BOOTSTRAP_SECRET` can be omitted once
  credentials have been provisioned.
- `CRUXIBLE_SERVER_STATE_DIR` ensures the server-owned state stays under the runtime user's directory.
- Claim the bootstrap secret, create role-specific runtime credentials, and use
  those tokens from the agent environment. See
  [Runtime Auth And Agent Roles](runtime-auth-and-agent-roles.md).

### 4. Install only the client in the agent environment

```bash
python3 -m venv .venv-agent
. .venv-agent/bin/activate
pip install cruxible-client
```

The agent environment should not have the `cruxible` runtime installed.

### 5. Connect to the isolated daemon

```python
from cruxible_client import CruxibleClient

with CruxibleClient(
    base_url="http://127.0.0.1:8100",
    token="<runtime-credential-token>",
) as client:
    result = client.validate(config_path="config.yaml")
    print(result.valid)
```

### 6. Lock down the agent user

This setup only works as a real boundary if the agent user does not also have a privilege-escalation path into the runtime environment.

At minimum, the agent user should not have:

- `sudo` access to become `cruxd`
- membership in the `docker` group
- read access to `/var/lib/cruxible`
- access to the `cruxible` source tree or runtime virtualenv

## Option 2: Separate Host or VM

This is the stronger version of the same pattern.

Run the `cruxible` runtime on a different machine and keep the state directory there. The agent machine installs only `cruxible-client` and connects over HTTP.

Daemon host:

```bash
python3 -m venv /opt/cruxible
/opt/cruxible/bin/pip install cruxible
env \
  CRUXIBLE_SERVER_STATE_DIR=/var/lib/cruxible \
  CRUXIBLE_SERVER_AUTH=true \
  CRUXIBLE_RUNTIME_BOOTSTRAP_SECRET=change-me-once \
  CRUXIBLE_HOST=0.0.0.0 \
  CRUXIBLE_PORT=8100 \
  /opt/cruxible/bin/cruxible server start
```

Agent host:

```bash
python3 -m venv .venv-agent
. .venv-agent/bin/activate
pip install cruxible-client
```

Python client:

```python
from cruxible_client import CruxibleClient

with CruxibleClient(
    base_url="https://your-cruxible-host.example.com",
    token="<runtime-credential-token>",
) as client:
    result = client.validate(config_path="config.yaml")
    print(result.valid)
```

For real deployments, put the HTTP service behind normal operational controls such as TLS, firewall rules, and standard secret handling.

## MCP Caveat

Today, `cruxible-mcp` is part of the `cruxible` package.

That means the easiest local MCP setup still installs the full runtime package on the machine where the agent runs. It is convenient, but it is not the strongest isolation story.

If you need a real boundary today, the cleanest path is:

- the `cruxible` runtime on the isolated host
- `cruxible-client` in the agent environment
- HTTP-based access from the agent side

Use local MCP for convenience and development. Use client-to-daemon separation when isolation matters.

## Docker Caveat

Docker is useful for packaging and reproducibility. It is not automatically a security boundary.

Docker only helps with isolation if the agent cannot:

- run `docker`
- access the Docker socket
- mount the runtime volume
- inspect the runtime container filesystem

If the agent can do those things, it can usually recover the graph data anyway.

## Practical Recommendation

Choose one of these modes deliberately:

- default local mode: fastest onboarding, advisory permissions only
- isolated mode: more setup, meaningful runtime boundary

If you only need convenience, use the standard local setup.

If you need the graph to be genuinely inaccessible to the agent, use one of the isolated patterns above.


========================================================================
SOURCE: https://docs.cruxible.ai/hosted-runtime-image/
========================================================================

# Hosted Runtime Image

The hosted runtime image packages `cruxible` (daemon included) for private runtime
containers. It starts the daemon (`cruxible server start`) as a non-root
`cruxible` user and stores mutable server state under `/var/lib/cruxible/server`.

Build with any Docker-compatible backend. OrbStack works for local development:

```bash
docker build -f deploy/runtime/Dockerfile -t cruxible-core-runtime:test .
```

Run with a mounted state directory and a runtime-supplied bootstrap secret:

```bash
STATE_DIR="$(mktemp -d)"
chmod 0777 "${STATE_DIR}"
docker run --rm \
  -e CRUXIBLE_RUNTIME_BOOTSTRAP_SECRET=bootstrap-secret \
  -v "${STATE_DIR}":/var/lib/cruxible/server \
  -p 127.0.0.1:8100:8100 \
  cruxible-core-runtime:test
```

The image intentionally fails fast if `/var/lib/cruxible/server` is not an
external Docker mount, or if the non-root `cruxible` user cannot write to it.
This prevents hosted runtime state from being stored only in the container's
ephemeral filesystem layer.

The external Cloud control plane (the separate `cruxible-cloud-api` package, not
`cruxible`) is what prepares each per-instance host state directory before
starting the runtime container. By default it applies mode `0777`, matching the
local smoke-test pattern above so the non-root container user can write through
the bind mount on a normal Linux host. Tighter host-ownership modes are
configured on that control plane, not through any `cruxible` environment
variable an operator of this image sets directly.

Verify the server:

```bash
curl http://127.0.0.1:8100/health
```

Expected response:

```json
{"status":"ok"}
```

Do not bake bootstrap secrets or runtime credentials into the image. Provide
them at container runtime through environment variables or the future deployment
secret layer. See [Runtime Auth And Agent Roles](runtime-auth-and-agent-roles.md)
for the bootstrap and credential model.

## Shared Profile Customer Code Policy

Set `CRUXIBLE_HOSTED_SERVER_PROFILE=shared` for runtimes that may host
untrusted or multi-tenant material. In this profile, provider execution and
Python provider loading are denied unless
`CRUXIBLE_HOSTED_ISOLATED_EXECUTION_BACKEND` is set to a supported isolated
backend. The current supported backend name is `docker`.

Unsupported or missing isolated backends fail with the public-safe error code
`customer_code_execution_unsupported`.

## Private Runtime Network

Hosted runtimes should not publish port `8100` on the public host interface.
Public traffic should enter through external/future Cloud components — the edge
proxy or `cruxible-cloud-api`, neither of which ships in this repo — and
Cloud/API should reach runtimes over a private Docker network.

For local development, create a writable state directory and run the private
network proof:

```bash
STATE_DIR="$(mktemp -d)"
chmod 0777 "${STATE_DIR}"
CRUXIBLE_RUNTIME_STATE_DIR="${STATE_DIR}" \
  docker compose -f deploy/local/private-runtime-network.compose.yml up \
  --build --abort-on-container-exit runtime-probe
```

The `runtime` service uses `expose: ["8100"]` for same-network discovery but
does not publish `8100` to the host. The `runtime-probe` service can reach
`http://runtime:8100/health` because it joins the same private Docker network.

On a future Droplet or VM deployment, this same boundary should be reinforced
with firewall/VPC rules: public ingress is limited to the edge proxy ports
(`80`/`443`) and SSH, while runtime port `8100` remains private to Cloud/API or
the runtime network.
