Assay grades a business's AI readiness from their own page source. It refuses to build the outreach packet until you have recomputed the findings from source you pasted yourself.
Six checks · MIT licensed · Your Places key, your Anthropic login, no Assay server
Hey, I'm Ryan LenkI run a one-person SEO, GEO and AEO practice out of Pennsylvania. Assay is the tool I prospect with. More about me.
Discovery, then the two settings screens that put your name and your colour on the document.
One operator, one business at a time. It automates the process I already ran by hand.
Google Places API, one search area at a time.
Six checks fetch the business's own pages and score the raw source. A summary of a page is never evidence.
You paste your own view-source and the check recomputes. A finding you do not reconcile cannot reach a document.
A PDF scorecard, a schema starter kit, and delivery drafts.
Nothing auto-sends. src/main/send/provider.ts defines the outbound signature and nothing implements it.
What you tell a prospect is that every finding in their document points at something they can see in their own source with Ctrl+U.
Generation refuses first. src/main/packet/generate.ts throws before any file is written if a finding has not been confirmed against source you pasted.
Confirmations expire after 72 hours. Past that, you re-paste.
Three of the five also break a test or a type: law 2 in scripts/test-packet.js, law 3 in scripts/test-approval.js, law 4 at compile time.
| # | Law | Enforced by |
|---|---|---|
| 1 | AI-summarized page content is never evidence. Raw source only. | src/main/evidence/fetch-raw.ts is the only network egress for target sites. The only other evidence is your own paste, wrapped by src/main/confirmation/gate.ts. Agents get no fetch tool, no browser and no search. |
| 2 | Never fabricate a review, testimonial or result. | src/main/packet/guardrails.ts, swept over every artifact before anything is written. It refuses rather than repairs. |
| 3 | Nothing auto-sends, auto-posts or auto-prints. Approval is per item. | src/main/approval/gate.ts registers every approved item in a module-private WeakSet at the moment it is minted, so assertMinted refuses anything that did not come through the gate. The unique symbol on the type is a compile-time brand and is erased at build; the runtime guarantee is the set. |
| 4 | Every score prints its instrument and its base. | The Score type at src/shared/types.ts has no optional fields, so omitting the base is a compile error. |
| 5 | Discovery is the Places API plus the business's own site. | A single discovery adapter. src/main/evidence/fetch-raw.ts refuses Google Maps and Search hosts, and private or loopback addresses, on every redirect hop. |
Nothing here watches what an assistant actually says about a business. It measures what that business publishes for one to read.
Two delivered client scans, which between them publish three scored properties. scripts/test-instrument.js fails npm test if the instrument stops reproducing those three exactly.
Where a finding compares the site against the Google listing, which is NAP consistency and booking path, only the site half is reconciled. The listing half is the app's own capture on both passes.
Nothing counts until you confirm it.
Six checks · Nothing sends in this build
Every claim about how the tool behaves points at a file in the repo.
github.com/itsryanlenk/assay ▸MIT licensed · Electron desktop app · Source only, no packaged build