@validation-os/core 0.5.0 → 0.6.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/chunk-22WJF5EZ.js +572 -0
- package/dist/chunk-22WJF5EZ.js.map +1 -0
- package/dist/{chunk-W7VU5JOU.js → chunk-43RQBE2E.js} +1 -12
- package/dist/{chunk-W7VU5JOU.js.map → chunk-43RQBE2E.js.map} +1 -1
- package/dist/derivation/index.d.ts +2 -2
- package/dist/derivation/index.js +33 -5
- package/dist/index-BlXH_thK.d.ts +577 -0
- package/dist/index.d.ts +6 -31
- package/dist/index.js +22 -26
- package/dist/index.js.map +1 -1
- package/dist/{provider-hW8Hxf-n.d.ts → provider-DGr5Xq5U.d.ts} +1 -1
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +1 -1
- package/dist/{types-B3eI7ASx.d.ts → types-BAyl0w2E.d.ts} +66 -32
- package/package.json +1 -1
- package/dist/chunk-4Q2B2QT3.js +0 -279
- package/dist/chunk-4Q2B2QT3.js.map +0 -1
- package/dist/index-CieW13mJ.d.ts +0 -234
package/dist/index.js
CHANGED
|
@@ -4,36 +4,24 @@ import {
|
|
|
4
4
|
StaleVersionError,
|
|
5
5
|
isNotFoundError,
|
|
6
6
|
isStaleVersionError
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-43RQBE2E.js";
|
|
8
8
|
import {
|
|
9
|
-
|
|
9
|
+
COMPLETENESS_SLOTS,
|
|
10
|
+
MARKET_RUNG_VALUES,
|
|
10
11
|
REGISTERS,
|
|
11
12
|
TESTING_RUNGS,
|
|
13
|
+
assumptionComplete,
|
|
14
|
+
assumptionCompleteness,
|
|
12
15
|
confidence,
|
|
13
16
|
derivation_exports,
|
|
14
17
|
derivedImpacts,
|
|
18
|
+
missingCompletenessSlots,
|
|
15
19
|
readingStrength,
|
|
16
20
|
risk,
|
|
17
21
|
sourceQuality
|
|
18
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-22WJF5EZ.js";
|
|
19
23
|
import "./chunk-PZ5AY32C.js";
|
|
20
24
|
|
|
21
|
-
// src/presence.ts
|
|
22
|
-
var ASSUMPTION_PRESENCE_FIELDS = [
|
|
23
|
-
"5 Whys",
|
|
24
|
-
"Metric for truth",
|
|
25
|
-
"Scoring justification"
|
|
26
|
-
];
|
|
27
|
-
function isPresent(value) {
|
|
28
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
29
|
-
}
|
|
30
|
-
function missingPresenceFields(record) {
|
|
31
|
-
return ASSUMPTION_PRESENCE_FIELDS.filter((field) => !isPresent(record[field]));
|
|
32
|
-
}
|
|
33
|
-
function assumptionPresenceComplete(record) {
|
|
34
|
-
return missingPresenceFields(record).length === 0;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
25
|
// src/reading-input.ts
|
|
38
26
|
function str(v) {
|
|
39
27
|
return typeof v === "string" && v !== "" ? v : null;
|
|
@@ -52,8 +40,7 @@ function toReadingInput(r) {
|
|
|
52
40
|
credibility: num(r.Credibility),
|
|
53
41
|
date: str(r.Date),
|
|
54
42
|
magnitudeBand: r.magnitudeBand,
|
|
55
|
-
experimentId: str(r.experimentId)
|
|
56
|
-
goalId: str(r.goalId)
|
|
43
|
+
experimentId: str(r.experimentId)
|
|
57
44
|
};
|
|
58
45
|
}
|
|
59
46
|
|
|
@@ -92,23 +79,32 @@ function recomputeDerived(input) {
|
|
|
92
79
|
for (const a of assumptions) {
|
|
93
80
|
const c = confidenceById.get(a.id) ?? 0;
|
|
94
81
|
const di = impactById.get(a.id) ?? 0;
|
|
95
|
-
out.set(a.id, {
|
|
82
|
+
out.set(a.id, {
|
|
83
|
+
confidence: c,
|
|
84
|
+
derivedImpact: di,
|
|
85
|
+
risk: risk(di, c),
|
|
86
|
+
// Completeness is a *structural* readiness meter: it reads a.Impact as
|
|
87
|
+
// present/absent, not its value, so a moot assumption (whose Impact the
|
|
88
|
+
// Derived Impact pass zeroes) still counts its scored Impact slot.
|
|
89
|
+
completeness: assumptionCompleteness(a)
|
|
90
|
+
});
|
|
96
91
|
}
|
|
97
92
|
return out;
|
|
98
93
|
}
|
|
99
94
|
export {
|
|
100
|
-
|
|
101
|
-
|
|
95
|
+
COMPLETENESS_SLOTS as ASSUMPTION_PRESENCE_SLOTS,
|
|
96
|
+
MARKET_RUNG_VALUES,
|
|
102
97
|
NotFoundError,
|
|
103
98
|
REGISTERS,
|
|
104
99
|
RELATIONS,
|
|
105
100
|
StaleVersionError,
|
|
106
101
|
TESTING_RUNGS,
|
|
107
|
-
|
|
102
|
+
assumptionCompleteness,
|
|
103
|
+
assumptionComplete as assumptionPresenceComplete,
|
|
108
104
|
derivation_exports as derivation,
|
|
109
105
|
isNotFoundError,
|
|
110
106
|
isStaleVersionError,
|
|
111
|
-
|
|
107
|
+
missingCompletenessSlots as missingPresenceSlots,
|
|
112
108
|
recomputeDerived,
|
|
113
109
|
sourceQuality as recomputeSourceQuality,
|
|
114
110
|
readingStrength as recomputeStrength,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/
|
|
1
|
+
{"version":3,"sources":["../src/reading-input.ts","../src/recompute.ts"],"sourcesContent":["/**\n * The one place that maps a stored reading record → the derivation module's\n * typed input, so the pure functions stay decoupled from field names. Both the\n * server-side recompute pass and the dashboard's understanding layer map\n * through here, so a reading is read identically wherever Confidence is derived\n * or explained. Coercion is defensive because a record's fields are `unknown`.\n */\nimport type { AnyRecord } from \"./types.js\";\nimport type { AttributionReadingInput } from \"./derivation/index.js\";\n\nfunction str(v: unknown): string | null {\n return typeof v === \"string\" && v !== \"\" ? v : null;\n}\n\nfunction num(v: unknown): number {\n const n = Number(v);\n return Number.isFinite(n) ? n : 0;\n}\n\n/** Map a reading record (canonical Title-cased fields) to its derivation input. */\nexport function toReadingInput(r: AnyRecord): AttributionReadingInput {\n return {\n id: r.id,\n source: str(r.Source),\n rung: r.Rung as AttributionReadingInput[\"rung\"],\n result: r.Result as AttributionReadingInput[\"result\"],\n representativeness: num(r.Representativeness),\n credibility: num(r.Credibility),\n date: str(r.Date),\n magnitudeBand: r.magnitudeBand as AttributionReadingInput[\"magnitudeBand\"],\n experimentId: str(r.experimentId),\n };\n}\n","/**\n * Derive-on-write glue: recompute the four derived numbers for a whole\n * assumptions register from its readings and standing decisions, using the\n * pure derivation module. The API calls this server-side on every touching\n * write and writes the results back; a batch pass is the backstop for\n * non-dashboard writes.\n *\n * The stored-record → derivation-input mapping lives in `reading-input.ts`\n * (`toReadingInput`), shared with the dashboard's understanding layer so a\n * reading is read identically wherever Confidence is derived or explained.\n */\nimport {\n assumptionCompleteness,\n confidence,\n derivedImpacts,\n risk,\n} from \"./derivation/index.js\";\nimport { toReadingInput } from \"./reading-input.js\";\nimport type {\n AnyRecord,\n AssumptionDerived,\n AssumptionRecord,\n DecisionRecord,\n ReadingRecord,\n} from \"./types.js\";\n\n/** Standing decisions (Provisional/Active) contribute to Derived Impact. */\nconst STANDING_DECISION = new Set([\"Active\", \"Provisional\"]);\n\nexport interface RecomputeInput {\n assumptions: AssumptionRecord[];\n readings: ReadingRecord[];\n decisions: DecisionRecord[];\n}\n\n/** id → recomputed derived tuple for every assumption in the register. */\nexport function recomputeDerived(\n input: RecomputeInput,\n): Map<string, AssumptionDerived> {\n const { assumptions, readings, decisions } = input;\n\n // Confidence: group concluded readings by assumption.\n const readingsByAssumption = new Map<string, ReadingRecord[]>();\n for (const a of assumptions) readingsByAssumption.set(a.id, []);\n for (const r of readings) readingsByAssumption.get(r.assumptionId)?.push(r);\n\n const confidenceById = new Map<string, number>();\n for (const a of assumptions) {\n const rs = readingsByAssumption.get(a.id) ?? [];\n confidenceById.set(\n a.id,\n confidence(rs.map((r) => toReadingInput(r as unknown as AnyRecord))),\n );\n }\n\n // Derived Impact: standing-decision `Based on` links count +100 each.\n const basedOnCounts: Record<string, number> = {};\n for (const d of decisions) {\n if (!STANDING_DECISION.has(d.Status)) continue;\n for (const aid of d.basedOnIds ?? []) {\n basedOnCounts[aid] = (basedOnCounts[aid] ?? 0) + 1;\n }\n }\n const impactById = derivedImpacts(\n assumptions.map((a) => ({\n id: a.id,\n impact: a.moot ? 0 : a.Impact,\n moot: a.moot,\n dependsOnIds: a.dependsOnIds,\n })),\n basedOnCounts,\n );\n\n const out = new Map<string, AssumptionDerived>();\n for (const a of assumptions) {\n const c = confidenceById.get(a.id) ?? 0;\n const di = impactById.get(a.id) ?? 0;\n out.set(a.id, {\n confidence: c,\n derivedImpact: di,\n risk: risk(di, c),\n // Completeness is a *structural* readiness meter: it reads a.Impact as\n // present/absent, not its value, so a moot assumption (whose Impact the\n // Derived Impact pass zeroes) still counts its scored Impact slot.\n completeness: assumptionCompleteness(a),\n });\n }\n return out;\n}\n\n/** Recompute Source quality + Strength for a single reading. */\nexport {\n sourceQuality as recomputeSourceQuality,\n readingStrength as recomputeStrength,\n} from \"./derivation/index.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAUA,SAAS,IAAI,GAA2B;AACtC,SAAO,OAAO,MAAM,YAAY,MAAM,KAAK,IAAI;AACjD;AAEA,SAAS,IAAI,GAAoB;AAC/B,QAAM,IAAI,OAAO,CAAC;AAClB,SAAO,OAAO,SAAS,CAAC,IAAI,IAAI;AAClC;AAGO,SAAS,eAAe,GAAuC;AACpE,SAAO;AAAA,IACL,IAAI,EAAE;AAAA,IACN,QAAQ,IAAI,EAAE,MAAM;AAAA,IACpB,MAAM,EAAE;AAAA,IACR,QAAQ,EAAE;AAAA,IACV,oBAAoB,IAAI,EAAE,kBAAkB;AAAA,IAC5C,aAAa,IAAI,EAAE,WAAW;AAAA,IAC9B,MAAM,IAAI,EAAE,IAAI;AAAA,IAChB,eAAe,EAAE;AAAA,IACjB,cAAc,IAAI,EAAE,YAAY;AAAA,EAClC;AACF;;;ACLA,IAAM,oBAAoB,oBAAI,IAAI,CAAC,UAAU,aAAa,CAAC;AASpD,SAAS,iBACd,OACgC;AAChC,QAAM,EAAE,aAAa,UAAU,UAAU,IAAI;AAG7C,QAAM,uBAAuB,oBAAI,IAA6B;AAC9D,aAAW,KAAK,YAAa,sBAAqB,IAAI,EAAE,IAAI,CAAC,CAAC;AAC9D,aAAW,KAAK,SAAU,sBAAqB,IAAI,EAAE,YAAY,GAAG,KAAK,CAAC;AAE1E,QAAM,iBAAiB,oBAAI,IAAoB;AAC/C,aAAW,KAAK,aAAa;AAC3B,UAAM,KAAK,qBAAqB,IAAI,EAAE,EAAE,KAAK,CAAC;AAC9C,mBAAe;AAAA,MACb,EAAE;AAAA,MACF,WAAW,GAAG,IAAI,CAAC,MAAM,eAAe,CAAyB,CAAC,CAAC;AAAA,IACrE;AAAA,EACF;AAGA,QAAM,gBAAwC,CAAC;AAC/C,aAAW,KAAK,WAAW;AACzB,QAAI,CAAC,kBAAkB,IAAI,EAAE,MAAM,EAAG;AACtC,eAAW,OAAO,EAAE,cAAc,CAAC,GAAG;AACpC,oBAAc,GAAG,KAAK,cAAc,GAAG,KAAK,KAAK;AAAA,IACnD;AAAA,EACF;AACA,QAAM,aAAa;AAAA,IACjB,YAAY,IAAI,CAAC,OAAO;AAAA,MACtB,IAAI,EAAE;AAAA,MACN,QAAQ,EAAE,OAAO,IAAI,EAAE;AAAA,MACvB,MAAM,EAAE;AAAA,MACR,cAAc,EAAE;AAAA,IAClB,EAAE;AAAA,IACF;AAAA,EACF;AAEA,QAAM,MAAM,oBAAI,IAA+B;AAC/C,aAAW,KAAK,aAAa;AAC3B,UAAM,IAAI,eAAe,IAAI,EAAE,EAAE,KAAK;AACtC,UAAM,KAAK,WAAW,IAAI,EAAE,EAAE,KAAK;AACnC,QAAI,IAAI,EAAE,IAAI;AAAA,MACZ,YAAY;AAAA,MACZ,eAAe;AAAA,MACf,MAAM,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA,MAIhB,cAAc,uBAAuB,CAAC;AAAA,IACxC,CAAC;AAAA,EACH;AACA,SAAO;AACT;","names":[]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as Collection, c as AnyRecord, R as Relation,
|
|
1
|
+
import { C as Collection, c as AnyRecord, R as Relation, m as RecordRef } from './types-BAyl0w2E.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* The `DataProvider` — the single integration seam between the dashboard/API
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as DataProvider } from './provider-
|
|
2
|
-
import { C as Collection, c as AnyRecord, R as Relation,
|
|
1
|
+
import { D as DataProvider } from './provider-DGr5Xq5U.js';
|
|
2
|
+
import { C as Collection, c as AnyRecord, R as Relation, m as RecordRef } from './types-BAyl0w2E.js';
|
|
3
3
|
|
|
4
4
|
interface InMemoryProviderOptions {
|
|
5
5
|
now?: () => string;
|
package/dist/testing.js
CHANGED
|
@@ -5,11 +5,15 @@
|
|
|
5
5
|
* single source of truth). Field *meaning* lives there; this file is the
|
|
6
6
|
* checkable TypeScript shape the packages import.
|
|
7
7
|
*/
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* The five registers. `goals` was unified into `experiments` and the `people`
|
|
10
|
+
* reference collection retired (OPS-1305): Owner / Agreed by now reference a
|
|
11
|
+
* dashboard user (the auth-sourced team list), not a register row.
|
|
12
|
+
*/
|
|
13
|
+
declare const REGISTERS: readonly ["assumptions", "experiments", "readings", "decisions", "glossary"];
|
|
10
14
|
type Register = (typeof REGISTERS)[number];
|
|
11
|
-
/**
|
|
12
|
-
type Collection = Register
|
|
15
|
+
/** There is no reference collection any more — a collection is a register. */
|
|
16
|
+
type Collection = Register;
|
|
13
17
|
/** Every stored record carries an id, a version, and timestamps. */
|
|
14
18
|
interface BaseRecord {
|
|
15
19
|
id: string;
|
|
@@ -19,26 +23,37 @@ interface BaseRecord {
|
|
|
19
23
|
updatedAt: string;
|
|
20
24
|
}
|
|
21
25
|
type AssumptionStatus = "Draft" | "Live" | "Invalidated";
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
/**
|
|
27
|
+
* The unified evidence-plan lifecycle (OPS-1305). `Draft` is the new gate a
|
|
28
|
+
* commit clears (Draft→Running); conclude+verdict stays the Running→Closed
|
|
29
|
+
* gate. Absorbs what the retired Goal record's status used to carry.
|
|
30
|
+
*/
|
|
31
|
+
type ExperimentStatus = "Draft" | "Running" | "Closed";
|
|
24
32
|
type DecisionStatus = "Active" | "Provisional" | "Superseded" | "Reversed";
|
|
25
33
|
type GlossaryStatus = "Active" | "Provisional" | "Superseded";
|
|
26
34
|
type Result = "Validated" | "Invalidated" | "Inconclusive";
|
|
27
35
|
type MagnitudeBand = "Low" | "Typical" | "High";
|
|
28
36
|
type Feasibility = "High" | "Medium" | "Low";
|
|
29
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* The 8-rung activity-and-strength ladder (order = strength, weakest first).
|
|
39
|
+
* Two categories: Testing (recruited-sample instruments) and Market (open-world
|
|
40
|
+
* targets — the category formerly called "Goals", renamed with the unification,
|
|
41
|
+
* OPS-1305). The anchors and physics are unchanged by the rename.
|
|
42
|
+
*/
|
|
30
43
|
declare const TESTING_RUNGS: readonly ["Opinion", "Pitch-deck reaction", "Anecdotal", "Desk research", "Survey at scale", "Prototype usage"];
|
|
31
|
-
declare const
|
|
44
|
+
declare const MARKET_RUNG_VALUES: readonly ["Signed intent", "Paying users"];
|
|
32
45
|
type TestingRung = (typeof TESTING_RUNGS)[number];
|
|
33
|
-
type
|
|
34
|
-
type Rung = TestingRung |
|
|
46
|
+
type MarketRung = (typeof MARKET_RUNG_VALUES)[number];
|
|
47
|
+
type Rung = TestingRung | MarketRung;
|
|
35
48
|
/** Representativeness and Credibility are each picked from these. */
|
|
36
49
|
type SourceQualityPick = 1.0 | 0.7 | 0.5;
|
|
37
|
-
/** The
|
|
50
|
+
/** The derived numbers stored on an assumption (never hand-typed). */
|
|
38
51
|
interface AssumptionDerived {
|
|
39
52
|
derivedImpact: number;
|
|
40
53
|
risk: number;
|
|
41
54
|
confidence: number;
|
|
55
|
+
/** Structural readiness meter, 0–100 (see `derivation/completeness.ts`). */
|
|
56
|
+
completeness: number;
|
|
42
57
|
}
|
|
43
58
|
interface AssumptionRecord extends BaseRecord {
|
|
44
59
|
Title: string;
|
|
@@ -48,18 +63,16 @@ interface AssumptionRecord extends BaseRecord {
|
|
|
48
63
|
/** The hand-scored seed (0–100), the only hand-scored number here. */
|
|
49
64
|
Impact: number | null;
|
|
50
65
|
Status: AssumptionStatus;
|
|
66
|
+
/** Reference to a dashboard user (auth team list), not a `people` row. */
|
|
51
67
|
Owner: string[];
|
|
52
|
-
Gaps: string[];
|
|
53
68
|
moot: boolean;
|
|
54
69
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
70
|
+
* Kept, narrowed to the Impact-seed rationale (OPS-1305): why the seed
|
|
71
|
+
* `Impact` was scored as it was, incl. dated moot lines. The `5 Whys`,
|
|
72
|
+
* `Metric for truth`, and `Gaps` fields are gone — the why-trace lives in the
|
|
73
|
+
* `Depends on / Enables` chain and the audit check-types are transient grill
|
|
74
|
+
* stages, not stored tags. Readiness is the derived `completeness`.
|
|
60
75
|
*/
|
|
61
|
-
"5 Whys": string;
|
|
62
|
-
"Metric for truth": string;
|
|
63
76
|
"Scoring justification": string;
|
|
64
77
|
dependsOnIds: string[];
|
|
65
78
|
enablesIds: string[];
|
|
@@ -69,17 +82,25 @@ interface AssumptionRecord extends BaseRecord {
|
|
|
69
82
|
}
|
|
70
83
|
interface ReadingRecord extends BaseRecord {
|
|
71
84
|
Title: string;
|
|
72
|
-
/**
|
|
85
|
+
/** The independence/dedupe key — the generator (person / dataset / cohort). */
|
|
73
86
|
Source: string | null;
|
|
87
|
+
/**
|
|
88
|
+
* Provenance links (recording, dashboard, CRM row, user id) — 0..N, drives no
|
|
89
|
+
* math and never keys dedupe (OPS-1305). Split out from `Source` so the
|
|
90
|
+
* dedupe key stays narrow.
|
|
91
|
+
*/
|
|
92
|
+
contextLinks: string[];
|
|
74
93
|
assumptionId: string;
|
|
94
|
+
/** The originating plan, or null for a bare/found reading (no Goal origin). */
|
|
75
95
|
experimentId: string | null;
|
|
76
|
-
goalId: string | null;
|
|
77
96
|
Rung: Rung;
|
|
78
97
|
Representativeness: SourceQualityPick;
|
|
79
98
|
Credibility: SourceQualityPick;
|
|
80
|
-
/** For
|
|
99
|
+
/** For Market-rung readings: the magnitude band from the absolute outcome. */
|
|
81
100
|
magnitudeBand?: MagnitudeBand;
|
|
82
101
|
Result: Result;
|
|
102
|
+
/** The rationale for the rung / representativeness / credibility picks. */
|
|
103
|
+
"Grading justification": string;
|
|
83
104
|
Date: string | null;
|
|
84
105
|
Owner: string[];
|
|
85
106
|
derived: {
|
|
@@ -106,6 +127,11 @@ interface ExperimentRecord extends BaseRecord {
|
|
|
106
127
|
Feasibility: Feasibility | null;
|
|
107
128
|
Status: ExperimentStatus;
|
|
108
129
|
closureReason: "Completed" | "Early-stop" | "Kill" | null;
|
|
130
|
+
/** Optional deadline a committed plan carries (folded in from the Goal). */
|
|
131
|
+
Deadline: string | null;
|
|
132
|
+
/** Terminal closure outcome, null until Closed (folded in from the Goal). */
|
|
133
|
+
Outcome: "Achieved" | "Missed" | "Dropped" | null;
|
|
134
|
+
/** Reference to a dashboard user, not a `people` row. */
|
|
109
135
|
Owner: string[];
|
|
110
136
|
Date: string | null;
|
|
111
137
|
/** The embedded pre-registered bar lines; drives progress-to-conclusion. */
|
|
@@ -113,24 +139,32 @@ interface ExperimentRecord extends BaseRecord {
|
|
|
113
139
|
/** Convenience projection: the assumptions the bar lines test. */
|
|
114
140
|
barLineAssumptionIds: string[];
|
|
115
141
|
}
|
|
116
|
-
interface GoalRecord extends BaseRecord {
|
|
117
|
-
Title: string;
|
|
118
|
-
Status: GoalStatus;
|
|
119
|
-
Outcome: "Achieved" | "Missed" | "Dropped" | null;
|
|
120
|
-
Owner: string[];
|
|
121
|
-
Date: string | null;
|
|
122
|
-
basedOnIds: string[];
|
|
123
|
-
}
|
|
124
142
|
interface DecisionRecord extends BaseRecord {
|
|
125
143
|
Title: string;
|
|
126
144
|
Status: DecisionStatus;
|
|
145
|
+
/** The one-line statement of what was decided (promoted from the body). */
|
|
146
|
+
Statement: string;
|
|
147
|
+
/** Why the Unanimity score was scored as it was (promoted from the body). */
|
|
148
|
+
"Unanimity justification": string;
|
|
149
|
+
/** References to dashboard users (auth team list), not `people` rows. */
|
|
127
150
|
Owner: string[];
|
|
151
|
+
"Agreed by": string[];
|
|
128
152
|
basedOnIds: string[];
|
|
129
153
|
resolvesIds: string[];
|
|
130
154
|
}
|
|
155
|
+
/** One structured "don't say" entry on a glossary term. */
|
|
156
|
+
interface GlossaryAvoid {
|
|
157
|
+
audience: string;
|
|
158
|
+
phrase: string;
|
|
159
|
+
fix: string;
|
|
160
|
+
}
|
|
131
161
|
interface GlossaryRecord extends BaseRecord {
|
|
132
162
|
Title: string;
|
|
133
163
|
Status: GlossaryStatus;
|
|
164
|
+
/** All properties, no body (OPS-1305): the terminology check parses these. */
|
|
165
|
+
Definition: string;
|
|
166
|
+
Avoid: GlossaryAvoid[];
|
|
167
|
+
"How it differs": string;
|
|
134
168
|
}
|
|
135
169
|
/** A record of any register — the DataProvider's currency. */
|
|
136
170
|
type AnyRecord = BaseRecord & Record<string, unknown>;
|
|
@@ -140,6 +174,6 @@ interface RecordRef {
|
|
|
140
174
|
id: string;
|
|
141
175
|
}
|
|
142
176
|
/** The relations the dashboard can set (each writes both ends). */
|
|
143
|
-
type Relation = "assumption-reading" | "assumption-depends-on" | "assumption-contradicts" | "reading-experiment" | "
|
|
177
|
+
type Relation = "assumption-reading" | "assumption-depends-on" | "assumption-contradicts" | "reading-experiment" | "decision-based-on" | "decision-resolves";
|
|
144
178
|
|
|
145
|
-
export { type AssumptionRecord as A, type BarLine as B, type Collection as C, type DecisionRecord as D, type ExperimentRecord as E, type Feasibility as F,
|
|
179
|
+
export { type AssumptionRecord as A, type BarLine as B, type Collection as C, type DecisionRecord as D, type ExperimentRecord as E, type Feasibility as F, type GlossaryAvoid as G, MARKET_RUNG_VALUES as M, type Relation as R, type SourceQualityPick as S, TESTING_RUNGS as T, type ReadingRecord as a, type AssumptionDerived as b, type AnyRecord as c, type AssumptionStatus as d, type BaseRecord as e, type DecisionStatus as f, type ExperimentStatus as g, type GlossaryRecord as h, type GlossaryStatus as i, type MagnitudeBand as j, type MarketRung as k, REGISTERS as l, type RecordRef as m, type Register as n, type Result as o, type Rung as p, type TestingRung as q };
|
package/package.json
CHANGED
package/dist/chunk-4Q2B2QT3.js
DELETED
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
__export
|
|
3
|
-
} from "./chunk-PZ5AY32C.js";
|
|
4
|
-
|
|
5
|
-
// src/derivation/index.ts
|
|
6
|
-
var derivation_exports = {};
|
|
7
|
-
__export(derivation_exports, {
|
|
8
|
-
GOAL_RUNG_ANCHOR: () => GOAL_RUNG_ANCHOR,
|
|
9
|
-
RUNG_ANCHOR: () => RUNG_ANCHOR,
|
|
10
|
-
W0: () => W0,
|
|
11
|
-
confidence: () => confidence,
|
|
12
|
-
confidenceAttribution: () => confidenceAttribution,
|
|
13
|
-
confidenceTrajectory: () => confidenceTrajectory,
|
|
14
|
-
derivedImpacts: () => derivedImpacts,
|
|
15
|
-
experimentProgress: () => experimentProgress,
|
|
16
|
-
isConcluded: () => isConcluded,
|
|
17
|
-
isGoalRung: () => isGoalRung,
|
|
18
|
-
readingStrength: () => readingStrength,
|
|
19
|
-
risk: () => risk,
|
|
20
|
-
round2: () => round2,
|
|
21
|
-
scoreAndDedupe: () => scoreAndDedupe,
|
|
22
|
-
sign: () => sign,
|
|
23
|
-
sourceQuality: () => sourceQuality
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// src/derivation/round.ts
|
|
27
|
-
function round2(n) {
|
|
28
|
-
return Number(n.toFixed(2));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// src/types.ts
|
|
32
|
-
var REGISTERS = [
|
|
33
|
-
"assumptions",
|
|
34
|
-
"experiments",
|
|
35
|
-
"readings",
|
|
36
|
-
"goals",
|
|
37
|
-
"decisions",
|
|
38
|
-
"glossary"
|
|
39
|
-
];
|
|
40
|
-
var TESTING_RUNGS = [
|
|
41
|
-
"Opinion",
|
|
42
|
-
"Pitch-deck reaction",
|
|
43
|
-
"Anecdotal",
|
|
44
|
-
"Desk research",
|
|
45
|
-
"Survey at scale",
|
|
46
|
-
"Prototype usage"
|
|
47
|
-
];
|
|
48
|
-
var GOAL_RUNG_VALUES = ["Signed intent", "Paying users"];
|
|
49
|
-
|
|
50
|
-
// src/derivation/rung.ts
|
|
51
|
-
var RUNG_ANCHOR = {
|
|
52
|
-
Opinion: 3,
|
|
53
|
-
"Pitch-deck reaction": 6,
|
|
54
|
-
Anecdotal: 10,
|
|
55
|
-
"Desk research": 15,
|
|
56
|
-
"Survey at scale": 25,
|
|
57
|
-
"Prototype usage": 30
|
|
58
|
-
};
|
|
59
|
-
var GOAL_RUNG_ANCHOR = {
|
|
60
|
-
"Signed intent": { Low: 55, Typical: 68, High: 80 },
|
|
61
|
-
"Paying users": { Low: 75, Typical: 88, High: 99 }
|
|
62
|
-
};
|
|
63
|
-
var GOAL_RUNG_SET = new Set(GOAL_RUNG_VALUES);
|
|
64
|
-
function isGoalRung(rung) {
|
|
65
|
-
return GOAL_RUNG_SET.has(rung);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// src/derivation/strength.ts
|
|
69
|
-
function sign(result) {
|
|
70
|
-
if (result === "Validated") return 1;
|
|
71
|
-
if (result === "Invalidated") return -1;
|
|
72
|
-
return 0;
|
|
73
|
-
}
|
|
74
|
-
function isConcluded(result) {
|
|
75
|
-
return result === "Validated" || result === "Invalidated";
|
|
76
|
-
}
|
|
77
|
-
function readingStrength(input) {
|
|
78
|
-
const s = sign(input.result);
|
|
79
|
-
if (s === 0) return 0;
|
|
80
|
-
if (isGoalRung(input.rung)) {
|
|
81
|
-
const band = input.magnitudeBand ?? "Typical";
|
|
82
|
-
return GOAL_RUNG_ANCHOR[input.rung][band] * s;
|
|
83
|
-
}
|
|
84
|
-
return (RUNG_ANCHOR[input.rung] ?? 0) * s;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// src/derivation/source-quality.ts
|
|
88
|
-
function sourceQuality(representativeness, credibility) {
|
|
89
|
-
return round2(representativeness * credibility);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// src/derivation/confidence.ts
|
|
93
|
-
var W0 = 100;
|
|
94
|
-
function scoreAndDedupe(readings) {
|
|
95
|
-
const scored = readings.filter((r) => isConcluded(r.result)).map((r) => {
|
|
96
|
-
const strength = readingStrength(r);
|
|
97
|
-
const sq = sourceQuality(r.representativeness, r.credibility);
|
|
98
|
-
return { input: r, strength, sq, weight: Math.abs(strength) * sq };
|
|
99
|
-
}).filter((x) => x.strength !== 0);
|
|
100
|
-
const best = /* @__PURE__ */ new Map();
|
|
101
|
-
for (const x of scored) {
|
|
102
|
-
if (isGoalRung(x.input.rung)) {
|
|
103
|
-
best.set(x.input.id, x);
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
const key = x.input.source || x.input.id;
|
|
107
|
-
const cur = best.get(key);
|
|
108
|
-
const better = !cur || Math.abs(x.strength) > Math.abs(cur.strength) || Math.abs(x.strength) === Math.abs(cur.strength) && (x.input.date || "") > (cur.input.date || "");
|
|
109
|
-
if (better) best.set(key, x);
|
|
110
|
-
}
|
|
111
|
-
return [...best.values()];
|
|
112
|
-
}
|
|
113
|
-
function confidence(readings) {
|
|
114
|
-
const winners = scoreAndDedupe(readings);
|
|
115
|
-
let num = 0;
|
|
116
|
-
let den = W0;
|
|
117
|
-
for (const x of winners) {
|
|
118
|
-
num += x.weight * x.strength;
|
|
119
|
-
den += x.weight;
|
|
120
|
-
}
|
|
121
|
-
return den > 0 ? round2(num / den) : 0;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// src/derivation/attribution.ts
|
|
125
|
-
function bucketOf(r) {
|
|
126
|
-
if (r.experimentId) {
|
|
127
|
-
return {
|
|
128
|
-
key: r.experimentId,
|
|
129
|
-
kind: "experiment",
|
|
130
|
-
experimentId: r.experimentId,
|
|
131
|
-
goalId: null
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
if (r.goalId) {
|
|
135
|
-
return {
|
|
136
|
-
key: `goal:${r.goalId}`,
|
|
137
|
-
kind: "goal",
|
|
138
|
-
experimentId: null,
|
|
139
|
-
goalId: r.goalId
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
return { key: "direct", kind: "direct", experimentId: null, goalId: null };
|
|
143
|
-
}
|
|
144
|
-
function confidenceAttribution(readings) {
|
|
145
|
-
const winners = scoreAndDedupe(readings);
|
|
146
|
-
const den = winners.reduce((acc, x) => acc + x.weight, W0);
|
|
147
|
-
const byKey = /* @__PURE__ */ new Map();
|
|
148
|
-
let num = 0;
|
|
149
|
-
for (const x of winners) {
|
|
150
|
-
num += x.weight * x.strength;
|
|
151
|
-
const b = bucketOf(x.input);
|
|
152
|
-
const contribution = x.weight * x.strength / den;
|
|
153
|
-
const cur = byKey.get(b.key);
|
|
154
|
-
if (cur) {
|
|
155
|
-
cur.contribution += contribution;
|
|
156
|
-
cur.readingCount += 1;
|
|
157
|
-
cur.readingIds.push(x.input.id);
|
|
158
|
-
} else {
|
|
159
|
-
byKey.set(b.key, {
|
|
160
|
-
key: b.key,
|
|
161
|
-
kind: b.kind,
|
|
162
|
-
experimentId: b.experimentId,
|
|
163
|
-
goalId: b.goalId,
|
|
164
|
-
contribution,
|
|
165
|
-
magnitude: 0,
|
|
166
|
-
// finalised below
|
|
167
|
-
readingCount: 1,
|
|
168
|
-
readingIds: [x.input.id]
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
const movers = [...byKey.values()].map((m) => ({
|
|
173
|
-
...m,
|
|
174
|
-
contribution: round2(m.contribution),
|
|
175
|
-
magnitude: round2(Math.abs(m.contribution))
|
|
176
|
-
}));
|
|
177
|
-
movers.sort((a, b) => b.magnitude - a.magnitude);
|
|
178
|
-
return {
|
|
179
|
-
confidence: den > 0 ? round2(num / den) : 0,
|
|
180
|
-
movers
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// src/derivation/progress.ts
|
|
185
|
-
function experimentProgress(bars) {
|
|
186
|
-
const total = bars.length;
|
|
187
|
-
const settled = bars.filter(
|
|
188
|
-
(b) => typeof b.barVerdict === "string" && b.barVerdict.trim() !== ""
|
|
189
|
-
).length;
|
|
190
|
-
return {
|
|
191
|
-
total,
|
|
192
|
-
settled,
|
|
193
|
-
toGo: total - settled,
|
|
194
|
-
concluded: total > 0 && settled === total
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// src/derivation/trajectory.ts
|
|
199
|
-
function confidenceTrajectory(readings) {
|
|
200
|
-
const concluded = readings.filter((r) => isConcluded(r.result));
|
|
201
|
-
const undated = concluded.filter((r) => !r.date);
|
|
202
|
-
const dated = concluded.filter((r) => r.date);
|
|
203
|
-
if (dated.length === 0) return [];
|
|
204
|
-
const dates = [...new Set(dated.map((r) => r.date))].sort();
|
|
205
|
-
return dates.map((date) => ({
|
|
206
|
-
date,
|
|
207
|
-
// Everything dated on/before this point, plus the always-present undated.
|
|
208
|
-
confidence: confidence([
|
|
209
|
-
...undated,
|
|
210
|
-
...dated.filter((r) => r.date <= date)
|
|
211
|
-
])
|
|
212
|
-
}));
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// src/derivation/impact.ts
|
|
216
|
-
function derivedImpacts(assumptions, basedOnCounts = {}) {
|
|
217
|
-
const byId = new Map(assumptions.map((a) => [a.id, a]));
|
|
218
|
-
const dependents = /* @__PURE__ */ new Map();
|
|
219
|
-
for (const a of assumptions) dependents.set(a.id, []);
|
|
220
|
-
for (const a of assumptions) {
|
|
221
|
-
for (const dep of a.dependsOnIds) dependents.get(dep)?.push(a.id);
|
|
222
|
-
}
|
|
223
|
-
const memo = /* @__PURE__ */ new Map();
|
|
224
|
-
const compute = (id, seen) => {
|
|
225
|
-
const cached = memo.get(id);
|
|
226
|
-
if (cached !== void 0) return cached;
|
|
227
|
-
if (seen.has(id)) return 0;
|
|
228
|
-
seen.add(id);
|
|
229
|
-
const a = byId.get(id);
|
|
230
|
-
if (!a) return 0;
|
|
231
|
-
if (a.moot) {
|
|
232
|
-
memo.set(id, 0);
|
|
233
|
-
return 0;
|
|
234
|
-
}
|
|
235
|
-
const seed = a.impact ?? 0;
|
|
236
|
-
let S = 0;
|
|
237
|
-
for (const depId of dependents.get(id) ?? []) {
|
|
238
|
-
const d = byId.get(depId);
|
|
239
|
-
if (d && !d.moot) S += compute(depId, seen);
|
|
240
|
-
}
|
|
241
|
-
S += 100 * (basedOnCounts[id] ?? 0);
|
|
242
|
-
const value = seed + (100 - seed) * (S / (S + 100));
|
|
243
|
-
const rounded = round2(value);
|
|
244
|
-
memo.set(id, rounded);
|
|
245
|
-
return rounded;
|
|
246
|
-
};
|
|
247
|
-
const out = /* @__PURE__ */ new Map();
|
|
248
|
-
for (const a of assumptions) out.set(a.id, compute(a.id, /* @__PURE__ */ new Set()));
|
|
249
|
-
return out;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// src/derivation/risk.ts
|
|
253
|
-
function risk(derivedImpact, confidence2) {
|
|
254
|
-
return round2(derivedImpact * (1 - Math.max(0, confidence2) / 100));
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
export {
|
|
258
|
-
REGISTERS,
|
|
259
|
-
TESTING_RUNGS,
|
|
260
|
-
GOAL_RUNG_VALUES,
|
|
261
|
-
round2,
|
|
262
|
-
RUNG_ANCHOR,
|
|
263
|
-
GOAL_RUNG_ANCHOR,
|
|
264
|
-
isGoalRung,
|
|
265
|
-
sign,
|
|
266
|
-
isConcluded,
|
|
267
|
-
readingStrength,
|
|
268
|
-
sourceQuality,
|
|
269
|
-
W0,
|
|
270
|
-
scoreAndDedupe,
|
|
271
|
-
confidence,
|
|
272
|
-
confidenceAttribution,
|
|
273
|
-
experimentProgress,
|
|
274
|
-
confidenceTrajectory,
|
|
275
|
-
derivedImpacts,
|
|
276
|
-
risk,
|
|
277
|
-
derivation_exports
|
|
278
|
-
};
|
|
279
|
-
//# sourceMappingURL=chunk-4Q2B2QT3.js.map
|