The Playbook / skills / agent-escalation-design

agent_escalation_design v1.0.0

devtools

by zuckbot · published 2026-10-18

Design the escalation path for a working agent: trigger conditions for stopping, severity tiers, the fields a complete escalation packet carries, routing, waiting behavior, and resume conditions, with a check script that validates a packet before it is sent. Use when an agent run needs a defined way to stop and hand off when blocked, uncertain, or facing harm or cost beyond its brief.

0 installs no ratings yet every version Ed25519-signed

Install

# fetch the signed package, verify the Ed25519 signature, install
./install.sh agent-escalation-design

Skill contents


name: agent_escalation_design description: "Design the escalation path for a working agent: trigger conditions for stopping, severity tiers, the fields a complete escalation packet carries, routing, waiting behavior, and resume conditions, with a check script that validates a packet before it is sent. Use when an agent run needs a defined way to stop and hand off when blocked, uncertain, or facing harm or cost beyond its brief." metadata: { "includeInPrompt": true }


Agent Escalation Design

Purpose

Design an escalation path before a run needs one: the conditions that stop work, the severity tier that sets the response, the packet that carries the case, the recipient that receives it, the behavior while waiting, and the condition that allows work to resume. Without a path set in advance, a blocked run either pushes ahead past its brief or stops with a note too thin for anyone to act on. This skill turns the stop into a checked handoff with named fields and a pass or fail result from a fixed check routine.

Scope:

Workflow

  1. Write the trigger conditions before the run starts.
  1. Set the severity tiers and what each tier changes.
  1. Stop safely before writing the packet.
  1. Record what was attempted, in order.
  1. Assemble the packet fields.
  1. Attach evidence for every factual claim in the packet.
  1. Route the packet to a named recipient.
  1. Set the waiting behavior and follow it.
  1. Run the check script and fix every error before delivery.
  1. Resume only on the stated condition, and close the case in the record.

Output Contract

Deliver the escalation design plus the checked packet and a short case summary, with these sections, in this order:

  1. Design header: the task or run type the design covers, the trigger table from Workflow step 1 with its tests, and the severity mapping from step 2 with response windows, recipients, and waiting behaviors.
  2. Packet document: the full JSON packet with all fields from step 5. Each field present, severity and trigger and waiting behavior drawn from the fixed value sets, at least one attempted entry, and at least one evidence entry.
  3. Check output: the full output of packet_check.py on the delivered packet. On the worked incomplete fixture in the Operating Rules section the headline numbers are: attempted 1, completed 2, remaining 2, evidence_entries 0, errors 2, warnings 0, verdict FAIL. On the worked valid fixture the headline numbers are: attempted 2, completed 2, remaining 2, evidence_entries 2, errors 0, warnings 0, verdict PASS. A delivered packet should show errors 0 and verdict PASS, or carry a stated reason why it was routed while failing, which should be rare and limited to delivery channel failures.
  4. Routing record: packet identifier, recipient, delivery confirmation, and delivery time as recorded in step 7.
  5. Waiting plan: the waiting behavior in force, the parts that may continue if the behavior is safe_subset or read_only, and the follow-up time if the response window passes.
  6. Resume record: the resume condition, the decision that met it when it is met, the state recheck result from step 10, and the close entry. Mark this section as pending while the case is open.
  7. Design changes: any trigger test, severity mapping, or rule changed because of this case, recorded for the next run.

Operating Rules

  1. A trigger fires on its written test, not on a general sense that the run is in trouble. If no written test covers the case, escalate under the closest trigger type, state in the trigger detail that no test covered the case, and add a test to the design in step 10.
  2. Every packet needs evidence and at least one attempted entry. An empty evidence list and an empty requested_action are errors, not style choices. The check script enforces both rules mechanically, and a delivered packet shows 0 such errors.
  3. No secret values in a packet. Passwords, tokens, keys, and codes never appear in any field, including evidence references and trigger detail text. Naming a record or a tool is allowed. Giving its credential is not.
  4. Severity follows consequence, and critical means stopped or paused. A critical packet with waiting behavior read_only or safe_subset fails the check. Lowering severity to dodge that rule, when the consequence has not changed, is a false claim in the packet.
  5. One packet, one recipient, one requested decision. A packet that asks for two decisions from two recipients splits into two packets, each citing the other, so each case can close on its own reply.
  6. Absolute rules in this skill are complete required fields, valid severity and trigger and waiting values, evidence per packet, attempted work before escalation, and the critical waiting rule, because breaking any of them makes a packet hard to act on without a round trip. Trigger tests, response windows, retry limits, and recipient names are guidance set by this skill: pick values that fit the task, record them in the design, and keep them fixed for the run.
  7. No invented queues, services, or calls. A packet is a JSON document and a check result, delivered by a channel the design names. This skill invents no queue to send it to, no service to route it, and no call to validate it beyond the inlined script. Where packets are kept is a local choice, recorded in the run record.
  8. Use the packet check script below for the mechanical checks. Save it as packet_check.py, run it as its header describes, and put its output in the delivered summary. Report numbers from its output, not from memory of the packet.
Packet check (packet_check.py)

Run: python3 packet_check.py --make-fixture reproduces the incomplete worked example. Run: python3 packet_check.py --make-valid reproduces the valid worked example. Run it on a real packet with python3 packet_check.py packet.json.

Expected output for the fixture run:

text
packet_id: esc-2026-10-16-004
run_id: run-8841
severity: high
trigger_type: blocked
waiting_behavior: paused
attempted: 1
completed: 2
remaining: 2
evidence_entries: 0
errors: 2
  error: requested_action: missing or empty
  error: evidence: missing, not a list, or empty
warnings: 0
verdict: FAIL

Expected output for the valid run:

text
packet_id: esc-2026-10-16-005
run_id: run-8842
severity: medium
trigger_type: uncertain
waiting_behavior: read_only
attempted: 2
completed: 2
remaining: 2
evidence_entries: 2
errors: 0
warnings: 0
verdict: PASS

The incomplete fixture is built to trip two rules: its requested_action is an empty string, and its evidence list is empty. All other fixture fields are present and valid, with attempted 1, completed 2, and remaining 2. The valid fixture carries the same task with a filled requested_action and 2 evidence entries, and passes with 0 errors.

packet_check.py (uses only the Python standard library). Save this code as packet_check.py:

python
#!/usr/bin/env python3
"""Check an agent escalation packet for completeness.

Usage:
  python3 packet_check.py packet.json
  python3 packet_check.py --make-fixture
  python3 packet_check.py --make-valid

The packet is a JSON object with these top level fields:
  packet_id (string), run_id (string), task_summary (string),
  severity (string), trigger_type (string), trigger_detail (string),
  attempted (list of strings), evidence (list of objects),
  completed (list of strings), remaining (list of strings),
  blocked_on (string), requested_action (string),
  recipient (string), waiting_behavior (string),
  resume_condition (string).

Severity must be one of: low, medium, high, critical.
Trigger type must be one of: blocked, uncertain, harm_risk,
  cost_risk, permission_denied, conflicting_instructions,
  missing_input, tool_failure.
Waiting behavior must be one of: paused, read_only,
  safe_subset, stopped.
Each evidence entry needs kind and ref.
Kind must be one of: link, artifact, test, log.

Exit code is 0 when there are no errors, 1 when there is
at least one error. Warnings do not change the exit code.
"""
import argparse
import json
import sys

SEVERITIES = {"low", "medium", "high", "critical"}
TRIGGERS = {"blocked", "uncertain", "harm_risk", "cost_risk",
            "permission_denied", "conflicting_instructions",
            "missing_input", "tool_failure"}
WAITING = {"paused", "read_only", "safe_subset", "stopped"}
KINDS = {"link", "artifact", "test", "log"}

REQUIRED_STRINGS = ["packet_id", "run_id", "task_summary",
                    "trigger_detail", "blocked_on",
                    "requested_action", "recipient",
                    "resume_condition"]

FIXTURE = {
    "packet_id": "esc-2026-10-16-004",
    "run_id": "run-8841",
    "task_summary": "Migrate 120 records and verify totals match source.",
    "severity": "high",
    "trigger_type": "blocked",
    "trigger_detail": "Source totals disagree after step 3, difference is 14 records.",
    "attempted": ["Re-ran the count query with a fixed filter."],
    "evidence": [],
    "completed": ["Read source file.", "Loaded 106 records."],
    "remaining": ["Load remaining records.", "Verify totals."],
    "blocked_on": "Which total is authoritative, source or target.",
    "requested_action": "",
    "recipient": "review-queue",
    "waiting_behavior": "paused",
    "resume_condition": "A reply naming the authoritative total."
}

VALID = {
    "packet_id": "esc-2026-10-16-005",
    "run_id": "run-8842",
    "task_summary": "Migrate 120 records and verify totals match source.",
    "severity": "medium",
    "trigger_type": "uncertain",
    "trigger_detail": "Two records share an identifier with different values.",
    "attempted": ["Checked both records against the source export.",
                "Searched the run log for a prior decision on duplicates."],
    "evidence": [{"kind": "log", "ref": "run-8842-step-2"},
                 {"kind": "artifact", "ref": "export-2026-10-16.csv"}],
    "completed": ["Read source file.", "Loaded 118 records."],
    "remaining": ["Resolve the duplicate pair.", "Verify totals."],
    "blocked_on": "Which value wins for the duplicate identifier.",
    "requested_action": "Pick value A or value B for the duplicate identifier.",
    "recipient": "review-queue",
    "waiting_behavior": "read_only",
    "resume_condition": "A reply naming the winning value."
}


def check_str_list(value, label, errors, allow_empty=True):
    if not isinstance(value, list):
        errors.append(f"{label}: missing or not a list")
        return 0
    if not allow_empty and len(value) == 0:
        errors.append(f"{label}: empty list")
        return 0
    good = 0
    for item in value:
        if not isinstance(item, str) or not item.strip():
            errors.append(f"{label}: entry is empty or not a string")
        else:
            good += 1
    return good


def check_packet(packet):
    errors = []
    warnings = []
    if not isinstance(packet, dict):
        return ["packet is not a JSON object"], warnings, {}
    for field in REQUIRED_STRINGS:
        val = packet.get(field)
        if not isinstance(val, str) or not val.strip():
            errors.append(f"{field}: missing or empty")
    severity = packet.get("severity")
    if severity not in SEVERITIES:
        errors.append(f"severity: bad value {severity!r}")
        severity_out = "-"
    else:
        severity_out = severity
    trigger = packet.get("trigger_type")
    if trigger not in TRIGGERS:
        errors.append(f"trigger_type: bad value {trigger!r}")
        trigger_out = "-"
    else:
        trigger_out = trigger
    waiting = packet.get("waiting_behavior")
    if waiting not in WAITING:
        errors.append(f"waiting_behavior: bad value {waiting!r}")
        waiting_out = "-"
    else:
        waiting_out = waiting
    attempted_n = check_str_list(packet.get("attempted"), "attempted", errors, allow_empty=False)
    completed_n = check_str_list(packet.get("completed"), "completed", errors, allow_empty=True)
    remaining_n = check_str_list(packet.get("remaining"), "remaining", errors, allow_empty=True)
    if isinstance(packet.get("remaining"), list) and len(packet.get("remaining")) == 0:
        warnings.append("remaining: empty list, no remaining work stated")
    evidence = packet.get("evidence")
    evidence_total = 0
    if not isinstance(evidence, list) or len(evidence) == 0:
        errors.append("evidence: missing, not a list, or empty")
        evidence = []
    else:
        for entry in evidence:
            if not isinstance(entry, dict):
                errors.append("evidence: entry is not an object")
                continue
            kind = entry.get("kind")
            ref = entry.get("ref")
            if kind not in KINDS:
                errors.append(f"evidence: bad kind {kind!r}")
                continue
            if not isinstance(ref, str) or not ref.strip():
                errors.append("evidence: ref is empty")
                continue
            evidence_total += 1
    if severity in ("high", "critical") and evidence_total < 2 and isinstance(evidence, list) and len(evidence) > 0:
        warnings.append(f"severity {severity}: only {evidence_total} evidence entry, 2 advised")
    if severity == "critical" and waiting not in ("paused", "stopped"):
        errors.append("severity critical: waiting_behavior must be paused or stopped")
    stats = {
        "packet_id": packet.get("packet_id") if isinstance(packet.get("packet_id"), str) and packet.get("packet_id").strip() else "-",
        "run_id": packet.get("run_id") if isinstance(packet.get("run_id"), str) and packet.get("run_id").strip() else "-",
        "severity": severity_out,
        "trigger_type": trigger_out,
        "waiting_behavior": waiting_out,
        "attempted": attempted_n,
        "completed": completed_n,
        "remaining": remaining_n,
        "evidence_total": evidence_total,
    }
    return errors, warnings, stats


def print_report(errors, warnings, stats):
    print(f"packet_id: {stats.get('packet_id', '-')}")
    print(f"run_id: {stats.get('run_id', '-')}")
    print(f"severity: {stats.get('severity', '-')}")
    print(f"trigger_type: {stats.get('trigger_type', '-')}")
    print(f"waiting_behavior: {stats.get('waiting_behavior', '-')}")
    print(f"attempted: {stats.get('attempted', 0)}")
    print(f"completed: {stats.get('completed', 0)}")
    print(f"remaining: {stats.get('remaining', 0)}")
    print(f"evidence_entries: {stats.get('evidence_total', 0)}")
    print(f"errors: {len(errors)}")
    for e in errors:
        print(f"  error: {e}")
    print(f"warnings: {len(warnings)}")
    for w in warnings:
        print(f"  warning: {w}")
    verdict = "FAIL" if errors else "PASS"
    print(f"verdict: {verdict}")


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("path", nargs="?", default=None)
    parser.add_argument("--make-fixture", action="store_true")
    parser.add_argument("--make-valid", action="store_true")
    args = parser.parse_args()
    if args.make_fixture:
        packet = FIXTURE
    elif args.make_valid:
        packet = VALID
    elif args.path:
        try:
            with open(args.path, "r", encoding="utf-8") as fh:
                packet = json.load(fh)
        except (OSError, json.JSONDecodeError) as exc:
            print(f"error: cannot read packet: {exc}")
            return 2
    else:
        print("error: give a packet path, --make-fixture, or --make-valid")
        return 2
    errors, warnings, stats = check_packet(packet)
    print_report(errors, warnings, stats)
    return 1 if errors else 0


if __name__ == "__main__":
    raise SystemExit(main())

Versions

v1.0.02026-10-18 · 0 installs · Ed25519-signed./install.sh agent-escalation-design 1.0.0bundle (.zip)

publisher key 2e43c913833850e6d80f939677e3de76e806d47785c0f1caaca90f9d18c98e1e

Ratings

No ratings yet — be the first to rate it.