Skip to content

chore(deps): bump pnpm/action-setup from 4.1.0 to 4.4.0#1

Closed
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/github_actions/pnpm/action-setup-4.4.0
Closed

chore(deps): bump pnpm/action-setup from 4.1.0 to 4.4.0#1
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/github_actions/pnpm/action-setup-4.4.0

Conversation

@dependabot
Copy link
Copy Markdown
Contributor

@dependabot dependabot Bot commented on behalf of github Mar 14, 2026

Bumps pnpm/action-setup from 4.1.0 to 4.4.0.

Release notes

Sourced from pnpm/action-setup's releases.

v4.4.0

Updated the action to use Node.js 24.

v4.3.0

What's Changed

New Contributors

Full Changelog: pnpm/action-setup@v4.2.0...v4.3.0

v4.2.0

When there's a .npmrc file at the root of the repository, pnpm will be fetched from the registry that is specified in that .npmrc file #179

Commits

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

@dependabot dependabot Bot added dependencies Pull requests that update a dependency file github_actions Pull requests that update GitHub Actions code labels Mar 14, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 14, 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 Mar 14, 2026 7:22am
variscout_website Ready Ready Preview, Comment Mar 14, 2026 7:22am

Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 4.1.0 to 4.4.0.
- [Release notes](https://github.com/pnpm/action-setup/releases)
- [Commits](pnpm/action-setup@a7487c7...fc06bc1)

---
updated-dependencies:
- dependency-name: pnpm/action-setup
  dependency-version: 4.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
@jukka-matti
Copy link
Copy Markdown
Owner

Applied this update directly on main in a consolidated dependency bump commit (all 13 Dependabot updates at once). Closing this PR.

@dependabot @github
Copy link
Copy Markdown
Contributor Author

dependabot Bot commented on behalf of github Mar 20, 2026

OK, I won't notify you again about this release, but will get in touch when a new version is available. If you'd rather skip all updates until the next major or minor version, let me know by commenting @dependabot ignore this major version or @dependabot ignore this minor version. You can also ignore all major, minor, or patch releases for a dependency by adding an ignore condition with the desired update_types to your config file.

If you change your mind, just re-open this PR and I'll resolve any conflicts on it.

@dependabot dependabot Bot deleted the dependabot/github_actions/pnpm/action-setup-4.4.0 branch March 20, 2026 12:55
jukka-matti added a commit that referenced this pull request Apr 27, 2026
F-6 closes the Phase 6 acceptance gate. All 8 acceptance criteria covered:

- AC #1 visually confirmed in chrome — flipping investigation status to
  `resolved` renders the "Set up sustainment cadence" prompt in the editor
  (Coffee Moisture sample, vite at :5173).
- AC #1-#5 functionally covered by 78 component/page/service tests
  (SustainmentEditors, ProcessHubSustainmentRegion, ProcessHubFormat.sustainment,
  Dashboard.processHub, Editor.sustainment, blobClient).
- AC #5 also covered by 39 core sustainment tests (selectSustainmentBuckets +
  path constructors).
- AC #6 vacuously satisfied — no sustainment fields wired into AI context
  builders yet (consistent with spec open-question #4 deferring CoScout
  proactivity). No PII surface.
- AC #7 covered by 14 schema.v6 + sustainmentStorage migration tests.
- AC #8 confirmed: process-hubs/{hubId}/sustainment/... path constructors at
  packages/core/src/sustainment.ts:307-343, consumed by
  apps/azure/src/services/blobClient.ts:444-470.

Total deterministic verification: 153 tests across @variscout/core,
@variscout/azure-app components/pages/services. All green.

Index reconciliation in docs/superpowers/specs/index.md is intentionally
deferred — the file already carries unstaged operating-model workstream
edits that the user wants kept on a separate branch.

Deferred to a follow-up interactive walk (file as F-8 if regressions
surface): full Flow A configuration, Flow B-D execution (verdict logging,
drifting+escalate, handoff record), bucket-split visual rendering colors,
mobile/dark/fi locale spot checks, duplicate-render quirk visual
confirmation. Functional contract for all of these is covered by the 78
Phase 6 component/page tests.

Co-Authored-By: ruflo <ruv@ruv.net>
jukka-matti added a commit that referenced this pull request Apr 27, 2026
Both PWA and Azure FrameView shipped byte-identical 10-line
createEmptyMap helpers. Move the factory to @variscout/core/frame so
ProcessMap construction has a single home as Phase 2 of the Layered
Process View grows. Pure refactor — zero behavior change. Adds 3
unit tests covering shape, timestamp, and instance independence.

Phase 2 PR #1 of N (per ~/.claude/plans/lets-create-a-plan-proud-island.md).

Co-authored-by: ruflo <ruv@ruv.net>
jukka-matti added a commit that referenced this pull request Apr 27, 2026
Combines three sequenced PRs into one spec per feedback_full_vision_spec
memory:
- PR #1 (H1): team notes on state items — question / gemba / data-gap /
  decision kinds, persisted in ProcessHubInvestigationMetadata.stateNotes
- PR #2 (V2 follow-up): full EvidenceSheet rendering — refactors
  ProcessHubEvidenceContract to split countFor (sync) + loadFindingsFor
  (async), eliminates the synthetic-Finding cast from PR #99
- PR #3 (H2 launch): General Evidence Source for CSV/Excel — adds
  GENERIC_TABULAR_PROFILE to DATA_PROFILE_REGISTRY, mapping confirmation
  UI in ProcessHubEvidencePanel, cadence metadata

Resolves both prior open questions inline:
- Evidence contract split (countFor + loadFindingsFor + onChipClick +
  onFindingSelect — cleaner than overloading findingsFor sync/async)
- Mapping UX explicit confirmation (matches operating-model § "analyst
  confirms mappings" language)

Updates spec index, marks the parent V2 actionable-panel spec as
Delivered (PRs #98 #99).

Co-Authored-By: ruflo <ruv@ruv.net>
jukka-matti added a commit that referenced this pull request Apr 27, 2026
Sequenced TDD breakdown for 3 PRs:
- PR #1: team notes on state items (~250-350 LOC, 8 tasks). New
  ProcessStateNote types in core, ProcessHubInvestigationMetadata
  extension, ProcessHubNotesContract on the panel, StateItemNotesDrawer
  in apps/azure, Dashboard wiring via useStorage round-trip.
- PR #2: full EvidenceSheet rendering (~300-400 LOC, 5 tasks). Refactors
  ProcessHubEvidenceContract to split countFor (sync) + loadFindingsFor
  (async); EvidenceSheet bottom sheet; Dashboard owns sheet state +
  async load via useStorage; eliminates synthetic-Finding cast from PR
  #99.
- PR #3: General Evidence Source for CSV/Excel (~600-800 LOC, 4 tasks).
  GENERIC_TABULAR_PROFILE in DATA_PROFILE_REGISTRY; profile picker UI;
  mapping confirmation form; cadence selector (5 actual EvidenceCadence
  values); telemetry on save.

Three implementation reality notes flagged at top:
1. Actual DataProfileDefinition signature differs from spec sketch
   (single 'label' field; detect takes only rows; confidence is
   'high'/'medium'/'low' enum not number).
2. EvidenceCadence enum lacks 'monthly' — use existing 5 values.
3. Spec back-link upgrades to Markdown link in PR #1 Task 7.

Co-Authored-By: ruflo <ruv@ruv.net>
jukka-matti added a commit that referenced this pull request Apr 28, 2026
Aligns with the project-wide deterministic-ID preference (avoids
collision concerns from Math.random + millisecond timestamps).
crypto.randomUUID is available in all modern browsers.

Phase 3 PR #1, code-review followup.

Co-Authored-By: ruflo <ruv@ruv.net>
jukka-matti added a commit that referenced this pull request Apr 28, 2026
* feat(core): add ProcessStateNote types + isProcessStateNoteKind guard

Pure types for team notes attached to ProcessStateItem. 4 kinds in
stable order (question / gemba / data-gap / decision); type guard at
storage boundary protects against hand-edited or stale JSON.

Phase 3 PR #1, Task 2.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(core): add stateNotes field to ProcessHubInvestigationMetadata

Notes round-trip through the existing per-investigation Blob-Storage
cycle. No new storage paths.

Phase 3 PR #1, Task 3.

Co-Authored-By: ruflo <ruv@ruv.net>

* refactor(ui): require notes contract on ProcessHubCurrentStatePanel

Adds ProcessHubNotesContract as the 4th required prop. Each StateItemCard
gains a [+ note] signaling affordance (consumer opens its own drawer in
response) and renders the notes list with edit/delete on own notes.
Existing 20 tests rewired to pass notes={makeNotes()}; 5 new note-behavior
tests added.

The actual drawer (kind selector + textarea) is a separate component
in apps/azure (next task) — the panel exposes onRequestAddNote /
onRequestEditNote callbacks that the consumer wires to drawer state.

Phase 3 PR #1, Task 4.

Co-Authored-By: ruflo <ruv@ruv.net>

* fix(ui): un-nest notes buttons from interactive StateItemCard wrapper

Per @variscout/ui CLAUDE.md hard rule ('never nest button inside
button'): the [+ note] / Edit / Delete buttons were rendered inside
StateItemCard's role='button' wrapper. Restructured the card into a
visual outer wrapper containing two sibling regions:
- interactive region (card content + pill + chip) holds role=button +
  keyboard handlers when the action is supported
- non-interactive notes region (affordance + list) sits as a sibling

Also adds aria-label='Add note for {item.label}' to the [+ note] button
for screen-reader context.

stopPropagation calls on notes buttons removed — no longer needed since
buttons are not descendants of the interactive wrapper.

Phase 3 PR #1, code-review followup.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(azure): add StateItemNotesDrawer for adding/editing team notes

Inline drawer with 4 kind buttons + textarea + save/cancel. Save
disabled when text is empty/whitespace. onSave fires with current kind
and trimmed text; onCancel fires no-op. State resets when drawer is
reopened with new initial values.

Phase 3 PR #1, Task 5.

Co-Authored-By: ruflo <ruv@ruv.net>

* fix(azure): a11y + light-mode contrast on StateItemNotesDrawer kind buttons

- Add aria-pressed={isActive} to each kind button so screen readers
  announce the toggle state.
- Pair text-blue-400 with text-blue-700 (light-mode contrast) per the
  text-green-400 light contrast feedback rule.

Phase 3 PR #1, code-review followup.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(azure): wire team notes through ProcessHubReviewPanel + Dashboard

ProcessHubReviewPanel takes 4 new required props (onRequestAddNote,
onRequestEditNote, onDeleteNote, currentUserId) and builds notesFor()
from rollup.investigations[*].metadata.stateNotes (filtered by item.id,
sorted by createdAt asc).

To close the storage round-trip, ProcessContext gains stateNotes[] and
ProjectMetadata gains stateNotes[], so the Dashboard rollup picks them
up from investigation.metadata without loading the full project blob.

Dashboard manages drawer state (add/edit modes), wires the
StateItemNotesDrawer, and persists notes via the existing
useStorage().loadProject/saveProject chain (stored on
project.processContext.stateNotes). After save, loadProjects()
refreshes the rollup so the new note appears immediately. loadProjects
converted to useCallback so it can be a stable dep.

Telemetry: process_hub.state_note_{added,edited,deleted} with non-PII
payloads (hubId, kind, severity, lens — no note text, author, or
investigation IDs).

Phase 3 PR #1, Task 6.

Co-Authored-By: ruflo <ruv@ruv.net>

* fix(azure): harden team-notes save with in-flight guard + error handling

Three code-review followups for the team-notes wiring:

1. In-flight guard (isSavingNote useState) prevents stale-write race
   on rapid double-click or concurrent edits. Drawer Save button
   disables while save is in flight (new disabled prop on
   StateItemNotesDrawer).

2. saveProject failures wrapped in try/catch — drawer stays open on
   error so user can retry. handleDeleteNote logs error and skips
   telemetry/refresh on failure.

3. hubProjects filter normalizes processHubId via existing
   normalizeProcessHubId helper, matching the established codebase
   pattern (visibleProjects already does this).

Phase 3 PR #1, code-review followup.

Co-Authored-By: ruflo <ruv@ruv.net>

* docs: upgrade Phase 3 spec→plan back-link to Markdown link

Plan file now exists in main; convert from plain-text path to real
Markdown link so the cross-ref check resolves.

Phase 3 PR #1, Task 7.

Co-Authored-By: ruflo <ruv@ruv.net>

* fix(azure): use crypto.randomUUID for note IDs instead of Math.random

Aligns with the project-wide deterministic-ID preference (avoids
collision concerns from Math.random + millisecond timestamps).
crypto.randomUUID is available in all modern browsers.

Phase 3 PR #1, code-review followup.

Co-Authored-By: ruflo <ruv@ruv.net>

---------

Co-authored-by: ruflo <ruv@ruv.net>
jukka-matti added a commit that referenced this pull request Apr 30, 2026
The original Task 8 spec invented a 5th module-level Zustand store in
@variscout/hooks keyed by investigationId. Three issues with that:

1. Wrong layer — CLAUDE.md invariant locks 4 domain Zustand stores; window
   selection is viewer state, not a 5th domain store.
2. Memory leak by design — module-level Map<id, window> has no eviction in
   a long-lived PWA session.
3. Decision #1 already located the window on ProcessHubInvestigationMetadata
   alongside nodeMappings (see commit f059c59). The original plan's parallel
   cache ignored that, same class of mismatch the Decision #1 revision
   already corrected once.

Revised: useTimelineWindow becomes a thin pure projection. Caller passes
the investigation envelope and an onChange callback wired to its existing
persistInvestigation flow (canonical pattern at apps/azure/src/features/
processHub/useHubMigrationState.ts:67-114). Window persists where the
investigation persists (IndexedDB / Blob per ADR-059). No new Zustand
store, no zustand dep added to @variscout/hooks, package flow stays clean.

Adds timelineWindow?: TimelineWindow to ProcessHubInvestigationMetadata
in core (the type-level half of Decision #1's intent that was previously
documented but not yet wired into the type).

Note for Tasks 11/14: app-level wiring now hangs onChange off
persistInvestigation alongside the nodeMappings flow.

Co-Authored-By: ruflo <ruv@ruv.net>
jukka-matti added a commit that referenced this pull request Apr 30, 2026
* docs: lock load-bearing decisions for multi-level SCOUT V1

* feat(core): TimelineWindow discriminated union + type guards

Adds TimelineWindow (fixed | rolling | openEnded | cumulative) type,
TimelineWindowKind utility type, and four isXxxWindow type guards.
Barrel exported from ./timeline and re-exported from core root.
TDD: test written first, confirmed failing, then passing after impl.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(core): applyWindow filters rows by TimelineWindow over a timeColumn

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(core): detectScope classifies investigations as b0/b1/b2 by nodeMappings cardinality

Implements scope detection per §2 of the investigation-scope-and-drill-semantics
spec. Accepts ProcessHubInvestigation (the actual type carrying nodeMappings via
metadata), not a nonexistent Investigation envelope. 4 tests: absent/empty → b0,
length===1 → b2, length>1 → b1.

Co-Authored-By: ruflo <ruv@ruv.net>

* docs: revise Decision #1 to reflect actual ProcessHubInvestigation type

The original Decision #1 referenced a fictitious Investigation envelope with processContext.nodeMappings. The actual codebase models hub-attached investigations as ProcessHubInvestigation with metadata.nodeMappings. Updating to put TimelineWindow on ProcessHubInvestigationMetadata alongside nodeMappings — the intent (window separate from scope, co-located with mappings) is preserved.

Caught during V1 Task 3 (detectScope) implementation when the implementer needed to operate against the actual type.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(core): computeFindingWindowDrift + WindowContext on Finding

Implements Task 5 of multi-level SCOUT V1. Adds WindowContext type to
Finding (optional, backward-compatible) and computeFindingWindowDrift()
which compares stats-at-creation vs current-window stats using relative
change with a default 0.20 threshold. 4 tests pass. Interleaved doc
section added to statistics-reference.md.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(core): throughput module — computeOutputRate + computeBottleneck

Adds packages/core/src/throughput/ mirroring defect/yamazumi shape.
computeOutputRate buckets rows by time granularity and emits ratePerHour;
computeBottleneck ranks steps by rate and flags the slowest. 3/3 tests
pass. Re-exported from core barrel; statistics-reference Part 17 added.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(core): AnalysisModeStrategy gains dataRouter for (scope, phase, window) routing

Adds RouterScope, RouterPhase, RouterArgs, RouterResult, RouterHook and ChartSlots
types to analysisStrategy.ts. Extends AnalysisModeStrategy with an optional
dataRouter field (backward compat). All 5 registered strategies implement it with
locked routing decisions: investigation→useFilteredData, hub→useProductionLineGlanceData
(where supported). Process Flow mode has no strategy registration in V1.

Co-Authored-By: ruflo <ruv@ruv.net>

* docs: revise Task 8 to pure-projection over investigation metadata

The original Task 8 spec invented a 5th module-level Zustand store in
@variscout/hooks keyed by investigationId. Three issues with that:

1. Wrong layer — CLAUDE.md invariant locks 4 domain Zustand stores; window
   selection is viewer state, not a 5th domain store.
2. Memory leak by design — module-level Map<id, window> has no eviction in
   a long-lived PWA session.
3. Decision #1 already located the window on ProcessHubInvestigationMetadata
   alongside nodeMappings (see commit f059c59). The original plan's parallel
   cache ignored that, same class of mismatch the Decision #1 revision
   already corrected once.

Revised: useTimelineWindow becomes a thin pure projection. Caller passes
the investigation envelope and an onChange callback wired to its existing
persistInvestigation flow (canonical pattern at apps/azure/src/features/
processHub/useHubMigrationState.ts:67-114). Window persists where the
investigation persists (IndexedDB / Blob per ADR-059). No new Zustand
store, no zustand dep added to @variscout/hooks, package flow stays clean.

Adds timelineWindow?: TimelineWindow to ProcessHubInvestigationMetadata
in core (the type-level half of Decision #1's intent that was previously
documented but not yet wired into the type).

Note for Tasks 11/14: app-level wiring now hangs onChange off
persistInvestigation alongside the nodeMappings flow.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(hooks): useTimelineWindow — pure projection over investigation metadata

Co-Authored-By: ruflo <ruv@ruv.net>

* docs(hooks): JSDoc note re stable reference for investigation arg

Per code-review nit on 4adae8b: the returned `window` memoizes on
`metadata.timelineWindow` identity, so a parent passing a fresh object
literal each render would thrash the memo. Future Task 9 callers
(useDataRouter) read `window` from this hook, so calling out the
contract on the arg itself prevents the footgun. JSDoc-only; no
behavior change.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(hooks): useDataRouter + window-aware useFilteredData / useProductionLineGlanceData

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(ui): TimelineWindowPicker — four-kind window selector with secondary inputs

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(ui): DashboardLayoutBase exposes TimelineWindowPicker in dashboard chrome

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(ui): FindingCard renders window-context footer when present

Co-Authored-By: ruflo <ruv@ruv.net>

* refactor(azure): ProcessHubCapabilityTab consults useDataRouter, threads window

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(apps): wire timeline window into Dashboards + Hub Capability picker

Plan Task 14 V1 interpretation. Original plan used the old
useTimelineWindow({ investigationId, defaultKind }) signature that
Task 8's revision (commit cf5daaa) already replaced with a pure
projection over an investigation envelope. The investigation Dashboards
(Azure + PWA) don't receive a ProcessHubInvestigation envelope today —
they read from useProjectStore — so the persistence-aware hook can't
be used there in V1. Local useState is the correct V1 fit, with a
documented V2 follow-up to make the dashboards investigation-aware.

Hub Capability tab already had local window state from Task 13; this
commit extends the destructure to expose the setter and renders
TimelineWindowPicker above the dashboard composition. Cadence-default
override (rolling matched to hub.cadence on first mount) is deferred
to V1.5 per the plan revision.

Plan-file Task 14 body replaced with a V1-interpretation note.

Co-Authored-By: ruflo <ruv@ruv.net>

* chore: ADR-074 boundary check script + pre-commit wiring

Co-Authored-By: ruflo <ruv@ruv.net>

* docs: V1 documentation completeness sweep + lifecycle updates

Closes Task 16 (Steps 16.1–16.6, 16.8) of the Multi-level SCOUT V1 plan.

Created (3):
- docs/03-features/analysis/timeline-window-investigations.md
- docs/03-features/analysis/multi-level-dashboard.md
- docs/05-technical/architecture/timeline-window-architecture.md

Modified (16):
- Vision: methodology.md temporal-scope paragraph; eda-mental-model.md SCOUT-loops note
- Glossary: docs glossary + packages/core/src/glossary/terms.ts (timeline window, output rate, bottleneck, finding drift, hub-time, investigation-time)
- Journeys: USER-JOURNEYS.md + USER-JOURNEYS-CAPABILITY.md timeline-picker mentions
- Agent map: docs/llms.txt new entry points
- Per-package CLAUDE.md: core (timeline/throughput/findings), hooks (useTimelineWindow + useDataRouter), ui (TimelineWindowPicker), apps/azure + apps/pwa (multi-level integration)
- Lifecycle: spec status draft → delivered, last-reviewed 2026-04-30; decision-log V1 row + SCOUT journey row updated; ADR-074 strikes the 'to be added' note (boundary script ships in 6c68218)
- Plan-file checkboxes for Steps 16.1–16.6 + 16.8 ticked; 16.7/16.9 left for orchestrator

Step 16.7 (memory updates) handled separately by orchestrator. Step 16.9 (push + open PR) deferred to user-authorized step.

Co-Authored-By: ruflo <ruv@ruv.net>

* fix(core): replace .toFixed in defect formatMetricValue per hard rule

Pre-merge code review on PR #109 caught a hard-rule violation in the
defect strategy registration. packages/core/CLAUDE.md forbids .toFixed
on exported stat values; consumers should call formatStatistic from
@variscout/core/i18n. The line itself predates this PR (commit 69eca5a)
but the PR added dataRouter to the same strategy block, so folding the
fix in pre-merge per feedback_bundle_followups_pre_merge.md.

Replaces v.toFixed(1) with Math.round(v * 10) / 10 — same numeric output,
no toFixed. Mirrors the yamazumi strategy's Math.round(v * 100) pattern.

Co-Authored-By: ruflo <ruv@ruv.net>

---------

Co-authored-by: ruflo <ruv@ruv.net>
jukka-matti added a commit that referenced this pull request May 6, 2026
* data-flow F1+F2 P5.1: AzureHubRepository skeleton + composition root

Adds AzureHubRepository implementing HubRepository from @variscout/core/persistence.
Dispatch short-circuits HUB_PERSIST_SNAPSHOT to saveProcessHubToIndexedDB (bootstrap);
all other action kinds throw explicit P5.2/P5.3 not-yet-implemented error. Read APIs
wire against existing Azure Dexie tables (processHubs, evidenceSources, evidenceSnapshots,
evidenceSourceCursors); F3-pending entities (investigations, findings, questions,
causalLinks, suspectedCauses) are stubbed. Module-scoped azureHubRepository singleton
mirrors PWA composition-root pattern. apps/azure/CLAUDE.md updated with
persistence-boundary invariant documenting R12+R13 exceptions.

Co-Authored-By: ruflo <ruv@ruv.net>

* data-flow F1+F2 P5.1 review fixes: hubs.list tombstone filter + multi-hub outcomes test + dispatch error test

- hubs.list() now filters h.deletedAt === null; hubs.get stays unscoped (comment added)
- outcomes.get has O(n hubs) comment noting F3 normalization will remove the scan
- AzureHubRepository.read.test: hubs.list tombstone test + multi-hub outcomes.get test
- AzureHubRepository.test: HUB_PERSIST_SNAPSHOT error propagation test
- Fix 4: corrected misleading fake-indexeddb comment ("kept at line 1" → "must be the first import statement")

Co-Authored-By: ruflo <ruv@ruv.net>

* data-flow F1+F2 P5.2: Azure cascade helper with Dexie transaction

Adds cascadeArchiveDescendants(parentKind, parentId, archivedAt) in
apps/azure/src/persistence/cascadeArchive.ts. Uses transitiveCascade()
from @variscout/core/persistence; wraps all Dexie writes in a single
'rw' transaction covering evidenceSnapshots, evidenceSources,
evidenceSourceCursors, and processHubs. Per-kind stubs with F3 comments
for investigation/finding/question/causalLink/suspectedCause/rowProvenance/
canvasState (no Azure tables today). Outcome cascade explicitly deferred
to P5.3 per-action handler (hub blob mutation). Evidence source cursor
hub-range query uses compound PK [hubId+sourceId] between(). 10 new tests
covering: evidenceSource→cursor cascade, hub multi-table cascade,
investigation no-op, rollback, idempotency, empty-descendants. Barrel
updated to re-export the helper.

Co-Authored-By: ruflo <ruv@ruv.net>

* data-flow F1+F2 P5.2 review fixes: cascade JSDoc + rollback comment + fan-out test

- Fix 1: add ## Atomic call pattern (P5.3+) section to cascadeArchiveDescendants
  JSDoc prescribing the outer-transaction wrapping pattern P5.3 must use to
  keep parent-row update + descendant cascade atomic; explains Dexie 4 zone reuse.
- Fix 2: replace hedge comment in rollback test with confident pass-confirmation
  statement — rollback path verified green at P5.2 baseline (1098 tests).
- Fix 3: add cursor2 for src2 in the hub multi-table cascade test to cover the
  fan-out path (hub → both evidenceSource cursors via compound key-range scan).

Co-Authored-By: ruflo <ruv@ruv.net>

* data-flow F1+F2 P5.3: Azure per-action handlers + applyAction module

Implements all HubAction handlers in a new applyAction.ts module (Option B):
- Hub-blob mutations (HUB_UPDATE_GOAL, HUB_UPDATE_PRIMARY_SCOPE_DIMENSIONS,
  OUTCOME_ADD, OUTCOME_UPDATE, OUTCOME_ARCHIVE) — read-modify-write via
  saveProcessHubToIndexedDB.
- Direct Dexie table writes (EVIDENCE_ADD_SNAPSHOT, EVIDENCE_ARCHIVE_SNAPSHOT,
  EVIDENCE_SOURCE_ADD, EVIDENCE_SOURCE_UPDATE_CURSOR, EVIDENCE_SOURCE_REMOVE).
- EVIDENCE_SOURCE_REMOVE atomically wraps cascade + parent soft-delete in a
  single db.transaction; outer table list is a superset of cascadeArchiveDescendants'
  internal transaction to satisfy Dexie zone propagation.
- Session-only kinds (INVESTIGATION_*, FINDING_*, QUESTION_*, CAUSAL_LINK_*,
  SUSPECTED_CAUSE_*) — documented no-ops with F3 comment.
- Canvas kinds — documented no-ops with HUB_PERSIST_SNAPSHOT-flow comment.
- TypeScript exhaustiveness via assertNever() ensures compile-time completeness.

AzureHubRepository.dispatch now delegates to applyAction after the
HUB_PERSIST_SNAPSHOT short-circuit.

Adds 73 new tests (1098 → 1171). Adds sustainment-deferral note to
apps/azure/CLAUDE.md Invariants section.

Co-Authored-By: ruflo <ruv@ruv.net>

* data-flow F1+F2 P5.3 review fixes: parent-update rollback test + @internal barrel + idempotency notes

Co-Authored-By: ruflo <ruv@ruv.net>

* data-flow F1+F2 P6: Azure call-site migration to dispatch (storage facade + cursor sync)

Migration A: replace saveProcessHubToIndexedDB, saveEvidenceSourceToIndexedDB,
saveEvidenceSnapshotToIndexedDB in the three StorageProvider save methods with
azureHubRepository.dispatch(HUB_PERSIST_SNAPSHOT | EVIDENCE_SOURCE_ADD |
EVIDENCE_ADD_SNAPSHOT). Cloud-sync blocks unchanged. List/cache paths (lines 548,
595, 642) intentionally kept on direct localDb helpers (read-sync only).
Bootstrap save in listProcessHubs (line 548) left on direct call — it syncs cloud
→ local cache, not a hub-write dispatch path.

Migration B: replace db.evidenceSourceCursors.{get,put} in useEvidenceSourceSync
with azureHubRepository.evidenceSources.getCursor (Option B-1) and
dispatch(EVIDENCE_SOURCE_UPDATE_CURSOR). No direct Dexie usage remains in the file.

Tests: storage.test.ts — added db.processHubs mock for ensureDefaultProcessHubInIndexedDB;
added persistence mock for azureHubRepository.dispatch; added saveProcessHub /
saveEvidenceSource / saveEvidenceSnapshot tests asserting dispatch called with correct
action shapes. useEvidenceSourceSync.test.ts — replaced db.evidenceSourceCursors mock
with azureHubRepository mock; updated markSeen test to assert dispatch call shape.

Tests: 1173 → 1178 ✓ (5 new). Build: clean.

Co-Authored-By: ruflo <ruv@ruv.net>

* data-flow F1+F2 P6 review fix: tighten cloud-sync test mocks for evidence paths

Add hoisted mocks + vi.mock factory entries for all six evidence-blob
functions (listBlobEvidenceSnapshots, saveBlobEvidenceSnapshot,
updateBlobEvidenceSnapshots, listBlobEvidenceSources,
saveBlobEvidenceSource, updateBlobEvidenceSources). Previously the
saveEvidenceSnapshotToCloud path called undefined blob functions which
were silently swallowed, letting the cloud-sync assertion pass without
the blob calls ever firing. Tighten the existing snapshot online test
to assert exact dispatch shape (provenance: []) + blob function calls.
Add parallel online + cloud-sync test for saveEvidenceSource covering
dispatch shape and blob function calls. Tests: 1178→1179.

Co-Authored-By: ruflo <ruv@ruv.net>

* data-flow F1+F2 P7: ESLint dexie/db guard + dispatch-only docs

- P7.1: Update packages/stores, apps/pwa, apps/azure CLAUDE.md files to
  explicitly state the dispatch-only invariant (domain stores never import
  dexie directly; access via HubRepository / pwaHubRepository.dispatch /
  azureHubRepository.dispatch). Update "will add" references to past tense
  now that P7.2 delivers the rule. Enumerate R12+R13 exceptions clearly in
  each file.
- P7.2: Add ESLint no-restricted-imports rule in eslint.config.js blocking
  dexie package imports and **/db/schema glob imports outside the documented
  allow-list (persistence/, db/, wallLayoutStore.ts, azure services/storage
  + localDb + cloudSync + lib/persistence, test files). Smoke-tested: rule
  fires on ProcessHubEvidencePanel.tsx with clear actionable message; passes
  cleanly against current codebase (0 errors).
- P7.3: Confirmed pnpm lint is already a step in pr-ready-check.sh (no
  script change needed). pr-ready-check passes end-to-end.

Co-Authored-By: ruflo <ruv@ruv.net>

* data-flow F1+F2 P8.3 closeout: doc polish + decision-log + spec delivered

- Minor #1: add bootstrap cache-fill comment above storage.ts:548 for-loop
- Minor #2: R12 self-document comment on wallLayoutStore Dexie import
- Minor #3: fix plan frontmatter category (implementation-plan/audit → implementation)
- decision-log: F1+F2 closeout entry (what/why/deltas) + session backlog row
- spec: 2026-05-06-data-flow-foundation-design.md status active → delivered

Co-Authored-By: ruflo <ruv@ruv.net>

---------

Co-authored-by: ruflo <ruv@ruv.net>
jukka-matti added a commit that referenced this pull request May 6, 2026
* F3 P1-P4: PWA Dexie normalized schema cutover

- Renames IDB database from variscout-pwa to variscout-pwa-normalized
- Declares version(1) schema with 13 tables per spec D3
- Rewrites applyAction.ts as async transactional Dexie writes
- HUB_*/OUTCOME_*/HUB_PERSIST_SNAPSHOT get real bodies; rest stay no-op (F3.5/F5 wire)
- Decomposes canonicalProcessMap into canvasState table on HUB_PERSIST_SNAPSHOT
- PwaHubRepository.dispatch routes to transactional applyAction directly
- Joined hubs/outcomes/canvasState reads preserve in-memory ProcessHub shape
- Extracts opt-in helpers to apps/pwa/src/persistence/optIn.ts
- Best-effort Dexie.delete('variscout-pwa') cleanup of legacy DB on construction
- Deletes apps/pwa/src/db/hubRepository.ts (no remaining callers)

Tests deferred to P5 dispatch (existing tests intentionally stubbed
with it.todo placeholders so the build/typecheck stay green).

Refs: docs/superpowers/specs/2026-05-06-data-flow-foundation-design.md §3 D3, §5

Co-Authored-By: ruflo <ruv@ruv.net>

* F3 P1-P4 review fixes: stale-row cleanup + HUB_UPDATE invariant + cursor filter

Addresses code-quality review on commit 4823404:
- HUB_PERSIST_SNAPSHOT now deletes stale outcomes/canvasState rows that
  exist for the hub but are missing from the incoming snapshot, preserving
  the F2 blob-replacement invariant in the normalized world.
- HUB_UPDATE_GOAL / HUB_UPDATE_PRIMARY_SCOPE_DIMENSIONS now throw on
  missing hub, matching OUTCOME_ADD's loud-failure invariant.
- evidenceSources.getCursor now filters by hubId, aligning the PWA read
  contract with Azure's [hubId+sourceId] cursor semantics ahead of F3.5.
- joinHub queries wrapped in a read transaction to eliminate
  concurrent-write race in PwaHubRepository.hubs.{get,list}.

Co-Authored-By: ruflo <ruv@ruv.net>

* F3 P5: PWA persistence test rewrite for normalized schema

Replaces it.todo stubs from F3 P1-P4 with real test contracts against
the normalized 13-table schema + transactional dispatch:

- applyAction.test.ts (50): HUB_PERSIST_SNAPSHOT decompose + stale-row
  cleanup + atomicity rollback, HUB_UPDATE_GOAL/SCOPE_DIMENSIONS loud-fail
  on missing hub, OUTCOME_ADD/UPDATE/ARCHIVE lifecycle, exhaustive no-op
  coverage for EVIDENCE_*/INVESTIGATION_*/FINDING_*/QUESTION_*/CAUSAL_LINK_*/
  SUSPECTED_CAUSE_*/canvas action kinds.
- PwaHubRepository.test.ts (34): joined hub.get/list reads with
  outcomes + canvasState rebind, deletedAt filtering, read-transaction
  wrap on the three-table join, evidenceSources.getCursor hubId+sourceId
  composite filter (Issue 3 fix), legacy DB cleanup spy, stub read API
  smoke tests.
- SaveToBrowserButton.test.tsx (5): opt-in/forget round-trip via the
  optIn.ts module and pwaHubRepository.dispatch + .hubs.list path.
- modeA1.test.tsx (2): App-level opt-in restore effect — empty-flag lands
  on HomeScreen, opt-in + persisted hub renders GoalBanner.
- outcomePinMulti.test.tsx (4): framing toolbar renders one OutcomePin
  per outcome, OUTCOME_ADD increments rendered count, OUTCOME_ARCHIVE
  filter removes archived pins.

Test count: 174 -> 269 passing PWA tests (+95). All five F3 it.todo
stubs replaced with behavioral assertions; build + lint green.

Co-Authored-By: ruflo <ruv@ruv.net>

* F3 docs: decision-log closeout entry

Captures the three locked scope decisions from the F3 brainstorm
(rename DB, narrow action coverage, canvasState as separate table)
and flags the PWA<->Azure cursor schema parity drift as the F3.5
reconciliation point per Opus final-branch review's Important #1
recommendation.

Co-Authored-By: ruflo <ruv@ruv.net>

---------

Co-authored-by: ruflo <ruv@ruv.net>
jukka-matti added a commit that referenced this pull request May 8, 2026
8-task TDD-shaped plan executing the spec at
docs/superpowers/specs/2026-05-07-canvas-hypothesis-arrow-drawing-design.md.

Tasks (bottom-up):
1. useHypothesisDrawTool state machine + endpoint resolver (hooks)
2. activeCanvasTool field + auto-enable hypotheses overlay (hooks)
3. HypothesisDrawToolButton chrome component (ui)
4. HypothesisDraftPopover form (ui, hand-rolled positioning)
5. StepNodeMarker promoted-hypothesis pip (ui, Lucide Flag)
6. CanvasStepCard updates: replace badge, data-arrow-endpoint,
   gate click during draw
7. Canvas chrome integration: button + pointer handlers + rubber-band
   SVG + state-machine wiring through CanvasWorkspace
8. CanvasStepOverlay Remove button + investigations.md resolution
   + manual chrome walk steps

Plan-time deferrals locked: column resolution via card.metricColumn
(Risk #1); return-to-select after Save (Risk #2); hand-rolled
positioning, no new dep (Risk #5); Lucide Flag with status colors
(Risk #6).

Subagent-driven dispatch per master plan D6 + feedback_subagent_driven_default.
Sonnet for >=70% of dispatches; Opus for final review only.

Master plan section 4 8d entry updated with forward link to this plan.

Co-Authored-By: ruflo <ruv@ruv.net>
jukka-matti added a commit that referenced this pull request May 9, 2026
… V1 PRs

- Add PR-RPS-1 SHIPPED entry to §1
- Replace items #1, #3, #4 (Charter/Sustainment/Handoff piecewise) with the
  unified RPS V1 PR-RPS-2 through PR-RPS-10 sequence
- F5's HubAction work now subsumed by PR-RPS-9 + PR-RPS-10 (note added)
- Resolve §5 "F5 timing" question (moot post-RPS V1)
- Add 3 new heuristics to §6 (step-back-for-system-design, drop-methodology-
  bridges, code-review-must-checkout-PR-branch)
- Add RPS V1 spec + plan as related/references

Co-Authored-By: ruflo <ruv@ruv.net>
jukka-matti added a commit that referenced this pull request May 9, 2026
…148)

* feat(core): deriveMiniChartConfig for Wall Detective-pack mini-charts

Pure helper mapping Hypothesis.condition + column-type map + outcome →
{ kind: 'i-chart' | 'boxplot' | 'placeholder', factor, outcome }.

Consumed by useMiniChartData hook + HypothesisCard chart slot in
follow-up tasks (PR-RPS-3 Task 4–5).

Co-Authored-By: ruflo <ruv@ruv.net>

* refactor(core): tighten deriveMiniChartConfig nullability + test gaps

- outcome guard: !outcome → outcome == null (don't swallow empty string)
- new test: empty-children AND → placeholder reason 'no-factor'
- strengthen date-leaf test: full toEqual instead of kind-only
- drop call-site comment naming useMiniChartData / HypothesisCard

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(ui): MiniIChart — inline I-Chart for HypothesisCard chart slot

Display-only sparkline rendering numeric values as a line with a dashed
mean centerline. Theme-aware via useChartTheme (colors.mean line,
chrome.labelMuted centerline). No interaction; brushing comes in PR-RPS-4.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(ui): MiniBoxplot — inline outcome-by-factor boxplot for HypothesisCard

Display-only boxplot rendering one box per categorical group; falls back
to deterministic-jittered dots for groups under MIN_BOXPLOT_VALUES (7),
matching BoxplotBase convention. Theme-aware via useChartTheme.

Jitter PRNG seeded by category hash (mulberry32 + FNV-1a) — never
Math.random, per deterministic-stats rule (ADR-069).

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(ui,hooks): wire mini-charts into HypothesisCard chart slot

- New @variscout/hooks: useMiniChartData(hub, rows, columnTypes, outcome)
  memoizes deriveMiniChartConfig + projects rows into i-chart values[] or
  boxplot groups[] per category
- HypothesisCard CARD_H 228→288 to host an 80px chart slot at the top of
  the body (y=64 .. y=144). Inner <ChartSlot> component renders MiniIChart
  / MiniBoxplot / placeholder via <foreignObject>; full LOD only.
- Existing tag/readiness/clue rows shift down via parameterized constants
  (POST_CHART_Y); decorative "Suspected mechanism" caption removed.
- TAGGED_READINESS_Y gap fixed to 8px (was 6px) to pass layout-separation
  test; HypothesisCard.test.tsx updated to drop removed caption assertion.

Closes spec §6 D12 gap #1 — Wall Detective-pack mini-charts inside
hypothesis cards. Display-only; brushing comes in PR-RPS-4.

Co-Authored-By: ruflo <ruv@ruv.net>

* chore(ui): drop change-history comments per CLAUDE.md hygiene

Comments narrating old values / referencing task numbers belong in PR
descriptions, not source. Pure removals; no behaviour change.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(ui,apps): WallCanvas threads rows + columnTypes + outcomeColumn for mini-charts

WallCanvas now accepts rows / columnTypes / outcomeColumn props and forwards
them to each HypothesisCard, populating the chart slot introduced in the
prior commit. Both apps thread the data from useProjectStore: rawData,
detectColumns(rawData) → ColumnTypeMap, and outcome. Also fixes missing vi
import in MiniBoxplot.test.tsx (pre-existing test error from Task 3).

Co-Authored-By: ruflo <ruv@ruv.net>

* fix(ui): tributary group frame fits taller HypothesisCard (260 → 348)

Frame top is hubY-40 and cards extend to hubY+CARD_H; with CARD_H now 288
the prior 260px frame ended 68px above card bottom, clipping cards when
groupByTributary is on. 348 = CARD_H(288) + 60 to leave 20px of clearance
below the card row.

Co-Authored-By: ruflo <ruv@ruv.net>

---------

Co-authored-by: ruflo <ruv@ruv.net>
jukka-matti added a commit that referenced this pull request May 14, 2026
…leanup workstream) (#166)

* fix(8f-followup): delete legacy variscout-wall-layout Dexie DB on init

Closes 8f followup HIGH #3 — pre-8f users carried an orphan IndexedDB
forever after the wallLayoutStore → canvasViewportStore shape change.
Mirror PwaHubRepository's legacy-DB cleanup pattern. Tightens the
existing test that lied about asserting deletion.

Co-Authored-By: ruflo <ruv@ruv.net>

* refactor(8f-followup): migrate canvas UI strings to typed message catalogs

Closes 8f followup HIGH #5 — 47 hardcoded English strings across
SystemLevelView, CanvasLensPicker, MobileLevelPicker, NoFocalStepPrompt,
AuthorL3View, LocalMechanismView now route through MessageCatalog.
CANVAS_LENS_REGISTRY labels/descriptions translated at render time in
CanvasLensPicker via LENS_LABEL_KEY / LENS_DESC_KEY maps; the hooks
registry keeps English for non-UI consumers. Non-English locales receive
English placeholders pending a translation pass (TODO(i18n) comment).

Co-Authored-By: ruflo <ruv@ruv.net>

* fix(8f-followup): migrate Canvas empty-state to message catalog

Closes the i18n reviewer's flagged follow-up: Canvas/index.tsx:1042
rendered the lens registry's English label and a local
CANVAS_LEVEL_LABELS map as user-facing copy, bypassing i18n.

- Export LENS_LABEL_KEY from CanvasLensPicker for cross-component reuse
- Reuse canvas.mobile.{system,process,step} for level labels
- Add canvas.lensPicker.invalidAtLevel parameterized key (32 locales,
  English placeholder elsewhere per the catalog's translation pass plan)
- Drop CANVAS_LEVEL_LABELS + remove now-unused CANVAS_LENS_REGISTRY import

Co-Authored-By: ruflo <ruv@ruv.net>

* test(8f-followup): cover CanvasLensPicker lens × level predicate

Closes 8f followup LOW #20 — load-bearing CanvasLensPicker had no
dedicated test. 18 enabled/disabled cells (3 levels × 6 lenses) +
click-dispatch + aria-label assertions.

Co-Authored-By: ruflo <ruv@ruv.net>

* docs(8f-followup): refresh stale wallLayoutStore references in store comments

Closes 8f followup LOW #18 — viewStore.ts:140 + preferencesStore.ts:178
still mentioned wallLayoutStore in doc-strings after the PR1 rename to
canvasViewportStore.

Co-Authored-By: ruflo <ruv@ruv.net>

* docs(8f-followup): fix plan frontmatter category to allowed enum value

* refactor(8f-followup): extract getStepColumnAssignments to @variscout/core/frame

Closes 8f followup HIGH #2 — AuthorL3View's private focalStepColumns
helper duplicates business logic that should live in core/frame. The
helper now lives at packages/core/src/frame/stepColumns.ts with 6
unit tests; AuthorL3View imports it. Per ADR-074 amendment + ADR-081:
Canvas embeds owner-surface computation, doesn't re-derive.

Co-Authored-By: ruflo <ruv@ruv.net>

* fix(8f-followup): tie L1 specLimits to outcome's own measureSpecs entry

Closes 8f followup MEDIUM #8 — SystemLevelView trusted a flat specLimits
prop without ADR-073-anchored contract. Now accepts measureSpecs keyed by
column and derives from measureSpecs[map.ctsColumn] internally; old prop
renamed to specLimitsOverride (advisory/debug only, deprecated). Canvas
passes { [ctsColumn]: { usl, lsl, target, cpkTarget } } as measureSpecs.
Two regression tests assert the leak scenario: wrong-column measureSpecs
key produces '--' Cpk, not a silently wrong value.

Co-Authored-By: ruflo <ruv@ruv.net>

* refactor(8f-followup): replace LocalMechanismView's focalStepColumns duplicate

Same ADR-074 amendment violation that PR2 fixed in AuthorL3View. The
private helper now delegates to getStepColumnAssignments from
@variscout/core/frame (introduced in the prior commit), flattening the
structured result into the string[] this view needs.

Co-Authored-By: ruflo <ruv@ruv.net>

* docs(8f-followup): resolve lens × level matrix gap via spec amend

Closes 8f followup HIGH #4 via AMEND path (not expand). Git blame shows
both `performance` and `yamazumi` lenses were introduced with
`enabled: false` AND registry descriptions explicitly labeling them as
"Future ... lens" — intentional V2 placeholders, not bugs. Spec §10 was
over-promised at original ship.

- Spec §10 matrix amended: 6 cells marked `(V2 — deferred; lens not
  enabled in V1)` instead of pretending they ship enabled
- V2 expansion path documented inline
- investigations.md entry marked RESOLVED 2026-05-13 with rationale

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(8f-followup): replace setViewportLevel throw with warn + no-op (4.4)

l3 without focalStepId now emits console.warn and returns the viewport
unchanged instead of throwing. fitToContent guards the same path.
Updated test asserts warn was called and state did not change.

Co-Authored-By: ruflo <ruv@ruv.net>

* refactor(8f-followup): co-locate level math constants in core/canvas/viewport (4.7)

Move FIT_TO_CONTENT_ZOOM_BY_LEVEL from canvasViewportStore into
@variscout/core/canvas/viewport.ts and re-export it through the barrel.
Add LOD_SNAP_BOUNDARIES (L2_OVERVIEW_LOW=0.5, L2_DETAIL_HIGH=1.8) for
the upcoming snap-to-LOD feature. Single source of truth for level math.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(8f-followup): enforce 6px click-vs-drag deadband via clickDistance(6) (4.3)

Adds .clickDistance(6) to the d3-zoom behavior in useCanvasViewportInput.
Pointer moves ≤5px are treated as clicks; ≥6px as drags. Matches spec §6.3.

Co-Authored-By: ruflo <ruv@ruv.net>

* chore(8f-followup): delete dead worldToWallSvg + document CanvasViewport (4.5/4.6)

worldToWallSvg was an identity function with no callers outside its own test;
deleted function and test. CanvasViewport is used in Canvas/index.tsx — added
JSDoc comment explaining its role so the seam is documented.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(8f-followup): snap-to-LOD on wheel-stop via d3-zoom end handler (4.2)

Adds a 'end' listener to the zoom behavior. When the user releases the wheel
with zoom in [0.3, 0.5) or [1.8, 2.0), the viewport eases back to 0.5 or 1.8
respectively over 150ms. Exports snapTarget() for unit testing. LOD_SNAP_BOUNDARIES
lives in @variscout/core/canvas/viewport alongside LOD_THRESHOLDS.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(8f-followup): real LOD cross-fade + d3-transition snap (4.1/4.2 final)

LODSwitcher now renders both outgoing and incoming renderers in stacked
absolute divs during a 150ms window, then unmounts the outgoing. Uses
useState+useEffect+setTimeout — no external animation library needed.

useCanvasViewportInput snap-to-LOD uses d3-transition via
selection.transition().duration(150).call(zoomBehavior.transform, ...).
Adds d3-transition + @types/d3-transition to @variscout/hooks deps.

Tests: 4 LODSwitcher tests assert dual-render during transition and
single-render after 150ms via fake timers.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(8f-followup): per-Hub canvas viewport blob helpers in blobClient

Closes 8f followup HIGH #1 part 1/2 — adds loadBlobCanvasViewport +
saveBlobCanvasViewport mirroring the updateBlobEvidenceSnapshots
ETag-conditional pattern. Per ADR-081 §2 (Azure = IndexedDB + Blob sync
with ETag per ADR-079) and ADR-079.

Also adds getLocalViewportUpdatedAt to @variscout/stores so the Azure
lifecycle hook can compare timestamps without reading Dexie directly.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(8f-followup): wire Azure canvas viewport lifecycle to Blob sync

Closes 8f followup HIGH #1 part 2/2 — useCanvasViewportLifecycle (Azure)
now rehydrates from Blob after Dexie cache, debounced-persists to both
Dexie and Blob with ETag, and treats precondition-failed as last-write-
wins per spec §11 with App Insights telemetry on the conflict path.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(8f-followup): expose 4 remaining response-path CTAs at L3 column granularity

Closes 8f followup MEDIUM #9 — LocalMechanismView previously only
exposed Quick Action. Spec §5.3.a lists 5 CTAs at column-mechanism
granularity (Quick Action / Focused Investigation / IP / Sustainment /
Handoff). Threaded the 4 step-level callbacks already on CanvasProps
through to LocalMechanismView; per-column button row added with new
i18n keys (8 new MessageCatalog keys across 32 locale files). Parent
callbacks are step-only; column is visible only within the card row.

Co-Authored-By: ruflo <ruv@ruv.net>

* feat(8f-followup): mobile L3 without focalStepId navigates to step-list

Closes 8f followup MEDIUM #10 — MobileLevelPicker previously called
setLevel('l2') before setZoom(2.5) as an explicit l2 redirect comment
implied. The final committed state was already l3 (setZoom(2.5) fires
inferLevel→l3), but the intent was undocumented. Updated comment to
clarify the atomicity: both calls run synchronously before React
re-renders, so the final state is l3 with no focalStepId, and canvas
renders NoFocalStepPrompt (the step-list surface) per spec §7.

Co-Authored-By: ruflo <ruv@ruv.net>

* perf(8f-followup): selector-scope canvasViewport subscribe in d3-zoom hook

Closes 8f followup MEDIUM #11 — useCanvasViewportInput previously
subscribed to the whole canvasViewportStore; every unrelated mutation
(setRailOpen, setViewMode, openChartCluster, etc.) fired
syncElementToStoreViewport (which has its own diff-check guard, but
still did needless work). Now tracks prevViewportRef and short-circuits
on reference equality of state.viewports[hubId] — sync is skipped
entirely when the hub's viewport slice hasn't changed. Test added:
setRailOpen → d3 element __zoom unchanged.

Co-Authored-By: ruflo <ruv@ruv.net>

* chore(8f-followup): rename canvasViewport STORE_LAYER to annotation-per-hub

Closes 8f followup LOW #15 — canvasViewportStore state is keyed by hubId
not projectId since the 8f shape change. The annotation-per-project
label was technically truthful (per-project umbrella, hub-keyed inside)
but invited confusion. Renamed to annotation-per-hub; layerBoundary
test + packages/stores/CLAUDE.md table updated. 'annotation-per-project'
is now in the reserved/unused set; 'annotation-per-hub' is live.

Co-Authored-By: ruflo <ruv@ruv.net>

* docs(8f-followup): mark 19 of 20 findings RESOLVED on followup branch

---------

Co-authored-by: ruflo <ruv@ruv.net>
jukka-matti added a commit that referenced this pull request May 30, 2026
…on-loop guard

IM-0b-2 (ADR-087 §5): CanvasProcessMap.test.tsx — re-point the ctqColumn /
tributary / subgroupAxis / hunch cases to assert the new dispatch props are
CALLED (with onChange NOT called) when wired, and keep a legacy onChange
map-build fallback case per group for back-compat. Specs-editor cases keep
asserting setMeasureSpec routing (unchanged); step-structure cases unchanged.

CanvasWorkspace.test.tsx — add the #1-regression-risk test: a ctqColumn edit
dispatches onSetStepCtq -> persistCanvasStoreMap -> setProcessContext (controlled
prop round-trip) and MUST NOT re-hydrate / clobber the canvasStore. Asserts the
store carries the new ctqColumn (proving the dispatch path, not the onChange
fallback) and that hydrateCanvasDocument is NOT called again beyond mount.
Verified discriminating: removing the CanvasWorkspace onSetStepCtq wiring fails it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file github_actions Pull requests that update GitHub Actions code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant