fix(journal): the campaign journal 500'd for co-GM campaigns (#411) #460
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/411-co-gm-journal"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #411 (LOW).
The defect
get_campaign_notescalledscalar_one_or_none()on a query that returns one row per GM. A campaign with two GMs therefore raisedMultipleResultsFound, which becomes a 500 — for every member of that campaign, not just the GMs. Co-GMs are supported everywhere else in the app; this one query assumed exactly one.What a user reports is "my notes are gone". It is a read-path crash wearing a data-loss costume, which is why it belongs in this milestone despite being LOW.
What ships
Fetches every GM's public notes rather than "the" GM's, and attributes each to its author. Two unattributed notes stacked under a single "GM Notes" heading read as one person contradicting themselves — so the fix that merely stops the crash produces a subtly wrong page.
That changes the response shape:
gm_public_note: str | Nonebecomesgm_public_notes: list[{author, content}]. Both render sites (CampaignNotes.jsx,CampaignDetail.jsx) are updated in the same commit, and the author line only appears when there is more than one note, so single-GM campaigns look exactly as they did.The reader's own notes stay excluded — they already appear under
my_notes, and repeating them would read as the journal duplicating itself. That was the purpose of the originalgm_id != user_idcheck, preserved here as a set difference.Verification
Mutation-checked: restoring
scalar_one_or_none()fails 4 of 6 tests with the issue's ownMultipleResultsFound.The suite is deliberately not crash-only. A fix that returned one GM's note and dropped the other would satisfy "does not raise" while silently hiding a co-GM's writing, so there is a test asserting both notes arrive, one asserting attribution, and one covering the single-GM path so the common case cannot regress on the way past.
1,482 backend tests pass (was 1,476), 457 frontend. Lint clean at pinned ruff 0.4.4; eslint clean apart from one pre-existing warning in a file this PR does not otherwise touch.
🤖 Generated with Claude Code
get_campaign_notes called scalar_one_or_none() on a query returning one row per GM, so a campaign with two GMs raised MultipleResultsFound — a 500 for every member of that campaign, not just the GMs. The symptom a user reports is "my notes are gone", which is a read-path crash wearing a data-loss costume. Fetches every GM's public notes rather than "the" GM's, and attributes each to its author: two unattributed notes stacked under one "GM Notes" heading read as one person contradicting themselves. That changes CampaignNoteEntry.gm_public_note (str | None) to gm_public_notes (list[{author, content}]); both render sites are updated in the same commit, and the author line only shows when there is more than one. The reader's own notes stay excluded — they already appear under my_notes, and repeating them would read as the journal duplicating itself. That was the point of the original `gm_id != user_id` check, kept as a set difference. Mutation-checked: restoring scalar_one_or_none fails 4 of 6 tests with the issue's own MultipleResultsFound. One test covers the single-GM case, so the fix cannot regress the common path on its way past. 1,482 backend and 457 frontend tests pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>