@validation-os/dashboard 0.9.0 → 0.11.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.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/dashboard-app.tsx
4
- import { useCallback as useCallback5, useEffect as useEffect6, useState as useState13 } from "react";
4
+ import { useCallback as useCallback5, useEffect as useEffect6, useState as useState14 } from "react";
5
5
 
6
6
  // src/labels.ts
7
7
  var REGISTER_LABEL = {
@@ -49,7 +49,7 @@ var REGISTER_GROUPS = [
49
49
  ];
50
50
  var WORKFLOW_NAV = [
51
51
  { route: "next", label: "Next move", icon: "\u25C8", isDefault: true },
52
- { route: "pipeline", label: "Pipeline", icon: "\u25A6" }
52
+ { route: "pipeline", label: "Pipeline", icon: "\u25A4" }
53
53
  ];
54
54
 
55
55
  // src/next-move.ts
@@ -4781,7 +4781,9 @@ function RegisterBrowser({
4781
4781
  register,
4782
4782
  basePath,
4783
4783
  subtitle,
4784
- onOpenRecord
4784
+ onOpenRecord,
4785
+ lens,
4786
+ stage
4785
4787
  }) {
4786
4788
  const { records, loading, error, refresh: refreshList } = useList(
4787
4789
  register,
@@ -4803,18 +4805,25 @@ function RegisterBrowser({
4803
4805
  } = useRecord(register, openId, basePath);
4804
4806
  const [asOf] = useState10(() => (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
4805
4807
  const rows = records ?? [];
4808
+ const prefiltered = useMemo4(() => {
4809
+ return rows.filter((r) => {
4810
+ if (lens !== void 0 && (r.Lens ?? "") !== lens) return false;
4811
+ if (stage !== void 0 && (r.Stage ?? "") !== stage) return false;
4812
+ return true;
4813
+ });
4814
+ }, [rows, lens, stage]);
4806
4815
  const ctx = {
4807
4816
  asOf,
4808
- assumptions: register === "assumptions" ? rows : assumptions.records ?? [],
4809
- experiments: register === "experiments" ? rows : experiments.records ?? [],
4810
- readings: register === "readings" ? rows : readings.records ?? [],
4811
- decisions: register === "decisions" ? rows : []
4817
+ assumptions: register === "assumptions" ? prefiltered : assumptions.records ?? [],
4818
+ experiments: register === "experiments" ? prefiltered : experiments.records ?? [],
4819
+ readings: register === "readings" ? prefiltered : readings.records ?? [],
4820
+ decisions: register === "decisions" ? prefiltered : []
4812
4821
  };
4813
4822
  const shaped = useMemo4(
4814
- () => shapeRegister(register, rows, descriptor, ctx),
4823
+ () => shapeRegister(register, prefiltered, descriptor, ctx),
4815
4824
  // ctx is derived from the same inputs; listing them keeps the memo honest.
4816
4825
  // eslint-disable-next-line react-hooks/exhaustive-deps
4817
- [register, rows, descriptor, ctx.assumptions, ctx.experiments, ctx.readings, asOf]
4826
+ [register, prefiltered, descriptor, ctx.assumptions, ctx.experiments, ctx.readings, asOf]
4818
4827
  );
4819
4828
  const counts = needsHumanCounts(ctx);
4820
4829
  const humanCount = {
@@ -5420,28 +5429,352 @@ function StageStepper({ move }) {
5420
5429
  )) });
5421
5430
  }
5422
5431
 
5432
+ // src/stage-grid-surface.tsx
5433
+ import { useMemo as useMemo6, useState as useState12 } from "react";
5434
+
5435
+ // src/stage-grid-model.ts
5436
+ var STAGE_ORDER = [
5437
+ "Discovery",
5438
+ "Validation",
5439
+ "Scale",
5440
+ "Maturity"
5441
+ ];
5442
+ var STAGE_GLOSS = {
5443
+ Discovery: "Problem-solution fit \u2014 will they engage, care, disclose?",
5444
+ Validation: "Product-market fit \u2014 will they pay, sign, stay?",
5445
+ Scale: "Growth \u2014 can we acquire efficiently, does CAC<LTV hold at volume?",
5446
+ Maturity: "Defense \u2014 will incumbents respond, will regulators accept?"
5447
+ };
5448
+ var NO_LENS = "\u2014";
5449
+ var NO_STAGE = "\u2014";
5450
+ function stageOf(record) {
5451
+ const v = str(record.Stage);
5452
+ if (!v) return null;
5453
+ return STAGE_ORDER.includes(v) ? v : null;
5454
+ }
5455
+ function rankByRisk(records) {
5456
+ return [...records].map((r) => ({ r, risk: derivedNum(r, "risk") ?? 0 })).sort(
5457
+ (a, b) => a.risk !== b.risk ? b.risk - a.risk : a.r.id.localeCompare(b.r.id)
5458
+ ).map((x) => x.r);
5459
+ }
5460
+ function buildStageGrid(assumptions) {
5461
+ const lensOrder = [];
5462
+ const seenLens = /* @__PURE__ */ new Set();
5463
+ const pushLens = (lens) => {
5464
+ if (!seenLens.has(lens)) {
5465
+ seenLens.add(lens);
5466
+ lensOrder.push(lens);
5467
+ }
5468
+ };
5469
+ const buckets = /* @__PURE__ */ new Map();
5470
+ const ensureLens = (lens) => {
5471
+ let m = buckets.get(lens);
5472
+ if (!m) {
5473
+ m = /* @__PURE__ */ new Map();
5474
+ buckets.set(lens, m);
5475
+ }
5476
+ return m;
5477
+ };
5478
+ let hasNoLens = false;
5479
+ let hasNoStage = false;
5480
+ for (const a of assumptions) {
5481
+ const lens = str(a.Lens) ?? NO_LENS;
5482
+ const stage = stageOf(a) ?? NO_STAGE;
5483
+ if (lens === NO_LENS) hasNoLens = true;
5484
+ if (stage === NO_STAGE) hasNoStage = true;
5485
+ pushLens(lens);
5486
+ const byStage = ensureLens(lens);
5487
+ let list = byStage.get(stage);
5488
+ if (!list) {
5489
+ list = [];
5490
+ byStage.set(stage, list);
5491
+ }
5492
+ list.push(a);
5493
+ }
5494
+ if (hasNoLens) {
5495
+ lensOrder.splice(lensOrder.indexOf(NO_LENS), 1);
5496
+ lensOrder.push(NO_LENS);
5497
+ } else {
5498
+ const idx = lensOrder.indexOf(NO_LENS);
5499
+ if (idx >= 0) lensOrder.splice(idx, 1);
5500
+ }
5501
+ const stageOrder = [...STAGE_ORDER];
5502
+ if (hasNoStage) stageOrder.push(NO_STAGE);
5503
+ const cells = [];
5504
+ let maxCellCount = 0;
5505
+ let total = 0;
5506
+ for (const lens of lensOrder) {
5507
+ const byStage = buckets.get(lens) ?? /* @__PURE__ */ new Map();
5508
+ for (const stage of stageOrder) {
5509
+ const records = byStage.get(stage) ?? [];
5510
+ const ranked = rankByRisk(records);
5511
+ const count = ranked.length;
5512
+ maxCellCount = Math.max(maxCellCount, count);
5513
+ total += count;
5514
+ cells.push({
5515
+ lens,
5516
+ stage,
5517
+ count,
5518
+ assumptions: ranked,
5519
+ // density filled in a second pass once maxCellCount is known.
5520
+ density: 0
5521
+ });
5522
+ }
5523
+ }
5524
+ const norm2 = maxCellCount > 0 ? 1 / maxCellCount : 0;
5525
+ for (const cell of cells) {
5526
+ cell.density = cell.count * norm2;
5527
+ }
5528
+ return {
5529
+ lenses: lensOrder,
5530
+ stages: [...STAGE_ORDER],
5531
+ cells,
5532
+ maxCellCount,
5533
+ total
5534
+ };
5535
+ }
5536
+ function cellAt(view, lens, stage) {
5537
+ return view.cells.find((c) => c.lens === lens && c.stage === stage) ?? null;
5538
+ }
5539
+
5540
+ // src/stage-grid-surface.tsx
5541
+ import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
5542
+ function StageGridSurface({ basePath, onNavigate }) {
5543
+ const assumptions = useList("assumptions", basePath);
5544
+ const loading = assumptions.loading;
5545
+ const error = assumptions.error;
5546
+ const [open, setOpen] = useState12(null);
5547
+ const view = useMemo6(
5548
+ () => buildStageGrid(assumptions.records ?? []),
5549
+ [assumptions.records]
5550
+ );
5551
+ const refresh = () => assumptions.refresh();
5552
+ const openRecord = (id) => onNavigate({ name: "record", id });
5553
+ if (loading && !assumptions.records) {
5554
+ return /* @__PURE__ */ jsx18(StageGridFrame, { children: /* @__PURE__ */ jsx18("div", { className: "vos-empty", children: "Reading where your bets cluster\u2026" }) });
5555
+ }
5556
+ if (error) {
5557
+ return /* @__PURE__ */ jsx18(StageGridFrame, { children: /* @__PURE__ */ jsx18("div", { className: "vos-banner vos-banner-crit", children: /* @__PURE__ */ jsxs17("div", { className: "vos-banner-body", children: [
5558
+ /* @__PURE__ */ jsx18("b", { children: "Couldn't load the grid." }),
5559
+ /* @__PURE__ */ jsx18("span", { children: error })
5560
+ ] }) }) });
5561
+ }
5562
+ const cold = coldStartFor({
5563
+ assumptions: assumptions.records ?? [],
5564
+ experiments: [],
5565
+ readings: [],
5566
+ decisions: []
5567
+ });
5568
+ if (cold.cold) {
5569
+ return /* @__PURE__ */ jsxs17(StageGridFrame, { children: [
5570
+ /* @__PURE__ */ jsx18("div", { className: "vos-firstrun", children: FIRST_RUN_LINE }),
5571
+ /* @__PURE__ */ jsxs17("div", { className: "vos-card vos-cold vos-cold-stage-grid", children: [
5572
+ /* @__PURE__ */ jsx18("span", { className: "vos-cold-eyebrow", children: "No bets yet" }),
5573
+ /* @__PURE__ */ jsx18("p", { className: "vos-cold-body", children: "The Lens \xD7 Stage grid reads your business state off where your bets cluster. Write your first belief and the grid fills in \u2014 the densest cell per row is where that part of the business is." }),
5574
+ /* @__PURE__ */ jsx18(
5575
+ "button",
5576
+ {
5577
+ type: "button",
5578
+ className: "vos-btn",
5579
+ onClick: () => onNavigate({ name: "records", register: "assumptions", view: "all" }),
5580
+ children: "Write your first bet"
5581
+ }
5582
+ )
5583
+ ] })
5584
+ ] });
5585
+ }
5586
+ return /* @__PURE__ */ jsxs17(StageGridFrame, { total: view.total, onRefresh: refresh, onNavigate, children: [
5587
+ /* @__PURE__ */ jsxs17("div", { className: "vos-card vos-stage-grid-card", children: [
5588
+ /* @__PURE__ */ jsx18("div", { className: "vos-stage-grid-scroll", children: /* @__PURE__ */ jsxs17("table", { className: "vos-stage-grid", role: "grid", "aria-label": "Lens \xD7 Stage heatmap", children: [
5589
+ /* @__PURE__ */ jsx18("thead", { children: /* @__PURE__ */ jsxs17("tr", { children: [
5590
+ /* @__PURE__ */ jsx18("th", { scope: "col", className: "vos-stage-grid-corner", children: "Lens \u2193 / Stage \u2192" }),
5591
+ view.stages.map((stage) => /* @__PURE__ */ jsxs17("th", { scope: "col", className: "vos-stage-grid-col", children: [
5592
+ /* @__PURE__ */ jsx18("span", { className: "vos-stage-grid-stagename", children: stage }),
5593
+ /* @__PURE__ */ jsx18("span", { className: "vos-stage-grid-stagegloss", children: STAGE_GLOSS[stage] })
5594
+ ] }, stage))
5595
+ ] }) }),
5596
+ /* @__PURE__ */ jsx18("tbody", { children: view.lenses.map((lens) => /* @__PURE__ */ jsxs17("tr", { children: [
5597
+ /* @__PURE__ */ jsx18("th", { scope: "row", className: "vos-stage-grid-rowhead", children: lens }),
5598
+ view.stages.map((stage) => {
5599
+ const cell = cellAt(view, lens, stage);
5600
+ return /* @__PURE__ */ jsx18(
5601
+ StageCell,
5602
+ {
5603
+ cell,
5604
+ onClick: () => setOpen(cell)
5605
+ },
5606
+ stage
5607
+ );
5608
+ })
5609
+ ] }, lens)) })
5610
+ ] }) }),
5611
+ /* @__PURE__ */ jsx18("p", { className: "vos-hint vos-stage-grid-foot", children: "The densest cell per row is where that part of the business is \u2014 no flag, no declaration, the density tells you. Click a cell to drill into its assumptions, ranked by Risk. Thin/empty cells are gaps: Consumer \xD7 Maturity is honestly 0 (consumers don't drive defense bets); a thin Commercial \xD7 Scale row is under-tracking scale." })
5612
+ ] }),
5613
+ /* @__PURE__ */ jsx18(
5614
+ CellDrawer,
5615
+ {
5616
+ cell: open,
5617
+ onClose: () => setOpen(null),
5618
+ onOpenRecord: openRecord,
5619
+ onNavigate
5620
+ }
5621
+ )
5622
+ ] });
5623
+ }
5624
+ function StageGridFrame({
5625
+ total,
5626
+ onRefresh,
5627
+ onNavigate,
5628
+ children
5629
+ }) {
5630
+ return /* @__PURE__ */ jsxs17("div", { children: [
5631
+ /* @__PURE__ */ jsxs17("div", { className: "vos-head", children: [
5632
+ /* @__PURE__ */ jsxs17("div", { children: [
5633
+ /* @__PURE__ */ jsx18("h1", { children: "Lens \xD7 Stage \u2014 where your bets cluster" }),
5634
+ /* @__PURE__ */ jsx18("p", { children: "Every belief cross-tabbed by the actor it's about (Lens) and the kind of response it tests (Stage). The grid is the filter; Risk is the rank." })
5635
+ ] }),
5636
+ /* @__PURE__ */ jsx18("div", { className: "vos-spacer" }),
5637
+ total !== void 0 ? /* @__PURE__ */ jsxs17("span", { className: "vos-hint vos-stage-grid-total", children: [
5638
+ total,
5639
+ " ",
5640
+ total === 1 ? "belief" : "beliefs"
5641
+ ] }) : null,
5642
+ onRefresh ? /* @__PURE__ */ jsx18(
5643
+ "button",
5644
+ {
5645
+ type: "button",
5646
+ className: "vos-btn vos-btn-ghost",
5647
+ onClick: onRefresh,
5648
+ children: "\u21BB Refresh"
5649
+ }
5650
+ ) : null,
5651
+ onNavigate ? /* @__PURE__ */ jsx18(
5652
+ "button",
5653
+ {
5654
+ type: "button",
5655
+ className: "vos-btn vos-btn-ghost",
5656
+ onClick: () => onNavigate({ name: "records", register: "assumptions", view: "all" }),
5657
+ children: "View all \u2192"
5658
+ }
5659
+ ) : null
5660
+ ] }),
5661
+ /* @__PURE__ */ jsx18("div", { className: "vos-stage-grid-host", children })
5662
+ ] });
5663
+ }
5664
+ function StageCell({
5665
+ cell,
5666
+ onClick
5667
+ }) {
5668
+ const empty = cell.count === 0;
5669
+ const style = empty ? void 0 : {
5670
+ // Heatmap fill: opacity 0.08 → 0.92 across the density range, on the
5671
+ // accent token. Keeps the grid readable in both themes.
5672
+ "--vos-cell-alpha": (0.08 + cell.density * 0.84).toFixed(3)
5673
+ };
5674
+ return /* @__PURE__ */ jsx18(
5675
+ "td",
5676
+ {
5677
+ className: `vos-stage-grid-cell${empty ? " vos-stage-grid-cell-empty" : ""}`,
5678
+ style,
5679
+ children: /* @__PURE__ */ jsx18(
5680
+ "button",
5681
+ {
5682
+ type: "button",
5683
+ className: "vos-stage-grid-btn",
5684
+ onClick,
5685
+ "aria-label": `${cell.lens} \xD7 ${cell.stage}: ${cell.count} ${cell.count === 1 ? "belief" : "beliefs"}`,
5686
+ title: empty ? "No beliefs in this cell" : `Riskiest: ${cell.assumptions[0]?.Title ?? "\u2014"}`,
5687
+ children: /* @__PURE__ */ jsx18("span", { className: "vos-stage-grid-count vos-num", children: cell.count })
5688
+ }
5689
+ )
5690
+ }
5691
+ );
5692
+ }
5693
+ function CellDrawer({
5694
+ cell,
5695
+ onClose,
5696
+ onOpenRecord,
5697
+ onNavigate
5698
+ }) {
5699
+ return /* @__PURE__ */ jsxs17(
5700
+ DrawerShell,
5701
+ {
5702
+ open: cell !== null,
5703
+ onClose,
5704
+ ariaLabel: cell ? `${cell.lens} \xD7 ${cell.stage} \u2014 ${cell.count} ${cell.count === 1 ? "belief" : "beliefs"}` : "Cell drill-through",
5705
+ children: [
5706
+ /* @__PURE__ */ jsxs17("header", { className: "vos-drawer-header", children: [
5707
+ /* @__PURE__ */ jsxs17("div", { children: [
5708
+ /* @__PURE__ */ jsx18("p", { className: "vos-drawer-eyebrow", children: "Lens \xD7 Stage" }),
5709
+ /* @__PURE__ */ jsxs17("h2", { className: "vos-drawer-title", children: [
5710
+ cell?.lens,
5711
+ " \xD7 ",
5712
+ cell?.stage
5713
+ ] }),
5714
+ /* @__PURE__ */ jsx18("p", { className: "vos-hint", children: cell && cell.count > 0 ? `${cell.count} ${cell.count === 1 ? "belief" : "beliefs"}, ranked by Risk \u2014 riskiest first.` : "No beliefs in this cell." })
5715
+ ] }),
5716
+ cell && cell.stage !== "\u2014" && onNavigate && /* @__PURE__ */ jsx18(
5717
+ "button",
5718
+ {
5719
+ type: "button",
5720
+ className: "vos-btn vos-btn-secondary",
5721
+ onClick: () => onNavigate({
5722
+ name: "records",
5723
+ register: "assumptions",
5724
+ lens: cell.lens === "\u2014" ? void 0 : cell.lens,
5725
+ stage: cell.stage
5726
+ }),
5727
+ children: "Open in table \u2192"
5728
+ }
5729
+ )
5730
+ ] }),
5731
+ cell && cell.count > 0 ? /* @__PURE__ */ jsx18("div", { className: "vos-stage-grid-drawer-body", children: /* @__PURE__ */ jsx18(
5732
+ RegisterTable,
5733
+ {
5734
+ register: "assumptions",
5735
+ records: cell.assumptions,
5736
+ onRowClick: onOpenRecord
5737
+ }
5738
+ ) }) : cell ? /* @__PURE__ */ jsx18("p", { className: "vos-empty", style: { margin: 16 }, children: "No beliefs in this cell \u2014 a gap, not a bug." }) : null
5739
+ ]
5740
+ }
5741
+ );
5742
+ }
5743
+
5423
5744
  // src/route.ts
5424
5745
  var DEFAULT_ROUTE = { name: "next" };
5425
5746
  function parseRoute(hash, registers) {
5426
5747
  const h = hash.replace(/^#\/?/, "");
5427
5748
  if (!h) return DEFAULT_ROUTE;
5428
- const parts = h.split("/");
5749
+ const [pathPart = "", queryPart = ""] = h.split("?");
5750
+ const parts = pathPart.split("/");
5429
5751
  const head = parts[0] ?? "";
5752
+ const query = new URLSearchParams(queryPart ?? "");
5753
+ const lens = query.get("lens") ?? void 0;
5754
+ const stage = query.get("stage") ?? void 0;
5755
+ const view = query.get("view") ?? void 0;
5430
5756
  if (head === "next") return { name: "next" };
5431
5757
  if (head === "pipeline") return { name: "pipeline" };
5758
+ if (head === "stage-grid") return { name: "stage-grid" };
5432
5759
  if (head === "record") {
5433
5760
  const id = parts.slice(1).join("/");
5434
5761
  return id ? { name: "record", id } : DEFAULT_ROUTE;
5435
5762
  }
5436
5763
  if (registers.includes(head)) {
5437
- return { name: "records", register: head };
5764
+ return { name: "records", register: head, lens, stage, view };
5438
5765
  }
5439
5766
  return DEFAULT_ROUTE;
5440
5767
  }
5441
5768
  function formatRoute(route) {
5442
5769
  switch (route.name) {
5443
- case "records":
5444
- return route.register;
5770
+ case "records": {
5771
+ const q = new URLSearchParams();
5772
+ if (route.lens) q.set("lens", route.lens);
5773
+ if (route.stage) q.set("stage", route.stage);
5774
+ if (route.view) q.set("view", route.view);
5775
+ const qs = q.toString();
5776
+ return qs ? `${route.register}?${qs}` : route.register;
5777
+ }
5445
5778
  case "record":
5446
5779
  return `record/${route.id}`;
5447
5780
  default:
@@ -5450,7 +5783,7 @@ function formatRoute(route) {
5450
5783
  }
5451
5784
 
5452
5785
  // src/sidebar-nav.tsx
5453
- import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
5786
+ import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
5454
5787
  function SidebarNav({
5455
5788
  route,
5456
5789
  onNavigate,
@@ -5459,12 +5792,12 @@ function SidebarNav({
5459
5792
  registers
5460
5793
  }) {
5461
5794
  const activeRegister = route.name === "records" ? route.register : null;
5462
- return /* @__PURE__ */ jsxs17("nav", { className: "vos-nav", "aria-label": "Navigation", children: [
5463
- /* @__PURE__ */ jsxs17("div", { children: [
5464
- /* @__PURE__ */ jsx18("div", { className: "vos-nav-group", children: "Workflow" }),
5795
+ return /* @__PURE__ */ jsxs18("nav", { className: "vos-nav", "aria-label": "Navigation", children: [
5796
+ /* @__PURE__ */ jsxs18("div", { children: [
5797
+ /* @__PURE__ */ jsx19("div", { className: "vos-nav-group", children: "Workflow" }),
5465
5798
  WORKFLOW_NAV.map((item) => {
5466
5799
  const active = route.name === item.route;
5467
- return /* @__PURE__ */ jsxs17(
5800
+ return /* @__PURE__ */ jsxs18(
5468
5801
  "button",
5469
5802
  {
5470
5803
  type: "button",
@@ -5472,20 +5805,20 @@ function SidebarNav({
5472
5805
  "aria-current": active ? "page" : void 0,
5473
5806
  onClick: () => onNavigate({ name: item.route }),
5474
5807
  children: [
5475
- /* @__PURE__ */ jsx18("span", { className: "vos-nav-ic", "aria-hidden": "true", children: item.icon }),
5808
+ /* @__PURE__ */ jsx19("span", { className: "vos-nav-ic", "aria-hidden": "true", children: item.icon }),
5476
5809
  item.label,
5477
- item.isDefault ? /* @__PURE__ */ jsx18("span", { className: "vos-nav-default", children: "home" }) : null
5810
+ item.isDefault ? /* @__PURE__ */ jsx19("span", { className: "vos-nav-default", children: "home" }) : null
5478
5811
  ]
5479
5812
  },
5480
5813
  item.route
5481
5814
  );
5482
5815
  })
5483
5816
  ] }),
5484
- /* @__PURE__ */ jsxs17("div", { children: [
5485
- /* @__PURE__ */ jsx18("div", { className: "vos-nav-group", children: "Records" }),
5817
+ /* @__PURE__ */ jsxs18("div", { children: [
5818
+ /* @__PURE__ */ jsx19("div", { className: "vos-nav-group", children: "Records" }),
5486
5819
  registers.map((register) => {
5487
5820
  const active = register === activeRegister;
5488
- return /* @__PURE__ */ jsxs17(
5821
+ return /* @__PURE__ */ jsxs18(
5489
5822
  "button",
5490
5823
  {
5491
5824
  type: "button",
@@ -5493,9 +5826,9 @@ function SidebarNav({
5493
5826
  "aria-current": active ? "page" : void 0,
5494
5827
  onClick: () => onNavigate({ name: "records", register }),
5495
5828
  children: [
5496
- /* @__PURE__ */ jsx18("span", { className: "vos-nav-ic", "aria-hidden": "true", children: REGISTER_ICON[register] }),
5829
+ /* @__PURE__ */ jsx19("span", { className: "vos-nav-ic", "aria-hidden": "true", children: REGISTER_ICON[register] }),
5497
5830
  REGISTER_LABEL[register],
5498
- needsHuman?.[register] ? /* @__PURE__ */ jsx18(
5831
+ needsHuman?.[register] ? /* @__PURE__ */ jsx19(
5499
5832
  "span",
5500
5833
  {
5501
5834
  className: "vos-nav-alert",
@@ -5503,7 +5836,7 @@ function SidebarNav({
5503
5836
  children: formatCount(needsHuman[register] ?? 0)
5504
5837
  }
5505
5838
  ) : null,
5506
- /* @__PURE__ */ jsx18("span", { className: "vos-nav-count vos-num", children: counts?.[register] !== void 0 ? formatCount(counts[register] ?? 0) : "\xB7" })
5839
+ /* @__PURE__ */ jsx19("span", { className: "vos-nav-count vos-num", children: counts?.[register] !== void 0 ? formatCount(counts[register] ?? 0) : "\xB7" })
5507
5840
  ]
5508
5841
  },
5509
5842
  register
@@ -5514,12 +5847,12 @@ function SidebarNav({
5514
5847
  }
5515
5848
 
5516
5849
  // src/use-counts.ts
5517
- import { useCallback as useCallback4, useEffect as useEffect5, useMemo as useMemo6, useState as useState12 } from "react";
5850
+ import { useCallback as useCallback4, useEffect as useEffect5, useMemo as useMemo7, useState as useState13 } from "react";
5518
5851
  function useCounts(basePath = "/api") {
5519
- const [counts, setCounts] = useState12(null);
5520
- const [loading, setLoading] = useState12(true);
5521
- const [error, setError] = useState12(null);
5522
- const [tick, setTick] = useState12(0);
5852
+ const [counts, setCounts] = useState13(null);
5853
+ const [loading, setLoading] = useState13(true);
5854
+ const [error, setError] = useState13(null);
5855
+ const [tick, setTick] = useState13(0);
5523
5856
  useEffect5(() => {
5524
5857
  let live = true;
5525
5858
  setLoading(true);
@@ -5547,8 +5880,8 @@ function useNeedsHuman(basePath = "/api") {
5547
5880
  const assumptions = useList("assumptions", basePath);
5548
5881
  const experiments = useList("experiments", basePath);
5549
5882
  const decisions = useList("decisions", basePath);
5550
- const [asOf] = useState12(() => (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
5551
- return useMemo6(() => {
5883
+ const [asOf] = useState13(() => (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
5884
+ return useMemo7(() => {
5552
5885
  const counts = needsHumanCounts({
5553
5886
  asOf,
5554
5887
  assumptions: assumptions.records ?? [],
@@ -5564,7 +5897,7 @@ function useNeedsHuman(basePath = "/api") {
5564
5897
  }
5565
5898
 
5566
5899
  // src/dashboard-app.tsx
5567
- import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
5900
+ import { jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
5568
5901
  function initialsOf(name) {
5569
5902
  const parts = name.trim().split(/\s+/);
5570
5903
  const first = parts[0]?.[0] ?? "";
@@ -5580,7 +5913,7 @@ function ValidationOSDashboard({ config = {} }) {
5580
5913
  user,
5581
5914
  registers = REGISTER_ORDER
5582
5915
  } = config;
5583
- const [route, setRoute] = useState13(
5916
+ const [route, setRoute] = useState14(
5584
5917
  () => typeof window === "undefined" ? { name: "next" } : parseRoute(window.location.hash, registers)
5585
5918
  );
5586
5919
  const { counts } = useCounts(basePath);
@@ -5606,33 +5939,33 @@ function ValidationOSDashboard({ config = {} }) {
5606
5939
  }, []);
5607
5940
  const brandName = branding?.name ?? "Validation-OS";
5608
5941
  const brandMark = branding?.initials ?? "V";
5609
- return /* @__PURE__ */ jsxs18("div", { className: "vos-app", children: [
5610
- /* @__PURE__ */ jsxs18("div", { className: "vos-brand", children: [
5611
- /* @__PURE__ */ jsx19("span", { className: "vos-brand-dot", children: branding?.logoUrl ? /* @__PURE__ */ jsx19("img", { src: branding.logoUrl, alt: "" }) : brandMark }),
5942
+ return /* @__PURE__ */ jsxs19("div", { className: "vos-app", children: [
5943
+ /* @__PURE__ */ jsxs19("div", { className: "vos-brand", children: [
5944
+ /* @__PURE__ */ jsx20("span", { className: "vos-brand-dot", children: branding?.logoUrl ? /* @__PURE__ */ jsx20("img", { src: branding.logoUrl, alt: "" }) : brandMark }),
5612
5945
  " ",
5613
5946
  brandName
5614
5947
  ] }),
5615
- /* @__PURE__ */ jsxs18("div", { className: "vos-topbar", children: [
5616
- backendLabel ? /* @__PURE__ */ jsxs18("div", { className: "vos-backend", children: [
5617
- /* @__PURE__ */ jsx19("span", { className: "vos-live-dot" }),
5948
+ /* @__PURE__ */ jsxs19("div", { className: "vos-topbar", children: [
5949
+ backendLabel ? /* @__PURE__ */ jsxs19("div", { className: "vos-backend", children: [
5950
+ /* @__PURE__ */ jsx20("span", { className: "vos-live-dot" }),
5618
5951
  " Backend: ",
5619
- /* @__PURE__ */ jsx19("b", { children: backendLabel })
5952
+ /* @__PURE__ */ jsx20("b", { children: backendLabel })
5620
5953
  ] }) : null,
5621
- agentLabel ? /* @__PURE__ */ jsxs18("span", { className: "vos-hint", children: [
5954
+ agentLabel ? /* @__PURE__ */ jsxs19("span", { className: "vos-hint", children: [
5622
5955
  "Agent: ",
5623
- /* @__PURE__ */ jsx19("b", { style: { color: "var(--vos-text)" }, children: agentLabel })
5956
+ /* @__PURE__ */ jsx20("b", { style: { color: "var(--vos-text)" }, children: agentLabel })
5624
5957
  ] }) : null,
5625
- /* @__PURE__ */ jsx19("div", { className: "vos-spacer" }),
5626
- /* @__PURE__ */ jsx19("button", { type: "button", className: "vos-iconbtn", onClick: toggleTheme, children: "\u25D0 Theme" }),
5627
- user?.name ? /* @__PURE__ */ jsxs18("div", { className: "vos-user", children: [
5628
- /* @__PURE__ */ jsx19("span", { className: "vos-avatar", children: initialsOf(user.name) }),
5629
- /* @__PURE__ */ jsxs18("div", { children: [
5630
- /* @__PURE__ */ jsx19("span", { className: "vos-user-name", children: user.name }),
5631
- user.caption ? /* @__PURE__ */ jsx19("small", { children: user.caption }) : null
5958
+ /* @__PURE__ */ jsx20("div", { className: "vos-spacer" }),
5959
+ /* @__PURE__ */ jsx20("button", { type: "button", className: "vos-iconbtn", onClick: toggleTheme, children: "\u25D0 Theme" }),
5960
+ user?.name ? /* @__PURE__ */ jsxs19("div", { className: "vos-user", children: [
5961
+ /* @__PURE__ */ jsx20("span", { className: "vos-avatar", children: initialsOf(user.name) }),
5962
+ /* @__PURE__ */ jsxs19("div", { children: [
5963
+ /* @__PURE__ */ jsx20("span", { className: "vos-user-name", children: user.name }),
5964
+ user.caption ? /* @__PURE__ */ jsx20("small", { children: user.caption }) : null
5632
5965
  ] })
5633
5966
  ] }) : null
5634
5967
  ] }),
5635
- /* @__PURE__ */ jsx19(
5968
+ /* @__PURE__ */ jsx20(
5636
5969
  SidebarNav,
5637
5970
  {
5638
5971
  route,
@@ -5642,16 +5975,23 @@ function ValidationOSDashboard({ config = {} }) {
5642
5975
  registers
5643
5976
  }
5644
5977
  ),
5645
- /* @__PURE__ */ jsx19("main", { className: "vos-main", children: route.name === "records" ? /* @__PURE__ */ jsx19(
5646
- RegisterBrowser,
5647
- {
5648
- register: route.register,
5649
- basePath,
5650
- subtitle: REGISTER_SUBTITLE[route.register],
5651
- onOpenRecord: (id) => navigate({ name: "record", id })
5652
- },
5653
- route.register
5654
- ) : route.name === "record" ? /* @__PURE__ */ jsx19(
5978
+ /* @__PURE__ */ jsx20("main", { className: "vos-main", children: route.name === "records" ? (
5979
+ // The assumptions register renders the grid as its landing surface
5980
+ // (#assumptions with no query); cell-click and "view all" drill into
5981
+ // the filtered/unfiltered table by setting lens/stage/view on the route.
5982
+ route.register === "assumptions" && !route.lens && !route.stage && route.view !== "all" ? /* @__PURE__ */ jsx20(StageGridSurface, { basePath, onNavigate: navigate }, "stage-grid") : /* @__PURE__ */ jsx20(
5983
+ RegisterBrowser,
5984
+ {
5985
+ register: route.register,
5986
+ basePath,
5987
+ subtitle: REGISTER_SUBTITLE[route.register],
5988
+ onOpenRecord: (id) => navigate({ name: "record", id }),
5989
+ lens: route.lens,
5990
+ stage: route.stage
5991
+ },
5992
+ route.register + (route.lens ?? "") + (route.stage ?? "") + (route.view ?? "")
5993
+ )
5994
+ ) : route.name === "record" ? /* @__PURE__ */ jsx20(
5655
5995
  RecordPage,
5656
5996
  {
5657
5997
  recordId: route.id,
@@ -5660,7 +6000,7 @@ function ValidationOSDashboard({ config = {} }) {
5660
6000
  basePath
5661
6001
  },
5662
6002
  route.id
5663
- ) : route.name === "pipeline" ? /* @__PURE__ */ jsx19(PipelineSurface, { basePath, onNavigate: navigate }, "pipeline") : /* @__PURE__ */ jsx19(NextMoveSurface, { basePath, onNavigate: navigate }, "next") })
6003
+ ) : route.name === "pipeline" ? /* @__PURE__ */ jsx20(PipelineSurface, { basePath, onNavigate: navigate }, "pipeline") : route.name === "stage-grid" ? /* @__PURE__ */ jsx20(StageGridSurface, { basePath, onNavigate: navigate }, "stage-grid") : /* @__PURE__ */ jsx20(NextMoveSurface, { basePath, onNavigate: navigate }, "next") })
5664
6004
  ] });
5665
6005
  }
5666
6006
 
@@ -5690,15 +6030,15 @@ function composeConnectCommand(input) {
5690
6030
  }
5691
6031
 
5692
6032
  // src/connect-claude-code.tsx
5693
- import { useState as useState14 } from "react";
5694
- import { jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
6033
+ import { useState as useState15 } from "react";
6034
+ import { jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
5695
6035
  function ConnectClaudeCode({
5696
6036
  apiBaseUrl,
5697
6037
  tokenEnv,
5698
6038
  mintToken
5699
6039
  }) {
5700
- const [state, setState] = useState14({ phase: "idle" });
5701
- const [copied, setCopied] = useState14(false);
6040
+ const [state, setState] = useState15({ phase: "idle" });
6041
+ const [copied, setCopied] = useState15(false);
5702
6042
  async function generate() {
5703
6043
  setState({ phase: "minting" });
5704
6044
  setCopied(false);
@@ -5720,21 +6060,21 @@ function ConnectClaudeCode({
5720
6060
  } catch {
5721
6061
  }
5722
6062
  }
5723
- return /* @__PURE__ */ jsxs19("div", { children: [
5724
- /* @__PURE__ */ jsx20("div", { className: "vos-head", children: /* @__PURE__ */ jsxs19("div", { children: [
5725
- /* @__PURE__ */ jsx20("h1", { children: "Connect Claude Code" }),
5726
- /* @__PURE__ */ jsx20("p", { children: "Run the validation skills against this register from your own Claude Code \u2014 no repo, no keys to hunt down." })
6063
+ return /* @__PURE__ */ jsxs20("div", { children: [
6064
+ /* @__PURE__ */ jsx21("div", { className: "vos-head", children: /* @__PURE__ */ jsxs20("div", { children: [
6065
+ /* @__PURE__ */ jsx21("h1", { children: "Connect Claude Code" }),
6066
+ /* @__PURE__ */ jsx21("p", { children: "Run the validation skills against this register from your own Claude Code \u2014 no repo, no keys to hunt down." })
5727
6067
  ] }) }),
5728
- /* @__PURE__ */ jsxs19("ol", { className: "vos-hint", style: { lineHeight: 1.8 }, children: [
5729
- /* @__PURE__ */ jsx20("li", { children: "Generate your personal connection command below." }),
5730
- /* @__PURE__ */ jsx20("li", { children: "Paste it into a terminal in the workspace you'll run the skills in." }),
5731
- /* @__PURE__ */ jsxs19("li", { children: [
6068
+ /* @__PURE__ */ jsxs20("ol", { className: "vos-hint", style: { lineHeight: 1.8 }, children: [
6069
+ /* @__PURE__ */ jsx21("li", { children: "Generate your personal connection command below." }),
6070
+ /* @__PURE__ */ jsx21("li", { children: "Paste it into a terminal in the workspace you'll run the skills in." }),
6071
+ /* @__PURE__ */ jsxs20("li", { children: [
5732
6072
  "The command carries a token tied to ",
5733
- /* @__PURE__ */ jsx20("strong", { children: "you" }),
6073
+ /* @__PURE__ */ jsx21("strong", { children: "you" }),
5734
6074
  " \u2014 anything you write lands under your name. Don't share it."
5735
6075
  ] })
5736
6076
  ] }),
5737
- state.phase !== "ready" ? /* @__PURE__ */ jsx20(
6077
+ state.phase !== "ready" ? /* @__PURE__ */ jsx21(
5738
6078
  "button",
5739
6079
  {
5740
6080
  type: "button",
@@ -5744,11 +6084,11 @@ function ConnectClaudeCode({
5744
6084
  children: state.phase === "minting" ? "Generating\u2026" : "Generate connection command"
5745
6085
  }
5746
6086
  ) : null,
5747
- state.phase === "error" ? /* @__PURE__ */ jsx20("p", { className: "vos-hint", role: "alert", children: state.message }) : null,
5748
- state.phase === "ready" ? /* @__PURE__ */ jsxs19("div", { children: [
5749
- /* @__PURE__ */ jsx20("pre", { className: "vos-code", "aria-label": "Connection command", children: state.command }),
5750
- /* @__PURE__ */ jsxs19("div", { style: { display: "flex", gap: 8 }, children: [
5751
- /* @__PURE__ */ jsx20(
6087
+ state.phase === "error" ? /* @__PURE__ */ jsx21("p", { className: "vos-hint", role: "alert", children: state.message }) : null,
6088
+ state.phase === "ready" ? /* @__PURE__ */ jsxs20("div", { children: [
6089
+ /* @__PURE__ */ jsx21("pre", { className: "vos-code", "aria-label": "Connection command", children: state.command }),
6090
+ /* @__PURE__ */ jsxs20("div", { style: { display: "flex", gap: 8 }, children: [
6091
+ /* @__PURE__ */ jsx21(
5752
6092
  "button",
5753
6093
  {
5754
6094
  type: "button",
@@ -5757,31 +6097,31 @@ function ConnectClaudeCode({
5757
6097
  children: copied ? "Copied" : "Copy command"
5758
6098
  }
5759
6099
  ),
5760
- /* @__PURE__ */ jsx20("button", { type: "button", className: "vos-btn-ghost", onClick: generate, children: "Regenerate" })
6100
+ /* @__PURE__ */ jsx21("button", { type: "button", className: "vos-btn-ghost", onClick: generate, children: "Regenerate" })
5761
6101
  ] }),
5762
- /* @__PURE__ */ jsx20("p", { className: "vos-hint", style: { marginTop: 12 }, children: "Generating again revokes nothing on its own \u2014 remove old keys from your account settings when you rotate." })
6102
+ /* @__PURE__ */ jsx21("p", { className: "vos-hint", style: { marginTop: 12 }, children: "Generating again revokes nothing on its own \u2014 remove old keys from your account settings when you rotate." })
5763
6103
  ] }) : null
5764
6104
  ] });
5765
6105
  }
5766
6106
 
5767
6107
  // src/surface-placeholder.tsx
5768
- import { jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
6108
+ import { jsx as jsx22, jsxs as jsxs21 } from "react/jsx-runtime";
5769
6109
  function SurfacePlaceholder({
5770
6110
  title,
5771
6111
  subtitle,
5772
6112
  detail
5773
6113
  }) {
5774
- return /* @__PURE__ */ jsxs20("div", { children: [
5775
- /* @__PURE__ */ jsx21("div", { className: "vos-head", children: /* @__PURE__ */ jsxs20("div", { children: [
5776
- /* @__PURE__ */ jsx21("h1", { children: title }),
5777
- /* @__PURE__ */ jsx21("p", { children: subtitle })
6114
+ return /* @__PURE__ */ jsxs21("div", { children: [
6115
+ /* @__PURE__ */ jsx22("div", { className: "vos-head", children: /* @__PURE__ */ jsxs21("div", { children: [
6116
+ /* @__PURE__ */ jsx22("h1", { children: title }),
6117
+ /* @__PURE__ */ jsx22("p", { children: subtitle })
5778
6118
  ] }) }),
5779
- /* @__PURE__ */ jsx21("div", { className: "vos-empty", children: detail })
6119
+ /* @__PURE__ */ jsx22("div", { className: "vos-empty", children: detail })
5780
6120
  ] });
5781
6121
  }
5782
6122
 
5783
6123
  // src/register-counts.tsx
5784
- import { jsx as jsx22, jsxs as jsxs21 } from "react/jsx-runtime";
6124
+ import { jsx as jsx23, jsxs as jsxs22 } from "react/jsx-runtime";
5785
6125
  function RegisterCounts({
5786
6126
  counts,
5787
6127
  caption,
@@ -5791,8 +6131,8 @@ function RegisterCounts({
5791
6131
  const registers = REGISTER_ORDER.filter(
5792
6132
  (r) => counts[r] !== void 0
5793
6133
  );
5794
- return /* @__PURE__ */ jsxs21("section", { "aria-label": "Register counts", children: [
5795
- /* @__PURE__ */ jsx22("div", { className: "vos-tile-grid", children: registers.map((register) => /* @__PURE__ */ jsx22(
6134
+ return /* @__PURE__ */ jsxs22("section", { "aria-label": "Register counts", children: [
6135
+ /* @__PURE__ */ jsx23("div", { className: "vos-tile-grid", children: registers.map((register) => /* @__PURE__ */ jsx23(
5796
6136
  StatTile,
5797
6137
  {
5798
6138
  label: REGISTER_LABEL[register],
@@ -5802,7 +6142,7 @@ function RegisterCounts({
5802
6142
  },
5803
6143
  register
5804
6144
  )) }),
5805
- caption ? /* @__PURE__ */ jsx22("p", { className: "vos-hint", style: { marginTop: 16 }, children: caption }) : null
6145
+ caption ? /* @__PURE__ */ jsx23("p", { className: "vos-hint", style: { marginTop: 16 }, children: caption }) : null
5806
6146
  ] });
5807
6147
  }
5808
6148
  export {
@@ -5819,6 +6159,8 @@ export {
5819
6159
  FieldInput,
5820
6160
  GlossaryText,
5821
6161
  Markdown,
6162
+ NO_LENS,
6163
+ NO_STAGE,
5822
6164
  NextMoveSurface,
5823
6165
  PipelineSurface,
5824
6166
  REGISTER_GROUPS,
@@ -5837,9 +6179,12 @@ export {
5837
6179
  RegisterTable,
5838
6180
  RelationEditor,
5839
6181
  RiskBar,
6182
+ STAGE_GLOSS,
6183
+ STAGE_ORDER,
5840
6184
  ScoreImpactForm,
5841
6185
  SidebarNav,
5842
6186
  Sparkline,
6187
+ StageGridSurface,
5843
6188
  StatTile,
5844
6189
  StatusPill,
5845
6190
  SurfacePlaceholder,
@@ -5853,7 +6198,9 @@ export {
5853
6198
  buildPatch,
5854
6199
  buildPipeline,
5855
6200
  buildRecordPage,
6201
+ buildStageGrid,
5856
6202
  buildUnderstanding,
6203
+ cellAt,
5857
6204
  cellValue,
5858
6205
  coldStartFor,
5859
6206
  columnsFor,
@@ -5891,6 +6238,7 @@ export {
5891
6238
  ownerNames,
5892
6239
  parseRoute,
5893
6240
  primaryLabel,
6241
+ rankByRisk,
5894
6242
  readingAssumptionChips,
5895
6243
  readingBeliefVerdicts,
5896
6244
  resolveBarLines,
@@ -5902,6 +6250,7 @@ export {
5902
6250
  sortRecords,
5903
6251
  sparklinePath,
5904
6252
  stageMeters,
6253
+ stageOf,
5905
6254
  statusTone,
5906
6255
  tabsFor,
5907
6256
  toCreatePayload,