Commit Graph

2 Commits

Author SHA1 Message Date
Elie Habib
d1a4cf7780 docs(mintlify): add Route Explorer + Scenario Engine workflow pages (#3211)
* docs(mintlify): add Route Explorer + Scenario Engine workflow pages

Checkpoint for review on the IA refresh (per plan
docs/plans/2026-04-19-001-feat-docs-user-facing-ia-refresh-plan.md).

- docs/docs.json: link Country Resilience Index methodology under
  Intelligence & Analysis so the flagship 222-country feature is
  reachable from the main nav (previously orphaned). Add a new
  Workflows group containing route-explorer and scenario-engine.
- docs/route-explorer.mdx: standalone workflow page. Who it is for,
  Cmd+K entry, four tabs (Current / Alternatives / Land / Impact),
  inputs, keyboard bindings, map-state integration, PRO gating
  with free-tier blur + public-route highlight, data sources.
- docs/scenario-engine.mdx: standalone workflow page. Template
  categories (conflict / weather / sanctions / tariff_shock /
  infrastructure / pandemic), how a scenario activates on the map,
  PRO gating, pointers to the async job API.

Deferred to follow-up commits in the same PR:
  - documentation.mdx landing rewrite
  - features.mdx refresh
  - maritime-intelligence.mdx link-out to Route Explorer
  - Panels nav group (waits for PR 2 content)

All content grounded in live source files cited inline.

* docs(mintlify): fix Route Explorer + Scenario Engine review findings

Reviewer caught 4 cases where I described behavior I hadn't read
carefully. All fixes cross-checked against source.

- route-explorer (free-tier): the workflow does NOT blur a numeric
  payload behind a public demo route. On free tier, fetchLane()
  short-circuits to renderFreeGate() which blurs the left rail,
  replaces the tab area with an Upgrade-to-PRO card, and applies a
  generic public-route highlight on the map. No lane data is
  rendered in any tab. See src/components/RouteExplorer/
  RouteExplorer.ts:212 + :342.
- route-explorer (keyboard): Tab / Shift+Tab moves focus between the
  panel and the map. Direct field jumps are F (From), T (To), P
  (Product/HS2), not Tab-cycling. Also added the full KeyboardHelp
  binding list (S swap, ↑/↓ list nav, Enter commit, Cmd+, copy URL,
  Esc close, ? help, 1-4 tabs). See src/components/RouteExplorer/
  KeyboardHelp.ts:9 and RouteExplorer.ts:623.
- scenario-engine: the SCENARIO_TEMPLATES array only ships templates
  of 4 types today (conflict, weather, sanctions, tariff_shock).
  The ScenarioType union includes infrastructure and pandemic but
  no templates of those types ship. Dropped them from the shipped
  table and noted the type union leaves room for future additions.
- scenario-engine + api-scenarios: the worker writes
  status: 'done' (not 'completed') on success, 'failed' on error;
  pending is synthesised by the status endpoint when no worker
  record exists. Fixed both the new workflow page and the merged
  api-scenarios.mdx completed-response example + polling language.
  See scripts/scenario-worker.mjs:421 and
  src/components/SupplyChainPanel.ts:870.

* docs(mintlify): fix third-round review findings (real IDs + 4-state lifecycle)

- api-scenarios (template example): replaced invented
  hormuz-closure-30d / ["hormuz"] with the actually-shipped
  hormuz-tanker-blockade / ["hormuz_strait"] from scenario-
  templates.ts:80. Listed the other 5 shipped template IDs so
  scripted users aren't dependent on a single example.
- api-scenarios (status lifecycle): worker writes FOUR states,
  not three. Added the intermediate "processing" state with
  startedAt, written by the worker at job pickup (scenario-
  worker.mjs:411). Lifecycle now: pending → processing →
  done|failed. Both pending and processing are non-terminal.
- scenario-engine (scripted use blurb): mirror the 4-state
  language and link into the lifecycle table.
- scenario-engine (UI dismiss): replaced "Click Deactivate"
  with the actual × dismiss control on the scenario banner
  (aria-label: "Dismiss scenario") per
  src/components/SupplyChainPanel.ts:790. Also described the
  banner contents (name, chokepoints, countries, tagline).
- api-shipping-v2: while fixing chokepoint IDs, also corrected
  "hormuz" → "hormuz_strait" and "bab-el-mandeb" → "bab_el_mandeb"
  across all four occurrences in the shipping v2 page (from
  PR #3209). Real IDs come from server/_shared/chokepoint-
  registry.ts (snake_case, not kebab-case, not bare "hormuz").

* docs(mintlify): fix fourth-round findings (banner DOM, webhook TTL refresh)

- scenario-engine: accurate description of the rendered scenario
  banner. Always-present elements are the ⚠ icon, scenario name,
  top-5 impacted countries with impact %, and dismiss ×. Params
  chip (e.g. '14d · +110% cost') and 'Simulating …' tagline are
  conditional on the worker result carrying template parameters
  (durationDays, disruptionPct, costShockMultiplier). The banner
  never lists affected chokepoints by name — the map and the
  chokepoint cards surface those. Per renderScenarioBanner at
  src/components/SupplyChainPanel.ts:750.
- api-shipping-v2 (webhook TTL): register extends both the record
  and the owner-index set's 30-day TTL via atomic pipeline
  (SET + SADD + EXPIRE). rotate-secret and reactivate only
  extend the record's TTL — neither touches the owner-index set,
  so the owner index can expire independently if a caller only
  rotates/reactivates within a 30-day window. Re-register to keep
  both alive. Per api/v2/shipping/webhooks.ts:230 (register
  pipeline) and :325 (rotate setCachedJson on record only).

* docs(mintlify): fix PRO auth contract (trusted origin ≠ PRO)

- api-scenarios: 'X-WorldMonitor-Key (or trusted browser origin)
  + PRO' was wrong — isCallerPremium() explicitly skips
  trusted-origin short-circuits (keyCheck.required === false) and
  only counts (a) an env-valid or user-owned wm_-prefixed API key
  with apiAccess entitlement, or (b) a Clerk bearer with role=pro
  or Dodo tier ≥ 1. Browser calls work because premiumFetch()
  injects one of those credentials per request, not because Origin
  alone authenticates. Per server/_shared/premium-check.ts:34 and
  src/services/premium-fetch.ts:66.
- usage-auth: strengthened the 'Entitlement / tier gating' section
  to state outright that authentication and PRO entitlement are
  orthogonal, and that trusted Origin is NOT accepted as PRO even
  though it is accepted for public endpoints. Listed the two real
  credential forms that pass the gate.

* docs(mintlify): fix stale line cite (MapContainer.activateScenario at :1010)

Greptile review P2: prose cited MapContainer.ts:1004 but activateScenario
is declared at :1010. Line 1004 landed inside the JSDoc block.

* docs(mintlify): finish PR 1 — landing rewrite, features refresh, maritime link-out

Completes the PR 1 items from docs/plans/2026-04-19-001-feat-docs-user-
facing-ia-refresh-plan.md that were deferred after the checkpoint on
Route Explorer + Scenario Engine + CRI nav. No new pages — only edits
to existing pages to point at and cohere with the new workflow pages.

- documentation.mdx: landing rewrite. Dropped brittle counts (344
  news sources, 49 layers, 24 CII countries, 31+ sources, 24 typed
  services) in favor of durable product framing. Surfaced the
  shipped differentiators that were invisible on the landing
  previously: Country Resilience Index (222 countries, linked to
  its methodology page), AI daily brief, Route Explorer,
  Scenario Engine, MCP server. Kept CII and CRI as two distinct
  country-risk surfaces — do not conflate.
- features.mdx: replaced the 'all 55 panels' Cmd+K claim and the
  stale inventory list with family-grouped descriptions that
  include the panels this audit surfaced as missing (disease-
  outbreaks, radiation-watch, thermal-escalation, consumer-prices,
  latest-brief, forecast, country-resilience). Added a Workflows
  section linking to Route Explorer and Scenario Engine, and a
  Country-level risk section linking CII + CRI. Untouched
  sections (map, marker clustering, data layers, export, monitors,
  activity tracking) left as-is.
- maritime-intelligence.mdx: collapsed the embedded Route Explorer
  subsection to a one-paragraph pointer at /route-explorer so the
  standalone page is the canonical home.

Panels nav group remains intentionally unadded; it waits on PR 2
content to avoid rendering an empty group in Mintlify.
2026-04-19 18:39:36 +04:00
Elie Habib
e4c95ad9be docs(mintlify): cover MCP, OAuth, non-RPC endpoints, and usage (#3209)
* docs(mintlify): cover MCP, OAuth, non-RPC endpoints, and usage

Audit against api/ + proto/ revealed 9 OpenAPI specs missing from nav,
the scenario/v1 service undocumented, and MCP (32 tools + OAuth 2.1 flow)
with no user-facing docs. The stale Docs_To_Review/API_REFERENCE.md still
pointed at pre-migration endpoints that no longer exist.

- Wire 9 orphaned specs into docs.json: ConsumerPrices, Forecast, Health,
  Imagery, Radiation, Resilience, Sanctions, Thermal, Webcam
- Hand-write ScenarioService.openapi.yaml (3 RPCs) until it's proto-backed
  (tracked in issue #3207)
- New MCP page with tool catalog + client setup (Claude Desktop/web, Cursor)
- New MDX for OAuth, Platform, Brief, Commerce, Notifications, Shipping v2,
  Proxies
- New Usage group: quickstart, auth matrix, rate limits, errors
- Remove docs/Docs_To_Review/API_REFERENCE.md and EXTERNAL_APIS.md
  (referenced dead endpoints); add README flagging dir as archival

* docs(mintlify): move scenario docs out of generated docs/api/ tree

The pre-push hook enforces that docs/api/ is proto-generated only.
Replace the hand-written ScenarioService.openapi.yaml with a plain
MDX page (docs/api-scenarios.mdx) until the proto migration lands
(tracked in issue #3207).

* docs(mintlify): fix factual errors flagged in PR review

Reviewer caught 5 endpoints where I speculated on shape/method/limits
instead of reading the code. All fixes cross-checked against the
source:

- api-shipping-v2: route-intelligence is GET with query params
  (fromIso2, toIso2, cargoType, hs2), not POST with a JSON body.
  Response shape is {primaryRouteId, chokepointExposures[],
  bypassOptions[], warRiskTier, disruptionScore, ...}.
- api-commerce: /api/product-catalog returns {tiers, fetchedAt,
  cachedUntil, priceSource} with tier groups free|pro|api_starter|
  enterprise, not the invented {currency, plans}. Document the
  DELETE purge path too.
- api-notifications: Slack/Discord /oauth/start are POST + Clerk
  JWT + PRO (returning {oauthUrl}), not GET redirects. Callbacks
  remain GET.
- api-platform: /api/version returns the latest GitHub Release
  ({version, tag, url, prerelease}), not deployed commit/build
  metadata.
- api-oauth + mcp: /api/oauth/register limit is 5/60s/IP (match
  code), not 10/hour.

Also caught while double-checking: /api/register-interest and
/api/contact are 5/60min and 3/60min respectively (1-hour window,
not 1-minute). Both require Turnstile. Removed the fabricated
limits for share-url, notification-channels, create-checkout
(they fall back to the default per-IP limit).

* docs(mintlify): second-round fixes — verify every claim against source

Reviewer caught 7 more cases where I described API behavior I hadn't
read. Each fix below cross-checked against the handler.

- api-commerce (product-catalog): tiers are flat objects with
  monthlyPrice/annualPrice/monthlyProductId/annualProductId on paid
  tiers, price+period for free, price:null for enterprise. There is
  no nested plans[] array.
- api-commerce (referral/me): returns {code, shareUrl}, not counts.
  Code is a deterministic 8-char HMAC of the Clerk userId; binding
  into Convex is fire-and-forget via ctx.waitUntil.
- api-notifications (notification-channels): actual action set is
  create-pairing-token, set-channel, set-web-push, delete-channel,
  set-alert-rules, set-quiet-hours, set-digest-settings. Replaced
  the made-up list.
- api-shipping-v2 (webhooks): alertThreshold is numeric 0-100
  (default 50), not a severity string. Subscriber IDs are wh_+24hex;
  secret is raw 64-char hex (no whsec_ prefix). POST registration
  returns 201. Added the management routes: GET /{id},
  POST /{id}/rotate-secret, POST /{id}/reactivate.
- api-platform (cache-purge): auth is Authorization: Bearer
  RELAY_SHARED_SECRET, not an admin-key header. Body takes keys[]
  and/or patterns[] (not {key} or {tag}), with explicit per-request
  caps and prefix-blocklist behavior.
- api-platform (download): platform+variant query params, not
  file=<id>. Response is a 302 to a GitHub release asset; documented
  the full platform/variant tables.
- mcp: server also accepts direct X-WorldMonitor-Key in addition to
  OAuth bearer. Fixed the curl example which was incorrectly sending
  a wm_live_ API key as a bearer token.
- api-notifications (youtube/live): handler reads channel or videoId,
  not channelId.
- usage-auth: corrected the auth-matrix row for /api/mcp to reflect
  that OAuth is one of two accepted modes.

* docs(mintlify): fix Greptile review findings

- mcp.mdx: 'Five' slow tools → 'Six' (list contains 6 tools)
- api-scenarios.mdx: replace invalid JSON numeric separator
  (8_400_000_000) with plain integer (8400000000)

Greptile's third finding — /api/oauth/register rate-limit contradiction
across api-oauth.mdx / mcp.mdx / usage-rate-limits.mdx — was already
resolved in commit 4f2600b2a (reviewed commit was eb5654647).
2026-04-19 15:03:16 +04:00