aeat.application.operator_surface._classification module

Declared per-command risk classification, co-located with the manifest.

The MCP console’s tool annotations and its human-in-the-loop confirmation tier both need to know a command’s risk posture: is it destructive, idempotent, a filing handoff, a forbidden AEAT live-write, and does it reach the outside world (the AEAT sede)? Before this module those axes were inferred from small leaf-name frozensets scattered across the MCP layer, and openWorldHint was never set at all (research F3) - a new mutating verb outside the lists silently classified as non-destructive, and the annotation hint and the server gate could drift because they derived independently.

This module makes the classification ONE declared, typed record keyed by command key. The destructive / handoff / live-write axes - the genuinely-judgment ones - are DECLARED per command in _risk_table (ADR mcp-protocol-hardening H3, as its wording actually decided: “declared data keyed by command key … with a parity gate”); read_only and idempotent are derived from the manifest family mutability, and open_world is derived from the app.live./pull facts. The MCP annotation projection and the HITL confirmation tier both consume command_classification(), so the client hint and the server gate read one authority and cannot drift, and the no-silent-default parity gate asserts every mutating-family command carries an explicit declaration.

This replaces the earlier leaf-NAME frozensets, which matched on the command key’s trailing word and so let a new mutating verb named purge/wipe/finalize fall through, classify non-destructive, and auto-approve (the safety finding of the 2026-07-08 MCP console review).

class CommandClassification(**data)[source]

Bases: BaseModel

The declared risk posture of one command, the single classification authority.

read_only mirrors the manifest family mutability; destructive is true only for irreversible state-destroying verbs; idempotent for pure repeatable reads; handoff for filing-grade outputs a human confirms; live_write for a (never-exposed) AEAT-submission verb; open_world for a verb that reaches the outside AEAT sede.

Parameters:
command_key: str
read_only: bool
destructive: bool
idempotent: bool
handoff: bool
live_write: bool
open_world: bool
classify_command(command_key, *, mutability)[source]

Classify one command from its key, its manifest mutability, and its risk row.

The destructive / handoff / live-write axes come from the declared risk row (declared_risk()); a read-only command has no row and all three are False. read_only and idempotent derive from the family mutability (a live-write is never read-only - the stronger per-command declaration wins); open_world derives from the command path. This is the single derivation the annotation projection and the HITL tier consume.

Return type:

CommandClassification

Returns:

The command’s CommandClassification.

Parameters:
command_classification(command_key)[source]

Classify a command by key alone, resolving its family mutability from the manifest.

The by-key accessor the HITL confirmation tier and the persona handoff-deny rules consume when they hold only a command key (not the mutability the annotation builder already has). Reads the same declared risk table, so all three consumers share one authority.

Return type:

CommandClassification

Returns:

The command’s CommandClassification.

Parameters:

command_key (str)

classification_is_coherent(classification)[source]

Whether one classification’s axes are mutually consistent.

A tool is never both read-only and destructive; a read-only tool is idempotent; a live-write is never read-only. This is the invariant the parity gate asserts over the whole manifest command set.

Return type:

bool

Parameters:

classification (CommandClassification)