Skip to content

Commit 70f842c

Browse files
sahrizviclaude
andcommitted
fix(workspace): a missing workspace also clears a cached "memory off", and warns once (review)
- `memoryStatus` clears `memoryDisabledMemo` as well when the workspace is missing from the list, so cache-only readers (`workspace status` without a poll, the identity section) say unknown instead of a stale "disabled" - The "missing from the workspace list" warning is logged once per workspace while it stays missing Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018fJ3X7pcGT4R9yzjsJnqsV
1 parent 0e7cbc3 commit 70f842c

2 files changed

Lines changed: 21 additions & 3 deletions

File tree

‎packages/opencode/src/altimate/workspace/memory-sync.ts‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,8 @@ export function resetEnablementMemoForTests(): void {
213213

214214
/** Warn once per workspace, not once per write. */
215215
const missingFieldWarned = new Set<number>()
216+
/** Workspaces already reported missing from the list, so a lag of a few minutes warns once, not on every call. */
217+
const missingFromListWarned = new Set<number>()
216218

217219
/** Whether the bound workspace has memory switched on.
218220
*
@@ -246,12 +248,17 @@ async function memoryStatus(
246248
const match = workspaces.find((w) => w.id === binding.datamateId)
247249
if (!match) {
248250
// Missing from the list is not a confirmed toggle. The list lags a workspace created moments ago (one
249-
// service replica serves it stale for a few minutes), so this is unknown, like a failed request: not
250-
// cached either way, and the write path still fails closed on it.
251+
// service replica serves it stale for a few minutes), so this is unknown, like a failed request: neither
252+
// verdict is kept, so cache-only readers say unknown too, and the write path still fails closed on it.
251253
memoryEnabledCache.delete(binding.datamateId)
252-
log.warn("workspace missing from the workspace list; memory setting unknown", { workspace: binding.datamateId })
254+
memoryDisabledMemo.delete(binding.datamateId)
255+
if (!missingFromListWarned.has(binding.datamateId)) {
256+
missingFromListWarned.add(binding.datamateId)
257+
log.warn("workspace missing from the workspace list; memory setting unknown", { workspace: binding.datamateId })
258+
}
253259
return "error"
254260
}
261+
missingFromListWarned.delete(binding.datamateId)
255262
if (match.memoryEnabled === undefined && !missingFieldWarned.has(binding.datamateId)) {
256263
// Fail-closed is right, but a backend that has not shipped the field
257264
// turns the whole feature into a silent no-op. Say so once.

‎packages/opencode/test/altimate/workspace/memory-sync.test.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,17 @@ describe("memory_enabled", () => {
898898
expect(memoryEnabledCached(BINDING as any)).toBe("unknown")
899899
expect(callsTo("/datamates/memory/", "POST").length).toBe(0)
900900
})
901+
902+
test("a workspace last seen with memory off that drops out of the list reads as unknown, not off", async () => {
903+
// The cache-only readers (`workspace status` without a poll, the identity section) must not keep serving the
904+
// earlier "disabled" while the list cannot say anything about the workspace.
905+
workspaces = [{ id: 42, name: "acme", memory_enabled: false }]
906+
await backfill([block({ id: "was-off" })], BINDING as any)
907+
expect(memoryEnabledCached(BINDING as any)).toBe("disabled")
908+
workspaces = []
909+
await backfill([block({ id: "now-missing" })], BINDING as any)
910+
expect(memoryEnabledCached(BINDING as any)).toBe("unknown")
911+
})
901912
})
902913

903914
// ── read path ───────────────────────────────────────────────────────────────

0 commit comments

Comments
 (0)