Skip to content

feat: store adapter — redux/zustand writers ↔ readers (C6)#16

Merged
officialCodeWork merged 1 commit into
developmentfrom
build/phase-2/step-2.4-store-adapter
Jul 14, 2026
Merged

feat: store adapter — redux/zustand writers ↔ readers (C6)#16
officialCodeWork merged 1 commit into
developmentfrom
build/phase-2/step-2.4-store-adapter

Conversation

@officialCodeWork

Copy link
Copy Markdown
Owner

Summary

TRACKER Step 2.4 — failure mode C6, temporal decoupling. The hardest attribution case after C1: a component reads useSelector(s => s.users.list) and contains no fetch at all — the API that populated the slice was dispatched at login, by a different component, on a different page.

UserDirectory ──reads-state──▶ slice "users" ◀──writes-state── GET /api/users
                                                    ▲
                          createAsyncThunk payload ─┘  (dispatched by LoginPage)

traceLineage(UserDirectory)/api/users. traceLineage(LoginPage)/api/users too (it initiates the fetch via dispatch(fetchUsers())).

What's detected

  • Redux ToolkitcreateSlice (one global StateNode per slice), createAsyncThunk (payload creator mined with the full 1.x endpoint machinery), extraReducers addCase(thunk.fulfilled) association → writes-state edges
  • Zustandcreate() stores named use*; internal fetches become writers; hook calls become readers
  • ReadersuseSelector slice-path parsing links to the global slice node (old per-component fallback kept); dispatch(thunk()) links the dispatcher
  • CoretraceLineage follows writes-state edges backward through state nodes

Eval

  • New fixture c6-store-decoupled with cross-store poison assertions (redux slice's API must never bleed onto the zustand reader, and vice versa)
  • Scorecard: 129 pass / 0 fail / 0 xfail · precision 1.000 · recall 1.000 · match accuracy 1.000
  • 85 unit tests (5 new)

Also fixed in passing: a declaration-order bug (detectStores referenced addEdge before initialization).

Documentation

  • TRACKER.md — 2.4 done, Status → 2.5 (portals — the last step before Gate 2)

🤖 Generated with Claude Code

Step 2.4 (TRACKER). Failure mode C6, temporal decoupling:

- detectStores: createSlice → one global StateNode per slice (stateKind
  redux); createAsyncThunk payload creators mined for data sources;
  extraReducers addCase(thunk.fulfilled/rejected/pending) associates thunk
  sources to their slice via data-source --writes-state--> slice edges.
  Zustand: create() stores named use* become StateNodes with their internal
  fetches wired as writers.
- Readers: useSelector((s) => s.users.list) parses the slice path and links
  reads-state to the GLOBAL slice node (falling back to the old per-component
  node when no slice matches); zustand hook calls link to their store node;
  dispatch(fetchUsers()) gives the dispatching component a fetches-from edge.
- traceLineage: visiting a StateNode now follows incoming writes-state edges
  back to the populating data sources — reader → slice → login-time API.
- Fixture c6-store-decoupled: UserDirectory (NO fetch of its own) →
  /api/users; CartWidget → /api/cart; cross-store poison assertions.
- Fixed a declaration-order bug (detectStores called before addEdge existed).
- Scorecard: 129 pass / 0 fail; precision/recall 1.000. 85 unit tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@officialCodeWork
officialCodeWork merged commit acbf574 into development Jul 14, 2026
1 check passed
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