# Unreleased: 0.6.0

_Not released yet: these changes are on `main`, and the `latest` book describes them._

To try them, depend on `0.6.0-SNAPSHOT` from the snapshots repository, as [Gradle SNAPSHOT Configuration](../tooling/manual_setup.md#gradle-snapshot-configuration) shows.

~~~admonish info title="At a glance"
- **javac checks what each step of a Path chain returns.** A step of another kind of Path, or one that fails with an error the chain does not carry, is a compile error in the IDE as well as the build.
- **`@ImportOptics` pairs a JavaBeans-spelt wither with its getter**: a `witheMail` beside `geteMail()` gives an `eMail()` lens.
- **A `@GenerateFocus` message that offers a navigator asks first for the renames it needs.** Following its fix line no longer leads into another note or error.
- **A `@GenerateMapping` bean refusal's fix line compiles once followed**, and names the near miss it passed over, such as `setx(int)` beside `Integer getX()`.
- **A timeout cancels the task it timed out, as a `Scope`'s timeout does**, so a retried timeout leaves no attempt running.
~~~

---

## Mapping {#mapping}

- **A bean property's fix line compiles once followed** ([#1149](https://github.com/higher-kinded-j/higher-kinded-j/issues/1149)). The setter offered for `eMails` is `seteMails`, not `setEMails`. A rename to a keyword such as `default` is offered no marker, and a marker replaces the spec's own rename. A near miss such as `setx(int)` beside `Integer getX()` is named. See [When an unpaired accessor is refused](../mapping/rules.md#unpaired-accessors).
- **A mapping over a deprecated type compiles under `-Werror`** ([#1381](https://github.com/higher-kinded-j/higher-kinded-j/issues/1381)). A deprecated v1 DTO, a bean's deprecated getter or a protobuf-java field declared `deprecated` drew warnings inside the generated Impl, where your own `@SuppressWarnings` cannot reach. A `@GenerateMapping` or `@GenerateMerge` Impl that names or calls a deprecated element now suppresses `deprecation`, or `removal`, on its own class. See [Build-time impact](../optics/production_readiness.md#build-time-impact).
- **Two more PATCH-bean fix lines compile once followed** ([#1293](https://github.com/higher-kinded-j/higher-kinded-j/issues/1293)). A legacy setter such as `setTimeout(Integer)` beside a rename of `timeout` is offered no `@Unmapped` marker, since the rename takes that name. A primitive word such as `setx(int)` is offered its wrapper, `setX(Integer)`. See [A method that only starts like an accessor](../mapping/rules.md#word-accessors).
- **A mapping keeps every entry of an `IdentityHashMap`** ([#1483](https://github.com/higher-kinded-j/higher-kinded-j/issues/1483)). A same-typed `Map` component's copy, and `ValidatedPrism`'s `parseValues` and `buildValues`, rebuilt it as a `LinkedHashMap`, merging keys that were equal but not the same object. An `IdentityHashMap` now comes back one, with every key, as a map optic's result does. See [Same-typed containers cross as copies](../mapping/rules.md#same-typed-containers-cross-as-copies).
- **A nullness checker can check a generated mapping's copies and guards** ([#1431](https://github.com/higher-kinded-j/higher-kinded-j/issues/1431)). Its copy helpers, `parse` guards and leaf cache took or held a `null` without declaring it, so NullAway, run over a `@NullMarked` package, failed inside the Impl. Each now declares its nulls. See [Same-typed containers cross as copies](../mapping/rules.md#same-typed-containers-cross-as-copies).
- **The processor catches a `@Nullable` component that `build` would copy into one declared non-null** ([#1431](https://github.com/higher-kinded-j/higher-kinded-j/issues/1431)). `build` wrote the domain's `null` into a record component, setter or builder setter declared non-null, as a plain merge did into its target. Mark the site `@Nullable`, or declare the source non-null. See [A `@Nullable` component is not copied into a non-null one](../mapping/rules.md#nullable-copy-into-non-null).

---

## Optics {#optics}

- **Optics over a deprecated type compile under `-Werror`** ([#1375](https://github.com/higher-kinded-j/higher-kinded-j/issues/1375)). A record with a `HashBag` component, deprecated in commons-collections4 4.6.0, or any component of a deprecated type, drew warnings inside the generated lenses, traversals and focus classes, where your own `@SuppressWarnings` cannot reach. Each generated member that names a deprecated class now suppresses `deprecation`, or `removal`, itself. See [Build-time impact](../optics/production_readiness.md#build-time-impact).
- **Traversals over commons-collections4's `HashMultiSet`** ([#1379](https://github.com/higher-kinded-j/higher-kinded-j/issues/1379)). Apache's commons-collections4 4.6.0 deprecates `HashBag` and recommends `HashMultiSet` in its place. A `HashMultiSet` component now gets a traversal, as a `HashBag` one does, where it drew the note for a container no generator supports. See [Apache Commons Collections](../tooling/generator_plugins.md#apache-commons-collections).
- **`@ImportOptics` pairs a JavaBeans-spelt wither with its getter** ([#1148](https://github.com/higher-kinded-j/higher-kinded-j/issues/1148)). A `witheMail(String)` beside `geteMail()`, as Lombok's `beanspec` capitalisation writes them, gives an `eMail()` lens, as `withEMail` beside `getEMail()` does. The processor says why it refuses a class whose only wither is `with2D(int)`. See [Wither Classes to Lenses](../optics/importing_optics.md#wither-classes-to-lenses).
- **A spec lens reads a getter inherited from two interfaces as javac does** ([#1136](https://github.com/higher-kinded-j/higher-kinded-j/issues/1136)). An abstract source that inherits `String name()` from one interface and `CharSequence name()` from another is now read as returning `String`, so a `Lens<Src, String>` over it builds. A `@ViaBuilder` chain reads an inherited `toBuilder()` or `build()` the same way. See [Compiler Messages](../optics/compiler_errors.md#-reads--not-the-lenss-focus-).
- **Messages that offer a navigator ask first for the renames it needs** ([#1272](https://github.com/higher-kinded-j/higher-kinded-j/issues/1272)). The note to widen `items` beside an `Items` with a navigator, and five other messages, now ask for one of the two to be renamed first. They also name a field such as `last` that the navigator could not reach. See [Two fields with one navigator name](../optics/focus_navigation.md#two-fields-with-one-navigator-name).
- **An unknown `@GenerateFocus` field-list name is offered only a field the list can use** ([#1283](https://github.com/higher-kinded-j/higher-kinded-j/issues/1283)). It no longer offers a name the list already holds. `includeFields` is offered a field that can have a navigator, `widenFields` one it can widen, and any rename the field's navigator needs comes first. A name written twice in one list warns. See [Compiler Messages](../optics/compiler_errors.md#generatefocus-field-list-unknown-name).
- **`OpticOpFunctor.map` applies its function** ([#1154](https://github.com/higher-kinded-j/higher-kinded-j/issues/1154)). It returned the instruction unchanged, so code that mapped a lifted `OpticOp` failed later with `ClassCastException`. Each `OpticOp` record now carries a continuation `k`, as an `@EffectAlgebra` operation does, and an interpreter hands each result to `op.k()` in place of wrapping it in `Free.pure`. See [What an interpreter actually is](../optics/interpreters.md#what-an-interpreter-actually-is).
- **Generated optics over a sorted collection keep its comparator** ([#1430](https://github.com/higher-kinded-j/higher-kinded-j/issues/1430)). Traversals and Focus paths over `PSortedSet`, `PSortedMap` and the Eclipse sorted sets rebuild with the source's comparator, so a custom order is kept. The `…Rebuilding` factories on `Traversals` and `EachInstances` do the same for your own optics, and `getOrderedTypeArgumentIndex()` for your own generators. See [Sorted Variants and Comparators](../tooling/pcollections_optics.md#sorted-variants-and-comparators).
- **An `IdentityHashMap` written through an optic keeps every entry** ([#1458](https://github.com/higher-kinded-j/higher-kinded-j/issues/1458)). Each optic that writes a map rebuilt it as a `LinkedHashMap`, so two keys that were equal but not the same object became one, and an entry was lost. An `IdentityHashMap` now comes back one, with every key. See [The `forMapValues()` Traversal](../optics/common_data_structure_traversals.md#the-solution-formapvalues-traversal).
- **`StreamTraversal.toTraversal()` works over a `VStream`** ([#1442](https://github.com/higher-kinded-j/higher-kinded-j/pull/1442)). On `forVStream()`, or a composition built on it, the result read its source twice, so a `fromIterator` source came out empty, and threw `NoSuchElementException` when read again. It now reads the source once, and gives a stream that replays the results. See [Converting to a Traversal](../monads/vstream_advanced.md#conversion-to-standard-traversal).
- **`AffinePath.ofNullable` takes a getter that never returns null** ([#1488](https://github.com/higher-kinded-j/higher-kinded-j/issues/1488)). Under a nullness checker it took only a `Function<S, @Nullable A>`, so a `Function<S, String>` variable was refused. It now takes `Function<? super S, ? extends @Nullable A>`.

---

## Effect Paths {#effect-paths}

- **javac checks each chain step's kind of Path and error type** ([#1314](https://github.com/higher-kinded-j/higher-kinded-j/issues/1314)). A step passed to `via` or another chaining method must be the chain's own kind of Path, so a mismatch is a compile error rather than an exception at run time. A step may still fail with a variant of the chain's error. See [The Family Parameter](../effect/capabilities.md#the-family-parameter).
- **`EitherPath` gains `ensure`** ([#1338](https://github.com/higher-kinded-j/higher-kinded-j/issues/1338)): `ensure(test, onFail)` keeps a success whose value passes `test` and turns any other success into a failure built from its value, so a guard no longer needs an `if` returning a `Left`. See [EitherPath](../effect/path_either.md#error-handling).
- **`TrampolinePath` and `FreePath` combine three paths with `zipWith3`** ([#1302](https://github.com/higher-kinded-j/higher-kinded-j/issues/1302)): both already combined two paths with `zipWith`. The three-way combine stays lazy until `run()` or `foldMap`, so a deep chain stays stack-safe. See [Combinable (Applicative)](../effect/capabilities.md#combinable-applicative).
- **`NonDetPath` gains six list operations that `ListPath` already had** ([#1337](https://github.com/higher-kinded-j/higher-kinded-j/issues/1337)): `lastOption`, `anyMatch`, `allMatch`, `takeWhile`, `dropWhile` and `get`. Each behaves as its `ListPath` counterpart.
- **`NonDetPath` gains `foldRight`, `fold` and `foldMap`** ([#1322](https://github.com/higher-kinded-j/higher-kinded-j/issues/1322)): with `foldLeft`, it now has the four folds that `ListPath` and `StreamPath` offer, and each behaves as its `ListPath` counterpart. See the [Effect Path Cheatsheet](../cheatsheet.md).
- **`StreamPath` and `NonDetPath` gain `toListPath()`** ([#1152](https://github.com/higher-kinded-j/higher-kinded-j/issues/1152)): `ListPath`, `StreamPath` and `NonDetPath` now each convert to the other two.
- **`Monoid.of` builds a monoid from its empty value and its operation, and `Monoids.bigDecimalAddition()` sums money exactly** ([#1356](https://github.com/higher-kinded-j/higher-kinded-j/pull/1356)). `Monoid.of(Duration.ZERO, Duration::plus)` takes the place of an anonymous class, and an existing `Semigroup` can be the operation. See [Semigroup and Monoid](../functional/semigroup_and_monoid.md#a-monoid-of-your-own).
- **`Maybe.fromEither` converts an `Either` to a `Maybe`** ([#1405](https://github.com/higher-kinded-j/higher-kinded-j/issues/1405)): a `Right` becomes `Just` with its value, while a `Left` becomes `Nothing`, discarding the left value.
- **`Either` gains `fromOptional`** ([#1406](https://github.com/higher-kinded-j/higher-kinded-j/issues/1406)): converts a present Java `Optional` to `Right` and an empty one to `Left`, accepting either a direct error value or a supplier for the error.
- **`Either` gains `orElse` and `orElseGet`** ([#1407](https://github.com/higher-kinded-j/higher-kinded-j/issues/1407)). `orElse(other)` returns a `Right`'s value or `other`, and `orElseGet(recovery)` builds it from the error. A null argument is refused on a `Right` and on a `Left`, and a recovery that returns null is refused on a `Left`. A path uses `getOrElse`, because `orElse` there tries another path. See [Reading an Either](../monads/either_monad.md#reading-an-either).
- **`Either` gains `toOptional` and `swap`** ([#1404](https://github.com/higher-kinded-j/higher-kinded-j/issues/1404)). `toOptional()` keeps a `Right`'s value, and gives an empty `Optional` for a `Left`. `swap()` turns a `Left` into a `Right` and back, as `EitherPath.swap()` does.
- **A `fold` can return `null` to a caller whose result type is nullable** ([#1433](https://github.com/higher-kinded-j/higher-kinded-j/issues/1433)). `fold` on `Either`, `Validated`, `EitherOrBoth`, `EitherF`, `Try`, `EitherPath`, `ValidationPath`, `EitherOrBothPath` and `TryPath`, and the deprecated `foldFailureFirst` on `Try` and `TryPath`, take a `@Nullable` result type, so a nullness checker accepts `parsed.<@Nullable Integer>fold(_ -> null, n -> n)`. See [Values, never null](../hkts/core-concepts.md#values-never-null).
- **`VResultPath` runs in one call with `unsafeRun()` and `runAsync()`, as `VTaskPath` does** ([#803](https://github.com/higher-kinded-j/higher-kinded-j/issues/803)). `unsafeRun()` returns the `Either` that `run().run()` gave, and `runAsync()` starts the task on a virtual thread and returns a future of it. A typed failure still arrives as a `Left`, and only an exception the task throws escapes.
- **`Try` and `TryPath` gain `fold`, failure first** ([#546](https://github.com/higher-kinded-j/higher-kinded-j/issues/546)), as `Either.fold`, `Validated.fold` and their Paths' `fold` already are. `foldFailureFirst`, which takes the same two functions in the same order, is deprecated for removal in 0.7.0, and the `MigrateDeprecationsTo0_7_0` recipe renames each call. A `Try` traversal's generated code calls `fold`. See [Reading a Try](../monads/try_monad.md#handling-failures-foldfailurefirst-recover-recoverwith) and [Deprecated for removal in 0.7.0](#deprecated).
- **`ListPath` and `NonDetPath` name the null they refuse** ([#1438](https://github.com/higher-kinded-j/higher-kinded-j/issues/1438)). A null element, or a `map`, `zipWith` or `zipWith3` function that returns null, threw a `NullPointerException` with no message. The message now says which, as `StreamPath`'s does.
- **A `Selective` of your own runs only the branch its condition picks** ([#1358](https://github.com/higher-kinded-j/higher-kinded-j/pull/1358)). The default `whenS` ran its effect for a false condition, and the default `ifS` ran both branches. Both now leave the choice to `select`: one that keeps its contract skips what the condition does not pick, and one built from `ap` still runs every effect. See [Selective](../functional/selective.md).
- **`mapError` on `MaybePath`, `TryPath` and `CompletableFuturePath` carries the mapped error** ([#1332](https://github.com/higher-kinded-j/higher-kinded-j/issues/1332)): `recover` after it sees that error, where it used to see the old one. A success is a `Right` and the mapper is not called. `MaybePath` and `TryPath` return an `EitherPath`. `CompletableFuturePath` returns a `VResultPath`. See [Recoverable (MonadError)](../effect/capabilities.md#recoverable-monaderror).
- **`toEitherPath`, `toMaybePath` and `toTryPath` follow `recover` on a failed `CompletableFuturePath`** ([#1354](https://github.com/higher-kinded-j/higher-kinded-j/issues/1354)). A cancelled future gives `Left` of its `CancellationException`, or `Nothing`, instead of throwing. `toTryPath` holds the exception the future failed with, not a `CompletionException` around it.
- **A half-open `CircuitBreaker` admits a call only while running and successful probes are fewer than `successThreshold`** ([#1481](https://github.com/higher-kinded-j/higher-kinded-j/pull/1481)); it admitted every call. Any other call gets a `CircuitOpenException`, as when the circuit is open. A call now counts only towards the state it was admitted in, so a success from before an outage cannot close the circuit. See [Circuit Breaker](../resilience/circuit_breaker.md#the-state-machine).
- **Each attempt at a call that one `CircuitBreaker` guards twice counts once, at the guard nearest the dependency** ([#1482](https://github.com/higher-kinded-j/higher-kinded-j/issues/1482)). A repository and the service that calls it, each guarding one dependency with one breaker, counted every outcome twice. The inner guard now counts each attempt, and an open circuit turns a retry's later attempts away. See [The State Machine](../resilience/circuit_breaker.md#the-state-machine).
- **A `VStream`'s `recoverWith`, `mapError` and `onError` act on a failure at any element** ([#1029](https://github.com/higher-kinded-j/higher-kinded-j/issues/1029)). They acted on the first pull only, and so did `VStreamPath`'s. A later `recover` still resumes from the element after the failure. See [VStream](../monads/vstream.md).
- **A `VStream`'s `recover` after a failed `mapTask` carries on through the operators between them** ([#1029](https://github.com/higher-kinded-j/higher-kinded-j/issues/1029)). The elements after the failure skipped those operators, so a `filter` let them through, and a `map` or `zipWith` handed on elements of the wrong type. A failed element is now a `Step.Fail`, which each operator passes on. See [When an element fails](../monads/vstream.md#when-an-element-fails).
- **One exception instance thrown at several elements, or by several `VStream`s, no longer mixes them up** ([#1029](https://github.com/higher-kinded-j/higher-kinded-j/issues/1029)). A `recover` could go back to an earlier element or into another stream, and each failure added to the instance's suppressed exceptions. A failed element now holds the rest of its own stream, so nothing is added for it.
- **`chunk`, `chunkWhile`, `mapChunked` and the `parEvalMap` operators hand on what they read before a failure** ([#1029](https://github.com/higher-kinded-j/higher-kinded-j/issues/1029)). They dropped it, so a `forEach` after them missed elements that a `forEach` without them saw. An interrupt still ends the stream at once.
- **A `recover` after `parEvalMap`, `parEvalMapUnordered` or `parEvalFlatMap` carries on past a failed task** ([#1437](https://github.com/higher-kinded-j/higher-kinded-j/issues/1437)). The task's failure ended the stream, so swapping `mapTask` for `parEvalMap` lost recovery. Its element is now a failure in its place, as after `mapTask`, and the other tasks run on. See [When a task fails](../monads/vstream_parallel.md#when-a-task-fails).
- **`parEvalFlatMap` closes the streams it built but never reached** ([#1454](https://github.com/higher-kinded-j/higher-kinded-j/pull/1454)). When a `take` or `headOption` stopped inside one of a batch's streams, or a failure or an interrupt ended the stream, the rest of the batch's streams were dropped unclosed, and their finalisers never ran. Closing the stream now closes them.
- **`VStream.closeAfterFailure` is deprecated for removal in 0.7.0** ([#1029](https://github.com/higher-kinded-j/higher-kinded-j/issues/1029)). A failed element now holds the rest of the stream, so a pull that throws leaves nothing that `stream.close()` misses. See [Deprecated for removal in 0.7.0](#deprecated).
- **A `VStream` fails with the exception its task threw** ([#1410](https://github.com/higher-kinded-j/higher-kinded-j/pull/1410)). A checked one, such as an `IOException` from `mapTask`, a finaliser or a publisher, reached every terminal operation inside a `VTaskExecutionException`, or a `RuntimeException` from `parEvalMap`, `parEvalMapUnordered` and `merge`. `run()`, and the conversions that call it such as `toListPath()`, still wrap it.
- **`Par`, `Scope` and `VStreamPar` fail with the task's own `Throwable`** ([#1418](https://github.com/higher-kinded-j/higher-kinded-j/issues/1418)). A `Throwable` that is neither an `Exception` nor an `Error` reached the caller inside a `RuntimeException` once the task was forked, though the task run on its own failed with it. `VTask.asCallable()` now throws it unchanged.
- **`Instances.alternative(vstream())` gives `VStream`'s `Alternative`** ([#1425](https://github.com/higher-kinded-j/higher-kinded-j/pull/1425)). It failed with a `ClassCastException`, because the `vstream()` token held a plain monad, so code had to name `VStreamAlternative.INSTANCE`. `Instances.monad(vstream())` now returns that same object, which behaves as the monad did.
- **`VStreamPar.parEvalMapUnordered` emits each result as its task completes** ([#1419](https://github.com/higher-kinded-j/higher-kinded-j/issues/1419)). It waited for each batch's slowest task, then emitted the batch in input order. Now a slow task holds back only its own result. Its running tasks and waiting results are never more than `concurrency` together, so a slow consumer slows the reading. `VStreamPath`'s does the same.
- **`VStreamPar.parCollect` is deprecated for removal in 0.7.0** ([#1427](https://github.com/higher-kinded-j/higher-kinded-j/pull/1427)), with `VStreamPath.parCollect` and `PathOps.parCollectVStream`. A stream is read one pull at a time, so they run nothing in parallel and give the list `toList()` gives. Put work you want run in parallel in `parEvalMap`'s function, then call `toList()`. The `MigrateDeprecationsTo0_7_0` recipe rewrites each call. See [Deprecated for removal in 0.7.0](#deprecated).
- **A `VTaskPath` hands every handler the exception its task threw** ([#1396](https://github.com/higher-kinded-j/higher-kinded-j/pull/1396)). A checked one, such as an `IOException`, reached `runSafe`, `catching` and `toTryPath`, and any step after `via`, `then` or `guarantee`, wrapped in a `VTaskExecutionException`, so a retry on `IOException` gave up at once. Only `unsafeRun()`, which cannot throw one, still wraps it. See [VTaskPath](../effect/path_vtask.md#running-tasks).
- **`Context.recoverWith` recovers from a failure in an earlier `map` or `flatMap` function** ([#1443](https://github.com/higher-kinded-j/higher-kinded-j/issues/1443)). An exception thrown by the function, or a function that returned `null`, escaped a `recoverWith` later in the chain, though `recover` and `mapError` caught it. A recovery function that fails now hands its failure to the next `recoverWith` out.
- **A timeout cancels the task it timed out, as a `Scope`'s timeout does** ([#1372](https://github.com/higher-kinded-j/higher-kinded-j/issues/1372)). When the time runs out, `VTask.timeout` interrupts the task and fails once it has stopped, so a retry over a timeout leaves no attempt running. Every `withTimeout` but `CompletableFuturePath`'s builds on it, as do `ResilienceBuilder` and the circuit breaker's `callTimeout`. See [VTaskPath](../effect/path_vtask.md#timeouts).
- **`detachOnCancel()` lets a timeout fail on time over work that cannot stop at once** ([#1372](https://github.com/higher-kinded-j/higher-kinded-j/issues/1372)). The work is still interrupted, but nothing waits for it, so a call blocked where an interrupt cannot reach it still fails at the deadline, and a circuit breaker can open. It is on `VTask`, `IOPath`, `VTaskPath`, `VResultPath` and `VTaskContext`. See [Timeouts](../effect/path_vtask.md#timeouts).
- **A `fromPublisher` stream can be read again, and asks only for what it can hold** ([#1442](https://github.com/higher-kinded-j/higher-kinded-j/pull/1442)). It subscribed once, when built, so a second read waited for ever, and asked for more as each element arrived, however full its buffer. Each run now subscribes at its first pull, and asks for one more per element taken. See [Publisher to VStream](../monads/vstream_advanced.md#publisher-to-vstream).
- **A long `State` chain built with `map` or `ap` no longer overflows the stack** ([#1448](https://github.com/higher-kinded-j/higher-kinded-j/pull/1448)). Each `map` and `ap` step called the step before it, as `WithStatePath`'s `peek`, `zipWith`, `zipWith3` and `then` did, so tens of thousands of them threw `StackOverflowError`. They now run in the loop `flatMap` already used. See [State](../monads/state_monad.md).
- **`Natural` is the library's one natural-transformation type** ([#1455](https://github.com/higher-kinded-j/higher-kinded-j/pull/1455)). `GenericPath.mapK` takes a `Natural`, and `VStreamTransformations` gives `Natural`s. The effect package's `NaturalTransformation` now extends `Natural`, and is deprecated for removal in 0.7.0 with `FreePath.foldMapWith` and `FreeApPath.foldMapWith`. The `MigrateDeprecationsTo0_7_0` recipe rewrites each use. See [Deprecated for removal in 0.7.0](#deprecated).
- **A `VStream` terminal operation stops at an interrupt** ([#1428](https://github.com/higher-kinded-j/higher-kinded-j/issues/1428)). `toList`, `fold`, `drain` and the others did not check between pulls, so neither a timeout nor a cancelled `Scope` could stop a stream whose pulls never block. Each now fails with an `InterruptedException` and closes what it has read, so the finalisers there run. See [Running a stream](../monads/vstream.md#running-a-stream).
- **Stopping one run of a shared `VStream` leaves its other runs alone** ([#1461](https://github.com/higher-kinded-j/higher-kinded-j/issues/1461)). A `take(0)`, a failed first pull or a `close()` on a stream another thread was reading released that thread's `bracket` resource, ran its `onFinalize` finaliser, or stopped its `merge`, `fromPublisher` or `parEvalMapUnordered`. Each run now closes only what it opened. See [`bracket` opens and closes](../monads/vstream_resources.md#bracket-opens-and-closes).
- **`Free.foldMap` keeps what each step's value holds besides its result** ([#1460](https://github.com/higher-kinded-j/higher-kinded-j/pull/1460)). For a target that runs each step at once, such as `Writer` or `List`, it kept only the result. A `Writer` or `WriterT<Id, ...>` lost its log, a `List` its other elements and an `EitherOrBoth` its warnings. Each value now reaches the target whole. See [Free Monad](../monads/free_monad.md).
- **`Free.foldMap` runs a deep program into `Id`, `Either` or `Writer` without overflowing the stack** ([#1460](https://github.com/higher-kinded-j/higher-kinded-j/pull/1460)). A recursive program of under a thousand steps overflowed it. Into `IO` or `State`, a deep program already ran. A `List` step that gives several results still adds to the stack, and so does each step of a `Reader` when it runs.
- **A `Free` program over instructions with no `Functor` of their own can now be run, through `Coyoneda`** ([#1470](https://github.com/higher-kinded-j/higher-kinded-j/pull/1470)). `Coyoneda.foldMap(natural, functor)` runs a held value through an interpreter, and `Coyoneda.interpreter(natural, functor)` gives the interpreter that a `FreePath` needs when its `Functor` is `CoyonedaFunctor`. See [Coyoneda](../monads/coyoneda.md).
- **A long chain of `map` calls on a `Coyoneda` no longer overflows the stack** ([#1470](https://github.com/higher-kinded-j/higher-kinded-j/pull/1470)). Each `map` wrapped the function before it, so lowering ten thousand of them threw `StackOverflowError`. The functions are now applied one after another in a loop. See [Coyoneda](../monads/coyoneda.md).
- **A long `FreeAp` program no longer overflows the stack, and its interpreter is handed the instructions in the order they were written** ([#1470](https://github.com/higher-kinded-j/higher-kinded-j/pull/1470)). A fold of ten thousand `map2` or `zipWith` calls threw `StackOverflowError` from `foldMap`, and so from `FreeApAnalyzer.analyseWith`, which runs through it. The interpreter was also handed the instructions in reverse order. See [Free Applicative](../monads/free_applicative.md).
- **Every Path's `getOrElseGet` refuses a supplier that returns null** ([#1489](https://github.com/higher-kinded-j/higher-kinded-j/issues/1489)). On `MaybePath`, `EitherPath`, `ValidationPath`, `EitherOrBothPath` and `OptionalPath`, and in `Maybe.orElseGet`, a null from the supplier was handed back, though the result type is non-null. It now throws `NullPointerException`, as `TryPath.getOrElseGet` and `Either.orElseGet` already did.
- **`OptionalContext.io` and `JavaOptionalContext.io` take a supplier that never returns null** ([#1488](https://github.com/higher-kinded-j/higher-kinded-j/issues/1488)). Under a nullness checker they took only a `Supplier<@Nullable A>`, so a `Supplier<String>` variable was refused. They now take `Supplier<? extends @Nullable A>`.
- **Stop a `runAsync()` task by cancelling its future** ([#1412](https://github.com/higher-kinded-j/higher-kinded-j/issues/1412)). `cancel(true)` on the future from `runAsync()` on `VTask`, `VTaskPath`, `VResultPath`, `VTaskContext` or `VStream`, or from `EffectBoundary.runAsync`, interrupts the task, as a future from an `ExecutorService` does, and `cancel(false)` leaves it running. See [Running a task](../monads/vtask_monad.md#running-a-task).

---

## Spring {#spring}

- **A `VTaskPath` response timeout interrupts its task** ([#1412](https://github.com/higher-kinded-j/higher-kinded-j/issues/1412)). When `hkj.virtual-threads.default-timeout-ms` runs out, the starter answers 504 and interrupts the task, without waiting for it to stop. The task used to run on after the response had gone. See [VTaskPath](../spring/spring_boot_integration.md#vtaskpath-virtual-thread-async).

---

## Testing {#testing}

---

## Build and tooling {#build-and-tooling}

- **The HKJ checker names the fix beside javac's error for a mismatched step** ([#1314](https://github.com/higher-kinded-j/higher-kinded-j/issues/1314)). `error-type-mismatch` says where to type the chain, as in `write .<OrderError>mapError(...)`, or that the step's error needs mapping. `path-type-mismatch` now reads method-reference steps, `VResultPath` and the accumulating methods. See [Compile-Time Checks](../tooling/compile_checks.md).
- **A build with an HKJ plugin applied can set the checker's options** ([#1336](https://github.com/higher-kinded-j/higher-kinded-j/issues/1336)). A hand-written `-Xplugin:HKJChecker severity=warn` failed the build with `plug-in not found: HKJChecker`. The Gradle and Maven plugins now add no checker argument when the build already names the checker. See [Configuring the checker](../tooling/compile_checks.md#configuration).
- **A build with the HKJ Gradle plugin can turn on Gradle's configuration cache** ([#1374](https://github.com/higher-kinded-j/higher-kinded-j/issues/1374)). With the cache on, `hkjInstallSkills` and `hkjDiagnostics` failed, and so did every build with `skills = true`, which runs `hkjInstallSkills`. Both tasks now run with it, and the next build reuses the cache. See [With the Plugin](../tooling/gradle_plugin.md#with-the-plugin).
- **Installed Claude Code skills say which release they describe, and a build keeps one copy** ([#1373](https://github.com/higher-kinded-j/higher-kinded-j/issues/1373)). Each install writes only what changed, and `.claude/skills/.hkj-skills-version`. A build says when the stamp names another release, or is missing, as on every earlier copy. Copies in modules stay, and each module says so, until you [remove them](../tooling/claude_code_skills.md#copies-in-modules).
- **One `hkj-openrewrite` release takes a 0.4.x project's `Try.fold` calls to 0.6.0** ([#546](https://github.com/higher-kinded-j/higher-kinded-j/issues/546)). The swap in `MigrateDeprecationsTo0_5_0` now leaves 0.6.0's `fold` alone, since it swaps only a `fold` declared with the success function first. So run the 0.5.0 group before the move and the 0.7.0 group after, both from 0.6.x. See [0.5.0 deprecation migration](../tooling/openrewrite.md#050-deprecation-migration).
- **An OpenRewrite recipe adds the Path family to capability types** ([#1314](https://github.com/higher-kinded-j/higher-kinded-j/issues/1314)). `org.higherkindedj.openrewrite.AddPathFamily` rewrites `Chainable<A>` to `Chainable<?, A>`, and likewise the other capability types, a `PathProvider`'s `createPath` included. See [0.6.0 Path family](../tooling/openrewrite.md#060-path-family).
- **`javadoc` runs in a build with the HKJ Gradle plugin applied** ([#1385](https://github.com/higher-kinded-j/higher-kinded-j/issues/1385)). With preview features on, the default, javadoc received `--enable-preview` without the release it needs, so `javadoc` failed, and so did `build` in a project that publishes a javadoc jar. The plugin now passes `--release` as well, unless the task passes its own `-source` or `--release`. See [Defaults](../tooling/gradle_plugin.md#defaults).
- **The benchmark options the docs describe now work** ([#1439](https://github.com/higher-kinded-j/higher-kinded-j/pull/1439)). The benchmarks read only `-Pincludes`, so `-Pjmh.iterations`, `-Pjmh.fork`, `-Pjmh.profilers=gc` and the rest did nothing, and the `--includes` most pages gave failed. The build now reads each one, and [Benchmarks](../benchmarks.md#options) lists them.
- **A generator plugin needs only `hkj-processor` from HKJ** ([#1376](https://github.com/higher-kinded-j/higher-kinded-j/issues/1376)). `BaseTraversableGenerator` moves there under the same name, and javapoet, whose types the generator interface uses, comes with it. A plugin's build can drop a javapoet line it lists by hand, or an `hkj-processor-plugins` line it added to reach the base class. Its `module-info.java` keeps `requires com.palantir.javapoet;`. See [Step-by-Step Example](../tooling/generator_plugins.md#step-by-step-example).
- **The 0.5.0 migration marks more of the nulls 0.5.0 refuses** ([#1370](https://github.com/higher-kinded-j/higher-kinded-j/issues/1370)). Its new `DetectPossibleNullSuccessValuesRecipe` marks a `@Nullable` value passed on to a success factory, and a `@Nullable` method read by `Try.of` or a `map`. It marks a null check in a later step, which 0.5.0 never hands a `null`. A 0.5.0 project can run it alone. See [0.5.0 deprecation migration](../tooling/openrewrite.md#050-deprecation-migration).
- **`MigrateDeprecationsTo0_5_0` puts a lambda's mark on the `null` it returns** ([#1370](https://github.com/higher-kinded-j/higher-kinded-j/issues/1370)). The mark sat at the start of the call's chain, which could be many lines before the `null`. See [0.5.0 deprecation migration](../tooling/openrewrite.md#050-deprecation-migration).

---

## Documentation {#documentation}

- **`StreamPath` docs no longer call it lazy where the source is read at once** ([#1290](https://github.com/higher-kinded-j/higher-kinded-j/issues/1290)). The cheat sheet and home page describe sequences you can run more than once, and `toStreamPath` says the non-null focuses are collected into a list when it is called.
- **The StreamPath docs say when the source is read** ([#1278](https://github.com/higher-kinded-j/higher-kinded-j/issues/1278)). `Path.stream` reads its whole stream when it is called, so the stream must be finite. `Path.streamIterate`, `StreamPath.generate` and `StreamPath.fromSupplier` build a fresh stream each time the path runs, so `take` can limit an infinite one. See [Choosing the Right Streaming Tool](../monads/vstream.md#choosing-the-right-streaming-tool).
- **The Effect Paths glossary gains three entries** ([#1301](https://github.com/higher-kinded-j/higher-kinded-j/issues/1301)): `FreeApPath`, `FreePath` and `GenericPath`. Each gives a definition, its factories on `Path`, and its main operations. See [Effect Paths](../glossary/effect-paths.md#freeappath).
- **The Effect Paths glossary gains four more entries** ([#1258](https://github.com/higher-kinded-j/higher-kinded-j/issues/1258)): `ReaderPath`, `TrampolinePath`, `WithStatePath` and `WriterPath`. Each gives a definition, its factories on `Path`, its main operations and a link to its chapter page. See [Effect Paths](../glossary/effect-paths.md#readerpath).
- **The Effect Paths glossary gains the four sequence Paths** ([#1263](https://github.com/higher-kinded-j/higher-kinded-j/issues/1263)): `ListPath`, `NonDetPath`, `StreamPath` and `VStreamPath`. Each gives a definition, its factories on `Path`, its main operations and links for further reading. See [Effect Paths](../glossary/effect-paths.md#listpath).
- **Five examples keep one kind of Path and one error type through their chains.** Composition Patterns ran an `IOPath` step in an `EitherPath` chain, which throws when it runs; it now builds an `IOPath` chain. The Quickstart, a migration recipe and the `hkj-guide` and `hkj-spring` skills now name the chain's error type with a witness. See [Composition Patterns](../effect/composition.md#mistake-2-side-effects-in-map).
- **Generated Javadoc uses an article only before a type the processor chose** ([#1307](https://github.com/higher-kinded-j/higher-kinded-j/issues/1307)). A record, class, property or operation is named with none, as in `field of {@link Address}` and `Lifts the {@code Charge} operation`. A path or optic type keeps the article its name takes, as in `Creates an {@link AffinePath}`.
- **The `hkj-guide` skill turns guard clauses into `ensure`** ([#1338](https://github.com/higher-kinded-j/higher-kinded-j/issues/1338)). A new migration recipe replaces a run of `if (!ok) return Either.left(...)` checks with one `ensure` step for each, and the skill's Error Handling table lists `ensure`.
- **The `Profunctor` javadoc states its composition law correctly** ([#1362](https://github.com/higher-kinded-j/higher-kinded-j/pull/1362)). Two of its functions were swapped, so the law as written fails. It no longer says profunctors model the optics, which are van Laarhoven optics, and `Bifunctor`'s javadoc names `Function` as the only `Profunctor` instance. See [Profunctor](../functional/profunctor.md).
- **The `VTask` javadoc says which thread runs a task** ([#1396](https://github.com/higher-kinded-j/higher-kinded-j/pull/1396)). `run()` runs it on the calling thread, and `runAsync()`, `Par` and `Scope` run tasks on virtual threads. It said every task ran on a virtual thread, as did the VTaskPath page and two glossary entries.
- **You can have NullAway flag a null returned through `VTask.of` or `Try.of`** ([#1371](https://github.com/higher-kinded-j/higher-kinded-j/issues/1371)). In JSpecify mode alone, NullAway never checks what a lambda returns when passed as a JDK `Supplier`, `Callable` or `BiFunction`, as to `VTask.of`, `Try.of` or `IO.delay`, so the computation fails only when it runs. Two NullAway options make it check them. See [What a nullness checker sees](../hkts/core-concepts.md#what-a-nullness-checker-sees).
- **You can learn the type classes from one tested example.** Most Foundations pages and Tutorial 00 use the order service the Optics and Mapping chapters share. Each core type's page opens with a card saying when to reach for it, Supported Types is rebuilt around those cards, and a new page lists where an instance bends a law. See [Foundations](../hkts/foundations_intro.md).

---

## Upgrading from 0.5.0 {#upgrading}

### Before you upgrade {#before-you-upgrade}

- **From 0.4.x, run `MigrateDeprecationsTo0_5_0` while still on 0.4.x** ([#546](https://github.com/higher-kinded-j/higher-kinded-j/issues/546)). `fold` returns on `Try` and `TryPath`, failure first. A success-first `fold` left from 0.4.x compiles again where both functions accept either argument, and runs them swapped: `t.fold(v -> true, e -> false)` now gives `true` for a failure. Rebuild what was compiled against 0.4.x too. See [To 0.6.0](upgrading.md#to-060).
- **Expect the HKJ skills at the root of your build** ([#1373](https://github.com/higher-kinded-j/higher-kinded-j/issues/1373)). The Gradle plugin, and `mvn hkj:install-skills`, put a copy in each module. Both now install once, at the root, and leave the module copies alone. Each module that holds one says so until you install at the root and [remove it](../tooling/claude_code_skills.md#copies-in-modules).

### What a running program can notice {#runtime-changes}

#### Mapping {#runtime-mapping}

- **A mapping refuses a map holding two keys that `equals` cannot tell apart, unless it is an `IdentityHashMap`** ([#1483](https://github.com/higher-kinded-j/higher-kinded-j/issues/1483)). A same-typed `Map` copy, `parseValues` and `buildValues` lost an entry; they now throw. Hold such a map as an `IdentityHashMap`, which comes back a writable one, so look its keys up by the same object. See [Same-typed containers cross as copies](../mapping/rules.md#same-typed-containers-cross-as-copies).

#### Core types {#runtime-core-types}

- **`Validated.map`, and `map2` to `map5` on its monad, throw `NullPointerException` when their function returns null** ([#1318](https://github.com/higher-kinded-j/higher-kinded-j/issues/1318)). They threw `KindUnwrapException`, so catching that, or `IllegalArgumentException`, around the call no longer catches it. See [Values, never null](../hkts/core-concepts.md#values-never-null).
- **`FunctionKindHelper.narrow` throws `KindUnwrapException`, as the other kind helpers do** ([#1318](https://github.com/higher-kinded-j/higher-kinded-j/issues/1318)). A null `Kind` threw `NullPointerException`, so catching that no longer catches it. Another type's `Kind` threw `IllegalArgumentException`, which `KindUnwrapException` extends, so catching that still does. See [When `narrow` throws](../hkts/core-concepts.md#when-narrow-throws).
- **A `Selective` of your own no longer runs the effects its condition skips** ([#1358](https://github.com/higher-kinded-j/higher-kinded-j/pull/1358)), unless its `select` is built from `ap`. The default `whenS` ran its effect on a false condition, and the default `ifS` ran both branches, so a failing effect's error could hide the condition's. Run any effect you relied on yourself. The library's own instances are unchanged.
- **`Context.recoverWith` now catches a failure from an earlier `map` or `flatMap` function** ([#1443](https://github.com/higher-kinded-j/higher-kinded-j/issues/1443)). Code that let such a failure escape a `recoverWith` gets the recovered value instead, and a recovery function's own failure reaches the next `recoverWith` out. A checked exception such a function throws, as Kotlin code can, reaches `run()` inside a `ContextException`, as one from `fail` does.
- **`Free.foldMap` into a `Writer`, `List` or `EitherOrBoth` gives what each step's value holds** ([#1460](https://github.com/higher-kinded-j/higher-kinded-j/pull/1460)). A `Writer`'s log is no longer empty, and an `EitherOrBoth` that warned gives `Both`, where it gave `Right`. A `List` target runs the rest of the program, and its interpreter, once for each element a step gives.
- **`Const` and the optics' folds refuse a null value** ([#1470](https://github.com/higher-kinded-j/higher-kinded-j/pull/1470)). `new Const<>(null)` throws `NullPointerException`, as does a `mapFirst` or `bimap` function returning null. A traversal's fold fails where its function or monoid gives null, where before the monoid got the null. A side-effect fold needs a `Monoid<Unit>`, not `Monoid<Void>`. See [Values, never null](../hkts/core-concepts.md#values-never-null).
- **A `FreeAp` interpreter is handed the instructions in the order the program was written** ([#1470](https://github.com/higher-kinded-j/higher-kinded-j/pull/1470)), not in reverse. An interpreter that acts as it is called, such as one into `Id` that logs or calls a service, now does so in that order. Results, and the failures `Validated` accumulates, were already in order.

#### Optics {#runtime-optics}

- **Of two withers for one getter, `@ImportOptics` sets through the capitalised spelling** ([#1148](https://github.com/higher-kinded-j/higher-kinded-j/issues/1148)): the lens for a class with both `withEMail` and `witheMail` beside `eMail()` set through whichever the class declared first. It now sets through `withEMail`, and a note names `witheMail`, which a spec interface's `@Wither` can choose.
- **An optic interpreter that wraps each result in `Free.pure` skips the operation's continuation** ([#1154](https://github.com/higher-kinded-j/higher-kinded-j/issues/1154)): a program from `OpticPrograms` runs as before, but a function mapped onto an `OpticOp`, through `OpticOpFunctor` or `Free.translate`, is not applied. Hand the result to `op.k()` and return what it gives.
- **A sorted collection keeps its order through a generated optic** ([#1430](https://github.com/higher-kinded-j/higher-kinded-j/issues/1430)). A traversal over a `PSortedSet` or `PSortedMap`, and a Focus path over those or an Eclipse Collections sorted set, gave back a custom-ordered source in natural order. It now keeps the source's order. Where code relied on the re-sort, sort the result itself.
- **A map optic refuses any map but an `IdentityHashMap` holding two keys that `equals` cannot tell apart** ([#1458](https://github.com/higher-kinded-j/higher-kinded-j/issues/1458)), such as a `HashMap` whose key changed after it went in. Writing one lost an entry; it now throws `IllegalArgumentException`. Copy such a map into an `IdentityHashMap` first. An `IdentityHashMap` comes back one, so look its keys up by the same object.
- **A null optic, modifier or predicate fails when the program is built** ([#1315](https://github.com/higher-kinded-j/higher-kinded-j/issues/1315)). Each `OpticOp` record throws `NullPointerException`, and so does an `OpticPrograms` factory that builds one. It used to fail only when an interpreter ran the program. The validating interpreter no longer reports one as a failed operation. `set` and `setAll` still accept a null new value.

#### Effect Paths {#runtime-effect-paths}

- **A `ValidationPath` keeps its own semigroup through a step** ([#1314](https://github.com/higher-kinded-j/higher-kinded-j/issues/1314)): the result of `via`, `then`, `recoverWith` and `orElse` accumulated with the step's semigroup, and now accumulates with the receiver's. After `mapError`, whose placeholder semigroup throws, a later accumulation now throws even past a step that had a semigroup; use `mapErrorWith` instead.
- **An `EitherOrBothPath` keeps its own semigroup through a recovery** ([#1314](https://github.com/higher-kinded-j/higher-kinded-j/issues/1314)): the result of `recoverWith` and `orElse` accumulated with the recovery's semigroup, and now accumulates with the receiver's, as `via` and `then` already did.
- **`mapError` on `MaybePath`, `TryPath` and `CompletableFuturePath` applies the mapper** ([#1332](https://github.com/higher-kinded-j/higher-kinded-j/issues/1332)): it used to hand back the same path, and it never called the mapper. A failure now runs the mapper, and `recover` sees that error. `MaybePath` and `TryPath` return an `EitherPath`. `CompletableFuturePath` returns a `VResultPath`. `mapException` still stays a `TryPath`.
- **`toEitherPath` and `toMaybePath` on a cancelled `CompletableFuturePath` no longer throw** ([#1354](https://github.com/higher-kinded-j/higher-kinded-j/issues/1354)). They give `Left` of the `CancellationException`, and `Nothing`. `toTryPath` on a failed future holds that future's exception. Code which caught a `CancellationException` around `toEitherPath` or `toMaybePath`, or matched a `CompletionException` in a `toTryPath` failure, no longer sees one.
- **A `VStream`'s `recoverWith`, `mapError` and `onError` now handle a failure at a later element** ([#1029](https://github.com/higher-kinded-j/higher-kinded-j/issues/1029)). A failure at the second element went straight through them, so a fallback stream, an error mapper or a logging hook now runs where it did not. `recoverWith`, and `recover` when it ends the stream, close what they stop reading.
- **A `VStream`'s `recover` after a failed `mapTask` passes the elements after the failure through the operators between them** ([#1029](https://github.com/higher-kinded-j/higher-kinded-j/issues/1029)). A `map`, `filter` or `take` there now applies to them. A `recover` after `VStreamPar.merge` of several streams ends the stream there, and one after a task whose exception has suppression disabled carries on.
- **A failed `mapTask` element holds its place in a `VStream`** ([#1029](https://github.com/higher-kinded-j/higher-kinded-j/issues/1029)). `drop` and `dropWhile` drop it within the prefix they drop, so a failure there no longer fails the stream. `zipWith` pairs it with the other stream's element and ends with the shorter stream, and `throttle` and `metered` pace it.
- **`chunk`, `chunkWhile`, `mapChunked` and the `parEvalMap` operators hand on what they read before a failure, then fail** ([#1029](https://github.com/higher-kinded-j/higher-kinded-j/issues/1029)). A batch function or task now runs for those elements, with or without a `recover`, so a `forEach` or `headOption` after them sees them where it saw only the failure.
- **A failed task in `parEvalMap`, `parEvalMapUnordered` or `parEvalFlatMap` no longer cancels the others** ([#1437](https://github.com/higher-kinded-j/higher-kinded-j/issues/1437)). A `recover` after the operation carries on where it ended the stream, and a `take`, `headOption` or `forEach` gets the results ahead of the failure. Without a `recover`, a failing `parEvalMap` run waits for its batch's slowest task.
- **Code that pulls a parallel operation by hand gets a `Step.Fail` for a failed task, where the pull threw** ([#1437](https://github.com/higher-kinded-j/higher-kinded-j/issues/1437)). So does a task that returns null, and a `parEvalFlatMap` function that throws or returns null. An interrupted task, or a `parEvalMap` or `parEvalMapUnordered` function that throws, still ends the stream.
- **A finaliser or `bracket` release on a stream whose `mapTask` task fails runs when the stream is closed, not at the failure** ([#1029](https://github.com/higher-kinded-j/higher-kinded-j/issues/1029)). A `recover` after them reads on first. Without one, a terminal operation closes the stream after any `onError` or `mapError` has seen the failure. Code that pulls by hand closes the tail of the `Step.Fail`.
- **Code that pulls a `VStream` by hand gets a `Step.Fail` for a failed `mapTask` task, where the pull threw** ([#1029](https://github.com/higher-kinded-j/higher-kinded-j/issues/1029)). Its `tail` is the stream after the element, to close or read on from. An `instanceof` chain or `default` branch over `Step` misses it silently. Code compiled against 0.5.0 throws `MatchException` at an exhaustive switch until rebuilt.
- **A `VStream` hands over a checked exception as its task threw it** ([#1410](https://github.com/higher-kinded-j/higher-kinded-j/pull/1410)). Code that called `getCause()` on a `VTaskExecutionException` from `runSafe`, or on a `RuntimeException` from `parEvalMap`, `parEvalMapUnordered`, `merge` or `fromPublisher`, now receives the exception itself, as it does from a finaliser. `run()` still wraps one.
- **A forked task's `Throwable` that is neither an `Exception` nor an `Error` is no longer wrapped** ([#1418](https://github.com/higher-kinded-j/higher-kinded-j/issues/1418)). Code that called `getCause()` on the `RuntimeException` from `Par`, `Scope.join`, `parEvalMap` or `parEvalMapUnordered` now receives the `Throwable` itself. So does a caller of `asCallable().call()`, where a `catch (Exception e)` no longer catches it.
- **A `VTaskPath` handler sees a checked exception as its task threw it** ([#1396](https://github.com/higher-kinded-j/higher-kinded-j/pull/1396)). Code that called `getCause()` on a `VTaskExecutionException` now receives the exception itself. That holds in `runSafe` and `catching`, in a handler or retry after `via`, in the future from `runAsync()`, and in `VTaskContext.runOrElseGet` and `StreamTraversal.modifyVTask`. `unsafeRun()` still wraps one.
- **`recover`, `recoverWith`, `mapError` and `toEitherPath` on a `CompletableFuturePath` keep a `CompletionException` that has no cause** ([#1398](https://github.com/higher-kinded-j/higher-kinded-j/issues/1398)). A handler that was given an empty `RuntimeException` now gets the `CompletionException`.
- **A timed-out task is interrupted, and the timeout waits for it to stop** ([#1372](https://github.com/higher-kinded-j/higher-kinded-j/issues/1372)). A task blocked in `CompletableFuture.join()`, or otherwise out of an interrupt's reach, holds the failure back until it finishes, and an interrupt of the caller now waits for it too. To fail at the time limit, as before, time out `task.detachOnCancel()`.
- **A half-open `CircuitBreaker` rejects a call once `successThreshold` probes are running or have succeeded** ([#1481](https://github.com/higher-kinded-j/higher-kinded-j/pull/1481)). It let every call through. The rejection is a `CircuitOpenException` whose `status()` is `HALF_OPEN` and `retryAfter()` the call timeout, and `metrics().rejectedCalls()` counts it. A fallback or `onOpen` mapping handles it; code that tests for `OPEN` should accept `HALF_OPEN` too.
- **A `CircuitBreaker` call still running when the circuit changes state no longer moves it** ([#1481](https://github.com/higher-kinded-j/higher-kinded-j/pull/1481)), including across `reset()` and `tripOpen()`. Its outcome counted towards whatever state the circuit was in when it finished. The metrics still count it.
- **A half-open probe that an interrupt cannot reach holds its probe place until it stops** ([#1481](https://github.com/higher-kinded-j/higher-kinded-j/pull/1481)). With the default threshold of one, every other call is turned away meanwhile. Protect `task.detachOnCancel()`, as for any call that can hang, so that the call timeout frees its place.
- **A `CircuitBreaker` whose `recordFailure` predicate throws counts the call as a failure** ([#1481](https://github.com/higher-kinded-j/higher-kinded-j/pull/1481)). The predicate's exception replaced the call's. Now the call's own exception reaches the caller, with the predicate's attached as suppressed.
- **A call that one `CircuitBreaker` guards twice counts each attempt once** ([#1482](https://github.com/higher-kinded-j/higher-kinded-j/issues/1482)). Its `metrics()` counted each outcome at both guards, so a circuit opened at half its `failureThreshold`. A fallback between the guards also kept the circuit closed. Now the circuit opens, and the outer guard turns calls away before the fallback runs, so put the fallback outside it.
- **A `fromPublisher` stream subscribes when a run's first pull runs, not when it is built** ([#1442](https://github.com/higher-kinded-j/higher-kinded-j/pull/1442)). A hot publisher, such as a `SubmissionPublisher`, sends a run only what it publishes after the run subscribes, so what it publishes between building the stream and reading it is lost. Publish once the run has subscribed, which `getNumberOfSubscribers()` shows.
- **Code compiled against 0.5.0 that calls `GenericPath.mapK` or a `VStreamTransformations` method must be recompiled** ([#1455](https://github.com/higher-kinded-j/higher-kinded-j/pull/1455)). Each now names `Natural` in its signature, so the old call fails with `NoSuchMethodError`. A `mapK` call that passes a `NaturalTransformation` still compiles, with a removal warning.
- **A `VStream`'s `recover`, `recoverWith`, `mapError` and `onError` let an interrupt through** ([#1428](https://github.com/higher-kinded-j/higher-kinded-j/issues/1428)). Their functions saw it, so `recover` ended the stream with its value, `mapError` replaced it and an `onError` hook logged it. Now the run fails with the `InterruptedException`. To give a value when a run is stopped, recover outside what stops it, as in `toList().timeout(d).recover(e -> List.of())`.
- **A `VStream` terminal operation on an interrupted thread fails before it reads** ([#1428](https://github.com/higher-kinded-j/higher-kinded-j/issues/1428)). It read to the end unless a pull blocked. `run()` and `runSafe()` leave the status set after an interrupt, so a thread that goes on to read another stream fails at once. On a thread meant to carry on, clear the status first with `Thread.interrupted()`.
- **`VStreamPar.merge` fails with the `InterruptedException` when its reader is interrupted** ([#1428](https://github.com/higher-kinded-j/higher-kinded-j/issues/1428)). It threw a `RuntimeException` with the message "Merge interrupted". Now `runSafe()` gives the `InterruptedException`, `run()` throws it wrapped in a `VTaskExecutionException`, and a `recover` after the merge lets it through. Where code matched the message, match the `InterruptedException` instead.
- **`prepend` leaves the stream after its element alone until it is read** ([#1428](https://github.com/higher-kinded-j/higher-kinded-j/issues/1428)). `headOption()` or `take(1)` after `prepend` closed that stream, running its `onFinalize` finaliser though nothing had read it, where `VStream.of(x).concat(stream)` did not. To run a finaliser however far a run reads, attach it after `prepend`. See [`onFinalize`](../monads/vstream_resources.md#onfinalize-lightweight-cleanup).
- **Closing a `VStream` you pulled by hand no longer closes that run** ([#1461](https://github.com/higher-kinded-j/higher-kinded-j/issues/1461)). On a `bracket`, `merge`, `fromPublisher`, `parEvalMapUnordered` or `onFinalize` stream, `close()` after a pull ended the latest run, which could be another thread's. Close the tail the pull gave you instead. To stop another thread's run, time it out or interrupt that thread. See [Pulling by hand](../monads/vstream_resources.md#pulling-by-hand).
- **`getOrElseGet` on a Path, and `orElseGet` on a `Maybe`, throw when the supplier returns null** ([#1489](https://github.com/higher-kinded-j/higher-kinded-j/issues/1489)). They handed the null back. Code that relied on that gets `NullPointerException`, with `supplier must not return null`, or `otherSupplier` on a `Maybe`; return a value from the supplier.
- **`cancel(true)` on a future from `runAsync()` interrupts the task** ([#1412](https://github.com/higher-kinded-j/higher-kinded-j/issues/1412)). A task that ran on after its future was cancelled is now interrupted: a sleep, a wait or a blocking socket call in it fails, and Java closes that socket. A task that catches the interrupt and carries on still runs on. The future from `EffectBoundary.runAsync` does the same.
- **Cancelling a `CompletableFuturePath` over a `runAsync()` future interrupts the task** ([#1412](https://github.com/higher-kinded-j/higher-kinded-j/issues/1412)). That holds for `Path.future(task.runAsync())`, and for a path `withTimeout` or `completeOnTimeout` makes from it, with either flag. A `map` or `via` makes a new future, which breaks the chain. See [CompletableFuture](../monads/cf_monad.md#on-a-completablefuturepath).

#### Spring {#runtime-spring}

- **A `VTaskPath` request that times out interrupts its task** ([#1412](https://github.com/higher-kinded-j/higher-kinded-j/issues/1412)). The 504 goes out as before. The task used to run on in the background, and is now interrupted, as [Effect Paths](#runtime-effect-paths) says.
- **Start work that must outlive a `VTaskPath` response with its own `runAsync()`** ([#1412](https://github.com/higher-kinded-j/higher-kinded-j/issues/1412)). Started inside the `VTaskPath` the controller returns, it keeps running when the timeout interrupts the request's task. Or raise `hkj.virtual-threads.default-timeout-ms`.
- **A timed-out `CompletableFuturePath` request over `runAsync()` interrupts its task** ([#1412](https://github.com/higher-kinded-j/higher-kinded-j/issues/1412)). When `hkj.async.default-timeout-ms` runs out, the starter cancels the path's future, which now reaches the task.
- **`ObservableEffectBoundary.runAsync` passes a cancel through to the program, and records it** ([#1412](https://github.com/higher-kinded-j/higher-kinded-j/issues/1412)). Cancelling its future, with either flag, interrupts the program and counts the invocation once, as an error, `CancellationException`. A cancel used to leave the program running and record nothing.

### What stops a build that compiled {#build-changes}

| Area | Now fails the build | Do this | Issue |
|---|---|---|---|
| Effect Paths | A chain step of another kind of Path, or with an error, environment, state, log or witness type the chain does not carry | Convert the step, map its error, or type the chain on the wider error where it starts, as [Common Compiler Errors](../effect/compiler_errors.md) shows | [#1314](https://github.com/higher-kinded-j/higher-kinded-j/issues/1314) |
| Effect Paths | Code that names a capability type with its old arguments, such as `Effectful<A>`, or a `PathProvider` returning `Chainable<A>` | Write `Effectful<?, A>`, or `<P, A> Effectful<P, A>` in a helper that chains. The `AddPathFamily` recipe writes the first | [#1314](https://github.com/higher-kinded-j/higher-kinded-j/issues/1314) |
| Effect Paths | A chain whose receiver is typed with a wildcard error, such as `EitherPath<? extends AppError, String>` | Widen it first, as in `p.<AppError>mapError(e -> e).via(...)`, or declare it as `EitherPath<AppError, String>` | [#1314](https://github.com/higher-kinded-j/higher-kinded-j/issues/1314) |
| Effect Paths | A pattern or cast from a capability type to a concrete Path, such as `case MaybePath<String> m` on a `Chainable<?, String>`; a cast is unchecked, so it fails under `-Werror` | Match from `Composable<String>`, or build the concrete Path directly | [#1314](https://github.com/higher-kinded-j/higher-kinded-j/issues/1314) |
| Effect Paths | A step after `mapError` on a `MaybePath`, `TryPath` or `CompletableFuturePath` that returns the Path it started from | Return an `EitherPath` from the step (a `VResultPath` after a `CompletableFuturePath`), or use `recover` or `mapException` before changing the error type | [#1332](https://github.com/higher-kinded-j/higher-kinded-j/issues/1332) |
| Optics | An optic interpreter or other code of your own that names an `OpticOp` record: a pattern or type such as `OpticOp.Get<?, ?>` or `OpticOp.Set<S, A>`, or a constructor call such as `new OpticOp.Get<>(source, getter)` | Add the third type argument, as in `OpticOp.Get<?, ?, ?>`, and pass a continuation, `Function.identity()`, to a constructor. Hand each result to `op.k()` and return what it gives, in place of `Free.pure(result)` | [#1154](https://github.com/higher-kinded-j/higher-kinded-j/issues/1154) |
| Effect Paths | An exhaustive `switch` over `VStream.Step`, in code that pulls a stream by hand or in a stream of your own | Add a `Step.Fail` case. A stream of your own passes it on with itself over the tail, as its `Emit` case does; code that stops there closes the tail and throws the error | [#1029](https://github.com/higher-kinded-j/higher-kinded-j/issues/1029) |
| Optics | A call to the traversal `@GenerateTraversals` generated for a sorted container with a wildcard where it is ordered, such as `PSortedSet<? extends String>` or `PSortedMap<? extends String, V>`. The method is no longer generated, and a note says why | Declare the component with concrete type arguments, such as `PSortedSet<String>` | [#1430](https://github.com/higher-kinded-j/higher-kinded-j/issues/1430) |
| Effect Paths | Code that keeps a `VStreamTransformations` result as a `NaturalTransformation`, such as `NaturalTransformation<ListKind.Witness, VStreamKind.Witness> nt = VStreamTransformations.listToVStream()` | Declare it `Natural`, as the `MigrateDeprecationsTo0_7_0` recipe does | [#1455](https://github.com/higher-kinded-j/higher-kinded-j/pull/1455) |
| Mapping | A same-typed component declared `@Nullable`, copied by `build` into a record component, setter or builder setter declared non-null by an annotation or a `@NullMarked` scope, or by a plain merge into such a target component | Mark the site `@Nullable`, or declare the source component non-null | [#1431](https://github.com/higher-kinded-j/higher-kinded-j/issues/1431) |
| Build and tooling | Build logic that reads the `hkjInstallSkills` task's `outputDir` as a `Path` | Read `outputDir.get().asFile.toPath()`, and expect `.claude/skills` at the root of the build in every project | [#1374](https://github.com/higher-kinded-j/higher-kinded-j/issues/1374), [#1373](https://github.com/higher-kinded-j/higher-kinded-j/issues/1373) |

### Deprecated for removal in 0.7.0 {#deprecated}

| Deprecated | Replacement | Recipe |
|---|---|---|
| `VStream.closeAfterFailure(stream, failure)` | `stream.close()`; a failed `mapTask` task gives a `Step.Fail`, whose tail you close as an `Emit`'s | By hand |
| `VStreamPar.parCollect(stream, batchSize)` | `stream.toList()` | `ReplaceParCollectWithToListRecipe`, in [`MigrateDeprecationsTo0_7_0`](../tooling/openrewrite.md#070-deprecation-migration) |
| `VStreamPath.parCollect(batchSize)` | `toList()` on the path | `ReplaceParCollectWithToListRecipe` |
| `PathOps.parCollectVStream(path, batchSize)` | `path.toList()` | `ReplaceParCollectWithToListRecipe` |
| `Try.foldFailureFirst` and `TryPath.foldFailureFirst` | `fold(failureMapper, successMapper)`, the same two functions in the same order | `RenameTryFoldFailureFirst` |
| `Operation.FOLD_FAILURE_FIRST` | `Operation.FOLD`, which `Try.fold` reports | By hand |
| `NaturalTransformation<F, G>`, in `org.higherkindedj.hkt.effect` | `Natural<F, G>`, which it extends | `ReplaceNaturalTransformationWithNatural`, in `MigrateDeprecationsTo0_7_0` |
| `FreePath.foldMapWith(interpreter, monad)` | `foldMap(interpreter, monad)` | `ReplaceNaturalTransformationWithNatural` |
| `FreeApPath.foldMapWith(interpreter, applicative)` | `foldMapKind(interpreter, applicative)` | `ReplaceNaturalTransformationWithNatural` |

---

**Previous:** [Upgrading](upgrading.md)
**Next:** [v0.5.0](v0_5_0.md)
