Inside of foo’s setting body a dependency on the scoped key Test / bar is declared.
However, despite Test / bar being undefined in projX,
sbt is still able to resolve Test / bar to another scoped key,
resulting in foo initialized as 2.

sbt has a well-defined fallback search path called scope delegation.
This feature allows you to set a value once in a more general scope,
allowing multiple more-specific scopes to inherit the value.

Scope delegation rules

Here are the rules for scope delegation:

Rule 1: Scope axes have the following precedence: the subproject axis, the configuration axis, and then the task axis.

Rule 2: Given a scope, delegate scopes are searched by substituting the task axis in the following order:
the given task scoping, and then Zero, which is non-task scoped version of the scope.

Rule 3: Given a scope, delegate scopes are searched by substituting the configuration axis in the following order:
the given configuration, its parents, their parents and so on, and then Zero (same as unscoped configuration axis).

Rule 4: Given a scope, delegate scopes are searched by substituting the subproject axis in the following order:
the given subproject, ThisBuild, and then Zero.

Rule 5: A delegated scoped key and its dependent settings/tasks are evaluated without carrying the original context.

We will look at each rule in the rest of this page.

Rule 1: Scope axis precedence

Rule 1: Scope axes have the following precedence: the subproject axis, the configuration axis, and then the task axis.

In other words, given two scope candidates, if one has more specific value on the subproject axis,
it will always win regardless of the configuration or the task scoping.
Similarly, if subprojects are the same, one with more specific configuration value will always win regardless
of the task scoping. We will see more rules to define more specific.

Rule 2: The task axis delegation

Rule 2: Given a scope, delegate scopes are searched by substituting the task axis in the following order:
the given task scoping, and then Zero, which is non-task scoped version of the scope.

Here we have a concrete rule for how sbt will generate delegate scopes given a key.
Remember, we are trying to show the search path given an arbitrary (xxx / yyy).value.

Rule 3: The configuration axis search path

Rule 3: Given a scope, delegate scopes are searched by substituting the configuration axis in the following order:
the given configuration, its parents, their parents and so on, and then Zero (same as unscoped configuration axis).

The answer is abc-org.tempuri.
So based on Rule 4, the first search path is organization scoped to projB / Zero / Zero,
which is defined in projB as "org.tempuri".
This has higher precedence than the build-level setting ThisBuild / organization.

Note how “Provided by” shows that projD / Compile / console / scalacOptions
is provided by projD / Compile / scalacOptions.
Also under “Delegates”, all of the possible delegate candidates
listed in the order of precedence!

All the scopes with projD scoping on the subproject axis are listed first,
then ThisBuild, and Zero.

Within a subproject, scopes with Compile scoping on the configuration axis
are listed first, then falls back to Zero.

Finally, the task axis scoping lists the given task scoping console / and the one without.

.value lookup vs dynamic dispatch

Rule 5: A delegated scoped key and its dependent settings/tasks are evaluated without carrying the original context.

Note that scope delegation feels similar to class inheritance in an object-oriented language,
but there’s a difference. In an OO language like Scala if there’s a method named
drawShape on a trait Shape, its subclasses can override the behavior even when drawShape is used
by other methods in the Shape trait, which is called dynamic dispatch.

In sbt, however, scope delegation can delegate a scope to a more general scope,
like a project-level setting to a build-level settings,
but that build-level setting cannot refer to the project-level setting.

The answer is 2.12.2_0.1.0.
projD / version delegates to ThisBuild / version,
which depends on ThisBuild / scalaVersion.
Because of this reason, build level setting should be limited mostly to simple value assignments.