@validation-os/dashboard 0.15.5 → 0.16.0
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 +9 -1
- package/dist/index.js +402 -220
- package/dist/index.js.map +1 -1
- package/dist/styles.css +81 -3
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -55,6 +55,11 @@ var WORKFLOW_NAV = [
|
|
|
55
55
|
|
|
56
56
|
// src/assumption-detail.tsx
|
|
57
57
|
import { useMemo } from "react";
|
|
58
|
+
import {
|
|
59
|
+
QUESTION_TYPES,
|
|
60
|
+
STAGES
|
|
61
|
+
} from "@validation-os/core";
|
|
62
|
+
import { isNonEvidence, riskThresholdForStage } from "@validation-os/core/derivation";
|
|
58
63
|
|
|
59
64
|
// src/breadcrumb.tsx
|
|
60
65
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -77,7 +82,7 @@ function Breadcrumb({ trail, onNavigate }) {
|
|
|
77
82
|
}
|
|
78
83
|
|
|
79
84
|
// src/evidence-composition.ts
|
|
80
|
-
import { scoreAndDedupe, w0ForRung } from "@validation-os/core/derivation";
|
|
85
|
+
import { RUNG_ANCHOR, scoreAndDedupe, w0ForRung } from "@validation-os/core/derivation";
|
|
81
86
|
|
|
82
87
|
// src/derived-views.ts
|
|
83
88
|
var KILL_ZONE = -50;
|
|
@@ -156,20 +161,13 @@ var ALL_RUNGS = [
|
|
|
156
161
|
"Signed intent",
|
|
157
162
|
"Paying users"
|
|
158
163
|
];
|
|
159
|
-
var RUNG_CAPS = {
|
|
160
|
-
Talk: 6,
|
|
161
|
-
"Desk research": 15,
|
|
162
|
-
"Signed up": 50,
|
|
163
|
-
"Observed usage": 50,
|
|
164
|
-
"Signed intent": 50,
|
|
165
|
-
"Paying users": 50
|
|
166
|
-
};
|
|
167
164
|
function rungsForLens(lens) {
|
|
168
165
|
return LENS_RUNGS[lens] ?? ALL_RUNGS;
|
|
169
166
|
}
|
|
170
167
|
function buildEvidenceComposition(assumption, readings) {
|
|
171
168
|
const id = str(assumption.id) ?? "";
|
|
172
169
|
const lens = str(assumption.Lens) ?? "";
|
|
170
|
+
const questionType = str(assumption["Question Type"]) ?? "Existence";
|
|
173
171
|
const ladder = rungsForLens(lens);
|
|
174
172
|
const inputs = [];
|
|
175
173
|
for (const r of readings) {
|
|
@@ -183,6 +181,7 @@ function buildEvidenceComposition(assumption, readings) {
|
|
|
183
181
|
source: str(r.Source) ?? null,
|
|
184
182
|
rung,
|
|
185
183
|
result,
|
|
184
|
+
questionType,
|
|
186
185
|
representativeness: Number(r.Representativeness) || 1,
|
|
187
186
|
credibility: Number(r.Credibility) || 1,
|
|
188
187
|
date: str(r.Date),
|
|
@@ -208,7 +207,9 @@ function buildEvidenceComposition(assumption, readings) {
|
|
|
208
207
|
return {
|
|
209
208
|
rung,
|
|
210
209
|
contribution: e ? Math.round((e.contribution + Number.EPSILON) * 100) / 100 : 0,
|
|
211
|
-
|
|
210
|
+
// DEV-5890: cap is the rung's High anchor in the assumption's
|
|
211
|
+
// question-type sub-ladder — the ceiling (0 for non-evidence rungs).
|
|
212
|
+
cap: RUNG_ANCHOR[questionType]?.[rung]?.High ?? 0,
|
|
212
213
|
count: e?.count ?? 0
|
|
213
214
|
};
|
|
214
215
|
});
|
|
@@ -220,6 +221,7 @@ function buildEvidenceComposition(assumption, readings) {
|
|
|
220
221
|
}
|
|
221
222
|
function readingContributions(assumption, readings) {
|
|
222
223
|
const id = str(assumption.id) ?? "";
|
|
224
|
+
const questionType = str(assumption["Question Type"]) ?? "Existence";
|
|
223
225
|
const inputs = [];
|
|
224
226
|
for (const r of readings) {
|
|
225
227
|
const belief2 = readingBeliefFor(r, id);
|
|
@@ -232,6 +234,7 @@ function readingContributions(assumption, readings) {
|
|
|
232
234
|
source: str(r.Source) ?? null,
|
|
233
235
|
rung,
|
|
234
236
|
result,
|
|
237
|
+
questionType,
|
|
235
238
|
representativeness: Number(r.Representativeness) || 1,
|
|
236
239
|
credibility: Number(r.Credibility) || 1,
|
|
237
240
|
date: str(r.Date),
|
|
@@ -248,7 +251,7 @@ function readingContributions(assumption, readings) {
|
|
|
248
251
|
return inputs.map((input) => {
|
|
249
252
|
const w = winners.find((x) => x.input.id === input.id);
|
|
250
253
|
const used = winnerIds.has(input.id);
|
|
251
|
-
const strength = w ? w.strength : (input.result === "Validated" ? 1 : input.result === "Invalidated" ? -1 : 0) * (
|
|
254
|
+
const strength = w ? w.strength : (input.result === "Validated" ? 1 : input.result === "Invalidated" ? -1 : 0) * (RUNG_ANCHOR[questionType]?.[input.rung]?.Typical ?? 0);
|
|
252
255
|
const weight = w ? w.weight : 0;
|
|
253
256
|
return {
|
|
254
257
|
id: input.id,
|
|
@@ -261,7 +264,7 @@ function readingContributions(assumption, readings) {
|
|
|
261
264
|
}
|
|
262
265
|
|
|
263
266
|
// src/confidence-explainer.ts
|
|
264
|
-
import { RUNG_ANCHOR, W0_BY_RUNG } from "@validation-os/core/derivation";
|
|
267
|
+
import { RUNG_ANCHOR as RUNG_ANCHOR2, W0_BY_RUNG } from "@validation-os/core/derivation";
|
|
265
268
|
var RUNG_INFO = {
|
|
266
269
|
Talk: {
|
|
267
270
|
label: "Talk",
|
|
@@ -277,7 +280,7 @@ var RUNG_INFO = {
|
|
|
277
280
|
},
|
|
278
281
|
"Observed usage": {
|
|
279
282
|
label: "Observed usage (Consumer)",
|
|
280
|
-
description: "
|
|
283
|
+
description: "Usage sessions, analytics, telemetry, A/B tests. A do-rung \u2014 20 observed users bring this to ~75% of its cap."
|
|
281
284
|
},
|
|
282
285
|
"Signed intent": {
|
|
283
286
|
label: "Signed intent (Commercial)",
|
|
@@ -292,6 +295,7 @@ function buildConfidenceExplainer(assumption, readings) {
|
|
|
292
295
|
const comp = buildEvidenceComposition(assumption, readings);
|
|
293
296
|
const lens = str(assumption.Lens) ?? "";
|
|
294
297
|
const lensRungs = new Set(comp.rungs.map((r) => r.rung));
|
|
298
|
+
const questionType = str(assumption["Question Type"]) ?? "Existence";
|
|
295
299
|
const allRungs = [
|
|
296
300
|
"Talk",
|
|
297
301
|
"Desk research",
|
|
@@ -306,7 +310,7 @@ function buildConfidenceExplainer(assumption, readings) {
|
|
|
306
310
|
return {
|
|
307
311
|
rung,
|
|
308
312
|
w0: W0_BY_RUNG[rung] ?? 100,
|
|
309
|
-
anchors:
|
|
313
|
+
anchors: RUNG_ANCHOR2[questionType]?.[rung] ?? { Low: 0, Typical: 0, High: 0 },
|
|
310
314
|
contribution: e?.contribution ?? 0,
|
|
311
315
|
count: e?.count ?? 0,
|
|
312
316
|
inLens: lensRungs.has(rung),
|
|
@@ -1506,11 +1510,15 @@ function AssumptionDetail({
|
|
|
1506
1510
|
const page = buildRecordPage("assumptions", record, related);
|
|
1507
1511
|
const lens = String(record.Lens ?? "\u2014");
|
|
1508
1512
|
const stage = String(record.Stage ?? "\u2014");
|
|
1513
|
+
const questionType = String(record["Question Type"] ?? "\u2014");
|
|
1509
1514
|
const derived = record.derived ?? {};
|
|
1510
1515
|
const confidence2 = derived.confidence ?? 0;
|
|
1511
1516
|
const risk = derived.risk ?? 0;
|
|
1512
1517
|
const impact = derived.derivedImpact ?? 0;
|
|
1513
1518
|
const framed = derived.completeness ?? 0;
|
|
1519
|
+
const stageKey = STAGES.find((s) => s === stage);
|
|
1520
|
+
const riskThreshold = stageKey ? riskThresholdForStage(stageKey) : null;
|
|
1521
|
+
const clearedThreshold = riskThreshold != null ? risk <= riskThreshold : null;
|
|
1514
1522
|
const nextMove2 = nextMoveFor(record, experiments.records ?? []);
|
|
1515
1523
|
const linkedExperiments = liveExperiments(experiments.records ?? []).filter((e) => {
|
|
1516
1524
|
const ids = Array.isArray(e.barLineAssumptionIds) ? e.barLineAssumptionIds : [];
|
|
@@ -1538,7 +1546,8 @@ function AssumptionDetail({
|
|
|
1538
1546
|
/* @__PURE__ */ jsxs4("div", { className: "vos-detail-head", children: [
|
|
1539
1547
|
/* @__PURE__ */ jsx4("span", { className: "vos-detail-id vos-num", children: assumptionId }),
|
|
1540
1548
|
/* @__PURE__ */ jsx4("span", { className: "vos-detail-tag", children: lens }),
|
|
1541
|
-
/* @__PURE__ */ jsx4("span", { className: "vos-detail-tag", children: stage })
|
|
1549
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-detail-tag", children: stage }),
|
|
1550
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-detail-tag vos-detail-tag-qt", children: questionType })
|
|
1542
1551
|
] }),
|
|
1543
1552
|
/* @__PURE__ */ jsx4("div", { className: "vos-detail-title", children: statement }),
|
|
1544
1553
|
/* @__PURE__ */ jsxs4("div", { className: "vos-card vos-next-move", children: [
|
|
@@ -1551,6 +1560,24 @@ function AssumptionDetail({
|
|
|
1551
1560
|
/* @__PURE__ */ jsx4(ScoreCard, { label: "Confidence", value: formatSigned(confidence2) }),
|
|
1552
1561
|
/* @__PURE__ */ jsx4(ScoreCard, { label: "Framed", value: `${Math.round(framed)}%` })
|
|
1553
1562
|
] }),
|
|
1563
|
+
riskThreshold != null ? /* @__PURE__ */ jsxs4("div", { className: "vos-card vos-threshold-indicator", children: [
|
|
1564
|
+
/* @__PURE__ */ jsx4("div", { className: "vos-threshold-label", children: "Stage threshold" }),
|
|
1565
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-threshold-row", children: [
|
|
1566
|
+
/* @__PURE__ */ jsxs4("span", { className: "vos-num", children: [
|
|
1567
|
+
"Risk ",
|
|
1568
|
+
Math.round(risk)
|
|
1569
|
+
] }),
|
|
1570
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-threshold-bar vos-num", children: riskThreshold }),
|
|
1571
|
+
/* @__PURE__ */ jsx4(
|
|
1572
|
+
"span",
|
|
1573
|
+
{
|
|
1574
|
+
className: `vos-threshold-status ${clearedThreshold ? "vos-threshold-cleared" : "vos-threshold-needs"}`,
|
|
1575
|
+
children: clearedThreshold ? "Cleared for this stage" : "Needs evidence"
|
|
1576
|
+
}
|
|
1577
|
+
)
|
|
1578
|
+
] }),
|
|
1579
|
+
/* @__PURE__ */ jsx4("div", { className: "vos-threshold-hint", children: clearedThreshold ? `Risk at or below the ${stage} threshold \u2014 de-prioritized.` : `Risk above the ${stage} threshold \u2014 testing-priority.` })
|
|
1580
|
+
] }) : null,
|
|
1554
1581
|
/* @__PURE__ */ jsx4(EvidenceCompositionView, { assumption: record, readings: readings.records ?? [] }),
|
|
1555
1582
|
/* @__PURE__ */ jsx4(ConfidenceExplainerView, { assumption: record, readings: readings.records ?? [] }),
|
|
1556
1583
|
statement ? /* @__PURE__ */ jsxs4("div", { className: "vos-card vos-detail-section", children: [
|
|
@@ -1615,6 +1642,7 @@ function AssumptionDetail({
|
|
|
1615
1642
|
EvidenceList,
|
|
1616
1643
|
{
|
|
1617
1644
|
assumptionId,
|
|
1645
|
+
questionType,
|
|
1618
1646
|
readings: readings.records ?? [],
|
|
1619
1647
|
onNavigate
|
|
1620
1648
|
}
|
|
@@ -1623,6 +1651,7 @@ function AssumptionDetail({
|
|
|
1623
1651
|
}
|
|
1624
1652
|
function EvidenceList({
|
|
1625
1653
|
assumptionId,
|
|
1654
|
+
questionType,
|
|
1626
1655
|
readings,
|
|
1627
1656
|
onNavigate
|
|
1628
1657
|
}) {
|
|
@@ -1633,7 +1662,81 @@ function EvidenceList({
|
|
|
1633
1662
|
const rows = readingContributions(rec, readings);
|
|
1634
1663
|
return new Map(rows.map((r) => [r.id, r]));
|
|
1635
1664
|
}, [readings, assumptionId]);
|
|
1665
|
+
const qt = QUESTION_TYPES.find((q) => q === questionType);
|
|
1666
|
+
const { probative, flagged } = useMemo(() => {
|
|
1667
|
+
const prob = [];
|
|
1668
|
+
const flag = [];
|
|
1669
|
+
for (const r of linkedReadings) {
|
|
1670
|
+
const rung = String(r.Rung ?? "");
|
|
1671
|
+
if (qt && RUNGS.includes(rung) && isNonEvidence(qt, rung)) {
|
|
1672
|
+
flag.push(r);
|
|
1673
|
+
} else {
|
|
1674
|
+
prob.push(r);
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
return { probative: prob, flagged: flag };
|
|
1678
|
+
}, [linkedReadings, qt]);
|
|
1636
1679
|
if (linkedReadings.length === 0) return null;
|
|
1680
|
+
const renderRow = (r, flagged2) => {
|
|
1681
|
+
const id = String(r.id ?? "");
|
|
1682
|
+
const belief2 = readingBeliefFor(r, assumptionId);
|
|
1683
|
+
if (!belief2) return null;
|
|
1684
|
+
const result = String(belief2.Result ?? "Inconclusive");
|
|
1685
|
+
const justification = String(belief2["Grading justification"] ?? "");
|
|
1686
|
+
const excerpt = typeof belief2.excerpt === "string" && belief2.excerpt !== "" ? belief2.excerpt : snippetFromBody2(String(r.body ?? ""), assumptionId);
|
|
1687
|
+
const rung = String(r.Rung ?? "");
|
|
1688
|
+
const source = String(r.Source ?? "");
|
|
1689
|
+
const expId = r.experimentId ? String(r.experimentId) : null;
|
|
1690
|
+
const c = contribById.get(id);
|
|
1691
|
+
return /* @__PURE__ */ jsxs4(
|
|
1692
|
+
"button",
|
|
1693
|
+
{
|
|
1694
|
+
type: "button",
|
|
1695
|
+
className: `vos-evidence-row vos-verdict-${verdictTone(result)}${flagged2 ? " vos-evidence-row-flagged" : ""}`,
|
|
1696
|
+
onClick: () => onNavigate({ name: "reading", id }),
|
|
1697
|
+
children: [
|
|
1698
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-evidence-row-head", children: [
|
|
1699
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-evidence-date vos-num", children: String(r.Date ?? "") }),
|
|
1700
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-evidence-title", children: String(r.Title ?? id) }),
|
|
1701
|
+
/* @__PURE__ */ jsx4("span", { className: `vos-pill vos-pill-${verdictTone(result)}`, children: result }),
|
|
1702
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-rung-tag", children: rung }),
|
|
1703
|
+
flagged2 ? /* @__PURE__ */ jsx4("span", { className: "vos-evidence-flag", title: "This rung is non-evidence for this assumption's question type \u2014 reclassify the assumption or drop the reading.", children: "non-evidence" }) : null,
|
|
1704
|
+
c && c.used ? /* @__PURE__ */ jsxs4("span", { className: `vos-evidence-score vos-num vos-text-${contributionTone(c.contribution)}`, children: [
|
|
1705
|
+
formatSigned(c.contribution),
|
|
1706
|
+
" confidence"
|
|
1707
|
+
] }) : null
|
|
1708
|
+
] }),
|
|
1709
|
+
excerpt ? /* @__PURE__ */ jsxs4("div", { className: "vos-evidence-excerpt", children: [
|
|
1710
|
+
"\u201C",
|
|
1711
|
+
excerpt,
|
|
1712
|
+
"\u201D"
|
|
1713
|
+
] }) : null,
|
|
1714
|
+
justification ? /* @__PURE__ */ jsxs4("div", { className: `vos-belief-rationale vos-verdict-border-${verdictTone(result)}`, children: [
|
|
1715
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-belief-rationale-label", children: "grading rationale:" }),
|
|
1716
|
+
justification
|
|
1717
|
+
] }) : null,
|
|
1718
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-evidence-source", children: [
|
|
1719
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-evidence-source-link", title: source, children: source }),
|
|
1720
|
+
expId ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1721
|
+
" \xB7 from experiment ",
|
|
1722
|
+
/* @__PURE__ */ jsx4(
|
|
1723
|
+
"span",
|
|
1724
|
+
{
|
|
1725
|
+
className: "vos-link",
|
|
1726
|
+
onClick: (ev) => {
|
|
1727
|
+
ev.stopPropagation();
|
|
1728
|
+
onNavigate({ name: "experiment", id: expId });
|
|
1729
|
+
},
|
|
1730
|
+
children: expId
|
|
1731
|
+
}
|
|
1732
|
+
)
|
|
1733
|
+
] }) : null
|
|
1734
|
+
] })
|
|
1735
|
+
]
|
|
1736
|
+
},
|
|
1737
|
+
id
|
|
1738
|
+
);
|
|
1739
|
+
};
|
|
1637
1740
|
return /* @__PURE__ */ jsxs4("div", { className: "vos-card vos-detail-section", children: [
|
|
1638
1741
|
/* @__PURE__ */ jsxs4("div", { className: "vos-detail-section-label", children: [
|
|
1639
1742
|
"Evidence \xB7 ",
|
|
@@ -1642,65 +1745,20 @@ function EvidenceList({
|
|
|
1642
1745
|
linkedReadings.length === 1 ? "" : "s",
|
|
1643
1746
|
" of evidence"
|
|
1644
1747
|
] }),
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
type: "button",
|
|
1660
|
-
className: `vos-evidence-row vos-verdict-${verdictTone(result)}`,
|
|
1661
|
-
onClick: () => onNavigate({ name: "reading", id }),
|
|
1662
|
-
children: [
|
|
1663
|
-
/* @__PURE__ */ jsxs4("div", { className: "vos-evidence-row-head", children: [
|
|
1664
|
-
/* @__PURE__ */ jsx4("span", { className: "vos-evidence-date vos-num", children: String(r.Date ?? "") }),
|
|
1665
|
-
/* @__PURE__ */ jsx4("span", { className: "vos-evidence-title", children: String(r.Title ?? id) }),
|
|
1666
|
-
/* @__PURE__ */ jsx4("span", { className: `vos-pill vos-pill-${verdictTone(result)}`, children: result }),
|
|
1667
|
-
/* @__PURE__ */ jsx4("span", { className: "vos-rung-tag", children: rung }),
|
|
1668
|
-
c && c.used ? /* @__PURE__ */ jsxs4("span", { className: `vos-evidence-score vos-num vos-text-${contributionTone(c.contribution)}`, children: [
|
|
1669
|
-
formatSigned(c.contribution),
|
|
1670
|
-
" confidence"
|
|
1671
|
-
] }) : null
|
|
1672
|
-
] }),
|
|
1673
|
-
excerpt ? /* @__PURE__ */ jsxs4("div", { className: "vos-evidence-excerpt", children: [
|
|
1674
|
-
"\u201C",
|
|
1675
|
-
excerpt,
|
|
1676
|
-
"\u201D"
|
|
1677
|
-
] }) : null,
|
|
1678
|
-
justification ? /* @__PURE__ */ jsxs4("div", { className: `vos-belief-rationale vos-verdict-border-${verdictTone(result)}`, children: [
|
|
1679
|
-
/* @__PURE__ */ jsx4("span", { className: "vos-belief-rationale-label", children: "grading rationale:" }),
|
|
1680
|
-
justification
|
|
1681
|
-
] }) : null,
|
|
1682
|
-
/* @__PURE__ */ jsxs4("div", { className: "vos-evidence-source", children: [
|
|
1683
|
-
/* @__PURE__ */ jsx4("span", { className: "vos-evidence-source-link", title: source, children: source }),
|
|
1684
|
-
expId ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1685
|
-
" \xB7 from experiment ",
|
|
1686
|
-
/* @__PURE__ */ jsx4(
|
|
1687
|
-
"span",
|
|
1688
|
-
{
|
|
1689
|
-
className: "vos-link",
|
|
1690
|
-
onClick: (ev) => {
|
|
1691
|
-
ev.stopPropagation();
|
|
1692
|
-
onNavigate({ name: "experiment", id: expId });
|
|
1693
|
-
},
|
|
1694
|
-
children: expId
|
|
1695
|
-
}
|
|
1696
|
-
)
|
|
1697
|
-
] }) : null
|
|
1698
|
-
] })
|
|
1699
|
-
]
|
|
1700
|
-
},
|
|
1701
|
-
id
|
|
1702
|
-
);
|
|
1703
|
-
})
|
|
1748
|
+
probative.length > 0 ? /* @__PURE__ */ jsxs4("div", { className: "vos-evidence-group vos-evidence-group-probative", children: [
|
|
1749
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-evidence-group-label", children: [
|
|
1750
|
+
"Probative evidence \xB7 ",
|
|
1751
|
+
probative.length
|
|
1752
|
+
] }),
|
|
1753
|
+
probative.map((r) => renderRow(r, false))
|
|
1754
|
+
] }) : null,
|
|
1755
|
+
flagged.length > 0 ? /* @__PURE__ */ jsxs4("div", { className: "vos-evidence-group vos-evidence-group-flagged", children: [
|
|
1756
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-evidence-group-label", children: [
|
|
1757
|
+
"Flagged as non-evidence for this question type \xB7 ",
|
|
1758
|
+
flagged.length
|
|
1759
|
+
] }),
|
|
1760
|
+
flagged.map((r) => renderRow(r, true))
|
|
1761
|
+
] }) : null
|
|
1704
1762
|
] });
|
|
1705
1763
|
}
|
|
1706
1764
|
function contributionTone(v) {
|
|
@@ -1708,6 +1766,14 @@ function contributionTone(v) {
|
|
|
1708
1766
|
if (v < 0) return "crit";
|
|
1709
1767
|
return "neutral";
|
|
1710
1768
|
}
|
|
1769
|
+
var RUNGS = [
|
|
1770
|
+
"Talk",
|
|
1771
|
+
"Desk research",
|
|
1772
|
+
"Signed up",
|
|
1773
|
+
"Observed usage",
|
|
1774
|
+
"Signed intent",
|
|
1775
|
+
"Paying users"
|
|
1776
|
+
];
|
|
1711
1777
|
function snippetFromBody2(body, cue) {
|
|
1712
1778
|
if (!body) return "";
|
|
1713
1779
|
const quoteMatch = body.match(/## Quote\n+([\s\S]*?)(?=\n## |\n##$|$)/i);
|
|
@@ -1917,7 +1983,7 @@ function EvidenceCompositionView({
|
|
|
1917
1983
|
}
|
|
1918
1984
|
|
|
1919
1985
|
// src/assumptions-surface.tsx
|
|
1920
|
-
import { useMemo as useMemo2 } from "react";
|
|
1986
|
+
import { useMemo as useMemo2, useState as useState3 } from "react";
|
|
1921
1987
|
|
|
1922
1988
|
// src/next-move.ts
|
|
1923
1989
|
import {
|
|
@@ -2068,6 +2134,114 @@ import {
|
|
|
2068
2134
|
assumptionCompleteness,
|
|
2069
2135
|
readingBeliefInputs
|
|
2070
2136
|
} from "@validation-os/core";
|
|
2137
|
+
import { riskThresholdForStage as riskThresholdForStage2 } from "@validation-os/core/derivation";
|
|
2138
|
+
|
|
2139
|
+
// src/stage-grid-model.ts
|
|
2140
|
+
var STAGE_ORDER = [
|
|
2141
|
+
"Discovery",
|
|
2142
|
+
"Validation",
|
|
2143
|
+
"Scale",
|
|
2144
|
+
"Maturity"
|
|
2145
|
+
];
|
|
2146
|
+
var STAGE_GLOSS = {
|
|
2147
|
+
Discovery: "Problem-solution fit \u2014 will they engage, care, disclose?",
|
|
2148
|
+
Validation: "Product-market fit \u2014 will they pay, sign, stay?",
|
|
2149
|
+
Scale: "Growth \u2014 can we acquire efficiently, does CAC<LTV hold at volume?",
|
|
2150
|
+
Maturity: "Defense \u2014 will incumbents respond, will regulators accept?"
|
|
2151
|
+
};
|
|
2152
|
+
var NO_LENS = "\u2014";
|
|
2153
|
+
var NO_STAGE = "\u2014";
|
|
2154
|
+
function stageOf(record) {
|
|
2155
|
+
const v = str(record.Stage);
|
|
2156
|
+
if (!v) return null;
|
|
2157
|
+
return STAGE_ORDER.includes(v) ? v : null;
|
|
2158
|
+
}
|
|
2159
|
+
function rankByRisk(records) {
|
|
2160
|
+
return [...records].map((r) => ({ r, risk: derivedNum(r, "risk") ?? 0 })).sort(
|
|
2161
|
+
(a, b) => a.risk !== b.risk ? b.risk - a.risk : a.r.id.localeCompare(b.r.id)
|
|
2162
|
+
).map((x) => x.r);
|
|
2163
|
+
}
|
|
2164
|
+
function buildStageGrid(assumptions) {
|
|
2165
|
+
const lensOrder = [];
|
|
2166
|
+
const seenLens = /* @__PURE__ */ new Set();
|
|
2167
|
+
const pushLens = (lens) => {
|
|
2168
|
+
if (!seenLens.has(lens)) {
|
|
2169
|
+
seenLens.add(lens);
|
|
2170
|
+
lensOrder.push(lens);
|
|
2171
|
+
}
|
|
2172
|
+
};
|
|
2173
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
2174
|
+
const ensureLens = (lens) => {
|
|
2175
|
+
let m = buckets.get(lens);
|
|
2176
|
+
if (!m) {
|
|
2177
|
+
m = /* @__PURE__ */ new Map();
|
|
2178
|
+
buckets.set(lens, m);
|
|
2179
|
+
}
|
|
2180
|
+
return m;
|
|
2181
|
+
};
|
|
2182
|
+
let hasNoLens = false;
|
|
2183
|
+
let hasNoStage = false;
|
|
2184
|
+
for (const a of assumptions) {
|
|
2185
|
+
const lens = str(a.Lens) ?? NO_LENS;
|
|
2186
|
+
const stage = stageOf(a) ?? NO_STAGE;
|
|
2187
|
+
if (lens === NO_LENS) hasNoLens = true;
|
|
2188
|
+
if (stage === NO_STAGE) hasNoStage = true;
|
|
2189
|
+
pushLens(lens);
|
|
2190
|
+
const byStage = ensureLens(lens);
|
|
2191
|
+
let list = byStage.get(stage);
|
|
2192
|
+
if (!list) {
|
|
2193
|
+
list = [];
|
|
2194
|
+
byStage.set(stage, list);
|
|
2195
|
+
}
|
|
2196
|
+
list.push(a);
|
|
2197
|
+
}
|
|
2198
|
+
if (hasNoLens) {
|
|
2199
|
+
lensOrder.splice(lensOrder.indexOf(NO_LENS), 1);
|
|
2200
|
+
lensOrder.push(NO_LENS);
|
|
2201
|
+
} else {
|
|
2202
|
+
const idx = lensOrder.indexOf(NO_LENS);
|
|
2203
|
+
if (idx >= 0) lensOrder.splice(idx, 1);
|
|
2204
|
+
}
|
|
2205
|
+
const stageOrder = [...STAGE_ORDER];
|
|
2206
|
+
if (hasNoStage) stageOrder.push(NO_STAGE);
|
|
2207
|
+
const cells = [];
|
|
2208
|
+
let maxCellCount = 0;
|
|
2209
|
+
let total = 0;
|
|
2210
|
+
for (const lens of lensOrder) {
|
|
2211
|
+
const byStage = buckets.get(lens) ?? /* @__PURE__ */ new Map();
|
|
2212
|
+
for (const stage of stageOrder) {
|
|
2213
|
+
const records = byStage.get(stage) ?? [];
|
|
2214
|
+
const ranked = rankByRisk(records);
|
|
2215
|
+
const count = ranked.length;
|
|
2216
|
+
maxCellCount = Math.max(maxCellCount, count);
|
|
2217
|
+
total += count;
|
|
2218
|
+
cells.push({
|
|
2219
|
+
lens,
|
|
2220
|
+
stage,
|
|
2221
|
+
count,
|
|
2222
|
+
assumptions: ranked,
|
|
2223
|
+
// density filled in a second pass once maxCellCount is known.
|
|
2224
|
+
density: 0
|
|
2225
|
+
});
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
const norm2 = maxCellCount > 0 ? 1 / maxCellCount : 0;
|
|
2229
|
+
for (const cell of cells) {
|
|
2230
|
+
cell.density = cell.count * norm2;
|
|
2231
|
+
}
|
|
2232
|
+
return {
|
|
2233
|
+
lenses: lensOrder,
|
|
2234
|
+
stages: [...STAGE_ORDER],
|
|
2235
|
+
cells,
|
|
2236
|
+
maxCellCount,
|
|
2237
|
+
total
|
|
2238
|
+
};
|
|
2239
|
+
}
|
|
2240
|
+
function cellAt(view, lens, stage) {
|
|
2241
|
+
return view.cells.find((c) => c.lens === lens && c.stage === stage) ?? null;
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
// src/pipeline.ts
|
|
2071
2245
|
import {
|
|
2072
2246
|
beliefRisk,
|
|
2073
2247
|
beliefTestMeters,
|
|
@@ -2156,6 +2330,10 @@ function buildPipeline(assumptions, experiments) {
|
|
|
2156
2330
|
};
|
|
2157
2331
|
const framed = assumptionCompleteness(a);
|
|
2158
2332
|
const stage = deriveBeliefStage({ framed, confidence: d.confidence, test });
|
|
2333
|
+
const questionType = str3(a["Question Type"]);
|
|
2334
|
+
const stageName = str3(a.Stage);
|
|
2335
|
+
const stageKey = stageName && STAGE_ORDER.includes(stageName) ? stageName : null;
|
|
2336
|
+
const riskThreshold = stageKey ? riskThresholdForStage2(stageKey) : null;
|
|
2159
2337
|
rows.push({
|
|
2160
2338
|
id: a.id,
|
|
2161
2339
|
statement: str3(a.Title),
|
|
@@ -2168,7 +2346,11 @@ function buildPipeline(assumptions, experiments) {
|
|
|
2168
2346
|
framed: stage.framed,
|
|
2169
2347
|
planned: stage.planned,
|
|
2170
2348
|
tested: stage.tested,
|
|
2171
|
-
nextMove: nextMove(stage.stage, stage.killZone)
|
|
2349
|
+
nextMove: nextMove(stage.stage, stage.killZone),
|
|
2350
|
+
questionType,
|
|
2351
|
+
stage: stageName,
|
|
2352
|
+
riskThreshold,
|
|
2353
|
+
clearedThreshold: riskThreshold != null ? d.risk <= riskThreshold : null
|
|
2172
2354
|
});
|
|
2173
2355
|
}
|
|
2174
2356
|
rows.sort(
|
|
@@ -2188,8 +2370,13 @@ function weekOverWeekDelta(assumptions, readings, now) {
|
|
|
2188
2370
|
});
|
|
2189
2371
|
if (!anyDated) return null;
|
|
2190
2372
|
const cutoff = now.getTime() - 7 * 24 * 60 * 60 * 1e3;
|
|
2373
|
+
const assumptionsById = new Map(
|
|
2374
|
+
assumptions.map((a) => [String(a.id), a])
|
|
2375
|
+
);
|
|
2191
2376
|
const byAssumption = /* @__PURE__ */ new Map();
|
|
2192
|
-
for (const input of readings.flatMap(
|
|
2377
|
+
for (const input of readings.flatMap(
|
|
2378
|
+
(r) => readingBeliefInputs(r, assumptionsById)
|
|
2379
|
+
)) {
|
|
2193
2380
|
if (!input.assumptionId) continue;
|
|
2194
2381
|
const arr = byAssumption.get(input.assumptionId);
|
|
2195
2382
|
if (arr) arr.push(input);
|
|
@@ -2408,111 +2595,6 @@ function stageMeters(input) {
|
|
|
2408
2595
|
];
|
|
2409
2596
|
}
|
|
2410
2597
|
|
|
2411
|
-
// src/stage-grid-model.ts
|
|
2412
|
-
var STAGE_ORDER = [
|
|
2413
|
-
"Discovery",
|
|
2414
|
-
"Validation",
|
|
2415
|
-
"Scale",
|
|
2416
|
-
"Maturity"
|
|
2417
|
-
];
|
|
2418
|
-
var STAGE_GLOSS = {
|
|
2419
|
-
Discovery: "Problem-solution fit \u2014 will they engage, care, disclose?",
|
|
2420
|
-
Validation: "Product-market fit \u2014 will they pay, sign, stay?",
|
|
2421
|
-
Scale: "Growth \u2014 can we acquire efficiently, does CAC<LTV hold at volume?",
|
|
2422
|
-
Maturity: "Defense \u2014 will incumbents respond, will regulators accept?"
|
|
2423
|
-
};
|
|
2424
|
-
var NO_LENS = "\u2014";
|
|
2425
|
-
var NO_STAGE = "\u2014";
|
|
2426
|
-
function stageOf(record) {
|
|
2427
|
-
const v = str(record.Stage);
|
|
2428
|
-
if (!v) return null;
|
|
2429
|
-
return STAGE_ORDER.includes(v) ? v : null;
|
|
2430
|
-
}
|
|
2431
|
-
function rankByRisk(records) {
|
|
2432
|
-
return [...records].map((r) => ({ r, risk: derivedNum(r, "risk") ?? 0 })).sort(
|
|
2433
|
-
(a, b) => a.risk !== b.risk ? b.risk - a.risk : a.r.id.localeCompare(b.r.id)
|
|
2434
|
-
).map((x) => x.r);
|
|
2435
|
-
}
|
|
2436
|
-
function buildStageGrid(assumptions) {
|
|
2437
|
-
const lensOrder = [];
|
|
2438
|
-
const seenLens = /* @__PURE__ */ new Set();
|
|
2439
|
-
const pushLens = (lens) => {
|
|
2440
|
-
if (!seenLens.has(lens)) {
|
|
2441
|
-
seenLens.add(lens);
|
|
2442
|
-
lensOrder.push(lens);
|
|
2443
|
-
}
|
|
2444
|
-
};
|
|
2445
|
-
const buckets = /* @__PURE__ */ new Map();
|
|
2446
|
-
const ensureLens = (lens) => {
|
|
2447
|
-
let m = buckets.get(lens);
|
|
2448
|
-
if (!m) {
|
|
2449
|
-
m = /* @__PURE__ */ new Map();
|
|
2450
|
-
buckets.set(lens, m);
|
|
2451
|
-
}
|
|
2452
|
-
return m;
|
|
2453
|
-
};
|
|
2454
|
-
let hasNoLens = false;
|
|
2455
|
-
let hasNoStage = false;
|
|
2456
|
-
for (const a of assumptions) {
|
|
2457
|
-
const lens = str(a.Lens) ?? NO_LENS;
|
|
2458
|
-
const stage = stageOf(a) ?? NO_STAGE;
|
|
2459
|
-
if (lens === NO_LENS) hasNoLens = true;
|
|
2460
|
-
if (stage === NO_STAGE) hasNoStage = true;
|
|
2461
|
-
pushLens(lens);
|
|
2462
|
-
const byStage = ensureLens(lens);
|
|
2463
|
-
let list = byStage.get(stage);
|
|
2464
|
-
if (!list) {
|
|
2465
|
-
list = [];
|
|
2466
|
-
byStage.set(stage, list);
|
|
2467
|
-
}
|
|
2468
|
-
list.push(a);
|
|
2469
|
-
}
|
|
2470
|
-
if (hasNoLens) {
|
|
2471
|
-
lensOrder.splice(lensOrder.indexOf(NO_LENS), 1);
|
|
2472
|
-
lensOrder.push(NO_LENS);
|
|
2473
|
-
} else {
|
|
2474
|
-
const idx = lensOrder.indexOf(NO_LENS);
|
|
2475
|
-
if (idx >= 0) lensOrder.splice(idx, 1);
|
|
2476
|
-
}
|
|
2477
|
-
const stageOrder = [...STAGE_ORDER];
|
|
2478
|
-
if (hasNoStage) stageOrder.push(NO_STAGE);
|
|
2479
|
-
const cells = [];
|
|
2480
|
-
let maxCellCount = 0;
|
|
2481
|
-
let total = 0;
|
|
2482
|
-
for (const lens of lensOrder) {
|
|
2483
|
-
const byStage = buckets.get(lens) ?? /* @__PURE__ */ new Map();
|
|
2484
|
-
for (const stage of stageOrder) {
|
|
2485
|
-
const records = byStage.get(stage) ?? [];
|
|
2486
|
-
const ranked = rankByRisk(records);
|
|
2487
|
-
const count = ranked.length;
|
|
2488
|
-
maxCellCount = Math.max(maxCellCount, count);
|
|
2489
|
-
total += count;
|
|
2490
|
-
cells.push({
|
|
2491
|
-
lens,
|
|
2492
|
-
stage,
|
|
2493
|
-
count,
|
|
2494
|
-
assumptions: ranked,
|
|
2495
|
-
// density filled in a second pass once maxCellCount is known.
|
|
2496
|
-
density: 0
|
|
2497
|
-
});
|
|
2498
|
-
}
|
|
2499
|
-
}
|
|
2500
|
-
const norm2 = maxCellCount > 0 ? 1 / maxCellCount : 0;
|
|
2501
|
-
for (const cell of cells) {
|
|
2502
|
-
cell.density = cell.count * norm2;
|
|
2503
|
-
}
|
|
2504
|
-
return {
|
|
2505
|
-
lenses: lensOrder,
|
|
2506
|
-
stages: [...STAGE_ORDER],
|
|
2507
|
-
cells,
|
|
2508
|
-
maxCellCount,
|
|
2509
|
-
total
|
|
2510
|
-
};
|
|
2511
|
-
}
|
|
2512
|
-
function cellAt(view, lens, stage) {
|
|
2513
|
-
return view.cells.find((c) => c.lens === lens && c.stage === stage) ?? null;
|
|
2514
|
-
}
|
|
2515
|
-
|
|
2516
2598
|
// src/assumptions-surface.tsx
|
|
2517
2599
|
import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2518
2600
|
function AssumptionsSurface({
|
|
@@ -2779,7 +2861,17 @@ function PipelineRowView({ row, onOpen }) {
|
|
|
2779
2861
|
/* @__PURE__ */ jsxs5("span", { className: "vos-num", children: [
|
|
2780
2862
|
"conf ",
|
|
2781
2863
|
formatSigned(row.confidence)
|
|
2782
|
-
] })
|
|
2864
|
+
] }),
|
|
2865
|
+
row.questionType ? /* @__PURE__ */ jsx5("span", { className: "vos-pipe-tag vos-pipe-tag-qt", children: row.questionType }) : null,
|
|
2866
|
+
row.stage ? /* @__PURE__ */ jsx5("span", { className: "vos-pipe-tag vos-pipe-tag-stage", children: row.stage }) : null,
|
|
2867
|
+
row.riskThreshold != null ? /* @__PURE__ */ jsx5(
|
|
2868
|
+
"span",
|
|
2869
|
+
{
|
|
2870
|
+
className: `vos-pipe-tag vos-pipe-tag-thresh ${row.clearedThreshold ? "vos-pipe-tag-cleared" : "vos-pipe-tag-needs"}`,
|
|
2871
|
+
title: `Stage threshold ${row.riskThreshold} \u2014 ${row.clearedThreshold ? "cleared for this stage" : "needs evidence"}`,
|
|
2872
|
+
children: row.clearedThreshold ? "cleared" : `bar ${row.riskThreshold}`
|
|
2873
|
+
}
|
|
2874
|
+
) : null
|
|
2783
2875
|
] })
|
|
2784
2876
|
] }),
|
|
2785
2877
|
/* @__PURE__ */ jsx5("div", { className: "vos-pipe-prog-2seg", "aria-label": "Evidence progress (Framed + Known)", children: /* @__PURE__ */ jsx5(Meter2Seg, { framed: row.framed, knownPct, knownSign }) }),
|
|
@@ -2808,6 +2900,7 @@ function NextMovesSection({
|
|
|
2808
2900
|
recs,
|
|
2809
2901
|
onOpenAssumption
|
|
2810
2902
|
}) {
|
|
2903
|
+
const [openRec, setOpenRec] = useState3(null);
|
|
2811
2904
|
return /* @__PURE__ */ jsxs5("div", { className: "vos-next-moves", children: [
|
|
2812
2905
|
/* @__PURE__ */ jsx5("div", { className: "vos-next-moves-head", children: "Next moves" }),
|
|
2813
2906
|
/* @__PURE__ */ jsxs5("div", { className: "vos-next-moves-cols", children: [
|
|
@@ -2847,38 +2940,118 @@ function NextMovesSection({
|
|
|
2847
2940
|
] }),
|
|
2848
2941
|
/* @__PURE__ */ jsxs5("div", { className: "vos-card vos-next-moves-col", children: [
|
|
2849
2942
|
/* @__PURE__ */ jsx5("div", { className: "vos-next-moves-col-label", children: "Proposed experiments \xB7 one per lens" }),
|
|
2850
|
-
recs.length === 0 ? /* @__PURE__ */ jsx5("div", { className: "vos-muted vos-next-moves-empty", children: "Every risk has a live test." }) : recs.map((rec) => /* @__PURE__ */ jsxs5(
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2943
|
+
recs.length === 0 ? /* @__PURE__ */ jsx5("div", { className: "vos-muted vos-next-moves-empty", children: "Every risk has a live test." }) : recs.map((rec) => /* @__PURE__ */ jsxs5(
|
|
2944
|
+
"button",
|
|
2945
|
+
{
|
|
2946
|
+
type: "button",
|
|
2947
|
+
className: "vos-next-moves-rec vos-next-moves-rec-compact",
|
|
2948
|
+
onClick: () => setOpenRec(rec),
|
|
2949
|
+
children: [
|
|
2950
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-next-moves-rec-head", children: [
|
|
2951
|
+
/* @__PURE__ */ jsx5(
|
|
2952
|
+
"span",
|
|
2953
|
+
{
|
|
2954
|
+
className: "vos-next-moves-lens",
|
|
2955
|
+
style: { background: `${rec.lensColour}20`, color: rec.lensColour, borderColor: `${rec.lensColour}40` },
|
|
2956
|
+
children: rec.lens
|
|
2957
|
+
}
|
|
2958
|
+
),
|
|
2959
|
+
/* @__PURE__ */ jsx5("span", { className: "vos-next-moves-rec-type", children: rec.type }),
|
|
2960
|
+
/* @__PURE__ */ jsxs5("span", { className: "vos-next-moves-rec-risk vos-num", children: [
|
|
2961
|
+
Math.round(rec.maxRisk),
|
|
2962
|
+
" risk"
|
|
2963
|
+
] })
|
|
2964
|
+
] }),
|
|
2965
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-next-moves-rec-title", children: rec.title }),
|
|
2966
|
+
/* @__PURE__ */ jsx5("span", { className: "vos-next-moves-rec-open", "aria-hidden": "true", children: "\u2192" })
|
|
2967
|
+
]
|
|
2968
|
+
},
|
|
2969
|
+
rec.id
|
|
2970
|
+
))
|
|
2971
|
+
] })
|
|
2972
|
+
] }),
|
|
2973
|
+
openRec ? /* @__PURE__ */ jsx5(
|
|
2974
|
+
RecommendedExperimentDrawer,
|
|
2975
|
+
{
|
|
2976
|
+
rec: openRec,
|
|
2977
|
+
onOpenAssumption,
|
|
2978
|
+
onClose: () => setOpenRec(null)
|
|
2979
|
+
}
|
|
2980
|
+
) : null
|
|
2981
|
+
] });
|
|
2982
|
+
}
|
|
2983
|
+
function RecommendedExperimentDrawer({
|
|
2984
|
+
rec,
|
|
2985
|
+
onOpenAssumption,
|
|
2986
|
+
onClose
|
|
2987
|
+
}) {
|
|
2988
|
+
return /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
2989
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-scrim", onClick: onClose, "aria-hidden": "true" }),
|
|
2990
|
+
/* @__PURE__ */ jsxs5(
|
|
2991
|
+
"aside",
|
|
2992
|
+
{
|
|
2993
|
+
className: "vos-drawer vos-rec-drawer",
|
|
2994
|
+
role: "dialog",
|
|
2995
|
+
"aria-modal": "true",
|
|
2996
|
+
"aria-label": `Proposed experiment: ${rec.title}`,
|
|
2997
|
+
children: [
|
|
2998
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-drawer-header", children: [
|
|
2999
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
3000
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-drawer-eyebrow", children: "Proposed experiment" }),
|
|
3001
|
+
/* @__PURE__ */ jsx5("h2", { className: "vos-drawer-title", children: rec.title })
|
|
3002
|
+
] }),
|
|
3003
|
+
/* @__PURE__ */ jsxs5("div", { style: { marginLeft: "auto", display: "flex", gap: 8, alignItems: "center" }, children: [
|
|
3004
|
+
/* @__PURE__ */ jsx5(
|
|
3005
|
+
"span",
|
|
3006
|
+
{
|
|
3007
|
+
className: "vos-next-moves-lens",
|
|
3008
|
+
style: { background: `${rec.lensColour}20`, color: rec.lensColour, borderColor: `${rec.lensColour}40` },
|
|
3009
|
+
children: rec.lens
|
|
3010
|
+
}
|
|
3011
|
+
),
|
|
3012
|
+
/* @__PURE__ */ jsx5("span", { className: "vos-next-moves-rec-type", children: rec.type }),
|
|
3013
|
+
/* @__PURE__ */ jsxs5("span", { className: "vos-next-moves-rec-risk vos-num", children: [
|
|
3014
|
+
Math.round(rec.maxRisk),
|
|
3015
|
+
" risk"
|
|
3016
|
+
] }),
|
|
3017
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: "vos-btn vos-btn-sm", onClick: onClose, "aria-label": "Close", children: "\u2715" })
|
|
2864
3018
|
] })
|
|
2865
3019
|
] }),
|
|
2866
|
-
/* @__PURE__ */
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
className: "vos-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
3020
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-drawer-body", children: [
|
|
3021
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-rec-drawer-section", children: [
|
|
3022
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-drawer-eyebrow", children: "Why this test" }),
|
|
3023
|
+
/* @__PURE__ */ jsx5("p", { className: "vos-rec-drawer-rationale", children: rec.rationale })
|
|
3024
|
+
] }),
|
|
3025
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-rec-drawer-section", children: [
|
|
3026
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-drawer-eyebrow", children: "Bar" }),
|
|
3027
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-rec-drawer-bar", children: [
|
|
3028
|
+
/* @__PURE__ */ jsx5("strong", { children: "Right if:" }),
|
|
3029
|
+
" ",
|
|
3030
|
+
rec.barPreview
|
|
3031
|
+
] })
|
|
3032
|
+
] }),
|
|
3033
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-rec-drawer-section", children: [
|
|
3034
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-drawer-eyebrow", children: "Assumptions" }),
|
|
3035
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-next-moves-rec-chips", children: rec.assumptionIds.map((id) => /* @__PURE__ */ jsx5(
|
|
3036
|
+
"button",
|
|
3037
|
+
{
|
|
3038
|
+
type: "button",
|
|
3039
|
+
className: "vos-next-moves-rec-chip",
|
|
3040
|
+
onClick: () => onOpenAssumption(id),
|
|
3041
|
+
children: id
|
|
3042
|
+
},
|
|
3043
|
+
id
|
|
3044
|
+
)) })
|
|
3045
|
+
] }),
|
|
3046
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-rec-drawer-section", children: [
|
|
3047
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-drawer-eyebrow", children: "Protocol" }),
|
|
3048
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-rec-drawer-body", children: /* @__PURE__ */ jsx5(EvidenceBody, { text: rec.body }) })
|
|
3049
|
+
] })
|
|
3050
|
+
] }),
|
|
3051
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-drawer-footer", children: /* @__PURE__ */ jsx5("button", { type: "button", className: "vos-btn vos-btn-sm vos-btn-accent", children: "Accept & create experiment" }) })
|
|
3052
|
+
]
|
|
3053
|
+
}
|
|
3054
|
+
)
|
|
2882
3055
|
] });
|
|
2883
3056
|
}
|
|
2884
3057
|
function riskToneClass(risk) {
|
|
@@ -3733,9 +3906,9 @@ function barVerdictFor(exp, assumptionId) {
|
|
|
3733
3906
|
const line = bars.find((b) => b.assumptionId === assumptionId);
|
|
3734
3907
|
return line?.barVerdict ?? null;
|
|
3735
3908
|
}
|
|
3736
|
-
function buildCycles(assumptionId, readings, experiments) {
|
|
3909
|
+
function buildCycles(assumptionId, readings, experiments, assumptionsById) {
|
|
3737
3910
|
const mine = readings.filter((r) => readingGrades(r, assumptionId));
|
|
3738
|
-
const inputs = readings.flatMap(readingBeliefInputs2).filter((i) => i.assumptionId === assumptionId);
|
|
3911
|
+
const inputs = readings.flatMap((r) => readingBeliefInputs2(r, assumptionsById)).filter((i) => i.assumptionId === assumptionId);
|
|
3739
3912
|
const { movers } = confidenceAttribution(inputs);
|
|
3740
3913
|
const moverByKey = new Map(movers.map((m) => [m.key, m]));
|
|
3741
3914
|
const byExperiment = /* @__PURE__ */ new Map();
|
|
@@ -3859,7 +4032,10 @@ function buildJourney(assumptionId, records, now) {
|
|
|
3859
4032
|
const test = beliefTestMeters2(live.map(toStageExperimentInput)).get(assumptionId) ?? emptyTestMeter();
|
|
3860
4033
|
const framed = assumptionCompleteness2(belief2);
|
|
3861
4034
|
const stage = deriveBeliefStage2({ framed, confidence: confidence2, test });
|
|
3862
|
-
const
|
|
4035
|
+
const assumptionsById = new Map(
|
|
4036
|
+
records.assumptions.map((a) => [String(a.id), a])
|
|
4037
|
+
);
|
|
4038
|
+
const myReadingInputs = records.readings.flatMap((r) => readingBeliefInputs3(r, assumptionsById)).filter((i) => i.assumptionId === assumptionId);
|
|
3863
4039
|
const myExperiments = live.filter((e) => testsAssumption(e, assumptionId)).map((e) => ({ id: e.id, date: str4(e.Date) || str4(e.createdAt) || null }));
|
|
3864
4040
|
const events = assembleJourney({
|
|
3865
4041
|
belief: {
|
|
@@ -3873,7 +4049,12 @@ function buildJourney(assumptionId, records, now) {
|
|
|
3873
4049
|
const nextMove2 = rankNextMoves(toNextMoveInput(records)).find(
|
|
3874
4050
|
(m) => m.assumptionId === assumptionId
|
|
3875
4051
|
) ?? null;
|
|
3876
|
-
const cycles = buildCycles(
|
|
4052
|
+
const cycles = buildCycles(
|
|
4053
|
+
assumptionId,
|
|
4054
|
+
records.readings,
|
|
4055
|
+
records.experiments,
|
|
4056
|
+
assumptionsById
|
|
4057
|
+
);
|
|
3877
4058
|
return {
|
|
3878
4059
|
id: assumptionId,
|
|
3879
4060
|
statement: str4(belief2.Title),
|
|
@@ -4462,7 +4643,8 @@ import {
|
|
|
4462
4643
|
isConcluded as isConcluded2
|
|
4463
4644
|
} from "@validation-os/core/derivation";
|
|
4464
4645
|
function buildUnderstanding(assumption, readings, experiments) {
|
|
4465
|
-
const
|
|
4646
|
+
const assumptionsById = /* @__PURE__ */ new Map([[String(assumption.id), assumption]]);
|
|
4647
|
+
const inputs = readings.flatMap((r) => readingBeliefInputs4(r, assumptionsById)).filter((i) => i.assumptionId === assumption.id);
|
|
4466
4648
|
const { confidence: confidence2, movers } = confidenceAttribution2(inputs);
|
|
4467
4649
|
const experimentsById = new Map(experiments.map((e) => [e.id, e]));
|
|
4468
4650
|
const moverByExperiment = new Map(
|
|
@@ -7739,7 +7921,7 @@ function RegisterCounts({
|
|
|
7739
7921
|
import { useMemo as useMemo10, useState as useState17 } from "react";
|
|
7740
7922
|
import { rankNextMoves as rankNextMoves2 } from "@validation-os/core/derivation";
|
|
7741
7923
|
import { Fragment as Fragment12, jsx as jsx30, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
7742
|
-
var
|
|
7924
|
+
var STAGES2 = ["Framed", "Planned", "Tested", "Known"];
|
|
7743
7925
|
var MOVE_STAGE = {
|
|
7744
7926
|
"score-impact": 0,
|
|
7745
7927
|
"design-experiment": 1,
|
|
@@ -8007,7 +8189,7 @@ function WhyPanel({ top, ranked }) {
|
|
|
8007
8189
|
}
|
|
8008
8190
|
function StageStepper({ move }) {
|
|
8009
8191
|
const at = MOVE_STAGE[move];
|
|
8010
|
-
return /* @__PURE__ */ jsx30("div", { className: "vos-stepper", "aria-label": `Stage: ${
|
|
8192
|
+
return /* @__PURE__ */ jsx30("div", { className: "vos-stepper", "aria-label": `Stage: ${STAGES2[at]}`, children: STAGES2.map((label, i) => /* @__PURE__ */ jsx30(
|
|
8011
8193
|
"span",
|
|
8012
8194
|
{
|
|
8013
8195
|
className: `vos-step${i === at ? " vos-step-at" : ""}${i < at ? " vos-step-done" : ""}`,
|