"""Two rows of CLAUDE.md's canonical-patterns table, held in code rather than prose. A rule stated only in a comment drifts: this one had — 63 mutation responses without `ok`, and twelve route helpers that committed on their own, among them an approval whose decision, activation and token landed in three separate transactions. The debt burn of 2026-09 fixed the drift; these keep it fixed. """ import ast from pathlib import Path ROUTES = Path(__file__).parent.parent / "app" / "routes" MUTATING = {"post", "put", "patch", "delete"} # A route helper that commits where it stands, and why the commit has to be there rather than in # the handler. Anything else flushes and leaves the commit to its handler. COMMITTING_HELPERS = { "auth.py:_issue_token": "the token must be durable before the link that carries it is mailed", "auth.py:_consume_token": "burning a single-use token is its own unit: a later failure must " "not hand the token back", "auth.py:_complete_link": "the tail of the Google callback route, which answers with its result", "auth.py:_register_account": "a background task with its own session, i.e. a job wrapper", "auth.py:upsert_pending_invite": "commits only when it writes — the contract its two " "callers rely on (`_register_account` persists its own flushed user because a no-op here " "commits nothing)", "debug_sessions.py:_end_send": "gives back the claim a send took, in a `finally`: it runs when " "the handler is already unwinding and has no commit left to reach", } def _modules(): for path in sorted(ROUTES.rglob("*.py")): yield path.relative_to(ROUTES).as_posix(), ast.parse(path.read_text(encoding="utf-8")) FUNCTIONS = (ast.FunctionDef, ast.AsyncFunctionDef) # The calls a mutation may answer with in place of an acknowledgement: each describes the resource # it wrote (or is the logout's cookie-clearing response). A domain function's REPORT dict is not one # of them — `{"synced": n}` is an acknowledgement, and gets spread into `{"ok": True, **report}`. SERIALIZERS = { "JSONResponse", "collection_card", "_playlist_card", "_serialize", "_serialize_folder", "serialize", "serialize_job", "_serialize_profile", "_serialize_user", "_summary", "admin_get_quota", "describe", "owner_view", } def _called(node: ast.Call) -> str: f = node.func return f.id if isinstance(f, ast.Name) else getattr(f, "attr", "") def _is_route(fn: ast.FunctionDef | ast.AsyncFunctionDef) -> bool: return any( isinstance(d, ast.Call) and getattr(d.func, "attr", "") in MUTATING | {"get", "websocket"} for d in fn.decorator_list ) def _is_mutation_route(fn: ast.FunctionDef | ast.AsyncFunctionDef) -> bool: return any( isinstance(d, ast.Call) and getattr(d.func, "attr", "") in MUTATING for d in fn.decorator_list ) def test_every_mutation_answers_ok(): """`{"ok": true, …}` (or `{"deleted": id}`) for every mutation, sync or async — one field a client can read the same way everywhere. A serializer's return (SERIALIZERS) is exempt: it describes a resource, not an acknowledgement. A dict built elsewhere — returned by name, or straight from a domain call — is not: both hid acknowledgements without `ok` from a literal-only scan, so such a dict is spread into a literal.""" missing = [] for name, tree in _modules(): for fn in ast.walk(tree): if not (isinstance(fn, FUNCTIONS) and _is_mutation_route(fn)): continue for node in ast.walk(fn): if not isinstance(node, ast.Return): continue if isinstance(node.value, ast.Call) and _called(node.value) in SERIALIZERS: continue keys = ( {k.value for k in node.value.keys if isinstance(k, ast.Constant)} if isinstance(node.value, ast.Dict) else set() ) if not keys & {"ok", "deleted"}: missing.append(f"{name}:{node.lineno} {fn.name}") assert missing == [] def test_route_helpers_flush_and_leave_the_commit_to_the_handler(): """Every function in a route module that is not itself a route is a helper — whatever its name (two public ones committed unseen while this looked only at `_`-names) and sync or async.""" committing = set() for name, tree in _modules(): for fn in ast.walk(tree): if isinstance(fn, FUNCTIONS) and not _is_route(fn): if any( isinstance(n, ast.Call) and getattr(n.func, "attr", "") == "commit" for n in ast.walk(fn) ): committing.add(f"{name}:{fn.name}") assert committing == set(COMMITTING_HELPERS)