ADR-027: Version Auto-Increment on Content Change¶
| Attribute | Value |
|---|---|
| Status | Accepted |
| Date | 2026-02-25 |
| Deciders | Architecture Team |
| Related ADRs | ADR-023 (Content Sync Trigger), ADR-028 (Definition Initial Status) |
| Implementation | Content Synchronization Plan ยง2 (AD-CS-005), ยง4.3 |
Context¶
LabletDefinitions are designed as immutable versioned entities โ once a definition is ACTIVE and in use by LabletSessions, its content (UserSession content, CML topology, grading rules, device definitions) should not change underneath running sessions.
However, the upstream Mosaic authoring platform may republish content for the same form at any time. When ContentSyncService re-syncs an ACTIVE definition and detects a different content_package_hash, the system must handle the content change without disrupting existing sessions.
Three strategies were considered:
- In-place update: Overwrite the existing definition's content (breaks immutability)
- Version auto-increment: Create a new version of the definition, deprecate the old one
- Manual re-creation: Require the user to manually create a new definition version
Decision¶
1. Auto-Increment on Content Change¶
When content sync detects a new content_package_hash on an already-ACTIVE definition:
- Deprecate the current definition (set
status=DEPRECATED, record reason) - Clone the definition into a new version with the patch component incremented
- Apply the new content metadata to the cloned definition
- Activate the new definition (it will be ACTIVE with the fresh content)
Re-sync ACTIVE definition (hash changed):
v1.0.0 (ACTIVE, old hash) โ v1.0.0 (DEPRECATED, reason: "Content updated")
โ v1.0.1 (ACTIVE, new hash) created automatically
2. Version Increment Rules¶
| Current Version | New Version | Rule |
|---|---|---|
1.0.0 |
1.0.1 |
Patch increment (semver) |
2.3.7 |
2.3.8 |
Patch increment |
1.0 |
1.1 |
Minor increment (2-part fallback) |
1 |
1.1 |
Append .1 (1-part fallback) |
3. First Sync (No Change Detection)¶
When a PENDING_SYNC definition is synced for the first time, no version increment occurs. The content is simply recorded and the status transitions to ACTIVE:
4. Same Hash (No Change)¶
When re-sync produces the same content_package_hash, no action is taken beyond updating the synced_at timestamp. The definition remains ACTIVE at its current version.
Rationale¶
Why auto-increment (not in-place update)?¶
- Immutability: Running LabletSessions reference a specific definition version. Changing content under them would be unsafe (topology changes, grading rule changes).
- Auditability: Version history shows exactly when and why content changed.
- Rollback: The old version remains in the system (DEPRECATED, not deleted) and can be restored if needed.
- Session isolation: Sessions created before the change continue using v1.0.0. New sessions use v1.0.1.
Why not manual re-creation?¶
- Requires user intervention for every Mosaic republish (high friction)
- Users may not know when Mosaic content has changed
- Auto-detection during re-sync is a natural trigger point
Why patch increment (not major/minor)?¶
- Content republishes are typically bug fixes or minor updates (not breaking changes)
- Patch increment signals "compatible update" in semver conventions
- Users can override the version string on the new definition if they want a different number
Deprecation linkage¶
The deprecated definition stores:
deprecation_reason: Human-readable explanation (e.g., "Content updated (new hash: abc123...)")replacement_version: Pointer to the new version (e.g., "1.0.1")
This enables the UI to show "This version was superseded by v1.0.1" with a direct link.
Consequences¶
Positive¶
- Preserves immutability guarantee for running sessions
- Automatic handling of upstream content changes (no user intervention for routine updates)
- Full audit trail of content versions
- Clean deprecation linkage for UI navigation
Negative¶
- Version proliferation if Mosaic republishes frequently (mitigated: only triggers on hash change)
- Two definitions momentarily exist during the transition (old DEPRECATED + new ACTIVE)
- Definition cloning must handle all fields correctly (test coverage critical)
Risks¶
- Race condition: two concurrent syncs could create duplicate versions (mitigated:
sync_statusacts as a lock โ only one sync runs at a time per definition) - Clone field omission: a new field added to LabletDefinitionState but not included in the clone logic (mitigated: explicit field mapping in
create_version(), enforced by tests)
Related Documents¶
- Content Synchronization Implementation Plan โ ยง4.3
RecordContentSyncResultCommand - ADR-028: Definition Initial Status