[Frontend] Fix lore-proposal discovery: wrong link destination, triple-surfaced UI, and a proposals page reachable by one hidden link #375

Closed
opened 2026-08-25 20:42:14 +00:00 by claude-bot · 3 comments
Contributor

Impact: CRITICAL

Found in the August 2026 session lifecycle review (#319).

What the user experiences

Lore proposals are the payoff of the recording pipeline, and the one link the product gives a GM for this task sends them to the wrong page. SessionDetail's "Review proposals →" points at the wiki home, not the proposals queue. The actual proposals page (/wiki/proposals) has exactly one inbound link in the entire app, and it lives inside a sidebar that is hidden below 768px — so on a phone the page cannot be reached at all except by typing the URL. Meanwhile a third, divergent copy of the same queue is inlined on CampaignDetail, which only renders when the count is above zero (so a GM never learns the feature exists before it first fires), omits relationship proposals entirely, and truncates proposal bodies where the real page shows them in full. Both proposal fetches swallow errors, so a failed request looks identical to an empty queue.

Evidence

  • webapp/frontend/src/pages/SessionDetail.jsx:233 (also referenced at :104-106) — "Review proposals →" links to /campaigns/${session.campaign_id}/wiki, the storyline home, instead of /campaigns/${session.campaign_id}/wiki/proposals.
  • webapp/frontend/src/pages/CampaignStoryline.jsx:272,290-300 — the sole inbound link to /wiki/proposals ("AI Proposals") lives inside <aside className="hidden md:flex …">.
  • webapp/frontend/src/pages/CampaignDetail.jsx:2665-2768 — a second, ~110-line inline implementation of the proposal queue, gated on {isGm && loreProposals.length > 0 && (…)}, omitting relationship proposals and truncating bodies with line-clamp-3 (:2741) where WikiProposals.jsx:381 shows them in full.
  • webapp/frontend/src/pages/CampaignDetail.jsx:467-469 and webapp/frontend/src/pages/CampaignStoryline.jsx:160-161 — both fetchLoreProposals calls are .catch(() => {}), so a fetch failure and an empty queue render identically.
  • The label "Bot Proposals" (CampaignDetail.jsx:2668) is factually wrong, not merely inconsistent — per this repo's CLAUDE.md and webapp/backend/app/services/summary_events.py:78-80, lore proposals are generated by the backend Celery worker; the bot has nothing to do with them.

Why it matters for a hosted product

The one link the product offers for the payoff step of its flagship pipeline goes to the wrong place, and the correct page is functionally unreachable on a phone or tablet. A GM who does find it sees a divergent, less complete version depending on which page they happened to land on.

Proposed fix

Fix the one-line destination bug on SessionDetail.jsx:233. Move the /wiki/proposals entry link out of the hidden md:flex aside into the main content area, beside "New article" (CampaignStoryline.jsx:340-345), so it is reachable regardless of viewport. Delete the inline proposal queue on CampaignDetail (:2665-2768) in favour of a single always-visible summary row — "N suggested wiki updates → Review" or "No pending suggestions" when empty, so the feature is discoverable before it first fires — linking to WikiProposals as the one canonical review page. Distinguish a fetch error from an empty queue on both fetch sites. Rename "Bot Proposals" to something accurate, e.g. "Suggested wiki updates" (part of the milestone's broader naming cleanup — see the vocabulary issue in this milestone). This is the audit's P6, P23, C2, and C6.

Acceptance criteria

  • SessionDetail.jsx:233 links to /campaigns/:id/wiki/proposals, not /campaigns/:id/wiki.
  • The /wiki/proposals entry link is visible below the md breakpoint (moved out of the hidden md:flex aside).
  • The inline proposal queue on CampaignDetail is removed in favour of a single summary row linking to WikiProposals.
  • The summary row is visible even when there are zero pending proposals ("No pending suggestions"), not only when count > 0.
  • Both proposal-fetch call sites distinguish a request failure from a genuinely empty queue in the UI.
  • "Bot Proposals" is renamed to a label that does not imply the Discord bot generates them.
**Impact: CRITICAL** Found in the August 2026 session lifecycle review (#319). ## What the user experiences Lore proposals are the payoff of the recording pipeline, and the one link the product gives a GM for this task sends them to the wrong page. `SessionDetail`'s "Review proposals →" points at the wiki home, not the proposals queue. The actual proposals page (`/wiki/proposals`) has exactly one inbound link in the entire app, and it lives inside a sidebar that is hidden below 768px — so on a phone the page cannot be reached at all except by typing the URL. Meanwhile a third, divergent copy of the same queue is inlined on `CampaignDetail`, which only renders when the count is above zero (so a GM never learns the feature exists before it first fires), omits relationship proposals entirely, and truncates proposal bodies where the real page shows them in full. Both proposal fetches swallow errors, so a failed request looks identical to an empty queue. ## Evidence - `webapp/frontend/src/pages/SessionDetail.jsx:233` (also referenced at `:104-106`) — "Review proposals →" links to `/campaigns/${session.campaign_id}/wiki`, the storyline home, instead of `/campaigns/${session.campaign_id}/wiki/proposals`. - `webapp/frontend/src/pages/CampaignStoryline.jsx:272,290-300` — the sole inbound link to `/wiki/proposals` ("AI Proposals") lives inside `<aside className="hidden md:flex …">`. - `webapp/frontend/src/pages/CampaignDetail.jsx:2665-2768` — a second, ~110-line inline implementation of the proposal queue, gated on `{isGm && loreProposals.length > 0 && (…)}`, omitting relationship proposals and truncating bodies with `line-clamp-3` (`:2741`) where `WikiProposals.jsx:381` shows them in full. - `webapp/frontend/src/pages/CampaignDetail.jsx:467-469` and `webapp/frontend/src/pages/CampaignStoryline.jsx:160-161` — both `fetchLoreProposals` calls are `.catch(() => {})`, so a fetch failure and an empty queue render identically. - The label "Bot Proposals" (`CampaignDetail.jsx:2668`) is factually wrong, not merely inconsistent — per this repo's `CLAUDE.md` and `webapp/backend/app/services/summary_events.py:78-80`, lore proposals are generated by the backend Celery worker; the bot has nothing to do with them. ## Why it matters for a hosted product The one link the product offers for the payoff step of its flagship pipeline goes to the wrong place, and the correct page is functionally unreachable on a phone or tablet. A GM who does find it sees a divergent, less complete version depending on which page they happened to land on. ## Proposed fix Fix the one-line destination bug on `SessionDetail.jsx:233`. Move the `/wiki/proposals` entry link out of the `hidden md:flex` aside into the main content area, beside "New article" (`CampaignStoryline.jsx:340-345`), so it is reachable regardless of viewport. Delete the inline proposal queue on `CampaignDetail` (`:2665-2768`) in favour of a single always-visible summary row — *"N suggested wiki updates → Review"* or *"No pending suggestions"* when empty, so the feature is discoverable before it first fires — linking to `WikiProposals` as the one canonical review page. Distinguish a fetch error from an empty queue on both fetch sites. Rename "Bot Proposals" to something accurate, e.g. "Suggested wiki updates" (part of the milestone's broader naming cleanup — see the vocabulary issue in this milestone). This is the audit's P6, P23, C2, and C6. ## Acceptance criteria - [ ] `SessionDetail.jsx:233` links to `/campaigns/:id/wiki/proposals`, not `/campaigns/:id/wiki`. - [ ] The `/wiki/proposals` entry link is visible below the `md` breakpoint (moved out of the `hidden md:flex` aside). - [ ] The inline proposal queue on `CampaignDetail` is removed in favour of a single summary row linking to `WikiProposals`. - [ ] The summary row is visible even when there are zero pending proposals ("No pending suggestions"), not only when count > 0. - [ ] Both proposal-fetch call sites distinguish a request failure from a genuinely empty queue in the UI. - [ ] "Bot Proposals" is renamed to a label that does not imply the Discord bot generates them.
Author
Contributor

Note: the link destination half of this issue has already landed, in the v3.11.5 hotfix (see #417).

SessionDetail.jsx now points Review proposals → at /campaigns/{id}/wiki/proposals instead of the wiki home. It was taken early because #417 made that link render for the first time — the status field it depends on was never populated by the API — and shipping a newly-visible link to the wrong page would have been a new defect rather than a fixed one.

The rest of this issue's scope is unchanged and still open: the three divergent proposal surfaces that need merging to one, and the fact that /wiki/proposals has exactly one inbound link, inside a hidden md:flex aside, making it unreachable below 768 px.

Note: the **link destination** half of this issue has already landed, in the v3.11.5 hotfix (see #417). `SessionDetail.jsx` now points `Review proposals →` at `/campaigns/{id}/wiki/proposals` instead of the wiki home. It was taken early because #417 made that link render for the first time — the status field it depends on was never populated by the API — and shipping a newly-visible link to the wrong page would have been a new defect rather than a fixed one. **The rest of this issue's scope is unchanged and still open:** the three divergent proposal surfaces that need merging to one, and the fact that `/wiki/proposals` has exactly one inbound link, inside a `hidden md:flex` aside, making it unreachable below 768 px.
Author
Contributor

Picking this up as v4.3.0 phase 6 (#514), first on the wiki lane (#375#385#395#396; the silent-error sweep #377 runs after every lane has settled). The one-line link fix; the proposals entry link moves out of the phone-hidden aside; the inline queue on the campaign page becomes one always-visible summary row linking to the single review page; both fetch sites distinguish an error from an empty queue. The rename to "Suggested wiki updates" already landed with #379.

Picking this up as v4.3.0 phase 6 (#514), first on the wiki lane (#375 → #385 → #395 → #396; the silent-error sweep #377 runs after every lane has settled). The one-line link fix; the proposals entry link moves out of the phone-hidden aside; the inline queue on the campaign page becomes one always-visible summary row linking to the single review page; both fetch sites distinguish an error from an empty queue. The rename to "Suggested wiki updates" already landed with #379.
Author
Contributor

Done in PR #527 (auto-merging on green); ships with v4.3.0.

The SessionDetail.jsx link named here was already fixed: #417 took it as part of making lore_generation_status reach the client, and shipped it as "#375, partial" in v4.2.3. It now has the regression test it never got, and the file is otherwise untouched (another lane owns it this phase).

The rest: the wiki front page's only route to the queue was in its hidden md:flex rail, so there was no route at all from a phone. It now also sits beside "New article" in the main column, with the pending count on it; the rail copy stays, since removing it would be a desktop regression for no gain. CampaignDetail's 105-line inline copy of the queue is gone: it omitted relationship suggestions entirely, clamped every body to three lines, and was gated on count > 0, so it vanished exactly when a GM was looking for it. In its place, one always-visible row that says which of "N suggested wiki updates" / "No pending suggestions" / "Couldn't load suggested wiki updates." is true, with Review going to WikiProposals as the single canonical page. Both fetchLoreProposals sites stopped being .catch(() => {}), and both counts now include relationship proposals, because vocabulary.js defines "suggested wiki updates" as entries and relationships, so the old entry-only badge was a half-truth.

9 new tests. No backend change. The rename this issue also asked for ("Bot Proposals") landed with #379.

Done in PR #527 (auto-merging on green); ships with v4.3.0. The `SessionDetail.jsx` link named here was already fixed: #417 took it as part of making `lore_generation_status` reach the client, and shipped it as "#375, partial" in v4.2.3. It now has the regression test it never got, and the file is otherwise untouched (another lane owns it this phase). The rest: the wiki front page's only route to the queue was in its `hidden md:flex` rail, so there was no route at all from a phone. It now also sits beside "New article" in the main column, with the pending count on it; the rail copy stays, since removing it would be a desktop regression for no gain. `CampaignDetail`'s 105-line inline copy of the queue is gone: it omitted relationship suggestions entirely, clamped every body to three lines, and was gated on `count > 0`, so it vanished exactly when a GM was looking for it. In its place, one always-visible row that says which of "N suggested wiki updates" / "No pending suggestions" / "Couldn't load suggested wiki updates." is true, with **Review** going to `WikiProposals` as the single canonical page. Both `fetchLoreProposals` sites stopped being `.catch(() => {})`, and both counts now include relationship proposals, because `vocabulary.js` defines "suggested wiki updates" as entries *and* relationships, so the old entry-only badge was a half-truth. 9 new tests. No backend change. The rename this issue also asked for ("Bot Proposals") landed with #379.
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Quest-Board#375
No description provided.