How this site is built, and where it falls short
A practice arguing that systems should show their working is obliged to show its own. Every number here was measured rather than asserted, and the open defects are listed beside the passes — a page of green ticks would tell you nothing.
Measured
Production build, gzipped, on the build output rather than a development server — a development server ships unminified React and would have flattered these by roughly fifteen times. You can reproduce any of it from the repository.
| Measurement | Value | How and what it means |
|---|---|---|
| JavaScript, homepage | 265 KB | Gzipped, production build, 10 files. Almost all of it is the React and Next runtime; the site's own code is a small fraction. Measured by summing the gzipped size of every chunk the prerendered homepage references. |
| JavaScript, heaviest interactive page | 269 KB | The examination simulator — 4 KB more than the homepage, because the instrument itself is a few hundred lines of deterministic logic and no library. |
| CSS, whole site | 8 KB | Gzipped. One hand-written stylesheet, 582 lines, covering every page and both themes. No framework, no utility classes, no unused rules to purge. |
| HTML, homepage | 17 KB | Gzipped, prerendered. The page is readable and complete before any JavaScript executes. |
| Production dependencies | 6 | next, react, react-dom, pg, zod, posthog-js. No UI library, no component library, no animation library, no charting library, no CSS framework. Every interactive instrument on this site is written from scratch against the platform. |
| Automated tests | 92 | Covering the diagnostic scoring, the booking screener's routing rules, the retrieval ranking, the examination verdicts and the abuse limiter. The build fails if any of them do. |
| Pages · API routes · indexed documents | 32 · 9 · 215 | Every page is prerendered as static HTML except the handful that must read the database. |
Open · not yet met
The part that makes the rest worth reading. Anything not measured is listed as not measured rather than left out, because omission is how a scorecard lies.
No published Lighthouse or Core Web Vitals figures
Instrumentation: present. PageTelemetry collects web-vitals in the browser (src/components/PageTelemetry.tsx), on every pageview. Delivery: unverified. Whether those events arrive and aggregate correctly has not been confirmed against the processor. Reporting: none. No field figure is published here, because insufficient data is not a score — and a score from a developer machine is not a claim about what a visitor experiences, which is the unfounded number this page exists to avoid.
No formal WCAG 2.2 AA audit
The site is built to the obvious disciplines — semantic HTML, visible focus, labeled controls, contrast from a fixed token palette, no keyboard traps, honored reduced-motion — and it has not been audited against real assistive technology by someone qualified. Until it has, no conformance is claimed.
Content-Security-Policy carries 'unsafe-inline' for scripts
A nonce policy in the App Router needs middleware on every request, which makes every route dynamic and costs the static prerender. This site renders no visitor-supplied HTML anywhere, so the surface a nonce would close is close to nil — but it is a real weakening and it is recorded rather than glossed. On the directive carrying most of the value: connect-src 'self' restricts which origins scripts running in the browser may contact — it is a browser-side restriction on this page's own scripts. It is not a guarantee against exfiltration, because analytics is deliberately proxied: the browser sends events to this site's own origin, and the server forwards them on to the analytics processor. The data path is website host → proxy → processor, and a same-origin endpoint can forward information onward — the header does not prevent that, and was never intended to.
Diagnostic and examination are self-reported
Both instruments measure what a visitor says, not what is true. The examination is designed around that limit rather than despite it — the follow-up question is there precisely because a claim and its evidence are different things. Neither should be read as an assessment.
Corrections
Where something published here turned out to be wrong, the correction is recorded with a date rather than quietly applied. Being publicly wrong on purpose is the rarest credibility signal available, and the cheapest to offer honestly.
RBI FREE-AI framework marked unverified
Confirmed against primary sources and now cited. The internal research note had carried it as unconfirmed since compilation; the restriction is lifted and the correction is dated at the head of that document.
NIST AI RMF cited as a standard this practice builds to
Removed from all four positioning surfaces. It was a category-level reference doing work it could not support. A citation to a NIST publication on ISO 23247 remains inside one article, because that is a primary source rather than a credential.
SR 11-7 cited as current model risk guidance
Superseded on 17 April 2026 by Fed SR 26-2 / OCC Bulletin 2026-13, which also places agentic and generative systems expressly outside scope. Corrected across the estate. This is the correction the practice is most often right about before others are.
What is true by construction
Determinism
The diagnostic, the booking screener, the search ranking and the examination are all deterministic and their methods are published on their own pages. No model sits in the path of any decision this site makes about a visitor. The same inputs always produce the same output, and you can check.
No third-party runtime
connect-src 'self' stops a browser script on this site from contacting another host directly. Analytics is proxied first-party and configured with no autocapture, no session recording and no person profiles — but the events it does send are then forwarded by the server to PostHog, which is a deliberate second hop the header above does not, and is not meant to, block. What actually happens to the address on each event — the specific claim this page previously got wrong — is in the inventory below and at /privacy, not summarized here in prose.
Works without JavaScript
Every page is prerendered and readable before any script runs. The interactive instruments need JavaScript to be driven, but their method and question set are rendered as static prose so a reviewer — or a crawler that executes nothing — can still examine how they work.
Readable by machines on purpose
A structured-data graph, an enriched llms.txt, an ungated search endpoint and a read-only MCP server. An assistant assembling a shortlist can quote the published positions accurately instead of paraphrasing them.
Every field this site collects
This table and the one at /privacy render the same source (src/content/collection-inventory.ts), so the two pages cannot describe the same field two different ways again. It is where the IP-address row lives now, rather than in a sentence on this page.
| What | Why, and when it is recorded | Processor | Retention |
|---|---|---|---|
| Diagnostic scores — four layer scores, sector, jurisdiction, action class, volume band | Recorded when you complete the diagnostic, to build a sector benchmark. No organization name, email, identifier or IP address is stored with the row. | This site's own database (managed Postgres) — table diagnostic_runs. | Kept indefinitely. There is no scheduled deletion job on this table. This row said "24 months" until 9 September 2026; nothing in the code enforced it, and the only automatic deletion anywhere in this database is a 24-hour purge of rate-limit counters. The rows carry no identifier, so there is nothing in them to erase on request — which is why a duration was never built, and not a reason to have published one. |
| Inquiry details — name, email, organization, situation, timeline | Recorded only when you submit the contact form, to respond to you. This is the only place identity is attached, and you attach it deliberately. | This site's own database (managed Postgres) — table inquiries. Forwarded to the practice principal's mailbox so the inquiry can be actioned. | Kept until you ask for erasure. There is no scheduled deletion job on this table — see "One retention rule" on the privacy page for what that means in practice. |
| Subscriber email | Recorded only if you subscribe, to send new writing. | This site's own database (managed Postgres) — table subscribers. | Until you unsubscribe. |
| Diagnostic delivery email — address, sector, overall score band | Recorded only if you ask for the diagnostic PDF by email. Sent with the generated PDF to Resend for delivery, and stored as a contact record so a conversation can follow. Held separately from the anonymous diagnostic row above; the two are never joined. Choosing Print or save as PDF instead sends nothing anywhere. | This site's own database (managed Postgres) — table report_requests — for the contact record; Resend for delivery. | Kept until you ask for erasure in the database. Resend's side is a separate path — see "Email-provider logs" below. |
| Teardown request — name, email, organization, the link or file you send, your notes | Recorded only if you request an asynchronous teardown, to read the artifact and write the critique back to you. | This site's own database (managed Postgres) — table teardown_requests — for the request fields. An uploaded file is forwarded to the practice principal by email and is not retained in the site's storage; only its filename and size are recorded, so the request can be tracked. | Request fields: kept until you ask for erasure. Attachment: not retained here after forwarding — see "Mailbox" and "Email-provider logs" below for where a copy may persist. |
| Anonymous usage events — page viewed, diagnostic started/completed, per-question progress and time bucket, article read depth, time-on-page bucket, page performance numbers, error counts, booking link clicked, form submitted | Recorded as you use the site, to see which pages are useful, where people give up, how far articles are read, how fast pages load, and which article preceded a diagnostic. Configured with memory-only persistence, manual bounded events, no autocapture, no session recording, no person profiles. Every property leaving the browser is a bounded category or a number — no form values, answers, organization names, email addresses, free text, URL, referrer, search term or user-agent string. Durations are reduced to four coarse buckets before capture. See src/lib/analytics.ts for the complete, typed list of events. | PostHog, via this site's first-party proxy. | 12 months. |
| IP address ($ip, attached by the analytics pipeline to every event) | PostHog treats an absent or empty $ip as an instruction to use the address on the connection, so a value has to be sent for the address not to be used. This site sends the fixed placeholder "0" on every event (src/lib/analytics-privacy.ts). A client-side sentinel alone is not the full control: the authoritative one is the PostHog project's own "Discard client IP data" setting, which PostHog documents as the only way to stop the fallback for every ingestion path, including ones this repository does not send events through directly. Both are meant to be in force (docs/posthog-privacy-settings.md). | PostHog. Sentinel applied client-side in src/lib/analytics-privacy.ts; discard setting applied in the PostHog project. | Events captured from 18 August 2026 onward: 12 months, with the other usage events above. Events captured 24 July – 17 August 2026, before the fix, carried a real address and derived location instead of the placeholder: open — see the dated status on the privacy page. |
| Rate-limiting digests — salted SHA-256 hashes derived from the requesting network address and, where a form supplies one, the email address and its domain | Written when a public form or report endpoint is called, to enforce a narrow request limit. Each value is salted and one-way hashed before it reaches storage, so no address, email or domain is recoverable from a counter row. A row holds a digest, a token count and a timestamp — nothing else. These are pseudonymous derived identifiers, used to count repeated requests without storing the original value; they are not a claim that the original cannot be recovered under every threat model. Counters are never joined to form or diagnostic records and are not used to recognize a returning visitor. | This site's own database (managed Postgres) — table rate_limit_buckets. | Deleted once the counter has refilled and can no longer deny anything — at most 24 hours. The only table in this inventory with a code-enforced deletion (src/lib/db.ts). |
| Referrer tag — e.g. utm_source=article | Stored alongside the records above, to know which page led somewhere useful. | This site's own database (managed Postgres), attached to the record it belongs to. | With the record it belongs to. |
| Mailbox — the practice principal's own email inbox | Receives a notification when an inquiry or teardown request is submitted, and carries a forwarded attachment where a teardown request included one. | The practice principal's own email provider — outside this application's database. | Not established in code. Governed by the principal's own mailbox retention, not by this application. |
| Email-provider logs — Resend's own delivery and operational logs | Deliver the diagnostic report PDF and the forwarded notifications above. | Resend. | Resend's own operational email-log retention. Not set by this application. |
| Database backups | Disaster recovery for the application records listed above. | The database hosting provider. | Not established in code. Governed by the hosting provider's backup policy, not by this application. |