diff --git a/src/reasonsmith/adapters/__init__.py b/src/reasonsmith/adapters/__init__.py index 9c657883..a6fb5a64 100644 --- a/src/reasonsmith/adapters/__init__.py +++ b/src/reasonsmith/adapters/__init__.py @@ -15,10 +15,10 @@ from reasonsmith.adapters.rules import RulesAdapter, RulesSUT __all__ = [ - "JSONLAdapter", - "JsonlSUT", "CallableAdapter", "CallableSUT", + "JSONLAdapter", + "JsonlSUT", "RulesAdapter", "RulesSUT", ] diff --git a/src/reasonsmith/adapters/callable.py b/src/reasonsmith/adapters/callable.py index 7481f257..0ae92fc1 100644 --- a/src/reasonsmith/adapters/callable.py +++ b/src/reasonsmith/adapters/callable.py @@ -19,7 +19,7 @@ from __future__ import annotations from collections.abc import Iterable, Mapping -from typing import Any, Optional +from typing import Any from reasonsmith.neural import DeclaredInputSpace from reasonsmith.sut import BaseSUT @@ -32,8 +32,8 @@ def __init__( self, target: Any, declared_capabilities: set[str] | Iterable[str], - test_inputs: Optional[Iterable[Any]] = None, - decisions: Optional[Iterable[dict[str, Any]]] = None, + test_inputs: Iterable[Any] | None = None, + decisions: Iterable[dict[str, Any]] | None = None, input_space: DeclaredInputSpace | Mapping[str, Any] | None = None, frontier_ai_status: str | None = None, ): diff --git a/src/reasonsmith/adapters/rules.py b/src/reasonsmith/adapters/rules.py index 9488dc88..033e6b07 100644 --- a/src/reasonsmith/adapters/rules.py +++ b/src/reasonsmith/adapters/rules.py @@ -38,7 +38,7 @@ import ast from collections.abc import Iterable -from typing import Any, Optional +from typing import Any from reasonsmith.rulelang import ( UnsupportedConstructError, @@ -92,11 +92,11 @@ class RulesAdapter(BaseSUT): def __init__( self, rules: list[str] | str, - variables: Optional[dict[str, str]] = None, - constraints: Optional[list[str] | str] = None, - declared_capabilities: Optional[set[str] | Iterable[str]] = None, - test_inputs: Optional[Iterable[dict[str, Any]]] = None, - computes: Optional[Iterable[str]] = None, + variables: dict[str, str] | None = None, + constraints: list[str] | str | None = None, + declared_capabilities: set[str] | Iterable[str] | None = None, + test_inputs: Iterable[dict[str, Any]] | None = None, + computes: Iterable[str] | None = None, frontier_ai_status: str | None = None, ): if isinstance(rules, str): @@ -126,7 +126,7 @@ def __init__( if variables is not None: self._variables = dict(variables) else: - self._variables = {v: "real" for v in sorted(discovered_vars)} + self._variables = dict.fromkeys(sorted(discovered_vars), "real") if isinstance(computes, (str, bytes)): raise ValueError( diff --git a/src/reasonsmith/analysis.py b/src/reasonsmith/analysis.py index f7a6a346..56f4381e 100644 --- a/src/reasonsmith/analysis.py +++ b/src/reasonsmith/analysis.py @@ -54,7 +54,7 @@ import ast import copy from dataclasses import dataclass, field -from typing import Any, Optional +from typing import Any import z3 @@ -173,7 +173,7 @@ class PackAnalysis: """What the four questions answered, and every one skipped rather than answered.""" pack_id: str - satisfiable: Optional[bool] + satisfiable: bool | None unsatisfiable_core: tuple[str, ...] = () relations: tuple[Relation, ...] = () vacuities: tuple[VacuityFinding, ...] = () @@ -183,7 +183,7 @@ class PackAnalysis: notes: tuple[str, ...] = field(default_factory=tuple) #: `None` when the optional decision procedure is not installed, which is not the same fact as #: "it was installed and found nothing" and must not render as it. - temporal: Optional[TemporalAnalysis] = None + temporal: TemporalAnalysis | None = None class _PackScope(_Scope): @@ -223,7 +223,7 @@ def contains(self, signal: str, phrase: str) -> Any: return atom -def _state_property(req: Requirement) -> tuple[Optional[ast.Expression], str]: +def _state_property(req: Requirement) -> tuple[ast.Expression | None, str]: """The state property this analysis encodes for a requirement, or a reason it encodes none.""" if req.formalism == "counterfactual": return None, ( @@ -273,7 +273,7 @@ def _encoded(node: ast.AST, scope: _Scope, what: str) -> Any: return _as_bool(_ast_to_z3(node, scope), what) -def _valid(assertions: list[Any], formula: Any, timeout_ms: int) -> Optional[bool]: +def _valid(assertions: list[Any], formula: Any, timeout_ms: int) -> bool | None: """Whether `formula` holds everywhere the assertions admit; `None` if the solver cannot say.""" solver = z3.Solver() solver.set("timeout", timeout_ms) @@ -287,8 +287,8 @@ def _valid(assertions: list[Any], formula: Any, timeout_ms: int) -> Optional[boo return None -def _parents(tree: ast.AST) -> dict[int, tuple[ast.AST, str, Optional[int]]]: - parents: dict[int, tuple[ast.AST, str, Optional[int]]] = {} +def _parents(tree: ast.AST) -> dict[int, tuple[ast.AST, str, int | None]]: + parents: dict[int, tuple[ast.AST, str, int | None]] = {} for node in ast.walk(tree): for name, value in ast.iter_fields(node): if isinstance(value, list): @@ -392,7 +392,7 @@ def vacuous_subformulas( def _has_reported_ancestor( node: ast.AST, - parents: dict[int, tuple[ast.AST, str, Optional[int]]], + parents: dict[int, tuple[ast.AST, str, int | None]], reported: set[int], ) -> bool: current = node @@ -405,7 +405,7 @@ def _has_reported_ancestor( def _satisfiability_and_relations( pack: Pack, timeout_ms: int -) -> tuple[Optional[bool], tuple[str, ...], tuple[Relation, ...], list[str], list[str]]: +) -> tuple[bool | None, tuple[str, ...], tuple[Relation, ...], list[str], list[str]]: """Encode every encodable requirement of a pack once, then ask the two formula questions.""" skipped: list[str] = [] encoded: list[tuple[str, ast.Expression]] = [] @@ -435,7 +435,7 @@ def _satisfiability_and_relations( for req_id, formula in formulas.items(): solver.assert_and_track(formula, req_id) outcome = solver.check() - satisfiable: Optional[bool] = None + satisfiable: bool | None = None core: tuple[str, ...] = () if outcome == z3.sat: satisfiable = True @@ -463,7 +463,7 @@ def _satisfiability_and_relations( return satisfiable, core, tuple(relations), skipped, notes -def _temporal_analysis(pack: Pack) -> tuple[Optional[TemporalAnalysis], list[str], list[str]]: +def _temporal_analysis(pack: Pack) -> tuple[TemporalAnalysis | None, list[str], list[str]]: """Decide the pack's temporal duties as finite-trace formulas, or say why one was not. The whole fragment reaches this, not only the shapes the Z3 reduction misses: an entailment @@ -608,7 +608,7 @@ def _mutation_coverage( pack: Pack, sut: SystemUnderTest, system_domains: tuple[str, ...], - system_scope: Optional[str], + system_scope: str | None, ) -> tuple[tuple[MutationScore, ...], str, list[str]]: """Re-run the pack against every mutant of the system's declared rules. @@ -640,7 +640,7 @@ def _mutation_coverage( domains = system_domains or tuple(getattr(sut, "system_domains", ()) or ()) scope = system_scope or getattr(sut, "system_scope", getattr(sut, "declared_scope", None)) - def build(mutated: list[str]) -> Optional[RulesAdapter]: + def build(mutated: list[str]) -> RulesAdapter | None: try: adapter = RulesAdapter( rules=mutated, @@ -692,9 +692,9 @@ def build(mutated: list[str]) -> Optional[RulesAdapter]: def _verdict_map( pack: Pack, sut: SystemUnderTest, - system_scope: Optional[str], + system_scope: str | None, system_domains: tuple[str, ...], -) -> dict[str, tuple[str, Optional[str]]]: +) -> dict[str, tuple[str, str | None]]: report = check_conformance( sut, pack, system_scope=system_scope, system_domains=system_domains or None ) @@ -709,9 +709,9 @@ def _verdict_map( def analyse_pack( pack: Pack, - sut: Optional[SystemUnderTest] = None, + sut: SystemUnderTest | None = None, *, - system_scope: Optional[str] = None, + system_scope: str | None = None, system_domains: tuple[str, ...] = (), timeout_ms: int = 5000, ) -> PackAnalysis: @@ -734,7 +734,7 @@ def analyse_pack( vacuities: list[VacuityFinding] = [] domain_label = "every assignment to the signals the properties read" - logic_scope: Optional[_Scope] = None + logic_scope: _Scope | None = None logic_assertions: list[Any] = [] declared_computes: Any = None if sut is not None: diff --git a/src/reasonsmith/artifacts/__init__.py b/src/reasonsmith/artifacts/__init__.py index fb96cb75..b9c0e87b 100644 --- a/src/reasonsmith/artifacts/__init__.py +++ b/src/reasonsmith/artifacts/__init__.py @@ -97,23 +97,23 @@ from reasonsmith.spec import normalize_claimed_semantics __all__ = [ + "DECISION_THRESHOLD_KEY", "DECLARATION_REFUTED", "DECLARED_NON_MONOTONE", "EXACT_REASONS_KEY", "EXACT_SEMANTICS_KEY", - "DECISION_THRESHOLD_KEY", "MONOTONE_KEY", "NO_INTERPRETATION", "NO_SEMANTICS_REFERENCE", "RECOUNTED_REASONS", "UNDECLARED_MONOTONICITY", - "InferenceArtifact", "DeclaredInputSpace", + "InferenceArtifact", "OnnxArtifact", "admits_interpretation", + "decision_threshold", "default_label", "deletion_semantics_refusal", - "decision_threshold", "reason_set_is_exact", "reference_semantics", "semantics_reference_refusal", diff --git a/src/reasonsmith/artifacts/ground_program.py b/src/reasonsmith/artifacts/ground_program.py index d0e740d1..b831dff8 100644 --- a/src/reasonsmith/artifacts/ground_program.py +++ b/src/reasonsmith/artifacts/ground_program.py @@ -130,7 +130,7 @@ def probability(self, fact: Atom) -> float: """This fact's probability under the base interpretation — half of the wider surface.""" return float(self.base[fact]) - def at(self, fact: Atom, probability: float) -> "GroundProgramArtifact": + def at(self, fact: Atom, probability: float) -> GroundProgramArtifact: """The same inference at `probability` for `fact`, and the same reason set to score it over. The widened perturbation. It re-scores what the base enumeration found and never @@ -151,6 +151,6 @@ def at(self, fact: Atom, probability: float) -> "GroundProgramArtifact": decision_threshold=self.decision_threshold, ) - def without(self, fact: Atom) -> "GroundProgramArtifact": + def without(self, fact: Atom) -> GroundProgramArtifact: """The same inference with `fact` at probability zero — the deletion probe's one call.""" return self.at(fact, 0.0) diff --git a/src/reasonsmith/artifacts/reason_trace.py b/src/reasonsmith/artifacts/reason_trace.py index 681931ee..b7770e1c 100644 --- a/src/reasonsmith/artifacts/reason_trace.py +++ b/src/reasonsmith/artifacts/reason_trace.py @@ -173,7 +173,7 @@ def engine_value(self) -> float: """The system's own answer, re-run with the suppressed facts withheld.""" return float(self._answer(self._suppressed)) - def without(self, fact: Any) -> "ReasonTraceArtifact": + def without(self, fact: Any) -> ReasonTraceArtifact: return ReasonTraceArtifact( self.query, self._reasons, diff --git a/src/reasonsmith/autoformalize.py b/src/reasonsmith/autoformalize.py index c8523bb5..a19bb765 100644 --- a/src/reasonsmith/autoformalize.py +++ b/src/reasonsmith/autoformalize.py @@ -487,8 +487,8 @@ def _challenge_result(candidate: str, case: ChallengeCase, requirement: Requirem raise ValueError("counterfactual case pairs do not have one expected classification") return results[0] - var_types: dict[str, str] = {name: "bool" for name in bare_boolean_names(node)} - var_types.update({name: "real" for name in measured_magnitude_names(node)}) + var_types: dict[str, str] = dict.fromkeys(bare_boolean_names(node), "bool") + var_types.update(dict.fromkeys(measured_magnitude_names(node), "real")) scope = _ChallengeScope(var_types, case.signals) value = _formula(scope, node) solver = z3.Solver() @@ -674,12 +674,12 @@ def candidate_acceptable( "ChallengeSet", "RoundTripCheck", "SignOff", - "candidate_ready", "candidate_acceptable", - "verify_candidate", + "candidate_ready", "challenge_requirements", "check_challenges", "load_challenge_sets", "round_trip_check", "signoff", + "verify_candidate", ] diff --git a/src/reasonsmith/demo.py b/src/reasonsmith/demo.py index 586d865c..08d737e1 100644 --- a/src/reasonsmith/demo.py +++ b/src/reasonsmith/demo.py @@ -1153,7 +1153,7 @@ def decisions(self) -> list[dict]: def logic(self): """No rule set to reason over: the deployed engine is proof search over a ground program.""" - return None + return def artifact(self, decision: dict) -> dict | None: """The inference this decision came from, as the keyword arguments of `certify`.""" diff --git a/src/reasonsmith/drift.py b/src/reasonsmith/drift.py index 420cee56..7d1d5525 100644 --- a/src/reasonsmith/drift.py +++ b/src/reasonsmith/drift.py @@ -48,11 +48,12 @@ import re import sys import urllib.request +from collections.abc import Callable, Iterable from dataclasses import dataclass -from datetime import datetime, timezone +from datetime import UTC, datetime from html.parser import HTMLParser from pathlib import Path -from typing import Callable, Iterable, Literal, cast +from typing import Literal, cast from reasonsmith.spec import load_pack @@ -792,7 +793,7 @@ def check_statute_drift( ) return DriftReport( results=tuple(results), - checked_at=now if now is not None else datetime.now(timezone.utc), + checked_at=now if now is not None else datetime.now(UTC), ) diff --git a/src/reasonsmith/engines/__init__.py b/src/reasonsmith/engines/__init__.py index 0a560bc6..359e8a5e 100644 --- a/src/reasonsmith/engines/__init__.py +++ b/src/reasonsmith/engines/__init__.py @@ -26,10 +26,10 @@ from reasonsmith.engines.temporal import TemporalProofEngine __all__ = [ - "RecordEngine", + "CertificateEngine", "ObservedEngine", "ProbedEngine", "ProvedEngine", - "CertificateEngine", + "RecordEngine", "TemporalProofEngine", ] diff --git a/src/reasonsmith/engines/counterfactual.py b/src/reasonsmith/engines/counterfactual.py index 34384ec5..f0c86068 100644 --- a/src/reasonsmith/engines/counterfactual.py +++ b/src/reasonsmith/engines/counterfactual.py @@ -70,7 +70,7 @@ import ast from collections.abc import Callable, Iterable, Mapping from dataclasses import replace -from typing import Any, Optional +from typing import Any import z3 @@ -370,7 +370,7 @@ class CounterfactualProofEngine: def evaluate( req: Requirement, sut: SystemUnderTest, - records: Optional[list[dict[str, Any]]] = None, + records: list[dict[str, Any]] | None = None, timeout_ms: int = 5000, *, logic_data: Any = _UNSET_LOGIC, @@ -868,7 +868,7 @@ class PairedReplayEngine: def evaluate( req: Requirement, sut: SystemUnderTest, - records: Optional[list[dict[str, Any]]] = None, + records: list[dict[str, Any]] | None = None, *, trace_provider: Callable[[], Iterable[dict[str, Any]]] | None = None, max_values: int = DEFAULT_MAX_VALUES, diff --git a/src/reasonsmith/engines/probed.py b/src/reasonsmith/engines/probed.py index c3d4f556..9e9fcf72 100644 --- a/src/reasonsmith/engines/probed.py +++ b/src/reasonsmith/engines/probed.py @@ -72,7 +72,7 @@ import copy import random from collections.abc import Callable, Iterable, Mapping -from typing import Any, Optional +from typing import Any from reasonsmith.report import ( PROBE_BUDGET_KEY, @@ -292,7 +292,7 @@ def _shared_mutable_path( original: Any, cloned: Any, path: str = "input", - seen: Optional[set[tuple[int, int]]] = None, + seen: set[tuple[int, int]] | None = None, ) -> str | None: if isinstance(original, (type(None), bool, int, float, complex, str, bytes)): return None @@ -477,7 +477,7 @@ class ProbedEngine: def evaluate( req: Requirement, sut: SystemUnderTest, - records: Optional[list[dict[str, Any]]] = None, + records: list[dict[str, Any]] | None = None, trials: int = DEFAULT_TRIALS, seed: int = DEFAULT_SEED, *, diff --git a/src/reasonsmith/engines/proved.py b/src/reasonsmith/engines/proved.py index 43cec37d..7894b60e 100644 --- a/src/reasonsmith/engines/proved.py +++ b/src/reasonsmith/engines/proved.py @@ -70,7 +70,7 @@ import ast import math -from typing import Any, Optional +from typing import Any import z3 @@ -98,9 +98,9 @@ "REAL_ARITHMETIC_LIMIT", "LogicDeclarationError", "ProvedEngine", + "UnsupportedConstructError", "decision_runner", "encode_logic_domain", - "UnsupportedConstructError", "read_declared_logic", ] @@ -209,7 +209,7 @@ class _Scope: is the empty namespace, so a single-copy encoding is labelled exactly as it always was. """ - def __init__(self, var_types: Optional[dict[str, str]], namespace: str = ""): + def __init__(self, var_types: dict[str, str] | None, namespace: str = ""): self.var_types: dict[str, str] = dict(var_types or {}) self.namespace = namespace self.current: dict[str, Any] = {} @@ -855,7 +855,7 @@ def _values_agree(encoded: Any, computed: Any) -> bool: def _check_encoding_against_interpreter( rules: list[str], scope: _Scope, model: z3.ModelRef -) -> Optional[tuple[str, str]]: +) -> tuple[str, str] | None: """Check the Z3 encoding against the reference interpreter on one witness the solver chose. Returns `None` when they agree, else the kind of divergence and a message naming the witness. @@ -947,7 +947,7 @@ class ProvedEngine: def evaluate( req: Requirement, sut: SystemUnderTest, - records: Optional[list[dict[str, Any]]] = None, + records: list[dict[str, Any]] | None = None, timeout_ms: int = 5000, *, logic_data: Any = _UNSET_LOGIC, diff --git a/src/reasonsmith/engines/temporal.py b/src/reasonsmith/engines/temporal.py index b907d35b..b9d1ca4d 100644 --- a/src/reasonsmith/engines/temporal.py +++ b/src/reasonsmith/engines/temporal.py @@ -51,7 +51,7 @@ import ast from dataclasses import replace -from typing import Any, Optional +from typing import Any from reasonsmith.report import RequirementResult from reasonsmith.rulelang import ( @@ -129,7 +129,7 @@ class TemporalProofEngine: def evaluate( req: Requirement, sut: SystemUnderTest, - records: Optional[list[dict[str, Any]]] = None, + records: list[dict[str, Any]] | None = None, timeout_ms: int = 5000, *, logic_data: Any = _UNSET_LOGIC, diff --git a/src/reasonsmith/event_time.py b/src/reasonsmith/event_time.py index 2aa671f7..f9e58174 100644 --- a/src/reasonsmith/event_time.py +++ b/src/reasonsmith/event_time.py @@ -12,7 +12,7 @@ import calendar import re from dataclasses import dataclass -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from typing import Final @@ -111,7 +111,7 @@ def parse_timestamp(value: str) -> datetime: if parsed.tzinfo is None or parsed.utcoffset() is None: raise EventTimeError(f"timestamp {value!r} is naive; an explicit offset is required") try: - return parsed.astimezone(timezone.utc) + return parsed.astimezone(UTC) except (OverflowError, ValueError) as exc: raise EventTimeError(f"timestamp {value!r} cannot be normalised to UTC: {exc}") from exc @@ -120,7 +120,7 @@ def _utc_instant(value: datetime) -> datetime: """Return an aware UTC instant, refusing the host machine's local timezone fallback.""" if not isinstance(value, datetime) or value.tzinfo is None or value.utcoffset() is None: raise EventTimeError("event arithmetic requires an aware timestamp with an explicit offset") - return value.astimezone(timezone.utc) + return value.astimezone(UTC) def format_timestamp(value: datetime) -> str: @@ -237,10 +237,10 @@ def measure_pair( __all__ = [ "CALENDAR_POLICY", + "TIMEZONE_POLICY", "Duration", "EventPair", "EventTimeError", - "TIMEZONE_POLICY", "add_calendar_months", "deadline_for", "format_timestamp", diff --git a/src/reasonsmith/examples/language_model_notices.py b/src/reasonsmith/examples/language_model_notices.py index 49531a11..e05b6971 100644 --- a/src/reasonsmith/examples/language_model_notices.py +++ b/src/reasonsmith/examples/language_model_notices.py @@ -56,7 +56,8 @@ def complete(prompt: str) -> str: from __future__ import annotations import re -from typing import Any, Callable +from collections.abc import Callable +from typing import Any from reasonsmith.adapters.callable import CallableAdapter from reasonsmith.neural import DeclaredInputSpace, render_template diff --git a/src/reasonsmith/examples/recounted_reason_trace.py b/src/reasonsmith/examples/recounted_reason_trace.py index 2a46d806..62da5fd3 100644 --- a/src/reasonsmith/examples/recounted_reason_trace.py +++ b/src/reasonsmith/examples/recounted_reason_trace.py @@ -68,7 +68,7 @@ def decisions(self) -> list[dict[str, Any]]: def logic(self) -> None: """This system has no exposed encoding for reasonsmith to enumerate.""" - return None + return def artifact(self, decision: dict[str, Any]) -> ReasonTraceArtifact: """Return only the system's own account of the reasons for this decision.""" diff --git a/src/reasonsmith/explanations.py b/src/reasonsmith/explanations.py index 189b09e2..ce9ba54f 100644 --- a/src/reasonsmith/explanations.py +++ b/src/reasonsmith/explanations.py @@ -56,8 +56,9 @@ from __future__ import annotations +from collections.abc import Callable from dataclasses import dataclass -from typing import Any, Callable +from typing import Any __all__ = ["DEFAULT_PROBE_BUDGET", "DeletionSearch", "contrastive_sets"] diff --git a/src/reasonsmith/ltlf.py b/src/reasonsmith/ltlf.py index 407b732a..ee96cac8 100644 --- a/src/reasonsmith/ltlf.py +++ b/src/reasonsmith/ltlf.py @@ -108,9 +108,9 @@ import re import shutil import subprocess +from collections.abc import Sequence from dataclasses import dataclass, field -from functools import lru_cache -from typing import Sequence +from functools import cache from reasonsmith.rulelang import ( BOUNDED_RESPONSE_CALL, @@ -126,10 +126,10 @@ __all__ = [ "ATOM_BUDGET", - "Abstraction", "LTLF_ABSTRACTION_LIMIT", "LTLF_EXTRA", "UNAVAILABLE_NOTE", + "Abstraction", "accepts", "atom_count", "available", @@ -180,7 +180,7 @@ _PAST_OPERATORS = TEMPORAL_OPERATORS - set(_UNARY_RENDERING) - set(_BINARY_RENDERING) -@lru_cache(maxsize=None) +@cache def _verify_black_binary(path: str) -> bool: try: res = subprocess.run( diff --git a/src/reasonsmith/manyvalued.py b/src/reasonsmith/manyvalued.py index 88f39f84..b7dd3117 100644 --- a/src/reasonsmith/manyvalued.py +++ b/src/reasonsmith/manyvalued.py @@ -51,9 +51,9 @@ from __future__ import annotations import ast -from collections.abc import Mapping +from collections.abc import Callable, Mapping from dataclasses import dataclass -from typing import Any, Callable +from typing import Any from reasonsmith.rulelang import ( DEGREE_CALL, diff --git a/src/reasonsmith/neural.py b/src/reasonsmith/neural.py index 3cc86fd9..683268d4 100644 --- a/src/reasonsmith/neural.py +++ b/src/reasonsmith/neural.py @@ -37,7 +37,7 @@ _PLACEHOLDER = re.compile(r"(? bool: +def _constraint_holds(space: DeclaredInputSpace, values: Mapping[str, Any]) -> bool: """Evaluate constraints that are decidable from one complete assignment.""" def compare(left: Any, op: str, right: Any) -> bool: return { @@ -67,7 +67,7 @@ def compare(left: Any, op: str, right: Any) -> bool: def _validate_complete_assignment( - space: "DeclaredInputSpace", values: Mapping[str, Any] + space: DeclaredInputSpace, values: Mapping[str, Any] ) -> None: """Validate slot domains and constraints for a complete declared assignment.""" for slot in space.slots: @@ -94,7 +94,7 @@ def _validate_complete_assignment( def render_template( - space: "DeclaredInputSpace", values: Mapping[str, Any], *, validate: bool = True + space: DeclaredInputSpace, values: Mapping[str, Any], *, validate: bool = True ) -> str: """Render a declared template deterministically for one complete input assignment. @@ -142,7 +142,7 @@ def replace(match: re.Match[str]) -> str: return _PLACEHOLDER.sub(replace, template.text) -def render_declared_template(space: "DeclaredInputSpace", values: Mapping[str, Any]) -> str: +def render_declared_template(space: DeclaredInputSpace, values: Mapping[str, Any]) -> str: """Compatibility spelling for the shared declared-template renderer.""" return render_template(space, values) @@ -221,7 +221,7 @@ def type(self) -> str: return self.kind @classmethod - def from_value(cls, value: Any) -> "InputSlot": + def from_value(cls, value: Any) -> InputSlot: if isinstance(value, cls): return value if not isinstance(value, Mapping): @@ -319,7 +319,7 @@ class TemplateSpec: escaping: str @classmethod - def from_value(cls, value: Any, signals: set[str]) -> "TemplateSpec": + def from_value(cls, value: Any, signals: set[str]) -> TemplateSpec: if isinstance(value, cls): return value if isinstance(value, str): @@ -477,7 +477,7 @@ def __init__( object.__setattr__(self, "outcomes", MappingProxyType(normalized_outcomes)) @classmethod - def from_value(cls, value: Any) -> "DeclaredInputSpace": + def from_value(cls, value: Any) -> DeclaredInputSpace: if isinstance(value, cls): return value if not isinstance(value, Mapping): @@ -508,7 +508,7 @@ class OutputDecoder: classes: tuple[Any, ...] @classmethod - def from_value(cls, value: Any, signal: str) -> "OutputDecoder": + def from_value(cls, value: Any, signal: str) -> OutputDecoder: if not isinstance(value, Mapping): _fail(f"decoder for output signal {signal!r} must be a total mapping") kind = value.get("kind", "threshold") @@ -840,7 +840,7 @@ def bind(raw: Any, graph: Mapping[str, Any], label: str, output: bool) -> Tensor ) @staticmethod - def from_value(value: Any) -> "OnnxArtifact": + def from_value(value: Any) -> OnnxArtifact: if isinstance(value, OnnxArtifact): return value if not isinstance(value, Mapping): @@ -851,12 +851,12 @@ def from_value(value: Any) -> "OnnxArtifact": __all__ = [ "SUPPORTED_SCHEMA_VERSION", "SUPPORTED_VNNLIB_VERSIONS", - "InputSlot", - "TemplateSpec", "DeclaredInputSpace", - "render_template", - "render_declared_template", + "InputSlot", + "OnnxArtifact", "OutputDecoder", + "TemplateSpec", "TensorBinding", - "OnnxArtifact", + "render_declared_template", + "render_template", ] diff --git a/src/reasonsmith/neural_queries.py b/src/reasonsmith/neural_queries.py index 16ccdbd3..1e548e0e 100644 --- a/src/reasonsmith/neural_queries.py +++ b/src/reasonsmith/neural_queries.py @@ -81,7 +81,7 @@ def __post_init__(self) -> None: class NeuralVerifier(Protocol): def verify( self, - query: "CompiledNeuralQuery", + query: CompiledNeuralQuery, *, timeout: float | None = None, mode: str = "bounded-search", @@ -97,11 +97,11 @@ def __init__(self, runs: VerifierRun | Mapping[str, Any] | Sequence[Any]): self._runs = [run if isinstance(run, VerifierRun) else VerifierRun(**run) for run in runs] if not self._runs: raise ValueError("fake verifier needs at least one controlled run") - self.calls: list[tuple["CompiledNeuralQuery", float | None, str]] = [] + self.calls: list[tuple[CompiledNeuralQuery, float | None, str]] = [] def verify( self, - query: "CompiledNeuralQuery", + query: CompiledNeuralQuery, *, timeout: float | None = None, mode: str = "bounded-search", @@ -155,7 +155,7 @@ def model(self) -> bytes: def query(self) -> str: return self.vnnlib - def validate(self) -> "CompiledNeuralQuery": + def validate(self) -> CompiledNeuralQuery: validate_compiled_query(self) return self @@ -596,7 +596,7 @@ def compile_local_robustness_query( raise ValueError(f"local-robustness centre is outside bounds for {c.signal!r}") centre_values[c.signal] = value radii = ( - {signal: radius for signal in centre_values} + dict.fromkeys(centre_values, radius) if isinstance(radius, (int, float)) else dict(radius) ) @@ -903,26 +903,26 @@ def verify_query( __all__ = [ "VERIFIER_STATUSES", - "VerifierStatus", - "QueryShape", - "VerifierRun", - "NeuralVerifier", + "CompiledNeuralQuery", + "CompiledQuery", "FakeNeuralVerifier", - "FakeVerifier", "FakeOracle", - "WitnessCheck", + "FakeVerifier", + "NeuralVerifier", "OracleCheck", - "CompiledNeuralQuery", - "CompiledQuery", "OracleResult", + "QueryShape", + "VerifierRun", + "VerifierStatus", + "WitnessCheck", + "check_neural_witness", + "check_witness", "compile_counterfactual_query", "compile_ecoa_counterfactual_query", - "compile_monotonicity_query", - "compile_local_robustness_query", "compile_linf_robustness_query", + "compile_local_robustness_query", + "compile_monotonicity_query", + "run_neural_query", "validate_compiled_query", - "check_witness", - "check_neural_witness", "verify_query", - "run_neural_query", ] diff --git a/src/reasonsmith/neural_verifiers/__init__.py b/src/reasonsmith/neural_verifiers/__init__.py index 93f42313..1a688bda 100644 --- a/src/reasonsmith/neural_verifiers/__init__.py +++ b/src/reasonsmith/neural_verifiers/__init__.py @@ -44,19 +44,19 @@ __all__ += [ "ABCROWN_COMMIT", "ABCROWN_VERSION", + "ALPHA_BETA_CROWN_COMMIT", + "ALPHA_BETA_CROWN_VERSION", "ABCROWNAdapter", + "ABCROWNResourceLimits", "ABCROWNVerifier", "ABCrownAdapter", "ABCrownVerifier", + "AlphaBetaCROWNVerifier", "AlphaBetaCrownAdapter", "AlphaBetaCrownVerifier", - "AlphaBetaCROWNVerifier", - "ABCROWNResourceLimits", - "ALPHA_BETA_CROWN_VERSION", - "ALPHA_BETA_CROWN_COMMIT", - "map_abcrown_status", - "parse_abcrown_status", "DifferentialResult", "compare_checks", "compare_runs", + "map_abcrown_status", + "parse_abcrown_status", ] diff --git a/src/reasonsmith/neural_verifiers/abcrown.py b/src/reasonsmith/neural_verifiers/abcrown.py index f7690a06..3353e12b 100644 --- a/src/reasonsmith/neural_verifiers/abcrown.py +++ b/src/reasonsmith/neural_verifiers/abcrown.py @@ -657,24 +657,24 @@ def budget_expired() -> bool: ALPHA_BETA_CROWN_COMMIT = ABCROWN_COMMIT __all__ = [ - "ABCROWN_VERSION", "ABCROWN_COMMIT", - "VNNLIB_VERSION", - "BOUNDED_SEARCH_MODE", - "COMPLETE_MODE", "ABCROWN_NATIVE_STATUSES", "ABCROWN_STATUS_MAP", - "ABCROWNResourceLimits", - "ResourceLimits", - "ALPHA_BETA_CROWN_VERSION", + "ABCROWN_VERSION", "ALPHA_BETA_CROWN_COMMIT", - "parse_abcrown_status", - "map_abcrown_status", + "ALPHA_BETA_CROWN_VERSION", + "BOUNDED_SEARCH_MODE", + "COMPLETE_MODE", + "VNNLIB_VERSION", + "ABCROWNAdapter", + "ABCROWNResourceLimits", "ABCROWNVerifier", + "ABCrownAdapter", "ABCrownVerifier", - "AlphaBetaCrownVerifier", "AlphaBetaCROWNVerifier", "AlphaBetaCrownAdapter", - "ABCrownAdapter", - "ABCROWNAdapter", + "AlphaBetaCrownVerifier", + "ResourceLimits", + "map_abcrown_status", + "parse_abcrown_status", ] diff --git a/src/reasonsmith/neural_verifiers/differential.py b/src/reasonsmith/neural_verifiers/differential.py index 2cbefd35..3f2d3e20 100644 --- a/src/reasonsmith/neural_verifiers/differential.py +++ b/src/reasonsmith/neural_verifiers/differential.py @@ -79,4 +79,4 @@ def compare_checks(left: OracleCheck, right: OracleCheck) -> DifferentialResult: return result -__all__ = ["DifferentialResult", "compare_runs", "compare_checks"] +__all__ = ["DifferentialResult", "compare_checks", "compare_runs"] diff --git a/src/reasonsmith/neural_verifiers/marabou.py b/src/reasonsmith/neural_verifiers/marabou.py index 16a53b00..927b2552 100644 --- a/src/reasonsmith/neural_verifiers/marabou.py +++ b/src/reasonsmith/neural_verifiers/marabou.py @@ -567,12 +567,12 @@ def budget_expired() -> bool: __all__ = [ - "MARABOU_VERSION", - "VNNLIB_VERSION", "BOUNDED_SEARCH_MODE", "COMPLETE_MODE", "DEFAULT_SUPPORTED_OPERATORS", - "ResourceLimits", - "MarabouVerifier", + "MARABOU_VERSION", + "VNNLIB_VERSION", "MarabouAdapter", + "MarabouVerifier", + "ResourceLimits", ] diff --git a/src/reasonsmith/plugins.py b/src/reasonsmith/plugins.py index 666cb6f0..aa89294b 100644 --- a/src/reasonsmith/plugins.py +++ b/src/reasonsmith/plugins.py @@ -50,10 +50,11 @@ import copy import warnings +from collections.abc import Callable from dataclasses import replace from importlib.metadata import entry_points from pathlib import Path -from typing import Any, Callable +from typing import Any from reasonsmith.verdict import Strength, Verdict diff --git a/src/reasonsmith/proposer.py b/src/reasonsmith/proposer.py index 7b7e7b31..4ec85951 100644 --- a/src/reasonsmith/proposer.py +++ b/src/reasonsmith/proposer.py @@ -458,17 +458,17 @@ def main(argv: list[str] | None = None) -> int: __all__ = [ + "DEFAULT_ATTEMPTS", + "PROPOSER_EXTRA", + "UNAVAILABLE_NOTE", "AgreementMeasurement", "AgreementRow", "ClaudeModel", "CommandModel", - "DEFAULT_ATTEMPTS", "ModelUnavailable", "OllamaModel", - "PROPOSER_EXTRA", "Proposal", "ProposalAttempt", - "UNAVAILABLE_NOTE", "measure_agreement", "model_from_environment", "propose", diff --git a/src/reasonsmith/published_counts.py b/src/reasonsmith/published_counts.py index fdd0c077..869b2da7 100644 --- a/src/reasonsmith/published_counts.py +++ b/src/reasonsmith/published_counts.py @@ -7,7 +7,7 @@ from __future__ import annotations import json -from datetime import datetime, timezone +from datetime import UTC, datetime from pathlib import Path from reasonsmith.drift import STATUTORY_PACKS, quote_corpus_sha256 @@ -70,7 +70,7 @@ def published_counts() -> dict[str, object]: # A quote is a requirement in a statutory pack; Table 7 rows quote the paper instead. return { "schema_version": 1, - "generated_at": datetime.now(timezone.utc).isoformat(), + "generated_at": datetime.now(UTC).isoformat(), "source": "reasonsmith tree (verdict.py, spec.py, packs/, docs/legal-sources.md)", "rungs": [strength.value for strength in Strength], "pack_count": len(packs), diff --git a/src/reasonsmith/report.py b/src/reasonsmith/report.py index 12036af5..a6b9e67d 100644 --- a/src/reasonsmith/report.py +++ b/src/reasonsmith/report.py @@ -259,7 +259,7 @@ def jsonable_finite(value: float) -> bool: _is_present = is_present -def certificate_findings(result: "RequirementResult") -> list[dict[str, Any]]: +def certificate_findings(result: RequirementResult) -> list[dict[str, Any]]: """Expose failed certificate measurements as findings without changing the duty verdict.""" return [ { @@ -328,7 +328,7 @@ def _probe_scope_line(budget: Mapping[str, Any]) -> str: ) -def positive_scope_boundary(result: "RequirementResult") -> str | None: +def positive_scope_boundary(result: RequirementResult) -> str | None: """Return the run-specific boundary a satisfied result must carry on every surface.""" if result.verdict is not Verdict.SATISFIED or result.strength is None: return None diff --git a/src/reasonsmith/rulelang.py b/src/reasonsmith/rulelang.py index b4ea8720..0a827c38 100644 --- a/src/reasonsmith/rulelang.py +++ b/src/reasonsmith/rulelang.py @@ -42,7 +42,8 @@ import io import math import tokenize -from typing import Any, Iterable, Mapping, cast +from collections.abc import Iterable, Mapping +from typing import Any, cast from reasonsmith.event_time import EventTimeError, parse_duration @@ -243,7 +244,6 @@ def is_present(value: Any) -> bool: class UnsupportedConstructError(Exception): """Raised when rule or specification text uses a construct this language does not model.""" - pass class NotAStatementError(UnsupportedConstructError): @@ -259,7 +259,6 @@ class NotAStatementError(UnsupportedConstructError): message string is not an interface, and nothing may tell the two apart by reading one. """ - pass def fold_ascii_case(text: str) -> str: @@ -1798,7 +1797,7 @@ def eval_temporal_trace(node: ast.AST, records: list[dict[str, Any]]) -> list[An kleene_or( [ kleene_and_binary(right[j], kleene_and(left[j + 1 : i + 1])) - for j in range(0, i + 1) + for j in range(i + 1) ] ) for i in range(n) diff --git a/src/reasonsmith/statute_replay.py b/src/reasonsmith/statute_replay.py index acf9e9b5..9bd3fbcf 100644 --- a/src/reasonsmith/statute_replay.py +++ b/src/reasonsmith/statute_replay.py @@ -17,10 +17,11 @@ import argparse import hashlib import json +from collections.abc import Iterable from dataclasses import dataclass from datetime import datetime from pathlib import Path -from typing import Any, Iterable, Literal +from typing import Any, Literal from reasonsmith.drift import ( PROVISIONS, @@ -108,7 +109,7 @@ def from_file( retrieved_at: str, synthetic: bool = False, kind: str | None = None, - ) -> "SourceSnapshot": + ) -> SourceSnapshot: raw = Path(path).read_bytes() source_kind = kind or SOURCES_BY_KEY[key].kind data: SourcePayload = raw if source_kind == "pdf" else raw.decode("utf-8") @@ -150,7 +151,7 @@ def __post_init__(self) -> None: raise ValueError("a statutory revision requires the pack file SHA-256") @classmethod - def from_manifest(cls, manifest: str | Path) -> "StatuteRevision": + def from_manifest(cls, manifest: str | Path) -> StatuteRevision: manifest_path = Path(manifest) data = json.loads(manifest_path.read_text(encoding="utf-8")) if data.get("schema_version") != SCHEMA_VERSION: diff --git a/src/reasonsmith/sut.py b/src/reasonsmith/sut.py index 45b016d5..526ca503 100644 --- a/src/reasonsmith/sut.py +++ b/src/reasonsmith/sut.py @@ -111,7 +111,7 @@ from dataclasses import dataclass from datetime import datetime from functools import lru_cache -from typing import Any, Optional, Protocol, runtime_checkable +from typing import Any, Protocol, runtime_checkable from reasonsmith.event_time import EventTimeError, parse_timestamp from reasonsmith.neural import DeclaredInputSpace @@ -707,9 +707,9 @@ class FullCapabilitySUT(BaseSUT): def __init__( self, - extra_capabilities: Optional[set[str]] = None, + extra_capabilities: set[str] | None = None, system_scope: str = "high-risk", - system_domains: Optional[Iterable[str]] = None, + system_domains: Iterable[str] | None = None, ): declared = _table7_signals() | {"decision", "timestamp"} | (extra_capabilities or set()) super().__init__(declared) @@ -737,7 +737,7 @@ class NoReasonsSUT(BaseSUT): """ def __init__( - self, system_scope: str = "high-risk", system_domains: Optional[Iterable[str]] = None + self, system_scope: str = "high-risk", system_domains: Iterable[str] | None = None ): super().__init__((_table7_signals() | {"decision", "timestamp"}) - REASON_SIGNALS) self.was_executed = False diff --git a/src/reasonsmith/verdict.py b/src/reasonsmith/verdict.py index 0f78bb0d..e0dcb425 100644 --- a/src/reasonsmith/verdict.py +++ b/src/reasonsmith/verdict.py @@ -64,9 +64,9 @@ from __future__ import annotations +from collections.abc import Iterable from enum import Enum from functools import total_ordering -from typing import Iterable @total_ordering diff --git a/tests/test_deviation_duty.py b/tests/test_deviation_duty.py index d451dd6e..f1273b27 100644 --- a/tests/test_deviation_duty.py +++ b/tests/test_deviation_duty.py @@ -175,7 +175,7 @@ class _BooleanAdapter(SilentDropAdapter): """A boolean answer must not be coerced to the real number one.""" def infer(self, program, base, queries): - return {query: True for query in queries} + return dict.fromkeys(queries, True) class _FixedArtifact: @@ -595,7 +595,7 @@ def test_a_system_exposing_no_artefact_is_unattainable_never_satisfied(): """Silence is not compliance, and no weaker rung stands in for the measurement.""" req = requirement() sut = BaseSUT(set(req.requires)) - result = evaluate_requirement(req, sut, [{name: 0.0 for name in req.requires}]) + result = evaluate_requirement(req, sut, [dict.fromkeys(req.requires, 0.0)]) assert result.verdict == Verdict.INCONCLUSIVE assert result.strength == Strength.UNATTAINABLE diff --git a/tests/test_drift.py b/tests/test_drift.py index 7759f80a..fb619665 100644 --- a/tests/test_drift.py +++ b/tests/test_drift.py @@ -9,7 +9,7 @@ import hashlib import json -from datetime import datetime, timezone +from datetime import UTC, datetime from io import BytesIO from pathlib import Path @@ -434,7 +434,7 @@ def test_drift_issue_creation_is_serialized_and_uses_the_exact_title(self): class TestReport: def test_render_and_json_round_trip(self, tmp_path): - now = datetime(2026, 8, 1, 3, 0, tzinfo=timezone.utc) + now = datetime(2026, 8, 1, 3, 0, tzinfo=UTC) results = ( DriftResult("gdpr", "r1", "Article 22(1)", "http://x/", "match", "q", ""), DriftResult("gdpr", "r2", "Article 22(3)", "http://x/", "differ", "old", "new"), diff --git a/tests/test_event_time.py b/tests/test_event_time.py index 1144871f..56779766 100644 --- a/tests/test_event_time.py +++ b/tests/test_event_time.py @@ -2,7 +2,7 @@ from __future__ import annotations -from datetime import datetime, timezone +from datetime import UTC, datetime import pytest @@ -91,7 +91,7 @@ def test_offsets_are_normalised_before_subtraction_including_a_dst_transition() result = _evaluate(_trace("2026-03-08T01:30:00-05:00", "2026-03-09T02:30:00-04:00")) assert result.verdict is Verdict.SATISFIED assert result.details["event_pairs"][0]["delta_seconds"] == 86400.0 - assert parse_timestamp("2026-03-08T01:30:00-05:00").tzinfo is timezone.utc + assert parse_timestamp("2026-03-08T01:30:00-05:00").tzinfo is UTC def test_leap_day_is_an_actual_elapsed_day() -> None: @@ -468,11 +468,11 @@ def fromisoformat(value: str) -> datetime: event_time.parse_timestamp("2026-01-01T00:00:00Z") class BrokenAware: - tzinfo = timezone.utc + tzinfo = UTC @staticmethod def utcoffset(): - return timezone.utc.utcoffset(None) + return UTC.utcoffset(None) @staticmethod def astimezone(zone): diff --git a/tests/test_json_record.py b/tests/test_json_record.py index 868f4b88..b081c226 100644 --- a/tests/test_json_record.py +++ b/tests/test_json_record.py @@ -134,7 +134,7 @@ class _ConstantEngine: claimed_semantics = "distribution semantics" def infer(self, program, base, queries): - return {q: 0.5 for q in queries} + return dict.fromkeys(queries, 0.5) q, a, b, c, d = Atom("q"), Atom("a"), Atom("b"), Atom("c"), Atom("d") program = GroundProgram((Rule(q, (a, b)), Rule(q, (a, c)), Rule(q, (b, d)))) diff --git a/tests/test_semantic_laws.py b/tests/test_semantic_laws.py index 5fa6e98e..83c3523f 100644 --- a/tests/test_semantic_laws.py +++ b/tests/test_semantic_laws.py @@ -290,7 +290,7 @@ def __init__(self, fact): self.fact = fact def infer(self, program, base, queries): - return {query: 1.0 - base[self.fact] for query in queries} + return dict.fromkeys(queries, 1.0 - base[self.fact]) artefact = GroundProgramArtifact( instance.program, diff --git a/tests/test_statistical_basis.py b/tests/test_statistical_basis.py index 99171dc3..21e5c24c 100644 --- a/tests/test_statistical_basis.py +++ b/tests/test_statistical_basis.py @@ -20,13 +20,13 @@ def plan(): - value = {name: "declared" for name in SAMPLING_REQUIRED_FIELDS} + value = dict.fromkeys(SAMPLING_REQUIRED_FIELDS, "declared") value.update(design="iid_binomial", weights="none", clustering="none") return value def authority(): - return {name: "declared" for name in AUTHORITY_REQUIRED_FIELDS} + return dict.fromkeys(AUTHORITY_REQUIRED_FIELDS, "declared") def rows(): diff --git a/tests/test_sufficient_reasons.py b/tests/test_sufficient_reasons.py index 993775b0..61e429ee 100644 --- a/tests/test_sufficient_reasons.py +++ b/tests/test_sufficient_reasons.py @@ -85,7 +85,7 @@ class _IgnoresEveryProof: claimed_semantics = "distribution semantics" def infer(self, program, base, queries): - return {query: 0.0 for query in queries} + return dict.fromkeys(queries, 0.0) def _artifact(**overrides) -> dict: @@ -260,7 +260,7 @@ def test_a_partial_enumeration_degrades_to_undetermined_and_never_to_deleted(): def test_no_budget_makes_this_instrument_name_more_missing_reasons_than_a_complete_search(): """Stated as a sweep, because it is the one property a budget must not be able to invert.""" complete = set(certify(**_artifact()).missing_reasons()) - for budget in range(0, 12): + for budget in range(12): named = set(certify(**_artifact(budget=budget)).missing_reasons()) assert named <= complete, ( f"a {budget}-probe search named {named - complete} that a complete search does not" @@ -396,7 +396,7 @@ class _ConstantEngine: claimed_semantics = "distribution semantics" def infer(self, program, base, queries): - return {q: 0.5 for q in queries} + return dict.fromkeys(queries, 0.5) q, a, b, c, d = Atom("q"), Atom("a"), Atom("b"), Atom("c"), Atom("d") program = GroundProgram((Rule(q, (a, b)), Rule(q, (a, c)), Rule(q, (b, d)))) diff --git a/tests/test_v02_stage3.py b/tests/test_v02_stage3.py index c8f12352..e3ff501f 100644 --- a/tests/test_v02_stage3.py +++ b/tests/test_v02_stage3.py @@ -792,7 +792,7 @@ def decisions(self): # Every atom of the property is a bare Boolean, so every record must establish that # kind. These two are lawful: automated and significant, but on an Article 22(2)(b) # basis, which is the one branch that does not require the intervention route. - lawful = {signal: False for signal in req.requires} + lawful = dict.fromkeys(req.requires, False) lawful["artifact_logs_solely_automated"] = True lawful["artifact_logs_significant_effect"] = True lawful["provenance_basis_union_or_member_state_law"] = True @@ -1704,7 +1704,7 @@ def test_article_22_still_quantifies_over_flags_the_rules_never_assign(): ) sut = RulesAdapter( rules=["approved = score >= 650"], - variables={"score": "int", "approved": "bool", **{flag: "bool" for flag in flags}}, + variables={"score": "int", "approved": "bool", **dict.fromkeys(flags, "bool")}, constraints=["score >= 0", "score <= 1000"], test_inputs=[{"score": 700}, {"score": 300}], declared_capabilities=set(flags),