packages feed

keiki-0.9.0.0: src/Keiki/Internal/WireSchema.hs

{-# LANGUAGE GADTs #-}
{-# LANGUAGE RoleAnnotations #-}
{-# LANGUAGE TypeAbstractions #-}
{-# LANGUAGE TypeFamilies #-}

-- | Internal constructors and operations for structural wire schemas.
--
-- The public surface is re-exported abstractly from "Keiki.Core". Keeping
-- these constructors in a hidden module prevents consumers from turning
-- names or casts into trusted replay evidence.
module Keiki.Internal.WireSchema
  ( WireSchema,
    WireFieldSchema,
    InCtorSchema,
    InCtorFieldSchema,
    WireCtorPath,
    AppendWireFields,
    AppendInCtorFields,
    WireSchemaAvailability (..),
    InCtorSchemaAvailability (..),
    WireHeadRelation (..),
    InputHeadRelation (..),
    InputWireHeadRelation (..),
    WireFieldAlignment (..),
    WireSchemaComparison (..),
    InCtorFieldAlignment (..),
    InCtorSchemaComparison (..),
    InWireFieldAlignment (..),
    InWireSchemaComparison (..),
    wireSchemaUnavailable,
    wireSchemaAvailability,
    inCtorSchemaUnavailable,
    inCtorSchemaAvailability,
    trustedWireSchema,
    trustedInCtorSchema,
    compositionOnlyWireSchema,
    compositionOnlyInCtorSchema,
    wireFieldsNil,
    wireFieldsCons,
    inCtorFieldsNil,
    inCtorFieldsCons,
    appendWireFieldSchema,
    appendInCtorFieldSchema,
    wireCtorPathRoot,
    prefixWireCtorPathLeft,
    prefixWireCtorPathRight,
    genericPrefixWireCtorPathLeft,
    genericPrefixWireCtorPathRight,
    prefixWireSchemaLeft,
    prefixWireSchemaRight,
    prefixInCtorSchemaLeft,
    prefixInCtorSchemaRight,
    compareWireSchemas,
    compareInCtorSchemas,
    compareInCtorWireSchemas,
    classifyInputWireSchemas,
    inputWireSpineRelationsForTesting,
    inCtorSchemaPath,
    wireSchemaPrefixRelationForTesting,
    inCtorSchemaPrefixRelationForTesting,
  )
where

import Data.Kind (Type)
import Data.Typeable (Typeable)
import GHC.TypeLits (Symbol)
import Type.Reflection (eqTypeRep, typeRep, type (:~~:) (HRefl))

-- | One structural step through either a Generic sum or an explicitly
-- checked 'Either' composition boundary.
data WireCtorPathStep
  = WireCtorPathLeft
  | WireCtorPathRight
  deriving stock (Eq, Show)

-- | A constructor's ordinal path through its carrier's sum tree.
--
-- The carrier is phantom because the type itself supplies the boundary;
-- callers cannot inspect or construct paths outside this hidden module.
newtype WireCtorPath co = WireCtorPath [WireCtorPathStep]

type role WireCtorPath nominal

-- | A typed, ordered description of a constructor's fields. Selector text
-- is diagnostic only; the spine position and 'Typeable' dictionary are the
-- proof evidence.
data WireFieldSchema fields where
  WireFieldsNil :: WireFieldSchema ()
  WireFieldsCons ::
    (Typeable field) =>
    Maybe String ->
    WireFieldSchema rest ->
    WireFieldSchema (field, rest)

type role WireFieldSchema nominal

-- | A typed, ordered description of an input constructor's slots. Slot
-- labels remain diagnostic; the spine position and 'Typeable' dictionary
-- are the proof evidence used to align an input read with an output field.
data InCtorFieldSchema (fields :: [(Symbol, Type)]) where
  InCtorFieldsNil :: InCtorFieldSchema '[]
  InCtorFieldsCons ::
    (Typeable field) =>
    Maybe String ->
    InCtorFieldSchema rest ->
    InCtorFieldSchema ('(name, field) ': rest)

type role InCtorFieldSchema nominal

-- | Typed path from an outer composition carrier back to the one payload type
-- introduced by the polymorphic identity boundary. The root pins payload and
-- carrier to the same type; each prefix records which 'Either' arm preserved
-- that payload. Lockstep comparison can therefore recover payload equality
-- from equal spines without a cast or a 'Typeable' dictionary.
data CompositionOnlySpine carrier payload where
  CompositionOnlyRoot :: CompositionOnlySpine carrier carrier
  CompositionOnlyLeft ::
    CompositionOnlySpine carrier payload ->
    CompositionOnlySpine (Either carrier other) payload
  CompositionOnlyRight ::
    CompositionOnlySpine carrier payload ->
    CompositionOnlySpine (Either other carrier) payload

type role CompositionOnlySpine nominal nominal

-- | Type-level append for the nested-pair field encoding.
type family AppendWireFields (left :: Type) (right :: Type) :: Type where
  AppendWireFields () right = right
  AppendWireFields (field, rest) right =
    (field, AppendWireFields rest right)

-- | Type-level append for input slot lists. Kept private so the schema
-- builder can combine Generic product spines without depending on
-- "Keiki.Core" and creating an import cycle.
type family
  AppendInCtorFields
    (left :: [(Symbol, Type)])
    (right :: [(Symbol, Type)]) ::
    [(Symbol, Type)]
  where
  AppendInCtorFields '[] right = right
  AppendInCtorFields (field ': rest) right =
    field ': AppendInCtorFields rest right

-- | Structural evidence carried by one output wire constructor.
data WireSchema co fields where
  UnavailableWireSchema :: WireSchema co fields
  CompositionOnlyWireSchema ::
    CompositionOnlySpine co field ->
    WireSchema co (field, ())
  TrustedWireSchema ::
    WireCtorPath co ->
    WireFieldSchema fields ->
    WireSchema co fields

type role WireSchema nominal nominal

-- | Structural evidence carried by one input constructor.
data InCtorSchema ci (fields :: [(Symbol, Type)]) where
  UnavailableInCtorSchema :: InCtorSchema ci fields
  CompositionOnlyInCtorSchema ::
    CompositionOnlySpine ci field ->
    InCtorSchema ci '[ '("payload", field)]
  TrustedInCtorSchema ::
    WireCtorPath ci ->
    InCtorFieldSchema fields ->
    InCtorSchema ci fields

type role InCtorSchema nominal nominal

-- | Public observation of whether structural proof evidence is present.
data WireSchemaAvailability
  = WireSchemaTrusted
  | WireSchemaUnavailable
  deriving stock (Eq, Show)

-- | Public observation of whether input-constructor proof evidence exists.
data InCtorSchemaAvailability
  = InCtorSchemaTrusted
  | InCtorSchemaUnavailable
  deriving stock (Eq, Show)

-- | Public, proof-safe classification of two output heads.
data WireHeadRelation
  = WireHeadsStructurallyEqual
  | WireHeadsStructurallyDifferent
  | WireHeadsUnwitnessed
  deriving stock (Eq, Show)

-- | Public, proof-safe classification of two input constructors.
data InputHeadRelation
  = InputHeadsStructurallyEqual
  | InputHeadsStructurallyDifferent
  | InputHeadsUnwitnessed
  deriving stock (Eq, Show)

-- | Public, proof-safe classification of one input constructor against one
-- output wire constructor at the same carrier — the observer form of the
-- checked composition alignment. The typed alignment witness stays internal.
data InputWireHeadRelation
  = InputWireHeadsAligned
  | InputWireHeadsStructurallyDifferent
  | InputWireHeadsUnwitnessed
  deriving stock (Eq, Show)

-- | A position-by-position type alignment between two field spines.
data WireFieldAlignment left right where
  WireFieldsAlignedNil :: WireFieldAlignment () ()
  WireFieldsAlignedCons ::
    (Typeable field) =>
    WireFieldAlignment left right ->
    WireFieldAlignment (field, left) (field, right)

-- | Internal comparison retaining the typed alignment needed by symbolic
-- translation. The public classifier projects this to 'WireHeadRelation'.
data WireSchemaComparison left right where
  WireSchemasEqual ::
    WireFieldAlignment left right ->
    WireSchemaComparison left right
  WireSchemasDifferent :: WireSchemaComparison left right
  WireSchemasUnwitnessed :: WireSchemaComparison left right

-- | Position-by-position alignment between two input slot spines.
data InCtorFieldAlignment left right where
  InCtorFieldsAlignedNil :: InCtorFieldAlignment '[] '[]
  InCtorFieldsAlignedCons ::
    (Typeable field) =>
    InCtorFieldAlignment left right ->
    InCtorFieldAlignment ('(leftName, field) ': left) ('(rightName, field) ': right)

-- | Internal comparison retaining the input-field alignment witness.
data InCtorSchemaComparison left right where
  InCtorSchemasEqual ::
    InCtorFieldAlignment left right ->
    InCtorSchemaComparison left right
  InCtorSchemasDifferent :: InCtorSchemaComparison left right
  InCtorSchemasUnwitnessed :: InCtorSchemaComparison left right

-- | Position-by-position alignment from input slots to an output wire's
-- nested-pair fields. This is the typed bridge used by composition.
data InWireFieldAlignment inputFields wireFields where
  InWireFieldsAlignedNil :: InWireFieldAlignment '[] ()
  InWireFieldsAlignedCons ::
    InWireFieldAlignment inputRest wireRest ->
    InWireFieldAlignment ('(name, field) ': inputRest) (field, wireRest)

-- | Checked structural relationship between an input constructor and an
-- output wire constructor over the same carrier.
data InWireSchemaComparison inputFields wireFields where
  InWireSchemasEqual ::
    InWireFieldAlignment inputFields wireFields ->
    InWireSchemaComparison inputFields wireFields
  InWireSchemasDifferent :: InWireSchemaComparison inputFields wireFields
  InWireSchemasUnwitnessed :: InWireSchemaComparison inputFields wireFields

-- | Explicitly mark a wire as lacking structural proof evidence.
wireSchemaUnavailable :: WireSchema co fields
wireSchemaUnavailable = UnavailableWireSchema

-- | Observe whether a schema is trusted without exposing its evidence.
wireSchemaAvailability :: WireSchema co fields -> WireSchemaAvailability
wireSchemaAvailability UnavailableWireSchema = WireSchemaUnavailable
wireSchemaAvailability CompositionOnlyWireSchema {} = WireSchemaUnavailable
wireSchemaAvailability TrustedWireSchema {} = WireSchemaTrusted

-- | Explicitly mark an input constructor as lacking structural evidence.
inCtorSchemaUnavailable :: InCtorSchema ci fields
inCtorSchemaUnavailable = UnavailableInCtorSchema

-- | Observe whether an input schema is trusted without exposing evidence.
inCtorSchemaAvailability ::
  InCtorSchema ci fields ->
  InCtorSchemaAvailability
inCtorSchemaAvailability UnavailableInCtorSchema = InCtorSchemaUnavailable
inCtorSchemaAvailability CompositionOnlyInCtorSchema {} = InCtorSchemaUnavailable
inCtorSchemaAvailability TrustedInCtorSchema {} = InCtorSchemaTrusted

-- | Internal trusted-schema constructor used only by Generic derivation.
trustedWireSchema ::
  WireCtorPath co ->
  WireFieldSchema fields ->
  WireSchema co fields
trustedWireSchema = TrustedWireSchema

-- | Internal trusted input-schema constructor used only by Generic
-- derivation and checked sum lifting.
trustedInCtorSchema ::
  WireCtorPath ci ->
  InCtorFieldSchema fields ->
  InCtorSchema ci fields
trustedInCtorSchema = TrustedInCtorSchema

-- | Hidden composition-only evidence for the polymorphic identity boundary.
-- It aligns the one payload slot with the one wire field but deliberately
-- remains unavailable to symbolic constructor-identity proofs.
compositionOnlyInCtorSchema ::
  InCtorSchema carrier '[ '("payload", carrier)]
compositionOnlyInCtorSchema = CompositionOnlyInCtorSchema CompositionOnlyRoot

-- | Output-side half of 'compositionOnlyInCtorSchema'.
compositionOnlyWireSchema :: WireSchema carrier (carrier, ())
compositionOnlyWireSchema = CompositionOnlyWireSchema CompositionOnlyRoot

wireFieldsNil :: WireFieldSchema ()
wireFieldsNil = WireFieldsNil

wireFieldsCons ::
  (Typeable field) =>
  Maybe String ->
  WireFieldSchema rest ->
  WireFieldSchema (field, rest)
wireFieldsCons = WireFieldsCons

inCtorFieldsNil :: InCtorFieldSchema '[]
inCtorFieldsNil = InCtorFieldsNil

inCtorFieldsCons ::
  (Typeable field) =>
  Maybe String ->
  InCtorFieldSchema rest ->
  InCtorFieldSchema ('(name, field) ': rest)
inCtorFieldsCons = InCtorFieldsCons

appendWireFieldSchema ::
  WireFieldSchema left ->
  WireFieldSchema right ->
  WireFieldSchema (AppendWireFields left right)
appendWireFieldSchema WireFieldsNil right = right
appendWireFieldSchema (WireFieldsCons label rest) right =
  WireFieldsCons label (appendWireFieldSchema rest right)

appendInCtorFieldSchema ::
  InCtorFieldSchema left ->
  InCtorFieldSchema right ->
  InCtorFieldSchema (AppendInCtorFields left right)
appendInCtorFieldSchema InCtorFieldsNil right = right
appendInCtorFieldSchema (InCtorFieldsCons label rest) right =
  InCtorFieldsCons label (appendInCtorFieldSchema rest right)

-- | The path of a constructor in a carrier with no enclosing sum node.
wireCtorPathRoot :: WireCtorPath co
wireCtorPathRoot = WireCtorPath []

prefixWireCtorPathLeft ::
  WireCtorPath co1 ->
  WireCtorPath (Either co1 co2)
prefixWireCtorPathLeft (WireCtorPath path) =
  WireCtorPath (WireCtorPathLeft : path)

prefixWireCtorPathRight ::
  WireCtorPath co2 ->
  WireCtorPath (Either co1 co2)
prefixWireCtorPathRight (WireCtorPath path) =
  WireCtorPath (WireCtorPathRight : path)

-- | Prefix a path while walking a 'GHC.Generics' sum. The result carrier is
-- supplied by the enclosing trusted schema; this less constrained operation
-- stays hidden with the Generic implementation.
genericPrefixWireCtorPathLeft :: WireCtorPath from -> WireCtorPath to
genericPrefixWireCtorPathLeft (WireCtorPath path) =
  WireCtorPath (WireCtorPathLeft : path)

-- | Right-arm counterpart of 'genericPrefixWireCtorPathLeft'.
genericPrefixWireCtorPathRight :: WireCtorPath from -> WireCtorPath to
genericPrefixWireCtorPathRight (WireCtorPath path) =
  WireCtorPath (WireCtorPathRight : path)

-- | Preserve a trusted schema while crossing a checked sum boundary.
prefixWireSchemaLeft ::
  WireSchema co1 fields ->
  WireSchema (Either co1 co2) fields
prefixWireSchemaLeft UnavailableWireSchema = UnavailableWireSchema
prefixWireSchemaLeft (CompositionOnlyWireSchema spine) =
  CompositionOnlyWireSchema (CompositionOnlyLeft spine)
prefixWireSchemaLeft (TrustedWireSchema path fields) =
  TrustedWireSchema (prefixWireCtorPathLeft path) fields

-- | Preserve a trusted schema while crossing a checked sum boundary.
prefixWireSchemaRight ::
  WireSchema co2 fields ->
  WireSchema (Either co1 co2) fields
prefixWireSchemaRight UnavailableWireSchema = UnavailableWireSchema
prefixWireSchemaRight (CompositionOnlyWireSchema spine) =
  CompositionOnlyWireSchema (CompositionOnlyRight spine)
prefixWireSchemaRight (TrustedWireSchema path fields) =
  TrustedWireSchema (prefixWireCtorPathRight path) fields

-- | Preserve trusted input evidence through a checked left sum boundary.
prefixInCtorSchemaLeft ::
  InCtorSchema ci1 fields ->
  InCtorSchema (Either ci1 ci2) fields
prefixInCtorSchemaLeft UnavailableInCtorSchema = UnavailableInCtorSchema
prefixInCtorSchemaLeft (CompositionOnlyInCtorSchema spine) =
  CompositionOnlyInCtorSchema (CompositionOnlyLeft spine)
prefixInCtorSchemaLeft (TrustedInCtorSchema path fields) =
  TrustedInCtorSchema (prefixWireCtorPathLeft path) fields

-- | Preserve trusted input evidence through a checked right sum boundary.
prefixInCtorSchemaRight ::
  InCtorSchema ci2 fields ->
  InCtorSchema (Either ci1 ci2) fields
prefixInCtorSchemaRight UnavailableInCtorSchema = UnavailableInCtorSchema
prefixInCtorSchemaRight (CompositionOnlyInCtorSchema spine) =
  CompositionOnlyInCtorSchema (CompositionOnlyRight spine)
prefixInCtorSchemaRight (TrustedInCtorSchema path fields) =
  TrustedInCtorSchema (prefixWireCtorPathRight path) fields

-- | Compare two trusted schemas. Paths count as different only when they
-- diverge at a common position. A proper-prefix relation remains
-- unwitnessed because the corresponding match sets can overlap.
compareWireSchemas ::
  WireSchema co left ->
  WireSchema co right ->
  WireSchemaComparison left right
compareWireSchemas UnavailableWireSchema _ = WireSchemasUnwitnessed
compareWireSchemas _ UnavailableWireSchema = WireSchemasUnwitnessed
compareWireSchemas CompositionOnlyWireSchema {} _ = WireSchemasUnwitnessed
compareWireSchemas _ CompositionOnlyWireSchema {} = WireSchemasUnwitnessed
compareWireSchemas
  (TrustedWireSchema (WireCtorPath leftPath) leftFields)
  (TrustedWireSchema (WireCtorPath rightPath) rightFields) =
    case comparePaths leftPath rightPath of
      PathsEqual ->
        maybe
          WireSchemasUnwitnessed
          WireSchemasEqual
          (alignWireFields leftFields rightFields)
      PathsDiverge -> WireSchemasDifferent
      PathsPrefixRelated -> WireSchemasUnwitnessed

-- | Compare two input constructors using path and slot-type evidence.
compareInCtorSchemas ::
  InCtorSchema ci left ->
  InCtorSchema ci right ->
  InCtorSchemaComparison left right
compareInCtorSchemas UnavailableInCtorSchema _ = InCtorSchemasUnwitnessed
compareInCtorSchemas _ UnavailableInCtorSchema = InCtorSchemasUnwitnessed
compareInCtorSchemas CompositionOnlyInCtorSchema {} _ = InCtorSchemasUnwitnessed
compareInCtorSchemas _ CompositionOnlyInCtorSchema {} = InCtorSchemasUnwitnessed
compareInCtorSchemas
  (TrustedInCtorSchema (WireCtorPath leftPath) leftFields)
  (TrustedInCtorSchema (WireCtorPath rightPath) rightFields) =
    case comparePaths leftPath rightPath of
      PathsEqual ->
        maybe
          InCtorSchemasUnwitnessed
          InCtorSchemasEqual
          (alignInCtorFields leftFields rightFields)
      PathsDiverge -> InCtorSchemasDifferent
      PathsPrefixRelated -> InCtorSchemasUnwitnessed

-- | Compare one input constructor with one output wire constructor.
compareInCtorWireSchemas ::
  InCtorSchema carrier inputFields ->
  WireSchema carrier wireFields ->
  InWireSchemaComparison inputFields wireFields
compareInCtorWireSchemas UnavailableInCtorSchema _ = InWireSchemasUnwitnessed
compareInCtorWireSchemas _ UnavailableWireSchema = InWireSchemasUnwitnessed
compareInCtorWireSchemas
  (CompositionOnlyInCtorSchema inputSpine)
  (CompositionOnlyWireSchema wireSpine) =
    compareCompositionOnlySpines inputSpine wireSpine
compareInCtorWireSchemas CompositionOnlyInCtorSchema {} _ = InWireSchemasUnwitnessed
compareInCtorWireSchemas _ CompositionOnlyWireSchema {} = InWireSchemasUnwitnessed
compareInCtorWireSchemas
  (TrustedInCtorSchema (WireCtorPath inputPath) inputFields)
  (TrustedWireSchema (WireCtorPath wirePath) wireFields) =
    case comparePaths inputPath wirePath of
      PathsEqual ->
        maybe
          InWireSchemasUnwitnessed
          InWireSchemasEqual
          (alignInWireFields inputFields wireFields)
      PathsDiverge -> InWireSchemasDifferent
      PathsPrefixRelated -> InWireSchemasUnwitnessed

-- | Regression observers pinning every 'compareInCtorWireSchemas' arm that
-- involves a composition-only spine. Composition-only evidence has no public
-- producer, so the scenarios are constructed here, where the constructors are
-- in scope, and specs assert the full labelled list. Construction-free: the
-- observer returns only proof-safe relations.
inputWireSpineRelationsForTesting :: [(String, InputWireHeadRelation)]
inputWireSpineRelationsForTesting =
  [ ("root/root", classifyInputWireSchemas rootInput rootWire),
    ( "left/left",
      classifyInputWireSchemas
        (prefixInCtorSchemaLeft rootInput :: InCtorSchema (Either Int Bool) '[ '("payload", Int)])
        (prefixWireSchemaLeft rootWire)
    ),
    ( "right/right",
      classifyInputWireSchemas
        (prefixInCtorSchemaRight rootInput :: InCtorSchema (Either Bool Int) '[ '("payload", Int)])
        (prefixWireSchemaRight rootWire)
    ),
    ( "left/right",
      classifyInputWireSchemas
        (prefixInCtorSchemaLeft rootInput :: InCtorSchema (Either Int Int) '[ '("payload", Int)])
        (prefixWireSchemaRight rootWire)
    ),
    ( "root/left-prefixed",
      classifyInputWireSchemas
        (compositionOnlyInCtorSchema :: InCtorSchema (Either Int Bool) '[ '("payload", Either Int Bool)])
        (prefixWireSchemaLeft rootWire)
    ),
    ("composition-only/trusted", classifyInputWireSchemas rootInput trustedRootWire),
    ("trusted/composition-only", classifyInputWireSchemas trustedRootInput rootWire),
    ("unavailable/composition-only", classifyInputWireSchemas inCtorSchemaUnavailable rootWire)
  ]
  where
    rootInput = compositionOnlyInCtorSchema :: InCtorSchema Int '[ '("payload", Int)]
    rootWire = compositionOnlyWireSchema :: WireSchema Int (Int, ())
    trustedRootWire = trustedWireSchema wireCtorPathRoot wireFieldsNil :: WireSchema Int ()
    trustedRootInput = trustedInCtorSchema wireCtorPathRoot inCtorFieldsNil :: InCtorSchema Int '[]

-- | Project 'compareInCtorWireSchemas' to its proof-safe relation, dropping
-- the typed alignment witness. Exists so specs can pin each comparison arm
-- directly without access to internal witnesses.
classifyInputWireSchemas ::
  InCtorSchema carrier inputFields ->
  WireSchema carrier wireFields ->
  InputWireHeadRelation
classifyInputWireSchemas inputSchema wireSchema =
  case compareInCtorWireSchemas inputSchema wireSchema of
    InWireSchemasEqual _ -> InputWireHeadsAligned
    InWireSchemasDifferent -> InputWireHeadsStructurallyDifferent
    InWireSchemasUnwitnessed -> InputWireHeadsUnwitnessed

compareCompositionOnlySpines ::
  CompositionOnlySpine carrier inputField ->
  CompositionOnlySpine carrier wireField ->
  InWireSchemaComparison '[ '("payload", inputField)] (wireField, ())
compareCompositionOnlySpines CompositionOnlyRoot CompositionOnlyRoot =
  InWireSchemasEqual (InWireFieldsAlignedCons InWireFieldsAlignedNil)
compareCompositionOnlySpines CompositionOnlyRoot CompositionOnlyLeft {} =
  InWireSchemasUnwitnessed
compareCompositionOnlySpines CompositionOnlyRoot CompositionOnlyRight {} =
  InWireSchemasUnwitnessed
compareCompositionOnlySpines CompositionOnlyLeft {} CompositionOnlyRoot =
  InWireSchemasUnwitnessed
compareCompositionOnlySpines CompositionOnlyRight {} CompositionOnlyRoot =
  InWireSchemasUnwitnessed
compareCompositionOnlySpines
  (CompositionOnlyLeft inputRest)
  (CompositionOnlyLeft wireRest) =
    compareCompositionOnlySpines inputRest wireRest
compareCompositionOnlySpines CompositionOnlyLeft {} CompositionOnlyRight {} =
  InWireSchemasDifferent
compareCompositionOnlySpines CompositionOnlyRight {} CompositionOnlyLeft {} =
  InWireSchemasDifferent
compareCompositionOnlySpines
  (CompositionOnlyRight inputRest)
  (CompositionOnlyRight wireRest) =
    compareCompositionOnlySpines inputRest wireRest

-- | Hidden symbolic identity: trusted paths become prefix constraints;
-- unavailable evidence remains on the conservative fallback path.
inCtorSchemaPath :: InCtorSchema ci fields -> Maybe [Bool]
inCtorSchemaPath UnavailableInCtorSchema = Nothing
inCtorSchemaPath CompositionOnlyInCtorSchema {} = Nothing
inCtorSchemaPath (TrustedInCtorSchema (WireCtorPath path) _) =
  Just (map stepIsLeft path)
  where
    stepIsLeft WireCtorPathLeft = True
    stepIsLeft WireCtorPathRight = False

-- | Regression observer for the otherwise-unforgeable proper-prefix case.
-- Exported through the testing internals of "Keiki.Core"; it grants no
-- ability to construct trusted evidence.
wireSchemaPrefixRelationForTesting :: WireHeadRelation
wireSchemaPrefixRelationForTesting =
  let root =
        TrustedWireSchema wireCtorPathRoot WireFieldsNil :: WireSchema () ()
      prefixed =
        TrustedWireSchema
          (genericPrefixWireCtorPathLeft wireCtorPathRoot)
          WireFieldsNil ::
          WireSchema () ()
   in case compareWireSchemas root prefixed of
        WireSchemasEqual _ -> WireHeadsStructurallyEqual
        WireSchemasDifferent -> WireHeadsStructurallyDifferent
        WireSchemasUnwitnessed -> WireHeadsUnwitnessed

-- | Regression observer for the input-side proper-prefix rule.
inCtorSchemaPrefixRelationForTesting :: InputHeadRelation
inCtorSchemaPrefixRelationForTesting =
  let root =
        TrustedInCtorSchema wireCtorPathRoot InCtorFieldsNil ::
          InCtorSchema () '[]
      prefixed =
        TrustedInCtorSchema
          (genericPrefixWireCtorPathLeft wireCtorPathRoot)
          InCtorFieldsNil ::
          InCtorSchema () '[]
   in case compareInCtorSchemas root prefixed of
        InCtorSchemasEqual _ -> InputHeadsStructurallyEqual
        InCtorSchemasDifferent -> InputHeadsStructurallyDifferent
        InCtorSchemasUnwitnessed -> InputHeadsUnwitnessed

data PathComparison
  = PathsEqual
  | PathsDiverge
  | PathsPrefixRelated

comparePaths :: [WireCtorPathStep] -> [WireCtorPathStep] -> PathComparison
comparePaths [] [] = PathsEqual
comparePaths [] (_ : _) = PathsPrefixRelated
comparePaths (_ : _) [] = PathsPrefixRelated
comparePaths (left : leftRest) (right : rightRest)
  | left /= right = PathsDiverge
  | otherwise = comparePaths leftRest rightRest

alignWireFields ::
  WireFieldSchema left ->
  WireFieldSchema right ->
  Maybe (WireFieldAlignment left right)
alignWireFields WireFieldsNil WireFieldsNil = Just WireFieldsAlignedNil
alignWireFields WireFieldsNil WireFieldsCons {} = Nothing
alignWireFields WireFieldsCons {} WireFieldsNil = Nothing
alignWireFields
  (WireFieldsCons @fieldLeft _ leftRest)
  (WireFieldsCons @fieldRight _ rightRest) =
    case eqTypeRep (typeRep @fieldLeft) (typeRep @fieldRight) of
      Just HRefl -> WireFieldsAlignedCons <$> alignWireFields leftRest rightRest
      Nothing -> Nothing

alignInCtorFields ::
  InCtorFieldSchema left ->
  InCtorFieldSchema right ->
  Maybe (InCtorFieldAlignment left right)
alignInCtorFields InCtorFieldsNil InCtorFieldsNil = Just InCtorFieldsAlignedNil
alignInCtorFields InCtorFieldsNil InCtorFieldsCons {} = Nothing
alignInCtorFields InCtorFieldsCons {} InCtorFieldsNil = Nothing
alignInCtorFields
  (InCtorFieldsCons @fieldLeft _ leftRest)
  (InCtorFieldsCons @fieldRight _ rightRest) =
    case eqTypeRep (typeRep @fieldLeft) (typeRep @fieldRight) of
      Just HRefl -> InCtorFieldsAlignedCons <$> alignInCtorFields leftRest rightRest
      Nothing -> Nothing

alignInWireFields ::
  InCtorFieldSchema inputFields ->
  WireFieldSchema wireFields ->
  Maybe (InWireFieldAlignment inputFields wireFields)
alignInWireFields InCtorFieldsNil WireFieldsNil = Just InWireFieldsAlignedNil
alignInWireFields InCtorFieldsNil WireFieldsCons {} = Nothing
alignInWireFields InCtorFieldsCons {} WireFieldsNil = Nothing
alignInWireFields
  (InCtorFieldsCons @inputField _ inputRest)
  (WireFieldsCons @wireField _ wireRest) =
    case eqTypeRep (typeRep @inputField) (typeRep @wireField) of
      Just HRefl -> InWireFieldsAlignedCons <$> alignInWireFields inputRest wireRest
      Nothing -> Nothing