A served value or a contribution's may be the operator's: ${setting:…} fills there too, refused by name when unset

Issue 173's rule needs it: a mail provider serves its domain and an identity provider its issuer,
and neither is the definition's to state. Declared as ${setting:<key>}, filled from the layers after
the overrides; a key so asked for is not stray.
This commit is contained in:
2026-09-30 22:34:43 +02:00
parent 05d977666a
commit 29be985c23
3 changed files with 73 additions and 5 deletions
+27 -1
View File
@@ -99,6 +99,11 @@ func Settle(base map[string]any, layers []Layer) (map[string]any, error) {
// overridden lays settings over a map whose keys are its contract: a contribution, a served fact.
// Only the keys the map already declares are touched; the rest of a layer is somebody else's
// business (a file's, another destination's) and is left to reach it there.
//
// A declared value may itself be the operator's, `${setting:<key>}` (ADR 0155): a mail provider
// serves its domain, an identity provider its issuer, and neither is the definition's to state.
// Filled from the layers after the overrides, and refused by name when nothing sets it — a literal
// placeholder handed to a consumer is a service configured against a string nobody meant.
func overridden(base map[string]any, layers []Layer, what string) (map[string]any, error) {
kept := make([]Layer, 0, len(layers))
for _, layer := range layers {
@@ -110,7 +115,28 @@ func overridden(base map[string]any, layers []Layer, what string) (map[string]an
}
kept = append(kept, Layer{From: layer.From, Values: values})
}
return settle(base, kept, nil, what)
merged, err := settle(base, kept, nil, what)
if err != nil {
return nil, err
}
for key, value := range merged {
s, ok := value.(string)
if !ok {
continue
}
for _, asked := range settingsUsed(s) {
v, set := settingValue(layers, asked)
if !set {
return nil, fmt.Errorf(
"%s says ${setting:%s} for %q, and nothing sets %q — an operator's value is the "+
"assignment's, never the definition's (novox/hq ADR 0112)%s",
what, asked, key, asked, orNoSettings(layers))
}
s = strings.ReplaceAll(s, "${setting:"+asked+"}", plainly(v))
}
merged[key] = s
}
return merged, nil
}
// settle lays the layers over a module's own values, in order.