@validation-os/dashboard 0.16.2 → 0.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +63 -9
- package/dist/index.js +188 -18
- package/dist/index.js.map +1 -1
- package/dist/styles.css +65 -4
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1464,11 +1464,11 @@ declare function NextMoveSurface({ basePath, onNavigate }: NextMoveSurfaceProps)
|
|
|
1464
1464
|
|
|
1465
1465
|
/** The four discovery stages, in their canonical ordinal order (1→4). The
|
|
1466
1466
|
* stored value is the name; the ordinal is for sort/display only. */
|
|
1467
|
-
declare const STAGE_ORDER: readonly ["Discovery", "Validation", "Scale", "Maturity"];
|
|
1468
|
-
type StageValue = (typeof STAGE_ORDER)[number];
|
|
1467
|
+
declare const STAGE_ORDER$1: readonly ["Discovery", "Validation", "Scale", "Maturity"];
|
|
1468
|
+
type StageValue$1 = (typeof STAGE_ORDER$1)[number];
|
|
1469
1469
|
/** A short, plain-language gloss for each stage — the column header's
|
|
1470
1470
|
* subtitle and the cell tooltip. Drawn from docs/stage-policy.md. */
|
|
1471
|
-
declare const STAGE_GLOSS: Record<StageValue, string>;
|
|
1471
|
+
declare const STAGE_GLOSS: Record<StageValue$1, string>;
|
|
1472
1472
|
/** One cell of the Lens × Stage grid. */
|
|
1473
1473
|
interface StageGridCell {
|
|
1474
1474
|
/** The Lens (row) this cell sits under. */
|
|
@@ -1476,7 +1476,7 @@ interface StageGridCell {
|
|
|
1476
1476
|
/** The Stage (column) this cell sits under — one of the four canonical
|
|
1477
1477
|
* stages, or the `NO_STAGE` ("—") sentinel for records with no/invalid
|
|
1478
1478
|
* Stage (the gate-leakage bucket, only emitted when needed). */
|
|
1479
|
-
stage: StageValue | typeof NO_STAGE;
|
|
1479
|
+
stage: StageValue$1 | typeof NO_STAGE$1;
|
|
1480
1480
|
/** How many assumptions hold this Lens × Stage pair. */
|
|
1481
1481
|
count: number;
|
|
1482
1482
|
/** The assumptions in this cell, ranked by Risk (highest first). Empty
|
|
@@ -1494,7 +1494,7 @@ interface StageGridView {
|
|
|
1494
1494
|
* a trailing "—" row for assumptions with no Lens set. */
|
|
1495
1495
|
lenses: string[];
|
|
1496
1496
|
/** The four stages in their canonical ordinal order. */
|
|
1497
|
-
stages: StageValue[];
|
|
1497
|
+
stages: StageValue$1[];
|
|
1498
1498
|
/** One cell per (lens, stage) pair, in row-major order: lens-first,
|
|
1499
1499
|
* stage-within-lens. */
|
|
1500
1500
|
cells: StageGridCell[];
|
|
@@ -1507,11 +1507,11 @@ interface StageGridView {
|
|
|
1507
1507
|
total: number;
|
|
1508
1508
|
}
|
|
1509
1509
|
declare const NO_LENS = "\u2014";
|
|
1510
|
-
declare const NO_STAGE = "\u2014";
|
|
1510
|
+
declare const NO_STAGE$1 = "\u2014";
|
|
1511
1511
|
/** A stage value from a record, validated against the fixed vocabulary.
|
|
1512
1512
|
* Returns null for an empty or unrecognised value — the caller decides
|
|
1513
1513
|
* whether to bucket those or drop them. */
|
|
1514
|
-
declare function stageOf(record: AnyRecord): StageValue | null;
|
|
1514
|
+
declare function stageOf(record: AnyRecord): StageValue$1 | null;
|
|
1515
1515
|
/** Sort a list of assumptions by Risk, highest first. Stable on tie by id —
|
|
1516
1516
|
* matches the pipeline board's "riskiest first" convention. Pure: returns a
|
|
1517
1517
|
* new array, leaves the input alone. */
|
|
@@ -1543,7 +1543,7 @@ declare function buildStageGrid(assumptions: AnyRecord[]): StageGridView;
|
|
|
1543
1543
|
/** The cell at a given (lens, stage) — null if no such cell (e.g. a stage
|
|
1544
1544
|
* not in the canonical order). The "—" row / "—" column are addressable
|
|
1545
1545
|
* with the `NO_LENS` / `NO_STAGE` sentinels. */
|
|
1546
|
-
declare function cellAt(view: StageGridView, lens: string, stage: StageValue | typeof NO_STAGE): StageGridCell | null;
|
|
1546
|
+
declare function cellAt(view: StageGridView, lens: string, stage: StageValue$1 | typeof NO_STAGE$1): StageGridCell | null;
|
|
1547
1547
|
|
|
1548
1548
|
/**
|
|
1549
1549
|
* The Lens × Stage heatmap surface (docs/stage-policy.md §The dashboard
|
|
@@ -1573,6 +1573,60 @@ interface StageGridSurfaceProps {
|
|
|
1573
1573
|
}
|
|
1574
1574
|
declare function StageGridSurface({ basePath, onNavigate }: StageGridSurfaceProps): react.JSX.Element;
|
|
1575
1575
|
|
|
1576
|
+
/**
|
|
1577
|
+
* The Lens × Stage × Question Type heatmap view-model (DEV-5890) — pure, no
|
|
1578
|
+
* React, no I/O. Extends the 2D Lens × Stage grid with a Question Type
|
|
1579
|
+
* filter axis and Risk-weighted heat.
|
|
1580
|
+
*
|
|
1581
|
+
* The grid is the **filter**, Risk is the **heat**. Each cell's colour
|
|
1582
|
+
* intensity reflects the aggregate Risk (sum of derived.risk) of the
|
|
1583
|
+
* assumptions in that cell, not just the count. A cell with 3 high-Risk
|
|
1584
|
+
* assumptions is hotter than a cell with 10 low-Risk ones.
|
|
1585
|
+
*
|
|
1586
|
+
* The Question Type axis is a filter: selecting a type (e.g. "Existence")
|
|
1587
|
+
* shows only assumptions of that type in the grid. "All" shows everything.
|
|
1588
|
+
*/
|
|
1589
|
+
|
|
1590
|
+
declare const STAGE_ORDER: readonly ["Discovery", "Validation", "Scale", "Maturity"];
|
|
1591
|
+
type StageValue = (typeof STAGE_ORDER)[number];
|
|
1592
|
+
/** The 7 question types + "All" for the filter. */
|
|
1593
|
+
declare const QUESTION_TYPE_FILTERS: readonly ["All", "Existence", "Prevalence", "CausalEffect", "WillingnessToPay", "ValueUtility", "Regulatory", "Feasibility"];
|
|
1594
|
+
type QuestionTypeFilter = (typeof QUESTION_TYPE_FILTERS)[number];
|
|
1595
|
+
interface HeatCell {
|
|
1596
|
+
lens: string;
|
|
1597
|
+
stage: StageValue | typeof NO_STAGE;
|
|
1598
|
+
questionType: string | null;
|
|
1599
|
+
count: number;
|
|
1600
|
+
/** The sum of Risk across all assumptions in this cell — the heat value. */
|
|
1601
|
+
totalRisk: number;
|
|
1602
|
+
/** The average Risk per assumption — used for the heat scale. */
|
|
1603
|
+
avgRisk: number;
|
|
1604
|
+
/** The assumptions in this cell, ranked by Risk (highest first). */
|
|
1605
|
+
assumptions: AnyRecord[];
|
|
1606
|
+
/** Heat intensity in [0, 1] — avgRisk / maxAvgRisk across the grid. */
|
|
1607
|
+
heat: number;
|
|
1608
|
+
}
|
|
1609
|
+
interface HeatGridView {
|
|
1610
|
+
lenses: string[];
|
|
1611
|
+
stages: StageValue[];
|
|
1612
|
+
/** The question types present in the data (for the filter tabs). */
|
|
1613
|
+
questionTypes: string[];
|
|
1614
|
+
cells: HeatCell[];
|
|
1615
|
+
maxAvgRisk: number;
|
|
1616
|
+
maxCellCount: number;
|
|
1617
|
+
total: number;
|
|
1618
|
+
}
|
|
1619
|
+
declare const NO_STAGE = "\u2014";
|
|
1620
|
+
/**
|
|
1621
|
+
* Build the Lens × Stage × Question Type grid. When `questionTypeFilter` is
|
|
1622
|
+
* "All" or null, all assumptions are included. When a specific type is
|
|
1623
|
+
* selected, only assumptions of that type are shown. Heat = avg Risk per cell.
|
|
1624
|
+
*/
|
|
1625
|
+
declare function buildHeatGrid(assumptions: AnyRecord[], questionTypeFilter?: QuestionTypeFilter): HeatGridView;
|
|
1626
|
+
declare function heatCellAt(view: HeatGridView, lens: string, stage: StageValue | typeof NO_STAGE): HeatCell | null;
|
|
1627
|
+
/** Map a heat value [0, 1] to a 5-step colour scale. */
|
|
1628
|
+
declare function heatLevel(heat: number): 0 | 1 | 2 | 3 | 4;
|
|
1629
|
+
|
|
1576
1630
|
interface ScoreImpactFormProps {
|
|
1577
1631
|
/** The assumption being weighted (its version guards the write). */
|
|
1578
1632
|
assumption: AnyRecord;
|
|
@@ -1995,4 +2049,4 @@ declare const REGISTER_GROUPS: {
|
|
|
1995
2049
|
registers: Collection[];
|
|
1996
2050
|
}[];
|
|
1997
2051
|
|
|
1998
|
-
export { type BacklinkItem, BeliefJourney, type BeliefJourneyProps, type BeliefVerdict, BeliefVerdicts, CONFLICT_MESSAGE, type CellKind, type ColdStart, type ColumnDef, ConfidenceCell, ConnectClaudeCode, type ConnectClaudeCodeProps, type ConnectCommandInput, type Counts, type CycleReadingView, type CycleView, DEFAULT_TOKEN_ENV, DIRECT_CYCLE_KEY, type DashboardConfig, type DetailRelationItem, type DetailRow, type DetailRowKind, type Draft, EditBeliefForm, type EditBeliefFormProps, EditFields, type ExperimentView, FIRST_RUN_LINE, type FieldEditor, FieldInput, type FieldKind, type FormField, type GlossaryTerm, GlossaryText, type GlossaryTextProps, type GroupBucket, type GroupByAxis, type HumanText, type JourneyColdState, type JourneyEventView, type JourneyView, type LinkArgs, type LinkChoice, type LinkifyNode, type LinkifyOptions, Markdown, type Meter, type MeterKind, type MovePresentation, NO_LENS, NO_STAGE, type NeedsHumanByRegister, type NeedsHumanCounts, type NeighbourChip, type NestedGroup, type NextColdStart, type NextMoveRecords, NextMoveSurface, type NextMoveSurfaceProps, type OtherMover, type Pill, type PipelineColdStart, type PipelineRow, PipelineSurface, type PipelineView, REGISTER_GROUPS, REGISTER_ICON, REGISTER_LABEL, REGISTER_ORDER, REGISTER_SINGULAR, REGISTER_SUBTITLE, RISK_CRIT, RISK_WARN, RecordDrawer, type RecordDrawerProps, RecordForm, type RecordFormProps, RecordPage, type RecordPageModel, type RecordPageProps, type RecordTabId, RegisterBrowser, type RegisterBrowserProps, type RegisterContext, RegisterCounts, type RegisterCountsProps, RegisterTable, type RegisterTableProps, type RelatedSet, RelationEditor, type RelationEditorProps, type RelationPanel, type ResolvedBarLine, type ResolvedRow, type RiskBand, RiskBar, type Route, STAGE_GLOSS, STAGE_ORDER, type SaveResult, type SavedView, type ScoreChip, ScoreImpactForm, type ScoreImpactFormProps, type ShapedRegister, SidebarNav, type SidebarNavProps, type SortSpec, Sparkline, type StageGridCell, StageGridSurface, type StageGridSurfaceProps, type StageGridView, type StageMeterInput, type StageMeterView, type StageValue, StatTile, StatusPill, type StepInForm, type StoryStepIn, SurfacePlaceholder, type SurfacePlaceholderProps, type TabDef, type TermPreview, type Tone, type Understanding, UnderstandingPanel, type UseCountsResult, type UseCreateResult, type UseLinkResult, type UseListResult, type UseNeedsHumanResult, type UseRecordResult, type UseSavedViewsResult, type UseUpdateResult, ValidationOSDashboard, type ValidationOSDashboardProps, type ViewDescriptor, WriteDecisionForm, type WriteDecisionFormProps, backlinkPanels, bodyPreview, buildCycles, buildJourney, buildPatch, buildPipeline, buildRecordPage, buildStageGrid, buildUnderstanding, cellAt, cellValue, coldStartFor, columnsFor, composeConnectCommand, confidenceTone, configureRiskBands, defaultTabId, detailRows, dontConfuseWith, draftFrom, editableFields, emptyDraft, eventStepIn, eventTone, filterRecords, formFieldsFor, formatCount, formatRoute, formatSigned, formatValue, groupByAxesFor, groupRecords, hasEdits, headerPills, humanInputFields, interpretSave, journeyColdState, leadingMeters, linkChoicesFrom, linkify, missingRequired, movePresentation, needsHumanCounts, nestReadingsByPlan, ownerNames, parseRoute, primaryLabel, rankByRisk, readingAssumptionChips, readingBeliefVerdicts, resolveBarLines, riskBand, riskFraction, riskLevel, scoreChip, shapeRegister, sortRecords, sparklinePath, stageMeters, stageOf, statusTone, tabsFor, toCreatePayload, toGlossaryTerms, toNextMoveInput, toStageExperimentInput, useCounts, useCreate, useLink, useList, useNeedsHuman, useRecord, useSavedViews, useUpdate, weekOverWeekDelta };
|
|
2052
|
+
export { type BacklinkItem, BeliefJourney, type BeliefJourneyProps, type BeliefVerdict, BeliefVerdicts, CONFLICT_MESSAGE, type CellKind, type ColdStart, type ColumnDef, ConfidenceCell, ConnectClaudeCode, type ConnectClaudeCodeProps, type ConnectCommandInput, type Counts, type CycleReadingView, type CycleView, DEFAULT_TOKEN_ENV, DIRECT_CYCLE_KEY, type DashboardConfig, type DetailRelationItem, type DetailRow, type DetailRowKind, type Draft, EditBeliefForm, type EditBeliefFormProps, EditFields, type ExperimentView, FIRST_RUN_LINE, type FieldEditor, FieldInput, type FieldKind, type FormField, type GlossaryTerm, GlossaryText, type GlossaryTextProps, type GroupBucket, type GroupByAxis, type HeatCell, type HeatGridView, type HumanText, type JourneyColdState, type JourneyEventView, type JourneyView, type LinkArgs, type LinkChoice, type LinkifyNode, type LinkifyOptions, Markdown, type Meter, type MeterKind, type MovePresentation, NO_LENS, NO_STAGE$1 as NO_STAGE, type NeedsHumanByRegister, type NeedsHumanCounts, type NeighbourChip, type NestedGroup, type NextColdStart, type NextMoveRecords, NextMoveSurface, type NextMoveSurfaceProps, type OtherMover, type Pill, type PipelineColdStart, type PipelineRow, PipelineSurface, type PipelineView, QUESTION_TYPE_FILTERS, type QuestionTypeFilter, REGISTER_GROUPS, REGISTER_ICON, REGISTER_LABEL, REGISTER_ORDER, REGISTER_SINGULAR, REGISTER_SUBTITLE, RISK_CRIT, RISK_WARN, RecordDrawer, type RecordDrawerProps, RecordForm, type RecordFormProps, RecordPage, type RecordPageModel, type RecordPageProps, type RecordTabId, RegisterBrowser, type RegisterBrowserProps, type RegisterContext, RegisterCounts, type RegisterCountsProps, RegisterTable, type RegisterTableProps, type RelatedSet, RelationEditor, type RelationEditorProps, type RelationPanel, type ResolvedBarLine, type ResolvedRow, type RiskBand, RiskBar, type Route, STAGE_GLOSS, STAGE_ORDER$1 as STAGE_ORDER, type SaveResult, type SavedView, type ScoreChip, ScoreImpactForm, type ScoreImpactFormProps, type ShapedRegister, SidebarNav, type SidebarNavProps, type SortSpec, Sparkline, type StageGridCell, StageGridSurface, type StageGridSurfaceProps, type StageGridView, type StageMeterInput, type StageMeterView, type StageValue$1 as StageValue, StatTile, StatusPill, type StepInForm, type StoryStepIn, SurfacePlaceholder, type SurfacePlaceholderProps, type TabDef, type TermPreview, type Tone, type Understanding, UnderstandingPanel, type UseCountsResult, type UseCreateResult, type UseLinkResult, type UseListResult, type UseNeedsHumanResult, type UseRecordResult, type UseSavedViewsResult, type UseUpdateResult, ValidationOSDashboard, type ValidationOSDashboardProps, type ViewDescriptor, WriteDecisionForm, type WriteDecisionFormProps, backlinkPanels, bodyPreview, buildCycles, buildHeatGrid, buildJourney, buildPatch, buildPipeline, buildRecordPage, buildStageGrid, buildUnderstanding, cellAt, cellValue, coldStartFor, columnsFor, composeConnectCommand, confidenceTone, configureRiskBands, defaultTabId, detailRows, dontConfuseWith, draftFrom, editableFields, emptyDraft, eventStepIn, eventTone, filterRecords, formFieldsFor, formatCount, formatRoute, formatSigned, formatValue, groupByAxesFor, groupRecords, hasEdits, headerPills, heatCellAt, heatLevel, humanInputFields, interpretSave, journeyColdState, leadingMeters, linkChoicesFrom, linkify, missingRequired, movePresentation, needsHumanCounts, nestReadingsByPlan, ownerNames, parseRoute, primaryLabel, rankByRisk, readingAssumptionChips, readingBeliefVerdicts, resolveBarLines, riskBand, riskFraction, riskLevel, scoreChip, shapeRegister, sortRecords, sparklinePath, stageMeters, stageOf, statusTone, tabsFor, toCreatePayload, toGlossaryTerms, toNextMoveInput, toStageExperimentInput, useCounts, useCreate, useLink, useList, useNeedsHuman, useRecord, useSavedViews, useUpdate, weekOverWeekDelta };
|
package/dist/index.js
CHANGED
|
@@ -2680,6 +2680,148 @@ function stageMeters(input) {
|
|
|
2680
2680
|
];
|
|
2681
2681
|
}
|
|
2682
2682
|
|
|
2683
|
+
// src/heat-grid-model.ts
|
|
2684
|
+
var STAGE_ORDER2 = [
|
|
2685
|
+
"Discovery",
|
|
2686
|
+
"Validation",
|
|
2687
|
+
"Scale",
|
|
2688
|
+
"Maturity"
|
|
2689
|
+
];
|
|
2690
|
+
var STAGE_GLOSS2 = {
|
|
2691
|
+
Discovery: "Problem-solution fit \u2014 will they engage, care, disclose?",
|
|
2692
|
+
Validation: "Product-market fit \u2014 will they pay, sign, stay?",
|
|
2693
|
+
Scale: "Growth \u2014 can we acquire efficiently, does CAC<LTV hold at volume?",
|
|
2694
|
+
Maturity: "Defense \u2014 will incumbents respond, will regulators accept?"
|
|
2695
|
+
};
|
|
2696
|
+
var QUESTION_TYPE_FILTERS = [
|
|
2697
|
+
"All",
|
|
2698
|
+
"Existence",
|
|
2699
|
+
"Prevalence",
|
|
2700
|
+
"CausalEffect",
|
|
2701
|
+
"WillingnessToPay",
|
|
2702
|
+
"ValueUtility",
|
|
2703
|
+
"Regulatory",
|
|
2704
|
+
"Feasibility"
|
|
2705
|
+
];
|
|
2706
|
+
var NO_LENS2 = "\u2014";
|
|
2707
|
+
var NO_STAGE2 = "\u2014";
|
|
2708
|
+
function stageOf2(record) {
|
|
2709
|
+
const v = str(record.Stage);
|
|
2710
|
+
if (!v) return null;
|
|
2711
|
+
return STAGE_ORDER2.includes(v) ? v : null;
|
|
2712
|
+
}
|
|
2713
|
+
function questionTypeOf(record) {
|
|
2714
|
+
return str(record["Question Type"]);
|
|
2715
|
+
}
|
|
2716
|
+
function rankByRisk2(records) {
|
|
2717
|
+
return [...records].map((r) => ({ r, risk: derivedNum(r, "risk") ?? 0 })).sort(
|
|
2718
|
+
(a, b) => a.risk !== b.risk ? b.risk - a.risk : a.r.id.localeCompare(b.r.id)
|
|
2719
|
+
).map((x) => x.r);
|
|
2720
|
+
}
|
|
2721
|
+
function buildHeatGrid(assumptions, questionTypeFilter = "All") {
|
|
2722
|
+
const filtered = questionTypeFilter === "All" || !questionTypeFilter ? assumptions : assumptions.filter((a) => questionTypeOf(a) === questionTypeFilter);
|
|
2723
|
+
const lensOrder = [];
|
|
2724
|
+
const seenLens = /* @__PURE__ */ new Set();
|
|
2725
|
+
const pushLens = (lens) => {
|
|
2726
|
+
if (!seenLens.has(lens)) {
|
|
2727
|
+
seenLens.add(lens);
|
|
2728
|
+
lensOrder.push(lens);
|
|
2729
|
+
}
|
|
2730
|
+
};
|
|
2731
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
2732
|
+
const ensureLens = (lens) => {
|
|
2733
|
+
let m = buckets.get(lens);
|
|
2734
|
+
if (!m) {
|
|
2735
|
+
m = /* @__PURE__ */ new Map();
|
|
2736
|
+
buckets.set(lens, m);
|
|
2737
|
+
}
|
|
2738
|
+
return m;
|
|
2739
|
+
};
|
|
2740
|
+
let hasNoLens = false;
|
|
2741
|
+
let hasNoStage = false;
|
|
2742
|
+
for (const a of filtered) {
|
|
2743
|
+
const lens = str(a.Lens) ?? NO_LENS2;
|
|
2744
|
+
const stage = stageOf2(a) ?? NO_STAGE2;
|
|
2745
|
+
if (lens === NO_LENS2) hasNoLens = true;
|
|
2746
|
+
if (stage === NO_STAGE2) hasNoStage = true;
|
|
2747
|
+
pushLens(lens);
|
|
2748
|
+
const byStage = ensureLens(lens);
|
|
2749
|
+
let list = byStage.get(stage);
|
|
2750
|
+
if (!list) {
|
|
2751
|
+
list = [];
|
|
2752
|
+
byStage.set(stage, list);
|
|
2753
|
+
}
|
|
2754
|
+
list.push(a);
|
|
2755
|
+
}
|
|
2756
|
+
if (hasNoLens) {
|
|
2757
|
+
lensOrder.splice(lensOrder.indexOf(NO_LENS2), 1);
|
|
2758
|
+
lensOrder.push(NO_LENS2);
|
|
2759
|
+
} else {
|
|
2760
|
+
const idx = lensOrder.indexOf(NO_LENS2);
|
|
2761
|
+
if (idx >= 0) lensOrder.splice(idx, 1);
|
|
2762
|
+
}
|
|
2763
|
+
const stageOrder = [...STAGE_ORDER2];
|
|
2764
|
+
if (hasNoStage) stageOrder.push(NO_STAGE2);
|
|
2765
|
+
const cells = [];
|
|
2766
|
+
let maxAvgRisk = 0;
|
|
2767
|
+
let maxCellCount = 0;
|
|
2768
|
+
let total = 0;
|
|
2769
|
+
for (const lens of lensOrder) {
|
|
2770
|
+
const byStage = buckets.get(lens) ?? /* @__PURE__ */ new Map();
|
|
2771
|
+
for (const stage of stageOrder) {
|
|
2772
|
+
const records = byStage.get(stage) ?? [];
|
|
2773
|
+
const ranked = rankByRisk2(records);
|
|
2774
|
+
const count = ranked.length;
|
|
2775
|
+
const totalRisk = ranked.reduce(
|
|
2776
|
+
(sum, r) => sum + (derivedNum(r, "risk") ?? 0),
|
|
2777
|
+
0
|
|
2778
|
+
);
|
|
2779
|
+
const avgRisk = count > 0 ? totalRisk / count : 0;
|
|
2780
|
+
maxAvgRisk = Math.max(maxAvgRisk, avgRisk);
|
|
2781
|
+
maxCellCount = Math.max(maxCellCount, count);
|
|
2782
|
+
total += count;
|
|
2783
|
+
cells.push({
|
|
2784
|
+
lens,
|
|
2785
|
+
stage,
|
|
2786
|
+
questionType: questionTypeFilter === "All" ? null : questionTypeFilter,
|
|
2787
|
+
count,
|
|
2788
|
+
totalRisk,
|
|
2789
|
+
avgRisk,
|
|
2790
|
+
assumptions: ranked,
|
|
2791
|
+
heat: 0
|
|
2792
|
+
});
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
const norm2 = maxAvgRisk > 0 ? 1 / maxAvgRisk : 0;
|
|
2796
|
+
for (const cell of cells) {
|
|
2797
|
+
cell.heat = cell.avgRisk * norm2;
|
|
2798
|
+
}
|
|
2799
|
+
const qtSet = /* @__PURE__ */ new Set();
|
|
2800
|
+
for (const a of assumptions) {
|
|
2801
|
+
const qt = questionTypeOf(a);
|
|
2802
|
+
if (qt) qtSet.add(qt);
|
|
2803
|
+
}
|
|
2804
|
+
return {
|
|
2805
|
+
lenses: lensOrder,
|
|
2806
|
+
stages: [...STAGE_ORDER2],
|
|
2807
|
+
questionTypes: [...qtSet].sort(),
|
|
2808
|
+
cells,
|
|
2809
|
+
maxAvgRisk,
|
|
2810
|
+
maxCellCount,
|
|
2811
|
+
total
|
|
2812
|
+
};
|
|
2813
|
+
}
|
|
2814
|
+
function heatCellAt(view, lens, stage) {
|
|
2815
|
+
return view.cells.find((c) => c.lens === lens && c.stage === stage) ?? null;
|
|
2816
|
+
}
|
|
2817
|
+
function heatLevel(heat) {
|
|
2818
|
+
if (heat <= 0) return 0;
|
|
2819
|
+
if (heat < 0.2) return 1;
|
|
2820
|
+
if (heat < 0.4) return 2;
|
|
2821
|
+
if (heat < 0.7) return 3;
|
|
2822
|
+
return 4;
|
|
2823
|
+
}
|
|
2824
|
+
|
|
2683
2825
|
// src/assumptions-surface.tsx
|
|
2684
2826
|
import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2685
2827
|
function AssumptionsSurface({
|
|
@@ -2754,7 +2896,11 @@ function GridPane({
|
|
|
2754
2896
|
readings,
|
|
2755
2897
|
onNavigate
|
|
2756
2898
|
}) {
|
|
2757
|
-
const
|
|
2899
|
+
const [qtFilter, setQtFilter] = useState3("All");
|
|
2900
|
+
const view = useMemo2(
|
|
2901
|
+
() => buildHeatGrid(assumptions, qtFilter),
|
|
2902
|
+
[assumptions, qtFilter]
|
|
2903
|
+
);
|
|
2758
2904
|
const recs = useMemo2(
|
|
2759
2905
|
() => buildRecommendedExperiments(assumptions, experiments),
|
|
2760
2906
|
[assumptions, experiments]
|
|
@@ -2795,40 +2941,67 @@ function GridPane({
|
|
|
2795
2941
|
} else {
|
|
2796
2942
|
onNavigate({
|
|
2797
2943
|
name: "assumptions",
|
|
2798
|
-
lens: cell.lens ===
|
|
2799
|
-
stage: cell.stage ===
|
|
2944
|
+
lens: cell.lens === NO_LENS2 ? void 0 : cell.lens,
|
|
2945
|
+
stage: cell.stage === NO_STAGE2 ? void 0 : cell.stage
|
|
2800
2946
|
});
|
|
2801
2947
|
}
|
|
2802
2948
|
}
|
|
2949
|
+
const qtTabs = QUESTION_TYPE_FILTERS.filter(
|
|
2950
|
+
(qt) => qt === "All" || view.questionTypes.includes(qt)
|
|
2951
|
+
);
|
|
2803
2952
|
return /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
2804
2953
|
/* @__PURE__ */ jsxs5("div", { className: "vos-card vos-stage-grid-card", children: [
|
|
2805
|
-
/* @__PURE__ */ jsx5("div", { className: "vos-
|
|
2954
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-qt-filter-bar", children: qtTabs.map((qt) => /* @__PURE__ */ jsx5(
|
|
2955
|
+
"button",
|
|
2956
|
+
{
|
|
2957
|
+
type: "button",
|
|
2958
|
+
className: `vos-qt-tab ${qtFilter === qt ? "vos-qt-tab-active" : ""}`,
|
|
2959
|
+
onClick: () => setQtFilter(qt),
|
|
2960
|
+
children: qt
|
|
2961
|
+
},
|
|
2962
|
+
qt
|
|
2963
|
+
)) }),
|
|
2964
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-stage-grid-scroll", children: /* @__PURE__ */ jsxs5("table", { className: "vos-stage-grid", role: "grid", "aria-label": "Lens \xD7 Stage heatmap (heat = Risk)", children: [
|
|
2806
2965
|
/* @__PURE__ */ jsx5("thead", { children: /* @__PURE__ */ jsxs5("tr", { children: [
|
|
2807
2966
|
/* @__PURE__ */ jsx5("th", { scope: "col", className: "vos-stage-grid-corner", children: "Lens \u2193 / Stage \u2192" }),
|
|
2808
2967
|
view.stages.map((s) => /* @__PURE__ */ jsxs5("th", { scope: "col", className: "vos-stage-grid-col", children: [
|
|
2809
2968
|
/* @__PURE__ */ jsx5("span", { className: "vos-stage-grid-stagename", children: s }),
|
|
2810
|
-
/* @__PURE__ */ jsx5("span", { className: "vos-stage-grid-stagegloss", children:
|
|
2969
|
+
/* @__PURE__ */ jsx5("span", { className: "vos-stage-grid-stagegloss", children: STAGE_GLOSS2[s] })
|
|
2811
2970
|
] }, s))
|
|
2812
2971
|
] }) }),
|
|
2813
2972
|
/* @__PURE__ */ jsx5("tbody", { children: view.lenses.map((lens) => /* @__PURE__ */ jsxs5("tr", { children: [
|
|
2814
2973
|
/* @__PURE__ */ jsx5("th", { scope: "row", className: "vos-stage-grid-rowhead", children: lens }),
|
|
2815
2974
|
view.stages.map((s) => {
|
|
2816
|
-
const cell =
|
|
2975
|
+
const cell = heatCellAt(view, lens, s);
|
|
2817
2976
|
return /* @__PURE__ */ jsx5("td", { className: "vos-stage-grid-cell", children: /* @__PURE__ */ jsx5(
|
|
2818
2977
|
"button",
|
|
2819
2978
|
{
|
|
2820
2979
|
type: "button",
|
|
2821
|
-
className: `vos-stage-grid-btn vos-heat-${heatLevel(cell.
|
|
2980
|
+
className: `vos-stage-grid-btn vos-heat-${heatLevel(cell.heat)}`,
|
|
2822
2981
|
disabled: cell.count === 0,
|
|
2823
2982
|
onClick: () => cellClick(cell),
|
|
2824
|
-
|
|
2983
|
+
title: `${cell.count} assumptions \xB7 avg Risk ${Math.round(cell.avgRisk)} \xB7 total Risk ${Math.round(cell.totalRisk)}`,
|
|
2984
|
+
"aria-label": `${cell.count} assumptions in ${lens} \xD7 ${s}, average risk ${Math.round(cell.avgRisk)}`,
|
|
2825
2985
|
children: cell.count === 0 ? "\xB7" : cell.count === 1 ? "1" : String(cell.count)
|
|
2826
2986
|
}
|
|
2827
2987
|
) }, s);
|
|
2828
2988
|
})
|
|
2829
2989
|
] }, lens)) })
|
|
2830
2990
|
] }) }),
|
|
2831
|
-
/* @__PURE__ */
|
|
2991
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-heat-legend", children: [
|
|
2992
|
+
/* @__PURE__ */ jsx5("span", { className: "vos-heat-legend-label", children: "Risk heat:" }),
|
|
2993
|
+
/* @__PURE__ */ jsx5("span", { className: "vos-heat-swatch vos-heat-0" }),
|
|
2994
|
+
" low",
|
|
2995
|
+
/* @__PURE__ */ jsx5("span", { className: "vos-heat-swatch vos-heat-1" }),
|
|
2996
|
+
/* @__PURE__ */ jsx5("span", { className: "vos-heat-swatch vos-heat-2" }),
|
|
2997
|
+
/* @__PURE__ */ jsx5("span", { className: "vos-heat-swatch vos-heat-3" }),
|
|
2998
|
+
/* @__PURE__ */ jsx5("span", { className: "vos-heat-swatch vos-heat-4" }),
|
|
2999
|
+
" high"
|
|
3000
|
+
] }),
|
|
3001
|
+
/* @__PURE__ */ jsxs5("p", { className: "vos-hint vos-stage-grid-foot", children: [
|
|
3002
|
+
"Heat = average Risk per cell. Click a cell to drill into its assumptions, ranked by Risk. Filter by Question Type above.",
|
|
3003
|
+
qtFilter !== "All" ? ` Showing ${qtFilter} claims only.` : ""
|
|
3004
|
+
] })
|
|
2832
3005
|
] }),
|
|
2833
3006
|
needsFraming.length > 0 || recs.length > 0 ? /* @__PURE__ */ jsx5(
|
|
2834
3007
|
NextMovesSection,
|
|
@@ -2840,13 +3013,6 @@ function GridPane({
|
|
|
2840
3013
|
) : null
|
|
2841
3014
|
] });
|
|
2842
3015
|
}
|
|
2843
|
-
function heatLevel(density) {
|
|
2844
|
-
if (density <= 0) return 0;
|
|
2845
|
-
if (density < 0.25) return 1;
|
|
2846
|
-
if (density < 0.5) return 2;
|
|
2847
|
-
if (density < 0.75) return 3;
|
|
2848
|
-
return 4;
|
|
2849
|
-
}
|
|
2850
3016
|
function PipelineBoard({
|
|
2851
3017
|
assumptions,
|
|
2852
3018
|
experiments,
|
|
@@ -2858,8 +3024,8 @@ function PipelineBoard({
|
|
|
2858
3024
|
const filtered = useMemo2(() => {
|
|
2859
3025
|
if (filterLens === void 0 && filterStage === void 0) return assumptions;
|
|
2860
3026
|
return assumptions.filter((a) => {
|
|
2861
|
-
const l = String(a.Lens ??
|
|
2862
|
-
const s = String(a.Stage ??
|
|
3027
|
+
const l = String(a.Lens ?? NO_LENS2);
|
|
3028
|
+
const s = String(a.Stage ?? NO_STAGE2);
|
|
2863
3029
|
return (filterLens === void 0 || l === filterLens) && (filterStage === void 0 || s === filterStage);
|
|
2864
3030
|
});
|
|
2865
3031
|
}, [assumptions, filterLens, filterStage]);
|
|
@@ -8507,6 +8673,7 @@ export {
|
|
|
8507
8673
|
NO_STAGE,
|
|
8508
8674
|
NextMoveSurface,
|
|
8509
8675
|
PipelineSurface,
|
|
8676
|
+
QUESTION_TYPE_FILTERS,
|
|
8510
8677
|
REGISTER_GROUPS,
|
|
8511
8678
|
REGISTER_ICON,
|
|
8512
8679
|
REGISTER_LABEL,
|
|
@@ -8538,6 +8705,7 @@ export {
|
|
|
8538
8705
|
backlinkPanels,
|
|
8539
8706
|
bodyPreview,
|
|
8540
8707
|
buildCycles,
|
|
8708
|
+
buildHeatGrid,
|
|
8541
8709
|
buildJourney,
|
|
8542
8710
|
buildPatch,
|
|
8543
8711
|
buildPipeline,
|
|
@@ -8569,6 +8737,8 @@ export {
|
|
|
8569
8737
|
groupRecords,
|
|
8570
8738
|
hasEdits,
|
|
8571
8739
|
headerPills,
|
|
8740
|
+
heatCellAt,
|
|
8741
|
+
heatLevel,
|
|
8572
8742
|
humanInputFields,
|
|
8573
8743
|
interpretSave,
|
|
8574
8744
|
journeyColdState,
|