From a453f7a3041fc755cac936c39c463f9a0722d50e Mon Sep 17 00:00:00 2001 From: holkexyz Date: Fri, 23 Jan 2026 20:08:08 +1300 Subject: [PATCH 1/8] add ops for workscope logic model --- .changeset/add-ops-lexicon.md | 35 +++++++++++++++++++++++++ lexicons/org/hypercerts/helper/ops.json | 34 ++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 .changeset/add-ops-lexicon.md create mode 100644 lexicons/org/hypercerts/helper/ops.json diff --git a/.changeset/add-ops-lexicon.md b/.changeset/add-ops-lexicon.md new file mode 100644 index 00000000..792c213b --- /dev/null +++ b/.changeset/add-ops-lexicon.md @@ -0,0 +1,35 @@ +--- +"@hypercerts-org/lexicon": minor +--- + +Add ops lexicon for work scope logic operator nodes + +**New Features:** + +- **New lexicon (`org.hypercerts.helper.ops`):** + - Operator node record type for work scope logic expressions + - Supports nested boolean logic operations (AND, OR, NOT) + - Fields: + - `op` (required): Operator identifier (e.g., 'all', 'any', 'not') + - `args` (required): Array of strongRefs pointing to `org.hypercerts.helper.workScopeTag` (leaf atoms) or other `org.hypercerts.helper.ops` records (nested expressions) + - `createdAt` (required): Client-declared timestamp + - Enables complex work scope logic by allowing operators to reference other operators or scope tags recursively + - Operator semantics are defined by consuming applications + +**Usage:** + +The `org.hypercerts.helper.ops` lexicon works together with `org.hypercerts.helper.workScopeTag` to create nested boolean logic expressions for work scope definitions. Operators can be referenced via strongRefs in activity records' `workScope` field. + +**Example:** + +```json +{ + "$type": "org.hypercerts.helper.ops", + "op": "all", + "args": [ + { "uri": "at://.../workScopeTag/ipfs", "cid": "..." }, + { "uri": "at://.../workScopeTag/go", "cid": "..." } + ], + "createdAt": "2024-01-01T00:00:00Z" +} +``` diff --git a/lexicons/org/hypercerts/helper/ops.json b/lexicons/org/hypercerts/helper/ops.json new file mode 100644 index 00000000..0960637a --- /dev/null +++ b/lexicons/org/hypercerts/helper/ops.json @@ -0,0 +1,34 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.helper.ops", + "defs": { + "main": { + "type": "record", + "description": "Operator node for work scope logic. Nesting is achieved by having args strongRefs point to either workScopeTag records (leaf atoms) or other ops records (nested expressions). Operator semantics are defined by consuming applications.\n\nExamples: op='all' (AND), op='any' (OR), op='not' (NOT; typically unary).", + "key": "tid", + "record": { + "type": "object", + "required": ["createdAt", "op", "args"], + "properties": { + "op": { + "type": "string", + "maxLength": 64, + "description": "Operator identifier. Semantics are defined by the evaluating application. Examples: 'all', 'any', 'not'." + }, + "args": { + "type": "array", + "minLength": 1, + "maxLength": 100, + "items": { "type": "ref", "ref": "com.atproto.repo.strongRef" }, + "description": "Arguments. Each strongRef should point to either org.hypercerts.helper.workScopeTag (leaf) or org.hypercerts.helper.ops (nested). For op='not', args SHOULD have exactly one element (enforced by clients)." + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Client-declared timestamp when this record was originally created" + } + } + } + } + } +} From c2587b527a37ac6af5bc90f9ad75adf3a082bf2e Mon Sep 17 00:00:00 2001 From: holkexyz Date: Fri, 23 Jan 2026 20:22:21 +1300 Subject: [PATCH 2/8] update schemas.md and ERD --- ERD.puml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/ERD.puml b/ERD.puml index 33fb3b4d..7fb8a7fc 100644 --- a/ERD.puml +++ b/ERD.puml @@ -145,6 +145,29 @@ dataclass location { !endif } +' org.hypercerts.helper.workScopeTag +dataclass workScopeTag { + !if (SHOW_FIELDS == "true") + createdAt + key + label + kind? + description? + parent? + aliases[]? + externalReference? + !endif +} + +' org.hypercerts.helper.ops +dataclass ops { + !if (SHOW_FIELDS == "true") + createdAt + op + args[] + !endif +} + ' Certified badges (data class + award + response) dataclass badgeDefinition { !if (SHOW_FIELDS == "true") @@ -298,6 +321,11 @@ activity::contributors -l--> contributorInformation activity::contributors --> contributionDetails activity::rights --> rights activity::locations --> location +activity::workScope --> workScopeTag +activity::workScope --> ops + +ops::args --> workScopeTag +ops::args --> ops : "recursive\nnesting" contributorInformation::identifier --> contributorEntity : identifies From 345d8ab4c14249ee0ef70cb16634f981723c7cc0 Mon Sep 17 00:00:00 2001 From: holkexyz Date: Fri, 23 Jan 2026 20:30:14 +1300 Subject: [PATCH 3/8] fix ordering of required fields --- lexicons/org/hypercerts/helper/ops.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lexicons/org/hypercerts/helper/ops.json b/lexicons/org/hypercerts/helper/ops.json index 0960637a..8accc4a4 100644 --- a/lexicons/org/hypercerts/helper/ops.json +++ b/lexicons/org/hypercerts/helper/ops.json @@ -8,7 +8,7 @@ "key": "tid", "record": { "type": "object", - "required": ["createdAt", "op", "args"], + "required": ["op", "args", "createdAt"], "properties": { "op": { "type": "string", From 3fea1dbe092d4870727eea9f957093dccf21b0c2 Mon Sep 17 00:00:00 2001 From: holkexyz Date: Wed, 18 Feb 2026 20:54:16 +1000 Subject: [PATCH 4/8] rename workScopeExpr.md to workScopeExpr.json --- lexicons/org/hypercerts/claim/activity.json | 2 +- .../org/hypercerts/helper/workScopeExpr.json | 67 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 lexicons/org/hypercerts/helper/workScopeExpr.json diff --git a/lexicons/org/hypercerts/claim/activity.json b/lexicons/org/hypercerts/claim/activity.json index 70d851a3..946252fc 100644 --- a/lexicons/org/hypercerts/claim/activity.json +++ b/lexicons/org/hypercerts/claim/activity.json @@ -54,7 +54,7 @@ "workScope": { "type": "union", "refs": ["com.atproto.repo.strongRef", "#workScopeString"], - "description": "Work scope definition. Either a strongRef to a work-scope logic record (structured, nested logic), or a free-form string for simple or legacy scopes. The work scope record should conform to the org.hypercerts.helper.workScopeTag lexicon." + "description": "Defines the scope of work covered by this activity claim. May be a string, a structured scope expression (via org.hypercerts.helper.workScopeExpr), or an advanced logic tree (via org.hypercerts.helper.ops). If no inclusion or exclusion constraints are defined, the scope is treated as unconstrained and all work is considered in scope." }, "startDate": { "type": "string", diff --git a/lexicons/org/hypercerts/helper/workScopeExpr.json b/lexicons/org/hypercerts/helper/workScopeExpr.json new file mode 100644 index 00000000..313a93f7 --- /dev/null +++ b/lexicons/org/hypercerts/helper/workScopeExpr.json @@ -0,0 +1,67 @@ +{ + "lexicon": 1, + "id": "org.hypercerts.helper.workScopeExpr", + "defs": { + "main": { + "type": "record", + "description": "A reusable work-scope boolean expression (simple flat form): (ALL allOf) AND (ANY anyOf, if present) AND (NONE noneOf). Designed to cover the vast majority of practical work-scope definitions (include, require, exclude) without recursion. For full nested boolean logic or complex conditional expressions, use org.hypercerts.helper.ops.\n\nAn empty work-scope expression represents an unconstrained scope. If `allOf`, `anyOf`, and `noneOf` are all absent or empty, the expression imposes no filtering constraints. In this case, all work is considered in scope by default.", + "key": "tid", + "record": { + "type": "object", + "required": [ + "version", + "createdAt" + ], + "properties": { + "version": { + "type": "integer", + "minimum": 1, + "description": "Schema version for this expression. Start with 1. Enables forward-compatible evolution of evaluation semantics." + }, + "label": { + "type": "string", + "maxLength": 140, + "description": "Optional short human-readable label for UI display (e.g., 'OSS docs/code — no marketing')." + }, + "description": { + "type": "string", + "maxLength": 4000, + "description": "Optional longer explanation of the scope intent, interpretation guidance, or edge-case clarifications." + }, + "allOf": { + "type": "array", + "items": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + }, + "maxLength": 100, + "description": "All referenced tags must match for something to be considered in-scope. Typically refs to org.hypercerts.helper.workScopeTag." + }, + "anyOf": { + "type": "array", + "items": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + }, + "maxLength": 100, + "description": "At least one referenced tag must match (if anyOf is present and non-empty). Typically refs to org.hypercerts.helper.workScopeTag." + }, + "noneOf": { + "type": "array", + "items": { + "type": "ref", + "ref": "com.atproto.repo.strongRef" + }, + "maxLength": 100, + "description": "None of the referenced tags may match. If any excluded tag matches, the contribution or activity is considered out-of-scope." + }, + "createdAt": { + "type": "string", + "format": "datetime", + "description": "Timestamp when this work-scope expression was created." + } + } + } + } + } +} From ff67b0b2247a8d3e788ddf31b8a24d27ac296881 Mon Sep 17 00:00:00 2001 From: holkexyz Date: Wed, 18 Feb 2026 20:54:47 +1000 Subject: [PATCH 5/8] chore: regenerate after rename --- SCHEMAS.md | 30 +++++++++---------- .../org/hypercerts/helper/workScopeExpr.json | 5 +--- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/SCHEMAS.md b/SCHEMAS.md index b673eba5..e593e1ce 100644 --- a/SCHEMAS.md +++ b/SCHEMAS.md @@ -15,21 +15,21 @@ Hypercerts-specific lexicons for tracking impact work and claims. #### Properties -| Property | Type | Required | Description | Comments | -| ------------------------ | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | -| `title` | `string` | ✅ | Title of the hypercert. | maxLength: 256 | -| `shortDescription` | `string` | ✅ | Short summary of this activity claim, suitable for previews and list views. Rich text annotations may be provided via `shortDescriptionFacets`. | maxLength: 3000, maxGraphemes: 300 | -| `shortDescriptionFacets` | `ref` | ❌ | Rich text annotations for `shortDescription` (mentions, URLs, hashtags, etc). | | -| `description` | `string` | ❌ | Optional longer description of this activity claim, including context or interpretation. Rich text annotations may be provided via `descriptionFacets`. | maxLength: 30000, maxGraphemes: 3000 | -| `descriptionFacets` | `ref` | ❌ | Rich text annotations for `description` (mentions, URLs, hashtags, etc). | | -| `image` | `union` | ❌ | The hypercert visual representation as a URI or image blob. | | -| `workScope` | `union` | ❌ | Work scope definition. Either a strongRef to a work-scope logic record (structured, nested logic), or a free-form string for simple or legacy scopes. The work scope record should conform to the org.hypercerts.helper.workScopeTag lexicon. | | -| `startDate` | `string` | ❌ | When the work began | | -| `endDate` | `string` | ❌ | When the work ended | | -| `contributors` | `ref` | ❌ | An array of contributor objects, each containing contributor information, weight, and contribution details. | | -| `rights` | `ref` | ❌ | A strong reference to the rights that this hypercert has. The record referenced must conform with the lexicon org.hypercerts.claim.rights. | | -| `locations` | `ref` | ❌ | An array of strong references to the location where activity was performed. The record referenced must conform with the lexicon app.certified.location. | | -| `createdAt` | `string` | ✅ | Client-declared timestamp when this record was originally created | | +| Property | Type | Required | Description | Comments | +| ------------------------ | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | +| `title` | `string` | ✅ | Title of the hypercert. | maxLength: 256 | +| `shortDescription` | `string` | ✅ | Short summary of this activity claim, suitable for previews and list views. Rich text annotations may be provided via `shortDescriptionFacets`. | maxLength: 3000, maxGraphemes: 300 | +| `shortDescriptionFacets` | `ref` | ❌ | Rich text annotations for `shortDescription` (mentions, URLs, hashtags, etc). | | +| `description` | `string` | ❌ | Optional longer description of this activity claim, including context or interpretation. Rich text annotations may be provided via `descriptionFacets`. | maxLength: 30000, maxGraphemes: 3000 | +| `descriptionFacets` | `ref` | ❌ | Rich text annotations for `description` (mentions, URLs, hashtags, etc). | | +| `image` | `union` | ❌ | The hypercert visual representation as a URI or image blob. | | +| `workScope` | `union` | ❌ | Defines the scope of work covered by this activity claim. May be a string, a structured scope expression (via org.hypercerts.helper.workScopeExpr), or an advanced logic tree (via org.hypercerts.helper.ops). If no inclusion or exclusion constraints are defined, the scope is treated as unconstrained and all work is considered in scope. | | +| `startDate` | `string` | ❌ | When the work began | | +| `endDate` | `string` | ❌ | When the work ended | | +| `contributors` | `ref` | ❌ | An array of contributor objects, each containing contributor information, weight, and contribution details. | | +| `rights` | `ref` | ❌ | A strong reference to the rights that this hypercert has. The record referenced must conform with the lexicon org.hypercerts.claim.rights. | | +| `locations` | `ref` | ❌ | An array of strong references to the location where activity was performed. The record referenced must conform with the lexicon app.certified.location. | | +| `createdAt` | `string` | ✅ | Client-declared timestamp when this record was originally created | | #### Defs diff --git a/lexicons/org/hypercerts/helper/workScopeExpr.json b/lexicons/org/hypercerts/helper/workScopeExpr.json index 313a93f7..bf050fcb 100644 --- a/lexicons/org/hypercerts/helper/workScopeExpr.json +++ b/lexicons/org/hypercerts/helper/workScopeExpr.json @@ -8,10 +8,7 @@ "key": "tid", "record": { "type": "object", - "required": [ - "version", - "createdAt" - ], + "required": ["version", "createdAt"], "properties": { "version": { "type": "integer", From 6467d840ed52ef3b03348e3e7ce88fda90c50991 Mon Sep 17 00:00:00 2001 From: holkexyz Date: Wed, 18 Feb 2026 21:08:39 +1000 Subject: [PATCH 6/8] add knownValues to ops, add workScopeExpr to ERD, update changeset --- .changeset/add-ops-lexicon.md | 33 ++++++++++++------------- ERD.puml | 18 ++++++++++++++ lexicons/org/hypercerts/helper/ops.json | 3 ++- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/.changeset/add-ops-lexicon.md b/.changeset/add-ops-lexicon.md index 792c213b..cdef7acb 100644 --- a/.changeset/add-ops-lexicon.md +++ b/.changeset/add-ops-lexicon.md @@ -2,7 +2,7 @@ "@hypercerts-org/lexicon": minor --- -Add ops lexicon for work scope logic operator nodes +Add work scope logic lexicons (ops and workScopeExpr) **New Features:** @@ -10,26 +10,25 @@ Add ops lexicon for work scope logic operator nodes - Operator node record type for work scope logic expressions - Supports nested boolean logic operations (AND, OR, NOT) - Fields: - - `op` (required): Operator identifier (e.g., 'all', 'any', 'not') + - `op` (required): Operator identifier with knownValues `'all'`, `'any'`, `'not'` - `args` (required): Array of strongRefs pointing to `org.hypercerts.helper.workScopeTag` (leaf atoms) or other `org.hypercerts.helper.ops` records (nested expressions) - `createdAt` (required): Client-declared timestamp - Enables complex work scope logic by allowing operators to reference other operators or scope tags recursively - Operator semantics are defined by consuming applications -**Usage:** - -The `org.hypercerts.helper.ops` lexicon works together with `org.hypercerts.helper.workScopeTag` to create nested boolean logic expressions for work scope definitions. Operators can be referenced via strongRefs in activity records' `workScope` field. +- **New lexicon (`org.hypercerts.helper.workScopeExpr`):** + - A flat boolean expression for simple work-scope definitions: (ALL allOf) AND (ANY anyOf) AND (NONE noneOf) + - Covers the majority of practical scope definitions without recursion + - Fields: + - `version` (required): Schema version integer (start with 1) + - `label`: Optional short human-readable label for UI display + - `description`: Optional longer explanation of scope intent + - `allOf`: Array of strongRefs — all must match for in-scope + - `anyOf`: Array of strongRefs — at least one must match + - `noneOf`: Array of strongRefs — none may match (exclusion) + - `createdAt` (required): Client-declared timestamp + - An empty expression (all arrays absent/empty) represents an unconstrained scope -**Example:** +**Usage:** -```json -{ - "$type": "org.hypercerts.helper.ops", - "op": "all", - "args": [ - { "uri": "at://.../workScopeTag/ipfs", "cid": "..." }, - { "uri": "at://.../workScopeTag/go", "cid": "..." } - ], - "createdAt": "2024-01-01T00:00:00Z" -} -``` +The `org.hypercerts.helper.ops` and `org.hypercerts.helper.workScopeExpr` lexicons work together with `org.hypercerts.helper.workScopeTag` to define work scope logic. Use `workScopeExpr` for simple flat include/exclude/require patterns, and `ops` for full nested boolean logic. Both can be referenced via strongRefs in activity records' `workScope` field. diff --git a/ERD.puml b/ERD.puml index ca17aff7..7d127376 100644 --- a/ERD.puml +++ b/ERD.puml @@ -160,6 +160,19 @@ dataclass workScopeTag { !endif } +' org.hypercerts.helper.workScopeExpr +dataclass workScopeExpr { + !if (SHOW_FIELDS == "true") + version + label? + description? + allOf[]? + anyOf[]? + noneOf[]? + createdAt + !endif +} + ' org.hypercerts.helper.ops dataclass ops { !if (SHOW_FIELDS == "true") @@ -340,8 +353,13 @@ activity::contributors --> contributionDetails activity::rights --> rights activity::locations --> location activity::workScope --> workScopeTag +activity::workScope --> workScopeExpr activity::workScope --> ops +workScopeExpr::allOf --> workScopeTag +workScopeExpr::anyOf --> workScopeTag +workScopeExpr::noneOf --> workScopeTag + ops::args --> workScopeTag ops::args --> ops : "recursive\nnesting" diff --git a/lexicons/org/hypercerts/helper/ops.json b/lexicons/org/hypercerts/helper/ops.json index 8accc4a4..270ce7aa 100644 --- a/lexicons/org/hypercerts/helper/ops.json +++ b/lexicons/org/hypercerts/helper/ops.json @@ -13,7 +13,8 @@ "op": { "type": "string", "maxLength": 64, - "description": "Operator identifier. Semantics are defined by the evaluating application. Examples: 'all', 'any', 'not'." + "knownValues": ["all", "any", "not"], + "description": "Operator identifier. Semantics are defined by the evaluating application. Examples: 'all' (AND), 'any' (OR), 'not' (NOT)." }, "args": { "type": "array", From 4d28df3fbe16a8e54771b2dc5017f472f2756fbf Mon Sep 17 00:00:00 2001 From: holkexyz Date: Wed, 18 Feb 2026 21:11:05 +1000 Subject: [PATCH 7/8] regenerate SCHEMAS.md --- SCHEMAS.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/SCHEMAS.md b/SCHEMAS.md index ba514cdf..3c2d1f93 100644 --- a/SCHEMAS.md +++ b/SCHEMAS.md @@ -278,11 +278,11 @@ Examples: op='all' (AND), op='any' (OR), op='not' (NOT; typically unary). #### Properties -| Property | Type | Required | Description | Comments | -| ----------- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | -| `op` | `string` | ✅ | Operator identifier. Semantics are defined by the evaluating application. Examples: 'all', 'any', 'not'. | maxLength: 64 | -| `args` | `ref` | ✅ | Arguments. Each strongRef should point to either org.hypercerts.helper.workScopeTag (leaf) or org.hypercerts.helper.ops (nested). For op='not', args SHOULD have exactly one element (enforced by clients). | maxLength: 100 | -| `createdAt` | `string` | ✅ | Client-declared timestamp when this record was originally created | | +| Property | Type | Required | Description | Comments | +| ----------- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | +| `op` | `string` | ✅ | Operator identifier. Semantics are defined by the evaluating application. Examples: 'all' (AND), 'any' (OR), 'not' (NOT). | maxLength: 64, Known values: `all`, `any`, `not` | +| `args` | `ref` | ✅ | Arguments. Each strongRef should point to either org.hypercerts.helper.workScopeTag (leaf) or org.hypercerts.helper.ops (nested). For op='not', args SHOULD have exactly one element (enforced by clients). | maxLength: 100 | +| `createdAt` | `string` | ✅ | Client-declared timestamp when this record was originally created | | --- From 5c982e8ea1b84532268c92902430782f91ef58bd Mon Sep 17 00:00:00 2001 From: holkexyz Date: Wed, 18 Feb 2026 23:45:10 +1000 Subject: [PATCH 8/8] update README workScope example to reflect workScopeExpr and ops options --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a4f9a033..367d3e7d 100644 --- a/README.md +++ b/README.md @@ -65,12 +65,16 @@ const activityRecord = { $type: ACTIVITY_NSID, title: "My Impact Work", shortDescription: "Description here", - // workScope can be a strongRef to a work-scope record: + // workScope can be: + // 1. A strongRef to a workScopeExpr (flat boolean expression): workScope: { - uri: "at://did:plc:alice/org.hypercerts.helper.workScopeTag/abc123", + uri: "at://did:plc:alice/org.hypercerts.helper.workScopeExpr/abc123", cid: "...", }, - // OR a simple string: workScope: "Environmental conservation", + // 2. A strongRef to an ops record (nested logic tree): + // { uri: "at://did:plc:alice/org.hypercerts.helper.ops/abc123", cid: "..." } + // 3. A simple string: + // "Environmental conservation", startDate: "2023-01-01T00:00:00Z", endDate: "2023-12-31T23:59:59Z", createdAt: new Date().toISOString(),