@validation-os/dashboard 0.15.6 → 0.16.1
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 +367 -189
- package/dist/index.js.map +1 -1
- package/dist/styles.css +167 -0
- 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 { confidenceFloorForStage, 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,16 @@ 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 confFloor = stageKey ? confidenceFloorForStage(stageKey) : null;
|
|
1522
|
+
const clearedThreshold = riskThreshold != null && confFloor != null ? risk <= riskThreshold && confidence2 >= confFloor : null;
|
|
1514
1523
|
const nextMove2 = nextMoveFor(record, experiments.records ?? []);
|
|
1515
1524
|
const linkedExperiments = liveExperiments(experiments.records ?? []).filter((e) => {
|
|
1516
1525
|
const ids = Array.isArray(e.barLineAssumptionIds) ? e.barLineAssumptionIds : [];
|
|
@@ -1538,7 +1547,8 @@ function AssumptionDetail({
|
|
|
1538
1547
|
/* @__PURE__ */ jsxs4("div", { className: "vos-detail-head", children: [
|
|
1539
1548
|
/* @__PURE__ */ jsx4("span", { className: "vos-detail-id vos-num", children: assumptionId }),
|
|
1540
1549
|
/* @__PURE__ */ jsx4("span", { className: "vos-detail-tag", children: lens }),
|
|
1541
|
-
/* @__PURE__ */ jsx4("span", { className: "vos-detail-tag", children: stage })
|
|
1550
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-detail-tag", children: stage }),
|
|
1551
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-detail-tag vos-detail-tag-qt", children: questionType })
|
|
1542
1552
|
] }),
|
|
1543
1553
|
/* @__PURE__ */ jsx4("div", { className: "vos-detail-title", children: statement }),
|
|
1544
1554
|
/* @__PURE__ */ jsxs4("div", { className: "vos-card vos-next-move", children: [
|
|
@@ -1551,6 +1561,17 @@ function AssumptionDetail({
|
|
|
1551
1561
|
/* @__PURE__ */ jsx4(ScoreCard, { label: "Confidence", value: formatSigned(confidence2) }),
|
|
1552
1562
|
/* @__PURE__ */ jsx4(ScoreCard, { label: "Framed", value: `${Math.round(framed)}%` })
|
|
1553
1563
|
] }),
|
|
1564
|
+
riskThreshold != null ? /* @__PURE__ */ jsx4(
|
|
1565
|
+
ThresholdBar,
|
|
1566
|
+
{
|
|
1567
|
+
risk,
|
|
1568
|
+
threshold: riskThreshold,
|
|
1569
|
+
confidence: confidence2,
|
|
1570
|
+
confFloor: confFloor ?? 0,
|
|
1571
|
+
stage,
|
|
1572
|
+
cleared: clearedThreshold ?? false
|
|
1573
|
+
}
|
|
1574
|
+
) : null,
|
|
1554
1575
|
/* @__PURE__ */ jsx4(EvidenceCompositionView, { assumption: record, readings: readings.records ?? [] }),
|
|
1555
1576
|
/* @__PURE__ */ jsx4(ConfidenceExplainerView, { assumption: record, readings: readings.records ?? [] }),
|
|
1556
1577
|
statement ? /* @__PURE__ */ jsxs4("div", { className: "vos-card vos-detail-section", children: [
|
|
@@ -1615,6 +1636,7 @@ function AssumptionDetail({
|
|
|
1615
1636
|
EvidenceList,
|
|
1616
1637
|
{
|
|
1617
1638
|
assumptionId,
|
|
1639
|
+
questionType,
|
|
1618
1640
|
readings: readings.records ?? [],
|
|
1619
1641
|
onNavigate
|
|
1620
1642
|
}
|
|
@@ -1623,6 +1645,7 @@ function AssumptionDetail({
|
|
|
1623
1645
|
}
|
|
1624
1646
|
function EvidenceList({
|
|
1625
1647
|
assumptionId,
|
|
1648
|
+
questionType,
|
|
1626
1649
|
readings,
|
|
1627
1650
|
onNavigate
|
|
1628
1651
|
}) {
|
|
@@ -1633,7 +1656,81 @@ function EvidenceList({
|
|
|
1633
1656
|
const rows = readingContributions(rec, readings);
|
|
1634
1657
|
return new Map(rows.map((r) => [r.id, r]));
|
|
1635
1658
|
}, [readings, assumptionId]);
|
|
1659
|
+
const qt = QUESTION_TYPES.find((q) => q === questionType);
|
|
1660
|
+
const { probative, flagged } = useMemo(() => {
|
|
1661
|
+
const prob = [];
|
|
1662
|
+
const flag = [];
|
|
1663
|
+
for (const r of linkedReadings) {
|
|
1664
|
+
const rung = String(r.Rung ?? "");
|
|
1665
|
+
if (qt && RUNGS.includes(rung) && isNonEvidence(qt, rung)) {
|
|
1666
|
+
flag.push(r);
|
|
1667
|
+
} else {
|
|
1668
|
+
prob.push(r);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
return { probative: prob, flagged: flag };
|
|
1672
|
+
}, [linkedReadings, qt]);
|
|
1636
1673
|
if (linkedReadings.length === 0) return null;
|
|
1674
|
+
const renderRow = (r, flagged2) => {
|
|
1675
|
+
const id = String(r.id ?? "");
|
|
1676
|
+
const belief2 = readingBeliefFor(r, assumptionId);
|
|
1677
|
+
if (!belief2) return null;
|
|
1678
|
+
const result = String(belief2.Result ?? "Inconclusive");
|
|
1679
|
+
const justification = String(belief2["Grading justification"] ?? "");
|
|
1680
|
+
const excerpt = typeof belief2.excerpt === "string" && belief2.excerpt !== "" ? belief2.excerpt : snippetFromBody2(String(r.body ?? ""), assumptionId);
|
|
1681
|
+
const rung = String(r.Rung ?? "");
|
|
1682
|
+
const source = String(r.Source ?? "");
|
|
1683
|
+
const expId = r.experimentId ? String(r.experimentId) : null;
|
|
1684
|
+
const c = contribById.get(id);
|
|
1685
|
+
return /* @__PURE__ */ jsxs4(
|
|
1686
|
+
"button",
|
|
1687
|
+
{
|
|
1688
|
+
type: "button",
|
|
1689
|
+
className: `vos-evidence-row vos-verdict-${verdictTone(result)}${flagged2 ? " vos-evidence-row-flagged" : ""}`,
|
|
1690
|
+
onClick: () => onNavigate({ name: "reading", id }),
|
|
1691
|
+
children: [
|
|
1692
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-evidence-row-head", children: [
|
|
1693
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-evidence-date vos-num", children: String(r.Date ?? "") }),
|
|
1694
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-evidence-title", children: String(r.Title ?? id) }),
|
|
1695
|
+
/* @__PURE__ */ jsx4("span", { className: `vos-pill vos-pill-${verdictTone(result)}`, children: result }),
|
|
1696
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-rung-tag", children: rung }),
|
|
1697
|
+
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,
|
|
1698
|
+
c && c.used ? /* @__PURE__ */ jsxs4("span", { className: `vos-evidence-score vos-num vos-text-${contributionTone(c.contribution)}`, children: [
|
|
1699
|
+
formatSigned(c.contribution),
|
|
1700
|
+
" confidence"
|
|
1701
|
+
] }) : null
|
|
1702
|
+
] }),
|
|
1703
|
+
excerpt ? /* @__PURE__ */ jsxs4("div", { className: "vos-evidence-excerpt", children: [
|
|
1704
|
+
"\u201C",
|
|
1705
|
+
excerpt,
|
|
1706
|
+
"\u201D"
|
|
1707
|
+
] }) : null,
|
|
1708
|
+
justification ? /* @__PURE__ */ jsxs4("div", { className: `vos-belief-rationale vos-verdict-border-${verdictTone(result)}`, children: [
|
|
1709
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-belief-rationale-label", children: "grading rationale:" }),
|
|
1710
|
+
justification
|
|
1711
|
+
] }) : null,
|
|
1712
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-evidence-source", children: [
|
|
1713
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-evidence-source-link", title: source, children: source }),
|
|
1714
|
+
expId ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1715
|
+
" \xB7 from experiment ",
|
|
1716
|
+
/* @__PURE__ */ jsx4(
|
|
1717
|
+
"span",
|
|
1718
|
+
{
|
|
1719
|
+
className: "vos-link",
|
|
1720
|
+
onClick: (ev) => {
|
|
1721
|
+
ev.stopPropagation();
|
|
1722
|
+
onNavigate({ name: "experiment", id: expId });
|
|
1723
|
+
},
|
|
1724
|
+
children: expId
|
|
1725
|
+
}
|
|
1726
|
+
)
|
|
1727
|
+
] }) : null
|
|
1728
|
+
] })
|
|
1729
|
+
]
|
|
1730
|
+
},
|
|
1731
|
+
id
|
|
1732
|
+
);
|
|
1733
|
+
};
|
|
1637
1734
|
return /* @__PURE__ */ jsxs4("div", { className: "vos-card vos-detail-section", children: [
|
|
1638
1735
|
/* @__PURE__ */ jsxs4("div", { className: "vos-detail-section-label", children: [
|
|
1639
1736
|
"Evidence \xB7 ",
|
|
@@ -1642,65 +1739,20 @@ function EvidenceList({
|
|
|
1642
1739
|
linkedReadings.length === 1 ? "" : "s",
|
|
1643
1740
|
" of evidence"
|
|
1644
1741
|
] }),
|
|
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
|
-
})
|
|
1742
|
+
probative.length > 0 ? /* @__PURE__ */ jsxs4("div", { className: "vos-evidence-group vos-evidence-group-probative", children: [
|
|
1743
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-evidence-group-label", children: [
|
|
1744
|
+
"Probative evidence \xB7 ",
|
|
1745
|
+
probative.length
|
|
1746
|
+
] }),
|
|
1747
|
+
probative.map((r) => renderRow(r, false))
|
|
1748
|
+
] }) : null,
|
|
1749
|
+
flagged.length > 0 ? /* @__PURE__ */ jsxs4("div", { className: "vos-evidence-group vos-evidence-group-flagged", children: [
|
|
1750
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-evidence-group-label", children: [
|
|
1751
|
+
"Flagged as non-evidence for this question type \xB7 ",
|
|
1752
|
+
flagged.length
|
|
1753
|
+
] }),
|
|
1754
|
+
flagged.map((r) => renderRow(r, true))
|
|
1755
|
+
] }) : null
|
|
1704
1756
|
] });
|
|
1705
1757
|
}
|
|
1706
1758
|
function contributionTone(v) {
|
|
@@ -1708,6 +1760,14 @@ function contributionTone(v) {
|
|
|
1708
1760
|
if (v < 0) return "crit";
|
|
1709
1761
|
return "neutral";
|
|
1710
1762
|
}
|
|
1763
|
+
var RUNGS = [
|
|
1764
|
+
"Talk",
|
|
1765
|
+
"Desk research",
|
|
1766
|
+
"Signed up",
|
|
1767
|
+
"Observed usage",
|
|
1768
|
+
"Signed intent",
|
|
1769
|
+
"Paying users"
|
|
1770
|
+
];
|
|
1711
1771
|
function snippetFromBody2(body, cue) {
|
|
1712
1772
|
if (!body) return "";
|
|
1713
1773
|
const quoteMatch = body.match(/## Quote\n+([\s\S]*?)(?=\n## |\n##$|$)/i);
|
|
@@ -1734,6 +1794,88 @@ function ScoreCard({
|
|
|
1734
1794
|
/* @__PURE__ */ jsx4("div", { className: "vos-score-label", children: label })
|
|
1735
1795
|
] });
|
|
1736
1796
|
}
|
|
1797
|
+
function ThresholdBar({
|
|
1798
|
+
risk,
|
|
1799
|
+
threshold,
|
|
1800
|
+
confidence: confidence2,
|
|
1801
|
+
confFloor,
|
|
1802
|
+
stage,
|
|
1803
|
+
cleared
|
|
1804
|
+
}) {
|
|
1805
|
+
const riskTrackMax = Math.max(risk, threshold) * 1.25;
|
|
1806
|
+
const riskPct = Math.min(100, risk / riskTrackMax * 100);
|
|
1807
|
+
const thresholdPct = Math.min(100, threshold / riskTrackMax * 100);
|
|
1808
|
+
const confPct = Math.min(100, Math.abs(confidence2));
|
|
1809
|
+
const confFloorPct = Math.min(100, confFloor);
|
|
1810
|
+
const riskBelow = risk <= threshold;
|
|
1811
|
+
const confAbove = confidence2 >= confFloor;
|
|
1812
|
+
const tone = cleared ? "good" : !riskBelow && !confAbove ? "crit" : "warn";
|
|
1813
|
+
return /* @__PURE__ */ jsxs4("div", { className: "vos-card vos-threshold-bar-card", children: [
|
|
1814
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-threshold-bar-header", children: [
|
|
1815
|
+
/* @__PURE__ */ jsxs4("span", { className: "vos-threshold-bar-title", children: [
|
|
1816
|
+
stage,
|
|
1817
|
+
" evidence bar",
|
|
1818
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-threshold-bar-stage-tag", children: stage })
|
|
1819
|
+
] }),
|
|
1820
|
+
/* @__PURE__ */ jsx4("span", { className: `vos-threshold-bar-status vos-threshold-bar-${tone}`, children: cleared ? "Cleared" : "Needs evidence" })
|
|
1821
|
+
] }),
|
|
1822
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-threshold-bar-section", children: [
|
|
1823
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-threshold-bar-section-label", children: [
|
|
1824
|
+
/* @__PURE__ */ jsx4("span", { children: "Risk" }),
|
|
1825
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-num vos-threshold-bar-section-val", children: Math.round(risk) }),
|
|
1826
|
+
/* @__PURE__ */ jsxs4("span", { className: "vos-threshold-bar-section-target", children: [
|
|
1827
|
+
"bar \u2264 ",
|
|
1828
|
+
threshold
|
|
1829
|
+
] })
|
|
1830
|
+
] }),
|
|
1831
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-threshold-bar-track", children: [
|
|
1832
|
+
/* @__PURE__ */ jsx4(
|
|
1833
|
+
"div",
|
|
1834
|
+
{
|
|
1835
|
+
className: `vos-threshold-bar-fill vos-fill-${riskBelow ? "good" : tone}`,
|
|
1836
|
+
style: { width: `${riskPct}%` }
|
|
1837
|
+
}
|
|
1838
|
+
),
|
|
1839
|
+
/* @__PURE__ */ jsx4(
|
|
1840
|
+
"div",
|
|
1841
|
+
{
|
|
1842
|
+
className: "vos-threshold-bar-marker",
|
|
1843
|
+
style: { left: `${thresholdPct}%` },
|
|
1844
|
+
title: `Risk threshold: ${threshold}`
|
|
1845
|
+
}
|
|
1846
|
+
)
|
|
1847
|
+
] })
|
|
1848
|
+
] }),
|
|
1849
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-threshold-bar-section", children: [
|
|
1850
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-threshold-bar-section-label", children: [
|
|
1851
|
+
/* @__PURE__ */ jsx4("span", { children: "Confidence" }),
|
|
1852
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-num vos-threshold-bar-section-val", children: formatSigned(confidence2) }),
|
|
1853
|
+
/* @__PURE__ */ jsxs4("span", { className: "vos-threshold-bar-section-target", children: [
|
|
1854
|
+
"floor \u2265 ",
|
|
1855
|
+
confFloor
|
|
1856
|
+
] })
|
|
1857
|
+
] }),
|
|
1858
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-threshold-bar-track vos-threshold-bar-track-conf", children: [
|
|
1859
|
+
/* @__PURE__ */ jsx4(
|
|
1860
|
+
"div",
|
|
1861
|
+
{
|
|
1862
|
+
className: `vos-threshold-bar-fill vos-fill-${confAbove ? "good" : tone}`,
|
|
1863
|
+
style: { width: `${confPct}%` }
|
|
1864
|
+
}
|
|
1865
|
+
),
|
|
1866
|
+
/* @__PURE__ */ jsx4(
|
|
1867
|
+
"div",
|
|
1868
|
+
{
|
|
1869
|
+
className: "vos-threshold-bar-marker vos-threshold-bar-marker-conf",
|
|
1870
|
+
style: { left: `${confFloorPct}%` },
|
|
1871
|
+
title: `Confidence floor: ${confFloor}`
|
|
1872
|
+
}
|
|
1873
|
+
)
|
|
1874
|
+
] })
|
|
1875
|
+
] }),
|
|
1876
|
+
/* @__PURE__ */ jsx4("div", { className: "vos-threshold-bar-hint", children: cleared ? `Risk ${Math.round(risk)} \u2264 ${threshold} and Confidence ${formatSigned(confidence2)} \u2265 ${confFloor} \u2014 cleared for ${stage}.` : `Risk ${Math.round(risk)} ${riskBelow ? "\u2264" : ">"} ${threshold}, Confidence ${formatSigned(confidence2)} ${confAbove ? "\u2265" : "<"} ${confFloor} \u2014 needs ${!riskBelow ? "lower Risk" : ""}${!riskBelow && !confAbove ? " + " : ""}${!confAbove ? "higher Confidence" : ""}.` })
|
|
1877
|
+
] });
|
|
1878
|
+
}
|
|
1737
1879
|
function riskTone(risk) {
|
|
1738
1880
|
if (risk >= 70) return "crit";
|
|
1739
1881
|
if (risk >= 40) return "warn";
|
|
@@ -2068,6 +2210,114 @@ import {
|
|
|
2068
2210
|
assumptionCompleteness,
|
|
2069
2211
|
readingBeliefInputs
|
|
2070
2212
|
} from "@validation-os/core";
|
|
2213
|
+
import { confidenceFloorForStage as confidenceFloorForStage2, riskThresholdForStage as riskThresholdForStage2 } from "@validation-os/core/derivation";
|
|
2214
|
+
|
|
2215
|
+
// src/stage-grid-model.ts
|
|
2216
|
+
var STAGE_ORDER = [
|
|
2217
|
+
"Discovery",
|
|
2218
|
+
"Validation",
|
|
2219
|
+
"Scale",
|
|
2220
|
+
"Maturity"
|
|
2221
|
+
];
|
|
2222
|
+
var STAGE_GLOSS = {
|
|
2223
|
+
Discovery: "Problem-solution fit \u2014 will they engage, care, disclose?",
|
|
2224
|
+
Validation: "Product-market fit \u2014 will they pay, sign, stay?",
|
|
2225
|
+
Scale: "Growth \u2014 can we acquire efficiently, does CAC<LTV hold at volume?",
|
|
2226
|
+
Maturity: "Defense \u2014 will incumbents respond, will regulators accept?"
|
|
2227
|
+
};
|
|
2228
|
+
var NO_LENS = "\u2014";
|
|
2229
|
+
var NO_STAGE = "\u2014";
|
|
2230
|
+
function stageOf(record) {
|
|
2231
|
+
const v = str(record.Stage);
|
|
2232
|
+
if (!v) return null;
|
|
2233
|
+
return STAGE_ORDER.includes(v) ? v : null;
|
|
2234
|
+
}
|
|
2235
|
+
function rankByRisk(records) {
|
|
2236
|
+
return [...records].map((r) => ({ r, risk: derivedNum(r, "risk") ?? 0 })).sort(
|
|
2237
|
+
(a, b) => a.risk !== b.risk ? b.risk - a.risk : a.r.id.localeCompare(b.r.id)
|
|
2238
|
+
).map((x) => x.r);
|
|
2239
|
+
}
|
|
2240
|
+
function buildStageGrid(assumptions) {
|
|
2241
|
+
const lensOrder = [];
|
|
2242
|
+
const seenLens = /* @__PURE__ */ new Set();
|
|
2243
|
+
const pushLens = (lens) => {
|
|
2244
|
+
if (!seenLens.has(lens)) {
|
|
2245
|
+
seenLens.add(lens);
|
|
2246
|
+
lensOrder.push(lens);
|
|
2247
|
+
}
|
|
2248
|
+
};
|
|
2249
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
2250
|
+
const ensureLens = (lens) => {
|
|
2251
|
+
let m = buckets.get(lens);
|
|
2252
|
+
if (!m) {
|
|
2253
|
+
m = /* @__PURE__ */ new Map();
|
|
2254
|
+
buckets.set(lens, m);
|
|
2255
|
+
}
|
|
2256
|
+
return m;
|
|
2257
|
+
};
|
|
2258
|
+
let hasNoLens = false;
|
|
2259
|
+
let hasNoStage = false;
|
|
2260
|
+
for (const a of assumptions) {
|
|
2261
|
+
const lens = str(a.Lens) ?? NO_LENS;
|
|
2262
|
+
const stage = stageOf(a) ?? NO_STAGE;
|
|
2263
|
+
if (lens === NO_LENS) hasNoLens = true;
|
|
2264
|
+
if (stage === NO_STAGE) hasNoStage = true;
|
|
2265
|
+
pushLens(lens);
|
|
2266
|
+
const byStage = ensureLens(lens);
|
|
2267
|
+
let list = byStage.get(stage);
|
|
2268
|
+
if (!list) {
|
|
2269
|
+
list = [];
|
|
2270
|
+
byStage.set(stage, list);
|
|
2271
|
+
}
|
|
2272
|
+
list.push(a);
|
|
2273
|
+
}
|
|
2274
|
+
if (hasNoLens) {
|
|
2275
|
+
lensOrder.splice(lensOrder.indexOf(NO_LENS), 1);
|
|
2276
|
+
lensOrder.push(NO_LENS);
|
|
2277
|
+
} else {
|
|
2278
|
+
const idx = lensOrder.indexOf(NO_LENS);
|
|
2279
|
+
if (idx >= 0) lensOrder.splice(idx, 1);
|
|
2280
|
+
}
|
|
2281
|
+
const stageOrder = [...STAGE_ORDER];
|
|
2282
|
+
if (hasNoStage) stageOrder.push(NO_STAGE);
|
|
2283
|
+
const cells = [];
|
|
2284
|
+
let maxCellCount = 0;
|
|
2285
|
+
let total = 0;
|
|
2286
|
+
for (const lens of lensOrder) {
|
|
2287
|
+
const byStage = buckets.get(lens) ?? /* @__PURE__ */ new Map();
|
|
2288
|
+
for (const stage of stageOrder) {
|
|
2289
|
+
const records = byStage.get(stage) ?? [];
|
|
2290
|
+
const ranked = rankByRisk(records);
|
|
2291
|
+
const count = ranked.length;
|
|
2292
|
+
maxCellCount = Math.max(maxCellCount, count);
|
|
2293
|
+
total += count;
|
|
2294
|
+
cells.push({
|
|
2295
|
+
lens,
|
|
2296
|
+
stage,
|
|
2297
|
+
count,
|
|
2298
|
+
assumptions: ranked,
|
|
2299
|
+
// density filled in a second pass once maxCellCount is known.
|
|
2300
|
+
density: 0
|
|
2301
|
+
});
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
const norm2 = maxCellCount > 0 ? 1 / maxCellCount : 0;
|
|
2305
|
+
for (const cell of cells) {
|
|
2306
|
+
cell.density = cell.count * norm2;
|
|
2307
|
+
}
|
|
2308
|
+
return {
|
|
2309
|
+
lenses: lensOrder,
|
|
2310
|
+
stages: [...STAGE_ORDER],
|
|
2311
|
+
cells,
|
|
2312
|
+
maxCellCount,
|
|
2313
|
+
total
|
|
2314
|
+
};
|
|
2315
|
+
}
|
|
2316
|
+
function cellAt(view, lens, stage) {
|
|
2317
|
+
return view.cells.find((c) => c.lens === lens && c.stage === stage) ?? null;
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
// src/pipeline.ts
|
|
2071
2321
|
import {
|
|
2072
2322
|
beliefRisk,
|
|
2073
2323
|
beliefTestMeters,
|
|
@@ -2156,6 +2406,11 @@ function buildPipeline(assumptions, experiments) {
|
|
|
2156
2406
|
};
|
|
2157
2407
|
const framed = assumptionCompleteness(a);
|
|
2158
2408
|
const stage = deriveBeliefStage({ framed, confidence: d.confidence, test });
|
|
2409
|
+
const questionType = str3(a["Question Type"]);
|
|
2410
|
+
const stageName = str3(a.Stage);
|
|
2411
|
+
const stageKey = stageName && STAGE_ORDER.includes(stageName) ? stageName : null;
|
|
2412
|
+
const riskThreshold = stageKey ? riskThresholdForStage2(stageKey) : null;
|
|
2413
|
+
const confFloor = stageKey ? confidenceFloorForStage2(stageKey) : null;
|
|
2159
2414
|
rows.push({
|
|
2160
2415
|
id: a.id,
|
|
2161
2416
|
statement: str3(a.Title),
|
|
@@ -2168,7 +2423,11 @@ function buildPipeline(assumptions, experiments) {
|
|
|
2168
2423
|
framed: stage.framed,
|
|
2169
2424
|
planned: stage.planned,
|
|
2170
2425
|
tested: stage.tested,
|
|
2171
|
-
nextMove: nextMove(stage.stage, stage.killZone)
|
|
2426
|
+
nextMove: nextMove(stage.stage, stage.killZone),
|
|
2427
|
+
questionType,
|
|
2428
|
+
stage: stageName,
|
|
2429
|
+
riskThreshold,
|
|
2430
|
+
clearedThreshold: riskThreshold != null ? d.risk <= riskThreshold && (confFloor == null || stage.confidence >= confFloor) : null
|
|
2172
2431
|
});
|
|
2173
2432
|
}
|
|
2174
2433
|
rows.sort(
|
|
@@ -2188,8 +2447,13 @@ function weekOverWeekDelta(assumptions, readings, now) {
|
|
|
2188
2447
|
});
|
|
2189
2448
|
if (!anyDated) return null;
|
|
2190
2449
|
const cutoff = now.getTime() - 7 * 24 * 60 * 60 * 1e3;
|
|
2450
|
+
const assumptionsById = new Map(
|
|
2451
|
+
assumptions.map((a) => [String(a.id), a])
|
|
2452
|
+
);
|
|
2191
2453
|
const byAssumption = /* @__PURE__ */ new Map();
|
|
2192
|
-
for (const input of readings.flatMap(
|
|
2454
|
+
for (const input of readings.flatMap(
|
|
2455
|
+
(r) => readingBeliefInputs(r, assumptionsById)
|
|
2456
|
+
)) {
|
|
2193
2457
|
if (!input.assumptionId) continue;
|
|
2194
2458
|
const arr = byAssumption.get(input.assumptionId);
|
|
2195
2459
|
if (arr) arr.push(input);
|
|
@@ -2408,111 +2672,6 @@ function stageMeters(input) {
|
|
|
2408
2672
|
];
|
|
2409
2673
|
}
|
|
2410
2674
|
|
|
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
2675
|
// src/assumptions-surface.tsx
|
|
2517
2676
|
import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2518
2677
|
function AssumptionsSurface({
|
|
@@ -2779,7 +2938,17 @@ function PipelineRowView({ row, onOpen }) {
|
|
|
2779
2938
|
/* @__PURE__ */ jsxs5("span", { className: "vos-num", children: [
|
|
2780
2939
|
"conf ",
|
|
2781
2940
|
formatSigned(row.confidence)
|
|
2782
|
-
] })
|
|
2941
|
+
] }),
|
|
2942
|
+
row.questionType ? /* @__PURE__ */ jsx5("span", { className: "vos-pipe-tag vos-pipe-tag-qt", children: row.questionType }) : null,
|
|
2943
|
+
row.stage ? /* @__PURE__ */ jsx5("span", { className: "vos-pipe-tag vos-pipe-tag-stage", children: row.stage }) : null,
|
|
2944
|
+
row.riskThreshold != null ? /* @__PURE__ */ jsx5(
|
|
2945
|
+
"span",
|
|
2946
|
+
{
|
|
2947
|
+
className: `vos-pipe-tag vos-pipe-tag-thresh ${row.clearedThreshold ? "vos-pipe-tag-cleared" : "vos-pipe-tag-needs"}`,
|
|
2948
|
+
title: `Stage threshold ${row.riskThreshold} \u2014 ${row.clearedThreshold ? "cleared for this stage" : "needs evidence"}`,
|
|
2949
|
+
children: row.clearedThreshold ? "cleared" : `bar ${row.riskThreshold}`
|
|
2950
|
+
}
|
|
2951
|
+
) : null
|
|
2783
2952
|
] })
|
|
2784
2953
|
] }),
|
|
2785
2954
|
/* @__PURE__ */ jsx5("div", { className: "vos-pipe-prog-2seg", "aria-label": "Evidence progress (Framed + Known)", children: /* @__PURE__ */ jsx5(Meter2Seg, { framed: row.framed, knownPct, knownSign }) }),
|
|
@@ -3814,9 +3983,9 @@ function barVerdictFor(exp, assumptionId) {
|
|
|
3814
3983
|
const line = bars.find((b) => b.assumptionId === assumptionId);
|
|
3815
3984
|
return line?.barVerdict ?? null;
|
|
3816
3985
|
}
|
|
3817
|
-
function buildCycles(assumptionId, readings, experiments) {
|
|
3986
|
+
function buildCycles(assumptionId, readings, experiments, assumptionsById) {
|
|
3818
3987
|
const mine = readings.filter((r) => readingGrades(r, assumptionId));
|
|
3819
|
-
const inputs = readings.flatMap(readingBeliefInputs2).filter((i) => i.assumptionId === assumptionId);
|
|
3988
|
+
const inputs = readings.flatMap((r) => readingBeliefInputs2(r, assumptionsById)).filter((i) => i.assumptionId === assumptionId);
|
|
3820
3989
|
const { movers } = confidenceAttribution(inputs);
|
|
3821
3990
|
const moverByKey = new Map(movers.map((m) => [m.key, m]));
|
|
3822
3991
|
const byExperiment = /* @__PURE__ */ new Map();
|
|
@@ -3940,7 +4109,10 @@ function buildJourney(assumptionId, records, now) {
|
|
|
3940
4109
|
const test = beliefTestMeters2(live.map(toStageExperimentInput)).get(assumptionId) ?? emptyTestMeter();
|
|
3941
4110
|
const framed = assumptionCompleteness2(belief2);
|
|
3942
4111
|
const stage = deriveBeliefStage2({ framed, confidence: confidence2, test });
|
|
3943
|
-
const
|
|
4112
|
+
const assumptionsById = new Map(
|
|
4113
|
+
records.assumptions.map((a) => [String(a.id), a])
|
|
4114
|
+
);
|
|
4115
|
+
const myReadingInputs = records.readings.flatMap((r) => readingBeliefInputs3(r, assumptionsById)).filter((i) => i.assumptionId === assumptionId);
|
|
3944
4116
|
const myExperiments = live.filter((e) => testsAssumption(e, assumptionId)).map((e) => ({ id: e.id, date: str4(e.Date) || str4(e.createdAt) || null }));
|
|
3945
4117
|
const events = assembleJourney({
|
|
3946
4118
|
belief: {
|
|
@@ -3954,7 +4126,12 @@ function buildJourney(assumptionId, records, now) {
|
|
|
3954
4126
|
const nextMove2 = rankNextMoves(toNextMoveInput(records)).find(
|
|
3955
4127
|
(m) => m.assumptionId === assumptionId
|
|
3956
4128
|
) ?? null;
|
|
3957
|
-
const cycles = buildCycles(
|
|
4129
|
+
const cycles = buildCycles(
|
|
4130
|
+
assumptionId,
|
|
4131
|
+
records.readings,
|
|
4132
|
+
records.experiments,
|
|
4133
|
+
assumptionsById
|
|
4134
|
+
);
|
|
3958
4135
|
return {
|
|
3959
4136
|
id: assumptionId,
|
|
3960
4137
|
statement: str4(belief2.Title),
|
|
@@ -4543,7 +4720,8 @@ import {
|
|
|
4543
4720
|
isConcluded as isConcluded2
|
|
4544
4721
|
} from "@validation-os/core/derivation";
|
|
4545
4722
|
function buildUnderstanding(assumption, readings, experiments) {
|
|
4546
|
-
const
|
|
4723
|
+
const assumptionsById = /* @__PURE__ */ new Map([[String(assumption.id), assumption]]);
|
|
4724
|
+
const inputs = readings.flatMap((r) => readingBeliefInputs4(r, assumptionsById)).filter((i) => i.assumptionId === assumption.id);
|
|
4547
4725
|
const { confidence: confidence2, movers } = confidenceAttribution2(inputs);
|
|
4548
4726
|
const experimentsById = new Map(experiments.map((e) => [e.id, e]));
|
|
4549
4727
|
const moverByExperiment = new Map(
|
|
@@ -7820,7 +7998,7 @@ function RegisterCounts({
|
|
|
7820
7998
|
import { useMemo as useMemo10, useState as useState17 } from "react";
|
|
7821
7999
|
import { rankNextMoves as rankNextMoves2 } from "@validation-os/core/derivation";
|
|
7822
8000
|
import { Fragment as Fragment12, jsx as jsx30, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
7823
|
-
var
|
|
8001
|
+
var STAGES2 = ["Framed", "Planned", "Tested", "Known"];
|
|
7824
8002
|
var MOVE_STAGE = {
|
|
7825
8003
|
"score-impact": 0,
|
|
7826
8004
|
"design-experiment": 1,
|
|
@@ -8088,7 +8266,7 @@ function WhyPanel({ top, ranked }) {
|
|
|
8088
8266
|
}
|
|
8089
8267
|
function StageStepper({ move }) {
|
|
8090
8268
|
const at = MOVE_STAGE[move];
|
|
8091
|
-
return /* @__PURE__ */ jsx30("div", { className: "vos-stepper", "aria-label": `Stage: ${
|
|
8269
|
+
return /* @__PURE__ */ jsx30("div", { className: "vos-stepper", "aria-label": `Stage: ${STAGES2[at]}`, children: STAGES2.map((label, i) => /* @__PURE__ */ jsx30(
|
|
8092
8270
|
"span",
|
|
8093
8271
|
{
|
|
8094
8272
|
className: `vos-step${i === at ? " vos-step-at" : ""}${i < at ? " vos-step-done" : ""}`,
|