WIP: Foundry slice 2, send to Foundry (#9, #146, #7, #148, #151) #1369

Draft
claude-bot wants to merge 43 commits from feat/9-foundry-push into main
Collaborator

Draft, not to merge before #1368 and v4.8.0 final. This branch contains slice 1 (#1368). It is open for CI.

Slice 2 of v5.0. The contract is docs/design/foundry-push.md.

  • Queue (D335): commands wait in Postgres. Only the holder delivers them: claimed under SKIP LOCKED with a per-campaign advisory lock (cap of 10 in flight), a Redis DB 0 nudge, a heartbeat drain, requeue on close or after 60 s, failed after 5 attempts or 14 days. Every drain re-checks the token; unpair and remove-GM stop delivery at once.
  • Links (#146): a second send updates the actor, an unchanged one is a no-op, and force skips that check.
  • Adapters (#7, #151): keyed by the campaign's one game system. pf2e npc_to_actor (#9, #148) works from a conjured NPC, a wiki page or a rules-library creature (compendium first). Unmapped values go to the notes. A page's public notes get only the table's text and its private notes only the :::gm blocks. Players' blocks and private values are never sent.
  • Module: applies each command once (actor flags), with an allowlisted create, GM-only whispers (D342) and scene_activated events.
  • Checked live on docker-test: with real Postgres, Redis and two workers, there were no duplicates under a 24-way claim, cross-worker nudges took under 1 s, unpair closed the socket in 0.02 s, and no token or payload appeared in the logs. The cap overshoot that check found is fixed, and was re-checked with 12 concurrent claims taking exactly 10.

Still to do: the Send to Foundry buttons and "N waiting" (after Q251–Q253), the wording review for them and the whispers, a render check, and a live push to foundry-dev.

🤖 Generated with Claude Code

**Draft, not to merge before #1368 and v4.8.0 final.** This branch contains slice 1 (#1368). It is open for CI. Slice 2 of v5.0. The contract is `docs/design/foundry-push.md`. - **Queue (D335):** commands wait in Postgres. Only the holder delivers them: claimed under SKIP LOCKED with a per-campaign advisory lock (cap of 10 in flight), a Redis DB 0 nudge, a heartbeat drain, requeue on close or after 60 s, failed after 5 attempts or 14 days. Every drain re-checks the token; unpair and remove-GM stop delivery at once. - **Links (#146):** a second send updates the actor, an unchanged one is a no-op, and `force` skips that check. - **Adapters (#7, #151):** keyed by the campaign's one game system. pf2e `npc_to_actor` (#9, #148) works from a conjured NPC, a wiki page or a rules-library creature (compendium first). Unmapped values go to the notes. A page's public notes get only the table's text and its private notes only the `:::gm` blocks. Players' blocks and private values are never sent. - **Module:** applies each command once (actor flags), with an allowlisted create, GM-only whispers (D342) and `scene_activated` events. - **Checked live on docker-test:** with real Postgres, Redis and two workers, there were no duplicates under a 24-way claim, cross-worker nudges took under 1 s, unpair closed the socket in 0.02 s, and no token or payload appeared in the logs. The cap overshoot that check found is fixed, and was re-checked with 12 concurrent claims taking exactly 10. **Still to do:** the Send to Foundry buttons and "N waiting" (after Q251–Q253), the wording review for them and the whispers, a render check, and a live push to foundry-dev. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A token keeps working only while its pairer is a GM (owning the campaign
is not enough), hello needs role 4 as pairing does, and a token is bound
to the Foundry user it was made for. The module only connects for a
full Gamemaster. Also: the CAMP-16 catalogue rows for pairing, the
migration head in webapp/CLAUDE.md, and the WebSocket note for a
self-hoster's proxy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test(webapp): Foundry settings tests, lint fix, Contents count
Some checks failed
CI / Bot tests and audit (pull_request) Has been cancelled
CI / Backend lint (ruff) (pull_request) Has been cancelled
CI / Feedback site tests (pull_request) Has been cancelled
CI / Foundry module tests (pull_request) Has been cancelled
CI / Bot/backend version sync (pull_request) Has been cancelled
CI / Docker image build (pull_request) Has been cancelled
CI / Does this change touch the recording path? (pull_request) Has been cancelled
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been cancelled
CI / Which journeys does this change need? (pull_request) Has been cancelled
CI / Frontend (shard 2/2) (pull_request) Has been cancelled
CI / Frontend (shard 1/2) (pull_request) Has been cancelled
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Has been cancelled
CI / Backend migration, tests, and audit (pull_request) Has been cancelled
4e2270a43e
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge remote-tracking branch 'origin/main' into feat/5-foundry-connect
Some checks failed
CI / Backend lint (ruff) (pull_request) Failing after 35s
CI / Foundry module tests (pull_request) Successful in 20s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 58s
CI / Which journeys does this change need? (pull_request) Successful in 22s
CI / Does this change touch the recording path? (pull_request) Successful in 23s
CI / Bot/backend version sync (pull_request) Successful in 29s
CI / Feedback site tests (pull_request) Successful in 1m33s
CI / Bot tests and audit (pull_request) Successful in 2m4s
CI / Docker image build (pull_request) Successful in 4m55s
CI / Backend migration, tests, and audit (pull_request) Failing after 8m30s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Successful in 11m35s
CI / Frontend (shard 2/2) (pull_request) Successful in 16m21s
CI / End-to-end journeys (phone) (pull_request) Successful in 13m2s
CI / Frontend (shard 1/2) (pull_request) Failing after 19m34s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 28m3s
CI / End-to-end journeys (desktop) (pull_request) Successful in 28m7s
036bbcfea0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only name, type npc, img, system and our own flag scope (link and
command) reach Actor.create; ownership, folders, embedded documents and
other modules' flags are dropped. A compendium copy also loses _stats
and sort. Found by the security review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(webapp): the Unpair confirm says what it does, and Remove sits at the row's end (#5)
Some checks failed
CI / Backend lint (ruff) (pull_request) Failing after 37s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m4s
CI / Does this change touch the recording path? (pull_request) Successful in 35s
CI / Foundry module tests (pull_request) Successful in 40s
CI / Which journeys does this change need? (pull_request) Successful in 17s
CI / Feedback site tests (pull_request) Successful in 1m35s
CI / Bot/backend version sync (pull_request) Successful in 58s
CI / Frontend (shard 2/2) (pull_request) Has been cancelled
CI / Bot tests and audit (pull_request) Has been cancelled
CI / Frontend (shard 1/2) (pull_request) Has been cancelled
CI / Backend migration, tests, and audit (pull_request) Has been cancelled
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been cancelled
CI / Docker image build (pull_request) Has been cancelled
6482412ac7
Render check: the confirm had a title and buttons only; it now repeats
the band's approved sentence (ac-030). The GM list no longer stops at
40rem, so Remove lines up at the column's right edge on desktop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
style: ruff format the Foundry socket test
Some checks failed
CI / Foundry module tests (pull_request) Successful in 18s
CI / Bot/backend version sync (pull_request) Successful in 33s
CI / Does this change touch the recording path? (pull_request) Successful in 21s
CI / Which journeys does this change need? (pull_request) Successful in 19s
CI / Backend lint (ruff) (pull_request) Successful in 1m20s
CI / Feedback site tests (pull_request) Successful in 1m40s
CI / Bot tests and audit (pull_request) Successful in 2m9s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 3m3s
CI / Docker image build (pull_request) Successful in 4m6s
CI / Frontend (shard 2/2) (pull_request) Successful in 8m1s
CI / Frontend (shard 1/2) (pull_request) Failing after 9m28s
CI / Backend migration, tests, and audit (pull_request) Failing after 9m31s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Successful in 8m55s
CI / End-to-end journeys (phone) (pull_request) Successful in 10m50s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 11m31s
CI / End-to-end journeys (desktop) (pull_request) Successful in 11m33s
9bdd5a5791
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Commands queue in Postgres and only the holder delivers them: claimed
by id under SKIP LOCKED, woken by a Redis DB 0 nudge, drained on the
heartbeat, requeued when the holder closes or after 60 s, failed after
5 attempts or 14 days (FV4 default). Every drain re-checks the token
and the claim requires the world row, so an unpair stops delivery at
once; unpair and remove-GM clear the holder key and nudge. Links make a
second send an update and an unchanged one a no-op (force skips that).
The pf2e adapter maps a conjured NPC, a wiki page (envelope or sheet
projection) or a rules-library creature (compendium first, FV3) to an
npc actor; unmapped values go to the notes as sanitised HTML, and
player-private values are never sent. Migration a5002b3c4d5e. A daily
Beat task expires old commands.

Built by a delegate; squashed here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Security review: the page source handed the raw body to the actor's
public notes, :::gm blocks and players' :::for/:::party text included.
Now the public notes get wiki_audience.table_text (empty for a page
narrower than the table, summary too), the private notes get only the
:::gm blocks (and a GM-only page's open text) through a new
wiki_audience.gm_text, and players' blocks are never sent. The new raw
read is registered in tests/wiki/test_body_readers.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The live check saw 24 commands in flight against a cap of 10 when
several sessions claimed at once: each counted, then claimed. A
transaction-scoped advisory lock per campaign now covers the count and
the claim.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test: the guards for the Foundry section and its tokens (#5)
Some checks failed
CI / Foundry module tests (pull_request) Successful in 15s
CI / Backend lint (ruff) (pull_request) Successful in 37s
CI / Bot/backend version sync (pull_request) Successful in 26s
CI / Which journeys does this change need? (pull_request) Successful in 32s
CI / Does this change touch the recording path? (pull_request) Successful in 34s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 57s
CI / Feedback site tests (pull_request) Successful in 1m35s
CI / Bot tests and audit (pull_request) Successful in 2m6s
CI / Docker image build (pull_request) Successful in 6m53s
CI / Frontend (shard 2/2) (pull_request) Successful in 7m44s
CI / Frontend (shard 1/2) (pull_request) Successful in 9m12s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Successful in 8m38s
CI / End-to-end journeys (phone) (pull_request) Successful in 11m20s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 12m7s
CI / End-to-end journeys (desktop) (pull_request) Successful in 13m1s
CI / Backend migration, tests, and audit (pull_request) Failing after 27m58s
0d20287c9a
CI: the settings list now has Foundry; the not-found route test asks
for a section that does not exist (every drawn one is built); and the
export ledger says foundry_tokens never travel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(webapp): the Foundry socket gets its sessions from main.py (#5)
All checks were successful
CI / Foundry module tests (pull_request) Successful in 17s
CI / Backend lint (ruff) (pull_request) Successful in 39s
CI / Bot/backend version sync (pull_request) Successful in 29s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m1s
CI / Which journeys does this change need? (pull_request) Successful in 40s
CI / Does this change touch the recording path? (pull_request) Successful in 42s
CI / Feedback site tests (pull_request) Successful in 1m36s
CI / Bot tests and audit (pull_request) Successful in 2m8s
CI / Docker image build (pull_request) Successful in 6m12s
CI / Frontend (shard 2/2) (pull_request) Successful in 8m21s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Successful in 8m58s
CI / Frontend (shard 1/2) (pull_request) Successful in 10m12s
CI / End-to-end journeys (phone) (pull_request) Successful in 23m48s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 25m35s
CI / End-to-end journeys (desktop) (pull_request) Successful in 25m59s
CI / Backend migration, tests, and audit (pull_request) Successful in 39m37s
eb6417c1f2
CI: tests/test_task_session_hygiene.py allows the pooled
AsyncSessionLocal only in database.py, main.py and cli.py. The socket
is a web connection, not a Celery task, so main.py hands the factory to
it through app.state rather than the router importing it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge remote-tracking branch 'origin/feat/5-foundry-connect' into feat/9-foundry-push
All checks were successful
CI / Foundry module tests (pull_request) Successful in 18s
CI / Backend lint (ruff) (pull_request) Successful in 36s
CI / Bot/backend version sync (pull_request) Successful in 27s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m4s
CI / Which journeys does this change need? (pull_request) Successful in 41s
CI / Does this change touch the recording path? (pull_request) Successful in 42s
CI / Feedback site tests (pull_request) Successful in 1m36s
CI / Bot tests and audit (pull_request) Successful in 2m13s
CI / Frontend (shard 2/2) (pull_request) Successful in 8m14s
CI / Docker image build (pull_request) Successful in 9m13s
CI / Frontend (shard 1/2) (pull_request) Successful in 9m52s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Successful in 9m4s
CI / End-to-end journeys (phone) (pull_request) Successful in 10m21s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 11m4s
CI / End-to-end journeys (desktop) (pull_request) Successful in 11m5s
CI / Backend migration, tests, and audit (pull_request) Successful in 39m26s
66a52fda93
docs: D343-D345, where Send to Foundry goes, rules-library creatures, and the 14-day wait (#9)
All checks were successful
CI / Backend lint (ruff) (pull_request) Successful in 33s
CI / Does this change touch the recording path? (pull_request) Successful in 35s
CI / Foundry module tests (pull_request) Successful in 43s
CI / Which journeys does this change need? (pull_request) Successful in 21s
CI / Bot/backend version sync (pull_request) Successful in 57s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m7s
CI / Bot tests and audit (pull_request) Successful in 2m15s
CI / Feedback site tests (pull_request) Successful in 3m35s
CI / Docker image build (pull_request) Successful in 6m19s
CI / Frontend (shard 2/2) (pull_request) Successful in 8m44s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Successful in 11m29s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 13m16s
CI / Frontend (shard 1/2) (pull_request) Successful in 20m55s
CI / End-to-end journeys (phone) (pull_request) Successful in 21m17s
CI / End-to-end journeys (desktop) (pull_request) Successful in 23m16s
CI / Backend migration, tests, and audit (pull_request) Successful in 41m27s
68118a13a2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GET /foundry gains can_send (paired, an adapter for the campaign's game
system, and no mismatch), so a system with no adapter shows no button
rather than one that fails (the frontend could not tell before). And,
from the security review: a sidebar row is public only when its
audience is the table and GM-only only when it is the GMs; a party's,
a player's own, chosen people's or an unknown audience's row is never
sent, as with the page's text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
style: ruff format the push route tests
Some checks failed
CI / Foundry module tests (pull_request) Successful in 20s
CI / Backend lint (ruff) (pull_request) Successful in 30s
CI / Which journeys does this change need? (pull_request) Successful in 22s
CI / Does this change touch the recording path? (pull_request) Successful in 23s
CI / Bot/backend version sync (pull_request) Successful in 51s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 59s
CI / Bot tests and audit (pull_request) Successful in 2m3s
CI / Feedback site tests (pull_request) Successful in 3m5s
CI / Docker image build (pull_request) Successful in 5m3s
CI / Frontend (shard 2/2) (pull_request) Successful in 15m41s
CI / End-to-end journeys (phone) (pull_request) Successful in 11m3s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 11m39s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Successful in 18m19s
CI / Frontend (shard 1/2) (pull_request) Successful in 20m21s
CI / Backend migration, tests, and audit (pull_request) Has been cancelled
CI / End-to-end journeys (desktop) (pull_request) Has been cancelled
30458ac498
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(webapp): Send to Foundry's accessible name starts with what it shows (#9)
All checks were successful
CI / Foundry module tests (pull_request) Successful in 22s
CI / Bot/backend version sync (pull_request) Successful in 29s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m15s
CI / Backend lint (ruff) (pull_request) Successful in 1m19s
CI / Does this change touch the recording path? (pull_request) Successful in 21s
CI / Feedback site tests (pull_request) Successful in 1m52s
CI / Which journeys does this change need? (pull_request) Successful in 31s
CI / Bot tests and audit (pull_request) Successful in 2m19s
CI / Docker image build (pull_request) Successful in 2m41s
CI / Frontend (shard 2/2) (pull_request) Successful in 8m30s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Successful in 9m14s
CI / End-to-end journeys (phone) (pull_request) Successful in 12m12s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 12m54s
CI / End-to-end journeys (desktop) (pull_request) Successful in 12m57s
CI / Frontend (shard 1/2) (pull_request) Successful in 26m19s
CI / Backend migration, tests, and audit (pull_request) Successful in 41m38s
fea1c3bc13
Render check: axe's label-content-name-mismatch (WCAG 2.5.3) on every
state showing the button. The name is now the visible label, then
which one: "Send to Foundry: Odile Marsh", "Send again: …",
"Try again: …".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
style(foundry): ruff format
Some checks failed
CI / Foundry module tests (pull_request) Successful in 17s
CI / Bot/backend version sync (pull_request) Successful in 27s
CI / Does this change touch the recording path? (pull_request) Successful in 29s
CI / Backend lint (ruff) (pull_request) Successful in 1m12s
CI / Which journeys does this change need? (pull_request) Successful in 23s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 2m3s
CI / Feedback site tests (pull_request) Successful in 2m39s
CI / Bot tests and audit (pull_request) Successful in 3m21s
CI / Docker image build (pull_request) Successful in 5m12s
CI / Frontend (shard 1/2) (pull_request) Successful in 10m44s
CI / Backend migration, tests, and audit (pull_request) Has been cancelled
CI / End-to-end journeys (phone) (pull_request) Has been cancelled
CI / Frontend (shard 2/2) (pull_request) Has been cancelled
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been cancelled
CI / End-to-end journeys (keyboard) (pull_request) Has been cancelled
CI / End-to-end journeys (desktop) (pull_request) Has been cancelled
7e3c0ef4f7
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(foundry): the status marker shows only the state word (#6)
Some checks failed
CI / Backend lint (ruff) (pull_request) Successful in 44s
CI / Foundry module tests (pull_request) Successful in 51s
CI / Bot/backend version sync (pull_request) Successful in 32s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m23s
CI / Does this change touch the recording path? (pull_request) Successful in 32s
CI / Which journeys does this change need? (pull_request) Successful in 31s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been cancelled
CI / Feedback site tests (pull_request) Has been cancelled
CI / Docker image build (pull_request) Has been cancelled
CI / Frontend (shard 1/2) (pull_request) Has been cancelled
CI / Bot tests and audit (pull_request) Has been cancelled
CI / Backend migration, tests, and audit (pull_request) Has been cancelled
CI / Frontend (shard 2/2) (pull_request) Has been cancelled
b56d416dad
Live test: the players list is too narrow for "Quest Board: Connected"
and it trailed off as "Quest ...". The marker now shows Connected,
Waiting, Connecting or Not connected; the full sentence, with who holds
the link, is its accessible name and hover title.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(foundry): the status marker has its own full-width row above Latency and FPS (#6)
All checks were successful
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m18s
CI / Backend lint (ruff) (pull_request) Successful in 41s
CI / Foundry module tests (pull_request) Successful in 19s
CI / Does this change touch the recording path? (pull_request) Successful in 31s
CI / Bot/backend version sync (pull_request) Successful in 54s
CI / Which journeys does this change need? (pull_request) Successful in 30s
CI / Bot tests and audit (pull_request) Successful in 4m6s
CI / Feedback site tests (pull_request) Successful in 2m17s
CI / Docker image build (pull_request) Successful in 6m29s
CI / Frontend (shard 2/2) (pull_request) Successful in 10m1s
CI / Frontend (shard 1/2) (pull_request) Successful in 13m0s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Successful in 11m12s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 14m19s
CI / End-to-end journeys (desktop) (pull_request) Successful in 14m25s
CI / End-to-end journeys (phone) (pull_request) Successful in 23m48s
CI / Backend migration, tests, and audit (pull_request) Successful in 1h12m17s
e6f8cf7460
Live test: inside #players it landed in a narrow cell and was cut to
"Quest ..." or "Conne...". Foundry 14's players list puts the
performance-stats row last in #players-active; the marker now sits in
its own row just above it, so "Quest Board: Connected" fits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
All checks were successful
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m18s
Required
Details
CI / Backend lint (ruff) (pull_request) Successful in 41s
Required
Details
CI / Foundry module tests (pull_request) Successful in 19s
Required
Details
CI / Does this change touch the recording path? (pull_request) Successful in 31s
Required
Details
CI / Bot/backend version sync (pull_request) Successful in 54s
Required
Details
CI / Which journeys does this change need? (pull_request) Successful in 30s
Required
Details
CI / Bot tests and audit (pull_request) Successful in 4m6s
Required
Details
CI / Feedback site tests (pull_request) Successful in 2m17s
Required
Details
CI / Docker image build (pull_request) Successful in 6m29s
Required
Details
CI / Frontend (shard 2/2) (pull_request) Successful in 10m1s
Required
Details
CI / Frontend (shard 1/2) (pull_request) Successful in 13m0s
Required
Details
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Successful in 11m12s
Required
Details
CI / End-to-end journeys (keyboard) (pull_request) Successful in 14m19s
Required
Details
CI / End-to-end journeys (desktop) (pull_request) Successful in 14m25s
Required
Details
CI / End-to-end journeys (phone) (pull_request) Successful in 23m48s
Required
Details
CI / Backend migration, tests, and audit (pull_request) Successful in 1h12m17s
Required
Details
This pull request is marked as a work in progress.
This branch is out-of-date with the base branch
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/9-foundry-push:feat/9-foundry-push
git switch feat/9-foundry-push
Sign in to join this conversation.
No description provided.