Skip to main content
v0.7 is the “signal that discriminates” release. v0.6 closed the loop but drove it with the wrong measurement, and a measured benchmark run regressed. This page is what you need to change.
Most integrations need two edits: drop task_hint from your system_context() call, and add settle=True to your turn scope. Everything else migrates itself.

Why it changed

Three findings from a measured run, each of which is a breaking change below:
  1. The trigger fired on everything. agent_reliability / agent_consistency are worst-case rollups that floor near ~0.2 for essentially every real session, so “score below 0.5” was true almost always.
  2. Promotion was close to random. Candidate rules were scored on that same non-discriminating metric, so the promote/retire decision carried little information.
  3. The agent operated the harness instead of doing the task. Handed 14 tools, its whole rule corpus in every prompt, and a check-your-mailbox-every-turn mandate, 9 of the 13 rules it wrote were about gaming its own diagnostic protocol.
And one bug underneath all of it: the trend machinery was inert. Score history is keyed per session, and hosts hooked the harness once per task-trial — so every series had exactly one sample and trend_min_samples was never reachable.

1. The trigger is trace-level

What changed. trigger_mode defaults to "trace". Instead of two session composites per turn, the harness scores trace-level metrics in three tiers and Tier 1 breaches on the trajectory — a stall or a regression — never on an absolute floor. What you do. Nothing, to get the new behavior. But two things are worth knowing:
  • Your thresholds carry different meaning now. HARNESS_RELIABILITY_THRESHOLD and HARNESS_CONSISTENCY_THRESHOLD only apply in session mode. What matters in trace mode is gate_window (the primary knob), gate_target, gate_drop, and the Tier-2 thresholds. Re-calibrate.
  • Notices look different. Each metric now carries trace_id and tier, the notice carries a scope_hint, and the condition vocabulary gained stall and regression. If you parse notices or signatures downstream, update those consumers.
To keep the old behavior — for an A/B baseline, or to defer the migration:
Session mode is fully supported and unchanged. Note that the outcome verifier is trace-mode only.

2. Turn boundaries need a barrier

What changed. Harness.settle(session_id) and harness.turn(session_id, settle=True) are new. This is the fix for the inert-trend bug: without a per-turn barrier, an agent runs several turns ahead of its own diagnosis and a session can end before its first notice arrives. What you do.
Audit your turn granularity while you are here. If your loop calls on_turn_end once per task rather than once per turn, the trajectory gate gets one sample per series and can never fire — the same failure that made v0.6’s trend detection inert. Framework adapters wire the framework’s natural boundary, which for a coarse one-call-per-workflow API is not the boundary you want. See Framework adapters.
The barrier is on its own barrier_timeout_s (default 180s), separate from drain_timeout_s. On expiry the work continues detached and SettleResult.timed_out is set.

3. The rules workspace is agent-owned

What changed. harness_rules.md is now a skill root — protocol, tool table, and a generated References index, with no rule text. Rules live in rules/global.md, rules/scoped.md, and any <topic>.md the agent creates. The system context injects no rule bodies; the agent pulls them with the new harness_rules_read. What you do.
PandaHarnessHook.startup_context() and compose_system_preamble() lost the same parameter. Your existing rules migrate themselves. rules.jsonl records are migrated on read, preserving v0.6’s meaning: No migration command and no rewrite pass; the first render regenerates the rules/ subtree. If you have a customized harness_rules.md, note that provisioning never overwrites an existing root — but the References section below the generated marker is regenerated.
Retrieval is now keyed on scope, not on whether a rule happens to carry tags. In v0.6 any rule added without a source notice had no tags and therefore became a permanent global by accident. tags still exist, for ranking.

4. Four tools were removed

What changed. 14 tools → 10. Added: harness_rules_read. And harness_rule_add gained a scope argument, defaulting to the source notice’s scope_hint. What you do. If you enumerate tool names, or assert a tool count, update it. HarnessToolset no longer accepts history= or evalset= — nothing left in it reads them.

New in v0.7, opt-in

  • Outcome verifierHarness.create(..., verifier=...). A (session_id, end_state) -> float | bool | None oracle becomes the outcome_correct metric and, when present, decides promotion instead of a judged proxy. Prefer a continuous score: a pass/fail flag that is almost always 0 discriminates no better than the metrics this release replaces.
  • Tier 3enable_tier3=true adds plan_adherence, plan_quality, step_efficiency to explain a confirmed Tier-2 breach. Never a breach source of its own.
  • hook.pending_sessions — a read-only view for host-side phase barriers, so callers no longer need the private task map.

Behavior fixes worth knowing

These need no action, but they change what you’ll observe:
  • Validation and regression re-score on trace metrics (Tier 1 + Tier 2, against the replay’s last trace), not the session composites. Deltas in a regression report will name different metrics than they did in v0.6.
  • Promotion target is chosen per case from the deltas that actually arrived, in trust order: outcome_correct → the rule’s metric → the triggering signature. A wired verifier with no verdict for a particular task no longer vetoes promotion.
  • Severity mapping is deliberate. A Tier-1-only fire is advisory trend and captures no eval case; only a Tier-2-confirmed breach is a breach and becomes a promotable case. Expect a smaller, cleaner eval set.
  • One platform run per turn for Tier 1, over every new trace, and one history write per trace instead of two per metric.
  • Trace listings no longer burn the retry budget on a warm session — an empty listing is only retried while the session has never yet produced a trace.
  • --signal-weights is session-only. It is a validation error on the trace path, so the harness omits it there.

A quick checklist

1

Drop task_hint

harness.system_context() — no arguments.
2

Add the barrier

harness.turn(session_id, settle=True), or an explicit await harness.settle(session_id).
3

Confirm turn granularity

One on_turn_end per agent turn, not per task. Verify it: after a multi-turn session, state/score_history.json should hold more than one sample per (session, metric).
4

Update tool-name lists and notice parsers

10 tools; harness_rules_read is new; notices carry trace_id, tier, scope_hint; signatures can say stall: / regression:.
5

Re-calibrate

Tune gate_window to your task horizon, and re-measure the Tier-2 thresholds against your traffic.
6

Consider a verifier

If you have any ground truth, wire it — it is the strongest promotion signal available and costs nothing per turn.