All lessons › Function E — The Agentic SOC › Recover and Root Cause — From Stopped to Back at Target
E5.4

Validating the fix — re-measuring the indicators the incident moved

E5
▶
Lightboard recording

Not recorded yet. The lesson below is complete and runnable today — the video is added later without touching this page.

Video placeholderpublishes automatically from recordings/E5.4.mp4
Risk

The remediation is marked done because the ticket closed, not because the control it restored was measured.

Control

Automatic re-measurement of the affected KCIs after a fix, with the before-and-after attached to the incident record.

The lab

Re-run the KCI measurement against a fixed estate and show which indicators recovered and which did not.

◉Use case relevance

The ticket is closed and the incident is marked remediated. Re-measure the indicators it moved and two of them never came back — including the detection time, which improved from 194 minutes to 118 and is still eight times its target.

At CyberTravels. validate_fix() re-measures the indicators the incident moved and attaches the before and after. An indicator it cannot measure comes back in unmeasured rather than as a pass — which is the ticket closing on its own authority, and the most common way a remediation is recorded as done.

◴What this lesson is — Day 0, Day 1, Day 2

Closing a remediation on a re-measurement rather than on a ticket, with the before and after attached to the incident — and an indicator that cannot be measured reported as unmeasured rather than as a pass.

Day 0 — why

A closed ticket is not evidence that a control came back.

Day 1 — how

Re-measure the indicators the incident moved, automatically, on every fix rather than the memorable ones.

Day 2 — measure

Indicators restored to target versus merely improved. Detection went from 194 minutes to 118 against a 15-minute target, which every system that does not re-measure records as done.

Measure this

what to measuredenominator or target
indicators restored to target, against merely improvedtwo counts — detection went 194 minutes to 118 against a 15-minute target

▦The framework, and how it works

   kci        healthy   during     after fix    verdict

   KCI-01      1.00      0.41       1.00        restored
   KCI-02      1.00      0.00       1.00        restored
   KCI-03      1.00      0.86       0.94        NOT restored
   KCI-04      9 min     194 min    118 min     NOT restored
   KCI-05      1.00      0.97       1.00        restored

                                    ^
                                    |
              the incident record would have said "remediated"
              for all five. improvement is not restoration.

A fix is a claim. The key control indicators built in F1.1 are how the claim gets checked: re-measure the indicators the incident moved, and see which came back to target.

Doing it automatically is the point. Done by hand, it happens for the incidents somebody remembers — which are not the ones where it matters.

Anchor → E1.0. This is where recover actually ends: an indicator back at target, not a closed ticket. CyberTravels' detection interval went from 194 minutes to 118 against a 15-minute target — a real improvement that every system not re-measuring would record as remediated.

2 · Improvement is not restoration

The uncomfortable row in this run is detection time. It improved from 194 minutes to 118 and is still eight times its target, so the control the root cause record named has been partially built.

Partially built is not built. Without the re-measurement it reads as done, and the incident record would have said "remediated" for every indicator the incident touched.

3 · Six indicators, three readings each

Healthy, during the incident, and after the fix. Restored and not-restored are reported separately, and untouched indicators are kept out of the report entirely.

▶Real time execution as skill

The skill — skills/response/kci-fix-validation/SKILL.md

name: kci-fix-validation
description: >-
  Re-measure the key control indicators an incident moved and report which the
  fix actually restored. Use when closing a remediation, when a ticket is marked
  done without evidence, or when a control was rebuilt and nobody has checked
  whether it now meets its target.
allowed-tools: Read, Grep, Glob

Closing a ticket is not evidence that a control came back

A fix is a claim. The KCIs are how the claim is checked: re-measure the indicators the incident moved and see which returned to target.

Doing it automatically is the point. Done by hand, it happens for the incidents somebody remembers, which are not the ones where it matters.

When to use this

On every remediation that claims to restore a control, before the incident is closed — and again at the next measurement cycle, because a fix that holds for a week and then regresses is common.

Step-by-step

1 — Take the KCIs from the root cause record. Not all of them; the ones the incident actually moved.

2 — Read the healthy baseline, not just the target. A control that was never at target before the incident did not regress.

3 — Re-measure after the fix, the same way. A different measurement is a different indicator.

4 — Report restored, not restored, and untouched separately. Untouched indicators are noise in the report and belong out of it.

5 — Attach before/during/after to the incident record. That is the artefact an auditor asks for, and it is worth more than the postmortem.

Example

Input — six indicators with three readings each, in scripts/kci_fix_validation.py.

Output — the part that changes the outcome:

   KCI-03  refunds with a matching approval
          target >= 0.99, measured 0.94 — the fix did not reach this one
   KCI-04  mean minutes to detect a scope breach
          target <= 15, measured 118.0 — the fix did not reach this one

Detection improved from 194 minutes to 118 and is still eight times target. Without the re-measurement, that reads as done.

Output contract

{
  "restored": ["str"],
  "not_restored": ["str"],
  "regressed": ["str"]
}

Common edge cases

  • Partially restored. Improvement is not restoration; the target is the test, not the direction of travel.
  • The indicator was already failing before the incident. Then it is a standing gap, not incident damage.
  • The measurement changed with the fix. A new instrument reading better is not a control reading better.

Failure modes

  • Marking remediated on ticket closure. The most common way a control gap survives its own incident.
  • Reporting an average across indicators. Two restored and two not is not "50% recovered", it is two open gaps.
  • Measuring once. Regression at the next cycle is the normal case.

Three ways to do this lesson. Pick one. They run the same procedure against the same code — what changes is who drives it, and how much you see.

1In your AI agent — you are taught

The agent reads the lesson, explains the idea, fetches the code, runs the check and answers your questions as you go. This is the one to take if you are here to learn.

Install it, once. From the folder you cloned. --all does every agent it finds on your machine:

python3 scripts/install_skills.py --all --lessons E5.4

Then restart your agent and open it in that folder. On Windows, if python3 says Python was not found, use python instead.

Then pick e5-4-validating-the-fix:

agentinstall just for this agentstart it
Claude Codepython3 scripts/install_skills.py --tool claude --lessons E5.4type /e5-4-validating-the-fix
GitHub Copilot (VS Code)python3 scripts/install_skills.py --tool copilot --lessons E5.4type /e5-4-validating-the-fix in Chat
Cursorpython3 scripts/install_skills.py --tool cursor --lessons E5.4type / in Agent chat and search for it
Codexpython3 scripts/install_skills.py --tool agents --lessons E5.4type $e5-4-validating-the-fix, or run /skills

2As one Python command — you just run it

No agent, no conversation. The same lesson executes and prints the same readback. Take this one if you have no assistant, if you are reviewing rather than learning, or if you want the result without the teaching around it.

python3 scripts/lesson.py E5.4

3Read the code, and the difference — nothing runs

Neither of the above, and no model needed. The first command writes CyberTravels exactly as it stood at the end of this lesson; the second prints the diff — the lines this lesson added or changed, and nothing from any later one.

python3 scripts/checkpoint.py --at E5.4 --out work/cybertravels
python3 scripts/checkpoint.py --at E5.4 --diff

On Windows, type python wherever these say python3 — and use \ instead of / in the paths. Everything else is identical. A0.0 step 2 explains why.

Ways 1 and 2 need a model; way 3 does not. A signed-in Claude Code CLI needs no API key, and any OpenAI-compatible endpoint works too. With neither, the audit exits 2 and says so rather than inventing an answer. A0.0 sets this up.

Ways 1 and 2 both end with the same readback: What I did, What changed, The number and Read this next. A step that was skipped or refused is reported as exactly that, never as a pass.

If you did the last lesson’s challenge, expect this to stop. Most challenges ask you to edit the code, and ways 1 and 2 will not overwrite your edits: you get stopped, not a failure, with two options — --out for a fresh folder that keeps your work, or --force to discard it and start this lesson clean.

✓What you just proved

Three indicators restored and two not, against an incident record that would have called all five remediated.

✎Your turn

Add a sixth indicator that regressed — better than target before, worse after. The report has nowhere to put it yet.

Tools used

OPA

Skill source: skills/response/kci-fix-validation

← PreviousE5.3 · Post-incident change surface — picking the layer the fix belongs in