Repository: 7mind/izumi
Pull request #1358 (2021-01-12 02:04:30)

WIP 1.0 release notes

codecov-io: # [Codecov](https://codecov.io/gh/7mind/izumi/pull/1358?src=pr&el=h1) Report
> Merging [#1358](https://codecov.io/gh/7mind/izumi/pull/1358?src=pr&el=desc) (e003464) into [develop](https://codecov.io/gh/7mind/izumi/commit/c8561ca3bf78988f0bc2462fc7f518d9f2b11533?el=desc) (c8561ca) will **decrease** coverage by `0.11%`.
> The diff coverage is `0.00%`.


```diff
@@             Coverage Diff             @@
##           develop    #1358      +/-   ##
===========================================
- Coverage    55.73%   55.62%   -0.12%     
===========================================
  Files          454      454              
  Lines         9313     9309       -4     
  Branches       450      429      -21     
===========================================
- Hits          5191     5178      -13     
- Misses        4122     4131       +9     
```


Patch:
diff --git a/changelog-1.0.1.md b/changelog-1.0.1.md
new file mode 100644
index 000000000..f348513b8
--- /dev/null
+++ b/changelog-1.0.1.md
@@ -0,0 +1,43 @@
+<p align="center">
+  <a href="https://izumi.7mind.io/">
+  <img width="40%" src="https://github.com/7mind/izumi/blob/develop/doc/microsite/src/main/tut/media/izumi-logo-full-purple.png?raw=true" alt="Izumi"/>
+  </a>
+</p>
+
+<details>
+  <summary>What is it?</summary>
+
+
+Izumi (*jp. 泉水, spring*) is an ecosystem of independent libraries and frameworks allowing you to significantly increase productivity of your Scala development.
+
+including the following components:
+
+1. [distage](https://izumi.7mind.io/distage/) – Transparent and debuggable Dependency Injection framework for pure FP Scala,
+2. [distage-testkit](https://izumi.7mind.io/distage/distage-testkit) – Hyper-pragmatic pure FP Test framework. Shares heavy resources globally across all test suites; lets you easily swap implementations of component; uses your effect type for parallelism.
+3. [distage-framework-docker](https://izumi.7mind.io/distage/distage-framework-docker) – A distage extension for using docker containers in tests or for local application runs, comes with example Postgres, Cassandra, Kafka & DynamoDB containers.
+4. [LogStage](https://izumi.7mind.io/logstage/) – Automatic structural logs from Scala string interpolations,
+5. [BIO](https://izumi.7mind.io/bio/) - A typeclass hierarchy for tagless final style with Bifunctor and Trifunctor effect types. Focused on ergonomics and ease of use with zero boilerplate.
+6. [izumi-reflect](https://github.com/zio/izumi-reflect) (moved to [zio/izumi-reflect](https://github.com/zio/izumi-reflect)) - Portable, lightweight and kind-polymorphic alternative to `scala-reflect`'s Typetag for Scala, Scala.js, Scala Native and ([soon](https://github.com/7mind/dotty-typetag-research)) Dotty
+7. [IdeaLingua](https://izumi.7mind.io/idealingua/) (moved to [7mind/idealingua-v1](https://github.com/7mind/idealingua-v1)) – API Definition, Data Modeling and RPC language, optimized for fast prototyping – like gRPC or Swagger, but with a human face. Generates RPC servers and clients for Go, TypeScript, C# and Scala,
+8. [Opinionated SBT plugins](https://izumi.7mind.io/sbt/) (moved to [7mind/sbtgen](https://github.com/7mind/sbtgen)) – Reduces verbosity of SBT builds and introduces new features – inter-project shared test scopes and BOM plugins (from Maven)
+9. [Percept-Plan-Execute-Repeat (PPER)](https://izumi.7mind.io/pper/) – A pattern that enables modeling very complex domains and orchestrate deadly complex processes a lot easier than you're used to.
+
+</details>
+
+---------------------------------------
+
+# Changes since 1.0.0
+
+### distage
+
+  * Fixed performance issues and a bug in handling of weak set bindings in compile-time checker
+
+### LogStage
+
+  * Add extension methods `.fail` & `.terminate` to `LogIO2`, they support a pattern of logging an error message and immediately failing or terminating with the same message (#1351)
+
+# Pull Requests merged since 1.0.0
+
+  * Add `LogIO2#fail`, `LogIO2#terminate` utility methods (#1351)
+  * Qualify names in IzArtifactMaterializer (#1339)
+  * Disable searching roles by subtype if `-Dizumi.distage.roles.reflection=false`, reduces launch time by omitting unnecessary work (#1349)
diff --git a/changelog-1.0.2.md b/changelog-1.0.2.md
new file mode 100644
index 000000000..9a5cd4f21
--- /dev/null
+++ b/changelog-1.0.2.md
@@ -0,0 +1,58 @@
+<p align="center">
+  <a href="https://izumi.7mind.io/">
+  <img width="40%" src="https://github.com/7mind/izumi/blob/develop/doc/microsite/src/main/tut/media/izumi-logo-full-purple.png?raw=true" alt="Izumi"/>
+  </a>
+</p>
+
+<details>
+  <summary>What is it?</summary>
+
+
+Izumi (*jp. 泉水, spring*) is an ecosystem of independent libraries and frameworks allowing you to significantly increase productivity of your Scala development.
+
+including the following components:
+
+1. [distage](https://izumi.7mind.io/distage/) – Transparent and debuggable Dependency Injection framework for pure FP Scala,
+2. [distage-testkit](https://izumi.7mind.io/distage/distage-testkit) – Hyper-pragmatic pure FP Test framework. Shares heavy resources globally across all test suites; lets you easily swap implementations of component; uses your effect type for parallelism.
+3. [distage-framework-docker](https://izumi.7mind.io/distage/distage-framework-docker) – A distage extension for using docker containers in tests or for local application runs, comes with example Postgres, Cassandra, Kafka & DynamoDB containers.
+4. [LogStage](https://izumi.7mind.io/logstage/) – Automatic structural logs from Scala string interpolations,
+5. [BIO](https://izumi.7mind.io/bio/) - A typeclass hierarchy for tagless final style with Bifunctor and Trifunctor effect types. Focused on ergonomics and ease of use with zero boilerplate.
+6. [izumi-reflect](https://github.com/zio/izumi-reflect) (moved to [zio/izumi-reflect](https://github.com/zio/izumi-reflect)) - Portable, lightweight and kind-polymorphic alternative to `scala-reflect`'s Typetag for Scala, Scala.js, Scala Native and ([soon](https://github.com/7mind/dotty-typetag-research)) Dotty
+7. [IdeaLingua](https://izumi.7mind.io/idealingua/) (moved to [7mind/idealingua-v1](https://github.com/7mind/idealingua-v1)) – API Definition, Data Modeling and RPC language, optimized for fast prototyping – like gRPC or Swagger, but with a human face. Generates RPC servers and clients for Go, TypeScript, C# and Scala,
+8. [Opinionated SBT plugins](https://izumi.7mind.io/sbt/) (moved to [7mind/sbtgen](https://github.com/7mind/sbtgen)) – Reduces verbosity of SBT builds and introduces new features – inter-project shared test scopes and BOM plugins (from Maven)
+9. [Percept-Plan-Execute-Repeat (PPER)](https://izumi.7mind.io/pper/) – A pattern that enables modeling very complex domains and orchestrate deadly complex processes a lot easier than you're used to.
+
+</details>
+
+---------------------------------------
+
+# Changes since 1.0.1
+
+### distage
+
+  * Documentation updates and new documentation chapters.
+  * Rename `PlannerInput.noGC`->`.everything`, add constructors with NonEmptySet for Roots/PlannerInput, fix Roots scaladoc, add `NonEmptySet#widen` (#1355)
+  * Add `Blocker` binding to cats.effect.IO's default module (#1362)
+  * Change `Lifecycle#mapK` to accept `bio.Morphism1` instead of `cats.FunctionK` (still source compatible due to conversion) (#1363)
+
+### distage-framework-docker
+
+  * add `DockerContainer#dependOnContainerPorts` shortcut, makes it easier to expose ports as env vars in a container (#1360)
+    * rename `dependOnDocker`->`dependOnContainer` for consistency, old name is deprecate
+
+### fundamentals-platform
+
+  * Show macro settings errors as `info` instead of `warning` (#1361)
+
+# Pull Requests merged since 1.0.1
+
+    * WIP 1.0 release notes (#1358)
+    * add `DockerContainer#dependOnContainerPorts` shortcut, makes it easier to expose ports as env vars in a container, rename `dependOnDocker`->`dependOnContainer` (#1360)
+    * Show macro settings errors as `info` instead of `warning` (#1361)
+    * Change `Lifecycle#mapK` to accept `bio.Morphism1` instead of `cats.FunctionK` (still source compatible due to conversion) (#1363)
+    * Add `Blocker` binding to cats.effect.IO's default module (#1362)
+    * Make all tests in `distage-testkit` docs run, improve examples, rewrite Out-of-the-box-typeclass instances chapter, Make `ZIO Has Bindings` example more understandable (#1357)
+    * Fix `DefaultModule` for ZIO+Cats, include cats-effect typeclass instances when `interop-cats` is on the classpath, not just when `cats-effect` is on the classpath (#1356)
+    * rename `PlannerInput.noGC`->`.everything`, add constructors with NonEmptySet for Roots/PlannerInput, fix Roots scaladoc, add `NonEmptySet#widen` (#1355)
+    * Avoid constructing Set in ModuleDefDSL `include` (#1353)
+    * Allow chaining after ModuleDefDSL `todo` keyword (#1352)
diff --git a/changelog.md b/changelog.md
new file mode 100644
index 000000000..76a6b19e1
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,313 @@
+<p align="center">
+  <a href="https://izumi.7mind.io/">
+  <img width="40%" src="https://github.com/7mind/izumi/blob/develop/doc/microsite/src/main/tut/media/izumi-logo-full-purple.png?raw=true" alt="Izumi"/>
+  </a>
+</p>
+
+<details>
+  <summary>What is it?</summary>
+
+
+Izumi (*jp. 泉水, spring*) is an ecosystem of independent libraries and frameworks allowing you to significantly increase productivity of your Scala development.
+
+including the following components:
+
+1. [distage](https://izumi.7mind.io/distage/) – Transparent and debuggable Dependency Injection framework for pure FP Scala,
+2. [distage-testkit](https://izumi.7mind.io/distage/distage-testkit) – Hyper-pragmatic pure FP Test framework. Shares heavy resources globally across all test suites; lets you easily swap implementations of component; uses your effect type for parallelism.
+3. [distage-framework-docker](https://izumi.7mind.io/distage/distage-framework-docker) – A distage extension for using docker containers in tests or for local application runs, comes with example Postgres, Cassandra, Kafka & DynamoDB containers.
+4. [LogStage](https://izumi.7mind.io/logstage/) – Automatic structural logs from Scala string interpolations,
+5. [BIO](https://izumi.7mind.io/bio/) - A typeclass hierarchy for tagless final style with Bifunctor and Trifunctor effect types. Focused on ergonomics and ease of use with zero boilerplate.
+6. [izumi-reflect](https://github.com/zio/izumi-reflect) (moved to [zio/izumi-reflect](https://github.com/zio/izumi-reflect)) - Portable, lightweight and kind-polymorphic alternative to `scala-reflect`'s Typetag for Scala, Scala.js, Scala Native and ([soon](https://github.com/7mind/dotty-typetag-research)) Dotty
+7. [IdeaLingua](https://izumi.7mind.io/idealingua/) (moved to [7mind/idealingua-v1](https://github.com/7mind/idealingua-v1)) – API Definition, Data Modeling and RPC language, optimized for fast prototyping – like gRPC or Swagger, but with a human face. Generates RPC servers and clients for Go, TypeScript, C# and Scala,
+8. [Opinionated SBT plugins](https://izumi.7mind.io/sbt/) (moved to [7mind/sbtgen](https://github.com/7mind/sbtgen)) – Reduces verbosity of SBT builds and introduces new features – inter-project shared test scopes and BOM plugins (from Maven)
+9. [Percept-Plan-Execute-Repeat (PPER)](https://izumi.7mind.io/pper/) – A pattern that enables modeling very complex domains and orchestrate deadly complex processes a lot easier than you're used to.
+
+</details>
+
+---------------------------------------
+
+We're happy to present Izumi 1.0 release! There are a lot of features included this release that we have not anticipated that we'd be able to include in 1.0,
+and even some that we did not consider possible to actually implement in Scala. It happened to be quite delayed because of that, but we believe that in the end it was worth the wait.
+
+Key highlights: Compile-time checks, GraalVM/Native Image support and a massive improvement of BIO hierarchy.
+
+Compatibilty note: there are multiple renames for all kinds of entities in this release, old names are preserved as deprecated aliases to ease migration from 1.0.
+
+Thanks to all our contributors and especially @Caparow, @CoreyOConnor and @VladPodilnyk!
+
+# Major Features since 0.10.18
+
+## distage
+
+### Compile-time safety
+
+Total compile-time safety lands for `distage` in 1.0, although previously we thought it was impossible to implement!
+
+The new compile-time checks provide fast feedback during development and remove the need to launch tests or launch application itself or write special tests to check the correctness of wiring.
+
+For users of `distage-extension-config`, config parsing will be verified at compile-time against the default config files in resource folder.
+There is an option to disable config checking or check against a specific resource file instead of defaults.
+
+Checking is not enabled out-of-the-box, you must enable it by adding a “trigger” object in test scope. This object will emit compile-time errors for any issues or omissions in your `ModuleDefs`.
+It will recompile itself as necessary to provide feedback during development.
+
+See [“Compile-time checks” chapter](https://izumi.7mind.io/distage/distage-framework#compile-time-checks) for details.
+
+### Mutators
+
+Since 1.0 you may add "mutator" bindings to your module definitions. Mutators apply functions to an object before it's visible to others in the object graph, they may also request additional dependencies for use in the function. Mutators provide a way to do partial overrides or slight modifications of some existing component without redefining it fully.
+
+Motivating example: Suppose you're using a config case class in your `distage-testkit` tests, and for one of the test you want to use a modified value for one of the fields in it. Before 1.0 you'd have to duplicate the config binding into a new key and apply the modifying function to it:
+
+```scala
+import distage.{Id, ModuleDef}
+import distage.config.ConfigModuleDef
+import izumi.distage.testkit.TestConfig
+import izumi.distage.testkit.scalatest.SpecIdentity
+
+class MyTest extends SpecIdentity {
+  override def config: TestConfig = super.config.copy(
+    moduleOverrides = new ConfigModuleDef {
+      makeConfig[Config]("config.myconfig").named("duplicate")
+      make[Config].from {
+        (thatConfig: Config @Id("duplicate")) =>
+          modifyingFunction(thatConfig)
+      }
+    }
+  )
+}
+```
+
+Now instead of overriding the entire binding, we may use a mutator:
+
+```scala
+class MyTest extends SpecIdentity {
+  override def config: TestConfig = super.config.copy(
+    moduleOverrides = new ModuleDef {
+      modify[Config](modifyingFunction(_))
+    }
+  )
+}
+```
+
+See [“Mutator Bindings” chapter](https://izumi.7mind.io/distage/basics#mutator-bindings) for details.
+
+### Out-of-the-box typeclass instances
+
+Before 1.0, it was a major chore to have to add bindings for typeclass instances such as `Sync[F]` and `ContextShift[F]` for your effect type to use them in DI. While pre-made modules existed in `distage-framework` library, they were hard to discover and had to be manually included into your own modules.
+
+Since 1.0, instances for [BIO](https://izumi.7mind.io/bio/) & [cats-effect](https://typelevel.org/cats-effect/) typeclass hierarchies are added automatically when you specify the effect type for your wiring and are can be summoned in the object graph without adding them manually, out of the box.
+
+See [“Out-of-the-box typeclass instances” chapter](https://izumi.7mind.io/distage/basics#out-of-the-box-typeclass-instances) for details.
+
+## distage-testkit
+
+### Multi-Level Memoization
+
+`distage-testkit`'s memoization mechanism allows you to share heavy components, such as docker containers managed by [`distage-framework-docker`](https://izumi.7mind.io/distage/distage-framework-docker), globally across all test suites, or across a defined subset of test suites. The scope of sharing is derived implicitly from parameters set in test suite's `TestConfig`, test suites with compatible `TestConfig`'s — compatible in a way that executing them will result in identical memoized components — will acquire those memoized components only once and share them.
+
+This scheme had a weakness in that incompatible `TestConfig`s would cause a re-creation of all memoized instances, even very heavy ones.
+
+Since 1.0, memoization has been generalized to support unlimited nesting, with new strategy, the memoization environment may be manually partitioned into levels and if a change in `TestConfig` does not cause a divergence at one of the levels, the nested levels may then fully reuse the object sub-graph of all parent levels that do not diverge.
+
+For clarity, the memoization tree structure is printed before test runs. For example, a memoization tree of a project with the following test suites:
+
+```scala
+class SameLevel_1_WithActivationsOverride extends Spec3[ZIO] {
+  override protected def config: TestConfig = {
+    super.config.copy(
+        memoizationRoots = Map(
+          1 -> Set(DIKey[MemoizedInstance], DIKey[MemoizedLevel1]),
+          2 -> Set(DIKey[MemoizedLevel2]),
+        ),
+    )
+  }
+}
+
+class SameLevel_1_2_WithAdditionalLevel3 extends SameLevel_1_WithActivationsOverride {
+  override protected def config: TestConfig = {
+    super.config.copy(
+      memoizationRoots =
+        super.config.memoizationRoots ++
+        Set(DIKey[MemoizedLevel3]),
+    )
+  }
+}
+```
+
+May be visualized as follows:
+
+![Memoization Tree Log during tests](https://izumi.7mind.io/distage/media/memoization-tree.png)
+
+See ["Memoization Levels" chapter](https://izumi.7mind.io/distage/distage-testkit#multi-level-memoization) for details.
+
+### distage-testkit documentation
+
+@CoreyOConnor has contributed a brand new, shining microsite page for `distage-testkit`! It contains both reference material and a tutorial to get started, so check it out on ["distage-testkit" section](https://izumi.7mind.io/distage/distage-testkit)!
+
+## distage-framework-docker
+
+You can now set `Docker.ContainerConfig` `alwaysPull` field to `false` to disable pulling docker images for the container by @vilunov (#1300)
+
+## BIO
+
+`BIO` is an hierarchy of typeclasses for tagless final style with bifunctor and trifunctor effects. In 1.0 it has been majorly restructured.
+
+First, all classes have been renamed according to a new naming convention:
+
+* Bifunctor class naming pattern has changed from `BIO<name>` to `<name>2`. e.g. `BIOFunctor` is now `Functor2`, `BIO` class is now `IO2`
+* Trifunctor class naming pattern has changed from `BIO<name>3` to `<name>3`, e.g. `BIOMonadAsk3` is now `MonadAsk3`, `BIO3` is now `IO3`
+
+When using old names, deprecation warnings will guide towards the new names.
+
+Second, `Parallel*`, `Concurrent*` and `Temporal*` capabilities now do not require `IO*` (an analogue of `cats-effect`'s Sync), enabling you to use concurrency and parallelism without inviting side-effects in.
+
+Third, compatibility with `cats-effect` has been improved. @VladPodilnyk has contributed Discipline laws (#1249) which allowed extending cats conversions' support to cover `ConcurrentEffect` whereas before BIO could only convert up to `Concurrent`. Now you can run `http4s` and all the other cats-effect libraries with just BIO typeclasses, without requiring any cats-effect typeclasses.
+
+Forth, new primitives were added, bifunctor `Free` monad, as well as `FreeError` and `FreePanic` data types were contributed by @Caparow, they provide building blocks for DSLs when combined with a DSL describing functor. `Morphism1/2/3` provide unboxed natural transformations for functors with 1,2,3-arity respectively, with `Isomorphism1/2/3` modeling two-way transformations.
+
+And last, instances of BIO for `monix-bio` have been added to default implicit scope, also contributed by @VladPodilnyk. (these do not force a `monix-bio` dependency)
+
+The new hierarchy is visualized as follows:
+
+![BIO Hierarchy](https://izumi.7mind.io/bio/media/bio-relationship-hierarchy.svg)
+
+See ["BIO Hierarchy" chapter](https://izumi.7mind.io/bio/) for details.
+
+## LogStage
+
+@an-tex has contributed a fix for `LogstageCodec[Throwable]` instance (#1206)
+
+Classes were renamed to follow the new `BIO` naming convention, e.g. `LogBIO` has been renamed to `LogIO2`. When using old names, deprecation warnings will guide towards the new names.
+
+## Other changes
+
+### Reworked Role Launcher
+
+`distage` roles are a generalization of application entrypoints. An application may have many roles, which can be either one-shot tasks or persistent services.
+
+Roles come with default command-line parsing and config reading behavior. Before `1.0`, these and other aspects of `distage-framework`'s Role Launcher have been hard to customize or override. Since 1.0 Role Launcher has been rewritten to be configured using `distage` itself and all of its behavior can now be customized or
+overridden using custom modules.
+
+See ["Roles" chapter](https://izumi.7mind.io/distage/distage-framework.html#roles) for details.
+
+### Compile-time plugin discovery
+
+`distage` supports classpath discovery of modules through an extension. Before 1.0, this extension was hard to use in Graal Native Image applications due to reliance on runtime reflection in the implementation.
+
+Since 1.0 you can perform plugin scanning at compile-time using [`PluginConfig.compileTime`](https://izumi.7mind.io/api/izumi/distage/plugins/PluginConfig$.html#compileTime(pluginsPackage:String):izumi.distage.plugins.PluginConfig).
+
+Compile-time scanning enables plugin-based development workflow on platforms without runtime reflection, such as Graal Native, however the runtime extension aspect of plugins is lost.
+
+See ["Compile-time scanning" chapter](https://izumi.7mind.io/distage/distage-framework#compile-time-scanning) for details.
+
+### IntegrationCheck[F]
+
+Contributed by @Caparow, `IntegrationCheck`'s may now be parameterized by `F[_]` and use effects.
+
+See ["Using IntegrationCheck" chapter](https://izumi.7mind.io/distage/distage-testkit#using-integrationcheck) for details.
+
+### Lifecycle instances
+
+Contributed by @VladPodilnyk, `distage.Lifecycle` now has typeclass instances for `cats` `Monad`, `Monoid` and `BIO` `Functor2`, `Functor3` in default implicit scope. (these do not force a `cats` dependency)
+
+#### Changes in Activation behavior
+
+Configuring the object graph using `Activation` changed in 1.0, namely:
+
+1. Opposite activations now prune. Before, a sole binding with an activation such as `make[MyRepo].tagged(Repo.Dummy)` would be accessible in the object graph even if a contradictory activation such as `Repo.Prod` was activated. This is fixed now, and `MyRepo` is removed in such a case, it can only be made available again in an opposite axis by defining a binding with `.tagged(Repo.Prod)`
+
+2. Set elements and mutators are now subject to configuration. Before, a declarations such as
+  ```scala
+  many[Int]
+    .add(1).tagged(Mode.Test)
+    .add(3).tagged(Mode.Prod)
+  ```
+  would not have the intended effect, with _both_ integers remaining in `Set[Int]`. Since 1.0, activations apply to set elements too, so `Set[Int]` would contain either `Set(1)` or `Set(3)` depending on the value of `Mode`.
+  For mutators, individual mutations can be tagged, and will be applied according to activation:
+  ```scala
+  modify[Int](_ + 10).tagged(Mode.Test)
+  modify[Int](_ + 1).tagged(Mode.Prod)
+  ```
+
+3. Semantics for multi-dimensional activations have been cleaned up, when used, they now behave according to "specificity rules". Bindings with no assigned activations can now behave as "defaults" in cases where all other bindings are definitely unapplicable. See ["Specificity and defaults" chapter](https://izumi.7mind.io/distage/basics#specificity-and-defaults) for details
+
+See ["Activation Axis" chapter](https://izumi.7mind.io/distage/basics#activation-axis) for details
+
+#### Renames
+
+Multiple entities in `distage` have been renamed, including the following:
+
+* `DIResource`, `DIResourceBase` -> `Lifecycle`
+* `DIEffect` -> `QuasiIO`
+* `ProviderMagnet` -> `Functoid`
+
+When using old names, deprecation warnings will guide towards the new names.
+
+# Pull Requests merged since 0.10.18
+
+  * Fix #1226 Finalize static plan checker API (#1296)
+  * Add a docker container config flag to control explicit image pulling (#1300)
+  * Fix #1226 New compile-time checker API (#1285)
+  * Remove 2.12 compat from FunctionK/FunctionKK (you need `-Xsource:2.13` for the rest of the library anyway and it enables correct implicit scopes for abstract types) (#1293)
+  * Add cats/bio instances for `Lifecycle` (#1280)
+  * Feature: Free monad (#1278)
+  * Fix #1274 remove BIO* prefix from all BIO classes and switch to a new naming scheme (#1276)
+  * Fix #1275: rename `DIEffect` to `QuasiEffect` (#1292)
+  * Add `make[X].addDependencies` and `modify[X].addDependencies` syntax in ModuleDef (#1288)
+  * Make `Lifecycle#flatMap` interruptible (#1286)
+  * Make `Lifecycle.liftF` & `Lifecycle.LiftF` interruptible (#1284)
+  * Documentation yak shave (#1283)
+  * Fix mutators overriding normal bindings and each other. Add mutator syntax to syntax reference. Mention that javax.inject.Named is supported in docs. (#1272)
+  * remove ListSet (#1273)
+  * Fix #894: remove http4s dependency in microsite, use a command-line calculator as an example instead. (#1271)
+  * Feature/memoization levels (#1251)
+  * Document Lifecycle inheritance helpers, document using them to workaround for implicit injection, `acquire`/`release`/`extract` methods. Add `Lifecycle#widen`/`#widenF` (#1270)
+  * Re-add `bootstrapPluginConfig` field to `RoleAppMain` (#1268)
+  * Turns out there's no such word as "overriden". Rename `overridenBy` to `overriddenBy` (double D) (#1263)
+  * Test built-in BIO* instances using cats-effect Laws  (#1249)
+  * Fix #1138:  Preserve interruption when converting ZManaged/Resource to `Lifecycle` (#1258)
+  * Rename `DIResourceBase` to `Lifecycle` (#1247)
+  * Add Tele2 to users (#1257)
+  * Sort docker networks by `id` when trying to reuse network, add more logging to container/network reuse (#1256)
+  * Fix #1073 Assert that effect type is compatible in `OrderedPlan#assertValid`. Rename `assertImportsResolvedOrThrow` -> `assertValid` (#1254)
+  * Include support module for chosen effect type automatically, deprecate old `XDIEffectModule`'s. Fix #1015 move support modules for effect types from distage-framework to distage-core,  (#1235)
+  * Implement `BIOPrimitives` for `monix-bio` (#1246)
+  * Add links to test suites written in `distage-testkit` to docs (#1244)
+  * distage-testkit: Add `AssertSync` (#1243)
+  * wip #1225 (#1242)
+  * Add BIOConcurrent between BIOAsync & BIOParallel (#1240)
+  * Re-add Launchers[F] to RoleAppMain (#1241)
+  * Fix #1051 Move BIOTemporal to a side-hierarchy (#1236)
+  * Update distage-testkit example to latest version from `distage-example` (now includes `Scene.Managed` example) (#1237)
+  * testkit memoization and parallel execution docs (#1200)
+  * Add missing BIOParallel's Monad syntax (#1234)
+  * Fix monix-bio BIOTemporal instance: count the time correctly, require cats-effect Timer instead of Clock2 (available in monix-bio by default); fix BIOTemporal instance resolution (#1231)
+  * Allow non-Throwable errors in `BIOError#withFilter`, include source position in error message (#1230)
+  * Fix #1054: rename `BIOMonadError`->`BIOError`, downgrade BIOApplicativeError to an Applicative on both parameters, instead of a Monad on the error (#1229)
+  * Convenient plugins that handles cats typeclasses. (#1227)
+  * Update definition of `race`/`racePair` for ZIO to match current interop-cats (#1228)
+  * Fix #1178: preserve `@Id` annotation in higher-kinded type aliases (#1209)
+  * Feature: identity compat for integration checks (#1157)
+  * Rename `Injector.apply` with activation parameter to `Injector.withBootstrapActivation` since the passed activation is no longer applied to non-bootstrap modules (#1222)
+  * Use axis defaults from #1030 in `BootstrapLocator` (#1223)
+  * Rename `World.Dummy` back to `World.Mock` (#1224)
+  * Role-based application uses Injector to bootstrap (#1208)
+  * Fix #1204 synchronize default LogstageCodec for Throwable with LogstageCirceRenderingPolicy implementation for Throwable (#1206)
+  * Fix #1201 LogstageCodec for Boolean (#1202)
+  * Add `DIResource.unit`, make `DIResource.pure` polymorphic in `F` (#1198)
+  * add `BIORunner#unsafeRunAsyncAsFuture`, deprecate `AsEither` suffixes (#1199)
+  * Monix BIO instances (#996)
+  * Support incremental compilation in `StaticPluginChecker` compile-time checking macro (#1194)
+  * distage testkit docs part 1 (#1145)
+  * ConfigWriter fixes: fix regression when config minimization happened before GC, removed ConfigPathExtractor as it can seemingly no longer work with new `trisect` implementation, add band-aid to ConfigWriter to always include "activation" section before #779 (#1177)
+  * docker-global-reuse followup: Merge GlobalDockerReusePolicy & DockerReusePolicy; fix ContainerNetworkDef reuse & IntegrationCheckException (#1159)
+  * WIP: Adapt distage-testkit to 0.11.0 Activation API changes (#1143)
+  * locator can be accessed outside of launcher (#1147)
+  * ProviderMagnet -> Functoid (#1148)
+  * plugin scanning done in compile time (#1139)
+  * Feature/flexible reuse (#1146)
+  * provisioner collects operation timings (#1137)
+  * Use F[_] in integraion check. (#1152)
+  * Fix #1124 NullPointerException in LogstageCirceRenderingPolicy when Throwable#getMessage returns null (e.g. in NullPointerException) (#1149)
+  * 0.11.0: Mutators (#845)
diff --git a/distage/distage-core-api/src/main/scala/izumi/distage/AbstractLocator.scala b/distage/distage-core-api/src/main/scala/izumi/distage/AbstractLocator.scala
index b5e006ab2..8ab1759df 100644
--- a/distage/distage-core-api/src/main/scala/izumi/distage/AbstractLocator.scala
+++ b/distage/distage-core-api/src/main/scala/izumi/distage/AbstractLocator.scala
@@ -13,7 +13,7 @@ trait AbstractLocator extends Locator {
     lookupLocalUnsafe(key)
       .map {
         value =>
-          assert(key.tpe <:< SafeType.get[T], s"$key in not a subtype of ${SafeType.get[T]}")
+          require(key.tpe <:< SafeType.get[T], s"$key in not a subtype of ${SafeType.get[T]}")
           TypedRef(value.asInstanceOf[T], key.tpe, isByName = false)
       }
   }
@@ -54,6 +54,6 @@ trait AbstractLocator extends Locator {
   private[this] final def recursiveLookup[T: Tag](key: DIKey, locator: Locator): Option[TypedRef[T]] = {
     locator
       .lookupLocal[T](key)
-      .orElse(locator.parent.flatMap(p => recursiveLookup(key, p)))
+      .orElse(locator.parent.flatMap(p => recursiveLookup[T](key, p)))
   }
 }
diff --git a/distage/distage-core-api/src/main/scala/izumi/distage/model/definition/dsl/ModuleDefDSL.scala b/distage/distage-core-api/src/main/scala/izumi/distage/model/definition/dsl/ModuleDefDSL.scala
index 021fb64e8..49bd39a82 100644
--- a/distage/distage-core-api/src/main/scala/izumi/distage/model/definition/dsl/ModuleDefDSL.scala
+++ b/distage/distage-core-api/src/main/scala/izumi/distage/model/definition/dsl/ModuleDefDSL.scala
@@ -11,6 +11,7 @@ import izumi.distage.model.definition.dsl.ModuleDefDSL.{MakeDSL, MakeDSLUnnamedA
 import izumi.distage.model.providers.Functoid
 import izumi.distage.model.reflection.{DIKey, SafeType}
 import izumi.functional.bio.Local3
+import izumi.functional.bio.data.Morphism1
 import izumi.fundamentals.platform.language.CodePositionMaterializer
 import izumi.reflect.{Tag, TagK, TagK3}
 import zio._
@@ -625,12 +626,7 @@ object ModuleDefDSL {
   }
 
   @inline private[this] def provideLifecycle[F[-_, +_, +_], R, E, A](F: Local3[F])(resource: Lifecycle[F[R, E, ?], A], r: R): Lifecycle[F[Any, E, ?], A] = {
-    new Lifecycle[F[Any, E, ?], A] {
-      override type InnerResource = resource.InnerResource
-      override def acquire: F[Any, E, InnerResource] = F.provide(resource.acquire)(r)
-      override def release(rr: InnerResource): F[Any, E, Unit] = F.provide(resource.release(rr))(r)
-      override def extract[B >: A](rr: InnerResource): Either[F[Any, E, A], A] = resource.extract(rr).left.map(F.provide(_)(r))
-    }
+    resource.mapK(Morphism1[F[R, E, ?], F[Any, E, ?]](F.provide(_)(r)))
   }
 
   // DSL state machine
diff --git a/distage/distage-framework/src/main/scala/izumi/distage/roles/RoleAppMain.scala b/distage/distage-framework/src/main/scala/izumi/distage/roles/RoleAppMain.scala
index 06b171b05..0182f2772 100644
--- a/distage/distage-framework/src/main/scala/izumi/distage/roles/RoleAppMain.scala
+++ b/distage/distage-framework/src/main/scala/izumi/distage/roles/RoleAppMain.scala
@@ -48,7 +48,7 @@ abstract class RoleAppMain[F[_]](
   override val tagK: TagK[F],
   val defaultModule: DefaultModule[F],
   val artifact: IzArtifactMaterializer,
-) extends RoleCheckableApp[F] { self =>
+) extends RoleCheckableApp[F] {
 
   protected def pluginConfig: PluginConfig
   protected def bootstrapPluginConfig: PluginConfig = PluginConfig.empty
@@ -59,15 +59,14 @@ abstract class RoleAppMain[F[_]](
     *
     * @see [[izumi.distage.roles.RoleAppBootModule]] for initial values of [[roleAppBootModule]]
     *
-    * @note The components added here are visible during the creation of the app, not *inside* the app,
-    *       to add components *inside* the app, add a mutator for the component `Module @Id("roleapp")`,
-    *       example:
+    * @note The components added here are visible during the creation of the app, but *not inside* the app,
+    *       to override components *inside* the app, use `pluginConfig` & [[izumi.distage.plugins.PluginConfig#overriddenBy]]:
     *
     *       {{{
-    *       override def roleAppBootOverrides(@unused argv: ArgV): Module = super.roleAppBootOverrides(argv) ++ new ModuleDef {
-    *         modify[Module].named("roleapp")(_ ++ new ModuleDef {
-    *           make[MyComponentX](
-    *         })
+    *       override def pluginConfig: PluginConfig = {
+    *         super.pluginConfig overriddenBy new PluginDef {
+    *           make[MyComponentX]]
+    *         }
     *       }
     *       }}}
     */
@@ -102,15 +101,15 @@ abstract class RoleAppMain[F[_]](
     * object WiringTest extends PlanCheck.Main(MyApp, PlanCheckConfig(...))
     * }}}
     */
-  @open class PlanCheck[Cfg <: PlanCheckConfig.Any](cfg: Cfg = PlanCheckConfig.empty)(implicit planCheck: PlanCheckMaterializer[self.type, Cfg])
-    extends izumi.distage.framework.PlanCheck.Main[self.type, Cfg](self, cfg)
+  @open class PlanCheck[Cfg <: PlanCheckConfig.Any](cfg: Cfg = PlanCheckConfig.empty)(implicit planCheck: PlanCheckMaterializer[this.type, Cfg])
+    extends izumi.distage.framework.PlanCheck.Main[this.type, Cfg](this, cfg)
 
   /** @see [[izumi.distage.framework.PlanCheck.assertAppCompileTime]] */
   def assertAppCompileTime[Cfg <: PlanCheckConfig.Any](
     cfg: Cfg = PlanCheckConfig.empty
-  )(implicit planCheck: PlanCheckMaterializer[self.type, Cfg]
-  ): PlanCheckMaterializer[self.type, Cfg] = {
-    izumi.distage.framework.PlanCheck.assertAppCompileTime[self.type, Cfg](self, cfg)
+  )(implicit planCheck: PlanCheckMaterializer[this.type, Cfg]
+  ): PlanCheckMaterializer[this.type, Cfg] = {
+    izumi.distage.framework.PlanCheck.assertAppCompileTime[this.type, Cfg](this, cfg)
   }
 
   override final def roleAppBootModule: Module = {
diff --git a/distage/distage-framework/src/main/scala/izumi/distage/roles/launcher/RoleAppEntrypoint.scala b/distage/distage-framework/src/main/scala/izumi/distage/roles/launcher/RoleAppEntrypoint.scala
index 6336a89b2..cb7628226 100644
--- a/distage/distage-framework/src/main/scala/izumi/distage/roles/launcher/RoleAppEntrypoint.scala
+++ b/distage/distage-framework/src/main/scala/izumi/distage/roles/launcher/RoleAppEntrypoint.scala
@@ -81,7 +81,7 @@ object RoleAppEntrypoint {
       }
     }
 
-    protected def runTasks(index: Map[String, Object])(implicit F: QuasiIO[F]): F[Unit] = {
+    protected def runTasks(index: Map[String, AbstractRole[F]])(implicit F: QuasiIO[F]): F[Unit] = {
       val tasksToRun = parameters.roles.flatMap {
         r =>
           index.get(r.role) match {
@@ -117,16 +117,15 @@ object RoleAppEntrypoint {
     }
 
     private def getRoleIndex(rolesLocator: Locator): Map[String, AbstractRole[F]] = {
-      roles
-        .availableRoleBindings.flatMap {
-          b =>
-            rolesLocator.index.get(b.binding.key) match {
-              case Some(value: AbstractRole[F]) =>
-                Seq(b.descriptor.id -> value)
-              case _ =>
-                Seq.empty
-            }
-        }.toMap
+      roles.availableRoleBindings.flatMap {
+        b =>
+          rolesLocator.lookupInstance[AbstractRole[F]](b.binding.key) match {
+            case Some(value) =>
+              Seq(b.descriptor.id -> value)
+            case _ =>
+              Seq.empty
+          }
+      }.toMap
     }
 
   }
diff --git a/distage/distage-framework/src/main/scala_2.12/izumi/distage/framework/PlanCheckConfig.scala b/distage/distage-framework/src/main/scala_2.12/izumi/distage/framework/PlanCheckConfig.scala
index 403655aad..f250d78ec 100644
--- a/distage/distage-framework/src/main/scala_2.12/izumi/distage/framework/PlanCheckConfig.scala
+++ b/distage/distage-framework/src/main/scala_2.12/izumi/distage/framework/PlanCheckConfig.scala
@@ -3,6 +3,8 @@ package izumi.distage.framework
 import izumi.fundamentals.platform.language.literals.{LiteralBoolean, LiteralString}
 
 /**
+  * Options to alter the behavior of [[izumi.distage.framework.PlanCheck]]
+  *
   * If on Scala `2.12` you're getting errors such as
   *
   * {{{
diff --git a/distage/distage-framework/src/main/scala_2.13/izumi/distage/framework/PlanCheckConfig.scala b/distage/distage-framework/src/main/scala_2.13/izumi/distage/framework/PlanCheckConfig.scala
index 9e7629779..5c60df175 100644
--- a/distage/distage-framework/src/main/scala_2.13/izumi/distage/framework/PlanCheckConfig.scala
+++ b/distage/distage-framework/src/main/scala_2.13/izumi/distage/framework/PlanCheckConfig.scala
@@ -1,6 +1,8 @@
 package izumi.distage.framework
 
 /**
+  * Options to alter the behavior of [[izumi.distage.framework.PlanCheck]]
+  *
   * @param roles              "*" to check all roles,
   *
   *                           "role1 role2" to check specific roles,
@@ -22,19 +24,19 @@ package izumi.distage.framework
   *
   * @param config             Config resource file name, e.g. "application.conf" or "*" if using the same config settings as `roleAppMain`
   *
-  * @param checkConfig        Try to parse config file checking all the config bindings added using [[izumi.distage.config.ConfigModuleDef]] default: `true`
+  * @param checkConfig        Try to parse config file checking all the config bindings added using [[izumi.distage.config.ConfigModuleDef]]. Default: `true`
   *
-  * @param printBindings      Print all the bindings loaded from plugins when a problem is found during plan checking. default: `false`
+  * @param printBindings      Print all the bindings loaded from plugins when a problem is found during plan checking. Default: `false`
   *
-  * @param onlyWarn           Do not abort compilation when errors are found, just print a warning instead. Does not affect plan checks performed at runtime. default: `false`
+  * @param onlyWarn           Do not abort compilation when errors are found, just print a warning instead. Does not affect plan checks performed at runtime. Default: `false`
   */
-final class PlanCheckConfig[Roles <: String, ExcludeActivations <: String, Config <: String, CheckConfig <: Boolean, PrintBindings <: Boolean, OnlyWarn <: Boolean](
-  val roles: Roles,
-  val excludeActivations: ExcludeActivations,
-  val config: Config,
-  val checkConfig: CheckConfig,
-  val printBindings: PrintBindings,
-  val onlyWarn: OnlyWarn,
+final case class PlanCheckConfig[Roles <: String, ExcludeActivations <: String, Config <: String, CheckConfig <: Boolean, PrintBindings <: Boolean, OnlyWarn <: Boolean](
+  roles: Roles,
+  excludeActivations: ExcludeActivations,
+  config: Config,
+  checkConfig: CheckConfig,
+  printBindings: PrintBindings,
+  onlyWarn: OnlyWarn,
 )
 
 object PlanCheckConfig {
diff --git a/doc/microsite/src/main/tut/bio/00_bio.md b/doc/microsite/src/main/tut/bio/00_bio.md
index dcc27645e..713bad86f 100644
--- a/doc/microsite/src/main/tut/bio/00_bio.md
+++ b/doc/microsite/src/main/tut/bio/00_bio.md
@@ -116,3 +116,24 @@ you are also always importing the syntax-providing implicit conversions.
 This happens to be a great fit for Tagless Final Style, since nearly all TF code will import the names of the used typeclasses.
 
 Implicit Punning for typeclass syntax relieves the programmer from having to manually import syntax implicits in every file in their codebase.
+
+## cats-effect compatibilty
+
+Import `izumi.functional.bio.catz._` for shim compatibilty with cats-effect. You can run `http4s` and all the other cats-effect libraries with just BIO typeclasses, without requiring any cats-effect typeclasses.
+
+## Data Types
+
+`Ref2/3`, `Promise2/3` and `Semaphore2/3` provide basic concurrent mutable state primitives. They require a `Primitives2/3` capability to create.
+With `Primitiives*[F]` in implicit scope, use `F.mkRef`/`F.mkPromise`/`F.mkSemaphore` respectively. (See also example at top of the page)
+
+`Free` monad, as well as `FreeError` and `FreePanic`  provide building blocks for DSLs when combined with a DSL describing functor.
+
+`Morphism1/2/3` provide unboxed natural transformations for functors with 1,2,3-arity respectively, with `Isomorphism1/2/3` modeling two-way transformations.
+
+## Misc Capabilities
+
+`Clock1/2/3` gives access to current time.
+
+`Entropy1/2/3` models random numbers.
+
+`UnsafeRun2/3` allows executing effects (it is required for conversion to cats' `ConcurrentEffect` which also allows unsafe running)
diff --git a/doc/microsite/src/main/tut/distage/basics.md b/doc/microsite/src/main/tut/distage/basics.md
index df87ad005..ee4b9fd6c 100644
--- a/doc/microsite/src/main/tut/distage/basics.md
+++ b/doc/microsite/src/main/tut/distage/basics.md
@@ -361,6 +361,57 @@ runWith(Activation(Style -> Style.AllCaps, Mode -> Mode.Prod))
 runWith(Activation(Style -> Style.AllCaps, Mode -> Mode.Test))
 ```
 
+#### Specificity and defaults
+
+When multiple dimensions are attached to a binding, bindings with less specified dimensions will be considered less specific
+and will be overridden by bindings with more dimensions, if all of those dimensions are explicitly set.
+
+A binding with no attached dimensions is considered a "default" vs. a binding with attached dimensions. A default will be chosen only if all other bindings are explicitly contradicted by passed activations. If the dimensions for other bindings are merely unset, it will cause an ambiguity error.
+
+Example of these rules:
+
+```scala mdoc:to-string
+import scala.util.Try
+
+sealed trait Color
+case object RED extends Color
+case object Blue extends Color
+case object Green extends Color
+
+// Defaults:
+
+def DefaultsModule = new ModuleDef {
+  make[Color].from(Green)
+  make[Color].tagged(Style.AllCaps).from(RED)
+}
+
+Injector().produceRun(DefaultsModule, Activation(Style -> Style.AllCaps))(println(_: Color))
+
+Injector().produceRun(DefaultsModule, Activation(Style -> Style.Normal))(println(_: Color))
+
+// ERROR Ambiguous without Style
+Try { Injector().produceRun(DefaultsModule, Activation.empty)(println(_: Color)) }.isFailure
+
+// Specificity
+
+def SpecificityModule = new ModuleDef {
+  make[Color].tagged(Mode.Test).from(Blue)
+  make[Color].tagged(Mode.Prod).from(Green)
+  make[Color].tagged(Mode.Prod, Style.AllCaps).from(RED)
+}
+
+Injector().produceRun(SpecificityModule, Activation(Mode -> Mode.Prod, Style -> Style.AllCaps))(println(_: Color))
+
+Injector().produceRun(SpecificityModule, Activation(Mode -> Mode.Test, Style -> Style.AllCaps))(println(_: Color))
+
+Injector().produceRun(SpecificityModule, Activation(Mode -> Mode.Prod, Style -> Style.Normal))(println(_: Color))
+
+Injector().produceRun(SpecificityModule, Activation(Mode -> Mode.Test))(println(_: Color))
+
+// ERROR Ambiguous without Mode
+Try { Injector().produceRun(SpecificityModule, Activation(Style -> Style.Normal))(println(_: Color)) }.isFailure
+```
+
 ## Resource Bindings, Lifecycle
 
 You can specify object lifecycle by injecting @scaladoc[distage.Lifecycle](izumi.distage.model.definition.Lifecycle), [cats.effect.Resource](https://typelevel.org/cats-effect/datatypes/resource.html) or
@@ -572,6 +623,8 @@ what exact components are available for each effect type, see
 @scaladoc[MonixBIOSupportModule](izumi.distage.modules.support.MonixBIOSupportModule),
 @scaladoc[ZIOCatsEffectInstancesModule](izumi.distage.modules.typeclass.ZIOCatsEffectInstancesModule), respectively.
 
+DefaultModule occurs as an implicit parameter in `distage` entrypoints that require an effect type parameter, namely: `Injector[F]()` in `distage-core`, @ref[`extends RoleAppMain[F]`](distage-framework.md#roles) and @ref[`extends PlanCheck.Main[F]`](distage-framework.md#compile-time-checks) in `distage-framework` and @ref[`extends Spec1[F]`](distage-testkit.md) in `distage-testkit`.
+
 ## Set Bindings
 
 Set bindings are useful for implementing listeners, plugins, hooks, http routes, healthchecks, migrations, etc.
@@ -701,7 +754,7 @@ Mutators provide a way to do partial overrides or slight modifications of some e
 Example:
 
 ```scala mdoc:reset:to-string
-import distage.{ModuleDef, Id, Injector}
+import distage.{Id, Injector, ModuleDef}
 
 def startingModule = new ModuleDef {
   make[Int].fromValue(1) // 1
@@ -730,6 +783,61 @@ Injector().produceRun(
 )((currentInt: Int) => currentInt): Int
 ```
 
+Another example: Suppose you're using a config case class in your @ref[`distage-testkit`](distage-testkit.md) tests, and for one of the test you want to use a modified value for one of the fields in it. Before 1.0 you'd have to duplicate the config binding into a new key and apply the modifying function to it:
+
+```scala mdoc:reset:invisible
+import scala.Predef.{identity => modifyingFunction, _}
+
+final case class Config(a: Int, b: Int, z: Int)
+```
+
+```scala mdoc:to-string
+import distage.{Id, ModuleDef}
+import distage.config.ConfigModuleDef
+import izumi.distage.testkit.TestConfig
+import izumi.distage.testkit.scalatest.SpecIdentity
+
+class HACK_OVERRIDE0_MyTest extends SpecIdentity {
+  override def config: TestConfig = super.config.copy(
+    moduleOverrides = new ConfigModuleDef {
+      makeConfig[Config]("config.myconfig").named("duplicate")
+      make[Config].from {
+        (thatConfig: Config @Id("duplicate")) =>
+          modifyingFunction(thatConfig)
+      }
+    }
+  )
+}
+```
+
+Now instead of overriding the entire binding, we may use a mutator:
+
+```scala mdoc:override:to-string
+class HACK_OVERRIDE1_MyTest extends SpecIdentity {
+  override def config: TestConfig = super.config.copy(
+    moduleOverrides = new ModuleDef {
+      modify[Config](modifyingFunction(_))
+    }
+  )
+}
+```
+
+Mutators are subject to configuration using @ref[Activation Axis](#activation-axis) and will be applied conditionally, if tagged:
+
+```scala mdoc:to-string
+import distage.{Activation, Injector, Mode}
+
+def axisIncrement = new ModuleDef {
+  make[Int].fromValue(1)
+  modify[Int](_ + 10).tagged(Mode.Test)
+  modify[Int](_ + 1).tagged(Mode.Prod)
+}
+
+Injector().produceRun(axisIncrement, Activation(Mode -> Mode.Test))((currentInt: Int) => currentInt): Int
+
+Injector().produceRun(axisIncrement, Activation(Mode -> Mode.Prod))((currentInt: Int) => currentInt): Int
+```
+
 ## Effect Bindings
 
 Sometimes we want to effectfully create a component, but the resulting component or data does not need to be deallocated.
@@ -741,7 +849,7 @@ In these cases we can use `.fromEffect` to create a value using an effectful con
 Example with a `Ref`-based Tagless Final `KVStore`:
 
 ```scala mdoc:reset:to-string
-import distage.{ModuleDef, Injector}
+import distage.{Injector, ModuleDef}
 import izumi.functional.bio.{Error2, Primitives2, F}
 import zio.{Task, IO}
 
@@ -848,7 +956,7 @@ def module2 = new ModuleDef {
 Another example:
 
 ```scala mdoc:reset:to-string
-import distage.{ModuleDef, Injector}
+import distage.{Injector, ModuleDef}
 import zio.console.{putStrLn, Console}
 import zio.{UIO, URIO, Ref, Task, Has}
 
@@ -1161,7 +1269,7 @@ class ActorFactoryImpl(sessionStorage: SessionStorage) extends ActorFactory {
 `@With` annotation can be used to specify the implementation class, to avoid leaking the implementation type in factory method result:
 
 ```scala mdoc:reset:to-string
-import distage.{ModuleDef, Injector, With}
+import distage.{Injector, ModuleDef, With}
 
 trait Actor {
   def receive(msg: Any): Unit
diff --git a/doc/microsite/src/main/tut/distage/distage-framework-docker.md b/doc/microsite/src/main/tut/distage/distage-framework-docker.md
index 356cd0239..34571a0ba 100644
--- a/doc/microsite/src/main/tut/distage/distage-framework-docker.md
+++ b/doc/microsite/src/main/tut/distage/distage-framework-docker.md
@@ -238,15 +238,15 @@ health check.
 
 ### Usage in Integration Tests
 
-A common use case is using Docker containers to provide service implementations for integration test, such as using a PostgreSQL container for verifying an application that uses a PostgreSQL database.
-`distage` container resources are easy to integrate as providers.
+A common use case is using Docker containers to provide service implementations for integration test,
+such as using a PostgreSQL container for verifying an application that uses a PostgreSQL database.
 
 Consider the example application below. This application is written to depend on a
 [doobie](https://tpolecat.github.io/doobie/) `Transactor`, which is constructed from a
 `PostgresServerConfig`.
 
 ```scala mdoc:silent
-import cats.effect.ContextShift
+import cats.effect.{Async, ContextShift}
 import doobie.Transactor
 import doobie.syntax.connectionio._
 import doobie.syntax.string._
@@ -277,24 +277,22 @@ final case class PostgresServerConfig(
 
 object TransactorFromConfigModule extends ModuleDef {
   make[Transactor[IO]].from {
-    (config: PostgresServerConfig, contextShift: ContextShift[IO]) =>
-      implicit val CS = contextShift
+    (config: PostgresServerConfig, async: Async[IO], contextShift: ContextShift[IO]) =>
 
       Transactor.fromDriverManager[IO](
         driver = "org.postgresql.Driver",
         url    = s"jdbc:postgresql://${config.host}:${config.port}/${config.database}",
         user   = config.username,
         pass   = config.password,
-      )
+      )(async, contextShift)
   }
 }
 ```
 
-Note that the above code is agnostic of environment. Provided a `PostgresServerConfig`, the
-`Transactor` needed by `PostgresExampleApp` can be constructed.
+Note that the above code is agnostic of environment.
+Provided a `PostgresServerConfig`, the `Transactor` needed by `PostgresExampleApp` can be constructed.
 
-An integration test would use a module that provides the `PostgresServerConfig` from a
-`PostgresDocker.Container`:
+An integration test would use a module that provides the `PostgresServerConfig` from a `PostgresDocker.Container`:
 
 ```scala mdoc:to-string
 object PostgresUsingDockerModule extends ModuleDef {
diff --git a/doc/microsite/src/main/tut/distage/distage-framework.md b/doc/microsite/src/main/tut/distage/distage-framework.md
index 82c000b94..8d2d20863 100644
--- a/doc/microsite/src/main/tut/distage/distage-framework.md
+++ b/doc/microsite/src/main/tut/distage/distage-framework.md
@@ -131,7 +131,7 @@ import com.example.myapp.MainLauncher
 object WiringCheck extends PlanCheck.Main(MainLauncher)
 ```
 
-This object will emit compile-time errors for any issues or omissions in your `ModuleDefs`, it will be recompiled as
+This object will emit compile-time errors for any issues or omissions in your `ModuleDefs`. It will recompile itself as
 necessary to provide feedback during development.
 
 By default, all possible roles and activations are checked efficiently.
@@ -325,18 +325,19 @@ values in later configs:
 ## Plugins
 
 `distage-extension-plugins` module adds classpath discovery for modules that inherit a marker trait `PluginBase`.
-Plugins enable extreme late-binding; e.g. they allow a program to extend itself at launch time with new `Plugin` classes
-on the classpath. Plugins are compatible with @ref[compile-time checks](distage-framework.md#compile-time-checks) as
-long as they're defined in a separate module.
 
-To use plugins, first add the `distage-extension-plugins` library:
+Plugins reduce friction in adding new components, a programmer needs only to define new plugins and does not also have to stop to add a new plugin to a central wiring point. Plugins also enable extreme late-binding: they allow a program to extend itself at launch time with new `Plugin` classes on the classpath.
+
+Plugins are compatible with @ref[compile-time checks](distage-framework.md#compile-time-checks) as long as they're defined in a separate module.
+
+To use plugins, add the `distage-extension-plugins` library:
 
 @@dependency[sbt] { group="io.7mind.izumi"
 artifact="distage-extension-plugins_2.13"
 version="$izumi.version$"
 }
 
-Create a module extending the `PluginDef` trait instead of `ModuleDef`:
+To declare a plugin, create a top-level object extending the `PluginDef` trait instead of `ModuleDef`:
 
 ```scala mdoc:reset:invisible
 import com.example.petstore._
@@ -355,7 +356,7 @@ object PetStorePlugin extends PluginDef {
 }
 ```
 
-Collect all the `PluginDef` classes and objects in a package:
+Use `PluginLoader` to find all the `PluginDef` objects in a specific package or its subpackages:
 
 ```scala mdoc:to-string
 val pluginConfig = PluginConfig.cached(
@@ -366,10 +367,10 @@ val pluginConfig = PluginConfig.cached(
 val appModules = PluginLoader().load(pluginConfig)
 ```
 
-Wire the collected modules as usual:
+You may then pass plugins to `Injector` as ordinary modules:
 
 ```scala mdoc:to-string
-// combine all modules into one
+// combine all plugins into one
 
 val appModule = appModules.result.merge
 


Test patch:
diff --git a/distage/distage-core/src/test/scala/izumi/distage/impl/BootstrapTest.scala b/distage/distage-core/src/test/scala/izumi/distage/impl/BootstrapTest.scala
index eed1542b0..67fb993c9 100644
--- a/distage/distage-core/src/test/scala/izumi/distage/impl/BootstrapTest.scala
+++ b/distage/distage-core/src/test/scala/izumi/distage/impl/BootstrapTest.scala
@@ -27,7 +27,7 @@ class BootstrapTest extends AnyWordSpec {
         context.get[PlanAnalyzer]("another.one")
       }
 
-      intercept[AssertionError] {
+      intercept[IllegalArgumentException] {
         context.lookupLocal[Long](DIKey.get[PlanAnalyzer])
       }
 
diff --git a/distage/distage-core/src/test/scala/izumi/distage/injector/AxisTest.scala b/distage/distage-core/src/test/scala/izumi/distage/injector/AxisTest.scala
index 3f883ca8e..929b606b2 100644
--- a/distage/distage-core/src/test/scala/izumi/distage/injector/AxisTest.scala
+++ b/distage/distage-core/src/test/scala/izumi/distage/injector/AxisTest.scala
@@ -5,7 +5,7 @@ import izumi.distage.fixtures.BasicCases._
 import izumi.distage.fixtures.SetCases.SetCase1
 import izumi.distage.model.PlannerInput
 import izumi.distage.model.definition.StandardAxis.{Mode, Repo}
-import izumi.distage.model.definition.{Activation, BootstrapModuleDef, ModuleDef}
+import izumi.distage.model.definition.{Activation, Axis, BootstrapModuleDef, ModuleDef}
 import izumi.distage.model.exceptions.{BadSetAxis, ConflictResolutionException}
 import izumi.distage.model.plan.Roots
 import izumi.fundamentals.platform.functional.Identity
@@ -230,4 +230,62 @@ class AxisTest extends AnyWordSpec with MkInjector {
     assert(definition.bindings.size == 3)
     assert(instance.x ne null)
   }
+
+  "choose according to specificity rules" in {
+    object Style extends Axis {
+      case object AllCaps extends AxisChoiceDef
+      case object Normal extends AxisChoiceDef
+    }
+
+    sealed trait Color
+    case object RED extends Color
+    case object Blue extends Color
+    case object Green extends Color
+
+    def DefaultsModule = new ModuleDef {
+      make[Color].from(Green)
+      make[Color].tagged(Style.AllCaps).from(RED)
+    }
+
+    assert(
+      Injector().produceRun(DefaultsModule, Activation(Style -> Style.AllCaps))(identity(_: Color))
+      == RED
+    )
+
+    assert(
+      Injector().produceRun(DefaultsModule, Activation(Style -> Style.Normal))(identity(_: Color))
+      == Green
+    )
+
+    assertThrows[ConflictResolutionException](Injector().produceRun(DefaultsModule, Activation.empty)(println(_: Color)))
+
+    def SpecificityModule = new ModuleDef {
+      make[Color].tagged(Mode.Test).from(Blue)
+      make[Color].tagged(Mode.Prod).from(Green)
+      make[Color].tagged(Mode.Prod, Style.AllCaps).from(RED)
+    }
+
+    assert(
+      Injector().produceRun(SpecificityModule, Activation(Mode -> Mode.Prod, Style -> Style.AllCaps))(identity(_: Color))
+      == RED
+    )
+
+    assert(
+      Injector().produceRun(SpecificityModule, Activation(Mode -> Mode.Test, Style -> Style.AllCaps))(identity(_: Color))
+      == Blue
+    )
+
+    assert(
+      Injector().produceRun(SpecificityModule, Activation(Mode -> Mode.Prod, Style -> Style.Normal))(identity(_: Color))
+      == Green
+    )
+
+    assert(
+      Injector().produceRun(SpecificityModule, Activation(Mode -> Mode.Test))(identity(_: Color))
+      == Blue
+    )
+
+    assertThrows[ConflictResolutionException](Injector().produceRun(SpecificityModule, Activation(Style -> Style.Normal))(identity(_: Color)))
+  }
+
 }
diff --git a/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/DebugProperties.scala b/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/DebugProperties.scala
index 8d9e2d8a8..4211f97d0 100644
--- a/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/DebugProperties.scala
+++ b/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/DebugProperties.scala
@@ -8,9 +8,9 @@ import izumi.fundamentals.platform.properties
   * @see [[DebugProperties]]
   */
 object DebugProperties extends properties.DebugProperties {
-  /** Print debug messages, `false` by default */
+  /** Print debug messages, default: `false` */
   final val `izumi.distage.testkit.debug` = BoolProperty("izumi.distage.testkit.debug")
 
-  /** Cache created [[izumi.distage.testkit.services.dstest.TestEnvironment]], `true` by default */
+  /** Cache [[izumi.distage.testkit.services.dstest.TestEnvironment]], default: `true` */
   final val `izumi.distage.testkit.environment.cache` = BoolProperty("izumi.distage.testkit.environment.cache")
 }
diff --git a/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/TestConfig.scala b/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/TestConfig.scala
index eddc06b08..789c2dbe3 100644
--- a/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/TestConfig.scala
+++ b/distage/distage-testkit-core/src/main/scala/izumi/distage/testkit/TestConfig.scala
@@ -161,7 +161,7 @@ object TestConfig {
       AxisDIKeys(Map(Set.empty -> set.toSet[DIKey]))
 
     @inline implicit def fromSetMap(map: Iterable[(Set[_ <: AxisChoice], Set[_ <: DIKey])]): AxisDIKeys =
-      AxisDIKeys(map.toMap[Set[_ <: AxisChoice], Set[_ <: DIKey]].asInstanceOf[Map[Set[AxisChoice], Set[DIKey]]])
+      AxisDIKeys(map.iterator.map { case (k, v) => k.toSet[AxisChoice] -> v.toSet[DIKey] }.toMap)
 
     @inline implicit def fromSingleMap(map: Iterable[(AxisChoice, DIKey)]): AxisDIKeys =
       AxisDIKeys(map.iterator.map { case (k, v) => Set(k) -> Set(v) }.toMap)
diff --git a/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/memoized/DistageMemoizationEnvsTest.scala b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/memoized/DistageMemoizationEnvsTest.scala
index e2f3054b2..d8484358f 100644
--- a/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/memoized/DistageMemoizationEnvsTest.scala
+++ b/distage/distage-testkit-scalatest/src/test/scala/izumi/distage/testkit/distagesuite/memoized/DistageMemoizationEnvsTest.scala
@@ -43,8 +43,8 @@ abstract class DistageMemoizationEnvsTest extends Spec3[ZIO] with AssertZIO {
     super.config
       .copy(
         memoizationRoots = Map(
-          1 -> Set(DIKey.get[MemoizedInstance], DIKey.get[MemoizedLevel1]),
-          2 -> Set(DIKey.get[MemoizedLevel2]),
+          1 -> Set(DIKey[MemoizedInstance], DIKey.get[MemoizedLevel1]),
+          2 -> Set(DIKey[MemoizedLevel2]),
         ),
         pluginConfig = super.config.pluginConfig.enablePackage("izumi.distage.testkit.distagesuite") ++ new PluginDef {
           make[MemoizedInstance].from {
@@ -116,7 +116,8 @@ class SameLevel_1_WithoutLastMemoizationLevel extends DistageMemoizationEnvsTest
 class SameLevel_1_2_WithAdditionalLevel3 extends DistageMemoizationEnvsTest {
   override protected def config: TestConfig = {
     super.config.copy(
-      memoizationRoots = super.config.memoizationRoots ++ Map(10000 -> Set(DIKey.get[MemoizedLevel3])),
+      memoizationRoots = super.config.memoizationRoots ++
+        Set(DIKey[MemoizedLevel3]),
       activation = distage.Activation(Repo -> Repo.Prod),
     )
   }
diff --git a/doc/microsite/src/main/tut/distage/distage-testkit.md b/doc/microsite/src/main/tut/distage/distage-testkit.md
index 395966436..2104de49b 100644
--- a/doc/microsite/src/main/tut/distage/distage-testkit.md
+++ b/doc/microsite/src/main/tut/distage/distage-testkit.md
@@ -251,8 +251,7 @@ __runTest__(new ScoreSimpleTest with MdocTest { def name = "ScoreSimpleTest" })
 
 All of the base classes support test cases that are effects with assertions. Functions returning effects will have
 arguments provided from the object graph. These test cases are supported by
-@scaladoc[`in` from DSWordSpecStringWrapper](izumi.distage.testkit.services.scalatest.dstest.DistageAbstractScalatestSpec$$DSWordSpecStringWrapper)
-.
+@scaladoc[`in` from DSWordSpecStringWrapper](izumi.distage.testkit.services.scalatest.dstest.DistageAbstractScalatestSpec$$DSWordSpecStringWrapper).
 
 The different effect types fix the `F[_]` argument for this syntax:
 
@@ -261,8 +260,8 @@ The different effect types fix the `F[_]` argument for this syntax:
 - `Spec3`: `F[Any, Throwable, _]`
 
 With our demonstration application we'll use this to verify the `Score.echoConfig` method. The `Config` required is from
-the `distage` object graph defined in `moduleOverrides`. By using a function with a `Config` argument in `in`, the
-required argument will be injected by `distage-testkit`.
+the `distage` object graph defined in `moduleOverrides`.
+By using a function with a `Config` argument in `in`, the required argument will be injected by `distage-testkit`.
 
 ```scala mdoc:fakepackage:to-string
 "fakepackage app": Unit
@@ -572,62 +571,117 @@ user bindings if different behavior or support for custom effect types is requir
 
 The execution of tests is grouped into:
 
-- [memoization environments](#resource-reuse-memoization).
+- [memoization levels](#resource-reuse-memoization).
 - test suite
 - test cases
 
-The default is to run all of these in parallel. The @scaladoc[`TestConfig`](izumi.distage.testkit.TestConfig) has
-options to change the behavior for each of these groups. The default is
-@scaladoc[`ParallelLevel.Unlimited`](izumi.distage.testkit.TestConfig$$ParallelLevel$$Unlimited$) which does not
-constrain the number of parallel tests. `ParallelLevel.Fixed(n: Int)` limits the execution to at most `n` test cases.
+The default is to run all of these in parallel.
+
+The @scaladoc[`TestConfig`](izumi.distage.testkit.TestConfig) has options to change the behavior for each of these groups.
+The default is @scaladoc[`ParallelLevel.Unlimited`](izumi.distage.testkit.TestConfig$$ParallelLevel$$Unlimited$) which does not constrain the number of parallel tests.
+`ParallelLevel.Fixed(n: Int)` limits the execution to at most `n` test cases.
 While `ParallelLevel.Sequential` executes the test cases one at a time.
 
-- `parallelEnvs` - Parallel level for distinct memoization environments.
-- `parallelSuites` - Parallel level for test suites.
-- `parallelTests` - Parallel level for test cases.
+- `parallelEnvs` - Parallelism level for distinct memoization environments.
+- `parallelSuites` - Parallelism level for test suites.
+- `parallelTests` - Parallelism level for test cases.
 
 If a group is configured to execute sequentially this will execute after the parallel tests.
 
-For example, the `BonusServiceTest` above consists of two test cases and one test suite. Both test cases will be
-executed in parallel using the async behavior of the effect type. The
-`NotUsingMemoTest` and `UsingMemoTest` below demonstrate executing the test cases sequentially for each test suite.
-However, the test suites will execute in parallel as they use the same memoization environment.
+For example, the `BonusServiceTest` above consists of two test cases and one test suite.
+Both test cases will be executed in parallel using the capabilities of the effect type.
+
+The `NotUsingMemoTest` and `UsingMemoTest` test suites below demonstrate executing the test cases sequentially for each test suite.
+However, the two suites themselves will execute in parallel as they are in the same memoization environment.
 
 ### Resource Reuse - Memoization
 
-Injected values are summoned from the object graph for each test. Without using memoization, the components will be
-acquired and released for each test. This may be unwanted. For example, a single PostgreSQL container may be required
-for a sequence of test cases. In which case the PostgreSQL component should be memoized for the duration of those test
-cases. Configuring memoization enables changing whether instantiating a component results in a fresh component or reuses
-an existing, memoized, instance.
+For each test, a new object graph with injected values is created.
+Without using memoization, all components will be created, acquired and released anew for each test case.
+This may be unwanted.
+For example, you may wish to reuse a single PostgreSQL container for a sequence of test cases.
+In which case the PostgreSQL component should be memoized for the duration of those test cases.
 
-Further, the memoization environment determines how the test cases are scheduled for execution. See
-[the execution order section for further information.](#execution-order)
+Configuring memoization determines whether summoning a component results in a fresh component or reuses an existing, memoized, instance.
+
+Further, the memoization environment determines how the test cases are scheduled for execution.
+See [the execution order section for further information.](#execution-order)
 
 #### Memoization Environments
 
-The memoization applied when an component is summoned is defined by the *memoization environment*. Each distinct
+Memoization strategy applied when a component is summoned is defined by the *memoization environment*. Each distinct
 memoization environment uses a distinct memoization store. When a component instance is memoized that instance is shared
-across all tests that use the same memoization environment. The
+across all tests that use the same memoization environment.
 @scaladoc[`TestConfig`](izumi.distage.testkit.TestConfig) contains the options that define the memoization environment:
 
 1. `memoizationRoots` - These components will be acquired once and shared across all tests that used the same
    memoization environment.
-2. `activation` - Chosen activation axis. Changes in Activation that alter implementations of components in
-   memoizationRoots OR their dependencies will cause the test to execute in a new memoization environment.
+2. `activation` - Chosen activation axis. Differences in Activation that affect the memoized part of the graph (that
+   alter implementations of components in `memoizationRoots` *or* their transitive dependencies) will
+   cause the test to execute in a new memoization environment.
 3. `pluginConfig` - Defines the plugins to source module definitions.
-4. `forcedRoots` - Components treated as a dependency of every test. A component added to this and
-   `memoizationRoots` will be acquired at the start of all tests and released at the end of all tests.
+4. `forcedRoots` - Components treated as a dependency of every test. A component added both to `forcedRoots` and
+   `memoizationRoots` will be acquired at the start of all tests and released at the end of all tests in the memoization
+   environment.
 5. `moduleOverrides` - Overrides the modules from `pluginConfig`.
 
-The module environment depends on instantiation of the `memoizationRoots` and `forcedRoots` components. Changes to the
+The module environment depends on instantiation of the `memoizationRoots` components. Changes to the
 config that alter implementations of these components *or* their dependencies will change the memoization environment
-used. This includes, but is not limited to, changes to
-`activation`, `pluginConfig` and `moduleOverrides`.
+used. This includes, but is not limited to, changes to `activation`, `pluginConfig` and `moduleOverrides`.
+
+When the `TestConfig` option @scaladoc[`debugOutput`](izumi.distage.testkit.TestConfig) is true the debug output will include memoization environment diagnostics.
+This can also be controlled using the [`izumi.distage.testkit.debug`](izumi.distage.testkit.DebugProperties$) system property.
+
+#### Memoization Levels
+
+Since version `1.0` the above memoization environments scheme has been generalized to support unlimited nesting of memoization environments.
+
+Nested memoization levels allow more and better sharing of heavy components among test suites. With previous strategy of single-level memoization environments, any change in `TestConfig` that forces a new memoization environment would cause every single memoized component to be recreated in a new environment.
+
+With new strategy, the memoization environment may be manually partitioned into levels and if a change in `TestConfig` does not cause a divergence at one of the levels, the nested levels may then fully reuse the object sub-graph of all parent levels that do not diverge.
 
-When the `TestConfig` option @scaladoc[`debugOutput`](izumi.distage.testkit.TestConfig)
-is true the debug output will include memoization environment diagnostics. This can also be controlled using
-the `izumi.distage.testkit.debug` system property.
+For clarity, the memoization tree structure is printed before test runs. For example, a memoization tree of a project with the following test suites:
+
+```scala mdoc:invisible
+import distage.DIKey
+import izumi.distage.testkit.TestConfig
+
+class MemoizedInstance
+class MemoizedLevel1
+class MemoizedLevel2
+class MemoizedLevel3
+```
+
+```scala mdoc:to-string
+class SameLevel_1_WithActivationsOverride extends Spec3[ZIO] {
+  override protected def config: TestConfig = {
+    super.config.copy(
+        memoizationRoots = Map(
+          1 -> Set(DIKey[MemoizedInstance], DIKey[MemoizedLevel1]),
+          2 -> Set(DIKey[MemoizedLevel2]),
+        ),
+    )
+  }
+}
+
+class SameLevel_1_2_WithAdditionalLevel3 extends SameLevel_1_WithActivationsOverride {
+  override protected def config: TestConfig = {
+    super.config.copy(
+      memoizationRoots =
+        super.config.memoizationRoots ++
+        Set(DIKey[MemoizedLevel3]),
+    )
+  }
+}
+```
+
+May be visualized as follows:
+
+![Memoization Tree Log during tests](media/memoization-tree.png)
+
+Technical note: divergence of memoization levels is calculated based on equality of @ref[recipes of future object graphs](debugging.md#pretty-printing-plans), not equality of allocated/existing object graphs.
+
+Note: [original github ticket](https://github.com/7mind/izumi/issues/1188)
 
 #### Examples
 
@@ -812,6 +866,8 @@ If memoized, they will be acquired and released once, before all and after all t
 
 They provide an alternative to ScalaTest's native `beforeEach/beforeAll` that can use functional effects instead of mutability (However, `All` here includes the entire memoization environment, not the enclosing test suite)
 
+Forced roots may be configured per-activation / combination of activations, e.g. you may force postgres table setup to happen only in test environments with `Repo -> Repo.Prod` activation.
+
 ### Test Selection
 
 #### Using `IntegrationCheck`