Skip to main content

pcdlint (Prompt-Cache Determinism Linter)

CI Status Python 3.10+ License: MIT

A static taint analysis linter that detects code patterns silently invalidating LLM Prompt Caching (OpenAI & Anthropic SDKs).


The $10,000 Bug Explained

LLM prompt caching works by storing the prefix of a prompt in a fast cache. When the prefix matches a cached version, the model skips re-processing the prefix — delivering a 10x read discount versus the full write cost.

The catch? The cache key is computed from the exact byte sequence of the prompt prefix. A single non-deterministic character at position 0 — like datetime.now() — changes every single byte, making the cache completely useless.

Full Write Cost:  ██████████  ($0.030 / 1K tokens)
Cache Read Cost:  █          ($0.003 / 1K tokens)  ← 10x cheaper

One datetime.now() at character 0 silently destroys cache hits, wasting money on every request.


❌ Bad Code (0% Cache Hit)

from datetime import datetime

system = f"Time: {datetime.now()}\n{STATIC_RULES}"
#                                    ^^^^^^^^^^^^ PREFIX TAINTED — every request has a unique prefix

client.chat.completions.create(
    model="gpt-4",
    system=system,  # Cache MISS every time!
)

✅ Good Code (90% Cost Reduction)

from datetime import datetime

system = f"{STATIC_RULES}\nTime: {datetime.now()}"
#       ^^^^^^^^^^^^^^ Static prefix first, dynamic value at the end — cache prefix preserved

client.chat.completions.create(
    model="gpt-4",
    system=system,  # Cache HIT — prefix bytes match across requests!
)

Rule Reference

Rule ID Name Severity Description
PCL001 prefix-taint-injection ERROR Dynamic value placed before static prompt text invalidates cache prefix
PCL002 unsorted-json-in-prefix WARNING json.dumps() without sort_keys=True produces non-deterministic output
PCL003 set-iteration-in-prompt ERROR Python's PYTHONHASHSEED randomizes set iteration order across processes
PCL004 dynamic-tools-mutation WARNING Changing tool definition order invalidates the entire prompt cache hierarchy

Installation

pip install pcdlint

Or install from source with dev dependencies:

pip install -e ".[dev]"

CLI Usage

# Check a single file
pcdlint check src/my_app.py

# Check multiple paths
pcdlint check src/ tests/

# Output as JSON
pcdlint check src/ --format json

# Fail on warnings (useful for CI)
pcdlint check src/ --fail-on-warn

GitHub Actions CI Integration

name: pcdlint
on: [push, pull_request]
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install pcdlint
      - run: pcdlint check src/ --fail-on-warn

How It Works

  1. AST Parsing: Uses Python's built-in ast module to parse source files — no external compilers needed.
  2. Taint Tracking: Identifies non-deterministic sources (datetime.now(), uuid.uuid4(), os.urandom(), etc.) and tracks their propagation through f-strings, concatenation, and .format().
  3. Prefix Analysis: Precisely determines if a tainted expression appears before a static prefix solid in the prompt string.
  4. Rule Matching: Checks tainted prompts against LLM API sink calls (chat.completions.create, messages.create, etc.) and reports violations.

Metadata

Release files for pcdlint 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pcdlint 0.1.0
File Size Uploaded
pcdlint-0.1.0.tar.gz 20.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pcdlint 0.1.0
File Interpreter ABI Platform
pcdlint-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 38.2 kB

Release files / pcdlint-0.1.0.tar.gz

Download URL pcdlint-0.1.0.tar.gz
Size 20.7 kB
Tags Source
SHA-256 checksum
How to use checksums
2b1e44c66a93092f7b03b025165fc5f16ea016f6bdbc7dfb8f78880bbaa26e69
BLAKE2b-256 checksum
How to use checksums
6c8d0ae2b7d3f0f56fff4475e8c380fc5a9c704da1bcbb04a416fd765acc4634
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release files / pcdlint-0.1.0-py3-none-any.whl

Download URL pcdlint-0.1.0-py3-none-any.whl
Size 17.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d7e8cb88f720063717136ffd2e421adc0cf697bebf8330f73e9e2bfada459c27
BLAKE2b-256 checksum
How to use checksums
53332cd78b09c679a240295df46edf26b7b2f151b745cc4e64833aa7841bd1b5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.0

2 release files

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page