Skip to content

feat: let the registry enumerate the breakers it created - #162

Merged
bagowix merged 4 commits into
mainfrom
feat/registry-enumeration
Aug 12, 2026
Merged

feat: let the registry enumerate the breakers it created#162
bagowix merged 4 commits into
mainfrom
feat/registry-enumeration

Conversation

@bagowix

@bagowix bagowix commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Summary

Registry could only be asked about a name you already knew — get,
get_existing, close_all, aclose_all. That is exactly the case that does
not hold where the registry matters most: every HTTP integration creates its
breakers lazily, one per host, so the set of names only exists at runtime.
Listing them for a diagnostics endpoint during a METRICS_ONLY rollout, or
applying an operator override to all of them before a maintenance window, meant
reaching into the private _breakers dict.

def names(self) -> tuple[str, ...]: ...
def items(self) -> tuple[tuple[str, CircuitBreaker], ...]: ...

Both return an immutable point-in-time copy taken under the registry lock,
mirroring what _snapshot() already does for teardown: a breaker created
afterwards is not in it, and the returned tuple never changes. names() covers
the "which hosts exist" question on its own; items() pairs each breaker with
its name, which _snapshot() cannot — it returns breakers only, enough for
teardown but not for a diagnostics listing.

No __iter__ / __len__: a live view over a dict mutated under a lock is the
wrong shape here, and explicit copy-returning methods make the snapshot
semantics obvious at the call site.

Docs: reference, the safe-rollout section of the states guide (bulk operator
action), the four transport integration pages and the README diagnostics
paragraph, plus the regenerated llms-full.txt.

Checklist

  • Tests added or updated (suite stays at 100% coverage)
  • uv run ruff format --check and uv run ruff check pass
  • uv run mypy, uv run pyright and uv run pyrefly check pass
  • Docs updated (docs/) for user-facing changes
  • CHANGELOG.md [Unreleased] updated
  • Commits follow Conventional Commits

uv run griffe check interlock --search . is clean — the change is purely
additive. _state_machine.py / _engine.py are untouched, so no mutmut run.

Related issues

Closes #157

Added

  • Added Registry.names() to return an immutable snapshot of cached breaker names.
  • Added Registry.items() to return an immutable snapshot of (name, CircuitBreaker) pairs.
  • Added enumeration support for breakers created lazily by transport integrations.

Changed

  • Documented point-in-time snapshot semantics for Registry.names() and Registry.items().
  • Documented bulk operations and runtime-created breaker inspection across registry and transport integration guides.

`Registry` could only be asked about a name you already knew — `get`,
`get_existing`, `close_all`, `aclose_all`. That is exactly the case that
does not hold where the registry matters most: every HTTP integration
creates its breakers lazily, one per host, so the set of names only
exists at runtime.

Listing them for a diagnostics endpoint during a `METRICS_ONLY` rollout,
or applying an operator override to all of them before a maintenance
window, meant reaching into the private `_breakers` dict.

`names()` and `items()` return that set as an immutable point-in-time
copy taken under the registry lock, mirroring what `_snapshot()` already
does for teardown: a breaker created afterwards is not in it, and the
returned tuple never changes.

Closes #157
Both sides added a `### Added` entry to the unreleased changelog; kept
both, main's `call_sync`/`call_async` entry first.
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: bf8076bc-0256-4dba-ad14-565e53b59dce

📥 Commits

Reviewing files that changed from the base of the PR and between e33250b and 160d80c.

📒 Files selected for processing (5)
  • docs/integrations/aiohttp.md
  • docs/integrations/httpx.md
  • docs/integrations/httpx2.md
  • docs/integrations/requests.md
  • docs/llms-full.txt
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/llms-full.txt
📜 Recent review details
⏰ Context from checks skipped due to timeout. (13)
  • GitHub Check: GitGuardian Security Checks
  • GitHub Check: Run benchmarks
  • GitHub Check: Platform smoke (macos-latest, Python 3.14)
  • GitHub Check: quality (3.11)
  • GitHub Check: Platform smoke (macos-latest, Python 3.11)
  • GitHub Check: quality (3.12)
  • GitHub Check: quality (3.14)
  • GitHub Check: quality (3.13)
  • GitHub Check: Platform smoke (windows-latest, Python 3.14)
  • GitHub Check: Platform smoke (windows-latest, Python 3.11)
  • GitHub Check: Coverage
  • GitHub Check: quality (3.14t)
  • GitHub Check: Analyze (python)
⚠️ CI failures not shown inline (2)

GitHub Actions: Code scanning AI findings on PR #162 / 0_github-advanced-security.txt: Code scanning AI findings on PR #162

Conclusion: failure

View job details

##[group]Run set -euo pipefail
 �[36;1mset -euo pipefail�[0m
 �[36;1mecho "RUNNER_TEMP=$RUNNER_TEMP"�[0m
 �[36;1mfind "$RUNNER_TEMP" -maxdepth 1 -type f -name 'git-credentials-*.config' -print -delete�[0m
 �[36;1m�[0m
 �[36;1m# Generate a unique token and stop processing workflow commands to prevent the runtime from injecting commands�[0m
 �[36;1mSTOP_***REDACTED_SECRET_ASSIGNMENT*** /proc/sys/kernel/random/uuid)�[0m
 �[36;1m�[0m
 �[36;1m# Use a trap to ensure we always resume command processing and check for�[0m
 �[36;1m# fallback error annotations, even if the runtime exits with a non-zero code�[0m
 �[36;1m# (which would otherwise cause set -e to abort the shell before we get here).�[0m
 �[36;1m# The trap preserves the original exit code.�[0m
 �[36;1mcopilot_cleanup() {�[0m
 �[36;1m  �[0m
 �[36;1m  if [ -n "${GIT_PROXY_PID:-}" ] && kill -0 "$GIT_PROXY_PID" 2>/dev/null; then�[0m
 �[36;1m    echo "Stopping git-proxy (pid=$GIT_PROXY_PID)..."�[0m
 �[36;1m    kill "$GIT_PROXY_PID" 2>/dev/null || true�[0m
 �[36;1m    for _ in {1..25}; do�[0m
 �[36;1m      if ! kill -0 "$GIT_PROXY_PID" 2>/dev/null; then break; fi�[0m
 �[36;1m      sleep 0.2�[0m
 �[36;1m    done�[0m
 �[36;1m    if kill -0 "$GIT_PROXY_PID" 2>/dev/null; then�[0m
 �[36;1m      echo "git-proxy did not stop gracefully; forcing termination."�[0m
 �[36;1m      kill -KILL "$GIT_PROXY_PID" 2>/dev/null || true�[0m
 �[36;1m    fi�[0m
 �[36;1m    wait "$GIT_PROXY_PID" 2>/dev/null || true�[0m
 �[36;1m  fi�[0m
 �[36;1m  �[0m
 �[36;1m  echo "::$STOP_***REDACTED_SECRET_ASSIGNMENT***
 �[36;1m  FALLBACK_FILE="${RUNNER_TEMP}/copilot-fallback-error.txt"�[0m
 �[36;1m  if [ -f "$FALLBACK_FILE" ]; then�[0m
 �[36;1m    FALLBACK_MSG=$(head -c 500 "$FALLBACK_FILE" | tr -d '\n\r')�[0m
 �[36;1m    echo "::error title=Copilot Error::${FALLBACK_MSG}"�[0m

GitHub Actions: Code scanning AI findings on PR #162 / github-advanced-security: Code scanning AI findings on PR #162

Conclusion: failure

View job details

##[group]Run set -euo pipefail
 �[36;1mset -euo pipefail�[0m
 �[36;1mecho "RUNNER_TEMP=$RUNNER_TEMP"�[0m
 �[36;1mfind "$RUNNER_TEMP" -maxdepth 1 -type f -name 'git-credentials-*.config' -print -delete�[0m
 �[36;1m�[0m
 �[36;1m# Generate a unique token and stop processing workflow commands to prevent the runtime from injecting commands�[0m
 �[36;1mSTOP_***REDACTED_SECRET_ASSIGNMENT*** /proc/sys/kernel/random/uuid)�[0m
 �[36;1m�[0m
 �[36;1m# Use a trap to ensure we always resume command processing and check for�[0m
 �[36;1m# fallback error annotations, even if the runtime exits with a non-zero code�[0m
 �[36;1m# (which would otherwise cause set -e to abort the shell before we get here).�[0m
 �[36;1m# The trap preserves the original exit code.�[0m
 �[36;1mcopilot_cleanup() {�[0m
 �[36;1m  �[0m
 �[36;1m  if [ -n "${GIT_PROXY_PID:-}" ] && kill -0 "$GIT_PROXY_PID" 2>/dev/null; then�[0m
 �[36;1m    echo "Stopping git-proxy (pid=$GIT_PROXY_PID)..."�[0m
 �[36;1m    kill "$GIT_PROXY_PID" 2>/dev/null || true�[0m
 �[36;1m    for _ in {1..25}; do�[0m
 �[36;1m      if ! kill -0 "$GIT_PROXY_PID" 2>/dev/null; then break; fi�[0m
 �[36;1m      sleep 0.2�[0m
 �[36;1m    done�[0m
 �[36;1m    if kill -0 "$GIT_PROXY_PID" 2>/dev/null; then�[0m
 �[36;1m      echo "git-proxy did not stop gracefully; forcing termination."�[0m
 �[36;1m      kill -KILL "$GIT_PROXY_PID" 2>/dev/null || true�[0m
 �[36;1m    fi�[0m
 �[36;1m    wait "$GIT_PROXY_PID" 2>/dev/null || true�[0m
 �[36;1m  fi�[0m
 �[36;1m  �[0m
 �[36;1m  echo "::$STOP_***REDACTED_SECRET_ASSIGNMENT***
 �[36;1m  FALLBACK_FILE="${RUNNER_TEMP}/copilot-fallback-error.txt"�[0m
 �[36;1m  if [ -f "$FALLBACK_FILE" ]; then�[0m
 �[36;1m    FALLBACK_MSG=$(head -c 500 "$FALLBACK_FILE" | tr -d '\n\r')�[0m
 �[36;1m    echo "::error title=Copilot Error::${FALLBACK_MSG}"�[0m
🧰 Additional context used
📓 Path-based instructions (3)
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

Document user-facing changes in English Markdown documentation and keep generated documentation mirrors synchronized.

Files:

  • docs/integrations/aiohttp.md
  • docs/integrations/httpx.md
  • docs/integrations/httpx2.md
  • docs/integrations/requests.md
{interlock/**/*.py,docs/**/*.md,docs/llms-full.txt,docs/llms.txt}

📄 CodeRabbit inference engine (Custom checks)

When a change affects user-facing behaviour through the public API, integrations, or configuration options, update the relevant page under docs/ and regenerate docs/llms-full.txt; when adding a new documentation page, list it under ## Docs in docs/llms.txt.

Files:

  • docs/integrations/aiohttp.md
  • docs/integrations/httpx.md
  • docs/integrations/httpx2.md
  • docs/integrations/requests.md
docs/**/*.md

⚙️ CodeRabbit configuration file

User-facing documentation. Check that code samples match the current public API and would actually run. A new page must also be listed in docs/llms.txt under ## Docs. Keep the existing voice: short sentences, no marketing.

Files:

  • docs/integrations/aiohttp.md
  • docs/integrations/httpx.md
  • docs/integrations/httpx2.md
  • docs/integrations/requests.md
🧠 Learnings (1)
📚 Learning: 2026-08-07T15:34:59.595Z
Learnt from: CR
Repo: bagowix/interlock PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-08-07T15:34:59.595Z
Learning: Applies to {interlock/**/*.py,docs/**/*.md,docs/llms-full.txt,docs/llms.txt} : When a change affects user-facing behaviour through the public API, integrations, or configuration options, update the relevant page under `docs/` and regenerate `docs/llms-full.txt`; when adding a new documentation page, list it under `## Docs` in `docs/llms.txt`.

Applied to files:

  • docs/integrations/httpx.md
🪛 LanguageTool
docs/integrations/aiohttp.md

[locale-violation] ~128-~128: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...n-time copies, without the ones created afterwards. Use an EventListener for production ...

(AFTERWARDS_US)

docs/integrations/httpx.md

[locale-violation] ~77-~77: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...point-in-time copies: a breaker created afterwards is not in them. After tuning threshold...

(AFTERWARDS_US)

docs/integrations/httpx2.md

[locale-violation] ~81-~81: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...point-in-time copies: a breaker created afterwards is not in them. After tuning threshold...

(AFTERWARDS_US)

docs/integrations/requests.md

[locale-violation] ~132-~132: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...n-time copies, without the ones created afterwards. Use an EventListener for production ...

(AFTERWARDS_US)

🔇 Additional comments (4)
docs/integrations/aiohttp.md (1)

127-129: LGTM!

docs/integrations/httpx.md (1)

74-77: LGTM!

docs/integrations/httpx2.md (1)

78-81: LGTM!

docs/integrations/requests.md (1)

131-133: LGTM!


Walkthrough

Registry now exposes names() and items() as lock-protected tuple snapshots of created breakers. Tests cover snapshot behavior. Reference, integration, README, and changelog documentation describe the new API.

Changes

Registry enumeration

Layer / File(s) Summary
Implement and validate snapshot methods
interlock/registry.py, tests/test_registry.py
Added names() and items() with point-in-time tuple semantics. Tests cover empty registries, created breakers, and later creations.
Document the public API
docs/reference.md, docs/llms-full.txt
Documented return values, lock-protected snapshots, and breaker inspection examples.
Document runtime registry diagnostics
README.md, docs/guides/states.md, docs/integrations/*, CHANGELOG.md
Documented enumeration of breakers created by HTTP integrations and bulk inspection actions.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested labels: enhancement

🚥 Pre-merge checks | ✅ 9
✅ Passed checks (9 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title uses the required feat prefix, lowercase imperative wording, no period, and clearly describes registry enumeration.
Linked Issues check ✅ Passed The implementation, tests, and documentation satisfy issue #157 with lock-protected immutable snapshots from names() and items().
Out of Scope Changes check ✅ Passed All code, tests, and documentation changes directly support the Registry enumeration objective in issue #157.
Zero-Dependency Core ✅ Passed The feature diff changes only interlock/registry.py, adding no imports; pyproject dependencies remains []; init.py has no interlock.integrations re-export.
Changelog Entry ✅ Passed The PR diff adds a bullet under ## [Unreleased] describing public Registry.names() and items() and their snapshot behavior.
Docs And Llm Mirror ✅ Passed Public Registry.names/items were added; docs/reference.md and affected guide/integration pages were updated, and docs/llms-full.txt contains matching changes. No new docs page was added.
Tests Accompany Behaviour Change ✅ Passed The PR adds substantive methods in interlock/registry.py and changes tests/test_registry.py with six tests for names() and items().
Public Api Surface ✅ Passed The merge-base diff changes neither interlock/init.py nor pipeline.py; exported names and public declarations/signatures are identical.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/registry-enumeration

Comment @coderabbitai help to get the list of available commands.

@codspeed-hq

codspeed-hq Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 28 untouched benchmarks


Comparing feat/registry-enumeration (160d80c) with main (34d4fa7)

Open in CodSpeed

@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/test_registry.py (1)

132-132: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract repeated Registry() setup into a pytest fixture.

The six tests duplicate the same registry construction. Move it to a fixture and inject the fixture into each test.

As per coding guidelines, “create fixtures for repeated setup.”

Also applies to: 138-140, 146-147, 156-156, 162-164, 170-171

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_registry.py` at line 132, Extract the repeated Registry()
construction into a pytest fixture in the test module, then inject that fixture
into all six affected tests instead of creating local Registry instances.
Preserve each test’s existing behavior while reusing the fixture value.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/integrations/httpx.md`:
- Around line 74-75: Update the transport or middleware diagnostics paragraphs
in docs/integrations/httpx.md lines 74-75, docs/integrations/httpx2.md lines
78-79, docs/integrations/aiohttp.md lines 126-128, and
docs/integrations/requests.md lines 130-132 to document both names() and
items(). State that both return fixed point-in-time listings, so breakers
created after enumeration are excluded.

---

Nitpick comments:
In `@tests/test_registry.py`:
- Line 132: Extract the repeated Registry() construction into a pytest fixture
in the test module, then inject that fixture into all six affected tests instead
of creating local Registry instances. Preserve each test’s existing behavior
while reusing the fixture value.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 556921a6-ea6b-44ac-bc8c-84cbec07cc98

📥 Commits

Reviewing files that changed from the base of the PR and between 34d4fa7 and 0b1e580.

📒 Files selected for processing (11)
  • CHANGELOG.md
  • README.md
  • docs/guides/states.md
  • docs/integrations/aiohttp.md
  • docs/integrations/httpx.md
  • docs/integrations/httpx2.md
  • docs/integrations/requests.md
  • docs/llms-full.txt
  • docs/reference.md
  • interlock/registry.py
  • tests/test_registry.py
📜 Review details
⏰ Context from checks skipped due to timeout. (11)
  • GitHub Check: quality (3.11)
  • GitHub Check: Platform smoke (macos-latest, Python 3.11)
  • GitHub Check: quality (3.12)
  • GitHub Check: Platform smoke (windows-latest, Python 3.14)
  • GitHub Check: Platform smoke (macos-latest, Python 3.14)
  • GitHub Check: Platform smoke (windows-latest, Python 3.11)
  • GitHub Check: Coverage
  • GitHub Check: quality (3.14t)
  • GitHub Check: quality (3.14)
  • GitHub Check: quality (3.13)
  • GitHub Check: Run benchmarks
⚠️ CI failures not shown inline (2)

GitHub Actions: Code scanning AI findings on PR #162 / github-advanced-security: Code scanning AI findings on PR #162

Conclusion: failure

View job details

##[group]Run set -euo pipefail
 �[36;1mset -euo pipefail�[0m
 �[36;1mecho "RUNNER_TEMP=$RUNNER_TEMP"�[0m
 �[36;1mfind "$RUNNER_TEMP" -maxdepth 1 -type f -name 'git-credentials-*.config' -print -delete�[0m
 �[36;1m�[0m
 �[36;1m# Generate a unique token and stop processing workflow commands to prevent the runtime from injecting commands�[0m
 �[36;1mSTOP_***REDACTED_SECRET_ASSIGNMENT*** /proc/sys/kernel/random/uuid)�[0m
 �[36;1m�[0m
 �[36;1m# Use a trap to ensure we always resume command processing and check for�[0m
 �[36;1m# fallback error annotations, even if the runtime exits with a non-zero code�[0m
 �[36;1m# (which would otherwise cause set -e to abort the shell before we get here).�[0m
 �[36;1m# The trap preserves the original exit code.�[0m
 �[36;1mcopilot_cleanup() {�[0m
 �[36;1m  �[0m
 �[36;1m  if [ -n "${GIT_PROXY_PID:-}" ] && kill -0 "$GIT_PROXY_PID" 2>/dev/null; then�[0m
 �[36;1m    echo "Stopping git-proxy (pid=$GIT_PROXY_PID)..."�[0m
 �[36;1m    kill "$GIT_PROXY_PID" 2>/dev/null || true�[0m
 �[36;1m    for _ in {1..25}; do�[0m
 �[36;1m      if ! kill -0 "$GIT_PROXY_PID" 2>/dev/null; then break; fi�[0m
 �[36;1m      sleep 0.2�[0m
 �[36;1m    done�[0m
 �[36;1m    if kill -0 "$GIT_PROXY_PID" 2>/dev/null; then�[0m
 �[36;1m      echo "git-proxy did not stop gracefully; forcing termination."�[0m
 �[36;1m      kill -KILL "$GIT_PROXY_PID" 2>/dev/null || true�[0m
 �[36;1m    fi�[0m
 �[36;1m    wait "$GIT_PROXY_PID" 2>/dev/null || true�[0m
 �[36;1m  fi�[0m
 �[36;1m  �[0m
 �[36;1m  echo "::$STOP_***REDACTED_SECRET_ASSIGNMENT***
 �[36;1m  FALLBACK_FILE="${RUNNER_TEMP}/copilot-fallback-error.txt"�[0m
 �[36;1m  if [ -f "$FALLBACK_FILE" ]; then�[0m
 �[36;1m    FALLBACK_MSG=$(head -c 500 "$FALLBACK_FILE" | tr -d '\n\r')�[0m
 �[36;1m    echo "::error title=Copilot Error::${FALLBACK_MSG}"�[0m

GitHub Actions: Code scanning AI findings on PR #162 / 0_github-advanced-security.txt: Code scanning AI findings on PR #162

Conclusion: failure

View job details

##[group]Run set -euo pipefail
 �[36;1mset -euo pipefail�[0m
 �[36;1mecho "RUNNER_TEMP=$RUNNER_TEMP"�[0m
 �[36;1mfind "$RUNNER_TEMP" -maxdepth 1 -type f -name 'git-credentials-*.config' -print -delete�[0m
 �[36;1m�[0m
 �[36;1m# Generate a unique token and stop processing workflow commands to prevent the runtime from injecting commands�[0m
 �[36;1mSTOP_***REDACTED_SECRET_ASSIGNMENT*** /proc/sys/kernel/random/uuid)�[0m
 �[36;1m�[0m
 �[36;1m# Use a trap to ensure we always resume command processing and check for�[0m
 �[36;1m# fallback error annotations, even if the runtime exits with a non-zero code�[0m
 �[36;1m# (which would otherwise cause set -e to abort the shell before we get here).�[0m
 �[36;1m# The trap preserves the original exit code.�[0m
 �[36;1mcopilot_cleanup() {�[0m
 �[36;1m  �[0m
 �[36;1m  if [ -n "${GIT_PROXY_PID:-}" ] && kill -0 "$GIT_PROXY_PID" 2>/dev/null; then�[0m
 �[36;1m    echo "Stopping git-proxy (pid=$GIT_PROXY_PID)..."�[0m
 �[36;1m    kill "$GIT_PROXY_PID" 2>/dev/null || true�[0m
 �[36;1m    for _ in {1..25}; do�[0m
 �[36;1m      if ! kill -0 "$GIT_PROXY_PID" 2>/dev/null; then break; fi�[0m
 �[36;1m      sleep 0.2�[0m
 �[36;1m    done�[0m
 �[36;1m    if kill -0 "$GIT_PROXY_PID" 2>/dev/null; then�[0m
 �[36;1m      echo "git-proxy did not stop gracefully; forcing termination."�[0m
 �[36;1m      kill -KILL "$GIT_PROXY_PID" 2>/dev/null || true�[0m
 �[36;1m    fi�[0m
 �[36;1m    wait "$GIT_PROXY_PID" 2>/dev/null || true�[0m
 �[36;1m  fi�[0m
 �[36;1m  �[0m
 �[36;1m  echo "::$STOP_***REDACTED_SECRET_ASSIGNMENT***
 �[36;1m  FALLBACK_FILE="${RUNNER_TEMP}/copilot-fallback-error.txt"�[0m
 �[36;1m  if [ -f "$FALLBACK_FILE" ]; then�[0m
 �[36;1m    FALLBACK_MSG=$(head -c 500 "$FALLBACK_FILE" | tr -d '\n\r')�[0m
 �[36;1m    echo "::error title=Copilot Error::${FALLBACK_MSG}"�[0m
🧰 Additional context used
📓 Path-based instructions (9)
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

Document user-facing changes in English Markdown documentation and keep generated documentation mirrors synchronized.

Files:

  • docs/integrations/httpx.md
  • docs/integrations/requests.md
  • docs/guides/states.md
  • CHANGELOG.md
  • README.md
  • docs/integrations/aiohttp.md
  • docs/reference.md
  • docs/integrations/httpx2.md
{interlock/**/*.py,docs/**/*.md,docs/llms-full.txt,docs/llms.txt}

📄 CodeRabbit inference engine (Custom checks)

When a change affects user-facing behaviour through the public API, integrations, or configuration options, update the relevant page under docs/ and regenerate docs/llms-full.txt; when adding a new documentation page, list it under ## Docs in docs/llms.txt.

Files:

  • docs/integrations/httpx.md
  • docs/integrations/requests.md
  • docs/guides/states.md
  • docs/integrations/aiohttp.md
  • interlock/registry.py
  • docs/llms-full.txt
  • docs/reference.md
  • docs/integrations/httpx2.md
docs/**/*.md

⚙️ CodeRabbit configuration file

User-facing documentation. Check that code samples match the current public API and would actually run. A new page must also be listed in docs/llms.txt under ## Docs. Keep the existing voice: short sentences, no marketing.

Files:

  • docs/integrations/httpx.md
  • docs/integrations/requests.md
  • docs/guides/states.md
  • docs/integrations/aiohttp.md
  • docs/reference.md
  • docs/integrations/httpx2.md
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Support Python 3.11 and newer; use Python 3.11+ features where required.
Keep the core zero-dependency and use only the standard library; external dependencies must be isolated behind optional integrations.
Use injected Clock instances for all time access; do not call time.monotonic() or sleep() directly in logic.
Implement the core as an I/O-free state machine with a single threading.Lock around the await-free critical section, never held across the protected call.
Use Protocols for extension points: Clock, SlidingWindow, Storage, FailureClassifier, and EventListener; do not inherit from internal classes.
Expose one public CircuitBreaker class for sync and async operation, with separate internal paths selected by coroutine detection.
Expose the public API through the package __init__.py; keep helpers underscore-prefixed and hidden.
Use absolute imports, placed at the top of the file, ordered as standard library, third-party, then local imports with blank lines between groups.
Use a maximum line length of 100 characters, single-quoted strings, f-strings, and pathlib.Path instead of os.path.
Annotate every parameter and return value; use modern generic syntax and X | None instead of Optional[X].
Use StrEnum or module-level constants instead of magic constants.
When a constructor or function has three or more arguments, pass them by keyword.
Keep functions focused on one job, generally no longer than 20–30 lines, with minimal side effects and extracted repeated loop logic.
Use async/await for I/O-bound work, asyncio.TaskGroup instead of asyncio.gather, and asyncio.to_thread or ProcessPoolExecutor for CPU-bound work.
Do not mix sync and async in one function; never await a sync callable or block on an async callable.
Fail fast on invalid input or state by raising immediately; do not continue with partial results or invented defaults.
Catch only expected exceptions, log them with context, and re-raise; do not use ...

Files:

  • tests/test_registry.py
  • interlock/registry.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

tests/**/*.py: Use pytest functions rather than test classes, with names formatted as test__unit_of_work__state_under_test__expected_behavior.
Mirror package layout in test filenames, use Arrange-Act-Assert, and create fixtures for repeated setup.
Use injected Clock instances for deterministic tests; do not use sleep() in tests.
Use pytest-asyncio and @pytest.mark.asyncio for asynchronous tests, and use pytest-mock to isolate external dependencies.
Use Hypothesis property-based tests for the state machine and cover all transitions and races.
Write the reproducing test before a bug fix and specify the required behavior before implementing a feature.

Files:

  • tests/test_registry.py

⚙️ CodeRabbit configuration file

pytest functions only, never test classes. Names follow test__unit_of_work__state_under_test__expected_behavior in lower case. One behaviour per test, Arrange-Act-Assert. Time is the injected fake Clock — any real sleep or wall-clock read is flakiness, flag it. Async tests use @pytest.mark.asyncio; state-machine work carries hypothesis property tests. Coverage must stay at 100%: point out uncovered branches the diff introduces. Tests run under -n auto, so anything relying on ordering or shared global state is a bug.

Files:

  • tests/test_registry.py
CHANGELOG.md

📄 CodeRabbit inference engine (AGENTS.md)

Add every change to the [Unreleased] section under Added, Fixed, or Changed, explaining user impact rather than only symbol movement.

Files:

  • CHANGELOG.md

⚙️ CodeRabbit configuration file

Keep a Changelog format. New entries go under ## [Unreleased] in Added / Fixed / Changed. An entry describes what a user could not do before and can now, not which symbol moved. Only the release commit dates a section and updates the link references.

Files:

  • CHANGELOG.md
interlock/**/*.py

📄 CodeRabbit inference engine (Custom checks)

Every production behaviour change in interlock/ must be accompanied by a change under tests/; changes limited to docstrings, comments, or type annotations are exempt. Bug fixes must include at least one regression test that fails without the production fix.

Files:

  • interlock/registry.py

⚙️ CodeRabbit configuration file

Core rules (AGENTS.md is authoritative): (1) Zero-dependency core — anything under interlock/ except interlock/integrations/ may import stdlib only. Flag every third-party import as a blocking issue. (2) No fallbacks, no silent excepts, no a or b or c for required config or data, no hidden retries. Invalid input or state raises immediately. interlock/_notify.py is the one sanctioned swallow (listener hooks are observability, logged with traceback, BaseException still propagates) — do not suggest generalising or "fixing" it. (3) Time comes only from the injected Clock protocol; direct time.monotonic()/time.sleep() in library logic is a bug. (4) Style: 100-char lines, single quotes, f-strings, pathlib, full annotations, X | None never Optional[X], keyword arguments for calls with 3+ arguments, no magic constants (StrEnum or module constants), functions under ~30 lines. (5) Extension points are Protocols (Clock, SlidingWindow, Storage, FailureClassifier, EventListener) — do not propose inheriting internal classes. (6) Sync and async live in one CircuitBreaker with separate internal paths; never propose Sync*/Async* twins and never mix the two paths in one function. (7) Public API is exported from interlock/init.py; everything else is underscore-prefixed. New public symbols need __all__ and a docstring. (8) Python 3.11 is the floor — no 3.12+ syntax or stdlib.

Files:

  • interlock/registry.py
{interlock/*.py,interlock/!(integrations)/**/*.py,pyproject.toml}

📄 CodeRabbit inference engine (Custom checks)

Keep the core zero-dependency: files under interlock/ outside interlock/integrations/ may import only the standard library or other interlock modules; [project] dependencies in pyproject.toml must remain empty; and interlock/__init__.py must not re-export from interlock.integrations.

Files:

  • interlock/registry.py
docs/llms-full.txt

⚙️ CodeRabbit configuration file

Generated artefact — produced by uv run python scripts/build_llms_full.py. Do not review its content or suggest edits; only confirm it was regenerated together with the docs/ changes in the same PR.

Files:

  • docs/llms-full.txt
🧠 Learnings (3)
📚 Learning: 2026-08-07T15:34:32.493Z
Learnt from: CR
Repo: bagowix/interlock PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-08-07T15:34:32.493Z
Learning: Applies to CHANGELOG.md : Add every change to the `[Unreleased]` section under `Added`, `Fixed`, or `Changed`, explaining user impact rather than only symbol movement.

Applied to files:

  • CHANGELOG.md
📚 Learning: 2026-08-07T15:34:32.493Z
Learnt from: CR
Repo: bagowix/interlock PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-08-07T15:34:32.493Z
Learning: Applies to **/*.py : Expose one public `CircuitBreaker` class for sync and async operation, with separate internal paths selected by coroutine detection.

Applied to files:

  • CHANGELOG.md
📚 Learning: 2026-08-07T15:34:59.595Z
Learnt from: CR
Repo: bagowix/interlock PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-08-07T15:34:59.595Z
Learning: Applies to {interlock/**/*.py,docs/**/*.md,docs/llms-full.txt,docs/llms.txt} : When a change affects user-facing behaviour through the public API, integrations, or configuration options, update the relevant page under `docs/` and regenerate `docs/llms-full.txt`; when adding a new documentation page, list it under `## Docs` in `docs/llms.txt`.

Applied to files:

  • docs/llms-full.txt
🪛 LanguageTool
CHANGELOG.md

[style] ~21-~21: To form a complete sentence, be sure to include a subject.
Context: ...as an immediate success. - **Registry can now be enumerated: names() and `items...

(MISSING_IT_THERE)


[style] ~22-~22: Consider an alternative for the overused word “exactly”.
Context: ...about a name you already knew, which is exactly the case that does not hold where it ...

(EXACTLY_PRECISELY)


[locale-violation] ~28-~28: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...r the registry lock — a breaker created afterwards is not in it, and the returned tuple ...

(AFTERWARDS_US)

docs/reference.md

[locale-violation] ~68-~68: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...er the registry lock: a breaker created afterwards is not in it, and the returned tuple ne...

(AFTERWARDS_US)

🔇 Additional comments (3)
docs/guides/states.md (1)

118-127: 📐 Maintainability & Code Quality

Confirm the generated documentation mirror.

docs/llms-full.txt is not included in the supplied files, so this review cannot confirm that the new Registry enumeration guidance was regenerated there.

As per path instructions: docs/llms-full.txt is a generated artefact and must be regenerated with the docs/ changes in the same PR.

Source: Path instructions

README.md (1)

97-98: LGTM!

CHANGELOG.md (1)

21-29: LGTM!

Comment thread docs/integrations/httpx.md Outdated
…uides

The four HTTP integration pages mentioned only `items()` and left the
snapshot boundary to the reference, so the page a reader lands on while
debugging a per-host breaker did not say that the listing is fixed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/integrations/httpx.md`:
- Around line 75-77: Update the integration guides to consistently describe
resolved breaker names: in docs/integrations/httpx.md lines 75-77 and
docs/integrations/httpx2.md lines 79-81, show cached-breaker lookup with
get_existing(name) and describe names() and items() as point-in-time snapshots
of resolved breaker names; in docs/integrations/aiohttp.md lines 127-129 and
docs/integrations/requests.md lines 131-133, document the returned resolved
breaker name rather than the raw request host.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: dd7abd94-49aa-4571-8037-abe2992aeaf2

📥 Commits

Reviewing files that changed from the base of the PR and between 0b1e580 and e33250b.

📒 Files selected for processing (5)
  • docs/integrations/aiohttp.md
  • docs/integrations/httpx.md
  • docs/integrations/httpx2.md
  • docs/integrations/requests.md
  • docs/llms-full.txt
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/llms-full.txt
📜 Review details
⏰ Context from checks skipped due to timeout. (13)
  • GitHub Check: github-advanced-security
  • GitHub Check: quality (3.14t)
  • GitHub Check: Coverage
  • GitHub Check: quality (3.11)
  • GitHub Check: quality (3.12)
  • GitHub Check: Platform smoke (windows-latest, Python 3.14)
  • GitHub Check: Platform smoke (macos-latest, Python 3.14)
  • GitHub Check: Platform smoke (macos-latest, Python 3.11)
  • GitHub Check: Platform smoke (windows-latest, Python 3.11)
  • GitHub Check: quality (3.14)
  • GitHub Check: quality (3.13)
  • GitHub Check: Run benchmarks
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.md

📄 CodeRabbit inference engine (AGENTS.md)

Document user-facing changes in English Markdown documentation and keep generated documentation mirrors synchronized.

Files:

  • docs/integrations/aiohttp.md
  • docs/integrations/httpx.md
  • docs/integrations/httpx2.md
  • docs/integrations/requests.md
{interlock/**/*.py,docs/**/*.md,docs/llms-full.txt,docs/llms.txt}

📄 CodeRabbit inference engine (Custom checks)

When a change affects user-facing behaviour through the public API, integrations, or configuration options, update the relevant page under docs/ and regenerate docs/llms-full.txt; when adding a new documentation page, list it under ## Docs in docs/llms.txt.

Files:

  • docs/integrations/aiohttp.md
  • docs/integrations/httpx.md
  • docs/integrations/httpx2.md
  • docs/integrations/requests.md
docs/**/*.md

⚙️ CodeRabbit configuration file

User-facing documentation. Check that code samples match the current public API and would actually run. A new page must also be listed in docs/llms.txt under ## Docs. Keep the existing voice: short sentences, no marketing.

Files:

  • docs/integrations/aiohttp.md
  • docs/integrations/httpx.md
  • docs/integrations/httpx2.md
  • docs/integrations/requests.md
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: bagowix/interlock PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-08-07T15:34:32.493Z
Learning: Applies to **/*.py : Expose one public `CircuitBreaker` class for sync and async operation, with separate internal paths selected by coroutine detection.
📚 Learning: 2026-08-07T15:34:59.595Z
Learnt from: CR
Repo: bagowix/interlock PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-08-07T15:34:59.595Z
Learning: Applies to {interlock/**/*.py,docs/**/*.md,docs/llms-full.txt,docs/llms.txt} : When a change affects user-facing behaviour through the public API, integrations, or configuration options, update the relevant page under `docs/` and regenerate `docs/llms-full.txt`; when adding a new documentation page, list it under `## Docs` in `docs/llms.txt`.

Applied to files:

  • docs/integrations/httpx.md
🪛 LanguageTool
docs/integrations/aiohttp.md

[locale-violation] ~128-~128: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...me copies, without the hosts first seen afterwards. Use an EventListener for production ...

(AFTERWARDS_US)

docs/integrations/httpx.md

[locale-violation] ~76-~76: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...point-in-time copies: a host first seen afterwards is not in them. After tuning threshold...

(AFTERWARDS_US)

docs/integrations/httpx2.md

[locale-violation] ~80-~80: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...point-in-time copies: a host first seen afterwards is not in them. After tuning threshold...

(AFTERWARDS_US)

docs/integrations/requests.md

[locale-violation] ~132-~132: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...me copies, without the hosts first seen afterwards. Use an EventListener for production ...

(AFTERWARDS_US)

Comment thread docs/integrations/httpx.md Outdated
Comment on lines +75 to +77
runtime, so `transport.registry.items()` lists the breakers created so far and
`names()` just their hosts. Both are point-in-time copies: a host first seen
afterwards is not in them.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use resolved breaker names consistently across the integration guides.

The registry key can differ from the request host when name_resolver is configured. The documentation must use get_existing(name) and describe names() and items() in terms of resolved breaker names.

  • docs/integrations/httpx.md#L75-L77: replace “their hosts” with resolved breaker-name wording and use get_existing(name).
  • docs/integrations/httpx2.md#L79-L81: replace host-based enumeration wording with resolved breaker-name wording and use get_existing(name).
  • docs/integrations/aiohttp.md#L127-L129: document the returned breaker name rather than the raw host.
  • docs/integrations/requests.md#L131-L133: document the returned breaker name rather than the raw host.

As per path instructions: use get_existing(name) to inspect a cached breaker and describe names() and items() as snapshots of breakers created so far.

🧰 Tools
🪛 LanguageTool

[locale-violation] ~76-~76: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...point-in-time copies: a host first seen afterwards is not in them. After tuning threshold...

(AFTERWARDS_US)

📍 Affects 4 files
  • docs/integrations/httpx.md#L75-L77 (this comment)
  • docs/integrations/httpx2.md#L79-L81
  • docs/integrations/aiohttp.md#L127-L129
  • docs/integrations/requests.md#L131-L133
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/integrations/httpx.md` around lines 75 - 77, Update the integration
guides to consistently describe resolved breaker names: in
docs/integrations/httpx.md lines 75-77 and docs/integrations/httpx2.md lines
79-81, show cached-breaker lookup with get_existing(name) and describe names()
and items() as point-in-time snapshots of resolved breaker names; in
docs/integrations/aiohttp.md lines 127-129 and docs/integrations/requests.md
lines 131-133, document the returned resolved breaker name rather than the raw
request host.

Source: Path instructions

The host is only the default breaker key: a configured `name_resolver`
makes `names()` return whatever it produced, so wording the listing as
"hosts" was wrong for exactly the users who reach for the knob.
@bagowix

bagowix commented Aug 12, 2026

Copy link
Copy Markdown
Owner Author

Good catch on the resolver — applied in part (160d80c).

Applied: the enumeration wording no longer says "hosts". name_resolver
makes the registry key whatever the resolver returns, so names() yields
breaker names, not hosts, for exactly the users who reach for that knob. All
four pages now say "the names they were created under" / "the breakers created
so far".

Not changed: the surrounding get_existing(host) sentences. That wording
predates this PR, and on each page it sits directly above the name_resolver
section that introduces custom keys, so it reads as the default-resolver case it
documents. Rewriting it across four guides is a separate docs change, not
something this PR should carry — happy to open a follow-up issue if you think
the default-case phrasing is misleading on its own.

@bagowix
bagowix merged commit 1d4c0f9 into main Aug 12, 2026
21 of 22 checks passed
@bagowix
bagowix deleted the feat/registry-enumeration branch August 12, 2026 17:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Registry lacks public enumeration (names() / items())

1 participant