Skip to content

Migrate to Astro 6 (phase 1)#308

Open
dionysuzx wants to merge 18 commits into
mainfrom
astro-migration-phase-1-impl
Open

Migrate to Astro 6 (phase 1)#308
dionysuzx wants to merge 18 commits into
mainfrom
astro-migration-phase-1-impl

Conversation

@dionysuzx
Copy link
Copy Markdown
Collaborator

@dionysuzx dionysuzx commented Jun 5, 2026

This is the phase 1 of astro migration. There will be another phase which refactors existing react components into astro idioms.

TL;DR:

  • phase 1: set the routing structure / astro skeleton, minimize intra-component disruption
  • phase 2: systematically convert every route into more useful static html

There was a lot that went into the PR, you can see the initial spec at astro-phase-1-migration-plan.md. See below for a longer description of the approach.


note: this section may go stale if/when the PR iterates, but it serves as a high level idea on the initial approach.

I'd like to write quickly about how I approached this PR, please ask me for further details. I spent most of my time on the astro-phase-1-migration-plan.md. The astro migration went through several autonomous iterations, and I discovered the best approach would be to just set up the routing/skeleton with astro, and then in a follow-up PR, systematically loop through each of these routes that are at runtime hydrating the react shell on each page route, and render more useful static HTML for it with a .astro file, or at least to the degree that we can without reducing the quality of our UX (basically, be judicious about our usage of islands). The main motivation of this initiative is useful static HTML for LLMs and crawlers.

After settling on the phase 1 migration plan, I proceeded to do implementations:

  • Claude effort ultracode (using dynamic workflows)
  • Codex effort high (using /goal loop)

I cross-analyzed both implementations with ultracode and codex high/xhigh, and we all determined that the Claude dynamic workflows implementation served as a stronger foundational base (as per my injected coding principles and the intent for phase 1 migration). During this process I aggregated issues/findings into a single review artifact, and passed it to claude ultracode to resolve the findings, using my GitHub @dionysuz-bot codex agent as backpressure on the resolution commits (the loop was basically inspect issue / reason about it -> push changes or present argument -> wait for dionysuz-bot until no blocking issues).

After all issues were addressed from the ultracode loop, I did a final pass of ultracode review + three parallel codex high reviews; both reported back with ~zero blocking changes. This was really exciting to see! The ultracode reviews were multi-dimensional adversarial reviews. The codex high reviews admittedly feel poorly suited to the size of the diff, that's also why I did multiple in parallel for fuzz. A codex /goal loop could've been better, especially if driving it in claude-dynamic-workflow fashion), but out of interest of time and for simplicity I went for multiple regular reviews.

After these passes I did a human review pass, quickly reviewing every file individually in GitHub, mainly looking at structure / high-level seams and boundaries, comments, and stylistic nitpicks. I commented across the PR with my issues and had Claude ultracode resolve issues. I also worked with codex imperatively during this manual process until I clicked the checkbox on each file on GitHub as looking ~correct by my human eye and making sure I'm comfortable.

Finally I did one more review pass of ultracode claude and multi-codex to confirm no blocking issues after our changes from human review.

@netlify
Copy link
Copy Markdown

netlify Bot commented Jun 5, 2026

Deploy Preview for eth-forkcast ready!

Name Link
🔨 Latest commit 81ba2c5
🔍 Latest deploy log https://app.netlify.com/projects/eth-forkcast/deploys/6a261977dcd02b0008d16aa5
😎 Deploy Preview https://deploy-preview-308--eth-forkcast.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 93 (🟢 up 2 from production)
Accessibility: 100 (no change from production)
Best Practices: 100 (no change from production)
SEO: 100 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

Replace the Vite + React Router SPA with an Astro 6 static foundation that
owns routing, layouts, document head, metadata, redirects, sitemap, and the
build/preview/check commands. React page bodies hydrate as client:only islands.

- astro.config.mjs: output static, site, @astrojs/react + @astrojs/sitemap,
  Tailwind v4 Vite plugin; configured redirects for legacy aliases and
  pending PR-only EIPs (/eips/{id} -> GitHub PR)
- Astro pages for all canonical routes; getStaticPaths for EIPs (canonical
  only), calls (detail + concrete type scopes), and devnets (local specs +
  active networks)
- Build-time snapshots (upcoming calls, ethPandaOps networks) shared by
  getStaticPaths and the hydrated islands so links never point to un-emitted routes
- eip-stage-changes.json emitted by an Astro endpoint via a shared pure helper
- Remove react-router-dom in favor of a small navigation shim; provider-less
  cross-island theme store; per-call search via a window event
- Astro-owned head/meta/theme bootstrap/initial pageview; polished 404 page;
  robots points at sitemap-index.xml
- Drop SPA scaffolding: index.html, App, main, useMetaTags, generate-static-pages,
  generate-eip-stage-changes, public/_redirects, public/404.html
@dionysuzx dionysuzx force-pushed the astro-migration-phase-1-impl branch from 1ef39f0 to 24753b0 Compare June 5, 2026 18:29
@dionysuz-bot
Copy link
Copy Markdown

Blocking: the migration drops the existing /calls/{githubIssue} aliases. Before this PR, numeric single-segment call paths were redirected by call.issue to the canonical /calls/{type}/{number} page; now src/pages/calls/[...path].astro:19 only emits canonical call.path entries and upcoming {type}/{number} pages. In the built output, /calls/acde/238 exists, but an existing alias like /calls/2018 -> /calls/aa/001 does not, so all 199 completed calls with issue metadata now 404.

Please preserve these as static redirects during the Astro migration. A clean fix would be to derive a call alias redirect map from protocol-calls.generated.json or the same call domain data and feed it into Astro redirects alongside the other legacy redirects, with a small route snapshot/assertion so future migrations do not silently drop published URL aliases.

dionysuzx added 3 commits June 5, 2026 14:43
Incorporate the review feedback and cherry-pick organizational patterns from the
alternate implementations, guided by docs/astro-migration-phase-1.md and our
coding principles.

Route invariant / correctness:
- calls/[type]: stop emitting one-off pseudo-series scope pages (/calls/one-off-*);
  concrete-series scopes and one-off *call* pages are unaffected
- calls/[...path]: emit an upcoming-call watch page only when the index links it
  internally (shared hasUpcomingWatchPage predicate); video-less upcoming calls
  link out to GitHub, so no unreachable routes are generated
- CallPlanPage: fix a /calls/acdt/66 404 — route the "last discussed" link through
  a shared formatCallReference helper that zero-pads to the emitted /calls/acdt/066
  (de-duplicated out of EipNotice/EipTimeline)
- StakeholderUpgradePage: guard the back-link via getUpgradePagePath

Single source of truth (no build-vs-runtime drift):
- extract the upcoming-call issue parser and YouTube-URL extractor into one pure
  module (domain/calls/upcomingCallParsing.ts), shared by the build snapshot
  script and the runtime fetch path instead of hand-synced copies
- extract pure devnet-network derivation into domain/devnets/networks.ts; the
  React hook is now a thin wrapper over it

Organization / cruft:
- typed page-metadata registry (domain/routes/pageMetadata.ts) replaces the inline
  title/description repeated across the static pages
- richer EIP <title> (include the layman title) via existing helpers
- fold the generic lib/ bucket into domain/calls + components/
- drop the unused vite and @vitejs/plugin-react devDeps

Verified: lint, 44 tests, astro check (162 files, 0 errors), build (815 pages),
and browser spot-checks (canonical routes, legacy redirect, real 404 with nav,
theme toggle, devnet route, and zero-padded agenda call links).
@dionysuzx
Copy link
Copy Markdown
Collaborator Author

@dionysuz-bot this round incorporates the review feedback and cherry-picks organizational polish from the alternate implementations. Requesting a re-review.

Route invariant / correctness

  • calls/[type] no longer emits one-off pseudo-series scope pages (/calls/one-off-*); concrete-series scopes and one-off call pages are unaffected.
  • calls/[...path] emits an upcoming-call watch page only when the index links it internally (shared hasUpcomingWatchPage predicate); video-less upcoming calls link out to GitHub, so no unreachable routes are generated.
  • Fixed a /calls/acdt/66 404 in the agenda planner — the "last discussed" link now routes through a shared formatCallReference helper that zero-pads to the emitted /calls/acdt/066 (de-duplicated out of EipNotice/EipTimeline).
  • Guarded StakeholderUpgradePage's back-link via getUpgradePagePath.

Single source of truth (no build-vs-runtime drift)

  • Extracted the upcoming-call issue parser and the YouTube-URL extractor into one pure module (domain/calls/upcomingCallParsing.ts), shared by the build snapshot script and the runtime fetch path — no more hand-synced copies.
  • Extracted pure devnet-network derivation into domain/devnets/networks.ts; useDevnetNetworks is now a thin wrapper over it.

Organization / cruft

  • Typed page-metadata registry (domain/routes/pageMetadata.ts) replaces the inline title/description repeated across static pages.
  • Richer EIP <title> (includes the layman title) via existing helpers.
  • Folded the generic lib/ bucket into domain/calls + components/.
  • Dropped the unused vite and @vitejs/plugin-react devDeps.

Verified locally: lint, 44 tests, astro check (162 files, 0 errors), build (815 pages), and browser spot-checks (canonical routes, legacy redirect, real 404 with nav, theme toggle, network-only devnet route, zero-padded agenda call links).

@dionysuz-bot
Copy link
Copy Markdown

No blocking findings.

I re-reviewed the route invariants, shared upcoming-call parsing, call-reference padding, devnet snapshot derivation, and the Astro metadata/navigation changes. The current approach looks consistent with the stated intent and with the project’s preference for explicit domain helpers over hand-synced route/link logic.

Verified locally after installing dependencies from the lockfile: npm test, npm run lint, npm run check, and npm run build all pass.

The SPA matched a bare issue number and redirected it to the call's canonical
/calls/{series}/{number} page (the old CallPage issue-redirect effect); the
migration had dropped this, 404ing ~199 previously-shared URLs. Restore it as
build-time Astro redirects derived from the compiled call data via one pure
helper (src/domain/calls/callRoutes.ts), so the alias set stays in sync with the
pages the build emits and can never drift — the same pattern as the pending-EIP
redirects. One-off calls are handled uniformly (e.g. /calls/1954 ->
/calls/one-off-1954/001, the friendly entry point for a series-less call).

Add a unit test asserting every completed call with an issue keeps a unique
alias, so a future call sync cannot silently drop or clobber a shared URL.
Update the plan and intentional-feature-removals docs to match (these aliases
are now a preserved redirect, not a removed route).
@dionysuzx
Copy link
Copy Markdown
Collaborator Author

@dionysuz-bot follow-up: this restores the /calls/{github-issue-number} aliases your earlier review flagged — implemented as build-time Astro configured redirects rather than the SPA's in-React redirect.

  • Derived from the compiled call data via one pure helper (src/domain/calls/callRoutes.ts) and merged into astro.config.mjs redirects alongside the existing legacy + pending-EIP redirects, so the alias map stays in sync with the emitted pages and can't drift.
  • One-off calls are handled uniformly: /calls/1954/calls/one-off-1954/001.
  • Added a unit test asserting every completed call with an issue keeps a unique alias (no published URL silently dropped or clobbered) — the small assertion you suggested.
  • Updated the plan and intentional-feature-removals.md docs: these aliases are now a preserved redirect, not a removed route.

Verified: 47 tests, lint, astro check (164 files, 0 errors), build (199 alias redirect pages emitted; /calls/2018/calls/aa/001 and /calls/1954/calls/one-off-1954/001 confirmed in the browser).

Requesting a re-review.

@dionysuz-bot
Copy link
Copy Markdown

No blocking issues found.

The follow-up addresses my earlier alias concern. buildCallIssueRedirects() keeps the rule pure and data-derived, astro.config.mjs wires it into configured redirects at the right boundary, and the invariant test now catches duplicate issue numbers clobbering published /calls/{issue} URLs. I also manually checked the generated data: 199 calls with issues, 199 unique aliases, including /calls/2018 -> /calls/aa/001 and /calls/1954 -> /calls/one-off-1954/001.

I couldn’t run the targeted Vitest test locally because this checkout does not have dependencies installed (vitest is missing), so this review is based on static inspection plus your reported verification.

Route pageview tracking moved into the Astro shell (Layout.astro fires the
initial trackPageView); the hook's trackPageView has no remaining callers.
Removing it avoids future re-wiring that would double-count. The shell stays
the single pageview source; the hook keeps interaction-event tracking
(trackEvent / trackUpgradeView / trackLinkClick).
@dionysuzx
Copy link
Copy Markdown
Collaborator Author

@dionysuz-bot follow-up: removed the dead useAnalytics.trackPageView (commit 48ec26b). Route pageview tracking now lives solely in the Astro shell (Layout.astro fires the initial trackPageView); the hook method had no remaining callers, so removing it prevents future re-wiring that would double-count. The hook keeps its interaction-event tracking (trackEvent / trackUpgradeView / trackLinkClick).

I also evaluated the astro.config.mjs silent {} fallback in pendingEipRedirects() and intentionally left it as-is:

  • The "silently ship zero pending-EIP redirects" path isn't reachable: eips.json is a hard static import in src/data/eips.ts (consumed by eips/[id].astro's getStaticPaths), so a build without it fails loudly (Could not resolve "./eips.json" from "src/data/eips.ts" — verified) rather than emitting a quietly-wrong site. The try/catch only avoids crashing config load (e.g. an editor/language-server on a fresh checkout before compile-data, which every npm flow runs first).
  • A PROD-gated warning wouldn't work anyway: import.meta.env.PROD is undefined at config-load time (the config runs in Node before the Vite app context).

Verified: lint, 47 tests, astro check (0 errors). Requesting a re-review.

@dionysuz-bot
Copy link
Copy Markdown

Re-reviewed the follow-up and related Astro shell/config paths. No blocking issues found.

The pageview tracking now has a single owner in Layout.astro, and I agree the pendingEipRedirects() config-load fallback is acceptable given src/data/eips.ts hard-imports eips.json for the actual emitted EIP routes.

I could not re-run lint/tests in this checkout because dependencies are not installed (vitest / eslint not found), but the source review did not surface correctness or architecture blockers.

@dionysuzx dionysuzx changed the title Astro migration (phase 1) Migrate to Astro 6 (phase 1) Jun 7, 2026
@dionysuzx dionysuzx marked this pull request as ready for review June 7, 2026 20:02
@dionysuzx dionysuzx requested a review from wolovim June 7, 2026 20:04
Comment thread src/domain/calls/callRoutes.test.ts Outdated
Comment thread src/domain/calls/callRoutes.ts Outdated
@dionysuzx
Copy link
Copy Markdown
Collaborator Author

the navbar logo has an undesirable flicker. consider:

1. Convert `SiteNav`/`Logo` shell to `.astro` so header, links, and logo are in the initial HTML.
2. Keep only interactive pieces hydrated: theme toggle, call search button behavior, mobile menu/dropdown if needed.
3. Inline the wordmark SVG or at least give the image fixed dimensions/preload so it does not wait on React plus image decode.
4. Optionally add `prefetch: true` in `astro.config.mjs` and `data-astro-prefetch` to main internal nav links.

Comment thread src/components/calls-index/CallsIndexTimeline.tsx Outdated
Comment thread src/components/CallsIndexPage.tsx Outdated
Comment thread src/components/stakeholder/StakeholderUpgradePage.tsx Outdated
Comment thread astro.config.mjs Outdated
Comment thread astro.config.mjs
Comment thread astro.config.mjs
Comment thread src/data/generated/devnet-networks.json
…ct polish

- Convert the site nav/logo from a `client:only` React island to a static
  `SiteNav.astro`, fixing the logo flicker: the header, links, and wordmark now
  render in the initial HTML (fixed dimensions, prefetched links). Theme toggle,
  upgrade dropdown, mobile menu, and call-search button are wired by a small
  client script; the now-unused Logo/SiteNav/ThemeToggle/ThemeContext React
  modules are removed. Enable Astro `prefetch` and mark main nav links.
- Remove the dead non-embedded branch of StakeholderUpgradePage (the stakeholders
  view is only ever embedded, so the standalone shell + back-link were
  unreachable) and drop the now-vestigial `embedded` prop.
- astro.config: fold the redirect builders' try/catch into one `readCompiledData`
  helper, and rename `legacyRedirects` -> `aliasRedirects`.
- Trim process/marker cruft from comments across the PR (no "Phase 1", "the bot"),
  keeping references that explain why current code is shaped as it is; rewrite
  the comments that described the nav as a React island.
@dionysuzx dionysuzx force-pushed the astro-migration-phase-1-impl branch from d574ead to de8a048 Compare June 7, 2026 22:42
@dionysuzx
Copy link
Copy Markdown
Collaborator Author

Done — the navbar flicker is fixed. Converted SiteNav/Logo to a static SiteNav.astro so the header, links, and wordmark render in the initial HTML (your steps 1 + 3): the wordmark <img> now has explicit width/height + fetchpriority="high", so it no longer waits on React hydration + image decode. Added prefetch: true in astro.config.mjs and data-astro-prefetch on the nav links (step 4).

One deviation from step 2: instead of keeping the interactive pieces as hydrated React islands, I wired them with a small vanilla <script> in the .astro component — theme toggle, upgrade dropdown (CSS hover/focus-within + Escape/aria-expanded), mobile menu, and the call-search button (which still just dispatches the existing forkcast:open-call-search window event). Two reasons:

  • It makes the nav fully React-free, so there's zero hydration flash on any nav element — including the theme toggle, whose sun/moon icon is now swapped purely by CSS from the pre-paint .dark class.
  • It let me delete the now-unused Logo / SiteNav.tsx / ThemeToggle / ThemeContext modules (≈440 lines). useTheme/ThemeContext had no other consumers — page bodies style off the .dark class via CSS, not JS — so the React theme machinery was only ever serving the nav toggle. The forkcast:themechange event + localStorage contract is preserved by the vanilla toggle, so anything that wants to react to theme changes still can.

I also updated the comments that described the nav as a React island (callSearch.ts, CallPage.tsx).

Verified in a browser against the built output: logo + links in the initial HTML (fixed dims), theme toggle persists + swaps icon + restores, upgrade dropdown opens on hover/focus with aria-expanded, mobile menu toggles, and the static call-search button still opens the CallPage search modal. lint / 47 tests / astro check (0 errors) / build (815 pages) all pass.

dionysuzx added 2 commits June 7, 2026 18:47
Fold the route-change catalog into docs/astro-migration-phase-1.md under a new
"Intentional Route Changes" section, so there's a single doc to keep under docs/
for posterity. Repoint the in-doc cross-reference and the astro.config.mjs
comment at the consolidated section, and remove docs/intentional-feature-removals.md.
Drop the `readCompiledData` dedup helper and inline the try/catch in
`pendingEipRedirects()` / `callIssueRedirects()` again — the original two-block
form is simpler and reads fine. Keeps the comment cleanups and the
`legacyRedirects` -> `aliasRedirects` rename.
Comment thread src/components/ui/SiteNav.astro
Comment thread astro.config.mjs Outdated
dionysuzx added 2 commits June 7, 2026 19:09
The dropdown's visibility is already fully CSS-driven (group-hover /
group-focus-within). Drop the five JS listeners that mirrored that state into
aria-expanded — a synced attribute that could momentarily desync (e.g. Escape
while hovering) and earned nothing the CSS didn't already do. Keep aria-haspopup,
and keep a single Escape handler that closes the menu by blurring focus out.
Per review, `legacyRedirects` reads more clearly: these are legacy aliases that
we can still extend. Comment updated to match.
@dionysuzx
Copy link
Copy Markdown
Collaborator Author

dionysuzx commented Jun 7, 2026

A few changes I’d like in this PR:

  • Rename navigation.tsx to something narrower, like urlState.tsx or browserLocation.tsx. The helper is not a router replacement; it is same-page URL state plus real browser navigation for path changes. The name should make that boundary clear.

  • Remove <Navigate> and its remaining call sites. There are only a couple, and client-side redirects from React page bodies are routing behavior that Phase 1 is trying to move out of React. Astro getStaticPaths() should emit valid detail pages, configured redirects should handle intentional aliases, and unknown paths should fall through to Astro’s real 404 instead of being normalized by a hydrated island.

  • Make snapshot-runtime-routes stricter for CI/deploy builds. Keeping the previous snapshot on fetch failure is useful for local/offline development, but production builds should not silently ship stale upcoming-call or devnet route data. Please fail when CI=true, or add an explicit escape hatch like ALLOW_STALE_ROUTE_SNAPSHOTS=1.

Also, add a small section in astro-migration-phase-1.md for phase 2 / subsequent PRs. under that, include:

  • Replace temporary <Link to="..."> usages with normal <a href="..."> where the link is just cross-page navigation.

  • Remove React Router compatibility props from the temporary link helper, such as ignored state / replace.

  • Continue narrowing the URL helper API so it only exposes browser URL state needed by hydrated islands, not router-shaped abstractions.

  • As more routes move from React bodies into Astro, move query/hash ownership into page-specific Astro or component primitives where that becomes natural.

dionysuzx added 3 commits June 7, 2026 19:40
…vigate>

The helper is not a router; it is same-page URL state plus real browser navigation
for path changes. Rename `navigation.tsx` -> `browserLocation.tsx` so the name
reflects that boundary, and update all importers.

Remove the `<Navigate>` declarative-redirect export and its two call sites
(EipPage, DevnetSpecPage). Client-side route normalization belongs to Astro now:
getStaticPaths emits only valid detail pages, configured redirects handle aliases,
and unknown paths fall through to Astro's 404 — so those fallbacks were
unreachable. Also drops DevnetSpecPage's now-dead `loading` guard (the snapshot
hook resolves synchronously).
Keeping the previous snapshot on a fetch failure is right for local/offline dev,
but CI/deploy builds shouldn't silently ship stale or empty upcoming-call / devnet
route data. Fail when CI=true; set ALLOW_STALE_ROUTE_SNAPSHOTS=1 to opt back into
the lenient behavior.
Captures the deferred cleanups: replace temporary <Link> with <a> for plain
cross-page links, drop the React Router compat props, keep narrowing the
browser-location helper, and move query/hash ownership into Astro primitives as
routes migrate.
@dionysuzx
Copy link
Copy Markdown
Collaborator Author

Agreed with all four — done and pushed (4aa891d, ff94c2a, 733bcf4).

Rename the URL helper. navigation.tsxbrowserLocation.tsx, all 29 importers updated, and the file's eslint/header comment adjusted. I went with browserLocation over urlState because the module spans both reading the location (useLocation, useSearchParams) and navigating it (Link, useNavigate), not just query-string state — but happy to switch to urlState if you prefer that emphasis.

Remove <Navigate>. Dropped the export and both call sites — EipPage (<Navigate to="/"> for an unknown id) and DevnetSpecPage (<Navigate to="/devnets"> for "nothing found"). Both were unreachable in the static model: getStaticPaths() only emits pages for valid EIP ids / devnet spec+network ids, so a rendered page always has its data, and genuinely unknown URLs hit Astro's real 404 rather than being normalized by the island. They now just render nothing in the impossible case. While there, I also removed DevnetSpecPage's dead loading guard (the devnet hook resolves synchronously from the committed snapshot).

Stricter snapshot in CI. snapshot-runtime-routes now fails the build on a fetch failure when CI=true, instead of silently keeping a stale/empty snapshot. Local/offline dev keeps the previous snapshot as before, and ALLOW_STALE_ROUTE_SNAPSHOTS=1 opts CI back into the lenient behavior.

Phase 2 doc section. Added a "Phase 2 and Beyond" section to astro-migration-phase-1.md with the four follow-ups you listed (replace temporary <Link> with <a> for plain cross-page links; drop the React Router compat props like ignored state/replace; keep narrowing the browserLocation helper to just the URL state islands need; move query/hash ownership into Astro/component primitives as routes migrate).

Verified: lint, 47 tests, astro check (0 errors), build (815 pages), and browser-checked that a real EIP page (/eips/7702) and devnet page (/devnets/bal-devnet-3) still render their bodies and don't redirect.

Comment thread src/components/browserLocation.tsx Outdated
@@ -0,0 +1,172 @@
/* eslint-disable react-refresh/only-export-components -- browser-location helpers (hooks + Link) intentionally share one module */
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dionysuz-bot i don't really like this name browserLocation.tsx. we had navigation.tsx before, which looks better to me. if it's too wide, maybe we could do navigationHelpers.tsx, but i think navigation.tsx is actually best. what do you think?

…harden helpers + tests

- deploy.yml (build:fresh): pass GITHUB_TOKEN so the build-time GitHub API snapshot
  fetch uses the authenticated rate limit; an unauthenticated 60/hr cap (CI sets
  FAIL_ON_STALE) could otherwise fail a deploy despite a committed snapshot. (#311
  already removed the fetch from the non-deploy build, so only build:fresh needs it.)
- browserLocation: compare normalized pathnames in useNavigate so a trailing-slash
  directory URL (/calls/) and a no-slash target (/calls) stay same-path and keep the
  in-place query update instead of a full reload. Note Link's ignored `replace`.
- EipPage: drop the cross-EIP tab-reset effect that's dead under Astro full-reload nav
  (keep scroll-to-top).
- callReference: throw a descriptive error on a malformed "{series}/{number}" ref
  instead of a cryptic padStart crash; cover with a test.
- upcomingCalls.test: add behavior tests for the route-deciding parsers
  (parseUpcomingCallFromIssue, extractYouTubeUrl, resolveUpcomingCallNumber,
  hasUpcomingWatchPage).
- astro.config: redirect builders rethrow non-ENOENT errors (corrupt data fails loud;
  missing data stays graceful).
- docs: clarify call-index filter buttons (ACD scopes path-route; breakout series use
  dropdown query state).
@dionysuzx
Copy link
Copy Markdown
Collaborator Author

@dionysuz-bot pushed 8bc7f4aa addressing the review findings — please re-review.

Build reliability

  • deploy.yml: wired GITHUB_TOKEN into the build:fresh step so the build-time GitHub-API route-snapshot fetch uses the authenticated rate limit. An unauthenticated 60-req/hr-per-IP cap (CI sets FAIL_ON_STALE) could otherwise hard-fail a deploy despite a committed snapshot. (Rebased onto Make route snapshots explicit and deterministic #311, which already removed the fetch from the non-deploy build, so only build:fresh needs the token — the PR-verify path is now hermetic and needs nothing.)

Correctness / cleanups

  • browserLocation: useNavigate now compares normalized pathnames, so a trailing-slash directory URL (/calls/, the GitHub Pages canonical form) and a no-slash target (/calls) stay "same path" and keep the in-place query update instead of degrading to a full reload.
  • EipPage: removed the cross-EIP tab-reset effect that's dead under Astro full-reload nav (kept scroll-to-top).
  • callReference: throws a descriptive error on a malformed {series}/{number} ref instead of a cryptic padStart crash (+ test).
  • astro.config: redirect builders rethrow non-ENOENT errors (corrupt data fails loud; missing data stays graceful).

Tests

  • Added behavior tests for the route-deciding parsers (parseUpcomingCallFromIssue, extractYouTubeUrl, resolveUpcomingCallNumber, hasUpcomingWatchPage). Suite now 57/57.

Docs

  • Clarified in the phase-1 doc that the call-index top-level filter buttons path-route only the ACD scopes; breakout series stay dropdown query state (their /calls/[type] routes still resolve).

Local gates green: test 57/57, lint clean, astro check 0/0/0, build 815 pages.

@dionysuz-bot
Copy link
Copy Markdown

Re-reviewed 8bc7f4aa. I don’t see any blocking correctness, architecture, or style issues.

The follow-up addresses the prior concerns cleanly: deploy uses an authenticated GitHub API token for build:fresh, same-path navigation now handles trailing-slash canonical URLs, route snapshot/link generation share the same predicates, malformed call refs fail clearly, and corrupt redirect data no longer fails silently.

Local verification:

  • npm test passed: 57/57
  • npm run lint passed
  • npm run check passed: 0 errors / 0 warnings / 0 hints
  • npm run build passed: 815 pages built

@dionysuzx
Copy link
Copy Markdown
Collaborator Author

dionysuzx commented Jun 8, 2026

EDIT: see #312 for the fast follow


migration to .astro files and more static html is for phase 2, but in the case of the /upgrade/glamsterdam page, which is very popular, i think there is an easy opportunity to just make the header / internal navbar static

i'm referring to this whole part that is above the main content:

← All Network Upgrades
Glamsterdam Upgrade
Major network upgrade featuring Block-level Access Lists and ePBS. Named after the combination of "Amsterdam" (execution layer upgrade, named after the previous Devconnect location) and "Gloas" (consensus layer upgrade, named after a star).

View Meta EIP Discussion
Upcoming
Overview
Stakeholders
Devnet Inclusion
Client Priority
Test Complexity
image

this is not blocking for implementation. at the least let's do it as a fast follow PR: so open a github issue and implement quickly (can also include the same situation on the /calls page for selecting call filters). i slightly prefer a separate issue/PR actually, to avoid scope-creeping this PR.

@dionysuzx
Copy link
Copy Markdown
Collaborator Author

@dionysuz-bot i don't really like this name browserLocation.tsx. we had navigation.tsx before, which looks better to me. if it's too wide, maybe we could do navigationHelpers.tsx, but i think navigation.tsx is actually best. what do you think?

@dionysuz-bot
Copy link
Copy Markdown

I agree that navigation.tsx is the better name here.

browserLocation.tsx is accurate about the implementation detail, but it reads too narrow for what callers are importing: Link, useNavigate, useLocation, and useSearchParams. This module is really the app’s small navigation boundary while Astro owns routing, so navigation.tsx communicates the call-site concept better.

I’d avoid navigationHelpers.tsx unless we need to disambiguate from a UI nav component. The Helpers suffix does not add much, and the module doc can carry the important nuance that this is browser-backed URL/navigation, not a client router.

@dionysuzx
Copy link
Copy Markdown
Collaborator Author

I agree that navigation.tsx is the better name here.

browserLocation.tsx is accurate about the implementation detail, but it reads too narrow for what callers are importing: Link, useNavigate, useLocation, and useSearchParams. This module is really the app’s small navigation boundary while Astro owns routing, so navigation.tsx communicates the call-site concept better.

I’d avoid navigationHelpers.tsx unless we need to disambiguate from a UI nav component. The Helpers suffix does not add much, and the module doc can carry the important nuance that this is browser-backed URL/navigation, not a client router.

@dionysuz-bot /operate land a commit on this PR that renames browserLocation.tsx to navigation.tsx. make sure to search for any other references to browserLocation, browser-location, or anything else that should change as part of the rename.

@dionysuz-bot
Copy link
Copy Markdown

Done. Pushed 213a0839 (Rename browser location helper to navigation) to this PR.

Renamed src/components/browserLocation.tsx to src/components/navigation.tsx, updated all imports, and updated the related eslint rationale plus the migration doc reference. I also searched for browserLocation, browser-location, BrowserLocation, and browserlocation; no stale matches remain.

Validation passed:

  • npm run check
  • npm run lint

Clarify navigation helper's purpose and suggest renaming.
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