Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b8ba169
docs: lock load-bearing decisions for multi-level SCOUT V1
jukka-matti Apr 29, 2026
00c3228
feat(core): TimelineWindow discriminated union + type guards
jukka-matti Apr 29, 2026
2d92565
feat(core): applyWindow filters rows by TimelineWindow over a timeColumn
jukka-matti Apr 29, 2026
c1baf82
feat(core): detectScope classifies investigations as b0/b1/b2 by node…
jukka-matti Apr 29, 2026
f059c59
docs: revise Decision #1 to reflect actual ProcessHubInvestigation type
jukka-matti Apr 29, 2026
ad39bec
feat(core): computeFindingWindowDrift + WindowContext on Finding
jukka-matti Apr 29, 2026
c3642f2
feat(core): throughput module — computeOutputRate + computeBottleneck
jukka-matti Apr 29, 2026
f303666
feat(core): AnalysisModeStrategy gains dataRouter for (scope, phase, …
jukka-matti Apr 29, 2026
cf5daaa
docs: revise Task 8 to pure-projection over investigation metadata
jukka-matti Apr 29, 2026
4adae8b
feat(hooks): useTimelineWindow — pure projection over investigation m…
jukka-matti Apr 29, 2026
ee345dd
docs(hooks): JSDoc note re stable reference for investigation arg
jukka-matti Apr 29, 2026
bcebd54
feat(hooks): useDataRouter + window-aware useFilteredData / useProduc…
jukka-matti Apr 30, 2026
0864f98
feat(ui): TimelineWindowPicker — four-kind window selector with secon…
jukka-matti Apr 30, 2026
607f5d2
feat(ui): DashboardLayoutBase exposes TimelineWindowPicker in dashboa…
jukka-matti Apr 30, 2026
4e7443b
feat(ui): FindingCard renders window-context footer when present
jukka-matti Apr 30, 2026
49e088e
refactor(azure): ProcessHubCapabilityTab consults useDataRouter, thre…
jukka-matti Apr 30, 2026
af11e6d
feat(apps): wire timeline window into Dashboards + Hub Capability picker
jukka-matti Apr 30, 2026
6c68218
chore: ADR-074 boundary check script + pre-commit wiring
jukka-matti Apr 30, 2026
5566d14
docs: V1 documentation completeness sweep + lifecycle updates
jukka-matti Apr 30, 2026
17cae9d
fix(core): replace .toFixed in defect formatMetricValue per hard rule
jukka-matti Apr 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ pnpm docs:check
# SSOT check — warn if CLAUDE.md sections duplicate content across files
bash scripts/check-ssot.sh

# ADR-074 level-spanning-surface boundary check
bash scripts/check-level-boundaries.sh

# CLAUDE.md size check — fail if >120% of per-file budget
bash scripts/check-claude-md-size.sh

Expand Down
1 change: 1 addition & 0 deletions apps/azure/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Azure Team App — Feature-Sliced Design with Zustand feature stores, IndexedDB
- App Insights wired at `src/lib/appInsights.ts`. `services/storage.ts` is the facade for both local + cloud.
- Domain stores from `@variscout/stores` are the source of truth for project/investigation/improvement/session data. Feature stores hold UI-only state.
- File Picker: local files only (`components/FileBrowseButton.tsx`). SharePoint picker removed per ADR-059.
- Multi-level surfaces: SCOUT dashboard (investigation-time picker) and Hub Capability tab (hub-time, rolling default matched to cadence) link as peers; ADR-074 boundary policy applies.

## Test command

Expand Down
11 changes: 9 additions & 2 deletions apps/azure/src/components/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import {
useJourneyPhase,
useCapabilityIChartData,
} from '@variscout/hooks';
import type { AIContext } from '@variscout/core';
import type { AIContext, TimelineWindow } from '@variscout/core';
import type { ViewState } from '@variscout/hooks';
import { Activity, BarChart3, Gauge, Timer, ArrowLeft, Settings2 } from 'lucide-react';

Expand Down Expand Up @@ -198,7 +198,12 @@ const Dashboard = ({
const selectedPoints = useProjectStore(s => s.selectedPoints);
const clearSelection = useProjectStore(s => s.clearSelection);
const defectMapping = useProjectStore(s => s.defectMapping);
const { filteredData } = useFilteredData();
// Multi-level SCOUT V1: local timeline-window state. Investigation-level
// persistence — V2 wires through useTimelineWindow when the dashboards
// become investigation-aware (Dashboard currently reads useProjectStore
// and does not receive a ProcessHubInvestigation envelope).
const [timelineWindow, setTimelineWindow] = useState<TimelineWindow>({ kind: 'cumulative' });
const { filteredData } = useFilteredData({ window: timelineWindow });
const { stats, isComputing } = useAnalysisStats();
const { stagedStats } = useStagedAnalysis();
const { getTerm } = useGlossary();
Expand Down Expand Up @@ -705,6 +710,8 @@ const Dashboard = ({
) : (
<div className="flex flex-1 min-h-0">
<DashboardLayoutBase
timelineWindow={timelineWindow}
onTimelineWindowChange={setTimelineWindow}
outcome={outcome}
factors={factors}
columnAliases={columnAliases}
Expand Down
65 changes: 61 additions & 4 deletions apps/azure/src/components/ProcessHubCapabilityTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,80 @@
*
* Migration banner + modal are wired in T11 at hub-level (separate file).
*
* See spec docs/superpowers/specs/2026-04-28-production-line-glance-surface-wiring-design.md.
* Multi-level SCOUT V1 (Task 13): the call site now consults `useDataRouter`
* (via the standard mode strategy) as a sanity-check on the dataflow choice
* and threads a `TimelineWindow` into `useProductionLineGlanceData`. The
* dashboard composition itself stays hardcoded — slot-component-registry is
* a V2/V3 concern and no such registry exists in V1.
*
* See spec docs/superpowers/specs/2026-04-28-production-line-glance-surface-wiring-design.md
* and docs/superpowers/specs/2026-04-29-multi-level-scout-design.md.
*/
import React from 'react';
import { ProductionLineGlanceDashboard, ProductionLineGlanceFilterStrip } from '@variscout/ui';
import { useProductionLineGlanceData, useProductionLineGlanceFilter } from '@variscout/hooks';
import React, { useState } from 'react';
import {
ProductionLineGlanceDashboard,
ProductionLineGlanceFilterStrip,
TimelineWindowPicker,
} from '@variscout/ui';
import {
useDataRouter,
useProductionLineGlanceData,
useProductionLineGlanceFilter,
} from '@variscout/hooks';
import { detectScope } from '@variscout/core';
import type { TimelineWindow } from '@variscout/core';
import { useHubProvision } from '../features/processHub';
import type { ProcessHubInvestigation, ProcessHubRollup } from '@variscout/core';

export interface ProcessHubCapabilityTabProps {
rollup: ProcessHubRollup<ProcessHubInvestigation>;
}

const DEFAULT_WINDOW: TimelineWindow = { kind: 'cumulative' };

export const ProcessHubCapabilityTab: React.FC<ProcessHubCapabilityTabProps> = ({ rollup }) => {
const provision = useHubProvision({ rollup });
const filter = useProductionLineGlanceFilter();

// Hub-level window state. Hub doesn't carry its own TimelineWindow envelope
// in V1 — `useTimelineWindow` is keyed on a single ProcessHubInvestigation,
// and the hub view aggregates many. Local useState is the correct V1 fit.
// TODO(multi-level-scout V1.5): default the window to rolling matched to
// hub.cadence on first mount.
// TODO(multi-level-scout V2): wire hub-level window persistence.
const [window, setWindow] = useState<TimelineWindow>(DEFAULT_WINDOW);

// Scope detection: hub views are typically `b1` (multi-step, multi-member).
// If a single representative member exists, use detectScope on it; otherwise
// default to `b1` (the hub-aggregate scope).
const scope = provision.members.length === 1 ? detectScope(provision.members[0]) : 'b1';

// Sanity-check the dataflow choice via the strategy router. The actual hook
// used (`useProductionLineGlanceData`) must remain known at compile time —
// React forbids conditional hook calls. The router is a validation hook + a
// V2/V3 extension point.
const router = useDataRouter({
mode: 'standard',
modeOptions: { standardIChartMetric: 'capability' },
scope,
phase: 'hub',
window,
context: filter.value,
});
if (import.meta.env.DEV && router.hook !== 'useProductionLineGlanceData') {
console.warn(
`[ProcessHubCapabilityTab] dataRouter expected 'useProductionLineGlanceData', got '${router.hook}'`
);
}

const data = useProductionLineGlanceData({
hub: provision.hub,
members: provision.members,
rowsByInvestigation: provision.rowsByInvestigation,
contextFilter: filter.value,
window,
// timeColumnByInvestigation is not reachable at hub level in V1; the hook
// skips windowing for any member without a time column (safer fail-mode).
});

return (
Expand All @@ -37,6 +91,9 @@ export const ProcessHubCapabilityTab: React.FC<ProcessHubCapabilityTabProps> = (
value={filter.value}
onChange={filter.onChange}
/>
<div className="flex items-center gap-2 px-3 py-2 border-b border-edge bg-surface">
<TimelineWindowPicker window={window} onChange={setWindow} />
</div>
<div className="flex-1 min-h-0">
<ProductionLineGlanceDashboard
cpkTrend={data.cpkTrend}
Expand Down
2 changes: 1 addition & 1 deletion apps/pwa/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Free PWA. Session-only (no persistence), Context-based state, education tier.

- State via React Context (`DataContext`). No Zustand stores in PWA.
- Embedded mode supported for iframes (see flows in `docs/02-journeys/flows/pwa-education.md`).
- Entry: `src/components/Dashboard.tsx`.
- Entry: `src/components/Dashboard.tsx`. Hosts the timeline-window picker in the dashboard chrome (investigation-time, default `open-ended`); session-local in V1.

## Test command

Expand Down
17 changes: 15 additions & 2 deletions apps/pwa/src/components/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,13 @@ import { useFilteredData, useAnalysisStats, useStagedAnalysis } from '@variscout
import { useDashboardCharts } from '../hooks/useDashboardCharts';
import type { UseFilterNavigationReturn } from '../hooks/useFilterNavigation';
import { Activity } from 'lucide-react';
import { getColumnNames, getEtaSquared, type SpecLimits, type Finding } from '@variscout/core';
import {
getColumnNames,
getEtaSquared,
type SpecLimits,
type Finding,
type TimelineWindow,
} from '@variscout/core';
import { resolveMode as resolveModeUtil } from '@variscout/core/strategy';
import { subgroupAxisColumns } from '@variscout/core/frame';
import { useProjectionStore } from '../features/projection/projectionStore';
Expand Down Expand Up @@ -133,7 +139,12 @@ const Dashboard = ({
const clearSelection = useProjectStore(s => s.clearSelection);
const analysisMode = useProjectStore(s => s.analysisMode);
const defectMapping = useProjectStore(s => s.defectMapping);
const { filteredData } = useFilteredData();
// Multi-level SCOUT V1: local timeline-window state. Investigation-level
// persistence — V2 wires through useTimelineWindow when the dashboards
// become investigation-aware (Dashboard currently reads useProjectStore
// and does not receive a ProcessHubInvestigation envelope).
const [timelineWindow, setTimelineWindow] = useState<TimelineWindow>({ kind: 'cumulative' });
const { filteredData } = useFilteredData({ window: timelineWindow });
const { stats, isComputing } = useAnalysisStats();
const { stagedStats } = useStagedAnalysis();
const [analysisLensTab, setAnalysisLensTab] = useState<AnalysisLensTab>('probability');
Expand Down Expand Up @@ -666,6 +677,8 @@ const Dashboard = ({

{/* Dashboard View */}
<DashboardLayoutBase
timelineWindow={timelineWindow}
onTimelineWindowChange={setTimelineWindow}
outcome={effectiveOutcome}
factors={effectiveFactors}
columnAliases={columnAliases}
Expand Down
2 changes: 2 additions & 0 deletions docs/01-vision/eda-mental-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ Step 1 produces the first version of the analysis plan: the issue statement, the

Steps 2-4 are iterative. The analyst does not pass through them once — they cycle through them repeatedly, each iteration producing a more refined understanding. This is the EDA inner loop, and it is the heart of the methodology.

In VariScout's multi-level SCOUT, each loop carries an explicit **timeline window** — fixed, rolling, open-ended, or cumulative — so the temporal scope of every iteration is preserved alongside its filters and findings. See [Timeline Windows in Investigations](../03-features/analysis/timeline-window-investigations.md).

For full thesis excerpts and figure descriptions, see [Turtiainen (2019) Reference](references/turtiainen-2019-eda-mental-model.md).

---
Expand Down
2 changes: 2 additions & 0 deletions docs/01-vision/methodology.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ A critical distinction underpins the investigation flow:
- **Issue Statement** (the input): A vague concern that initiates the investigation. Example: _"Fill weight on line 3 seems too variable."_ Watson (2019a) defines an issue as a concern arising from a gap between customer expectation and observation.
- **Problem Statement** (the output): A precise declaration answering Watson's three questions: (1) What measure needs to change? (2) How should it change? (3) What is the scope? Example: _"Reduce fill weight variation on line 3, night shift, heads 5-8, from Cpk 0.62 to target Cpk 1.33."_

**Temporal scope is part of the third question.** The "when does this happen?" dimension is captured by a **timeline window** on the investigation — a tagged variant of fixed, rolling, open-ended, or cumulative — that travels with every chart, every Finding, and every drift comparison. Without an explicit window, "scope" stays vague; with one, the analysis can answer "this happens during these dates, on this rolling horizon, since this start" precisely. See [Timeline Windows in Investigations](../03-features/analysis/timeline-window-investigations.md).

The gap between the two is the entire EDA journey. Every question asked and answered in VariScout exists to close this gap.

#### Question Generation
Expand Down
77 changes: 77 additions & 0 deletions docs/03-features/analysis/multi-level-dashboard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
title: Multi-Level Dashboard
audience: [analyst]
category: analysis
status: delivered
related: [timeline-window-investigations, capability, process-hub-capability, stats-panel]
---

# Multi-Level Dashboard

SCOUT's analysis dashboard now spans the three Process Learning Levels. The same screen reads the **system outcome** (L1 Y), the **process flow** (L2 X), and the **local mechanism** (L3 x) — without re-rendering the workspace for each level and without the analyst leaving SCOUT.

> **Journey phase:** SCOUT — multi-level reading. FRAME owns L2 authoring; Investigation Wall owns L3 hypothesis canvas.

---

## What "multi-level" means here

Each VariScout surface owns exactly one level and lenses the others by linking, never by re-rendering. The boundary is enforced structurally; see [ADR-074](../../07-decisions/adr-074-scout-level-spanning-surface-boundary-policy.md).

| Level | Owned by | Lensed by |
| ---------------------- | ------------------------------------------------ | ---------------------------------------------------- |
| **L1 — Outcome (Y)** | SCOUT dashboard | Hub Capability tab, Investigation Wall, Evidence Map |
| **L2 — Flow (X)** | FRAME (authoring) + Hub Capability tab (reading) | SCOUT chrome, Investigation Wall |
| **L3 — Mechanism (x)** | Investigation Wall | SCOUT (factor selectors), Evidence Map, INVESTIGATE |

In SCOUT V1, the multi-level reading is delivered by:

1. **Scope detection** — the strategy detects whether the data covers a baseline (B0), a single-node investigation (B1), or multiple nodes (B2) and routes data to the four chart slots accordingly. No new charts; the existing four-slot grid stays the same.
2. **Timeline windows** — every chart, every metric, and every Finding agrees on the same temporal scope. See [Timeline Windows in Investigations](timeline-window-investigations.md).
3. **Throughput basics** — `computeOutputRate` and `computeBottleneck` ship in V1 to give L2 (flow) reading a baseline. Cycle time, FPY, RTY arrive in the second slice; OEE, takt, lead time, and WIP in the third. See spec §8.

---

## How the surfaces relate

SCOUT, the Process Hub Capability tab, the Evidence Map, and the Investigation Wall are **peers** in V1 — not siblings inside one pane. The dashboard is the entry point; the others are link targets.

| Surface | Role |
| ---------------------- | ------------------------------------------------------------------------------------------------- |
| **SCOUT dashboard** | Outcome reading. Four chart slots. Timeline window in chrome. Findings saved with window context. |
| **Hub Capability tab** | Flow reading. Per-step Cpk distribution, capability over time, hub-time rolling default. |
| **Evidence Map** | Factor network. Click a factor → focus its statistical context. Receives links from Findings. |
| **Investigation Wall** | Hypothesis canvas. Hubs accumulate evidence (data + Gemba + expert) per SuspectedCause. |

Crossing surfaces preserves the active timeline window. When you click from a Finding to its Evidence Map factor, or from a Hub Capability bar to a SuspectedCause hub, the window context travels with you. Drift detection runs on entry: if today's window has shifted more than 20% from the saved Finding context, the Finding flags it.

---

## What V1 does not change

- The four chart slots (I-Chart, Variation Sources, Adaptive Lens, Pareto/Capability) keep their roles.
- Drill A (drill into a single factor) keeps its existing semantics.
- No new chart types. No Drill C, no Plan D, no per-mode multi-level expansion beyond Standard EDA.
- FRAME thin-spot helpers stay deferred — see decision-log §5.

---

## When to use which surface

- **Outcome question** ("Is the customer-facing measurement in spec? When did it shift?") → SCOUT dashboard with the timeline picker.
- **Flow question** ("Which step is the bottleneck? Which step has the worst Cpk?") → Process Hub Capability tab.
- **Mechanism question** ("What evidence supports this suspected cause? What's missing?") → Investigation Wall.
- **Factor question** ("Which factors drive this Y? How are they connected?") → Evidence Map.

The dashboard does not try to answer all four — it is the L1 reading surface. The other three are one click away.

---

## See also

- [Timeline Windows in Investigations](timeline-window-investigations.md)
- [Timeline Window Architecture](../../05-technical/architecture/timeline-window-architecture.md)
- [Multi-level SCOUT design spec](../../superpowers/specs/2026-04-29-multi-level-scout-design.md)
- [ADR-074 — surface boundary policy](../../07-decisions/adr-074-scout-level-spanning-surface-boundary-policy.md)
- [Process Hub Capability](process-hub-capability.md)
- [Statistics Panel](stats-panel.md)
Loading