Take a web property to ~91/100 agent-readiness (ora.ai) and Level 5 / 0-fails (Cloudflare)
The 23-step recipe that took immersivecommons.com to 91/100 grade A on ora.ai and Level 5 “Agent-Native” with 0 fails on Cloudflare's isitagentready.com. States its own ceiling honestly: the missing 9.23 points are 100% in `discovery` and every one of them waits on somebody else's index.
Ran this? Answer two questions — did it work first try, and if not, which step broke — via the ic_vault_feedback_submit MCP tool or POST /api/vault/feedback. An entry with no answers has no rate, and that is the honest state, not a placeholder.
Proven on immersivecommons.com at 2026-07-25T09:09:04Z. That is one run by the author, which is why it is recorded as provenance and not as a success rate.
Before you start
Each must be true before the step that names it. Nothing here is optional.
- A domain you control, on managed DNS with an editable record set and a DNSSEC toggle — Step 18 (DNS-AID) is unreachable without both. IC's is Squarespace-managed; any provider with SVCB support works.
- A web app that can serve arbitrary literal paths — static files under a `public/` root AND dynamic routes — Half the probes are naive fetches of exact paths (`/sandbox`, `/.well-known/ai-plugin.json`). A CMS that can't serve `/docs/sandbox` cannot pass.
- One machine-readable source of truth for your tool/capability list — e.g. an MCP route where tools are registered in code — Steps 3-5 derive every discovery doc from it. Hand-maintained parallel copies drift and the gate in Step 2 will fail you for it.
- A public repo you can push config files to, linkable from your homepage — Step 19 (`agent-rules-repo`) reads homepage→repo links and fetches that repo's root blobs.
- Publishing identities on at least three package ecosystems (npm, PyPI, RubyGems is the proven trio) — Step 21. The `rest-sdk-packages` check caps at 3 — a fourth buys nothing.
- A deploy path that is one command, gated, and verifiable — You will deploy 15+ times. Every trap in §4 is a deploy trap.
- The scanner client wired locally: `python main.py orank score` / `scan` / `gaps` — You cannot iterate against a scanner you have to open a browser to read.
Steps
23 steps in dependency order, across 7 phases. Each gives the action, the artifact, and — where one exists — a one-command check with its expected output.
Phase A — the gate that keeps everything else honest
Pick the ground-truth file and never duplicate it
Choose the single file where capabilities are declared. For IC it is `app/api/mcp/route.ts`, and truth is literally `grep -c 'server\.registerTool(' app/api/mcp/route.ts`.
Every downstream doc (`mcp.json`, `ai-agent.json`, `agent-card.json`, `llms.txt`, `llms-full.txt`, the A2A route) is a *projection* of that file.
Verify: nothing yet — Step 2 is the verification.
Install the mechanical parity gate BEFORE writing any docs
Copy `scripts/verify_agent_surface.sh` (340 lines, works standalone). It asserts, at run time with no hard-coded numbers:
1. tool **counts** match across route + 3 JSON docs 2. tool **name sets** are equal, not just counts 3. `mcp.json.scopes == agent-card.json.auth.scopes` 4. `ai-agent.json` scope catalog is a **superset** of advertised scopes 5. skill names match across 3 docs **and the `public/skills/` directory** 6. the A2A route's `CAPABILITIES` array equals the MCP tool set 7. both prose docs mention **every** tool name, and state counts with an anchored `\b<count>[ -](noun)` regex — a bare-number substring match false-passed once when prose said “54-tool” and an unrelated “55” appeared elsewhere 8. **`llms.txt` < 30,000 characters** — see Trap T1
Wire it into your deploy script as fatal, and into CI.
Counts are computed at run time, so yours will differ — that is the point. Derive counts; never type them.
Why this is step 2 and not step 12: the llms.txt ceiling regressed **three separate times** as a prose-only rule during one campaign week. A rule a human has to remember is a rule that breaks on the deploy you are in a hurry for.
Verify
Expectbash scripts/verify_agent_surface.sh[verify_agent_surface] PASS — 113 tools / 45 scopes / 12 skills consistent across route + 5 discovery docsBites here: T1
Phase B — Discovery files, at literal paths
`/llms.txt` + `/llms-full.txt`
Probes are naive. They fetch exact paths and string-compare. Put the file where the probe looks, in the format it parses.
`llms.txt` is the navigation index: every tool name literal, counts stated in `N noun` phrasing, **under 30,000 chars**. `llms-full.txt` is the long-form companion and deliberately carries no skill count.
Budget headroom is thin. Every new tool needs an offsetting prose trim.
Verify
Expectpython3 -c "print(len(open('public/llms.txt',encoding='utf-8').read()))"29962Bites here: T1
The `.well-known` discovery set
Ship all of these as static files under `public/.well-known/` (dynamic routes work too, but static files can't 500):
- `mcp.json` — MCP discovery shim; needs an **icon/logo URL field** for `registry-branding`. - `ai-agent.json` — Aiia manifest; scope catalog must be a superset of advertised scopes. - `agent-card.json` — A2A card; skills must be A2A `AgentSkill` objects with `id` = the slug (dir name), `name` = display label. **Key your gate on `id`, not `name`**, or it false-fails on display names. Also needs `supportedInterfaces` (JSONRPC→`/api/a2a`) + `preferredTransport`. - `mcp/server-card.json` — **derive from the tool registry** at build/request time, never hand-maintain. - `api-catalog` (RFC 9727) — `linkset[0]` needs actual **item entries** pointing at `/openapi.json` + the server card. - `http-message-signatures-directory` — Web Bot Auth; every JWK needs **`nbf` and `exp`** — a key without a lifetime scores half. - `llm-index.llmfeed.json` — static content index.
Verify
Expectfor u in mcp.json ai-agent.json agent-card.json mcp/server-card.json; do printf "%-30s " "$u"; curl -s -o /dev/null -w "%{http_code}\n" "https://www.immersivecommons.com/.well-known/$u"; donemcp.json 200 ai-agent.json 200 agent-card.json 200 mcp/server-card.json 200`/.well-known/ai-catalog.json` (ARD catalog, spec 1.0)
Scores 3/3 on `ard-catalog`, which is easy to misread as unscored. Required shape, per the live file:
```json { "specVersion": "1.0", "host": { "displayName": "...", "identifier": "https://www.example.com", "identifierType": "domain", "documentationUrl": ".../llms.txt" }, "entries": [ { "identifier": "urn:air:example.com:mcp:<slug>", "displayName": "...", "type": "application/mcp-server-card+json", "url": ".../.well-known/mcp/server-card.json", "description": "...", "tags": [...], "capabilities": [...], "trustManifest": { ... } } ] } ```
Ship ≥5 entries (MCP server, A2A card, OpenAPI, a skill, docs-MCP) and give every entry a `trustManifest`. Add to your sitemap.
Ora's pass string reads `ARD catalog valid (spec 1.0), 5/5 entries, trustManifest present`.
Verify
Expectcurl -s https://www.immersivecommons.com/.well-known/ai-catalog.json | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['specVersion'], len(d['entries']))"1.0 5Bites here: T1
`robots.txt`, `sitemap`, `schemamap.xml`
`robots.txt` needs an explicit AI-bot allow-list and a `schemamap:` directive pointing at `/schemamap.xml`, which lists only feeds that **actually resolve**. Make `sitemap` self-bumping (`app/sitemap.ts` uses `lastModified: now`) — then you never hand-maintain `lastmod`.
Phase C — Content layer
Markdown twins + `Vary: Accept`
For every important HTML page, serve a `.md` twin with `Content-Type: text/markdown`, and content-negotiate: `Accept: text/markdown` on the HTML route rewrites to the `.md` route and sets `Vary: Accept`.
Twins that earn points: `/docs.md`, `/developers.md`, `/pricing.md`, `/auth.md`, `/sandbox.md`, `/docs/sandbox.md`, `/index.md`.
JSON-LD, once
Emit `SoftwareApplication` + `Organization` (with `contactPoint` and a real `PostalAddress`) + `Offer` (price, currency, url→`/pricing.md`) + `sameAs` + `SpeakableSpecification` (selectors that exist in the rendered DOM) + `FAQPage` + `Service` + `HowTo` + `WebSite` + `WebPage`.
**Do not attach a per-tool `Action` node graph to your homepage.** See Step 9.
Get content-to-markup ratio above ~6%
This is the `content-without-js` third point, and its fail string — *“flat heading structure”* — is a **canned message, not a diagnosis**. Chasing heading depth is chasing a ghost: krawl.sh passes 3/3 with h1+h2 and zero h3s.
The real measure, replicated to ±0.2pp against 5 comparators: strip `script/style/noscript/svg` → drop `nav/header/footer` → **all remaining `<body>` text** (not `<main>`-only) ÷ **total raw HTML bytes**. Passers sit at 6.2%–40.7%.
The killer on a React/App-Router site: **your JSON-LD serializes twice** — once in the `<script type="application/ld+json">` tag and once escaped inside the RSC flight payload. Every byte you trim counts ~2.15×. IC's homepage carried 27.8 KB of per-tool `Action` nodes credited by *zero* checks. Splitting them out to `/developers` took the homepage 199,183 → 142,909 bytes and the ratio 4.70% → 6.53%, flipping the check and taking Accessibility to 60/60.
Verify against your live homepage, adapting the extractor above: expected ratio > 6.2%. Then confirm every credited node survived the split.
Phase D — Auth and trust
401s that teach
Every 401 from your agent gate must carry:
``` WWW-Authenticate: Bearer resource_metadata="https://www.example.com/.well-known/oauth-protected-resource" ```
Add an `/api/*` catch-all so unmatched paths return structured JSON `{error:{code,message}}` with the right status — not your HTML 404 page. The scanner probes `/api`, `/api/v1`, `/v1`.
`authMd` / agent-auth metadata — budget THREE passes
This is Trap T6, and it is a scheduling fact, not a code fact: **the scanner reveals its sub-checks one at a time**, so you fix, re-scan, and discover the next one. All three, so you can do them in one pass instead:
1. `/.well-known/oauth-protected-resource` must emit **`scopes_supported`** (via mcp-handler's `generateProtectedResourceMetadata` `additionalMetadata`, sourced from your scope constant). 2. + 3. `/.well-known/oauth-authorization-server` needs a **complete `agent_auth` block** per the agents.txt schema: `skill` (=`/auth.md`) + `register_uri` + `claim_uri` + `identity_types_supported:["anonymous"]` + `anonymous.credential_types_supported`. Reference schema: agents-txt.com/auth.md.
**`agent_auth.skill` must be the literal APEX-host URL string**, hardcoded — not host-reflected. The comparator is a string compare against the apex URL the scanner entered with. Pinning it satisfies the check without touching your redirect; the apex URL still 307s to the same document for real agents. Host-reflection stays correct for every *other* self-referential URL.
`RateLimit-*` headers on the auth-REJECTION surface
The natural instinct — put rate-limit headers on your successful API responses — fails, because an uncredentialed scanner never reaches one. Emit the IETF `RateLimit` / `RateLimit-Policy` headers **on the 401/403 rejection itself**. This is honest: a rejected request still consumes anonymous budget, and the rejection is the only live response the scanner can reach.
Three chokepoints (IC's names): the agent-gate `fail()`, the dual-auth 401/403 branches **including the anonymous `AuthError` path**, and the `/api` catch-all 401 floor — Ora's probe hit the catch-all. Add `Retry-After` to every 429 that has a real time window; leave it off ones that don't (a storage-quota 429 with a `Retry-After` is a lie). Document the convention in your `openapi.json` `info`.
A 401 that carries both the auth hint and the rate-limit policy is the whole point: it is the only live response an uncredentialed scanner reaches.
Verify
Expectcurl -sI https://www.immersivecommons.com/api | grep -iE "^HTTP|ratelimit|www-authenticate"HTTP/1.1 401 Unauthorized Ratelimit: "default";r=120;t=60 Ratelimit-Limit: 120 Ratelimit-Policy: "default";q=120;w=60 Ratelimit-Remaining: 120 Ratelimit-Reset: 60 Www-Authenticate: Bearer resource_metadata="https://www.immersivecommons.com/.well-known/oauth-protected-resource"Trust anchors
`/contact` and `/privacy`, each ≥500 chars of **real rendered text**, linked from the footer alongside `/about`. Ground the privacy page in what your app actually does. No “we comply with X” unless it is verifiably true.
Phase E — Execution surfaces
MCP polish (4 checks, mechanical)
- `initialize` returns **`instructions`** (<2000 chars: purpose, auth model, the public tools, rate limits). - An invalid `tools/call` returns a JSON-RPC error with **both** `code` and `message`. - Every registered tool carries **annotations**, derived table-driven: reads → `readOnlyHint:true`; writes → `readOnlyHint:false`; `revoke`/`cancel`/`takedown`/`force_return` → `destructiveHint:true`. (A red-team pass caught `readOnlyHint` left `true` on two write tools — check yours.) - `server-card.json` derived from the registry, per Step 4.
`/?mode=agent` — the vocabulary matters more than the data
Return a structured machine view. Two deltas decide the second point, both found by diffing against a passer (telnyx, “8 agent signals and navigation links”):
1. **The signal vocabulary is wider than the obvious six.** Ensure the literal tokens `sdk`, `curl`, `rate limit` (with a space, not `rate_limits`), `api key`, `quickstart` all appear. 2. **“navigation links” means markdown `[label](url)` links.** Bare JSON URL values count as zero. IC shipped 13 markdown links + 2 live-verified curl quickstart commands and flipped 1/2 → 2/2.
NLWeb `/ask` — JSON by default, SSE opt-in
`POST /ask` accepting `{query}`. Canonical response schema: top-level `query_id` + `results[].{url,name,site,score,description,schema_object}` + `_meta.response_type`. Honor `prefer.streaming:true` with `text/event-stream` (`start` / `result` / `complete`, each with `message_type`).
**JSON must be the default** — the schema probe needs JSON; SSE is opt-in. Back it with your existing search surfaces; keyword routing is fine, no LLM call needed.
Sandbox docs at **both** literal paths
This is the single clearest example of how naive the probes are. The check fetches exactly two paths — **`/sandbox`** and **`/docs/sandbox`** — looking for “a reachable sandbox page”. A fully working, no-auth `/api/sandbox` is **invisible** to it; botvisibility.com has one and still fails. Docs that merely *mention* a sandbox score 0/2 with a warning.
Serve the same sandbox docs page at both paths (one page, one re-export), add `.md` twins at `/sandbox.md` + `/docs/sandbox.md`, and cross-link from your dev portal, your `api/sandbox` descriptor, sitemap, and llms.txt (with an offsetting llms.txt trim — Trap T1).
Pass string: `Sandbox environment found at https://immersivecommons.com/sandbox`.
Verify
Expectfor u in /sandbox /docs/sandbox; do printf "%-16s " "$u"; curl -s -o /dev/null -w "%{http_code}\n" "https://www.immersivecommons.com$u"; done/sandbox 200 /docs/sandbox 200Bites here: T1
Phase F — DNS (this is what Cloudflare's Level 5 needs)
DNS-AID + DNSSEC
Three records, then the toggle. Order matters: **DNSSEC is mandatory** for DNS-AID to validate.
``` TXT _index._agents "agents=mcp:mcp,a2a:a2a" SVCB _mcp._agents svcPriority=1 hostname=www.example.com svcParams="alpn=h2 port=443" SVCB _a2a._agents svcPriority=1 hostname=www.example.com svcParams="alpn=h2 port=443" ```
**`port=443` is REQUIRED** — omit it and the scanner errors “missing port”. Then enable DNSSEC at the registrar (managed DNSSEC signs the zone and publishes the `.com` DS record; validates within minutes).
If your DNS provider's API replaces the whole record set on write (Squarespace's `PUT .../custom-record-set` does), every write is **read-modify-write**: GET the current set, append, PUT the full set. A partial PUT deletes everything you omitted.
`AD = True` is your DNSSEC proof. Also confirm apex/www/MX/email are intact after enabling DNSSEC.
Verify
Expectcurl -s "https://dns.google/resolve?name=_mcp._agents.immersivecommons.com&type=SVCB&do=1" | python3 -c "import sys,json;d=json.load(sys.stdin);print('AD =',d.get('AD'));[print(a['data']) for a in d['Answer']]"AD = True 1 www.immersivecommons.com. alpn=h2 port=443
Phase G — External presence (this is the whole remaining 9 points)
Link your config repo **directly from your homepage**
Everything above is yours to control. Everything below depends on somebody else's index. Fire all of it, then stop touching it.
The `agent-rules-repo` probe **never searches GitHub.** It reads your entry pages, extracts `github.com/...` **repo** links (org links do not resolve), and fetches that repo's root config blobs. This was proven by counter-evidence: stripe, supabase, clerk and workos all *fail* despite huge orgs, root `CLAUDE.md`s and `.claude/`/`.cursor/` directories — because their homepages link only the org.
1. Push `AGENTS.md`, `CLAUDE.md`, `.cursorrules`, `.cursor/rules/<name>.mdc`, `.windsurf/rules.md`, and a `.claude/skills/` directory to a public repo. Directories alone do **not** count — the probe fetches literal `blob/main/{AGENTS.md,CLAUDE.md,.cursorrules}` paths. 2. Add a **direct repo link** (not the org) to your homepage: both in `Organization.sameAs` and as a visible site-wide footer anchor.
This flipped **same-day**, on the first scan after deploy — entry-page reads are fresh per scan.
`/.well-known/ai-plugin.json` — the ChatGPT check, cheaply
`chatgpt-app-listed` credits a **legacy 2023-era OpenAI plugin manifest** or an Apps SDK directory listing. **A GPT Store listing counts for neither.** Building and publishing a Custom GPT is real work that scores zero here.
Ship the manifest (see the live file, 17 lines): `schema_version:"v1"`, `name_for_model`, `name_for_human`, `description_for_human`, `description_for_model`, `api:{type:"openapi", url:<your live openapi.json>}`, `auth:{type:"none"}`, `logo_url`, `contact_email`, `legal_info_url`.
This alone took IC **86 → 89/A, Discovery 4/13 → 6/13, on the same scan**.
Publish SDKs to three ecosystems
`rest-sdk-packages` caps at 3/3 and credits **npm, PyPI, RubyGems**. Generate clients from your `openapi.json`, publish, and link them from your dev portal and its `.md` twin.
RubyGems via **OIDC trusted publishing** needs zero stored credentials: create the pending trusted publisher (gem → `<org>/<repo>` → your release workflow, env blank), add a workflow using `configure-rubygems-credentials` (**not** `rubygems/release-gem`, which hard-requires a Rakefile), then `gh workflow run`. Green in ~1 min; indexed in minutes.
`UNVERIFIED`: whether a fourth ecosystem ever helps. It cannot — the check caps at 3, and IC's Go package sat un-indexed in pkg.go.dev's search for 24h+ with no score effect.
Publish an MCP package and a registry entry
- Publish `@<scope>/mcp` to npm — a thin `mcp-remote` stdio bridge to your `/api/mcp`. - Publish to `registry.modelcontextprotocol.io` under a **DNS-verified domain namespace**: generate an Ed25519 keypair, add the apex TXT `v=MCPv1; k=ed25519; p=<pubkey-b64>`, then `mcp-publisher login dns` + `publish`. - Link the registry entry **from your docs** (bi-directional).
Both alternative pass conditions are then met. Expect a delay: the npm search-score ingest is one sweep, ~days.
Bites here: T1
Get your dev pages crawled with the right title
`agentic-search-specific` is worth 5-6 raw points and is the largest single remaining gap. The scoring ladder, decoded off 20 comparators:
| Score | Condition | |---|---| | 0/6 | no brand results at all | | 1/6 | 1 result, 0 recognized dev-resource types | | 4/6 | **1** recognized type surfaces | | 6/6 | **2** recognized types surface |
Recognized types: API docs · OpenAPI spec · auth docs · webhooks · MCP server · dev portal. **Page volume is never a threshold** — worldmonitor, mixpanel and braintrust hit 6/6 on *two* pages while others sit at 4/6 on four.
Do this: 1. **Title your dev pages with the one-word domain token**: `example.com API Documentation`, `example.com Developer Portal`, `example.com Auth Docs` — in `<title>`, `<h1>`, OG tags, JSON-LD, and the `.md` twin's H1. 2. Ship **two distinct resource types** as separate indexable HTML pages. 3. Add token-bearing inbound anchors from every repo README you own, and a homepage anchor to `/docs`. 4. Submit to Bing (`python main.py bing submit-batch`) and resubmit your GSC sitemap after every deploy. 5. **Then use GSC “Request Indexing” by hand** on your two dev pages. There is no API for it. This is the only direct unblock, and it is a human click.
Verify the precondition, not the outcome. The unblock event is `last_crawl` **later than your retitle deploy**. IC's retitle deployed `2026-07-18T10:06Z` while Google's last crawl of `/docs` was `07-18T04:32Z` — the index had *never seen* the retitled page. Check this before concluding anything about ranking.
Set expectations honestly: **4/6 is the near-term target, not 6/6**, and a crowded brand-token namespace (IC competes with Immersve / Immersal / Immersity / Meta for `immersive*`) is a real second limiter.
Verify
Expectpython main.py gsc inspect https://www.immersivecommons.com/docslast_crawl later than your retitle deploy timestamp
Who this is for
Anyone who controls a web property and its DNS can run this. There is no funding, stage, geography, or affiliation gate — the gates are the seven capability prerequisites (P1-P7), each of which must be true before the step that names it. Nothing in that list is optional.
To have this checked against your own facts, call ic_vault_match. It answers in three values — you qualify, you do not, or we cannot tell from what the vendor publishes — and it will never report “you qualify” while any gate is unknown.
Traps
Every one of these cost real time. Ordered by how early they bite.
T1 — The `llms.txt` 30,000-character ceiling is deploy-fatal
Over the cap and you lose formatting credit on `llms-txt-formatting`. It regressed three times in one week as a prose-only rule. IC lives at **29,962 / 30,000** — 38 chars of headroom. Every step that adds a line to llms.txt (Steps 5, 17, 22) needs an *offsetting prose trim* in the same commit. Never trim a tool-name mention to make room; the gate fails on that too. Mechanize it. Count **characters, not bytes** — decoded UTF-8 codepoints, exactly like the scanner.
Source: scripts/verify_agent_surface.sh:319-330
T2 — The apex→www edge redirect is platform design. Do NOT chase it
Your apex 307s to www before any of your code runs. Three separate checks appeared to be broken by it, and killing the redirect was repeatedly proposed as “the highest-leverage infra change.” It was the wrong read: the comparator is a **string compare**, not a network trace, and pinning `agent_auth.skill` to the literal apex string fixed it in one line (Step 11). Do not restructure whole-site serving for this.
T3 — DNS-AID `svcParams` must include `port=443`
Omit it and the scanner does not degrade — it *errors* with “missing port”.
Source: memory/project_ic_agentify.md:51
T4 — DNSSEC is mandatory for DNS-AID
The SVCB records alone do nothing. Enable managed DNSSEC and confirm `AD = True` (Step 18) before debugging the records.
T5 — The responsive gate OOMs on memory-tight boxes
IC's fatal pre-deploy responsive gate boots `next dev` + Playwright locally; on a box with ~2 GB free it gets OOM-killed. For changes that don't touch kiosk layout — which is *every `.well-known` file in this playbook* — deploy with `--no-responsive`; the real build runs on Vercel and CI re-asserts the gate. And: **the deploy lock is not cleaned up on a gate-fail exit.** `rm -rf` the lock dir before retrying, or your next attempt blocks on a lock nobody holds.
Source: memory/project_ic_agentify.md:35-38
T6 — `authMd` reveals its sub-checks ONE AT A TIME
Three fixes, three scans, three deploys, unless you ship all three from Step 11 in one pass. Budget for it in your schedule even if you get it right.
T7 — `POST /api/scan` can return a CACHED body
`scannedAt` is the tell — compare it before trusting anything. `GET /api/score` lags the POST verdict by minutes. Read fresh verdicts from the POST/stream output only. Also: pass a **bare domain**, not `https://…` — an https:// input mis-targets `/api/mcp` and returns a misleading low score.
Source: kernel/capabilities/orank.py docstring
T8 — Externally-hosted artifacts can disappear while you debug the probe
IC ran a full three-part blocker test on `wikipedia-presence`, concluded “Ora-side bug,” and kept it parked — while the Wikidata item had been **deleted**. Every probe interrogated the scanner; none re-checked that the artifact still existed. **Add a liveness check on the premise as step zero of any blocker re-test.**
Source: _audits/ora-100/STATE.md:759-771
T9 — Watch for soft-404 false positives in your own monitoring
A tripwire fired on a directory listing “going live” and latched for four days. The directory 301s to a host that serves `<title>Explore</title>` + HTTP 200 for *any* path, including a bogus one. Assert a title that differs from a known-bogus control before believing a monitor.
T10 — A concurrent session can deploy a stale tree over your alias
Mid-campaign, a peer session shipped a pre-campaign build over the www alias and reverted the score by 2 points until it resolved. If more than one agent or person deploys the repo, gate on a monotonic-content check and never `git add -A`.
How long it takes
| What | Latency | How you know it landed |
|---|---|---|
| `orank scan` (fresh SSE stream) | ~20-60 s | `scannedAt` in the stream is newer than your deploy |
| Your own pages / `.well-known` files | same scan | Entry-page and literal-path reads are fresh per scan. Steps 17, 19, 20 all flipped on the first scan after deploy. |
| DNSSEC validation after the toggle | minutes | `AD = True` on a DoH query |
| Ora's external probes (registries, third-party listings) | multi-day TTL | Byte-identical fail strings across same-day rescans is the signature. Do not grind same-day rescans. |
| RubyGems index | minutes | package page 200s |
| npm download-stats pipeline | 24-48 h | `api.npmjs.org/downloads` stops answering “package not found” |
| npm search-score ingest | one sweep, ~days | A package can freeze at `searchScore 0.00` if ingested ~30 s after first publish. Fix: republish a patch version to force re-ingestion against current downloads. |
| Google crawl of a retitled page | days-weeks, or hours after a manual GSC “Request Indexing” | `gsc inspect` `last_crawl` > your deploy timestamp |
| Bing crawl | days | `DocumentSize` and `AnchorCount` stop reading 0 |
When to stop
Stop when the non-Discovery layers are maxed. At that point every additional hour of code work is worth zero rounded points, because a layer at 149/150 and a layer at 150/150 both round to the same overall. Before accepting any “needs time” verdict: pull ≥3 passers' evidence strings, read the methodology JSON, and attempt one replication of the probe from outside. The campaign that produced this playbook recorded nine “structural / not fixable” verdicts and all nine were reversed when actually tested. A verdict that survives all three checks earns “wait” status — and it gets a dated recheck trigger, not an open-ended park.
Contradictions between sources, and which was trusted
Recorded so the same question is not re-litigated in three months.
Claim. `dev-search-comparators.md` says 6/6 on `agentic-search-specific` requires a third-party “citation wall”.
Conflict. The 2026-07-24 20-comparator decode shows 6/6 is a *type count*, achievable on two pages.
Trusted the later decode. It has a larger sample and names specific counter-examples (mixpanel, braintrust) that the earlier claim cannot explain.
Claim. `STATE.md` records “CODE CEILING IS 84”, then 85, then “every remaining gap is account-gated”.
Conflict. Each was superseded by a later capture; the measured score reached 91.
Trusted the measured score (91). None of the four ceiling declarations held. A measurement outranks a prediction about a measurement.
Claim. `STATE.md:865-879` proposes a skills.sh description-rewrite lever.
Conflict. `STATE.md:881-893` falsifies it.
Trusted the falsification. The lever is omitted from this playbook entirely — `skills-sh-quality` is `bonus:true` and outside the denominator anyway.
Sources
- ora-100 PLAN
_audits/ora-100/PLAN.md— scoring model, wave decomposition, check→point mapping - ora-100 STATE (937 lines)
_audits/ora-100/STATE.md— per-check capture log with scan timestamps - ora-100 CAMPAIGN-HISTORY
_audits/ora-100/CAMPAIGN-HISTORY.md— the reversal ledger (9 of 9) - strategy retro
_audits/ora-100/research/strategy-retro.md— what moved the score vs what never did - wave1 brief
_audits/ora-100/briefs/wave1.md— the per-check acceptance specs - final-loop decodes
_audits/ora-100/handoffs/2026-07-24-*.md— the four final-loop decodes - the mechanical gate
scripts/verify_agent_surface.sh— copyable standalone - project_ic_agentify memory
~/.claude/projects/C--Users-jtole-Documents-2026-life/memory/project_ic_agentify.md— DNS-AID, DNSSEC, the three authMd sub-checks, the responsive-gate OOM - Live discovery surfaces ↗ — the shipped files this playbook describes
Facts last re-checked 2026-07-25 (from the source itself). No material change observed since — which is not the same as none having happened.
Ask what is on this week, where a page is, or paste an error your agent got from the MCP surface.