fix(webapp): the party's trail, as it needs to work (#1269, #1271, #1273, #1275, #1276) #1272

Merged
claude-bot merged 9 commits from fix/trail-order-by-session into main 2026-10-29 21:01:07 +00:00
Collaborator

Closes #1269. Closes #1271. Closes #1273. Closes #1275. Closes #1276.

The owner's fixes to the 4.6.x map from testing on dev, all for 4.6.2 (owner, 2026-10-29: "this is effectively patching what we built").

Backend

  • Migration a1269b0c1d2e (revises f1263a0b1c2d; unreleased, so all three columns are in one migration):
    • trail_stops.after_session_number (#1269): the session a stop comes after. A between-sessions stop takes the last session played when it was entered. Backfilled.
    • trail_stops.position (#1275): a GM's order within a group. NULL until the group is first reordered.
    • trail_stops.jumped (#1273): no line leads to this stop. Not null, default false.
  • map_service.trail_order(): (after_session_number NULLS FIRST, own before between, position NULLS LAST, created_at, id). Used by list_trail and the export.
  • POST /parties/{id}/trail/{stop_id}/move {"direction": "earlier"|"later"} (GM): moves a stop within its group and numbers the group. 422 at either end, 404 for another party's stop.
  • add_trail_stop takes jumped, and stamps created_at with the call's time instead of the transaction's (two stops in one transaction tied).
  • GET .../trail adds jumped and after_session_number. Additive only, and not a bot route, so no contract bump.
  • Export/import carries after_session_key, position and jumped. Older files place between-sessions stops from created_at.

Frontend

  • #1271: every marker kind can be a stop, found by searching its name instead of picking from a dropdown.
  • #1275: "Add a stop…" replaces "Where the party ended…". The trail list is grouped by session, newest first, with the latest open. Earlier/Later buttons for a GM.
  • #1273: after a stop at a portal, the form asks "Where did they come out of …?" (a marker on any map, a spot, or empty). The portal stop itself never asks, so a session can end mid-portal. "No line to it: they went by portal or magic" is ticked after a portal. The page saves the plan's stops in order, asking for each spot. The trail is drawn in segments, broken before a jumped stop and where it changes map with no link.
  • #1276: "On this map" is grouped by kind and folded, with a "Find a marker" search that opens the groups with matches.

Wording

Mine, apart from "Add a stop…" (the owner's): "Where did they come out of …?", "Where did they end up?", "No line to it: they went by portal or magic", "by portal or magic", "Earlier"/"Later", "Find a marker", "Save the stops"/"Choose the spots", "Click or tap where they came out" and "Click or tap where they ended up".

Checked

  • Backend on docker-test (throwaway Postgres): maps API, edit API, both map migrations, the trail-order migration, the head pin, export files and maps, and setup copy: 209 passed. ruff format and check pass.
  • Frontend: vitest src/pages/wiki, with Map.test.jsx at 61 (new tests for the portal flow, a jump, trail groups and reordering, and On this map). eslint reports 0 errors. changelog.py check passes.

Not checked

  • The full backend suite (CI runs it).
  • No render-check in a browser.
  • PR #1274 (the portal rename) touches the same files; whichever merges second needs a small rebase.

🤖 Generated with Claude Code

Closes #1269. Closes #1271. Closes #1273. Closes #1275. Closes #1276. The owner's fixes to the 4.6.x map from testing on dev, all for 4.6.2 (owner, 2026-10-29: "this is effectively patching what we built"). ### Backend - Migration `a1269b0c1d2e` (revises `f1263a0b1c2d`; unreleased, so all three columns are in one migration): - `trail_stops.after_session_number` (#1269): the session a stop comes after. A between-sessions stop takes the last session played when it was entered. Backfilled. - `trail_stops.position` (#1275): a GM's order within a group. NULL until the group is first reordered. - `trail_stops.jumped` (#1273): no line leads to this stop. Not null, default false. - `map_service.trail_order()`: (after_session_number NULLS FIRST, own before between, position NULLS LAST, created_at, id). Used by `list_trail` and the export. - `POST /parties/{id}/trail/{stop_id}/move {"direction": "earlier"|"later"}` (GM): moves a stop within its group and numbers the group. 422 at either end, 404 for another party's stop. - `add_trail_stop` takes `jumped`, and stamps `created_at` with the call's time instead of the transaction's (two stops in one transaction tied). - `GET .../trail` adds `jumped` and `after_session_number`. Additive only, and not a bot route, so no contract bump. - Export/import carries `after_session_key`, `position` and `jumped`. Older files place between-sessions stops from `created_at`. ### Frontend - **#1271:** every marker kind can be a stop, found by searching its name instead of picking from a dropdown. - **#1275:** "Add a stop…" replaces "Where the party ended…". The trail list is grouped by session, newest first, with the latest open. Earlier/Later buttons for a GM. - **#1273:** after a stop at a portal, the form asks "Where did they come out of …?" (a marker on any map, a spot, or empty). The portal stop itself never asks, so a session can end mid-portal. "No line to it: they went by portal or magic" is ticked after a portal. The page saves the plan's stops in order, asking for each spot. The trail is drawn in segments, broken before a `jumped` stop and where it changes map with no link. - **#1276:** "On this map" is grouped by kind and folded, with a "Find a marker" search that opens the groups with matches. ### Wording Mine, apart from "Add a stop…" (the owner's): "Where did they come out of …?", "Where did they end up?", "No line to it: they went by portal or magic", "by portal or magic", "Earlier"/"Later", "Find a marker", "Save the stops"/"Choose the spots", "Click or tap where they came out" and "Click or tap where they ended up". ### Checked - Backend on `docker-test` (throwaway Postgres): maps API, edit API, both map migrations, the trail-order migration, the head pin, export files and maps, and setup copy: **209 passed**. ruff format and check pass. - Frontend: vitest `src/pages/wiki`, with Map.test.jsx at 61 (new tests for the portal flow, a jump, trail groups and reordering, and On this map). eslint reports 0 errors. `changelog.py check` passes. ### Not checked - The full backend suite (CI runs it). - No render-check in a browser. - PR #1274 (the portal rename) touches the same files; whichever merges second needs a small rebase. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The trail was ordered by created_at and the map takes the last stop as
"The party is here", so backfilling earlier sessions moved the party.
trail_stops.after_session_number holds the session a stop comes after:
its own session's number, or for a stop between sessions the last played
session's when it was entered. The trail sorts by that, a session's own
stops before those made between it and the next, then by entry.

Migration a1269b0c1d2e backfills existing stops. The campaign export
carries the position as after_session_key; an older file's between-sessions
stops are placed from their created_at, as the migration does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The form offered only place, portal, holding and quest markers, so a
danger such as "Goblin Ambush" could not be a stop, and it was a
dropdown, which a long campaign's map would outgrow. It is now a search
over every marker on the map, the first eight matches listed by name and
kind; with none chosen it goes on to choose a spot, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test(webapp): a trail stop's place survives an import (#1269)
Some checks failed
CI / Bot/backend version sync (pull_request) Successful in 29s
CI / Backend lint (ruff) (pull_request) Successful in 34s
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 / Feedback site tests (pull_request) Successful in 52s
CI / Which journeys does this change need? (pull_request) Successful in 26s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m2s
CI / Bot tests and audit (pull_request) Successful in 2m9s
CI / Docker image build (pull_request) Successful in 8m24s
CI / Frontend (shard 2/2) (pull_request) Successful in 9m4s
CI / Frontend (shard 1/2) (pull_request) Successful in 10m57s
CI / End-to-end journeys (desktop) (pull_request) Successful in 14m38s
CI / Backend migration, tests, and audit (pull_request) Has been cancelled
2c1b4b5bb3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- trail_stops.position (a GM's order within a session) and .jumped (no
  line leads here), in the unreleased migration a1269b0c1d2e. POST
  .../trail/{stop_id}/move moves a stop earlier or later in its group.
- "Add a stop" replaces "Where did the party end up?". After a stop at a
  portal it asks where the party came out (a marker on any map, a spot,
  or nothing yet); the page saves the plan's stops in order, asking for
  each spot. The trail is drawn in segments, broken before a jumped stop
  and where it changes map with no link.
- The trail list is grouped by session, newest first, the latest open,
  with Earlier/Later for a GM. "On this map" is grouped by kind, folded,
  with a search.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(webapp): a trail stop's entry time is the call's, not the transaction's (#1275)
All checks were successful
CI / Does this change touch the recording path? (pull_request) Successful in 14s
CI / Which journeys does this change need? (pull_request) Successful in 14s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Bot/backend version sync (pull_request) Successful in 25s
CI / Docker image build (pull_request) Successful in 25s
CI / Backend lint (ruff) (pull_request) Successful in 29s
CI / Feedback site tests (pull_request) Successful in 47s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 2m24s
CI / Bot tests and audit (pull_request) Successful in 3m44s
CI / End-to-end journeys (desktop) (pull_request) Successful in 10m42s
CI / Frontend (shard 2/2) (pull_request) Successful in 14m58s
CI / Frontend (shard 1/2) (pull_request) Successful in 17m33s
CI / Backend migration, tests, and audit (pull_request) Successful in 1h3m48s
378571ffdc
Two stops added in one transaction tied on created_at (now() is the
transaction's start), and the trail's order within a group then fell to
their ids.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude-bot changed title from fix(webapp): the party's trail in session order, and a searchable stop form (#1269, #1271) to fix(webapp): the party's trail, as it needs to work (#1269, #1271, #1273, #1275, #1276) 2026-10-29 17:06:13 +00:00
"Link to another map…" lists this map too, without the portal itself;
the server refuses a portal linked to itself. After a stop at a portal,
"Where did they come out?" puts that portal's linked exits first,
marked "linked".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge remote-tracking branch 'origin/main' into fix/trail-order-by-session
All checks were successful
CI / Bot/backend version sync (pull_request) Successful in 26s
CI / Backend lint (ruff) (pull_request) Successful in 33s
CI / Docker image build (pull_request) Successful in 34s
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 / Feedback site tests (pull_request) Successful in 48s
CI / Which journeys does this change need? (pull_request) Successful in 14s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 2m43s
CI / Bot tests and audit (pull_request) Successful in 4m27s
CI / End-to-end journeys (desktop) (pull_request) Successful in 14m14s
CI / Frontend (shard 2/2) (pull_request) Successful in 18m29s
CI / Frontend (shard 1/2) (pull_request) Successful in 22m43s
CI / Backend migration, tests, and audit (pull_request) Successful in 55m4s
ec080c989b
# Conflicts:
#	webapp/frontend/src/pages/wiki/Map.jsx
claude-bot scheduled this pull request to auto merge when all checks succeed 2026-10-29 20:06:00 +00:00
claude-bot deleted branch fix/trail-order-by-session 2026-10-29 21:01:07 +00:00
Sign in to join this conversation.
No description provided.