docs(probe): ack-leg third attempt — unavailable by configuration, GUID leg still assumed
This commit is contained in:
+141
-2
@@ -24,13 +24,13 @@ the blocker rather than rediscovering it.
|
|||||||
|---|---|
|
|---|---|
|
||||||
| GUID stable across polls and `ALM → RTN` | Answered — yes (2026-05-01 capture in `AlarmClientDiscovery.md`, reconfirmed 2026-08-18) |
|
| GUID stable across polls and `ALM → RTN` | Answered — yes (2026-05-01 capture in `AlarmClientDiscovery.md`, reconfirmed 2026-08-18) |
|
||||||
| GUID stable across clear-then-re-raise | Answered 2026-08-18 — **no**, a re-raise mints a new GUID |
|
| GUID stable across clear-then-re-raise | Answered 2026-08-18 — **no**, a re-raise mints a new GUID |
|
||||||
| GUID stable across `UNACK → ACK` | **Open** — the rig cannot be driven into an acknowledged state at all |
|
| GUID stable across `UNACK → ACK` | **Open** — the rig cannot be driven into an acknowledged state; 2026-08-18 (third attempt) identifies the reason as the test attribute's `MxSecurityOperate` classification, which no non-interactive ack surface on the rig can satisfy |
|
||||||
| `COUNT` = total active vs records-in-reply under a capped fetch | Answered 2026-08-18 — **records in the reply** |
|
| `COUNT` = total active vs records-in-reply under a capped fetch | Answered 2026-08-18 — **records in the reply** |
|
||||||
|
|
||||||
The 2026-08-17 run below is kept because it is the record of the wrong-verb blocker. The
|
The 2026-08-17 run below is kept because it is the record of the wrong-verb blocker. The
|
||||||
2026-08-18 run cleared that blocker with `AuthenticateUser` + `WriteSecured` and answered
|
2026-08-18 run cleared that blocker with `AuthenticateUser` + `WriteSecured` and answered
|
||||||
two of the three questions; the acknowledge leg is now blocked on something narrower and
|
two of the three questions; the acknowledge leg is now blocked on something narrower and
|
||||||
different, described in "Second attempt".
|
different, described in "Second attempt" and diagnosed in "Third attempt".
|
||||||
|
|
||||||
## First attempt (2026-08-17): plain `Write`
|
## First attempt (2026-08-17): plain `Write`
|
||||||
|
|
||||||
@@ -264,8 +264,147 @@ MXAccess-side entry point and the wnwrap-side entry point is inert.
|
|||||||
spurious Clear plus a spurious Raise, which is the same shape the now-observed re-raise
|
spurious Clear plus a spurious Raise, which is the same shape the now-observed re-raise
|
||||||
behaviour produces and which `ComputeTransitions` already handles as two instances.
|
behaviour produces and which `ComputeTransitions` already handles as two instances.
|
||||||
|
|
||||||
|
## Third attempt (2026-08-18): why the acknowledge is refused
|
||||||
|
|
||||||
|
The second attempt left two candidate explanations for `AlarmAckByName` returning `rc=0`
|
||||||
|
and changing nothing: the rig enforces an acknowledgement security requirement the wnwrap
|
||||||
|
consumer cannot meet, or wnwrap's ack is simply broken here. This attempt was read-only —
|
||||||
|
no writes, no alarms raised, no configuration touched — and settles the first question:
|
||||||
|
the requirement is **enforced**, and the alarm attribute's security classification is what
|
||||||
|
enforces it.
|
||||||
|
|
||||||
|
### Method
|
||||||
|
|
||||||
|
Read-only inspection of the `ZB` Galaxy Repository over `sqlcmd -S localhost -d ZB -E`,
|
||||||
|
plus the already-built `mxa` CLI (`C:\Users\dohertj2\Desktop\wwtools\mxaccesscli\src\MxAccess.Cli\bin\x86\Release\net48\mxa.exe`)
|
||||||
|
for runtime reads. Nothing was written and nothing was installed. The `lmxopcua\gr` schema
|
||||||
|
notes referenced elsewhere in this repo are **not present on this box** (`Test-Path` is
|
||||||
|
`False`), so the schema was located by querying `sys.tables` / `sys.columns` directly.
|
||||||
|
|
||||||
|
### The test attribute is classified `MxSecurityOperate`
|
||||||
|
|
||||||
|
UDA security classification lives in `dynamic_attribute.security_classification`, keyed by
|
||||||
|
`gobject_id` — for the `$TestMachine` template that is `1055`:
|
||||||
|
|
||||||
|
```
|
||||||
|
attribute_name | security_classification | mx_attribute_category
|
||||||
|
ProtectedValue | 2 | 10
|
||||||
|
ProtectedValue1 | 3 | 10
|
||||||
|
TestAlarm001 | 1 | 10
|
||||||
|
TestAlarm002 | 1 | 10
|
||||||
|
TestAlarm003 | 1 | 10
|
||||||
|
TestChangingInt | 1 | 10
|
||||||
|
```
|
||||||
|
|
||||||
|
The enum is pinned by two independent sources rather than assumed. `ProtectedValue` and
|
||||||
|
`ProtectedValue1` are the mxaccess analysis project's documented *secured-write* and
|
||||||
|
*verified-write* fixtures (`C:\Users\dohertj2\Desktop\mxaccess\docs\galaxy-test-fixtures.md`),
|
||||||
|
and `docs/NMX-COM-Contracts.md` in the same project records "Galaxy security classification
|
||||||
|
(`2` for `SecuredWrite`, `3` for VerifiedWrite)" — so `2` and `3` land on exactly the two
|
||||||
|
attributes that are supposed to carry them. `galaxy-test-fixtures.md` also records the
|
||||||
|
provisioning verb used for every UDA in that inventory: `--security MxSecurityOperate`.
|
||||||
|
`TestAlarm001` therefore reads `1` = **`MxSecurityOperate`**.
|
||||||
|
|
||||||
|
That is the missing piece from 2026-08-17. `Operate` is not free access: it requires an
|
||||||
|
authenticated galaxy identity holding Operate permission on the object's security group.
|
||||||
|
An unauthenticated `Write` is refused with `SecurityError` `1008`, and the same write after
|
||||||
|
`AuthenticateUser` succeeds — which is precisely the pair of results both prior attempts
|
||||||
|
recorded. Galaxy security is live on this rig, not disabled.
|
||||||
|
|
||||||
|
`wwAlarmConsumerClass.AlarmAckByName` carries an operator *name* string and a comment. It
|
||||||
|
carries no authenticated user id, no credential, and no token — there is no parameter on
|
||||||
|
either the 6-arg or the 8-arg overload that could convey one. A consumer calling it cannot
|
||||||
|
satisfy an `Operate` classification, and `rc=0` followed by no state change is what an ack
|
||||||
|
dropped downstream of an accepted call looks like.
|
||||||
|
|
||||||
|
The one step this stops short of is a direct experiment: the rig's security configuration
|
||||||
|
is out of scope for a read-only probe, so "an `Operate` alarm cannot be acknowledged
|
||||||
|
without an authenticated identity" is inferred from the classification plus the observed
|
||||||
|
`1008`/`rc=0` pattern rather than observed by relaxing the classification and watching the
|
||||||
|
ack start working. The human step below is exactly that experiment.
|
||||||
|
|
||||||
|
### There is no writeable `.Ack` attribute — the second attempt targeted the only one there is
|
||||||
|
|
||||||
|
A plausible reading of the second attempt was that it wrote the wrong sub-attribute:
|
||||||
|
ArchestrA alarm extensions are commonly described as exposing a writeable `.Ack` alongside
|
||||||
|
the read-only `.Acked`. On this galaxy they do not. Every `Ack`-named attribute the alarm
|
||||||
|
primitives define:
|
||||||
|
|
||||||
|
```
|
||||||
|
attribute_name | security_classification | mx_attribute_category
|
||||||
|
Acked | -1 | 2
|
||||||
|
AckMsg | 0 | 6
|
||||||
|
Bad.Acked | -1 | 2
|
||||||
|
Bad.AckMsg | 0 | 6
|
||||||
|
TimeAlarmAcked | -1 | 2
|
||||||
|
AlarmAckCnt | -1 | 2
|
||||||
|
AlarmAckErrorsCnt | -1 | 2
|
||||||
|
AlarmMostUrgentAcked | -1 | 2
|
||||||
|
AlarmUnAckedCnt | -1 | 2
|
||||||
|
```
|
||||||
|
|
||||||
|
No `Ack`. `Acked` carries `security_classification = -1` — no classification at all, which
|
||||||
|
is what a non-writeable attribute carries, as distinct from the `1` on the writeable
|
||||||
|
`TestAlarm001`. That is the configuration-side counterpart of the `detail=1007`
|
||||||
|
operational refusal the second attempt got when it wrote `.Acked`: not a permission
|
||||||
|
refusal, but an attribute that has no write path to refuse. The second attempt had already
|
||||||
|
found the only MXAccess-side candidate, and it is read-only by definition.
|
||||||
|
|
||||||
|
### No non-interactive acknowledge surface is installed
|
||||||
|
|
||||||
|
The `wwtools` collection on the box (`aalogcli`, `aot`, `graccesscli`, `grdb`, `histdb`,
|
||||||
|
`mbproxy`, `mxaccesscli`, `secrets`) is the most likely home for a scriptable ack. There
|
||||||
|
isn't one. `mxa --help` lists `diag`, `info`, `read`, `read-batch`, `subscribe`,
|
||||||
|
`subscribe-batch`, `write`, `write-batch` — a tag data-plane only, with no alarm surface.
|
||||||
|
`graccesscli` is a Galaxy Repository configuration tool (`object uda add`, `instance
|
||||||
|
deploy`), which acts at configure/deploy time and not on live alarm state. Reading the
|
||||||
|
galaxy's own authentication mode at runtime is also unavailable: `Galaxy.AuthenticationMode`
|
||||||
|
does not resolve over MXAccess (`Category=4 Detail=6`), and the value is not in
|
||||||
|
`dynamic_attribute` — only the attribute *definition* names `AuthenticationMode` and
|
||||||
|
`_AuthenticationModeEnum` exist in `attribute_definition`.
|
||||||
|
|
||||||
|
So every remaining acknowledge surface on this rig is interactive: the IDE's alarm client,
|
||||||
|
InTouch, or an ArchestrA graphic bound to the alarm. Driving those is out of scope.
|
||||||
|
|
||||||
|
### Status of the acknowledge leg
|
||||||
|
|
||||||
|
**Unavailable by configuration, and the GUID question stays assumed.** The two are separate
|
||||||
|
statements and both matter:
|
||||||
|
|
||||||
|
- The wnwrap ack path is unavailable on this rig as configured, for an identified reason
|
||||||
|
rather than an unknown one. This is a real finding for the gateway: `AcknowledgeByName`
|
||||||
|
will behave the same way — accepted, inert — on any customer galaxy whose alarmed
|
||||||
|
attributes carry a non-free-access security classification. It is worth noting in the
|
||||||
|
alarm client's documentation that a silent `rc=0` is not proof of acknowledgement.
|
||||||
|
- Whether wnwrap re-mints the record GUID on `UNACK_ALM → ACK_ALM` is still unobserved, and
|
||||||
|
after three attempts it stays assumed. As the second attempt already noted, this remains
|
||||||
|
a documentation gap rather than a correctness one: a re-minted GUID produces a spurious
|
||||||
|
Clear plus a spurious Raise, the same shape the observed re-raise behaviour produces, and
|
||||||
|
`ComputeTransitions` already handles that correctly as two instances.
|
||||||
|
|
||||||
|
#### What a human would need to do
|
||||||
|
|
||||||
|
Either of these answers it; the second is cheaper and also confirms or refutes the
|
||||||
|
classification hypothesis above, which the read-only probe could only infer.
|
||||||
|
|
||||||
|
1. Acknowledge `TestMachine_001.TestAlarm001` from an interactive System Platform client
|
||||||
|
(IDE alarm client, InTouch, or an ArchestrA graphic) while a wnwrap probe polls
|
||||||
|
`GetXmlCurrentAlarms2` against `\\DESKTOP-6JL3KKO\Galaxy!TestArea`, and record whether
|
||||||
|
`STATE` reaches `ACK_ALM` and whether `GUID` survives the transition. Raise the alarm
|
||||||
|
first with `AuthenticateUser` + `WriteSecured` as the second attempt did.
|
||||||
|
2. Reclassify `TestAlarm001` on the `$TestMachine` template to free access and redeploy —
|
||||||
|
`graccesscli object uda ... --security MxSecurityFreeAccess` against `$TestMachine`,
|
||||||
|
then `instance deploy TestMachine_001` — and re-run the second attempt's ack probe
|
||||||
|
unchanged. If `AlarmAckByName` then moves `STATE` to `ACK_ALM`, the classification is
|
||||||
|
confirmed as the blocker and the GUID question is answered in the same run. Restore the
|
||||||
|
classification to `MxSecurityOperate` afterwards, since the secured-write fixtures in
|
||||||
|
`WorkerLiveMxAccessSmokeTests` depend on the alarm UDAs being secured.
|
||||||
|
|
||||||
### Rig state left behind
|
### Rig state left behind
|
||||||
|
|
||||||
|
The third attempt changed nothing. `TestMachine_001.TestAlarm001` and its `.Acked` subtag
|
||||||
|
both read `false` at the end of the session, matching the state the second attempt left.
|
||||||
|
|
||||||
The three `TestMachine_00{1,2,3}.TestAlarm001` UDAs are back to `false` and their
|
The three `TestMachine_00{1,2,3}.TestAlarm001` UDAs are back to `false` and their
|
||||||
`.InAlarm` subtags read `false`, but each leaves a `UNACK_RTN` record in the wnwrap
|
`.InAlarm` subtags read `false`, but each leaves a `UNACK_RTN` record in the wnwrap
|
||||||
snapshot, since nothing can acknowledge them away. `SnapshotActiveAlarms` counts only
|
snapshot, since nothing can acknowledge them away. `SnapshotActiveAlarms` counts only
|
||||||
|
|||||||
Reference in New Issue
Block a user