Skip to content

fix: port bug fixes from hackutd-harp (check-in RSVP gate, decision emails, phone/draft/validation, mobile UI) - #165

Merged
balebbae merged 13 commits into
mainfrom
devin/1789083549-port-hackutd-harp-fixes
Sep 11, 2026
Merged

balebbae merged 13 commits into
mainfrom
devin/1789083549-port-hackutd-harp-fixes

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Summary

Ports the 11 bug-fix commits that landed on hackutd/hackutd-harp but never made it back to harp (cherry-picked with original authorship; Zero Day branding hunks dropped). Grouped by area:

Check-in / scans

  • Check-in scan respected accepted only, not RSVP — a hacker who declined or never RSVP'd could still be checked in. POST /admin/scans now uses Application.GetCheckInEligibility (status + rsvp_status + walk_ins.promoted_at in one query) and refuses unless rsvp_status = confirmed or the user is a promoted walk-in. Gated by a new super-admin setting check_in_requires_rsvp (default true, GET/PUT /superadmin/settings/check-in-requires-rsvp, toggle in PermissionsTab). Duplicate-scan 409 is now checked before eligibility so a re-scan of an already-checked-in hacker doesn't surface as a 403. 403 bodies carry a message the scanner UI shows ("rsvp declined — hacker gave up their spot", etc.).

Decision emails

  • Recipients were marked decision_email_sent before dispatch — a crash/redeploy mid-run left unsent hackers permanently skipped. Now each recipient is marked individually after the provider accepts the send (10s bounded write); failures stay unmarked and retryable. Adds an atomic.Bool in-flight guard (409 on concurrent runs) and a sync.WaitGroup that drainBackgroundJobs waits on during graceful shutdown.

Hacker application flow

  • Phone was US-only (^\+1\d{10}$) — new phone-input.ts country-code picker + E.164 validation on both client and server (applications.go, rsvp.go, travelrsvp.go).
  • Draft saves rejected when a select option was removed from the schemavalidateResponses no longer enforces option membership in draft mode; SchemaSelect/SchemaCombobox surface obsolete values with a "Clear answer" affordance (getObsoleteOptions).
  • Wizard never picked up schema changes / bad-request errors showed as "check your connection"ApplicationWizard reconciles the live schema from PATCH responses (draft.ts, OutdatedAnswersNotice), and 400s are attributed to the specific answers that were sent (application-regressions.test.mjs, npm run test:applications).
  • Stale validation errors on selects/checkboxes never clearedmode: "onTouched" + explicit onBlur on custom triggers.
  • Mobile: FormItem grid uses grid-cols-[minmax(0,1fr)], summary/review rows wrap, and select/combobox text gets min-w-0 so long labels/answers/options can't force horizontal scroll. The fork's .zero-hacker-surface { overflow-x: clip } is replaced by overflow-x-clip on harp's HackerLayout SidebarInset.

Admin / infra

  • Super-admin schema editor: reordering a field didn't stickstampFields now preserves display_order.
  • Forms overview page couldn't scroll inside AdminLayout.
  • Admins logging in on mobile were sent to the desktop-only admin portalAuthCallbackPage routes them to the hacker app on < 768px.
  • Dockerfile go mod download flaked on Cloud Build (HTTP/2 stream resets) — GODEBUG=http2client=0 + GOMAXPROCS=4 + 3-attempt retry.

Fork-only changes intentionally not ported: HackUTD/Zero Day branding, data-hacker-form-dropdown hooks, claude.md edits. Swagger docs regenerated (task gen-docs, no diff).

Verified locally: gofmt -l ., go vet ./..., go test ./cmd/api/ ./internal/..., and in client/portal: npm run lint, npm run test:applications, npm run build — all green.

Link to Devin session: https://app.devin.ai/sessions/39d24a258d914ea7963556448bfcd893
Open in Devin Desktop: https://app.devin.ai/desktop/session/39d24a258d914ea7963556448bfcd893?variant=devin
Requested by: @balebbae

AdminLayout pins the admin shell to h-svh and hides its overflow, so a
page that sizes to its own content is clipped with no way to reach the
rest. FormDetail already scrolls per tab and ResponsesTable manages its
own, but the overview and its loading skeleton returned a plain block,
so anything past the fold was unreachable.

Measured before: 1577px of content in a 520px viewport with maxScroll 0.
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@balebbae
balebbae merged commit 88245d8 into main Sep 11, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant