Turn document · 2026-05-19

# Upgrade apps/web to Next.js 16.2.6

The web app now builds and passes focused validation on Next.js 16.2.6 with React 19. The change keeps route behavior and synthetic admin proxy behavior intact while refreshing the root and Docker workspace Bun lockfiles.

## Summary

Upgraded `apps/web` from the Next 14 / React 18 stack to Next 16.2.6 and React 19.2.x. The Bun lockfile was refreshed, the Docker workspace lock snapshot was synced, and a React 19 nullable ref type issue exposed by the Next 16 build was fixed.

## Changes Made

- Updated `apps/web/package.json` to request `next ^16.2.6`, `react ^19.2.0`, and `react-dom ^19.2.0`.
- Updated React type dependencies to `@types/react ^19.2.7` and added `@types/react-dom ^19.2.3`.
- Ran `bun install`, which resolved Next to `16.2.6` and React/React DOM to `19.2.6` in `bun.lock`.
- Ran `bun run sync:docker-workspace` so `deployment/docker/workspace-root/bun.lock` matches the root lock snapshot.
- Adjusted the terminal list ref types to accept `HTMLDivElement | null`, matching React 19's stricter ref object typing.
- Allowed Next 16 to regenerate `apps/web/next-env.d.ts` with its updated TypeScript reference comment and generated route type import.

## Context

The requested upgrade was intentionally dependency-focused. No routes, backend contracts, environment variable names, or shared package exports were changed. Before editing, the web build and the targeted route tests passed on the previous locked Next 14.2.35 stack.

## Important Implementation Details

No broad codemod was run. The only source-code change was a targeted type correction in `apps/web/app/terminal.tsx`. Next 16's build now runs with Turbopack by default in this project and completed successfully after the ref typing was narrowed to the actual nullable runtime value.

The Docker workspace sync changed the mirrored lockfile, but did not need to rewrite the mirrored package manifest or TypeScript base config.

## Relevant Diff Snippets

    "next": "^16.2.6",
    "react": "^19.2.0",
    "react-dom": "^19.2.0",
    "@types/react": "^19.2.7",
    "@types/react-dom": "^19.2.3"

    type ListScrollState = {
      listRef: React.RefObject<HTMLDivElement | null>;
      listNode: HTMLDivElement | null;
      setListRef: (node: HTMLDivElement | null) => void;
    };

## Expected Impact for End-Users

There should be no intentional user-facing behavior change. The expected visible behavior remains: `/`, `/tape`, and `/news` render the terminal app; `/signals`, `/charts`, and `/replay` redirect to `/`; synthetic admin API routes keep their gated proxy behavior.

## Validation

- Baseline before edits: `bun --cwd=apps/web run build` passed on Next 14.2.35.
- Baseline before edits: `bun test apps/web/app/routes.test.ts` passed, 3 tests.
- Baseline before edits: `bun test apps/web/app/terminal.test.ts` passed, 70 tests.
- Baseline before edits: `bun test apps/web/app/api/admin/synthetic/routes.test.ts` passed, 4 tests.
- After upgrade: `bun --cwd=apps/web run build` passed on Next 16.2.6.
- After upgrade: `bun test apps/web/app/routes.test.ts` passed, 3 tests.
- After upgrade: `bun test apps/web/app/terminal.test.ts` passed, 70 tests.
- After upgrade: `bun test apps/web/app/api/admin/synthetic/routes.test.ts` passed, 4 tests.
- After upgrade: `bun run check:docker-workspace` passed.
- Manual smoke: `bun run dev:web` served Next 16.2.6 on `localhost:3000`.
- Manual smoke: browser checks confirmed `/`, `/tape`, and `/news` render with title `Islandflow Terminal`.
- Manual smoke: `/signals`, `/charts`, and `/replay` returned `307` redirects to `/`.
- Manual smoke: synthetic admin status and control routes returned gated `404` responses when the internal UI flag was off.

## Issues, Limitations, and Mitigations

During `dev:web` smoke testing, the browser logged a live socket channel validation warning because only the web app was running, not the full backend service stack. Route rendering, redirect behavior, and gated synthetic admin proxy behavior were still verified. A full-stack live feed verification can be done separately with `bun run dev` if needed.

The upgrade did not include a full monorepo test run because the acceptance bar was intentionally web-focused.

## Follow-up Work

- No required follow-up Beads issue was opened for this upgrade.
- Optional: run a full-stack live feed smoke with infra and services running if you want runtime stream confidence beyond the web-focused acceptance checks.
- Optional: run the full monorepo `bun test` suite before a larger release branch merge.

## Helpful Links

- Next.js 16 upgrade guide
- React 19 release notes
- Bun install documentation