ci: stabilize ratchet digests across the Python matrix - #857
Conversation
The reviewed manifests in tests/ratchets pin an ast_hash per classified
source site, and CI verifies those same manifests on 3.11 and 3.14. The
digests came from ast.dump, whose output is not stable across that range:
* 3.12 added PEP 695 type_params to FunctionDef/AsyncFunctionDef/ClassDef.
* 3.13 started omitting fields equal to their default, so args=[] and
keywords=[] vanish from the dump.
Every hash for the affected nodes therefore shifted on 3.14, failing five
ratchets with a wall of "removed or structurally changed" sites that no
commit caused. Validate Quick (3.14) has been red on main since #808, and
it fails every PR that touches nothing related, Dependabot's included.
Pin one canonical serialization instead of inheriting the stdlib's. It
reproduces 3.11's ast.dump(annotate_fields=True, include_attributes=False)
byte for byte -- verified over 1.38M nodes across src/ and tests/ -- so the
baselines reviewed against that output stay valid and no manifest changes.
The digest is now identical on 3.11, 3.12, 3.13, and 3.14.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The competitor benchmark environments are deliberately frozen: `uv lock
--exclude-newer` records the cutoff in each lock's [options] table so the
benchmark resolves the same transitive snapshot every run. Regenerating one
of those locks without the flag drops the table, which is exactly what a
Dependabot bump to perf/framework_environments/<framework> produces.
The cutoff was read at import time and indexed with ["options"], so that
bump raised KeyError('options') during collection: the whole perf module
errored out, 0 of its tests ran, and the quick lane reported an opaque
missing key. The literal-pinned cutoff assertion that exists to catch this
drift never got to run. PR #855 fails this way today, and #591 landed the
same drift before.
Read the cutoff leniently and resolve it where it is used, so a stripped
lock now fails as three focused tests naming the lock and the command to
re-pin it, while the module's other 13 tests still run.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 51 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe benchmark now validates missing lock cutoff metadata and shares competitor definitions. Ratchet inventories now use centralized, interpreter-stable AST serialization and digest helpers. ChangesBenchmark lock validation
Shared AST digest ratchet
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
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 `@perf/bench_framework_latency.py`:
- Line 50: Remove synchronous lock-file I/O from perf/bench_framework_latency.py
by replacing import-time cache construction and Path.read_text() usage with
explicit asynchronous lock initialization, then make every lock-dependent caller
await that initialization. In tests/perf/test_framework_latency_benchmark.py at
line 149, convert the test to async and await the fixture file write.
In `@tests/perf/test_framework_latency_benchmark.py`:
- Around line 155-170: The test covers require_lock_exclude_newer and
worker_specs under the missing cutoff condition, but does not verify the
validation in _lock_metadata itself. Under the same monkeypatch setup
(LOCK_EXCLUDE_NEWER_BY_FRAMEWORK with "pipecat" set to None), add an assertion
that _lock_metadata raises RuntimeError when invoked. This ensures the
report-generation validation in _lock_metadata is tested and prevents future
refactoring from removing it undetected.
🪄 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: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 38bed6ae-00ed-4822-9d18-d9cb024ffefd
📒 Files selected for processing (10)
perf/bench_framework_latency.pytests/perf/test_framework_latency_benchmark.pytests/ratchets/_ast_digest.pytests/ratchets/_pause_generation_inventory.pytests/ratchets/_source_inventory.pytests/ratchets/_teardown_budget_inventory.pytests/ratchets/_turn_commit_inventory.pytests/ratchets/_turn_lifecycle_inventory.pytests/ratchets/_turn_predicate_inventory.pytests/ratchets/test_ast_digest.py
_lock_metadata() gained the same cutoff guard as worker_specs(), but only the latter was asserted. Removing the guard from the report path left the suite green; it now fails with DID NOT RAISE. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why the Dependabot PRs are failing
Two unrelated causes, both ours rather than the bumps':
#856 (cryptography 49→50) —
Validate Quick (3.14)only. The reviewed manifests intests/ratchets/pin anast_hashper classified source site, and CI verifies the same manifests on 3.11 and 3.14. Those digests came fromast.dump, whose output is not stable across that range:type_paramstoFunctionDef/AsyncFunctionDef/ClassDefargs=[]/keywords=[]vanishSo every hash for the affected nodes shifted on 3.14 and five ratchets failed with a wall of "removed or structurally changed" sites that no commit caused. This is not specific to Dependabot —
Validate Quick (3.14)has been red onmainsince #808 and fails any PR that touches nothing related.#855 (aiohttp in
/perf/framework_environments/pipecat) — nearly every job. Those competitor environments are deliberately frozen:uv lock --exclude-newerrecords the cutoff in the lock's[options]table so the benchmark resolves the same transitive snapshot each run. Dependabot regenerated the lock without the flag, dropping the table. The cutoff was read at import with["options"], so it raisedKeyError: 'options'during collection — the whole perf module errored, 0 of its tests ran, and the lane reported an opaque missing key. The literal-pinned cutoff assertion that exists to catch exactly this drift never got to run.What this changes
tests/ratchets/_ast_digest.pypins one canonical serialization instead of inheriting the stdlib's. It reproduces 3.11'sast.dump(annotate_fields=True, include_attributes=False)byte for byte — verified over 1,379,718 nodes acrosssrc/andtests/— so baselines reviewed against that output stay valid and no manifest changes were needed. The digest is now identical on 3.11, 3.12, 3.13, and 3.14.test_ast_digest.pypins the canonical form and digest so a revert toast.dumpfails loudly on whichever interpreter regresses.I enumerated every AST field difference across 3.11–3.14 programmatically rather than assuming:
type_paramson those three node types is the only one.perf/bench_framework_latency.pyreads the cutoff leniently and resolves it at use. A stripped lock now fails as three focused tests naming the lock and the exact re-pin command, while the module's other 13 tests still run:Verification
Full credential-free suite, run with each interpreter:
pre-commit run --all-files(incl. ruff, import-linter, zizmor),mypy src/easycat, andruff format --checkall pass. TheKeyErrorpath was reproduced by stripping[options]from the real pipecat lock, then restored.Follow-ups for the two open PRs
KeyError. The same drift landed once before in build(deps): bump pipecat-ai from 1.0.0 to 1.4.0 in /perf/framework_environments/pipecat #591 and needed manual repair (perf: repair locked benchmark worker,perf: stabilize framework locks).perf/framework_environments/*despitedirectory: "/". I did not touchdependabot.yml: GitHub documents glob support fordirectoriesbut documents no negation/exclusion syntax, so any exclusion I added would be unverifiable in CI and might silently do nothing. Worth a follow-up that can be validated against a live Dependabot run.🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Refactor
Tests