social.grain lexicons for every method the Rust server will serve
MergedThis pull adds social.grain.* copies of the 47 unspecced methods. It also adds the dev.hatk replacements, sync.indexRecords and 16 admin methods, and a README mapping old names to new. It removes nothing, and it re-points two web imports to the new generated type names.
The full diff is about 400k characters, and I couldn't read it all. I read AGENTS.md, sync/indexRecords.json and notification/registerPush.json, and listed the lexicon tree. Both files are well-formed. The array minLength/maxLength and the declared errors are valid, and the shapes match what the description says. I didn't check the other new lexicons or the web import changes.
The check, test and rust checks were still running or queued when I looked. Please confirm they pass, especially the hatk generate types result and the two aliased web imports, before merging.
The naming questions are yours to settle, so I have no objection to the current choices. Once the checks pass, nothing here blocks the merge.
This is the same pull as round 2 of the review: social.grain.* copies of the unspecced methods, the dev.hatk replacements, sync.indexRecords, the admin methods, and a README mapping old names to new. It removes nothing. My earlier review asked you to confirm that check, test and rust pass, especially hatk generate types and the two aliased web imports. I can't confirm that yet: all three checks are still queued on commit 11c0ba9. I found nothing wrong in the parts I read. I didn't re-read the full diff, which is too large to cover. The naming questions are yours to settle. Please don't merge until the three checks are green.
- social.grain lexicons for every method the Rust server will serve
- main

What changes
Renamed methods. Each of the 47
social.grain.unspecced.*methods gets a copy under a named namespace (actor,graph,gallery,comment,story,notification,privateGallery,group,group.pool,server). Parameters, input and output keep the same shape, so porting a caller is a rename. The one exception isgetNotifications: itscountOnlybecomessocial.grain.notification.getUnreadCount.Defs a method declared for itself (
getMutes#muteItem,getPrivateGallery#privateItem, ...) move to its namespace'sdefs.json. So does the one inline output object:searchActorsTypeahead's actor, which becomessocial.grain.actor.defs#typeaheadItem. Existing views are reused rather than copied.Names are verb and noun throughout, as Bluesky names its methods, for example:
group.createGroup,group.getGroup,group.listGroups,group.joinGroupgroup.pool.getGallery,group.pool.listFeedprivateGallery.createPrivateGallery,privateGallery.listSharedPrivateGalleries,privateGallery.listMembers,privateGallery.getSpaceSupportactor.searchActors,story.getAuthorsReplacements for
dev.hatk.social.grain.feed.getFeedtakes the feed'sat-uri, asapp.bsky.feed.getFeeddoes, and keeps hatk's filters.social.grain.feed.describeFeedsreturns{did, feeds: [feedView{uri, name, label}]}, likedescribeFeedGenerator.social.grain.actor.getPreferences/putPreferencesare shaped like Bluesky's: a typed union (pinnedFeedsPref,photoMetadataPref,themePref,notificationsPref), replaced as a whole.social.grain.notification.updateSeenreplaces thelastSeenNotificationspreference, andgetUnreadCountcounts from it. Both are named after Bluesky's.social.grain.notification.registerPush/unregisterPushare shaped like Bluesky's:serviceDid,token,platform(ios/android/web) andappId.New methods.
social.grain.sync.indexRecords: called with service auth, takes{uris}(1 to 25), returns{indexed, notFound}. Its errors areRepoMismatchandUnknownCollection.social.grain.admin.*: one method for each hatk admin endpoint:listReports,resolveReport,getReviewQueue,getReviewSubject,actOnReview,startScan,getReviewStatsapplyLabel,negateLabel,listLabelDefinitionsaddRepos,removeRepos,resyncRepos,getRepo,listRepos(itsqcovers hatk's account search)getInfo,searchRecordsTakedowns use
com.atproto.admin.updateSubjectStatus.lexicons/README.mdmaps every old method to its new one. Nothing is removed: hatk serves theunspeccedanddev.hatkmethods on this branch until cutover.Why
The Rust server serves
social.grain.*only. Once every method has a home, the web app and the apps can be ported to the new names one call at a time.Records and methods under the same name.
social.grain.groupandsocial.grain.privateGalleryare space declarations, and their names are also prefixes of methods:social.grain.group.createGroup,social.grain.privateGallery.getPrivateGallery. Nothing forbids this. An NSID's authority is everything before its last segment, so:_lexicon.grain.social;_lexicon.group.grain.social;_lexicon.pool.group.grain.social.On disk the files sit side by side (
group.jsonbesidegroup/), asgroup/defs.jsonand the group records already do.hatk and method lexicons without handlers. hatk tolerates them. It loads and validates every lexicon and never asks for a handler. Its view discovery is scoped to each namespace, and none of the new
*Viewdefs matches a record in its namespace.The one catch is codegen. hatk adds a prefix to a type's name only when two lexicons share an NSID's last segment, or two defs share a name. So some old generated names gained prefixes. Two web imports used them, and they now import the new names aliased to the old ones:
StoryAuthornow importsGetStoryAuthorsStoryAuthor;GetKnownFollowersFollowerItemnow importsGrainUnspeccedGetKnownFollowersFollowerItem.How it was checked
goat lex lint lexicons/reports no errors in any new file. Its remaining warnings are either in files that already existed or about fields namedidortype.social.grain.*resolves.npx hatk generate types, every$hatk/$hatk/clientimport inserver/,web/src/,test/andseeds/resolves against the regenerated exports.npx vp fmt --checkis clean.check,testandrustrun on the runner.Shortcomings
_lexiconTXT record, whichpublish-lexiconsmakes when it has the Cloudflare token. The new authorities, each under.grain.social:feed,sync,admin,server,notification,privateGallery,pool.group,story,commentandgraph.labels/reset,labels/rescanandwhoamiadmin endpoints have no counterpart.listNotificationsstill returnsunseenCount, to keep its port a rename.Review
Settled by the owner:
social.grain.group.pool;actor.searchActors;notification.getUnreadCountsplit fromlistNotifications;privateGallery.listMembers;ios/android/web;indexRecordscap of 25;story.getAuthors;suggestedItembecomesactor.defs#suggestedFollow, andgetKnownFollowers#followerItembecomesgraph.defs#knownFollowerItem.Left as they are, on purpose:
at-uri. The suggested form isat://<grain service DID>/social.grain.feed.generator/<name>.Still open: whether
listNotificationsshould dropunseenCountnow thatgetUnreadCountexists. Bluesky's returnsseenAtinstead.🤖 Generated with Claude Code