Skip to content
Merged
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
6 changes: 0 additions & 6 deletions .changeset/add-ai-agent-skill.md

This file was deleted.

9 changes: 0 additions & 9 deletions .changeset/add-certified-link-evm.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/add-missing-def-descriptions.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/add-pub-leaflet-lexicons.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/attachment-content-type-known-values.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/badge-known-values-strongref.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/collection-short-description-facets.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/collection-type-known-values.md

This file was deleted.

7 changes: 0 additions & 7 deletions .changeset/description-union-type.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/funding-receipt-field-types.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/funding-receipt-text-variant.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/make-funding-receipt-from-optional.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/numeric-string-scores.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/optional-badge-icon.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/remove-contributors-max-length.md

This file was deleted.

205 changes: 205 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,210 @@
# @hypercerts-org/lexicon

## 0.11.0

The v0.11.0 release introduces EVM identity linking, stronger type safety across badge and funding schemas, vendored Leaflet lexicons for runtime validation, and a set of schema refinements based on real-world usage from the first month of v0.10.0 adoption. All changes listed below are merged to `main` and will ship together as a single coordinated release.

```bash
npm install @hypercerts-org/lexicon@0.11.0
```

---

### New lexicons

#### EVM identity linking

`app.certified.link.evm`

A new record type for creating verifiable links between ATProto identities and EVM wallet addresses. Each record contains a cryptographic proof — currently EIP-712 typed data signatures for EOA wallets — that binds a DID to an Ethereum address onchain.

The `proof` field is an open union, so future signature methods (ERC-1271, ERC-6492) can be added without breaking existing records.
Comment thread
aspiers marked this conversation as resolved.

```jsonc
{
"address": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
"proof": {
"$type": "app.certified.link.evm#eip712Proof",
"signature": "0x...",
"message": {
"did": "did:plc:abc123",
"evmAddress": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
"chainId": "1",
"timestamp": "1711929600",
"nonce": "0",
},
},
"createdAt": "2026-04-03T00:00:00.000Z",
}
```

#### Vendored Leaflet and richtext lexicons

`pub.leaflet.*` · `pub.leaflet.richtext.facet`

The package now ships the full set of Leaflet block and richtext facet lexicon JSON files (17 files). Previously, these external schemas were shimmed at the TypeScript type level via `@atcute` packages, which caused `LexiconDefNotFoundError` at runtime when validating records with `description` or facet fields. Vendoring them fixes runtime validation and removes the `@atcute/leaflet` and `@atcute/bluesky` dependencies.

This is a **packaging change**, not a schema change. No record structures are affected.

---

### Breaking changes

This release includes three breaking schema changes. All were identified early enough in adoption that the team decided the long-term gains outweigh the migration cost.

#### Evaluation scores are now strings

`org.hypercerts.context.evaluation`

The `min`, `max`, and `value` fields on evaluation scores changed from `integer` to `string`. ATProto has no native decimal type, and integer-only scores made use cases like "3.7 out of 5" impossible.

```diff
// Before
- { "min": 0, "max": 10, "value": 7 }
+ // After
+ { "min": "0", "max": "10", "value": "7.5" }
```

**Who needs to update:**

| Consumer | Action |
| -------- | ---------------------------------------------------------------------------------------------------------------------- |
| Indexers | Change column type from `INTEGER` to `TEXT`. Backfill existing records. Update any numeric sorting or filtering logic. |
| AppViews | Parse scores as strings. Use `parseFloat()` for numeric display where appropriate. |
| Frontend | Handle both numeric strings (`"3.7"`) and potentially non-numeric strings (`"A+"`) in display components. |

#### Badge references are now strong refs

`app.certified.badge.award` · `app.certified.badge.response`

Badge awards and responses now reference their parent records via `com.atproto.repo.strongRef` instead of plain lexicon refs. Strong references include both a URI and a content hash (CID), pinning the reference to a specific version of the badge definition. This prevents the meaning of an award from drifting if the underlying badge definition is later modified.

```diff
// Before
- "badge": "at://did:plc:abc/app.certified.badge.definition/123"

// After
+ "badge": {
+ "uri": "at://did:plc:abc/app.certified.badge.definition/123",
+ "cid": "bafyrei..."
+ }
```

**Who needs to update:**

| Consumer | Action |
| ------------- | ----------------------------------------------------------------------------------------------------------------- |
| Indexers | Update parsing to expect `{ uri, cid }` objects instead of plain URI strings for `badge` and `badgeAward` fields. |
| AppViews | Update resolution logic. Dereference using both `uri` and `cid` for content verification. |
| SDK consumers | Regenerate types. Any code constructing badge awards or responses must supply the full strong ref. |

#### Funding receipt fields normalized

`org.hypercerts.funding.receipt`

The `from`, `to`, and `for` fields have been reworked for consistency and stronger type safety.

| Field | Before (v0.10.0) | After |
| ------ | ----------------- | ------------------------------------------------------------------------------------------ |
| `from` | Required, DID ref | **Optional**, union of `#text` \| `app.certified.defs#did` \| `com.atproto.repo.strongRef` |
| `to` | Plain string | Union of `#text` \| `app.certified.defs#did` \| `com.atproto.repo.strongRef` |
| `for` | AT-URI string | `com.atproto.repo.strongRef` (pins to a specific record version) |

The `from` and `to` fields were asymmetric — `from` required an AT Protocol identity while `to` accepted any string. Now both are three-way unions that accept a free-text string (`#text` — for display names, wallet addresses, or other identifiers), a DID, or a strong reference. This treats senders and recipients uniformly while preserving the ability to reference non-ATProto participants. `from` is also optional, properly supporting anonymous funding. `for` is now a strong ref, ensuring the receipt always points to the exact version of the activity it funded.

```diff
// Before
- "from": { "$type": "app.certified.defs#did", "did": "did:plc:sender" },
- "to": "did:plc:recipient",
- "for": "at://did:plc:abc/org.hypercerts.claim.activity/123"

// After — with a DID
+ "from": { "$type": "app.certified.defs#did", "did": "did:plc:sender" },
+ "to": { "$type": "app.certified.defs#did", "did": "did:plc:recipient" },

// After — with a free-text identifier
+ "to": { "$type": "org.hypercerts.funding.receipt#text", "value": "0xAb58...eC9B" },

// After — for field
+ "for": {
+ "uri": "at://did:plc:abc/org.hypercerts.claim.activity/123",
+ "cid": "bafyrei..."
+ }
```

**Who needs to update:**

| Consumer | Action |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Indexers | Update parsing for `from`, `to` (now unions with `$type` discriminator — handle all three variants), and `for` (now `{ uri, cid }`). Allow `NULL` for `from`. |
| AppViews | Update resolution logic for all three fields. `for` requires dereferencing via both URI and CID. Handle `#text` variants for display. |
| SDK consumers | Regenerate types. Code constructing receipts must supply union-typed `to` and strong ref `for`. |
| Frontend | Update forms to construct proper union objects for sender/recipient. Handle `#text` for non-ATProto participants. Handle missing `from` for anonymous display. |

---

### Schema changes

> **A note on "optional" fields:** Even new optional fields require attention from indexers and AppViews. If an indexer doesn't store a new field, that data is silently lost for every downstream consumer. The changes below are non-breaking in the strict sense — existing records remain valid — but ignoring them means incomplete data.

#### Known values

Several free-text string fields now declare `knownValues` — a set of canonical values that establish interoperability conventions across the ecosystem. Custom values are still permitted. Think of these as Schelling points, not constraints.

| Lexicon | Field | Known values |
| ----------------------------------- | ------------- | -------------------------------------------------------------------------------------------------- |
| `org.hypercerts.collection` | `type` | `favorites` · `project` · `portfolio` · `program` |
| `org.hypercerts.context.attachment` | `contentType` | `report` · `audit` · `evidence` · `testimonial` · `methodology` |
| `app.certified.badge.definition` | `badgeType` | `endorsement` · `verification` · `participation` · `certification` · `affiliation` · `recognition` |

**Action:** Indexers should index these values for filtering and categorization. AppViews and frontends can use them for dropdowns, search facets, and display grouping. No schema migration required — the underlying field type is still a string.

#### Badge icon is now optional

`app.certified.badge.definition`

The `icon` field moved from `required` to optional. Not all badges have a visual representation — endorsements, participation records, and text-based certifications can now omit the icon entirely.

**Action:** Indexers should allow `NULL` in the icon column. Frontend developers must add a fallback or placeholder when rendering badges without an icon — apps that assume `icon` is always present will crash or render broken UI.

#### Contributors array is uncapped

`org.hypercerts.claim.activity`

Removed the `maxLength: 1000` constraint on the `contributors` array. ATProto records have a natural 1 MB size limit (~2,000–4,000 contributors), making the artificial cap unnecessary.

**Action:** Indexers and AppViews with hardcoded length limits matching the old max should remove them. Frontends should implement pagination or lazy loading for large contributor lists to avoid performance issues.

#### Rich text on collection short descriptions

`org.hypercerts.collection`

Added `shortDescriptionFacets` — an optional array of rich text facets (mentions, URLs, hashtags) that annotate the `shortDescription` field. This brings collections in line with activity claims, which already supported facets.

**Action:** Indexers must store the new field when present — without it, rich text annotations (links, mentions) are permanently lost. AppViews should include facets in API responses. Frontends can render rich text using the standard ATProto facet model.

---

### Documentation improvements

- **Contributor and item defs** now have descriptions, improving TypeScript IntelliSense and AI code generation.
- **`occurredAt` vs `createdAt`** semantics clarified on funding receipts. `occurredAt` is when the funding happened in the real world; `createdAt` is when the record was written to the PDS.
- **Stale references** in board and measurement lexicon descriptions have been corrected.
- **README** rewritten with an ASCII namespace map and structured reference tables.
- **AI agent skill** added for downstream developers. Install via `npx skills add hypercerts-org/hypercerts-lexicon`.

---

### Upgrading

```bash
npm install @hypercerts-org/lexicon@0.11.0
```

The source of truth for lexicon definitions is the [NPM package](https://www.npmjs.com/package/@hypercerts-org/lexicon) and the published ATProto repository. The `main` branch on GitHub is a development branch — do not build production applications against it.

After upgrading, regenerate your TypeScript types and run your validation suite against the updated schemas. The package includes all lexicon JSON files and pre-built type definitions.

## 0.10.0

### Minor Changes
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hypercerts-org/lexicon",
"version": "0.10.0",
"version": "0.11.0",
"description": "ATProto lexicon definitions and TypeScript types for the Hypercerts protocol",
"type": "module",
"main": "./dist/index.cjs",
Expand Down
Loading