feat(webapp): browse in place, slice 1 — the sheet's pickers open on what fits (#1243) #1318

Merged
claude-bot merged 12 commits from feat/1243-sheet-pickers into main 2026-11-02 21:30:13 +00:00
Collaborator

Part of #1243, slice 1 of 3 (D317: sheet first, then Prep's drawer, then Find). It includes the design (lane 6h, D314–D317) from #1317, which this replaces.

Server

  • /sheets/{id}/picker and /sheets/{id}/options take stage 3's f_*, sort, page, per_page and all=1.
  • Both return:
    • fits: what fits the character at that step (D314);
    • fits_label;
    • filters and sorts, counted against the selection;
    • total.
  • fits is declared as data in each system's browse.json, using a documented vocabulary of facts (docs/CONTENT-PACKS.md):
    • PF2e feat: type, class or ancestry, and level, which is the level being reached at level-up;
    • PF2e spell: tradition, and rank up to the highest;
    • PF2e item: level, and price up to the gold on the sheet;
    • 5e spell: class, and level up to the highest slot;
    • 5e feat: category.
    • A fact the sheet lacks is left out.
  • With no params, the old D291 narrowing is kept.
  • /options now always answers an object, {results, fits, …}, instead of a bare list. Its only caller is LevelUp, which is updated.
  • A widened list marks rows the character can't take with eligible: false and a why. Preview and apply still refuse them; a test proves it (Sudden Charge for a rogue gives "A fighter feat." and a 422).

Screen

  • fitFilters.jsx, shared by Add to the sheet and level-up's feat step (5e's improvement-or-feat step too):
    • the picker opens, takes fits, and asks again with it, so the counts and the total agree;
    • the head reads "Feats N · fit Ossian · 3 set · Show every one";
    • stage 3's filter row, which folds behind "Filters (N)" on a phone;
    • a Pager.
  • Rows the character can't take are greyed, with their reason, and their Add or Choose button is disabled.

Checked

  • Backend, on postgres:18: lint and checks are clean.
    • 20 new tests.
    • One run of the sheet suite plus the reference browse, facets, search and library tests gave 904 passed and 1 failed: a bound-counting test that broke on an extra picker call. That call was removed.
    • The sheet and reference subset then gave 187 passed.
  • Frontend: lint has 0 errors.
    • 18 new tests.
    • src/pages/sheet, wiki, ui and Home: 1116 passed.
    • The whole suite, run before the last test fix, had 2 failures. One was the LevelUp test that was then fixed. The other was a Home test that passed alone.
  • Not yet: CI's full suites and a render check.

New wording goes on the Wording Review page before this merges.

🤖 Generated with Claude Code

Part of #1243, slice 1 of 3 (D317: sheet first, then Prep's drawer, then Find). It includes the design (lane 6h, D314–D317) from #1317, which this replaces. **Server** - `/sheets/{id}/picker` and `/sheets/{id}/options` take stage 3's `f_*`, `sort`, `page`, `per_page` and `all=1`. - Both return: - `fits`: what fits the character at that step (D314); - `fits_label`; - `filters` and `sorts`, counted against the selection; - `total`. - `fits` is declared as data in each system's `browse.json`, using a documented vocabulary of facts (`docs/CONTENT-PACKS.md`): - PF2e feat: type, class or ancestry, and level, which is the level being reached at level-up; - PF2e spell: tradition, and rank up to the highest; - PF2e item: level, and price up to the gold on the sheet; - 5e spell: class, and level up to the highest slot; - 5e feat: category. - A fact the sheet lacks is left out. - With no params, the old D291 narrowing is kept. - **`/options` now always answers an object**, `{results, fits, …}`, instead of a bare list. Its only caller is LevelUp, which is updated. - A widened list marks rows the character can't take with `eligible: false` and a `why`. Preview and apply still refuse them; a test proves it (Sudden Charge for a rogue gives "A fighter feat." and a 422). **Screen** - `fitFilters.jsx`, shared by Add to the sheet and level-up's feat step (5e's improvement-or-feat step too): - the picker opens, takes `fits`, and asks again with it, so the counts and the total agree; - the head reads "Feats N · fit Ossian · 3 set · Show every one"; - stage 3's filter row, which folds behind "Filters (N)" on a phone; - a Pager. - Rows the character can't take are greyed, with their reason, and their Add or Choose button is disabled. **Checked** - Backend, on postgres:18: lint and checks are clean. - 20 new tests. - One run of the sheet suite plus the reference browse, facets, search and library tests gave 904 passed and 1 failed: a bound-counting test that broke on an extra picker call. That call was removed. - The sheet and reference subset then gave 187 passed. - Frontend: lint has 0 errors. - 18 new tests. - `src/pages/sheet`, `wiki`, `ui` and `Home`: 1116 passed. - The whole suite, run before the last test fix, had 2 failures. One was the LevelUp test that was then fixed. The other was a Home test that passed alone. - **Not yet:** CI's full suites and a render check. New wording goes on the Wording Review page before this merges. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
design: browse the rules, stage 4 -- browse where it is used (#1243)
Some checks failed
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 2m28s
CI / Backend lint (ruff) (pull_request) Successful in 33s
CI / Docker image build (pull_request) Has been cancelled
CI / Does this change touch the recording path? (pull_request) Has been cancelled
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 / Backend migration, tests, and audit (pull_request) Has been cancelled
CI / Bot tests and audit (pull_request) Has been cancelled
CI / Frontend (shard 1/2) (pull_request) Has been cancelled
CI / Frontend (shard 2/2) (pull_request) Has been cancelled
CI / Feedback site tests (pull_request) Has been cancelled
CI / Bot/backend version sync (pull_request) Has been cancelled
3f79809734
Lane 6h: the sheet's pickers open on what fits the character (a class
feat, a wizard's spells, gear within the gold; 5e too), Prep's Rules &
bestiary drawer gains Browse with creatures at the party's levels, and
Find at the table browses in place with Add to the shelf; four questions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
design: browse in place decided, D314-D317 (#1243)
All checks were successful
CI / Backend lint (ruff) (pull_request) Successful in 1m5s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 2m22s
CI / Feedback site tests (pull_request) Successful in 1m47s
CI / Does this change touch the recording path? (pull_request) Successful in 21s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Bot/backend version sync (pull_request) Successful in 1m1s
CI / Which journeys does this change need? (pull_request) Successful in 22s
CI / Bot tests and audit (pull_request) Successful in 3m59s
CI / Frontend (shard 2/2) (pull_request) Successful in 7m57s
CI / Docker image build (pull_request) Successful in 5m22s
CI / End-to-end journeys (desktop) (pull_request) Has been skipped
CI / Frontend (shard 1/2) (pull_request) Successful in 9m21s
CI / Backend migration, tests, and audit (pull_request) Successful in 30m58s
674e47d1c8
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Picker and level-up options answer fits, fits_label, filters, sorts and total,
and take stage 3's f_* / sort / page / per_page / all=1. What fits is declared
in each system's browse.json (fits rules over named sheet facts).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Merge branch 'feat/1243-sheet-fe' into feat/1243-sheet-pickers
Some checks failed
CI / Does this change touch the recording path? (pull_request) Successful in 19s
CI / Bot/backend version sync (pull_request) Successful in 56s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Backend lint (ruff) (pull_request) Successful in 58s
CI / Which journeys does this change need? (pull_request) Successful in 20s
CI / Feedback site tests (pull_request) Successful in 1m41s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m54s
CI / Bot tests and audit (pull_request) Successful in 3m23s
CI / Docker image build (pull_request) Successful in 6m8s
CI / Frontend (shard 2/2) (pull_request) Successful in 7m56s
CI / Frontend (shard 1/2) (pull_request) Successful in 9m31s
CI / End-to-end journeys (desktop) (pull_request) Successful in 11m15s
CI / Backend migration, tests, and audit (pull_request) Has been cancelled
85d4f0c138
fix(webapp): the sheet pickers read filters as the routes send them (#1243)
Some checks failed
CI / Which journeys does this change need? (pull_request) Successful in 13s
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 / Backend lint (ruff) (pull_request) Successful in 30s
CI / Bot/backend version sync (pull_request) Successful in 38s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 56s
CI / Feedback site tests (pull_request) Successful in 1m22s
CI / Bot tests and audit (pull_request) Successful in 2m2s
CI / Frontend (shard 2/2) (pull_request) Successful in 8m44s
CI / Docker image build (pull_request) Successful in 9m9s
CI / Frontend (shard 1/2) (pull_request) Successful in 20m49s
CI / End-to-end journeys (desktop) (pull_request) Successful in 14m34s
CI / Backend migration, tests, and audit (pull_request) Has been cancelled
fc1249e07a
The render check found no filter row: /picker and /options send filters
as a list with sorts beside it, and the row read only the nested
/reference/filters shape. Read both; a test uses the routes' own shape.

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

Render check, first pass

It ran on the journeys stack, both starters loaded, with a level-1 PF2e fighter made through Start a character. Every page returned 200 with 0 console errors. Axe was clean, and there was no overflow on a phone.

Found: no filter row anywhere. /picker and /options send filters as a list with sorts beside it, while the screen read only /reference/filters's nested {filters, sorts} shape. So the row, its counts, the phone fold and the Price filter were all silently empty. The unit tests mocked the nested shape, which is why they passed.

Fixed in fc1249e0: the row reads both shapes, and a new test uses the routes' own shape. Sheet tests: 274 passed; lint has 0 errors.

Still being checked, in a second render pass:

  • whether the first, unfiltered list is ever drawn before the fitting one;
  • why the gear picker fit only 2 items for the new fighter.

Working as intended:

  • the head ("Feats 367 · fit … · 1 set", "Feats 21 · fit … · 3 set" at level-up, then "Feats 1,838" after "Show every one");
  • the requests: the first has no f_*, the second has f_level=..1, and "Show every one" sends all=1.
## Render check, first pass It ran on the journeys stack, both starters loaded, with a level-1 PF2e fighter made through Start a character. Every page returned 200 with 0 console errors. Axe was clean, and there was no overflow on a phone. **Found: no filter row anywhere.** `/picker` and `/options` send `filters` as a list with `sorts` beside it, while the screen read only `/reference/filters`'s nested `{filters, sorts}` shape. So the row, its counts, the phone fold and the Price filter were all silently empty. The unit tests mocked the nested shape, which is why they passed. **Fixed in fc1249e0:** the row reads both shapes, and a new test uses the routes' own shape. Sheet tests: 274 passed; lint has 0 errors. **Still being checked, in a second render pass:** - whether the first, unfiltered list is ever drawn before the fitting one; - why the gear picker fit only 2 items for the new fighter. **Working as intended:** - the head ("Feats 367 · fit … · 1 set", "Feats 21 · fit … · 3 set" at level-up, then "Feats 1,838" after "Show every one"); - the requests: the first has no `f_*`, the second has `f_level=..1`, and "Show every one" sends `all=1`.
Author
Collaborator

Render check, second pass, and two fixes

Second pass (fc1249e0):

  • The filter row now draws, with counts, on desktop and phone.
  • "Filters (N)" folds and opens on a phone.
  • Level-up shows "Feats 21 · 3 set", with Class and Fighter set.
  • The unfiltered first answer is never drawn (0 rows until the fitting answer lands).
  • Axe was clean on desktop and on the open phone fold, with 0 console errors.

Fixed after it:

  • "Nothing matches." flashed while the first answer loaded. The Picker now takes pending, and both pickers say "Searching…" until their first answer arrives.
  • A new character's 0 gp capped gear at free items (total 525 → 2). Gold now counts as a fact only when the sheet records more than 0, so a sheet with no gold sets no price.

Checks:

  • Backend: lint is clean, and test_sheets_pickers_fit plus test_sheets_levelup gave 45 passed, including a new zero-gold test.
  • Frontend: lint has 0 errors. src/pages/sheet and src/ui/Picker gave 280 passed and 2 failed; both failures were 15-second timeouts at a load average of 33, in files this change doesn't touch.

CI decides. The owner approved the wording (part n) on 2 Nov, so this merges when CI is green.

## Render check, second pass, and two fixes **Second pass (fc1249e0):** - The filter row now draws, with counts, on desktop and phone. - "Filters (N)" folds and opens on a phone. - Level-up shows "Feats 21 · 3 set", with Class and Fighter set. - The unfiltered first answer is never drawn (0 rows until the fitting answer lands). - Axe was clean on desktop and on the open phone fold, with 0 console errors. **Fixed after it:** - "Nothing matches." flashed while the first answer loaded. The Picker now takes `pending`, and both pickers say "Searching…" until their first answer arrives. - A new character's 0 gp capped gear at free items (total 525 → 2). Gold now counts as a fact only when the sheet records more than 0, so a sheet with no gold sets no price. **Checks:** - Backend: lint is clean, and `test_sheets_pickers_fit` plus `test_sheets_levelup` gave 45 passed, including a new zero-gold test. - Frontend: lint has 0 errors. `src/pages/sheet` and `src/ui/Picker` gave 280 passed and 2 failed; both failures were 15-second timeouts at a load average of 33, in files this change doesn't touch. CI decides. The owner approved the wording (part n) on 2 Nov, so this merges when CI is green.
claude-bot scheduled this pull request to auto merge when all checks succeed 2026-11-02 20:55:48 +00:00
fix(webapp): the pickers say Searching while they load, and no gold sets no price (#1243)
All checks were successful
CI / Backend lint (ruff) (pull_request) Successful in 36s
CI / Feedback site tests (pull_request) Successful in 56s
CI / Does this change touch the recording path? (pull_request) Successful in 19s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m12s
CI / Which journeys does this change need? (pull_request) Successful in 20s
CI / Bot/backend version sync (pull_request) Successful in 1m17s
CI / Bot tests and audit (pull_request) Successful in 2m16s
CI / Docker image build (pull_request) Successful in 2m32s
CI / Frontend (shard 2/2) (pull_request) Successful in 9m2s
CI / Frontend (shard 1/2) (pull_request) Successful in 10m49s
CI / End-to-end journeys (desktop) (pull_request) Successful in 11m46s
CI / Backend migration, tests, and audit (pull_request) Successful in 34m13s
6c14298a06
From the second render check: "Nothing matches." flashed while the first
answer came, and a new character's 0 gp capped gear at free items.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign in to join this conversation.
No description provided.