Skip to content

fix(demo,ui): correct /api/news handler shape + bump persist version v4→v5#240

Merged
luokerenx4 merged 1 commit into
masterfrom
fix/demo-news-handler-shape
Jun 1, 2026
Merged

fix(demo,ui): correct /api/news handler shape + bump persist version v4→v5#240
luokerenx4 merged 1 commit into
masterfrom
fix/demo-news-handler-shape

Conversation

@luokerenx4
Copy link
Copy Markdown
Contributor

Summary

Two coupled fixes for the news crash the user reported (demo mode):
clicking into News crashed the page AND poisoned the persisted UI
state so subsequent reloads kept crashing.

What was wrong

Fix 1: `/api/news` handler shape mismatch (the crash)

PR #232's legacy chat excision replaced the old news/notifications handler with a new `ui/src/demo/handlers/newsList.ts` that returned:

```ts
{ articles: [...], hasMore: false }
```

with each article shaped `{ id, title, source, url, publishedAt, summary, tickers }`.

But `ui/src/api/types.ts` defines the contract as `NewsListResponse`:

```ts
{ items: NewsArticle[], count, lookback }
```

with each `NewsArticle` shaped `{ time, title, content, source, link, categories }`.

`NewsPage` calls `setArticles(res.items)` — wrong shape → `articles = undefined` → render does `[...articles].reverse()` → throws "undefined is not iterable" as an unhandled exception.

Fix: rewires the handler to use `demoNewsArticles` (the PR-4 Stage 2 fixture that has the correct `NewsArticle` shape with 6 realistic items) and the correct `{ items, count, lookback }` wrapper.

Fix 2: persist version bump v4 → v5 (the recovery)

A user who'd already opened the news tab has `selectedSidebar:'news'` + a news-kind tab persisted to `localStorage["openalice.workspace.v2"]`. Reload replays that selection → re-triggers the crash → loop. Even after the handler fix, anyone who hit the bug once stays stuck.

Bumping the zustand persist version from 4 to 5 makes rehydrate drop the poisoned state (no migrate function — schema bump clears, per the store's loud-fail contract; matches the v4 bump pattern from the legacy chat excision).

Cost: users with healthy state lose their open-tabs/layout. Necessary — they were on the crash path too if they navigated to news.

Pattern note: same bug pattern as PR #235

PR #232 also introduced an inline ad-hoc type for the `pushToolGate` test probe that didn't match the real `AgentWorkResultProbe` interface (fixed in #235). This is the second case in the same merge where the rewrite introduced a structural shape mismatch that passed esbuild (no type enforcement) but breaks at runtime.

In both cases, importing/referencing the canonical type from `ui/src/api/types.ts` or `src/core/agent-work.ts` instead of writing inline shapes would have caught it at strict tsc time. Worth flagging for any future Stage-2-ish demo handler additions.

Verification

Check Result
`pnpm -F open-alice-ui exec tsc -b` clean
`pnpm -F open-alice-ui build` (prod) main chunk has zero `demoNewsArticles` / fixture-string leaks
`pnpm dev:demo` + Playwright `/news` lists 6 articles, expand-article click works, single zustand info log "couldn't be migrated since no migrate function was provided" is the expected v4→v5 dropping behavior

Boundary touch

`ui/src/tabs/store.ts` is non-demo UI code, but the change is a one-line version-number bump with a comment — same shape as v4's bump from PR #232 (commit 7415131 "bump tab-store persist version").

🤖 Generated with Claude Code

…v4→v5

Two coupled fixes.

## 1. News handler shape mismatch (the crash)

PR #232's legacy-chat excision replaced the old notifications/news
handler with a new `newsList.ts` that returns:

  { articles: [...], hasMore: false }

with each article shaped { id, title, source, url, publishedAt,
summary, tickers }.

But the contract per ui/src/api/types.ts is NewsListResponse:

  { items: NewsArticle[], count, lookback }

with each NewsArticle shaped { time, title, content, source, link,
categories }.

NewsPage calls `setArticles(res.items)` — wrong shape gives
`articles = undefined`, then render does `[...articles].reverse()`
which throws "undefined is not iterable" as an unhandled exception.
React error boundary upstream catches it on the page level but the
crash poisons any persisted tab state pointing at the news view.

Rewires the handler to use `demoNewsArticles` (PR-4 Stage 2 fixture,
correct NewsArticle shape, 6 realistic items) and the correct
{ items, count, lookback } wrapper.

## 2. Persist version bump (the recovery)

A user who'd already opened the news tab has `selectedSidebar:'news'`
+ a news tab kind persisted to localStorage `openalice.workspace.v2`.
Reload replays that selection → re-triggers the crash → loop. Bumping
the persist version from 4 to 5 makes zustand drop the poisoned state
on rehydrate (no migrate function — schema bump clears, per this
store's loud-fail contract — matching v4's bump pattern from the
legacy chat excision).

Cost: users with healthy state lose their open-tabs/layout. Necessary
trade-off — they were on the crash path too if they navigated to news.

Verified:
- pnpm -F open-alice-ui exec tsc -b: clean
- pnpm -F open-alice-ui build (prod): main chunk has zero
  `demoNewsArticles` / fixture-string leaks
- pnpm dev:demo + Playwright: /news lists 6 articles correctly,
  expand-article click works, single zustand info log "couldn't be
  migrated since no migrate function was provided" is the expected
  v4→v5 dropping behavior

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 1, 2026

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

Project Deployment Actions Updated (UTC)
openalice-demo Ready Ready Preview, Comment Jun 1, 2026 3:50am

Request Review

@luokerenx4 luokerenx4 merged commit fae8c19 into master Jun 1, 2026
3 checks passed
pull Bot pushed a commit to bhardwajRahul/OpenAlice that referenced this pull request Jun 1, 2026
…nging /api/* surfaces

Three recent demo crashes (TraderAlice#235 / TraderAlice#238 / TraderAlice#240) all came from the same
pattern: a refactor changed what production code returns or expects,
but the parallel demo handler in `ui/src/demo/handlers/` kept the old
(or invented an ad-hoc) shape. `pnpm test` doesn't catch it because
esbuild doesn't enforce types.

Adds demo mode to the existing "Subsystem guides" section alongside
the event/listener/producer guide. The text names the two cheap habits
that prevent the failure mode:

1. Import the canonical type from `ui/src/api/types.ts` (or wherever
   the contract lives) when writing a demo handler, instead of inlining
   an ad-hoc shape.
2. Run `pnpm -F open-alice-ui dev:demo` and walk the affected surface
   before declaring the refactor done. The catchAll's
   `[demo] unmocked …` warn surfaces missing endpoints; visible crashes
   surface shape mismatches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant