Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Stability in a Probabilistic Substrate: How Agents Fight LLM Drift


title: ‘Stability in a Probabilistic Substrate: How Agents Fight LLM Drift’ authors:


Stability in a Probabilistic Substrate: How Agents Fight LLM Drift

The Fundamental Problem

Traditional software fails with an error you can catch and fix. LLM-based systems fail silently — the model just doesn’t do what you asked, and you only notice when the output is wrong.

There is no assert that can fail hard on LLM behavior. No type system can guarantee that the LLM will follow instructions. The entire substrate is probabilistic — every API call carries a risk of hallucination, instruction skipping, or rationalization.

The question is not “how do we eliminate failures?” — it is “how do we reduce failure rates to acceptable levels?”

What’s Actually Reliable vs. What Isn’t

Reliable — Enforced by Code, Not Prompts

These mechanisms are deterministic — they work regardless of LLM behavior:

MechanismHow It WorksAgent
Tool denialPermission.fromConfig({ "*": "deny" }) — runtime blocks tool access, LLM cannot overrideOpenCode compaction
Circuit breakerAfter 3 consecutive failures → feature permanently disabled for sessionClaude Code auto-compact
Token thresholdsCompaction triggers at exact token count: effectiveContextWindow - 13_000Claude Code
History boundariesfilterCompacted() streams from DB and stops at compaction marker — deterministic codeOpenCode
Overflow guardsWon’t compress if it would increase token countQwen Code
PTL retry truncationDrops 20% of groups on prompt-too-long, retries up to 3 timesClaude Code

Tool denial — the strongest guarantee available:

// OpenCode compaction agent — ALL tools denied
compaction: {
  permission: Permission.merge(
    defaults,
    Permission.fromConfig({ "*": "deny" }),  // ← hard guarantee
    user
  ),
}

The compaction agent literally cannot call tools. This is enforced by the runtime permission system, not by asking the LLM nicely.

Circuit breaker — prevents infinite failure loops:

// Claude Code — src/services/compact/autoCompact.ts
MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES = 3

After 3 consecutive auto-compact failures, the circuit breaker trips and auto-compact is permanently disabled for that session. No LLM compliance required — this is a state machine transition.

PTL (Prompt-Too-Long) retry truncation — emergency escape hatch:

When the compaction request itself hits the provider’s context limit:

Unreliable — Depends on LLM Compliance

These techniques reduce failure rates but do not eliminate them:

TechniqueHow It WorksReliability
HARD-GATE tags<HARD-GATE>Do NOT proceed until X</HARD-GATE> — explicit stop signals in XMLSoft — LLM can ignore
Rationalization tablesPre-empt known failure modes: "I'm confident" → Confidence ≠ evidenceReduces drift, doesn’t eliminate
Two-stage reviewImplementer → Spec Reviewer → Code Quality ReviewerCatches violations, doesn’t prevent
Structured outputSummary templates: ## Goal, ## Instructions, etc.Improves compliance, not guaranteed
Skill instructionsSKILL.md files with behavior-shaping instructionsLLM can skip or modify
Commitment devicesTodoWrite checklists that must be completedVisible but not enforced

HARD-GATE tags from Superpowers:

<HARD-GATE>
Do NOT proceed with implementation until you have:
1. A clear spec from the user
2. A test plan
3. A list of files you will modify
</HARD-GATE>

This is a soft signal. The LLM can (and does) ignore it. There is no runtime enforcement — it is just XML-formatted text in the prompt.

Rationalization tables from Superpowers’ verification-before-completion:

ExcuseReality
“Should work now”RUN the verification
“I’m confident”Confidence ≠ evidence
“Just this once”No exceptions
“Linter passed”Linter ≠ compiler
“Agent said success”Verify independently
“I’m tired”Exhaustion ≠ excuse

These counter pre-empt known rationalizations, but they only work if the LLM reads and follows the table. Under context pressure, the table can be truncated.

Two-stage review from Superpowers’ subagent-driven development:

Implementer → Spec Reviewer → Code Quality Reviewer
     ↑                              ↓
     └──── fix issues ←─────────────┘
              (re-review loop)

The spec reviewer is explicitly instructed: “Do not trust the implementer’s report. Read the actual code.” This catches violations after the fact but does not prevent them.

The Adversarial Testing Methodology

Superpowers uses a RED → GREEN → REFACTOR cycle applied to prompts:

  1. RED phase: Run a pressure scenario WITHOUT the skill. Document the agent’s exact rationalizations verbatim.

  2. GREEN phase: Write the skill addressing those specific failures.

  3. REFACTOR phase: Re-run with the skill, verify compliance. Find new rationalizations, add counters.

  4. Repeat until no new loopholes appear.

The testing methodology from writing-skills/testing-skills-with-subagents.md:

“If you didn’t watch an agent fail without the skill, you don’t know if the skill prevents the right failures.”

Pressure scenarios include:

The Failure Modes That Remain

Even with all these techniques, failures still occur:

Failure ModeWhy It HappensMitigation
Skill skippingLLM rationalizes away the 1% rule despite Red Flags tableTwo-stage review catches after the fact
Context overflow truncationSkill content gets cut off mid-ruleToken-efficient design (<150 words for frequent skills)
Model version changesNew model breaks compliance without any test catching itRe-run adversarial tests after model updates
Skill conflictsTwo skills give overlapping/contradictory instructionsLLM picks arbitrarily — no resolution mechanism
Compaction qualitySummary omits critical contextStructured summary template with required sections
Silent hallucinationLLM generates plausible but incorrect summaryNo programmatic assertion available — inherent limitation

The Honest Assessment

From the Superpowers source analysis:

"It’s prompt engineering at scale. The best available techniques (explicit counters, rationalization tables, commitment devices, hard gates, two-stage review) make it more robust than naive prompting — but it’s still fundamentally probabilistic. The system reduces failure rates through empirical testing rather than eliminating them through guarantees."

“The difference from a traditional orchestrator: a traditional system fails with an error you can catch and fix. This system fails silently — the LLM just doesn’t do what you asked, and you only notice when the output is wrong.”

Defense in Depth: The Layered Approach

The most robust agents use multiple layers that catch different failure modes:

Layer 0: HARD CONSTRAINTS (code-enforced)
  ├── Tool permissions (runtime blocks access)
  ├── Circuit breakers (hard failure limits)
  ├── Token thresholds (exact trigger points)
  └── History boundaries (deterministic markers)

Layer 1: PROMPT ENGINEERING (soft, but tested)
  ├── HARD-GATE XML tags
  ├── Rationalization tables
  ├── Structured output templates
  └── Skill instructions with token efficiency

Layer 2: REVIEW GATES (catches after the fact)
  ├── Spec reviewer (verifies against requirements)
  ├── Code quality reviewer (checks SOLID, tests)
  └── verification-before-completion (evidence before claims)

Layer 3: EMERGENCY ESCAPE HATCHES
  ├── PTL retry truncation (drops history on overflow)
  ├── Circuit breaker (disables after repeated failures)
  └── Manual override (/compress, force compact)

No single layer is sufficient. The combination makes failures less likely and more detectable — but never impossible.

Key Takeaway

You cannot programmatically enforce LLM behavior. The best you can do is:

  1. Enforce constraints at the runtime level where possible (tool permissions, token limits, circuit breakers)

  2. Test prompts adversarially to discover failure modes before deployment

  3. Build review gates that catch violations after the fact

  4. Accept the probabilistic nature of the substrate and design accordingly

The agents analyzed in this repository represent the state of the art in managing this problem. They make failures less likely through empirical testing rather than eliminating them through guarantees. This is a fundamental limitation of building on LLM APIs, not a temporary engineering gap.