feat(agent-readiness): close the agent-journey gaps — pricing/signup/support discovery + status advertisement fixes (#4850 #4854 #4856 #4857)#4867
Conversation
…support discovery + status advertisement fixes Five external agent-journey runs (signup, pricing, developer-auth, support, integration intents) showed a clean split: intents answerable via the Link-header → .well-known → auth.md chain scored 90-100%, while pricing, signup, and support — discoverable only via llms.txt or nowhere — forced agents into slug-guessing, UA 403s, and misleading-200 SPA shells. Closes #4850 (signup journey): - llms.txt/llms-full.txt + auth.md: document the descriptive User-Agent policy (edge WAF challenges curl/*, python-requests/*, short UAs — and masks 404s as 403s for those UAs) - docs/accounts.mdx (+ nav): agent-facing account-creation guide — free tier needs no account; Clerk modal flow; MCP OAuth path for agents - vercel.json: 301 /blog/posts → /blog (posts live at /blog/posts/<slug>/, the parent had no index) Closes #4854 (pricing journey): - .well-known/api-catalog: RFC 9727 service-meta advertising pricing.md, the live product-catalog JSON, and support.md on the api context object - middleware.ts: /api/product-catalog joins PUBLIC_API_PATHS — the keyless read-only pricing catalog was UA-403'd for the exact agents it serves; robots.txt gets the matching Allow carve-out (llms.txt precedent) - docs/pricing.mdx (+ nav) with a drift guard test extracting prices from convex/config/productCatalog.ts source text (no generator import) - docs/openapi/CommerceService.openapi.yaml wired into the api-reference nav — placed OUTSIDE docs/api/ (that directory is generated: proto-check regen diff + injector scripts would clobber a hand-authored spec); kept out of the root openapi.json to respect the #4853 ~1MB size budget - docs.json redirects for the guessed slugs: plans/tiers → pricing, rate-limit(s) → usage-rate-limits, mcp-api-key → usage-auth - pricing.md: Limits & Overage section (hard 429 + Retry-After, no silent overage billing) + live JSON catalog pointer + UA note Closes #4856 (status advertisement, stale since #4715): - api-catalog status href + 3x vercel.json Link rel="status" now point at the keyless /api/health?compact=1 form - /api/health 401 carries WWW-Authenticate with RFC 9728 resource_metadata (RFC 7235 §3.1 made it mandatory; it was absent) and a body hint naming the public compact form - deploy-config guards: every advertised status URL must be the keyless compact form; service-meta entries pinned Closes #4857 (support journey): - public/support.md (root, sitemap-listed) + docs/support.mdx (+ nav): consolidated channels (support@/enterprise@, GitHub issues, Discord, status page, Turnstile form marked human-only) + explicit no-SLA statement per tier - vercel.json: /contact, /support, /help 301 → /docs/support (they served the misleading-200 dashboard SPA shell via the catch-all rewrite) - server-card.json documentation block: support repointed to /docs/support (was getting-started), pricing + productCatalog entries added Out of scope, noted: the Cloudflare-side WAF UA rule (dashboard config, not repo) and the root openapi info.description (generated + size-budgeted). Claude-Session: https://claude.ai/code/session_01N3xTXNUrCiy1xqy3VbkMRx
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR closes four agent-journey readiness gaps in one coordinated batch — pricing/signup/support discovery and status URL advertisement fixes. The changes span agent-facing metadata files (
Confidence Score: 4/5Safe to merge — the core runtime changes (middleware bypass, health 401 header) are narrow, well-tested, and guarded by new regression tests; the only issue is a latent false-failure risk in the new drift test for future prices above $999. The pricing drift test's usd() helper strips commas before checking content.includes(dollars), so a plan priced at $1,000+ would produce token $1500.00 while docs would naturally contain $1,500.00, causing a false test failure when the price is actually correct. No current plan is affected (max is $399.99/year), but the pattern is worth hardening before a price change crosses that threshold. Everything else — middleware bypass, health WWW-Authenticate, status link corrections, slug redirects — is well-structured and backed by tests. tests/pricing-docs-drift.test.mjs — the usd() comma-stripping logic; everything else looks solid. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Agent arrives at worldmonitor.app] --> B{Reads Link header}
B --> C[api-catalog well-known]
C --> D[service-desc: openapi.yaml]
C --> E[service-meta: pricing.md + product-catalog + support.md]
C --> F[status: api-health compact=1]
C --> G[oauth-protected-resource]
F --> F1{Has API key?}
F1 -->|compact=1 keyless| F2[200 OK public status]
F1 -->|bare URL keyless| F3[401 + WWW-Authenticate + compact hint]
E --> H[GET api-product-catalog]
H --> I{UA gate check}
I -->|bot UA PUBLIC_API_PATHS bypass| J[200 JSON pricing catalog]
I -->|unlisted path| K[403 Forbidden]
G --> L[OAuth 2.1 PKCE flow]
L --> M[Agent credentials minted]
subgraph slugs [Slug Redirects]
N[contact, support, help slugs] --> O[docs-support page]
P[plans, tiers slugs] --> Q[pricing page]
R[rate-limits slug] --> S[usage-rate-limits page]
end
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A[Agent arrives at worldmonitor.app] --> B{Reads Link header}
B --> C[api-catalog well-known]
C --> D[service-desc: openapi.yaml]
C --> E[service-meta: pricing.md + product-catalog + support.md]
C --> F[status: api-health compact=1]
C --> G[oauth-protected-resource]
F --> F1{Has API key?}
F1 -->|compact=1 keyless| F2[200 OK public status]
F1 -->|bare URL keyless| F3[401 + WWW-Authenticate + compact hint]
E --> H[GET api-product-catalog]
H --> I{UA gate check}
I -->|bot UA PUBLIC_API_PATHS bypass| J[200 JSON pricing catalog]
I -->|unlisted path| K[403 Forbidden]
G --> L[OAuth 2.1 PKCE flow]
L --> M[Agent credentials minted]
subgraph slugs [Slug Redirects]
N[contact, support, help slugs] --> O[docs-support page]
P[plans, tiers slugs] --> Q[pricing page]
R[rate-limits slug] --> S[usage-rate-limits page]
end
Reviews (1): Last reviewed commit: "feat(agent-readiness): close the agent-j..." | Re-trigger Greptile |
| const usd = (cents) => | ||
| (cents / 100).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).replace(/,/g, ''); |
There was a problem hiding this comment.
The
usd() helper strips commas via .replace(/,/g, ''), so a price of 150000 cents produces the search token $1500.00. If the docs file contains $1,500.00 (comma-formatted, which is the natural human-readable form), content.includes('$1500.00') returns false and the test fails even though the price is correct. All current plan prices are below $1,000 so no active failure, but any future plan priced at or above $1,000 would trigger this. Consider also checking the comma-formatted variant so the test stays green regardless of how the docs author wrote the number.
| const usd = (cents) => | |
| (cents / 100).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).replace(/,/g, ''); | |
| const usd = (cents) => | |
| (cents / 100).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); | |
| // Returns the set of dollar strings to search for in doc content: both with | |
| // and without comma grouping, so $1,500.00 and $1500.00 both match. | |
| const usdVariants = (cents) => { | |
| const formatted = usd(cents); | |
| return [formatted, formatted.replace(/,/g, '')]; | |
| }; |
… 401 and product-catalog, pin advertised markdown, cover the API annual plan (#4874) Review of merged #4867 confirmed 5 of 6 findings (the sixth was half-right: the Commerce spec IS fetchable via the Mintlify proxy — it just wasn't linked from any descriptor). - product-catalog partial-Dodo branch stamped X-Product-Catalog-Source: dodo while the body said priceSource: "partial" — header now carries the same source as the body. Spec: header enum gains partial, body priceSource documented, tier price nullable (Enterprise returns price: null). - /api/health 401 advertised a Bearer/OAuth challenge, but validateApiKey only reads X-WorldMonitor-Key / X-Api-Key headers and tester cookies — agents were pointed at a flow that cannot succeed. Challenge is now ApiKey realm="worldmonitor-health", header="X-WorldMonitor-Key"; test asserts Bearer is absent. - api-catalog service-meta links the Commerce OpenAPI spec URL (it lives outside the root bundle for the #4853 size budget; without the link no advertised descriptor reached it). - pricing.md + support.md pinned like auth.md: SPA catch-all exclusions (both regex copies), explicit text/markdown + CORS header rules, and deploy-config guards incl. file-existence (deleting the static file must 404, not fall through to the dashboard-HTML misleading-200). - pricing.md prose + JSON summary and docs/pricing.mdx gain the active API annual plan ($999/year, currentForCheckout in productCatalog.ts, live in /api/product-catalog — previously omitted everywhere). - drift test hardened: numeric comparison of pricing.md's machine-readable JSON block (stale JSON now fails even when prose was updated), comma-tolerant prose matching, api_starter_annual covered. Claude-Session: https://claude.ai/code/session_01N3xTXNUrCiy1xqy3VbkMRx
What
One coordinated batch closing all four agent-journey readiness issues — they share
llms.txt(×3),vercel.json(×3), and the well-known files (×2), so separate PRs would conflict on every shared surface.Closes #4850 · Closes #4854 · Closes #4856 · Closes #4857
Why
Five external agent-journey runs (Claude Code · Sonnet 4.6) probed the site with real intents. The split was exact:
Intents answerable via the Link-header →
.well-known→auth.md/OpenAPI chain cost ~5–10× less. Pricing/signup/support lived only inllms.txt(or nowhere) — agents on the well-known chain guessed slugs, hit UA-403s, and parsed misleading-200 SPA shells.Changes by issue
#4850 — signup
llms.txt,llms-full.txt,auth.md: descriptive User-Agent required; the edge WAF challengescurl/*/python-requests/*/short UAs and masks 404s as 403s for them.docs/accounts.mdx: free tier needs no account; Clerk modal flow (not fetchable — agents are told so); MCP OAuth path for agent credentials./blog/posts→/blog301 (posts live at/blog/posts/<slug>/; the parent had no index).#4854 — pricing
.well-known/api-catalog: RFC 9727service-metaon the API context advertisingpricing.md, the liveGET /api/product-catalogJSON, andsupport.md.middleware.ts:/api/product-catalogadded toPUBLIC_API_PATHS(was UA-403'd for the exact agents it exists to serve) +robots.txtAllow carve-out, mirroring the/api/llms.txtprecedent. DELETE stays behind the endpoint's own auth — the bypass only skips UA filtering.docs/pricing.mdx+tests/pricing-docs-drift.test.mjs: prices asserted againstconvex/config/productCatalog.tssource text (no generator import), so a catalog price change names every stale doc.docs/openapi/CommerceService.openapi.yamlwired into the api-reference nav. Deliberately outsidedocs/api/(fully generated dir — proto-check regen diff + injector scripts) and outside the root spec (fix(agent-readiness): $ref-dedupe error responses in /openapi.json — back under orank's ~1MB function-calling cap #4853 ~1MB size budget).docs.jsonredirects for every slug the journey agent guessed:plans/tiers→pricing,rate-limit(s)→usage-rate-limits,mcp-api-key→usage-auth.pricing.md: Limits & Overage section (hard 429 +Retry-After, no silent overage billing — worded to survive feat(api): add Dodo overage event contract #4791), JSON catalog pointer, UA note.#4856 — status advertisement (stale since #4715)
statushref + 3×vercel.jsonLink rel="status"→/api/health?compact=1(keyless 200; the bare form 401s since fix(security): require auth for detailed health metadata #4715)./api/health401 now carriesWWW-Authenticatewith RFC 9728resource_metadata(RFC 7235 §3.1 — it was absent entirely) + a body hint naming the compact form.#4857 — support
public/support.md(root, sitemap-listed) +docs/support.mdx: all channels consolidated (support@ / enterprise@, GitHub issues, Discord, status page; Turnstile form marked human-only) + explicit per-tier no-SLA statement./contact,/support,/help301 →/docs/support(all three served the misleading-200 dashboard SPA shell).server-card.jsondocumentation block:supportrepointed (was getting-started),pricing+productCatalogadded.Out of scope (deliberate)
openapi.yamlinfo.descriptionpricing link — the bundle is sebuf-generated and size-budgeted (fix(agent-readiness): $ref-dedupe error responses in /openapi.json — back under orank's ~1MB function-calling cap #4853); api-catalogservice-metacarries the same pointer instead.Verification
tests/middleware-bot-gate.test.mts53 pass (new product-catalog bypass block + exact-path negative)tests/deploy-config.test.mjs107 pass (status-compact + Link-header + service-meta guards)tests/health-redis-down-status.test.mjs8 pass (WWW-Authenticate + hint contract)tests/pricing-docs-drift.test.mjs7 pass (new)docs:checkgreen,tsc --noEmitgreen, all edited JSON/YAML parsehttps://claude.ai/code/session_01N3xTXNUrCiy1xqy3VbkMRx