Skip to content

Data-Flow Foundation F1+F2 — PR2: PWA repository + composition migration#131

Merged
jukka-matti merged 10 commits into
mainfrom
data-flow-foundation-pr2
May 6, 2026
Merged

Data-Flow Foundation F1+F2 — PR2: PWA repository + composition migration#131
jukka-matti merged 10 commits into
mainfrom
data-flow-foundation-pr2

Conversation

@jukka-matti
Copy link
Copy Markdown
Owner

Summary

Phase 2 of the Data-Flow Foundation F1+F2 plan (docs/superpowers/plans/2026-05-06-data-flow-foundation-f1-f2.md, §"Phase P3" + §"Phase P4"). Builds on PR1 (#130, types + interfaces).

This PR moves PWA hub-blob persistence behind pwaHubRepository, an implementation of @variscout/core/persistence#HubRepository. UI/composition-layer call sites (SaveToBrowserButton, App.tsx) now dispatch HUB_PERSIST_SNAPSHOT and other HubActions instead of reaching for hubRepository.saveHub/loadHub directly. canvasStore gains a parallel dispatch(action: CanvasAction) entry per audit R15. F3 (PR7+) will normalize the PWA blob into per-table writes; PR2 deliberately delegates to existing storage per locked decision D-P2.

  • Phase P3 (apps/pwa/src/persistence/): PwaHubRepository class implementing HubRepository; Immer-based applyAction with exhaustive kind switch + assertNever default; cascade walkers via transitiveCascade; module-scoped singleton at the composition root.
  • Phase P4 (UI/app composition + canvasStore + docs): SaveToBrowserButton dispatches HUB_PERSIST_SNAPSHOT; App.tsx loads via pwaHubRepository.hubs.list(); canvasStore.dispatch delegates to existing per-action methods (transitional wrapper, removed in PR3 cleanup); packages/stores/CLAUDE.md and apps/pwa/CLAUDE.md document the dispatch boundary.

Per audit revisions: R10 scoped P4 to UI/app files (domain stores never had Dexie calls); R15 kept canvasStore method-per-action API in PR2; R14 documented addHubComment network IO as a deliberate exception.

Stats

  • 10 commits on data-flow-foundation-pr2
  • @variscout/pwa: 189 → 298 tests (+109)
  • @variscout/stores: 261 → 272 tests (+11 from canvasStore.dispatch)
  • All packages build clean; bash scripts/pr-ready-check.sh green

Reviews completed

  • P3.5 (P3 spec + code-quality): PASS, 0 nits
  • P4.8 (P4 spec + code-quality): PASS, 1 nit — fixed at 47870af1
  • P4.9 final-branch review: APPROVED for merge with 1 suggestion — fixed at f074d110

Test plan

  • CI green
  • Manual claude --chrome walk (P4.7, deferred from controller session — chrome_* tools unavailable):
    • Open existing showcase data; verify rendering unchanged
    • Paste new CSV via Mode A.1 (Stage 1+2+3) + click "Save to this browser"; reload tab; verify hub restored
    • Verify SaveToBrowserButton round-trip with HUB_PERSIST_SNAPSHOT dispatch path

Out of scope (carried forward)

  • F3 (PR7+) PWA Dexie schema cutover; per-table writes; investigation/finding persistence
  • F2 PR3 (AzureHubRepository + ESLint guard + final Opus review)
  • 6 watchlist items in plan §"Open watchlist items" — all intentionally unchanged in PR2

🤖 Generated with Claude Code

jukka-matti and others added 10 commits May 6, 2026 15:31
Implements the PwaHubRepository class skeleton that satisfies the full
HubRepository interface from @variscout/core/persistence.  The write path
(dispatch) is fully wired (load → applyAction → save; throws on no-active-hub);
applyAction is stubbed in a separate expansion-seam file (P3.2 fills it in).
Grouped read APIs serve data from the single hub blob: hubs, outcomes, and
canvasState are real; the remaining 7 entity groups return empty stubs with
PWA-blob-only comments (F3 normalizes).  Module-scoped singleton export matches
the existing hubRepository pattern; composition-root decision deferred to P4.6.
33 new smoke tests; 222 passing (up from 189).  Build + typecheck clean.

Co-Authored-By: ruflo <ruv@ruv.net>
Implements applyAction(hub, action) as an exhaustive Immer switch over
all HubAction kinds. Hub-resident actions (HUB_UPDATE_GOAL, HUB_UPDATE_
PRIMARY_SCOPE_DIMENSIONS, OUTCOME_ADD/UPDATE/ARCHIVE) produce real draft
mutations; non-hub-resident actions (INVESTIGATION_*, FINDING_*, QUESTION_*,
CAUSAL_LINK_*, SUSPECTED_CAUSE_*, EVIDENCE_*, EVIDENCE_SOURCE_*) are
documented no-ops per the PWA blob constraint (F3 normalizes). Canvas
actions (PLACE_CHIP_ON_STEP etc.) are no-ops because canvasStore is the
canonical mutation surface; hub's canonicalProcessMap is overwritten via
HUB_PERSIST_SNAPSHOT when the user saves.

Exhaustiveness: TypeScript assertNever() default case verified — removing
any case from the switch causes a compile error. Adds immer ^11.1.4 as
explicit dep. Adds 46 new tests (268 total, 222 baseline); fake timers
pin Date.now() for deterministic archive timestamps.

Co-Authored-By: ruflo <ruv@ruv.net>
Replace stub helpers with real cascadeArchiveDescendantsInDraft that walks
transitiveCascade(parentKind) and soft-marks descendants on the draft hub.
On PWA blob, only hub→outcome yields observable mutations; all other parent
kinds are structurally correct but no-op until F3 normalizes. Added 19 tests
covering the hub→outcome cascade, idempotency, parentId mismatch, and all
non-hub parent kinds as regression-proof no-ops.

Co-Authored-By: ruflo <ruv@ruv.net>
Add 8 integration tests in a new `describe('dispatch end-to-end')` block that
prove the loadHub → applyAction → saveHub wiring is intact for each action
category: HUB_PERSIST_SNAPSHOT, HUB_UPDATE_GOAL, OUTCOME_ADD, OUTCOME_UPDATE,
OUTCOME_ARCHIVE (happy + idempotency with fake-timer advancement),
INVESTIGATION_ARCHIVE (documented no-op for PWA blob), and PLACE_CHIP_ON_STEP
(documented no-op per R15 canvas strategy).

Co-Authored-By: ruflo <ruv@ruv.net>
Replace direct hubRepository.saveHub calls in SaveToBrowserButton with
pwaHubRepository.dispatch({ kind: 'HUB_PERSIST_SNAPSHOT', hub }), so all
hub writes go through the single dispatch path.

Also fixes PwaHubRepository.dispatch to short-circuit on HUB_PERSIST_SNAPSHOT
— the bootstrap case where no hub exists yet (first "Save to this browser"
click) no longer throws. applyAction remains complete over HubAction for purity.

Co-Authored-By: ruflo <ruv@ruv.net>
Replace hubRepository.loadHub() with pwaHubRepository.hubs.list() in the
Mode A.1 bootstrap effect. hubRepository.getOptInFlag() is kept as a direct
call — no HubAction equivalent exists until F3 adds HubMetaAction.

Co-Authored-By: ruflo <ruv@ruv.net>
Add dispatch(action: CanvasAction) entry point to canvasStore that
switches on action.kind and delegates to the existing per-action methods.
assertNever default enforces exhaustiveness at compile time. Per-action
methods kept as transitional wrappers (PR3 removes them). Covers all 10
CanvasAction kinds; createStepFromChip and hydrateCanvasDocument remain
method-only (not in CanvasAction union). Adds 11 tests (272 total).

Co-Authored-By: ruflo <ruv@ruv.net>
Per F1+F2 plan §P4.6:
- packages/stores/CLAUDE.md: dispatch boundary lives at app/UI composition
  root via pwaHubRepository (PWA) / azureHubRepository (Azure, PR3 pending);
  audit R14 (addHubComment network IO) + R15 (canvasStore.dispatch) exceptions
  documented; wallLayoutStore whitelist note (R12).
- apps/pwa/CLAUDE.md: pwaHubRepository module-scoped singleton at
  apps/pwa/src/persistence/; HUB_PERSIST_SNAPSHOT is the bootstrap path;
  getOptInFlag/setOptInFlag remain the documented direct-access exception.
P4.8 review nit — replaces "P4.6 will lock this decision" with the
shipped state pointer to apps/pwa/CLAUDE.md.
P4.9 final reviewer flagged that "(PR3 ESLint rule)" reads as live
enforcement. Restated as "(F2 PR3 will enforce with an ESLint rule)".
@vercel
Copy link
Copy Markdown

vercel Bot commented May 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
mean-beoynd-lite-pwa Ready Ready Preview, Comment May 6, 2026 1:49pm
variscout_website Ready Ready Preview, Comment May 6, 2026 1:49pm

@jukka-matti jukka-matti merged commit 7fc1a36 into main May 6, 2026
3 checks passed
@jukka-matti jukka-matti deleted the data-flow-foundation-pr2 branch May 6, 2026 13:56
jukka-matti added a commit that referenced this pull request May 6, 2026
…otocol for PR3

PR2 (PWA repository + composition migration) merged at 7fc1a36.
Plan updated with Session 2 status block + revised Session 3 resume
protocol (Phase P5-P8 sequence, fresh worktree at
.worktrees/data-flow-foundation-pr3/, Sonnet workhorse + Opus for
P8.1 final-branch review). Original Session 1 status preserved as
PR1 history.

Co-Authored-By: ruflo <ruv@ruv.net>
jukka-matti added a commit that referenced this pull request May 6, 2026
PR3 (Azure repository + ESLint guard) merged at 2490fc8, completing
F1+F2 across all three squash commits on origin/main:
- d2822ea PR #130 (F1 types)
- 7fc1a36 PR #131 (F2 PWA)
- 2490fc8 PR #132 (F2 Azure + ESLint guard)

Plan updated with F1+F2 SHIPPED block + Session 3 closeout (P5-P8
delivered, plan-reality deltas, F-series forward, watchlist).
Original Session 2 closeout preserved as PR2 history.

Spec already flipped status: active → delivered as part of PR3 P8.2.

Co-Authored-By: ruflo <ruv@ruv.net>
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