Sample deliverable
A page from a real drift report.
The agent-readiness audit produces a drift report, an assertion suite and a CI gate. This is one page of the first — four findings in the format they are delivered, each one traced from the sentence that made the claim to the check that now holds it.
The repository is my own. AgendaProfe is the production system behind the flagship case study — real users, real payments, and the most disciplined context layer I operate. A client's report is redacted: file paths become apps/<service>/…, claim text is paraphrased where it names anything internal, and the assertion suite ships in the repository rather than on a web page. Nothing here is redacted, because there is no client to protect and the findings are worth more intact.
Every line below is copied out of the delivered artifact. The command output near the bottom is the tool's real output, exit code included.
Drift report — excerpt
- Repository
- AgendaProfe — pnpm monorepo, Next.js web + Expo mobile
- Context layer in scope
CLAUDE.md (repo) · CLAUDE.md (hub repo)- Run against
main, 6 August 2026- Assertion suite
- 10 assertions — 4 passing, 6 failing, 0 unverifiable
- This excerpt
- 4 findings of the full report
Finding F-01
Severity: High
Two decommissioned vendors' environment variables are still declared in the build tooling
- Claim
Do NOT reintroduce a Supabase/Vercel code path or env var.
CLAUDE.md#L382-383
- Evidence
Five of them are declared right now, in the build task's env array: VERCEL_ENV, VERCEL_GIT_COMMIT_REF, SUPABASE_URL, SUPABASE_ANON_KEY and SUPABASE_SERVICE_ROLE_KEY.
turbo.json#L10-16
Both vendors were fully decommissioned — the projects torn down, the code coupling removed — and the decision record says so in the same words the context file uses. The instruction not to reintroduce them was written while the build tooling already declared them.
- Why it matters
- An agent reading the declaration list treats those variables as part of the current contract. The failure is not a broken build — it is an agent provisioning, documenting or branching on infrastructure that no longer exists, confidently, because the repository told it to.
- Fix
- Delete the five entries. They are cache keys for a build that never reads them, so removal changes nothing at runtime and narrows the hash.
- Assertion left behind
{
"claim": "Do NOT reintroduce a Supabase/Vercel code path or env var.",
"kind": "env_var_absent",
"args": { "name": "SUPABASE_URL", "files": ["turbo.json"] },
"source": "CLAUDE.md#L382"
}
One assertion per variable — five in total. A rule naming five things becomes five checks, so a partial cleanup reports as partial rather than green.
Finding F-02
Severity: High
The one-resolver rule names a file that no longer holds the resolver
- Claim
One entitlements resolver: entitlementsFor() in src/lib/subscriptions/entitlements.ts is the single place Free/Pro/Founding/comped tier logic is decided — don't branch on plan/tier ad hoc elsewhere.
CLAUDE.md#L56-58, repeated at #L637
- Evidence
The named file is now a re-export shim. The resolver moved to the shared package so web and mobile compute entitlements identically, and the shim's own header comment says edit the resolver in packages/shared/src/subscriptions-entitlements.ts — which is where the function is defined, at line 146.
apps/web/src/lib/subscriptions/entitlements.ts#L1-12 · packages/shared/src/subscriptions-entitlements.ts#L146
The rule itself is still correct and still load-bearing: there is exactly one resolver. Only the address is wrong — and it is wrong in the file an agent reads before touching billing.
- Why it matters
- An agent told to change tier logic opens the shim, finds no logic, and does the reasonable thing: writes the branch it needs where it already is. That is precisely the ad-hoc branching the rule exists to prevent, caused by the rule.
- Fix
- Repoint both mentions at the shared package, and say the web path is a re-export kept for import compatibility.
- Assertion left behind
{
"claim": "One entitlements resolver: `entitlementsFor()` in
`src/lib/subscriptions/entitlements.ts`.",
"kind": "symbol_at_path",
"args": {
"symbol": "entitlementsFor",
"path": "apps/web/src/lib/subscriptions/entitlements.ts"
},
"source": "CLAUDE.md#L56-58"
}
Reported as a suspected finding and confirmed by hand before it went in the report. symbol_at_path is regex-based and documented as unable to see through a re-export, so a failure here could have been the tool's limitation rather than real drift. It was real drift: the resolver has genuinely moved. A finding that had turned out to be the tool's blind spot would appear in the report labelled as such, with this same reasoning — never silently dropped, and never quietly counted.
Finding F-03
Severity: Medium
Two context files give different rules for how a change lands
- Claim
Exception (as of 2026-07-12): docs-only and Tier 1 trivial changes may push straight to main.
CLAUDE.md#L392-393 — the repository's own file
Every change in every repo lands through a pull request — decided 2026-08-04, replacing the 2026-07-31 rule that reserved pull requests for repos where a merge does something irreversible or public.
CLAUDE.md#L88 — the hub repository, which also lists this repository as pull request: required at #L100
- Evidence
Neither sentence is false on its own. Both files are current, both are read by agents, and the later rule — 2026-08-04 — supersedes the earlier one, which no agent reading only the repository's file will ever discover. The repository file still presents the withdrawn exception as live policy, and branch protection does not settle it either: it is a private repository, where the enforcement switch is a paid feature.
- Why it matters
- Agents follow whichever version they read first, and a session scoped to one repository only ever reads one of them. The observable outcome is inconsistent: identical changes land through a pull request or straight on
main depending on which file was in context. - Fix
- Withdraw the exception in the repository's file and link to the hub rule, so the supersession is visible from the file that is actually loaded.
- Assertion left behind
None. No assertion can express this.
Every mechanical check verifies one file against the repository. This finding is two files disagreeing with each other, and both pass. It comes out of the contradiction pass, which is a person reading each context file against the others — and it is the reason this is an audit rather than a tool you install. The report says plainly that nothing guards this one; a green suite that implied otherwise would be worse than no suite.
Finding F-04
Severity: Low
A command's description drifted from what the command runs
- Claim
pnpm verify:push — typecheck + unit only (no DB needed).
CLAUDE.md#L170, repeated at #L440
- Evidence
The script is pnpm typecheck && pnpm lint && pnpm test. It also runs lint. The description has been two-thirds right for long enough that the missing third is invisible.
package.json#L38
- Why it matters
- Barely, on its own — this is what a Low is. It earns its place because of how it was found: the assertion covering this claim passes. The script exists, so the check is green, and the sentence is still wrong.
- Fix
- Three words. Say typecheck, lint and unit.
- Assertion left behind
The existing script_exists assertion, unchanged — and it will keep passing.
The honest limit of the gate, stated in the deliverable rather than discovered later. Six assertion kinds check whether the thing a sentence names exists; none reads the adjective attached to it. What the suite guarantees is that no claim silently becomes false about a file, a script, a symbol, a workflow trigger or an environment variable. Prose accuracy beyond that is what the read-out and the re-check are for.
The first run
The report is not the product. The assertion suite is, and the report is its first run — this output, on the commit the audit was performed against. It ships as .groundtruth.jsonc in the repository root and runs on every pull request in two lines of YAML.
$ groundtruth check
Context layer: CLAUDE.md
10 assertion(s) — 4 passing, 6 failing, 0 unverifiable
✗ CLAUDE.md#L382 "Do NOT reintroduce a Supabase/Vercel code path or env var."
SUPABASE_URL found in turbo.json
✗ CLAUDE.md#L382 "Do NOT reintroduce a Supabase/Vercel code path or env var."
SUPABASE_SERVICE_ROLE_KEY found in turbo.json
✗ CLAUDE.md#L382 "Do NOT reintroduce a Supabase/Vercel code path or env var."
SUPABASE_ANON_KEY found in turbo.json
✗ CLAUDE.md#L382 "Do NOT reintroduce a Supabase/Vercel code path or env var."
VERCEL_ENV found in turbo.json
✗ CLAUDE.md#L377 "no `VERCEL_ENV` branches (prod-vs-preview is decided by `APP_URL`)"
VERCEL_GIT_COMMIT_REF found in turbo.json
✗ CLAUDE.md#L56-58 "One entitlements resolver: `entitlementsFor()` in
`src/lib/subscriptions/entitlements.ts`."
entitlementsFor is not exported from apps/web/src/lib/subscriptions/entitlements.ts
✓ CLAUDE.md#L300-303 "`checks.yml` via `main-checks.yml` runs automatically on
every PR into `main`."
main-checks.yml triggers on 'pull_request' (branch 'main')
✓ CLAUDE.md#L170 "`pnpm verify:push` — typecheck + unit only (no DB needed)."
scripts.verify:push = "pnpm typecheck && pnpm lint && pnpm test"
✓ CLAUDE.md#L22-24 "`apps/web/src/middleware.ts` is the only location Next.js
will load middleware from."
apps/web/src/middleware.ts exists
✓ CLAUDE.md#L24-27 "A copy at `apps/web/middleware.ts` compiles but is silently
never invoked. Never move it."
apps/web/middleware.ts does not exist
exit 1
Six failures is what the exit code is for. Every one of them was true when it was written, and the four passing assertions matter just as much — they are the rules that are still load-bearing, now held in place rather than trusted.
Delivered alongside this page: the rest of the findings, the full assertion suite, the gate wired into CI, the context-economics read on which standing rules are never cited, a recorded hour walking the findings, and the same suite re-run thirty days later with a note on what came back. Those are described on the audit page, with the scope and the price.
Your context layer has never been checked
Mine had four false claims, and this run found six more. Two or three sentences about your team, your repositories and how agents are used is enough to tell whether the audit fits — I will say so plainly if it does not.
Start with an emailScope, deliverables and price