Loading…
Reference in a new issue
No description provided.
Delete branch "build/lane1-home-server"
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?
Lane 1's second server change, behind Home (#727, series item 4; HOME-2's templates and one-shot, HOME-1's star and wish). Backend,
docs/API.md,CHANGELOG.md; nothing in the frontend or the bot.Templates on
POST /api/campaigns(IS-222, IS-356):templateisweekly(a reminder the day before and two hours before; sessions default to a fixed time),biweekly_vote(a reminder the day before; sessions default to a vote, closing after two days), or{"from_campaign_id"}(the settings of one of your own campaigns, nineteen settings-shaped columns enumerated inSETTINGS_COPIED_FROM_TEMPLATE, and nothing from its story, members, sessions, wiki or Discord; 403 unless you run or own it). No template creates a series: that needs a day and a time the form does not ask for. Presets apply first and any field you send wins, the same rule as the #743 fix.The one-shot (IS-223):
one_shot: {when, when_text}marks the campaign (is_one_shot, returned in the list and the detail), sets lighter reminders and no journals, and creates one session at once through the ordinary path, tentative atwhenif it parses, else proposed with "When, roughly: …" kept in its description.SessionCreatenow lets a tentative session be created with no proposed times, which that needs; a zero-slot session was already reachable throughPATCH, and confirming one is refused as before.One star, one wish (IS-213, IS-216):
GET /api/me/star-wishreturns at most one prompt, the most recently completed session in the last 36 hours in a campaign you are in, that you have not answered or dismissed, and never for a GM about their own session.POST /api/sessions/{id}/feedbacktakes a line each and whether to sign it, once per person, inside the window;…/feedback/dismissis "Not this time" and is never an error.GET /api/sessions/{id}/feedback(GM only) returns the lines withsigned_byonly when the person signed, and carries no user id at all, so an unsigned line cannot be attributed by joining against the member list. Feedback is deleted with the account, and named in the deletion audit like every other personal table.Fixes #743:
create_campaignnow honours the eight fieldsCampaignCreatealready declared and validated (guild_id,notification_channel_id,vote_notification_mode,vote_auto_close_hours,recap_email_enabled,journal_entry_enabled,narrative_style,transcription_language) instead of silently dropping them.New column
campaigns.default_scheduling_mode(nullable, written only by the templates for now, read by nothing yet, returned in the detail). Migrationa6b7c8d9ebfcchained onf5a6b7c8d9eb, house style, verified up, down and up, and the schema diffed object for object againstcreate_all.Tests: 3,760 backend (55 new),
ruffclean.🤖 Generated with Claude Code
Lane 1's Home page, server side. Three surfaces from the drawing: the choice that opens New campaign (IS-356), the templates behind it (IS-222, IS-223), and the day-after prompt (IS-213, IS-216). **Templates.** `POST /api/campaigns` takes an optional `template` — `weekly`, `biweekly_vote`, or `{"from_campaign_id": ...}` — and an optional `one_shot`. Both absent is today's behaviour exactly. The two are mutually exclusive (422): "How do you want to start?" is a set of radio buttons, and there is no one-shot weekly campaign. A preset is applied first and anything the request names explicitly then overrides it. A template never creates the recurring series, even the weekly one. A series needs a day and a time, the form asks for neither, and inventing "Thursdays at 8" would be a guess a GM then has to find and undo. It is a Settings step. `{"from_campaign_id": ...}` is settings only, from a campaign the caller runs or owns (403 otherwise, 404 for one that is gone or in the trash). Nineteen named columns copy; nothing else does. Not the name, not the story, not the people — and pointedly not the webhook, the Discord ids, the share token or the campaign's AI keys, because a "template" that silently pointed a new campaign at another group's channel, or spent their API budget, would be a security bug wearing a convenience feature's clothes. Presets and copied values are deep-copied: two campaigns sharing one JSONB list is an in-place edit away from rewriting each other. **The one-shot** sets `campaigns.is_one_shot`, trims reminders to two hours, turns the journal off, and creates its single session there and then — at the time given, or with no time at all and what the person typed kept verbatim ("When, roughly: Sat 4 Oct, afternoon"). A free-text date is not a date, and storing a guessed timestamp would put a reminder on the calendar for a moment nobody chose. Both branches are `tentative`, so the session is `proposed` — pencilled in, not fixed. It goes through `session_service.create_session`, so it notifies the campaign like any other session; `SessionCreate` is relaxed to let `tentative` — and only `tentative` — be created with no time. **One star, one wish.** New `session_feedback` table, `GET /api/me/star-wish`, `POST /api/sessions/{id}/feedback`, `.../feedback/dismiss` and the GM's `GET /api/sessions/{id}/feedback`. Asked once, on Home, about one session completed in the last 36 hours, never to the GM who ran it, and never chased — there is no notification kind for it. "Not this time" is *stored*, because a missing row and a declined prompt would otherwise be the same nothing and the card would come back on the next page load. The GM's read carries no name unless the person signed: "without names, unless you put yours in" is kept in the service that builds the response, and the shape has no `user_id` for a client to join against. Deleting an account deletes these rows outright rather than repointing them at the tombstone. **#743, on the way.** `CampaignCreate` declared `guild_id`, `notification_channel_id`, `vote_notification_mode`, `vote_auto_close_hours`, `recap_email_enabled`, `journal_entry_enabled`, `narrative_style` and `transcription_language`, ran each one's validator — and then built the campaign from six fields and dropped the other eight without a word. They are honoured rather than removed: the validators were already there and are already the same module-level functions `PATCH` runs, so there was nothing to duplicate and nothing that can drift. Migration `a6b7c8d9ebfc`, on `f5a6b7c8d9eb`. Verified upgrade → downgrade -1 → upgrade against Postgres 16, and the DDL it produces was diffed against `create_all`'s: identical, object for object. 55 new tests; the full backend suite is 3760 passed, 13 skipped. Fixes #743. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>