Skip to content

fix(web): make itemMatchesRef workspace-aware in ItemDetail (IDEA-2135)#1008

Merged
xarmian merged 1 commit into
mainfrom
fix/itemdetail-ws-aware-matchesref
Jul 22, 2026
Merged

fix(web): make itemMatchesRef workspace-aware in ItemDetail (IDEA-2135)#1008
xarmian merged 1 commit into
mainfrom
fix/itemdetail-ws-aware-matchesref

Conversation

@xarmian

@xarmian xarmian commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

What

Makes the no-{#key} switch-boundary predicate itemMatchesRef in web/src/lib/components/items/ItemDetail.svelte workspace-aware.

Why (IDEA-2135)

itemMatchesRef compared only ref/slug/id identity, never workspace. On a reused embedded ItemDetail instance, navigating ws1?item=TASK-1 → ws2?item=TASK-1 — where both workspaces own a TASK-1 — kept the predicate true across the switch (wsSlug changed but itemSlug stayed TASK-1 and the {#if openItemRef} never unmounts the pane). Consequences until ws2's loadData() resolved:

  1. Stale collabKey — stayed pinned to ws1's item.id, mounting a collab provider mis-keyed to the old item.
  2. rawMode carry-over — the TASK-2124 switch-gated reset keyed off itemMatchesRef, so raw markdown mode carried across the workspace switch.

scrollReady, resolvedIdentity, collabKey, and the rawMode-reset gate all derive from this one predicate, so the correct fix is at the predicate level — they then tighten together and stay consistent (the fix the idea asked for).

How

  • Stamp loadedItemWsSlug = reqWsSlug at the item-adoption point in loadData, inside the existing myItemGen === itemGen gate so it's lock-stepped with item and the two can never disagree about which workspace item belongs to.
  • Fold loadedItemWsSlug === wsSlug into the itemMatchesRef $derived.

The Item type carries workspace_id (UUID) but the component only has wsSlug (slug); stamping the load-time slug avoids coupling to a slug→id resolver. Every other item = path (SSE refresh, archive, restore, migration refetch, sync, saves, version restore) operates on the same item in the current workspace, so loadedItemWsSlug can't go stale relative to item.

Blast radius

In all single-workspace usage loadedItemWsSlug === wsSlug is always true → byte-identical behavior. The arm only flips false in the rare cross-workspace-reuse window (correctly). Barely reachable by design (no natural UI affordance preserves ?item= across a workspace switch); filed as hardening.

Gates

  • cd web && npm run check — svelte-check 0 errors.
  • Codex CLI review (-s read-only, diff main..HEAD) — CLEAN; specifically probed whether loadedItemWsSlug can go stale and wrongly falsify the predicate in single-workspace usage (it can't).

Frontend-only; no internal/store/dialect, MCP catalog, or collab-schema surface touched.

Closes IDEA-2135. TASK-2283.

https://claude.ai/code/session_01EZ6yr6pAUFb1uffan912ra

The no-{#key} switch-boundary predicate compared only ref/slug identity,
never workspace. On a reused embedded ItemDetail instance, navigating
ws1?item=TASK-1 -> ws2?item=TASK-1 (both workspaces owning TASK-1) kept
the predicate true across the switch, leaving collabKey pinned to ws1's
item.id and rawMode carried over until ws2's loadData resolved.

Stamp the wsSlug each item is loaded under (loadedItemWsSlug, lock-stepped
with item adoption inside the myItemGen===itemGen gate) and fold
loadedItemWsSlug === wsSlug into itemMatchesRef. scrollReady, collabKey,
resolvedIdentity, and the rawMode-reset gate all derive from it, so they
tighten together and stay consistent. Single-workspace usage is unchanged
(the arm is always true there).

TASK-2283.

Claude-Session: https://claude.ai/code/session_01EZ6yr6pAUFb1uffan912ra
@xarmian
xarmian force-pushed the fix/itemdetail-ws-aware-matchesref branch from 1560823 to d7d351e Compare July 22, 2026 23:30
@xarmian
xarmian merged commit e2ec876 into main Jul 22, 2026
6 checks passed
@xarmian
xarmian deleted the fix/itemdetail-ws-aware-matchesref branch July 22, 2026 23:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant