Skip to content

deploy to prod#847

Merged
JoaquinBN merged 26 commits into
mainfrom
dev
Jun 28, 2026
Merged

deploy to prod#847
JoaquinBN merged 26 commits into
mainfrom
dev

Conversation

@JoaquinBN

Copy link
Copy Markdown
Collaborator

No description provided.

JoaquinBN added 26 commits June 26, 2026 01:17
Builders, Validators, and Community each get a role landing that adapts to
three states: signed-out, signed-in without the role, and earned. Dedicated
journey routes guide users step by step to unlock their role, and journeys
now grant the ROLE itself rather than points: farmable point grants are
removed, and points come only from verifiable tasks (starring the boilerplate
repo, posting on X). Journey progress is durable via zero-point marker
contributions that also drive a "started" state.

The Builder journey is connect GitHub plus star the boilerplate repo. The
Community journey is five verified steps (link X, link Discord, follow on X,
join Discord, and a verified X post) and grants the Creator role on
completion, with the post checked through Sorsa against the linked handle.
The Validator journey stays a single waitlist form and keeps the graduation
reward. Linking GitHub now records a contribution like X and Discord.
Community membership is only created by completing the journey: the previous
automatic creation from community contributions, POAP claims, and Discord XP
is removed. Sidebar subsections stay locked until the relevant journey is
complete, the profile shows an in-progress role with an owner-only grey
badge, and the What's New dialog is shown only to returning users.

## Implementation Notes
- frontend/src/components/funnel/: role funnel dispatcher, per-role landings,
  and shared journey building blocks
- frontend/src/routes/{BuilderJourney,CommunityJourney}.svelte, ValidatorWaitlist.svelte:
  dedicated journey views
- frontend/src/lib/roleState.js: funnel state machine (none/started/earned),
  role/category mapping, journey paths
- frontend/src/{App,components/Sidebar,components/profile/*,routes/Profile}.svelte:
  routing, locked subsections, in-progress role card and header badge
- frontend/src/components/WhatsNewDialog.svelte: gate to returning users only
- backend/users/views.py: start/complete journey endpoints, link_github_account,
  community journey status and X-post verification
- backend/creators/community_journey.py, models.py, views.py: five-step status,
  CommunityPostProof, journey-gated membership
- backend/{leaderboard/models,poaps/signals,community_xp/services}.py: remove
  automatic Creator creation
- backend/social_tasks/sorsa_client.py: tweet lookup for X-post verification
- backend/.../migrations: community-link-github contribution type, builder star
  task, CommunityPostProof
Clean up the over-engineering surfaced by the funnel review with no
behavior change. Two role landing components dropped a duplicate CTA
label that mirrored the primary one, and the validator landing's
repeated waitlist badge is now a single snippet. On the backend, the
three social-link award endpoints and the two journey-start endpoints
now delegate to shared helpers; every public endpoint and response
stays identical.

## Claude Implementation Notes
- backend/users/views.py: Add `_award_social_link_points` (link_x/discord/github become thin wrappers) and `_mark_journey_started` (start_builder/start_role become thin wrappers). Endpoints, messages and points unchanged. start_builder now also ensures a 0-point `builder-welcome` multiplier exists, matching the generic path (harmless on a 0-point type).
- frontend/src/components/funnel/CommunityLanding.svelte: Remove redundant `finalLabel`, reuse `primaryLabel`.
- frontend/src/components/funnel/ValidatorLanding.svelte: Remove redundant `finalLabel`; extract the duplicated "on the waitlist" badge markup into a `{#snippet}` rendered in both spots.
Applies the safe CodeRabbit findings across the builder, community, and
validator funnels with behavior preserved on the happy paths.

Backend: the builder/community completion and legacy creator-join endpoints
are now idempotent under concurrent requests (row lock + get_or_create instead
of check-then-create), the welcome-marker write runs in a single locked
transaction, public profiles no longer expose in-progress funnel state
(has_*_welcome / link booleans), the community X-post gate requires an exact
@mention (boundary regex, not substring), CommunityPostProof.verified_at now
tracks re-verification, the GitHub-link seed migration fails closed when the
builder category is missing, and the Sorsa tweet-info parser guards a
malformed user payload instead of 500-ing.

Frontend: journey init/copy/storage errors that were silently swallowed now
surface as toasts, the builder resources route is gated like the locked
sidebar, stored onboarding roles are validated before redirect, the community
landing copy no longer overpromises points (the journey grants the role; tasks
earn points), the hero progress bar collapses to zero at 0%, the inert notice
"dismiss" control is removed, profiles stop showing earned Community members as
"in progress" while status loads, and the shared journey components drop the
banned 'badge' prop name for 'contribution'.

## Claude Implementation Notes
- backend/users/views.py: idempotent + row-locked complete_builder_journey, complete_community_journey, and _mark_journey_started.
- backend/users/serializers.py: strip has_*_welcome / has_community_link_* from public_profile responses.
- backend/creators/views.py: join_creator_view uses get_or_create in an atomic block.
- backend/creators/community_journey.py: post_matches requires an exact @handle via boundary regex.
- backend/creators/models.py + migrations/0003_communitypostproof.py: verified_at auto_now instead of auto_now_add.
- backend/social_tasks/sorsa_client.py: guard non-dict user payload, raise SorsaError.
- backend/contributions/migrations/0073_create_community_link_github.py: fail closed if builder category absent.
- backend/users/tests/test_social_link_rewards.py, test_community_journey.py: fixtures match prod (builder category; handle from genlayer_handle()).
- frontend journeys (JourneyHeroCard/JourneyStepRow): badge prop -> contribution*; progress fill 0-width at 0%.
- frontend funnel/RoleLanding + routes/BuilderJourney/CommunityJourney/ValidatorWaitlist: navigate-on-success, init/storage/copy error toasts, execCommand return check.
- frontend JourneyNotice: remove inert dismiss control.
- frontend App.svelte: gate /builders/resources with roleGatedRoute.
- frontend ProfileCompletionGuard: validate stored onboarding role.
- frontend Sidebar: request-key guard on community lock-state fetch.
- frontend SocialLink: surface GitHub link-reward failure.
- frontend Profile.svelte: only show Community "in progress" for a loaded started-incomplete journey.
- frontend funnel/CommunityLanding: corrected points copy.
- frontend profile/RoleJourneyCard: anchor CTA instead of push().
…fort refresh

Second batch of CodeRabbit findings on the funnel work.

Backend: the default-multiplier bootstrap is now serialized by contribution
type (lock the type row before exists()/create()), so concurrent requests from
different users can no longer duplicate the default multiplier; the Sorsa
tweet-info parser rejects a non-string username instead of letting lstrip raise
an AttributeError that escapes SorsaError handling.

Frontend: the remaining locked validator/builder subsections
(all-contributions, participants, wall-of-shame) are gated at the route level
to match the locked sidebar, so direct navigation can't skip the funnel; and a
transient userStore.loadUser() failure no longer blocks entering a journey or
the waitlist redirect once the server-side mutation has already succeeded
(refresh is now best-effort).

## Claude Implementation Notes
- backend/users/views.py: lock ContributionType row before the multiplier exists()/create() in both _mark_journey_started and _award_social_link_points.
- backend/social_tasks/sorsa_client.py: raise SorsaError when user.username is not a string.
- frontend/src/App.svelte: roleGatedRoute for /builders/all-contributions, /validators/all-contributions, /validators/participants, /validators/wall-of-shame (sidebar already locks these).
- frontend/src/components/funnel/RoleLanding.svelte: navigate right after startRoleJourney succeeds; loadUser best-effort (fire-and-forget).
- frontend/src/routes/ValidatorWaitlist.svelte: startValidatorJourney is the only required step; loadUser + sessionStorage best-effort; redirect always runs on join success.
The role funnels are for onboarding newcomers only: anyone who already holds
a role (community member, builder, or waitlisted/graduated validator) keeps
full access without being pushed back through a journey. "Earned" is now read
straight from role membership for all three categories, so existing community
members reach their dashboard and unlocked subsections immediately. This drops
the live community journey-status round-trip that previously gated the earned
state, which also removes the spinner on every community entry, the sidebar
lock flash, and the misclassification of an earned member as in-progress on a
transient status-call failure.

Builder status auto-grant on steward-accept and leaderboard recalc no longer
re-creates the removed builder-welcome (+20) and builder (+50) point
contributions (the +20 had also become out-of-range against its now point-free
type); it still grants the role, which is human-gated, not farmable. The
in-progress funnel state stays owner-only but is no longer stripped from the
owner's own public profile, so their private grey "only you can see this"
badge renders again.

## Implementation Notes
- creators/community_journey.py: journey_status treats a Creator as
  started/complete/is_member regardless of the newer marker + step records
- users/views.py: complete_community_journey short-circuits an existing Creator
  with a 200 before the step gate (removes the now-dead later guard)
- leaderboard/models.py: ensure_builder_status only ensures the Builder profile,
  no longer writes the builder-welcome/builder point contributions
- users/serializers.py: in-progress funnel flags are stripped only from other
  viewers on a public profile, not from the owner
- components/funnel/RoleFunnel.svelte, App.svelte, components/Sidebar.svelte:
  community earned/lock state reads role membership synchronously like the
  other roles; removed the async journey-status gating
- users/tests/test_community_journey.py: assert existing Creators are
  grandfathered instead of re-funneled
The Builder journey now enables the Claim button as soon as the role-gating
steps are done, connecting GitHub and starring the boilerplate repo, which
matches the backend that grants the role on the star task alone. Adding
networks, getting testnet GEN, and deploying are relabeled as recommended next
steps rather than blockers, so an eligible builder can no longer be stalled by
client-side-only progress that does not survive across devices. The displayed
journey and the actual requirement now agree.

Separately, the personal authenticated-only surfaces (submit contribution, my
submissions, edit submission, edit profile, and notifications) are wrapped so a
signed-out visitor is sent to sign in instead of seeing the page shell.

## Implementation Notes
- routes/BuilderJourney.svelte: claim gated on the required steps
  (wallet/github/star) instead of all six; networks, top-up, and deploy marked
  "Recommended"; hero, notice, and completion copy updated; removed the now
  unused step counters
- App.svelte: wrap /submit-contribution, /my-submissions, /contributions/:id,
  /profile, and /notifications in protectedRoute
Introduces a shared AuthenticatedRoleLanding component and trims the per-role
landings (Community and Validator) down onto it, removing duplicated signed-in
markup. The journey hero and step-row components and the builder, community,
and validator journey views get UI polish, and the profile header and rankings
widget are adjusted to match. Playwright artifacts are added to .gitignore.

## Implementation Notes
- components/funnel/AuthenticatedRoleLanding.svelte: new shared signed-in role
  landing
- components/funnel/{RoleLanding,BuilderLanding,CommunityLanding,ValidatorLanding}.svelte:
  route the signed-in state through the shared landing, drop duplicated markup
- components/funnel/journeys/{JourneyHeroCard,JourneyStepRow}.svelte: presentation polish
- routes/{BuilderJourney,CommunityJourney,ValidatorWaitlist}.svelte: journey view polish
- components/profile/{ProfileHeader,RankingsWidget}.svelte: profile adjustments
- users/tests/test_builder_journey.py: extend builder journey coverage
- .gitignore: ignore Playwright report/test-results artifacts
RoleFunnel now shows a neutral spinner while the session is still being
verified, instead of deriving the signed-in state too early: a logged-in user
with an existing session no longer briefly sees the signed-out landing flash
before their dashboard loads.

The validator journey start now preflights the waitlist contribution type and
wraps the started marker and the waitlist contribution in a single
transaction, so a misconfigured type or a failed write can no longer leave a
user marked "started" without the single waitlist step.

Also reworded an owner-only profile comment to drop forbidden "badge" wording.

## Implementation Notes
- components/funnel/RoleFunnel.svelte: gate rendering on authState.hasVerified
  so the spinner covers the pending-verification window
- users/views.py: start_validator_journey resolves validator-waitlist before
  marking started and runs both writes inside one transaction.atomic()
- users/serializers.py: in-progress funnel state comment uses "indicator"
…nel-rework

Rework portal onboarding into role funnels and journeys
…ator-modals

Update funnel creator journeys and modals
…nity-task-points

Show community journey task points
* Add user funnel analytics tracking

* Address analytics review feedback

* Address analytics consent and funnel gaps

* Use Tailwind for analytics consent banner
* Add grouped evidence URL requirements for contribution types

Contribution types gain an optional evidence URL group rule: each group
must be satisfied by at least one submitted evidence URL, with AND across
groups and OR within a group, so a type can require a contract code link
and a deployed-contract explorer link together. Grouped types are
implicitly accepted when an accepted-types whitelist is set, matching the
existing required-types behavior. A new GenLayer Explorer Contract evidence
type recognizes Asimov, Bradbury, Studio, and Testnet explorer contract
addresses, and the Studio import contract pattern is tightened to avoid
false matches.

## Implementation Notes
- backend/contributions/models.py: Add required_evidence_url_type_groups JSONField to ContributionType.
- backend/contributions/serializers.py: Enforce the group rule during evidence validation and union grouped types into the accepted whitelist.
- backend/contributions/migrations/0073_contributiontype_required_evidence_url_type_groups.py: Field migration.
- backend/contributions/migrations/0074_intelligent_contracts_evidence_types.py: Seed the GenLayer Explorer Contract type and tighten the studio-contract regex.
- backend/contributions/tests/test_required_evidence_url_type_groups.py: Cover the group rule and whitelist interaction.
- backend/contributions/tests/test_required_evidence_url_types.py: Make setUp robust to migration-seeded URL types.

* Update changelog

* Combine Intelligent Contracts migrations into one

Merge the required_evidence_url_type_groups field add and the evidence URL
type seed into a single migration; the two operations are independent so
they need no separate steps.

## Implementation Notes
- backend/contributions/migrations/0075_intelligent_contracts_evidence_types.py: Single migration with the AddField plus the RunPython seed (explorer type + tightened studio-contract regex).
- backend/contributions/migrations/0076_intelligent_contracts_evidence_types.py: Removed; folded into 0075.
- backend/contributions/tests/test_required_evidence_url_type*.py: Update seed-migration reference in setUp comment.

* Validate grouped evidence URL config and tighten related tests

ContributionType save now rejects a malformed required_evidence_url_type_groups
value (non-list, empty group, or unknown EvidenceURLType slug) instead of
letting it become an impossible submission rule. The whitelist mismatch error
now lists grouped types among the accepted ones, matching the acceptance logic.
The studio-contract test fixture uses the tightened production pattern with a
real import-contract URL so the suite exercises the shipped detection.

## Implementation Notes
- backend/contributions/models.py: Validate required_evidence_url_type_groups shape and slugs in ContributionType.clean().
- backend/contributions/serializers.py: Include grouped type names in the whitelist "Expected" error text.
- backend/contributions/tests/test_required_evidence_url_type_groups.py: Add clean() validation tests for malformed and unknown-slug groups.
- backend/contributions/tests/test_required_evidence_url_types.py: Align the studio-contract fixture and test URL with the tightened regex.
The heatmap now derives its calendar bounds from the backend's declared
activity window instead of the first/last populated rows, so the grid keeps
its full six-month span even if zero-activity edge days are ever omitted. The
view toggle exposes its selected state to assistive tech via aria-pressed, and
the legacy snapshot upconvert path now normalizes the full v5 schema so older
snapshots and fresh payloads share an identical shape.

## Claude Implementation Notes
- backend/api/overview_metrics.py: default latest_week_by_source to {} when upconverting v3/v4 snapshots to v5
- backend/api/tests.py: assert latest_week_by_source is present in the v3-to-v5 normalization test
- frontend/src/components/portal/ActivityHeatmap.svelte: accept activityWindow prop and prefer it for grid start/end, falling back to row bounds
- frontend/src/components/portal/NetworkActivity.svelte: pass activity_window to the heatmap and add aria-pressed to the Graph/Activity tabs
The heatmap grid bounds already followed the backend's declared window, but cell
inclusion still keyed on row presence, so a date missing from inside the window
would blank out and shift month labels. Cell inclusion now derives from the
window range, and absent days render as zero-count cells instead of gaps.

## Claude Implementation Notes
- frontend/src/components/portal/ActivityHeatmap.svelte: compute inRange from the window-derived first/last date range (and past-or-today) rather than Boolean(row); missing rows already default to zero counts
* Add network activity heatmap tab

* Address review feedback on network activity heatmap

The heatmap now derives its calendar bounds from the backend's declared
activity window instead of the first/last populated rows, so the grid keeps
its full six-month span even if zero-activity edge days are ever omitted. The
view toggle exposes its selected state to assistive tech via aria-pressed, and
the legacy snapshot upconvert path now normalizes the full v5 schema so older
snapshots and fresh payloads share an identical shape.

## Claude Implementation Notes
- backend/api/overview_metrics.py: default latest_week_by_source to {} when upconverting v3/v4 snapshots to v5
- backend/api/tests.py: assert latest_week_by_source is present in the v3-to-v5 normalization test
- frontend/src/components/portal/ActivityHeatmap.svelte: accept activityWindow prop and prefer it for grid start/end, falling back to row bounds
- frontend/src/components/portal/NetworkActivity.svelte: pass activity_window to the heatmap and add aria-pressed to the Graph/Activity tabs

* Make declared activity window authoritative for heatmap cells

The heatmap grid bounds already followed the backend's declared window, but cell
inclusion still keyed on row presence, so a date missing from inside the window
would blank out and shift month labels. Cell inclusion now derives from the
window range, and absent days render as zero-count cells instead of gaps.

## Claude Implementation Notes
- frontend/src/components/portal/ActivityHeatmap.svelte: compute inRange from the window-derived first/last date range (and past-or-today) rather than Boolean(row); missing rows already default to zero counts
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0cdab9af-27ff-455c-bf2f-1cdcd1fd0cb1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@JoaquinBN JoaquinBN merged commit a6f0ab6 into main Jun 28, 2026
6 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.

2 participants