ad-tech-inc-resilient-http 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ad_tech_inc_resilient_http-0.1.0/.gemini/agents/issue-runner.sh +96 -0
- ad_tech_inc_resilient_http-0.1.0/.gemini/prompts/issue-worker.md +30 -0
- ad_tech_inc_resilient_http-0.1.0/.gemini/skills/github-issues/SKILL.md +118 -0
- ad_tech_inc_resilient_http-0.1.0/.gemini/skills/resilience-expert/SKILL.md +39 -0
- ad_tech_inc_resilient_http-0.1.0/.gemini/skills/resilience-expert/references/architecture.md +23 -0
- ad_tech_inc_resilient_http-0.1.0/.gemini/skills/resilience-expert/references/testing-patterns.md +44 -0
- ad_tech_inc_resilient_http-0.1.0/.github/workflows/ci.yml +35 -0
- ad_tech_inc_resilient_http-0.1.0/.github/workflows/publish.yml +33 -0
- ad_tech_inc_resilient_http-0.1.0/.gitignore +10 -0
- ad_tech_inc_resilient_http-0.1.0/.python-version +1 -0
- ad_tech_inc_resilient_http-0.1.0/.vscode/settings.json +5 -0
- ad_tech_inc_resilient_http-0.1.0/GEMINI.md +32 -0
- ad_tech_inc_resilient_http-0.1.0/PKG-INFO +334 -0
- ad_tech_inc_resilient_http-0.1.0/README.md +317 -0
- ad_tech_inc_resilient_http-0.1.0/docker-compose.yml +11 -0
- ad_tech_inc_resilient_http-0.1.0/docs/close.mmd +10 -0
- ad_tech_inc_resilient_http-0.1.0/docs/close.png +0 -0
- ad_tech_inc_resilient_http-0.1.0/docs/closev2.png +0 -0
- ad_tech_inc_resilient_http-0.1.0/docs/diagrams/architecture.mmd +17 -0
- ad_tech_inc_resilient_http-0.1.0/docs/diagrams/sequence_flow.mmd +33 -0
- ad_tech_inc_resilient_http-0.1.0/docs/diagrams/state_machine.mmd +11 -0
- ad_tech_inc_resilient_http-0.1.0/docs/half_open.mmd +13 -0
- ad_tech_inc_resilient_http-0.1.0/docs/half_open.png +0 -0
- ad_tech_inc_resilient_http-0.1.0/docs/open.mmd +7 -0
- ad_tech_inc_resilient_http-0.1.0/docs/open.png +0 -0
- ad_tech_inc_resilient_http-0.1.0/examples/fastapi_integration.py +48 -0
- ad_tech_inc_resilient_http-0.1.0/examples/simulate_outage.py +78 -0
- ad_tech_inc_resilient_http-0.1.0/examples/slack_example.py +46 -0
- ad_tech_inc_resilient_http-0.1.0/examples/stripe_example.py +44 -0
- ad_tech_inc_resilient_http-0.1.0/pyproject.toml +30 -0
- ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/__init__.py +20 -0
- ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/circuit_breaker.py +94 -0
- ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/client.py +84 -0
- ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/config.py +11 -0
- ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/exceptions.py +10 -0
- ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/failure_store.py +125 -0
- ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/fallback.py +21 -0
- ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/http.py +48 -0
- ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/retry.py +22 -0
- ad_tech_inc_resilient_http-0.1.0/src/resilient_http_client/types.py +7 -0
- ad_tech_inc_resilient_http-0.1.0/tests/test_circuit_breaker.py +196 -0
- ad_tech_inc_resilient_http-0.1.0/tests/test_failure_store.py +94 -0
- ad_tech_inc_resilient_http-0.1.0/tests/test_fallback.py +44 -0
- ad_tech_inc_resilient_http-0.1.0/tests/test_flaky_resilience.py +127 -0
- ad_tech_inc_resilient_http-0.1.0/tests/test_integration.py +97 -0
- ad_tech_inc_resilient_http-0.1.0/tests/test_retry_policy.py +20 -0
- ad_tech_inc_resilient_http-0.1.0/uv.lock +333 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
#-------------------------
|
|
6
|
+
# Config
|
|
7
|
+
#-------------------------
|
|
8
|
+
ITERATIONS="${1:-10}"
|
|
9
|
+
|
|
10
|
+
MAIN_BRANCH="${MAIN_BRANCH:-main}"
|
|
11
|
+
BRANCH="${BRANCH:-overnight-batch-$(date +%Y%m%d-%H%M%S)}"
|
|
12
|
+
|
|
13
|
+
PROMPT_FILE=".gemini/prompts/issue-worker.md"
|
|
14
|
+
|
|
15
|
+
SUCCESS_FILE=".agent-complete"
|
|
16
|
+
STUCK_FILE=".agent-stuck"
|
|
17
|
+
|
|
18
|
+
#-------------------------
|
|
19
|
+
# Helpers
|
|
20
|
+
#-------------------------
|
|
21
|
+
cleanup() {
|
|
22
|
+
rm -f "$SUCCESS_FILE" "$STUCK_FILE"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
trap cleanup EXIT
|
|
26
|
+
|
|
27
|
+
#-------------------------
|
|
28
|
+
# Validate
|
|
29
|
+
#-------------------------
|
|
30
|
+
if ! command -v gemini >/dev/null 2>&1; then
|
|
31
|
+
echo "gemini command not found."
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
if ! command -v gh >/dev/null 2>&1; then
|
|
36
|
+
echo "GitHub CLI (gh) is not installed. Please install it to use this agent."
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
if [ ! -f "$PROMPT_FILE" ]; then
|
|
41
|
+
echo "Prompt file missing: $PROMPT_FILE"
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
46
|
+
echo "Working directory is dirty. Commit or stash changes first."
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
#-------------------------
|
|
51
|
+
# Setup
|
|
52
|
+
#-------------------------
|
|
53
|
+
git checkout "$MAIN_BRANCH"
|
|
54
|
+
git pull origin "$MAIN_BRANCH"
|
|
55
|
+
|
|
56
|
+
git checkout -b "$BRANCH"
|
|
57
|
+
|
|
58
|
+
echo "Branch: $BRANCH"
|
|
59
|
+
echo "Iterations: $ITERATIONS"
|
|
60
|
+
echo "Prompt: $PROMPT_FILE"
|
|
61
|
+
|
|
62
|
+
#-------------------------
|
|
63
|
+
# Agent Loop
|
|
64
|
+
#-------------------------
|
|
65
|
+
for ((i=1; i<=ITERATIONS; i++)); do
|
|
66
|
+
|
|
67
|
+
echo
|
|
68
|
+
echo "=== Iteration $i/$ITERATIONS ==="
|
|
69
|
+
|
|
70
|
+
if ! timeout 5m gemini -p "$(cat "$PROMPT_FILE")"; then
|
|
71
|
+
echo "Gemini timed out"
|
|
72
|
+
exit 1
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
if [ -f "$STUCK_FILE" ]; then
|
|
76
|
+
echo "Agent stuck:"
|
|
77
|
+
cat "$STUCK_FILE"
|
|
78
|
+
exit 1
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
if [ -f "$SUCCESS_FILE" ]; then
|
|
82
|
+
echo "All tasks completed."
|
|
83
|
+
break
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
done
|
|
87
|
+
|
|
88
|
+
#-------------------------
|
|
89
|
+
# Push
|
|
90
|
+
#-------------------------
|
|
91
|
+
git push -u origin "$BRANCH"
|
|
92
|
+
|
|
93
|
+
echo
|
|
94
|
+
echo "Finished."
|
|
95
|
+
echo "Review:"
|
|
96
|
+
echo "git log $MAIN_BRANCH..$BRANCH"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Issue Worker
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Complete one issue at a time.
|
|
6
|
+
|
|
7
|
+
## Instructions
|
|
8
|
+
|
|
9
|
+
1. Use github-issues skill to fetch open issues.
|
|
10
|
+
2. Check existing Tasks and skip issues already completed or in_progress.
|
|
11
|
+
3. Pick the most appropriate remaining issue.
|
|
12
|
+
4. Create a Task and mark it in_progress.
|
|
13
|
+
5. Implement the fix or feature.
|
|
14
|
+
6. Add tests if needed.
|
|
15
|
+
7. Run formatting.
|
|
16
|
+
8. Run the full test suite.
|
|
17
|
+
|
|
18
|
+
If tests fail:
|
|
19
|
+
- attempt fixes
|
|
20
|
+
- after 3 failed attempts mark the Task stuck
|
|
21
|
+
- create .agent-stuck with issue number and reason
|
|
22
|
+
|
|
23
|
+
If no issues remain:
|
|
24
|
+
- create .agent-complete
|
|
25
|
+
- exit
|
|
26
|
+
|
|
27
|
+
Commit changes:
|
|
28
|
+
ISSUE #<number>: <short description>
|
|
29
|
+
|
|
30
|
+
Mark the Task completed.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: github-issues
|
|
3
|
+
description: Use this skill whenever work should be selected from GitHub issues, tasks need tracking, or an autonomous agent is processing repository issues.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GitHub Issues Skill
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
Process repository issues one at a time in a predictable and repeatable manner.
|
|
11
|
+
|
|
12
|
+
## Workflow
|
|
13
|
+
|
|
14
|
+
### 1. Fetch Issues
|
|
15
|
+
|
|
16
|
+
Retrieve all open GitHub issues.
|
|
17
|
+
|
|
18
|
+
Ignore:
|
|
19
|
+
|
|
20
|
+
* closed issues
|
|
21
|
+
* duplicate issues
|
|
22
|
+
* blocked issues
|
|
23
|
+
* issues already marked completed
|
|
24
|
+
* issues already marked in_progress
|
|
25
|
+
|
|
26
|
+
### 2. Select Issue
|
|
27
|
+
|
|
28
|
+
Choose exactly one issue.
|
|
29
|
+
|
|
30
|
+
Priority order:
|
|
31
|
+
|
|
32
|
+
1. high priority
|
|
33
|
+
2. oldest open issue
|
|
34
|
+
3. smallest independent issue
|
|
35
|
+
|
|
36
|
+
### 3. Start Task
|
|
37
|
+
|
|
38
|
+
Create or update task tracking.
|
|
39
|
+
|
|
40
|
+
Mark issue status:
|
|
41
|
+
|
|
42
|
+
in_progress
|
|
43
|
+
|
|
44
|
+
Record:
|
|
45
|
+
|
|
46
|
+
* issue number
|
|
47
|
+
* title
|
|
48
|
+
|
|
49
|
+
### 4. Implement
|
|
50
|
+
|
|
51
|
+
Implement only the changes necessary to satisfy the issue.
|
|
52
|
+
|
|
53
|
+
Avoid unrelated refactoring.
|
|
54
|
+
|
|
55
|
+
Follow existing project conventions.
|
|
56
|
+
|
|
57
|
+
### 5. Testing
|
|
58
|
+
|
|
59
|
+
Add or update tests if required.
|
|
60
|
+
|
|
61
|
+
Run formatting.
|
|
62
|
+
|
|
63
|
+
Run the complete test suite.
|
|
64
|
+
|
|
65
|
+
### 6. Failure Handling
|
|
66
|
+
|
|
67
|
+
If tests fail:
|
|
68
|
+
|
|
69
|
+
1. investigate
|
|
70
|
+
2. fix
|
|
71
|
+
3. rerun tests
|
|
72
|
+
|
|
73
|
+
Maximum attempts: 3
|
|
74
|
+
|
|
75
|
+
If still failing:
|
|
76
|
+
|
|
77
|
+
Create:
|
|
78
|
+
|
|
79
|
+
.agent-stuck
|
|
80
|
+
|
|
81
|
+
Contents:
|
|
82
|
+
|
|
83
|
+
Issue #<number>
|
|
84
|
+
Reason: <explanation>
|
|
85
|
+
|
|
86
|
+
Mark task:
|
|
87
|
+
|
|
88
|
+
stuck
|
|
89
|
+
|
|
90
|
+
Stop execution.
|
|
91
|
+
|
|
92
|
+
### 7. Success Handling
|
|
93
|
+
|
|
94
|
+
Commit changes:
|
|
95
|
+
|
|
96
|
+
ISSUE #<number>: <short description>
|
|
97
|
+
|
|
98
|
+
Mark task:
|
|
99
|
+
|
|
100
|
+
completed
|
|
101
|
+
|
|
102
|
+
### 8. Completion
|
|
103
|
+
|
|
104
|
+
If no issues remain:
|
|
105
|
+
|
|
106
|
+
Create:
|
|
107
|
+
|
|
108
|
+
.agent-complete
|
|
109
|
+
|
|
110
|
+
Exit successfully.
|
|
111
|
+
|
|
112
|
+
## Rules
|
|
113
|
+
|
|
114
|
+
* Work on exactly one issue.
|
|
115
|
+
* Never skip testing.
|
|
116
|
+
* Never skip formatting.
|
|
117
|
+
* Never create both `.agent-complete` and `.agent-stuck`.
|
|
118
|
+
* Prefer small reviewable commits.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: resilience-expert
|
|
3
|
+
description: Expert guidance for maintaining, extending, and debugging the Resilient HTTP Client library. Use when adding resilience patterns, new storage backends, or fixing integration bugs in examples and tests.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Resilience Expert
|
|
7
|
+
|
|
8
|
+
Expert guidance for the `resilient-http-client` library.
|
|
9
|
+
|
|
10
|
+
## Core Concepts
|
|
11
|
+
|
|
12
|
+
- **Architecture**: See [architecture.md](references/architecture.md) for how components coordinate.
|
|
13
|
+
- **Testing**: See [testing-patterns.md](references/testing-patterns.md) for standardized mocks.
|
|
14
|
+
|
|
15
|
+
## Workflows
|
|
16
|
+
|
|
17
|
+
### Extending the Library
|
|
18
|
+
|
|
19
|
+
1. **New Resilience Pattern**: Implement the logic in a new module, integrate it into `ResilientHttpClient.request()`, and add configuration to `ResilienceConfig`.
|
|
20
|
+
2. **New Storage Backend**: Inherit from the same interface used by `FailureStore` (methods like `get_state`, `set_state`, `increment_failures`).
|
|
21
|
+
|
|
22
|
+
### Debugging Issues
|
|
23
|
+
|
|
24
|
+
1. **Verify State**: Check the `FailureStore` state in Redis using `redis-cli`.
|
|
25
|
+
2. **Check Transitions**: Use `simulate_outage.py` to verify Circuit Breaker transitions (CLOSED -> OPEN -> HALF-OPEN -> CLOSED).
|
|
26
|
+
3. **Await Everything**: Ensure all async methods in `FailureStore` and `Client` are properly awaited.
|
|
27
|
+
|
|
28
|
+
### Testing Requirements
|
|
29
|
+
|
|
30
|
+
- Always add unit tests in `tests/`.
|
|
31
|
+
- Use `pytest-asyncio` for async tests.
|
|
32
|
+
- Maintain consistency with `FakeRedis` and `MockResponse` mocks.
|
|
33
|
+
- Ensure `PYTHONPATH=.` is set when running tests.
|
|
34
|
+
|
|
35
|
+
## Rules
|
|
36
|
+
|
|
37
|
+
- Use `CircuitState` enum for all state-related logic.
|
|
38
|
+
- Prefer `async with` context managers for `ResilientHttpClient`.
|
|
39
|
+
- Maintain docstrings for public interfaces in `src/client.py`.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Resilient HTTP Client Architecture
|
|
2
|
+
|
|
3
|
+
The library is designed around the **Orchestrator** pattern where `ResilientHttpClient` coordinates several specialized components.
|
|
4
|
+
|
|
5
|
+
## Component Coordination
|
|
6
|
+
|
|
7
|
+
1. **ResilientHttpClient**: The entry point. It manages the lifecycle of the request.
|
|
8
|
+
2. **CircuitBreaker**: Consulted before every request. Updates state based on success/failure.
|
|
9
|
+
3. **FailureStore**: Persistent storage (e.g., Redis) for failure metrics and circuit state.
|
|
10
|
+
4. **RetryPolicy**: Determines if and when to retry based on exponential backoff.
|
|
11
|
+
5. **HttpExecutor**: Wraps `httpx` and normalizes responses into `HttpResponse`.
|
|
12
|
+
6. **FallbackHandler**: Executes registered fallback logic when all else fails.
|
|
13
|
+
|
|
14
|
+
## Request Flow
|
|
15
|
+
|
|
16
|
+
1. Check `circuit.allow_request()`.
|
|
17
|
+
2. If blocked, return `fallback.run("circuit_open")`.
|
|
18
|
+
3. Loop for retries:
|
|
19
|
+
a. Execute `http.send()`.
|
|
20
|
+
b. If success, `circuit.on_success()` and return data.
|
|
21
|
+
c. If failure, `circuit.on_failure()`.
|
|
22
|
+
d. Check `retry.can_retry()`. If yes, `retry.wait()` and continue.
|
|
23
|
+
4. If loop exits, return `fallback.run(last_error)`.
|
ad_tech_inc_resilient_http-0.1.0/.gemini/skills/resilience-expert/references/testing-patterns.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Testing Patterns
|
|
2
|
+
|
|
3
|
+
Consistency in testing is critical to ensure mocks correctly simulate the production components.
|
|
4
|
+
|
|
5
|
+
## FakeRedis Mock
|
|
6
|
+
|
|
7
|
+
Always use the standardized `FakeRedis` for unit and integration tests. It must support `nx` and `ex` arguments.
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
class FakeRedis:
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self.db = {}
|
|
13
|
+
|
|
14
|
+
async def get(self, key):
|
|
15
|
+
return self.db.get(key)
|
|
16
|
+
|
|
17
|
+
async def set(self, key, value, ex=None, nx=False):
|
|
18
|
+
if nx and key in self.db:
|
|
19
|
+
return False
|
|
20
|
+
self.db[key] = value
|
|
21
|
+
return True
|
|
22
|
+
|
|
23
|
+
async def incr(self, key):
|
|
24
|
+
self.db[key] = int(self.db.get(key, 0)) + 1
|
|
25
|
+
return self.db[key]
|
|
26
|
+
|
|
27
|
+
async def expire(self, key, ttl, nx=False):
|
|
28
|
+
pass # Optional: implement for TTL specific tests
|
|
29
|
+
|
|
30
|
+
async def delete(self, key):
|
|
31
|
+
self.db.pop(key, None)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Mocking HTTP Responses
|
|
35
|
+
|
|
36
|
+
When mocking `HttpExecutor`, return a `MockResponse` object that implements `is_success`, `status_code`, and `data`.
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
class MockResponse:
|
|
40
|
+
def __init__(self, is_success, status_code, data):
|
|
41
|
+
self.is_success = is_success
|
|
42
|
+
self.status_code = status_code
|
|
43
|
+
self.data = data
|
|
44
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Test on Python ${{ matrix.python-version }}
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.12"]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v5
|
|
22
|
+
with:
|
|
23
|
+
enable-cache: true
|
|
24
|
+
version: "latest"
|
|
25
|
+
|
|
26
|
+
- name: Set up Python
|
|
27
|
+
uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: ${{ matrix.python-version }}
|
|
30
|
+
|
|
31
|
+
- name: Install dependencies
|
|
32
|
+
run: uv sync --all-extras --dev
|
|
33
|
+
|
|
34
|
+
- name: Run pytest
|
|
35
|
+
run: uv run pytest
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
name: Build and Publish
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
environment: pypi
|
|
12
|
+
permissions:
|
|
13
|
+
id-token: write # Required for trusted publishing
|
|
14
|
+
contents: read
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v5
|
|
20
|
+
with:
|
|
21
|
+
enable-cache: true
|
|
22
|
+
version: "latest"
|
|
23
|
+
|
|
24
|
+
- name: Set up Python
|
|
25
|
+
uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.12"
|
|
28
|
+
|
|
29
|
+
- name: Build package
|
|
30
|
+
run: uv build
|
|
31
|
+
|
|
32
|
+
- name: Publish package to PyPI
|
|
33
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# ๐ก๏ธ Resilient HTTP Client - Project Instructions
|
|
2
|
+
|
|
3
|
+
This file provides foundational guidance for AI agents working on the `resilient-http-client` project.
|
|
4
|
+
|
|
5
|
+
## ๐๏ธ Architectural Core
|
|
6
|
+
This library is built on the principle of **strict state management** for resilience patterns.
|
|
7
|
+
- **State Machine**: All circuit breaker logic must strictly follow the `CLOSED`, `OPEN`, `HALF-OPEN` transitions.
|
|
8
|
+
- **Async First**: All network and storage operations MUST be asynchronous and properly awaited.
|
|
9
|
+
- **Pluggable Storage**: The `FailureStore` is the source of truth. Ensure any changes maintain compatibility with distributed backends (like Redis).
|
|
10
|
+
|
|
11
|
+
## ๐ ๏ธ Tech Stack & Tooling
|
|
12
|
+
- **Language**: Python 3.12+
|
|
13
|
+
- **HTTP Engine**: `httpx` (Asynchronous)
|
|
14
|
+
- **Dependency Management**: `uv`
|
|
15
|
+
- **Testing**: `pytest` with `pytest-asyncio`
|
|
16
|
+
- **Quality**: Adhere to PEP 8; use explicit type hints for all public APIs.
|
|
17
|
+
|
|
18
|
+
## ๐งช Testing Mandates
|
|
19
|
+
A change is not complete without verification.
|
|
20
|
+
- **Unit Tests**: Every new resilience logic must have 100% branch coverage in `tests/`.
|
|
21
|
+
- **Chaos Testing**: When modifying `CircuitBreaker` or `RetryPolicy`, you must run or update `tests/test_flaky_resilience.py` to simulate real-world failure modes.
|
|
22
|
+
- **Integration**: Verify changes against the Redis backend if storage logic is modified.
|
|
23
|
+
|
|
24
|
+
## ๐ Workflows
|
|
25
|
+
- **Specialized Guidance**: Always activate the `resilience-expert` skill (`activate_skill(name="resilience-expert")`) before making architectural changes.
|
|
26
|
+
- **Environment**: Always run commands with `PYTHONPATH=.` or via `uv run` to ensure local modules are discoverable.
|
|
27
|
+
- **Validation**: Before finishing a task, run `pytest` to ensure no regressions.
|
|
28
|
+
|
|
29
|
+
## ๐ References
|
|
30
|
+
- Architecture: `docs/diagrams/architecture.mmd`
|
|
31
|
+
- State Machine: `docs/diagrams/state_machine.mmd`
|
|
32
|
+
- Core Implementation: `src/client.py`
|