fix(board): sync the board after buddy and authored card writes (#233) - #271
Draft
daniilperkin wants to merge 2 commits into
Draft
daniilperkin wants to merge 2 commits into
daniilperkin wants to merge 2 commits into
Conversation
Issue #233: keeping an AI reply as a checklist wrote the card server-side, but the board kept serving what it had already read — a stale cache, not a stale server. The write path (POST /me/board/cards) had no relationship to the board query, so an open board behind the floating dock never refetched at all, and a visit within the 30s `staleTime` was served the pre-card board. This closes every write into the board that lives outside the board itself, following the pattern PathStepCard already established: - SaveReplyToBoard ("Keep as checklist") — the reported defect. - SaveToBoard ("Keep on my board") — the shared button behind chat replies, buddy replies and task cards. - SelectionActions ("Add to board") — cards authored from a selection. - useBuddyConversation — the buddy's own board writes: - confirmed board actions (place/amend/tick/reword checklist, place_note) invalidate after a write that actually succeeded. `claim_goal` is in the set too: confirming it also pins the CURRENT_TASK card onto the board ("It's on your board too"). - the `place_card` tool applies mid-stream with no confirmation step, so the `tool_use` event is the only signal a card landed; the turn marks the board stale when it ends — the failing path included, since a turn that placed a card and then broke still wrote one. The three component paths invalidate by project (`board.byProject`); the buddy hook invalidates `board.all()`, because the backend re-resolves the project server-side for buddy writes and the client never learns which board it wrote. Writes that fail invalidate nothing — a broken save must not cost the board its cache. The remaining addCard caller, useGeneratedPathCards, needs nothing here: its only caller (BoardPage's generate flow) already calls the board's own `refresh` after applying the plan. Refs #233
Covers the sync behaviour the fix introduces, and the boundaries it must not cross: - SaveReplyToBoard (new): the #233 regression itself — saving the checklist marks `board.byProject(selectedProjectId)` stale; a reply without a list renders nothing; a failed save toasts and leaves the cache alone. - SaveToBoard (new): the shared "keep this" button invalidates once the card is really on the board, still reports to `onSaved` for its callers, and does neither when the write failed. - SelectionActions (existing, one test added): the toolbar's save marks the board stale. - useBuddy (existing, new `board synchronisation` describe): a confirmed board action marks the board stale, `claim_goal` included; a failed confirm and a non-board action (request_attestation) do not; a turn that ran `place_card` marks it, and a turn that ran other tools does not. Two harness notes for whoever extends these next: the seeded board query is observer-less (pure `setQueryData`), so it must not get `gcTime: 0` — it would be garbage-collected before the invalidation can be observed and `isInvalidated` would read as `undefined`. And the save-button test clears storage via an optional call (`window.localStorage?.clear()`) because jsdom ships with or without a Storage backing depending on the Node version in use. Refs #233
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related issue
#233 — Synchronize Buddy-created checklists with the board immediately
Short summary
"Keep as checklist" wrote the card server-side, but the board kept serving what it had already read — a stale React Query cache, not a stale server. The write path (
POST /me/board/cards) had no relationship to the board query, so an open board behind the floating buddy dock never refetched at all, and a visit within the 30 sstaleTimewas served the pre-card board.This PR makes every board write that happens outside the board itself mark the board cache stale, following the invalidation pattern
PathStepCardalready established. The board then shows the change on its next read — immediately when it is open — with no manual refresh.The surfaces that now sync
SaveReplyToBoard("Keep as checklist")board.byProject(selectedProjectId)SaveToBoard("Keep on my board")board.byProject(selectedProjectId)SelectionActions("Add to board")board.byProject(selectedProjectId)useBuddyConversation.confirmActionplace_checklist,amend_checklist,tick_checklist_items,reword_checklist_item,place_note— andclaim_goal, which pins the CURRENT_TASK cardboard.all()useBuddyConversation.sendMessageplace_cardtool, which applies mid-answerboard.all()when the turn ran it (failure path included)Decisions worth knowing
board.all()rather than the selected project's key: the backend re-resolves the project server-side (the caller's single onboarding project) and the client never learns which board it wrote.claim_goalis in the board-action set because confirming it writes twice: the claim, and the CURRENT_TASK card ("It's on your board too").place_cardis deliberately not confirmed, so thetool_useevent during the turn is the only signal a card landed; the sync is applied when the turn ends, the failing path included — a turn that placed a card and then broke still wrote one.useGeneratedPathCards'addCardloop — its only caller (BoardPage.handleGenerate) already callsuseBoard().refresh()after applying the plan;useBoard's own mutations self-manage.Checks
npm run format:check,npm run lint,npm run build)tests/unit/features/buddy/SaveReplyToBoard.test.tsx,tests/unit/features/board/SaveToBoard.test.tsx,tests/unit/features/buddy/useBuddy.test.tsx,tests/unit/features/board/SelectionActions.test.tsx)How to verify manually
/boardfirst (so its cache is warm), then open the buddy dock without leaving the page. Ask something the buddy answers with a list, press Keep as checklist — the new checklist must appear on the board behind the dock without pressing Refresh./board, close it, go to/buddyand keep a reply as a checklist within 30 s. Navigate back to/board— the card must be there without Refresh./buddy, trigger a board action (checklist/note proposal) and confirm it — the board must reflect it on next view; a failed confirm must leave the board untouched./board— the card must be there.Commits
28300d49fix(board): sync the cached board after buddy and authored card writes02bf8038test(board): pin board invalidation across buddy and save surfacesVerified locally on this branch:
npm run format:checkgreen,npm run buildgreen,npm run lintgreen,npm run unitgreen (330 files / 3211 tests),npm run a11ygreen (55 files / 69 tests).