ShadowClaw 1.2.1¶
Adding the Grafana row for Risk Factors surfaced two reasons the panels would have been empty against a sensor that was working perfectly.
Metrics and logs were the same gate¶
_emit_risk_factors gated the whole method on emit_otlp_metrics and
returned before the log export. The documented path into DefenseClaw's
Grafana posts straight to Loki, which serves /otlp/v1/logs only and 404s
the metrics path — so on that deployment, the documented one, Risk Factors
were never emitted at all. Metrics and logs are now gated independently.
Nested attributes, again, from the other direction¶
The log record also carried its values in nested attributes while every
panel parses the body with | json. That is well-formed OTLP which Loki
surfaces as neither a label nor a parseable field — the same failure class
the Kinetic Envelope's flat-record design exists to avoid, arrived at here
from the opposite direction first. Risk Factors now emit a flat body
through otlpevent.ktp_risk_factor_record, beside finding.recorded and
agent.activity, with degraded_inputs joined on a comma the way
signals is on a finding, so a regex can match it. See
On the wire.
The row enforces "never panel the value alone" rather than restating it¶
The four factor stats filter on degraded_inputs and read unobserved
instead of showing the 1.0 substitute, so an unprivileged host no longer
burns red at adversarial_pressure 0.714. Inputs unobserved sits
leftmost because it is what tells you whether the four to its right mean
anything. The timeseries deliberately does not filter: a line that vanishes
when the sensor goes dark is indistinguishable from one that fell to calm,
so substituted values are drawn with coverage charted beside them. See
The dashboard row enforces it.
Verification¶
Three dashboard contract tests hold that shape, each verified to fail under
the mutation it exists to catch: a renamed field in an unwrap (a stat
panel templates nothing, so a rename there would otherwise return a
clean-looking zero rather than an error), a stat panel that unwraps a
factor without the degraded_inputs guard, and a typo'd factor name that
would silently match every poll. Eight tests fail if the early return comes
back. All fourteen queries were run against a live Loki 3.x and accepted.
Patch rather than minor¶
The gauges are unchanged, and the old body shape was never emitted on the path that reads it.