Kotlin
Skill SKaiNET-developers/SKaiNET-coding-skills/skainet-contributor-skills/skills/kotlin
Use ONLY when editing Kotlin (`.kt`) source files INSIDE the SKaiNET repository (i.e. you're contributing to SKaiNET itself, not consuming it as a library). Enforces project idioms: explicit-API mode, package layout under `sk.ainet.*`, sealed hierarchies, `value class` for type-safe wrappers, no Java-style getters in Kotlin code. Do NOT fire when the user is writing application code that depends on SKaiNET as a library — that's the consumer plugin's territory. Does not fire on DSL invocation sites, build files, or test files within SKaiNET either (those are covered by other contributor skills).From its SKILL.md
npx -y skills add SKaiNET-developers/SKaiNET-coding-skills --skill kotlinAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
7.3 KB, ~1.7k tokens by cl100k_base, as published. Nobody here has run it
kotlin
Idiomatic Kotlin coding rules for production code in SKaiNET. Covers package layout, API stability, nullability, sealed hierarchies, and the expect/actual boundary. Style topics that are project-specific.
When to use
- Editing or adding a
.ktfile under any module'scommonMain/,jvmMain/, or platform-specific main source set. - Reviewing whether a public API is shaped correctly (explicit visibility, no leaked Java-style getters, correct
@PublishedApidiscipline). - Deciding whether something should be a
data class,value class,sealed class,sealed interface, orobject.
When NOT to use
- Writing inside a
tensor { },pipeline<...>(),sequential<...> { }, ordag { }block — those are the DSL skills. - Editing
build.gradle.kts,settings.gradle.kts,libs.versions.toml, or files underbuild-logic/— that'sgradle-multimodule. - Writing tests — that's
skainet-testing. - Deciding which source set a file belongs in — that's
kmp.
Hard rules
explicitApi()is on for everykotlin { }block in this project. Every public top-level declaration MUST carry an explicit visibility modifier (public,internal,private). Do not writefun foo()at top level — writepublic fun foo()orinternal fun foo().- Package layout is
sk.ainet.<area>[.<sub-area>]. New files MUST live undersrc/<sourceset>/kotlin/sk/ainet/.... Do not introduce new top-level packages. - No Java-style getters. Expose Kotlin properties (
val,var) — nevergetFoo()/setFoo()on a Kotlin class. Java consumers reach Kotlin through the dedicated facades insk/ainet/java/(covered byskainet-java-interop). value classfor any wrapper around a primitive that has semantic meaning (e.g. tensor IDs, layer names, axis indices). Not for things that need equality on multiple fields — those aredata class.- Sealed hierarchies for closed sets of variants. Initialization strategies, layer kinds, dtype tags use
sealed class/sealed interface. Do not useenum classif the variants carry data (seeInitializationTypeinTensorDSL.kt). - Nullability is meaningful. A non-null type means "always present"; a
T?type means "callers must handle absence." Never use!!to silence the compiler outside a documented invariant — preferrequireNotNull(x) { "<reason>" }so the failure is loud. - Coroutines are structured. Suspend functions belong on a
CoroutineScopeprovided by the caller; never launch intoGlobalScope. Hot streams useFlow; cold one-shot APIs use suspend functions. - Public-API additions are gated by binary-compatibility-validator. When the build fails because
*.apichanged, regenerate the dump (./gradlew apiDump) and own the change in the same commit — don't suppress the check. @PublishedApi internalis the only way to expose internals to inline functions. Don't widen visibility just to satisfyinline fun.
Workflow
- Locate the right file: open the existing
sk.ainet.<area>package the change belongs to. Don't create a new package without strong justification. - Decide the right shape: data class, value class, sealed hierarchy, object, or plain function. Match what surrounding code already does.
- Write the declaration with explicit visibility. Add KDoc only when the why isn't obvious from the name.
- If the type crosses the JVM/Java boundary, hand off to
skainet-java-interopfor the facade — keep the Kotlin definition idiomatic. - If the change breaks a
*.apidump, regenerate it (./gradlew :module:apiDump) and include the diff in the same change.
Canonical examples
Explicit API + sealed hierarchy (used for tensor initialisation):
public sealed class InitializationType<out V> {
public object Zeros : InitializationType<Nothing>()
public object Ones : InitializationType<Nothing>()
public data class Fill<V>(val value: Number) : InitializationType<V>()
public data class Normal<V>(val mean: Float, val std: Float, val random: Random) : InitializationType<V>()
public data class Uniform<V>(val min: Float, val max: Float, val random: Random) : InitializationType<V>()
public data class Custom<V>(val generator: (indices: IntArray) -> V) : InitializationType<V>()
public data class RandomCustom<V>(val generator: (random: Random) -> V, val random: Random) :
InitializationType<V>()
}
// from: SKaiNET/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/dsl/TensorDSL.kt:272-281
Data-shape DSL with explicit visibility on every entry point:
@TensorDsl
public fun <T : DType, V> tensor(
executionContext: ExecutionContext,
dtype: KClass<T>,
content: TensorDefineDsl<T, V>.() -> Tensor<T, V>
): Tensor<T, V> { ... }
// from: SKaiNET/skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/dsl/TensorDSL.kt:17-25
Module-level KMP plugins (so explicit-API is enforced):
kotlin {
explicitApi()
// ... targets ...
}
// from: SKaiNET/skainet-lang/skainet-lang-core/build.gradle.kts:14-16
Related skills
- Source-set placement (
commonMainvsjvmMainvsiosArm64Main) — see../kmp/SKILL.md. - Adding the file to a new module or registering it in the build — see
../gradle-multimodule/SKILL.md. - Bridging the new declaration to Java consumers — see
../skainet-java-interop/SKILL.md.
Anti-patterns
// WRONG — implicit visibility, will fail explicit-API check
fun computeDiff(expected: Float, actual: Float): Float = expected - actual
// RIGHT
public fun computeDiff(expected: Float, actual: Float): Float = expected - actual
// WRONG — Java-style accessors on a Kotlin class
public class Layer { fun getInChannels(): Int = inChannels }
// RIGHT — Kotlin property
public class Layer { public val inChannels: Int get() = ... }
// WRONG — variants with payload as enum
public enum class InitKind { ZEROS, ONES, FILL /* value? */ }
// RIGHT — sealed hierarchy carries data
public sealed class InitializationType<out V> { ... }
// WRONG — `!!` silently asserts a hidden invariant
val x = map[id]!!
// RIGHT — surface the invariant in the failure message
val x = requireNotNull(map[id]) { "missing layer id=$id" }
References
references/style-rules.md— explicit-API, package, naming, KDoc rules with one-line examples.references/api-stability.md— binary-compatibility-validator workflow and@PublishedApidiscipline.
What ships with it: 3 files
8.3 KB alongside SKILL.md
evals/
- evals.json2.3 KB
references/
- api-stability.md2.8 KB
- style-rules.md3.3 KB