Skip to content

ci: encode the definition of done as CodeRabbit review gates - #146

Merged
bagowix merged 1 commit into
mainfrom
ci/coderabbit-review-config
Aug 7, 2026
Merged

ci: encode the definition of done as CodeRabbit review gates#146
bagowix merged 1 commit into
mainfrom
ci/coderabbit-review-config

Conversation

@bagowix

@bagowix bagowix commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Summary

Add a .coderabbit.yaml so the automated PR review enforces this project's
rules instead of generic Python advice.

  • Path instructions (11 globs). Encode the parts of AGENTS.md that are
    invisible in a diff: stdlib-only core outside interlock/integrations/,
    time only through the injected Clock, the lock in _engine.py never held
    across the protected call, X | None over Optional[X], keyword arguments
    at 3+ parameters, the test__unit__state__expected naming convention, and
    SHA-pinned actions under .github/workflows/. The sanctioned swallow in
    _notify.py is called out explicitly so the bot stops trying to "fix" it.
  • Pre-merge checks. Five custom checks mirror the definition of done:
    zero-dependency core (error), [Unreleased] changelog entry, docs plus a
    regenerated docs/llms-full.txt, tests alongside behaviour changes, and
    public API surface (requires the breaking-change label plus a migration
    note, matching the griffe check gate). Title check enforces Conventional
    Commits, since PRs are squash-merged.
  • Noise control. pylint and markdownlint are off — ruff and pymarkdown
    already gate those files, and a second unconfigured rule set only produces
    contradictory nits. Docstring coverage is off: public API carries
    docstrings, private helpers deliberately do not.
  • Safety. auto_apply_labels stays false and breaking-change is absent
    from the labeling instructions, because that label switches off the griffe
    API-compatibility gate — not something a bot should do unattended.
  • Knowledge base. filePatterns points at AGENTS.md and
    CONTRIBUTING.md; the defaults look for CLAUDE.md files, which are
    gitignored here and would never be visible.

docs/llms-full.txt is deliberately not in path_filters: those patterns
also drive sparse-checkout, and excluding the file would blind the check that
verifies the mirror was regenerated. A path instruction tells the reviewer to
confirm its regeneration without reviewing its contents.

reviews.auto_review.base_branches names main explicitly. On installation
the bot skipped PR #129 with "auto reviews are disabled on base/target branches
other than the default branch", although that PR targets main and the base
was never changed. The explicit entry removes the dependency on how CodeRabbit
resolves the default branch.

Validated against https://coderabbit.ai/integrations/schema.v2.json — no
errors. Note that CodeRabbit reads the file from the branch under review, so
this PR is its own smoke test: the Run configuration block should report
Configuration used: .coderabbit.yaml instead of defaults.

Checklist

  • Tests added or updated (suite stays at 100% coverage) — n/a, no library
    code changed; the suite is untouched
  • 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 — n/a, tooling only
  • CHANGELOG.md [Unreleased] updated — n/a, no user-visible change
  • Commits follow Conventional Commits

Related issues

Added

  • Added CodeRabbit review gates for dependencies, concurrency, typing, tests, documentation, CI security, changelog updates, and public API changes.
  • Added Conventional Commit title validation.
  • Added knowledge-base sources for AGENTS.md and CONTRIBUTING.md.

Changed

  • Added review rules for state-machine behavior, engine lock scope, public API changes, and supported Python versions.
  • Set main as the review base branch.
  • Disabled pylint, markdownlint, docstring coverage, and automatic label application.

Add `.coderabbit.yaml` so the automated review enforces the rules from
AGENTS.md that a generic Python reviewer cannot infer from a diff.

- Eleven `path_instructions` covering the zero-dependency core, the
  `_notify.py` sanctioned swallow, the engine lock scope, `Clock`-only
  time, the test naming convention and the SHA-pinning rule for
  workflows.
- Five custom pre-merge checks mirroring the definition of done:
  zero-dependency core (error), changelog entry, docs plus the
  `llms-full.txt` mirror, tests alongside behaviour changes and public
  API surface.
- Disable pylint and markdownlint: ruff and pymarkdown already own those
  files, and a second unconfigured rule set is pure noise.
- Keep `auto_apply_labels` off. The `breaking-change` label disables the
  griffe API gate, so it stays a human decision.
- Point `code_guidelines.filePatterns` at AGENTS.md and CONTRIBUTING.md;
  the default patterns look for CLAUDE.md files, which are gitignored.

`base_branches` names `main` explicitly: right after installation the bot
skipped PR #129 as targeting a non-default branch even though its base is
`main`.
@codspeed-hq

codspeed-hq Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 21 untouched benchmarks


Comparing ci/coderabbit-review-config (4fbc60c) with main (fb40c51)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (c8bd26b) during the generation of this report, so fb40c51 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@codecov

codecov Bot commented Aug 7, 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 commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Added a complete .coderabbit.yaml configuration. It defines Python review rules, directory-specific guidance, automated checks, enabled analysis tools, repository knowledge sources, and code-generation conventions.

Changes

CodeRabbit configuration

Layer / File(s) Summary
Review rules and repository guidance
.coderabbit.yaml
Defines global Python rules and focused guidance for state machines, integrations, tests, benchmarks, examples, documentation, CI, and project conventions.
Review automation and validation
.coderabbit.yaml
Configures review triggers, finishing touches, pre-merge checks, and enabled analysis tools.
Assistant behavior and code generation
.coderabbit.yaml
Configures chat behavior, knowledge sources, repository guidance files, learning scopes, and Python docstring and pytest generation rules.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 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 ci prefix, an imperative lowercase summary, no trailing period, and remains under 72 characters.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Zero-Dependency Core ✅ Passed PR diff adds only .coderabbit.yaml; core imports and init.py are unchanged, and pyproject.toml dependencies remains [] as in the parent commit.
Changelog Entry ✅ Passed The commit changes only the root-level .coderabbit.yaml, an explicitly exempt developer-tooling dot-file; no published package code changes.
Docs And Llm Mirror ✅ Passed HEAD changes only .coderabbit.yaml; no interlock/, integration, or runtime configuration files changed, so the user-facing behavior condition does not apply.
Tests Accompany Behaviour Change ✅ Passed The PR changes only .coderabbit.yaml; no .py file under interlock/ and no behavior change is present.
Public Api Surface ✅ Passed PR adds only .coderabbit.yaml; interlock/init.py, interlock/pipeline.py, and CHANGELOG.md are unchanged, with no export or local signature differences.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/coderabbit-review-config

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

@bagowix
bagowix merged commit 0f2aa6b into main Aug 7, 2026
18 checks passed
@bagowix
bagowix deleted the ci/coderabbit-review-config branch August 7, 2026 15:30

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 @.coderabbit.yaml:
- Around line 116-118: Align the public API definitions in the two review rules:
update the rule around the interlock/__init__.py guidance and the rule around
interlock/pipeline.py so both recognize the same exported symbols and apply
identical __all__ and docstring requirements. Preserve the existing Python 3.11
compatibility rule.
- Around line 122-126: Update the critical-section guidance in the
.coderabbit.yaml comment to say that holding the threading.Lock across the
protected call can deadlock, rather than asserting it always deadlocks; retain
the existing warning about serialization and throughput impact.
🪄 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: 7bfa1ded-e09b-4755-8dc6-01c39913e39c

📥 Commits

Reviewing files that changed from the base of the PR and between c8bd26b and 4fbc60c.

📒 Files selected for processing (1)
  • .coderabbit.yaml

Comment thread .coderabbit.yaml
Comment on lines +116 to +118
(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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Resolve the public API definition conflict.

Lines 116-118 define only interlock/__init__.py exports as public. Lines 286-292 also treat interlock/pipeline.py symbols as public. A valid interlock.pipeline export can therefore receive incompatible review feedback. Define the same public API surface in both rules.

🤖 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 @.coderabbit.yaml around lines 116 - 118, Align the public API definitions in
the two review rules: update the rule around the interlock/__init__.py guidance
and the rule around interlock/pipeline.py so both recognize the same exported
symbols and apply identical __all__ and docstring requirements. Preserve the
existing Python 3.11 compatibility rule.

Comment thread .coderabbit.yaml
Comment on lines +122 to +126
The critical section. Verify: the state machine stays I/O-free and
unaware of sync vs async; the threading.Lock covers only the await-free
acquire and record sections and is never held across the protected call
(a call under the lock is a deadlock and a throughput bug); HALF_OPEN
still bounds concurrent probes. Ask whether `uv run mutmut run` was run —

Copy link
Copy Markdown

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python - <<'PY'
import threading

lock = threading.Lock()
assert lock.acquire()
try:
    assert lock.acquire(blocking=False) is False
finally:
    lock.release()
PY

Repository: bagowix/interlock

Length of output: 155


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- .coderabbit.yaml ---'
sed -n '118,130p' .coderabbit.yaml

printf '%s\n' '--- AGENTS.md files ---'
find .. -name AGENTS.md -print

python3 - <<'PY'
import threading

# A call that does not touch the lock completes while the lock is held.
lock = threading.Lock()
completed = []

def independent_call():
    completed.append("done")

with lock:
    independent_call()
assert completed == ["done"]

# A same-thread re-entry cannot acquire a non-reentrant Lock.
reentrant = threading.Lock()
with reentrant:
    assert reentrant.acquire(blocking=False) is False

# A second caller waits, but does not itself create a deadlock.
serialized = threading.Lock()
started = threading.Event()
finished = threading.Event()

def caller():
    started.set()
    with serialized:
        finished.set()

with serialized:
    thread = threading.Thread(target=caller)
    thread.start()
    assert started.wait(1)
    assert not finished.is_set()

serialized.release()
thread.join(1)
assert finished.is_set() and not thread.is_alive()
PY

Repository: bagowix/interlock

Length of output: 1267


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import threading

# Holding the lock does not deadlock an independent call.
lock = threading.Lock()
completed = []

def independent_call():
    completed.append("done")

lock.acquire()
try:
    independent_call()
finally:
    lock.release()
assert completed == ["done"]

# A same-thread re-entry cannot acquire a non-reentrant Lock.
lock.acquire()
try:
    assert lock.acquire(blocking=False) is False
finally:
    lock.release()

# A second caller waits and then proceeds after the lock is released.
lock.acquire()
started = threading.Event()
finished = threading.Event()

def caller():
    started.set()
    with lock:
        finished.set()

thread = threading.Thread(target=caller)
thread.start()
assert started.wait(1)
assert not finished.is_set()
lock.release()
thread.join(1)
assert finished.is_set() and not thread.is_alive()
PY

Repository: bagowix/interlock

Length of output: 155


Change “is a deadlock” to “can deadlock.” Holding threading.Lock across a call serializes callers. It deadlocks only if the call re-enters the lock or waits for work that requires it.

🤖 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 @.coderabbit.yaml around lines 122 - 126, Update the critical-section
guidance in the .coderabbit.yaml comment to say that holding the threading.Lock
across the protected call can deadlock, rather than asserting it always
deadlocks; retain the existing warning about serialization and throughput
impact.

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.

1 participant