Repository navigation
feat(workspace): record workspace sync state and publish changes to clients #1429
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,7 @@ import { WorkspaceApi, type ProjectIdentifier } from "./api-client" | |
| import { resolveProjectIdentifier } from "./detect" | ||
| import * as MemorySync from "./memory-sync" | ||
| import * as SkillSync from "./skill-sync" | ||
| import * as SyncState from "./sync-state" | ||
| import { | ||
| clearLocalBinding, | ||
| currentScope, | ||
|
|
@@ -59,6 +60,11 @@ export interface StatusReport { | |
| * per-process, so a fresh session has not synced yet even for a project whose | ||
| * snapshot is current on disk. Callers must not render it as "never synced". */ | ||
| skillsSyncedAt: number | null | ||
| // altimate_change start — workspace sync state | ||
| /** When this project's workspace memory was last loaded, from the shared sync state, or | ||
| * null when no load has been recorded for this binding. */ | ||
| memoryLoadedAt?: number | null | ||
| // altimate_change end | ||
| } | ||
|
|
||
| export interface RefreshReport { | ||
|
|
@@ -156,6 +162,7 @@ export async function status( | |
| ...(memory === "unreadable" ? { memoryUnreadable: true as const } : {}), | ||
| skillsEnabled: SkillSync.isEnabled(), | ||
| skillsSyncedAt: await skillsSyncedAt(directory, binding), | ||
| memoryLoadedAt: binding ? (SyncState.read(directory, binding.datamateId)?.entities.memory?.lastCheckedAt ?? null) : null, | ||
| } | ||
| } | ||
|
|
||
|
Comment on lines
162
to
168
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: sed -n '1380,1425p;1450,1505p;1520,1565p' packages/opencode/src/altimate/workspace/memory-sync.ts
sed -n '154,172p' packages/opencode/src/altimate/workspace/manage.ts
sed -n '300,320p' packages/opencode/src/plugin/tui/altimate/workspace-sidebar.tsx
sed -n '170,210p' packages/opencode/src/altimate/workspace/sync-state.tsRepository: AltimateAI/altimate-code Length of output: 11929 🏁 Script executed: set -o pipefail
printf '%s\n' '--- MemorySync.refresh callers ---'
rg -n -F --glob '*.ts' --glob '*.tsx' -- 'MemorySync.refresh' packages/opencode/src packages/opencode/test || test "$?" -eq 1
rg -n -F --glob '*.ts' --glob '*.tsx' -- 'refresh(directory' packages/opencode/src packages/opencode/test || test "$?" -eq 1
printf '%s\n' '--- loadWorkspaceMemory outcomes and error paths ---'
rg -n -F --glob '*.ts' -- 'status: "error"' packages/opencode/src/altimate/workspace/memory-sync.ts
rg -n -F --glob '*.ts' -- 'MemorySync' packages/opencode/src/altimate/workspace packages/opencode/src/plugin | head -120
printf '%s\n' '--- status type/data path ---'
rg -n -F --glob '*.ts' --glob '*.tsx' -- 'memoryLoadedAt' packages/opencode/src packages/opencode/test
rg -n -F --glob '*.ts' --glob '*.tsx' -- 'memoryUnreadable' packages/opencode/src packages/opencode/test
printf '%s\n' '--- relevant bounded source ---'
nl -ba packages/opencode/src/altimate/workspace/manage.ts | sed -n '1,230p'
nl -ba packages/opencode/src/plugin/tui/altimate/workspace-sidebar.tsx | sed -n '1,360p'Repository: AltimateAI/altimate-code Length of output: 37406 🏁 Script executed: set -o pipefail
printf '%s\n' '--- memory-sync outcome and error-producing definitions ---'
nl -ba packages/opencode/src/altimate/workspace/memory-sync.ts | sed -n '1360,1475p'
rg -n -F --glob '*.ts' -- 'async function memoryStatus' packages/opencode/src/altimate/workspace/memory-sync.ts
rg -n -F --glob '*.ts' -- 'function memoryStatus' packages/opencode/src/altimate/workspace/memory-sync.ts
rg -n -F --glob '*.ts' -- 'async function memoryCounts' packages/opencode/src/altimate/workspace/manage.ts
nl -ba packages/opencode/src/altimate/workspace/manage.ts | sed -n '320,365p'
printf '%s\n' '--- sync-state entity shape ---'
nl -ba packages/opencode/src/altimate/workspace/sync-state.ts | sed -n '1,220p'Repository: AltimateAI/altimate-code Length of output: 19886 🏁 Script executed: nl -ba packages/opencode/src/altimate/workspace/memory-sync.ts | sed -n '200,285p'
rg -n -F --glob '*.ts' --glob '*.tsx' -- 'memoryEnabledForPoller' packages/opencode/src/altimate/workspace packages/opencode/testRepository: AltimateAI/altimate-code Length of output: 6431 🏁 Script executed: set -o pipefail
printf '%s\n' '--- commitLoad callers and hydrate entrypoints ---'
rg -n -F --glob '*.ts' --glob '*.tsx' -- 'commitLoad(' packages/opencode/src/altimate/workspace packages/opencode/src
rg -n -F --glob '*.ts' --glob '*.tsx' -- 'hydrate(' packages/opencode/src/altimate/workspace packages/opencode/src | head -100
nl -ba packages/opencode/src/altimate/workspace/memory-sync.ts | sed -n '1468,1565p'
nl -ba packages/opencode/src/altimate/workspace/memory-sync.ts | sed -n '1565,1665p'Repository: AltimateAI/altimate-code Length of output: 8868 Do not expose failed checks as When Only expose the timestamp when the memory sync entity has Suggested fix--- "a/packages/opencode/src/altimate/workspace/manage.ts"
+++ "b/packages/opencode/src/altimate/workspace/manage.ts"
@@ -155,15 +155,16 @@
? await resolveBinding(directory).catch(() => null)
: ((await readLocalBinding(directory).catch(() => null)) ??
(await resolveBinding(directory).catch(() => null)))
const memory = await memoryCounts(directory, binding, opts.poll === true)
+ const memorySync = binding ? SyncState.read(directory, binding.datamateId)?.entities.memory : undefined
return {
binding,
memory: memory === "unreadable" ? null : memory,
...(memory === "unreadable" ? { memoryUnreadable: true as const } : {}),
skillsEnabled: SkillSync.isEnabled(),
skillsSyncedAt: await skillsSyncedAt(directory, binding),
- memoryLoadedAt: binding ? (SyncState.read(directory, binding.datamateId)?.entities.memory?.lastCheckedAt ?? null) : null,
+ memoryLoadedAt: memorySync?.status === "ok" ? memorySync.lastCheckedAt : null,
}
}
/** The age of the last clean skill sync FOR THIS BINDING, or null. */🤖 Prompt for AI Agents |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1376,7 +1376,7 @@ type LoadOutcome = ( | |
| | { status: "unlinked" } | ||
| | { status: "disabled" } | ||
| | { status: "error" } | ||
| ) & { epoch?: string; dir?: string | null } | ||
| ) & { epoch?: string; dir?: string | null; /** The workspace the load read, once resolved. */ datamateId?: number } | ||
|
|
||
| /** Read this project's workspace memory. Pure: it publishes nothing, so a slow | ||
| * load that has been superseded cannot write over a newer result. */ | ||
|
|
@@ -1403,9 +1403,10 @@ async function loadWorkspaceMemory(directory?: string): Promise<LoadOutcome> { | |
| vouched = epoch | ||
| if (!stable) return { status: "error", epoch, dir } | ||
| if (!binding) return { status: "unlinked", epoch, dir } | ||
| const datamateId = binding.datamateId | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Post-bind load failures lose the workspace ID before this recorder sees them. Move the ID into the error outcome so failed checks update the workspace entity while retaining its previous items. Prompt for AI agents |
||
| const enabled = await memoryStatus(binding) | ||
| if (enabled === "error") return { status: "error", epoch, dir } | ||
| if (enabled === "disabled") return { status: "disabled", epoch, dir } | ||
| if (enabled === "error") return { status: "error", epoch, dir, datamateId } | ||
| if (enabled === "disabled") return { status: "disabled", epoch, dir, datamateId } | ||
|
|
||
| const ownProjectKey = projectKeyFor(binding) | ||
| const ownWorkspace = String(binding.datamateId) | ||
|
|
@@ -1441,7 +1442,7 @@ async function loadWorkspaceMemory(directory?: string): Promise<LoadOutcome> { | |
| if (block.expires && new Date(block.expires) <= new Date()) continue | ||
| blocks.push(block) | ||
| } | ||
| return { status: "loaded", blocks, epoch, dir } | ||
| return { status: "loaded", blocks, epoch, dir, datamateId } | ||
| } catch (err) { | ||
| log.warn("workspace memory load failed", { err: String(err) }) | ||
| // Stamped like any other outcome: without an epoch the session would reload (and make | ||
|
|
@@ -1461,6 +1462,9 @@ function commitLoad(sessionID: string, state: SessionMemory, outcome: LoadOutcom | |
| if (outcome.epoch !== epochFor(outcome.dir ?? null)) return | ||
| state.loadedEpoch = outcome.epoch | ||
| state.dir = outcome.dir ?? null | ||
| // altimate_change start — workspace sync state | ||
| recordSyncState(outcome) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Explicit refresh failures return before Prompt for AI agents |
||
| // altimate_change end | ||
| // An error keeps whatever the session had and is not retried every turn (as before). | ||
| if (outcome.status === "error") return | ||
| state.overlay = outcome.status === "loaded" ? outcome.blocks : [] | ||
|
|
@@ -1469,6 +1473,29 @@ function commitLoad(sessionID: string, state: SessionMemory, outcome: LoadOutcom | |
| } | ||
| } | ||
|
|
||
| // altimate_change start — workspace sync state | ||
| /** Record a committed load in the workspace sync state. Memory is loaded per session but | ||
| * recorded per project, so a second session reading the same blocks is not a change. | ||
| * Detached: the load is already committed and nothing waits on status metadata. A load | ||
| * that could not resolve a workspace, or found memory off, records nothing. */ | ||
| function recordSyncState(outcome: LoadOutcome): void { | ||
| if (!outcome.dir || outcome.datamateId === undefined) return | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MAJOR: most memory load failures never reach the sync state.
Fix:
|
||
| if (outcome.status !== "loaded" && outcome.status !== "error") return | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: Prompt for AI agents |
||
| const { dir, datamateId } = outcome | ||
| const items = | ||
| outcome.status === "loaded" | ||
| ? Object.fromEntries( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WARNING: Preserve all applicable blocks when building memory status items
Reply with |
||
| outcome.blocks.map((block) => [block.id, { label: blockTitle(block), version: block.updated ?? "" }]), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WARNING: Detect edits even when mirror metadata retains its old timestamp
Reply with |
||
| ) | ||
| : undefined | ||
| void import("./sync-state") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WARNING: Drain pending memory status writes before a one-shot run exits This detached import/write is not part of the hydration or refresh promise. Reply with |
||
| .then((SyncState) => | ||
| SyncState.record(dir, datamateId, "memory", items ? { items } : { error: "could not load workspace memory" }), | ||
| ) | ||
| .catch((err) => log.warn("could not record the memory sync state", { err: String(err) })) | ||
| } | ||
| // altimate_change end | ||
|
|
||
| /** A session's cloud overlay. Returns a copy so a caller cannot mutate the | ||
| * cached state in place. */ | ||
| export function overlayBlocks(sessionID: string): RemoteMemoryBlock[] { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -101,6 +101,8 @@ export interface Manifest { | |
| interface RemoteSummary { | ||
| publicId: string | ||
| updatedAt: string | ||
| /** Display name, for the sync-state change list. Optional: sync never depends on it. */ | ||
| name?: string | ||
| } | ||
|
|
||
| /** ``CustomSkillDetail.files`` — ``CustomSkillFileMeta`` is ``{path, size}``. */ | ||
|
|
@@ -244,6 +246,39 @@ export function describeSyncProblems(result: SyncResult): { title: string; messa | |
| return { title: `${n} workspace skill${n === 1 ? "" : "s"} skipped`, message: lines.join("\n") } | ||
| } | ||
|
|
||
| // altimate_change start — workspace sync state | ||
| /** Record this run in the workspace sync state. `rows` is the list the published snapshot | ||
| * describes, or null when the run left the snapshot as it was — the items are then | ||
| * unknown to this run and the previous record stands. Imported lazily: the state module | ||
| * reaches the bus, which this module must not load on the opted-out path. */ | ||
| async function recordSyncState( | ||
| directory: string, | ||
| datamateId: number, | ||
| result: SyncResult, | ||
| rows: RemoteSummary[] | null, | ||
| dropped: Set<string>, | ||
| ): Promise<void> { | ||
| const problem = describeSyncProblems(result) | ||
| const items = rows | ||
| ? Object.fromEntries( | ||
| rows | ||
| .filter((row) => !dropped.has(row.publicId)) | ||
| .map((row) => [row.publicId, { label: displayId(row.name ?? row.publicId), version: row.updatedAt }]), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WARNING: Use the version actually published for a carried skill When downloading remote skill v2 fails, the existing v1 directory and its manifest entry are carried into the partial published snapshot ( Reply with |
||
| ) | ||
| : undefined | ||
| try { | ||
| const SyncState = await import("./sync-state") | ||
| await SyncState.record(directory, datamateId, "skills", { | ||
| ...(items ? { items } : {}), | ||
| ...(problem ? { error: `${problem.title}: ${problem.message.split("\n").join("; ")}` } : {}), | ||
| }) | ||
| } catch (err) { | ||
| // Status metadata: never the reason a sync fails. | ||
| log.warn("could not record the skill sync state", { err: String(err) }) | ||
| } | ||
| } | ||
| // altimate_change end | ||
|
|
||
| /** A fixed, user-facing reason for a skill that failed to sync. The raw error | ||
| * can carry request URLs, server text or local paths — diagnostics for the | ||
| * log, not for a toast. */ | ||
|
|
@@ -746,10 +781,14 @@ function parsePage( | |
| const rows: RemoteSummary[] = [] | ||
| for (const row of p.items) { | ||
| if (!row || typeof row !== "object") return null | ||
| const r = row as { public_id?: unknown; updated_at?: unknown } | ||
| const r = row as { public_id?: unknown; updated_at?: unknown; name?: unknown } | ||
| if (typeof r.public_id !== "string" || !r.public_id) return null | ||
| if (typeof r.updated_at !== "string" || !r.updated_at) return null | ||
| rows.push({ publicId: r.public_id, updatedAt: r.updated_at }) | ||
| rows.push({ | ||
| publicId: r.public_id, | ||
| updatedAt: r.updated_at, | ||
| ...(typeof r.name === "string" && r.name ? { name: r.name } : {}), | ||
| }) | ||
| } | ||
| return { rows, pages: rawPages, total } | ||
| } | ||
|
|
@@ -1109,6 +1148,15 @@ export async function syncSkills(directory: string): Promise<SyncResult> { | |
| // Set once the workspace's list has actually been read. Only then has this | ||
| // project been "checked", and only then should the poll interval start. | ||
| let sawRemote = false | ||
| // altimate_change start — workspace sync state | ||
| // What the sync-state record needs: the workspace checked, the list it read, the skills | ||
| // that list named but this run could not install (and had no previous copy of), and | ||
| // whether the run left a snapshot describing that list. | ||
| let checkedDatamateId: number | undefined | ||
| let remoteRows: RemoteSummary[] | null = null | ||
| const dropped = new Set<string>() | ||
| let published = false | ||
| // altimate_change end | ||
| const run = (async () => { | ||
| // Checked BEFORE the binding: `resolveBinding` needs credentials too, so a | ||
| // disconnected client would otherwise return on a null binding and never | ||
|
|
@@ -1212,6 +1260,7 @@ export async function syncSkills(directory: string): Promise<SyncResult> { | |
| return | ||
| } | ||
| const binding = outcome.binding | ||
| checkedDatamateId = binding.datamateId | ||
|
|
||
| // Refuse to touch a directory we did not create. Everything below either | ||
| // deletes this tree or replaces it wholesale, so without this a user's own | ||
|
|
@@ -1279,6 +1328,7 @@ export async function syncSkills(directory: string): Promise<SyncResult> { | |
| return | ||
| } | ||
| sawRemote = true | ||
| remoteRows = remote | ||
| syncedFor.set(canon, accountKeyOf(creds.altimateInstanceName, creds.altimateUrl, account)) | ||
| // The workspace has skills now, even if installing them fails below. | ||
| if (remote.length > 0) await clearEmptyRecordFor(canon, currentEmptyKey) | ||
|
|
@@ -1289,6 +1339,7 @@ export async function syncSkills(directory: string): Promise<SyncResult> { | |
| // live by then — another process can swap a partial snapshot in | ||
| // between, and the marker would vouch for a sync this run never made. | ||
| validated = manifest | ||
| published = true | ||
| return | ||
| } | ||
|
|
||
|
|
@@ -1302,6 +1353,7 @@ export async function syncSkills(directory: string): Promise<SyncResult> { | |
| if (written && (after?.status !== "bound" || after.binding.datamateId !== binding.datamateId)) | ||
| await withdrawEmptyRecord(canon, written) | ||
| changed = true | ||
| published = true | ||
| log.info("workspace has no custom skills; removed the local snapshot") | ||
| return | ||
| } | ||
|
|
@@ -1354,6 +1406,7 @@ export async function syncSkills(directory: string): Promise<SyncResult> { | |
| failed = true | ||
| log.warn("skipping a workspace skill with an unusable id", { skill: summary.publicId }) | ||
| skippedSkills.push({ skill: displayId(summary.publicId), reason: "its id is not usable as a folder name" }) | ||
| dropped.add(summary.publicId) | ||
| continue | ||
| } | ||
| try { | ||
|
|
@@ -1435,6 +1488,7 @@ export async function syncSkills(directory: string): Promise<SyncResult> { | |
| } | ||
| if (!carried) { | ||
| await fs.rm(path.join(staging, summary.publicId), { recursive: true, force: true }).catch(() => {}) | ||
| dropped.add(summary.publicId) | ||
| } | ||
| log.warn("skipping a workspace skill; the rest of the snapshot still publishes", { | ||
| skill: summary.publicId, | ||
|
|
@@ -1516,6 +1570,7 @@ export async function syncSkills(directory: string): Promise<SyncResult> { | |
| await fs.rm(retired, { recursive: true, force: true }).catch(() => {}) | ||
| await clearEmptyRecordFor(canon, currentEmptyKey) | ||
| changed = true | ||
| published = true | ||
| log.info("workspace skills synced", { | ||
| datamateId: binding.datamateId, | ||
| skills: remote.length, | ||
|
|
@@ -1566,7 +1621,11 @@ export async function syncSkills(directory: string): Promise<SyncResult> { | |
| // too — otherwise a problem it fixed would stay latched, and its return | ||
| // would never be announced. | ||
| if (skippedSkills.length === 0 && !syncError) store.announced.delete(canon) | ||
| return { changed, skipped: skippedSkills, error: syncError } | ||
| const result: SyncResult = { changed, skipped: skippedSkills, error: syncError } | ||
| // altimate_change start — workspace sync state | ||
| if (checkedDatamateId !== undefined) await recordSyncState(canon, checkedDatamateId, result, published ? remoteRows : null, dropped) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WARNING: Retire persisted status when a snapshot is deactivated The new recorder only runs after a bound sync; Reply with |
||
| // altimate_change end | ||
| return result | ||
| })() | ||
| inFlight.set(canon, settled) | ||
| try { | ||
|
Comment on lines
1621
to
1631
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift 🔎 Supported by static analysis🏁 Script executed: rg -n 'publish|stage|binding|epoch|notifyBindingChanged|skillRoot' packages/opencode/src/altimate/workspace/skill-sync.ts | tail -85
sed -n '1540,1635p' packages/opencode/src/altimate/workspace/skill-sync.tsRepository: AltimateAI/altimate-code Length of output: 9914 🏁 Script executed: set -o pipefail
printf '%s\n' '--- sync publication and binding checks ---'
nl -ba packages/opencode/src/altimate/workspace/skill-sync.ts | sed -n '1235,1585p'
printf '%s\n' '--- managed-root and skill discovery references ---'
rg -n -F --glob '*.ts' -- 'managedRoot(' packages/opencode/src packages/opencode/test || test "$?" -eq 1
rg -n -F --glob '*.ts' -- 'workspace skills' packages/opencode/src packages/opencode/test || test "$?" -eq 1
rg -n -F --glob '*.ts' -- 'SKILLS' packages/opencode/src/altimate packages/opencode/src | head -160 || true
printf '%s\n' '--- binding-change cleanup and sync callers ---'
rg -n -F --glob '*.ts' -- 'notifyBindingChanged' packages/opencode/src packages/opencode/test || test "$?" -eq 1
rg -n -F --glob '*.ts' -- 'syncSkills(' packages/opencode/src packages/opencode/test || test "$?" -eq 1Repository: AltimateAI/altimate-code Length of output: 41184 🏁 Script executed: printf '%s\n' '--- publication block ---'
nl -ba packages/opencode/src/altimate/workspace/skill-sync.ts | sed -n '1288,1385p;1480,1585p'
printf '%s\n' '--- managed root readers and binding cleanup ---'
rg -n -F --glob '*.ts' -- 'managedRoot(' packages/opencode/src packages/opencode/test || true
rg -n -F --glob '*.ts' -- 'notifyBindingChanged' packages/opencode/src packages/opencode/test || true
rg -n -F --glob '*.ts' -- 'WORKSPACE_SKILLS' packages/opencode/src packages/opencode/test || trueRepository: AltimateAI/altimate-code Length of output: 14697 🏁 Script executed: printf '%s\n' '--- binding-change notifier ---'
nl -ba packages/opencode/src/altimate/workspace/state.ts | sed -n '820,855p;1275,1320p'
printf '%s\n' '--- skill discovery implementation and managed-root consumers ---'
rg -n -F --glob '*.ts' -- 'discover' packages/opencode/src/skill packages/opencode/src/altimate packages/opencode/src/session || true
rg -n -F --glob '*.ts' -- 'refreshSkillRegistry' packages/opencode/src packages/opencode/test || true
nl -ba packages/opencode/src/skill/index.ts | sed -n '1,260p'Repository: AltimateAI/altimate-code Length of output: 40618 🏁 Script executed: printf '%s\n' '--- skill discovery roots and registry ---'
nl -ba packages/opencode/src/skill/index.ts | sed -n '235,335p;390,535p'
printf '%s\n' '--- sync-triggered registry refresh ---'
nl -ba packages/opencode/src/session/prompt.ts | sed -n '120,150p;360,425p'Repository: AltimateAI/altimate-code Length of output: 19533 🏁 Script executed: nl -ba packages/opencode/src/skill/index.ts | sed -n '308,390p'
rg -n -F --glob '*.ts' -- 'withoutForeignSnapshots' packages/opencode/src packages/opencode/test || true
rg -n -F --glob '*.ts' -- 'current binding' packages/opencode/src/skill packages/opencode/src/altimate/workspace || trueRepository: AltimateAI/altimate-code Length of output: 4718 🏁 Script executed: nl -ba packages/opencode/src/altimate/workspace/skill-sync.ts | sed -n '1655,1745p'
rg -n -F --glob '*.ts' -- 'snapshotIsOurs' packages/opencode/src packages/opencode/test || trueRepository: AltimateAI/altimate-code Length of output: 3131 🏁 Script executed: nl -ba packages/opencode/src/altimate/workspace/skill-sync.ts | sed -n '100,190p;1090,1120p'Repository: AltimateAI/altimate-code Length of output: 7130 Prevent stale snapshots from crossing workspace rebinds. A read-side guard exists, but Use a binding epoch or equivalent serialized binding check around the publication swap. Discard stale staging and skip state recording when the captured binding is no longer current. 🤖 Prompt for AI Agents |
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2:
memoryLoadedAtreports the last check even when that check failed. After a successful load followed by an outage, the sidebar shows the outage time as “memory loaded”; only expose this timestamp when the memory entity status isok(or preserve a separate last-successful-load timestamp).Prompt for AI agents