Kraken’s nightly architectural scan flagged that KrillAppMeta.kt and KrillAppEmit.kt both exhaustively when over KrillApp subtypes with no test-time enforcement that their contracts stay in sync. The concern was that a new subtype added to one but not the other would cause silent UI state omission.
The sealed-class when in both files IS compile-time exhaustive for regular KrillApp subtypes — the compiler enforces both are updated simultaneously. However, meta() and processor() intentionally diverge for Avatar and MenuCommand types: meta() returns gracefully (Avatar → AvatarMetadata, MenuCommand → ServerMetaData placeholder), while processor() throws IllegalStateException. This intentional asymmetry had no test coverage. Commit a009a63ee added the explicit arms to KrillAppEmit in #567 but omitted matching meta() contract tests.
KrillAppMetaContractTest in shared/src/commonTest/kotlin/krill/zone/shared/ with four tests:
meta() succeeds for every allKrillApps entry (the processable types)Avatar.meta() returns AvatarMetadata without throwingMenuCommand.*.meta() return ServerMetaData without throwingallKrillApps excludes Avatar and MenuCommand subtypesKrillAppEmitExhaustiveTest), simultaneously add a test for the non-throwing path in the sibling function (KrillAppMeta.meta()). The two files share a contract; one-sided test coverage is a gap.when provides compile-time protection for new non-Avatar/non-MenuCommand subtypes — but does not protect the intentional behavioral asymmetry between meta() and processor() for no-processor types. That gap can only be caught by a runtime test.