feat(deploy): reject Sql Query tags naming undeclared queries; warn on Writable/writable disagreement (lmxopcua#781)
v2-ci / unit-tests (push) Has been cancelled

Item 5: DraftValidator now emits SqlQueryNotDeclared (always blocking) for a Query tag whose
query is absent from the merged driver config's queries map, via the driver's own tag parse.
Item 3: StartDeployment appends a non-blocking warning to the accepted message when the Tag
entity's AccessLevel and the Sql blob's writable flag disagree.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
Joseph Doherty
2026-11-09 18:08:39 -05:00
parent 7d989fb529
commit 241318e7e5
6 changed files with 239 additions and 0 deletions
@@ -465,6 +465,12 @@ public sealed class AdminOperationsActor : ReceiveActor
_log.Warning("StartDeployment: TagConfig warning (Warn mode, non-blocking): {Warning}", x.Text);
}
// lmxopcua#781 item 3: always non-blocking (independent of TagConfigValidationMode) — the Tag
// entity's Writable flag and the Sql blob's "writable" disagree.
var writableWarnings = SqlTagDeployChecks.WritableMismatchWarnings(draft);
foreach (var w in writableWarnings)
_log.Warning("StartDeployment: Sql writable mismatch (non-blocking): {Warning}", w);
if (errors.Count > 0)
{
var summary = string.Join("; ", errors.Select(e => $"[{e.Code}] {e.Message}"));
@@ -547,6 +553,7 @@ public sealed class AdminOperationsActor : ReceiveActor
var messageParts = new List<string>();
if (advisory is not null) messageParts.Add(advisory);
messageParts.AddRange(tagConfigWarnings.Select(x => x.Text));
messageParts.AddRange(writableWarnings);
var acceptedMessage = messageParts.Count > 0 ? string.Join("; ", messageParts) : null;
return new ComposeOutcome(