Skip to main content

Anatomy of a notice

A DiagnosticNotice is everything the agent needs to diagnose one detected degradation:
{
  "id": "n-20260703T053511-f2019e57",
  "created_at": "2026-07-03T05:35:11.698999+00:00",
  "session_id": "s-demo-1",
  "turn_index": 1,
  "severity": "breach",
  "metrics": [
    {
      "name": "agent_reliability",
      "value": 0.30,
      "threshold": 0.5,
      "reason": "identical repeated tool call detected",
      "conditions": ["breach"]
    }
  ],
  "flagged_traces": ["trace-1"],
  "signal_breakdown": {
    "trace-1": { "loop_detection": 0.1, "tool_correctness": 0.2 }
  },
  "dump_path": "/harness/traces/n-20260703T053511-f2019e57.json",
  "summary": "agent_reliability=0.30 [breach, threshold 0.50]; flagged traces: trace-1",
  "signatures": ["breach:agent_reliability"],
  "status": "pending",
  "resolution": null
}
FieldPurpose
metricsEvery alerting metric with its value, threshold, platform reason, and which conditions fired.
flagged_tracesThe trace ids the platform identified as problematic — the starting point for harness_trace_inspect.
signal_breakdownPer-trace signal scores (loop_detection, tool_correctness, …) already extracted from the score metadata.
dump_pathAn immutable, verbose evaluation dump under traces/ for the full picture.
signaturesThe compact condition labels — used for de-dup, rule tags, and eval-case matching.
resolutionFilled on acknowledgment: when, and which rule resolved it.
Notice ids are timestamp-sortable and validated as safe path components (the agent supplies them to tools, and they become filenames).

The mailbox

mailbox/
├── pending/<notice-id>.json     # awaiting the agent
├── processed/<notice-id>.json   # acknowledged, with resolution attached
└── status.json                  # {pending_count, max_severity, latest_id}
  • The hook posts to pending/; the agent acknowledges via harness_mailbox_ack, which moves the notice to processed/ with a Resolution (acked_at, optional rule_id, optional note).
  • status.json is the cheap summary the system-context banner reads — composing the prompt never scans the directory.
  • The banner carries only a count and a severity enum. No eval-derived free text enters the preamble; rich content stays behind explicit tool calls, where it is sanitized and framed as untrusted data.
harness.mailbox.pending()          # list[DiagnosticNotice], oldest first
harness.mailbox.read(notice_id)    # pending or processed
harness.mailbox.acknowledge(notice_id, rule_id="r-...", note="mitigated")

Trace dumps

Two artifacts under traces/:
  • latest_eval.json — always rewritten with the most recent evaluation, alerting or not. A cheap “what did the harness last see” probe.
  • <notice-id>.json — one immutable dump per notice: every score with thresholds, conditions, per-trace signals, and (with enrich_flagged_traces=true) the worst flagged trace’s TOOL spans fetched at notice time.

The journal

journal.jsonl is the append-only, cross-run event log — the agent’s long-term memory of its own health. The agent mines it through harness_journal (recent events, filterable by type) and harness_reflect (an assembled reflection context: recent notices, the live rule set, validation outcomes, and per-rule effectiveness counts).
harness.journal.recent(20, types=("notice", "rule_promote"))
The full event-type list is in How it works. Because the journal spans process restarts, a fresh harness over the same workspace inherits everything: rules re-enter the context, and past notices inform new baselines.
Everything in the workspace is plain JSON on disk. When in doubt, cat it — the file layout is the API surface for operators.