Skip to content

feat(agent): structured Monitor->Diagnostic handoff (typed, not prose) - #87

Merged
atomicdragonranch merged 1 commit into
masterfrom
feat/80-structured-handoff
Jul 9, 2026
Merged

atomicdragonranch merged 1 commit into
masterfrom
feat/80-structured-handoff

Conversation

@atomicdragonranch

Copy link
Copy Markdown
Owner

Closes #80.

Problem

MonitorToDiagnosticHandoff.anomaly_context was a raw prose string, the #1 context-passing anti-pattern from the multi-agent pattern. The Diagnostic sub-agent had to re-parse a narrative to recover what breached, by how much, and where. The downstream DiagnosisReport already uses structured claim-source records, so the first handoff should match.

Fix

  • New DetectedAnomaly model: anomaly_type, summary, detected_at, plus optional metric / observed_value / baseline / threshold / breach_direction / affected_component / source_signal_ids.
  • MonitorToDiagnosticHandoff.anomaly: DetectedAnomaly replaces the string. The size-guard now truncates the only unbounded field (summary) so an over-long narrative can't overflow the diagnostic prompt.
  • _detect_anomalies returns DetectedAnomaly | None: injects the schema into the detection prompt and parses the conclusion via _parse_detection, which falls back to wrapping prose in summary so detection always yields a typed object. No extra LLM call; detection stays one response, so orchestration call-counts are unchanged.
  • _spawn_diagnostic_agent takes the typed anomaly and injects its JSON. Runbook resolution runs off anomaly.summary, keeping _resolve_runbooks' string contract so the steering/runbook tests are untouched. Single-agent extractor uses the typed fields.

Tests

DetectedAnomaly required/optional/round-trip; handoff typed + summary truncation + round-trip; _parse_detection JSON success + prose fallback; _detect_anomalies returns typed anomaly / None when healthy.

Full suite 229 passed; ruff + mypy clean.

https://claude.ai/code/session_01R5VygSzbGTggW7mHd3PVwE

Closes #80.

MonitorToDiagnosticHandoff.anomaly_context was a raw prose string, the #1
context-passing anti-pattern: the Diagnostic sub-agent had to re-parse a
narrative to recover what breached, by how much, and where. The downstream
DiagnosisReport already uses structured claim-source records, so the first
handoff now matches.

- New DetectedAnomaly model (anomaly_type, summary, detected_at, plus optional
  metric / observed_value / baseline / threshold / breach_direction /
  affected_component / source_signal_ids) carries typed context.
- MonitorToDiagnosticHandoff.anomaly: DetectedAnomaly replaces the string; the
  size-guard now truncates the only unbounded field (summary) so an over-long
  narrative can never overflow the diagnostic prompt.
- _detect_anomalies returns DetectedAnomaly | None: it injects the schema into
  the detection prompt and parses the conclusion via _parse_detection, which
  falls back to wrapping prose in summary so detection always yields a typed
  object (no crash, no extra LLM call, detection stays one response).
- _spawn_diagnostic_agent takes the typed anomaly and injects its JSON; runbook
  resolution runs off anomaly.summary (keeps _resolve_runbooks' string contract,
  so steering/runbook tests are untouched). Single-agent extractor uses the
  typed fields.

Tests: DetectedAnomaly required/optional/round-trip, handoff typed + summary
truncation + round-trip, _parse_detection JSON + prose fallback, and
_detect_anomalies returns typed anomaly / None. Full suite 229 passed; ruff +
mypy clean.
@atomicdragonranch
atomicdragonranch merged commit 93526be into master Jul 9, 2026
3 checks passed
@atomicdragonranch
atomicdragonranch deleted the feat/80-structured-handoff branch July 9, 2026 18:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(agent): structured Monitor->Diagnostic handoff (not raw prose)

1 participant