feat(webapp): browse the rules, stage 3 — each system's own filters (#1242) #1312

Merged
claude-bot merged 13 commits from feat/1242-system-filters into main 2026-11-02 17:36:16 +00:00
Collaborator

Closes #1242. It includes the design from #1307 (the note, lane 6g, and D310–D313), which this replaces.

What it adds

  • Browse definitions, data in app/sheets/builtin/{pf2e,dnd5e}/browse.json with a schema. They declare each kind's filters and sorts, as D310 sets out.
  • reference_entries.facets:
    • migration b1242c0d1e2f: the column plus a GIN jsonb_path_ops index, with a real downgrade;
    • filled at import;
    • refilled by a Celery task at worker start whenever a definition's version changes, so existing installs fill themselves and the deploy needs no manual step;
    • python -m app.cli refill_facets does the same by hand.
  • API:
    • f_<key> and sort on the page routes (campaign, library and admin pack);
    • GET /api/game-systems/{key}/reference/filters and GET /api/admin/content-packs/{id}/browse/filters, which return the filters with counts computed exclude-self (D311) and the sorts (D313);
    • a filter that no record can fill is left out.
  • The page:
    • the filter row as drawn: ranges with units, pressed choices with counts, a type-ahead for long multis (traits), and "Only" checkboxes;
    • a range that replaces level takes its place;
    • "· N set" and "Clear them", and the Sort select;
    • every filter is in the address, and "Copy a link to it" carries them;
    • on a phone, "Filters (N)" folds the system's filters (D312).
  • The same row appears in a campaign, in the library and in Admin's Browse.

Checked so far

  • Backend: lint is clean, and the checks pass.
    • The 63 new facet tests pass.
    • The reference, content-pack, CLI and sheets modules gave 1313 passed and 1 failed. That failure was the sheet-definition test reading browse.json as a sheet; it was fixed and that test file re-run green.
    • Migration upgrade, downgrade and re-upgrade all ran on Postgres 18.
    • Every path was checked against all 6,155 records of the real starter packs.
  • Frontend: lint has 0 errors.
    • The affected set gave 359 passed, and the 2 failures there (AppFrame, Party) passed when re-run alone.
    • The 4 new tests that failed on "Occult211" are fixed in d80f883d, but those three new files weren't re-run on their own after the fix.
    • The full vitest run didn't finish on the loaded host, at a load average of about 30.
  • Not yet: the full suites, which CI runs, and the render check, which is running.

Out of scope: packs carrying their own browse definition, #1308.

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

🤖 Generated with Claude Code

Closes #1242. It includes the design from #1307 (the note, lane 6g, and D310–D313), which this replaces. **What it adds** - **Browse definitions**, data in `app/sheets/builtin/{pf2e,dnd5e}/browse.json` with a schema. They declare each kind's filters and sorts, as D310 sets out. - **`reference_entries.facets`**: - migration `b1242c0d1e2f`: the column plus a GIN `jsonb_path_ops` index, with a real downgrade; - filled at import; - refilled by a Celery task at worker start whenever a definition's version changes, so existing installs fill themselves and the deploy needs no manual step; - `python -m app.cli refill_facets` does the same by hand. - **API:** - `f_<key>` and `sort` on the page routes (campaign, library and admin pack); - `GET /api/game-systems/{key}/reference/filters` and `GET /api/admin/content-packs/{id}/browse/filters`, which return the filters with counts computed exclude-self (D311) and the sorts (D313); - a filter that no record can fill is left out. - **The page:** - the filter row as drawn: ranges with units, pressed choices with counts, a type-ahead for long multis (traits), and "Only" checkboxes; - a range that `replaces` level takes its place; - "· N set" and "Clear them", and the Sort select; - every filter is in the address, and "Copy a link to it" carries them; - on a phone, "Filters (N)" folds the system's filters (D312). - The same row appears in a campaign, in the library and in Admin's Browse. **Checked so far** - Backend: lint is clean, and the checks pass. - The 63 new facet tests pass. - The reference, content-pack, CLI and sheets modules gave 1313 passed and 1 failed. That failure was the sheet-definition test reading `browse.json` as a sheet; it was fixed and that test file re-run green. - Migration upgrade, downgrade and re-upgrade all ran on Postgres 18. - Every path was checked against all 6,155 records of the real starter packs. - Frontend: lint has 0 errors. - The affected set gave 359 passed, and the 2 failures there (AppFrame, Party) passed when re-run alone. - The 4 new tests that failed on "Occult211" are fixed in d80f883d, but those three new files weren't re-run on their own after the fix. - The full vitest run didn't finish on the loaded host, at a load average of about 30. - **Not yet:** the full suites, which CI runs, and the render check, which is running. **Out of scope:** packs carrying their own browse definition, #1308. 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 3 -- each system's filters (#1242)
Some checks failed
CI / Does this change touch the recording path? (pull_request) Successful in 11s
CI / Which journeys does this change need? (pull_request) Successful in 11s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Bot/backend version sync (pull_request) Successful in 54s
CI / Backend lint (ruff) (pull_request) Successful in 57s
CI / Feedback site tests (pull_request) Successful in 1m23s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m46s
CI / Docker image build (pull_request) Successful in 2m34s
CI / End-to-end journeys (desktop) (pull_request) Has been skipped
CI / Bot tests and audit (pull_request) Successful in 4m0s
CI / Frontend (shard 2/2) (pull_request) Successful in 9m51s
CI / Frontend (shard 1/2) (pull_request) Successful in 12m3s
CI / Backend migration, tests, and audit (pull_request) Has been cancelled
42474120bb
docs/design/browse-filters.md: filters declared per system in a browse
definition (data, as D76), their values copied at import into a facets
column with one GIN index (PG18: the slowest filter 102 ms -> 5 ms), and
what the page shows. Lane 6g draws it: PF2e spells, creatures and
equipment, 5e spells and equipment, a phone; four questions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
design: system filters decided, D310-D313 (#1242)
All checks were successful
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 57s
CI / Backend lint (ruff) (pull_request) Successful in 32s
CI / Bot/backend version sync (pull_request) Successful in 24s
CI / Feedback site tests (pull_request) Successful in 51s
CI / Does this change touch the recording path? (pull_request) Successful in 29s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Which journeys does this change need? (pull_request) Successful in 18s
CI / Bot tests and audit (pull_request) Successful in 2m5s
CI / Docker image build (pull_request) Successful in 2m25s
CI / End-to-end journeys (desktop) (pull_request) Has been skipped
CI / Frontend (shard 2/2) (pull_request) Successful in 8m42s
CI / Frontend (shard 1/2) (pull_request) Successful in 11m52s
CI / Backend migration, tests, and audit (pull_request) Successful in 1h12m54s
9745ea1d35
Co-Authored-By: Claude Opus 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>
Browse definitions as data per system, a facets column filled at import and
refilled at worker start, f_* and sort on the reference page, and the filters
route with D311 counts.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Merge branch 'feat/1242-frontend' into feat/1242-system-filters
Some checks failed
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m9s
CI / Backend lint (ruff) (pull_request) Successful in 37s
CI / Feedback site tests (pull_request) Successful in 59s
CI / Bot/backend version sync (pull_request) Successful in 29s
CI / Does this change touch the recording path? (pull_request) Successful in 23s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Bot tests and audit (pull_request) Successful in 2m30s
CI / Which journeys does this change need? (pull_request) Successful in 26s
CI / Docker image build (pull_request) Successful in 5m29s
CI / Backend migration, tests, and audit (pull_request) Failing after 10m13s
CI / Frontend (shard 1/2) (pull_request) Successful in 12m35s
CI / End-to-end journeys (desktop) (pull_request) Has been cancelled
CI / Frontend (shard 2/2) (pull_request) Has been cancelled
6398ed341a
Author
Collaborator

Render check (journeys stack, both starters loaded)

The run: 30 shots at 1280×800 and 390×844, in light and dark, as Dana and Priya. Every shot returned HTTP 200, with 0 console errors and no overflow on a phone. Axe found no violations on the spells row, the creatures row, or the open phone fold.

What renders:

  • PF2e spells: Rank in place of Level, Tradition chips with counts, "Only" (Cantrips, Focus spells), Rarity, "· 2 set / Clear them", and Sort.
  • PF2e creatures: Size chips, with Small 0 kept in the row, and the trait type-ahead with an "Undead ×" chip.
  • PF2e equipment: Price "0 to 5 gp".
  • 5e spells: School, Class (Wizard), and Ritual and Concentration.
  • 5e creatures: CR 0 to 30 in place of Level.
  • The phone, folded and open.
  • A campaign's Rules page, which adds its Pack row.
  • Admin's pack Browse page.

The API agrees with the page:

  • filters?kind=spell gives tradition arcane 607, divine 369, occult 548 and primal 450.
  • With f_rank=1..3 those drop to 333, 192, 299 and 264 (exclude-self, as D311 sets out).
  • The page total for occult with rank 1–3 is 299, the same as the chip.

Found and fixed in this PR:

  • A PF2e cantrip read "Spell 7" (Allegro heightens to 7). It now reads "Cantrip 7".
  • A row's one-line summary showed markdown ("Area 1 cubic foot"). It's now plain words.

Two notes, not defects:

  • "Filters (1)" counts only the folded system filters. "· 2 set" also counts Rank, which stays in view.
  • A campaign's counts include its own pack, so Arcane reads 613 there and 607 in the library.
## Render check (journeys stack, both starters loaded) The run: 30 shots at 1280×800 and 390×844, in light and dark, as Dana and Priya. Every shot returned HTTP 200, with 0 console errors and no overflow on a phone. Axe found no violations on the spells row, the creatures row, or the open phone fold. **What renders:** - PF2e spells: Rank in place of Level, Tradition chips with counts, "Only" (Cantrips, Focus spells), Rarity, "· 2 set / Clear them", and Sort. - PF2e creatures: Size chips, with Small 0 kept in the row, and the trait type-ahead with an "Undead ×" chip. - PF2e equipment: Price "0 to 5 gp". - 5e spells: School, Class (Wizard), and Ritual and Concentration. - 5e creatures: CR 0 to 30 in place of Level. - The phone, folded and open. - A campaign's Rules page, which adds its Pack row. - Admin's pack Browse page. **The API agrees with the page:** - `filters?kind=spell` gives tradition arcane 607, divine 369, occult 548 and primal 450. - With `f_rank=1..3` those drop to 333, 192, 299 and 264 (exclude-self, as D311 sets out). - The page total for occult with rank 1–3 is 299, the same as the chip. **Found and fixed in this PR:** - A PF2e cantrip read "Spell 7" (Allegro heightens to 7). It now reads "Cantrip 7". - A row's one-line summary showed markdown ("**Area** 1 cubic foot"). It's now plain words. Two notes, not defects: - "Filters (1)" counts only the folded system filters. "· 2 set" also counts Rank, which stays in view. - A campaign's counts include its own pack, so Arcane reads 613 there and 607 in the library.
fix(webapp): cantrips read Cantrip N, and a row's summary drops its markdown (#1242)
Some checks failed
CI / Backend lint (ruff) (pull_request) Successful in 34s
CI / Bot/backend version sync (pull_request) Successful in 25s
CI / Docker image build (pull_request) Successful in 28s
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 / Which journeys does this change need? (pull_request) Successful in 18s
CI / Bot tests and audit (pull_request) Successful in 2m7s
CI / Feedback site tests (pull_request) Successful in 2m13s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 2m43s
CI / Frontend (shard 2/2) (pull_request) Successful in 10m18s
CI / Backend migration, tests, and audit (pull_request) Failing after 10m54s
CI / Frontend (shard 1/2) (pull_request) Successful in 12m14s
CI / End-to-end journeys (desktop) (pull_request) Successful in 14m45s
9511b34bc4
Found by the render check: Allegro (a cantrip heightened to 7) read
"Spell 7", and Approximate's summary read "**Area** 1 cubic foot".

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

A correction to my last comment: the cantrip and summary fixes (9511b34b) were not verified when I called them fixed.

On docker-test, the 8 affected files gave 76 passed and 7 failed, at a load average of 40–45. My filter kept the failing test names but not their errors, so I can't yet say whether those 7 are host timeouts or real. They're spread across files the change doesn't touch (RulesLibrary, Rules, RulesBrowse, RulesFilters, RulesOpen), which looks like load, but that's a guess. I'm pushing so CI's runners decide. If CI fails, I'll pull the log.

A correction to my last comment: the cantrip and summary fixes (9511b34b) were not verified when I called them fixed. On docker-test, the 8 affected files gave 76 passed and 7 failed, at a load average of 40–45. My filter kept the failing test names but not their errors, so I can't yet say whether those 7 are host timeouts or real. They're spread across files the change doesn't touch (RulesLibrary, Rules, RulesBrowse, RulesFilters, RulesOpen), which looks like load, but that's a guess. I'm pushing so CI's runners decide. If CI fails, I'll pull the log.
test: the one-head pin follows the facets migration (#1242)
All checks were successful
CI / Does this change touch the recording path? (pull_request) Successful in 13s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Bot/backend version sync (pull_request) Successful in 21s
CI / Docker image build (pull_request) Successful in 28s
CI / Which journeys does this change need? (pull_request) Successful in 17s
CI / Feedback site tests (pull_request) Successful in 43s
CI / Backend lint (ruff) (pull_request) Successful in 1m19s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 2m14s
CI / Bot tests and audit (pull_request) Successful in 3m32s
CI / Frontend (shard 2/2) (pull_request) Successful in 12m34s
CI / End-to-end journeys (desktop) (pull_request) Successful in 12m24s
CI / Frontend (shard 1/2) (pull_request) Successful in 14m48s
CI / Backend migration, tests, and audit (pull_request) Successful in 51m46s
8decefb8ac
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-02 17:24:55 +00:00
Author
Collaborator

CI's first backend run stopped on one real failure: the one-head pin in tests/export/test_import_jobs_migration.py still named a1269b0c1d2e. It now names b1242c0d1e2f, and it's the only test that pins the head.

Both frontend jobs passed on that same CI run, so the 7 failures I saw earlier on docker-test were host load (load average 40–45), not this change.

The full backend suite on postgres:18, run on docker-test against the branch with the fix: 10500 passed, 14 skipped, 0 failed, in 47 minutes.

This PR is set to merge when CI is green. The owner approved its wording (part m) on 2 Nov.

CI's first backend run stopped on one real failure: the one-head pin in `tests/export/test_import_jobs_migration.py` still named `a1269b0c1d2e`. It now names `b1242c0d1e2f`, and it's the only test that pins the head. Both frontend jobs passed on that same CI run, so the 7 failures I saw earlier on docker-test were host load (load average 40–45), not this change. **The full backend suite** on postgres:18, run on docker-test against the branch with the fix: **10500 passed, 14 skipped, 0 failed**, in 47 minutes. This PR is set to merge when CI is green. The owner approved its wording (part m) on 2 Nov.
Sign in to join this conversation.
No description provided.