Skip to content

v0.7: compound failures and synthetic adversarial responses

Available in 0.7.0. Core installation remains standalone. Run the offline resilience example with uv run python examples/advanced/resilience.py.

Cascades

from agentic_chaos import cascade, chaos_call, chaos_session
from agentic_chaos import RateLimitStormFault, TokenTimeoutFault

fault = cascade(first=RateLimitStormFault(burst_count=3),
                then=TokenTimeoutFault(hang_seconds=0), delay_between=0.1)
with chaos_session([fault]) as session:
    # Your retrying application calls chaos_call() on each attempt.
    # The first three attempts raise 429s, the fourth times out, then calls pass.
    pass

A cascade schedules stages across calls, rather than executing the target twice in one call. Rate-limit stages exhaust their positive burst count; other stages fire once. Skipped probability/eligibility leaves the stage pending. Delay is a minimum interval from the previous stage's completion to the next injection; there is no background timer. Child events retain their native fault type and ID and add detail.cascade_id and zero-based detail.cascade_stage.

Use fresh instances per sequential campaign; concurrent calls sharing one cascade are unsupported. Nested cascades are rejected. Use chaos_call or chaos_acall, not streaming-only faults. Circuit-breaker checks apply before stage dispatch, and each child event consumes one injection. Dry-run plans use the cascade name and advance private stage state without sleeping or changing real faults; target calls still execute, as with all Chaos previews.

from agentic_chaos import RateLimitStormFault, find_breaking_point
result = find_breaking_point(my_retrying_app, fault=RateLimitStormFault,
                             param="burst_count", range=(1, 20))
print(result.breaking_point, result.trials)

The inclusive integer search requires deterministic, monotonic recovery: success at lower severity and failure at higher severity. It records (severity, passed) trials, returns None when no tested threshold fails, and takes logarithmically many trials. Every trial has a fresh fault/session; reset application/external state yourself and supply seeds through fault_kwargs. An exception from the application counts as failure; setup and recovered(output) predicate errors propagate. Without a predicate, returning normally means recovery. This is a measured threshold under the stated assumptions, not proof of monotonicity or a production capacity guarantee. Keep target calls bounded with their own timeouts.

Response poisoning and containment evidence

PoisonedResponseFault(strategy=..., payload=..., seed=...) supports:

Strategy Synthetic transformation
confident_wrong Replace text with a confident wrong answer
partial_hallucination Append a fabricated reference
format_violation Replace text with malformed JSON
prompt_injection Replace text with an instruction override
policy_bypass Replace text with an approval-bypass request

Strings and content/text envelopes use the existing degradation adapter. Unsupported envelopes raise TypeError; expose text through your own adapter for provider-specific shapes. Default payloads are deterministic templates, not model-generated fact-aware attacks. seed controls probability selection and is recorded along with the exact strategy and payload in event detail. Payloads may be sensitive if customized; native reports contain them. Embedded instructions are data: Chaos never dispatches them.

The offline example publishes a synthetic policy version, seed, payload, expected protected/bypassed outcomes and observed dispatch list. Its deliberately bypassed negative control must fail containment. This validates only that isolated toy boundary; injection, timeout, task recovery and output quality alone do not prove real Sidecar/MCP containment. Cross-framework containment remains future work.

Explicit cost estimates

agentic-chaos estimate my_app.py --fault rate_limit_storm --retries 3 \
  --calls 1 --cost-per-call-usd 0.04

Emits JSON: baseline $0.04, extra $0.12, total $0.16. The scenario path is only a label: no file is read, imported or executed. The caller supplies price and retry assumptions; every retry is modeled as a fully billed call. Provider 429s may cost nothing, so this is a budget scenario, not inferred billing or a forecast.

Evidence and ecosystem compatibility

No new shared schema is introduced. Existing ChaosReport, calls, contracts and AIOS Run exports continue to work. Child cascade and poisoning provenance travels in event detail and exported native evidence. Lens consumes native chaos events; Tower counts native outcomes independently of deployment health. MCP discovers poisoned_response through the existing registry and can run a trusted local script using these APIs; hosted HTTP still rejects script execution. Cascades and search are Python orchestration APIs, not newly exposed MCP tools. Sidecar needs 0.8.3 for an optional-extra range admitting Chaos 0.7. Evals retains ownership of scores and release gates. AIOS mappings document these producer-local extensions; no normative schema change or conformance promotion is implied.