Email-from-DB example¶
This example combines two trusted effects: db_query returns a bounded list
of rows from a typed schema, and send_email sends to a given address. Agent
code iterates over the query result and sends to each row's email field —
the prover models the loop symbolically and checks that every recipient
came from the right column.
It's the smallest example that exercises symbolic iteration over a trusted-call return value plus column-binding constraints.
Trusted module¶
Both functions declare their effect boundaries with @clauz3.trusted:
db_query reads rows and send_email sends a message. db_query's
@clauz3.ensures(...) caps the result length, which lets the prover bound
the loop.
examples/email-from-db/tools/db/trusted/effects.py
from tools.db.trusted.schemas import UserRow
import clauz3
@clauz3.trusted
def send_email(addr: str, msg: str) -> None:
"""MOCK trusted email sender."""
pass
@clauz3.trusted
@clauz3.ensures(lambda result: len(result) <= 100)
def db_query(table: str, where: dict[str, object] | None = None) -> list[UserRow]:
"""Trusted DB query returning at most 100 UserRows.
``table`` is the table name. ``where`` is an optional filter dict;
only rows where all key=value constraints hold are returned.
"""
return []
The schema is a clauz3.Row subclass — its fields become the row schema
the prover binds against when iterating the query result.
examples/email-from-db/tools/db/trusted/schemas.py
The contract module uses effect(send_email) and effect(db_query), passing
the imported trusted functions. Its addresses_from(schema, field) helper
binds every recipient to a column of a schema, and count_at_most(n) is a
simple aggregate on the email relation.
examples/email-from-db/tools/db/trusted/contracts.py
"""Contract vocabulary for the email-from-db example.
Includes contracts for both the database query effects and the email effects.
"""
from tools.db.trusted.effects import db_query, send_email
from clauz3.spec import ContractSpec, contract, effect
from clauz3.spec import no_guarantees as core_no_guarantees
Email = effect(send_email)
DB = effect(db_query)
# ── Email contracts ───────────────────────────────────────────────────────────
@contract
def addresses_from(schema: type, field: str) -> ContractSpec:
"""Guarantee every email recipient came from `schema`.`field`."""
column = getattr(schema, field)
return Email.all(lambda e: e.addr == column)
@contract
def count_at_most(n: int) -> ContractSpec:
"""Guarantee at most n emails are sent."""
return Email.count() <= n
@contract
def only(addresses: list[str]) -> ContractSpec:
"""Guarantee that every sent email targets one of these addresses."""
return Email.all(lambda e: e.addr in addresses)
@contract
def none() -> ContractSpec:
"""Guarantee that no email is sent."""
return Email.empty()
@contract
def no_guarantees() -> ContractSpec:
"""Make no guarantees about email effects."""
return core_no_guarantees()
@contract
def unique_recipients() -> ContractSpec:
"""Guarantee that no reachable execution emails the same address twice."""
return Email.distinct(lambda e: e.addr)
@contract
def at_most(count: int) -> ContractSpec:
"""Guarantee that at most count emails are sent."""
return Email.count() <= count
# ── Database contracts ────────────────────────────────────────────────────────
@contract
def only_table(table: str) -> ContractSpec:
"""Guarantee every db_query reads from `table` only."""
return DB.all(lambda e: e.table == table)
@contract
def only_where(filter_dict: dict[str, object]) -> ContractSpec:
"""Guarantee every db_query uses exactly this `where` filter."""
return DB.all(lambda e: e.where == filter_dict)
A passing case¶
Looping over the consented users and emailing each one's email column
discharges both addresses_from(UserRow, "email") and
count_at_most(100) (the latter from db_query's @clauz3.ensures length
cap).
examples/email-from-db/cases/newsletter_pass.py
# ruff: noqa
import clauz3
from tools.db.trusted import contracts
from tools.db.trusted.effects import db_query, send_email
from tools.db.trusted.schemas import UserRow
@clauz3.guarantee(contracts.addresses_from(UserRow, "email"))
@clauz3.guarantee(contracts.count_at_most(100))
def main() -> None:
for row in db_query("users", where={"consented": True}):
send_email(row.email, "Newsletter is out!")
A failing case¶
Asserting emails.none() and then sending inside a loop over db_query(...)
is rejected: the loop can produce at least one reachable send_email fact.
examples/email-from-db/cases/email_loop_fail.py
# ruff: noqa
import clauz3
from tools.db.trusted import contracts
from tools.db.trusted.effects import db_query, send_email
@clauz3.guarantee(contracts.none())
def main() -> None:
for row in db_query("users"):
send_email(row.email, "hi")
All cases¶
Browse the full set on the all-cases page.
How they run¶
examples/email-from-db/Justfile
db_trusted := "tools/db/trusted"
prove := "uv run clauz3 prove --trusted-root " + db_trusted
# Proves each case in one process: *_pass.py must prove, *_fail.py must not.
prove-cases := "uv run python -m clauz3._prove_cases --trusted-root " + db_trusted
test: lint cases
lint:
uv run clauz3 lint {{db_trusted}}/effects.py
cases:
{{prove-cases}} cases/*_pass.py cases/*_fail.py