Skip to content

Release: hackathon judging, email judge/admin access, submissions & payouts#174

Merged
sacha-l merged 37 commits into
mainfrom
develop
Jun 13, 2026
Merged

Release: hackathon judging, email judge/admin access, submissions & payouts#174
sacha-l merged 37 commits into
mainfrom
develop

Conversation

@sacha-l

@sacha-l sacha-l commented Jun 13, 2026

Copy link
Copy Markdown
Collaborator

Release PR: promotes develop to main, shipping the hackathon judging system and everything since #168 to production. This is the first production release of the judging / email-access / submissions feature set.

Highlights

Hackathon judging system (new)

  • Public per-program project submissions, with a required 2 to 3 sentence brief and a "checked-in guest" (Luma signup) gate on the intake form.
  • Email-invited judges score submissions against a rubric (Requirements /2, Tech Stack /5, Innovation /5) in claimable batches of 10, then submit a locking ballot.
  • Gated unified leaderboard: results stay locked until every registered judge has submitted and every submission has coverage, then show ranked averages plus a per-judge breakdown.
  • Global admins select winners by prize tier, publish a public results page, promote winning submissions into Stadium projects, and track payouts (mark paid).

Email-based admin and judge access (new)

  • Invite admins and judges by email; they sign in with a Supabase magic link (no wallet required), scoped to a single program by role (admin or judge).
  • Email admins can run the lighter organizer actions (guests import, sponsors, program edit). Winner selection, publishing, and payouts stay wallet/global-admin only.

Email notifications

  • Resend-backed transport powers submission confirmations (with resubmit/overwrite and a "late" flag), admin/judge invites, and the existing project notifications.
  • Optional global BCC archive (EMAIL_BCC) on every outbound email.

Signing UX

  • High-stakes actions (mark paid, award prize, publish results, milestone confirm-payment) require a fresh signature each time; low-stakes program edit rides the cached admin session.
  • Viewing an admin program page no longer auto-prompts a wallet signature; admin data loads behind an explicit "Load admin data" step.

Programs and UI

  • Programs surfaced on the landing page; project search moved into program pages; key-info headers on the admin/judge view plus a public key-info page.
  • Fixes: published RESULTS page crash (missing import), program-edit slug being cleared on open, mobile audio toggle, leaderboard eligibility flags, stable React keys.

Tooling and data

  • Test-wallet harness and the stadium-tester browser suite; a judging simulation.
  • 13 Supabase migrations (already applied to production) covering submissions, scores, ballots, batch claims, prizes/results, payout, and email-admin roles.

Scope

78 files changed, +7,066 / -706. Bundles #168, #170, #171, #172, #173.

Deploy prerequisites

  • Production Supabase migrations applied (13).
  • Railway: ADMIN_SESSION_SECRET (boot-blocker on the new code), RESEND_API_KEY + RESEND_FROM_EMAIL, FRONTEND_URL.
  • Vercel production: VITE_SUPABASE_URL + VITE_SUPABASE_ANON_KEY, VITE_USE_MOCK_DATA=false, VITE_USE_TEST_WALLET off.
  • Supabase Auth: production domain added to the redirect-URL allow-list.

🤖 Generated with Claude Code

sacha-l added 30 commits May 23, 2026 11:47
The mute toggle relied on widget.setVolume(0), but volume is hardware-controlled
on mobile (notably iOS) where setVolume is a no-op — so tapping mute kept playing
at full volume. Mute now pauses and unmute plays (setVolume(100) on desktop);
play/pause honor the user's tap on every platform.
Editable add/remove lists keyed their controlled inputs by array index, so
deleting a middle row reused the wrong DOM node and shifted focus/values onto
a sibling. Give each row a stable client-generated id (kept length-synced with
the data) and key by it. Also fold the index into TeamPaymentSection's
read-only member key: a wallet address is not unique across members (mock data
redacts several to one string), so keying by address alone collided and tripped
React's duplicate-key warning.
project.controller used raw console.log for the updateProject payload preview
and the M2-agreement confirmation; route them through the existing logger util
(info/success) like the rest of the controller.
…am pages

The landing page now shows only the hero, live stats, and program cards
(heading "Explore past programs"); the featured unit and the projects
filter/grid are gone. The search view (search box, WINNERS/ALL toggle,
category filters, grid + detail modal) is extracted into a reusable
ProjectExplorer and rendered on each program's detail page, scoped to
that program's entries.
The Programs page open section was labelled "Grid"; relabel it
"Ongoing programs" to match the page's intent.
WebZero programs run across any tech and platform, not just Polkadot.
Drop "on Polkadot" from the hackathons program-card blurb.
…rboard

Bitrefill hackathon judging, iteration 1.

Intake:
- Public POST /programs/:slug/submissions (rate-limited, honeypot, dedup by
  Luma email) backed by a new program_submissions table.
- SubmitProjectModal on hackathon program pages (name, Luma email, title,
  video link, GitHub url).

Judging:
- Email judges: new 'role' on program_admin_emails (admin|judge) reusing the
  existing magic-link invite; requireProgramJudge grants scoped write to the
  scoring routes only, never to payment/approval routes.
- Rubric scoring (requirements/2, tech stack/5, innovation/5) with notes,
  draft save, and a ballot submit that locks once finalized.
- Leaderboard gated until every registered judge submits; ranks by mean /12
  with per-criterion breakdown, tie-break on innovation then tech stack, CSV
  export.
- ProgramJudgingSection rendered for wallet admins and email judges.

Mock fixtures (mockJudging.ts) so the Vercel preview exercises the full flow.
Tests: validator, scoring service (gating/tally/eligibility), submission
controller, requireProgramJudge middleware. Server 323 passing.
Role-playing agents (submitter, 3 judges, organizer) drive the real
controllers, requireProgramJudge middleware, and scoring service against an
in-memory Supabase fake. Walks the full journey (submit -> invite -> score ->
ballots -> gated leaderboard + tally) plus edge cases (dedup, validation,
non-invited block, out-of-range, ballot gating, lock-after-submit) and
regenerates SIMULATION_REPORT.md. 10 scenarios, all green.
An ineligible submission (Luma email not in the signup list) could rank or win
with nothing signalling it — the scoring view flagged it but the leaderboard
did not. Leaderboard rows now include `eligible`; the UI tags ranked rows
NOT IN LUMA. Surfaced by the judging simulation (Comet Bridge ranked #2).
…eam tracking

Admins can promote a winning/selected submission into a real `projects` row
so it flows into the existing project + payout machinery (team_members,
payments, bounties_processed, the admin project tables).

- Migration: promoted_project_id on program_submissions (FK -> projects, links
  + makes promotion idempotent).
- scoringService.promoteToProject: creates the project (title/repo/demo carried,
  hackathon_* backfilled from the program, submitter as a team member, Luma
  email + video stamped into the description since team_members has no email
  column), then links it back. Idempotent.
- POST /programs/:slug/submissions/:id/promote (requireProgramAdmin — judges
  cannot create projects).
- Client: 'Add to Stadium projects' control on the admin scoring table (wallet
  admins only via canPromote); shows a link to the project once promoted.
- Payout wallet is added by an admin at payout time via the existing flow (no
  wallet collected at submission).

Simulation extended: promote the winner -> project + team member + back-link +
NOT-PAID state -> record a payout. fakeSupabase gained .update(). Server 340
passing; client build + lint clean.
Payout tracking is a simple admin toggle, not wallet/on-chain machinery: an
admin marks a (winning) submission paid/unpaid.

- Migration: paid / paid_at / paid_by on program_submissions.
- PATCH /programs/:slug/submissions/:id/paid (requireProgramAdmin) records who
  + when; reversible. Surfaced on submission rows.
- Client: MARK PAID / MARK UNPAID toggle + ·PAID badge in the admin scoring
  table (wallet admins only).
- Simulation: admin marks the winner paid (paid_by/paid_at asserted) and toggles
  it back. Replaces the earlier manual payments-row stub.

Server 343 passing; client build + lint clean.
A logged-in client must see only their own event, and only what their role
needs. Two fixes:

- requireProgramViewer is now role-aware: only ADMIN-role emails (and wallet
  admins) get the program read surface. JUDGE-role emails are NOT viewers, so a
  judge can no longer read the event's applicant PII, signups, inbox, audit log,
  or admin roster — judging only (requireProgramJudge).
- AdminProgramPage probes the email session's actual permissions and renders
  accordingly: judges see ONLY the scoring section; admins additionally see
  applications. Neither can reach another event (every call is gated by
  program + grant server-side).

Cross-event isolation already held server-side (gates scope by slug -> program
-> grant); now covered explicitly by the simulation, alongside the judge
least-privilege check. Updated the viewer middleware test for the role-aware
gate. Server 346 passing; client build + lint clean.
Make email + admin sign-in testable before the event.

- npm run verify:email -- you@addr : sends one test message through the real
  Resend transport (no admin auth, no DB) and prints the message id or the exact
  failure. Confirms RESEND_API_KEY + verified RESEND_FROM_EMAIL in one command.
- GET /api/health now reports email.resendConfigured / fromConfigured (booleans
  only) so a deployed env can be checked without sending.
- docs/TESTING_EMAIL_AND_SIGNIN.md: runbook clarifying the TWO email systems
  (Resend for invites/notifications vs Supabase Auth for the magic-link sign-in),
  the deployed test steps, the redirect-allowlist + non-mock-deployment caveats,
  and a scale note (custom SMTP in Supabase for ~100 sign-ins).

No production code paths change. Server 346 passing.
…panel by default

- Point the SoundCloud widget at the otherside-podcast track instead of the
  pommeshdrms profile, and update the now-playing card metadata (fallback
  name, genre prefix, profile link, iframe title) to match.
- Default the brightness/audio rack to collapsed on every viewport (was
  mobile-only); drop the now-unused isMobileViewport helper.
…ey suite

Add 'npm run dev:harness' (client): boots mock mode + the //Alice test wallet
with Alice whitelisted as a global admin (one env var), so the stadium-tester
agent connects as a real-signing wallet and can reach every admin + judging
surface — no real server, no secrets (Alice's seed is the public dev mnemonic;
testWalletInjection no-ops in prod builds).

Commit the canonical wallet-gated suite (flows/client-journey.spec.mjs): sign
in -> public submit -> score every submission -> submit ballot -> leaderboard
reveals the winner -> promote -> mark paid. Validated 5/5 green against the
harness. Document the launch + limitation (auto-sign can't verify the
routine-vs-important signing UX) in SKILL.md.
…nd-trips

In mock mode, submitProjectSubmission was a no-op that returned a fake id, so
a project submitted through the public form never appeared on the judging
surface. The judging list was hardcoded to three seed rows, so the preview
couldn't demonstrate the full submit -> score -> leaderboard -> promote -> pay
journey on a project you actually submitted.

mockJudging now persists public submissions to localStorage (addSubmission)
and merges them into every judging view (judgeView, submitBallot, leaderboard,
setPaid). Added rows are treated eligible (the form asks for the Luma email)
and, having no second-judge score, average over this judge alone on the
leaderboard. resetForTests clears the new key. Verified live against the
dev:harness: a submitted PixelPay Wallet shows up in SCORE alongside the seeds.
Submitters now must describe what their project does, so judges have context
beyond a title and two links. The brief is a required field (max 500 chars)
on the public submit form, validated client- and server-side, stored on the
submission, and shown to judges in the scoring card above the rubric.

- SubmitProjectModal: new WHAT DOES IT DO? textarea with a live char counter;
  required + max-length checks in validate().
- submission.validator: projectBrief required, trimmed, max 500 (BRIEF_MAX
  mirrors the client constant); returned in value.
- program-submission.repository: persist + transform project_brief.
- Supabase migration: add nullable project_brief TEXT (required enforced in
  the app; nullable keeps the migration safe for any pre-existing rows).
- ProgramJudgingSection: render the brief per submission.
- api.ts: projectBrief on ApiSubmission + the submit payload.
- mock + sim + tests updated; seeds get briefs so the preview/judging demo
  reads realistically.

Server 348 passing; client build + lint clean. Verified live on dev:harness:
submit with a brief -> it renders in judging; empty brief is blocked.
Reworks the judging panel so judges score and a platform (global) admin elects
winners afterward, replacing the per-card ADD TO STADIUM + MARK PAID actions
(those endpoints stay, just unwired from the panel).

- Judging panel: drop promote/paid buttons. SCORE tab shows ballot progress
  once a judge submits ('2 of 3 judges in, waiting on the rest'). The second
  tab is now RESULTS: ranked once every judge submits, with a per-row prize
  selector + PUBLISH RESULTS for platform admins only (canSelectWinners =
  isGlobalAdmin); read-only with winner badges for judges/per-program admins.
- Prizes are configurable per program (ProgramFormModal tier editor), default
  500/200/100 EUR Bitrefill giftcards. Flexible: any tier to any number of
  winners. Awarding is gated to global admins and blocked until judging is
  complete (409).
- Public results: new GET /:slug/results returns PII-free submissions +
  winners only after an explicit publish. ProgramDetailPage renders a ·RESULTS
  section (reusing UnitCard) with winners highlighted by prize, ordered prize
  desc; no Luma email or scores exposed.
- Schema: additive nullable migration — prize_tiers + results_published_at on
  programs; prize_amount/currency/label + awarded_at/by on program_submissions.
  Transforms, repos (setPrize, setResultsPublished), and scoring service
  (ballotProgress, leaderboard prize, isJudgingComplete, publicResults) updated.
- Mock parity for the dev:harness demo; validator + controller tests for authz
  (403 non-global, 409 mid-judging) and the PII-free public payload; sim
  journey rewritten to award + publish + public results.

Server 365 passing; client build + lint clean. Verified live on dev:harness:
score -> submit (progress banner) -> assign 500/200/100 -> publish -> public
RESULTS shows all 3 winners, PII-free.
Lets judges split a large submission field. Submissions group into fixed
batches of 10 (derived from stable order; BATCH_SIZE shared client+server). A
judge claims batches ('Claim next 10' picks the least-covered batch so judges
auto-distribute; specific batches claimable too), scores only their claimed
batches, bulk-saves per batch, then submits once those are scored. Multiple
judges may share a batch; every project ends up scored by at least one judge.

Because not every judge scores every project, the leaderboard / winner-selection
gate changes from 'all registered judges submitted' to COVERAGE: every
submission has at least one score from a submitted judge. The RESULTS view now
lists all submissions ranked, each row expandable to the individual per-judge
scores.

- Migration: program_judge_batch_claims (program, judge_email, batch_number).
- New program-judge-batch.repository; submission-score.repository.upsertMany.
- scoring.service: batch derivation, claimBatch (least-covered), listForJudge
  (batches + claimedBatches + batchNumber), submitBallot scoped to claimed
  batches, coverage-based isJudgingComplete + leaderboard (locked reports
  submissionsScored/Total + pendingJudges), judgeScores breakdown on rows.
- Controller + routes: POST /scoring/claim-batch, PUT /scoring/scores (bulk).
- Client: api types + claimBatch/saveScores; ProgramJudgingSection SCORE-tab
  batch strip + CLAIM NEXT 10 + grouped SAVE BATCH; RESULTS expandable per-judge
  breakdown. Mock parity for the dev:harness demo.

Server 376 passing; client build + lint clean. Verified live on dev:harness:
claim batch -> score -> SAVE BATCH -> submit -> coverage unlock -> RESULTS shows
each project's per-judge scores; public results stay PII-free.
Surfaces the at-a-glance numbers an organizer or judge wants on opening a
program: confirmed participants (Luma signups), projects submitted so far, and
a live countdown to the submission portal close.

- New GET /:slug/stats (requireProgramJudge — judges + admins, no PII) returning
  { confirmedParticipants, submissionsCount } from the existing countByProgramId
  methods on the signup + submission repos.
- ProgramStatsHeader: three LCDStat tiles; the countdown targets the program's
  eventEndsAt (display-only, no enforcement), ticks each minute, shows Dd Hh /
  Hh Mm / Mm (CLOSED / — at the edges) with exact minutes in a tooltip.
- Mounted at the top of both AdminProgramPage access branches (wallet admin +
  email judge/admin). Mock parity (stats() + a Bitrefill eventEndsAt so the
  countdown demos).

Server 378 passing; client build + lint clean. Verified live on dev:harness:
48 participants · 3 submitted · 6d 4h to close.
…d admin view

Bitrefill (hackathon-type) tweaks; M2/other program types are untouched.

Public page (hackathons):
- Replace the SPONSORS & HOW TO APPLY, APPLY (past-winner), and KEY DATES blocks
  with a single ·KEY INFO panel: cover image, date + location, a SIGN UP ON LUMA
  button (program.eventUrl), and the schedule (from the program's schedule
  content). Non-hackathons keep their existing sections + apply flow.
- Submit copy now says only checked-in attendees can submit.

Submission gating:
- submit() rejects (403) any email not on the program's imported Luma list — that
  imported list is the checked-in / approved-guest set. New
  programSignupRepository.existsByEmail; eligible flag is now always true.

Admin view (hackathons):
- EDIT button (top-right) opens the program form, gated to program admins; PATCH
  /:slug moves from global-only to requireProgramAdmin. New cover-image-URL field
  in the form.
- Hide sponsors, audit log, and the legacy applications table; relabel signups to
  ·LUMA-APPROVED GUESTS. Keep stats, judge invites, guests, and the judging panel.

Schema: additive cover_image_url on programs.

Mock: Bitrefill coverImageUrl + Luma eventUrl + schedule content; a checked-in
guests dataset wired to the guests table, the submit gate, and the stats count.

Server 380 passing; client build + lint clean. Verified live on dev:harness:
key-info panel renders; off-list email 403s, checked-in email submits; admin view
shows EDIT + guests + judging only.
The ·PROGRAM ADMINS block on the program page is now a single read-only list of
the program's people — email admins/judges (email + ADMIN/JUDGE role) and any
per-program wallet admins (by address). The separate ·EMAIL ADMINS & JUDGES
sub-section is gone.

All add/remove/invite controls move into the program EDIT modal (global admins
only, matching the server's global-only grant gating). ProgramAdminsSection now
takes an "editable" flag (read-only on the page, editable in the modal) and a
"reloadToken" so the page list refreshes after edits made in the modal.

Client-only — the grant endpoints and their auth are unchanged. Mock now persists
email grants (seeded with a Bitrefill judge + admin) so invite/remove demos.

Client build + lint clean. Verified live on dev:harness: page shows the read-only
list; EDIT modal hosts the controls; inviting a judge and closing refreshes the
page list.
Resolve ProgramDetailPage.tsx conflict with #168: keep the submit-modal state
(submitOpen) from this branch and adopt develop's ProjectExplorer for the
projects section (which removed the now-unused navigate/useNavigate). Hackathon
key-info / results / section-gating from this branch are preserved.
Bitrefill hackathon: judging suite + public/admin overhaul
Part A (Bitrefill preview): real event cover image (client/public/bitrefill-cover.png),
program name set to the Luma title "PROMPT x PURCHASE — A Bitrefill Hackathon", and
sacha@joinwebzero.com added as a checked-in guest so the submit flow can be tested.

Part B (Option 2 — move the SIWS line): no in-app action moves funds on-chain and the
email magic-link session is a verified identity, so email-invited program admins can now
run the lighter organizer actions without a wallet:
- requireProgramAdmin gains a write-enabled email path: a valid admin-role
  program_admin_emails grant administers the program (sponsors, signups import,
  applications status, program edit). Judge-role / no grant falls through to the wallet
  gate, unchanged. Wallet admins unaffected.
- Winners + payouts stay wallet/global-admin: promote + mark-paid now require
  isGlobalAdmin (joining award-prize + publish, which already did). create-program, the
  admin roster, and confirm-payment remain global-wallet.

Tests: new requireProgramAdmin email-path middleware test; promote/setPaid 403-for-
non-global + 200-for-global controller tests. Server 387 passing; client build + lint
clean. Verified live: cover image + name render; sacha@ submits; a non-listed email
still 403s.

Note: this is server-side authz only — surfacing the new controls to email admins in
the client (and teaching the EDIT modal to save via the email token) is a flagged
follow-up; no regression for wallet admins meanwhile.
…auth save (Part C)

Completes Option 2 on the client. Email-invited program admins (role=admin) can now
run the lighter organizer actions in the UI, matching the server gates from the
previous commit:
- ProgramFormModal: emailMode prop — when set, the save authorizes with the Supabase
  session header (signAuthHeader) instead of a SIWS signature. Wallet path unchanged
  (the SIWS branch is byte-identical, now under an else).
- AdminProgramPage: the EDIT button + program-edit modal are available to email admins
  (emailMode, x-supabase-token). The social-admin branch now shows the read-only admins
  list, the Luma-approved guests (CSV import), sponsors (non-hackathon), and editable
  applications. Relabel the email-admin session badge VIEW-ONLY -> ADMIN.

Winners/payouts stay wallet/global-admin (award-prize/publish/promote/mark-paid require
isGlobalAdmin, which an email session never sets).

Client build + lint clean; wallet admin view verified live (EDIT opens/cancels, sections
intact). The email-admin path is code-reviewed but not exercisable in the harness (no
email sign-in in mock) — it relies on the server gates + the shared section components
already used by the wallet path.
…e flag

Submitters now get a best-effort confirmation email on submit/resubmit, and can
resubmit to change their entry before the deadline.

- Resubmit = overwrite the existing entry (one per Luma email). submit() upserts
  (findByEmail -> updateSubmission 200 / create 201) instead of 409-ing duplicates.
- Confirmation email: new submission-confirmation.service + notification template
  (mirrors program-admin-invite; best-effort, never fails the submission), with a
  link to the program page + resubmit instructions + the deadline.
- Late flag: the deadline stays informational, but a submit after the program's
  event-end time is recorded late=true (new nullable column) and shown as a ·LATE
  badge in the judging panel + results. Carried on submissions, leaderboard rows,
  and public results.
- Client: submit-modal success copy + drop the 409 message; api types + mock
  upsert/late parity; one seeded late submission so the badge demos.

Server 391 passing; client build + lint clean. Verified live on dev:harness:
submit -> confirmation copy; resubmit same email -> overwrites (one entry, no 409);
a late submission shows ·LATE.
The transport now blind-copies every outbound email to the address(es) in
EMAIL_BCC (comma-separated), merged with any caller-supplied bcc. Recipients
never see it; leave EMAIL_BCC empty to disable. Documented in .env.example.
sacha-l added 7 commits June 12, 2026 16:57
The ·RESULTS section renders <UnitCard> but the import was dropped when
#168 swapped the projects grid to <ProjectExplorer>. tsc (strict:false)
didn't flag the bare identifier, so it built clean and threw
"UnitCard is not defined" at runtime the moment a program with published
results rendered. Re-add the named import from @/components/unit-card.
Email-admin authz + submission confirmation/resubmit + BCC (post-#170 follow-up)
High-stakes admin actions now re-sign a fresh wallet signature every time
instead of riding the cached admin session, so each is a deliberate human
confirmation. Low-stakes program edit moves the other way — onto the cached
session — so it stops nagging for a signature on every save.

- New SIWS statements: mark-paid, award-prize, publish-results, confirm-payment.
- ProgramJudgingSection: award prize + publish results re-sign via a new
  signWinnerAction prop; added a PAID toggle to the results table (global
  wallet admin only) wired to setSubmissionPaid with the same fresh-sign.
- Leaderboard now surfaces each submission's paid flag (scoring.service +
  ApiLeaderboardRow + mock) so the toggle reflects state.
- m2 confirm-payment (AdminPage M1/M2 payout handlers + WinnersTable payout)
  re-signs fresh via signAction('confirm-payment'); WinnersTable gets a
  dedicated signPaymentAction prop so other admin writes keep the cache.
- ProgramFormModal: drop the manual @talismn/siws block; program create/edit
  now rides signAuthHeader (cached bearer for wallet, Supabase for email).
  Removed the now-dead connectedAddress/emailMode props.

Email/per-program admins remain blocked from payouts/winners/publish
server-side (isPlatformAdmin) and in the UI (canSelectWinners).

Test: leaderboard surfaces paid; server suite green (392).
The auto-slug effect's stale slugEdited closure raced the hydrate effect on
open, running setSlug(slugify('')) after the loaded slug was set — leaving the
(disabled) slug field empty, which failed validate() and blocked EVERY program
edit save. Guard the auto-slug to create mode only (!editing) so edit keeps the
loaded slug. Surfaced while verifying the relaxed program-edit flow.
fix(programs): restore UnitCard import — RESULTS section runtime crash
feat(programs): fresh-signature gate on payouts/results + PAID toggle; relax program edit
@vercel

vercel Bot commented Jun 13, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
stadium Ready Ready Preview, Comment Jun 13, 2026 4:53pm

@sacha-l sacha-l merged commit 54bc2c8 into main Jun 13, 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