Repository navigation
Refuse a group-by that names none of the signup's fields - #546
Conversation
createSignup and updateSignup checked the reminder anchor against the signup's fields but never groupByFieldRefs, so POST /api/signups, PATCH /api/signups/[id] and the update_signup tool saved a ref that named no field and reported success. The public page then showed the slots ungrouped, and a field added later under that ref switched grouping on unasked. Both now refuse an unknown ref with invalid_input on settings.groupByFieldRefs. An update checks only when it sends groupByFieldRefs, so a row already holding a stale ref still saves its other settings. Fixes #516. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RBmdM1zj4ipf4f4Ry9ow1P
Tophat: passedI ran c928dfa locally with
UX: this changes what API callers and AI assistants see, and nothing changes on screen. When Screenshots are in https://claude.ai/code/session_01B8y9qbnsD2jQDnHZhMMKUF. Generated by Claude Code |
richshaw
left a comment
There was a problem hiding this comment.
Review: passed (round 1)
This PR adds checkGroupByRefs to src/services/signups.ts. createSignup uses it to check the template's fields, and updateSignup uses it to check the signup's stored fields, but only when the caller sends a non-null groupByFieldRefs. An unknown ref is refused with invalid_input on settings.groupByFieldRefs, the same shape the reminder-ref error has. It does what #516 asks.
- The check runs inside
withSignupWriteLock, againstlistFieldsForSignup(tx, …), so a field added or deleted at the same moment can't slip past it. sentGroupByreads the caller's rawsettings, not the merged ones. A stale ref already on the row therefore doesn't block an unrelated save, and a test covers that case.- The other writers already keep refs valid.
magicComposeToTemplatekeeps only declared refs, so Magic Compose andcreate_signupcan't trip the new check.deleteFieldprunes the deleted ref. Before a "Group slots by" save, the Build tab fetches any field it hasn't loaded (#398). - If another tab deleted the field just before a Build tab save, the save is now refused rather than storing a dead ref. That is the behaviour we want.
Blocking (0): none.
Optional (1):
- The issue's third outline item, refusing a group-by on number and time fields, is left out on purpose, and the description explains why. Magic Compose and
create_signupaccept those today, so this is the owner's call. If you want it, file it as a proposed issue. This PR doesn't need it.
Earlier threads: none. Copilot couldn't review because its quota is used up.
Checks:
- CI is green on c928dfa: lint-type-test, e2e and CodeQL.
- I read the diff and its callers by hand. I didn't need to run anything locally to prove a finding.
- The tophat passed on this same SHA (
<!-- pipeline-tophat sha=c928dfa… result=pass -->).
Why this waits for the owner: it's UX (pipeline:ux). API callers and AI assistants that send an unknown group-by ref now get a 400 with "groupByFieldRefs must name one of the signup's fields" where they used to get a success. Nothing changes on screen. See the tophat's 546-base-public-nope-desktop for the silently ungrouped page the old success led to.
https://claude.ai/code/session_01HejG9ZMkJpVdq1xjBbMDwb
Generated by Claude Code
|
Yep we should be able to group on number or time fields. |
|
Approved by the owner at The merge stage couldn't merge it: after #545 landed, the PR conflicts with The owner's comment (number and time fields should stay groupable) agrees with what this PR does, so nothing else changes. https://claude.ai/code/session_01DXydxGNyVt8sXcDp4XzG84 Generated by Claude Code |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013fWkt1ztQmhjajGwXkdGxu
|
Merged
https://claude.ai/code/session_013fWkt1ztQmhjajGwXkdGxu Generated by Claude Code |
Fixes #516.
The problem
createSignupandupdateSignupcheckedsettings.reminderFromFieldRefagainst the signup's fields, but never checkedsettings.groupByFieldRefs. SoPOST /api/signups,PATCH /api/signups/[id]and the connector'supdate_signupall saved a group-by ref that named no field, and reported success. The public page then showed the slots ungrouped without saying why. If a field with that ref was added later, grouping switched on even though nobody asked for it.The fix
src/services/signups.ts: a newcheckGroupByRefsrefuses any ref that isn't one of the signup's field refs. The error isinvalid_inputonsettings.groupByFieldRefs, with the bad ref asreceived, the same shape as the reminder-ref error.createSignupchecks the refs against the template's fields.updateSignupchecks only when the caller sends a non-nullgroupByFieldRefs. A row that already holds a stale ref still saves its other settings. Sendingnullclears the key as before.magicComposeToTemplate) andcreate_signupaccept a group-by on any declared field. Refusing number and time in the service would make those drafts fail outright. That is a separate decision with its own UX, so I left it out of this PR.Tests
src/services/signups.db.test.tsadds three tests:['ghost']is refused['nope']is refused, and the stored settings are left unchangedsrc/mcp/tools.db.test.ts:update_signupwith an unknown ref returnsinvalid_input, and the stored group-by stays as it was.pnpm lint && pnpm typecheck && pnpm knip && pnpm test(1295 tests) andpnpm test:db(495 tests).Generated by Claude Code