Skip to content
See what happened

See what happened

Your agent wrote two entries, and the gateway refused one write between them. This page reads that back, which is the thing kslate is for: answering afterwards what each agent did and what it was acting on.

You need the record from your first agent.

Who wrote a field, and on what basis

kslate blame shows one field’s whole story.

KSLATE_APIKEY=rem-token bin/kslate blame --server http://127.0.0.1:8080 \
  incidents/inc-7 actions_taken
actions_taken  (append, evidence required)
v2  19:49:58  apikey:remediator     "executed: roll back api deployment"
      evidence: kubectl output
      based_on: planned_action@2
      identity: apikey:remediator (shared-secret)
v1  19:49:58  apikey:remediator     "executed: restart payments-api pods"
      evidence: kubectl rollout restart deploy/payments-api
      based_on: planned_action@1
      identity: apikey:remediator (shared-secret)
--  19:49:58  apikey:remediator     REJECTED STALE_PREMISE
      based_on: planned_action@1

Four things, none of which your agent had to record on purpose:

  • who wrote it — apikey:remediator, from the credential on the connection. A request cannot name its own author.
  • what evidence they cited — the string you passed. kslate checks it is present, never that it is true.
  • what they were acting on — based_on: planned_action@2. The client filled that in from the version it read.
  • how well the credential was bound — shared-secret for an API key. Identity strength covers the stronger kinds.

The based_on line answers “was this decision current when it was made”. Entry v1 read planned_action@1, which the diagnoser replaced while your agent was deciding. Entry v2 read planned_action@2, the plan the record still holds.

The -- row is the write the gateway refused. It carries no version, because the gateway stored no entry for it. It is the first attempt at entry v2, sent with based_on: planned_action@1 after the plan had moved.

The whole record

kslate history is every write to every field, newest first.

KSLATE_APIKEY=rem-token bin/kslate history --server http://127.0.0.1:8080 incidents/inc-7
v2   19:49:58  actions_taken        apikey:remediator      append APPLIED
      evidence: kubectl output
      based_on: planned_action@2
      identity: apikey:remediator (shared-secret)
--   19:49:58  actions_taken        apikey:remediator      append STALE_PREMISE
      evidence: kubectl output
      based_on: planned_action@1
      identity: apikey:remediator (shared-secret)
v2   19:49:58  planned_action       apikey:diagnoser       put    APPLIED
      evidence: api logs show config parse error at 14:02:11
      identity: apikey:diagnoser (shared-secret)
v1   19:49:58  actions_taken        apikey:remediator      append APPLIED
      evidence: kubectl rollout restart deploy/payments-api
      based_on: planned_action@1
      identity: apikey:remediator (shared-secret)
v1   19:49:58  planned_action       apikey:diagnoser       put    APPLIED
      evidence: api pods in CrashLoopBackOff
      identity: apikey:diagnoser (shared-secret)

Five writes by two identities. Nobody told either agent about the other.

The refused write is in the record with the same detail as the applied ones: who sent it, what evidence it cited, what it was based on, and which check it failed. That is how you find an agent that acted on something stale.

The field that moved

blame on planned_action shows the same rejection from the other side.

KSLATE_APIKEY=rem-token bin/kslate blame --server http://127.0.0.1:8080 \
  incidents/inc-7 planned_action
planned_action  (versioned, evidence required)
v2  19:49:58  apikey:diagnoser      "roll back api deployment"
      evidence: api logs show config parse error at 14:02:11
      identity: apikey:diagnoser (shared-secret)
v1  19:49:58  apikey:diagnoser      "restart payments-api pods"
      evidence: api pods in CrashLoopBackOff
      identity: apikey:diagnoser (shared-secret)
      superseded by v2 after 0s
--  19:49:58  apikey:remediator     REJECTED STALE_PREMISE
      write: actions_taken  based_on: planned_action@1  (moved to v2 at 19:49:58)

v1 was the current plan for under a second. That is the superseded by v2 after 0s line. The -- row names what that cost: an append to actions_taken that the gateway refused, because it was based on v1 after v2 existed.

Next