ZI

Implements #519 /claim #519

Highlight: Hierarchical Tree vs Flat Action List

The spec proposes flat Vector[MigrationAction] where each action carries its path:

// Spec approach
case class DynamicMigration(actions: Vector[MigrationAction])
case class AddField(at: DynamicOptic, default: SchemaExpr[?]) extends MigrationAction

Per JDG feedback

“nested migrations can’t be flat, must be lists of lists where top level encodes depth”

, this implementation uses a recursive tree:

// This implementation
sealed trait MigrationStep {
case class Record(
fieldActions: Vector[FieldAction], // Actions at this level
nestedFields: Map[String, MigrationStep] // Recursive children
)
}
sealed trait FieldAction {
case class Add(name: String, defaultValue: DynamicValue) // No path, just name
}

This enables compile-time validation of deeply nested paths:

case class PersonV1(name: String, address: Address)
case class PersonV2(name: String, address: AddressV2)
case class AddressV2(street: String, city: String, zip: String) // nested field added
// .build validates that address.zip is handled, not just top-level fields
MigrationBuilder.from[PersonV1].to[PersonV2]
.add(_.address.zip, DynamicValue.Primitive("00000"))
.build // Compile error if nested field missing

Other Diff

Spec Implementation Justification
SchemaExpr[A, ?] DynamicValueTransform + PrimitiveConversion Separate value transforms from type conversions
Actions carry at: DynamicOptic Tree structure encodes path implicitly Cleaner nested validation, natural recursion for reverse

Claim

Total prize pool $4,000
Total paid $0
Status Pending
Submitted February 03, 2026
Last updated February 03, 2026

Contributors

HE

Helmy LuqmanulHakim

@elskow

100%

Sponsors

ZI

ZIO

@ZIO

$4,000