Skip to content

explorer: collapse dual mode state to single source of truth (#208 step 1)#213

Open
rdhyee wants to merge 1 commit into
isamplesorg:mainfrom
rdhyee:refactor/208-collapse-dual-mode-state
Open

explorer: collapse dual mode state to single source of truth (#208 step 1)#213
rdhyee wants to merge 1 commit into
isamplesorg:mainfrom
rdhyee:refactor/208-collapse-dual-mode-state

Conversation

@rdhyee
Copy link
Copy Markdown
Contributor

@rdhyee rdhyee commented May 12, 2026

Step 1 of the #208 state-management refactor. Foundational.

Problem

The zoomWatcher OJS cell carried explorer mode in two places:

  • closure-private `let mode = 'cluster';`
  • public `viewer._globeState.mode`

Both were updated together (`mode = 'point'; viewer._globeState.mode = 'point';`), but the duplication caused transient sync mismatches during boot. The URL round-trip harness captures this: `waitReadyAndPointMode` succeeds against `_globeState.mode` then a snapshot moments later sees a brief cluster state — the harness's iter 1 "mode A=cluster B=point" anomaly.

Codex's retrospective on PR #203 (item 7) recommended making `_globeState.mode` the single source of truth and wrapping transitions in one setter.

What this changes

  • `getMode()` getter reads from `viewer._globeState.mode`. All 10 former bare-`mode` reads in the zoomWatcher cell go through it: `tryEnterPointModeIfNeeded`, source-filter handler, facet-filter handler, camera-changed handler (`targetMode` fallback + two mode transitions + stats guard), hashchange post-flight reconciliation (both branches).
  • `setExplorerMode(next)` setter is the only writer. Used by `enterPointMode` and `exitPointMode`, which previously did the redundant `mode = X; viewer._globeState.mode = X;` pair.
  • Closure `let mode = 'cluster';` removed.

20 insertions, 16 deletions. No behavioral changes intended — just collapses the duplication.

Verification

  • `tests/playwright/url_roundtrip_investigation.js` — 5/6 iterations green on localhost.
  • The iter 1 anomaly ("mode A=cluster B=point" on no-op flight) is the same pre-existing boot-transition timing Codex flagged in the original retrospective; it's neither caused nor worsened by this refactor. Centralized camera reconciliation in step 2 will fix it.

Risks

  • Refactor that touches every mode-check site. Each call site verified individually; grep confirms no residual bare-`mode` reads outside the unrelated `setView` function's parameter, comments, and string literals.
  • `getMode()` adds a function-call indirection on every mode check. Negligible cost; readability win.
  • Boot mode anomaly NOT fixed by this PR. It's foundational for step 2 (route boot + camera + hashchange through one `reconcileCameraState` path), which IS the fix.

Sequencing reminder

Per Codex's recommended sequence (in #208):

  1. ✅ Collapse `mode` to single source — THIS PR
  2. Introduce `writeGlobeHash` + `setExplorerMode` and migrate the writers
  3. Route both `camera.changed` and `camera.moveEnd` through one `reconcileCameraState(reason)` settled-camera path

Each step gets its own PR with Codex review.

Test plan

  • url_roundtrip_investigation.js 5/6 green (iter 1 pre-existing anomaly)
  • Manual smoke on deploy preview: cluster→point→cluster transitions, source filter toggle, hashchange back/forward
  • Codex review

🤖 Generated with Claude Code

…sorg#208 step 1)

Step 1 of the isamplesorg#208 state-management refactor. The closure-private
\`mode\` variable in the zoomWatcher OJS cell duplicated
\`viewer._globeState.mode\`; both were updated together but the
duplication caused transient sync mismatches during boot (the URL
round-trip harness's iter 1 captures this — \`waitReadyAndPointMode\`
succeeds against \`_globeState.mode\` then the snapshot sees a brief
cluster state shortly after).

Codex's retrospective on PR isamplesorg#203 recommended making \`_globeState.mode\`
the single source of truth and wrapping mode transitions in one setter.
This change does exactly that:

- **\`getMode()\` getter** reads from \`viewer._globeState.mode\`. All
  former \`mode === ...\` reads in the zoomWatcher cell (10 sites:
  tryEnterPointModeIfNeeded, source-filter handler, facet-filter
  handler, camera-changed handler targetMode + transitions + stats
  guard, hashchange post-flight reconciliation) now go through it.
- **\`setExplorerMode(next)\` setter** is the only writer. Used by
  \`enterPointMode\` and \`exitPointMode\`, which previously did
  the redundant \`mode = X; viewer._globeState.mode = X;\` pair.
- **Closure \`let mode = 'cluster';\` removed.**

This is foundation work for step 2 (introduce \`writeGlobeHash\` /
\`reconcileCameraState\` and centralize the URL writers — Codex smell 1).

Verification: \`url_roundtrip_investigation.js\` — 5/6 iterations
green. The iter 1 anomaly ("mode A=cluster B=point" on no-op flight)
is the same pre-existing boot-transition timing that Codex flagged
in the original retrospective; it's neither caused nor worsened by
this refactor and will be addressed when the camera reconciliation
path is centralized in step 2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
rdhyee added a commit to rdhyee/isamplesorg.github.io that referenced this pull request May 12, 2026
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