Protocol Message Processor
Stage 0 stub + compile-time validator.
The KSP entry point is preserved so KSP wiring stays valid; every code emitter has been removed. Capability returns one stage at a time per the Stages A–H plan in.
Until lands, this processor enforces the rules that are load-bearing right now:
§8 raw-bytes ban.
@ProtocolMessagedata classes cannot carry raw-bytes types in their fields (ReadBuffer,WriteBuffer,PlatformBuffer,ByteArray,ByteBuffer). The walk includes the field's declared type, the inner type of@JvmInline value classwrappers, and the type arguments of generic types. Walks short-circuit when a node is, or extends,com.ditchoom.buffer.codec.Payload— the Payload marker is the documented escape hatch and the consumer takes responsibility for the bytes it holds.R1: adjacent-
@LengthFromrejection.@LengthFrom("X")on field F where X is the field immediately preceding F in the same@ProtocolMessagedata class is a compile error when F has a viable@LengthPrefixedmigration target. Bound fields whose type extends the com.ditchoom.buffer.codec.Payload marker interface are skipped —@LengthPrefixeddoes not yet widen to cover Payload slots ( deferral); R1 expands to cover them once that widening lands.@LengthFromis otherwise reserved for genuine remote-prefix uses (length carried in a non-adjacent field, parsed elsewhere, or parent-passed via@DispatchOn).
** dispatcher rules.** A @ProtocolMessage sealed interface without @DispatchOn is a simple-dispatch parent. Every direct sealed subclass must carry @PacketType(value = N) with N in 0..255, and value must be unique within a parent. Missing/out-of-range/duplicate values are compile errors. Sealed parents carrying @DispatchOn are skipped ( surface).
** @DispatchOn value-class discriminator.** A @ProtocolMessage sealed interface parent carrying @DispatchOn(<DiscriminatorType>::class) is a bit-packed dispatch parent. The discriminator must be a @JvmInline value class with a single non-nullable numeric- scalar inner and exactly one @DispatchValue-annotated val property returning non-nullable Int. Each variant must be a data class with @PacketType(value = N) (N in 0..255, unique within the parent), and must declare the DiscriminatorType as its first constructor parameter so the variant codec reads/writes the byte naturally ( narrow — object and discriminator-from-context shapes are deferred).
** @LengthFrom shape (, slice 4 + ).** For @LengthFrom("siblingField") val payload: T: the bound field type must be either String or List<E> where E is a @ProtocolMessage data class. The referenced parameter must exist as a sibling declared before the bound parameter, and must resolve to a numeric type (Byte / Short / Int / Long / UByte / UShort / UInt / ULong). Diagnostics list the numeric val siblings declared before the bound field. The adjacent-@LengthFrom migration suggestion (R1) is independent and continues to fire when the referenced sibling is the immediately-preceding parameter.
** @When shape (, slices 2–3).** The bound parameter type must be nullable. Two source-expression forms are supported: - Simple-name form @When("siblingField"): the referenced constructor parameter must exist, must be declared before the bound parameter, and must be a non-nullable Boolean. - Dotted form @When("sibling.property"): the sibling must be a constructor parameter declared before the bound parameter; its type must be a value class; the property must be a val declared on that value class with no extra value parameters and a non-nullable Boolean return type. Deeper-than-one-level paths are rejected. Diagnostics for the dotted form name the available Boolean-returning val properties on the resolved sibling type. No constraint on the constructor default expression — KSP cannot inspect default expression trees.
Functions
After the last round, project every analyzed shape into the aggregate schema descriptor (SCHEMA_DRIFT.md). Emitting here — rather than per-round in process — guarantees a single codec-schema.txt covering all @ProtocolMessage types, even when KSP defers symbols across rounds.