~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain-android

1 collaborator
0
1

A sign-in with no Bluesky profile makes a bare grain profile

Merged
Merged chadtmiller.com asked to merge into feature/rust-calls from grain.social/grain-android:profile/seed-on-sign-in

What changes

Every account that signs in ends up with a grain profile. ProfileSeed, which runs in the background after MainViewModel.handleOAuthCallback, already made a grain profile from the Bluesky one. It now follows grain-serve's whole rule (crates/grain-serve/src/login/profile.rs on grain's login/bare-profile, pull at://did:plc:bcgltzqazw5tb6k2g3ttenbj/dev.knotless.pull/3mzcciru6kc2b), read from the account's PDS with com.atproto.repo.getRecord:

  1. social.grain.actor.profile/self with a non-empty displayName: nothing is written (Named).
  2. Otherwise, with an app.bsky.actor.profile/self: the grain profile takes its non-empty displayName and description and its avatar blob as it is, and the existing createdAt or now. An existing one is putRecord with swapRecord = its CID; else one is createRecord at self (unchanged).
  3. With no Bluesky profile and no grain profile: a bare {"$type": "social.grain.actor.profile", "createdAt": now} is created at self (Made). This is new: before, nothing was written.
  4. With no Bluesky profile and an unnamed grain profile: it is left as it is (NoBluesky).

The write goes to the PDS and is indexed through social.grain.sync.indexRecords, as every write is. The sign-in never waits on it and a failure is only logged.

Why

grain-serve indexes an account only once it has a social.grain.actor.profile (or a group declaration). It writes one at web login, but the app signs in at the account's own PDS, so grain-serve never sees an app sign-in. An account with no Bluesky profile, a non-Bluesky PDS account say, signed in on the app and was never tracked.

Why stacked on feature/rust-calls, not on main. Main's sign-in is OAuth against grain's own server (/oauth/par, /oauth/authorize), which grain-serve does not serve; there, hatk's login hook writes the profile itself. Sign-in at the account's PDS exists only on feature/rust-auth (pull Grain signs in at the account's own PDS, as an atproto OAuth client), and the profile seed this extends exists only on feature/rust-calls (pull The app calls grain-serve by its own method names), hooked into that sign-in. A second seed on main would hook a sign-in that does not reach grain-serve and would duplicate this one, so the rule is folded into the existing one and lands with the sign-in that needs it.

How it was checked

ProfileSeedTest (Robolectric, stub PDS) covers the four cases: a named profile left alone; made at self from Bluesky and put over on its CID keeping createdAt; a bare one made at self with exactly $type and createdAt, and indexed, when there is neither profile; an unnamed grain profile left with no Bluesky profile. The avatar-as-is test is unchanged. Left to the test workflow on the runner; nothing was built on this machine.

Shortcomings

  • Not tried on a device or emulator, or against a real PDS or grain-serve: no sign-in was run.
  • A PDS that answers a missing record with something other than RecordNotFound makes the seed fail (logged) rather than write.

🤖 Generated with Claude Code

chadtmiller.com added 1 commit
Approved

This makes ProfileSeed follow grain-serve's whole rule. With neither a Bluesky nor a grain profile, it now creates a bare {$type, createdAt} record at self. With a Bluesky profile it behaves as before. With an unnamed grain profile and no Bluesky profile it returns NoBluesky and leaves the record alone. I traced the new branch against fromBluesky: an empty Bluesky object gives exactly $type and createdAt, and the grain == null case goes to createRecord with rkey=self. The tests cover the new bare-create case and the left-alone case. I found nothing that needs to change. One nit: the added KDoc line in ProfileSeed.kt ("Both are read from the account's PDS, which has them before the index may; the") runs well past the other lines, so rewrap it if you touch the file again.

Approved

Since my last review the only change is that the long KDoc line in ProfileSeed.kt is rewrapped, which was the nit I raised. The logic and tests are unchanged from what I approved. I have nothing further.

Merged as 625b1c3