diff --git a/.changeset/document-location-sidecar-pattern.md b/.changeset/document-location-sidecar-pattern.md new file mode 100644 index 00000000..3879dcc1 --- /dev/null +++ b/.changeset/document-location-sidecar-pattern.md @@ -0,0 +1,5 @@ +--- +"@hypercerts-org/lexicon": patch +--- + +Document ATProto sidecar pattern for collections using app.certified.location. Collections can now have location metadata by creating a location record with the same TID, allowing location updates without changing the collection CID. Updated README with usage example and ERD with sidecar relationship. diff --git a/ERD.puml b/ERD.puml index 93d24604..885616a6 100644 --- a/ERD.puml +++ b/ERD.puml @@ -178,6 +178,8 @@ dataclass rights { } ' org.hypercerts.claim.collection +' Note: Collections can use app.certified.location as a sidecar +' by creating a location record with the same TID dataclass collection { !if (SHOW_FIELDS == "true") type @@ -284,7 +286,10 @@ measurement --> location !endif collection::activities --> activity -collection::location --> location +collection ..|> location : "sidecar\n(same TID)" + +project::activities --> activity +project::location --> location activity::contributions -l--> contribution activity::rights --> rights diff --git a/README.md b/README.md index 410c7654..3d976c7e 100644 --- a/README.md +++ b/README.md @@ -405,23 +405,123 @@ Hypercerts-specific lexicons for tracking impact work and claims. **Lexicon ID:** `org.hypercerts.claim.collection` -**Description:** A group of hypercerts that have a specific property. Can be a project or a collection. +**Description:** A collection/group of items (activities and/or other collections). Collections support recursive nesting. Use `org.hypercerts.claim.collection.project` as a sidecar (same TID) for project-specific metadata. **Key:** `tid` #### Properties -| Property | Type | Required | Description | Comments | -| ------------------ | -------- | -------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------- | -| `type` | `string` | ✅ | The type of this group. Can be 'project', 'collection', or custom string | Known values: `project`, `collection` | -| `title` | `string` | ✅ | The title of this collection | | -| `shortDescription` | `string` | ❌ | A short description of this collection | | -| `description` | `ref` | ❌ | Rich-text description, represented as a Leaflet linear document | References must conform to `pub.leaflet.pages.linearDocument#main` | -| `avatar` | `blob` | ❌ | Primary avatar image representing this collection across apps and views | Typically a square image | -| `coverPhoto` | `blob` | ❌ | The cover photo of this collection | | -| `activities` | `array` | ✅ | Array of activities with their associated weights in this collection | Each item references `org.hypercerts.claim.activity#activityWeight` | -| `location` | `ref` | ❌ | A strong reference to a location record describing where the work took place | References must conform to `app.certified.location` | -| `createdAt` | `string` | ✅ | Client-declared timestamp when this record was originally created | | +| Property | Type | Required | Description | Comments | +| ---------------------------- | -------- | -------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| `collectionTitle` | `string` | ✅ | The title of this collection | | +| `shortCollectionDescription` | `string` | ❌ | Short summary of this collection, suitable for previews and list views | | +| `collectionDescription` | `string` | ❌ | Full description of this collection, suitable for detail views | | +| `items` | `array` | ✅ | Array of strong references to items in this collection | Items can be activities (`org.hypercerts.claim.activity`) and/or other collections (`org.hypercerts.claim.collection`). Enables recursive nesting. | +| `location` | `ref` | ❌ | A strong reference to a location record describing where the work took place | The referenced record must conform to the `app.certified.location` lexicon. | +| `createdAt` | `string` | ✅ | Client-declared timestamp when this record was originally created | | + +#### Example: Creating a Collection with Nested Items + +```typescript +import { TID } from "@atproto/common"; + +const collectionRecord = { + $type: "org.hypercerts.claim.collection", + collectionTitle: "Climate Action Projects", + items: [ + // Reference to an activity + { + uri: "at://did:plc:alice/org.hypercerts.claim.activity/3k2abc", + cid: "...", + }, + // Reference to another activity + { + uri: "at://did:plc:bob/org.hypercerts.claim.activity/7x9def", + cid: "...", + }, + // Reference to another collection (recursive!) + { + uri: "at://did:plc:carol/org.hypercerts.claim.collection/4m5ghi", + cid: "...", + }, + ], + createdAt: new Date().toISOString(), +}; +``` + +--- + +### org.hypercerts.claim.collection.project + +**Lexicon ID:** `org.hypercerts.claim.collection.project` + +**Description:** Project-specific metadata for a collection. Uses the sidecar pattern with the same record key (TID) as the collection record. This allows collections to represent projects by adding rich-text descriptions and visual assets. + +**Key:** `tid` (same as the collection record) + +#### Properties + +| Property | Type | Required | Description | Comments | +| ------------------------- | -------- | -------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | +| `projectTitle` | `string` | ❌ | The title of this project | maxLength: 800, maxGraphemes: 80 | +| `shortProjectDescription` | `string` | ❌ | Short summary of this project, suitable for previews and list views | maxLength: 3000, maxGraphemes: 300 | +| `projectDescription` | `ref` | ✅ | Rich-text description of this project, represented as a Leaflet linear document | References must conform to `pub.leaflet.pages.linearDocument#main` | +| `avatar` | `blob` | ❌ | Primary avatar image representing this project across apps and views | Typically a square logo or project identity image; image/png or image/jpeg, maxSize: 1000000 | +| `coverPhoto` | `blob` | ❌ | The cover photo of this project | image/png or image/jpeg, maxSize: 1000000 | +| `createdAt` | `string` | ✅ | Client-declared timestamp when this project metadata was created | | + +#### Example: Creating a Project (Collection + Project Sidecar) + +```typescript +import { TID } from "@atproto/common"; + +const tid = TID.nextStr(); // Same TID for both records + +// Base collection record +const collectionRecord = { + $type: "org.hypercerts.claim.collection", + collectionTitle: "Carbon Offset Initiative", + items: [ + { + uri: "at://did:plc:alice/org.hypercerts.claim.activity/3k2abc", + cid: "...", + }, + { + uri: "at://did:plc:bob/org.hypercerts.claim.activity/7x9def", + cid: "...", + }, + ], + createdAt: new Date().toISOString(), +}; + +// Project sidecar with rich-text description and assets +const projectSidecar = { + $type: "org.hypercerts.claim.collection.project", + projectTitle: "Carbon Offset Initiative", + shortProjectDescription: "A project focused on carbon reduction", + projectDescription: { + uri: "at://did:plc:alice/pub.leaflet.pages.linearDocument/abc123", + cid: "...", + }, + avatar: avatarBlob, + coverPhoto: coverPhotoBlob, + createdAt: new Date().toISOString(), +}; + +// Create both with same TID +await createRecord({ + collection: "org.hypercerts.claim.collection", + rkey: tid, + record: collectionRecord, +}); +await createRecord({ + collection: "org.hypercerts.claim.collection.project", + rkey: tid, + record: projectSidecar, +}); +``` + +**Note**: The project sidecar is optional. Collections without this sidecar are simple groupings; collections with it are "projects" with rich documentation. --- diff --git a/lexicons/org/hypercerts/claim/collection.json b/lexicons/org/hypercerts/claim/collection.json index 5109b8b7..7981bbf4 100644 --- a/lexicons/org/hypercerts/claim/collection.json +++ b/lexicons/org/hypercerts/claim/collection.json @@ -51,11 +51,6 @@ "ref": "org.hypercerts.claim.activity#activityWeight" } }, - "location": { - "type": "ref", - "ref": "com.atproto.repo.strongRef", - "description": "A strong reference to a location record describing where the work took place. The referenced record must conform to the app.certified.location lexicon." - }, "createdAt": { "type": "string", "format": "datetime",