Problem
localFlags { } and remoteFlags { } are declared in separate DSL containers and generate
separate objects (GeneratedLocalFlags* / GeneratedRemoteFlags*), which strongly implies the
two are isolated. They are not isolated at runtime:
ConfigValues keys its in-memory snapshot by ConfigParam.key (the raw string). A local flag
and a remote flag declared with the same name/key map to the same snapshot slot
(documented last-write-wins), so they overwrite each other.
- More fundamentally,
ConfigValues.getValue(param) resolves any key through the same
provider chain (local → remote → default) regardless of which container declared it. A flag
authored in localFlags { } will therefore pick up a remote value if the remote provider
serves that key, and a remoteFlags { } flag will pick up a local override. The
local/remote split is purely a codegen/organizational concern today, with no runtime effect on
resolution.
Expectation
Because they live in separate containers and separate generated classes, a local flag and a
remote flag should be runtime-isolated:
- a
localFlags flag resolves only from the local provider (+ its default), never from remote;
- a
remoteFlags flag resolves only from the remote provider (+ its default), never from a local
override of a same-named local flag;
- a local flag and a remote flag with the same name do not collide in the snapshot.
Use case that hits this
The "client-readiness AND server-rollout" gating pattern: a feature is enabled only when both
the locally bundled flag (the client build has shipped/enabled it) and the server flag are
true — isEnabled(localFlag) && isEnabled(remoteFlag). This is a common hybrid (an analogue of
a bundled local_feature_toggle.json AND'd with remote config).
Today this is impossible to express cleanly: a local twin must be given an artificial distinct
key (e.g. a _LOCAL suffix) so the server never serves it and it doesn't collide with the
remote flag. That is a workaround, not a design.
Repro (1.2.0)
featured {
localFlags { boolean("FEATURE_X", default = true) } // client gate
remoteFlags { boolean("FEATURE_X", default = false) } // server rollout
}
// GeneratedLocalFlags.featureX and GeneratedRemoteFlags.featureX both have key "FEATURE_X"
// -> same snapshot slot; getValue(local) also reads the remote provider's "FEATURE_X".
Possible directions (maintainer's call)
- Namespace the snapshot key by container (e.g. prefix local keys internally), keeping the public
flag name unchanged.
- Route
localFlags strictly through the local provider and remoteFlags strictly through the
remote provider (carry the container on ConfigParam, resolve accordingly).
- Separate snapshots per container.
Version: 1.2.0
Problem
localFlags { }andremoteFlags { }are declared in separate DSL containers and generateseparate objects (
GeneratedLocalFlags*/GeneratedRemoteFlags*), which strongly implies thetwo are isolated. They are not isolated at runtime:
ConfigValueskeys its in-memory snapshot byConfigParam.key(the raw string). A local flagand a remote flag declared with the same name/key map to the same snapshot slot
(documented last-write-wins), so they overwrite each other.
ConfigValues.getValue(param)resolves any key through the sameprovider chain (local → remote → default) regardless of which container declared it. A flag
authored in
localFlags { }will therefore pick up a remote value if the remote providerserves that key, and a
remoteFlags { }flag will pick up a local override. Thelocal/remote split is purely a codegen/organizational concern today, with no runtime effect on
resolution.
Expectation
Because they live in separate containers and separate generated classes, a local flag and a
remote flag should be runtime-isolated:
localFlagsflag resolves only from the local provider (+ its default), never from remote;remoteFlagsflag resolves only from the remote provider (+ its default), never from a localoverride of a same-named local flag;
Use case that hits this
The "client-readiness AND server-rollout" gating pattern: a feature is enabled only when both
the locally bundled flag (the client build has shipped/enabled it) and the server flag are
true—isEnabled(localFlag) && isEnabled(remoteFlag). This is a common hybrid (an analogue ofa bundled
local_feature_toggle.jsonAND'd with remote config).Today this is impossible to express cleanly: a local twin must be given an artificial distinct
key (e.g. a
_LOCALsuffix) so the server never serves it and it doesn't collide with theremote flag. That is a workaround, not a design.
Repro (1.2.0)
featured { localFlags { boolean("FEATURE_X", default = true) } // client gate remoteFlags { boolean("FEATURE_X", default = false) } // server rollout } // GeneratedLocalFlags.featureX and GeneratedRemoteFlags.featureX both have key "FEATURE_X" // -> same snapshot slot; getValue(local) also reads the remote provider's "FEATURE_X".Possible directions (maintainer's call)
flag name unchanged.
localFlagsstrictly through the local provider andremoteFlagsstrictly through theremote provider (carry the container on
ConfigParam, resolve accordingly).Version: 1.2.0