Skip to content

Fix Level 2 candidate grid rendering empty until a chip is touched - #63

Merged
WilfordGrimley merged 1 commit into
masterfrom
claude/level2-candidate-grid-reset
Jul 18, 2026
Merged

Fix Level 2 candidate grid rendering empty until a chip is touched#63
WilfordGrimley merged 1 commit into
masterfrom
claude/level2-candidate-grid-reset

Conversation

@WilfordGrimley

Copy link
Copy Markdown

Description

Owner-observed, post-#58: Level 2's candidate grid sometimes shows only the suggested match with an empty grid until the user interacts with an attribute-chip filter (observed on Kiku, Night's Flower — CHK original absent until chips touched).

Diagnosis: (a) real bug, not (b) intentional design gap. Not a #58 regression — the layout reconciliation pass touched CardPanel/ChipRing's CSS only; this is a pre-existing state-reset bug in QuestionFeed.tsx's data flow, unrelated to layout.

Root cause: per-question local state (chipStates/revealed/filterExpanded/selectedCandidateId/etc) reset via a separate useEffect keyed on [item?.card.identifier, item?.type]. That dependency array silently skips the reset whenever two consecutive feed items share both values — which happens for real: a single card can carry more than one pending question (printing + artist + tags), or the same question can be re-served. A chip left "positive" from the previous card then filters the new card's candidates against an unrelated attribute, hiding some or all of them — until the user happens to touch a chip themselves, which is the only other thing that ever updates chipStates, incidentally "fixing" it by replacing the stale filter with a fresh one.

Fix: reset the per-question state inside the same .then() callback that calls setItem(...), instead of a separate effect with a dependency array that can fail to change between renders. This removes the entire bug class rather than trying to compute a "definitely always changes" key.

Checklist

  • I have installed pre-commit and installed the hooks with pre-commit install before creating any commits.
  • I have updated any related tests for code I modified or added new tests where appropriate.
    • New Jest test reproducing the exact same-identifier-and-type repeat condition, asserting all of the second item's candidates render and the filter panel resets closed. Verified this test actually catches the regression: temporarily reverted the fix and re-ran — the test fails (the second item's reveal overlay never appears at all, since revealed never resets when identifier+type repeat) — then restored the fix and confirmed it passes.
  • I have manually tested my changes as follows:
    • npx tsc --noEmit, npx jest --runInBand (251/251 passing, no flakes this run), npx eslint — all clean.
    • Full Playwright regression across every touched surface (QuestionFeedLevels, QuestionFeedConfirmSuggestion, QuestionFeedMobileLayout, QuestionFeedLayoutReconciliation): 17/17 passing.
  • I have updated any relevant documentation or created new documentation where appropriate.
    • docs/features/printing-tags.md's frontend architecture section updated in place with the root cause and fix rationale.

Merge-time checklist

None — this is layout-independent state-management logic, no real-device or live-CDN dependency; the Jest test's revert-and-confirm methodology is a stronger correctness guarantee than a real-device pass would add here.


Generated by Claude Code

Root cause: per-question local state (chipStates/revealed/
filterExpanded/selectedCandidateId/etc) reset via a separate
useEffect keyed on [item?.card.identifier, item?.type]. That
dependency array silently skips the reset whenever two consecutive
feed items share both values - which happens for real, since a
single card can carry more than one pending question, or the same
question can be re-served. A chip left "positive" from the previous
card then filters the new card's candidate grid against an unrelated
attribute, hiding candidates (up to all of them) until the user
happened to touch a chip themselves - the only other thing that ever
updated chipStates, which incidentally "fixed" it by replacing the
stale filter with a fresh one.

Not a #58 regression - the layout reconciliation pass touched
CardPanel/ChipRing's CSS only, not this state-reset logic, which
predates it.

Fix: reset the per-question state inside the same .then() callback
that calls setItem(), rather than a separate effect with a
dependency array that can fail to change. This removes the whole bug
class rather than trying to compute a "definitely always changes"
key.

Verified the new Jest test fails against the pre-fix code (confirmed
by temporarily reverting the fix and re-running: the second item's
reveal overlay never appears, since `revealed` never resets when
identifier+type repeat) and passes against the fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014dB7mGPQyoML2Ri5ZrprdK
@WilfordGrimley
WilfordGrimley merged commit 8463dd5 into master Jul 18, 2026
6 checks passed
@WilfordGrimley
WilfordGrimley deleted the claude/level2-candidate-grid-reset branch July 18, 2026 14:23
WilfordGrimley added a commit that referenced this pull request Jul 18, 2026
…staleness mini-pass (#79)

* publish_wiki.py: fix link rewriting for parent-relative paths and slug casing

GitHub wiki's native [[...]] auto-linking reinterprets our own docs/
[[file.md]] convention: [[../troubleshooting.md]] became a dead literal
slug, and [[printing-tags.md]] linked the raw filename casing instead of
the published page name "Printing-Tags" (both visible live on
Catalog-Completion-Plan). Every internal link is now resolved against
its source file's real repo path and mapped through
wiki-publish-map.json: a published target becomes a same-wiki link using
its real page name, an unpublished-but-real target becomes an absolute
GitHub blob URL, and a target resolving to neither is a hard publish
error. docs_lint.py can't catch this class of bug since it only checks
links within docs/ itself, not the wiki-transform's own reinterpretation
downstream - the script now self-checks at publish time instead.

Also adds two docs/ pages that were listed in docs/README.md's index
but missing from the mapping entirely (found via the new validation):
documentation-process.md and upstream-wiki-drift.md.

* Docs staleness pass: Part 3 write-pass status + proposal B/C/E-1/E-2 markers

catalog-completion-plan.md: Part 3 heading and Status section updated to
reflect the completed write pass (run_id 20260718T145157-a12b1387, 13,275
votes, all hard bounds passed, 0/7,124 zero-resolution violations at full
population), pointing to docs/reports/2026-07-18-part3-write-pass-complete.md.
Filled the dangling "see the follow-up entry below" reference and replaced
the now-stale "HOLD #P3 stands" language. Part 4's heading now notes it's
confirmed unstarted with HOLD #B prep queued.

proposal-b-bleed-normalization.md: top summary no longer lists the
prior-resolution batch fetch as remaining work - it shipped as PR-1 (#72).

proposal-g-user-accounts-saved-decks.md: noted the build-order queue
(E-1 #61, E-2 #62, Level-2 grid fix #63, audit pass #64, GIS error UX #65,
Proposal B #66/#72, Proposal C part (a) #67) has fully cleared.

docs/README.md: added proposal-b and proposal-c to the "Plans & proposals"
table - both have dedicated docs but were missing from the index entirely,
despite the section's own stated policy that any proposal with a dedicated
doc gets a row.

printing-tags.md and vote-system.md checked per the mini-pass's minimum
list; no changes needed - printing-tags.md already defers Stage 8+ status
to catalog-completion-plan.md, and vote-system.md has no AI-terminology or
merged-PR staleness.

* Report relay: wiki review findings (publish-script fix, Part 3 status, staleness mini-pass)

* CI: black-format publish_wiki.py, prettier-format cache-transition-resilience.md

black reformatting for publish_wiki.py (never run locally - only
py_compile was checked, missing the repo's black rev 22.8.0 pinned in
.pre-commit-config.yaml). cache-transition-resilience.md's prettier
drift predates this PR (introduced by #75) and is unrelated to its
content - swept here since the "Formatting and static type checking"
check runs pre-commit against all files, not just the diff, and this
PR's own CI needs to go green.

---------

Co-authored-by: Claude <noreply@anthropic.com>
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.

2 participants