Fix #318: cross-page cache invalidation #321

Merged
crueber merged 1 commit from fix/issue-318 into main 2026-09-11 12:06:05 +00:00
Owner

Fixes #318 — milestone reassignment showed stale membership in milestone-filtered issue lists until reload (client cache-invalidation gap; server path already correct).\n\nRoot cause: the useData promise cache is global across Solid-router navigations, but invalidation rode per-page SSE subscriptions — the issue page's num accept filter gated the shared issues:{full}:* invalidation, and the milestones page subscribed to no issue frames at all.\n\nChanges (no new endpoints, no polling, no new deps):\n- web/src/lib/data.js: new invalidateIssueLists(full) — mutation-site reconcile of the issues:{full}: prefix (Issues.jsx windows + issues:{full}:milestone:{id} entries) plus milestones:{full} counts.\n- web/src/pages/Issue.jsx: all 8 thread-mutation tails route through afterMutation() (pinned thread reconcile + invalidateIssueLists); useCollabStream accept filter dropped (frame keys are self-scoping — issue:{full}:{num} names the frame's own thread — so the filter only suppressed shared-prefix invalidation).\n- web/src/pages/Milestones.jsx: subscribes to issue frames (Issues.jsx pattern) so inline lists + counts self-heal while mounted.\n- web/src/lib/collab.js: issue frame map gains milestones:{full} (reassignment/close move counts without touching any milestone object).\n- Tests: new web/test/unit/issue-invalidation.test.js (frame→key set, prefix coverage of both stale surfaces, mutation-site refetch scoping); collab-lib.test.js frame-table pin updated.\n- Docs: decision appended to docs/features/08_ui_sdk.md Decisions (law 12).\n\nOut of scope (noted in decision): pull pages keep their num filters — same latent pattern, follow-up.\n\nVerification: targeted node --test green (issue-invalidation, collab-lib, data-guard: 14/14); every other unit file green per-file; smoke.test.js hangs identically on clean main (pre-existing environmental — needs a live server, skip probe itself stalls); vite build passes (JSX compiles). Browser check: open (shared obscura daemon blocks loopback; no daemon started per instructions).

Fixes #318 — milestone reassignment showed stale membership in milestone-filtered issue lists until reload (client cache-invalidation gap; server path already correct).\n\nRoot cause: the useData promise cache is global across Solid-router navigations, but invalidation rode per-page SSE subscriptions — the issue page's num accept filter gated the shared `issues:{full}:*` invalidation, and the milestones page subscribed to no `issue` frames at all.\n\nChanges (no new endpoints, no polling, no new deps):\n- `web/src/lib/data.js`: new `invalidateIssueLists(full)` — mutation-site reconcile of the `issues:{full}:` prefix (Issues.jsx windows + `issues:{full}:milestone:{id}` entries) plus `milestones:{full}` counts.\n- `web/src/pages/Issue.jsx`: all 8 thread-mutation tails route through `afterMutation()` (pinned thread reconcile + `invalidateIssueLists`); `useCollabStream` accept filter dropped (frame keys are self-scoping — `issue:{full}:{num}` names the frame's own thread — so the filter only suppressed shared-prefix invalidation).\n- `web/src/pages/Milestones.jsx`: subscribes to `issue` frames (Issues.jsx pattern) so inline lists + counts self-heal while mounted.\n- `web/src/lib/collab.js`: `issue` frame map gains `milestones:{full}` (reassignment/close move counts without touching any milestone object).\n- Tests: new `web/test/unit/issue-invalidation.test.js` (frame→key set, prefix coverage of both stale surfaces, mutation-site refetch scoping); `collab-lib.test.js` frame-table pin updated.\n- Docs: decision appended to `docs/features/08_ui_sdk.md` Decisions (law 12).\n\nOut of scope (noted in decision): pull pages keep their num filters — same latent pattern, follow-up.\n\nVerification: targeted node --test green (issue-invalidation, collab-lib, data-guard: 14/14); every other unit file green per-file; `smoke.test.js` hangs identically on clean main (pre-existing environmental — needs a live server, skip probe itself stalls); vite build passes (JSX compiles). Browser check: open (shared obscura daemon blocks loopback; no daemon started per instructions).
Milestone reassignment showed stale membership in milestone-filtered
lists until reload: the promise cache is global across Solid-router
navigations but invalidation rode per-page SSE subscriptions. The
issue page reconciles every thread mutation at the mutation site via
invalidateIssueLists (issues: prefix + milestones counts), the
milestones page subscribes to issue frames, the issue frame map gains
milestones:{full}, and the issue page drops its num accept filter
(frame keys are self-scoping). Decision appended to 08 Decisions.
Sign in to join this conversation.
No description provided.