The session page, while it is finding its time: the frame, voting and pencilled in (SESS-1, lane 4) #812

Merged
claude-bot merged 6 commits from build/lane4-page-sess1 into main 2026-09-24 16:58:29 +00:00
Collaborator

Lane 4's first page PR (#730). This is the new session page's frame plus SESS-1. Every other status still renders the old SessionDetail, which is the rollout rule; the next page PRs replace the rest.

The frame

  • pages/session/SessionPage.jsx dispatches on status. A vote and pencilled in are new. Anything else goes to the legacy page, unchanged.
  • Page states: loading, "This session is not here", and a banner for a GM action that failed. The banner names what didn't happen and what's unchanged, with Try again. Nothing rendered is destroyed.
  • The head: a Session N label, the title, and a chip that is always a word (Finding a time, Pencilled in). Then the description and the lead sentence.
  • The Manage menu (D52): Edit the title and description, Start the vote again, Let the table vote instead, and Cancel the session. Cancelling asks "Why, for the people you are telling" (D101).
  • The shell knows the session's place. The crumb is Campaign › Sessions › Session 14 (D86). The Contents lists the session's own sections under Sessions and follows the scroll. The tab title follows.

A vote

  • On a desktop, the grid. Your row is the only one with buttons. Everyone else's cells show a word and a dot. Each time carries its points, ★ "Ahead so far" on every time tied for the lead, and a clash line ("⚠ Marcus, Ade usually can't").
  • Answers: a saving state, and a failure that names the time with "Send “Yes” again".
  • The legend.
  • A traveller sees the reader's own time, with the table's day only where it differs (D17).
  • On a phone (D29), one row per time with your own Yes/Maybe/No, laid out by container query.
  • The GM:
    • Add another time, up to five; at five the button is disabled and says why.
    • Remove, with the drawing's question, while more than two times remain.
    • Confirm Monday, the leading time named by its weekday.
    • Start the vote again, through reopen-for-voting (#809).
  • Who is in.

Pencilled in (D50)

  • When: one time.
  • The GM gets Confirm it, and Move it, which previews how a series ripples (#809, #804). Let the table vote instead clears the soft answers (D100).
  • Could you make it?: players answer, and the GM isn't asked. Then "So far · 3 of 5 have said" and the roster.

Notes (SESS-7)

components/session/NotesPanel uses #811's API.

  • The audience switch is lane 0's AudiencePicker: "Just you and the GM" or "Everyone at the table" for a player, "GMs only" or the table for a GM.
  • "Last changed by …".
  • The 409 "Show me his".
  • A GM sees the players' own notes in a fold (D97/D106).

Shared pieces

  • The roster, the Yes/Maybe/No row and the tally helpers move from This week to components/session/, and This week uses them.
  • On the server, campaign_timezone and my_role are added to GET /api/sessions/{id}, additively.

Departures (recorded on #730)

  • Adding or moving a time takes it in the campaign's zone, as the drawing and lane 3's session form do.
  • The notes switch uses the design system's audience words.
  • The GM has no row in the vote grid and isn't counted.
  • "Start the vote again" is one modal, not two steps.

Checks, on the tree merged with main (#811)

  • Frontend: 155 files, 2144 tests. Lint: 0 errors.
  • Backend: the targeted tests pass (the new fields, sessions, notes, scheduling: 132). Ruff is clean.
  • A render check against the drawing in Chromium, at 390px and at desktop width, light and dark, fixed two faults: the grid's person disc stacked above the name, and the phone's answer buttons wrapped.

🤖 Generated with Claude Code

Lane 4's first page PR (#730). This is the new session page's frame plus **SESS-1**. **Every other status still renders the old `SessionDetail`**, which is the rollout rule; the next page PRs replace the rest. ## The frame - **`pages/session/SessionPage.jsx` dispatches on status.** A vote and pencilled in are new. Anything else goes to the legacy page, unchanged. - **Page states:** loading, "This session is not here", and a banner for a GM action that failed. The banner names what didn't happen and what's unchanged, with Try again. Nothing rendered is destroyed. - **The head:** a `Session N` label, the title, and a chip that is always a word (**Finding a time**, **Pencilled in**). Then the description and the lead sentence. - **The Manage menu (D52):** Edit the title and description, Start the vote again, Let the table vote instead, and Cancel the session. Cancelling asks "Why, for the people you are telling" (D101). - **The shell knows the session's place.** The crumb is `Campaign › Sessions › Session 14` (D86). The Contents lists the session's own sections under Sessions and follows the scroll. The tab title follows. ## A vote - **On a desktop, the grid.** Your row is the only one with buttons. Everyone else's cells show a word and a dot. Each time carries its points, ★ "Ahead so far" on every time tied for the lead, and a clash line ("⚠ Marcus, Ade usually can't"). - **Answers:** a saving state, and a failure that names the time with "Send “Yes” again". - **The legend.** - **A traveller** sees the reader's own time, with the table's day only where it differs (D17). - **On a phone (D29),** one row per time with your own Yes/Maybe/No, laid out by container query. - **The GM:** - **Add another time**, up to five; at five the button is disabled and says why. - **Remove**, with the drawing's question, while more than two times remain. - **Confirm Monday**, the leading time named by its weekday. - **Start the vote again**, through `reopen-for-voting` (#809). - **Who is in.** ## Pencilled in (D50) - **When:** one time. - **The GM** gets Confirm it, and Move it, which previews how a series ripples (#809, #804). **Let the table vote instead** clears the soft answers (D100). - **Could you make it?**: players answer, and the GM isn't asked. Then "So far · 3 of 5 have said" and the roster. ## Notes (SESS-7) `components/session/NotesPanel` uses #811's API. - The audience switch is lane 0's `AudiencePicker`: "Just you and the GM" or "Everyone at the table" for a player, "GMs only" or the table for a GM. - "Last changed by …". - The 409 "Show me his". - A GM sees the players' own notes in a fold (D97/D106). ## Shared pieces - The roster, the Yes/Maybe/No row and the tally helpers move from This week to `components/session/`, and This week uses them. - On the server, `campaign_timezone` and `my_role` are added to `GET /api/sessions/{id}`, additively. ## Departures (recorded on #730) - Adding or moving a time takes it in **the campaign's zone**, as the drawing and lane 3's session form do. - The notes switch uses the design system's audience words. - The GM has no row in the vote grid and isn't counted. - "Start the vote again" is one modal, not two steps. ## Checks, on the tree merged with main (#811) - Frontend: **155 files, 2144 tests**. Lint: 0 errors. - Backend: the targeted tests pass (the new fields, sessions, notes, scheduling: 132). Ruff is clean. - **A render check against the drawing** in Chromium, at 390px and at desktop width, light and dark, fixed two faults: the grid's person disc stacked above the name, and the phone's answer buttons wrapped. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
GET /api/sessions/{id} now carries two facts that are not the session's:
campaign_timezone, which D17's reconciling line is drawn from, and my_role,
which decides whether the reader gets the GM's drawing or a player's. Both
are additive and set only on the page's own read, so every other response
that shares SessionResponse keeps them null. The role query replaces the
GM-only existence check it was derived from, so the transcript strip is
decided by the same answer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This week's roster, its readings (tallyOf, the order, whose name and pronouns
a row carries, whether answers are open) and its Yes/Maybe/No row are what the
session page draws too, so they are lifted into components/session/ rather
than copied, and This week uses the extracted versions. Their CSS moves with
them, as ThisWeek.css said it would the day a second page wanted it. The
session page's words (the state chips, its headings, Manage, the three
answers) join vocabulary.js.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
useDeclareCampaign's session may now carry the page's sections, [{id, label}],
because a session's headings differ by its state and only the page knows which
list it is drawing. The Contents hangs them under Sessions below the
"Session 14" sub-head, and the frame observes them with useSectionInView as it
does Profile's. The crumb reads "<Campaign> > Sessions > Session 14", Sessions a
link to the list, on every session address; that step is marked inTitle:false
so the tab stays "Session 14 · The Amberwake Accord · Quest Board". The vote
email's old #vote lands on "Times to vote on" through SECTION_ALIASES.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
NotesPanel reads GET /api/sessions/{id}/notes and saves with
PUT .../notes/{audience} and the version it loaded, as the parallel notes PR
specifies. A player writes for "Just you and the GM" or the table, a GM for
"GMs only" or the table (lane 0's AudiencePicker), and a line under the switch
says in words who reads it. The table note says who changed it last and when.
A 409 keeps what was typed and offers the winning version ("Show me his", from
the person's own pronouns). A GM also reads each player's own note, read-only,
in a "Players' own notes" fold. The API client also gains the reschedule
preview (#804).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
/sessions/:id is now pages/session/SessionPage, which loads the session and
draws it by status: a proposed vote gets the vote, a proposed tentative
session gets pencilled in (D50), and every other status renders the old
SessionDetail unchanged (lazily, in its own chunk) until 5b and later move
them across. The page owns its states (loading; not here for a 404 or a 403;
a GM action that failed as a banner over a page still all there, with Try
again) and the GM's state-changing actions: confirm, cancel with a reason
(D101), move (with the series ripple, D99/#804), and putting it to a vote
again or instead (D100, reopen-for-voting, the 422's own sentence).

The vote is a real table on a desktop and a list on a phone (D29), chosen by
a container query on the section: every answer a word beside its dot, the
reader's row the only one with buttons, each named for its answer and time,
cycling and saving cell by cell, a failed answer marked and sent again as the
intent it was. Every time tied for the lead is starred, none while the top is
0; who usually can't is written out; every time is the reader's own, with
"Monday for the table" only where the day differs (D17). The GM adds a time
(in the campaign's zone, as the session form authors), removes one after
saying how many answers go, and confirms the time ahead by its weekday;
Manage holds the rest, the destructive items apart (D52). The GM is not asked
in a vote or in pencilled in.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merge remote-tracking branch 'origin/main' into build/lane4-page-sess1
All checks were successful
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 55s
CI / Bot/backend version sync (pull_request) Successful in 1m11s
CI / Backend lint (ruff) (pull_request) Successful in 1m17s
CI / Does this change touch the recording path? (pull_request) Successful in 18s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Feedback site tests (pull_request) Successful in 1m52s
CI / Bot tests and audit (pull_request) Successful in 3m39s
CI / Docker image build (pull_request) Successful in 4m8s
CI / Frontend tests, audit, and build (pull_request) Successful in 5m23s
CI / Backend migration, tests, and audit (pull_request) Successful in 11m20s
CI / End-to-end journeys (desktop, phone, keyboard-only) (pull_request) Successful in 10m28s
2c9082b5e4
# Conflicts:
#	webapp/frontend/src/api/sessions.js
Sign in to join this conversation.
No description provided.