feat(m9/T28b): trigger analysis-kind selector (UI) + --trigger-kind (CLI)

Surfaces the T28a backend "analysisKind" discriminator in both authoring
surfaces: an Advisory|Strict <select> (id="alarm-trigger-kind" /
"script-trigger-kind") added to the Expression fragment of
AlarmTriggerEditor and ScriptTriggerEditor, and a --trigger-kind option
on template alarm/script add+update in the CLI.

Key/value contract: "analysisKind":"Strict" when strict; key omitted for
Advisory — exactly as ValidationService.IsStrictAnalysis reads it.
Selector only shown for Expression triggers; non-Expression triggers do
not emit the key even if IsStrictAnalysisKind is set on the model.

Both projects build 0 warnings; 101 CentralUI Trigger tests + 33 CLI
Template tests pass.
This commit is contained in:
Joseph Doherty
2026-06-18 10:44:57 -04:00
parent f618ac0322
commit dcc6f623e2
8 changed files with 684 additions and 2 deletions
@@ -136,6 +136,8 @@
_operator = _model.Operator;
_thresholdText = _model.Threshold?.ToString("R", CultureInfo.InvariantCulture);
(_intervalText, _intervalUnit) = SplitInterval(_model.IntervalMs);
// M9-T28b: sync the analysis-kind selector from the loaded model.
_analysisKindValue = _model.IsStrictAnalysisKind ? "Strict" : "Advisory";
}
/// <summary>Chooses the largest whole unit (min/sec/ms) that represents the period exactly.</summary>
@@ -272,6 +274,21 @@
<div class="form-text">
A boolean C# expression — e.g. <code>Attributes["Temperature"] &gt; 80</code>.
</div>
@* M9-T28b: analysis-kind selector — Advisory keeps the blank-expression finding
as a non-blocking warning; Strict escalates it to a deploy-blocking error. *@
<div class="mt-2">
<label for="script-trigger-kind" class="form-label small text-uppercase text-muted fw-semibold mb-1">
Analysis kind
</label>
<select id="script-trigger-kind"
class="form-select form-select-sm"
style="max-width: 280px;"
@bind="_analysisKindValue"
@bind:after="OnAnalysisKindChanged">
<option value="Advisory">Advisory — blank expression is a warning</option>
<option value="Strict">Strict — blank expression blocks deploy</option>
</select>
</div>
};
private async Task OnExpressionChanged(string value)
@@ -280,6 +297,15 @@
await Emit();
}
// M9-T28b: backing field + handler for the Advisory|Strict selector.
private string _analysisKindValue = "Advisory";
private async Task OnAnalysisKindChanged()
{
_model.IsStrictAnalysisKind = string.Equals(_analysisKindValue, "Strict", StringComparison.OrdinalIgnoreCase);
await Emit();
}
// ── Fire mode (Conditional + Expression) ───────────────────────────────
private RenderFragment RenderMode() => __builder =>