• v3.5.0 2ea6f26531

    v3.5.0
    Some checks failed
    Release / Build and push versioned images (push) Failing after 24s
    Release / Create Forgejo release (push) Successful in 25s
    CI / Docker image build (push) Successful in 30s
    CI / Backend lint (ruff) (push) Successful in 55s
    CI / Frontend tests, audit, and build (push) Successful in 1m43s
    CI / Bot tests and audit (push) Successful in 2m22s
    CI / Backend migration, tests, and audit (push) Successful in 4m4s
    Stable

    rbrooks released this 2026-07-17 15:24:21 +00:00 | 725 commits to main since this release

    Frontend platform & PWA.

    Modernises the SPA foundation: a shared API client with session-expiry
    handling, route-level code splitting, real ESLint enforcement, and installable
    PWA scaffolding. Frontend suite grew 69 → 106 tests. No API contract change.

    Added

    • [webapp] Quest Board is installable — a web manifest, icons (including a
      maskable one) and a service worker mean you can add it to a phone home screen
      and open it as a standalone app. The app shell and every route chunk are
      precached, so a refresh with no signal renders the real UI instead of the
      browser's offline error. Authenticated API responses are deliberately never
      cached: they always go to the network, so nothing stale is ever shown as
      current. New deploys activate immediately rather than leaving anyone on a
      stale bundle. (#108)

    Changed

    • [webapp] First visit now follows your system light/dark setting instead of
      always starting dark, and the theme is applied before first paint, so
      light-mode users no longer get a dark flash on every load. An explicit choice
      via the toggle still wins and persists; until you make one, the app keeps
      following the OS. (#108)
    • [webapp] Pages now load on demand — routes are code-split, cutting the
      initial JavaScript download by 64% (686 kB → 244 kB; 172 kB → 78 kB
      gzipped). Opening the dashboard no longer downloads the admin panel and the
      entire markdown toolchain first: the wiki's 169 kB markdown renderer is
      fetched only when you actually open a wiki article. (#105)

    Fixed

    • [webapp] An expired session now sends you to the login page — every
      src/api/* module runs through a shared client that handles 401 centrally.
      Previously auth was only checked once at mount, so when the 8h session expired
      mid-use every request failed while the app carried on rendering a dead page.
      The client also aborts in-flight requests on navigation (a slow response from
      a route you already left can no longer resolve late and overwrite fresher
      state) and de-duplicates identical concurrent GETs behind a short TTL cache,
      so a dashboard with N campaigns no longer issues N uncached requests per
      mount. (#103)
    • [frontend] ESLint now actually enforces the React hooks rules — the config
      had registered a no-op stub for eslint-plugin-react-hooks, so
      exhaustive-deps had never linted anything. Wiring up the real plugin
      surfaced five genuine findings, including a recording-dashboard effect that
      would have rebuilt its live-status EventSource on every render. (#21)
    • [deps] Renovate no longer decouples pydantic from pydantic-core — the
      compiled bot/requirements.txt lockfile is now managed by Renovate's
      pip-compile manager (which regenerates the lock) instead of
      pip_requirements (which edits pins in place, one package per PR). pydantic
      pins pydantic-core to an exact version, so in-place bumps broke every
      pydantic update with ResolutionImpossible. Lock regenerated to a consistent
      pair (pydantic 2.13.4 / pydantic-core 2.46.4), which also picked up a
      missing pyjwt pin required by redis. (#178)
    Downloads