Repository navigation
epic(lists): audit existing work and define the implementation plan #8198
Description
Owner: Sebastian (@Chardot)
Goal
Turn Lists into a usable, shareable, extensible feature that can support both user needs and editorial/featured-feed programming.
Context
Lists are considered a priority. Earlier BGV designs were approved and described as dev-ready. Sebastian should review those designs against current product needs and existing implementation work before new development begins.
Key product needs
- Create and view lists.
- Add new items to an existing list over time; lists should not remain static.
- Share lists via a reliable public/share link.
- Find lists more easily through improved search and discovery.
- Support collaborative or co-created lists, where appropriate.
- Support public lists as Featured Feed content.
- Make lists usable for editorial programming tied to a content calendar, including scheduled featured placement.
Example: a "Purpose of Divine" list created in May is useful editorial content, but it is currently static and difficult to share. Lists should support ongoing additions and easy promotion.
Investigation required
- Search GitHub for all existing issues, PRs, branches, labels, and references related to: lists, list sharing, public lists, collaborative lists, featured lists, saved lists, collections, list search/discovery, BGV designs.
- Review the existing Lists designs and identify what is already approved and ready to build, what no longer matches current requirements, and what design decisions are missing.
- Review current implementation status across mobile, web, backend, and any relevant Nostr/Funnel Cake data model work.
- Identify duplicate, stale, blocked, or overlapping issues and link them rather than recreating work.
- Update this umbrella issue with a concise inventory of existing work and recommended issue structure.
Deliverables
- A linked inventory of relevant existing issues, PRs, designs, and code paths.
- Clear status for each item: shipped, in progress, blocked, outdated, duplicate, or not started.
- A proposed phased plan:
- V1: core list creation/viewing plus reliable sharing.
- V2: adding to lists, public-list discovery/search, Featured Feed integration.
- V3: collaborative/co-created lists and any advanced permissions/moderation needs.
- Proposed technical approach and dependencies across mobile, web, backend, and relay/data layers.
- UX open questions, including ownership, edit permissions, collaboration, visibility, deletion, and share-link behavior.
- A set of child issues or updates to existing issues, with owners and sequencing.
Acceptance criteria
- We know exactly what Lists work already exists and where.
- Existing work is linked and consolidated rather than duplicated.
- Sebastian has a clear recommended starting point for implementation.
- The plan supports public/editorial lists for Featured Feed scheduling.
- The team can make a prioritization decision based on a defined V1, dependencies, and open questions.
Starting inventory (first pass, 2026-08-26)
This is a research pass to seed the work, not the finished deliverable. The headline finding is that most of V1 and a large share of V2 already exist and ship today — Lists is closer to a consolidation, polish and product-decision problem than a greenfield build.
No open umbrella existed
Both prior epics are closed and neither carries an inventory:
| Issue | State | Note |
|---|---|---|
#2261 epic(lists): lists |
CLOSED 2026-06-29 | Body is a single Figma link (below). One sub-issue only (#3267). |
#602 [Epic] 8. Curated Lists (NIP-51) |
CLOSED 2026-05-15 | Legacy launch epic. Sub-issues #576 List Creation, #577 List Management, #578 Adding Videos, #579 List Viewing, #580 Special Lists, #1823 Collaborator — all closed. |
This issue supersedes both as the live umbrella.
Designs to review
- Lists (from epic(lists): lists #2261): https://www.figma.com/design/rp1DsDEUuCaicW0lk6I2aZ/UI-Design?node-id=11251-236128
- Home/Explore architecture + Editorial Collections (Update Architecture of the Home Feed and apply Editorial Collections #1535, closed): https://www.figma.com/design/rp1DsDEUuCaicW0lk6I2aZ/UI-Design?node-id=5661-31440 — proposed "Editorial" home collections (
For You/New/Following/Subscribed Lists 1..n) and an Explore layout withListsas a first-class tab. Directly relevant to editorial programming; needs re-checking against what shipped.
I could not verify the Figma node contents in this pass — the design-vs-current-requirements review is still open and is Sebastian's first task.
Data model as built
Two distinct kinds are in play and the naming in the codebase blurs them:
- kind 30005 — curated video lists. What "Lists" means in the product sense. Public/private, ordered, collaborator tags.
- kind 30000 — people lists. A separate feature (
mobile/lib/features/people_lists/) sharing theFeatureFlag.curatedListsmaster switch.
Both sit behind FeatureFlag.curatedLists, which defaults ON (mobile/lib/features/feature_flags/services/build_configuration.dart:16). A second flag, FeatureFlag.profileListFeatures, defaults OFF and gates people-list entry points on profiles and search tiles. Worth an early decision on whether that flag still has a reason to exist.
Mobile — what ships today
CuratedListService (mobile/lib/services/curated_list_service.dart, 1847 lines) already implements essentially the full CRUD surface:
| Capability | Method | Status |
|---|---|---|
| Create list | createList |
Shipped |
| Add item over time | addVideoToList |
Shipped — lists are not structurally static |
| Remove item | removeVideoFromList |
Shipped |
| Edit metadata | updateList |
Shipped |
| Delete | deleteOwnedList |
Shipped (see open bugs) |
| Manual ordering | reorderVideos / getOrderedVideoIds |
Shipped |
| Collaborators | addCollaborator / removeCollaborator / canCollaborate |
Shipped |
| Subscribe to others' lists | subscribeToList / unsubscribeFromList |
Shipped |
| Private lists | NIP-44 encrypted items, PR #6661 | Shipped |
| Tags / local search | getListsByTag / searchLists |
Shipped |
Other code paths:
mobile/lib/services/curated_list_relay_gateway.dart(700) — NIP-44 seal/unseal, NIP-09 redactionmobile/packages/curated_list_repository/— converter + relay discovery/searchmobile/packages/models/lib/src/curated_list.dart— modelmobile/lib/screens/curated_list_feed_screen.dart,curated_list_by_author_screen.dart,discover_lists_screen.dartmobile/lib/widgets/add_to_list_dialog.dart— add-to-list entry point, reachable from the video share action, profile list grid and the Explore Lists tabmobile/lib/screens/search_results/widgets/lists_section.dart— Lists results in searchmobile/lib/router/route_paths.dart—/list/{listId},/list/{pubkey}/{listId},/discover-lists,/people-lists/new
So the "Purpose of Divine list is static" complaint is not a missing capability. Adding items works. The likely causes are discoverability of the add-to-list entry point, or the list not being owned/public in the shape the sharing path requires. Worth reproducing against that specific list before scoping any build.
Sharing — the real V1 gap
curated_list_feed_screen.dart:415-485. The share action is gated on isOwned && isPublic && pubkey != null:
- A viewer looking at someone else's public list gets no share action at all. Only the owner can share.
- A public list whose local record has no author pubkey silently loses the menu entry.
- The URL is hand-built as
'https://divine.video' + /list/{pubkey}/{listId}at the call site rather than through a shared share-link builder.
Deep links themselves are in place — mobile/docs/DEEP_LINK_URL_REFERENCE.md:248-266,282-283,362 documents both /list/{listId} and /list/{pubkey}/{listId}, and divine-web#482 added /list/* to the Apple app-site-association. The /list/{listId} (no pubkey) form resolves only against local data, which is why it is not a durable share target. "Reliable public/share link" is mostly a matter of always using the authored form and opening the action to non-owners.
Discovery and search — relay-only
DiscoverListsScreen and CuratedListRepository.searchAllLists query relays directly. Funnel Cake is used by the list repository only for thumbnail resolution (curated_list_repository.dart:396), and there is no Funnel Cake list search/discovery endpoint — POST /api/videos/bulk with from_event {kind: 30005, d_tag} resolves a known list's videos, nothing more (mobile/docs/FUNNELCAKE_API_REFERENCE.md:187). This is the structural reason list discovery is slow and thin, and the main backend ask for V2.
Open mobile issues
Protocol / correctness:
- fix(lists): kind 30005 writes prose into .content, which NIP-51 reserves for private items #7588 — kind 30005 writes prose into
.content, which NIP-51 reserves for private items (protocol correctness; likely a V1 gate) - fix(lists): relay sync still imports private+collaborative lists outside the merge path, bricking edits #8057 — relay sync still imports private+collaborative lists outside the merge path, bricking edits
- fix(lists): serialize relay sync with list mutations #7207 — serialize relay sync with list mutations (deferred from PR feat(lists): publish private lists to relays with NIP-44 encrypted items #6661)
- fix(lists): deleteOwnedList unserialized, can race with item mutations #7725 —
deleteOwnedListunserialized, can race with item mutations - fix(lists): deleteOwnedList docstring contradicts implementation #7726 —
deleteOwnedListdocstring contradicts implementation - refactor(lists): remove dead deleteList method #7727 — remove dead
deleteListmethod - test(lists): cover the mid-backfill owner change guarding stale-key republish #8052 — test coverage for mid-backfill owner change
Discovery / UX:
- fix(discover): Discover Lists shows infinite loading spinner on revisit within app session #7805 — Discover Lists shows an infinite loading spinner on revisit within a session (directly blocks "find lists more easily")
- refactor(lists): curated-list feed pushes duplicate the route and disagree on the extra #6749 — curated-list feed pushes duplicate the route and disagree on the extra
Structure:
- refactor(lists): split curated_list_service.dart by concern #4514 — split
curated_list_service.dartby concern. Note this file is baselined in the service god-file ceiling (mobile/scripts/baseline/service_god_file_sizes.txt); any Lists work must shrink or hold it, never raise it. This is a hard dependency on new feature work, not optional cleanup. - refactor(structure): co-locate features using the people_lists pattern #4745 — co-locate features using the
people_listspattern
People lists (kind 30000):
- fix(people-lists): surface batch-add failures with user-visible recovery #3265 surface batch-add failures · docs(people-lists): document legacy local-data loss or add migration #3266 document legacy local-data loss · fix(people-lists): bound deletion tombstone growth in local cache #3269 bound deletion tombstone growth · fix(people-lists): track per-mutation delete outcomes #6696 per-mutation delete outcomes · fix(people-lists): screen reports failed delete when pending mutation fails #7715 screen reports failed delete on pending-mutation failure
Adjacent feature already scoped on kind 30005:
- feat(video): publish and watch ordered video series #8105 — feat: publish and watch ordered video series. Full implementation plan merged in PR docs: trim video series v1 design #8055 /
docs/superpowers/specs/2026-08-22-video-series-design.md. AddsisSeriesto the list model and aseriesa-tag on videos. Not started. Overlaps this umbrella's model and list-write boundary — sequencing must be agreed, not discovered mid-build.
Recently shipped mobile PRs (context for "what changed since the design")
#6394 preserve addressable video tags · #6476 flag toggle no longer pops the screen · #6482 keep app-shell bloc on a live repository · #6502 stop background sync when the flag turns off · #6513 delete dialog fatal on a dead route · #6617 make owned lists visible and shareable · #6661 private lists via NIP-44 · #6729 notify when videos are added to public lists · #6760 show creator on subscribed lists · #6766 addressable list-add notifications · #6861 dismiss editor before relay ack · #7097 refresh discover list subscriptions · #7564 bound public list relay reads · #8034 stop a deleted list returning on next sync · #8054 keep private lists private during relay sync.
Also closed and worth reading for prior product decisions: #7724 (private→public downgrade on collaborative conflict), #7728 (owner captured once during backfill), and the two release-decision issues #8048 / #8049.
Web — divine-web
Substantially built, and further along on people lists than mobile in some respects:
- OPEN [TC-SETTINGS-003] Settings screen organization #505 —
feat(lists): integrate people lists into discovery, search, profile, and sidebar. The last item of a deliberate PR stack; explicitly "do not start until the lower-level PRs have landed." This is the natural web counterpart to this umbrella — coordinate, do not duplicate. - Shipped: [TC-MOD-004] Mute user #501 kind-30000 types/parser/hooks · [TC-MOD-006] Block user #503 detail members/videos · [TC-MOD-007] Unblock user #504 CRUD dialogs · [Feature] 1.2 Key Import (nsec) #549 protocol mutations · [Feature] 3.6 Video Upload #558 detail controls · [TC-MOD-005] Unmute user #502 unified list detail routing for 30000 and 30005 · [TC-SETTINGS-022] Default language detection #514/[TC-SETTINGS-025] Date and time formatting #518 public profile list discovery · [Feature] 3.1 Camera Access & Permissions #553/[Feature] 8.4 List Viewing #579 relay hints on detail routes · [Epic] 10. Content Moderation #604/[Epic] 11. Settings & Configuration #605 e-tag list membership (video lists were showing 0 videos) · [Epic] 12. Offline & Sync #606/[Epic] 13. Performance & Optimization #607 relay dedup truncating list results · feat: Add semantic label to items in the video playback screen #657 make video lists easier to identify and revisit · [TC-LIST-007] Add video to list #482
/list/*deep links in AASA · [Feature] 1.5 Keycast #550 configurable featured tab - Closed as overlap: [Feature] 1.7 Name Service & Reserved Name Management #552
chore(lists): reconcile people-list PR overlap before #505, [TC-OFFLINE-009] Warning before logout with uploading videos #545, [TC-PERF-005] Feed loading speed #546 — prior art for exactly the consolidation this umbrella is asking for. - Web canonicalises list paths as
buildListPath(pubkey, listId). Mobile builds its share URL inline. Getting those to agree is a concrete, small, cross-surface V1 task.
Backend — Funnel Cake / featured feeds
This is the biggest surprise in the inventory: the Featured-Feed-from-public-list path is already built server-side.
- divine-funnelcake#910 —
feat(featured-feeds): support account list hashtag and mixed sourcesis the canonical backend tracking issue. Per its 2026-08-17 status, shipped: source definitions and admin CRUD (feat: scrollable view for video feed #911), source-only config enablement (fix: replace GIF loading indicator with sprite sheet animation #912), public-list source member resolution (feat: video feed from repo #913 — accepts kind30005only), composed source feeds (refactor: remove app video event model #935), admin preview (Account / Deletion | P0 | Cannot delete email/password account because key is required but not generated #937), source-awarehas_content/gate diagnostics (feat: implement nip50 in video repo #944), display-onlypill_label(epic(reliability): general app stability #1013), refresh health monitoring (feat(action-sheet): add VineBottomSheetActionMenu component with action items #1014), operator attribution (fix: Log in | P2 | Likes/follows/reposts do not sync across two devices for same account #936). - Clients consume a server-composed feed by opaque featured-tab config id and are deliberately blind to whether the source is accounts, hashtags, public lists, manual curation, or a mix. Mobile shell shipped in feat(explore): configurable featured hashtag tab #6858, web in divine-web#550.
- Scheduling already exists.
FeaturedTabConfig(mobile/packages/funnelcake_api_client/lib/src/models/featured_tab_config.dart) carriesstartsAt/endsAt/enabled/visibleToMinors. So "scheduled featured placement" for editorial programming is largely a matter of using this, not building it. - Remaining backend/ops gates named in feat: user information repository #910: ratify the default that public-list resolution accepts kind 30005 only; the mobile cache-TTL-vs-poll-cadence question (both 5 min, so a failed poll can drop a cached tab exactly at TTL); admin-dashboard#24 (
pill_labelpreservation) and 📊 [Camera Spike] Performance Benchmarking Framework #20 (persist the admin pubkey allowlist). - Open backend follow-ups: feat: add comprehensive FunnelCake API diagnostics #1029 remove deprecated
position· [Feature] 5.4.1 Follow/Unfollow (NIP-02) #978 shuffle/randomized ordering · fix: "Save clips to library" only saves last clip #1079 per-tab display glyph · refactor: repost toggle and complete reposted grid #1038 gate state has no reason for a closed window/disabled tab/stale snapshot · fix: Repost feature is not working and videos do not appear in Profile #933 generic featured-tab source members table. - Admin dashboard: 🚀 [DevOps] Setup CI/CD Pipeline for Automated Deployment #11 (closed) operator surface · 🎥 [Camera Spike] Frame Capture Approach Analysis #19 (closed) rebuilt setup page · OPEN Implement FFmpeg video frame extraction #37 make custom video ordering reliable · 📱 [Mobile] Replace Local GIF Generation with Cloudinary Upload #27 news-hack checkbox · 📊 [Camera Spike] Performance Benchmarking Framework #20 allowlist.
- Unrelated to featured feeds but relevant to any list-count surface: divine-funnelcake#867 (in-network follow graph unions every kind-30000 set, so blocked accounts count as follows).
No content-calendar issue exists anywhere in the org. An org-wide search for "content calendar" returns nothing. If editorial programming needs a calendar view rather than per-tab start/end dates, that is genuinely not started and needs its own scope.
Stale branches to triage or delete
origin/create-people-list, origin/feat/curated-list-repo-discovery, origin/feat/reliable-nostr-publish-pr5-lists, origin/fix/videoGridAndLists. No open Lists PRs on mobile or web.
Proposed phasing (for Sebastian to confirm or replace)
V1 — core creation/viewing plus reliable sharing. Mostly consolidation, not construction.
- Reproduce the "Purpose of Divine" list against the current build and establish whether the complaint is capability, discoverability, or list state.
- Open the share action to non-owners on public lists; always emit the authored
/list/{pubkey}/{listId}form; move URL construction behind one builder shared with web'sbuildListPath. - fix(lists): kind 30005 writes prose into .content, which NIP-51 reserves for private items #7588 (NIP-51
.contentmisuse) — decide whether this gates V1. It is a protocol-correctness bug on the public share target. - fix(discover): Discover Lists shows infinite loading spinner on revisit within app session #7805 (Discover Lists infinite spinner).
- Decide the fate of
FeatureFlag.profileListFeatures(OFF today). - refactor(lists): split curated_list_service.dart by concern #4514 shrink
curated_list_service.dart— a prerequisite, since the god-file ceiling blocks growth.
V2 — adding to lists, discovery/search, Featured Feed.
- Confirm add-to-list entry points are reachable where users expect them.
- The real work: a Funnel Cake list discovery/search read model, so discovery stops being relay-only.
- Featured Feed integration is mostly already available — the task is exercising feat: video feed from repo #913's public-list source end-to-end from the admin dashboard and closing feat: user information repository #910's remaining gates, plus the cache-TTL/poll-cadence issue.
- Decide sequencing against feat(video): publish and watch ordered video series #8105 (video series), which touches the same model and write boundary.
V3 — collaboration, permissions, moderation.
- Collaborator plumbing exists but is where the recent bug cluster lives (fix(lists): relay sync still imports private+collaborative lists outside the merge path, bricking edits #8057, fix(lists): relay merge silently downgrades a private list to public on collaborative conflict #7724, fix(lists): serialize relay sync with list mutations #7207, fix(lists): deleteOwnedList unserialized, can race with item mutations #7725). Harden before promoting.
- Moderation of public lists that reach the Featured Feed is not scoped anywhere yet.
Open questions
- Ownership and edit rights on a collaborative list — who can add, remove, reorder, rename, delete? Today
canCollaborateis a tag check with no per-action granularity. - Share-link behavior for non-owners — should any viewer be able to share a public list? What does a private-list share attempt do?
- Editorial lists: whose account owns them? A Divine editorial list published from a staff npub behaves like any user list, including deletion. Is that acceptable for programmed featured content?
- Deletion semantics — what happens to a list that is currently live in a Featured Feed when its owner deletes it? Backend caches a composed snapshot.
- Does editorial programming need a calendar, or are per-tab
startsAt/endsAtenough? Nothing exists for the former. - kind 30000 vs 30005 — do people lists and video lists stay one feature behind one flag, or split?
- Moderation of public lists surfaced editorially — a public list can contain anything; the Featured Feed carries Divine's voice.
- Web/mobile parity target — web is ahead on people lists, mobile ahead on private lists. Which is the reference implementation?
Inventory compiled 2026-08-26. States verified against GitHub at that time; re-check before relying on any status here.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsIn progress
Activity
[-]Lists: audit existing work and define implementation plan[/-][+]epic(lists): audit existing work and define the implementation plan[/+]6 remaining items
NotThatKindOfDrLiz commentedon Sep 24, 2026
Two things from recent meetings.
Direction (11 Sep): build list search first, and add the other access modes later. Please confirm this as the V1 ordering in the phasing above.
Two new questions for V1