fix(alarms,acl): one condition event per Part 9 method call; gate Server-object model-change/audit events (#587, #588)

#587 — a client Acknowledge/Confirm/AddComment/Shelve/Enable/Disable emitted no condition event: the SDK's
ReportStateChange is gated on AutoReportStateChanges (never set), and the engine re-projection found the node
already in the new state and was suppressed by the delta-gate. WireMethodTransitionReporting now wraps each
method's SDK handler and, on Good, reports exactly once via ReportConditionEvent, under Lock so the routed
re-projection cannot race in ahead of the SDK applying the state. Independent of the SDK's imprecise
AreEventsMonitored bookkeeping. The shelve methods' state is applied by the wrapper (the SDK never applies
it). Native conditions' Confirmed/Shelving are method-owned (NativeAlarmProjector's constants would re-report
every ack and undo every shelve). MaxTimeShelved set to int.MaxValue ms at materialise — Create() left it 0
and every TimedShelve failed BadShelvingTimeOutOfRange. Wrong "SDK auto-fires (E2)" comments fixed.

#588 — with ACL on, GeneralModelChange and AuditWriteUpdate events no longer go to Server.ReportEvent
(ungated to every i=2253 item): ReportToServerObject delivers them per item, a model change narrowed to the
Affected nodes the session may Browse (dropped when none), an audit write only with Read on its source plus
the SDK audit/encryption guard. Flag off: unchanged.

#586 review: materialise raises only the new condition's monitored unit (was a full O(N) reconcile per
condition ⇒ O(N²) deploys under Lock).

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-08 00:54:08 -04:00
parent 390c8269a0
commit 167a48bffa
10 changed files with 1454 additions and 66 deletions
@@ -650,11 +650,12 @@ public sealed class AclEventSubscriptionEnforcementTests
/// including a condition rebuilt by a redeploy while the item was live.
/// </summary>
/// <remarks>
/// <c>ConditionState.EventsMonitored()</c> gates whether the SDK reports a condition's method-driven
/// state changes at all. The SDK base raised it by attaching the item to every root notifier; #586
/// <c>ConditionState.EventsMonitored()</c> gates whether the SDK reports a condition's method audit
/// events. The SDK base raised it by attaching the item to every root notifier; #586
/// attaches to nothing, so the first cut left it false on a server whose only subscribers are
/// Server-object items — the machine-consumer default. Asserted server-side because the consequence on
/// the wire depends on a separate, still-open reporting gap.
/// Server-object items — the machine-consumer default. Asserted server-side: method-driven state-change
/// events no longer depend on it (Gitea #587, <c>ConditionMethodEventTests</c>), and audit events only
/// leave the server while it is auditing.
/// </remarks>
[Fact]
public async Task A_server_object_item_alone_marks_conditions_monitored_until_it_is_deleted()