Conversation
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
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
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.
No description provided.