Skip to content

feat(tips): consolidated TIPS page with per-chain toggle - #38

Merged
montycheese merged 8 commits into
mainfrom
feat-tips
Jul 30, 2026
Merged

feat(tips): consolidated TIPS page with per-chain toggle#38
montycheese merged 8 commits into
mainfrom
feat-tips

Conversation

@montycheese

Copy link
Copy Markdown
Contributor

What

Ports the standalone TIPS UI into omni as a TIPS sidebar page, replacing the three separately-deployed per-chain sites (Base Mainnet, Base Sepolia, Zeronet) with a single consolidated view. Chain becomes a runtime parameter: a toggle at the top of every TIPS route drives a ?chain= query that selects the data source server-side and threads through all links.

Completion criteria (all met)

  • TIPS appears in the sidebar; /tips renders the dashboard (Blocks + Rejected Transactions tabs, tx-hash search)
  • Chain toggle (Mainnet / Sepolia / Zeronet) updates ?chain= and refetches the correct chain's data
  • Full route set restyled to the omni design system: dashboard + block / bundle / txn detail pages
  • Detail pages carry the selected chain (back links + per-chain block-explorer URLs)
  • Single deployment serves all three chains
  • typecheck / lint / build green; mobile layout clean

Architecture

  • API ported under /api/tips — thin S3 + JSON-RPC wrappers (one read-through block cache with write-through PutObject preserved). Chain selected by env prefix: TIPS_MAINNET_*, TIPS_SEPOLIA_*, TIPS_ZERONET_* (S3 bucket/region/endpoint/creds + RPC URL). Mirrors the snapshots per-network env pattern.
  • Chain contract in app/tips/chains.ts — single source of truth for chain ids, labels, and explorer URLs (overridable via NEXT_PUBLIC_TIPS_<CHAIN>_EXPLORER_URL).
  • Types — the client re-exports the API response types (type-only, erased at build) so there's one source of truth and shape drift fails at compile time; no server/@aws-sdk code reaches the client bundle.
  • AnalyticstrackTipsChainSelect fires on toggle; wiring row added to AGENTS.md.

Config

Secrets go in Vercel project env only (never in git). .env.example documents the per-chain variables. Set the three TIPS_* credential sets before this reads live data.

Verification

Verified locally with Playwright: nav item present; dashboard renders; toggling to Sepolia switches polling from ?chain=mainnet to ?chain=sepolia; block detail back-link and explorer URL carry the chain (base-sepolia.blockscout.com); tips_chain_select event emits; graceful empty/error states; clean at 390px width. Live data (S3/RPC) requires the Vercel env creds and was not exercisable locally.

Ports the standalone TIPS UI into omni as a sidebar page, replacing the
three separately-deployed per-chain sites with one view. Chain
(Base Mainnet / Base Sepolia / Zeronet) is a runtime parameter driven by
a `?chain=` query and a toggle on every route.

- API ported under /api/tips: thin S3 + JSON-RPC wrappers, chain-selected
  by env prefix (TIPS_MAINNET_/TIPS_SEPOLIA_/TIPS_ZERONET_), read-through
  block cache with write-through PutObject preserved. Secrets via Vercel
  env only.
- Full route set restyled to the omni design system: dashboard (Blocks +
  Rejected tabs, tx search) plus block / bundle / txn detail pages, all
  chain-aware (links and explorer URLs carry the selected chain).
- Chain model contract in app/tips/chains.ts; per-chain explorer overrides
  via NEXT_PUBLIC_TIPS_<CHAIN>_EXPLORER_URL.
- Analytics: trackTipsChainSelect fires on toggle; wiring documented in
  AGENTS.md. Sitemap and .env.example updated; updates llms.txt.
@cb-heimdall

Copy link
Copy Markdown
Collaborator

🟡 Heimdall Review Status

Requirement Status More Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1

@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
omni-ui Ready Ready Preview Jul 30, 2026 6:29pm

Request Review

TIPS is internal-only. The public (Vercel) build leaves the flag unset, so
the section is entirely absent; the internal build (cb/ui) sets it to 1 at
`next build` time. Same public source, two deployables.

When the flag is off:
- the sidebar entry is dropped from NAV_ITEMS (not just shown as "Soon")
- middleware returns a real 404 for /tips and /tips/* at the edge. The route
  is statically prerendered, so the layout's notFound() alone serves 404
  content with a 200 status; the edge check enforces the correct status.
- app/tips/layout.tsx also calls notFound() as a content-level backstop
- every /api/tips/* route returns 404 via a shared guard, so its existence
  isn't leaked by 500s from missing credentials
- /tips is excluded from the generated llms/agents artifacts via a new
  llms.config.mjs; it is deliberately not in the public sitemap either

next.config: emit output: 'standalone' so the internal Docker image can run
server.js without node_modules (no-op on Vercel).
@montycheese

Copy link
Copy Markdown
Contributor Author

Added: TIPS is now gated behind a build-time flag NEXT_PUBLIC_ENABLE_TIPS so it ships internal-only.

  • Public / Vercel (flag unset): the section is fully absent — no sidebar link, /tips + /tips/* and /api/tips/* all return 404, and it's kept out of the sitemap and the generated llms/AGENTS artifacts.
  • Internal (NEXT_PUBLIC_ENABLE_TIPS=1 at build): TIPS present as before.

Same public source, two deployables. This pairs with the internal cb/ui shell repo (separate change) that builds this repo with the flag on and serves it inside Coinbase with IRSA-based S3 access. Also added output: 'standalone' to next.config for the internal Docker image (no-op on Vercel).

Verified both states on a production build: flag-off → 404 across all TIPS routes + link absent; flag-on → 200 + link present.

Replace the single NEXT_PUBLIC_ENABLE_TIPS flag with a declarative deployment
matrix. deploy.config.mjs maps each environment-specific surface to the build
targets that include it; NEXT_PUBLIC_DEPLOY_TARGET (set by the build script)
selects the target. Adding an internal- or external-only page is now a one-line
config edit that nav, middleware, and the llms generator all read.

- deploy.config.mjs: SURFACES matrix + surfaceEnabled / disabledRoutePrefixes /
  disabledRouteGlobs helpers. Authored as .mjs so both the app and the node
  llms scripts import it; NEXT_PUBLIC_DEPLOY_TARGET is inlined at build, so a
  disabled surface is still dead-code-eliminated (route + API 404).
- app/tips/flag.ts: TIPS_ENABLED now derives from surfaceEnabled('tips').
- middleware.ts: 404 any disabledRoutePrefixes() at the edge (generic; no
  per-page edit).
- llms.config.mjs: exclude = disabledRouteGlobs() (auto).
- package.json: add build:internal (sets NEXT_PUBLIC_DEPLOY_TARGET=internal).
- deploy.config.test.mjs: surfaceEnabled / disabledRoutePrefixes /
  disabledApiPrefixes / disabledRouteGlobs under external + internal targets,
  default-external, and unknown-surface-enabled-everywhere.
- add dev:internal script (NEXT_PUBLIC_DEPLOY_TARGET=internal next dev) so the
  internal variant (with TIPS) can be run locally, mirroring build:internal.
- README + AGENTS.md: document the external/internal targets, how to run both
  locally, and the deploy.config.mjs workflow for adding env-specific pages.
- .env.example: explain the target is set by the *:internal scripts, and the
  caveat of pinning it in .env.local.
The repo had no CI of its own — every check on a PR came from org-level
tooling (CodeQL, StepSecurity, Heimdall, Vercel), so tests, typecheck, lint,
and docs:check never ran on a pull request.

- .github/workflows/ci.yml, two jobs:
  - verify: typecheck, lint, test, docs:check.
  - public-build-excludes-internal: builds the default (external) target and
    asserts internal-only routes 404 and never appear in the nav or sitemap.
    Verified both ways — it passes on an external build and fails on an
    internal one, so the deployment matrix can't silently regress.

Docs corrections found while wiring this up:
- README claimed docs:check was a "CI gate" when nothing ran it; now it is,
  and the CI setup is documented.
- Drop the "dead-code-eliminated" claim (deploy.config.mjs, app/tips/flag.ts,
  AGENTS.md). A disabled surface is unreachable — routes and API 404, dropped
  from nav/sitemap/llms — but its client chunks may still be emitted, so the
  guarantee is reachability, not omission from the bundle.
- Document that the *:internal scripts take precedence over a value pinned in
  .env.local, so pinning a local default never locks you out of either variant.
- AGENTS.md: call out that SURFACES is an exception list, so an internal-only
  page with no entry fails open, and point at the CI job to extend.

Co-Authored-By: Claude <noreply@anthropic.com>
…jobs

Each runs as its own check so a failure names the thing that broke and can be
required individually in branch protection, rather than one combined job.

Also give the docs job a self-describing name: it verifies the generated agent
index (public/llms.txt, llms-full.txt, AGENTS.md), which is committed and goes
stale when routes change.

Co-Authored-By: Claude <noreply@anthropic.com>
@montycheese
montycheese merged commit 5dedfed into main Jul 30, 2026
10 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants