From skainet-contributor-skills
Use ONLY when writing or editing unit tests INSIDE the SKaiNET repository — tests under `SKaiNET/skainet-*/src/commonTest/`, `*/jvmTest/`, or `SKaiNET/skainet-test/skainet-test-java/`. Enforces the in-repo test policy: Kotest spec runner on JVM, `kotlin.test` in commonTest, `TensorAssertions.assertTensorClose` with explicit `ToleranceConfig.{STRICT/STANDARD/RELAXED/GRADIENT}`, Java JUnit 5 in `skainet-test-java`. Trigger tokens include `assertTensorClose`, `assertArrayClose`, `ToleranceConfig`, `GroundTruthTensor`. Do NOT fire on a CONSUMER project writing its own tests against SKaiNET as a dependency — those tests don't have access to `skainet-test-groundtruth` and can use any framework they like.
How this skill is triggered — by the user, by Claude, or both
Slash command
/skainet-contributor-skills:skainet-testingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Common unit-test strategy across every SKaiNET module: tolerance-aware tensor comparisons, Kotest as the JVM/common runner, JUnit 5 for the Java mirror, fixed source-set placement.
Common unit-test strategy across every SKaiNET module: tolerance-aware tensor comparisons, Kotest as the JVM/common runner, JUnit 5 for the Java mirror, fixed source-set placement.
*/commonTest/, */jvmTest/, or skainet-test-java/src/test/ is being added or edited.assertTensorClose, ToleranceConfig, GroundTruthTensor, or "compare tensors / floats with tolerance".skainet-data-dsl.skainet-nn-dsl.build.gradle.kts to add a Kotest dependency — coordinate with gradle-multimodule for the catalog accessor and with kmp for the source set.sk.ainet.test.groundtruth.TensorAssertions — never kotlin.test.assertEquals on raw tensor data, never assertArrayEquals with hardcoded epsilons inside production tests.atol from ToleranceConfig (or call ToleranceConfig.forOperation(name)). Bare numeric literals like 1e-5f MUST NOT appear at the call site.STRICT (1e-6) for add / subtract / multiply / divide / relu / flatten / reshape; STANDARD (1e-5) for matmul / conv1d / conv2d / conv3d / sum / mean / variance; RELAXED (1e-4) for sigmoid / gelu / silu / softmax / logSoftmax / leakyRelu; GRADIENT (1e-4) for any backward / autograd assertion; VERY_RELAXED (1e-3) only when the operation has documented numerical instability.commonTest/ whenever they don't reference JVM-only APIs; JVM-only tests live in jvmTest/. Java tests live in skainet-test/skainet-test-java/src/test/java/sk/ainet/java/.commonTest (multiplatform) uses kotlin.test because Kotest's runner is JVM-only — do not import Kotest from commonTest source sets.GroundTruthTensor (or a FloatArray + Shape) and call TensorAssertions.assertTensorClose(...) / assertArrayClose(...). Never call tensor.getData().copyToFloatArray() and then assertEquals element by element.commonTest/ with kotlin.test.jvmTest/ with Kotest.skainet-test-java/src/test/java/... with JUnit 5.FloatArray + Shape, or load a GroundTruthTensor fixture).TensorAssertions.assertTensorClose(expected, actual, atol = ToleranceConfig.STANDARD) — pass rtol only if the comparison is dominated by relative error (rare; default 1e-5f is fine for most cases).TensorAssertions.assertShapeEquals(expected, actual). For Kotest infix style, prefer actual shouldBeCloseTo expected.1e-5f, 0.001f, etc. as tolerance arguments — they live in ToleranceConfig).Kotest StringSpec on JVM (preferred for new tests in JVM-only modules):
import io.kotest.core.spec.style.StringSpec
import sk.ainet.context.DirectCpuExecutionContext
import sk.ainet.lang.tensor.Shape
import sk.ainet.lang.tensor.dsl.tensor
import sk.ainet.lang.types.FP32
import sk.ainet.test.groundtruth.GroundTruthTensor
import sk.ainet.test.groundtruth.TensorAssertions
import sk.ainet.test.groundtruth.ToleranceConfig
class MatmulSpec : StringSpec({
"2x2 matmul matches expected within STANDARD tolerance" {
val ctx = DirectCpuExecutionContext.create()
val a = tensor<FP32, Float>(ctx, FP32::class) {
tensor { shape(2, 2) { from(1f, 2f, 3f, 4f) } }
}
val b = tensor<FP32, Float>(ctx, FP32::class) {
tensor { shape(2, 2) { from(5f, 6f, 7f, 8f) } }
}
val c = a.ops.matmul(a, b)
val expected = GroundTruthTensor(
data = floatArrayOf(19f, 22f, 43f, 50f),
shape = Shape(2, 2)
)
TensorAssertions.assertTensorClose(
expected = expected,
actual = c,
atol = ToleranceConfig.STANDARD
)
}
})
Multiplatform commonTest (no Kotest — kotlin.test only):
import kotlin.test.Test
import sk.ainet.lang.nn.DefaultNeuralNetworkExecutionContext
import sk.ainet.lang.tensor.Shape
import sk.ainet.lang.tensor.dsl.tensor
import sk.ainet.lang.types.FP32
import sk.ainet.test.groundtruth.TensorAssertions
import sk.ainet.test.groundtruth.ToleranceConfig
class SoftmaxCommonTest {
@Test
fun `softmax over a 1x4 row sums to 1 within RELAXED tolerance`() {
val ctx = DefaultNeuralNetworkExecutionContext()
val x = tensor<FP32, Float>(ctx, FP32::class) {
tensor { shape(1, 4) { from(1f, 2f, 3f, 4f) } }
}
val y = x.ops.softmax(x, dim = -1)
val sum = y.ops.sum(y, null)
TensorAssertions.assertArrayClose(
expected = floatArrayOf(1f),
actual = floatArrayOf(sum.data.get(0) as Float),
expectedShape = Shape(1),
actualShape = sum.shape,
atol = ToleranceConfig.RELAXED
)
}
}
Java JUnit 5 mirror — see TensorJavaOpsTest.java:
@Test
void matmul() {
Tensor<?, ?> a = SKaiNET.tensor(ctx, new int[]{2, 3}, DType.fp32(),
new float[]{1f, 2f, 3f, 4f, 5f, 6f});
Tensor<?, ?> b = SKaiNET.tensor(ctx, new int[]{3, 2}, DType.fp32(),
new float[]{7f, 8f, 9f, 10f, 11f, 12f});
Tensor<?, ?> c = TensorJavaOps.matmul(a, b);
assertArrayEquals(new int[]{2, 2}, c.getShape().getDimensions());
float[] result = c.getData().copyToFloatArray();
assertArrayEquals(new float[]{58f, 64f, 139f, 154f}, result, 1e-4f);
}
// from: SKaiNET/skainet-test/skainet-test-java/src/test/java/sk/ainet/java/TensorJavaOpsTest.java:79-93
The Java mirror uses raw assertArrayEquals with a numeric epsilon because Java consumers don't depend on skainet-test-groundtruth. Inside Kotlin tests, TensorAssertions is mandatory.
testImplementation lines, Kotest plugin) — see ../gradle-multimodule/SKILL.md.commonTest vs jvmTest placement — see ../kmp/SKILL.md.../skainet-java-interop/SKILL.md.skainet-data-dsl skill (in the sibling consumer plugin).// WRONG — bare epsilon at the call site
assertEquals(expected[0], actual.data[0] as Float, 1e-5f)
// RIGHT — TensorAssertions + ToleranceConfig
TensorAssertions.assertArrayClose(expected, actualData, expShape, actShape, atol = ToleranceConfig.STANDARD)
// WRONG — Kotest imported into commonTest
import io.kotest.core.spec.style.StringSpec // commonTest cannot run Kotest
// RIGHT — kotlin.test for commonTest, Kotest only in jvmTest / src/test
import kotlin.test.Test
// WRONG — comparing the wrong tolerance
TensorAssertions.assertTensorClose(expected, sigmoidOut, atol = ToleranceConfig.STRICT)
// sigmoid has accumulated transcendental error
// RIGHT — RELAXED for transcendentals
TensorAssertions.assertTensorClose(expected, sigmoidOut, atol = ToleranceConfig.RELAXED)
references/tolerance-table.md — the full operation → tolerance map taken from ToleranceConfig.forOperation.references/assertion-api.md — every public function on TensorAssertions and the extension infix sugar, with signatures.Provides a checklist for code reviews covering functionality, security, performance, maintainability, tests, and quality. Use for pull requests, audits, team standards, and developer training.
npx claudepluginhub skainet-developers/skainet-coding-skills --plugin skainet-contributor-skills