Commit Graph
5 Commits
Author SHA1 Message Date
Alexandre Reyes Martins 8ea94a119f fix: restore baseline validation (#3142) 2026-08-26 17:15:49 +03:00
Alexandre Reyes Martins d19ff96c02 fix(sync): guard delete actions by default
Follow-up to #2574 and f95f1ab18, which guarded the archive path. The
delete path had the same two defects and worse consequences.

`useSessionUIStore.deleteSession`/`deleteSessions` declared an `options`
parameter and discarded it on both paths, so a caller-supplied runtime
key was a silent no-op. `SessionDialogs.tsx:416` already passes options
today and they never reach the action.

The delete path also never rechecked the runtime. Session IDs are not
unique across runtimes, so a response produced by a previous runtime
could commit `finalizeConfirmedSessionDeletion` against the runtime the
user switched to: evicting an unrelated session from the live and global
stores and calling `cleanupPersistedSessionState`, which erases queued
messages, todos, folder membership, inline-comment drafts, chat draft,
and pins. That is user data loss, not stale cache.

`cleanupPersistedSessionState` already rejects an identity whose runtime
is no longer active, but `finalizeConfirmedSessionDeletion` defeated that
check by passing the live `getRuntimeKey()` at commit time, comparing a
value with itself. It now forwards the captured key.

Adopt the default-on shape from f95f1ab18: `expectedRuntimeKey` defaults
to the active runtime in `deleteSession`, `deleteSessionInDirectory` and
the new canonical `deleteSessions` action, and is rechecked before the
request and before every reconciliation. A `404` still means "already
deleted" and commits cleanup, but only while the captured runtime is
active; after a switch it describes the wrong runtime and the action
reports failure instead of committing.

Also documents the throw contract of `patchSessionMetadata`, a
non-blocking nit raised by the review bot on #2574.
2026-08-02 15:13:06 +00:00
Alexandre Reyes Martins 2e8fc6e192 fix(sync): honor expectedRuntimeKey in archive actions
`useSessionUIStore.archiveSessions` declared an `options` parameter and
discarded it, so any caller passing a captured runtime key got a silent
no-op. The archive path also never rechecked the runtime, letting a
response produced by a previous runtime reconcile the live and global
session stores of the runtime the user had switched to.

Move the batch to a canonical `archiveSessions()` action, add an optional
`expectedRuntimeKey` to `archiveSession()`, `patchSessionMetadata()`, and
`cleanupReviewMetadataBeforeDelete()`, and recheck that key before every
store reconciliation. A guarded batch stops at the first observed runtime
change: server-confirmed sessions stay in `archivedIds` and every
unconfirmed ID is returned in `failedIds`, so existing partial-failure
feedback stays truthful. Callers that pass no key keep prior behavior.

Type the store option as `ArchiveSessionsOptions` instead of
`Record<string, unknown>`, since the loose type allowed the drop.
2026-08-02 11:55:02 +00:00
Alexandre Reyes Martins b54741bab5 test(sync): cover the archived session query boundary
Add coverage for the archived-only narrowing: mixed pages return archived
records only, active pages stay unfiltered, a page that is full upstream
but fully filtered out keeps paginating, onPage receives only accepted
records, and id dedupe still stops a repeating page.

Document the OpenCode `archived` flag semantics in the owning store
documentation.
2026-08-02 11:51:25 +00:00
Alexandre Reyes Martins 7eb43fc5d5 fix(sync): narrow the archived session query at the data boundary
OpenCode's `archived` flag drops the `time_archived IS NULL` condition
instead of selecting archived rows, so the archived list came back with
active sessions mixed in. Filter at the boundary and keep pagination
progress measured on the raw response.
2026-08-02 11:45:40 +00:00