diff --git a/ZB.MOM.WW.Secrets/CHANGELOG.md b/ZB.MOM.WW.Secrets/CHANGELOG.md
index 3522f3a..00cbe84 100644
--- a/ZB.MOM.WW.Secrets/CHANGELOG.md
+++ b/ZB.MOM.WW.Secrets/CHANGELOG.md
@@ -5,6 +5,32 @@ follows [Keep a Changelog](https://keepachangelog.com/); versions are shared acr
package in the family (see `Directory.Build.props`) even when a given release only touches
one of them.
+## 0.6.2 — 2026-08-11
+
+### Changed
+
+- **The under-content-root rejection message now teaches the container case.** Suggested by the
+ OtOpcUa session, which is the only real containerized deployment in this family and the one that
+ hit the false positive. A store on a volume mounted inside the content root is durable across
+ container replacement but is rejected anyway, because the rule cannot tell a mount from an
+ ordinary directory. The message now says so, gives the fix (mount the same volume at a second
+ path outside the content root; the file sits at the volume root either way, so an existing store
+ carries over), and forecloses the wrong fix by stating that no override exists.
+
+ A false positive's entire cost is the confusion in the moment, so a boot error that names its own
+ fix costs nothing on the true-positive path and converts the false positive into a two-minute
+ edit. **Message only — no behaviour change.** Existing 0.6.1 consumers can take this whenever they
+ next touch the pin; it does not warrant a re-pin round on its own.
+
+ Their argument for keeping the rule strict is recorded because it is better than the trade-off I
+ had written down: the workaround is an improvement rather than a concession (it separates the
+ application from its durable state at the filesystem level — the exact distinction the incident
+ blurred); an `AllowStoreUnderContentRoot` flag has the lifecycle of a knob set once to clear a
+ symptom and never revisited, surviving the redeploy that changes the layout it was reasoned
+ against; and mount detection would make the verdict depend on runtime platform state, so the same
+ path would pass on one host and fail on another — strictly worse than a stable false positive
+ with a cheap documented fix.
+
## 0.6.1 — 2026-08-11
### Fixed
diff --git a/ZB.MOM.WW.Secrets/Directory.Build.props b/ZB.MOM.WW.Secrets/Directory.Build.props
index 67d33d2..de1e817 100644
--- a/ZB.MOM.WW.Secrets/Directory.Build.props
+++ b/ZB.MOM.WW.Secrets/Directory.Build.props
@@ -5,7 +5,7 @@
enable
enable
latest
- 0.6.1
+ 0.6.2
true
README.md
diff --git a/ZB.MOM.WW.Secrets/src/ZB.MOM.WW.Secrets/Configuration/SecretsOptionsValidator.cs b/ZB.MOM.WW.Secrets/src/ZB.MOM.WW.Secrets/Configuration/SecretsOptionsValidator.cs
index 2027495..2b37da4 100644
--- a/ZB.MOM.WW.Secrets/src/ZB.MOM.WW.Secrets/Configuration/SecretsOptionsValidator.cs
+++ b/ZB.MOM.WW.Secrets/src/ZB.MOM.WW.Secrets/Configuration/SecretsOptionsValidator.cs
@@ -80,7 +80,12 @@ public sealed class SecretsOptionsValidator : OptionsValidatorBase f.Contains("content root", StringComparison.Ordinal));
}
+ [Fact]
+ public void The_rejection_message_teaches_the_container_case()
+ {
+ // A false positive's entire cost is the confusion in the moment, and the known one is a
+ // store on a volume mounted inside the content root — durable in a container, rejected
+ // anyway, because the rule cannot tell a mount from an ordinary directory. A boot error
+ // that names its own fix costs nothing on the true-positive path and turns that false
+ // positive into a two-minute edit. Asserted rather than left to the doc comment, because
+ // the message is the only part of this a stuck operator will actually read.
+ var env = new StubHostEnvironment { ContentRootPath = Root("app") };
+ var validator = new SecretsOptionsValidator(env);
+
+ ValidateOptionsResult result =
+ validator.Validate(name: null, OptionsWith(Root("app", "data", "secrets.db")));
+
+ Assert.True(result.Failed);
+ string message = Assert.Single(result.Failures!);
+ Assert.Contains("mounted volume", message, StringComparison.OrdinalIgnoreCase);
+ Assert.Contains("OUTSIDE the content root", message, StringComparison.Ordinal);
+ // And that it forecloses the wrong fix, rather than leaving the reader to hunt for a flag.
+ Assert.Contains("not look for an override", message, StringComparison.OrdinalIgnoreCase);
+ }
+
[Fact]
public void Validator_accepts_an_absolute_path_outside_the_content_root()
{