@validation-os/dashboard 0.15.1 → 0.15.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.js +292 -55
- package/dist/index.js.map +1 -1
- package/dist/styles.css +225 -59
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -219,6 +219,75 @@ function buildEvidenceComposition(assumption, readings) {
|
|
|
219
219
|
};
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
// src/confidence-explainer.ts
|
|
223
|
+
import { RUNG_ANCHOR, W0_BY_RUNG } from "@validation-os/core/derivation";
|
|
224
|
+
var RUNG_INFO = {
|
|
225
|
+
Talk: {
|
|
226
|
+
label: "Talk",
|
|
227
|
+
description: "Opinions, pitch reactions, anecdotes. Quick to get but slow to move the needle \u2014 needs ~10 distinct sources to approach the cap."
|
|
228
|
+
},
|
|
229
|
+
"Desk research": {
|
|
230
|
+
label: "Desk research",
|
|
231
|
+
description: "Published sources, competitor analysis, market reports. Authoritative \u2014 2 strong sources nearly saturate this rung."
|
|
232
|
+
},
|
|
233
|
+
"Signed up": {
|
|
234
|
+
label: "Signed up (Consumer)",
|
|
235
|
+
description: "Consumers signing up for the product. A do-rung \u2014 20 signups bring this to ~75% of its cap."
|
|
236
|
+
},
|
|
237
|
+
"Observed usage": {
|
|
238
|
+
label: "Observed usage (Consumer)",
|
|
239
|
+
description: "Prototype usage, analytics, telemetry. A do-rung \u2014 20 observed users bring this to ~75% of its cap."
|
|
240
|
+
},
|
|
241
|
+
"Signed intent": {
|
|
242
|
+
label: "Signed intent (Commercial)",
|
|
243
|
+
description: "LOIs, signed letters of intent from businesses. A do-rung \u2014 20 signed intents bring this to ~75% of its cap."
|
|
244
|
+
},
|
|
245
|
+
"Paying users": {
|
|
246
|
+
label: "Paying users (Commercial)",
|
|
247
|
+
description: "Closed commitments \u2014 revenue. The strongest do-rung \u2014 20 paying users bring this to ~75% of its cap."
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
function buildConfidenceExplainer(assumption, readings) {
|
|
251
|
+
const comp = buildEvidenceComposition(assumption, readings);
|
|
252
|
+
const lens = str(assumption.Lens) ?? "";
|
|
253
|
+
const lensRungs = new Set(comp.rungs.map((r) => r.rung));
|
|
254
|
+
const allRungs = [
|
|
255
|
+
"Talk",
|
|
256
|
+
"Desk research",
|
|
257
|
+
"Signed up",
|
|
258
|
+
"Observed usage",
|
|
259
|
+
"Signed intent",
|
|
260
|
+
"Paying users"
|
|
261
|
+
];
|
|
262
|
+
const rungs = allRungs.map((rung) => {
|
|
263
|
+
const e = comp.rungs.find((r) => r.rung === rung);
|
|
264
|
+
const info = RUNG_INFO[rung] ?? { label: rung, description: "" };
|
|
265
|
+
return {
|
|
266
|
+
rung,
|
|
267
|
+
w0: W0_BY_RUNG[rung] ?? 100,
|
|
268
|
+
anchors: RUNG_ANCHOR[rung] ?? { Low: 0, Typical: 0, High: 0 },
|
|
269
|
+
contribution: e?.contribution ?? 0,
|
|
270
|
+
count: e?.count ?? 0,
|
|
271
|
+
inLens: lensRungs.has(rung),
|
|
272
|
+
label: info.label,
|
|
273
|
+
description: info.description
|
|
274
|
+
};
|
|
275
|
+
});
|
|
276
|
+
const confidence2 = assumption.derived?.confidence ?? 0;
|
|
277
|
+
const totalContribution = comp.totalContribution;
|
|
278
|
+
const rungsWithEvidence = rungs.filter((r) => r.count > 0);
|
|
279
|
+
const w0Sum = rungsWithEvidence.reduce((s, r) => s + r.w0, 0);
|
|
280
|
+
const summary = rungsWithEvidence.length === 0 ? "No concluded evidence yet. Confidence is 0 \u2014 the bet is open. Any Validated or Invalidated evidence at any rung will start moving this number." : rungsWithEvidence.length === 1 ? `All evidence is at the ${rungsWithEvidence[0].rung} rung (${rungsWithEvidence[0].count} source${rungsWithEvidence[0].count === 1 ? "" : "s"}). ${rungsWithEvidence[0].description}` : `Evidence spans ${rungsWithEvidence.length} rungs. The strongest push comes from ${rungs.reduce((a, b) => Math.abs(b.contribution) > Math.abs(a.contribution) ? b : a).rung}.`;
|
|
281
|
+
return {
|
|
282
|
+
confidence: confidence2,
|
|
283
|
+
formula: "Confidence = \u03A3(weight \xD7 strength) / (\u03A3 W0[rungs with evidence] + \u03A3 weights)",
|
|
284
|
+
rungs,
|
|
285
|
+
totalContribution,
|
|
286
|
+
denominator: w0Sum,
|
|
287
|
+
summary
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
222
291
|
// src/markdown.tsx
|
|
223
292
|
import { useState } from "react";
|
|
224
293
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
@@ -1426,6 +1495,7 @@ function AssumptionDetail({
|
|
|
1426
1495
|
/* @__PURE__ */ jsx4(ScoreCard, { label: "Framed", value: `${Math.round(framed)}%` })
|
|
1427
1496
|
] }),
|
|
1428
1497
|
/* @__PURE__ */ jsx4(EvidenceCompositionView, { assumption: record, readings: readings.records ?? [] }),
|
|
1498
|
+
/* @__PURE__ */ jsx4(ConfidenceExplainerView, { assumption: record, readings: readings.records ?? [] }),
|
|
1429
1499
|
statement ? /* @__PURE__ */ jsxs4("div", { className: "vos-card vos-detail-section", children: [
|
|
1430
1500
|
/* @__PURE__ */ jsx4("div", { className: "vos-detail-section-label", children: "Body" }),
|
|
1431
1501
|
/* @__PURE__ */ jsx4(GlossaryText, { text: statement, terms: glossaryTerms, selfId: assumptionId })
|
|
@@ -1513,11 +1583,10 @@ function AssumptionDetail({
|
|
|
1513
1583
|
/* @__PURE__ */ jsx4("span", { className: `vos-pill vos-pill-${verdictTone(result)}`, children: result }),
|
|
1514
1584
|
/* @__PURE__ */ jsx4("span", { className: "vos-rung-tag", children: rung })
|
|
1515
1585
|
] }),
|
|
1516
|
-
/* @__PURE__ */ jsxs4("div", { className: `vos-
|
|
1517
|
-
|
|
1518
|
-
justification
|
|
1519
|
-
|
|
1520
|
-
] }),
|
|
1586
|
+
justification ? /* @__PURE__ */ jsxs4("div", { className: `vos-belief-rationale vos-verdict-border-${verdictTone(result)}`, children: [
|
|
1587
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-belief-rationale-label", children: "grading rationale:" }),
|
|
1588
|
+
justification
|
|
1589
|
+
] }) : null,
|
|
1521
1590
|
/* @__PURE__ */ jsxs4("div", { className: "vos-reading-source", children: [
|
|
1522
1591
|
source,
|
|
1523
1592
|
expId ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
@@ -1631,6 +1700,72 @@ function nextMoveFor(assumption, experiments) {
|
|
|
1631
1700
|
if (framed) return "Design an experiment";
|
|
1632
1701
|
return "Frame the belief";
|
|
1633
1702
|
}
|
|
1703
|
+
function ConfidenceExplainerView({
|
|
1704
|
+
assumption,
|
|
1705
|
+
readings
|
|
1706
|
+
}) {
|
|
1707
|
+
const view = useMemo(
|
|
1708
|
+
() => buildConfidenceExplainer(assumption, readings),
|
|
1709
|
+
[assumption, readings]
|
|
1710
|
+
);
|
|
1711
|
+
return /* @__PURE__ */ jsxs4("details", { className: "vos-card vos-detail-section vos-explainer", children: [
|
|
1712
|
+
/* @__PURE__ */ jsxs4("summary", { className: "vos-explainer-summary", children: [
|
|
1713
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-detail-section-label", children: "How Confidence is calculated" }),
|
|
1714
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-explainer-conf vos-num", children: formatSigned(view.confidence) })
|
|
1715
|
+
] }),
|
|
1716
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-explainer-body", children: [
|
|
1717
|
+
/* @__PURE__ */ jsx4("p", { className: "vos-explainer-formula", children: view.formula }),
|
|
1718
|
+
/* @__PURE__ */ jsx4("p", { className: "vos-explainer-summary-text", children: view.summary }),
|
|
1719
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-explainer-rungs", children: [
|
|
1720
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-explainer-rungs-head", children: [
|
|
1721
|
+
/* @__PURE__ */ jsx4("span", { children: "Rung" }),
|
|
1722
|
+
/* @__PURE__ */ jsx4("span", { children: "W0 (prior)" }),
|
|
1723
|
+
/* @__PURE__ */ jsx4("span", { children: "Anchors (L/T/H)" }),
|
|
1724
|
+
/* @__PURE__ */ jsx4("span", { children: "Evidence" }),
|
|
1725
|
+
/* @__PURE__ */ jsx4("span", { children: "Contribution" })
|
|
1726
|
+
] }),
|
|
1727
|
+
view.rungs.map((r) => /* @__PURE__ */ jsxs4(
|
|
1728
|
+
"div",
|
|
1729
|
+
{
|
|
1730
|
+
className: `vos-explainer-rung ${!r.inLens ? "is-not-lens" : ""} ${r.count > 0 ? "has-evidence" : ""}`,
|
|
1731
|
+
children: [
|
|
1732
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-explainer-rung-name", title: r.description, children: r.label }),
|
|
1733
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-explainer-rung-w0 vos-num", children: r.w0 }),
|
|
1734
|
+
/* @__PURE__ */ jsxs4("span", { className: "vos-explainer-rung-anchors vos-num", children: [
|
|
1735
|
+
r.anchors.Low,
|
|
1736
|
+
"/",
|
|
1737
|
+
r.anchors.Typical,
|
|
1738
|
+
"/",
|
|
1739
|
+
r.anchors.High
|
|
1740
|
+
] }),
|
|
1741
|
+
/* @__PURE__ */ jsx4("span", { className: "vos-explainer-rung-count vos-num", children: r.count > 0 ? `${r.count} source${r.count === 1 ? "" : "s"}` : "\u2014" }),
|
|
1742
|
+
/* @__PURE__ */ jsx4("span", { className: `vos-explainer-rung-contrib vos-num ${r.contribution > 0 ? "vos-text-good" : r.contribution < 0 ? "vos-text-crit" : ""}`, children: r.count > 0 ? formatSigned(r.contribution) : "\u2014" })
|
|
1743
|
+
]
|
|
1744
|
+
},
|
|
1745
|
+
r.rung
|
|
1746
|
+
))
|
|
1747
|
+
] }),
|
|
1748
|
+
/* @__PURE__ */ jsxs4("div", { className: "vos-explainer-foot", children: [
|
|
1749
|
+
/* @__PURE__ */ jsxs4("p", { children: [
|
|
1750
|
+
/* @__PURE__ */ jsx4("strong", { children: "W0" }),
|
|
1751
|
+
" = the prior weight for each rung \u2014 how many distinct sources it takes to approach that rung's cap. Desk research has a low W0 (2 \u2014 one authoritative source nearly saturates it); Talk has a higher W0 (6.5 \u2014 needs ~10 sources); do-rungs have high W0s (327 \u2014 needs ~20 sources to reach 75% of the cap)."
|
|
1752
|
+
] }),
|
|
1753
|
+
/* @__PURE__ */ jsxs4("p", { children: [
|
|
1754
|
+
/* @__PURE__ */ jsx4("strong", { children: "Strength" }),
|
|
1755
|
+
" = the rung's anchor \xD7 the sign of the result (Validated = +, Invalidated = \u2212, Inconclusive = 0). The anchor is the band (Low/Typical/High) the evidence lands at."
|
|
1756
|
+
] }),
|
|
1757
|
+
/* @__PURE__ */ jsxs4("p", { children: [
|
|
1758
|
+
/* @__PURE__ */ jsx4("strong", { children: "Weight" }),
|
|
1759
|
+
" = |Strength| \xD7 source quality \xD7 commitment factor (1.0 for evidence linked to an experiment, 0.85 for found evidence)."
|
|
1760
|
+
] }),
|
|
1761
|
+
/* @__PURE__ */ jsxs4("p", { children: [
|
|
1762
|
+
/* @__PURE__ */ jsx4("strong", { children: "Contribution" }),
|
|
1763
|
+
" = (Weight \xD7 Strength) / denominator. The sum of all contributions equals Confidence. Evidence at a higher rung always outweighs evidence at a lower rung \u2014 the rung ladder dominates."
|
|
1764
|
+
] })
|
|
1765
|
+
] })
|
|
1766
|
+
] })
|
|
1767
|
+
] });
|
|
1768
|
+
}
|
|
1634
1769
|
function EvidenceCompositionView({
|
|
1635
1770
|
assumption,
|
|
1636
1771
|
readings
|
|
@@ -1964,6 +2099,38 @@ function weekOverWeekDelta(assumptions, readings, now) {
|
|
|
1964
2099
|
}
|
|
1965
2100
|
|
|
1966
2101
|
// src/recommended-experiments.ts
|
|
2102
|
+
var MAX_RECOMMENDED = 2;
|
|
2103
|
+
var MAX_CLUSTER_SIZE = 3;
|
|
2104
|
+
var MAX_NEEDS_FRAMING = 2;
|
|
2105
|
+
function buildNeedsFraming(assumptions) {
|
|
2106
|
+
const live = assumptions.filter((a) => {
|
|
2107
|
+
const status = str(a.Status);
|
|
2108
|
+
const moot = a.moot === true;
|
|
2109
|
+
return !moot && (status === "Live" || status === "Draft");
|
|
2110
|
+
});
|
|
2111
|
+
const items = live.map((a) => {
|
|
2112
|
+
const id = str(a.id) ?? "";
|
|
2113
|
+
const completeness = derivedNum(a, "completeness") ?? 0;
|
|
2114
|
+
const risk = derivedNum(a, "risk") ?? 0;
|
|
2115
|
+
const lens = str(a.Lens) ?? "\u2014";
|
|
2116
|
+
const stage = str(a.Stage) ?? "\u2014";
|
|
2117
|
+
const title = str(a.Title) ?? id;
|
|
2118
|
+
const hint = framingHint(a, completeness);
|
|
2119
|
+
return { id, title, risk, completeness, lens, stage, hint };
|
|
2120
|
+
}).filter((a) => a.completeness < 100).sort((a, b) => b.risk - a.risk).slice(0, MAX_NEEDS_FRAMING);
|
|
2121
|
+
return items;
|
|
2122
|
+
}
|
|
2123
|
+
function framingHint(a, completeness) {
|
|
2124
|
+
const hasScoring = Boolean(str(a["Scoring justification"]));
|
|
2125
|
+
const hasDescription = Boolean(str(a.Description));
|
|
2126
|
+
if (completeness < 50) {
|
|
2127
|
+
return hasDescription ? "The belief is written but incomplete \u2014 fill in the scoring justification and any missing framing fields." : "The belief needs a description \u2014 what exactly is being claimed, in falsifiable terms.";
|
|
2128
|
+
}
|
|
2129
|
+
if (!hasScoring) {
|
|
2130
|
+
return "Add a scoring justification \u2014 why is the Impact seed scored as it is?";
|
|
2131
|
+
}
|
|
2132
|
+
return "Nearly framed \u2014 check the 5 Whys and the metric for truth are complete.";
|
|
2133
|
+
}
|
|
1967
2134
|
var LENS_TO_TYPE = {
|
|
1968
2135
|
Consumer: "Observation",
|
|
1969
2136
|
Commercial: "Test"
|
|
@@ -1992,16 +2159,22 @@ function buildRecommendedExperiments(assumptions, experiments) {
|
|
|
1992
2159
|
if (bucket) bucket.push(a);
|
|
1993
2160
|
else clusters.set(key, [a]);
|
|
1994
2161
|
}
|
|
2162
|
+
const clusterEntries = [...clusters.entries()].map(([key, cluster]) => {
|
|
2163
|
+
const ranked = cluster.sort((a, b) => (derivedNum(b, "risk") ?? 0) - (derivedNum(a, "risk") ?? 0)).slice(0, MAX_CLUSTER_SIZE);
|
|
2164
|
+
const maxRisk = Math.max(...ranked.map((a) => derivedNum(a, "risk") ?? 0), 0);
|
|
2165
|
+
return { key, cluster: ranked, maxRisk };
|
|
2166
|
+
}).sort((a, b) => b.maxRisk - a.maxRisk).slice(0, MAX_RECOMMENDED);
|
|
1995
2167
|
const recs = [];
|
|
1996
|
-
for (const
|
|
1997
|
-
const
|
|
1998
|
-
const
|
|
1999
|
-
const maxRisk = Math.max(...risks, 0);
|
|
2168
|
+
for (const { key, cluster, maxRisk } of clusterEntries) {
|
|
2169
|
+
const lens = key.split("\xD7")[0] ?? "\u2014";
|
|
2170
|
+
const stage = key.split("\xD7")[1] ?? "\u2014";
|
|
2000
2171
|
const assumptionIds = cluster.map((a) => str(a.id) ?? "").filter(Boolean).sort();
|
|
2001
2172
|
const type = LENS_TO_TYPE[lens ?? ""] ?? "Desk research";
|
|
2173
|
+
const titles = cluster.map((a) => str(a.Title) ?? str(a.id) ?? "");
|
|
2002
2174
|
const title = cluster.length === 1 ? `Test ${assumptionIds[0]}` : `Test ${cluster.length} ${lens} \xB7 ${stage} beliefs`;
|
|
2003
2175
|
const rationale = cluster.length === 1 ? `One belief (${assumptionIds[0]}) at ${Math.round(maxRisk)} risk with no live test. Designing an experiment here would buy down the most risk.` : `${cluster.length} beliefs share ${lens} \xB7 ${stage}, the riskiest at ${Math.round(maxRisk)} risk. One experiment can address them all.`;
|
|
2004
2176
|
const barPreview = `The riskiest belief moves out of the kill zone, or stays in it.`;
|
|
2177
|
+
const body = generateExperimentBody(type, lens, stage, cluster, maxRisk);
|
|
2005
2178
|
recs.push({
|
|
2006
2179
|
id: assumptionIds.join("+"),
|
|
2007
2180
|
type,
|
|
@@ -2009,7 +2182,8 @@ function buildRecommendedExperiments(assumptions, experiments) {
|
|
|
2009
2182
|
assumptionIds,
|
|
2010
2183
|
maxRisk,
|
|
2011
2184
|
rationale,
|
|
2012
|
-
barPreview
|
|
2185
|
+
barPreview,
|
|
2186
|
+
body
|
|
2013
2187
|
});
|
|
2014
2188
|
}
|
|
2015
2189
|
recs.sort(
|
|
@@ -2017,6 +2191,29 @@ function buildRecommendedExperiments(assumptions, experiments) {
|
|
|
2017
2191
|
);
|
|
2018
2192
|
return recs;
|
|
2019
2193
|
}
|
|
2194
|
+
function generateExperimentBody(type, lens, stage, cluster, maxRisk) {
|
|
2195
|
+
const beliefs = cluster.map((a) => `- **${str(a.id)}**: ${str(a.Title) ?? str(a.id) ?? ""}`).join("\n");
|
|
2196
|
+
const rung = type === "Observation" ? "Observed usage" : type === "Test" ? "Signed intent" : "Desk research";
|
|
2197
|
+
return `## What this tests
|
|
2198
|
+
|
|
2199
|
+
This ${type.toLowerCase()} addresses ${cluster.length} ${lens} \xB7 ${stage} belief${cluster.length === 1 ? "" : "s"} at ${Math.round(maxRisk)} risk \u2014 the riskiest untested cluster on the board.
|
|
2200
|
+
|
|
2201
|
+
${beliefs}
|
|
2202
|
+
|
|
2203
|
+
## How to run it
|
|
2204
|
+
|
|
2205
|
+
${type === "Observation" ? `Set up a prototype or analytics instrument that captures real user behaviour. Watch how ${cluster.length === 1 ? "this belief plays out" : "these beliefs play out"} in actual usage \u2014 not what people say they'd do, but what they actually do. Log each observation as evidence at the **${rung}** rung.` : type === "Test" ? `Reach out to potential ${lens === "Consumer" ? "customers" : "businesses"} and seek a signed commitment \u2014 a letter of intent, a pre-order, a pilot agreement. The commitment is the evidence; it lands at the **${rung}** rung.` : `Research published sources, competitor behaviour, and market data. Find external evidence that bears on ${cluster.length === 1 ? "this belief" : "these beliefs"}. Log each source as evidence at the **Desk research** rung.`}
|
|
2206
|
+
|
|
2207
|
+
## Questions to answer
|
|
2208
|
+
|
|
2209
|
+
${cluster.map((a, i) => `${i + 1}. Does ${str(a.Title) ?? str(a.id) ?? ""} hold \u2014 or does the evidence break it?`).join("\n")}
|
|
2210
|
+
|
|
2211
|
+
## Pre-registered bars
|
|
2212
|
+
|
|
2213
|
+
- **Right if:** the riskiest belief moves out of the kill zone (Confidence > 0).
|
|
2214
|
+
- **Wrong if:** the evidence invalidates the riskiest belief (Confidence enters the kill zone).
|
|
2215
|
+
`;
|
|
2216
|
+
}
|
|
2020
2217
|
|
|
2021
2218
|
// src/stage-meters.ts
|
|
2022
2219
|
function clamp(pct2) {
|
|
@@ -2255,6 +2452,10 @@ function GridPane({
|
|
|
2255
2452
|
() => buildRecommendedExperiments(assumptions, experiments),
|
|
2256
2453
|
[assumptions, experiments]
|
|
2257
2454
|
);
|
|
2455
|
+
const needsFraming = useMemo2(
|
|
2456
|
+
() => buildNeedsFraming(assumptions),
|
|
2457
|
+
[assumptions]
|
|
2458
|
+
);
|
|
2258
2459
|
const cold = coldStartFor({
|
|
2259
2460
|
assumptions,
|
|
2260
2461
|
experiments,
|
|
@@ -2322,9 +2523,10 @@ function GridPane({
|
|
|
2322
2523
|
] }) }),
|
|
2323
2524
|
/* @__PURE__ */ jsx5("p", { className: "vos-hint vos-stage-grid-foot", children: "The densest cell per row is where that part of the business is. Click a cell to drill into its assumptions, ranked by Risk; a single-assumption cell opens the detail directly." })
|
|
2324
2525
|
] }),
|
|
2325
|
-
recs.length > 0 ? /* @__PURE__ */ jsx5(
|
|
2326
|
-
|
|
2526
|
+
needsFraming.length > 0 || recs.length > 0 ? /* @__PURE__ */ jsx5(
|
|
2527
|
+
NextMovesSection,
|
|
2327
2528
|
{
|
|
2529
|
+
needsFraming,
|
|
2328
2530
|
recs,
|
|
2329
2531
|
onOpenAssumption: (id) => onNavigate({ name: "assumption", id })
|
|
2330
2532
|
}
|
|
@@ -2461,37 +2663,84 @@ function Meter2Seg({
|
|
|
2461
2663
|
] })
|
|
2462
2664
|
] });
|
|
2463
2665
|
}
|
|
2464
|
-
function
|
|
2666
|
+
function NextMovesSection({
|
|
2667
|
+
needsFraming,
|
|
2465
2668
|
recs,
|
|
2466
2669
|
onOpenAssumption
|
|
2467
2670
|
}) {
|
|
2468
|
-
return /* @__PURE__ */ jsxs5("div", { className: "vos-
|
|
2469
|
-
/* @__PURE__ */ jsx5("div", { className: "vos-
|
|
2470
|
-
/* @__PURE__ */
|
|
2471
|
-
/* @__PURE__ */ jsxs5("div", { className: "vos-
|
|
2472
|
-
/* @__PURE__ */
|
|
2473
|
-
|
|
2474
|
-
|
|
2671
|
+
return /* @__PURE__ */ jsxs5("div", { className: "vos-next-moves", children: [
|
|
2672
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-next-moves-head", children: "Next moves" }),
|
|
2673
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-next-moves-cols", children: [
|
|
2674
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-card vos-next-moves-col", children: [
|
|
2675
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-next-moves-col-label", children: [
|
|
2676
|
+
"Needs framing \xB7 top ",
|
|
2677
|
+
needsFraming.length
|
|
2678
|
+
] }),
|
|
2679
|
+
needsFraming.length === 0 ? /* @__PURE__ */ jsx5("div", { className: "vos-muted vos-next-moves-empty", children: "Every belief is fully framed." }) : needsFraming.map((a) => /* @__PURE__ */ jsxs5(
|
|
2680
|
+
"button",
|
|
2681
|
+
{
|
|
2682
|
+
type: "button",
|
|
2683
|
+
className: "vos-next-moves-item",
|
|
2684
|
+
onClick: () => onOpenAssumption(a.id),
|
|
2685
|
+
children: [
|
|
2686
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-next-moves-item-head", children: [
|
|
2687
|
+
/* @__PURE__ */ jsx5("span", { className: "vos-next-moves-item-id vos-num", children: a.id }),
|
|
2688
|
+
/* @__PURE__ */ jsxs5("span", { className: `vos-next-moves-item-risk vos-num vos-text-${riskToneClass(a.risk)}`, children: [
|
|
2689
|
+
Math.round(a.risk),
|
|
2690
|
+
" risk"
|
|
2691
|
+
] })
|
|
2692
|
+
] }),
|
|
2693
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-next-moves-item-title", children: a.title }),
|
|
2694
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-next-moves-item-hint", children: a.hint })
|
|
2695
|
+
]
|
|
2696
|
+
},
|
|
2697
|
+
a.id
|
|
2698
|
+
))
|
|
2475
2699
|
] }),
|
|
2476
|
-
/* @__PURE__ */
|
|
2477
|
-
"
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
children:
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2700
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-card vos-next-moves-col", children: [
|
|
2701
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-next-moves-col-label", children: [
|
|
2702
|
+
"Proposed experiments \xB7 top ",
|
|
2703
|
+
recs.length
|
|
2704
|
+
] }),
|
|
2705
|
+
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("details", { className: "vos-pipe-rec vos-next-moves-rec", children: [
|
|
2706
|
+
/* @__PURE__ */ jsxs5("summary", { className: "vos-pipe-rec-head", children: [
|
|
2707
|
+
/* @__PURE__ */ jsx5("span", { className: "vos-pipe-rec-type", children: rec.type }),
|
|
2708
|
+
/* @__PURE__ */ jsx5("span", { className: "vos-pipe-rec-title", children: rec.title }),
|
|
2709
|
+
/* @__PURE__ */ jsxs5("span", { className: "vos-pipe-rec-risk vos-num", children: [
|
|
2710
|
+
Math.round(rec.maxRisk),
|
|
2711
|
+
" risk"
|
|
2712
|
+
] })
|
|
2713
|
+
] }),
|
|
2714
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-pipe-rec-body-inner", children: [
|
|
2715
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-pipe-rec-chips", children: rec.assumptionIds.map((id) => /* @__PURE__ */ jsx5(
|
|
2716
|
+
"button",
|
|
2717
|
+
{
|
|
2718
|
+
type: "button",
|
|
2719
|
+
className: "vos-pipe-rec-chip",
|
|
2720
|
+
onClick: () => onOpenAssumption(id),
|
|
2721
|
+
children: id
|
|
2722
|
+
},
|
|
2723
|
+
id
|
|
2724
|
+
)) }),
|
|
2725
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-pipe-rec-rationale", children: rec.rationale }),
|
|
2726
|
+
/* @__PURE__ */ jsxs5("div", { className: "vos-pipe-rec-bar", children: [
|
|
2727
|
+
/* @__PURE__ */ jsx5("strong", { children: "Right if:" }),
|
|
2728
|
+
" ",
|
|
2729
|
+
rec.barPreview
|
|
2730
|
+
] }),
|
|
2731
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-pipe-rec-body", children: /* @__PURE__ */ jsx5(EvidenceBody, { text: rec.body }) }),
|
|
2732
|
+
/* @__PURE__ */ jsx5("div", { className: "vos-pipe-rec-actions", children: /* @__PURE__ */ jsx5("button", { type: "button", className: "vos-btn vos-btn-sm vos-btn-accent", children: "Accept & create experiment" }) })
|
|
2733
|
+
] })
|
|
2734
|
+
] }, rec.id))
|
|
2491
2735
|
] })
|
|
2492
|
-
] }
|
|
2736
|
+
] })
|
|
2493
2737
|
] });
|
|
2494
2738
|
}
|
|
2739
|
+
function riskToneClass(risk) {
|
|
2740
|
+
if (risk >= 70) return "crit";
|
|
2741
|
+
if (risk >= 40) return "warn";
|
|
2742
|
+
return "good";
|
|
2743
|
+
}
|
|
2495
2744
|
function Breadcrumb2({
|
|
2496
2745
|
trail
|
|
2497
2746
|
}) {
|
|
@@ -2715,20 +2964,18 @@ function ExperimentDetail({
|
|
|
2715
2964
|
] }),
|
|
2716
2965
|
beliefs.length > 0 ? /* @__PURE__ */ jsx7("div", { className: "vos-reading-quotes", children: beliefs.map((b) => {
|
|
2717
2966
|
const justification = String(b["Grading justification"] ?? "");
|
|
2718
|
-
|
|
2719
|
-
if (!excerpt) return null;
|
|
2967
|
+
if (!justification) return null;
|
|
2720
2968
|
return /* @__PURE__ */ jsxs7(
|
|
2721
2969
|
"div",
|
|
2722
2970
|
{
|
|
2723
|
-
className: `vos-reading-
|
|
2971
|
+
className: `vos-reading-rationale vos-verdict-border-${verdictTone2(b.Result)}`,
|
|
2724
2972
|
children: [
|
|
2725
2973
|
/* @__PURE__ */ jsxs7("span", { className: "vos-reading-quote-id vos-num", children: [
|
|
2726
2974
|
b.assumptionId,
|
|
2727
2975
|
" \xB7"
|
|
2728
2976
|
] }),
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
'"'
|
|
2977
|
+
/* @__PURE__ */ jsx7("span", { className: "vos-reading-rationale-label", children: "grading rationale:" }),
|
|
2978
|
+
justification
|
|
2732
2979
|
]
|
|
2733
2980
|
},
|
|
2734
2981
|
b.assumptionId
|
|
@@ -2827,10 +3074,6 @@ function ExperimentDetail({
|
|
|
2827
3074
|
function pct(n, total) {
|
|
2828
3075
|
return total === 0 ? 0 : n / total * 100;
|
|
2829
3076
|
}
|
|
2830
|
-
function truncate(s, max) {
|
|
2831
|
-
if (s.length <= max) return s;
|
|
2832
|
-
return s.slice(0, max - 1) + "\u2026";
|
|
2833
|
-
}
|
|
2834
3077
|
function verdictTone2(verdict) {
|
|
2835
3078
|
if (verdict === "Validated") return "good";
|
|
2836
3079
|
if (verdict === "Invalidated") return "crit";
|
|
@@ -2982,7 +3225,6 @@ function ReadingDetail({
|
|
|
2982
3225
|
const bl = experiment && Array.isArray(experiment.barLines) ? experiment.barLines.find((x) => x?.assumptionId === b.assumptionId) : null;
|
|
2983
3226
|
const result = String(b.Result ?? "Inconclusive");
|
|
2984
3227
|
const justification = String(b["Grading justification"] ?? "");
|
|
2985
|
-
const excerpt = justification || truncate2(body, 120);
|
|
2986
3228
|
return /* @__PURE__ */ jsxs9("div", { className: `vos-belief-card vos-verdict-border-${verdictTone3(result)}`, children: [
|
|
2987
3229
|
/* @__PURE__ */ jsxs9("div", { className: "vos-belief-head", children: [
|
|
2988
3230
|
/* @__PURE__ */ jsx9(
|
|
@@ -2998,10 +3240,9 @@ function ReadingDetail({
|
|
|
2998
3240
|
/* @__PURE__ */ jsx9("span", { className: `vos-pill vos-pill-${verdictTone3(result)}`, children: result }),
|
|
2999
3241
|
/* @__PURE__ */ jsx9("span", { className: "vos-rung-tag", children: rung })
|
|
3000
3242
|
] }),
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
'"'
|
|
3243
|
+
justification ? /* @__PURE__ */ jsxs9("div", { className: `vos-belief-rationale vos-verdict-border-${verdictTone3(result)}`, children: [
|
|
3244
|
+
/* @__PURE__ */ jsx9("span", { className: "vos-belief-rationale-label", children: "grading rationale:" }),
|
|
3245
|
+
justification
|
|
3005
3246
|
] }) : null,
|
|
3006
3247
|
bl ? /* @__PURE__ */ jsxs9("div", { className: "vos-belief-bar", children: [
|
|
3007
3248
|
/* @__PURE__ */ jsx9("div", { className: "vos-belief-bar-label", children: "Pre-registered bar" }),
|
|
@@ -3049,10 +3290,6 @@ function verdictTone3(verdict) {
|
|
|
3049
3290
|
if (verdict === "Invalidated") return "crit";
|
|
3050
3291
|
return "neutral";
|
|
3051
3292
|
}
|
|
3052
|
-
function truncate2(s, max) {
|
|
3053
|
-
if (s.length <= max) return s;
|
|
3054
|
-
return s.slice(0, max - 1) + "\u2026";
|
|
3055
|
-
}
|
|
3056
3293
|
|
|
3057
3294
|
// src/readings-surface.tsx
|
|
3058
3295
|
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|