Lane 8 page P7: the map; tiling that works; the trail kept in its campaign (WIKI-11, IS-124, IS-102) #923

Merged
claude-bot merged 4 commits from build/lane8-p7-map into main 2026-09-29 07:01:36 +00:00
Collaborator

Lane 8 page P7 (#734). Building the page against the real stack turned up four server bugs from PR 6, one of them a cross-campaign read/write. They're fixed here.

Security and correctness (server)

  • The trail wasn't scoped to its campaign. GET/POST /campaigns/{cid}/parties/{party_id}/trail never checked party.campaign_id. Any member could read another campaign's trail by party id, and a GM of one campaign could add stops to another's party. Both return 404 now, and a stop's marker, map and session must belong to this campaign.
  • A stop on a map the reader can't see leaked where it was (its map, marker and position). It still lists its session and days on the road; the place fields are null.
  • A GM's marker could link a page, holding or zoom map from another campaign by id; now refused.
  • Creating an atlas, a map or a marker 500'd (MissingGreenlet: the audience collection of a new row was lazy-loaded). Nothing tested creation through the API; now a test does.
  • Tiling always failed. The worker mounted media read-only (its comment predates PR 6's tiling task), so every upload ended in "Read-only file system". The mount is writable in all three compose files, and OPERATIONS.md says so for customised overrides. Operators with an override that redefines the worker's volumes must drop :ro.
  • A failed map showed the GM the raw exception, server paths included. It says why in words now.
  • Edge tiles were saved at their partial size and stretched to 256 px by any map viewer. They're padded to a full tile.

New routes (additive, no migration)

  • A player adds her own marker (IS-076): a note, place or danger, for herself and the GMs, the party, or the table; nothing linked.
  • DELETE /markers/{id} (a GM any, a player her own); GET /maps/{id}/links (both ends visible); GET /lore/{id}/markers ("On the map"); markers carry created_by.

The page

/wiki/map uses Leaflet 1.9.4 (CRS.Simple over the image's pixels) on the private tiles route, falling back to the whole image until tiles are cut. It has crumbs up the atlas, filters by kind, the party's trail (one stop per place, with its sessions and days on the road) and "The party is here" inside its town's label (IS-082). A panel per marker offers: open its map, travel a link, open its page, Reveal it… with an Undo, and Remove. A GM adds a map (as its own atlas or opened from a marker) and records where the party ended. Every marker is also listed under the map; the canvas pans with the arrow keys and zooms with its buttons. The wiki front page says where the party is (IS-124), and an article says where its page is on the map (IS-102, IS-087). The Contents gains The map.

Departures, stated in Map.jsx: a marker can't be moved or renamed (there's no update route), and travel links are followed but made only through the API.

Checked

  • Backend: new API tests for every rule above; wiki suite 538 passed; tiling tests 4 passed; full suite result in a comment below.
  • Frontend: lint clean, 2702 tests, build.
  • Render check in the e2e stack (real 390×844 and 1440×900, light and dark, as Dana and Priya; I looked at the shots): 22 of 22 have no sideways scroll. Tiles load (28 per map view). Priya sees 4 markers and Dana 6 (two GM-only). Arrow keys pan the map, all markers are focusable, and a player's add-marker dialog on a phone shows her three audiences. The first passes found the read-only mount, the stretched edge tiles, Leaflet's hidden tile layer, raw-error text and piled-up labels, all fixed in 0369065c.

Found in passing and fixed separately: #922 (the wiki named the demo GM in every campaign) and #919 (a Tuesday-only test failure).

Part of #734.

🤖 Generated with Claude Code

Lane 8 page P7 (#734). Building the page against the real stack turned up four server bugs from PR 6, one of them a cross-campaign read/write. They're fixed here. ## Security and correctness (server) - **The trail wasn't scoped to its campaign.** `GET/POST /campaigns/{cid}/parties/{party_id}/trail` never checked `party.campaign_id`. Any member could read another campaign's trail by party id, and a GM of one campaign could add stops to another's party. Both return 404 now, and a stop's marker, map and session must belong to this campaign. - **A stop on a map the reader can't see leaked where it was** (its map, marker and position). It still lists its session and days on the road; the place fields are null. - A GM's marker could link a page, holding or zoom map **from another campaign** by id; now refused. - **Creating an atlas, a map or a marker 500'd** (`MissingGreenlet`: the audience collection of a new row was lazy-loaded). Nothing tested creation through the API; now a test does. - **Tiling always failed.** The worker mounted `media` read-only (its comment predates PR 6's tiling task), so every upload ended in "Read-only file system". The mount is writable in all three compose files, and `OPERATIONS.md` says so for customised overrides. **Operators with an override that redefines the worker's volumes must drop `:ro`.** - A failed map showed the GM the raw exception, **server paths included**. It says why in words now. - Edge tiles were saved at their partial size and stretched to 256 px by any map viewer. They're padded to a full tile. ## New routes (additive, no migration) - A player adds her own marker (IS-076): a note, place or danger, for herself and the GMs, the party, or the table; nothing linked. - `DELETE /markers/{id}` (a GM any, a player her own); `GET /maps/{id}/links` (both ends visible); `GET /lore/{id}/markers` ("On the map"); markers carry `created_by`. ## The page `/wiki/map` uses Leaflet 1.9.4 (`CRS.Simple` over the image's pixels) on the private tiles route, falling back to the whole image until tiles are cut. It has crumbs up the atlas, filters by kind, the party's trail (one stop per place, with its sessions and days on the road) and "The party is here" inside its town's label (IS-082). A panel per marker offers: open its map, travel a link, open its page, **Reveal it…** with an Undo, and Remove. A GM adds a map (as its own atlas or opened from a marker) and records where the party ended. Every marker is also listed under the map; the canvas pans with the arrow keys and zooms with its buttons. The wiki front page says **where the party is** (IS-124), and an article says where its page is **on the map** (IS-102, IS-087). The Contents gains The map. Departures, stated in `Map.jsx`: a marker can't be moved or renamed (there's no update route), and travel links are followed but made only through the API. ## Checked - Backend: new API tests for every rule above; wiki suite 538 passed; tiling tests 4 passed; full suite result in a comment below. - Frontend: lint clean, 2702 tests, build. - **Render check** in the e2e stack (real 390×844 and 1440×900, light and dark, as Dana and Priya; I looked at the shots): 22 of 22 have no sideways scroll. Tiles load (28 per map view). Priya sees 4 markers and Dana 6 (two GM-only). Arrow keys pan the map, all markers are focusable, and a player's add-marker dialog on a phone shows her three audiences. The first passes found the read-only mount, the stretched edge tiles, Leaflet's hidden tile layer, raw-error text and piled-up labels, all fixed in 0369065c. Found in passing and fixed separately: #922 (the wiki named the demo GM in every campaign) and #919 (a Tuesday-only test failure). Part of #734. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Security (lane 8 PR 6's routes):
- GET/POST /parties/{party_id}/trail never checked the party was this
  campaign's: any member could read another campaign's trail by id, and
  a GM of one campaign could add stops to another's party. Both are 404
  now, and a stop's marker, map and session must be this campaign's.
- A trail stop on a map or marker the reader can't see listed its map,
  marker and position. It still lists its session and days on the road,
  but not where it is.
- A GM's marker could link a page, holding or zoom map from another
  campaign by id; refused.

A crash: creating an atlas, a map or a marker through the API assigned
the audience people of a row whose collection was never loaded, so the
lazy load ran outside the async session (MissingGreenlet, a 500). Nothing
tested creation through the API; now a test does.

For P7's pages:
- A player adds her own marker (IS-076): a note, a place or a danger, for
  herself and the GMs, the party or the table, and nothing linked.
- DELETE /markers/{id}: a GM any, a player her own.
- GET /maps/{id}/links: travel links whose both ends the reader can see.
- GET /lore/{id}/markers: where a page is on the map ("On the map").
- A marker says who added it (created_by).

No migration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lane 8 page P7. /campaigns/:id/wiki/map draws a map with Leaflet over the
private tiles route (CRS.Simple on the image's pixels; the image itself
until the tiles are cut), with the atlas's crumbs, filters by kind, the
party's trail and where it is, and a panel for the chosen marker: open its
map, travel a link, open its page, Reveal it... with an undo, Remove. A
player adds her own marker with her three audiences (IS-076); a GM adds a
map (its own atlas, or opened from a marker) and where the party ended.
Every marker is listed under the map too (the text alternative); the
canvas pans with the arrow keys and zooms with its buttons.

The wiki's front page says where the party is (IS-124), and an article's
margin says where the page is on the map (IS-102, IS-087). The Contents
gains The map. Adds leaflet 1.9.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(webapp): maps are cut into tiles, and drawn from them (#734)
Some checks failed
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m4s
CI / Backend lint (ruff) (pull_request) Successful in 34s
CI / Feedback site tests (pull_request) Successful in 46s
CI / Bot/backend version sync (pull_request) Successful in 27s
CI / Docker image build (pull_request) Successful in 54s
CI / Does this change touch the recording path? (pull_request) Successful in 22s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Bot tests and audit (pull_request) Successful in 5m20s
CI / Backend migration, tests, and audit (pull_request) Failing after 12m24s
CI / Frontend tests, audit, and build (pull_request) Successful in 20m51s
CI / End-to-end journeys (desktop, phone, keyboard-only) (pull_request) Successful in 40m32s
0369065cc8
Found by P7's render check in the e2e stack:
- The worker mounted the media volume read-only, so every map's tiling
  failed with "Read-only file system" on every install (it only ever
  worked in the unit test's temp dir). It is writable in all three
  compose files; OPERATIONS.md says so for customised overrides.
- The failed map's reason was the raw exception, server paths and all,
  shown to the GM. It is a plain sentence now (failure_words).
- An edge tile was saved at its partial size (an 800 px map ends in a
  32 px strip) and a map viewer stretches every tile to 256 px. Edge
  tiles are padded to a full tile.
- Leaflet's tile layer hid itself below zoom 0, where every whole map
  fits, so no tile was ever requested: minZoom on the layer too.
- The canvas takes the map's own shape (capped), so a wide coast on a
  phone isn't a strip in a tall empty box.
- Only places and travel links always wear labels (the drawing's rule);
  a note, quest, danger or holding shows its label on hover, focus or
  selection, and a trail stop shows its sessions above the point.

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

Full backend suite, local (fresh Postgres, ruff 0.4.4 check + format clean): 5830 passed, 13 skipped, 1 failed. The failure is test_scheduling_lane4.py::test_needs_you_asks_again_after_a_move, which fails on every UTC Tuesday on main too and is fixed by #919. This branch picks up that fix when main is merged in.

Full backend suite, local (fresh Postgres, ruff 0.4.4 check + format clean): **5830 passed, 13 skipped, 1 failed**. The failure is `test_scheduling_lane4.py::test_needs_you_asks_again_after_a_move`, which fails on every UTC Tuesday on main too and is fixed by #919. This branch picks up that fix when main is merged in.
Merge remote-tracking branch 'origin/main' into build/lane8-p7-map
All checks were successful
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m44s
CI / Bot tests and audit (pull_request) Successful in 3m10s
CI / Backend lint (ruff) (pull_request) Successful in 1m0s
CI / Feedback site tests (pull_request) Successful in 1m20s
CI / Bot/backend version sync (pull_request) Successful in 46s
CI / Does this change touch the recording path? (pull_request) Successful in 26s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Docker image build (pull_request) Successful in 1m1s
CI / Frontend tests, audit, and build (pull_request) Successful in 23m15s
CI / Backend migration, tests, and audit (pull_request) Successful in 30m40s
CI / End-to-end journeys (desktop, phone, keyboard-only) (pull_request) Successful in 21m11s
db1cd49fec
# Conflicts:
#	webapp/frontend/src/App.jsx
Sign in to join this conversation.
No description provided.