Let a speaker fix the format, track and tags they picked - #323
Merged
EllAchE merged 2 commits intoAug 18, 2026
Merged
Conversation
All three were asked on the public form and none survived into the portal's edit view. Track showed up as read-only header text; format and tags did not show up at all. A speaker who chose the wrong track had no way to say so, on exactly the fields organizers filter, route and schedule on -- so the correction went by email, if it happened. The edit view now offers the event's own lists: two selects and a checkbox list, the same three questions the form asked. Values are ids rather than names, so renaming a track does not detach the submissions that chose it, and the server action checks each one against the same list rather than trusting the post. A form that does not ask one of these renders no control for it, and a save from that form now leaves the column alone rather than clearing it. Tags are replaced as a set, the way a submit replaces them; an empty set means untagged, and no `tagIds` at all means the caller was not editing tags.
A question conditioned on "Session format is Workshop" was rendered on a Talk everywhere a submission is read back: the speaker's portal editor and the reviewer's Questionnaire panel. The reviewer's copy is the damaging one, because "Workshop prerequisites —" reads as a question the speaker skipped. The visibility engine was right; what it was handed was not. `isFieldVisible` treats a condition whose parent is missing from the map as no condition — correct for a rule orphaned by a deleted question, wrong for a parent the caller filtered out first. `submissionFields` dropped the built-ins before resolving, so every rule pointing at the built-in format, track or level was unresolvable and its field showed unconditionally. `askedQuestions` resolves over the whole form, built-in columns rehydrated back into answer positions, and leaves dropping the built-ins to the caller. Three surfaces move onto it: - The portal editor renders only the questions this submission was asked. A file question is resolved but still not rendered, because it is a value another question can be conditioned on and the portal has nowhere to re-upload from. - `saveSubmissionAction` reads back only those questions. Reading every question the form holds stored an empty answer to one never shown, which afterwards is indistinguishable from a speaker who was asked and skipped. `updateMySubmission` resolves against the values being written, so a speaker switching a Talk to a Workshop is asked the workshop questions by that same save. - The reviewer's Questionnaire filters at render, so rows already carrying an empty answer from the old write path read correctly without a backfill. An answer whose question is gone from the form is kept — there is no rule left to consult, and the panel already falls back to the raw key. Switching format reveals the newly asked questions on the next load: the format control sits outside `FieldSet`, so the browser cannot recompute visibility live. Found by `sbek` CFP-S2 against run 2026-08-17T05-46-05.
EllAchE
added a commit
that referenced
this pull request
Aug 18, 2026
…322) * Give the portal back the dropdown the CFP asked Audience level with A speaker picks Beginner, Intermediate or Advanced on the public form. Opening the same submission in the portal to edit it presented Audience level as an empty text box: any string at all could be saved into the column the review queue filters on and the exports group by, and nothing in the product would ever match it again. The edit view now renders the options the form itself offers, read from the form rather than hardcoded, so an organizer who replaced the list gets their list. A form whose organizer removed the field offers nothing rather than an empty box. Server-side too. The edit view posts to a server action, and a server action takes whatever it is given, so the value is checked against the same list there. Two exceptions, both because the old box let anything through: a blank clears the field, which is what an optional question means, and whatever the record already says passes unchanged -- a speaker fixing a typo in their title should not be blocked by a value they did not touch and could not re-enter. Moving off such a value is one-way; the select offers it so it is visible, and choosing anything else is final. * Leave the level alone on a form that does not ask for one The select only renders when the form has the field, so an empty `level` on a form without it means nobody was shown the question -- not that a speaker cleared it. Writing it through dropped an existing value on the next save of anything else on the page. * Let a speaker fix the format, track and tags they picked (#323) * Let a speaker fix the format, track and tags they picked All three were asked on the public form and none survived into the portal's edit view. Track showed up as read-only header text; format and tags did not show up at all. A speaker who chose the wrong track had no way to say so, on exactly the fields organizers filter, route and schedule on -- so the correction went by email, if it happened. The edit view now offers the event's own lists: two selects and a checkbox list, the same three questions the form asked. Values are ids rather than names, so renaming a track does not detach the submissions that chose it, and the server action checks each one against the same list rather than trusting the post. A form that does not ask one of these renders no control for it, and a save from that form now leaves the column alone rather than clearing it. Tags are replaced as a set, the way a submit replaces them; an empty set means untagged, and no `tagIds` at all means the caller was not editing tags. * Show only the questions a submission was actually asked (#326) A question conditioned on "Session format is Workshop" was rendered on a Talk everywhere a submission is read back: the speaker's portal editor and the reviewer's Questionnaire panel. The reviewer's copy is the damaging one, because "Workshop prerequisites —" reads as a question the speaker skipped. The visibility engine was right; what it was handed was not. `isFieldVisible` treats a condition whose parent is missing from the map as no condition — correct for a rule orphaned by a deleted question, wrong for a parent the caller filtered out first. `submissionFields` dropped the built-ins before resolving, so every rule pointing at the built-in format, track or level was unresolvable and its field showed unconditionally. `askedQuestions` resolves over the whole form, built-in columns rehydrated back into answer positions, and leaves dropping the built-ins to the caller. Three surfaces move onto it: - The portal editor renders only the questions this submission was asked. A file question is resolved but still not rendered, because it is a value another question can be conditioned on and the portal has nowhere to re-upload from. - `saveSubmissionAction` reads back only those questions. Reading every question the form holds stored an empty answer to one never shown, which afterwards is indistinguishable from a speaker who was asked and skipped. `updateMySubmission` resolves against the values being written, so a speaker switching a Talk to a Workshop is asked the workshop questions by that same save. - The reviewer's Questionnaire filters at render, so rows already carrying an empty answer from the old write path read correctly without a backfill. An answer whose question is gone from the form is kept — there is no rule left to consult, and the panel already falls back to the raw key. Switching format reveals the newly asked questions on the next load: the format control sits outside `FieldSet`, so the browser cannot recompute visibility live. Found by `sbek` CFP-S2 against run 2026-08-17T05-46-05.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The second half of the major call-for-papers defect whose first half is #322. Stacked on that
branch — base is
portal-level-select-20260817t1815, notmain, because both touch the same threefiles. Merge #322 first and this rebases onto
maincleanly; GitHub will retarget it automatically.What was wrong
submissionFieldsfilters every built-in key out of what the portal editor renders, so the editorhardcodes the built-ins it wants — and it wanted only
title,descriptionandlevel. Format,Track and Tags were asked on the way in and then simply not shown again. Track was visible on the
detail header, as text, which reads as "shown but locked" rather than "not implemented".
So the three fields an organizer filters, routes and schedules on had no self-service correction
route. A speaker who picked the wrong track emailed someone, or did not.
The fix
submissionTaxonomy(formId, eventId)returns the event's formats, tracks and tags, eachnullwhere the form does not ask — and it only queries the lists the form does ask for. The editor
renders two selects and a checkbox list from that, which is how the public form asks the same three
questions.
Values are ids, not names, matching the public form and the columns underneath: renaming a track
must not detach the submissions that chose it.
Server-side,
choiceErrorandtagsErrorcheck each value against the same list. A<select>limits the browser, not the request, and these are foreign keys.
Three behaviours worth naming:
empty value means "nobody was shown this". Writing it through would drop a value on the next save
of something unrelated.
levelhad this same hazard from Give the portal back the dropdown the CFP asked Audience level with #322 and is fixed in the first commithere, on that branch.
speaker looking at tags they did not choose, with nothing saying anything was dropped.
tagIdsin the payload means "not editing tags"; an empty array means "untagged". Thedistinction matters because a checkbox list posts nothing when every box is clear.
An event that asks for tags but has none yet gets a sentence saying so rather than an empty box.
Verification
bun run lint,bun run typecheck,bun run test(191 files, 2054 tests) andbun run buildallpass.
bun run docs:openapiandbun run docs:mcpregenerate to no diff.Thirteen new tests across
choiceErrorandtagsError, including posting a visible name where anid belongs, a form that does not ask, and an event with the question but no tags.
Not covered: the controls themselves and the
submission_tagreplacement. This repo has no DOM testtooling and no test database in the unit suite, so neither a rendered checkbox list nor a delete-then-
insert is something a test here can observe.
Part of the
2026-08-17T05-46-05evaluation cycle. Not merged, not deployed.