Lane 11 PR 16: Level up (#737) #987

Merged
claude-bot merged 11 commits from build/lane11-16-levelup into main 2026-10-01 15:25:41 +00:00
Collaborator

Part of #737 (lane 11, stat blocks and character sheets). Contract: docs/design/lane11-build-contract.md §4, PR 16. Built on the recommendations for the open questions L12–L15 (on the Questions page); nothing here is hard to change if an answer differs.

What it does

  • Routes #26–28: level-up options, preview and apply. No migration.
  • XP from the ledger (D204). A character's XP is every award to them or their party since the sheet was made (@ext.xp_awarded).
  • Settings → General (IS-461). A campaign levels by XP or by milestone.
  • "Level n is waiting" in the sheet's header, with Level up and Not yet. A GM can level any character at any time.
  • The Level up page. Hit points (PF2e worked out; 5e rolled on the class die or taken fixed, the die kept and the roll standing, D85), class features, feats from the loaded content (one that can't be taken is shown disabled, with why), skill increase, attribute boosts, 5e improvement or a feat, then What changes.
  • Reach level n keeps the sheet as a version first and adds a "reached level n" milestone (D210, IS-462).

Fixed after the render check

  1. Hit points step. Its figure now comes from the same staged evaluations What changes reads, so the two cannot disagree. A Constitution boost taken at this level is counted. Worded as the sheet's sum (D253): "Level 5 brings 14 Hit Points. Max HP 44 → 58: Ancestry 8 + 5 levels 50 (Class 8 + Con 2 each)." Singular for 1.
  2. Feat card. "From" appears once; the category is capitalised (in cardLines, so the Add-to-the-sheet and Start-a-character cards get it too).
  3. What changes on a phone. At a narrow width the Why column goes and each reason sits under its label, so nothing is off screen and the page does not scroll sideways. The caption is left-aligned; a row with nothing before it shows "—".
  4. The rolled die reads "9 rolled", announced once.
  5. A player in a milestone campaign opening /level-up directly is told why from the sheet (advancement.level_why, additive) and no preview call is made, where it used to draw a 403.
  6. Found in the re-render: "Every attribute +1" when four of six were boosted. The collapsed row says "every" only when every one of the kind moved. Otherwise it names up to four ("Strength · Dexterity · Constitution · Intelligence") or counts them ("8 other skills"). This also corrects "Every other skill +1" at Ossian's level 7, where only the trained skills rise. It departs from the drawing's wording in that case and is on the Questions page as L16.

Also merges PR 14's Table change: the scroll region is one label prop.

Checked

  • Backend: full suite 6562 passed, 14 skipped before item 6; tests/sheets re-run after it, 712 passed.
  • Frontend: vitest 3566 passed, 1 skipped before items 3 and 6; src/ui, src/pages/sheet, Members and Settings General re-run after, 676 passed; eslint 0 errors.
  • ruff 0.4.4 format --check and check; changelog, version sync and every coverage/lane*.py.
  • Rendered in an e2e stack at 390×844 (touch) and 1440, light and dark: 130 shots over the PF2e and 5e flows, Settings, the GM and player views in a milestone campaign, plus a re-shoot of What changes. No console errors and no non-2xx calls.

Known

  • Plain class: "Rogue" typed into a PF2e sheet does not link the class record, so its Hit Points have no class figure; the Start a character flow sets it properly. Noted for PR 17's seed and J7.
  • level_why for a member who isn't the sheet's owner is the generic "You can't edit this sheet."

🤖 Generated with Claude Code

Part of #737 (lane 11, stat blocks and character sheets). Contract: `docs/design/lane11-build-contract.md` §4, PR 16. Built on the recommendations for the open questions L12–L15 (on the Questions page); nothing here is hard to change if an answer differs. ## What it does - **Routes #26–28**: level-up options, preview and apply. No migration. - **XP from the ledger (D204).** A character's XP is every award to them or their party since the sheet was made (`@ext.xp_awarded`). - **Settings → General (IS-461).** A campaign levels by XP or by milestone. - **"Level n is waiting"** in the sheet's header, with Level up and Not yet. A GM can level any character at any time. - **The Level up page.** Hit points (PF2e worked out; 5e rolled on the class die or taken fixed, the die kept and the roll standing, D85), class features, feats from the loaded content (one that can't be taken is shown disabled, with why), skill increase, attribute boosts, 5e improvement or a feat, then **What changes**. - **Reach level n** keeps the sheet as a version first and adds a "reached level n" milestone (D210, IS-462). ## Fixed after the render check 1. **Hit points step.** Its figure now comes from the same staged evaluations What changes reads, so the two cannot disagree. A Constitution boost taken at this level is counted. Worded as the sheet's sum (D253): "Level 5 brings 14 Hit Points. Max HP 44 → 58: Ancestry 8 + 5 levels 50 (Class 8 + Con 2 each)." Singular for 1. 2. **Feat card.** "From" appears once; the category is capitalised (in `cardLines`, so the Add-to-the-sheet and Start-a-character cards get it too). 3. **What changes on a phone.** At a narrow width the Why column goes and each reason sits under its label, so nothing is off screen and the page does not scroll sideways. The caption is left-aligned; a row with nothing before it shows "—". 4. **The rolled die** reads "9 rolled", announced once. 5. **A player in a milestone campaign opening /level-up directly** is told why from the sheet (`advancement.level_why`, additive) and no preview call is made, where it used to draw a 403. 6. **Found in the re-render: "Every attribute +1" when four of six were boosted.** The collapsed row says "every" only when every one of the kind moved. Otherwise it names up to four ("Strength · Dexterity · Constitution · Intelligence") or counts them ("8 other skills"). This also corrects "Every other skill +1" at Ossian's level 7, where only the trained skills rise. It departs from the drawing's wording in that case and is on the Questions page as L16. Also merges PR 14's Table change: the scroll region is one `label` prop. ## Checked - Backend: full suite 6562 passed, 14 skipped before item 6; `tests/sheets` re-run after it, 712 passed. - Frontend: vitest 3566 passed, 1 skipped before items 3 and 6; `src/ui`, `src/pages/sheet`, Members and Settings General re-run after, 676 passed; eslint 0 errors. - ruff 0.4.4 `format --check` and `check`; changelog, version sync and every `coverage/lane*.py`. - Rendered in an e2e stack at 390×844 (touch) and 1440, light and dark: 130 shots over the PF2e and 5e flows, Settings, the GM and player views in a milestone campaign, plus a re-shoot of What changes. No console errors and no non-2xx calls. ## Known - Plain `class: "Rogue"` typed into a PF2e sheet does not link the class record, so its Hit Points have no class figure; the Start a character flow sets it properly. Noted for PR 17's seed and J7. - `level_why` for a member who isn't the sheet's owner is the generic "You can't edit this sheet." 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The step's gain was the level's formula before this level's boosts, so a
Constitution boost taken at the level left it short of the Hit Points row
in What changes. It is now the rise in hp.max across the same staged
evaluations What changes reads, with before, after and the after sum.

advancement.level_why is why this reader can't level the sheet now (None
when they can), so the page need not ask route #27 to be told.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Hit points: the step's sentence reads the server's before, after and
  gain, says 1 Hit Point, and words the sum as the sheet does (D253).
- The feat card says From once (cardLines already does) and its Category
  with a capital, on every card that prints one.
- What changes keeps a readable width and scrolls in a labelled, focusable
  region on a phone (Table's new region prop); the caption is left-aligned;
  a row with nothing before it shows a dash.
- A rolled die reads and is announced as '7 rolled'.
- A reader the sheet says can't level it is shown advancement.level_why and
  a way back, and route #27 is not called.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The Table's scroll region is PR 14's `label` prop; What changes names it
with its caption (#737)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
At a narrow width the Why column goes and each reason sits under its label,
so nothing is off screen. The collapsed row said "Every attribute" when four
of six were boosted and "Every other skill" when only the trained ones rose:
it now names up to four, or counts them ("8 other skills").

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Merge remote-tracking branch 'origin/main' into build/lane11-16-levelup
All checks were successful
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m1s
CI / Backend lint (ruff) (pull_request) Successful in 1m11s
CI / Bot/backend version sync (pull_request) Successful in 26s
CI / Does this change touch the recording path? (pull_request) Successful in 38s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Feedback site tests (pull_request) Successful in 1m54s
CI / Docker image build (pull_request) Successful in 2m25s
CI / Bot tests and audit (pull_request) Successful in 4m13s
CI / Frontend (shard 2/2) (pull_request) Successful in 6m41s
CI / Frontend (shard 1/2) (pull_request) Successful in 8m21s
CI / End-to-end journeys (phone) (pull_request) Successful in 6m44s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 7m12s
CI / End-to-end journeys (desktop) (pull_request) Successful in 7m30s
CI / Backend migration, tests, and audit (pull_request) Successful in 18m54s
365035d1c1
Sign in to join this conversation.
No description provided.