Source code for manna.archives._audit

"""Audit — the falsifiable probe (or manual marker) a Note carries.

An `Audit` is the declarative half of a curated claim: how to re-check it live.
It carries NO network code — the live runner (`evals/audit.py`) reads these and
executes them. Keeping `Audit` pure keeps the model layer dependency-free.

`expect` reuses the caveat vocabulary the probe engine already understands:
  ok | error | empty | nonempty | count | manual

`manual` marks a claim that a single ADQL probe can't check (download recipes,
naming conventions, async-only behaviours) — it must carry a `reason`.
"""

from dataclasses import dataclass

AUDIT_EXPECTS: frozenset[str] = frozenset({"ok", "error", "empty", "nonempty", "count", "manual"})


[docs] def has_table(table: str) -> str: """ADQL asking whether `table` is present in tap_schema.tables.""" return f"SELECT table_name FROM tap_schema.tables WHERE table_name = '{table}'"
[docs] def has_cols(table: str, columns: tuple[str, ...]) -> str: """ADQL selecting which of `columns` are present on `table`.""" inlist = ", ".join(f"'{c}'" for c in columns) return ( f"SELECT column_name FROM tap_schema.columns " f"WHERE table_name = '{table}' AND column_name IN ({inlist})" )
[docs] @dataclass(frozen=True) class Audit: expect: str adql: str = "" columns: tuple[str, ...] = () reason: str = "" def __post_init__(self) -> None: if self.expect not in AUDIT_EXPECTS: raise ValueError( f"unknown audit.expect {self.expect!r}; one of {sorted(AUDIT_EXPECTS)}" ) if self.expect == "manual": if not self.reason: raise ValueError("manual audit requires a non-empty reason") if self.adql: raise ValueError("manual audit must not carry adql") elif self.expect == "count": if not self.columns: raise ValueError("count audit requires a non-empty columns tuple") if not self.adql: raise ValueError("count audit requires adql (use Audit.count)") else: # ok | error | empty | nonempty if not self.adql: raise ValueError(f"{self.expect} audit requires adql")
[docs] @classmethod def probe(cls, *, expect: str, adql: str) -> "Audit": return cls(expect=expect, adql=adql)
[docs] @classmethod def count(cls, *, table: str, columns) -> "Audit": cols = tuple(columns) return cls(expect="count", adql=has_cols(table, cols), columns=cols)
[docs] @classmethod def manual(cls, reason: str) -> "Audit": return cls(expect="manual", reason=reason)