Conversation
Location catalogs (models, providers, agents, commands, skills, MCP, references, integrations) accumulated for every workspace a renderer ever visited, and credential events reloaded them for every known location. Consumers now hold a location while they need it: the current LocationProvider, provider and integration readers, and every open tab. Releasing the last hold drops the catalogs and their sync state after the current task, so route swaps and draft promotion do not reload them. Light metadata (info, vcs, running shells) and the default location stay resident. Event-driven refreshes only reload catalogs that are loaded or loading. Adds a production benchmark that visits and closes 5/15/30 workspaces with 1,200-model catalogs and records retained heap and catalog requests.
Credential events now refresh only loaded catalogs, matching the TUI, which syncs the current location before the account manager opens.
b704940 to
519ec67
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
It changes core client-side caching/residency and invalidation behavior across app + client, so it warrants final human validation despite strong test coverage.
Pull request overview
This PR introduces explicit “location residency” for workspace catalogs: catalogs stay loaded while a tab/route holds a location, and are released (dropping heavy catalogs but keeping lightweight metadata) once the last holder releases—while also guarding against Solid location proxies mutating during session moves.
Changes:
- Add
data.location.retain()with hold counting + generation-based guards so obsolete queued reads and late responses don’t repopulate released caches. - Wire residency holds into the app (route + open tabs) so switching among open tabs avoids refetches, and closing the last tab releases catalogs.
- Add targeted client/app tests plus an e2e performance benchmark to validate correctness and request/memory behavior across close/reopen, late responses, and session movement.
File summaries
| File | Description |
|---|---|
| packages/tui/test/cli/tui/dialog-integration.test.tsx | Updates TUI integration dialog integration test to pre-sync required catalogs and align expectations with “refresh only loaded catalogs” behavior. |
| packages/client/test/solid-location.test.ts | Adds focused unit tests covering retain/release, generation guards, late responses, and event-driven refresh semantics. |
| packages/client/src/solid/data.ts | Implements hold-based catalog residency (retain) and generation-guarded sync/invalidation to prevent stale work from repopulating released locations. |
| packages/app/test-browser/location-residency.test.ts | Adds browser-side tests validating tab-driven residency and correct behavior across session moves. |
| packages/app/src/workspaces/location.tsx | Ensures mounted workspace route holds its location catalogs via data.location.retain. |
| packages/app/src/runtime/server/runtime.tsx | Installs tab-driven residency in the runtime by wiring tabs into createLocationResidency. |
| packages/app/src/runtime/server/residency.ts | New helper that retains/release locations based on currently open tabs for a given server connection. |
| packages/app/src/providers/catalog/providers.ts | Retains location while provider catalog UI is mounted so its catalogs don’t get released mid-use. |
| packages/app/src/providers/catalog/integrations.ts | Retains location while integration catalog UI is mounted for the same residency semantics. |
| packages/app/e2e/utils/mock-server.ts | Extends mock server to serve per-directory locations and configurable catalogs (agents/commands/skills) for new benchmarks. |
| packages/app/e2e/performance/timeline/location-catalog.fixture.ts | New fixture building a large, realistic per-directory catalog payload used by the benchmark. |
| packages/app/e2e/performance/timeline/location-catalog-benchmark.spec.ts | New Playwright benchmark exercising restore/visit/close/credential refresh/reopen/reconnect to validate request patterns and retained heap. |
| packages/app/e2e/performance/README.md | Documents the new location catalog benchmark scenarios and environment knobs. |
| packages/app/e2e/performance/playwright.catalogs.config.ts | Adds a dedicated Playwright config for running the location-catalog benchmark against a prebuilt dist. |
Review details
- Files reviewed: 14/14 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| import config from "./playwright.config" | ||
|
|
||
| const port = Number(process.env.PLAYWRIGHT_PORT ?? 4795) | ||
| export default { | ||
| ...config, | ||
| testMatch: "timeline/location-catalog-benchmark.spec.ts", | ||
| webServer: { | ||
| command: `bun x vite preview --outDir "${process.env.CATALOGS_DIST}" --host 127.0.0.1 --port ${port} --strictPort`, | ||
| url: `http://127.0.0.1:${port}`, | ||
| reuseExistingServer: false, | ||
| timeout: 120_000, | ||
| }, | ||
| } |
|
Automated PR Cleanup Thank you for contributing to opencode. Due to the high volume of PRs from users and AI agents, we periodically close older PRs using automated criteria so maintainers can focus review time on the most active and community-supported contributions. This PR was closed because it matched the following cleanup criteria:
PRs created within the last month are not affected by this cleanup. If you believe this PR was closed incorrectly, or if you are still actively working on it, please leave a comment explaining why it should be reopened. A maintainer can review and reopen it if appropriate. Thanks again for taking the time to contribute. |
directoryandworkspaceIDat retain time. A live Solid session-location proxy can change in place during movement; cleanup must invalidate the original location, not its destination.location.sync()continuation aftersyncInfo().Movement Identity
Actual
createData+createLocationResidency, tested with bothsession.movedand remembered metadata reconciliation, confirms that the retained proxy changes from non-default A to non-default B. Befored19561c6bc, cleanup cleared A but invalidated B's cache markers, leaving A unable to reload.519ec6772bd19561c6bcThe real-client regression covers all nine released catalog categories. The production correction is one two-field identity snapshot; generation guards and direct unheld callers are unchanged.
Current Controls
Unchanged production catalog workload below, 30 workspaces, three forced-GC samples per case. Reviewed source
519ec6772buses its previously verifiedrepair-after-distbundle; corrected sourced19561c6bchas a new build. Six production source-map entries and 770 asset hashes per bundle were verified/recorded separately. These controls preserve the earlier retention behavior, not establish a new performance gain.Every sample on both builds: zero late refresh requests, nine reopen reads, zero warm-switch reads, and nine reconnect reads. Heap is post-GC renderer-main-isolate memory minus initial Home, not total desktop RAM. No new latency claim.
Earlier Benchmark
credential.switched, reopen one workspace, then reconnect. The pending case gates all first catalog responses until tabs are closed and the event is handled, then releases them on Home and waits for body readers to finish.afb3a14299and after7c8a32d2cb, with six changed production sources verified against source maps and 770 asset hashes recorded per build. Their frontend source is equivalent to public revisionscbbc0dd69cand304dfd04e8/519ec6772b. They are not measurements of the later identity snapshot ind19561c6bc.Three serial forced-GC samples per case. Heap is renderer-main-isolate retained memory minus the initial Home sample, in MiB. Loaded cases sample after close; pending cases sample after late bodies settle.
499e22bf52baseline retained 13.0 / 24.0 / 40.5 MiB after closing 5 / 15 / 30 directories. Those historical measurements used the older base and are not the controlled repair comparison above.Separate natural-GC timing: five loaded directories, 20 samples per build, counterbalanced in 10-sample ABBA blocks. Playwright-observed reopen-to-model-ready:
The distribution does not establish reduced reopen latency; on-demand catalog reads remain the trade-off.
Scope