CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 8s
CI & Build / integration (push) Successful in 28s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Failing after 46s
CI & Build / Build & push image (push) Skipped
The read half. `notes_due_for_verification` + `mark_note_verified` + the MCP
pair, ordered `verified_at ASC NULLS FIRST`: never-checked outranks
checked-long-ago, because a note nobody has ever confirmed is a claim with no
evidence behind it at all. Postgres sorts NULLs LAST on ASC by default, so
getting this wrong would not error — it would silently invert the one signal
the sweep exists to carry, which is why it has a test of its own.
A SIBLING of rules_due_for_verification, not a shared implementation, and this
milestone is a deliberate self-application of note 3163: the row could have
been shared, the QUERY could not. That sweep scopes by rulebook ownership XOR
project ownership because rules have no sharing ACL at all; a note scopes by
the note ACL — browse, not read, so a record shared one-to-one never arrives
in a passive surface unasked (decision 2094).
What genuinely IS common moved to services/verification.py: how a stamp reads,
how old it is, and the three states `last_verified` distinguishes — None ("a
decision, the question does not apply"), "never" ("a fact nobody has
confirmed"), a date. Rulebooks now imports it rather than defining it, so this
is a consolidation and not a third copy.
A failed check writes NOTHING, carried over from 312: there is no "verified
false" state, because a note whose check failed is not in a special condition
— it is WRONG, and recording the failure as a flag would let it sit there
being false with the sweep quietly satisfied that somebody had looked.
Two decisions worth naming:
The sweep does NOT filter to non-task, non-snippet records even though the
write path permits a check on nothing else. Such a row would be in an ILLEGAL
state and this is the one surface that could say so; hiding it to match the
invariant would make the sweep agree with a database it had stopped
describing.
A negative `older_than_days` raises instead of meaning "everything" — silently
answering a different question is the failure shape this guards.
`notes_due_for_verification` is classified read-only in server.py, spelled out
because its name matches none of the prefixes the completeness test derives
from. `rules_due_for_verification` is in the same position and is NOT listed,
so it fails closed for read keys today — filed as #3191 rather than fixed
here, since widening an auth boundary on a tool I did not write is the
operator's call.
58 lines
2.6 KiB
Python
58 lines
2.6 KiB
Python
"""What a record's own check MEANS — shared by rules and notes.
|
|
|
|
Two record types carry `verify_with` / `expires_when` / `verified_at`: rules
|
|
(milestone 312) and notes (milestone 317). What they share is BEHAVIOUR — how
|
|
a stamp is read, how old it is, what "never" means — not storage and not the
|
|
query. Note 3163 is the rule this file is an instance of: `semantic_search_*`
|
|
could never have been shared between them because a rule scopes by rulebook
|
|
ownership and a note scopes by the note ACL, so the two sweeps are siblings.
|
|
These functions are the part that genuinely is common, factored so it exists
|
|
once rather than twice.
|
|
|
|
Everything here is pure, sync and DUCK-TYPED: it reads `verify_with` and
|
|
`verified_at` off whatever it is handed. That is deliberate. A shared base
|
|
class or a protocol would tie two SQLAlchemy models together to share four
|
|
lines of date arithmetic — the DRY costume, in the same note's words.
|
|
"""
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
def last_verified_label(record) -> str | None:
|
|
"""How long ago this record's check passed — None when it carries none.
|
|
|
|
Three states, and the distinction between the last two is the whole point:
|
|
|
|
- `None` — this record is a DECISION. There is nothing to go and check,
|
|
and the question does not apply. Most records.
|
|
- "never" — it asserts a fact and NOBODY HAS EVER CONFIRMED IT. The one
|
|
worth acting on, and why the sweep sorts these first.
|
|
- a date — somebody checked, then.
|
|
|
|
Callers attach this to a payload only when it is not None, so "no key" and
|
|
"never verified" do not become two states every client has to tell apart.
|
|
"""
|
|
if not getattr(record, "verify_with", None):
|
|
return None
|
|
stamp = getattr(record, "verified_at", None)
|
|
return stamp.date().isoformat() if stamp else "never"
|
|
|
|
|
|
def days_since_verified(record) -> int | None:
|
|
"""Whole days since the check last passed; None if it never has.
|
|
|
|
Computed rather than left to the reader, because "2026-06-14" and "74
|
|
days" prompt different reactions and only one of them is the question
|
|
being asked.
|
|
|
|
Naive stamps are read as UTC. Postgres hands these back tz-aware, but a
|
|
restore, a fixture or a sqlite-backed test may not, and subtracting an
|
|
aware datetime from a naive one raises — which would make the sweep fail
|
|
on exactly the rows it exists to surface.
|
|
"""
|
|
stamp = getattr(record, "verified_at", None)
|
|
if stamp is None:
|
|
return None
|
|
if stamp.tzinfo is None:
|
|
stamp = stamp.replace(tzinfo=timezone.utc)
|
|
return (datetime.now(timezone.utc) - stamp).days
|