Skip to content

Add grouped evidence URL requirements for contribution types#844

Merged
JoaquinBN merged 5 commits into
devfrom
JoaquinBN/intelligent-contracts-contribution-type
Jun 28, 2026
Merged

Add grouped evidence URL requirements for contribution types#844
JoaquinBN merged 5 commits into
devfrom
JoaquinBN/intelligent-contracts-contribution-type

Conversation

@JoaquinBN

@JoaquinBN JoaquinBN commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator

Adds an optional evidence URL group rule to contribution types: each group must be satisfied by at least one submitted evidence URL (AND across groups, OR within a group), letting a type require both a contract code link and a deployed-contract explorer link in the same submission. Grouped types are implicitly accepted when an accepted-types whitelist is set, matching the existing required-types behavior. Introduces a GenLayer Explorer Contract evidence type covering the Asimov, Bradbury, Studio, and Testnet explorers, and tightens the Studio import contract pattern to avoid false matches.

Summary by CodeRabbit

  • New Features

    • Added support for grouped evidence URL requirements on contribution types.
    • Submissions can now satisfy multiple evidence requirements together, including combinations like code and explorer links.
    • A new evidence URL category is recognized for GenLayer explorer contract addresses.
  • Bug Fixes

    • Improved evidence link validation so required link combinations are enforced more consistently.
    • Tightened matching for certain contract links to reduce false positives.

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.
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@JoaquinBN, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 33 minutes. Learn how PR review limits work.

To continue reviewing without waiting, enable usage-based billing in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f9fb6032-13f6-45ce-93bd-1070b8d3e9cb

📥 Commits

Reviewing files that changed from the base of the PR and between dc11afa and 252efe4.

📒 Files selected for processing (4)
  • backend/contributions/models.py
  • backend/contributions/serializers.py
  • backend/contributions/tests/test_required_evidence_url_type_groups.py
  • backend/contributions/tests/test_required_evidence_url_types.py
📝 Walkthrough

Walkthrough

Adds a required_evidence_url_type_groups JSON field to ContributionType with AND-across-groups/OR-within-group semantics. A migration seeds a new GenLayer Explorer evidence URL type and tightens the studio-contract URL regex. SubmittedContributionSerializer._validate_evidence_items is updated to include grouped types in the accepted whitelist and to enforce per-group presence in submissions.

Changes

Required Evidence URL Type Groups

Layer / File(s) Summary
ContributionType field and migration
backend/contributions/models.py, backend/contributions/migrations/0075_intelligent_contracts_evidence_types.py
Adds required_evidence_url_type_groups JSONField to ContributionType. Migration defines EXPLORER evidence URL type constants, tightens studio-contract regex, upserts the Explorer type, and wires both the field addition and data migration.
Serializer group enforcement
backend/contributions/serializers.py
Expands accepted_types to union in IDs resolved from required_evidence_url_type_groups slugs, then adds a new validation pass that fails if any required group has no matching detected URL-type slug in the submission.
Tests and fixture updates
backend/contributions/tests/test_required_evidence_url_type_groups.py, backend/contributions/tests/test_required_evidence_url_types.py
New RequiredEvidenceURLTypeGroupsTest class covers rejection of missing paired/grouped evidence, acceptance of valid pairs, empty-group disabling, and whitelist interaction. Existing test setUp switches to update_or_create keyed by slug with ownership_social_account='' defaults.
Changelog
CHANGELOG.md
Adds Unreleased bullet describing grouped evidence URL requirements and the new Explorer evidence type.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.08% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: grouped evidence URL requirements for contribution types.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch JoaquinBN/intelligent-contracts-contribution-type

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.

…contracts-contribution-type

# Conflicts:
#	CHANGELOG.md
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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/contributions/models.py`:
- Around line 161-170: The JSONField for grouped slugs on
required_evidence_url_type_groups currently allows arbitrary JSON, but
SubmittedContributionSerializer._validate_evidence_items() assumes a list of
string lists; add model-level validation on the Contribution model (or a
dedicated clean/validator for required_evidence_url_type_groups) to reject
non-list, non-nested-list, or non-string entries before save. Make the
validation target the required_evidence_url_type_groups field itself so invalid
values like a flat list or single string are blocked when the contribution type
is saved, instead of reaching serializer submission checks.

In `@backend/contributions/serializers.py`:
- Around line 708-720: The whitelist mismatch message in serializers.py is still
missing grouped evidence URL types even though accepted_types now includes them.
Update the error-text construction in the same validation flow that uses
accepted_qs, required_type_ids, and group_type_ids so the “Expected” list
reflects all valid types, including required_evidence_url_type_groups, not just
explicit accepted types plus required_evidence_url_types. Keep the change local
to the whitelist validation path so the user-facing error matches the actual
acceptance logic.

In `@backend/contributions/tests/test_required_evidence_url_types.py`:
- Around line 59-67: The `studio-contract` test fixture is still using the old
`EvidenceURLType.objects.update_or_create` pattern, so it does not reflect the
tightened production matching. Update the `studio_type` setup in
`test_required_evidence_url_types.py` to use the same `url_patterns` behavior as
migration `0075_intelligent_contracts_evidence_types.py`, and change the studio
test URL input in this module to a real `contracts?import-contract=0x...` URL.
Make sure the assertions in this test class continue to exercise
`EvidenceURLType` detection through the updated `url_patterns` and `studio_type`
fixture.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a384a36c-3a67-4711-ad8f-1171af471025

📥 Commits

Reviewing files that changed from the base of the PR and between 61d2cea and dc11afa.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • backend/contributions/migrations/0075_intelligent_contracts_evidence_types.py
  • backend/contributions/models.py
  • backend/contributions/serializers.py
  • backend/contributions/tests/test_required_evidence_url_type_groups.py
  • backend/contributions/tests/test_required_evidence_url_types.py

Comment thread backend/contributions/models.py
Comment thread backend/contributions/serializers.py
Comment thread backend/contributions/tests/test_required_evidence_url_types.py
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.
@JoaquinBN JoaquinBN merged commit a1c788b into dev Jun 28, 2026
3 checks passed
@JoaquinBN JoaquinBN deleted the JoaquinBN/intelligent-contracts-contribution-type branch June 28, 2026 16:37
JoaquinBN added a commit that referenced this pull request Jun 28, 2026
* Rework portal onboarding into role funnels and journeys

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

* Update changelog

* Dedup journey/link endpoints and trim funnel landing dead code

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.

* Address PR-837 review: harden funnel idempotency, privacy, and copy

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().

* Address PR-837 review round 2: route gates, multiplier races, best-effort 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.

* Apply role journeys to newcomers, grandfather existing members

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

* Gate builder claim on GitHub+star; protect personal routes

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

* Consolidate funnel role landings and polish journey UI

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

* Fix funnel signed-out flash and validator start atomicity

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"

* Update funnel creator journeys and modals

* Address funnel PR review feedback

* Update route OG previews (#840)

* Update unauthenticated landing pages (#841)

* Fix community leaderboard search test fixture

* Update journey onboarding views (#842)

* Show community journey task points

* Add user funnel analytics tracking (#838)

* Add user funnel analytics tracking

* Address analytics review feedback

* Address analytics consent and funnel gaps

* Use Tailwind for analytics consent banner

* Add network activity heatmap tab

* Add grouped evidence URL requirements for contribution types (#844)

* 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.

* 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

* Add social task eligibility gates (#846)
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