feat: add hat stewards dashboard#33
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
Next review available in: 38 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 reviews. How do review 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 refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR adds a Hat Stewards feature: a database table and migration for role-safe mappings, server-only libraries for querying the Hats and Superfluid subgraphs, server actions for saving/archiving/restoring mappings, an admin form UI, and a new ChangesHat Stewards feature
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant HatStewardsPage
participant RoleSafesLib as role-safes.ts
participant HatsLib as hats.ts
participant StreamsLib as streams.ts
participant DB
User->>HatStewardsPage: GET /hat-stewards
HatStewardsPage->>HatStewardsPage: check session canAccess
HatStewardsPage->>HatsLib: listHatOptions() (admin only)
HatsLib-->>HatStewardsPage: HatOptionsResult
HatStewardsPage->>RoleSafesLib: listHatStewardRoleSafes()
RoleSafesLib->>DB: query hatStewardRoleSafes
DB-->>RoleSafesLib: rows
RoleSafesLib-->>HatStewardsPage: HatStewardRoleSafe[]
HatStewardsPage->>StreamsLib: listIncomingStreamsByReceiver(safeAddresses)
StreamsLib-->>HatStewardsPage: streams map
HatStewardsPage->>StreamsLib: getStreamRunwayForStreams(streams)
StreamsLib-->>HatStewardsPage: runway result
HatStewardsPage-->>User: render metric cards + RoleStreamCard list
sequenceDiagram
participant Admin
participant AdminForm as hat-steward-admin-form.tsx
participant Actions as actions.ts
participant RoleSafesLib as role-safes.ts
participant DB
Admin->>AdminForm: submit "Save mapping" form
AdminForm->>Actions: saveHatStewardRoleSafeWithState(formData)
Actions->>Actions: requireAdminSession()
Actions->>Actions: getHatFromForm() / normalize safe address
Actions->>DB: update or insert (onConflictDoUpdate)
DB-->>Actions: mapping saved
Actions->>DB: write audit event
Actions->>Actions: revalidatePath + redirect
Actions-->>AdminForm: HatStewardRoleSafeFormState (on error)
AdminForm-->>Admin: show updated list or validation error
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ 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 |
There was a problem hiding this comment.
Pull request overview
Adds a new “Hat Stewards” dashboard area to the member/admin UI, backed by a new DB table for mapping Hats Protocol roles to Safe addresses and enriched with Superfluid stream/runway data.
Changes:
- Introduces
/hat-stewardspage with member-visible stream summaries and an admin-only mapping management UI. - Adds Hats + Superfluid subgraph integration helpers (hat option listing + stream/runway queries).
- Adds database schema + migration for
hat_steward_role_safes, plus documentation/env updates and nav/e2e coverage.
Reviewed changes
Copilot reviewed 13 out of 14 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/e2e/responsive-dashboard.spec.ts | Adds responsive dashboard route coverage for the new Hat Stewards page (member + admin markers). |
| src/lib/hat-stewards/streams.ts | Fetches incoming Superfluid streams and computes per-quarter projections + runway estimates. |
| src/lib/hat-stewards/role-safes.ts | DB read helpers + Safe address normalization and Safe URL generation for role-to-safe mappings. |
| src/lib/hat-stewards/hats.ts | Fetches Hats options from the Hats subgraph and resolves labels (including IPFS metadata). |
| src/db/schema.ts | Adds hat_steward_role_safes table and indexes to the Drizzle schema. |
| src/components/app-header.tsx | Adds “Hat Stewards” to the DAO navigation links. |
| src/app/hat-stewards/page.tsx | Implements the Hat Stewards dashboard page (metrics, stream list, admin section). |
| src/app/hat-stewards/hat-steward-admin-form.tsx | Client-side admin modal/form to add/archive/restore role-safe mappings. |
| src/app/hat-stewards/actions.ts | Server actions for save/archive/restore with session gating, cache revalidation, and audit logging. |
| README.md | Documents the Hat Stewards feature and related environment variables/integrations. |
| drizzle/meta/0020_snapshot.json | Updates Drizzle snapshot metadata to include the new table. |
| drizzle/meta/_journal.json | Registers the new migration in the Drizzle journal. |
| drizzle/0020_medical_mysterio.sql | Adds SQL migration creating hat_steward_role_safes + indexes. |
| .env.example | Adds Hats and Superfluid env vars used by the new dashboard/admin picker. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (7)
src/app/hat-stewards/hat-steward-admin-form.tsx (3)
172-191: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueNo confirmation before archiving a mapping.
The archive button submits immediately with no confirmation step, so a misclick archives an active role-Safe mapping. Restore exists as a mitigation, but a lightweight confirm (e.g.
window.confirmor a small inline confirm state) would reduce accidental archives.🤖 Prompt for 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. In `@src/app/hat-stewards/hat-steward-admin-form.tsx` around lines 172 - 191, The archive action in HatStewardAdminForm submits immediately, which can accidentally archive an active role-Safe mapping. Update the form/button flow around the archive/restore controls in HatStewardAdminForm so that the archive path requires a lightweight confirmation (for example, a confirm prompt or inline confirm state) before invoking archiveHatStewardRoleSafe, while keeping restoreHatStewardRoleSafe unchanged.
106-109: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueNo pending/disabled state on submit while the Server Action is in flight.
useActionStatereturns a thirdisPendingvalue that isn't destructured here, so the "Save mapping" button stays clickable during submission, risking duplicate submits on slow connections.🤖 Prompt for 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. In `@src/app/hat-stewards/hat-steward-admin-form.tsx` around lines 106 - 109, The submit flow in hat-steward-admin-form is missing the pending state from useActionState, so the Save mapping button never reflects an in-flight Server Action. Update the destructuring around useActionState in the component to capture the third isPending value, then wire that flag into the submit button so it becomes disabled while saveHatStewardRoleSafeWithState is running and prevents duplicate submits.
110-113: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win
activeMappingsincludes archived mappings, mislabeling hats as "(mapped)".
activeMappingsis built from everyroleSafeinroleSafes, including archived ones (Boolean(roleSafe.archivedAt)per Line 154). Since re-selecting an archived hat is valid (onConflictDoUpdateinsaveHatStewardRoleSafeclearsarchivedAt), tagging it as "(mapped)" in the selector may discourage admins from re-adding an archived role, even though doing so is the correct restore path.🐛 Suggested fix
const activeMappings = new Set( - roleSafes.flatMap((roleSafe) => [roleSafe.hatId, roleSafe.hatPrettyId]), + roleSafes + .filter((roleSafe) => !roleSafe.archivedAt) + .flatMap((roleSafe) => [roleSafe.hatId, roleSafe.hatPrettyId]), );Also applies to: 223-229
🤖 Prompt for 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. In `@src/app/hat-stewards/hat-steward-admin-form.tsx` around lines 110 - 113, The `activeMappings` set in `hat-steward-admin-form.tsx` is currently built from every entry in `roleSafes`, which causes archived roles to be treated as still mapped and shown as “(mapped)” in the selector. Update the logic around `activeMappings` (and the related selector rendering in the hat steward form) to exclude archived records by checking `roleSafe.archivedAt`, so only non-archived `roleSafes` contribute to the mapped label while archived hats remain re-selectable for restoration via `saveHatStewardRoleSafe`.src/app/hat-stewards/actions.ts (3)
109-116: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winAudit
actionis always"update"regardless of actual operation.Create, update, archive, and restore all log
action: "update", only distinguished by the free-textsummary. This weakens any downstream filtering/reporting onauditEvents.action. Consider using distinct action values (e.g.create,archive,restore) ifwriteAuditEvent's contract allows it.Also applies to: 141-148, 197-204, 229-236
🤖 Prompt for 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. In `@src/app/hat-stewards/actions.ts` around lines 109 - 116, The audit events in the hat steward actions are all being written with action set to "update", which prevents reliable filtering by operation. Update the calls to writeAuditEvent in the create, update, archive, and restore flows within actions.ts so each operation uses a distinct action value that matches the actual behavior, while keeping the existing summary and metadata fields intact.
178-208: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winFetch-then-update pattern in archive/restore adds an unnecessary round trip and TOCTOU window.
getHatStewardRoleSafe(id)is fetched only to validate existence and grabhatPrettyIdfor the audit event, then a separateupdateis issued. Between the two calls another request could mutate the row, and this doubles DB round trips. Using.returning()on the update and checking for an empty result would remove both the extra query and the race window.Also applies to: 210-240
🤖 Prompt for 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. In `@src/app/hat-stewards/actions.ts` around lines 178 - 208, The archive flow in archiveHatStewardRoleSafe does a separate getHatStewardRoleSafe lookup before updating, which adds an extra DB round trip and a TOCTOU window. Change the update on hatStewardRoleSafes to use .returning() so you can confirm the row existed and retrieve hatPrettyId from the updated result, then use that returned data for writeAuditEvent and keep the redirect/revalidate behavior unchanged.
88-153: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value
saveHatStewardRoleSafe'sidupdate branch is dead in the current UI. The add-mapping form never postsid; the hiddenidinput in this component is for archive/restore, not this action. If an edit flow isn’t planned soon, split or remove the branch to avoid carrying unused code.🤖 Prompt for 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. In `@src/app/hat-stewards/actions.ts` around lines 88 - 153, The `saveHatStewardRoleSafe` action still contains an `id`-based update branch, but the current UI never submits `id` for this flow, so that path is effectively dead. Update `saveHatStewardRoleSafe` to either remove the unused `id` branch or split the edit behavior into a separate action if it is meant to support future editing, and keep the add/upsert path focused on the `safeAddress`/`hat` save behavior. Verify the form payloads used by this action so the code matches the actual add-mapping UI rather than the archive/restore hidden `id` field.src/app/hat-stewards/page.tsx (1)
250-261: 🚀 Performance & Scalability | 🔵 TrivialConsider caching subgraph queries.
listHatOptions,listIncomingStreamsByReceiver, andgetStreamRunwayForStreamsall run as uncached, sequential network calls on every page request. Under load, this could add material latency and hammer the Hats/Superfluid subgraphs. Consider revalidation/caching (e.g.,unstable_cache,revalidatetags, or short TTL memoization) for these reads if not already handled inside the respective lib functions.🤖 Prompt for 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. In `@src/app/hat-stewards/page.tsx` around lines 250 - 261, The page data flow in hat-stewards is making repeated uncached subgraph reads through listHatOptions, listIncomingStreamsByReceiver, and getStreamRunwayForStreams. Add caching or revalidation around these read paths, ideally near the page-level orchestration that fetches hatOptions, roleSafes, streamsResult, and runwayResult, using a short TTL, revalidation tags, or unstable_cache if the underlying helpers do not already cache internally.
🤖 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 `@src/app/hat-stewards/actions.ts`:
- Around line 96-107: The update branch in saveHatStewardRoleSafeWithState
currently bypasses the hat_steward_role_safes_hat_id_unique protection and can
throw on reassignment. Mirror the insert path’s conflict handling for the
id-based update in the block using db.update(hatStewardRoleSafes), and make the
catch logic around saveHatStewardRoleSafeWithState treat unique-violation errors
as a friendly validation response instead of an unhandled crash.
In `@src/app/hat-stewards/hat-steward-admin-form.tsx`:
- Around line 26-69: ModalShell renders a dialog overlay but lacks keyboard
accessibility controls, so add focus management and close behavior to the
ModalShell component. Update the ModalShell implementation to move focus into
the dialog when it opens, trap Tab navigation within the dialog contents,
restore focus to the triggering element on close, and close the modal on Escape
using a keydown listener; if you prefer, switch to the native dialog pattern
with showModal() and inert siblings. Use the ModalShell, Link close control, and
role="dialog" wrapper as the main places to wire this behavior.
---
Nitpick comments:
In `@src/app/hat-stewards/actions.ts`:
- Around line 109-116: The audit events in the hat steward actions are all being
written with action set to "update", which prevents reliable filtering by
operation. Update the calls to writeAuditEvent in the create, update, archive,
and restore flows within actions.ts so each operation uses a distinct action
value that matches the actual behavior, while keeping the existing summary and
metadata fields intact.
- Around line 178-208: The archive flow in archiveHatStewardRoleSafe does a
separate getHatStewardRoleSafe lookup before updating, which adds an extra DB
round trip and a TOCTOU window. Change the update on hatStewardRoleSafes to use
.returning() so you can confirm the row existed and retrieve hatPrettyId from
the updated result, then use that returned data for writeAuditEvent and keep the
redirect/revalidate behavior unchanged.
- Around line 88-153: The `saveHatStewardRoleSafe` action still contains an
`id`-based update branch, but the current UI never submits `id` for this flow,
so that path is effectively dead. Update `saveHatStewardRoleSafe` to either
remove the unused `id` branch or split the edit behavior into a separate action
if it is meant to support future editing, and keep the add/upsert path focused
on the `safeAddress`/`hat` save behavior. Verify the form payloads used by this
action so the code matches the actual add-mapping UI rather than the
archive/restore hidden `id` field.
In `@src/app/hat-stewards/hat-steward-admin-form.tsx`:
- Around line 172-191: The archive action in HatStewardAdminForm submits
immediately, which can accidentally archive an active role-Safe mapping. Update
the form/button flow around the archive/restore controls in HatStewardAdminForm
so that the archive path requires a lightweight confirmation (for example, a
confirm prompt or inline confirm state) before invoking
archiveHatStewardRoleSafe, while keeping restoreHatStewardRoleSafe unchanged.
- Around line 106-109: The submit flow in hat-steward-admin-form is missing the
pending state from useActionState, so the Save mapping button never reflects an
in-flight Server Action. Update the destructuring around useActionState in the
component to capture the third isPending value, then wire that flag into the
submit button so it becomes disabled while saveHatStewardRoleSafeWithState is
running and prevents duplicate submits.
- Around line 110-113: The `activeMappings` set in `hat-steward-admin-form.tsx`
is currently built from every entry in `roleSafes`, which causes archived roles
to be treated as still mapped and shown as “(mapped)” in the selector. Update
the logic around `activeMappings` (and the related selector rendering in the hat
steward form) to exclude archived records by checking `roleSafe.archivedAt`, so
only non-archived `roleSafes` contribute to the mapped label while archived hats
remain re-selectable for restoration via `saveHatStewardRoleSafe`.
In `@src/app/hat-stewards/page.tsx`:
- Around line 250-261: The page data flow in hat-stewards is making repeated
uncached subgraph reads through listHatOptions, listIncomingStreamsByReceiver,
and getStreamRunwayForStreams. Add caching or revalidation around these read
paths, ideally near the page-level orchestration that fetches hatOptions,
roleSafes, streamsResult, and runwayResult, using a short TTL, revalidation
tags, or unstable_cache if the underlying helpers do not already cache
internally.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: edc3ef02-0726-41e6-962e-ccd082f4445f
📒 Files selected for processing (14)
.env.exampleREADME.mddrizzle/0020_medical_mysterio.sqldrizzle/meta/0020_snapshot.jsondrizzle/meta/_journal.jsonsrc/app/hat-stewards/actions.tssrc/app/hat-stewards/hat-steward-admin-form.tsxsrc/app/hat-stewards/page.tsxsrc/components/app-header.tsxsrc/db/schema.tssrc/lib/hat-stewards/hats.tssrc/lib/hat-stewards/role-safes.tssrc/lib/hat-stewards/streams.tstests/e2e/responsive-dashboard.spec.ts
This pull request introduces a new admin feature for managing "Hat Steward" role-to-Safe mappings, including database schema, server actions, and UI components. It also updates documentation and environment configuration to support this functionality and related integrations.
Hat Steward Role Safe Mapping Feature:
hat_steward_role_safesto store mappings between Hats Protocol roles and Safe addresses, with unique and archival indexing for efficient management.src/app/hat-stewards/actions.tsfor creating, updating, archiving, and restoring Hat Steward role Safe mappings, including admin session checks and audit event logging.src/app/hat-stewards/hat-steward-admin-form.tsxfor managing these mappings, including modal UI for adding, archiving, and restoring mappings, with validation and search/filter functionality.Configuration and Documentation Updates:
.env.examplefor Hats Protocol and Superfluid subgraph integration, supporting the new admin UI and data sources. [1] [2]README.mdto document the new Hat Steward mapping feature, environment variables, and integration points for Hats Protocol and Superfluid. [1] [2]Database Migration Tracking:
drizzle/meta/_journal.jsonto ensure the schema changes are tracked and applied.Summary by CodeRabbit
New Features
Bug Fixes