Skip to content

LightspeedDMS/claude-pace-maker

Repository files navigation

Claude Pace Maker

Intelligent credit consumption throttling and code quality enforcement for Claude Code.

Features

  • Credit Throttling: Adaptive pacing for 5-hour and 7-day usage windows
  • Stale Data Detection: Resilient pacing calculations when usage data is outdated
  • Model Preference: Nudges Claude to use specific model for subagents (quota balancing)
  • Intent Validation: Requires intent declaration before code modifications
  • TDD Enforcement: Core code paths require test declarations
  • Clean Code Checks: Blocks security vulnerabilities, anti-patterns, and logic bugs
  • Folder Exclusion: Exclude paths from TDD/Clean Code enforcement
  • Session Lifecycle: Prevents premature session endings via AI validation
  • Langfuse Telemetry: Optional tracing integration with Langfuse for session/trace/span tracking
  • Blockage Telemetry: Track and report validation blocks via pace-maker blockage-stats
  • Daily Log Rotation: Automatic log rotation (one file per day, 15 days retention)
  • Enhanced Status Display: Shows versions, Langfuse connectivity, and error counts
  • Prompt Intelligence (Intel): Optional per-prompt metadata (frustration, specificity, task type, quality, iteration) attached to Langfuse traces for dashboard analytics
  • Secrets Management: Sanitizes sensitive data from Langfuse trace outputs
  • Langfuse Auto-Provisioning: Automatic API key provisioning with configurable URL
  • Activity Indicators: Real-time hook activity tracking (IV, TD, CC, ST, CX, PA, PL, LF, SS, SM, SE, SA, UP) with color-coded status
  • Global API Poll Coordination: SQLite singleton prevents redundant API polling across concurrent sessions
  • Companion Tool Installer: pace-maker install claude-usage-monitor installs the usage monitoring dashboard

Installation

Option 1: Claude Code Plugin (recommended)

Install pace-maker as a Claude Code plugin via a marketplace for automatic hook registration and updates.

Prerequisites: A marketplace repo that includes pace-maker (see Setting Up a Marketplace below).

# One-time: add your organization's marketplace
claude plugin marketplace add https://github.com/YourOrg/claude-plugins.git

# Install pace-maker
claude plugin install claude-pace-maker@claude-plugins

That's it. The first Claude Code session after installation automatically bootstraps:

  • ~/.claude-pace-maker/config.json with production defaults
  • ~/.claude-pace-maker/source_code_extensions.json for validation
  • ~/.local/bin/pace-maker CLI symlink (if ~/.local/bin is in PATH)

No install.sh or manual settings.json editing required.

Option 2: pipx

pipx install git+https://github.com/LightspeedDMS/claude-pace-maker.git
claude-pace-maker

Option 3: From source

git clone https://github.com/LightspeedDMS/claude-pace-maker.git
cd claude-pace-maker
./install.sh

Setting Up a Marketplace

To distribute pace-maker across your organization, add it as a submodule in a Claude Code marketplace repository.

1. Create a marketplace repo

mkdir claude-plugins && cd claude-plugins
git init
mkdir -p .claude-plugin

2. Add the marketplace manifest

Create .claude-plugin/marketplace.json:

{
  "$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
  "name": "your-org-plugins",
  "description": "Internal Claude Code plugins for YourOrg",
  "plugins": [
    {
      "name": "claude-pace-maker",
      "description": "Usage observability, adaptive pacing, and Langfuse telemetry for Claude Code",
      "category": "productivity",
      "source": {
        "source": "url",
        "url": "https://github.com/LightspeedDMS/claude-pace-maker.git"
      },
      "homepage": "https://github.com/LightspeedDMS/claude-pace-maker"
    }
  ]
}

3. (Optional) Add as a git submodule

If you prefer vendoring the plugin source instead of referencing the URL:

git submodule add https://github.com/LightspeedDMS/claude-pace-maker.git plugins/claude-pace-maker

Then update marketplace.json to use a local source:

{
  "name": "claude-pace-maker",
  "source": "./plugins/claude-pace-maker"
}

4. Push and distribute

git add -A && git commit -m "Add claude-pace-maker plugin"
git remote add origin https://github.com/YourOrg/claude-plugins.git
git push -u origin main

Users install with:

claude plugin marketplace add https://github.com/YourOrg/claude-plugins.git
claude plugin install claude-pace-maker@your-org-plugins

Plugin Directory Structure

When installed as a plugin, pace-maker uses this structure:

claude-pace-maker/                    (repo root = plugin root)
  .claude-plugin/
    plugin.json                       (metadata: name, description, author, version)
  hooks/
    hooks.json                        (7 hook definitions using ${CLAUDE_PLUGIN_ROOT})
  scripts/
    hook.sh                           (unified entry point, lazy-init, Python dispatch)
    pace-maker                        (CLI wrapper script, symlink target)
  config/
    config.defaults.json              (default config.json template)
    source_code_extensions.json       (file extensions for validation)
  src/pacemaker/                      (Python source code)

The ${CLAUDE_PLUGIN_ROOT} variable is set by Claude Code at runtime, pointing to the plugin's installation directory. All hook commands reference this variable for portability.


Migrating from Legacy Installation

If you previously installed pace-maker via install.sh and want to switch to plugin mode:

# 1. Install the plugin via marketplace (see above)

# 2. Run the migration script to remove legacy hooks from settings.json
cd /path/to/claude-pace-maker
bash migrate-to-plugin.sh

# 3. Verify: settings.json should no longer contain pace-maker hook entries
cat ~/.claude/settings.json | jq '.hooks'

The migration script:

  • Removes all pace-maker hook entries from ~/.claude/settings.json
  • Deletes legacy hook scripts from ~/.claude/hooks/ (pre-tool-use.sh, post-tool-use.sh, etc.)
  • Removes the ~/.claude/hooks/pacemaker/ directory
  • Preserves all non-pace-maker hooks intact

Your ~/.claude-pace-maker/config.json and database are preserved β€” no reconfiguration needed.

CLI Commands

pace-maker status                  # Show current status
pace-maker on|off                  # Master switch - enable/disable ALL hooks
pace-maker weekly-limit on|off    # Enable/disable 7-day limit
pace-maker 5-hour-limit on|off    # Enable/disable 5-hour limit
pace-maker tempo on|off           # Enable/disable session lifecycle (global)
pace-maker tempo session on|off   # Override tempo for current session
pace-maker reminder on|off        # Enable/disable subagent reminder
pace-maker intent-validation on|off  # Enable/disable pre-tool validation
pace-maker tdd on|off             # Enable/disable TDD enforcement
pace-maker clean-code list        # List all clean code rules
pace-maker clean-code add NAME DESCRIPTION  # Add custom clean code rule
pace-maker clean-code remove NAME # Remove clean code rule
pace-maker core-paths list        # List core code paths
pace-maker core-paths add PATH    # Add core code path
pace-maker core-paths remove PATH # Remove core code path
pace-maker prefer-model opus|sonnet|haiku|auto  # Set model preference for subagents
pace-maker langfuse on|off|status|configure  # Langfuse telemetry controls
pace-maker loglevel 0-4           # Set log level (0=OFF, 1=ERROR, 2=WARNING, 3=INFO, 4=DEBUG)
pace-maker excluded-paths list         # List excluded paths (skip TDD/clean code)
pace-maker excluded-paths add PATH     # Add excluded path
pace-maker excluded-paths remove PATH  # Remove excluded path
pace-maker secrets add VALUE           # Declare a secret value for masking
pace-maker secrets addfile PATH        # Declare a file as containing secrets
pace-maker secrets list                # List declared secrets
pace-maker blockage-stats              # Show validation blockage statistics
pace-maker install claude-usage-monitor  # Install claude-usage-monitor companion tool
pace-maker version                # Show version
pace-maker help                   # Show help

Intent Validation System

Two-stage AI validation system that ensures code quality and intent transparency.

Two-Stage Validation Architecture

Stage 1: Fast Declaration Check (~2-4 seconds)

  • Model: Claude Sonnet 4.5
  • Validates intent declaration exists with all 3 required components
  • Checks TDD declarations for core code paths
  • Returns: YES, NO, or NO_TDD

Stage 2: Comprehensive Code Review (~10-15 seconds)

  • Model: Claude Opus 4.5 (falls back to Sonnet on rate limits)
  • Validates code matches declared intent exactly
  • Checks for clean code violations (security, anti-patterns, bugs)
  • Detects scope creep and unauthorized changes
  • Returns: APPROVED or detailed violation feedback

Required Declaration Format

Before any code edit, declare in the SAME message as the Write/Edit tool:

  1. FILE: Which file is being modified
  2. CHANGES: What specific changes are being made
  3. GOAL: Why the changes are being made

Example:

I will modify src/auth.py to add a validate_password() function
that checks password strength, to improve security.

[then use Write/Edit tool in same message]

TDD Enforcement for Core Code

Files in core paths (src/, lib/, core/, source/, libraries/, kernel/) require:

Option A - Declare test coverage:

I will modify src/auth.py to add validate_password().
Test coverage: tests/test_auth.py - test_validate_password_rejects_weak()

Option B - Quote user permission to skip TDD:

I will modify src/auth.py to add validate_password().
User permission to skip TDD: User said "skip tests for this" in message 3.

The quoted permission must exist in the last 5 messages. Fabricated quotes are rejected.

Clean Code Violations Blocked

Violation Description
Hardcoded secrets API keys, passwords, tokens in code
SQL injection String concatenation in queries
Bare except except: without specific exception type
Swallowed exceptions except: pass without logging
Magic numbers Unexplained numeric literals
Mutable defaults def func(x=[]) anti-pattern
Commented-out code Dead code without explanation
Deep nesting 6+ levels of conditionals
Large methods Functions exceeding ~50 lines
Undeclared fallbacks Hidden default behaviors
Logic bugs Off-by-one errors, boundary issues
Over-mocked tests Tests that mock the code under test
Scope creep Code changes not declared in intent
Missing functionality Declared features not implemented
Unauthorized deletions Removing code not mentioned in intent

Configuration Files

All configuration is externalized for easy customization:

  • Config: ~/.claude-pace-maker/config.json - Main configuration (enable/disable features, log level)
  • Extensions: ~/.claude-pace-maker/source_code_extensions.json - Source file extensions to validate
  • Clean Code Rules: ~/.claude-pace-maker/clean_code_rules.yaml - Customizable code quality rules
  • Core Paths: ~/.claude-pace-maker/core_paths.yaml - Directories requiring TDD enforcement
  • Prompts: ~/.claude/hooks/pacemaker/prompts/ - Validation prompt templates

Use CLI commands to manage rules and paths without editing YAML directly.

Modifying Validation Prompts Without Loops

⚠️ CRITICAL: When modifying intent validator prompts, disable validation temporarily to prevent infinite loops.

Problem: Editing validation prompts while validation is enabled creates recursive validation loops.

Solution: Temporarily bypass validation during prompt development.

# BEFORE modifying prompts in src/pacemaker/prompts/
pace-maker intent-validation off

# Now safe to edit validation prompt files:
# - src/pacemaker/prompts/pre_tool_use/pre_tool_validator_prompt.md
# - src/pacemaker/prompts/stop/stop_hook_validator_prompt.md
# - src/pacemaker/prompts/common/intent_declaration_prompt.md

# Make your prompt changes...

# AFTER completing prompt modifications
pace-maker intent-validation on

Workflow:

  1. Disable validation: pace-maker intent-validation off
  2. Edit prompt files in src/pacemaker/prompts/
  3. Deploy changes: ./install.sh (copies prompts to ~/.claude/hooks/)
  4. Re-enable validation: pace-maker intent-validation on
  5. Test the updated prompts

Note: This applies to ALL prompt file modifications that affect validation logic. Prompts for other hooks (session_start, user_commands) don't require validation bypass.

Credit Throttling

How It Works

  1. Monitors credit usage via Claude API
  2. Calculates target pace based on time remaining in window
  3. Applies adaptive delays when over budget
  4. Weekend-aware: excludes Sat/Sun from calculations

Algorithm

  • Safety Buffer: Targets 95% of allowance (5% headroom)
  • 12-Hour Preload: First 12 weekday hours get 10% allowance
  • Zero Tolerance: Throttles immediately when over budget
  • Adaptive Delay: Calculates exact delay to reach 95% by window end

Resilient Fallback Mode

When Anthropic's usage API becomes unavailable (HTTP 429 rate limiting), pace-maker automatically switches to fallback mode β€” synthesizing utilization estimates from accumulated token costs so that pacing continues uninterrupted.

How It Works

Normal Mode                        Fallback Mode
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    429 error     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Real API     β”‚ ──────────────> β”‚  Synthetic estimates  β”‚
β”‚  utilization  β”‚                 β”‚  from token costs     β”‚
β”‚  from Claude  β”‚ <────────────── β”‚  + baseline snapshot  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    API recovers  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  1. Entry: When the API returns 429 or exponential backoff is active, pace-maker captures the last known utilization as a baseline and begins tracking token costs per session
  2. During fallback: Each hook invocation accumulates cost (input, output, cache tokens Γ— model pricing). Synthetic utilization = baseline + accumulated_cost Γ— coefficient. Reset windows are projected forward using the last known reset timestamp
  3. Window rollovers: When a 5-hour or 7-day window rolls over during fallback, the accumulated cost offset is recorded so only post-rollover costs count toward the new window
  4. Exit: When the API returns a successful response, pace-maker compares its synthetic prediction against the real value, calibrates the coefficient, and returns to normal mode

Coefficient Calibration

The mapping from dollar cost to utilization percentage depends on your subscription tier. Pace-maker ships with default coefficients for 5x and 20x tiers, but automatically learns better values over time:

  • When the API recovers after a fallback period, the system computes: ratio = real_utilization / synthetic_prediction
  • The coefficient is adjusted: new = weighted_average(old_coefficient, measured_coefficient)
  • Calibrated coefficients are stored per tier in SQLite (calibrated_coefficients table) and used in preference to defaults
  • A clamp range (0.1×–10Γ—) prevents wild swings from outlier measurements
  • Each calibration event increments a sample counter for progressively stable averaging

UsageModel β€” Single Source of Truth

The UsageModel class (src/pacemaker/usage_model.py) is the unified access point for all usage data:

from pacemaker.usage_model import UsageModel

model = UsageModel()
snapshot = model.get_current_usage()  # Returns real or synthetic
if snapshot:
    print(f"5h: {snapshot.five_hour_util:.1f}%")
    print(f"Synthetic: {snapshot.is_synthetic}")

Key design principles:

  • Stateless between calls: All state lives in SQLite (WAL mode for concurrent access)
  • Can be instantiated fresh on every hook invocation β€” no in-memory state to stale
  • Automatic mode selection: get_current_usage() returns real API data in normal mode, synthetic estimates in fallback mode
  • Concurrency-safe: Cost accumulation uses INSERT-only pattern (no read-modify-write races)

State Storage

All fallback state is stored in SQLite tables (replacing previous JSON files):

Table Purpose
api_cache Last successful API response (raw)
fallback_state_v2 Current mode, baseline, tier, rollover offsets
accumulated_costs Per-session cost records (INSERT-only)
backoff_state Exponential backoff tracking
calibrated_coefficients Learned cost→utilization mappings per tier
profile_cache Cached profile data for tier detection

Langfuse Telemetry Integration

Optional integration with Langfuse for observability and tracing.

Configuration

pace-maker langfuse on                           # Enable Langfuse
pace-maker langfuse off                          # Disable Langfuse
pace-maker langfuse status                       # Show connection status
pace-maker langfuse configure URL PUBLIC SECRET  # Set credentials

Features

  • Session Tracking: Creates Langfuse sessions per Claude Code conversation
  • Trace Hierarchy: Tracks main conversation and subagent traces
  • Span Details: Records tool calls, token usage, and timing
  • 24-Hour Metrics: Aggregates sessions, traces, and spans

Requirements

  • Self-hosted or cloud Langfuse instance
  • API credentials (public key + secret key)

Prompt Intelligence Telemetry (Intel)

Optional per-prompt metadata that Claude emits at the start of responses. The intel line is automatically parsed, stripped from visible output, and attached to the corresponding Langfuse trace as metadata.

Intel Line Format

Claude emits a single line starting with the Β§ marker:

Β§ β–³0.8 β—Žsurg β– bug β—‡0.7 ↻2

Symbol Vocabulary

Symbol Field Type Values Description
Β§ (marker) - - Identifies the intel line (required)
β–³ frustration float 0.0 - 1.0 User frustration level (0=calm, 1=high)
β—Ž specificity enum surg, const, outc, expl How specific the request is
β–  task_type enum bug, feat, refac, research, test, docs, debug, conf, other Nature of the work
β—‡ quality float 0.0 - 1.0 Claude's confidence in understanding/solution
↻ iteration int 1 - 9 Number of attempts on this issue

Specificity values:

  • surg = Surgical (very specific, targeted change)
  • const = Constrained (specific scope with boundaries)
  • outc = Outcome-focused (goal stated, approach open)
  • expl = Exploratory (vague, needs clarification)

Langfuse Metadata Keys (for Dashboards)

Intel fields are attached to Langfuse traces with the intel_ prefix. Use these keys when building Langfuse dashboards, filters, and analytics:

Langfuse Metadata Key Source Field Type Example Value
intel_frustration β–³ float 0.8
intel_specificity β—Ž string "surg"
intel_task_type β–  string "bug"
intel_quality β—‡ float 0.7
intel_iteration ↻ int 2

Dashboard examples:

  • Filter traces by intel_task_type = "bug" to see all bug fix conversations
  • Chart intel_frustration over time to detect recurring pain points
  • Group by intel_specificity to analyze prompt quality patterns
  • Alert on intel_iteration >= 4 to identify stuck conversations
  • Correlate intel_quality with session duration for efficiency metrics

Data Flow

  1. Session start: Intel guidance prompt is injected (full symbol vocabulary, from prompts/session_start/intel_guidance.md)
  2. User prompt submit: Short nudge reminder injected after each user prompt (Β§ intel: Start your response with a Β§ intel line...)
  3. Claude responds: Optionally emits Β§ intel line at the start of a response
  4. Post-tool hook: Parses intel from the last 3 assistant messages in the transcript
  5. Langfuse push: Attaches parsed intel as trace metadata (upsert to current trace)
  6. Output cleanup: Intel line is stripped from trace output so users never see it

All fields are optional. Missing or invalid fields are silently skipped (no defaults injected).

Model Preference (Quota Balancing)

Controls which model Claude uses for subagent Task tool calls to balance quota consumption across models.

The Problem

Claude Pro Max has separate quotas for different models (Opus, Sonnet). When one model's quota is consumed faster than another, you can hit rate limits on one while having unused capacity on the other.

Example scenario:

  • 7-day overall usage: 82%
  • Sonnet-specific usage: 96%
  • Opus-specific usage: 60%

Without intervention, Claude defaults to Sonnet for subagents, exhausting that quota while Opus capacity sits unused.

The Solution

pace-maker prefer-model opus    # Force subagents to use Opus
pace-maker prefer-model sonnet  # Force subagents to use Sonnet
pace-maker prefer-model haiku   # Force subagents to use Haiku
pace-maker prefer-model auto    # No preference (default behavior)

How It Works

When a model preference is set, the system injects mandatory nudges at two points:

  1. Session Start: Shows current usage stats and the required model
  2. Post-Tool Reminders: After every tool use, reminds Claude to use the preferred model

The nudge is assertive:

⚠️  MANDATORY MODEL PREFERENCE: OPUS

   You MUST use model: "opus" for ALL Task tool subagent calls.

   WHY: This is for QUOTA BALANCING, not capability.
   The user needs to balance token consumption across models to maximize
   their usage window. Even if the default model 'works fine', using the
   preferred model (opus) helps prevent hitting rate limits.

   REQUIRED FORMAT:
   Task(subagent_type='...', model='opus', prompt='...')

Important Notes

  • Main session model cannot change mid-conversation - to switch the main conversation model, restart with claude --model opus
  • The nudge is about resource management, not capability - Claude should use the preferred model even if the default "works fine"
  • Set to auto to disable nudging and return to default behavior

Session Lifecycle (Tempo)

Prevents Claude from ending sessions prematurely.

When Claude attempts to stop:

  1. System reads original user prompt
  2. Extracts last 10 conversation messages
  3. AI validates if request was completed
  4. APPROVED: Session ends
  5. BLOCKED: Claude receives feedback and continues

Hooks

Hook Function
SessionStart Initializes state, injects intel guidance prompt, Langfuse session creation
UserPromptSubmit Captures prompts, handles CLI commands, deferred trace creation
PreToolUse Intent validation, TDD checks, clean code validation
PostToolUse Credit throttling, subagent reminders, intel parsing, Langfuse trace/span push, activity event recording
Stop Session completion validation, Langfuse trace finalization with token usage
SubagentStart/Stop Tracks subagent context, Langfuse subagent trace management

Configuration File

~/.claude-pace-maker/config.json:

{
  "enabled": true,
  "weekly_limit_enabled": true,
  "five_hour_limit_enabled": true,
  "tempo_enabled": true,
  "intent_validation_enabled": true,
  "tdd_enabled": true,
  "subagent_reminder_enabled": true,
  "preferred_subagent_model": "auto",
  "base_delay": 5,
  "max_delay": 350,
  "safety_buffer_pct": 95.0,
  "preload_hours": 12.0,
  "log_level": 2,
  "langfuse_enabled": false,
  "langfuse_host": "",
  "langfuse_public_key": "",
  "langfuse_secret_key": ""
}

Requirements

  • Python 3.9+
  • Claude Agent SDK
  • jq
  • Bash

Documentation

License

MIT

About

Prevent Claude Code overages by throttling tool usage when getting close to the 5-hour limit and weekly limit. Impose clean code and tdd rules before files are written to disk.

Topics

Resources

License

Stars

Watchers

Forks

Contributors