Skip to content
2 changes: 2 additions & 0 deletions packages/opencode/src/altimate/workspace/skill-sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ const REALM_ID = Math.random().toString(36).slice(2, 8)
* (review) */
const STAGING_LEASE_MS = 30 * 60 * 1000

// test/preload.ts reads this store through this key (its skill-sync leak guard), without
// importing this module. Renaming the key or `inFlight` silently disables that guard.
const STORE_KEY = Symbol.for("altimate.workspace.skill-sync.store")

export interface SkippedSkill {
Expand Down
15 changes: 10 additions & 5 deletions packages/opencode/test/altimate/plugin/workspace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const { syncInternals } = await import("../../../src/altimate/workspace/engine-s
// and recordApprovedBinding for tenant/apiUrl scoping. Re-import allows
// per-test override of the module state.
import { AltimateApi } from "../../../src/altimate/api/client"
import { stubEmptySkillList } from "../workspace/skill-list-fixture"
import { WorkspaceApi } from "../../../src/altimate/workspace/api-client"
import { findNamesakes } from "../../../src/altimate/workspace/workspace-name"
import { createRoot } from "solid-js"
Expand Down Expand Up @@ -149,9 +150,13 @@ describe("detectProjectRemote", () => {
// ─────────────────────────────────────────────────────────────────────────────

describe("workspace binding cache", () => {
let restoreFetch = () => {}
beforeEach(() => {
stubCreds("acme", "https://api.acme.example.com")
// Recording a link here awaits its skill sync; answered offline, whatever the workspace flag is.
restoreFetch = stubEmptySkillList("api.acme.example.com")
})
afterEach(() => restoreFetch())

test("records and reads back a binding for the same directory + tenant", async () => {
await recordApprovedBinding("/work/proj-a", {
Expand All @@ -160,7 +165,7 @@ describe("workspace binding cache", () => {
repoRemote: "git@github.com:acme/proj-a.git",
projectPath: "/work/proj-a",
linkedAt: 1_700_000_000_000,
})
}, { awaitBackfill: true })
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.

const read = await readLocalBinding("/work/proj-a")
expect(read).not.toBeNull()
Expand Down Expand Up @@ -474,7 +479,7 @@ describe("workspace binding cache", () => {
repoRemote: "git@github.com:acme/x.git",
projectPath: "/work/proj-a",
linkedAt: 1,
})
}, { awaitBackfill: true })
expect(existsSync(cachePath())).toBe(true)
const mode = statSync(cachePath()).mode & 0o777
expect(mode).toBe(0o600)
Expand All @@ -487,7 +492,7 @@ describe("workspace binding cache", () => {
repoRemote: "git@github.com:acme/proj-a.git",
projectPath: "/work/proj-a",
linkedAt: 1,
})
}, { awaitBackfill: true })

// Switch account → the cached binding must not be surfaced.
unstubCreds()
Expand All @@ -504,7 +509,7 @@ describe("workspace binding cache", () => {
repoRemote: "git@github.com:acme/proj-a.git",
projectPath: "/work/proj-a",
linkedAt: 1,
})
}, { awaitBackfill: true })

unstubCreds()
stubCreds("acme", "https://different-host.example.com")
Expand All @@ -520,7 +525,7 @@ describe("workspace binding cache", () => {
repoRemote: "git@github.com:acme/proj-a.git",
projectPath: "/work/proj-a",
linkedAt: 1,
})
}, { awaitBackfill: true })
const read = await readLocalBinding("/work/proj-b")
expect(read).toBeNull()
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const { createAndBindInline, bindOrRebindInline, runWorkspaceManage } = await im
"@/plugin/tui/altimate/workspace"
)
const { recordApprovedBinding } = await import("@/altimate/workspace/state")
const { flushPendingSyncs } = await import("@/altimate/workspace/skill-sync")
const { HIDDEN_BINDING_MESSAGE, QUICK_WORKSPACE_PRIVATE_NOTE } = await import(
"@/altimate/workspace/api-client"
)
Expand Down Expand Up @@ -116,7 +117,12 @@ beforeEach(() => {
opened.length = 0
stubFetch()
})
afterEach(() => {
afterEach(async () => {
// The TUI create leaves its skill sync detached so the dialog can close at once. Settled here,
// against this file's stub, so it cannot run on into the next test.
// Bounded well inside the test timeout: a sync that never settles is the guard's to name, not a
// 30s stall here.
await flushPendingSyncs(5_000)
globalThis.fetch = ORIGINAL_FETCH
process.exitCode = 0 // Bun ignores `= undefined`; a leaked 1 fails later files
})
Expand Down Expand Up @@ -420,7 +426,7 @@ describe("TUI: /workspace menu options", () => {
repoRemote: BINDING.repo_remote,
projectPath: null,
linkedAt: Date.now(),
})
}, { awaitBackfill: true })
const m = menuApi()
await runWorkspaceManage(m.api, "/tmp/proj")
const values = m.cap.options.map((o) => o.value)
Expand Down
24 changes: 12 additions & 12 deletions packages/opencode/test/altimate/workspace/identity-section.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ describe("systemSection", () => {
repoRemote: null,
projectPath: projectDir,
linkedAt: Date.now(),
})
}, { awaitBackfill: true })
resetEnablementMemoForTests()
memoryEnabledCache.set(77, { checkedAt: Date.now() })
try {
Expand Down Expand Up @@ -273,7 +273,7 @@ describe("systemSection", () => {
repoRemote: null,
projectPath: projectDir,
linkedAt: Date.now(),
})
}, { awaitBackfill: true })
expect(await inProject(systemSection)).toContain('is "analytics"')
await clearLocalBinding(projectDir, { scope: ACME_SCOPE })
globalThis.fetch = (async () =>
Expand Down Expand Up @@ -312,7 +312,7 @@ describe("systemSection", () => {
repoRemote: null,
projectPath: projectDir,
linkedAt: Date.now() - 10 * 60 * 1000,
})
}, { awaitBackfill: true })
const { expireValidationForTests } = await import("../../../src/altimate/workspace/state")
expireValidationForTests?.(projectDir)
let unlinkedMidFlight = false
Expand Down Expand Up @@ -358,7 +358,7 @@ describe("systemSection", () => {
repoRemote: null,
projectPath: projectDir,
linkedAt: Date.now() - 10 * 60 * 1000,
})
}, { awaitBackfill: true })
const { expireValidationForTests } = await import("../../../src/altimate/workspace/state")
expireValidationForTests(projectDir)
globalThis.fetch = (async () => {
Expand Down Expand Up @@ -430,7 +430,7 @@ describe("systemSection", () => {
repoRemote: null,
projectPath: projectDir,
linkedAt: Date.now() - 10 * 60 * 1000,
})
}, { awaitBackfill: true })
const { expireValidationForTests } = await import("../../../src/altimate/workspace/state")
expireValidationForTests(projectDir)
// First step: bound (served from cache while the server is asked).
Expand Down Expand Up @@ -458,7 +458,7 @@ describe("systemSection", () => {
repoRemote: null,
projectPath: other,
linkedAt: Date.now(),
})
}, { awaitBackfill: true })
globalThis.fetch = (async () =>
new Response(JSON.stringify({ detail: "not found" }), {
status: 404,
Expand Down Expand Up @@ -500,7 +500,7 @@ describe("systemSection", () => {
repoRemote: null,
projectPath: projectDir,
linkedAt: Date.now(),
})
}, { awaitBackfill: true })
expect(await inProject(systemSection)).toContain('is "Linked"')
})

Expand All @@ -515,7 +515,7 @@ describe("systemSection", () => {
repoRemote: null,
projectPath: projectDir,
linkedAt: Date.now() - 10 * 60 * 1000,
})
}, { awaitBackfill: true })
const { expireValidationForTests } = await import("../../../src/altimate/workspace/state")
expireValidationForTests(projectDir)
let release!: () => void
Expand Down Expand Up @@ -637,7 +637,7 @@ describe("systemSection", () => {
repoRemote: null,
projectPath: projectDir,
linkedAt: Date.now(),
})
}, { awaitBackfill: true })
setCreds("acme")
let switched = false
globalThis.fetch = (() =>
Expand Down Expand Up @@ -672,7 +672,7 @@ describe("systemSection", () => {
repoRemote: null,
projectPath: projectDir,
linkedAt: Date.now(),
})
}, { awaitBackfill: true })
let t = 5_000_000
setClockForTests(() => t)
expect(await inProject(systemSection)).toContain('is "mine"') // memo filled for A
Expand Down Expand Up @@ -784,7 +784,7 @@ describe("systemSection", () => {
repoRemote: null,
projectPath: projectDir,
linkedAt: Date.now(),
})
}, { awaitBackfill: true })
const saved: Record<string, string | undefined> = {}
const pinEnv: Record<string, string> = {
ALTIMATE_CODE_SERVE: "1",
Expand Down Expand Up @@ -825,7 +825,7 @@ describe("systemSection", () => {
repoRemote: null,
projectPath: projectDir,
linkedAt: Date.now(),
})
}, { awaitBackfill: true })
const saved: Record<string, string | undefined> = {}
const pinEnv: Record<string, string> = {
ALTIMATE_CODE_SERVE: "1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const { recordApprovedBinding, cachePath } = await import(

// Stub credentials so state.ts's tenant/apiUrl scoping is deterministic.
import { AltimateApi } from "../../../src/altimate/api/client"
import { stubEmptySkillList } from "./skill-list-fixture"
type Creds = Awaited<ReturnType<typeof AltimateApi.getCredentials>>
const originalIsConfigured = AltimateApi.isConfigured
const originalGetCreds = AltimateApi.getCredentials
Expand All @@ -62,10 +63,13 @@ function unstubCreds() {
originalGetCreds
}

let restoreFetch = () => {}
const ORIGINAL_DISABLE = process.env.ALTIMATE_DISABLE_WORKSPACE

beforeEach(() => {
stubCreds()
// The resolver fixture awaits the skill sync that recording a link starts; answered offline.
restoreFetch = stubEmptySkillList("localhost:5001")
setResolvedWorkspaceId(null)
// Clean cache file between tests so state doesn't leak across cases.
try {
Expand All @@ -79,6 +83,7 @@ beforeEach(() => {
})
afterEach(() => {
unstubCreds()
restoreFetch()
setResolvedWorkspaceId(null)
if (ORIGINAL_DISABLE === undefined) delete process.env.ALTIMATE_DISABLE_WORKSPACE
else process.env.ALTIMATE_DISABLE_WORKSPACE = ORIGINAL_DISABLE
Expand Down Expand Up @@ -123,7 +128,7 @@ describe("resolveWorkspaceForLaunch", () => {
repoRemote: null,
projectPath: DIRECTORY,
linkedAt: 0,
})
}, { awaitBackfill: true })
Comment thread
ralphstodomingo marked this conversation as resolved.
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
})

test("no --workspace arg → no env var set, no-op", async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/opencode/test/altimate/workspace/manage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ describe("renames", () => {
repoRemote: "git@github.com:acme/app.git",
projectPath: projectDir,
linkedAt: Date.now(),
} as any)
} as any, { awaitBackfill: true })
expect(fired).toBeGreaterThan(0)
} finally {
stop()
Expand Down
18 changes: 9 additions & 9 deletions packages/opencode/test/altimate/workspace/memory-sync.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1199,7 +1199,7 @@ describe("reaping blocks archived elsewhere", () => {
await recordApprovedBinding(
here,
{ datamateId: 99, datamateName: "other", repoRemote: null, projectPath: here, linkedAt: Date.now() },
{ seed: false },
{ awaitBackfill: true, seed: false },
)
}
return inner(scope, id, directory)
Expand Down Expand Up @@ -1705,7 +1705,7 @@ describe("binding changes", () => {
const b = { ...BINDING, datamateId: 43, datamateName: "beta", projectPath: dir, linkedAt: 2 }
workspaces = [...workspaces, { id: 43, name: "beta", memory_enabled: true }]
syncInternals.resolveBinding = async () => b as any
await recordApprovedBinding(dir, b)
await recordApprovedBinding(dir, b, { awaitBackfill: true })
await hydrate(SES)
expect(overlayBlocks(SES).map((x) => x.id)).toEqual(["from-b"])
})
Expand All @@ -1721,7 +1721,7 @@ describe("binding changes", () => {
// The first lookup adopts (and notifies); later ones read the cache, as in production.
if (!adopted) {
adopted = true
await recordApprovedBinding(dir, { ...BINDING, projectPath: dir, linkedAt: 3 }, { seed: false })
await recordApprovedBinding(dir, { ...BINDING, projectPath: dir, linkedAt: 3 }, { awaitBackfill: true, seed: false })
}
return BINDING as any
}
Expand Down Expand Up @@ -1752,7 +1752,7 @@ describe("refresh racing a relink", () => {
await reachedList
const { recordApprovedBinding } = await import("../../../src/altimate/workspace/state")
const dir = mkdtempSync(path.join(SANDBOX, "race-"))
await recordApprovedBinding(dir, { ...BINDING, datamateId: 44, projectPath: dir, linkedAt: 4 }, { seed: false })
await recordApprovedBinding(dir, { ...BINDING, datamateId: 44, projectPath: dir, linkedAt: 4 }, { awaitBackfill: true, seed: false })
release?.()
const result = await pending
// Superseded: neither its read nor the prior overlay belongs to the new binding.
Expand All @@ -1776,7 +1776,7 @@ describe("overlay invalidation", () => {
await hydrate(SES)
expect(overlayBlocks(SES).length).toBe(1)
const dir = mkdtempSync(path.join(SANDBOX, "hide-"))
await recordApprovedBinding(dir, { ...BINDING, datamateId: 45, projectPath: dir, linkedAt: 5 }, { seed: false })
await recordApprovedBinding(dir, { ...BINDING, datamateId: 45, projectPath: dir, linkedAt: 5 }, { awaitBackfill: true, seed: false })
expect(overlayBlocks(SES)).toEqual([])
})

Expand Down Expand Up @@ -1819,7 +1819,7 @@ describe("epoch bracketing and scope", () => {
calls++
// First lookup returns A, but the relink to B lands before it returns.
if (calls === 1) {
await recordApprovedBinding(dir, { ...BINDING, datamateId: 46, projectPath: dir, linkedAt: 6 }, { seed: false })
await recordApprovedBinding(dir, { ...BINDING, datamateId: 46, projectPath: dir, linkedAt: 6 }, { awaitBackfill: true, seed: false })
return BINDING as any
}
return { ...BINDING, datamateId: 46 } as any
Expand All @@ -1841,10 +1841,10 @@ describe("epoch bracketing and scope", () => {
const other = mkdtempSync(path.join(SANDBOX, "other-"))
await refresh(SES, mine)
expect(overlayBlocks(SES).map((x) => x.id)).toEqual(["mine"])
await recordApprovedBinding(other, { ...BINDING, datamateId: 47, projectPath: other, linkedAt: 7 }, { seed: false })
await recordApprovedBinding(other, { ...BINDING, datamateId: 47, projectPath: other, linkedAt: 7 }, { awaitBackfill: true, seed: false })
expect(overlayBlocks(SES).map((x) => x.id)).toEqual(["mine"])
// A change to this project's own binding still hides it.
await recordApprovedBinding(mine, { ...BINDING, datamateId: 48, projectPath: mine, linkedAt: 8 }, { seed: false })
await recordApprovedBinding(mine, { ...BINDING, datamateId: 48, projectPath: mine, linkedAt: 8 }, { awaitBackfill: true, seed: false })
expect(overlayBlocks(SES)).toEqual([])
})
})
Expand Down Expand Up @@ -1887,7 +1887,7 @@ describe("superseded failures", () => {
await reachedList
const dir = mkdtempSync(path.join(SANDBOX, "supersede-"))
syncInternals.resolveBinding = async () => b as any
await recordApprovedBinding(dir, { ...b, projectPath: dir, linkedAt: 9 }, { seed: false })
await recordApprovedBinding(dir, { ...b, projectPath: dir, linkedAt: 9 }, { awaitBackfill: true, seed: false })
release?.()
await first
failList = false
Expand Down
5 changes: 5 additions & 0 deletions packages/opencode/test/altimate/workspace/routing-pin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const { resolvePinnedBindingForRouting, recordApprovedBinding, __resetPinValidat
const { refresh, precedenceInternals } = await import("../../../src/altimate/workspace/precedence")
const { Instance } = await import("../../../src/project/instance")
const { SNOWFLAKE_TOOLS } = await import("./precedence-fixture")
const { stubEmptySkillList } = await import("./skill-list-fixture")
const { AltimateApi } = await import("../../../src/altimate/api/client")
const { WorkspaceApi } = await import("../../../src/altimate/workspace/api-client")

Expand Down Expand Up @@ -97,6 +98,7 @@ async function seedLocalLink(datamateId = 7, datamateName = "project-link") {
}

const ORIGINAL_PILOT = process.env.ALTIMATE_DISABLE_WORKSPACE
let restoreFetch = () => {}

beforeEach(() => {
// `derive` short-circuits on `pilot-off` before it ever reads a binding.
Expand All @@ -105,6 +107,8 @@ beforeEach(() => {
listCalls = 0
__resetPinValidation()
stubCreds()
// Seeding a link awaits its skill sync (`seedLocalLink`); answered offline, per test.
restoreFetch = stubEmptySkillList("api.test")
stubList([
{ id: 42, name: "pinned-workspace" },
{ id: 7, name: "project-link" },
Expand All @@ -113,6 +117,7 @@ beforeEach(() => {
})

afterEach(() => {
restoreFetch()
clearPin()
delete process.env.ALTIMATE_INTEGRATIONS
// Restored per test, not only in `afterAll`: `beforeEach` sets it unconditionally, so leaving
Expand Down
Loading
Loading