#132 wasn't complete, I still can't remove a milestone from an issue #143

Closed
opened 2026-09-05 16:17:10 +00:00 by crueber · 3 comments
Owner

image

See this picture. I still can't pull a milestone off an issue.

![image](/attachments/1246ccda-6467-422d-9e41-b1e44b3b5c86) See this picture. I still can't pull a milestone off an issue.
Author
Owner

Investigated + fix proposed in PR #145 (fix/issue-143, not merged - review requested).

What I found: the served stack already runs the fixed milestone path (bundle hash vs fresh origin/main build differs only by the unrelated #142 owners change; backend null-clear + service clear-after-close are covered by tests), and fresh-browser drives clear fine on open AND closed issues (PATCH 200, API-confirmed, zero console errors). But your session's server log shows the set-PATCH landing at 16:15:23 with NO clear-PATCH ever arriving - the click died client-side with zero feedback.

The silent-drop window I could prove in code: the thread route reuses one component instance across issue numbers, so an in-flight sidebar mutation's busy guard leaks onto the next issue and its early-return swallows the click (no PATCH, no tray entry, menu just closes). The PR pins num()/key() up front, reconciles the pinned key, and resets the guards on navigation.

Side note: #2's milestone is now cleared on the live stack (your desired end state); #1 untouched at v1.1.

Investigated + fix proposed in PR #145 (fix/issue-143, not merged - review requested). What I found: the served stack already runs the fixed milestone path (bundle hash vs fresh origin/main build differs only by the unrelated #142 owners change; backend null-clear + service clear-after-close are covered by tests), and fresh-browser drives clear fine on open AND closed issues (PATCH 200, API-confirmed, zero console errors). But your session's server log shows the set-PATCH landing at 16:15:23 with NO clear-PATCH ever arriving - the click died client-side with zero feedback. The silent-drop window I could prove in code: the thread route reuses one component instance across issue numbers, so an in-flight sidebar mutation's busy guard leaks onto the next issue and its early-return swallows the click (no PATCH, no tray entry, menu just closes). The PR pins num()/key() up front, reconciles the pinned key, and resets the guards on navigation. Side note: #2's milestone is now cleared on the live stack (your desired end state); #1 untouched at v1.1.
Author
Owner

REVIEW PR #145 (fix/issue-143, 57a86b8) — milestone-removal stale busy guard.

MECHANISM: confirmed. Thread route reuses one Issue.jsx instance across nums; labelBusy Set + milestoneBusy bool lived across navigation, so an in-flight sidebar PATCH on issue A left the guard set while viewing issue B — busy early-return fires no PATCH + no tray entry, menu just closes. Tail also used live key()/num()/reload(), reconciling the wrong issue. Diagnosis matches code.

FIX COMPLETENESS:

  • web/src/pages/Issue.jsx:65-76 — nav effect resets both guards on num() change. Correct; declarations hoisted above effect so no early read. OK.
  • :214-234 toggleLabelApply + :245-261 selectMilestone — both pin n=num()/ck=key() synchronously before first await (thread() read for next/fields is in the same sync tick, so also pinned), patchCached(ck)+patch(n), then num()===n ? reload() : invalidate(ck). Covers BOTH sidebar paths sharing the pattern. OK.
  • Guard-reset race walked: A-toggle in flight -> nav to B (guards cleared) -> tail invalidate(ckA) reconciles A, then deletes k from cleared set (no-op). Parallel A+B PATCHes target different pinned nums/keys — no double-fire on one resource, no lost update. Milestone bool same (tail sets false idempotently). Same-issue double-click still guarded (no nav between). OK.
  • Normal flows unchanged: no-nav path is reload(), identical to before; reset fires only on num change. OK.
  • Laws: 1 no new deps (2-file diff, no package.json/import changes); 7 every real click PATCHes or reports preserved, no-op milestone select still silently skips by design; 8 page+doc only; 12 doc entry in same commit accurate (docs/features/02_issues.md pinned-issue bullet matches code).

NOTE (non-blocking follow-up, not this issue): reaction guards getBusy (:128-144) + react/toggleReaction tails (:159-194) share the stale-key shape (live key()/num()/reload(), guard not reset on nav) — cross-issue swallow needs same seq:content collision so narrower than the sidebar case, and comment/close/patch (:94-120, :196-203) have stale-reload() but no guard. Suggest separate issue if worth pinning those too; out of scope for #143 milestone-removal.

VERIFY (scratch worktree /tmp/pr145 @57a86b8, node_modules symlinked from main worktree, main untouched): node --test web/test/unit/*.test.js = 317 pass / 0 fail; vite build (web/node_modules/.bin/vite build) = OK (121 modules, built in ~1.6s). No browser drive (JSX guard/pin logic + reasoning per instructions).

MERGE RECOMMENDATION: ready to merge.

REVIEW PR #145 (fix/issue-143, 57a86b8) — milestone-removal stale busy guard. MECHANISM: confirmed. Thread route reuses one Issue.jsx instance across nums; labelBusy Set + milestoneBusy bool lived across navigation, so an in-flight sidebar PATCH on issue A left the guard set while viewing issue B — busy early-return fires no PATCH + no tray entry, menu just closes. Tail also used live key()/num()/reload(), reconciling the wrong issue. Diagnosis matches code. FIX COMPLETENESS: - web/src/pages/Issue.jsx:65-76 — nav effect resets both guards on num() change. Correct; declarations hoisted above effect so no early read. OK. - :214-234 toggleLabelApply + :245-261 selectMilestone — both pin n=num()/ck=key() synchronously before first await (thread() read for next/fields is in the same sync tick, so also pinned), patchCached(ck)+patch(n), then num()===n ? reload() : invalidate(ck). Covers BOTH sidebar paths sharing the pattern. OK. - Guard-reset race walked: A-toggle in flight -> nav to B (guards cleared) -> tail invalidate(ckA) reconciles A, then deletes k from cleared set (no-op). Parallel A+B PATCHes target different pinned nums/keys — no double-fire on one resource, no lost update. Milestone bool same (tail sets false idempotently). Same-issue double-click still guarded (no nav between). OK. - Normal flows unchanged: no-nav path is reload(), identical to before; reset fires only on num change. OK. - Laws: 1 no new deps (2-file diff, no package.json/import changes); 7 every real click PATCHes or reports preserved, no-op milestone select still silently skips by design; 8 page+doc only; 12 doc entry in same commit accurate (docs/features/02_issues.md pinned-issue bullet matches code). NOTE (non-blocking follow-up, not this issue): reaction guards getBusy (:128-144) + react/toggleReaction tails (:159-194) share the stale-key shape (live key()/num()/reload(), guard not reset on nav) — cross-issue swallow needs same seq:content collision so narrower than the sidebar case, and comment/close/patch (:94-120, :196-203) have stale-reload() but no guard. Suggest separate issue if worth pinning those too; out of scope for #143 milestone-removal. VERIFY (scratch worktree /tmp/pr145 @57a86b8, node_modules symlinked from main worktree, main untouched): node --test web/test/unit/*.test.js = 317 pass / 0 fail; vite build (web/node_modules/.bin/vite build) = OK (121 modules, built in ~1.6s). No browser drive (JSX guard/pin logic + reasoning per instructions). MERGE RECOMMENDATION: ready to merge.
Author
Owner

Fixed by PR #145 (review clean; stale busy-guard root cause pinned per action; 317/317 node tests), merged. Closing.

Fixed by PR #145 (review clean; stale busy-guard root cause pinned per action; 317/317 node tests), merged. Closing.
crueber added this to the v1 milestone 2026-09-10 22:20:47 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
crueber/walhub#143
No description provided.