Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
44fef68
Bring docs/proposals/ into the rename branch
elijahr Apr 27, 2026
707a440
Rename bigfoot to tripwire; default guard=error
elijahr Apr 27, 2026
9598c76
Add passthrough_safe; UnsafePassthroughError on warn
elijahr Apr 27, 2026
0c32dc7
Per-protocol guard levels via [tool.tripwire.guard]
elijahr Apr 27, 2026
a85ca10
Distinguish post-sandbox interactions from leaked ones
elijahr Apr 27, 2026
7cba2db
Pedagogical GuardedCallError messages with call site
elijahr Apr 27, 2026
9260819
Fix mypy: add PostSandboxInteractionError to test_init expected_all
elijahr Apr 27, 2026
1b3bc8b
Add @pytest.mark.guard for per-test override
elijahr Apr 27, 2026
32c9ee2
Strict TOML validation with typo suggestions
elijahr Apr 27, 2026
6e2e5e3
README: when to pick which guard default
elijahr Apr 27, 2026
b0a4975
Fix README: correct stale guard=warn default-mode prose
elijahr Apr 27, 2026
c97afc1
Raise from allow/restrict outside sandbox
elijahr Apr 27, 2026
33aab91
Test contextvars across asyncio + threadpool boundaries
elijahr Apr 27, 2026
c7b3c58
Fix audit findings: green-mirage test, missing deny in CHANGELOG, em-…
elijahr Apr 27, 2026
eea6049
Merge origin/main; adopt _mock-suffix drop from PR #56
elijahr Apr 27, 2026
25d4750
Use compat shim for tomllib in test_guard_levels
elijahr Apr 27, 2026
ec6b73f
Force spawn context in processpool propagation test
elijahr Apr 27, 2026
a77066e
Use jwt instead of crypto in guard dispatch tests
elijahr Apr 27, 2026
bc83000
Lock _active_sandbox_ids for free-threaded safety
elijahr Apr 27, 2026
8e18bc2
Skip processpool propagation test on Windows free-threaded build
elijahr Apr 27, 2026
74c8116
Skip whole contextvar propagation file on Windows free-threaded
elijahr Apr 27, 2026
1d8c696
Thread canonical plugin name through get_verifier_or_raise
elijahr Apr 27, 2026
c49cffd
Normalize @pytest.mark.guard argument via _resolve_guard_levels
elijahr Apr 27, 2026
d0a8f12
Test canonical-name override and marker case normalization
elijahr Apr 27, 2026
50bee68
Spawn TPE workers with empty context to fix asyncio hang on 3.14t
elijahr Apr 28, 2026
3fbde61
Cache plugin lookup by name for hot-path dispatch
elijahr Apr 28, 2026
a5eda1d
docs: address Gemini review feedback on PR #57
elijahr Apr 28, 2026
3c6306f
Drop subprocess_mock proxy references in docs reference
elijahr Apr 28, 2026
be90f46
Rename PyPI dist to python-tripwire
elijahr Apr 28, 2026
809af40
Look up dist metadata by python-tripwire in smoke test
elijahr Apr 28, 2026
c78b274
Eager-populate plugin lookup cache; rsplit source_id for method
elijahr Apr 28, 2026
4f84452
Extend user_frame to PostSandbox/UnsafePassthrough errors; fix punctu…
elijahr Apr 28, 2026
045bcb4
Discover entry-point plugins in lookup cache; lift deferred imports; …
elijahr Apr 28, 2026
33daddf
Mark file_io, celery, native plugins as passthrough_safe=False
elijahr Apr 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 38 additions & 38 deletions .claude/skills/adding-plugins/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Adding Plugins to bigfoot
# Adding Plugins to tripwire

Use when: the user wants to create a new bigfoot plugin, says "add a plugin", "write a plugin", "I want a [X] plugin", or describes a library/service they want bigfoot to intercept.
Use when: the user wants to create a new tripwire plugin, says "add a plugin", "write a plugin", "I want a [X] plugin", or describes a library/service they want tripwire to intercept.

## Overview

This skill guides the complete lifecycle of adding a new plugin to bigfoot: discovery, architecture classification, TDD implementation, integration (registry, proxy, `__init__.py`), documentation, examples, and README updates. It works standalone or integrates with the `develop` skill if available.
This skill guides the complete lifecycle of adding a new plugin to tripwire: discovery, architecture classification, TDD implementation, integration (registry, proxy, `__init__.py`), documentation, examples, and README updates. It works standalone or integrates with the `develop` skill if available.

---

Expand Down Expand Up @@ -52,7 +52,7 @@ If StateMachinePlugin was chosen:
### 1.5 Integration Context

Ask:
- **Does this library make calls that another bigfoot plugin already intercepts?** (e.g., boto3 uses HTTP, elasticsearch uses HTTP, gRPC uses HTTP)
- **Does this library make calls that another tripwire plugin already intercepts?** (e.g., boto3 uses HTTP, elasticsearch uses HTTP, gRPC uses HTTP)
- If yes, document the layering. Users typically disable the lower-level plugin.
- **Should this plugin be default-enabled?** Most are. Set `default_enabled=False` only for plugins that are too broad (file I/O) or too specialized (ctypes/cffi).
- **What pyproject.toml extra name should it use?** Usually matches the registry name. Check existing extras in `pyproject.toml` for conventions.
Expand Down Expand Up @@ -83,7 +83,7 @@ Plugin Name: [name]
Registry Name: [e.g., "redis", "mongo", "pika"]
Plugin Class: [e.g., "RedisPlugin", "MongoPlugin"]
Base Class: [BasePlugin | StateMachinePlugin]
File: src/bigfoot/plugins/[name]_plugin.py
File: src/tripwire/plugins/[name]_plugin.py
Test File: tests/unit/test_[name]_plugin.py

Import Name: [Python import, e.g., "redis", "pymongo"]
Expand Down Expand Up @@ -195,15 +195,15 @@ from __future__ import annotations

import pytest

from bigfoot._context import _current_test_verifier
from bigfoot._errors import InteractionMismatchError, UnmockedInteractionError
from bigfoot._verifier import StrictVerifier
from tripwire._context import _current_test_verifier
from tripwire._errors import InteractionMismatchError, UnmockedInteractionError
from tripwire._verifier import StrictVerifier

# Import the library directly -- all optional deps are in bigfoot[dev].
# Import the library directly -- all optional deps are in python-tripwire[dev].
# Never use pytest.importorskip (green mirage).
import [lib]

from bigfoot.plugins.[name]_plugin import (
from tripwire.plugins.[name]_plugin import (
_[LIB]_AVAILABLE,
[Name]MockConfig,
[Name]Plugin,
Expand Down Expand Up @@ -245,12 +245,12 @@ def clean_plugin_counts() -> None:
Run the test file and confirm all tests fail (no implementation yet):

```bash
cd /Users/elijahrutschman/Development/bigfoot && uv run pytest tests/unit/test_[name]_plugin.py -v
cd [PROJECT_ROOT] && uv run pytest tests/unit/test_[name]_plugin.py -v
```

### 3.3 Write Plugin Implementation

Create `src/bigfoot/plugins/[name]_plugin.py`.
Create `src/tripwire/plugins/[name]_plugin.py`.

**BasePlugin implementation structure:**

Expand All @@ -265,13 +265,13 @@ from collections import deque
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any, ClassVar

from bigfoot._base_plugin import BasePlugin
from bigfoot._context import _get_verifier_or_raise
from bigfoot._errors import UnmockedInteractionError
from bigfoot._timeline import Interaction
from tripwire._base_plugin import BasePlugin
from tripwire._context import _get_verifier_or_raise
from tripwire._errors import UnmockedInteractionError
from tripwire._timeline import Interaction

if TYPE_CHECKING:
from bigfoot._verifier import StrictVerifier
from tripwire._verifier import StrictVerifier

# Optional dependency guard
try:
Expand Down Expand Up @@ -387,12 +387,12 @@ class [Name]Plugin(BasePlugin):

Update these files to register the new plugin:

**`src/bigfoot/_registry.py`** -- Add a `PluginEntry`:
**`src/tripwire/_registry.py`** -- Add a `PluginEntry`:
```python
PluginEntry("[name]", "bigfoot.plugins.[name]_plugin", "[Name]Plugin", "[availability_check]"),
PluginEntry("[name]", "tripwire.plugins.[name]_plugin", "[Name]Plugin", "[availability_check]"),
```

**`src/bigfoot/__init__.py`** -- Add:
**`src/tripwire/__init__.py`** -- Add:
1. Import (with try/except for optional deps)
2. Proxy class (`_[Name]Proxy`)
3. Proxy singleton (`[name]_mock = _[Name]Proxy()`)
Expand All @@ -412,12 +412,12 @@ And add to the `all` extra.
### 3.5 Run Tests

```bash
cd /Users/elijahrutschman/Development/bigfoot && uv run pytest tests/unit/test_[name]_plugin.py tests/unit/test_init.py -v
cd [PROJECT_ROOT] && uv run pytest tests/unit/test_[name]_plugin.py tests/unit/test_init.py -v
```

Then full suite:
```bash
cd /Users/elijahrutschman/Development/bigfoot && uv run pytest tests/ -x
cd [PROJECT_ROOT] && uv run pytest tests/ -x
```

---
Expand Down Expand Up @@ -466,7 +466,7 @@ If `auditing-green-mirage` skill is available, invoke it. Otherwise, verify:
### Gate 5: Full Test Suite

```bash
cd /Users/elijahrutschman/Development/bigfoot && uv run pytest tests/ -x
cd [PROJECT_ROOT] && uv run pytest tests/ -x
```

ALL tests must pass.
Expand All @@ -490,13 +490,13 @@ mock responses and assert exactly what your code sent.
## Installation

```bash
pip install bigfoot[[name]]
pip install python-tripwire[[name]]
```

## Quick Start

```python
import bigfoot
import tripwire

def my_function():
"""Production code that uses [library]."""
Expand All @@ -505,14 +505,14 @@ def my_function():

def test_my_function():
# 1. Register mocks
bigfoot.[name]_mock.mock_[operation]([args], returns=[value])
tripwire.[name]_mock.mock_[operation]([args], returns=[value])

# 2. Run production code in sandbox
with bigfoot:
with tripwire:
result = my_function()

# 3. Assert what happened
bigfoot.[name]_mock.assert_[operation]([expected_fields])
tripwire.[name]_mock.assert_[operation]([expected_fields])
assert result == [expected]
```

Expand All @@ -521,7 +521,7 @@ def test_my_function():
### [operation_name]

```python
bigfoot.[name]_mock.mock_[operation](
tripwire.[name]_mock.mock_[operation](
[params],
returns=[value],
raises=None, # optional: raise this exception instead
Expand All @@ -536,22 +536,22 @@ bigfoot.[name]_mock.mock_[operation](
### Typed Helpers

```python
bigfoot.[name]_mock.assert_[operation]([params])
tripwire.[name]_mock.assert_[operation]([params])
```

### Generic Assert

```python
bigfoot.assert_interaction(
bigfoot.[name]_mock.sentinel.[operation],
tripwire.assert_interaction(
tripwire.[name]_mock.sentinel.[operation],
[field]=[value],
)
```

## Exception Simulation

```python
bigfoot.[name]_mock.mock_[operation](
tripwire.[name]_mock.mock_[operation](
[params],
returns=None,
raises=[LibraryError]("simulated failure"),
Expand All @@ -576,15 +576,15 @@ Create `docs/reference/[name]-plugin.md`:
```markdown
# [Name]Plugin API Reference

::: bigfoot.plugins.[name]_plugin.[Name]Plugin
::: tripwire.plugins.[name]_plugin.[Name]Plugin
options:
show_source: false
members:
- mock_[operation]
- assert_[operation]
[... list all public methods]

::: bigfoot.plugins.[name]_plugin.[Name]MockConfig
::: tripwire.plugins.[name]_plugin.[Name]MockConfig
options:
show_source: false
```
Expand Down Expand Up @@ -632,8 +632,8 @@ Before marking the plugin as complete, verify ALL items:
- [ ] Tests written first (TDD)
- [ ] Tests cover all required categories (9+ base, additional for specific architectures)
- [ ] Plugin implementation complete
- [ ] `src/bigfoot/_registry.py` updated with `PluginEntry`
- [ ] `src/bigfoot/__init__.py` updated (import, proxy class, singleton, `__all__`)
- [ ] `src/tripwire/_registry.py` updated with `PluginEntry`
- [ ] `src/tripwire/__init__.py` updated (import, proxy class, singleton, `__all__`)
- [ ] `tests/unit/test_init.py` updated
- [ ] `pyproject.toml` updated (optional dependency extra, added to `all` extra)
- [ ] All quality gates passed (completeness, code review, fact-check, green mirage, tests)
Expand Down Expand Up @@ -669,7 +669,7 @@ When building a new plugin, use the closest existing plugin as a template:
| Socket-level | SocketPlugin | `socket_plugin.py` |
| WebSocket | WebSocketPlugin | `websocket_plugin.py` |

## Reference: bigfoot Invariants
## Reference: tripwire Invariants

These rules are inviolable. Every plugin must follow them:

Expand Down
6 changes: 3 additions & 3 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: Bug Report
description: Report a bug in bigfoot
description: Report a bug in tripwire
labels: ["bug"]
body:
- type: textarea
Expand All @@ -22,9 +22,9 @@ body:
required: true

- type: input
id: bigfoot-version
id: tripwire-version
attributes:
label: bigfoot version
label: tripwire version
placeholder: "0.6.0"
validations:
required: true
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
run: ruff check src/ tests/

- name: Mypy
run: mypy src/bigfoot/ --strict
run: mypy src/tripwire/ --strict

test:
uses: ./.github/workflows/test.yml
50 changes: 25 additions & 25 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# bigfoot - Agent Instructions
# tripwire - Agent Instructions

## Build & Test Commands

Expand All @@ -13,19 +13,19 @@ uv run mkdocs serve # Serve docs locally at localhost:8000

## Architecture Overview

bigfoot is a deterministic test interaction auditor for Python. It intercepts external calls (HTTP, DB, subprocess, etc.) via monkeypatching and enforces three guarantees: every call must be pre-authorized (mocked), every recorded interaction must be explicitly asserted, and every registered mock must be triggered.
tripwire is a deterministic test interaction auditor for Python. It intercepts external calls (HTTP, DB, subprocess, etc.) via monkeypatching and enforces three guarantees: every call must be pre-authorized (mocked), every recorded interaction must be explicitly asserted, and every registered mock must be triggered.

### Key Modules

| Path | Purpose |
|------|---------|
| `src/bigfoot/_verifier.py` | `StrictVerifier` - the core orchestrator |
| `src/bigfoot/_base_plugin.py` | `BasePlugin` ABC for stateless intercept plugins |
| `src/bigfoot/_state_machine_plugin.py` | `StateMachinePlugin` ABC for lifecycle plugins |
| `src/bigfoot/_registry.py` | `PLUGIN_REGISTRY` with `PluginEntry` dataclasses |
| `src/bigfoot/__init__.py` | Proxy singletons, `__all__` exports |
| `src/bigfoot/plugins/` | All plugin implementations |
| `src/bigfoot/pytest_plugin.py` | Auto-use fixture for pytest integration |
| `src/tripwire/_verifier.py` | `StrictVerifier` - the core orchestrator |
| `src/tripwire/_base_plugin.py` | `BasePlugin` ABC for stateless intercept plugins |
| `src/tripwire/_state_machine_plugin.py` | `StateMachinePlugin` ABC for lifecycle plugins |
| `src/tripwire/_registry.py` | `PLUGIN_REGISTRY` with `PluginEntry` dataclasses |
| `src/tripwire/__init__.py` | Proxy singletons, `__all__` exports |
| `src/tripwire/plugins/` | All plugin implementations |
| `src/tripwire/pytest_plugin.py` | Auto-use fixture for pytest integration |

### Plugin Patterns

Expand All @@ -41,13 +41,13 @@ bigfoot is a deterministic test interaction auditor for Python. It intercepts ex

## Guard Mode

Guard mode is enabled by default (`[tool.bigfoot] guard = true`). It installs I/O plugin interceptors at session startup and blocks any real external call that happens outside a sandbox.
Guard mode is enabled by default (`[tool.tripwire] guard = true`). It installs I/O plugin interceptors at session startup and blocks any real external call that happens outside a sandbox.

- Tests that need real network access (e.g., boto3 setup making DNS/socket calls) should use `@pytest.mark.allow("dns", "socket")` or `with bigfoot.allow("dns", "socket"):`.
- To narrow the allowlist and re-guard specific plugins, use `@pytest.mark.deny(...)` or `with bigfoot.deny(...)`. Deny removes plugins from the current allowlist; it nests and restores on exit.
- Tests that need real network access (e.g., boto3 setup making DNS/socket calls) should use `@pytest.mark.allow("dns", "socket")` or `with tripwire.allow("dns", "socket"):`.
- To narrow the allowlist and re-guard specific plugins, use `@pytest.mark.deny(...)` or `with tripwire.deny(...)`. Deny removes plugins from the current allowlist; it nests and restores on exit.
- Non-I/O plugins must set `supports_guard: ClassVar[bool] = False` (e.g., LoggingPlugin, JwtPlugin, CryptoPlugin, CeleryPlugin, MockPlugin).
- Guard-eligible interceptors must handle `_GuardPassThrough` (catch it and call the original function).
- Allowed calls are invisible to bigfoot and are not recorded on the timeline.
- Allowed calls are invisible to tripwire and are not recorded on the timeline.

## Testable Documentation Examples

Expand All @@ -57,8 +57,8 @@ All code examples shown in plugin guide documentation MUST be runnable and teste

1. **Example files** live in `examples/{name}/` with:
- `__init__.py` (empty package marker)
- `app.py` (production code - the function under test, NO bigfoot imports)
- `test_app.py` (bigfoot test following the standard pattern)
- `app.py` (production code - the function under test, NO tripwire imports)
- `test_app.py` (tripwire test following the standard pattern)

2. **Guide pages** include these files via `pymdownx.snippets` in their "Full example" sections:
````markdown
Expand All @@ -84,42 +84,42 @@ All code examples shown in plugin guide documentation MUST be runnable and teste
```python
"""Brief description."""

import bigfoot
import tripwire

from .app import production_function


def test_something():
# Register mocks BEFORE the sandbox
bigfoot.plugin_proxy.mock_xxx(...)
tripwire.plugin_proxy.mock_xxx(...)

with bigfoot:
with tripwire:
result = production_function(...)

# Value assertions
assert result == expected

# Interaction assertions (AFTER the sandbox)
bigfoot.plugin_proxy.assert_xxx(...)
tripwire.plugin_proxy.assert_xxx(...)
```

### Rules

- **Never** put inline code examples in guide "Full example" sections. Always use snippet includes from `examples/`.
- **Every** new plugin guide must have a corresponding `examples/` directory with working tests.
- If a library generates DEBUG logs (boto3, pymongo, celery, etc.), add an autouse fixture to silence them so they don't interfere with LoggingPlugin.
- **Never** use `pytest.importorskip()` in tests. All optional dependencies are included in `bigfoot[dev]` and are expected to be installed. Skipping on missing imports is a green mirage.
- **Never** use `pytest.importorskip()` in tests. All optional dependencies are included in `python-tripwire[dev]` and are expected to be installed. Skipping on missing imports is a green mirage.
- The `.claude/skills/adding-plugins/SKILL.md` skill automates the full plugin creation lifecycle including examples and docs.

## Selective Installation

Core plugins (subprocess, logging, database, socket, file-io, native, dns) require no extras. Optional plugins need:

```bash
pip install bigfoot[all] # Everything
pip install bigfoot[http] # httpx, requests, urllib
pip install bigfoot[redis] # redis
pip install bigfoot[boto3] # botocore
pip install bigfoot[pymongo] # pymongo
pip install python-tripwire[all] # Everything
pip install python-tripwire[http] # httpx, requests, urllib
pip install python-tripwire[redis] # redis
pip install python-tripwire[boto3] # botocore
pip install python-tripwire[pymongo] # pymongo
# ... see pyproject.toml [project.optional-dependencies] for full list
```
Loading
Loading