Skip to content

RationGuard

RationGuard

License

Detect and rebut rationalization patterns in AI agent output.

AI agents make excuses. "Standing by for instructions." "Too complex to fix." "I'll handle it next pass." These sound reasonable but mean the agent stopped working.

rationguard catches these patterns and provides rebuttals — in real-time via pluk, injected into the agent's system prompt (prevention), or checked against agent output after each response (detection).


Install

npm install -g @hivecommons/rationguard @hivecommons/pluk

Releases

Maintainers publish releases by pushing a v* tag. The tag workflow runs the test gate, then publishes @hivecommons/rationguard to npm with provenance using the repository NPM_TOKEN Actions secret.


Quick Start

One command — creates a tmux session, starts the AI CLI, wires pluk event capture, opens a terminal window for you to interact, and runs rationguard in this terminal to detect rationalizations:

rationguard attach my-agent --cli=claude --rebuttal=send --dangerous

What happens:

  1. A tmux session named my-agent is created
  2. claude --dangerously-skip-permissions starts inside it (the --dangerous flag maps to the right permission-skip flag per CLI)
  3. pluk captures all terminal output via tmux pipe-pane
  4. A new terminal window opens attached to the tmux session — this is where you interact with the agent
  5. rationguard watches the pluk event stream in your original terminal, printing detections
  6. When a rationalization is detected, the rebuttal is sent directly into the tmux session — the agent receives it as its next message
# Start goose with auto-approve
rationguard attach my-agent --cli=goose --dir=/path/to/project --dangerous

# Start copilot with rebuttals
rationguard attach my-agent --cli=copilot --rebuttal=send

# Pass extra CLI arguments
rationguard attach my-agent --cli=claude --cli-args="--model opus" --dangerous

# Don't open a terminal window, and print debug output for each step
rationguard attach my-agent --cli=claude --no-open --verbose

# Launch a custom executable instead of the default resolved from --cli
rationguard attach my-agent --command=/usr/local/bin/my-claude-wrapper

# Omit raw terminal bytes from the pluk event log
rationguard attach my-agent --cli=claude --no-raw

attach also accepts:

  • --command=<path> — override the CLI executable/command to launch, bypassing the default resolved from --cli.
  • --no-raw — don't include raw terminal bytes in the pluk event log (omits --include-raw from the internal pluk watch invocation).

--dangerous flag

Maps to the correct permission-skip flag per CLI:

CLI Flag applied
claude --dangerously-skip-permissions
codex --full-auto
goose --non-interactive

For other CLIs (copilot, gemini, or any --command wrapper) --dangerous is a no-op: no flag is added, so pass the CLI's own auto-approve option via --cli-args. If the tmux session already exists, attach does not restart the CLI; it prints a warning with the command to restart it with the flag.

Already have sessions running?

# See what's running
rationguard sessions

SESSION          CLI       STATE     TMUX  LAST ACTIVITY EVENTS
scanner          claude    working   ●     2s ago        1204
helper           claude    idle      ●     45s ago       892

# Watch one
rationguard watch scanner --rebuttal=send

sessions and watch look for pluk logs in /var/run/pluk by default. Point them elsewhere with --run-dir=/path/to/pluk/run or by setting the PLUK_RUN_DIR environment variable (the flag takes precedence if both are set). Add --verbose to watch to print debug output.

Add --diagnostics (or --diagnostics=<seconds>) to watch for opt-in, local-only health visibility on a long-running session: a bounded JSON summary (flush/match/rebuttal counts) is printed to stderr on a fixed interval (default 60s) and once more when the watcher stops. It never includes the session name, raw output, or excuse text — only fixed counter categories — and nothing is sent anywhere.

When rationguard detects an excuse, it prints the match and sends the rebuttal directly to the agent:

⚠ 100% False Completion — "tests are all passing"
  Rebuttal: Did you actually run the checks THIS cycle? Paste the output.
  → Sent rebuttal to scanner

Rebuttals are deduplicated at two levels:

  1. Per-pattern cooldown (30s) — the same excuse pattern won't trigger another rebuttal within 30 seconds, even across multiple output flushes
  2. Per-text dedup — if multiple patterns match with identical rebuttal text in the same detection cycle, only one is sent

After sending any rebuttal, rationguard enters a 60-second quiet period — all detections are suppressed. This prevents feedback loops where the agent's response to a rebuttal (e.g., "all checks pass, everything looks good") triggers new detections.


Three Modes

Mode 1: Real-time detection via pluk (live)

Connect to a pluk event stream and check agent output as it happens.

# Watch a session (subscribe to pluk JSONL log)
rationguard watch my-agent --cli=claude

# Auto-rebut: send rebuttals back to the agent
rationguard watch my-agent --rebuttal=send

# JSON output for dashboards
rationguard watch my-agent --json

Mode 2: Post-response detection

Pipe agent output through rationguard check after each response.

# Check text directly
rationguard check "Standing by for further instructions. Nothing to do."

# Check a file
rationguard check --file=agent-output.txt

# Pipe input
echo "$AGENT_RESPONSE" | rationguard check

# JSON output
rationguard check --json "I will defer this to next pass"
✗ Found 2 rationalization pattern(s):

  45% False Completion
     Pattern:  "no work found"
     Matched:  "nothing to do"
     Rebuttal: Verify by checking the issue queue, task list, or backlog.

  45% False Completion
     Pattern:  "standing by / awaiting instructions"
     Matched:  "standing by"
     Rebuttal: You are not a receptionist. Check your task queue,
               scan for issues, or find work proactively.

Mode 3: System prompt prevention

Generate a defense table and inject it into your agent's system prompt. The agent self-polices against known excuses.

rationguard prompt

# Output as a YAML block instead of markdown
rationguard prompt --format=yaml

Outputs a markdown table (or YAML block with --format=yaml) you paste into CLAUDE.md, system prompt, or instructions file.


The 5 Excuse Categories

Category What it sounds like What's really happening
False Completion "All done", "steady state", "checks are green" Agent stopped checking without verifying
Complexity Dodge "Too complex", "needs approval", "not actionable" Agent avoiding hard work
Deferral "Next pass", "waiting for CI", "filed an issue" Agent procrastinating
Lane Confusion "Not my job", "another agent handles that" Agent deflecting responsibility
Partial Credit "Made progress", "partially addresses it", "probably fine" Agent claiming done when it isn't

Auto-Learning

rationguard learns new excuse patterns from your agents over time.

# Record a new excuse
rationguard add "synergizing the deliverables" \
  --category=false-completion \
  --rebuttal="That is not an action. State what you actually did."

# After 3 sightings, auto-promotes to a custom excuse
rationguard sightings

Custom excuses live in .rationguard/custom-excuses.json (per-project) or ~/.rationguard/custom-excuses.json (global).


Programmatic API

import { check, Watcher, generatePromptBlock } from '@hivecommons/rationguard';

// Check text
const result = check('Standing by for instructions.');
if (!result.clean) {
  for (const match of result.matches) {
    console.log(`${match.excuse.category}: ${match.excuse.rebuttal}`);
  }
}

// Real-time watching via pluk
const watcher = new Watcher({
  session: 'my-agent',
  mode: 'subscribe',
  rebuttal: 'send',
  onDetection(d) {
    console.log('Detected:', d.matches.length, 'rationalizations');
  },
});
await watcher.start();

// Generate prompt block for system prompt injection
const block = generatePromptBlock();

Cheat Sheet

Command What it does
rationguard attach <session> --dangerous Start agent + pluk + rationguard + terminal (skip permissions)
rationguard sessions List active pluk-monitored agent sessions
rationguard watch <session> Real-time detection via pluk
rationguard watch <session> --rebuttal=send Detect and auto-rebut (30s cooldown + 60s quiet period)
rationguard check <text> Check for excuse patterns
rationguard check --file=<path> Check file contents
rationguard prompt Generate defense table for system prompts
rationguard prompt --format=yaml Generate defense table as YAML instead of markdown
rationguard sessions --run-dir=<path> / watch --run-dir=<path> Point at pluk logs elsewhere (or set PLUK_RUN_DIR)
rationguard attach <session> --no-open --verbose Skip opening a terminal window and print debug output
rationguard add "excuse" --category=... Record an excuse sighting
rationguard list Show all known excuses
rationguard sightings Show recorded sightings and counts

Runbooks

Operational procedures live in runbooks/:


Works With


Origin

rationguard was extracted from the Hive multi-agent orchestration system. Hive runs 5+ AI agents continuously and discovered that agents rationalize inaction with predictable patterns. The original excuse-rebuttal tables (49 excuses across 5 agents) were built empirically — every excuse was observed in production before it was catalogued.

The 15 default excuses in rationguard are the universal patterns that apply to any AI agent, not just Hive's.


License

Apache-2.0

About

Detect and rebut rationalization patterns in AI agent output

Resources

Code of conduct

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages