Skip to content

fix(auth): make org session policy and cookie-cache version cache-coherent#5947

Open
waleedlatif1 wants to merge 13 commits into
stagingfrom
fix/session-policy-cache-coherence
Open

fix(auth): make org session policy and cookie-cache version cache-coherent#5947
waleedlatif1 wants to merge 13 commits into
stagingfrom
fix/session-policy-cache-coherence

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #5862 (org session policies). An audit of that PR found one real correctness bug plus several smaller issues; this fixes them.

The bug. #5862 split an org's securityPolicyVersion and sessionPolicySettings across two independent process-local caches (60s TTL each, invalidated only on the pod that handled the write). On a multi-replica deployment a pod could observe the bumped version while still holding the pre-save policy, and that skew silently undid a just-saved tightening:

  1. Saving a policy eagerly clamps every member session in SQL. Shortening expires_at makes Better Auth's refresh predicate (expiresAt - expiresIn + updateAge <= now, api/routes/session.mjs:205) unconditionally true — so the save itself marks the whole org due for refresh.
  2. The version bump expires the session-data cookie (session.mjs:96), forcing the DB path.
  3. The refresh proposes now + 30d and session.update.before re-clamps it — but against the stale policy, so it passed through unclamped, reverting the eager clamp.
  4. With expiresAt back at 30 days the predicate is false again, so nothing re-clamped that session for another ~24h.

Net effect: enterprise session bounds silently unenforced for up to ~24h for an unpredictable subset of sessions. Worst case is an org's first policy save, where the stale value is "no policy" — i.e. zero enforcement. values-production.yaml ships replicaCount: 2 and the HPA maxes at 10, so this is the deployed topology.

The fix. Both fields live on the same organization row, so they are now read and cached as one record. A pod that still sees version N also serves policy N and forces no cookie mismatch — the skew is removed by construction rather than one direction of it being patched. Session CREATE additionally reads through the cache: unlike a refresh it is not already preceded by a version-mismatch read, so a stale record would otherwise mint a full-length session that nothing re-clamps for 24h.

Also in this change

  • A failed entitlement read no longer disables enforcement. isOrganizationOnEnterprisePlan swallows its own errors and returns false, so getSessionPolicy could not distinguish "downgraded" from "the check threw" — a transient billing-read blip silently stopped enforcing a security control. Added resolveOrganizationEnterprisePlan, which propagates errors; isOrganizationOnEnterprisePlan keeps its fail-closed contract for every existing caller. Stored bounds are only writable by an entitled org, so their presence is the source of truth when the check is unavailable — the same reasoning already documented on the PII-redaction path in lib/logs/execution/logger.ts.
  • A failed org-record read serves the last known-good record instead of falling back to defaults, which would have dropped the org's bounds entirely. A sustained outage retries on a short backoff rather than on every request.
  • A failed membership lookup in session.create.before no longer skips the clamp — it falls back to the cached membership instead of minting a 30-day session off a transient error.
  • Both caches are now bounded (expired entries first, then least-recently-refreshed). They previously grew one entry per distinct org/user for the life of the process.
  • The cookie-cache version fn short-circuits when organizations are disabled, so those deployments pay no lookup on the authenticated hot path.
  • Idle-timeout copy corrected. Activity is recorded at most once per 24h, so expiresAt = lastRefresh + N and sign-out lands between N-24 and N hours after last use — never later. The docs and UI hint claimed an exact N. Behavior is unchanged and always errs conservative; only the copy was wrong.

Type of Change

  • Bug fix
  • Documentation update

Testing

  • New security-policy.test.ts: the one-row collapse (the coherence guarantee), invalidation, cache bypass, last-known-good on failure, cookie-version format, org-less and orgs-disabled short-circuits.
  • Extended session-policy.test.ts: entitlement gating incl. the failure path, impersonation exemption, membership fallback, freshMembershipOrgId short-circuit, ISO-string normalization, cache bypass.
  • New sessions/revoke/route.test.ts — this route had no tests and is the only destructive endpoint in the feature. Covers all four authz branches, the 404, and the two easy-to-break carve-outs: the caller's own token and the impersonator's real sessions.
  • 1,346 tests green across lib/auth, lib/billing, api/organizations, lib/invitations, components/settings, ee.
  • Typecheck, biome, and check:api-validation all pass.

Not included (evaluated and deliberately skipped)

  • Better Auth secondaryStorage (Redis-backed sessions). Verified in node_modules: storeSessionInDatabase defaults falsy, so the session table would stop being written — eagerClampOrgSessions and the org-wide revoke-all would match zero rows and report success, writing a "Revoked 0 member sessions" audit record while every session stayed alive. It also suppresses verification rows, breaking apps/realtime one-time-token auth, and makes Redis a hard dependency for all authentication. It moves session rows; the stale caches hold org policy/version/membership, so it does not address this bug at all.
  • Redis pub/sub cache invalidation. Genuinely attractive as a follow-up — it would cut org-wide revocation latency from ~60s to milliseconds, and ioredis + lib/core/config/redis.ts already exist as an optional dependency. Kept separate because pub/sub is fire-and-forget: it narrows the window but cannot close it, so the in-process fix is required either way.
  • Raising NEGATIVE_MEMBERSHIP_CACHE_TTL_MS to cut hot-path queries. Measured the load first: the member lookup is a unique-index point lookup on a tiny table, and the added volume is a small fraction of what a single authenticated request already spends on authz. Meanwhile the 15s value is load-bearing — Better Auth's SSO plugin writes member rows straight through the adapter and this app registers no member hook, so raising it would widen the revocation-bypass window for JIT-provisioned members to buy a negligible saving.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

…erent

The session-policy enforcement added in #5862 split an org's
`securityPolicyVersion` and `sessionPolicySettings` across two independent
process-local caches. On a multi-replica deployment a pod could observe the
bumped version while still holding the pre-save policy, and that skew silently
undid a just-saved tightening:

1. Saving a policy eagerly clamps every member session in SQL. Shortening
   `expires_at` makes Better Auth's refresh predicate
   (`expiresAt - expiresIn + updateAge <= now`) unconditionally true, so the
   save itself marks the whole org due for refresh.
2. The version bump expires the session-data cookie, forcing the DB path.
3. The refresh proposes `now + 30d` and `session.update.before` re-clamps it —
   but against the stale policy, so it passed through unclamped.
4. With `expiresAt` back at 30 days the predicate is false again, so nothing
   re-clamped that session for another ~24h.

Both fields live on the same `organization` row, so they are now read and
cached as one record: a pod that still sees version N also serves policy N and
forces no cookie mismatch, removing the skew by construction rather than
patching one direction of it. Session CREATE additionally reads through the
cache — unlike a refresh it is not already preceded by a version-mismatch read,
so a stale record would otherwise mint a full-length session.

Also in this change:

- A failed entitlement read no longer disables enforcement. Stored bounds are
  only writable by an entitled org, so their presence is the source of truth
  when the plan check is unavailable; `resolveOrganizationEnterprisePlan`
  propagates errors so "downgraded" and "check failed" stay distinguishable.
  This matches the reasoning already documented on the PII-redaction path.
- A failed org-record read serves the last known-good record instead of
  falling back to defaults, which would have dropped the org's bounds entirely.
- A failed membership lookup in `session.create.before` no longer skips the
  clamp; it falls back to the cached membership.
- Both caches are bounded (expired entries first, then least-recently-refreshed)
  — they previously grew one entry per distinct org/user for the process life.
- The cookie-cache version fn short-circuits when organizations are disabled,
  so those deployments pay no lookup on the authenticated hot path.
- Idle-timeout copy now states the real guarantee: activity is recorded at most
  once per 24h, so sign-out lands between (value - 24) and (value) hours after
  last use, never later.

Tests: new coverage for the org-record collapse, last-known-good behavior,
entitlement failure, the impersonation exemption, and the previously untested
revoke route (authz branches, self-token and impersonator exclusions).
@vercel

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jul 25, 2026 2:31am

Request Review

@cursor

cursor Bot commented Jul 25, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Changes authentication session create/update, policy enforcement, and cookie-cache versioning—security-critical paths where incorrect clamping or stale policy could weaken enterprise session controls.

Overview
Fixes a multi-replica bug where a bumped security policy version could pair with a stale cached session policy, letting session refreshes re-clamp to old limits and undo a just-saved tightening for up to ~24h.

Enforcement: getSessionPolicy no longer uses a process cache—it reads the org row on every clamp (sign-in / refresh). Enterprise entitlement for enforcement uses resolveOrganizationEnterprisePlan so a billing read failure does not silently drop stored bounds. Session create / update hooks resolve membership once, fail closed for known org members when policy cannot be read, and refuse to extend expiry on refresh errors; an after hook re-applies policy for SSO JIT sessions created before the member row exists.

Cookie invalidation: Policy save and org-wide revoke call setSecurityPolicyVersion with the RETURNING version instead of invalidating caches; version lookup uses bounded LRU caches, monotonic guards against late reads, and safer fallbacks than defaulting to version 1. Membership resolution is centralized in security-policy (with short negative TTL and invalidateMembershipCache).

API / UX: Revoke route returns the bumped version from the transaction and publishes it; docs and session-policy UI clarify idle timeout as a window (activity logged at most every 24h), with a new FAQ.

Tests: New coverage for security-policy, extended session-policy, and the org sessions revoke route.

Reviewed by Cursor Bugbot for commit 6bb1323. Configure here.

@greptile-apps

greptile-apps Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This follow-up strengthens organization session-policy enforcement and cache coherence.

  • Removes session-policy caching so enforcement reads current organization settings.
  • Adds monotonic publication for cached security-policy versions after policy updates and org-wide revocations.
  • Refines membership and policy-read failure handling across session creation and refresh.
  • Re-clamps sessions created before SSO JIT membership exists.
  • Bounds version and membership caches and updates idle-timeout documentation and UI copy.
  • Expands coverage for policy enforcement, cache behavior, authorization, and session revocation.

Confidence Score: 5/5

The PR appears safe to merge within the scope of the prior review threads.

No blocking failures remain.

Important Files Changed

Filename Overview
apps/sim/lib/auth/security-policy.ts Introduces bounded LRU caches, monotonic version publication, explicit membership lookup semantics, and organization-disabled short-circuiting.
apps/sim/lib/auth/session-policy.ts Removes policy caching, reads stored policy settings at enforcement time, and preserves enforcement when entitlement resolution fails.
apps/sim/lib/auth/auth.ts Resolves membership once during session creation, prevents expiry extension on policy-read failure, and handles post-session SSO JIT membership.
apps/sim/app/api/organizations/[id]/session-policy/route.ts Publishes the committed security-policy version after atomically saving policy settings and clamping sessions.
apps/sim/app/api/organizations/[id]/sessions/revoke/route.ts Returns and publishes the committed version from the atomic session-revocation transaction.

Reviews (11): Last reviewed commit: "revert(auth): drop the TTL-overrun retry..." | Re-trigger Greptile

Comment thread apps/sim/lib/auth/security-policy.ts Outdated
Comment thread apps/sim/lib/auth/security-policy.ts Outdated
Comment thread apps/sim/lib/billing/core/subscription.ts
Self-review follow-ups on the cache-coherence change:

- Splitting the entitlement check out of the combined policy cache meant it ran
  on every session refresh. A policy save or org-wide revoke makes every member
  session refresh at once, so a large org would have stampeded the billing
  tables with one plan check per session. Entitlement is now cached on its own
  TTL, independent of the org security record (a policy save invalidates the
  policy but says nothing about the plan). Staleness is harmless in both
  directions, and a failed check falls back to the last known decision.
- Pruning trimmed exactly to the cap, so the next insert was over again and
  every subsequent write rescanned the whole map. Prune to a low water mark
  instead, which keeps eviction amortized O(1).
- The session create hook no longer writes an `expiresAt: undefined` key when
  the clamp has nothing to narrow; it only ever overrides a real value.
- Drop the redundant `isBillingEnabled` short-circuit — the same condition is
  already the first branch of `resolveOrganizationEnterprisePlan`.
Follows the pattern the two sibling enterprise-settings call sites already
establish. `lib/logs/execution/logger.ts` and
`lib/workflows/executor/execution-core.ts` both resolve org enterprise settings
with a single indexed read at enforcement time and carry explicit comments
rejecting a cached/re-checked resolution, because a stale or failed read there
silently skips the control.

The session policy is the same shape of thing, and it is not a hot path: it is
read on sign-in and on a sliding session refresh, which the 24h cookie cache
limits to roughly once per user per day. Caching it bought nothing and cost the
correctness bug this branch set out to fix — so drop the cache rather than build
machinery to keep two caches coherent. Better Auth also documents
`cookieCache.version` as a static string bumped at deploy and offers no
cross-instance invalidation, so the version TTL is inherent to the design while
the policy cache never was.

This removes, rather than fixes, the whole class of problems the previous
revision introduced: no merged record, no cache-bypass flag, no last-known-good
fallback, no entitlement cache, no destructive-invalidation window, and no
ordering hazard between concurrent readers. `security-policy.ts` is back to
caching only the cookie-cache version — which genuinely is per-request — plus
the membership lookup.

Failure posture is now chosen per call site instead of globally fail-open:

- `getSessionPolicy` and `getMemberOrganizationId` propagate read errors.
- The session UPDATE hook refuses to EXTEND on a failed read, keeping the
  member's current expiry rather than granting a fresh 30 days the policy may
  forbid.
- The session CREATE hook allows the sign-in but logs that the session went
  unclamped — refusing sign-ins would turn a read blip into an org-wide outage.
- `getSessionCookieCacheVersion` still never throws; it runs on every request,
  and its fallback only costs a cookie mismatch and a database session read.

Kept from the previous revision: the entry caps with low-water-mark eviction,
the organizations-disabled short-circuit, the propagating enterprise-plan
resolver, and the corrected idle-timeout copy.
…resolver

Review catch. `resolveOrganizationEnterprisePlan` existed to distinguish "not
entitled" from "the check could not run", but it called
`getOrganizationSubscriptionUsable` with the default `onError: 'return-null'` —
so a transient subscription read failed to `null`, read as "no usable
subscription", and reported a downgrade anyway. The one failure mode the
resolver was written for was the one it still swallowed.

Also guard the version cache against out-of-order writes: the counter only ever
increments, so a read resolving lower than the cached value started before the
newer one and must not overwrite it.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/auth/auth.ts
Comment thread apps/sim/lib/auth/security-policy.ts Outdated
…cache

Review catch. The monotonic guard skipped STORING a superseded version but
still RETURNED it, and clearing the entry on write left no floor to compare
against — so an in-flight pre-bump read could land after the bump and re-seed
the old version for a full TTL. Either path keeps cookie versions matched, which
is precisely how a revoked session keeps serving from the cookie cache.

Both routes already compute the new counter, so they now return it from the
same UPDATE and publish it directly. That leaves the cache always holding a
floor, makes a late read unable to win on either the return path or the store
path, and drops a redundant read.

Also documents the session-create trade explicitly: when both the direct
membership read and the cached fallback fail, the org is unknown and the
sign-in proceeds unclamped. Refusing it would convert a `member` read failure
into a total authentication outage for every user, including the majority who
belong to no org. The delay is bounded — the update hook clamps retroactively
from `createdAt` at the next cookie-cache expiry, and any admin action bumps
the version and closes the window immediately.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/auth/auth.ts Outdated
Review catch, and the reviewer is right that the previous comment described
behavior the code no longer had. `getSessionPolicy` now throws, so the create
hook's catch covered two different failures and answered both the same way.

They deserve opposite answers:

- Org KNOWN — membership resolved, only the policy read failed. This user is
  definitely governed by an org, so the "would break sign-in for everyone"
  rationale simply does not apply. Fail closed. Minting a full-length session
  for a member whose policy could not be read is the exact outcome the feature
  exists to prevent, and the blast radius is org members during a window where
  the `organization` read fails while the `member` read just succeeded.
- Org UNKNOWN — both membership reads failed, so a governed member is
  indistinguishable from the majority who belong to no org. Failing closed here
  would take authentication down for everyone to protect a policy most of them
  do not have. Allow, and let the update hook clamp retroactively.

Throwing an APIError from this hook is the established pattern in this file —
the blocked-email check directly above does the same.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit b467685. Configure here.

Line-by-line pass over the final diff. No behavior change; every item is either
a comment that had drifted from the code or state the code did not need.

- The create hook's comment claimed a failed membership read still clamps "via
  the cached membership". Since `getMemberOrganizationId` now throws, that only
  holds when the cache is warm. Say what the code actually does: `null` means
  confirmed org-less, `undefined` means could-not-tell, and the catch branches
  on which one it got.
- `isPolicyEnforced(orgId, hasBounds)` took a boolean parameter that inverted
  its own name — it returned `true` for an org with no policy at all. Split
  into a plain early return for the no-bounds case and `isEntitledToEnforce`,
  which now only answers the question its name asks.
- The revoke route aliased a transaction result to rename one field; destructure
  it instead.
- The policy UPDATE returned `id` purely as an existence check that the version
  column it also returns already provides.
- Two test names still described invalidating a cache that is now published to.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit b9d8047. Configure here.

- `getSessionPolicy`'s docstring still said the CREATE hook allows the sign-in
  on a failed read. It has refused when the governing org is known since the
  fail-closed split; say so.
- `SECURITY_POLICY_VERSION_CACHE_TTL_MS` had no consumer outside this module,
  and exporting one of the two sibling TTLs but not the other was arbitrary.
- Pass an explicit `ttl` for both membership outcomes instead of relying on
  `undefined` to mean the constructor default, so the positive/negative
  asymmetry is visible at the call site.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 30d5496. Configure here.

Two defects found by an independent audit of the session lifecycle.

1. First SSO sign-in into a policy-bearing org produced an UNCLAMPED 30-day
   session. `@better-auth/sso` creates the session before it writes `member`
   (`handleOAuthUserInfo` then `assignOrganizationFromProvider`), and it writes
   through the adapter directly, so no member hook fires. The create hook saw no
   membership and applied Better Auth's default expiry — and because a 30-day
   expiry reads as "not due for refresh", the sliding refresh would not re-clamp
   it for another day. Against an 8-hour policy that is a full day of bypass, on
   every first SSO login, deterministically.

   Closed with an `after` hook that re-applies the policy once membership is
   visible. It runs only for sessions created without an org — the only ones
   that can be affected — and is best-effort so it can never break sign-in.

2. The create hook's catch branched on a local that was stale by the time it was
   read. When the direct membership read failed, `clampExpiryForSession` could
   still resolve the org from cache and then throw on the policy read; the catch
   saw the stale `undefined` and took the permissive branch — failing OPEN on a
   path whose comment promised fail-closed. Membership is now resolved once, up
   front, through the shared cached helper, and both the clamp and the catch use
   that single value.

Also: normalize the update hook's fallback expiry like its happy path, and
correct the `getSessionPolicy` cost note — an org with bounds stored pays the
entitlement check too, so roughly four reads, not one.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/auth/security-policy.ts
…read failure

Review catch. The success path re-checks the cache after its await so a
concurrent publish wins, but the catch path returned `DEFAULT_VERSION`
unconditionally.

The default is not the safe fallback it looks like — it is precisely the version
a never-bumped org carries. So a lookup that started before a revoke, and then
failed, could report `1` while this process already held the bumped counter:
the cookie-cache version would still MATCH pre-bump cookies and the
just-revoked session would keep being served from cache, which is the one thing
the bump exists to stop.

The catch now returns the cached value when one is present. It can only be
equal to or higher than the default, so it forces the same revalidation or more.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/auth/security-policy.ts

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 39ef3bd. Configure here.

Review catch. The post-read monotonic guard can only out-rank a stale value
while the entry that would out-rank it still exists. A read that stays in flight
past the cache TTL has watched that entry expire, so it can store a pre-bump
version with nothing left to catch it — and then serve it for another full TTL,
keeping pre-bump cookies matched and revoked sessions on the cookie cache.

Reaching that state needs an indexed single-row lookup to hang for a minute, so
it is pathological rather than likely, but the fix is small: time the read and,
if it outlived the TTL, read once more rather than trusting a value that can no
longer be checked. Logged at warn, since a lookup that slow is itself worth
seeing.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/auth/security-policy.ts Outdated

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 5b5bb5e. Configure here.

The retry added last round does not converge. Review correctly pointed out that
the replacement read can itself be slow across a later bump, in exactly the same
way — so the guard needs a guard, indefinitely. That is a signal the mechanism
is wrong, not that it needs another layer.

The root constraint: a monotonic floor kept in a TTL cache disappears with the
TTL, so a read still in flight when it expires has nothing to lose against. No
amount of re-reading fixes that. Closing it needs a floor that outlives the
cache, which means cross-process state — the deferred Redis invalidation
follow-up — not more retries here.

So the retry is reverted and the limit is documented where the guard lives. The
cheap post-read comparison stays: it costs two lines, needs no timing, and
covers the realistic case where the newer entry is still alive. The residual is
bounded — a stale read that wins serves a pre-bump version for at most one more
TTL, making worst-case propagation two TTLs rather than one. That is a slower
instance of a latency this feature already documents, not a new class of
failure, and it is strictly better than the pre-PR behavior where the policy
itself could also be stale.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 6bb1323. Configure here.

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