Turn document

# Fix Forgejo CI terminal test mock alias

The remaining Forgejo-only failure was a Next.js module-shape mismatch in the terminal client component. I switched the terminal screen to a namespace import for `next/navigation` so Forgejo no longer trips over Bun's named-export resolution for `usePathname`.

Updated: 2026-05-30 01:57 EDT Beads: islandflow-3l6 Validation: targeted terminal test + full Bun suite passed

## New Changes as of 2026-05-30 01:57 EDT

This update follows the earlier Bun PATH and test-harness fixes. Forgejo was still failing inside the terminal component itself, where Bun 1.3.14 treated the direct `usePathname` import as a named-export mismatch. The component now reads the hook from the namespace import instead.

### Summary of changes

- Changed `apps/web/app/terminal.tsx` to import `next/navigation` as a namespace.
- Replaced the three direct `usePathname()` calls with `nextNavigation.usePathname()`.
- Left the earlier test mocks in place so the suite still covers both the package specifier and Bun's resolved path.

### Why this change was made

The previous test-level mocks were enough for local Bun, but Forgejo's Bun 1.3.14 runtime still errored on the named export lookup inside the client component. Changing the import shape removes that check instead of asking the test harness to paper over it.

### Code diff

###

    import * as nextNavigation from "next/navigation";
            Related issues or PRs
            islandflow-3l6

## Summary

The remaining Forgejo failure was inside the terminal client component, not the install or typecheck stages. Using a namespace import keeps Bun from tripping over the `usePathname` named-export lookup in the runner.

## Changes Made

- Updated `apps/web/app/terminal.tsx` to read `usePathname` through the `nextNavigation` namespace.
- Kept the earlier test-harness aliases intact, since they still cover the old runner behavior and make the tests resilient.
- Left the earlier Bun PATH and redirect-mock fixes intact, since they were already solving the other CI failure modes.

## Context

The repository already had the Bun executable path fix and the routes mock alias fix in place. The remaining failure surfaced only in the full CI-shaped test run, where Bun 1.3.14 was stricter about the terminal client component's direct named import from `next/navigation`.

## Important Implementation Details

- The terminal screen now reaches the pathname hook through the module namespace, which avoids Bun's stricter named-export check in CI.
- This stays narrowly scoped to the client component and does not change the route semantics or the visible UI behavior.
- The existing test mocks remain useful as guardrails, but the component import no longer depends on them to satisfy Bun's module loader.

## Relevant Diff Snippets

Rendered with `@pierre/diffs/ssr` from the current working tree. It shows the terminal client component switching to a namespace import for `next/navigation` and updating the three pathname reads accordingly.

apps/web/app/terminal.tsx
-4+4

    12345675369 unmodified lines53775378537953805381538253831844 unmodified lines72287229723072317232723372341863 unmodified lines9098909991009101910291039104"use client";
    import Link from "next/link";import { usePathname } from "next/navigation";import {  createContext,  memo,5369 unmodified lines};
    const useTerminalState = () => {  const pathname = usePathname();  const routeFeatures = useMemo(() => getRouteFeatures(pathname), [pathname]);  const [mode, setMode] = useState<TapeMode>("live");  const [replaySource, setReplaySource] = useState<string | null>(null);1844 unmodified lines};
    export const FlowFilterPopover = ({ filters, onChange }: FlowFilterPopoverProps) => {  const pathname = usePathname();  const [open, setOpen] = useState(false);  const rootRef = useRef<HTMLDivElement | null>(null);  const activeCount = countActiveFlowFilterGroups(filters);1863 unmodified lines
    export function TerminalAppShell({ children }: { children: ReactNode }) {  const state = useTerminalState();  const pathname = usePathname();  const [drawerOpen, setDrawerOpen] = useState(false);  const tickerFieldId = useId();  const tickerHintId = useId();12345675369 unmodified lines53775378537953805381538253831844 unmodified lines72287229723072317232723372341863 unmodified lines9098909991009101910291039104"use client";
    import Link from "next/link";import * as nextNavigation from "next/navigation";import {  createContext,  memo,5369 unmodified lines};
    const useTerminalState = () => {  const pathname = nextNavigation.usePathname();  const routeFeatures = useMemo(() => getRouteFeatures(pathname), [pathname]);  const [mode, setMode] = useState<TapeMode>("live");  const [replaySource, setReplaySource] = useState<string | null>(null);1844 unmodified lines};
    export const FlowFilterPopover = ({ filters, onChange }: FlowFilterPopoverProps) => {  const pathname = nextNavigation.usePathname();  const [open, setOpen] = useState(false);  const rootRef = useRef<HTMLDivElement | null>(null);  const activeCount = countActiveFlowFilterGroups(filters);1863 unmodified lines
    export function TerminalAppShell({ children }: { children: ReactNode }) {  const state = useTerminalState();  const pathname = nextNavigation.usePathname();  const [drawerOpen, setDrawerOpen] = useState(false);  const tickerFieldId = useId();  const tickerHintId = useId();

## Expected Impact for End-Users

Forgejo should stop failing on the terminal screen's CI-only module resolution mismatch, which reduces false negative pipeline runs and makes it easier to trust the branch when the suite passes.

## Validation

- `env PATH="$HOME/.bun/bin:/usr/bin:/bin" bun test apps/web/app/terminal.test.ts` passed: 74 tests, 0 failures.
- `env PATH="$HOME/.bun/bin:/usr/bin:/bin" bun test` passed: 250 tests, 0 failures.

## Issues, Limitations, and Mitigations

This fix is intentionally narrow. If another CI-only Next.js import path shows up later, the same namespace-import pattern should be applied to the affected component or test file instead of broadening the mock surface globally. That keeps the failure signal honest and the test harness easy to reason about.

## Follow-up Work

- Watch the next Forgejo run on this branch to confirm the namespace import clears the last failure.
- If another module-shape mismatch appears, fold the shared mock setup into a tiny helper rather than repeating the alias logic by hand.