-
Notifications
You must be signed in to change notification settings - Fork 105
BE-162: Refactor OntologyTypeVersion
to support pre-release versions with lane-based drafts
#7872
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BE-162: Refactor OntologyTypeVersion
to support pre-release versions with lane-based drafts
#7872
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #7872 +/- ##
==========================================
- Coverage 55.13% 55.12% -0.02%
==========================================
Files 1103 1103
Lines 98529 98664 +135
Branches 4575 4589 +14
==========================================
+ Hits 54325 54387 +62
- Misses 43585 43653 +68
- Partials 619 624 +5
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…d enhance documentation for pre-release versioning
Graphite Automations"Request Rust reviewers once CI passes" took an action on this PR • (10/15/25)1 reviewer was added to this PR based on Tim Diekmann's automation. "Request backend reviewers once CI passes" took an action on this PR • (10/15/25)1 reviewer was added to this PR based on Tim Diekmann's automation. |
685fdfb
to
680f50c
Compare
…action and add test for draft in lane identifier
Your organization requires reapproval when changes are made, so Graphite has dismissed approvals. See the output of git range-diff
at https://github.com/hashintel/hash/actions/runs/18561030442
…id lane identifiers and build metadata
…r incorrect revisions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thank you 🎉
Benchmark results
|
Function | Value | Mean | Flame graphs |
---|---|---|---|
resolve_policies_for_actor | user: empty, selectivity: high, policies: 10002 | Flame Graph | |
resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
resolve_policies_for_actor | user: empty, selectivity: medium, policies: 5001 | Flame Graph | |
resolve_policies_for_actor | user: seeded, selectivity: high, policies: 27604 | Flame Graph | |
resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 13450 | Flame Graph | |
resolve_policies_for_actor | user: system, selectivity: high, policies: 11308 | Flame Graph | |
resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
resolve_policies_for_actor | user: system, selectivity: medium, policies: 5628 | Flame Graph |
policy_resolution_large
Function | Value | Mean | Flame graphs |
---|---|---|---|
resolve_policies_for_actor | user: empty, selectivity: high, policies: 2002 | Flame Graph | |
resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
resolve_policies_for_actor | user: empty, selectivity: medium, policies: 1001 | Flame Graph | |
resolve_policies_for_actor | user: seeded, selectivity: high, policies: 3314 | Flame Graph | |
resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 1526 | Flame Graph | |
resolve_policies_for_actor | user: system, selectivity: high, policies: 2078 | Flame Graph | |
resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
resolve_policies_for_actor | user: system, selectivity: medium, policies: 1033 | Flame Graph |
policy_resolution_medium
Function | Value | Mean | Flame graphs |
---|---|---|---|
resolve_policies_for_actor | user: empty, selectivity: high, policies: 102 | Flame Graph | |
resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
resolve_policies_for_actor | user: empty, selectivity: medium, policies: 51 | Flame Graph | |
resolve_policies_for_actor | user: seeded, selectivity: high, policies: 269 | Flame Graph | |
resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 107 | Flame Graph | |
resolve_policies_for_actor | user: system, selectivity: high, policies: 133 | Flame Graph | |
resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
resolve_policies_for_actor | user: system, selectivity: medium, policies: 63 | Flame Graph |
policy_resolution_none
Function | Value | Mean | Flame graphs |
---|---|---|---|
resolve_policies_for_actor | user: empty, selectivity: high, policies: 2 | Flame Graph | |
resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
resolve_policies_for_actor | user: empty, selectivity: medium, policies: 1 | Flame Graph | |
resolve_policies_for_actor | user: system, selectivity: high, policies: 8 | Flame Graph | |
resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
resolve_policies_for_actor | user: system, selectivity: medium, policies: 3 | Flame Graph |
policy_resolution_small
Function | Value | Mean | Flame graphs |
---|---|---|---|
resolve_policies_for_actor | user: empty, selectivity: high, policies: 52 | Flame Graph | |
resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
resolve_policies_for_actor | user: empty, selectivity: medium, policies: 25 | Flame Graph | |
resolve_policies_for_actor | user: seeded, selectivity: high, policies: 94 | Flame Graph | |
resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 26 | Flame Graph | |
resolve_policies_for_actor | user: system, selectivity: high, policies: 66 | Flame Graph | |
resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
resolve_policies_for_actor | user: system, selectivity: medium, policies: 29 | Flame Graph |
representative_read_entity
Function | Value | Mean | Flame graphs |
---|---|---|---|
entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/block/v/1
|
Flame Graph | |
entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/book/v/1
|
Flame Graph | |
entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/building/v/1
|
Flame Graph | |
entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/organization/v/1
|
Flame Graph | |
entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/page/v/2
|
Flame Graph | |
entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/person/v/1
|
Flame Graph | |
entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/playlist/v/1
|
Flame Graph | |
entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/song/v/1
|
Flame Graph | |
entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/uk-address/v/1
|
Flame Graph |
representative_read_entity_type
Function | Value | Mean | Flame graphs |
---|---|---|---|
get_entity_type_by_id | Account ID: bf5a9ef5-dc3b-43cf-a291-6210c0321eba
|
Flame Graph |
representative_read_multiple_entities
Function | Value | Mean | Flame graphs |
---|---|---|---|
entity_by_property | traversal_paths=0 | 0 | |
entity_by_property | traversal_paths=255 | 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true | |
entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false | |
entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true | |
entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true | |
entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true | |
link_by_source_by_property | traversal_paths=0 | 0 | |
link_by_source_by_property | traversal_paths=255 | 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true | |
link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false | |
link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true | |
link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true | |
link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true |
scaling_read_entity_complete_one_depth
Function | Value | Mean | Flame graphs |
---|---|---|---|
entity_by_id | 1 entities | Flame Graph | |
entity_by_id | 10 entities | Flame Graph | |
entity_by_id | 25 entities | Flame Graph | |
entity_by_id | 5 entities | Flame Graph | |
entity_by_id | 50 entities | Flame Graph |
scaling_read_entity_complete_zero_depth
Function | Value | Mean | Flame graphs |
---|---|---|---|
entity_by_id | 1 entities | Flame Graph | |
entity_by_id | 10 entities | Flame Graph | |
entity_by_id | 25 entities | Flame Graph | |
entity_by_id | 5 entities | Flame Graph | |
entity_by_id | 50 entities | Flame Graph |
scaling_read_entity_linkless
Function | Value | Mean | Flame graphs |
---|---|---|---|
entity_by_id | 1 entities | Flame Graph | |
entity_by_id | 10 entities | Flame Graph | |
entity_by_id | 100 entities | Flame Graph | |
entity_by_id | 1000 entities | Flame Graph | |
entity_by_id | 10000 entities | Flame Graph |
scenarios
Function | Value | Mean | Flame graphs |
---|---|---|---|
full_test | query-limited | Flame Graph | |
full_test | query-unlimited | Flame Graph | |
linked_queries | query-limited | Flame Graph | |
linked_queries | query-unlimited | Flame Graph |
🌟 What is the purpose of this PR?
This PR refactors the ontology type version system to support semantic versioning with pre-release/draft versions. Previously,
OntologyTypeVersion
was a simpleu32
wrapper representing only published versions. Now it supports draft versions with lane-based isolation, enabling parallel development workflows while maintaining proper version ordering semantics.The new URL format supports both published versions (
v/2
) and draft versions (v/2-draft.lane.5
), following semantic versioning conventions where published versions have higher precedence than pre-release versions with the same major version.🔗 Related links
🔍 What does this change?
Core Type System Changes
OntologyTypeVersion
structure (libs/@blockprotocol/type-system/rust/src/ontology/id/mod.rs
)u32
wrapper to struct withmajor: u32
andpre_release: Option<PreRelease>
PreRelease
enum to support extensible pre-release types (currently onlyDraft
variant)major-draft.lane.revision
(e.g.,2-draft.abcd1234.5
)v1.alpha.3
)libs/@blockprotocol/type-system/rust/src/ontology/id/mod.rs
)libs/@blockprotocol/type-system/rust/src/ontology/id/error.rs
)ParseOntologyTypeVersionError
for version-specific errors with three variants:MissingVersion
- Empty version stringParseVersion(String)
- Invalid major version numberInvalidPreRelease(String, ParseDraftInfoError)
- Draft format validation failuresParseDraftInfoError
for draft format validation with four variants:IncorrectFormatting
- Missing dot separator or empty laneInvalidLane(String)
- SemVer-invalid lane identifierMissingRevision
- Empty revision after dotInvalidRevision(String, String)
- Non-numeric or out-of-range revisionParseVersionedUrlError::InvalidVersion
to use structured error types instead of stringsTypeScript/JavaScript Changes
libs/@blockprotocol/type-system/typescript/src/native/url.ts
)u32
rangelibs/@blockprotocol/type-system/typescript/src/native/url.ts
)compareOntologyTypeVersions()
now properly handles draft versionsisDraftVersion()
,extractMajorVersion()
,haveSameMajorVersion()
libs/@blockprotocol/type-system/typescript/src/native/url.ts
)["version", "error string"]
to["version", { reason: "...", inner: "..." }]
Database and API Updates
libs/@local/graph/
)OntologyTypeVersion::new()
calls to struct initialization.inner()
calls to.major
field accessToSql
/FromSql
implementations to handle drafts (with TODO for full support)Testing Improvements
Comprehensive Rust Test Suite
Refactored existing tests to use
from_str()
and proper error handling:OntologyTypeVersion::new()
to parsing from stringsResult<(), Box<dyn Error>>
for better error propagationAdded 11 new comprehensive test functions covering all edge cases:
ontology_version_parsing
- Tests parsing both published (1
,42
) and draft (2-draft.abcd1234.5
) version strings, verifies structure and roundtripontology_version_roundtrip
- Ensures parse → serialize → parse consistency for multiple version formatsontology_version_ordering_same_major
- Tests SemVer ordering rules for same major version (published > draft, revision ordering)ontology_version_ordering_different_major
- Tests major version precedence across published and draft versionsontology_version_ordering_different_lanes
- Tests lane-based ordering (lexicographic comparison)ontology_version_ordering_semver_prerelease_rules
- Comprehensive SemVer validation:2 < 10
)alpha < beta
)999 < 123abc
)ontology_version_equality
- Tests equality comparisons for published and draft versionsdraft_url_parsing
- Tests full URL parsing with draft versions and roundtripinvalid_prerelease_identifiers_rejected
- Tests rejection of:abc,def
)missing_lane_rejected
- Tests edge case:1-draft.123
(only revision, no lane separator)ParseDraftInfoError::IncorrectFormatting
missing_revision_rejected
- Tests edge case:1-draft.lane.
(lane present but empty revision)ParseDraftInfoError::MissingRevision
with exact input trackinginvalid_revision_rejected
- Tests rejection of:lane.abc
) →InvalidRevision
with message validationlane.-5
) →InvalidRevision
with exact value trackingmultiple_dots_in_lane_accepted
- Tests hierarchical lanes (lane.with.dots.5
,v1.alpha.3.10
)draft_in_lane_accepted
- Tests lane names containing "draft" (my-draft.2
)build_is_rejected
- Tests rejection of SemVer build metadata:1-draft.lane.1+build.123
)1+build.123
)1+build-draft.123
)Key testing improvements:
TypeScript Tests
Added TypeScript tests (
libs/@blockprotocol/type-system/typescript/test/url.test.ts
)Integration Coverage
ToSql
implementation forOntologyTypeVersion
includes an error (viatodo!()
) when attempting to serialize draft versions. This is intentional - draft support in the database layer is tracked separately in BE-161.🐾 Next steps
error-stack
for better error handling (tracked in BE-160 subtask)🛡 What tests cover this?
Rust Tests (in
libs/@blockprotocol/type-system/rust/src/ontology/id/mod.rs
)Core functionality tests:
ontology_version_parsing
- Parsing published and draft versionsontology_version_roundtrip
- Serialize/deserialize consistencyontology_version_ordering_same_major
- SemVer ordering (same major)ontology_version_ordering_different_major
- Major version precedenceontology_version_ordering_different_lanes
- Lane-based orderingontology_version_ordering_semver_prerelease_rules
- Complete SemVer compliance validationontology_version_equality
- Equality comparisonsdraft_url_parsing
- Full URL parsing with draft versionsError validation tests:
invalid_prerelease_identifiers_rejected
- Special characters and Unicode rejectionmissing_lane_rejected
- Edge case: only revision without lane (1-draft.123
)missing_revision_rejected
- Edge case: lane without revision (1-draft.lane.
)invalid_revision_rejected
- Non-numeric (abc
), negative (-5
), and overflow casesEdge case tests:
multiple_dots_in_lane_accepted
- Hierarchical lane names (lane.with.dots
)draft_in_lane_accepted
- Lane names containing "draft"build_is_rejected
- SemVer build metadata rejectionUpdated existing tests:
base_url_test
- Updated for new error typesversioned_url_test
- Updated for structured errorsrecord_id_conversions
- Updated for new structureTypeScript Tests (in
libs/@blockprotocol/type-system/typescript/test/url.test.ts
)isDraftVersion
,extractMajorVersion
, etc.)Integration Coverage
❓ How to test this?
Test Published Versions (No Behavior Change)
cargo nextest run --package type-system
turbo run test --filter '@blockprotocol/type-system'
Test Draft Version Parsing
Test Version Ordering
v/2
>v/2-draft.x.y
v/3
>v/2
>v/1
v/2-draft.a.1
<v/2-draft.b.1
<v/2-draft.b.2
Test Error Handling and Edge Cases
Verify error messages provide detailed context:
1-draft.123
→IncorrectFormatting
1-draft.lane.
→MissingRevision
1-draft.lane.abc
→InvalidRevision("abc", "invalid digit...")
2-draft.lane.-5
→InvalidRevision("-5", "invalid digit...")
Test Database Limitations (Expected Failure)
Attempting to serialize draft version to Postgres should panic with TODO message:
📹 Demo
No visual demo needed - this is infrastructure work for the type system. The changes are visible in:
Example of new URL format support: