ci / secret-sweep (pull_request) Successful in 5s
ci / ratchet (pull_request) Successful in 5s
ci / vault (pull_request) Successful in 6s
ci / conformance (pull_request) Successful in 9s
ci / doc-references (pull_request) Successful in 3s
ci / complete (pull_request) Successful in 2s
ci / spec-counts (pull_request) Successful in 26s
ci / client (pull_request) Successful in 33s
ci / analysis (pull_request) Successful in 1m30s
docs/BEST-PRACTICES.md declared eight criteria unmet for want of static and dynamic analysis, and the letter to an auditor points at that document. This closes them. scripts/check-static.py is six rules aimed at what this engine can actually have. The one worth having alone is a WebCrypto Promise used as a value: `if (SUBTLE.verify(...))` without `await` is truthy for a forged signature as readily as for a good one. The others are Math.random anywhere in src/, eval or new Function, loose equality on a secret-shaped name, an empty catch outside browser storage, and JSON.parse returned with no shape check. 🔴 The first version flagged nine things and seven were its own fault, which is how a rule teaches people to ignore it. Six were `return SUBTLE.importKey(...)` from an async function, which is correct and idiomatic. Two were empty catches around `localStorage.setItem`, which throws on a quota or in a private window and should be swallowed. Both rules are narrowed and both narrowings are explained where someone will read them. The ninth was real. `openBackup` returned `JSON.parse` of the decrypted plaintext with no shape check, so a backup whose payload was `null` or a bare string came back to a caller that reads fields off it. AES-GCM has authenticated those bytes under a passphrase-derived key, so it is a smaller hazard than the payload parser that test/fuzz.mjs found, and it is the same bug. It now throws `backupPayloadNotAnObject`. Every rule was checked by planting the bug it looks for and confirming it fires, then removing it and confirming the tree is clean. test/fuzz.mjs joins the same `analysis` job. A check nobody runs stops finding things, and this one found a real defect on the day it was written. The CI comment states the measured runtime, 45 seconds, rather than the 11 I guessed first. Gate: static analysis clean across 10 files, 8,001 fuzz cases with 0 problems, conformance 304 passed with 2 skipped, client 289, ratchet 35, vault 26, all 0 failures. Secret sweep clean across 91 files and doc-references resolves all 153 sections. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>