Skip to content

Rule Packets

Use a rule packet when you are about to paste the same rubric into multiple prompts.

A packet is a stored deterministic judge. It evaluates one JSON-shaped entity against ordered rules and returns the same answer every time. No LLM runs at apply time, so a packet_id can be handed to five agents and every one of them uses the same decision function.

Packets are good for gates, routing, review findings, surface filtering, and repeatable "should this proceed?" decisions. They are not a replacement for knowledge entries or prose instructions.

The Loop: Compile, Audit, Apply

1. Compile (bbox_compile)

Author a packet from a domain tag, a classification lattice (priority order), and a rules array. Each rule has an id, an antecedent (predicate AST), and a consequent (the output value when the rule fires).

Rules are first-match-wins. Put anomalies before general fallbacks.

bbox_compile(
  domain = "pr-triage",
  classification_lattice = ["fail", "flag", "manual", "pass", "info"],
  rules = [
    {
      "id": "fail_tests",
      "classification": "fail",
      "antecedent": {"op": "Eq", "field": "tests_pass", "value": false},
      "consequent": "REJECT"
    },
    {
      "id": "flag_api_change",
      "classification": "flag",
      "antecedent": {"op": "Eq", "field": "api_surface_changed", "value": true},
      "consequent": "FLAG"
    },
    {
      "id": "pass_default",
      "classification": "pass",
      "emit": "fallback",
      "antecedent": {"op": "True"},
      "consequent": "ACCEPT"
    }
  ]
)

Returns a packet_id (e.g. packet-7f01324e). Store it in your prompt or dispatch it to sub-agents.

2. Audit (bbox_audit)

Always run this after compile. Validates the packet against a known dataset of {entity, expected} pairs. A fidelity < 1.0 means the packet is misgeneralizing. This catches rule-ordering bugs, over-broad antecedents, and field-name typos before the packet goes live.

bbox_audit(
  packet_id = "packet-7f01324e",
  dataset = [
    {"entity": {"tests_pass": false, "api_surface_changed": false}, "expected": "REJECT"},
    {"entity": {"tests_pass": true, "api_surface_changed": true}, "expected": "FLAG"},
    {"entity": {"tests_pass": true, "api_surface_changed": false}, "expected": "ACCEPT"}
  ]
)

Two audit modes:

Mode Use when
first (default) Single-rule classification: compare the first match's consequent
all Multi-finding review packets: compare aggregate verdict + fired rule-id set

3. Apply (bbox_apply)

Evaluate any entity against the packet. No LLM, deterministic.

bbox_apply(packet_id = "packet-7f01324e", entity = {"tests_pass": true, "api_surface_changed": true})

Two apply modes:

Mode Use when
first (default) Classification: return the first matching rule's consequent
all Review: return every matching rule + aggregate verdict (Fail > Flag > Manual > Pass > Info)

Finding existing packets

Before authoring a new packet, check if one already exists for your domain:

// By domain
bbox_packet_list(domain = "pr-triage", latest_per_domain = true)

// By concept, when you don't know the domain label
bbox_packet_list(query = "breaking", limit = 10)

// Via knowledge search (surfaces packets + system memories)
bbox_knowledge(category = "packet")

Packet composition

Packets can embed other packets via the Apply predicate. Do not re-derive a rule you already compiled:

{
  "id": "flag_breaking",
  "antecedent": {
    "op": "Apply",
    "packet_id": "packet-deadbeef",
    "expect": "BREAKING"
  },
  "consequent": "FLAG"
}

The Apply predicate runs the referenced packet against the same entity and compares the result to expect. This lets you layer concerns: pr-triage delegates "is this breaking?" to breaking-change-detector.

Classification lattice

The classification_lattice defines the priority order for aggregate verdicts. Defaults to ["fail", "flag", "manual", "pass", "info"]. Supply a domain-specific lattice for auth, retry, design-iteration, etc.:

Domain Lattice
Auth ["deny", "allow"]
Retry ["dlq", "fail_fast", "backoff", "retry", "noop"]
Design review ["blocker", "concern", "suggestion", "advantage", "neutral"]

Predicate AST reference

Predicate Description
True Always matches (use as final fallback)
Eq{field, value} Entity field equals value
Neq{field, value} Entity field not-equals value
Gt{field, value} / Lt{field, value} Numeric comparison
Contains{field, value} String field contains substring
FieldExists{field} Entity has the named field
And{predicates[]} All sub-predicates match
Or{predicates[]} Any sub-predicate matches
Not{predicate} Negate a sub-predicate
Apply{packet_id, expect} Delegate to another packet's classification
RankGeFieldThreshold{rank_key, threshold_key} Role-rank ≥ resource-threshold (lookup-table driven)
IsAllowed{field, allowed:[]} Value is in the allowed set

Gap logging

When you want to express a rule the AST can't handle (regex matching, rate-limit thresholds, temporal constraints), log a gap:

bbox_packet_gap(
  description = "wanted regex matching on log messages; no StringMatches primitive",
  ast_feature_requested = "StringMatches"
)

Every gap logged is a vote for prioritizing new AST primitives. Query gaps later with bbox_packet_events(op = "gap").

When to use a packet vs. something else

Symptom Tool
"I'm coordinating sub-agents and pasting the same rubric into each prompt" bbox_compile: compile once, dispatch packet_id
"I need to classify 100+ entities against the same rubric" bbox_apply: deterministic, no LLM drift
"I want a durable decision function that survives system prompt changes" bbox_compile: packets are stored, not inlined in prompts
"I just need a one-off prose instruction" Don't compile. Use prose in the prompt.
"I need to remember a convention, not a classification" bbox_learn or bbox_remember