feat(webapp): the page's infobox — suggested facts by type and system, known facts, a map or picture with a caption (#1279) #1324

Merged
claude-bot merged 18 commits from feat/1279-infobox into main 2026-11-03 06:44:54 +00:00
Collaborator

Part of #1279, stage 1 with no AI (D319–D322; the drawing is lane 6i, also in this PR). Stage 2, the local model's suggestions, is #1280.

Reader

  • The margin becomes one box: the page's name in a title bar (not a heading), its type, the picture or a map with a caption, and pronouns as the first row on PC, NPC and Deity pages. Pronouns no longer show under the title.
  • Then the facts: the type's suggested facts in order, each showing its stored value or, while empty, the value Quest Board knows. Other facts follow. A blank fact is never shown.
  • Known values: First met and Last seen from session appearances, Player with pronouns from the campaign member, class, level and ancestry from the character sheet, a place's Region from its map, and fields from the rules entry the page is based on.
  • Privacy: known values are computed per reader. Sheet values only reach someone who may read the sheet, and a region or a map picture only someone who may view that map. When the GM has written a value for a label, no known value is ever sent for it, whoever can read the written row; otherwise a GM-only "Last seen" would leak the live one. A security review caught a weaker first version of that guard, and it was fixed with a test.

Editor

  • The facts panel starts with the type's suggested facts, empty, under "Suggested for an NPC in Pathfinder 2e". A known value shows greyed with its source ("from sessions", …), and typing replaces it.
  • Removing a suggested fact dismisses it for that page. Changing the type adds the new type's facts under a divider and drops nothing.
  • The Picture panel offers "A picture | A map" and a Caption field.

Server

  • Migration e1279a0b1c2d revises d1321c0e5a7b: lore_entries.image_caption, image_map_id (FK maps, SET NULL) and facts_dismissed (JSONB, default []). The downgrade is real.
  • app/services/suggested_facts.json holds the lists from lane 6i for Pathfinder 2e, D&D 5e and Generic.
  • The article read, create and update return image_caption, image_map and facts. GET /api/campaigns/{id}/wiki/suggested-facts is new. A map from another campaign is refused with 422.
  • Export, import and setup copy carry the caption, the dismissed facts, and the map (remapped).
  • The bot is unaffected, so the contract isn't bumped.

Checked

  • Backend, on the dev host: 115 passed across the new infobox tests, the export round trip, the migration pin, setup copy and lore service; the maps export tests passed alone (63). Lint, the version check and the changelog check are clean. Alembic has a single head, and upgrade, downgrade and upgrade run clean.
  • Frontend, on the merged branch: src/pages/wiki and src/wiki, 41 files and 604 tests passed. eslint has 0 errors.
  • Not yet: the full backend suite (CI), and the render check, which is running.

New wording goes on the Wording Review page before merge.

🤖 Generated with Claude Code

Part of #1279, stage 1 with no AI (D319–D322; the drawing is lane 6i, also in this PR). Stage 2, the local model's suggestions, is #1280. **Reader** - The margin becomes one box: the page's name in a title bar (not a heading), its type, the picture or a map with a caption, and pronouns as the first row on PC, NPC and Deity pages. Pronouns no longer show under the title. - Then the facts: the type's suggested facts in order, each showing its stored value or, while empty, the value Quest Board knows. Other facts follow. A blank fact is never shown. - Known values: First met and Last seen from session appearances, Player with pronouns from the campaign member, class, level and ancestry from the character sheet, a place's Region from its map, and fields from the rules entry the page is based on. - **Privacy:** known values are computed per reader. Sheet values only reach someone who may read the sheet, and a region or a map picture only someone who may view that map. When the GM has written a value for a label, no known value is ever sent for it, whoever can read the written row; otherwise a GM-only "Last seen" would leak the live one. A security review caught a weaker first version of that guard, and it was fixed with a test. **Editor** - The facts panel starts with the type's suggested facts, empty, under "Suggested for an NPC in Pathfinder 2e". A known value shows greyed with its source ("from sessions", …), and typing replaces it. - Removing a suggested fact dismisses it for that page. Changing the type adds the new type's facts under a divider and drops nothing. - The Picture panel offers "A picture | A map" and a Caption field. **Server** - Migration `e1279a0b1c2d` revises `d1321c0e5a7b`: `lore_entries.image_caption`, `image_map_id` (FK maps, SET NULL) and `facts_dismissed` (JSONB, default `[]`). The downgrade is real. - `app/services/suggested_facts.json` holds the lists from lane 6i for Pathfinder 2e, D&D 5e and Generic. - The article read, create and update return `image_caption`, `image_map` and `facts`. `GET /api/campaigns/{id}/wiki/suggested-facts` is new. A map from another campaign is refused with 422. - Export, import and setup copy carry the caption, the dismissed facts, and the map (remapped). - The bot is unaffected, so the contract isn't bumped. **Checked** - Backend, on the dev host: 115 passed across the new infobox tests, the export round trip, the migration pin, setup copy and lore service; the maps export tests passed alone (63). Lint, the version check and the changelog check are clean. Alembic has a single head, and upgrade, downgrade and upgrade run clean. - Frontend, on the merged branch: `src/pages/wiki` and `src/wiki`, 41 files and 604 tests passed. eslint has 0 errors. - **Not yet:** the full backend suite (CI), and the render check, which is running. New wording goes on the Wording Review page before merge. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
A title bar, the picture or one of the campaign's maps with a
caption, the facts that are filled, and the type's suggested facts
by game system in the editor. Facts Quest Board already knows (from
sessions, the sheet, the member, the map or the rules entry) are
filled in until the GM types over them. Four questions for review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The owner's answers to Q210–Q213. D316 notes where Find's Browse
button was built (#1320).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	docs/design/redesign-surfaces/sources/intro.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Migration e1279a0b1c2d (caption, map picture, dismissed facts), the
suggested_facts.json lists from lane 6i, wiki_facts (per-reader known values),
the article read and entry write, the suggested-facts read, and the campaign
file's round trip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge remote-tracking branch 'origin/main' into feat/1279-infobox
Some checks failed
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been cancelled
CI / Which journeys does this change need? (pull_request) Has been cancelled
CI / Docker image build (pull_request) Has been cancelled
CI / Feedback site tests (pull_request) Has been cancelled
CI / Frontend (shard 2/2) (pull_request) Has been cancelled
CI / Bot tests and audit (pull_request) Has been cancelled
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Has been cancelled
CI / Backend lint (ruff) (pull_request) Has been cancelled
CI / Backend migration, tests, and audit (pull_request) Has been cancelled
CI / Frontend (shard 1/2) (pull_request) Has been cancelled
CI / Does this change touch the recording path? (pull_request) Has been cancelled
CI / Bot/backend version sync (pull_request) Has been cancelled
9f146384b2
fix(webapp): the infobox says GMs only, as everywhere else (#1279, #1296)
Some checks failed
CI / Backend lint (ruff) (pull_request) Successful in 1m5s
CI / Feedback site tests (pull_request) Successful in 54s
CI / Docker image build (pull_request) Successful in 33s
CI / Bot/backend version sync (pull_request) Successful in 52s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 2m5s
CI / Does this change touch the recording path? (pull_request) Successful in 24s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Which journeys does this change need? (pull_request) Successful in 36s
CI / Bot tests and audit (pull_request) Successful in 3m54s
CI / Frontend (shard 2/2) (pull_request) Successful in 9m57s
CI / Frontend (shard 1/2) (pull_request) Successful in 11m38s
CI / End-to-end journeys (desktop) (pull_request) Has been cancelled
CI / Backend migration, tests, and audit (pull_request) Has been cancelled
45c666e042
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(webapp): a Generic campaign's facts say "Suggested for an NPC" (#1279)
Some checks failed
CI / Backend lint (ruff) (pull_request) Successful in 28s
CI / Which journeys does this change need? (pull_request) Successful in 15s
CI / Does this change touch the recording path? (pull_request) Successful in 17s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Feedback site tests (pull_request) Successful in 50s
CI / Bot/backend version sync (pull_request) Successful in 54s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 2m49s
CI / Bot tests and audit (pull_request) Successful in 4m54s
CI / Frontend (shard 1/2) (pull_request) Successful in 8m27s
CI / Docker image build (pull_request) Successful in 10m22s
CI / Frontend (shard 2/2) (pull_request) Successful in 17m56s
CI / End-to-end journeys (desktop) (pull_request) Successful in 31m10s
CI / Backend migration, tests, and audit (pull_request) Failing after 1h11m54s
7d7133e6af
The render check showed "Suggested for an NPC in Generic". Generic
names no system.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Collaborator

Render check: the seeded journeys stack built from this branch, light and dark, 1280×800 and 390×844, 36 shots. The migration ran up to e1279a0b1c2d.

  • Every page returned HTTP 200, with 0 console errors, no axe violations (WCAG 2.2 AA), and no sideways scroll on a phone.
  • NPC as the GM: the title bar, "NPC", picture and caption, then Pronouns, Role, First met and Last seen (known, linked to the sessions), Allegiance, and a GMs-only Secret row with its chip.
  • The same page as a player: no Secret row.
  • A settlement with a map as its picture: the thumbnail with its "Map" chip, the caption, and "Open the map ›".
  • An empty page: the box is only the title and type.
  • A PC: pronouns in the box, not under the title.
  • The editor: the suggested rows, the known values greyed with "from sessions", and "Empty facts aren't shown." Changing NPC to Settlement shows "Settlement suggests 7 facts. Your 3 filled facts stay." and keeps every filled row.
  • The Picture panel: both modes, with Caption.
  • Saving a caption: PATCH 200, the response carries facts, and the reader shows the new caption.
  • Contrast: the title bar is 7.8:1 (light) and 8.3:1 (dark); the greyed known value is 6.2:1 and 6.1:1. Targets are at least 34 px.

Fixed after the check:

  • In 7d7133e6, a Generic campaign's sub-line said "Suggested for an NPC in Generic". It now reads "Suggested for an NPC".
  • In 45c666e0, the chip said "GM only", and now says "GMs only", as everywhere else (#1296).

Not exercised in the render: known values from the map, the sheet and the member. The seed's campaign is Generic, and its PC list has no sheet facts. The backend tests cover each of these, including the denials.

Seen, not caused by this PR: the seed's campaign has "Pathfinder 2e" as free text but isn't linked to the registry, so it reads as Generic everywhere that uses the linked system (rules browsing, sheets, and now these facts).

**Render check:** the seeded journeys stack built from this branch, light and dark, 1280×800 and 390×844, 36 shots. The migration ran up to `e1279a0b1c2d`. - Every page returned HTTP 200, with 0 console errors, no axe violations (WCAG 2.2 AA), and no sideways scroll on a phone. - **NPC as the GM:** the title bar, "NPC", picture and caption, then Pronouns, Role, First met and Last seen (known, linked to the sessions), Allegiance, and a GMs-only Secret row with its chip. - **The same page as a player:** no Secret row. - **A settlement with a map as its picture:** the thumbnail with its "Map" chip, the caption, and "Open the map ›". - **An empty page:** the box is only the title and type. - **A PC:** pronouns in the box, not under the title. - **The editor:** the suggested rows, the known values greyed with "from sessions", and "Empty facts aren't shown." Changing NPC to Settlement shows "Settlement suggests 7 facts. Your 3 filled facts stay." and keeps every filled row. - **The Picture panel:** both modes, with Caption. - **Saving a caption:** PATCH 200, the response carries `facts`, and the reader shows the new caption. - **Contrast:** the title bar is 7.8:1 (light) and 8.3:1 (dark); the greyed known value is 6.2:1 and 6.1:1. Targets are at least 34 px. **Fixed after the check:** - In 7d7133e6, a Generic campaign's sub-line said "Suggested for an NPC in Generic". It now reads "Suggested for an NPC". - In 45c666e0, the chip said "GM only", and now says "GMs only", as everywhere else (#1296). **Not exercised in the render:** known values from the map, the sheet and the member. The seed's campaign is Generic, and its PC list has no sheet facts. The backend tests cover each of these, including the denials. **Seen, not caused by this PR:** the seed's campaign has "Pathfinder 2e" as free text but isn't linked to the registry, so it reads as Generic everywhere that uses the linked system (rules browsing, sheets, and now these facts).
test: the maps round trip downgrades the infobox migration first (#1279)
Some checks failed
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m2s
CI / Backend lint (ruff) (pull_request) Successful in 1m9s
CI / Bot/backend version sync (pull_request) Successful in 32s
CI / Does this change touch the recording path? (pull_request) Successful in 18s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Which journeys does this change need? (pull_request) Successful in 19s
CI / Feedback site tests (pull_request) Successful in 1m37s
CI / Bot tests and audit (pull_request) Successful in 2m18s
CI / Docker image build (pull_request) Successful in 5m42s
CI / Frontend (shard 2/2) (pull_request) Successful in 7m42s
CI / Frontend (shard 1/2) (pull_request) Successful in 9m25s
CI / End-to-end journeys (desktop) (pull_request) Successful in 15m29s
CI / Backend migration, tests, and audit (pull_request) Failing after 41m54s
19226a99bf
e1279a0b1c2d adds a foreign key to maps, so dropping maps in
8b8c9d0e1f2a's downgrade failed under create_all. The real chain
downgrades the later migration first; the test now does too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude-bot scheduled this pull request to auto merge when all checks succeed 2026-11-03 05:01:44 +00:00
Author
Collaborator

CI: the backend job failed on tests/wiki/test_maps_migration.py::test_up_down_and_up_again. The suite builds its schema with create_all, so the new lore_entries.image_map_id foreign key was there when 8b8c9d0e1f2a's downgrade dropped maps (DependentObjectsStillExistError). It ran with -x, and the job had 9,755 passed before it.

Fix, in 19226a99: the test's round trips now downgrade e1279a0b1c2d first and upgrade it last, as the real chain does. The migrations are unchanged. The maps and infobox migration tests passed on the dev host (13), and lint is clean.

The owner approved wording s-001 to s-009. This PR is set to merge when CI passes.

**CI:** the backend job failed on `tests/wiki/test_maps_migration.py::test_up_down_and_up_again`. The suite builds its schema with `create_all`, so the new `lore_entries.image_map_id` foreign key was there when `8b8c9d0e1f2a`'s downgrade dropped `maps` (`DependentObjectsStillExistError`). It ran with `-x`, and the job had 9,755 passed before it. **Fix**, in 19226a99: the test's round trips now downgrade `e1279a0b1c2d` first and upgrade it last, as the real chain does. The migrations are unchanged. The maps and infobox migration tests passed on the dev host (13), and lint is clean. The owner approved wording s-001 to s-009. This PR is set to merge when CI passes.
8d4e5f6a7b8c's downgrade also drops maps, which e1279a0b1c2d's
foreign key depends on under create_all.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The parties migration's upgrade doesn't make maps again, so the
infobox foreign key returns only where maps exists. A session's
title read by wiki_facts is listed as not a page's, and a type
label no longer uses str.title().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
style: ruff format wiki_facts (#1279)
All checks were successful
CI / Bot/backend version sync (pull_request) Successful in 56s
CI / Backend lint (ruff) (pull_request) Successful in 1m3s
CI / Which journeys does this change need? (pull_request) Successful in 12s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Does this change touch the recording path? (pull_request) Successful in 13s
CI / Feedback site tests (pull_request) Successful in 1m29s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m48s
CI / Bot tests and audit (pull_request) Successful in 3m4s
CI / Docker image build (pull_request) Successful in 5m20s
CI / Frontend (shard 2/2) (pull_request) Successful in 10m24s
CI / Frontend (shard 1/2) (pull_request) Successful in 11m50s
CI / End-to-end journeys (desktop) (pull_request) Successful in 24m29s
CI / Backend migration, tests, and audit (pull_request) Successful in 39m4s
50ef9a2466
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Collaborator

CI again: tests/wiki/test_parties_migration.py::test_up_down_and_up_again failed in the same way. 8d4e5f6a7b8c also drops maps in its downgrade. Those two are the only migrations that do.

To find anything else hiding behind -x, I ran every migration test plus all of tests/wiki on the dev host. That surfaced one more failure, test_title_readers: the new code's session-title read needed listing, and a type label used str.title().

Fixes:

  • 7e5586a2 and a135bbf0: the parties round trip downgrades the infobox migration first and re-adds its key only where maps exists. That migration's upgrade doesn't recreate maps.
  • a135bbf0: the session title is listed as not a page's title, and the type label no longer uses .title().
  • 50ef9a24: wiki_facts.py is formatted.

Result: 2,096 passed, 0 failed, and ruff format and check are clean. The PR is re-scheduled to merge when CI passes.

**CI again:** `tests/wiki/test_parties_migration.py::test_up_down_and_up_again` failed in the same way. `8d4e5f6a7b8c` also drops `maps` in its downgrade. Those two are the only migrations that do. To find anything else hiding behind `-x`, I ran **every migration test plus all of `tests/wiki`** on the dev host. That surfaced one more failure, `test_title_readers`: the new code's session-title read needed listing, and a type label used `str.title()`. **Fixes:** - **7e5586a2 and a135bbf0:** the parties round trip downgrades the infobox migration first and re-adds its key only where `maps` exists. That migration's upgrade doesn't recreate `maps`. - **a135bbf0:** the session title is listed as not a page's title, and the type label no longer uses `.title()`. - **50ef9a24:** `wiki_facts.py` is formatted. **Result:** 2,096 passed, 0 failed, and ruff format and check are clean. The PR is re-scheduled to merge when CI passes.
Sign in to join this conversation.
No description provided.