All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
0.3.1.3 - 2026-05-27
- Dead read-path query functions left orphaned by the v0.3.1.0 single-embed migration:
getStatusLists,getRepoCards, andgetBoardData(src/lib/actions/board.ts), andgetCommentsForCards(src/lib/actions/project-info.ts). All four had zero callers — the/board/[id]read path has resolved throughgetBoardBundle's PostgREST nested embed since v0.3.1.0. Pure code removal: no runtime, schema, or behavior change.createDefaultStatusLists(still called bycreateFirstBoardIfNeededand the board-data no-columns fallback) andgetCommentsCore(still used bymaintenance-project-info.ts) are retained.
0.3.1.2 - 2026-05-27
- E2E coverage for the
/board/[id]not-found contract (e2e/logged-in/board-not-found.spec.ts): a malformed board id (rejected byboardIdSchemabefore any Postgres round-trip) and a well-formed but unseeded board UUID (.maybeSingle()resolvesnull) both render the segment-local "Board not found" boundary. Previously thegetBoardBundlenull⇒notFound()path was only covered indirectly; this is the first end-to-end assertion that an invalid/board/[id]URL reaches the 404 page. Asserts page content rather than HTTP status, since the Next.js App Router may stream a 200 beforenotFound()throws.
0.3.1.1 - 2026-05-27
- Unit tests for
logBoardTiming(src/lib/utils/board-timing.ts) locking theBOARD_TIMING_LOGopt-in contract: a silent no-op when the flag is off (the production default), and exactly one structured line taggedboard-timingcarrying the board id plus every timing segment when it is on. Regression cover against accidental production log spam and against renaming theboard-timingmodule tag that operators grep on.
0.3.1.0 - 2026-05-25
/board/[id]read path collapsed from a four-query waterfall (plus a duplicate board fetch) into a single PostgREST nested embed (board+statuslist+repocard+ each card'sprojectinfocomment), deduplicated acrossgenerateMetadataand the page render viaReact.cache(). The board detail page now resolves its data in one database round-trip.- Embedded repo cards are ordered by
orderascending, so that if a board ever exceeds the PostgREST embed row cap, truncation drops the highest-order cards deterministically (parity with the previousgetRepoCardsordering).remapBoardEmbedstill re-sorts for render, so normal boards are unaffected.
BOARD_TIMING_LOGflag andlogBoardTimingutility (src/lib/utils/board-timing.ts) for opt-in board read-path timing instrumentation.- Unit suites for
remapBoardEmbed(column/card ordering, comment parity, RLS no-leak) andgetBoardBundle(malformed-id 404 guard, no Postgres round-trip).
/board/<malformed-uuid>now returns 404 instead of 500:getBoardBundlevalidates the board id withboardIdSchemabefore querying Postgres, restoring parity with the previous read path and avoiding Sentry noise.generateMetadatafalls back to a generic "Board" title on a transient embed failure instead of throwing. AReact.cache-memoized rejection would otherwise surface during metadata generation; the page render still replays the same rejection, so genuine failures are not hidden.
0.3.0 - 2026-04-29
- Silent GitHub OAuth token refresh on expired provider token. When a Server Action returns
errorCode: 'GITHUB_TOKEN_MISSING', the client redirects to/api/auth/github/refresh?next=<path>, which silently re-runs OAuth via Supabase and returns the user to the original page. sanitizeNextPathutility for open-redirect-safenextquery parameter handling. Reused by/auth/callbackand/api/auth/github/refresh.getForwardedClientIputility consolidatingx-forwarded-forparsing for rate-limit IP resolution.handleGitHubTokenMissingclient helper with module-level lock andsessionStorageattempt counter for loop protection.errorCode: 'GITHUB_TOKEN_MISSING'discriminant onActionResult<T>failure variant; emitted by 4 GitHub Server Actions.- Unit tests for the refresh route, both new hooks, the client helper, the cookie module, the 401 interceptor, and
errorCodepropagation across GitHub Server Actions. - E2E spec covering the refresh route's attempt-cap bail-out (
e2e/logged-in/silent-token-refresh.spec.ts). - gstack skill-routing rules in
CLAUDE.md.
- GitHub provider token cookie TTL extended from 8 hours to 30 days, aligned with the Supabase refresh token lifetime. Cookie remains
httpOnly,secure(production), andSameSite=Lax. signOut,deleteAccount, and the axios 401 interceptor now use the shareddeleteGitHubTokenCookiehelper.auth.ts,github.ts,public-board.ts, and the refresh route now use the sharedgetForwardedClientIphelper for consistent x-forwarded-for parsing.
- Bookmark-after-8-hours bug: users returning to a bookmarked
/board/<id>after the previous 8-hour cookie window no longer hit "GitHub token not found. Please sign in again." The flow now silently re-authenticates and lands them back on the original page.
Initial tracked release.