Skip to content

feat: PermissionsUser.events#2007

Merged
shrugs merged 2 commits intomainfrom
feat/permissions-user-events
Apr 28, 2026
Merged

feat: PermissionsUser.events#2007
shrugs merged 2 commits intomainfrom
feat/permissions-user-events

Conversation

@shrugs
Copy link
Copy Markdown
Member

@shrugs shrugs commented Apr 28, 2026

Reviewer Focus (Read This First)

Most of the surface area is mechanical mirrors of the existing Permission.events pattern. spend time on:

  1. apps/ensindexer/src/plugins/ensv2/handlers/ensv2/EnhancedAccessControl.ts — the new ensurePermissionsUserEvent call sits alongside the existing ensurePermissionsEvent. confirm it should fire on every EACRolesChanged (including the zero-roles branch where the permissionsUser row is deleted but the event log is preserved).
  2. apps/ensapi/src/omnigraph-api/schema/permissions.ts — the new PermissionsUser.events field. it intentionally keeps the full EventsWhereInput arg for parity with Permission.events, even though the join is already maximally narrow.
  3. integration test assertion in permissions.integration.test.ts — i replaced an initial wrong "disjoint sum" assertion with a topic-based (resource, user) scoping check after seeing it fail.

Problem & Motivation

closes #1963.

portal's /$name/roles/ and /resolver/$address/roles/ views want per-role-assignment history: which events granted/revoked/modified this specific role entry for this user. today the closest omnigraph query is Permission.events(where: { selector }) filtered client-side by account + resource. this collapses that into a server-side join.


What Changed (Concrete)

  1. new permissions_user_events join table in packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts (keyed on (permissionsUserId, eventId))
  2. new ensurePermissionsUserEvent(context, permissionsUserId, eventId) helper in apps/ensindexer/src/lib/ensv2/event-db-helpers.ts
  3. EACRolesChanged handler writes to both permissionsEvent and permissionsUserEvent join tables
  4. extended EventJoinTable union in find-events-resolver.ts
  5. new PermissionsUser.events(where: EventsWhereInput, ...) graphql field, mirror of Permission.events
  6. integration tests covering scope, topic-based (resource, user) validation, and selector_in filtering
  7. regenerated enssdk/src/omnigraph/generated/{schema.graphql,introspection.ts}
  8. AGENTS.md: new "Ponder" section documenting schema-rebuild + PK-cache constraints (restored after merge)
  9. root vitest.integration.config.ts: exclude .git/.claude/node_modules from the projects glob (was loading stale worktree configs)
  10. changeset (minor: ensdb-sdk, ensindexer, ensapi)

Design & Planning

straightforward parallel of Permission.events and its permissionsEvent join table

Self-Review

  • bugs caught: integration test originally asserted sum(per-user-events) == permission.events.count for root users. wrong — Permission.events covers all resources, while permissions.root.users[].events only covers root-resource users. replaced with a topic-based check that each event's topics[1] equals user.resource and topics[2] equals padded user.user.address.
  • logic simplified: n/a
  • naming / terminology improved: n/a
  • dead or unnecessary code removed: n/a

Cross-Codebase Alignment

  • search terms used: permissionsEvent, permissions_events, makePermissionsUserId, ensurePermissionsEvent, Permission.events, EventJoinTable
  • reviewed but unchanged: Permission.events field/resolver (left as-is — both fields coexist), Account.permissions, Domain.permissions, Resolver.permissions (existing connections, untouched)
  • deferred alignment: none

Downstream & Consumer Impact

  • public apis affected: new PermissionsUser.events graphql field. additive — no breaking changes. enssdk/src/omnigraph/generated/schema.graphql regenerated.
  • docs updated: AGENTS.md ponder constraints. no consumer-facing docs needed beyond the field description.
  • naming decisions worth calling out: field is events (matches Permission.events), join table is permissions_user_events (matches permissions_events).

consumers per the issue:

  • apps/portal/ /$name/roles/
  • apps/portal/ /resolver/$address/roles/

Testing Evidence

  • testing performed: pnpm test:integration apps/ensapi/src/omnigraph-api/schema/permissions.integration.test.ts → 27/27 pass. pnpm -F ensdb-sdk typecheck, pnpm -F ensindexer typecheck, pnpm -F ensapi typecheck, pnpm lint, pnpm generate:gqlschema all clean.
  • known gaps: no parallel pagination block for PermissionsUser.events (the existing Permission.events pagination block exercises the shared resolveFindEvents code path).
  • what reviewers have to reason about manually: that the indexer handler correctly populates the new join table on every EACRolesChanged regardless of the roles=0 delete branch.

Scope Reductions

  • follow-ups: parallel PermissionsUser.events pagination test using testEventPagination if reviewers want it. would need a top-level by-id query for PermissionsUser or awkward traversal.
  • why deferred: resolveFindEvents is shared and already covered by Permission.events pagination. adding a clone there is low-value churn.

Risk Analysis

  • risk areas: none
  • mitigations or rollback options: just revert
  • named owner if this causes problems: @shrugs

Pre-Review Checklist (Blocking)

  • I reviewed every line of this diff and understand it end-to-end
  • I'm prepared to defend this PR line-by-line in review
  • I'm comfortable being the on-call owner for this change
  • Relevant changesets are included (or explicitly not required)

Copilot AI review requested due to automatic review settings April 28, 2026 18:37
@shrugs shrugs requested a review from a team as a code owner April 28, 2026 18:37
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 28, 2026

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

Project Deployment Actions Updated (UTC)
admin.ensnode.io Ready Ready Preview, Comment Apr 28, 2026 6:48pm
ensnode.io Ready Ready Preview, Comment Apr 28, 2026 6:48pm
ensrainbow.io Ready Ready Preview, Comment Apr 28, 2026 6:48pm

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 28, 2026

🦋 Changeset detected

Latest commit: 9f49073

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 24 packages
Name Type
ensapi Major
ensindexer Major
ensadmin Major
ensrainbow Major
fallback-ensapi Major
enssdk Major
enscli Major
enskit Major
ensskills Major
@ensnode/datasources Major
@ensnode/ensrainbow-sdk Major
@ensnode/ensdb-sdk Major
@ensnode/ensnode-react Major
@ensnode/ensnode-sdk Major
@ensnode/integration-test-env Major
@ensnode/ponder-sdk Major
@ensnode/ponder-subgraph Major
@ensnode/shared-configs Major
@docs/ensnode Major
@docs/ensrainbow Major
@namehash/ens-referrals Major
@namehash/namehash-ui Major
@ensnode/ensindexer-perf-testing Major
@ensnode/enskit-react-example Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 28, 2026

📝 Walkthrough

Walkthrough

This PR adds a new PermissionsUser.events GraphQL field exposing event history for specific role assignments. It introduces a permissions_user_events join table to track event relationships at the user level, updates the indexer handler to populate this table when roles change, and extends the event resolver to support querying through it.

Changes

Cohort / File(s) Summary
Changeset & Documentation
.changeset/permissions-user-events.md, AGENTS.md
Adds feature announcement for PermissionsUser.events and documents Ponder indexing constraints (no migrations, re-index on schema/handler changes, primary-key-only entity access).
Database Schema
packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts
Introduces new permissions_user_events join table with composite primary key (permissionsUserId, eventId) to associate events with specific user-role assignments.
Indexer Infrastructure
apps/ensindexer/src/lib/ensv2/event-db-helpers.ts, apps/ensindexer/src/plugins/ensv2/handlers/ensv2/EnhancedAccessControl.ts
Adds ensurePermissionsUserEvent helper to insert user-event associations and updates EACRolesChanged handler to call it alongside existing permissions-event recording.
GraphQL API & Resolver
apps/ensapi/src/omnigraph-api/schema/permissions.ts, apps/ensapi/src/omnigraph-api/lib/find-events/find-events-resolver.ts
Adds PermissionsUser.events connection field and extends EventJoinTable type to include permissionsUserEvent for resolver filtering support.
Integration Tests
apps/ensapi/src/omnigraph-api/schema/permissions.integration.test.ts
Adds comprehensive test suite verifying event scoping to specific users, topic indexing correctness, and filtering via selector_in.
Configuration
vitest.integration.config.ts
Updates test discovery glob to explicitly exclude .git, .claude, and node_modules directories from project scanning.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 Hopping through events, hop by hop,
Each user's history, we'll never stop!
Roles that grant and roles that revoke,
In join tables now—no more smoke!
Per-role tales, bright and clear, 🌟
Portal's wishes, finally here!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Title check ✅ Passed The title 'feat: PermissionsUser.events' directly summarizes the main change—adding an events field to the PermissionsUser GraphQL type.
Linked Issues check ✅ Passed The PR implements all coding requirements from issue #1963: adds PermissionsUser.events field, creates the permissions_user_events join table, implements the indexer helper, updates the handler, adds integration tests, and exposes the new GraphQL field for clients.
Out of Scope Changes check ✅ Passed All changes are directly related to adding the PermissionsUser.events feature. AGENTS.md and vitest.integration.config.ts are supporting changes (documentation and test configuration) that facilitate the feature without introducing unrelated scope.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering all required sections with detailed context, design rationale, and testing evidence.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/permissions-user-events

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 and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds per-role-assignment event history to the Omnigraph by wiring a new permissions_user_events join table into the indexer write-path and exposing it as PermissionsUser.events, mirroring the existing Permission.events pattern.

Changes:

  • Introduces permissions_user_events join table + ensurePermissionsUserEvent() helper and writes it on every EACRolesChanged.
  • Extends Omnigraph to expose PermissionsUser.events(where: EventsWhereInput, ...) via the shared resolveFindEvents resolver.
  • Adds integration tests for scoping and filtering; updates Vitest integration workspace globbing and regenerates Omnigraph schema artifacts.

Reviewed changes

Copilot reviewed 9 out of 11 changed files in this pull request and generated no comments.

Show a summary per file
File Description
vitest.integration.config.ts Excludes .git / .claude / node_modules from integration-project globbing to avoid loading stale worktree configs.
packages/enssdk/src/omnigraph/generated/schema.graphql Regenerated schema exposing PermissionsUser.events + connection types.
packages/enssdk/src/omnigraph/generated/introspection.ts Regenerated introspection matching the schema change.
packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts Adds permissions_user_events join table keyed by (permissionsUserId, eventId).
apps/ensindexer/src/plugins/ensv2/handlers/ensv2/EnhancedAccessControl.ts On EACRolesChanged, writes both permissions_events and permissions_user_events join rows (including the zero-roles delete branch).
apps/ensindexer/src/lib/ensv2/event-db-helpers.ts Adds ensurePermissionsUserEvent() insert helper with conflict-do-nothing semantics.
apps/ensapi/src/omnigraph-api/schema/permissions.ts Adds PermissionsUser.events connection, scoped through permissionsUserEvent join table, with EventsWhereInput parity.
apps/ensapi/src/omnigraph-api/schema/permissions.integration.test.ts Adds integration coverage for scoping and selector_in filtering for PermissionsUser.events.
apps/ensapi/src/omnigraph-api/lib/find-events/find-events-resolver.ts Extends EventJoinTable union to include permissionsUserEvent.
AGENTS.md Documents Ponder expectations around rebuilds and PK-only access constraints.
.changeset/permissions-user-events.md Changeset for the additive Omnigraph/API + schema/indexer behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/ensapi/src/omnigraph-api/schema/permissions.integration.test.ts`:
- Around line 534-593: Add an integration test in
permissions.integration.test.ts that exercises the zero-roles deletion path:
create a new "grant" then a "revoke" (so PermissionsUser.roles becomes 0)
against V2_ETH_REGISTRY and then run the PermissionsUserEvents query to assert
the EACRolesChanged event for that revoke exists (use
EAC_ROLES_CHANGED_SELECTOR) and that the join/write side-effects for the event
were performed even though the PermissionsUser was removed (validate by checking
the event topics include the resource and padded user address and that the
revoke event is present while the PermissionsUser no longer has roles). Ensure
the new test references PermissionsUserEvents, V2_ETH_REGISTRY,
EAC_ROLES_CHANGED_SELECTOR, PermissionsUser and the roles == 0 branch so it
directly covers the grant→revoke flow.
🪄 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

Run ID: 531b89a6-1b25-4734-8313-5df6fbb5c836

📥 Commits

Reviewing files that changed from the base of the PR and between 6173160 and d1377cc.

⛔ Files ignored due to path filters (2)
  • packages/enssdk/src/omnigraph/generated/introspection.ts is excluded by !**/generated/**
  • packages/enssdk/src/omnigraph/generated/schema.graphql is excluded by !**/generated/**
📒 Files selected for processing (9)
  • .changeset/permissions-user-events.md
  • AGENTS.md
  • apps/ensapi/src/omnigraph-api/lib/find-events/find-events-resolver.ts
  • apps/ensapi/src/omnigraph-api/schema/permissions.integration.test.ts
  • apps/ensapi/src/omnigraph-api/schema/permissions.ts
  • apps/ensindexer/src/lib/ensv2/event-db-helpers.ts
  • apps/ensindexer/src/plugins/ensv2/handlers/ensv2/EnhancedAccessControl.ts
  • packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts
  • vitest.integration.config.ts

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 28, 2026

Greptile Summary

This PR adds PermissionsUser.events to the Omnigraph API, exposing per-role-assignment event history for a specific (contract, resource, user) tuple. It follows the existing Permission.events pattern exactly: a new permissions_user_events join table, an ensurePermissionsUserEvent helper called on every EACRolesChanged (including the zero-roles delete branch), and a shared resolveFindEvents resolver, with regenerated GraphQL schema and introspection artifacts.

Confidence Score: 5/5

Safe to merge — additive write path and new GraphQL field with no breaking changes.

All changes are additive and follow established patterns exactly. The handler correctly writes the join-table row on both branches of the zero-roles conditional. The previously flagged topic/resource type mismatch in the test is resolved via BigInt() conversion. No P0 or P1 findings.

No files require special attention.

Important Files Changed

Filename Overview
apps/ensindexer/src/plugins/ensv2/handlers/ensv2/EnhancedAccessControl.ts Correctly adds ensurePermissionsUserEvent call after both the upsert and delete branches; permissionsUserId is determined before the conditional, so the join-table row is written regardless of whether the permissionsUser row itself survives.
packages/ensdb-sdk/src/ensindexer-abstract/ensv2.schema.ts New permissionsUserEvent join table follows the same onchainTable + composite-PK pattern as permissionsEvent/domainEvent/resolverEvent. No FK constraint to permissionsUser, consistent with peers, enabling history preservation after row deletion.
apps/ensapi/src/omnigraph-api/schema/permissions.ts New PermissionsUser.events connection field wires resolveFindEvents through permissionsUserEvent scoped to parent.id; retains the full EventsWhereInput arg for API parity.
apps/ensapi/src/omnigraph-api/schema/permissions.integration.test.ts New PermissionsUser.events describe block covers scope, (resource, user) topic validation, selector_in filtering, and empty-filter edge case. The BigInt() conversion in the resource comparison correctly handles the hex-topic-vs-decimal-scalar mismatch flagged in a prior review.
.changeset/permissions-user-events.md Changeset only bumps ensapi; PR description claims minor bumps for ensdb-sdk and ensindexer as well — may be an oversight if those are published packages.

Sequence Diagram

sequenceDiagram
    participant Chain as Blockchain
    participant Handler as EACRolesChanged Handler
    participant DB as ensDb (Ponder)
    participant API as Omnigraph API

    Chain->>Handler: EACRolesChanged(resource, account, oldBitmap, newBitmap)
    Handler->>DB: ensureAccount(user)
    Handler->>DB: ensurePermissionsResource(contract, resource)
    alt newRoleBitmap == 0
        Handler->>DB: delete permissionsUser (id = permissionsUserId)
    else newRoleBitmap != 0
        Handler->>DB: upsert permissionsUser (id = permissionsUserId)
    end
    Handler->>DB: ensureEvent → eventId
    Handler->>DB: ensurePermissionsEvent(contract, eventId)
    Handler->>DB: ensurePermissionsUserEvent(permissionsUserId, eventId)
    Note over DB: permissions_user_events row persists even after permissionsUser deletion
    API->>DB: PermissionsUser.events query
    DB-->>API: JOIN permissionsUserEvent ON eventId WHERE permissionsUserId = parent.id
    API-->>API: apply EventsWhereInput filters
Loading

Reviews (3): Last reviewed commit: "feat: PermissionsUser.events" | Re-trigger Greptile

Comment thread apps/ensapi/src/omnigraph-api/schema/permissions.integration.test.ts Outdated
shrugs added 2 commits April 28, 2026 13:47
- AGENTS.md: document ponder schema-rebuild + PK-cache constraints
- vitest.integration.config.ts: exclude .git/.claude/node_modules from projects glob to avoid loading worktree configs
Exposes per-(contract, resource, user) event history on PermissionsUser,
collapsing the prior client-side pattern of filtering Permission.events
by account.

Closes #1963.
@shrugs
Copy link
Copy Markdown
Member Author

shrugs commented Apr 28, 2026

@greptile review

@shrugs
Copy link
Copy Markdown
Member Author

shrugs commented Apr 28, 2026

merging as highly confident in the implementation and to prioritize omnigraph feature velocity for this week

@shrugs shrugs merged commit e40ce8f into main Apr 28, 2026
21 checks passed
@shrugs shrugs deleted the feat/permissions-user-events branch April 28, 2026 19:39
@github-actions github-actions Bot mentioned this pull request Apr 28, 2026
shrugs added a commit that referenced this pull request Apr 29, 2026
- events: add sender column row, update from description to flag never
  HCA-aware, add sender to indexes list
- permissions_user_events: document join table (was missing from initial
  ENSDb docs in #2007)
- domains.ownerId: polymorphic ENSv1 effective-owner / ENSv2 on-chain
  owner (HCA-aware) wording
- registrations.registrantId / unregistrantId: ENSv2 HCA-aware note
- permissions_users.user: HCA-aware grantee description

Per #2014 (comment) review.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

Omnigraph: PermissionsUser.events (per-role event history)

2 participants