#!/usr/bin/env python3 """Enforce the AR-024 invariant: never a raw cosine, always the calibration. TRACES: AR-024 | SR-002 docs/requirements.md gives AR-024's verification tier as "Static check -- no bare cosine outside a tagged EXCEPTION | Grep-based; this is the invariant's enforcement". This is that check. Until it existed the invariant was enforced by reading, and reading missed a live violation: the identity matcher's no-calibration fallback thresholded raw cosine distance and fed `max(0, cosine)` into the Bayesian accumulation as though it were a posterior. WHAT IT CHECKS, precisely, because a static check that overclaims its reach is worse than one with a stated scope: Every call to `cosine_similarity(...)` in C++ source must either (a) have its result consumed immediately by a calibration -- the call is textually wrapped in `cal_(...)`, `calibrate_(...)`, `.probability(...)` or similar; or (b) sit under an exception comment -- the token is `EXCEPTION:` followed by `AR-024` and a reason -- within EXCEPTION_SCOPE_LINES above it. Note that this file deliberately never spells that token out. The traceability extractor scans scripts/ as source, so prose here describing the tag would be counted as recorded exceptions; four of them were, until this was noticed. The same trap the shared config warns about for the vendored parser tests. Anything else is a defect, per CLAUDE.md: "treat any bare cosine comparison in the code as a defect to be fixed". WHAT IT DOES NOT CHECK, and why you should not read a pass as more than it is: - It cannot follow a cosine through a variable across statements. A file that stores `float s = cosine_similarity(a, b);` and compares `s` three lines later is not caught. The codebase does not currently do this, and this check exists partly to keep it that way, but it is a convention backed by review, not by the tool. - It says nothing about GEMM output. The similarity engine returns a whole matrix of cosines and the matcher reads them directly; that path is correct by inspection (every value goes through `cal_.probability`) and is not verified here. - A retired constant reintroduced under a new name is invisible to it. Exit status is 0 when clean, 1 when a violation is found, 2 on a usage error. """ import argparse import pathlib import re import sys # How far above a use an exception tag may sit and still cover it. # Generous, because the house style puts a paragraph of reasoning between the # tag and the code -- but bounded, so a tag cannot silently cover a whole file. EXCEPTION_SCOPE_LINES = 25 CPP_SUFFIXES = {".h", ".hpp", ".hxx", ".cc", ".cpp", ".cxx", ".cu", ".cuh"} # src only, deliberately. The invariant governs what the PIPELINE decides -- # CLAUDE.md's rule is "tag the unit that decides" -- whereas a test legitimately # asserts properties of the metric space itself (that a vector's cosine with # itself is 1, that the annex ended up holding the spoke it should have). Those # are measurements of the code under test, not decisions shipped to a user, and # sweeping them in would produce a wall of blanket EXCEPTION tags that would # devalue the tag everywhere else. Pass --source-root tests to scan them anyway. DEFAULT_ROOTS = ["src"] # Directories that are never this repo's code. EXCLUDE_DIRS = { "build", "build-ort", "external", "vendor", "__pycache__", ".git", "node_modules", "models", } COSINE_CALL = re.compile(r"\bcosine_similarity\s*\(") # The result is immediately handed to a calibration. Matches the house shapes: # cal_(cosine_similarity(a, b)) # calibrate_(cosine_similarity(a, b)) # same_person(cosine_similarity(a, b)) # cal_.probability(cosine_similarity(a, b)) CALIBRATED = re.compile( r"(?:\b(?:cal_|cal|calibrate_|calibrate|same_person|same_person_probability)" r"\s*(?:\.\s*probability\s*)?\(\s*|\.\s*probability\s*\(\s*)" r"cosine_similarity\s*\(" ) EXCEPTION_TAG = re.compile(r"EXCEPT" + r"ION:\s*AR-" + r"024\b(.*)") # The function's own definition is not a use of it. DEFINITION = re.compile(r"^\s*(?:inline\s+|static\s+|constexpr\s+)*float\s+" r"cosine_similarity\s*\(") # The house style wraps long calls across lines: # const float p = calibrate_( # cosine_similarity(a, b)); # so the calibration and the call it guards are not always on one line. Joining # a small window before testing is what makes this check usable on real code # rather than a generator of false positives that trains people to ignore it. JOIN_LOOKBEHIND = 2 def iter_sources(root: pathlib.Path, roots): for rel in roots: base = root / rel if not base.exists(): continue for p in sorted(base.rglob("*")): if p.suffix.lower() not in CPP_SUFFIXES: continue if any(part in EXCLUDE_DIRS for part in p.relative_to(root).parts): continue yield p def covering_exception(lines, idx): """Return the reason text of an exception tag covering line `idx`.""" lo = max(0, idx - EXCEPTION_SCOPE_LINES) for j in range(idx, lo - 1, -1): m = EXCEPTION_TAG.search(lines[j]) if m: return m.group(1).strip(" -—*/") or "(no reason given)" return None def check_file(path: pathlib.Path, root: pathlib.Path): violations, exceptions = [], [] try: lines = path.read_text(encoding="utf-8", errors="replace").splitlines() except OSError as e: print(f"error: cannot read {path}: {e}", file=sys.stderr) return violations, exceptions rel = path.relative_to(root) for i, line in enumerate(lines): if not COSINE_CALL.search(line): continue # A comment mentioning the function is prose, not a use. stripped = line.lstrip() if stripped.startswith(("//", "///", "*", "/*")): continue if DEFINITION.match(line): continue # Join a small window so a call wrapped across lines is still seen as # calibrated. Whitespace is collapsed so the join reads as one statement. window = " ".join( lines[max(0, i - JOIN_LOOKBEHIND):i + 1] ) window = re.sub(r"\s+", " ", window) if CALIBRATED.search(window): continue reason = covering_exception(lines, i) if reason: exceptions.append((rel, i + 1, line.strip(), reason)) else: violations.append((rel, i + 1, line.strip())) return violations, exceptions def main(): ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) ap.add_argument("--root", default=None, help="repository root (default: the script's ../..)") ap.add_argument("--source-root", action="append", default=None, help="directory to scan; repeatable (default: src, tests)") args = ap.parse_args() root = pathlib.Path(args.root) if args.root \ else pathlib.Path(__file__).resolve().parents[2] roots = args.source_root or DEFAULT_ROOTS if not root.is_dir(): print(f"error: root {root} is not a directory", file=sys.stderr) return 2 all_violations, all_exceptions, n_files = [], [], 0 for p in iter_sources(root, roots): n_files += 1 v, e = check_file(p, root) all_violations += v all_exceptions += e if n_files == 0: # A scan that found nothing to read is a misconfiguration reporting a # pass, which is the failure mode the traceability gate also guards. print(f"error: scanned 0 source files under {root} ({', '.join(roots)})", file=sys.stderr) return 2 print("AR-024 — always the calibrated probability, never a raw cosine") print("=" * 72) print(f"Repo root : {root}") print(f"Files scanned : {n_files} ({', '.join(roots)})") print(f"Recorded excs. : {len(all_exceptions)}") print(f"Violations : {len(all_violations)}") if all_exceptions: print("\nRecorded exceptions (allowed, and each one is a claim to re-read):") for rel, ln, src, reason in all_exceptions: print(f" {rel}:{ln} {reason}") print(f" {src}") if all_violations: print("\nVIOLATIONS — a bare cosine with no recorded exception:") for rel, ln, src in all_violations: print(f" {rel}:{ln}") print(f" {src}") print("\nEvery similarity is converted through the sigmoid calibration") print("before it is used, compared, or thresholded. A raw cosine means") print("something different for every model, gallery and face size, and") print("it cannot be combined with anything else.") print("\nEither route it through the calibration, or, if the use is") print("genuinely about the metric space rather than about a decision,") print("record it:") print(" // " + "EXCEPT" + "ION: AR-" + "024 ") print("and add a row to CLAUDE.md's agreed-exceptions table.") return 1 print("\nOK: no bare cosine outside a recorded exception.") return 0 if __name__ == "__main__": sys.exit(main())