Skip to content

fix(swr): LAB-381 panel fast-follow — contextvar propagation, CWE-532 redaction, orphan cut - #235

Merged
27Bslash6 merged 3 commits into
mainfrom
lab-381-swr-panel-fixes
Jul 22, 2026
Merged

fix(swr): LAB-381 panel fast-follow — contextvar propagation, CWE-532 redaction, orphan cut#235
27Bslash6 merged 3 commits into
mainfrom
lab-381-swr-panel-fixes

Conversation

@27Bslash6

Copy link
Copy Markdown
Contributor

LAB-381: SWR panel fast-follow — contextvar propagation, CWE-532 redaction, orphan cut, dedup

Fast-follow to merged #228, implementing the expert panel's FIX-FIRST findings (Ray-approved on LAB-381). Blocks release-please #234 (v0.14.0) — hold that until this merges so SWR doesn't ship with the items below.

Must-fix

  • [MAJ] contextvar propagation in sync background revalidation. The daemon thread now runs under a contextvars.copy_context() snapshot captured in-request, matching what asyncio.create_task gives the async path. Previously, @cache + encryption + ContextVarExtractor hit an unset tenant var off-thread → fail-closed ValueError → swallowed → background refresh silently no-oped and every stale window degraded to a sync miss at evict_at. The AC test proves revalidation executes AND stores through the real encryption stack with a contextvar-only tenant.
  • [MIN / CWE-532] key redaction. SWR debug logs emit redact_cache_key() digests; the revalidation thread name is static (cachekit-swr-revalidate). Regression test forces the debug line and asserts no raw key in logs or thread names.

Should-fix (Ray agreed the recommendations)

  • Orphan cut: CachekitIOBackend.get_with_freshness_async + its test class removed (the handler layer runs sync backend methods in the thread pool; the async variant had zero callers — the LAB-388 trust-bug pattern). Dead refresh_ttl params dropped from both operation-handler freshness getters.
  • Disentangled machinery: backed-mode SWR gets _L2_SWR_MAX_CONCURRENT_REFRESHES; _swr_*_l2_swr_* so it no longer shares names or a constant with the _l1_swr_* machinery in the same file.
  • Fail-closed propagation tests through both freshness getters (mirrors the get_cached_value_async contract): DecryptionAuthenticationError raises, poisoned entry retained as evidence — an except-clause reorder can't demote to fail-open with green tests.
  • Dedup (net deletion in source): the 4× decrypt-failure tail → _handle_l2_read_error[_async]; the 4× L1-put idiom → _put_l1.

Docs gate

The contextvar fix changes documented behavior: the cache() docstring and configuration.md now state that the background recompute sees a snapshot of the caller's contextvars (contextvar-based tenant extraction works) while other request-scoped resources remain unavailable.

Verification

2197 tests pass (7 new: contextvar AC through real encryption, CWE-532 log/thread assertions, 2× fail-closed propagation), ruff clean, basedpyright 0 errors. Source diff is net-negative outside tests.

…action, orphan cut, dedup

Panel FIX-FIRST verdict on merged #228 head (Ray-approved); gates v0.14.0.

Must-fix:
- [MAJ] Sync background revalidation now runs under a copy_context()
  snapshot captured in-request, so contextvar-based tenant extraction
  (ContextVarExtractor + encryption) works off-thread exactly as the async
  path does via create_task. Without this the daemon thread hit an unset
  var -> fail-closed ValueError -> silently swallowed -> every stale window
  degraded to a sync miss at evict_at. AC test proves the revalidation
  actually executes AND stores through the real encryption stack.
- [MIN/CWE-532] The three SWR debug logs emit redact_cache_key() digests;
  the revalidation thread name is static ('cachekit-swr-revalidate', no key
  slice). Regression test forces the debug line and asserts no raw key.

Should-fix (Ray agreed the recommendations):
- Cut orphaned CachekitIOBackend.get_with_freshness_async + its tests (the
  handler runs sync backend methods in the thread pool; the async variant
  had no callers — LAB-388 trust-bug pattern). Dropped the dead refresh_ttl
  params from both operation-handler freshness getters.
- Backed-mode SWR gets its own _L2_SWR_MAX_CONCURRENT_REFRESHES constant;
  _swr_* -> _l2_swr_* rename disentangles it from the _l1_swr_* machinery.
- Fail-closed propagation tests through BOTH freshness getters
  (DecryptionAuthenticationError raises; poisoned entry retained) so an
  except-reorder can't demote to fail-open with green tests.
- Dedup, net deletion in source: 4x decrypt-failure tail ->
  _handle_l2_read_error[_async]; 4x L1-put idiom -> _put_l1 closure.

Docs pass: the contextvar fix CHANGES documented behavior — cache()
docstring + configuration.md now state contextvars are snapshotted and
visible to the recompute (other request-scoped resources still are not).

Gates: 2197 passed (7 new tests), ruff clean, basedpyright 0 errors.
Co-authored-by: multica-agent <github@multica.ai>
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 52 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9ad105eb-9372-44fd-84ae-08128a17736d

📥 Commits

Reviewing files that changed from the base of the PR and between 83b7c92 and 80c34d0.

📒 Files selected for processing (7)
  • docs/configuration.md
  • src/cachekit/backends/cachekitio/backend.py
  • src/cachekit/cache_handler.py
  • src/cachekit/decorators/intent.py
  • src/cachekit/decorators/wrapper.py
  • tests/unit/backends/test_cachekitio_swr_transport.py
  • tests/unit/test_swr_decorator.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch lab-381-swr-panel-fixes

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

@codecov

codecov Bot commented Jul 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.93671% with 4 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/cachekit/cache_handler.py 89.28% 1 Missing and 2 partials ⚠️
src/cachekit/decorators/wrapper.py 98.03% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

…E-532)

The crypto re-review of the fast-follow verified LAB-108 preservation,
contextvar isolation, and the rename/dedup, and found two remaining
redaction gaps on the SWR surface:

- StandardCacheHandler.get_with_freshness[_async] + the operation-handler
  freshness getters logged raw cache_key at ERROR/WARNING on backend
  errors — fires on routine outages, tenant-bearing keys in prod logs.
- The L1-only SWR debug lines (+ shared TTL-refresh done-callback) in
  wrapper.py still emitted raw keys.

All now use redact_cache_key(). NOTE: the pre-existing raw-key logging
convention in the non-SWR getters (plain get/get_buffer, sets) is
deliberately out of scope for this release-gating PR — repo-wide sweep
belongs in its own issue.

2197 tests green; ruff clean.

Co-authored-by: multica-agent <github@multica.ai>
@27Bslash6 27Bslash6 changed the title LAB-381: SWR panel fast-follow — contextvar propagation, CWE-532 redaction, orphan cut fix(swr): LAB-381 panel fast-follow — contextvar propagation, CWE-532 redaction, orphan cut Jul 22, 2026
# Conflicts:
#	src/cachekit/decorators/wrapper.py
@27Bslash6
27Bslash6 merged commit 9e276b6 into main Jul 22, 2026
14 checks passed
@27Bslash6
27Bslash6 deleted the lab-381-swr-panel-fixes branch July 22, 2026 12:00
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