@validation-os/dashboard 0.16.0 → 0.16.2

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 CHANGED
@@ -586,6 +586,9 @@ interface PipelineRow {
586
586
  nextMove: string;
587
587
  /** The assumption's Question Type (DEV-5890) — kind of claim. */
588
588
  questionType: string | null;
589
+ /** The max ceiling for this question type (highest anchor across all
590
+ * rungs) — the Known meter fills relative to this, not the absolute 100. */
591
+ questionTypeCeiling: number | null;
589
592
  /** The assumption's Stage (DEV-5890) — kind of response / threshold. */
590
593
  stage: string | null;
591
594
  /** The stage's Risk threshold (DEV-5890) — the stopping bar. */
package/dist/index.js CHANGED
@@ -59,7 +59,7 @@ import {
59
59
  QUESTION_TYPES,
60
60
  STAGES
61
61
  } from "@validation-os/core";
62
- import { isNonEvidence, riskThresholdForStage } from "@validation-os/core/derivation";
62
+ import { confidenceFloorForStage, isNonEvidence, riskThresholdForStage } from "@validation-os/core/derivation";
63
63
 
64
64
  // src/breadcrumb.tsx
65
65
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1518,7 +1518,8 @@ function AssumptionDetail({
1518
1518
  const framed = derived.completeness ?? 0;
1519
1519
  const stageKey = STAGES.find((s) => s === stage);
1520
1520
  const riskThreshold = stageKey ? riskThresholdForStage(stageKey) : null;
1521
- const clearedThreshold = riskThreshold != null ? risk <= riskThreshold : null;
1521
+ const confFloor = stageKey ? confidenceFloorForStage(stageKey) : null;
1522
+ const clearedThreshold = riskThreshold != null && confFloor != null ? risk <= riskThreshold && confidence2 >= confFloor : null;
1522
1523
  const nextMove2 = nextMoveFor(record, experiments.records ?? []);
1523
1524
  const linkedExperiments = liveExperiments(experiments.records ?? []).filter((e) => {
1524
1525
  const ids = Array.isArray(e.barLineAssumptionIds) ? e.barLineAssumptionIds : [];
@@ -1560,24 +1561,17 @@ function AssumptionDetail({
1560
1561
  /* @__PURE__ */ jsx4(ScoreCard, { label: "Confidence", value: formatSigned(confidence2) }),
1561
1562
  /* @__PURE__ */ jsx4(ScoreCard, { label: "Framed", value: `${Math.round(framed)}%` })
1562
1563
  ] }),
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,
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,
1581
1575
  /* @__PURE__ */ jsx4(EvidenceCompositionView, { assumption: record, readings: readings.records ?? [] }),
1582
1576
  /* @__PURE__ */ jsx4(ConfidenceExplainerView, { assumption: record, readings: readings.records ?? [] }),
1583
1577
  statement ? /* @__PURE__ */ jsxs4("div", { className: "vos-card vos-detail-section", children: [
@@ -1800,6 +1794,84 @@ function ScoreCard({
1800
1794
  /* @__PURE__ */ jsx4("div", { className: "vos-score-label", children: label })
1801
1795
  ] });
1802
1796
  }
1797
+ function ThresholdBar({
1798
+ risk,
1799
+ threshold,
1800
+ confidence: confidence2,
1801
+ confFloor,
1802
+ stage,
1803
+ cleared
1804
+ }) {
1805
+ const confPct = Math.min(100, Math.max(0, Math.abs(confidence2)));
1806
+ const confFloorPct = Math.min(100, confFloor);
1807
+ const riskBelow = risk <= threshold;
1808
+ const confAbove = confidence2 >= confFloor;
1809
+ const tone = cleared ? "good" : !riskBelow && !confAbove ? "crit" : "warn";
1810
+ return /* @__PURE__ */ jsxs4("div", { className: "vos-card vos-threshold-bar-card", children: [
1811
+ /* @__PURE__ */ jsxs4("div", { className: "vos-threshold-bar-header", children: [
1812
+ /* @__PURE__ */ jsxs4("span", { className: "vos-threshold-bar-title", children: [
1813
+ "Evidence needed",
1814
+ /* @__PURE__ */ jsx4("span", { className: "vos-threshold-bar-stage-tag", children: stage })
1815
+ ] }),
1816
+ /* @__PURE__ */ jsx4("span", { className: `vos-threshold-bar-status vos-threshold-bar-${tone}`, children: cleared ? "Cleared" : "Needs evidence" })
1817
+ ] }),
1818
+ /* @__PURE__ */ jsxs4("div", { className: "vos-threshold-bar-section", children: [
1819
+ /* @__PURE__ */ jsxs4("div", { className: "vos-threshold-bar-section-label", children: [
1820
+ /* @__PURE__ */ jsx4("span", { children: "Confidence" }),
1821
+ /* @__PURE__ */ jsx4("span", { className: "vos-num vos-threshold-bar-section-val", children: formatSigned(confidence2) }),
1822
+ /* @__PURE__ */ jsxs4("span", { className: "vos-threshold-bar-section-target", children: [
1823
+ "need \u2265 ",
1824
+ confFloor,
1825
+ " ",
1826
+ cleared ? "\u2713" : ""
1827
+ ] })
1828
+ ] }),
1829
+ /* @__PURE__ */ jsxs4("div", { className: "vos-threshold-bar-track vos-threshold-bar-track-tall", children: [
1830
+ /* @__PURE__ */ jsx4(
1831
+ "div",
1832
+ {
1833
+ className: "vos-threshold-bar-target-zone",
1834
+ style: { left: `${confFloorPct}%` }
1835
+ }
1836
+ ),
1837
+ /* @__PURE__ */ jsx4(
1838
+ "div",
1839
+ {
1840
+ className: `vos-threshold-bar-fill vos-fill-${confAbove ? "good" : tone}`,
1841
+ style: { width: `${confPct}%` }
1842
+ }
1843
+ ),
1844
+ /* @__PURE__ */ jsx4(
1845
+ "div",
1846
+ {
1847
+ className: "vos-threshold-bar-marker",
1848
+ style: { left: `${confFloorPct}%` },
1849
+ title: `Need Confidence \u2265 ${confFloor} for ${stage}`,
1850
+ children: /* @__PURE__ */ jsx4("span", { className: "vos-threshold-bar-marker-label vos-num", children: confFloor })
1851
+ }
1852
+ )
1853
+ ] }),
1854
+ /* @__PURE__ */ jsxs4("div", { className: "vos-threshold-bar-scale", children: [
1855
+ /* @__PURE__ */ jsx4("span", { className: "vos-num", children: "0" }),
1856
+ /* @__PURE__ */ jsx4("span", { className: "vos-num vos-threshold-bar-scale-mid", children: "50" }),
1857
+ /* @__PURE__ */ jsx4("span", { className: "vos-num", children: "100" })
1858
+ ] })
1859
+ ] }),
1860
+ /* @__PURE__ */ jsxs4("div", { className: "vos-threshold-bar-risk-summary", children: [
1861
+ /* @__PURE__ */ jsxs4("span", { className: `vos-threshold-bar-risk-val vos-text-${riskBelow ? "good" : "crit"}`, children: [
1862
+ "Risk ",
1863
+ Math.round(risk)
1864
+ ] }),
1865
+ /* @__PURE__ */ jsxs4("span", { className: "vos-threshold-bar-risk-cap", children: [
1866
+ "max ",
1867
+ threshold,
1868
+ " for ",
1869
+ stage
1870
+ ] })
1871
+ ] }),
1872
+ /* @__PURE__ */ jsx4("div", { className: "vos-threshold-bar-hint", children: cleared ? `Confidence ${formatSigned(confidence2)} \u2265 ${confFloor} and Risk ${Math.round(risk)} \u2264 ${threshold} \u2014 cleared for ${stage}.` : `Need Confidence \u2265 ${confFloor}${!confAbove ? ` (now ${formatSigned(confidence2)})` : " \u2713"} and Risk \u2264 ${threshold}${!riskBelow ? ` (now ${Math.round(risk)})` : " \u2713"}.` })
1873
+ ] });
1874
+ }
1803
1875
  function riskTone(risk) {
1804
1876
  if (risk >= 70) return "crit";
1805
1877
  if (risk >= 40) return "warn";
@@ -2134,7 +2206,11 @@ import {
2134
2206
  assumptionCompleteness,
2135
2207
  readingBeliefInputs
2136
2208
  } from "@validation-os/core";
2137
- import { riskThresholdForStage as riskThresholdForStage2 } from "@validation-os/core/derivation";
2209
+ import {
2210
+ confidenceFloorForStage as confidenceFloorForStage2,
2211
+ riskThresholdForStage as riskThresholdForStage2,
2212
+ RUNG_ANCHOR as RUNG_ANCHOR3
2213
+ } from "@validation-os/core/derivation";
2138
2214
 
2139
2215
  // src/stage-grid-model.ts
2140
2216
  var STAGE_ORDER = [
@@ -2331,9 +2407,17 @@ function buildPipeline(assumptions, experiments) {
2331
2407
  const framed = assumptionCompleteness(a);
2332
2408
  const stage = deriveBeliefStage({ framed, confidence: d.confidence, test });
2333
2409
  const questionType = str3(a["Question Type"]);
2410
+ let questionTypeCeiling = null;
2411
+ if (questionType && questionType in RUNG_ANCHOR3) {
2412
+ const subLadder = RUNG_ANCHOR3[questionType];
2413
+ questionTypeCeiling = Math.max(
2414
+ ...Object.values(subLadder).map((r) => r.High)
2415
+ );
2416
+ }
2334
2417
  const stageName = str3(a.Stage);
2335
2418
  const stageKey = stageName && STAGE_ORDER.includes(stageName) ? stageName : null;
2336
2419
  const riskThreshold = stageKey ? riskThresholdForStage2(stageKey) : null;
2420
+ const confFloor = stageKey ? confidenceFloorForStage2(stageKey) : null;
2337
2421
  rows.push({
2338
2422
  id: a.id,
2339
2423
  statement: str3(a.Title),
@@ -2348,9 +2432,10 @@ function buildPipeline(assumptions, experiments) {
2348
2432
  tested: stage.tested,
2349
2433
  nextMove: nextMove(stage.stage, stage.killZone),
2350
2434
  questionType,
2435
+ questionTypeCeiling,
2351
2436
  stage: stageName,
2352
2437
  riskThreshold,
2353
- clearedThreshold: riskThreshold != null ? d.risk <= riskThreshold : null
2438
+ clearedThreshold: riskThreshold != null ? d.risk <= riskThreshold && (confFloor == null || stage.confidence >= confFloor) : null
2354
2439
  });
2355
2440
  }
2356
2441
  rows.sort(
@@ -2842,7 +2927,8 @@ function PipelineBoard({
2842
2927
  }
2843
2928
  function PipelineRowView({ row, onOpen }) {
2844
2929
  const stripeTone = row.riskTone;
2845
- const knownPct = Math.max(0, Math.min(100, Math.abs(row.confidence)));
2930
+ const ceiling = row.questionTypeCeiling ?? 99;
2931
+ const knownPct = Math.max(0, Math.min(100, Math.abs(row.confidence) / ceiling * 100));
2846
2932
  const knownSign = row.confSign;
2847
2933
  return /* @__PURE__ */ jsxs5("div", { className: "vos-pipe-row vos-pipe-row-2seg", children: [
2848
2934
  /* @__PURE__ */ jsx5("div", { className: `vos-pipe-stripe vos-fill-${stripeTone}` }),