diff --git a/docs/unresolved.md b/docs/unresolved.md
new file mode 100644
index 0000000..c8f6738
--- /dev/null
+++ b/docs/unresolved.md
@@ -0,0 +1,23 @@
+# Unresolved Issues
+
+## [2026-03-24] JUnitSuiteLike mixin not available for ScalaTest 3.2.19 with Scala 3
+
+**Requirement / Bug:**
+CLAUDE.md prescribes that all unit tests should extend `AnyFunSuite with Matchers with JUnitSuiteLike`. However, the `JUnitSuiteLike` trait cannot be resolved in the current build configuration.
+
+**Root Cause (if known):**
+- ScalaTest 3.2.19 for Scala 3 does not provide `JUnitSuiteLike` in any public package.
+- The `co.helmethair:scalatest-junit-runner:0.1.11` dependency does not expose this trait.
+- There is no `org.scalatest:scalatest-junit_3` artifact available for version 3.2.19.
+- The trait may have been removed or changed in the ScalaTest 3.x → Scala 3 migration.
+
+**Attempted Fixes:**
+1. Tried importing from `org.scalatest.junit.JUnitSuiteLike` — not found
+2. Tried importing from `org.scalatestplus.junit.JUnitSuiteLike` — not found
+3. Tried importing from `co.helmethair.scalatest.junit.JUnitSuiteLike` — not found
+4. Attempted to add `org.scalatest:scalatest-junit_3:3.2.19` dependency — artifact does not exist in Maven Central
+
+**Suggested Next Step:**
+1. Either find the correct ScalaTest artifact/import for Scala 3 JUnit integration, or
+2. Update CLAUDE.md to reflect the actual constraint that unit tests should extend `AnyFunSuite with Matchers` (without `JUnitSuiteLike`), or
+3. Investigate whether a different test runner or configuration is needed to achieve JUnit integration with ScalaTest 3 in Scala 3
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index d01d6ea..56bfb9e 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -57,7 +57,9 @@
-
+
+
+
@@ -261,6 +263,14 @@
+
+
+
+
+
+
+
+
diff --git a/modules/core/src/main/scala/de/nowchess/chess/logic/GameContext.scala b/modules/core/src/main/scala/de/nowchess/chess/logic/GameContext.scala
new file mode 100644
index 0000000..7bb2e7b
--- /dev/null
+++ b/modules/core/src/main/scala/de/nowchess/chess/logic/GameContext.scala
@@ -0,0 +1,47 @@
+package de.nowchess.chess.logic
+
+import de.nowchess.api.board.{Board, Color, File, Piece, PieceType, Rank, Square}
+import de.nowchess.api.game.CastlingRights
+
+enum CastleSide:
+ case Kingside, Queenside
+
+case class GameContext(
+ board: Board,
+ whiteCastling: CastlingRights,
+ blackCastling: CastlingRights
+):
+ def castlingFor(color: Color): CastlingRights =
+ if color == Color.White then whiteCastling else blackCastling
+
+ def withUpdatedRights(color: Color, rights: CastlingRights): GameContext =
+ if color == Color.White then copy(whiteCastling = rights)
+ else copy(blackCastling = rights)
+
+object GameContext:
+ /** Convenience constructor for test boards: no castling rights on either side. */
+ def apply(board: Board): GameContext =
+ GameContext(board, CastlingRights.None, CastlingRights.None)
+
+ val initial: GameContext =
+ GameContext(Board.initial, CastlingRights.Both, CastlingRights.Both)
+
+extension (b: Board)
+ def withCastle(color: Color, side: CastleSide): Board =
+ val (kingFrom, kingTo, rookFrom, rookTo) = (color, side) match
+ case (Color.White, CastleSide.Kingside) =>
+ (Square(File.E, Rank.R1), Square(File.G, Rank.R1),
+ Square(File.H, Rank.R1), Square(File.F, Rank.R1))
+ case (Color.White, CastleSide.Queenside) =>
+ (Square(File.E, Rank.R1), Square(File.C, Rank.R1),
+ Square(File.A, Rank.R1), Square(File.D, Rank.R1))
+ case (Color.Black, CastleSide.Kingside) =>
+ (Square(File.E, Rank.R8), Square(File.G, Rank.R8),
+ Square(File.H, Rank.R8), Square(File.F, Rank.R8))
+ case (Color.Black, CastleSide.Queenside) =>
+ (Square(File.E, Rank.R8), Square(File.C, Rank.R8),
+ Square(File.A, Rank.R8), Square(File.D, Rank.R8))
+ val king = Piece(color, PieceType.King)
+ val rook = Piece(color, PieceType.Rook)
+ Board(b.pieces.removed(kingFrom).removed(rookFrom)
+ .updated(kingTo, king).updated(rookTo, rook))
diff --git a/modules/core/src/test/scala/de/nowchess/chess/logic/GameContextTest.scala b/modules/core/src/test/scala/de/nowchess/chess/logic/GameContextTest.scala
new file mode 100644
index 0000000..812a7c9
--- /dev/null
+++ b/modules/core/src/test/scala/de/nowchess/chess/logic/GameContextTest.scala
@@ -0,0 +1,81 @@
+package de.nowchess.chess.logic
+
+import de.nowchess.api.board.*
+import de.nowchess.api.game.CastlingRights
+import org.scalatest.funsuite.AnyFunSuite
+import org.scalatest.matchers.should.Matchers
+
+class GameContextTest extends AnyFunSuite with Matchers:
+
+ private def sq(f: File, r: Rank): Square = Square(f, r)
+ private def board(entries: (Square, Piece)*): Board = Board(entries.toMap)
+
+ test("GameContext.initial has Board.initial and CastlingRights.Both for both sides"):
+ GameContext.initial.board shouldBe Board.initial
+ GameContext.initial.whiteCastling shouldBe CastlingRights.Both
+ GameContext.initial.blackCastling shouldBe CastlingRights.Both
+
+ test("castlingFor returns white rights for Color.White"):
+ GameContext.initial.castlingFor(Color.White) shouldBe CastlingRights.Both
+
+ test("castlingFor returns black rights for Color.Black"):
+ GameContext.initial.castlingFor(Color.Black) shouldBe CastlingRights.Both
+
+ test("withUpdatedRights updates white castling without touching black"):
+ val ctx = GameContext.initial.withUpdatedRights(Color.White, CastlingRights.None)
+ ctx.whiteCastling shouldBe CastlingRights.None
+ ctx.blackCastling shouldBe CastlingRights.Both
+
+ test("withUpdatedRights updates black castling without touching white"):
+ val ctx = GameContext.initial.withUpdatedRights(Color.Black, CastlingRights.None)
+ ctx.blackCastling shouldBe CastlingRights.None
+ ctx.whiteCastling shouldBe CastlingRights.Both
+
+ test("withCastle: white kingside — king e1→g1, rook h1→f1"):
+ val b = board(
+ sq(File.E, Rank.R1) -> Piece.WhiteKing,
+ sq(File.H, Rank.R1) -> Piece.WhiteRook
+ )
+ val after = b.withCastle(Color.White, CastleSide.Kingside)
+ after.pieceAt(sq(File.G, Rank.R1)) shouldBe Some(Piece.WhiteKing)
+ after.pieceAt(sq(File.F, Rank.R1)) shouldBe Some(Piece.WhiteRook)
+ after.pieceAt(sq(File.E, Rank.R1)) shouldBe None
+ after.pieceAt(sq(File.H, Rank.R1)) shouldBe None
+
+ test("withCastle: white queenside — king e1→c1, rook a1→d1"):
+ val b = board(
+ sq(File.E, Rank.R1) -> Piece.WhiteKing,
+ sq(File.A, Rank.R1) -> Piece.WhiteRook
+ )
+ val after = b.withCastle(Color.White, CastleSide.Queenside)
+ after.pieceAt(sq(File.C, Rank.R1)) shouldBe Some(Piece.WhiteKing)
+ after.pieceAt(sq(File.D, Rank.R1)) shouldBe Some(Piece.WhiteRook)
+ after.pieceAt(sq(File.E, Rank.R1)) shouldBe None
+ after.pieceAt(sq(File.A, Rank.R1)) shouldBe None
+
+ test("withCastle: black kingside — king e8→g8, rook h8→f8"):
+ val b = board(
+ sq(File.E, Rank.R8) -> Piece.BlackKing,
+ sq(File.H, Rank.R8) -> Piece.BlackRook
+ )
+ val after = b.withCastle(Color.Black, CastleSide.Kingside)
+ after.pieceAt(sq(File.G, Rank.R8)) shouldBe Some(Piece.BlackKing)
+ after.pieceAt(sq(File.F, Rank.R8)) shouldBe Some(Piece.BlackRook)
+ after.pieceAt(sq(File.E, Rank.R8)) shouldBe None
+ after.pieceAt(sq(File.H, Rank.R8)) shouldBe None
+
+ test("withCastle: black queenside — king e8→c8, rook a8→d8"):
+ val b = board(
+ sq(File.E, Rank.R8) -> Piece.BlackKing,
+ sq(File.A, Rank.R8) -> Piece.BlackRook
+ )
+ val after = b.withCastle(Color.Black, CastleSide.Queenside)
+ after.pieceAt(sq(File.C, Rank.R8)) shouldBe Some(Piece.BlackKing)
+ after.pieceAt(sq(File.D, Rank.R8)) shouldBe Some(Piece.BlackRook)
+ after.pieceAt(sq(File.E, Rank.R8)) shouldBe None
+ after.pieceAt(sq(File.A, Rank.R8)) shouldBe None
+
+ test("GameContext single-arg apply defaults to CastlingRights.None for both sides"):
+ val ctx = GameContext(Board.initial)
+ ctx.whiteCastling shouldBe CastlingRights.None
+ ctx.blackCastling shouldBe CastlingRights.None