Skip to main content
Rules are the harness’s output: permanent operating constraints the agent writes for itself, learned from its own failures. They live in a structured store, render into the system prompt, and — since v0.6 — carry a full lifecycle and relevance-based injection.

The rule store

rules.jsonl is append-only; the latest record per rule id wins (so retiring or promoting a rule appends an updated record rather than rewriting history). Each rule carries:
FieldPurpose
id, created_atIdentity (r- + 10 hex chars) and provenance timestamp.
rule, rationaleThe constraint and why it exists — both sanitized on write.
source_notice_id, metricProvenance: which notice motivated it, which metric it targets.
statuscandidateactive | retired. See Rule validation.
tagsRetrieval hooks — auto-derived from the source notice (signatures, metric names, signal names, severity) plus any explicit tags. An untagged rule is global: always injected.
trialValidation bookkeeping: baseline vs. observed breach rates, sessions watched, replay attempts, and the final verdict.
Guard rails on add:
  • Dedup — a rule whose normalized text matches a live rule returns the existing rule instead of creating a duplicate.
  • Cap — at max_active_rules (default 50, counted over active + candidate) the add fails with a message telling the agent to retire something first. Compaction is agent-driven; nothing is silently evicted.
  • Sanitization — rule text and rationale pass the trust boundary (sanitize_text) before they can ever reach a prompt.

The rendered context

harness_rules.md is a projection, regenerated from the store on every change: the packaged template preamble (the self-healing contract and tool table), then the rules:
## Learned Mitigations

<!-- ACTIVE RULES — managed by the harness; use the harness rule tools -->

- **r-9772c134b8**: Never call the payment tool twice without first verifying
  the transaction status identifier.
  - rationale: Repeated identical payment call flagged by the reliability eval.
    (added 2026-07-03T05:35:11+00:00, from notice n-20260703T053511-f2019e57)

### Provisional rules (under evaluation)

_The following candidate rules are in force but not yet validated. Treat
them as tentative: apply them, but prefer validated rules when they conflict._

- **r-23b0460306** (candidate): Verify the transaction status before retrying.
  - rationale: ... (added ...; trial: 2/5 sessions observed)
Candidates render always — a rule must be in force to be measurable — but under a heading that makes their unproven status unmistakable, and the standing protocol tells the agent to prefer validated rules on conflict.

Task-conditioned retrieval

With more than a handful of rules, injecting all of them dilutes attention and lets a rule learned from one failure mode overfit onto unrelated tasks. With rule_retrieval=true (the default), the context renders:
  1. Every global (untagged) rule — always, exempt from the budget.
  2. The top-rules_context_topk (default 8) tagged rules ranked by lexical relevance to the current query: the pending notices’ signatures and metric names, plus an optional caller-supplied task hint:
system_prompt = harness.system_context(task_hint="charge a customer payment") + MY_PROMPT
  1. A one-line note when rules were trimmed, pointing at harness_rules_search / harness_rules_list — the context stays small while every rule stays reachable.
The scorer is deliberately simple and dependency-free: casefolded word tokens, tag hits weighted double over rule/rationale text hits, normalized by query size, ties broken by recency. breach:agent_reliability tokenizes to breach + agent_reliability, so signatures, metric names, and task words all match naturally. Behavior at the edges:
  • No signal (no pending notices, no hint) → everything renders; retrieval never hides rules without a reason.
  • No overlap → ranking degrades to recency under the same top-k.
  • Candidates never consume top-k slots — retrieval can’t starve a trial.
  • rule_retrieval=false restores render-everything (the pre-v0.6 behavior).
  • The on-disk harness_rules.md is always the full render; retrieval only shapes the composed prompt.

Working with rules programmatically

harness.rules.active()                      # validated rules
harness.rules.candidates()                  # rules under trial
harness.rules.search("payment breach")      # ranked (rule, score) pairs
harness.rules.retire("r-9772c134b8", reason="superseded by a broader rule")
harness.rules.effectiveness()               # per-rule notice counts before/after
The agent-facing equivalents are harness_rules_list, harness_rules_search, harness_rule_retire, harness_rule_status, and harness_reflect — see the toolset.