Build lane 4: sessions (SESS-1 to SESS-7) #730

Closed
opened 2026-09-22 00:12:21 +00:00 by claude-bot · 12 comments
Collaborator

Build lane 4 of the v4.5.0 redesign. The brief is docs/design/v4.5.0-brief.md (§9, the lanes in order); the build plan is docs/design/v4.5.0-build-plan.md (the rollout rule, the design-system mapping, the journeys as tests). Umbrella: #366. Goes after lane 3.

Scope

Surfaces: SESS-1 to SESS-7: a session before, during and after; voting and Are you in?; the transcript and summary review; the checklist; the recording facts.
The drawing: /feedback/prototype/lane3 — the page the build must match, still by still.
Decisions: the brief §7, and each surface's header in redesign-surfaces/sources/headers.md.

Server changes it needs (in this lane or an earlier one)

  • persisted corrections (D31); summary versions (IS-379); soft answers on pencilled-in sessions (D50); one notes model (D30)
  • the Talk tab reads lane 7's Discussions when that lane lands; until then the session keeps its own notes

Acceptance

  • Every placement coverage/lane3.py classifies as drawn or replaced is built as the prototype draws it; the script is the checklist. Rows it marks behaviour are tested, not drawn.
  • J2 (votes, RSVP, the confirmed session) and J5 (the checklist, transcript and summary review, approval) pass on desktop, on a phone and keyboard-only in the journeys suite (#726), with the axe scan clean (WCAG 2.2 AA, #367).
  • The brief's vocabulary (§5) throughout, from vocabulary.js; no page-level error destroys rendered content; every async operation shows progress and a next action on failure; disabled controls say why.
  • Main stays deployable after every PR of the lane: the new shell carries the old pages until each is replaced (the rollout rule).
  • The surfaces' headers in redesign-surfaces/sources/headers.md (Decided lines and the owner's review notes) are honoured; a departure is a decision change on #659 first.

🤖 Generated with Claude Code

**Build lane 4 of the v4.5.0 redesign.** The brief is `docs/design/v4.5.0-brief.md` (§9, the lanes in order); the build plan is `docs/design/v4.5.0-build-plan.md` (the rollout rule, the design-system mapping, the journeys as tests). Umbrella: #366. Goes after lane 3. ## Scope **Surfaces:** SESS-1 to SESS-7: a session before, during and after; voting and Are you in?; the transcript and summary review; the checklist; the recording facts. **The drawing:** `/feedback/prototype/lane3` — the page the build must match, still by still. **Decisions:** the brief §7, and each surface's header in `redesign-surfaces/sources/headers.md`. ## Server changes it needs (in this lane or an earlier one) - persisted corrections (D31); summary versions (`IS-379`); soft answers on pencilled-in sessions (D50); one notes model (D30) - the Talk tab reads lane 7's Discussions when that lane lands; until then the session keeps its own notes ## Acceptance - [ ] Every placement `coverage/lane3.py` classifies as **drawn** or **replaced** is built as the prototype draws it; the script is the checklist. Rows it marks **behaviour** are tested, not drawn. - [ ] J2 (votes, RSVP, the confirmed session) and J5 (the checklist, transcript and summary review, approval) pass on desktop, on a phone and keyboard-only in the journeys suite (#726), with the axe scan clean (WCAG 2.2 AA, #367). - [ ] The brief's vocabulary (§5) throughout, from `vocabulary.js`; no page-level error destroys rendered content; every async operation shows progress and a next action on failure; disabled controls say why. - [ ] Main stays deployable after every PR of the lane: the new shell carries the old pages until each is replaced (the rollout rule). - [ ] The surfaces' headers in `redesign-surfaces/sources/headers.md` (Decided lines and the owner's review notes) are honoured; a departure is a decision change on #659 first. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Collaborator

Two things lane 3 is leaving on your doorstep. Neither blocks anything; both are here so this lane starts from a list rather than a surprise.

1. viewerSecondaryTime is IS-003, and D17 retires it

webapp/frontend/src/utils/datetime.js has viewerSecondaryTime — the campaign-zone-plus-viewer-zone pair shown side by side. That is exactly what D17 supersedes:

Session times were to be shown in the campaign's zone and the reader's, side by side (IS-003). Two times make the reader work out which one is theirs.
→ One time, always the reader's own, always carrying its timezone abbreviation … When the reader's day differs from the table's, a line reconciles the two.

Its only two callers are SessionDetail.jsx and components/VotingGrid.jsx — both of which this lane replaces (D17's surfaces are CAMP-1 and SESS-1, SESS-2, SESS-3).

Lane 3 deliberately did not touch them: converting a page this lane owns, to a rule this lane has to apply anyway when it rebuilds it, would have been churn — and the right end state is that viewerSecondaryTime is deleted, not converted.

What to use instead: webapp/frontend/src/utils/when.js, which lane 3 consolidated and #767 corrected. whenText(iso) renders the reader's own zone with its abbreviation and deliberately takes no zone parameter — it had one, every caller passed the campaign's, and a parameter that can reinstate a retired decision is a parameter that will. tableDay(iso, tz) and zoneShift(iso, tz) give you D17's reconciliation sentence, and the offset is computed from the two wall-clock readings of that one instant, so summer time on one side only is handled.

Two things that will cost you a cycle otherwise, both learned the hard way in #767:

  • A zone's abbreviation is locale data, not a constant. Europe/London renders as "GMT+1" under en-US CLDR — which is what the CI image runs — and as "BST" under en-GB. Assert that a zone marker is present and is not the table's, not either literal.
  • TZ=… does not reach a headless browser on Windows; it reads the OS zone. Move the table's zone in your fixture instead of the reader's.

2. #760, session numbering, is most naturally yours

D86 (owner, 2026-09-22): a session gets a number, given at creation, frozen, never reused, so a cancelled session leaves a gap. The issue is #760, with the column, the backfill rule and the concurrency point written up.

Lanes 1, 2 and 3 have all shipped the session's title wherever the drawing says "Session 14", because no ordinal exists. That looks right and is not wrong — but this lane owns SESS-1 to SESS-7 and rewrites every surface that names a session, so doing the column as this lane's server PR means building against a number rather than retrofitting one afterwards.

Not a requirement, just the cheapest moment.

🤖 Generated with Claude Code

**Two things lane 3 is leaving on your doorstep.** Neither blocks anything; both are here so this lane starts from a list rather than a surprise. ## 1. `viewerSecondaryTime` **is** `IS-003`, and D17 retires it `webapp/frontend/src/utils/datetime.js` has `viewerSecondaryTime` — the campaign-zone-plus-viewer-zone pair shown side by side. That is exactly what **D17 supersedes**: > Session times were to be shown in the campaign's zone and the reader's, side by side (`IS-003`). Two times make the reader work out which one is theirs. > → **One time, always the reader's own**, always carrying its timezone abbreviation … When the reader's *day* differs from the table's, a line reconciles the two. Its only two callers are **`SessionDetail.jsx`** and **`components/VotingGrid.jsx`** — both of which this lane replaces (D17's surfaces are CAMP-1 and **SESS-1, SESS-2, SESS-3**). Lane 3 deliberately did not touch them: converting a page this lane owns, to a rule this lane has to apply anyway when it rebuilds it, would have been churn — and the right end state is that `viewerSecondaryTime` is **deleted**, not converted. **What to use instead:** `webapp/frontend/src/utils/when.js`, which lane 3 consolidated and #767 corrected. `whenText(iso)` renders the reader's own zone with its abbreviation and **deliberately takes no zone parameter** — it had one, every caller passed the campaign's, and a parameter that can reinstate a retired decision is a parameter that will. `tableDay(iso, tz)` and `zoneShift(iso, tz)` give you D17's reconciliation sentence, and the offset is computed from the two wall-clock readings of that one instant, so summer time on one side only is handled. Two things that will cost you a cycle otherwise, both learned the hard way in #767: - **A zone's abbreviation is locale data, not a constant.** `Europe/London` renders as **"GMT+1"** under en-US CLDR — which is what the CI image runs — and as "BST" under en-GB. Assert that *a* zone marker is present and is **not** the table's, not either literal. - **`TZ=…` does not reach a headless browser on Windows**; it reads the OS zone. Move the *table's* zone in your fixture instead of the reader's. ## 2. `#760`, session numbering, is most naturally yours **D86** (owner, 2026-09-22): a session gets a number, **given at creation, frozen, never reused**, so a cancelled session leaves a gap. The issue is **#760**, with the column, the backfill rule and the concurrency point written up. Lanes 1, 2 and 3 have all shipped the session's **title** wherever the drawing says "Session 14", because no ordinal exists. That looks right and is not wrong — but this lane owns `SESS-1` to `SESS-7` and rewrites every surface that names a session, so doing the column as **this lane's server PR** means building against a number rather than retrofitting one afterwards. Not a requirement, just the cheapest moment. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Collaborator

A third inheritance from lane 3: /sessions/:id fails the axe gate on 13 nodes

J6 (#770) walked as far as the session page and CI failed on it. The diagnosis is filed as #775; this is the part that is specifically lane 4's.

SessionDetail.jsx and its children (SessionRsvp.jsx, PostSessionChecklist.jsx) fail WCAG 2.2 AA 1.4.3 on 13 nodes, deterministically — reproduced three times with identical selectors and ratios. Two distinct bugs:

  1. Status chips with no light-mode counterpart. bg-amber-900/50 text-amber-300 and bg-green-900/50 text-green-300, bare and unconditional, so a translucent dark chip composites over light paper: 1.86:1 and 1.89:1.
  2. A light-mode text colour too weak for its background — gray-500 on gray-200, 3.9:1. This one is not a missing variant; it is simply the wrong grey.

Ratios run from 1.21:1 to 3.9:1 against a 4.5:1 minimum. The full table is on #775.

J6 no longer walks this page, deliberately: /sessions/:id was never in J6's scope (its recap act is This week's SE-006), the markup is this lane's to delete, and lane 3's acceptance gate should not be blocked on lane 4's debt. a11y-baseline.js is untouched and still {} — and per #771 it could not have expressed a single-page tolerance anyway.

What this lane inherits: the rebuild has to clear all thirteen, and J5 walks this page by name ("after the session: the checklist, transcript and summary review"). Whoever writes J5 will meet them on the first run, so it is worth clearing them before that rather than discovering them as a blocker.

One thing worth stealing from how this was found

J6's scan of this page passed twelve times locally and failed on the runner. It was not flaky markup — the page fails identically every time. The scan was racing the page. Every failing element belonged to a second wave of requests (the roster's chips, the checklist), and the assertions before axe() were satisfied by the first wave alone, so the chips landed inside axe's own injection window and whether the scan saw them came down to machine speed.

The rule is now in the harness README: wait for the slowest thing on the page before you scan it, and pick that thing deliberately — the last request the page makes, not the first. A scan whose target is a race certifies nothing, and it fails in the place least likely to be believed. Worth reading before writing J4 or J5.

🤖 Generated with Claude Code

## A third inheritance from lane 3: `/sessions/:id` fails the axe gate on 13 nodes J6 (#770) walked as far as the session page and CI failed on it. The diagnosis is filed as **#775**; this is the part that is specifically lane 4's. `SessionDetail.jsx` and its children (`SessionRsvp.jsx`, `PostSessionChecklist.jsx`) fail **WCAG 2.2 AA 1.4.3** on **13 nodes**, deterministically — reproduced three times with identical selectors and ratios. Two distinct bugs: 1. **Status chips with no light-mode counterpart.** `bg-amber-900/50 text-amber-300` and `bg-green-900/50 text-green-300`, bare and unconditional, so a translucent dark chip composites over light paper: 1.86:1 and 1.89:1. 2. **A light-mode text colour too weak for its background** — gray-500 on gray-200, 3.9:1. This one is not a missing variant; it is simply the wrong grey. Ratios run from **1.21:1** to 3.9:1 against a 4.5:1 minimum. The full table is on #775. **J6 no longer walks this page**, deliberately: `/sessions/:id` was never in J6's scope (its recap act is This week's `SE-006`), the markup is this lane's to delete, and lane 3's acceptance gate should not be blocked on lane 4's debt. `a11y-baseline.js` is untouched and still `{}` — and per #771 it could not have expressed a single-page tolerance anyway. **What this lane inherits:** the rebuild has to clear all thirteen, and **J5 walks this page by name** ("after the session: the checklist, transcript and summary review"). Whoever writes J5 will meet them on the first run, so it is worth clearing them before that rather than discovering them as a blocker. ### One thing worth stealing from how this was found J6's scan of this page passed twelve times locally and failed on the runner. It was not flaky markup — the page fails identically every time. **The scan was racing the page.** Every failing element belonged to a *second* wave of requests (the roster's chips, the checklist), and the assertions before `axe()` were satisfied by the first wave alone, so the chips landed inside axe's own injection window and whether the scan saw them came down to machine speed. The rule is now in the harness README: **wait for the slowest thing on the page before you scan it, and pick that thing deliberately — the last request the page makes, not the first.** A scan whose target is a race certifies nothing, and it fails in the place least likely to be believed. Worth reading before writing J4 or J5. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Collaborator

Picking lane 4 up now that lane 3 and its follow-ups have merged (#799–#802; #803, the test-race audit, is in CI).

Update to the inheritances above: #775's thirteen session-page nodes were fixed by #793 and #801, which cleared every dark-only colour app-wide. The rebuild still has to keep them clear.

First step: a survey covering the coverage checklist (coverage/lane3.py), the prototype's stills, the SESS headers and D-numbers, and the server state for D30, D31, D50, IS-379 and #760. Per-PR contracts follow from it, in the order lane 3 used: server PRs first, then the pages, then J2 and J5.

🤖 Generated with Claude Code

Picking lane 4 up now that lane 3 and its follow-ups have merged (#799–#802; #803, the test-race audit, is in CI). Update to the inheritances above: #775's thirteen session-page nodes were fixed by #793 and #801, which cleared every dark-only colour app-wide. The rebuild still has to keep them clear. First step: a survey covering the coverage checklist (`coverage/lane3.py`), the prototype's stills, the SESS headers and D-numbers, and the server state for D30, D31, D50, IS-379 and #760. Per-PR contracts follow from it, in the order lane 3 used: server PRs first, then the pages, then J2 and J5. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Collaborator

Owner's answers to the lane 4 survey questions (2026-09-24)

# Question Answer
L1 Two approve buttons drawn, one approval on the server A single approval. One button, placed where the state calls for it; its confirmation names every consequence.
L2 Do players see a summary before approval? Keep it visible in Quest Board, but gate the Discord post on approval. Players can read it in the app as soon as it's done; nothing reaches Discord, and so a broader audience, until the GM approves. Details being confirmed: see below.
L3 Crumb: number or title Follow D86. The crumb and the phone title are "Session 13"; the heading is the title.
L4 Who can read a session note GMs can always view. Anyone can edit, and it needs history. The owner suggested the table note might become a wiki page; being discussed.
L5 The automatic journal-entry note Stop generating it. Existing ones become table notes.
L6 Moving a confirmed session Re-ask everyone. Also: a recurring series shouldn't schedule the next session until the previous one has concluded, and after a reschedule it should be easy to get back into cadence. The owner's own game: a rescheduled session now falls after the series' next one. Design being confirmed.
L7 Pencilled in → "Let the table vote instead" Yes, and it clears the soft answers.
L8 Cancel reason Yes: stored, and sent in the email and the Discord notice (the Discord rendering is lane 10's).
L9 In-story dates Not free text. Based on the game's calendar: pick the day the session started (usually the day the previous one ended, or the next), say how many hours or days it covered, and have Quest Board work out the range. The calendar doesn't exist yet (#126, v4.6.0). Placement being confirmed.
L10 Naming a voice Defer until #542.
— Small departures (Q-11 corrections "Made this morning", Q-12 citations as transcript times) Agreed.

Also settled by D94 (#797): old summary versions stay as written after an erase, like the current summary.

🤖 Generated with Claude Code

## Owner's answers to the lane 4 survey questions (2026-09-24) | # | Question | Answer | |---|---|---| | L1 | Two approve buttons drawn, one approval on the server | **A single approval.** One button, placed where the state calls for it; its confirmation names every consequence. | | L2 | Do players see a summary before approval? | **Keep it visible in Quest Board, but gate the Discord post on approval.** Players can read it in the app as soon as it's done; nothing reaches Discord, and so a broader audience, until the GM approves. *Details being confirmed: see below.* | | L3 | Crumb: number or title | **Follow D86.** The crumb and the phone title are "Session 13"; the heading is the title. | | L4 | Who can read a session note | **GMs can always view. Anyone can edit, and it needs history.** The owner suggested the table note might become a wiki page; *being discussed.* | | L5 | The automatic journal-entry note | **Stop generating it.** Existing ones become table notes. | | L6 | Moving a confirmed session | **Re-ask everyone.** Also: **a recurring series shouldn't schedule the next session until the previous one has concluded**, and after a reschedule it should be easy to get back into cadence. The owner's own game: a rescheduled session now falls after the series' next one. *Design being confirmed.* | | L7 | Pencilled in → "Let the table vote instead" | **Yes**, and it clears the soft answers. | | L8 | Cancel reason | **Yes**: stored, and sent in the email and the Discord notice (the Discord rendering is lane 10's). | | L9 | In-story dates | **Not free text.** Based on the game's calendar: pick the day the session started (usually the day the previous one ended, or the next), say how many hours or days it covered, and have Quest Board work out the range. The calendar doesn't exist yet (#126, v4.6.0). *Placement being confirmed.* | | L10 | Naming a voice | **Defer** until #542. | | — | Small departures (Q-11 corrections "Made this morning", Q-12 citations as transcript times) | **Agreed.** | Also settled by D94 (#797): old summary versions stay as written after an erase, like the current summary. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Collaborator

The four follow-ups, settled (owner, 2026-09-24)

  • L2, the Discord post waits for approval.

    • No draft summary goes to the campaign's Discord channel when processing finishes. The summary goes out only when the GM approves.
    • Players can still read it in Quest Board straight away.
    • The 👍 / ⚠️ / ❌ accuracy check moves into Quest Board: under the summary, players answer "Does this look right?", and the GM sees the tally on Review.
    • Processing-failure notices go to the GM only, not the channel.
    • This changes when session_summarised fires. Check whether the bot's approved-repost path assumes a draft message exists. If the bot must change to cope, it's a contract change (bump both sides).
  • L4, notes.

    • Nothing is ever hidden from the GMs. A player chooses Just me and the GM or The table; a GM chooses GMs only or The table.
    • Anyone at the table can edit the table note.
    • It keeps a history of every save (who, when), and any version can be put back. It uses the same version store as the summaries (IS-379).
    • Make this a wiki page comes later, in the wiki lane (#734), to promote a note worth keeping.
  • L6, series: #804. Moving a series session moves the later ones where needed, keeping the pattern: each lands on a regular day at least half an interval after the one before. Built in the scheduling server PR.

  • L9, in-story dates, deferred to #126 (the game calendar, v4.6.0), with the owner's requirements added there:

    • a start day plus hours or days covered, with the range computed;
    • calendars for multiple game systems;
    • custom calendars, with year and month structure and multiple moons.

    Lane 4 builds no in-story dates. lane3.py's IS-092 rows move to deferred (#126), and IS-170 (naming a voice) goes to deferred on #542.

These become decision rows D95 onwards when lane 4's first PR lands.

🤖 Generated with Claude Code

## The four follow-ups, settled (owner, 2026-09-24) - **L2, the Discord post waits for approval.** - No draft summary goes to the campaign's Discord channel when processing finishes. The summary goes out only when the GM approves. - Players can still read it in Quest Board straight away. - The 👍 / ⚠️ / ❌ accuracy check moves **into Quest Board**: under the summary, players answer "Does this look right?", and the GM sees the tally on Review. - Processing-failure notices go to the GM only, not the channel. - This changes when `session_summarised` fires. Check whether the bot's approved-repost path assumes a draft message exists. If the bot must change to cope, it's a contract change (bump both sides). - **L4, notes.** - Nothing is ever hidden from the GMs. A player chooses **Just me and the GM** or **The table**; a GM chooses **GMs only** or **The table**. - Anyone at the table can edit the table note. - It keeps **a history of every save** (who, when), and any version can be put back. It uses the same version store as the summaries (IS-379). - **Make this a wiki page** comes later, in the wiki lane (#734), to promote a note worth keeping. - **L6, series: #804.** Moving a series session moves the later ones where needed, keeping the pattern: each lands on a regular day at least half an interval after the one before. Built in the scheduling server PR. - **L9, in-story dates, deferred to #126** (the game calendar, v4.6.0), with the owner's requirements added there: - a start day plus hours or days covered, with the range computed; - calendars for multiple game systems; - custom calendars, with year and month structure and multiple moons. Lane 4 builds no in-story dates. `lane3.py`'s IS-092 rows move to **deferred** (#126), and IS-170 (naming a voice) goes to deferred on #542. These become decision rows D95 onwards when lane 4's first PR lands. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Collaborator

Lane 4 progress: three server PRs merged

  • #807, session numbers (#760, D86). "Session 14" in the breadcrumb, bell, live bar, Home, This week and Sessions list. Migration f3a4b5c6d7e8.

  • #808, corrections that wait (D31) and every version of a summary (IS-379/D51). GM endpoints for pending corrections and for summary versions with put-back. The version store is generic, for the table note's history (D97). Migration c5d6e7f8a9b1.

  • #809, scheduling (D99, D100, D101, #804):

    • a move re-asks everyone, and the series moves with it, with a preview;
    • pencilled in → a vote, and a vote restart;
    • the cancel reason, with who and when;
    • recorded_seconds.

    Migration d7e8f9a0b1c3.

Still to come

  • Discord on approval (D96). Built. It is waiting on one owner answer: do summaries already posted to Discord before the upgrade count as cleared? The bot contract goes to v2.
  • The notes model (D97/D98). Waiting on one owner answer about existing private notes.
  • The pages (SESS-1, 2, 5 and 7; then Read, SESS-3; then Review, SESS-4), then J2 and J5.

🤖 Generated with Claude Code

## Lane 4 progress: three server PRs merged - **#807, session numbers (#760, D86).** "Session 14" in the breadcrumb, bell, live bar, Home, This week and Sessions list. Migration `f3a4b5c6d7e8`. - **#808, corrections that wait (D31) and every version of a summary (IS-379/D51).** GM endpoints for pending corrections and for summary versions with put-back. The version store is generic, for the table note's history (D97). Migration `c5d6e7f8a9b1`. - **#809, scheduling (D99, D100, D101, #804):** - a move re-asks everyone, and the series moves with it, with a preview; - pencilled in → a vote, and a vote restart; - the cancel reason, with who and when; - `recorded_seconds`. Migration `d7e8f9a0b1c3`. ## Still to come - **Discord on approval (D96).** Built. It is waiting on one owner answer: do summaries already posted to Discord before the upgrade count as cleared? The bot contract goes to v2. - **The notes model (D97/D98).** Waiting on one owner answer about existing private notes. - **The pages** (SESS-1, 2, 5 and 7; then Read, SESS-3; then Review, SESS-4), then J2 and J5. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Collaborator

Two more owner answers (2026-09-24)

  • Summaries already posted to Discord before D96 count as cleared (D105). They were public already, so gating them protects nothing. Only a summary that has never been posted waits for the GM's approval before it reaches /recap, /ask, the reminders' "Previously on" and the session-completed post. Going into the D96 PR.
  • Existing private notes: D97 applies to them too (D106). From the upgrade, every GM can read notes that were private. A player's private note becomes their "Just me and the GM" note; a GM's private note joins "GMs only". The CHANGELOG will say so plainly for self-hosters. Building in the notes PR now.
  • The owner also approved updating CLAUDE.md's recording-pipeline description (and bot/CLAUDE.md's) for D96. That goes in the D96 PR.

🤖 Generated with Claude Code

## Two more owner answers (2026-09-24) - **Summaries already posted to Discord before D96 count as cleared (D105).** They were public already, so gating them protects nothing. Only a summary that has never been posted waits for the GM's approval before it reaches `/recap`, `/ask`, the reminders' "Previously on" and the session-completed post. Going into the D96 PR. - **Existing private notes: D97 applies to them too (D106).** From the upgrade, every GM can read notes that were private. A player's private note becomes their "Just me and the GM" note; a GM's private note joins "GMs only". The CHANGELOG will say so plainly for self-hosters. Building in the notes PR now. - The owner also approved updating `CLAUDE.md`'s recording-pipeline description (and `bot/CLAUDE.md`'s) for D96. That goes in the D96 PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Collaborator

The first page PR (5a) is built: the session page's frame, and SESS-1

  • The new pages/session/SessionPage.jsx renders a vote (the desktop grid, and D29's phone list) and pencilled in. Every other status still goes to the old SessionDetail.
  • The shell knows the session's place: the crumb is Campaign › Sessions › Session 14, and the Contents lists the session's sections under Sessions.
  • NotesPanel is built against #811's API.
  • Its PR opens once #811 (the notes model) merges.

Departures, accepted:

  • Adding or moving a time takes it in the campaign's zone ("Time · CDT"). That's what the drawing's captions and lane 3's session form do. The 5a contract had said the reader's zone, and that was the error.
  • The notes switch uses lane 0's AudiencePicker words: "Just you and the GM", "Everyone at the table", "GMs only". These are the app-wide audience model, the same meaning as D97's "Just me and the GM" and "The table", so it reads the same everywhere notes or posts have an audience.
  • The GM has no row in the vote grid and isn't counted, consistent with D50's "the GM is not asked".
  • "Start the vote again" is one modal, not two steps.

🤖 Generated with Claude Code

## The first page PR (5a) is built: the session page's frame, and SESS-1 - The new `pages/session/SessionPage.jsx` renders a vote (the desktop grid, and D29's phone list) and pencilled in. Every other status still goes to the old `SessionDetail`. - The shell knows the session's place: the crumb is `Campaign › Sessions › Session 14`, and the Contents lists the session's sections under Sessions. - **NotesPanel** is built against #811's API. - Its PR opens once #811 (the notes model) merges. **Departures, accepted:** - **Adding or moving a time takes it in the campaign's zone** ("Time · CDT"). That's what the drawing's captions and lane 3's session form do. The 5a contract had said the reader's zone, and that was the error. - **The notes switch uses lane 0's `AudiencePicker` words:** "Just you and the GM", "Everyone at the table", "GMs only". These are the app-wide audience model, the same meaning as D97's "Just me and the GM" and "The table", so it reads the same everywhere notes or posts have an audience. - **The GM has no row in the vote grid and isn't counted,** consistent with D50's "the GM is not asked". - **"Start the vote again" is one modal, not two steps.** 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Collaborator

Lane 4 progress, 2026-09-24

Merged since the last update:

  • #811, one notes model (D97/D98/D106).
  • #812, the session page frame and SESS-1: a vote and pencilled in.
  • #813, SESS-2 and SESS-5: confirmed, being played, cancelled. It deletes VotingGrid, SessionRsvp and viewerSecondaryTime (IS-003, this issue's first inheritance).
  • #815, J2's lane 4 half (a vote, pencilled in, and no Discord) and J5's fixture: Session 4, recorded through the real pipeline, with scripted beats and no audio committed.
    • J2's no-Discord leg found #814, and #815 fixes it: after a worker process's first email, every later email was silently skipped.
  • #816, SESS-3: the Read tab, with /sessions/:id/review for the GM (the legacy page under the new head for now), audio_deleted_at, summary_limits, and D28's stripping.

Building: 7a, the GM's Review part one: still to do, the recording, the summary with versions and saved corrections, and the single approval (D95).

Then:

  • 7b: the events and their corrections, who was there, and highlights. It deletes SessionDetail.
  • 8b: J5's spec, and J6 through the door.

🤖 Generated with Claude Code

## Lane 4 progress, 2026-09-24 **Merged since the last update:** - **#811**, one notes model (D97/D98/D106). - **#812**, the session page frame and SESS-1: a vote and pencilled in. - **#813**, SESS-2 and SESS-5: confirmed, being played, cancelled. It deletes `VotingGrid`, `SessionRsvp` and `viewerSecondaryTime` (IS-003, this issue's first inheritance). - **#815**, J2's lane 4 half (a vote, pencilled in, and no Discord) and J5's fixture: Session 4, recorded through the real pipeline, with scripted beats and no audio committed. - J2's no-Discord leg found **#814**, and #815 fixes it: after a worker process's first email, every later email was silently skipped. - **#816**, SESS-3: the Read tab, with `/sessions/:id/review` for the GM (the legacy page under the new head for now), `audio_deleted_at`, `summary_limits`, and D28's stripping. **Building:** 7a, the GM's Review part one: still to do, the recording, the summary with versions and saved corrections, and the single approval (D95). **Then:** - **7b:** the events and their corrections, who was there, and highlights. It deletes `SessionDetail`. - **8b:** J5's spec, and J6 through the door. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Collaborator

Lane 4 is complete (2026-09-25). Left open for the owner's review.

Every session state now renders the new design, and SessionDetail is deleted. J2 and J5 pass on desktop, phone and keyboard-only, with axe clean. coverage/lane3.py is fully classified: every SESS row is built, replaced, behaviour, elsewhere or deferred.

What merged

Server

PR What it did
#807 Session numbers (#760, D86)
#808 Saved corrections (D31) and summary versions with put-back (IS-379/D51)
#809 Scheduling: a move re-asks everyone and carries the series with it (D99, #804); pencilled in → a vote (D100); the cancel reason (D101); recorded_seconds
#810 A summary reaches Discord only on approval (D96, D105). Bot contract v2.
#811 One notes model: GMs always see notes, and the table note keeps its history (D97, D98, D106)

Pages

PR What it did
#812 The frame; SESS-1 (a vote, pencilled in); the shell's place for a session
#813 SESS-2 (confirmed, being played) and SESS-5 (cancelled). Deleted VotingGrid, SessionRsvp and viewerSecondaryTime (IS-003).
#816 SESS-3, the Read tab, with "Does this look right?" for players
#817 SESS-4, the GM's Review, part one: the checklist, the recording, and the summary with versions, rewrite and one approval (D95)
#818 SESS-4, part two: the events and their corrections, who was there with pins, and highlights. The old page is deleted after a full audit; the erasure notes got a home.

Journeys

PR What it did
#815 J2's lane 4 half (a vote, pencilled in, no Discord), and J5's recorded-session fixture (no audio committed)
#820 J5 end to end (review, correct, rewrite, put back, approve, withdraw, re-approve, the wiki door), and J6 through to the Read tab

Decisions: #805 (D94) and #806 (D95–D104). D105 and D106 are in #810 and #811.

Bugs the lane found and fixed

  • #814: after each worker's first email, every later email was silently skipped. The guard test now covers all of app/.
  • #819: Home's Needs you never showed a transcript to review under the default "bin once transcribed".
  • A merge interaction that lazy-loaded the new notes column outside the greenlet (fixed in #811).
  • ui/Segmented couldn't be reached by keyboard with nothing chosen (fixed in #818).

Departures (each recorded in its PR)

  • Times are entered in the campaign's zone.
  • The notes switch uses the design system's audience words.
  • The GM isn't a row in the vote grid.
  • "Started at" comes from the confirmed time, because there is no start stamp.
  • "Four at the table" is lane 5's.
  • Reveals show their wall time.
  • Citations are transcript times (D104).
  • A row can carry several corrections.
  • The erasure notes appear on Read and Review, which the drawing doesn't show.

Deferred, and where it went

  • In-story dates → the game calendar (#126, v4.6.0, D102).
  • Naming a voice → #542 (D103).
  • The Talk tab → lane 7 (#733). The tab strip takes a list.
  • WikiProposals contrast, and the 60 s lore wait → lane 8 (#734).
  • The recording panel's redraw → lane 5.
  • A failed run's retention clock → #798.

Worth knowing before a deploy

  • Bot contract v2 (#810): upgrade the backend and the bot together.
  • Migrations since v4.4.1, in order:
    1. f3a4b5c6d7e8
    2. c5d6e7f8a9b1
    3. d7e8f9a0b1c3
    4. d6e7f8a9b1c2
    5. e9a0b1c2d3f4, the notes data migration: GMs can then read notes that were private (D106).
    6. a9b0c1d2e3f5

🤖 Generated with Claude Code

## Lane 4 is complete (2026-09-25). Left open for the owner's review. Every session state now renders the new design, and `SessionDetail` is deleted. J2 and J5 pass on desktop, phone and keyboard-only, with axe clean. `coverage/lane3.py` is fully classified: every SESS row is built, replaced, behaviour, elsewhere or deferred. ### What merged **Server** | PR | What it did | |---|---| | #807 | Session numbers (#760, D86) | | #808 | Saved corrections (D31) and summary versions with put-back (IS-379/D51) | | #809 | Scheduling: a move re-asks everyone and carries the series with it (D99, #804); pencilled in → a vote (D100); the cancel reason (D101); `recorded_seconds` | | #810 | A summary reaches Discord only on approval (D96, D105). Bot contract v2. | | #811 | One notes model: GMs always see notes, and the table note keeps its history (D97, D98, D106) | **Pages** | PR | What it did | |---|---| | #812 | The frame; SESS-1 (a vote, pencilled in); the shell's place for a session | | #813 | SESS-2 (confirmed, being played) and SESS-5 (cancelled). Deleted `VotingGrid`, `SessionRsvp` and `viewerSecondaryTime` (IS-003). | | #816 | SESS-3, the Read tab, with "Does this look right?" for players | | #817 | SESS-4, the GM's Review, part one: the checklist, the recording, and the summary with versions, rewrite and one approval (D95) | | #818 | SESS-4, part two: the events and their corrections, who was there with pins, and highlights. The old page is deleted after a full audit; the erasure notes got a home. | **Journeys** | PR | What it did | |---|---| | #815 | J2's lane 4 half (a vote, pencilled in, no Discord), and J5's recorded-session fixture (no audio committed) | | #820 | J5 end to end (review, correct, rewrite, put back, approve, withdraw, re-approve, the wiki door), and J6 through to the Read tab | **Decisions:** #805 (D94) and #806 (D95–D104). D105 and D106 are in #810 and #811. ### Bugs the lane found and fixed - **#814:** after each worker's first email, every later email was silently skipped. The guard test now covers all of `app/`. - **#819:** Home's Needs you never showed a transcript to review under the default "bin once transcribed". - A merge interaction that lazy-loaded the new notes column outside the greenlet (fixed in #811). - `ui/Segmented` couldn't be reached by keyboard with nothing chosen (fixed in #818). ### Departures (each recorded in its PR) - Times are entered in the campaign's zone. - The notes switch uses the design system's audience words. - The GM isn't a row in the vote grid. - "Started at" comes from the confirmed time, because there is no start stamp. - "Four at the table" is lane 5's. - Reveals show their wall time. - Citations are transcript times (D104). - A row can carry several corrections. - The erasure notes appear on Read and Review, which the drawing doesn't show. ### Deferred, and where it went - In-story dates → the game calendar (#126, v4.6.0, D102). - Naming a voice → #542 (D103). - The Talk tab → lane 7 (#733). The tab strip takes a list. - `WikiProposals` contrast, and the 60 s lore wait → lane 8 (#734). - The recording panel's redraw → lane 5. - A failed run's retention clock → #798. ### Worth knowing before a deploy - **Bot contract v2 (#810): upgrade the backend and the bot together.** - **Migrations since v4.4.1, in order:** 1. `f3a4b5c6d7e8` 2. `c5d6e7f8a9b1` 3. `d7e8f9a0b1c3` 4. `d6e7f8a9b1c2` 5. `e9a0b1c2d3f4`, the notes data migration: GMs can then read notes that were private (D106). 6. `a9b0c1d2e3f5` 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Collaborator

The owner's review of lane 4 on dev, 2026-09-25. Looks right: numbers, the vote, confirmed, Manage, cancelled, notes, all three parts of Review, and approving.

Skipped:

  • pencilled in, Move, and being played: #821 seeds these states;
  • the Discord post on approval: dev has no Discord.

Fix in batch R1 (fix/review-r1):

  • R1-6: the Read tab shows who was there and also says "nobody has said who was there yet".
**The owner's review of lane 4 on dev, 2026-09-25.** Looks right: numbers, the vote, confirmed, Manage, cancelled, notes, all three parts of Review, and approving. **Skipped:** - pencilled in, Move, and being played: #821 seeds these states; - the Discord post on approval: dev has no Discord. **Fix in batch R1 (`fix/review-r1`):** - **R1-6:** the Read tab shows who was there and also says "nobody has said who was there yet".
Author
Collaborator

Signed off by the owner on the Redesign Review page, 2026-10-01. Both re-checks for this lane (reading a session; its states) are marked OK. No changes asked for. Closing.

**Signed off by the owner on the Redesign Review page, 2026-10-01.** Both re-checks for this lane (reading a session; its states) are marked OK. No changes asked for. Closing.
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Quest-Board#730
No description provided.