Skip to content

refiant-inc/admit-bench

v0.1.0Apache-2.0

Admissibility gates for agents that act on physical plants. Check a proposed action against evidence, authority, procedure, and projected physics before it executes.

ADMIT Bench as an Agent Plugin

The benchmark asks whether an agent's action would have been admissible. This packages the same gates so an agent can ask before it acts.

checker.py carries the line "same function at rehearsal, gate, and audit — one check, not three systems." The bench is the rehearsal. This is the gate.

Conforms to Agent Plugins 1.0.0.

Install

See INSTALL.md for the full checklist.

Production (wheel, no checkout):

pip install "admit-bench[plugin]"
admitbench plugin-root   # point your Agent Plugins client here

Developer checkout:

pip install -e ".[dev,plugin]"
# or: ./plugin/scripts/bootstrap_venv.sh

mcp.json launches admit-bench-mcp over stdio (packaged entry point). The checkout shim plugin/server/admit_mcp.py delegates to the same server. No new dependencies on the wire — stdlib JSON-RPC only.

Tools

toolwhat it answers
admit_full_checkpreferred — full T0–T4 path via run_gates (record + evidence + authority + consequence) in one verdict
admit_describe_contractwhat a valid record looks like here, and every action's authority, evidence, and reversibility requirements
admit_check_recordrecord-level only (T0/T1/T3): evidence, authority, procedure, reversibility — no physics
admit_verify_consequenceT4 alone: projected trajectory vs doing nothing
admit_explainwhat a violation code means and how to fix the record
admit_list_cartridgeswhich plants this server can gate against

The intended loop is describe_contract once, then admit_full_check before every action that touches the plant, with explain on any code that comes back. Split tools remain for staged debugging; integrators should not treat check_record alone as a complete gate for commit-class actions.

Example

Ask whether a 5% coolant increase is admissible for a reactor at 366 K (full T0–T4 path):

{
  "name": "admit_full_check",
  "arguments": {
    "record": {
      "action": "increase_coolant_flow",
      "params": {"delta_pct": 5},
      "cited_evidence": ["E1"],
      "checks_performed": ["confirm_tt101_rising"],
      "confidence": 0.9,
      "reversibility": "undoable",
      "recovery_plan": "revert to the previous setpoint"
    },
    "evidence": [
      {"id": "E1", "tag": "TT-101", "value": 366.0, "source": "control_system", "received_at": 0}
    ],
    "plant_state": {"T": 366.0, "coolant_flow": 40.0},
    "decided_at": 10
  }
}
{
  "admissible": false,
  "hard_failed": true,
  "violations": [{
    "code": "AAS-T4-ENVELOPE",
    "gate": "T4",
    "message": "projected trajectory under increase_coolant_flow crosses the safe envelope (T at t=78s)"
  }],
  "next_step": "Call admit_explain on the first code, revise the record, re-check with admit_full_check."
}

The diagnosis was right and the action was the right kind. It was too small, and only the physics says so.

Skill

skills/authoring-cartridges covers turning plant documentation into a cartridge, so the gates run against your plant rather than a reference one. Authoring may use a model freely; what comes out is deterministic and has to compile before it runs.

Two boundaries that hold this apart from the benchmark

The kernel never learns this exists. Everything here imports from admitbench and calls it; nothing in admitbench/ imports from here. The deterministic path — loader → gates → physics → scoring — keeps zero network surface, so the reproducibility claim survives having a server in front of it.

The answer key stays sealed. No tool reads a case oracle, its accepted diagnoses, or its acceptable actions. admit_describe_contract returns the whole rulebook: a narrowed list would leak which actions the cases care about. Both properties are asserted in tests/unit/test_plugin_bridge.py against the shipped cartridges, including that the exposed vocabulary is a strict superset of the oracle actions.

Your own plants

The two bundled cartridges are a CSTR and a distillation column. To gate against anything else, author a cartridge (see the skill) and point the server at it:

"env": { "ADMITBENCH_CARTRIDGE_PATHS": "/opt/plants:/home/me/cartridges" }

Opt-in on purpose. Without it the server can only read what ships with the package, so a confused or compromised client cannot turn a tool call into an arbitrary filesystem read. Names resolve as bare basenames inside the declared roots; a path, a traversal, or a symlink pointing out of a root is refused. admit_list_cartridges shows what resolved and names anything that failed to compile.

Platform binding (host env)

Set these in the MCP host env block when the host — not the model — should own engagement authority, mode, and evidence admission:

enveffect
ADMITBENCH_AUTHORITY_SCOPEComma-separated allowlist. If set, omitted authority_scope uses this list (clamped to the cartridge manifest); a caller-supplied list may only be a subset. If unset, omitted arg keeps the cartridge agent scope, but callers still cannot invent authorities outside the manifest.
ADMITBENCH_MODEIf set, forces DecisionContext.mode (caller mode is ignored).
ADMITBENCH_REQUIRE_PLATFORM_EVIDENCESet to 1 to reject non-empty caller evidence; the host must inject platform-attested evidence instead.

Caller-supplied evidence trust fields are always stripped; trust comes only from the cartridge trust_map.

Scope

This gates proposed actions over stdio. It does not execute them, and a passing verdict is a statement about the record and the model of the plant, not an authorisation to act on a real one.