Projects/Autonomous Ops Agent/Part 3
Diagnose and Propose Actions
Map symptoms to a diagnosis object and a list of proposed actions with risk, destructiveness, and expected effect — without executing them.
Diagnosis is a structured object, not a paragraph. Proposals are a list of action intents the gate can address by id. This part writes a fake diagnostician that looks at metrics+logs and emits candidates. It never calls mutate tools.
Diagnosis schema
`` { "service": str, "severity": "sev1"|"sev2"|"sev3", "hypothesis": str, "evidence": [str], "slo_burn": bool }
Rules of thumb (code, not LLM):
- error_rate > 10× SLO or > 0.05 → sev1 if also user-facing checkout
- else error_rate > SLO or p95 > SLO → sev2
- else sev3 / no incident
Hypothesis: if logs contain `deploy` and `last_good=` → `bad_deploy`. Elif cpu > 0.9 and errors modest → `capacity`. Else `unknown`.
Unknown is allowed. Unknown must **not** map to rollback.
## Proposal schema
``
{
"id": "act-01",
"tool": str, # mutate tool name
"args": dict,
"destructive": bool,
"risk": "low"|"high",
"reason": str,
"expected": str
}Map:
bad_deploy→ rollback_deploy to prev_deploy_id, destructive True, risk highcapacity→ scale_replicas +1, destructive False (still gated if you classify scale as mutate — yes, still gated in this project)unknown→ page_oncall only (if you add it as low-risk; still not silent)
Never propose two mutually exclusive mutates in the same batch without saying pick_one. This project proposes one primary mutate plus optional page_oncall.
Run to execute this in your browser. Nothing is sent to a server.
Step-by-step policy split
- Observe with read tools (part 2).
- Diagnose in code or LLM constrained to the schema. If LLM, still run a validator: hypothesis in allowlist.
- Propose from a runbook dict keyed by hypothesis, not from free imagination. The fake functions above are the runbook.
- Do not execute in this part. Print proposals. Humans (you) inspect.
If you let the model invent tool: "rm_rf", the gate in part 4 must still fail closed. Runbook-first means the model chooses among known action templates.
Severity is for humans
Sev-1 pages people. The agent should not restart prod because sev1; it should propose the runbook action. Severity without a hypothesis is still page_oncall.
Watch out:A high CPU after a bad deploy can trick a capacity hypothesis. Prefer log evidence for rollback. Order your ifs with bad_deploy before capacity (as above).
Runbooks are data
Store the mapping as a dict, not only as if-statements, so a future LLM can choose a key rather than invent a tool:
{"bad_deploy": {"tool": "rollback_deploy", "destructive": True}, "capacity": {"tool": "scale_replicas", "destructive": False}, "unknown": {"tool": "page_oncall", "destructive": False}}
The diagnostician returns a hypothesis in that key set. The proposer fills args from metrics (to=prev_deploy_id). If the hypothesis is not a key, you page. This is the same allowlist idea as weather tools, applied to intents.
When an LLM writes the hypothesis, validate it with hyp in RUNBOOK. "creative" diagnoses like need_more_disk_probably must become unknown. Creativity in ops is how you get surprise restarts.
Proposals also need expected effect you can check after apply: "error_rate < slo_error". If the approved action runs and the world does not improve, the report status is unresolved not resolved. Applying is not healing.
Exercise
Add hypothesis bad_config if logs contain config checksum mismatch. Propose a non-destructive page_oncall only (no silent config rewrite). Write a case that must not rollback.
Check your understanding