workspace: resolver caches are scoped by tenant+host, not credential — a same-tenant account switch can inherit the previous account's cached link for up to 5 min

Open
#1,339 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
52/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
typescript

Research direction

Start in packages/opencode/src/altimate/workspace/state.ts at resolveBindingOutcome, then trace tenantKey(), accountScopedKey(), and the disk-cache scope. Compare their credential handling with resolvePinnedBinding and review the sidebar, skill-sync, and memory-sync callers. Done means same-tenant account switches no longer reuse another account’s fast path, with the existing cache migration effect understood.

Written by the indexing model from the issue text.

Description

Problem

resolveBindingOutcome in packages/opencode/src/altimate/workspace/state.ts scopes its per-process caches — lastValidatedAt, serverLookupMissed (via accountScopedKey) — and the on-disk binding cache by tenantKey(), which is {tenant, apiUrl} only. Two accounts on the same tenant and API host therefore share those entries.

Sequence (raised as MAJOR by the multi-model review on #1338; confirmed in the source):

  1. Alice resolves a linked project. lastValidatedAt[tenant|apiUrl|dir] is stamped and the binding is written to the disk cache.
  2. Within REVALIDATE_MS (5 min) the credentials switch to Bob — same tenant, same host, different personal API key.
  3. Bob's resolve sees the fresh stamp and returns Alice's cached binding as bound with no server check. Whatever rides on the binding (skill-sync, memory-sync, the identity line) is attributed to the wrong account for up to five minutes.

This is the layer under the two caches v0.12.1 did scope by credential digest (identity's memo, and pinValidation in resolvePinnedBinding, which set the pattern). Not a warehouse-authorization bypass — that check is server-side — and not a regression: tenantKey/accountScopedKey predate v0.12.x.

Expected

Thread a short credential digest (the sha256(apiKey).slice(0,16) pattern resolvePinnedBinding already uses) through tenantKey(), accountScopedKey() and the disk-cache scope, so a same-tenant account switch never inherits the other account's fast path. Note the migration effect: re-keying the disk cache invalidates every existing cached binding once (one extra server round-trip per project after upgrade), and the sidebar, skill-sync and memory-sync callers read the same scope — review them together.

Priority

Deferred. Switching accounts within one tenant mid-process is not a common workflow; the window is five minutes and the consequence is misattributed sync, not data access. Called out in the v0.12.1 CHANGELOG ("tracked separately").

Related: #1337 (routing ignores the IDE pin — different gap, same file), #1335.

🤖 Generated with Claude Code

https://claude.ai/code/session_012Q51zFUmPg1WwtS5CrGJE6

Dominant language
TypeScript
Stars
813
Forks
134
Avg merge
2d 5h
Merged PRs (30d)
62

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from AltimateAI/altimate-code

All issues in AltimateAI/altimate-code

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.