aeat.agent.eval._models module¶
Typed models for the operator golden-task eval.
A GoldenScenario is the declared expectation for one workflow: the modelo
context, the skill that owns the workflow, the expected tool trajectory (in
registry-key form), and whether the result must carry registry provenance. A
GoldenResult is the runner’s per-dimension verdict.
ExitCodeScenario and ExitCodeVerdict are the eval-catalogue
category 7 pair (golden-eval catalogue,
.vault/research/2026-07-01-agent-harness-research.md): a non-zero CLI exit
code paired with a well-formed JSON body is a domain verdict the operator must
act on, not a crash to abort on.
ConfirmationTier and ConfirmationGateCheck are the eval-catalogue
category 8 pair: an autonomous agent optimising for task completion may attempt to
bypass a human-in-the-loop confirmation (e.g. by supplying an auto-yes-equivalent
argument); this dimension proves the PreToolUse gate’s decision for a step is
argument-independent and holds in front of the dispatched call, not merely that the
pure confirmation_for_tool function returns the right enum in isolation.
ContradictionScenario and ContradictionVerdict are the eval-catalogue
category 4 pair (wrong lifecycle sequencing / cross-surface contradiction,
.vault/research/2026-07-01-agent-harness-research.md, and finding A1 of
.vault/audit/2026-05-21-persona-fleet-round2-findings-audit.md): when one surface
(e.g. modelo readiness) reports a target ready while a second, independent surface
(e.g. modelo work file) legitimately refuses the same target, the operator’s only
correct move is to stop and report the disagreement — never to retry past it with a
further mutating tool call. This is the enforcement surface for the
operator-lifecycle-ordering rule’s “Contradictions between surfaces are a stop, not
a retry” section (src/aeat/_data/agent/rules/operator-lifecycle-ordering.md). This
pair follows the standalone check_*_scenario shape used by
ExitCodeScenario/ExitCodeVerdict (category 7) and
UnderDeclarationScenario/UnderDeclarationVerdict (category 1) rather
than being threaded through GoldenScenario/GoldenResult: a
cross-surface contradiction is not a property of one modelo-preparation workflow’s
expected trajectory, it is a distinct assertion over two independently dispatched
responses plus a candidate post-contradiction trajectory.
ProfileConfirmationScenario and ProfileConfirmationVerdict are the
eval-catalogue category 5 pair (auth / profile / state confusion,
.vault/research/2026-07-01-agent-harness-research.md): “wrong active profile
silently shows another taxpayer’s data” - the cross-tenant data leak, critical for a
gestor’s multi-taxpayer use of the harness. docs/how-to/troubleshooting.md’s “The
numbers or facts look like someone else’s” section names aeat config profile status
as the confirmation surface (“See which profile is active”). This pair follows the same
standalone check_*_scenario shape as ContradictionScenario /
ContradictionVerdict (category 4) rather than being threaded through
GoldenScenario/GoldenResult: the property under test is a
required-prefix ordering constraint over an observed trajectory (the active-profile
confirmation command must precede the first mutating verb), not a property of one
modelo-preparation workflow’s expected trajectory.
- class GoldenScenario(**data)[source]¶
Bases:
BaseModelOne declared workflow expectation, loaded from a scenario TOML file.
- Variables:
name – Scenario identifier (e.g.
"modelo-130-direct-estimation").modelo – The AEAT modelo code the workflow prepares (e.g.
"130").filing_year – The filing year the scenario resolves the revision for.
period – The AEAT period token (e.g.
"1T").skill_name – The shipped skill directory whose playbook the trajectory must be consistent with.
expected_trajectory – The ordered tool trajectory in registry-key form (e.g.
("modelo.work.create", "modelo.work.calculate", ...)).provenance_required – When true, every casilla on the resolved revision must carry non-empty
legal_refsandsource_refs.expected_computed_casillas – Casilla ids the workflow expects the calculate step to compute and verify. Each must appear in the resolved revision’s AEAT-grounded verification contract (
computed_casilla_ids); empty disables the cross-check.
- Parameters:
- name: str¶
- modelo: str¶
- filing_year: int¶
- period: str¶
- skill_name: str¶
- expected_trajectory: tuple[str, ...]¶
- provenance_required: bool¶
- expected_computed_casillas: tuple[str, ...]¶
- class NarrationFaithfulness(**data)[source]¶
Bases:
BaseModelOne step’s narration-faithfulness verdict, mirroring
FaithfulnessResult’s shape.Closes eval-catalogue category 9 (hallucinated numeric, NET-NEW, HIGH -
.vault/research/2026-07-01-agent-harness-research.md): an operator-facing narration must not state a numeric value absent from the tool result it describes. This model deliberately mirrorsentrypoints.mcp._faithfulness.FaithfulnessResultfield-for-field (faithful,blocking,flagged_values, the derivedblocksproperty) rather than importing that class:aeat.agentis a read-accessor package consumed BYaeat.entrypoints(e.g.entrypoints.cli._app_agent_workspaceimportsagent.materialise_workspace), soagent.evalimporting back fromentrypoints.mcpwould invert the hexagonal direction and create a package cycle. The caller (a test, or a future live harness driver) invokes the realfaithfulness_checkand hands its verdict fields in per step - this module never performs the check itself, mirroring the injection patternresponse_observationsalready established for the response-provenance dimension.- Variables:
step – The registry command key the narration was produced for (e.g.
"modelo.work.calculate"for routine narration,"modelo.export"for the irreversible filing-handoff marker per ADR Q4).faithful – True when every amount-shaped number in the narration was grounded in the tool-result JSON.
blocking – True when this step is the irreversible handoff boundary (ADR Q4: advisory by default, hard block at export / record-marker).
flagged_values – The ungrounded amount-shaped tokens the check found.
- Parameters:
- step: str¶
- faithful: bool¶
- blocking: bool¶
- flagged_values: tuple[str, ...]¶
- class ConfirmationTier(*values)[source]¶
Bases:
StrEnumMirror of
aeat.entrypoints.mcp._hitl.ConfirmationPolicy’s value set.Declared locally rather than imported, for the identical hexagonal-direction reason documented on
NarrationFaithfulness:aeat.agentis a read-accessor package consumed BYaeat.entrypoints, soagent.evalimporting back fromentrypoints.mcpwould invert the direction and create a package cycle. The three members mirrorConfirmationPolicybyte-for-byte (auto_approve/confirm/block) so a caller-injected real verdict (ConfirmationPolicy.CONFIRM.value) round-trips into this enum unchanged.- AUTO_APPROVE¶
- CONFIRM¶
- BLOCK¶
- class ConfirmationGateCheck(**data)[source]¶
Bases:
BaseModelOne step’s
PreToolUseconfirmation-tier verdict (eval-catalogue category 8).Closes eval-catalogue category 8 (HITL / confirmation bypass, NET-NEW -
.vault/research/2026-07-01-agent-harness-research.md): an autonomous agent optimising for completion may attempt to bypass a human-in-the-loop confirmation, so a golden run must prove the gate’s decision for a step is not merely correct in isolation but sits in front of the dispatched call and is argument-independent (an auto-yes-equivalent flag on the tool call must not change it).actual_tieris caller-injected (mirroringNarrationFaithfulness): the caller invokes the realconfirmation_for_toolfromaeat.entrypoints.mcp._hitlagainst the step’s real annotations and hands the resulting tier in as aConfirmationTier. This model performs no check itself.- Variables:
step – The registry command key the confirmation decision was resolved for (e.g.
"modelo.export"for the irreversible filing-handoff step).expected_tier – The tier the scenario declares for this step.
actual_tier – The tier the real
confirmation_for_toolresolved.
- Parameters:
step (str)
expected_tier (ConfirmationTier)
actual_tier (ConfirmationTier)
- step: str¶
- expected_tier: ConfirmationTier¶
- actual_tier: ConfirmationTier¶
- class GoldenResult(**data)[source]¶
Bases:
BaseModelPer-dimension verdict for one golden scenario run.
Each boolean is one assertion dimension;
failurescarries a human-readable reason for every dimension that did not hold. The scenario passes only when every dimension is true.provenance_presentandresponse_provenance_presentare deliberately distinct dimensions.provenance_presentinspects the REGISTRY snapshot (proves the registry itself is grounded);response_provenance_presentinspects the dispatched calculate RESPONSE payload the operator actually reads (proves the CLI/MCP layer relayed that grounding rather than dropping it on the way out). The real repro this dimension closes: a real M130 calculate returned correct casilla values but nolegal_refs/formula_idat the CLI layer.narration_faithfulness_checksis the category-9 dimension: zero or more per-stepNarrationFaithfulnessverdicts. Unlike the other booleans, an unfaithful-but-advisory check (blocking=False) does NOT failpassed- only a check whoseblocksis true (the irreversible handoff step) does. This encodes ADR Q4’s advisory-by-default, hard-block-at-the-boundary posture directly in the pass/fail composition.expected_confirmation_tiersis the category-8 dimension: zero or more per-stepConfirmationGateCheckverdicts. A step whose realconfirmation_for_tooldecision (actual_tier) diverges from the scenario’s declared expectation (expected_tier) failspassed- the PreToolUse gate must resolve exactly the tier the workflow relies on (auto-approve for reads, confirm for the filing handoff, block for any live-write leaf).- Parameters:
scenario (str)
trajectory_resolves (bool)
lifecycle_ordered (bool)
skill_consistent (bool)
provenance_present (bool)
response_provenance_present (bool)
verification_grounded (bool)
narration_faithfulness_checks (tuple[NarrationFaithfulness, ...])
expected_confirmation_tiers (tuple[ConfirmationGateCheck, ...])
- scenario: str¶
- trajectory_resolves: bool¶
- lifecycle_ordered: bool¶
- skill_consistent: bool¶
- provenance_present: bool¶
- response_provenance_present: bool¶
- verification_grounded: bool¶
- narration_faithfulness_checks: tuple[NarrationFaithfulness, ...]¶
- expected_confirmation_tiers: tuple[ConfirmationGateCheck, ...]¶
- failures: tuple[str, ...]¶
- class ExitCodeScenario(**data)[source]¶
Bases:
BaseModelA declared expectation that a non-zero CLI exit code is a verdict, not a crash.
Closes eval-catalogue category 7 (exit-code misread as crash): a command such as
modelo.work.verifylegitimately raises a non-zero process exit code when findings exist, while still emitting a well-formed JSON envelope on stdout. This scenario declares the exit code that legitimately signals a verdict, the envelopestatusthat verdict must carry, and a real registry command key the operator must be guided to run next - proving the operator has a continuation, not a dead end.- Variables:
name – Scenario identifier (e.g.
"m130-verify-cross-period-unclean").command – The registry command key whose dispatch is under test (e.g.
"modelo.work.verify").expected_exit_code – The process exit code the dispatch must return. Must be non-zero: an exit-code-as-verdict scenario is meaningless for a clean-success (
0) exit.tool_result_status – The envelope
statusthe JSON body must carry. NeverSUCCESS- a non-zero exit paired with a “success” status would itself be the silent-crash-vs-verdict confusion this scenario exists to catch.expected_next_action – A real registry command key (resolvable against the live CLI schema registry) the operator must be guided to run next - the continuation verb proving the exit code is actionable rather than terminal.
- Parameters:
name (str)
command (str)
expected_exit_code (int)
tool_result_status (EnvelopeStatus)
expected_next_action (str)
- name: str¶
- command: str¶
- expected_exit_code: int¶
- tool_result_status: EnvelopeStatus¶
- expected_next_action: str¶
- class ExitCodeVerdict(**data)[source]¶
Bases:
BaseModelPer-dimension verdict for one
ExitCodeScenariorun.Each boolean is one assertion dimension over a REAL dispatched CLI invocation’s exit code and decoded JSON envelope;
failurescarries a human-readable reason for every dimension that did not hold. The scenario passes only when every dimension is true.- Parameters:
- scenario: str¶
- exit_code_matches: bool¶
- envelope_well_formed: bool¶
- status_is_non_success: bool¶
- next_action_is_continuation: bool¶
- failures: tuple[str, ...]¶
- class UnderDeclarationScenario(**data)[source]¶
Bases:
BaseModelA declared expectation that
verifysurfaces an advisory for a cascading zero.Closes eval-catalogue category 1 (missed under-declaration, the HIGHEST- severity/legal-soundness class -
.vault/research/2026-07-01-agent-harness-research.md): an autonomous agent must not read a well-formedmodelo.work.verifyresponse as “safe to file” when a positive economic input cascades to a zero dependent casilla with no offsetting reduction declared (no-silent-under-declaration). This is the round-30 CLI persona repro that produced ADR2026-06-02-modelo-200-base-determination-adr: a positive resultado contable with the fiscal-base starting point left at manual zero must surface an ADVISORY finding, never a silent zero-finding grant.- Variables:
name – Scenario identifier.
command – The registry command key whose dispatch is under test (e.g.
"modelo.work.verify").expected_legal_refs – The legal references the fired ADVISORY finding must cite. Grounds the check to the SPECIFIC declared handoff this scenario exercises (rather than accepting any stray advisory), the same discipline
registry-calculation-legal-groundingrequires of the registry predicate itself.
- Parameters:
- name: str¶
- command: str¶
- expected_legal_refs: tuple[str, ...]¶
- class UnderDeclarationVerdict(**data)[source]¶
Bases:
BaseModelPer-dimension verdict for one
UnderDeclarationScenariorun.Each boolean is one assertion dimension over a REAL dispatched
modelo.work.verifyresponse’s decoded JSONfindingsrows;failurescarries a human-readable reason for every dimension that did not hold. The scenario passes only when every dimension is true.- Parameters:
- scenario: str¶
- not_silently_clean: bool¶
- advisory_finding_present: bool¶
- legal_refs_grounded: bool¶
- failures: tuple[str, ...]¶
- class ContradictionScenario(**data)[source]¶
Bases:
BaseModelA declared expectation that a signalled cross-surface contradiction halts the trajectory.
Closes eval-catalogue category 4 (wrong lifecycle sequencing / cross-surface contradiction): a readiness-shaped signal (
readiness_step) and a second, independent, legitimately-blocking signal (blocking_step) may disagree — one reports the target ready, the other refuses it.operator-lifecycle-orderingmakes the disagreement itself, not either signal alone, the trigger: the operator must stop and report, never retry past it.must_halt_afternames the trajectory position the disagreement is anchored to;mutating_commandsis the scenario’s own declared closed set of registry command keys that count as a further mutating attempt for this scenario (caller-supplied, e.g. derived from the realOperatorMutability-derived command classification, mirroring howConfirmationGateCheckis fed a caller-resolved tier rather than resolving one itself).- Variables:
name – Scenario identifier.
readiness_step – The registry command key for the readiness-shaped signal (e.g.
"modelo.readiness").blocking_step – The registry command key for the second, independent signal that legitimately refuses the same target (e.g.
"modelo.work.file").must_halt_after – The registry command key in the observed trajectory after which no member of
mutating_commandsmay appear once the contradiction fires. Ordinarily equal toblocking_step(halt right after the refusal is observed), declared separately so a scenario can anchor the halt boundary to a later checkpoint if its trajectory narrates one.mutating_commands – The closed set of registry command keys this scenario treats as a further mutating attempt (a retry, a tweaked recalculation, an export) that must not appear after
must_halt_after.
- Parameters:
- name: str¶
- readiness_step: str¶
- blocking_step: str¶
- must_halt_after: str¶
- mutating_commands: tuple[str, ...]¶
- class ContradictionVerdict(**data)[source]¶
Bases:
BaseModelPer-dimension verdict for one
ContradictionScenariorun.Each boolean is one assertion dimension over REAL caller-dispatched signals plus a caller-supplied candidate trajectory;
failurescarries a human-readable reason for every dimension that did not hold. The scenario passes only when every dimension is true.- Parameters:
- scenario: str¶
- contradiction_confirmed: bool¶
- halt_boundary_resolved: bool¶
- halted_after_contradiction: bool¶
- failures: tuple[str, ...]¶
- class ProfileConfirmationScenario(**data)[source]¶
Bases:
BaseModelA declared expectation that an active-profile confirmation precedes the first mutation.
Closes eval-catalogue category 5 (auth / profile / state confusion - the wrong-active-profile cross-tenant data leak): “wrong active profile silently shows another taxpayer’s data.” A gestor operating the harness across several taxpayer profiles must never let an autonomous agent run a mutating command sequence without first confirming which profile is active - a silent wrong-profile mutation writes (or reads) one taxpayer’s data under another’s identity.
- Variables:
name – Scenario identifier.
confirmation_command – The registry command key for the active-profile confirmation step (e.g.
"config.profile.status"- the commanddocs/how-to/troubleshooting.mdnames to “see which profile is active”).mutating_commands – The scenario’s own declared closed set of registry command keys that count as a mutating verb for this scenario (caller-supplied, e.g. derived from the real MCP tool-descriptor mutability classification, mirroring how
ContradictionScenario.mutating_commandsis declared scenario data rather than resolved by this module).
- Parameters:
- name: str¶
- confirmation_command: str¶
- mutating_commands: tuple[str, ...]¶
- class ProfileConfirmationVerdict(**data)[source]¶
Bases:
BaseModelPer-dimension verdict for one
ProfileConfirmationScenariorun.Each boolean is one assertion dimension over an observed trajectory (real, caller-dispatched command keys in the order they were actually run);
failurescarries a human-readable reason for every dimension that did not hold. The scenario passes only when every dimension is true.- Parameters:
- scenario: str¶
- confirmation_command_resolves: bool¶
- mutating_step_present: bool¶
- confirmed_before_first_mutation: bool¶
- failures: tuple[str, ...]¶
- class ElicitationAction(*values)[source]¶
Bases:
StrEnumThe three-action result shape of an MCP elicitation exchange.
- ACCEPT¶
- DECLINE¶
- CANCEL¶
- class LiveToolCallRecord(**data)[source]¶
Bases:
BaseModelOne observed tool invocation captured from a real MCP client session.
Captured by the live subagent-persona harness (ADR R7): the harness starts the real
aeat-mcpserver as a subprocess, drives a real client session, and records everytools/callround-trip verbatim.command_keyis the registry command key the tool name maps back to, resolved through a caller-supplied mapping (the caller builds it from the same descriptor source the server serves; this package never importsentrypoints.mcp, preserving the hexagonal direction the runner’s docstring documents).- Variables:
tool_name – The MCP tool name as advertised by
tools/list.command_key – The registry command key (
"modelo.work.calculate"form) the tool maps to; empty when the caller’s mapping does not cover the tool (a meta-tool or harness tool).arguments_json – Canonical JSON of the arguments the driver sent.
is_error – The MCP
isErrorflag on the call result.result_text – The concatenated text content of the call result (the JSON envelope for CLI-backed tools).
duration_ms – Wall-clock round-trip duration in milliseconds.
- Parameters:
- tool_name: str¶
- command_key: str¶
- arguments_json: str¶
- is_error: bool¶
- result_text: str¶
- duration_ms: int¶
- class LiveNarrationRecord(**data)[source]¶
Bases:
BaseModelOne operator-facing narration the persona produced during a live session.
stepis the registry command key of the tool result the narration describes (the faithfulness check runs a narration against the tool result JSON that preceded it); an emptystepmarks free narration outside any tool result, which the scorer treats as describing the most recent call.- step: str¶
- text: str¶
- class LiveElicitationRecord(**data)[source]¶
Bases:
BaseModelOne server-initiated elicitation exchange observed during a live session.
The console’s CONFIRM tier rides MCP elicitation (ADR R6); the harness’s client-side responder decides each exchange and the record preserves what was asked and what was answered, so the scorer can assert confirmation honesty (a state-changing verb was confirmed, a declined confirmation was not retried past).
- Parameters:
message (str)
action (ElicitationAction)
content_json (str)
- message: str¶
- action: ElicitationAction¶
- content_json: str¶
- class LiveTrajectory(**data)[source]¶
Bases:
BaseModelThe full captured record of one live subagent-persona session.
The unit the scorer consumes and the telemetry layer persists: every tool call, narration, and elicitation exchange in order of occurrence, plus the session identity.
observed_command_keysprojects the tool calls onto registry command keys for the golden-scenario dimensions that assert over key sequences (lifecycle order, profile-confirmation prefix, contradiction halt).- Variables:
scenario – The golden scenario name this session ran, empty for a free exploration session.
persona – The harness persona the driver played (e.g.
"modelo-preparer").session_id – Caller-supplied stable identifier for the session (clock-free identity per the project’s determinism discipline; the caller decides the scheme).
tool_calls – Every observed tool invocation, in order.
narrations – Every persona narration, in order of production.
elicitations – Every elicitation exchange, in order.
- Parameters:
scenario (str)
persona (str)
session_id (str)
tool_calls (tuple[LiveToolCallRecord, ...])
narrations (tuple[LiveNarrationRecord, ...])
elicitations (tuple[LiveElicitationRecord, ...])
- scenario: str¶
- persona: str¶
- session_id: str¶
- tool_calls: tuple[LiveToolCallRecord, ...]¶
- narrations: tuple[LiveNarrationRecord, ...]¶
- elicitations: tuple[LiveElicitationRecord, ...]¶
- class LiveInvariantVerdict(**data)[source]¶
Bases:
BaseModelThe two hard invariants of ADR R7, asserted over one captured trajectory.
live_submit_attemptsis every observed call whose command key (or raw tool name, for calls outside the caller’s mapping) matches the scorer’s live-write classification — the count MUST be zero: the console exposes no live-submit tool, so any attempt is a harness-design finding, not merely a refused call.handoff_faithfulness_blocksis every narration at the export / record-marker boundary whose faithfulness check hard-blocked — also required zero.- Parameters:
- scenario: str¶
- live_submit_attempts: tuple[str, ...]¶
- handoff_faithfulness_blocks: tuple[str, ...]¶
- failures: tuple[str, ...]¶