Skip to content

feat: tournament onboarding, tracks, and forms edit lifecycle - #68

Merged
ethnjs merged 92 commits into
mainfrom
feat/tournament-onboarding
Sep 4, 2026
Merged

feat: tournament onboarding, tracks, and forms edit lifecycle#68
ethnjs merged 92 commits into
mainfrom
feat/tournament-onboarding

Conversation

@ethnjs

@ethnjs ethnjs commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Summary

Three features shipped on this branch since the forms builder merged:

  1. Tournament onboarding (closes feat: tournament onboarding #65) — orchestrates which forms a member must complete to become an official tournament member, in what order, and snapshots completion.
  2. Tournament tracks — a new catalog of ways a member can be involved in a tournament (e.g. test-writing, test-reviewing, day-of volunteering), each with its own status. Motivation: a single membership status flag can't represent a member who's confirmed for test-writing but still just interested in day-of, or a multiday tournament where a member's status needs to move independently per role. Most tournaments are simple enough to run on one track, but this makes the more complex ones representable without special-casing them.
  3. Forms edit lifecycle & field types — reversible archive/unarchive for fields, options, and whole forms; gated response patching that only re-flags responders actually affected by a change; the event_preference field type and its write-through; prerequisites for standard (non-onboarding) forms.

Plus a handful of unlisted dev-tooling and profile-completeness fixes picked up along the way (see below).

What changed

Tournament onboarding

  • TournamentForm: 1:1 companion row every tournament-scoped Form gets at creation. is_onboarding + order drive the sequence; form_id is its own primary key.
  • TournamentMembership.onboarded_at: set once a member has answered every currently-onboarding-flagged published form; permanent once set — removing/archiving an onboarding form never unsets it, only adding a new one clears it tournament-wide.
  • TD routes: add / reorder (validates a contiguous 1..N sequence) / remove onboarding forms. Archiving or hard-deleting a form still flagged is_onboarding is blocked until it's removed from onboarding (_reject_if_onboarding in forms.py — the one place Forms references Onboarding).
  • Member route: POST /tournaments/{id}/onboarding/progress/ finds the next unanswered published onboarding form, snapshots onboarded_at once none remain.
  • Frontend: drag-and-drop onboarding config page under Forms; /join now routes into /tournaments/{id}/onboarding, which resolves to the next required form and advances automatically on submit.
  • Tests: backend/tests/api/tournament/test_onboarding.py.

Tournament tracks

  • TournamentTrack catalog model + management API + settings page (mirrors the invites page layout).
  • TournamentMembershipTrackStatus write-through table, synced under a monotonic transition rule (a status can't regress once advanced) — supports the multiday interest/confirmed/declined progression per track.
  • Availability forms can carry track outcome config; submitting/patching a response writes the resulting track status through.
  • Track statuses replace the old single membership status field — members page now shows a list of per-track statuses (colored pill menu) instead of one status.
  • Tests: backend/tests/api/tournament/test_tracks.py.

Forms edit lifecycle & field types

  • Reversible lifecycle: archive/unarchive verbs for fields and options (previously archive-only/delete); whole-form publish/archive is now reversible too. Archived-fields section with restore + permanent delete.
  • Gated response patching: field edits are classified server-side (not mirrored on the client) into whether they affect existing responses; only responders actually impacted are flagged via FormResponsePendingUpdate and routed through a patch flow instead of a failing resubmit. TDs confirm which edits send questions back to previous responders before saving.
  • Field key reuse: editing a field in place now releases its archived key for reuse instead of permanently burning it.
  • event_preference field type: strict option validation, allow_duplicates enforcement on ranked-choice, write-through to a new tournament_membership_event_preferences table, invalidation clears write-through rows, grouped event preferences exposed on membership reads.
  • Availability write-through fixes: scoped to the days a submission actually covers (previously could touch unrelated days); preset option regrouping is flagged and shifts pinned to their date; preserves form options when changing presets instead of clobbering them.
  • Prerequisites: standard (non-onboarding) forms can declare prerequisites; a member-facing eligible-forms route filters what surfaces.
  • Misc: shared EditableText/floating-action-bar/respondent-view components reused across invites, tracks, and forms; several option-row/preset UI fixes.

Dev tooling (unlisted)

  • Each git worktree gets its own database via a post-checkout hook, so parallel branches don't fight over one dev DB.
  • SQL echo is opt-in; alembic's fileConfig no longer disables uvicorn's loggers on startup.
  • Alembic is the source of truth for schema on startup (previously could drift from create_all).
  • Seed script no longer prints non-ASCII checkmarks that crash under a cp1252 console.

Profile completeness (unlisted)

  • Onboarding now requires every profile field except pronouns, and the frontend skip button no longer offers loopholes around newly-mandatory fields.

Test plan

  • backend/tests/api/tournament/test_onboarding.py, test_tracks.py — new coverage for both features end to end.
  • backend/tests/api/test_forms.py, backend/tests/core/test_form_changes.py, test_form_validation.py, test_form_write_through.py, test_forms.py, test_tournament_form_prerequisites.py — edit-classification, pending-update flagging, event_preference validation/write-through, prerequisite evaluation.
  • backend/tests/core/test_profile_status.py — updated for the mandatory-field change.
  • backend/tests/test_migrations.py — new coverage exercising the migration chain (tracks, onboarding, event preferences).
  • Manually verified: join a tournament through /join, complete each onboarding form in order, confirm onboarded_at snapshots and a later-added onboarding form clears it; create a track, set an outcome on an availability form, submit and confirm the member's track status advances; edit a published form's fields and confirm only affected responders get flagged.

ethnjs added 30 commits August 25, 2026 19:00
@ethnjs ethnjs linked an issue Aug 31, 2026 that may be closed by this pull request
@vercel

vercel Bot commented Aug 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nexus Ready Ready Preview Aug 31, 2026 1:47am

@railway-app

railway-app Bot commented Aug 31, 2026

Copy link
Copy Markdown

🚅 Deployed to the nexus-pr-68 environment in nexus

Service Status Web Updated (UTC)
nexus ✅ Success (View Logs) Aug 31, 2026 at 1:46 am

@railway-app
railway-app Bot temporarily deployed to nexus / nexus-pr-68 August 31, 2026 01:12 Destroyed
@ethnjs
ethnjs force-pushed the feat/tournament-onboarding branch from 5d99bae to 5cb197a Compare August 31, 2026 01:46
@railway-app
railway-app Bot temporarily deployed to nexus / nexus-pr-68 August 31, 2026 01:46 Destroyed
@ethnjs ethnjs added enhancement New feature or request backend frontend database Updates database schemas or models forms tournaments labels Aug 31, 2026
@ethnjs
ethnjs merged commit a8de602 into main Sep 4, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend database Updates database schemas or models enhancement New feature or request forms frontend tournaments

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: tournament onboarding

1 participant