Skip to content

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.

Next