Skip to content

config: resolve Effective by folding per-loader layer trees#64

Open
bigbes wants to merge 1 commit into
masterfrom
bigbes/gh-no-layered-effective-resolution
Open

config: resolve Effective by folding per-loader layer trees#64
bigbes wants to merge 1 commit into
masterfrom
bigbes/gh-no-layered-effective-resolution

Conversation

@bigbes
Copy link
Copy Markdown
Collaborator

@bigbes bigbes commented May 12, 2026

Config.Effective / MutableConfig.Effective / EffectiveAll resolved a leaf entity against the single merged tree, so scope depth always dominated loader priority — e.g. an env var routed to the global scope lost to a YAML value set per instance.

Builder.Build now keeps each top-level collector's contribution as its own layer tree, and effective resolution folds those layers in ascending-priority order: within a layer the deepest inheritance scope wins, across layers the higher-priority loader wins, and non-conflicting sub-keys from different loaders coexist. MergeAppend/MergeDeep keys compose across layers; a MultiCollector folds into a single layer.

MutableConfig mutations are mirrored into the layered view: Set/Merge/Update record values in a runtime overlay that outranks every loader, and Delete records a tombstone (and prunes the overlay) so the deleted contribution is suppressed from each layer's scope chain — re-setting a deleted path clears its tombstone. Snapshot deep-clones layers, overlay, and tombstones. Configs not produced by a Builder (slices, Walk snapshots, effective sub-configs) keep the original single-tree resolution.

Behavior change worth a minor-version bump; CHANGELOG updated. Covered by new tests in config_test.go, inheritance_test.go, layered_extra_test.go, and merge_tree_test.go; golangci-lint run ./... is clean.

@coveralls
Copy link
Copy Markdown

coveralls commented May 12, 2026

Coverage Status

coverage: 86.112% (+0.7%) from 85.423% — bigbes/gh-no-layered-effective-resolution into master

@bigbes bigbes requested review from Mockird31 and sssciel May 12, 2026 13:55
Effective() resolved against the single merged tree, so a value set at a
broad inheritance scope by a high-priority loader could lose to a value set
at a narrow scope by a low-priority loader. Build now keeps each top-level
collector's contribution as its own layer tree and Effective folds those
layers in ascending-priority order: within a layer the deepest scope wins,
across layers the higher-priority loader wins, and non-conflicting sub-keys
from different loaders coexist. MergeAppend/MergeDeep keys compose across
layers. A MultiCollector is folded into a single layer.

MutableConfig mutations are mirrored into the layered view: Set/Merge/Update
record values in a runtime overlay that outranks every loader, and Delete
records a tombstone (and prunes the overlay) so the deleted contribution is
suppressed from each layer's scope chain; re-setting a deleted path clears
its tombstone. Snapshot deep-clones the layers, overlay, and tombstones.

Configs not produced by a Builder (slices, Walk snapshots, effective
sub-configs) keep the original single-tree resolution.
@bigbes bigbes force-pushed the bigbes/gh-no-layered-effective-resolution branch from 2d1187f to d254f56 Compare May 12, 2026 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants