[HYPER-536] introduce entityFollow lexicon for non DID follows - #236
Conversation
🦋 Changeset detectedLatest commit: 7b3d44b The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (8)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughAdds ChangesNon-account entity follows
Estimated code review effort: 2 (Simple) | ~15 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 7✅ Passed checks (7 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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.
Actionable comments posted: 4
🤖 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 `@ERD.puml`:
- Around line 209-213: Add the optional signatures field to the ERD dataclass
for app.certified.graph.entityFollow within the SHOW_FIELDS conditional,
alongside subject, via?, and createdAt, so all non-facet fields are rendered.
- Around line 216-223: Remove the non-lexicon entityFollowSubject definition and
its related edge from the ERD. Update entityFollow to represent the followed
subject with a subject union annotation, or replace it with an entity backed by
a matching lexicon JSON; ensure every remaining entity corresponds to a lexicon
definition.
In `@lexicons/app/certified/graph/entityFollow.json`:
- Around line 41-44: Update the recordSubject.uri schema in entityFollow to
require a complete DID-based record AT-URI, including did:<authority>,
collection, and rkey segments. Add AppView/publisher validation and tests
covering handle-only and partial URIs, or explicitly relax the documented
DID-form contract if full validation cannot be enforced.
In `@tests/validate-graph-entity-follow.test.ts`:
- Around line 85-96: Extend the recordSubject validation tests near the existing
missing-uri case with a negative case that supplies a non-AT URI such as an
HTTPS URL. Call validate with the same AppCertifiedGraphEntityFollow schema and
assert result.success is false, preserving the existing missing-uri coverage.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: fb890bef-dffa-41a7-b793-25e037cf8249
📒 Files selected for processing (7)
.agents/skills/building-with-hypercerts-lexicons/SKILL.md.changeset/add-graph-entity-follow.mdERD.pumlREADME.mdSCHEMAS.mdlexicons/app/certified/graph/entityFollow.jsontests/validate-graph-entity-follow.test.ts
The at-uri format constraint cannot enforce DID-form or full record URIs at the schema level, so the recordSubject.uri description now states explicitly what schema validation enforces vs. what writers must supply and consumers must verify. Adds a negative test covering a non-AT URI subject. Co-Authored-By: Rommie Code (Claude Fable 5)
Main introduced namespace permission sets that must enumerate every record collection; without this entry the new collection would not be grantable via the published set. Co-Authored-By: Rommie Code (Claude Fable 5)
aspiers
left a comment
There was a problem hiding this comment.
LGTM as mentioned earlier
We need a social graph for following things that are not accounts — most immediately, individual records. The existing app.certified.graph.follow cannot serve this: its subject is constrained to format: "did", and a strongRef pins a specific version via CID, so the reference would break (or dangle semantically) every time the followed record is updated.
Resolves HYPER-536.
Design
Weak reference by AT-URI, no CID. The subject references a record by its AT-URI (at://did/collection/rkey), which identifies the record's identity rather than one snapshot of its contents. Updates change a record's CID, not its URI, so the follow tracks the living record. This mirrors upstream precedent: app.bsky.graph.follow uses a weak ref (bare DID) for evolving relationships, while app.bsky.feed.like uses a strongRef because a like is about that version of a post. Following is inherently a relationship with something that evolves — the weak form is semantically correct, not a workaround. URIs must use the DID form (not handles, which can change or be reassigned).
New lexicon rather than updating follow. ATProto lexicon evolution rules forbid changing constraints on published fields, and a DID is not a valid at-uri — there is no non-breaking edit to follow that could accept both. Dropping the format constraint would silently weaken validation for every existing consumer (AppView indexes, filters, client hooks), and follow is already published to npm and the ATProto network. A new lexicon is purely additive: minor semver bump, existing records and indexers untouched.
entityFollow with an open-union subject, not recordFollow with a plain string. The lexicon's charter is "follows for anything that isn't an account," so the subject is an open union with a single variant today (#recordSubject). This is a deliberate one-way door: a plain at-uri string could never be evolved into a union later without a breaking change, whereas new union variants (URLs, on-chain addresses, etc.) are purely additive. The small ceremony cost — clients wrap the URI in { "$type": "...#recordSubject", "uri": ... } —
buys permanent extensibility.
DIDs are explicitly out of scope. Account follows belong to app.certified.graph.follow. Keeping the two collections mutually exclusive means the indexer/gql never has to merge two record types to compute a single relationship, and there is exactly one way to follow an account.
tid record key. An AT-URI is not a valid rkey (contains /), and deriving rkeys from subjects can't be enforced by schema validation anyway — the indexer must deduplicate regardless. tid matches every other atproto graph record (follows, likes, reposts), preserves creation-order scans, and would reuse the duplicate-ignoring logic the indexer should already have for follows.
Notes
Summary by CodeRabbit
Summary by CodeRabbit