Skip to content

fix(bounties): stop the board skeleton reading its grid class across the client boundary - #2515

Open
jwalkingjew wants to merge 1 commit into
preston/server-client-boundary-guardfrom
preston/bounties-loading-client-reference
Open

jwalkingjew wants to merge 1 commit into
preston/server-client-boundary-guardfrom
preston/bounties-loading-client-reference

Conversation

@jwalkingjew

Copy link
Copy Markdown
Collaborator

Stacked on #2478 — merge that first, then this. The diff against it is the bounties change alone.

The bug

app/bounties/loading.tsx is a Server Component. BountyBoardSkeleton puts BOARD_GRID_CLASS straight into a className, and that constant was declared in board-bounty-card.tsx, which is 'use client'.

Every export of a client module is a client reference on the server rather than the value it looks like — React writes it into the flight payload as an opaque placeholder. So the loading grid rendered with no grid classes at all, six placeholder blocks stacked in a column, until hydration replaced the whole loading state. BOARD_CARD_HEIGHT_PX went the same way into a style.

Found by the guard in #2478, where it was the one live case on the known-debt list.

The fix

Both constants move to board-layout.ts, which carries no directive.

That pulled the card heights along: BOARD_CARD_HEIGHT_PX is AVAILABLE_CARD_HEIGHT_PX, declared in bounty-card.tsx — also a client module — so putting the board's constants somewhere server-safe while leaving their input behind would have fixed nothing. The three sibling heights (COMPLETED_ / IN_PROGRESS_ / AVAILABLE_) go to bounty-card-layout.ts together, rather than splitting a set of three across two files for the next reader to puzzle over.

Both original modules re-export what they used to declare, so every existing importer — and every test that mocks them — is untouched. The only import that actually changes is the skeleton's, which is the one that crossed the boundary.

Verification

  • 102 tests across partials/bounties, partials/community-tab and the guard pass; lint and tsc clean.
  • The two entries come off test: guard against server components reading client-module values #2478's KNOWN list, and that test's staleness check is what enforces they had to — a fix that left them listed would fail it.
  • The guard now protects the fix. Point the skeleton's import back at board-bounty-card and it fails with exactly the pair again:
partials/bounties/bounty-board-skeleton.tsx -> BOARD_CARD_HEIGHT_PX  (from partials/bounties/board-bounty-card.tsx)
partials/bounties/bounty-board-skeleton.tsx -> BOARD_GRID_CLASS      (from partials/bounties/board-bounty-card.tsx)

What I did not check

I have not watched the bounties loading state render, before or after. The mechanism is confirmed by inspection and by the same flight-payload evidence that motivated #2478, but the visual symptom — a brief unstyled flash on a route behind a testnet gate and a feature flag — I am taking on reasoning. Worth ten seconds from someone who can open the board.

@vercel

vercel Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
geogenesis Ready Ready Preview Sep 25, 2026 1:25am UTC

Request Review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟢 Approval recommended

The constants now cross the server boundary safely while preserving existing client-facing imports.

Review effort: Balanced
Findings: None

What changed in this PR

Moves bounty layout constants into server-safe modules, fixing the server-rendered loading skeleton.

Changes:

  • Extracts card heights and board layout constants.
  • Preserves existing imports through re-exports.
  • Removes resolved cases from the server/client boundary guard.
File Description
client-values-in-server-boundaries.test.ts Removes fixed boundary violations.
bounty-card.tsx Imports and re-exports extracted heights.
bounty-card-layout.ts Defines server-safe card heights.
bounty-board-skeleton.tsx Uses server-safe board constants.
board-layout.ts Defines board grid and height constants.
board-bounty-card.tsx Uses and re-exports extracted constants.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@jwalkingjew
jwalkingjew force-pushed the preston/bounties-loading-client-reference branch from e1636ba to 8ee383a Compare September 21, 2026 19:56
@jwalkingjew
jwalkingjew force-pushed the preston/bounties-loading-client-reference branch from 8ee383a to 3de0018 Compare September 21, 2026 20:23
@jwalkingjew
jwalkingjew force-pushed the preston/bounties-loading-client-reference branch from 3de0018 to cfe83c5 Compare September 21, 2026 21:02
@jwalkingjew
jwalkingjew force-pushed the preston/bounties-loading-client-reference branch from cfe83c5 to 6103822 Compare September 21, 2026 21:07
@jwalkingjew
jwalkingjew force-pushed the preston/bounties-loading-client-reference branch from 6103822 to b19851a Compare September 21, 2026 21:29
@jwalkingjew
jwalkingjew force-pushed the preston/bounties-loading-client-reference branch from b19851a to 1775c04 Compare September 21, 2026 21:49
@jwalkingjew
jwalkingjew force-pushed the preston/bounties-loading-client-reference branch from 1775c04 to f52009f Compare September 21, 2026 22:54
@jwalkingjew
jwalkingjew force-pushed the preston/bounties-loading-client-reference branch from f52009f to 31aa320 Compare September 21, 2026 23:20
@jwalkingjew
jwalkingjew force-pushed the preston/bounties-loading-client-reference branch from 31aa320 to 14d8e1b Compare September 22, 2026 00:28
@jwalkingjew
jwalkingjew force-pushed the preston/bounties-loading-client-reference branch from 14d8e1b to d4c3931 Compare September 22, 2026 00:49
@jwalkingjew
jwalkingjew force-pushed the preston/bounties-loading-client-reference branch from d4c3931 to 29f611d Compare September 22, 2026 01:10
@jwalkingjew
jwalkingjew force-pushed the preston/bounties-loading-client-reference branch from 29f611d to 0140a71 Compare September 22, 2026 01:46
…the boundary

`app/bounties/loading.tsx` is a Server Component and `BountyBoardSkeleton` puts
`BOARD_GRID_CLASS` straight into a `className`. It was declared in
`board-bounty-card.tsx`, which is `'use client'`, and every export of a client
module is a client reference on the server rather than the value it looks like —
so the loading grid rendered with no grid at all until hydration replaced it.
`BOARD_CARD_HEIGHT_PX` went the same way into a `style`.

Both now live in `board-layout.ts`, which carries no directive. That needed the
card heights to come along, because `BOARD_CARD_HEIGHT_PX` is
`AVAILABLE_CARD_HEIGHT_PX` and that was declared in `bounty-card.tsx`, also a
client module — so the three sibling heights move to `bounty-card-layout.ts`
together rather than being split across two files. Both original modules
re-export what they used to declare, so every existing importer is untouched.

Found by the guard in #2478, which has the pair listed as known debt. They come
off that list here, and its staleness check is what makes sure they had to.

This branch was successfully deployed

1 active deployment
Preview — de6d856a Deployed Sep 25, 2026 by vercel[bot]
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