Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/hypercerts-api/references/endpoint-map.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ An `unanswered` award is a successful indexed award result, not a not-found resp
| List an actor's followers | `app.certified.graph.listActorFollowers` | `actor` is the followed actor DID. |
| List actors an actor follows | `app.certified.graph.listActorFollowing` | `actor` is the follower DID. |
| Check one actor-to-entity follow | `app.certified.graph.getEntityFollow` | Supply actor DID and entity AT-URI; check its nullable/error contract. |
| List actors following an entity | `app.certified.graph.listEntityFollowers` | `entity` is an entity AT-URI. |
| List actors following an entity | `app.certified.graph.listEntityFollowers` | `entity` is an entity AT-URI; `totalCount` reports distinct relationships before cursor and limit, including on empty pages. |
| List entities an actor follows | `app.certified.graph.listEntityFollowing` | `actor` is an actor DID; targets are hydrated from indexed records. |
| Browse recent raw follow records | `app.certified.graph.listRecentFollows` | Beta feed across publishers; `before` and pagination have distinct feed semantics. Not a per-actor relationship check. |

Expand Down
4 changes: 2 additions & 2 deletions .agents/skills/hypercerts-api/references/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"/xrpc/app.certified.graph.getFollow": {"get":{"operationId":"app_certified_graph_getFollow","tags":["app.certified.graph"],"description":"Returns the representative follow record for an actor-to-actor relationship, or null if none is indexed. No authentication is required.","parameters":[{"name":"actor","in":"query","required":true,"description":"DID of the actor who published the follow record.","schema":{"type":"string","format":"did"}},{"name":"subject","in":"query","required":true,"description":"DID named as the follow record's subject.","schema":{"type":"string","format":"did"}}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app.certified.graph.getFollow.output"}}}}},"x-lexicon-id":"app.certified.graph.getFollow","x-lexicon-errors":[{"name":"InvalidRequest","description":"A DID or query parameter is invalid."}]}},
"/xrpc/app.certified.graph.listActorFollowers": {"get":{"operationId":"app_certified_graph_listActorFollowers","tags":["app.certified.graph"],"description":"Lists actors whose follow records name the requested DID as subject. No authentication is required; duplicate relationships are collapsed before pagination, and only the returned actors are hydrated.","parameters":[{"name":"actor","in":"query","required":true,"description":"DID whose incoming follows are listed.","schema":{"type":"string","format":"did"}},{"name":"sortDirection","in":"query","required":false,"description":"Sorts by representative follow createdAt and URI; defaults to desc.","schema":{"type":"string","enum":["asc","desc"]}},{"name":"limit","in":"query","required":false,"description":"Maximum relationships per page; defaults to 25.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"description":"Opaque next-page cursor bound to sortDirection; keep the other query parameters unchanged between pages.","schema":{"type":"string"}}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app.certified.graph.listActorFollowers.output"}}}}},"x-lexicon-id":"app.certified.graph.listActorFollowers","x-lexicon-errors":[{"name":"InvalidRequest","description":"A DID or query parameter is invalid."}]}},
"/xrpc/app.certified.graph.listActorFollowing": {"get":{"operationId":"app_certified_graph_listActorFollowing","tags":["app.certified.graph"],"description":"Lists actors named as subjects by the requested DID's follow records. No authentication is required; duplicate relationships are collapsed before pagination, and only the returned actors are hydrated.","parameters":[{"name":"actor","in":"query","required":true,"description":"DID whose outgoing follows are listed.","schema":{"type":"string","format":"did"}},{"name":"sortDirection","in":"query","required":false,"description":"Sorts by representative follow createdAt and URI; defaults to desc.","schema":{"type":"string","enum":["asc","desc"]}},{"name":"limit","in":"query","required":false,"description":"Maximum relationships per page; defaults to 25.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"description":"Opaque next-page cursor bound to sortDirection; keep the other query parameters unchanged between pages.","schema":{"type":"string"}}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app.certified.graph.listActorFollowing.output"}}}}},"x-lexicon-id":"app.certified.graph.listActorFollowing","x-lexicon-errors":[{"name":"InvalidRequest","description":"A DID or query parameter is invalid."}]}},
"/xrpc/app.certified.graph.listEntityFollowers": {"get":{"operationId":"app_certified_graph_listEntityFollowers","tags":["app.certified.graph"],"description":"Lists actors following an entity AT-URI. No authentication is required; duplicate actor–entity relationships use the earliest createdAt, with URI ascending on ties. Only the returned page is hydrated: missing profile and organization sidecars are null, while lookup failures return errors rather than partial pages.","parameters":[{"name":"entity","in":"query","required":true,"description":"Full AT-URI of the followed record using a DID authority; any target collection is accepted, and matching is by URI, not CID.","schema":{"type":"string","format":"at-uri"}},{"name":"sortDirection","in":"query","required":false,"description":"Sorts by representative follow createdAt and URI; defaults to desc.","schema":{"type":"string","enum":["asc","desc"]}},{"name":"limit","in":"query","required":false,"description":"Maximum relationships per page; defaults to 25.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"description":"Opaque next-page cursor bound to sortDirection; keep the other query parameters unchanged between pages.","schema":{"type":"string"}}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app.certified.graph.listEntityFollowers.output"}}}}},"x-lexicon-id":"app.certified.graph.listEntityFollowers","x-lexicon-errors":[{"name":"InvalidRequest","description":"The entity AT-URI or another query parameter is invalid."}]}},
"/xrpc/app.certified.graph.listEntityFollowers": {"get":{"operationId":"app_certified_graph_listEntityFollowers","tags":["app.certified.graph"],"description":"Lists actors following an entity AT-URI. No authentication is required; duplicate actor–entity relationships use the earliest createdAt, with URI ascending on ties. totalCount reports distinct relationships before cursor and limit. Only the returned page is hydrated: missing profile and organization sidecars are null, while lookup failures return errors rather than partial pages.","parameters":[{"name":"entity","in":"query","required":true,"description":"Full AT-URI of the followed record using a DID authority; any target collection is accepted, and matching is by URI, not CID.","schema":{"type":"string","format":"at-uri"}},{"name":"sortDirection","in":"query","required":false,"description":"Sorts by representative follow createdAt and URI; defaults to desc.","schema":{"type":"string","enum":["asc","desc"]}},{"name":"limit","in":"query","required":false,"description":"Maximum relationships per page; defaults to 25.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"description":"Opaque next-page cursor bound to sortDirection; keep the other query parameters unchanged between pages.","schema":{"type":"string"}}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app.certified.graph.listEntityFollowers.output"}}}}},"x-lexicon-id":"app.certified.graph.listEntityFollowers","x-lexicon-errors":[{"name":"InvalidRequest","description":"The entity AT-URI or another query parameter is invalid."}]}},
"/xrpc/app.certified.graph.listEntityFollowing": {"get":{"operationId":"app_certified_graph_listEntityFollowing","tags":["app.certified.graph"],"description":"Lists entities followed by an actor DID. No authentication is required; duplicate actor–entity relationships use the earliest createdAt, with URI ascending on ties. Only returned-page targets are hydrated and resolved to the latest indexed record by URI; missing or unsupported targets retain their URI and follow with entity null, while lookup failures return errors rather than partial pages.","parameters":[{"name":"actor","in":"query","required":true,"description":"DID whose outgoing entity follows are listed.","schema":{"type":"string","format":"did"}},{"name":"sortDirection","in":"query","required":false,"description":"Sorts by representative follow createdAt and URI; defaults to desc.","schema":{"type":"string","enum":["asc","desc"]}},{"name":"limit","in":"query","required":false,"description":"Maximum relationships per page; defaults to 25.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"description":"Opaque next-page cursor bound to sortDirection; keep the other query parameters unchanged between pages.","schema":{"type":"string"}}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app.certified.graph.listEntityFollowing.output"}}}}},"x-lexicon-id":"app.certified.graph.listEntityFollowing","x-lexicon-errors":[{"name":"InvalidRequest","description":"The actor DID or another query parameter is invalid."}]}},
"/xrpc/app.certified.graph.listRecentFollows": {"get":{"operationId":"app_certified_graph_listRecentFollows","tags":["app.certified.graph"],"description":"Beta query for raw account- and entity-follow records from any publisher. No authentication is required; duplicates are preserved, and before is an exclusive bound on publisher-declared createdAt. Pages are not a snapshot, so late indexing or concurrent changes may cause records to be missed.","parameters":[{"name":"before","in":"query","required":false,"description":"Exclusive upper bound on publisher-declared createdAt, not indexedAt; omit to start with the newest records.","schema":{"type":"string","format":"datetime"}},{"name":"limit","in":"query","required":false,"description":"Maximum records per page; defaults to 25.","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"description":"Opaque next-page cursor; keep before unchanged between pages.","schema":{"type":"string"}}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app.certified.graph.listRecentFollows.output"}}}}},"x-lexicon-id":"app.certified.graph.listRecentFollows","x-lexicon-errors":[{"name":"InvalidRequest","description":"A datetime, limit, cursor, or query parameter is invalid."}]}},
"/xrpc/app.certified.link.getEvmLink": {"get":{"operationId":"app_certified_link_getEvmLink","tags":["app.certified.link"],"description":"Looks up an indexed EVM-link record by exact AT-URI and hydrates its actor. Authentication is not required.","parameters":[{"name":"uri","in":"query","required":true,"description":"Full AT-URI of an app.certified.link.evm record with a DID authority.","schema":{"type":"string","format":"at-uri"}}],"responses":{"200":{"description":"Successful response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app.certified.link.getEvmLink.output"}}}}},"x-lexicon-id":"app.certified.link.getEvmLink","x-lexicon-errors":[{"name":"InvalidRequest","description":"The URI is invalid or is not an app.certified.link.evm record AT-URI."},{"name":"RecordNotFound","description":"No indexed EVM-link record exists at this AT-URI."}]}},
Expand Down Expand Up @@ -118,7 +118,7 @@
"app.certified.graph.listActorFollowers.output": {"type":"object","required":["followers","totalCount"],"properties":{"followers":{"type":"array","description":"Incoming relationships; each displayed DID identifies the actor who published the follow.","items":{"$ref":"#/components/schemas/app.certified.graph.getFollow.actorFollowView"}},"totalCount":{"type":"integer","description":"Count of deduplicated relationships before cursor and limit are applied.","minimum":0},"cursor":{"type":"string","description":"Next-page cursor; omitted when no more results are available."}}},
"app.certified.graph.listActorFollowing.output": {"type":"object","required":["following","totalCount"],"properties":{"following":{"type":"array","description":"Outgoing relationships; each displayed DID identifies the actor named as the follow subject.","items":{"$ref":"#/components/schemas/app.certified.graph.getFollow.actorFollowView"}},"totalCount":{"type":"integer","description":"Count of deduplicated relationships before cursor and limit are applied.","minimum":0},"cursor":{"type":"string","description":"Next-page cursor; omitted when no more results are available."}}},
"app.certified.graph.listEntityFollowers.entityFollowerView": {"type":"object","description":"The follower actor and the raw entity-follow record representing its relationship.","required":["did","profile","organization","follow"],"properties":{"did":{"type":"string","format":"did"},"profile":{"anyOf":[{"$ref":"#/components/schemas/org.hypercerts.api.defs.profileView"},{"type":"null"}]},"organization":{"anyOf":[{"$ref":"#/components/schemas/org.hypercerts.api.defs.organizationView"},{"type":"null"}]},"follow":{"$ref":"#/components/schemas/app.certified.graph.getEntityFollow.entityFollowRecordView"}}},
"app.certified.graph.listEntityFollowers.output": {"type":"object","required":["followers"],"properties":{"followers":{"type":"array","items":{"$ref":"#/components/schemas/app.certified.graph.listEntityFollowers.entityFollowerView"}},"cursor":{"type":"string","description":"Next-page cursor; omitted when no more results are available."}}},
"app.certified.graph.listEntityFollowers.output": {"type":"object","required":["followers","totalCount"],"properties":{"followers":{"type":"array","items":{"$ref":"#/components/schemas/app.certified.graph.listEntityFollowers.entityFollowerView"}},"totalCount":{"type":"integer","description":"Count of distinct actor–entity relationships after duplicate records are collapsed, before cursor and limit are applied.","minimum":0},"cursor":{"type":"string","description":"Next-page cursor; omitted when no more results are available."}}},
"org.hypercerts.claim.activity.main": {"type":"record","description":"A hypercert record tracking impact work."},
"org.hypercerts.claim.activity.contributorIdentity": {"type":"object","description":"Contributor information as a string (DID or identifier).","required":["identity"],"properties":{"identity":{"type":"string","description":"The contributor identity string (DID or identifier).","maxLength":1000,"x-lexicon-maxGraphemes":100}}},
"com.atproto.repo.strongRef.main": {"type":"object","required":["uri","cid"],"properties":{"uri":{"type":"string","format":"at-uri"},"cid":{"type":"string","format":"cid"}}},
Expand Down
5 changes: 5 additions & 0 deletions .changeset/entity-follower-total-count.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hypercerts-org/hypercerts-api': minor
---

Entity follower results now include `totalCount`, the number of distinct actor–entity relationships before pagination. The count is also returned when a page has no followers.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ The HTTP gate fails if it discovers no suites, executes no `node:test` cases, or
- Context attachment and evaluation retrieval with publisher/evaluator sidecar hydration, list filters, pagination across tied `createdAt` values, and named runtime errors.
- Activity retrieval with contributor-sidecar hydration, author/organization/contributor/URI filters, tied timestamp pagination, and literal wildcard search.
- Collection retrieval with CBOR-derived CIDs, location/tag projections, author, organization, item and tag filters, title/shortDescription search, `createdAt`/URI pagination ties, and source-order item pagination with exact-version resolution.
- Graph actor/entity lookups and lists, tied-key pagination, nullable profile/organization sidecars, entity target resolution, and the global recent-follows `before` filter.
- Graph actor/entity lookups and lists, tied-key pagination, nullable profile/organization sidecars, entity target resolution, entity-follower totals across pages and empty pages, and the global recent-follows `before` filter.
- Feed-reaction subject and actor queries, earliest duplicate selection, URI-only subject matching across CIDs, distinct-actor totals, tied cursor pagination, page-only sidecar hydration, raw `via`/signature preservation, and actor queries without hydration.
- Contribution exact-record retrieval, repeated publisher filters, tied ascending/descending cursor pagination, nullable publisher sidecars, and named errors.
- Badge, badge-query, acknowledgement, contribution, and rights HTTP fixtures with CBOR-derived record CIDs; contribution DIDs are distinct from baseline fixture identities.
Expand Down
5 changes: 3 additions & 2 deletions api/lexicons/app.certified.graph.listEntityFollowers.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"defs": {
"main": {
"type": "query",
"description": "Lists actors following an entity AT-URI. No authentication is required; duplicate actor–entity relationships use the earliest createdAt, with URI ascending on ties. Only the returned page is hydrated: missing profile and organization sidecars are null, while lookup failures return errors rather than partial pages.",
"description": "Lists actors following an entity AT-URI. No authentication is required; duplicate actor–entity relationships use the earliest createdAt, with URI ascending on ties. totalCount reports distinct relationships before cursor and limit. Only the returned page is hydrated: missing profile and organization sidecars are null, while lookup failures return errors rather than partial pages.",
"parameters": {
"type": "params",
"required": ["entity"],
Expand Down Expand Up @@ -32,9 +32,10 @@
},
"output": {
"type": "object",
"required": ["followers"],
"required": ["followers", "totalCount"],
"properties": {
"followers": { "type": "array", "items": { "type": "ref", "ref": "#entityFollowerView" } },
"totalCount": { "type": "integer", "minimum": 0, "description": "Count of distinct actor–entity relationships after duplicate records are collapsed, before cursor and limit are applied." },
"cursor": { "type": "string", "description": "Next-page cursor; omitted when no more results are available." }
}
}
Expand Down
Loading
Loading