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:

sealed trait MigrationStep {
case class Record(
fieldActions: Vector[FieldAction], // Actions at this level
nestedFields: Map[String, MigrationStep] // Recursive children
)
}

CompTime Val via Phantom Types

// Phantom types encode field operations
sealed trait FieldTree
sealed trait Empty extends FieldTree
sealed trait Branch[Name <: String, Op <: Operation, Children <: FieldTree, Siblings <: FieldTree] extends FieldTree
// Builder carries phantom type parameters
final case class MigrationBuilder[A, B, Handled, Provided](...)

Each addTyped/dropTyped call accumulates into the phantom type. At buildTyped, a macro compares these against the actual schema diff:

TypedMigrationBuilderMacro.from[PersonV1].to[PersonV2]
.addTyped(_.address.zip, DynamicValue.string("00000"))
.buildTyped // Compile error if nested field missing

Nb: Macro files are excluded from coverage in build.sbt since macro code executes at comptime and cannot be instrumented for runtime coverage.

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 $8,000
Total paid $0
Status Pending
Submitted February 03, 2026
Last updated February 03, 2026

Contributors

HE

Helmy LuqmanulHakim

@elskow

100%

Sponsors

MA

marianaguzmanguerrero16-dev

@marianaguzmanguerrero16-dev

$4,000
ZI

ZIO

@ZIO

$4,000