Lane 11 PR 3: sheets on the server — tables, visibility, autosave, the hot half, versions, the projection (#737) #964

Merged
claude-bot merged 7 commits from build/lane11-3-server into main 2026-09-30 08:46:24 +00:00
Collaborator

Lane 11 (#737) PR 3: the server half of sheets, per the contract's §3.1, §3.7 and §3.8 (routes #1–12). It covers IS-407, 408, 414 and 435, and the server half of IS-419.

Migration 11a1b2c3d4e5 (on a9430d1e2f3b):

  • New tables: sheet_definitions, character_sheets (on the wiki page only, D202) and character_sheet_versions.
  • New columns: reference_entries.fields, shelf_cards.resources and campaigns.advancement, plus the reference kinds ancestry and heritage.
  • No data changes. Downgrade drops everything, first removing any ancestry/heritage records the old CHECK couldn't hold.
  • Checks: Alembic's compare_metadata is clean, and upgrade → downgrade → upgrade was run on a fresh database.

Services:

  • sheet_definitions_service: the built-in PF2e, 5e and generic definitions heal themselves at startup (D220). A newer version from a pack wins. Compiled definitions are cached by digest.
  • sheet_service:
    • Access (§3.7): the GM edits everything; a character's player edits their own sheet (a player always reads their whole sheet, L5); others read what the audience allows.
    • One visibility filter is used by every reader, the projection included.
    • Reads evaluate with PR 2's engine.
    • Writes are checked against the definition (a 422 names the field path), with expected_version autosave (409 {detail, current}, like Prep) and GM locks.
    • Entries resolve against the content and keep a snapshot, so a removed pack's row keeps its name and last numbers.
    • The hot half: HP, conditions and tracks are written under a row lock and make no version (IS-414). The table polls with since_rev.
    • Versions coalesce: one per author per 10 minutes of editing, plus one per restore, creation or level-up (L4). Restoring ("Put back") is supported.
    • The projection is written into LoreEntry.stats, filtered per reader (search, the LLM, the bot and the card never see hidden lines).
    • Export is Quest Board JSON; the picker searches over the loaded content.
  • Page restores: restoring an old page version now keeps the current projection instead of writing back the old one (it carried old audiences, a leak).

Routes (#1–12, with the full table in the build contract): the definition (immutable, cached, with an ETag), the campaign's definitions, and a page's sheet (GET and POST), plus PATCH autosave, add an entry, resources (POST and a polled GET), versions and restore, export, and the picker. LoreEntryResponse.sheet tells the article whether a sheet exists and whether the reader can edit it.

Tests:

  • Coverage: 34 new tests.
    • visibility for every role and every reader of the projection;
    • a stale version gives 409;
    • locks are refused for the player;
    • hot writes make no version;
    • coalescing, restore and export;
    • the built-in self-heal;
    • the migration.
  • Run: 3041 pass across 141 backend modules (every file that touches the audience service, reference entries, shelf cards, campaigns, lore, stats, game systems, content packs or migrations) plus tests/sheets.
  • Timing: evaluate plus the projection takes 4–7 ms for a 90-field sheet, so there's no debounce.
  • Lint and changelog: ruff is clean, and changelog.py check passes.

The old stat block keeps working until PR 7 and PR 13 (the rollout rule).

Changelog: changelog.d/737-lane11-server.added.md.

🤖 Generated with Claude Code

Lane 11 (#737) PR 3: the server half of sheets, per the contract's §3.1, §3.7 and §3.8 (routes #1–12). It covers IS-407, 408, 414 and 435, and the server half of IS-419. **Migration `11a1b2c3d4e5`** (on `a9430d1e2f3b`): - **New tables:** `sheet_definitions`, `character_sheets` (on the wiki page only, D202) and `character_sheet_versions`. - **New columns:** `reference_entries.fields`, `shelf_cards.resources` and `campaigns.advancement`, plus the reference kinds `ancestry` and `heritage`. - **No data changes.** Downgrade drops everything, first removing any ancestry/heritage records the old CHECK couldn't hold. - **Checks:** Alembic's `compare_metadata` is clean, and upgrade → downgrade → upgrade was run on a fresh database. **Services:** - **`sheet_definitions_service`:** the built-in PF2e, 5e and generic definitions heal themselves at startup (D220). A newer version from a pack wins. Compiled definitions are cached by digest. - **`sheet_service`:** - **Access (§3.7):** the GM edits everything; a character's player edits their own sheet (a player always reads their whole sheet, L5); others read what the audience allows. - **One visibility filter** is used by every reader, the projection included. - **Reads** evaluate with PR 2's engine. - **Writes** are checked against the definition (a 422 names the field path), with **`expected_version` autosave** (409 `{detail, current}`, like Prep) and GM locks. - **Entries** resolve against the content and keep a snapshot, so a removed pack's row keeps its name and last numbers. - **The hot half:** HP, conditions and tracks are written under a row lock and make **no version** (IS-414). The table polls with `since_rev`. - **Versions** coalesce: one per author per 10 minutes of editing, plus one per restore, creation or level-up (L4). Restoring ("Put back") is supported. - **The projection** is written into `LoreEntry.stats`, filtered per reader (search, the LLM, the bot and the card never see hidden lines). - **Export** is Quest Board JSON; the **picker** searches over the loaded content. - **Page restores:** restoring an old *page* version now keeps the current projection instead of writing back the old one (it carried old audiences, a leak). **Routes** (#1–12, with the full table in the build contract): the definition (immutable, cached, with an ETag), the campaign's definitions, and a page's sheet (GET and POST), plus PATCH autosave, add an entry, resources (POST and a polled GET), versions and restore, export, and the picker. `LoreEntryResponse.sheet` tells the article whether a sheet exists and whether the reader can edit it. **Tests:** - **Coverage:** 34 new tests. - visibility for every role and every reader of the projection; - a stale version gives 409; - locks are refused for the player; - hot writes make no version; - coalescing, restore and export; - the built-in self-heal; - the migration. - **Run:** **3041 pass** across 141 backend modules (every file that touches the audience service, reference entries, shelf cards, campaigns, lore, stats, game systems, content packs or migrations) plus `tests/sheets`. - **Timing:** evaluate plus the projection takes 4–7 ms for a 90-field sheet, so there's no debounce. - **Lint and changelog:** ruff is clean, and `changelog.py check` passes. The old stat block keeps working until PR 7 and PR 13 (the rollout rule). Changelog: `changelog.d/737-lane11-server.added.md`. 🤖 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>
Merge remote-tracking branch 'origin/main' into build/lane11-3-server
Some checks failed
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m11s
CI / Bot/backend version sync (pull_request) Successful in 32s
CI / Feedback site tests (pull_request) Successful in 50s
CI / Backend lint (ruff) (pull_request) Failing after 1m2s
CI / Bot tests and audit (pull_request) Successful in 2m13s
CI / Does this change touch the recording path? (pull_request) Successful in 40s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / End-to-end journeys (desktop) (pull_request) Has been cancelled
CI / End-to-end journeys (keyboard) (pull_request) Has been cancelled
CI / End-to-end journeys (phone) (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 / Backend migration, tests, and audit (pull_request) Has been cancelled
CI / Docker image build (pull_request) Has been cancelled
ea7fc3e923
style: ruff format the lane 11 sheets migration (#737)
All checks were successful
CI / Bot/backend version sync (pull_request) Successful in 45s
CI / Backend lint (ruff) (pull_request) Successful in 54s
CI / Feedback site tests (pull_request) Successful in 1m6s
CI / Docker image build (pull_request) Successful in 21s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m22s
CI / Does this change touch the recording path? (pull_request) Successful in 41s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Bot tests and audit (pull_request) Successful in 2m32s
CI / Frontend (shard 2/2) (pull_request) Successful in 6m46s
CI / Frontend (shard 1/2) (pull_request) Successful in 8m33s
CI / End-to-end journeys (phone) (pull_request) Successful in 7m22s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 8m17s
CI / End-to-end journeys (desktop) (pull_request) Successful in 8m27s
CI / Backend migration, tests, and audit (pull_request) Successful in 19m23s
548ca4d162
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign in to join this conversation.
No description provided.