Skip to content

[AP-2925] Enable server-side caching by default in v4 - #2964

Open
sameelarif wants to merge 3 commits into
mainfrom
sameelarif/ap-2925-ensure-caching-is-enabled-by-default-in-v4
Open

sameelarif wants to merge 3 commits into
mainfrom
sameelarif/ap-2925-ensure-caching-is-enabled-by-default-in-v4

Conversation

@sameelarif

@sameelarif sameelarif commented Sep 17, 2026

Copy link
Copy Markdown
Member

v4 shipped with server-side caching opt-in, where v3 had it on by default. Anyone who upgraded without passing cache lost caching without an error.

Change

Both defaults flip to true, so v4 matches v3: caching runs unless the instance or the call opts out.

Docs

v4/best-practices/caching.mdx described opt-in, which this makes wrong.

The v3 migration guide also mapped enableCachingcache. enableCaching was a v2-era local option that v3 had already replaced with cacheDir; the server-side option v3 users actually had was serverCache, which appeared nowhere in the v4 docs. Corrected the mapping and the diff example.

🤖 Generated with Claude Code


Summary by cubic

Fixes AP-2925 by restoring v3’s caching default in v4: caching was opt-in and is now enabled unless the instance or call opts out. This prevents upgrades from silently losing caching; the server’s project feature flag still controls availability.

  • Adds tests for the omitted default, explicit opt-out, and default cache lookup.
  • Corrects the migration guide to map serverCache to cache and remove outdated enableCaching/cacheDir mappings.
  • Updates caching docs for locator-scoped bypasses, DISABLED status, and the stagehand.act opt-out example.

Written for commit 5bedb73. Summary will update on new commits.

Review in cubic

v3 cached every eligible act/observe/extract unless the call opted out via
`serverCache: false` — the server decided, gated on the per-project
LaunchDarkly flag. v4 added a client-side gate that defaults to off, so
`withCache` returns before it ever calls /v1/cache/get and metadata reports
DISABLED. Anyone who upgraded without passing `cache` silently lost caching,
including projects with the LaunchDarkly flag already enabled.

Flip the default so v4 matches v3: caching is on unless the instance or the
call opts out. The server still gates every lookup on
`stagehand-api-server-caching`, so this only decides whether we ask.

No existing test covered the default — baseArgs() always passed
`caching: true` — which is how the inversion shipped. Added three: the
buildCacheContext default, an explicit opt-out, and a lookup with neither
the request nor the instance setting `cache`.

Docs said caching was opt-in, which this makes wrong. Also corrects the v3
migration guide, which mapped `enableCaching` (a v2-era local option that v3
had already replaced with `cacheDir`) to `cache`, and never mentioned
`serverCache` — the option v3 users actually had.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sameelarif
sameelarif requested a review from a team as a code owner September 17, 2026 17:50
@mintlify

mintlify Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
stagehand 🟢 Ready View Preview Sep 18, 2026, 4:47 PM

💡 Tip: Enable Automations to automatically generate PRs for you.

@changeset-bot

changeset-bot Bot commented Sep 17, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 5bedb73

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 4 files

Architecture diagram
sequenceDiagram
    participant App as Client App
    participant SH as Stagehand Client v4
    participant CacheSvc as Cache Service
    participant API as Stagehand API
    participant LD as LaunchDarkly
    participant Cache as Cache Store

    Note over App, Cache: Caching Flow

    App->>SH: create({ browser, apiKey })
    SH->>CacheSvc: buildCacheContext(initParams)
    CacheSvc->>CacheSvc: defaultCaching = cache ?? true

    App->>SH: act("...") or observe() or extract()
    SH->>CacheSvc: withCache({ caching, context, execute })

    alt Request or instance sets cache: false
        CacheSvc->>CacheSvc: resolvedCaching = false
        CacheSvc->>SH: execute() directly
        SH-->>App: Result with cache.status = "DISABLED"
    else Default path (no explicit cache option)
        CacheSvc->>CacheSvc: resolvedCaching = defaultCaching (true)
        CacheSvc->>CacheSvc: cachePage = asCachePage(page)
        CacheSvc->>API: GET /v1/cache/get
        API->>LD: Check stagehand-api-server-caching flag
        alt Flag disabled per project
            LD-->>API: false
            API-->>CacheSvc: No cache lookup
            CacheSvc->>CacheSvc: execute()
            SH-->>App: Result with cache.status = "DISABLED"
        else Flag enabled per project
            LD-->>API: true
            API->>Cache: Lookup by cache key
            alt Cache hit
                Cache-->>API: Cached result
                API-->>CacheSvc: Cache hit
                CacheSvc->>CacheSvc: onHit()
                SH-->>App: Result with cache.status = "HIT"
            else Cache miss
                Cache-->>API: Not found
                API-->>CacheSvc: Miss
                CacheSvc->>CacheSvc: execute()
                API->>Cache: Store result
                SH-->>App: Result with cache.status = "MISS"
            end
        end
    end
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/docs/v4/best-practices/caching.mdx Outdated
Comment thread packages/docs/v4/migrations/v3.mdx Outdated
Comment thread packages/docs/v4/migrations/v3.mdx Outdated
Comment thread packages/docs/v4/best-practices/caching.mdx Outdated
- Locator-scoped calls bypass the cache entirely
  (shouldBypassCacheForLocatorScope gates act/observe/extract on
  `locator` or a non-empty `ignoreLocators`, and withCache returns before
  any read or write). The intro claimed every call is cached, contradicting
  the page's own note further down. Qualify it and add the exclusion to
  Limitations, which is where the other caching caveats live.
- Drop the two em dashes, prohibited by .cubic/docs-style-guide.md:38.
- `page.act` is not a v4 API and `page` was never declared in that snippet,
  so the opt-out example failed when copied. Use `stagehand.act`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 2 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/docs/v4/best-practices/caching.mdx Outdated
docs-style-guide.md:35 mandates active voice, and the limitation bullet used
two passives ("are not cached", "is keyed") with no actor, which also made it
inconsistent with its neighbours ("Stagehand calls the LLM", "Stagehand falls
back"). Name Stagehand as the actor.

Two more of the same class in this PR's prose that review did not flag:
"Caching is enabled by default" is the guide's own example pattern, and
"request made by that instance" is a passive participle. Also corrects
"behaviour", the only British spelling against 13 uses of "behavior" in the
v4 docs.

Leaves the matching passive on line 346 alone: it is untouched legacy, and
the guide says not to block a focused PR on that.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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