Assay
New — Shipped Today

Real-Time Formal Verification
for AI-Generated Code

PostToolUse hooks intercept every Write and Edit in Claude Code. Deterministic checks. Zero LLM. The AI can't bypass it because the hook runs outside the AI's control.

0ms
LLM Latency
5
Check Types
638
Tests Passing

The Problem

AI writes code. That code ships. Nobody checks if the AI hallucinated a SQL injection, used a non-existent API, or forgot error handling.

Without Hooks

  • 1. AI writes SELECT * FROM users WHERE id = " + userId
  • 2. Code passes review (human or AI — both miss it)
  • 3. Ships to production
  • 4. Vulnerability discovered in prod (or by attacker)
  • 5. Incident response, data breach, trust loss

With Hooks

  • 1. AI writes the same bad code
  • 2. Hook fires instantly — blocks the write
  • 3. AI sees: "SQL injection detected. Fix before proceeding."
  • 4. AI rewrites with parameterized query
  • 5. Cost: $0. Time: milliseconds.

How It Works

The hook fires after every Write and Edit. It reads the full file, scans the changed region, and blocks if it finds issues. The AI must fix them before proceeding.

When code is clean

AI Writes Code
Claude calls Write or Edit
Assay Hook Fires
Reads file, runs 5 checks on changed region
Clean — Allowed
Exit 0. User never notices.

When code has issues

AI Writes Code
Claude writes SQL injection
Assay Hook Fires
Detects string concat in SQL
Blocked
Exit 1. Findings injected to AI.
AI Fixes & Retries
Parameterized query. Hook passes.
Claude Code — PostToolUse Hook
# AI writes a file with SQL injection...
Tool: Write → src/api/users.ts
ASSAY FORMAL CHECK FAILED:
- [sql_parameterized] Line 42: SQL string concatenation
- [error_handling] Line 38: async function 'getUser' has no error handling
Fix these issues before proceeding.
# AI automatically rewrites with parameterized query + try/catch...
Tool: Edit → src/api/users.ts (fixed)
✓ Assay hook passed — no findings in changed region

What It Catches

Five deterministic checkers that run in under 100ms. Pure regex. No LLM, no network call, no hallucination possible.

CheckWhat It DetectsSeverityLanguages
SQL InjectionString concatenation in SQL queries instead of parameterized statementscriticalAll
API MisuseNon-existent methods: .flatten(), .contains(), Object.fromPairs()criticalJS/TS, Python
Error HandlingAsync functions with no try/catch or .catch()highJS/TS
Input ValidationDirect use of req.body/params/query without validationhighJS/TS
Null SafetyNon-null assertions (value!.prop) covering nullable accessmediumTypeScript

Two Enforcement Points, One Verifier

The same formal verifier powers both real-time hooks and batch assessment. One engine, two doorways.

Development-Time

  • HookPostToolUse on Write/Edit
  • FormalClaimless pattern scanning
  • FormalDiff-aware region filtering
  • HookBlock + inject on findings

formal-verifier.ts

Pure TypeScript
Zero dependencies
Can't hallucinate

Assessment-Time

  • LLMMulti-pass claim verification
  • FormalPost-pass overlay on LLM verdicts
  • FormalOverride when formal disagrees
  • FormalReal coverage stats in output

Setup in 30 Seconds

Install the CLI, register the hook, and every Write/Edit gets verified automatically.

Terminal
# Step 1: Install Assay
$ npm install -g tryassay
# Step 2: Register the hook
$ assay hooks install
✓ PostToolUse hook registered for Write and Edit
Location: ~/.claude/settings.json
# Step 3: There is no step 3. Start coding.

Why Not Just Use a Linter?

Linters check syntax. Assay checks what the AI claimed vs what the code does.

CapabilityESLint / BiomeAssay HooksAssay Assess
Syntax & style checks
SQL injection detectionPlugin
API misuse (non-existent methods)
Blocks AI tool calls in real-time
Claim extraction from code
Multi-pass LLM verification
Formal override of LLM verdicts
Works without AI in the loop
Cost per check$0$0~$0.50–2

Try It Now

Run a full assessment on any project. The hooks come with the CLI.

$ npx tryassay assess ./your-project