Skip to content

Project as collection sidecar - #78

Merged
bitbeckers merged 3 commits into
developfrom
feat/project_as_collection_sidecar
Jan 20, 2026
Merged

Project as collection sidecar#78
bitbeckers merged 3 commits into
developfrom
feat/project_as_collection_sidecar

Conversation

@bitbeckers

@bitbeckers bitbeckers commented Jan 20, 2026

Copy link
Copy Markdown
Contributor

feat: Implement project as collection sidecar pattern

Overview

This PR refactors the project and collection lexicons to implement the "sidecar pattern" where projects are now represented as collections with an optional rich metadata sidecar. This change simplifies the data model while enabling more flexible organization through recursive collection nesting.

Key Changes

1. Project Sidecar Pattern (org.hypercerts.claim.collection.project)

  • Removed standalone org.hypercerts.claim.project lexicon
  • Created org.hypercerts.claim.collection.project as a sidecar record (shares same TID as collection)
  • Moved avatar and coverPhoto from base collection to project sidecar
  • Added rich-text description support via Leaflet linear document reference
  • Added projectTitle and shortProjectDescription fields
  • Collections without the sidecar = simple groupings
  • Collections with the sidecar = full projects with rich documentation

2. Recursive Collection Nesting (org.hypercerts.claim.collection)

  • Replaced activities array (with activityWeight objects) with simpler items array
  • items now accepts plain strongRef to both activities AND other collections
  • Enables recursive nesting: collections can contain collections
  • Renamed fields for clarity:
    • titlecollectionTitle
    • shortDescriptionshortCollectionDescription
  • Added collectionDescription for full-text descriptions
  • Added location reference to app.certified.location

3. Documentation Updates

  • Updated README with comprehensive examples showing:
    • How to create collections with recursive nesting
    • How to create projects using the sidecar pattern
    • Field descriptions and usage patterns
  • Updated ERD diagrams (ERD.puml) to reflect:
    • New collection field structure
    • Project sidecar relationship
    • Collection-to-location relationship
    • Recursive collection nesting

Summary by CodeRabbit

  • New Features

    • Collections can nest recursively (collections may contain other collections).
    • Optional project sidecars: collections can be enriched with project metadata (rich-text description, avatar, cover photo).
  • Refactor

    • Collection items now use a simple items array for references (replacing prior activity-weight structure).
    • Collection and project structures reorganized for clearer separation of base collections vs. optional project sidecars.
  • Documentation

    • Updated examples and field constraints to reflect new naming and limits.

✏️ Tip: You can customize this high-level summary in your review settings.

@changeset-bot

changeset-bot Bot commented Jan 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f1c6f28

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@hypercerts-org/lexicon Minor

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

@coderabbitai

coderabbitai Bot commented Jan 20, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR refactors the collection lexicon to use an items array with recursive nesting and introduces an optional collection project sidecar (same TID) holding projectTitle, rich projectDescription, avatar, coverPhoto, and createdAt; avatar/coverPhoto were removed from the base collection.

Changes

Cohort / File(s) Summary
Changesets
.changeset/add-project-sidecar.md, .changeset/refactor-collection-items-array.md
Adds changeset entries documenting the new project sidecar and the collection refactor to items-based composition replacing activities.
Collection lexicon (modified)
lexicons/org/hypercerts/claim/collection.json
Rename fields: titlecollectionTitle, shortDescriptionshortCollectionDescription, remove description rich-text in favor of collectionDescription; remove avatar/coverPhoto; replace activitiesitems (type: com.atproto.repo.strongRef); update required fields to ["collectionTitle","items","createdAt"].
Project sidecar lexicon (new)
lexicons/org/hypercerts/claim/collection/project.json
New lexicon keyed by tid for project metadata: projectTitle?, shortProjectDescription?, required projectDescription (pub.leaflet.pages.linearDocument ref), avatar?, coverPhoto?, createdAt.
Docs & diagrams
README.md, ERD.puml
README updated with examples and constraints (maxLength/maxGraphemes) and new “creating a project” example. ERD updated: collection fields renamed, items relation added with recursive edge, and new collectionProject dataclass (sidecar) added.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • aspiers
  • s-adamantine

Poem

🐇 I found a TID and gave it room to grow,
Collections nest like burrows row on row,
A sidecar holds portraits, pages deep and bright,
Avatars and covers snug in moonlit light,
Hop—project tales now bloom from simple night.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Project as collection sidecar' clearly and directly summarizes the main architectural change in the pull request: implementing a sidecar pattern where projects are represented as optional metadata attached to collections.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Lexicon Documentation Sync ✅ Passed JSON lexicon files and README documentation are properly synchronized with matching properties, though projectTitle description contains a semantic error consistently replicated across both.
Lexicons Styleguide Compliance ✅ Passed Both lexicons fully comply with AT Protocol Lexicon Style Guide conventions including namespace identifiers, camelCase naming, descriptions, required fields, and blob constraints.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ERD.puml (1)

204-216: Remove the deprecated project entity and its relationships from ERD.puml.

The org.hypercerts.claim.project lexicon no longer exists in the codebase. ERD.puml still defines this deprecated entity (lines 204–216) and references it in relationships (lines 314–315), violating the coding guideline that all ERD entities must correspond to lexicons defined in lexicons/**/*.json.

Note: The distinct org.hypercerts.claim.collection.project sidecar (line 192) correctly exists and should remain.

Required changes

Remove the deprecated project dataclass definition (lines 204–216) and its relationships:

-' org.hypercerts.claim.project (DEPRECATED)
-dataclass project {
-    !if (SHOW_FIELDS == "true")
-    title
-    shortDescription
-    description?
-    avatar?
-    coverPhoto?
-    activities[]
-    location?
-    createdAt
-    !endif
-}

Remove relationships (lines 314–315):

-project::activities --> activity
-project::location --> location
🤖 Fix all issues with AI agents
In `@lexicons/org/hypercerts/claim/collection/project.json`:
- Around line 25-29: The projectDescription field currently points to
"pub.leaflet.pages.linearDocument#main" but should use the strongRef pattern so
clients submit the { uri, cid } shape shown in the README; update the
projectDescription schema to reference com.atproto.repo.strongRef (replace the
existing "ref": "pub.leaflet.pages.linearDocument#main") and keep or adjust the
description to indicate it stores a strongRef to the Leaflet linear document so
validation matches other cross-record refs (e.g., com.atproto.repo.strongRef)
used in the codebase.

In `@README.md`:
- Around line 414-418: Update the README collection docs/examples to match the
current lexicon schema by replacing the `title` property with `collectionTitle`
and adding the new fields `shortCollectionDescription`, `collectionDescription`,
and `location` (including their types, required flags, and descriptions)
wherever the collection table and examples reference `title` (notably the table
rows around `title` and the example payloads mentioned at ranges 425-447 and
477-492); ensure property names exactly match the schema (`collectionTitle`,
`shortCollectionDescription`, `collectionDescription`, `location`), update the
descriptions to reflect their purpose, and adjust any examples or notes that
reference `title` so they produce valid records against the lexicon.

Comment on lines +25 to +29
"projectDescription": {
"type": "ref",
"ref": "pub.leaflet.pages.linearDocument#main",
"description": "Rich-text description of this project, represented as a Leaflet linear document."
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's see the full project.json file to understand the structure
cat -n lexicons/org/hypercerts/claim/collection/project.json

Repository: hypercerts-org/hypercerts-lexicon

Length of output: 2332


🏁 Script executed:

# Search for strongRef usage in other lexicon files to understand the pattern
rg "strongRef" lexicons/ -A 2 -B 2

Repository: hypercerts-org/hypercerts-lexicon

Length of output: 6317


🏁 Script executed:

# Check for usage of pub.leaflet.pages.linearDocument in the codebase
rg "linearDocument" --include="*.json" --include="*.md"

Repository: hypercerts-org/hypercerts-lexicon

Length of output: 518


🏁 Script executed:

# Check README.md for documentation about projectDescription
grep -n "projectDescription\|project.json\|collection" README.md | head -20

Repository: hypercerts-org/hypercerts-lexicon

Length of output: 2246


🏁 Script executed:

# Look for other examples of 'ref' type fields in the codebase
rg '"type":\s*"ref"' lexicons/ -A 1 -B 1 | head -40

Repository: hypercerts-org/hypercerts-lexicon

Length of output: 2482


🏁 Script executed:

# Search for Leaflet lexicon files
find lexicons -name "*leaflet*" -o -name "*linear*"

Repository: hypercerts-org/hypercerts-lexicon

Length of output: 59


🏁 Script executed:

# Look for pub.bsky patterns to understand ATProto definition refs
rg "pub\.bsky" lexicons/ | head -20

Repository: hypercerts-org/hypercerts-lexicon

Length of output: 59


🏁 Script executed:

# Check if there are any inline type definitions vs record references
rg '"type":\s*"ref"' lexicons/ -B 2 | grep -E "(ref|description)" | head -30

Repository: hypercerts-org/hypercerts-lexicon

Length of output: 2968


🏁 Script executed:

# Search for documentation on how refs are used in ATProto
rg "richtext\|facet" lexicons/ -l

Repository: hypercerts-org/hypercerts-lexicon

Length of output: 59


🌐 Web query:

ATProto lexicon how to reference external lexicon definitions in ref fields

💡 Result:

Use a full NSID (optionally with a fragment) as the ref string. Per the Lexicon spec, a ref value may be:

  • a global reference: the Lexicon NSID, optionally with a “#definitionName” fragment (e.g. com.example.defs#MyType), or
  • a local reference: a fragment-only string that points to a definition in the same file (e.g. #MyType). [1]

Notes/rules:

  • If the referenced definition is the file’s main definition, you can reference it by NSID without a fragment (e.g. com.example.record). Do not use a #main suffix in $type fields. [1]
  • Refs may not point to token types. [1]

Example ref entries:

  • External object type: { "type": "ref", "ref": "com.acme.defs#Address" }
  • Local definition: { "type": "ref", "ref": "#Address" }
  • Main definition reference (used in $type): "com.acme.person" (not "com.acme.person#main"). [1]

Source: AT Protocol Lexicon specification. [1]


🏁 Script executed:

# Check if pub.leaflet exists anywhere in the repo or if it's external
find . -type f -name "*.json" | xargs grep -l "leaflet\|pub\.leaflet" 2>/dev/null | head -10

Repository: hypercerts-org/hypercerts-lexicon

Length of output: 183


🏁 Script executed:

# Look at existing strongRef definition to understand the pattern
cat -n lexicons/com/atproto/repo/strongRef.json

Repository: hypercerts-org/hypercerts-lexicon

Length of output: 532


🏁 Script executed:

# Check how the README currently describes projectDescription to compare with review claim
sed -n '450,475p' README.md

Repository: hypercerts-org/hypercerts-lexicon

Length of output: 2492


🏁 Script executed:

# Check if there are any examples in the README showing how projectDescription should be used
sed -n '475,530p' README.md

Repository: hypercerts-org/hypercerts-lexicon

Length of output: 1661


🏁 Script executed:

# Search for any other direct definition references (not strongRef) to see if this is a pattern
rg '"ref":\s*"[^c]' lexicons/ | grep -v strongRef | grep -v "did" | head -20

Repository: hypercerts-org/hypercerts-lexicon

Length of output: 525


🏁 Script executed:

# Check the full context around where projectDescription is used in examples
sed -n '475,510p' README.md

Repository: hypercerts-org/hypercerts-lexicon

Length of output: 1082


Fix projectDescription ref to match the strongRef pattern shown in README examples.

The schema uses "ref": "pub.leaflet.pages.linearDocument#main", but the README example shows projectDescription receiving a strongRef-shaped value ({ uri, cid }). This mismatch will cause validation failures when clients submit valid payloads matching the documented example. All other cross-record references in the codebase use com.atproto.repo.strongRef; this field should too.

Proposed fix
           "projectDescription": {
             "type": "ref",
-            "ref": "pub.leaflet.pages.linearDocument#main",
-            "description": "Rich-text description of this project, represented as a Leaflet linear document."
+            "ref": "com.atproto.repo.strongRef",
+            "description": "Strong reference to a Leaflet linear document describing this project. The referenced record must conform to pub.leaflet.pages.linearDocument#main."
           },
🤖 Prompt for AI Agents
In `@lexicons/org/hypercerts/claim/collection/project.json` around lines 25 - 29,
The projectDescription field currently points to
"pub.leaflet.pages.linearDocument#main" but should use the strongRef pattern so
clients submit the { uri, cid } shape shown in the README; update the
projectDescription schema to reference com.atproto.repo.strongRef (replace the
existing "ref": "pub.leaflet.pages.linearDocument#main") and keep or adjust the
description to indicate it stores a strongRef to the Leaflet linear document so
validation matches other cross-record refs (e.g., com.atproto.repo.strongRef)
used in the codebase.

Comment thread README.md Outdated
@bitbeckers bitbeckers self-assigned this Jan 20, 2026
@bitbeckers
bitbeckers requested a review from aspiers January 20, 2026 06:42
- Replace activities array (activityWeight) with items array (strongRef)
- Items can reference activities and/or other collections (recursive nesting)
- Update collection description to reflect new structure
- Update README with example showing recursive nesting
- Update ERD to show items field and recursive relationship
- Delete org.hypercerts.claim.project lexicon
- Create org.hypercerts.claim.collection.project sidecar
- Move avatar and coverPhoto from collection to project sidecar
- Projects are now collections + project sidecar (same TID)
- Update README with project sidecar documentation and example
- Update ERD to show collectionProject sidecar entity
@bitbeckers
bitbeckers force-pushed the feat/project_as_collection_sidecar branch from 58972d9 to f1c6f28 Compare January 20, 2026 07:02

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@README.md`:
- Line 467: Update the README entry for the `projectTitle` field so its
description reads "The title of this project" instead of "The title of this
collection"; locate the table row containing the `projectTitle` symbol and
change only the descriptive text to match the sidecar context while keeping the
rest of the constraints (maxLength: 800, maxGraphemes: 80) unchanged.

Comment thread README.md
| Property | Type | Required | Description | Comments |
| ------------------------- | -------- | -------- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `projectTitle` | `string` | ❌ | The title of this project | maxLength: 800, maxGraphemes: 80 |
| `projectTitle` | `string` | ❌ | The title of this collection | maxLength: 800, maxGraphemes: 80 |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor: Correct the projectTitle description.

The description says "The title of this collection" but should say "The title of this project" to match the sidecar context.

📝 Suggested fix
-| `projectTitle`            | `string` | ❌       | The title of this collection                                                    | maxLength: 800, maxGraphemes: 80                                             |
+| `projectTitle`            | `string` | ❌       | The title of this project                                                       | maxLength: 800, maxGraphemes: 80                                             |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| `projectTitle` | `string` || The title of this collection | maxLength: 800, maxGraphemes: 80 |
| `projectTitle` | `string` || The title of this project | maxLength: 800, maxGraphemes: 80 |
🤖 Prompt for AI Agents
In `@README.md` at line 467, Update the README entry for the `projectTitle` field
so its description reads "The title of this project" instead of "The title of
this collection"; locate the table row containing the `projectTitle` symbol and
change only the descriptive text to match the sidecar context while keeping the
rest of the constraints (maxLength: 800, maxGraphemes: 80) unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants