@validation-os/core 0.1.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/LICENSE +21 -0
- package/dist/chunk-CUD5PEXL.js +94 -0
- package/dist/chunk-CUD5PEXL.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-ZNOCENEV.js +172 -0
- package/dist/chunk-ZNOCENEV.js.map +1 -0
- package/dist/derivation/index.d.ts +2 -0
- package/dist/derivation/index.js +28 -0
- package/dist/derivation/index.js.map +1 -0
- package/dist/index-BSd5_vwB.d.ts +115 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +83 -0
- package/dist/index.js.map +1 -0
- package/dist/provider-N0ylgK8f.d.ts +48 -0
- package/dist/testing.d.ts +24 -0
- package/dist/testing.js +96 -0
- package/dist/testing.js.map +1 -0
- package/dist/types-BS3hq4-i.d.ts +119 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Benji Fisher
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// src/provider.ts
|
|
2
|
+
var StaleVersionError = class extends Error {
|
|
3
|
+
constructor(register, id, expected, actual) {
|
|
4
|
+
super(
|
|
5
|
+
`Stale write to ${register}/${id}: caller had version ${expected}, stored is ${actual}.`
|
|
6
|
+
);
|
|
7
|
+
this.register = register;
|
|
8
|
+
this.id = id;
|
|
9
|
+
this.expected = expected;
|
|
10
|
+
this.actual = actual;
|
|
11
|
+
}
|
|
12
|
+
register;
|
|
13
|
+
id;
|
|
14
|
+
expected;
|
|
15
|
+
actual;
|
|
16
|
+
name = "StaleVersionError";
|
|
17
|
+
};
|
|
18
|
+
function isStaleVersionError(e) {
|
|
19
|
+
return e instanceof StaleVersionError;
|
|
20
|
+
}
|
|
21
|
+
var NotFoundError = class extends Error {
|
|
22
|
+
constructor(register, id) {
|
|
23
|
+
super(`No ${register} record with id ${id}.`);
|
|
24
|
+
this.register = register;
|
|
25
|
+
this.id = id;
|
|
26
|
+
}
|
|
27
|
+
register;
|
|
28
|
+
id;
|
|
29
|
+
name = "NotFoundError";
|
|
30
|
+
};
|
|
31
|
+
function isNotFoundError(e) {
|
|
32
|
+
return e instanceof NotFoundError;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/relations.ts
|
|
36
|
+
var RELATIONS = {
|
|
37
|
+
"assumption-reading": {
|
|
38
|
+
from: { register: "assumptions", field: "readingIds", cardinality: "many" },
|
|
39
|
+
to: { register: "readings", field: "assumptionId", cardinality: "one" }
|
|
40
|
+
},
|
|
41
|
+
"assumption-depends-on": {
|
|
42
|
+
from: {
|
|
43
|
+
register: "assumptions",
|
|
44
|
+
field: "dependsOnIds",
|
|
45
|
+
cardinality: "many"
|
|
46
|
+
},
|
|
47
|
+
to: { register: "assumptions", field: "enablesIds", cardinality: "many" }
|
|
48
|
+
},
|
|
49
|
+
"assumption-contradicts": {
|
|
50
|
+
from: {
|
|
51
|
+
register: "assumptions",
|
|
52
|
+
field: "contradictsIds",
|
|
53
|
+
cardinality: "many"
|
|
54
|
+
},
|
|
55
|
+
to: {
|
|
56
|
+
register: "assumptions",
|
|
57
|
+
field: "contradictsIds",
|
|
58
|
+
cardinality: "many"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"reading-experiment": {
|
|
62
|
+
from: { register: "readings", field: "experimentId", cardinality: "one" },
|
|
63
|
+
to: null
|
|
64
|
+
// experiment→readings is derived
|
|
65
|
+
},
|
|
66
|
+
"reading-goal": {
|
|
67
|
+
from: { register: "readings", field: "goalId", cardinality: "one" },
|
|
68
|
+
to: null
|
|
69
|
+
},
|
|
70
|
+
"decision-based-on": {
|
|
71
|
+
from: { register: "decisions", field: "basedOnIds", cardinality: "many" },
|
|
72
|
+
to: null
|
|
73
|
+
// never touches the assumption
|
|
74
|
+
},
|
|
75
|
+
"decision-resolves": {
|
|
76
|
+
from: { register: "decisions", field: "resolvesIds", cardinality: "many" },
|
|
77
|
+
to: null
|
|
78
|
+
// mooting the assumption is a gated business action, not a link
|
|
79
|
+
},
|
|
80
|
+
"goal-based-on": {
|
|
81
|
+
from: { register: "goals", field: "basedOnIds", cardinality: "many" },
|
|
82
|
+
to: null
|
|
83
|
+
// goal linkage is a per-goal view, never stored on the assumption
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export {
|
|
88
|
+
StaleVersionError,
|
|
89
|
+
isStaleVersionError,
|
|
90
|
+
NotFoundError,
|
|
91
|
+
isNotFoundError,
|
|
92
|
+
RELATIONS
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=chunk-CUD5PEXL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/provider.ts","../src/relations.ts"],"sourcesContent":["/**\n * The `DataProvider` — the single integration seam between the dashboard/API\n * and a concrete backend. Hand-rolled (patterns borrowed from\n * Refine/React-Admin's dataProvider, not forked). One adapter per backend;\n * Firestore is the first. Writing a new adapter is a bounded task: implement\n * these five methods.\n */\nimport type { AnyRecord, Collection, RecordRef, Relation } from \"./types.js\";\n\nexport interface DataProvider {\n /** Every row of a register — never a filtered view (a filter drops rows). */\n list(register: Collection): Promise<AnyRecord[]>;\n get(register: Collection, id: string): Promise<AnyRecord>;\n create(register: Collection, data: Partial<AnyRecord>): Promise<AnyRecord>;\n /**\n * Version-guarded update: `version` is the value the caller loaded. If the\n * stored version has moved on, the write is rejected with\n * {@link StaleVersionError} (surfaced to the user as a 409).\n */\n update(\n register: Collection,\n id: string,\n patch: Partial<AnyRecord>,\n version: number,\n ): Promise<AnyRecord>;\n /** Set a relation on both ends in one logical write. */\n link(relation: Relation, from: RecordRef, to: RecordRef): Promise<void>;\n}\n\n/**\n * Thrown when an update carries a version older than what is stored — a\n * concurrent edit landed first. The API turns this into a 409 with\n * plain-language copy; it is never surfaced as version jargon.\n */\nexport class StaleVersionError extends Error {\n override readonly name = \"StaleVersionError\";\n constructor(\n readonly register: Collection,\n readonly id: string,\n readonly expected: number,\n readonly actual: number,\n ) {\n super(\n `Stale write to ${register}/${id}: caller had version ${expected}, ` +\n `stored is ${actual}.`,\n );\n }\n}\n\nexport function isStaleVersionError(e: unknown): e is StaleVersionError {\n return e instanceof StaleVersionError;\n}\n\n/** Thrown when a get/update targets a record that does not exist (→ 404). */\nexport class NotFoundError extends Error {\n override readonly name = \"NotFoundError\";\n constructor(\n readonly register: Collection,\n readonly id: string,\n ) {\n super(`No ${register} record with id ${id}.`);\n }\n}\n\nexport function isNotFoundError(e: unknown): e is NotFoundError {\n return e instanceof NotFoundError;\n}\n","/**\n * Relation config — the single table describing, for each linkable relation,\n * which field on which register holds each end. `link()` sets both ends from\n * this table, so relations stay consistent no matter which side initiated.\n *\n * A `null` `to` end means the inverse is a *derived view*, not a stored field\n * (e.g. \"experiments testing me\" is computed over bar-lines; a decision's\n * `Based on` never touches the assumption). Those relations write one end.\n */\nimport type { Collection, Relation } from \"./types.js\";\n\nexport interface RelationEnd {\n register: Collection;\n field: string;\n cardinality: \"one\" | \"many\";\n}\n\nexport interface RelationSpec {\n /** The end named by `from` in `link(relation, from, to)`. */\n from: RelationEnd;\n /** The end named by `to`; null when the inverse is derived, not stored. */\n to: RelationEnd | null;\n}\n\nexport const RELATIONS: Record<Relation, RelationSpec> = {\n \"assumption-reading\": {\n from: { register: \"assumptions\", field: \"readingIds\", cardinality: \"many\" },\n to: { register: \"readings\", field: \"assumptionId\", cardinality: \"one\" },\n },\n \"assumption-depends-on\": {\n from: {\n register: \"assumptions\",\n field: \"dependsOnIds\",\n cardinality: \"many\",\n },\n to: { register: \"assumptions\", field: \"enablesIds\", cardinality: \"many\" },\n },\n \"assumption-contradicts\": {\n from: {\n register: \"assumptions\",\n field: \"contradictsIds\",\n cardinality: \"many\",\n },\n to: {\n register: \"assumptions\",\n field: \"contradictsIds\",\n cardinality: \"many\",\n },\n },\n \"reading-experiment\": {\n from: { register: \"readings\", field: \"experimentId\", cardinality: \"one\" },\n to: null, // experiment→readings is derived\n },\n \"reading-goal\": {\n from: { register: \"readings\", field: \"goalId\", cardinality: \"one\" },\n to: null,\n },\n \"decision-based-on\": {\n from: { register: \"decisions\", field: \"basedOnIds\", cardinality: \"many\" },\n to: null, // never touches the assumption\n },\n \"decision-resolves\": {\n from: { register: \"decisions\", field: \"resolvesIds\", cardinality: \"many\" },\n to: null, // mooting the assumption is a gated business action, not a link\n },\n \"goal-based-on\": {\n from: { register: \"goals\", field: \"basedOnIds\", cardinality: \"many\" },\n to: null, // goal linkage is a per-goal view, never stored on the assumption\n },\n};\n"],"mappings":";AAkCO,IAAM,oBAAN,cAAgC,MAAM;AAAA,EAE3C,YACW,UACA,IACA,UACA,QACT;AACA;AAAA,MACE,kBAAkB,QAAQ,IAAI,EAAE,wBAAwB,QAAQ,eACjD,MAAM;AAAA,IACvB;AARS;AACA;AACA;AACA;AAAA,EAMX;AAAA,EATW;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EALO,OAAO;AAY3B;AAEO,SAAS,oBAAoB,GAAoC;AACtE,SAAO,aAAa;AACtB;AAGO,IAAM,gBAAN,cAA4B,MAAM;AAAA,EAEvC,YACW,UACA,IACT;AACA,UAAM,MAAM,QAAQ,mBAAmB,EAAE,GAAG;AAHnC;AACA;AAAA,EAGX;AAAA,EAJW;AAAA,EACA;AAAA,EAHO,OAAO;AAO3B;AAEO,SAAS,gBAAgB,GAAgC;AAC9D,SAAO,aAAa;AACtB;;;AC1CO,IAAM,YAA4C;AAAA,EACvD,sBAAsB;AAAA,IACpB,MAAM,EAAE,UAAU,eAAe,OAAO,cAAc,aAAa,OAAO;AAAA,IAC1E,IAAI,EAAE,UAAU,YAAY,OAAO,gBAAgB,aAAa,MAAM;AAAA,EACxE;AAAA,EACA,yBAAyB;AAAA,IACvB,MAAM;AAAA,MACJ,UAAU;AAAA,MACV,OAAO;AAAA,MACP,aAAa;AAAA,IACf;AAAA,IACA,IAAI,EAAE,UAAU,eAAe,OAAO,cAAc,aAAa,OAAO;AAAA,EAC1E;AAAA,EACA,0BAA0B;AAAA,IACxB,MAAM;AAAA,MACJ,UAAU;AAAA,MACV,OAAO;AAAA,MACP,aAAa;AAAA,IACf;AAAA,IACA,IAAI;AAAA,MACF,UAAU;AAAA,MACV,OAAO;AAAA,MACP,aAAa;AAAA,IACf;AAAA,EACF;AAAA,EACA,sBAAsB;AAAA,IACpB,MAAM,EAAE,UAAU,YAAY,OAAO,gBAAgB,aAAa,MAAM;AAAA,IACxE,IAAI;AAAA;AAAA,EACN;AAAA,EACA,gBAAgB;AAAA,IACd,MAAM,EAAE,UAAU,YAAY,OAAO,UAAU,aAAa,MAAM;AAAA,IAClE,IAAI;AAAA,EACN;AAAA,EACA,qBAAqB;AAAA,IACnB,MAAM,EAAE,UAAU,aAAa,OAAO,cAAc,aAAa,OAAO;AAAA,IACxE,IAAI;AAAA;AAAA,EACN;AAAA,EACA,qBAAqB;AAAA,IACnB,MAAM,EAAE,UAAU,aAAa,OAAO,eAAe,aAAa,OAAO;AAAA,IACzE,IAAI;AAAA;AAAA,EACN;AAAA,EACA,iBAAiB;AAAA,IACf,MAAM,EAAE,UAAU,SAAS,OAAO,cAAc,aAAa,OAAO;AAAA,IACpE,IAAI;AAAA;AAAA,EACN;AACF;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,172 @@
|
|
|
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
|
+
derivedImpacts: () => derivedImpacts,
|
|
13
|
+
isGoalRung: () => isGoalRung,
|
|
14
|
+
readingStrength: () => readingStrength,
|
|
15
|
+
risk: () => risk,
|
|
16
|
+
round2: () => round2,
|
|
17
|
+
sign: () => sign,
|
|
18
|
+
sourceQuality: () => sourceQuality
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// src/derivation/round.ts
|
|
22
|
+
function round2(n) {
|
|
23
|
+
return Number(n.toFixed(2));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/types.ts
|
|
27
|
+
var REGISTERS = [
|
|
28
|
+
"assumptions",
|
|
29
|
+
"experiments",
|
|
30
|
+
"readings",
|
|
31
|
+
"goals",
|
|
32
|
+
"decisions",
|
|
33
|
+
"glossary"
|
|
34
|
+
];
|
|
35
|
+
var TESTING_RUNGS = [
|
|
36
|
+
"Opinion",
|
|
37
|
+
"Pitch-deck reaction",
|
|
38
|
+
"Anecdotal",
|
|
39
|
+
"Desk research",
|
|
40
|
+
"Survey at scale",
|
|
41
|
+
"Prototype usage"
|
|
42
|
+
];
|
|
43
|
+
var GOAL_RUNG_VALUES = ["Signed intent", "Paying users"];
|
|
44
|
+
|
|
45
|
+
// src/derivation/rung.ts
|
|
46
|
+
var RUNG_ANCHOR = {
|
|
47
|
+
Opinion: 3,
|
|
48
|
+
"Pitch-deck reaction": 6,
|
|
49
|
+
Anecdotal: 10,
|
|
50
|
+
"Desk research": 15,
|
|
51
|
+
"Survey at scale": 25,
|
|
52
|
+
"Prototype usage": 30
|
|
53
|
+
};
|
|
54
|
+
var GOAL_RUNG_ANCHOR = {
|
|
55
|
+
"Signed intent": { Low: 55, Typical: 68, High: 80 },
|
|
56
|
+
"Paying users": { Low: 75, Typical: 88, High: 99 }
|
|
57
|
+
};
|
|
58
|
+
var GOAL_RUNG_SET = new Set(GOAL_RUNG_VALUES);
|
|
59
|
+
function isGoalRung(rung) {
|
|
60
|
+
return GOAL_RUNG_SET.has(rung);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/derivation/strength.ts
|
|
64
|
+
function sign(result) {
|
|
65
|
+
if (result === "Validated") return 1;
|
|
66
|
+
if (result === "Invalidated") return -1;
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
function readingStrength(input) {
|
|
70
|
+
const s = sign(input.result);
|
|
71
|
+
if (s === 0) return 0;
|
|
72
|
+
if (isGoalRung(input.rung)) {
|
|
73
|
+
const band = input.magnitudeBand ?? "Typical";
|
|
74
|
+
return GOAL_RUNG_ANCHOR[input.rung][band] * s;
|
|
75
|
+
}
|
|
76
|
+
return (RUNG_ANCHOR[input.rung] ?? 0) * s;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/derivation/source-quality.ts
|
|
80
|
+
function sourceQuality(representativeness, credibility) {
|
|
81
|
+
return round2(representativeness * credibility);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/derivation/confidence.ts
|
|
85
|
+
var W0 = 100;
|
|
86
|
+
function confidence(readings) {
|
|
87
|
+
const scored = readings.filter((r) => r.result === "Validated" || r.result === "Invalidated").map((r) => ({
|
|
88
|
+
input: r,
|
|
89
|
+
strength: readingStrength(r),
|
|
90
|
+
sq: sourceQuality(r.representativeness, r.credibility)
|
|
91
|
+
})).filter((x) => x.strength !== 0);
|
|
92
|
+
const best = /* @__PURE__ */ new Map();
|
|
93
|
+
for (const x of scored) {
|
|
94
|
+
if (isGoalRung(x.input.rung)) {
|
|
95
|
+
best.set(x.input.id, x);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const key = x.input.source || x.input.id;
|
|
99
|
+
const cur = best.get(key);
|
|
100
|
+
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 || "");
|
|
101
|
+
if (better) best.set(key, x);
|
|
102
|
+
}
|
|
103
|
+
let num = 0;
|
|
104
|
+
let den = W0;
|
|
105
|
+
for (const x of best.values()) {
|
|
106
|
+
const w = Math.abs(x.strength) * x.sq;
|
|
107
|
+
num += w * x.strength;
|
|
108
|
+
den += w;
|
|
109
|
+
}
|
|
110
|
+
return den > 0 ? round2(num / den) : 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/derivation/impact.ts
|
|
114
|
+
function derivedImpacts(assumptions, basedOnCounts = {}) {
|
|
115
|
+
const byId = new Map(assumptions.map((a) => [a.id, a]));
|
|
116
|
+
const dependents = /* @__PURE__ */ new Map();
|
|
117
|
+
for (const a of assumptions) dependents.set(a.id, []);
|
|
118
|
+
for (const a of assumptions) {
|
|
119
|
+
for (const dep of a.dependsOnIds) dependents.get(dep)?.push(a.id);
|
|
120
|
+
}
|
|
121
|
+
const memo = /* @__PURE__ */ new Map();
|
|
122
|
+
const compute = (id, seen) => {
|
|
123
|
+
const cached = memo.get(id);
|
|
124
|
+
if (cached !== void 0) return cached;
|
|
125
|
+
if (seen.has(id)) return 0;
|
|
126
|
+
seen.add(id);
|
|
127
|
+
const a = byId.get(id);
|
|
128
|
+
if (!a) return 0;
|
|
129
|
+
if (a.moot) {
|
|
130
|
+
memo.set(id, 0);
|
|
131
|
+
return 0;
|
|
132
|
+
}
|
|
133
|
+
const seed = a.impact ?? 0;
|
|
134
|
+
let S = 0;
|
|
135
|
+
for (const depId of dependents.get(id) ?? []) {
|
|
136
|
+
const d = byId.get(depId);
|
|
137
|
+
if (d && !d.moot) S += compute(depId, seen);
|
|
138
|
+
}
|
|
139
|
+
S += 100 * (basedOnCounts[id] ?? 0);
|
|
140
|
+
const value = seed + (100 - seed) * (S / (S + 100));
|
|
141
|
+
const rounded = round2(value);
|
|
142
|
+
memo.set(id, rounded);
|
|
143
|
+
return rounded;
|
|
144
|
+
};
|
|
145
|
+
const out = /* @__PURE__ */ new Map();
|
|
146
|
+
for (const a of assumptions) out.set(a.id, compute(a.id, /* @__PURE__ */ new Set()));
|
|
147
|
+
return out;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/derivation/risk.ts
|
|
151
|
+
function risk(derivedImpact, confidence2) {
|
|
152
|
+
return round2(derivedImpact * (1 - Math.max(0, confidence2) / 100));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export {
|
|
156
|
+
REGISTERS,
|
|
157
|
+
TESTING_RUNGS,
|
|
158
|
+
GOAL_RUNG_VALUES,
|
|
159
|
+
round2,
|
|
160
|
+
RUNG_ANCHOR,
|
|
161
|
+
GOAL_RUNG_ANCHOR,
|
|
162
|
+
isGoalRung,
|
|
163
|
+
sign,
|
|
164
|
+
readingStrength,
|
|
165
|
+
sourceQuality,
|
|
166
|
+
W0,
|
|
167
|
+
confidence,
|
|
168
|
+
derivedImpacts,
|
|
169
|
+
risk,
|
|
170
|
+
derivation_exports
|
|
171
|
+
};
|
|
172
|
+
//# sourceMappingURL=chunk-ZNOCENEV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/derivation/index.ts","../src/derivation/round.ts","../src/types.ts","../src/derivation/rung.ts","../src/derivation/strength.ts","../src/derivation/source-quality.ts","../src/derivation/confidence.ts","../src/derivation/impact.ts","../src/derivation/risk.ts"],"sourcesContent":["/**\n * The shared derivation module — pure functions, no I/O.\n *\n * The same module the dashboard, the API (derive-on-write), and Claude Code\n * audits all call, so every writer computes the four derived numbers\n * identically. Ported from `doshi-validation-os/migration/remodel.mjs` and\n * kept in lock-step with `skills/_shared/ontology.yaml`.\n */\nexport { round2 } from \"./round.js\";\nexport {\n RUNG_ANCHOR,\n GOAL_RUNG_ANCHOR,\n isGoalRung,\n} from \"./rung.js\";\nexport { sign, readingStrength } from \"./strength.js\";\nexport type { StrengthInput } from \"./strength.js\";\nexport { sourceQuality } from \"./source-quality.js\";\nexport { confidence, W0 } from \"./confidence.js\";\nexport type { ConfidenceReadingInput } from \"./confidence.js\";\nexport { derivedImpacts } from \"./impact.js\";\nexport type { ImpactAssumptionInput } from \"./impact.js\";\nexport { risk } from \"./risk.js\";\n","/**\n * Round to 2 decimals, matching the migration's `+(n).toFixed(2)`.\n * Derived values are stored/displayed rounded; full precision is only used\n * transiently inside a single computation.\n */\nexport function round2(n: number): number {\n return Number(n.toFixed(2));\n}\n","/**\n * Shared registry types — the field vocabulary the whole system speaks.\n *\n * These mirror `skills/_shared/registry-schema.md` and `ontology.yaml` (the\n * single source of truth). Field *meaning* lives there; this file is the\n * checkable TypeScript shape the packages import.\n */\n\n/** The six registers plus the `people` reference collection. */\nexport const REGISTERS = [\n \"assumptions\",\n \"experiments\",\n \"readings\",\n \"goals\",\n \"decisions\",\n \"glossary\",\n] as const;\nexport type Register = (typeof REGISTERS)[number];\n\n/** `people` is a reference collection, not one of the six registers. */\nexport type Collection = Register | \"people\";\n\n/** Every stored record carries an id, a version, and timestamps. */\nexport interface BaseRecord {\n id: string;\n /** Optimistic-concurrency token; bumped on every write. */\n version: number;\n createdAt: string;\n updatedAt: string;\n}\n\n// ── Vocabularies (canonical select-option lists) ────────────────────────────\n\nexport type AssumptionStatus = \"Draft\" | \"Live\" | \"Invalidated\";\nexport type ExperimentStatus = \"Running\" | \"Closed\";\nexport type GoalStatus = \"Draft\" | \"Active\" | \"Closed\";\nexport type DecisionStatus =\n | \"Active\"\n | \"Provisional\"\n | \"Superseded\"\n | \"Reversed\";\nexport type GlossaryStatus = \"Active\" | \"Provisional\" | \"Superseded\";\n\nexport type Result = \"Validated\" | \"Invalidated\" | \"Inconclusive\";\nexport type MagnitudeBand = \"Low\" | \"Typical\" | \"High\";\nexport type Feasibility = \"High\" | \"Medium\" | \"Low\";\n\n/** The 8-rung activity-and-strength ladder (order = strength, weakest first). */\nexport const TESTING_RUNGS = [\n \"Opinion\",\n \"Pitch-deck reaction\",\n \"Anecdotal\",\n \"Desk research\",\n \"Survey at scale\",\n \"Prototype usage\",\n] as const;\nexport const GOAL_RUNG_VALUES = [\"Signed intent\", \"Paying users\"] as const;\nexport type TestingRung = (typeof TESTING_RUNGS)[number];\nexport type GoalRung = (typeof GOAL_RUNG_VALUES)[number];\nexport type Rung = TestingRung | GoalRung;\n\n/** Representativeness and Credibility are each picked from these. */\nexport type SourceQualityPick = 1.0 | 0.7 | 0.5;\n\n// ── Records ─────────────────────────────────────────────────────────────────\n\n/** The four derived numbers stored on an assumption (never hand-typed). */\nexport interface AssumptionDerived {\n derivedImpact: number;\n risk: number;\n confidence: number;\n}\n\nexport interface AssumptionRecord extends BaseRecord {\n Title: string;\n Description: string;\n Lens: string | null;\n Theme: string[];\n /** The hand-scored seed (0–100), the only hand-scored number here. */\n Impact: number | null;\n Status: AssumptionStatus;\n Owner: string[];\n Gaps: string[];\n moot: boolean;\n dependsOnIds: string[];\n enablesIds: string[];\n contradictsIds: string[];\n readingIds: string[];\n derived: AssumptionDerived;\n}\n\nexport interface ReadingRecord extends BaseRecord {\n Title: string;\n /** First-class link to the artifact — the independence-dedupe key. */\n Source: string | null;\n assumptionId: string;\n experimentId: string | null;\n goalId: string | null;\n Rung: Rung;\n Representativeness: SourceQualityPick;\n Credibility: SourceQualityPick;\n /** For Goal-rung readings: the magnitude band from the absolute outcome. */\n magnitudeBand?: MagnitudeBand;\n Result: Result;\n Date: string | null;\n Owner: string[];\n derived: { sourceQuality: number; strength: number };\n}\n\nexport interface ExperimentRecord extends BaseRecord {\n Title: string;\n Instrument: string | null;\n Feasibility: Feasibility | null;\n Status: ExperimentStatus;\n closureReason: \"Completed\" | \"Early-stop\" | \"Kill\" | null;\n Owner: string[];\n Date: string | null;\n barLineAssumptionIds: string[];\n}\n\nexport interface GoalRecord extends BaseRecord {\n Title: string;\n Status: GoalStatus;\n Outcome: \"Achieved\" | \"Missed\" | \"Dropped\" | null;\n Owner: string[];\n Date: string | null;\n basedOnIds: string[];\n}\n\nexport interface DecisionRecord extends BaseRecord {\n Title: string;\n Status: DecisionStatus;\n Owner: string[];\n basedOnIds: string[];\n resolvesIds: string[];\n}\n\nexport interface GlossaryRecord extends BaseRecord {\n Title: string;\n Status: GlossaryStatus;\n}\n\n/** A record of any register — the DataProvider's currency. */\nexport type AnyRecord = BaseRecord & Record<string, unknown>;\n\n// ── Relations (both-ends links) ─────────────────────────────────────────────\n\n/** A pointer to one record. */\nexport interface RecordRef {\n register: Collection;\n id: string;\n}\n\n/** The relations the dashboard can set (each writes both ends). */\nexport type Relation =\n | \"assumption-reading\"\n | \"assumption-depends-on\"\n | \"assumption-contradicts\"\n | \"reading-experiment\"\n | \"reading-goal\"\n | \"decision-based-on\"\n | \"decision-resolves\"\n | \"goal-based-on\";\n","/**\n * The evidence ladder anchors that feed Strength.\n *\n * Source of truth: `skills/_shared/ontology.yaml` → `vocabularies.rung`.\n * Testing rungs carry a single anchor; Goal rungs carry a magnitude band\n * (Low/Typical/High) picked from the absolute outcome.\n */\nimport type { GoalRung, MagnitudeBand, Rung, TestingRung } from \"../types.js\";\nimport { GOAL_RUNG_VALUES } from \"../types.js\";\n\nexport const RUNG_ANCHOR: Record<TestingRung, number> = {\n Opinion: 3,\n \"Pitch-deck reaction\": 6,\n Anecdotal: 10,\n \"Desk research\": 15,\n \"Survey at scale\": 25,\n \"Prototype usage\": 30,\n};\n\nexport const GOAL_RUNG_ANCHOR: Record<GoalRung, Record<MagnitudeBand, number>> =\n {\n \"Signed intent\": { Low: 55, Typical: 68, High: 80 },\n \"Paying users\": { Low: 75, Typical: 88, High: 99 },\n };\n\nconst GOAL_RUNG_SET = new Set<Rung>(GOAL_RUNG_VALUES);\n\nexport function isGoalRung(rung: Rung): rung is GoalRung {\n return GOAL_RUNG_SET.has(rung);\n}\n","/**\n * Strength — the signed reading value `s` the Confidence average reads.\n *\n * Formula (`ontology.yaml` → `derivations.strength`):\n * rung anchor (Goal rungs: × magnitude band) × sign(Result)\n * — Validated positive, Invalidated negative; 0 unless Validated/Invalidated.\n */\nimport type { MagnitudeBand, Result, Rung } from \"../types.js\";\nimport { GOAL_RUNG_ANCHOR, RUNG_ANCHOR, isGoalRung } from \"./rung.js\";\n\nexport function sign(result: Result): -1 | 0 | 1 {\n if (result === \"Validated\") return 1;\n if (result === \"Invalidated\") return -1;\n return 0;\n}\n\nexport interface StrengthInput {\n rung: Rung;\n result: Result;\n /** Only read for Goal rungs; defaults to \"Typical\" when absent. */\n magnitudeBand?: MagnitudeBand;\n}\n\nexport function readingStrength(input: StrengthInput): number {\n const s = sign(input.result);\n if (s === 0) return 0; // Inconclusive contributes nothing.\n if (isGoalRung(input.rung)) {\n const band = input.magnitudeBand ?? \"Typical\";\n return GOAL_RUNG_ANCHOR[input.rung][band] * s;\n }\n return (RUNG_ANCHOR[input.rung] ?? 0) * s;\n}\n","/**\n * Source quality — Representativeness × Credibility.\n *\n * Scales a Reading's *weight* in the Confidence average, within its rung.\n * We keep the raw product as the weight (matching the migration's\n * `remodel.mjs`); the five display anchors {0.25, 0.35, 0.5, 0.7, 1.0} are a\n * storage/display concern, not the weight used in the average.\n */\nimport { round2 } from \"./round.js\";\n\nexport function sourceQuality(\n representativeness: number,\n credibility: number,\n): number {\n return round2(representativeness * credibility);\n}\n","/**\n * Confidence — signed −100…100, 0 = no evidence.\n *\n * Formula (`ontology.yaml` → `derivations.confidence`):\n * (w0·0 + Σ wi·si) / (w0 + Σ wi), w0 = 100,\n * wi = |si| × Source quality, si = the reading's signed Strength.\n *\n * Only concluded Validated/Invalidated readings enter. Readings sharing a\n * Source against one belief dedupe to the strongest (largest |si|, most\n * recent on ties). Goal-rung readings never dedupe (each closed goal is its\n * own unit). No corroboration bump.\n */\nimport type { MagnitudeBand, Result, Rung } from \"../types.js\";\nimport { round2 } from \"./round.js\";\nimport { isGoalRung } from \"./rung.js\";\nimport { sourceQuality } from \"./source-quality.js\";\nimport { readingStrength } from \"./strength.js\";\n\n/** The neutral prior weight — a hard floor per the guardrails. */\nexport const W0 = 100;\n\nexport interface ConfidenceReadingInput {\n id: string;\n /** The independence-dedupe key. Null falls back to the reading's own id. */\n source: string | null;\n rung: Rung;\n result: Result;\n representativeness: number;\n credibility: number;\n /** ISO date; used only as the dedupe tie-break (most recent wins). */\n date?: string | null;\n magnitudeBand?: MagnitudeBand;\n}\n\ninterface Scored {\n input: ConfidenceReadingInput;\n strength: number;\n sq: number;\n}\n\nexport function confidence(readings: ConfidenceReadingInput[]): number {\n const scored: Scored[] = readings\n .filter((r) => r.result === \"Validated\" || r.result === \"Invalidated\")\n .map((r) => ({\n input: r,\n strength: readingStrength(r),\n sq: sourceQuality(r.representativeness, r.credibility),\n }))\n .filter((x) => x.strength !== 0);\n\n // Dedupe by Source; goal rungs never dedupe.\n const best = new Map<string, Scored>();\n for (const x of scored) {\n if (isGoalRung(x.input.rung)) {\n best.set(x.input.id, x);\n continue;\n }\n const key = x.input.source || x.input.id;\n const cur = best.get(key);\n const better =\n !cur ||\n Math.abs(x.strength) > Math.abs(cur.strength) ||\n (Math.abs(x.strength) === Math.abs(cur.strength) &&\n (x.input.date || \"\") > (cur.input.date || \"\"));\n if (better) best.set(key, x);\n }\n\n let num = 0;\n let den = W0;\n for (const x of best.values()) {\n const w = Math.abs(x.strength) * x.sq;\n num += w * x.strength;\n den += w;\n }\n return den > 0 ? round2(num / den) : 0;\n}\n","/**\n * Derived Impact — propagates dependents' pull into a belief's seed.\n *\n * Formula (`ontology.yaml` → `derivations.derived_impact`):\n * seed + (100 − seed) × S / (S + 100), where\n * S = Σ Derived Impact of assumptions whose `Depends on` names this row\n * + 100 per standing (Provisional/Active) decision naming it via\n * `Based on assumption`.\n * Goals never contribute. Moot rows pin to 0 and contribute nothing.\n *\n * One reverse-topological pass (dependents first) with memoization and a\n * cycle guard, matching `doshi-validation-os/migration/remodel.mjs`.\n */\nimport { round2 } from \"./round.js\";\n\nexport interface ImpactAssumptionInput {\n id: string;\n /** The hand-scored seed (0–100); null treated as 0. */\n impact: number | null;\n moot?: boolean;\n /** Ids this assumption depends on. */\n dependsOnIds: string[];\n}\n\n/**\n * @param assumptions the full register (never a filtered slice — a filter\n * silently drops dependents from the propagation).\n * @param basedOnCounts id → number of standing decisions with a `Based on`\n * link to that assumption. Each contributes +100 to S.\n */\nexport function derivedImpacts(\n assumptions: ImpactAssumptionInput[],\n basedOnCounts: Record<string, number> = {},\n): Map<string, number> {\n const byId = new Map(assumptions.map((a) => [a.id, a]));\n\n // dependents(X) = assumptions whose dependsOnIds includes X.\n const dependents = new Map<string, string[]>();\n for (const a of assumptions) dependents.set(a.id, []);\n for (const a of assumptions) {\n for (const dep of a.dependsOnIds) dependents.get(dep)?.push(a.id);\n }\n\n const memo = new Map<string, number>();\n const compute = (id: string, seen: Set<string>): number => {\n const cached = memo.get(id);\n if (cached !== undefined) return cached;\n if (seen.has(id)) return 0; // cycle guard\n seen.add(id);\n const a = byId.get(id);\n if (!a) return 0;\n if (a.moot) {\n memo.set(id, 0);\n return 0;\n }\n const seed = a.impact ?? 0;\n let S = 0;\n for (const depId of dependents.get(id) ?? []) {\n const d = byId.get(depId);\n if (d && !d.moot) S += compute(depId, seen);\n }\n S += 100 * (basedOnCounts[id] ?? 0);\n const value = seed + (100 - seed) * (S / (S + 100));\n const rounded = round2(value);\n memo.set(id, rounded);\n return rounded;\n };\n\n const out = new Map<string, number>();\n for (const a of assumptions) out.set(a.id, compute(a.id, new Set()));\n return out;\n}\n","/**\n * Risk — the belief's live standing.\n *\n * Formula (`ontology.yaml` → `derivations.risk`):\n * Derived Impact × (1 − max(0, Confidence) / 100)\n * Ranges 0 to Derived Impact. Negative confidence does not raise Risk above\n * Derived Impact (the max(0, …) clamp).\n */\nimport { round2 } from \"./round.js\";\n\nexport function risk(derivedImpact: number, confidence: number): number {\n return round2(derivedImpact * (1 - Math.max(0, confidence) / 100));\n}\n"],"mappings":"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","names":["confidence"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { C as ConfidenceReadingInput, G as GOAL_RUNG_ANCHOR, I as ImpactAssumptionInput, R as RUNG_ANCHOR, S as StrengthInput, W as W0, c as confidence, d as derivedImpacts, a as isGoalRung, r as readingStrength, b as risk, e as round2, f as sign, s as sourceQuality } from '../index-BSd5_vwB.js';
|
|
2
|
+
import '../types-BS3hq4-i.js';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GOAL_RUNG_ANCHOR,
|
|
3
|
+
RUNG_ANCHOR,
|
|
4
|
+
W0,
|
|
5
|
+
confidence,
|
|
6
|
+
derivedImpacts,
|
|
7
|
+
isGoalRung,
|
|
8
|
+
readingStrength,
|
|
9
|
+
risk,
|
|
10
|
+
round2,
|
|
11
|
+
sign,
|
|
12
|
+
sourceQuality
|
|
13
|
+
} from "../chunk-ZNOCENEV.js";
|
|
14
|
+
import "../chunk-PZ5AY32C.js";
|
|
15
|
+
export {
|
|
16
|
+
GOAL_RUNG_ANCHOR,
|
|
17
|
+
RUNG_ANCHOR,
|
|
18
|
+
W0,
|
|
19
|
+
confidence,
|
|
20
|
+
derivedImpacts,
|
|
21
|
+
isGoalRung,
|
|
22
|
+
readingStrength,
|
|
23
|
+
risk,
|
|
24
|
+
round2,
|
|
25
|
+
sign,
|
|
26
|
+
sourceQuality
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { j as GoalRung, M as MagnitudeBand, q as TestingRung, p as Rung, o as Result } from './types-BS3hq4-i.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Round to 2 decimals, matching the migration's `+(n).toFixed(2)`.
|
|
5
|
+
* Derived values are stored/displayed rounded; full precision is only used
|
|
6
|
+
* transiently inside a single computation.
|
|
7
|
+
*/
|
|
8
|
+
declare function round2(n: number): number;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The evidence ladder anchors that feed Strength.
|
|
12
|
+
*
|
|
13
|
+
* Source of truth: `skills/_shared/ontology.yaml` → `vocabularies.rung`.
|
|
14
|
+
* Testing rungs carry a single anchor; Goal rungs carry a magnitude band
|
|
15
|
+
* (Low/Typical/High) picked from the absolute outcome.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
declare const RUNG_ANCHOR: Record<TestingRung, number>;
|
|
19
|
+
declare const GOAL_RUNG_ANCHOR: Record<GoalRung, Record<MagnitudeBand, number>>;
|
|
20
|
+
declare function isGoalRung(rung: Rung): rung is GoalRung;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Strength — the signed reading value `s` the Confidence average reads.
|
|
24
|
+
*
|
|
25
|
+
* Formula (`ontology.yaml` → `derivations.strength`):
|
|
26
|
+
* rung anchor (Goal rungs: × magnitude band) × sign(Result)
|
|
27
|
+
* — Validated positive, Invalidated negative; 0 unless Validated/Invalidated.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
declare function sign(result: Result): -1 | 0 | 1;
|
|
31
|
+
interface StrengthInput {
|
|
32
|
+
rung: Rung;
|
|
33
|
+
result: Result;
|
|
34
|
+
/** Only read for Goal rungs; defaults to "Typical" when absent. */
|
|
35
|
+
magnitudeBand?: MagnitudeBand;
|
|
36
|
+
}
|
|
37
|
+
declare function readingStrength(input: StrengthInput): number;
|
|
38
|
+
|
|
39
|
+
declare function sourceQuality(representativeness: number, credibility: number): number;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Confidence — signed −100…100, 0 = no evidence.
|
|
43
|
+
*
|
|
44
|
+
* Formula (`ontology.yaml` → `derivations.confidence`):
|
|
45
|
+
* (w0·0 + Σ wi·si) / (w0 + Σ wi), w0 = 100,
|
|
46
|
+
* wi = |si| × Source quality, si = the reading's signed Strength.
|
|
47
|
+
*
|
|
48
|
+
* Only concluded Validated/Invalidated readings enter. Readings sharing a
|
|
49
|
+
* Source against one belief dedupe to the strongest (largest |si|, most
|
|
50
|
+
* recent on ties). Goal-rung readings never dedupe (each closed goal is its
|
|
51
|
+
* own unit). No corroboration bump.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/** The neutral prior weight — a hard floor per the guardrails. */
|
|
55
|
+
declare const W0 = 100;
|
|
56
|
+
interface ConfidenceReadingInput {
|
|
57
|
+
id: string;
|
|
58
|
+
/** The independence-dedupe key. Null falls back to the reading's own id. */
|
|
59
|
+
source: string | null;
|
|
60
|
+
rung: Rung;
|
|
61
|
+
result: Result;
|
|
62
|
+
representativeness: number;
|
|
63
|
+
credibility: number;
|
|
64
|
+
/** ISO date; used only as the dedupe tie-break (most recent wins). */
|
|
65
|
+
date?: string | null;
|
|
66
|
+
magnitudeBand?: MagnitudeBand;
|
|
67
|
+
}
|
|
68
|
+
declare function confidence(readings: ConfidenceReadingInput[]): number;
|
|
69
|
+
|
|
70
|
+
interface ImpactAssumptionInput {
|
|
71
|
+
id: string;
|
|
72
|
+
/** The hand-scored seed (0–100); null treated as 0. */
|
|
73
|
+
impact: number | null;
|
|
74
|
+
moot?: boolean;
|
|
75
|
+
/** Ids this assumption depends on. */
|
|
76
|
+
dependsOnIds: string[];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* @param assumptions the full register (never a filtered slice — a filter
|
|
80
|
+
* silently drops dependents from the propagation).
|
|
81
|
+
* @param basedOnCounts id → number of standing decisions with a `Based on`
|
|
82
|
+
* link to that assumption. Each contributes +100 to S.
|
|
83
|
+
*/
|
|
84
|
+
declare function derivedImpacts(assumptions: ImpactAssumptionInput[], basedOnCounts?: Record<string, number>): Map<string, number>;
|
|
85
|
+
|
|
86
|
+
declare function risk(derivedImpact: number, confidence: number): number;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The shared derivation module — pure functions, no I/O.
|
|
90
|
+
*
|
|
91
|
+
* The same module the dashboard, the API (derive-on-write), and Claude Code
|
|
92
|
+
* audits all call, so every writer computes the four derived numbers
|
|
93
|
+
* identically. Ported from `doshi-validation-os/migration/remodel.mjs` and
|
|
94
|
+
* kept in lock-step with `skills/_shared/ontology.yaml`.
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
type index_ConfidenceReadingInput = ConfidenceReadingInput;
|
|
98
|
+
declare const index_GOAL_RUNG_ANCHOR: typeof GOAL_RUNG_ANCHOR;
|
|
99
|
+
type index_ImpactAssumptionInput = ImpactAssumptionInput;
|
|
100
|
+
declare const index_RUNG_ANCHOR: typeof RUNG_ANCHOR;
|
|
101
|
+
type index_StrengthInput = StrengthInput;
|
|
102
|
+
declare const index_W0: typeof W0;
|
|
103
|
+
declare const index_confidence: typeof confidence;
|
|
104
|
+
declare const index_derivedImpacts: typeof derivedImpacts;
|
|
105
|
+
declare const index_isGoalRung: typeof isGoalRung;
|
|
106
|
+
declare const index_readingStrength: typeof readingStrength;
|
|
107
|
+
declare const index_risk: typeof risk;
|
|
108
|
+
declare const index_round2: typeof round2;
|
|
109
|
+
declare const index_sign: typeof sign;
|
|
110
|
+
declare const index_sourceQuality: typeof sourceQuality;
|
|
111
|
+
declare namespace index {
|
|
112
|
+
export { type index_ConfidenceReadingInput as ConfidenceReadingInput, index_GOAL_RUNG_ANCHOR as GOAL_RUNG_ANCHOR, type index_ImpactAssumptionInput as ImpactAssumptionInput, index_RUNG_ANCHOR as RUNG_ANCHOR, type index_StrengthInput as StrengthInput, index_W0 as W0, index_confidence as confidence, index_derivedImpacts as derivedImpacts, index_isGoalRung as isGoalRung, index_readingStrength as readingStrength, index_risk as risk, index_round2 as round2, index_sign as sign, index_sourceQuality as sourceQuality };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export { type ConfidenceReadingInput as C, GOAL_RUNG_ANCHOR as G, type ImpactAssumptionInput as I, RUNG_ANCHOR as R, type StrengthInput as S, W0 as W, isGoalRung as a, risk as b, confidence as c, derivedImpacts as d, round2 as e, sign as f, index as i, readingStrength as r, sourceQuality as s };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { R as Relation, C as Collection, A as AssumptionRecord, a as ReadingRecord, D as DecisionRecord, b as AssumptionDerived } from './types-BS3hq4-i.js';
|
|
2
|
+
export { c as AnyRecord, d as AssumptionStatus, B as BaseRecord, e as DecisionStatus, E as ExperimentRecord, f as ExperimentStatus, F as Feasibility, G as GOAL_RUNG_VALUES, g as GlossaryRecord, h as GlossaryStatus, i as GoalRecord, j as GoalRung, k as GoalStatus, M as MagnitudeBand, l as REGISTERS, m as RecordRef, n as Register, o as Result, p as Rung, S as SourceQualityPick, T as TESTING_RUNGS, q as TestingRung } from './types-BS3hq4-i.js';
|
|
3
|
+
export { D as DataProvider, N as NotFoundError, S as StaleVersionError, i as isNotFoundError, a as isStaleVersionError } from './provider-N0ylgK8f.js';
|
|
4
|
+
export { i as derivation, s as recomputeSourceQuality, r as recomputeStrength } from './index-BSd5_vwB.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Relation config — the single table describing, for each linkable relation,
|
|
8
|
+
* which field on which register holds each end. `link()` sets both ends from
|
|
9
|
+
* this table, so relations stay consistent no matter which side initiated.
|
|
10
|
+
*
|
|
11
|
+
* A `null` `to` end means the inverse is a *derived view*, not a stored field
|
|
12
|
+
* (e.g. "experiments testing me" is computed over bar-lines; a decision's
|
|
13
|
+
* `Based on` never touches the assumption). Those relations write one end.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
interface RelationEnd {
|
|
17
|
+
register: Collection;
|
|
18
|
+
field: string;
|
|
19
|
+
cardinality: "one" | "many";
|
|
20
|
+
}
|
|
21
|
+
interface RelationSpec {
|
|
22
|
+
/** The end named by `from` in `link(relation, from, to)`. */
|
|
23
|
+
from: RelationEnd;
|
|
24
|
+
/** The end named by `to`; null when the inverse is derived, not stored. */
|
|
25
|
+
to: RelationEnd | null;
|
|
26
|
+
}
|
|
27
|
+
declare const RELATIONS: Record<Relation, RelationSpec>;
|
|
28
|
+
|
|
29
|
+
interface RecomputeInput {
|
|
30
|
+
assumptions: AssumptionRecord[];
|
|
31
|
+
readings: ReadingRecord[];
|
|
32
|
+
decisions: DecisionRecord[];
|
|
33
|
+
}
|
|
34
|
+
/** id → recomputed derived tuple for every assumption in the register. */
|
|
35
|
+
declare function recomputeDerived(input: RecomputeInput): Map<string, AssumptionDerived>;
|
|
36
|
+
|
|
37
|
+
export { AssumptionDerived, AssumptionRecord, Collection, DecisionRecord, RELATIONS, ReadingRecord, type RecomputeInput, Relation, type RelationEnd, type RelationSpec, recomputeDerived };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NotFoundError,
|
|
3
|
+
RELATIONS,
|
|
4
|
+
StaleVersionError,
|
|
5
|
+
isNotFoundError,
|
|
6
|
+
isStaleVersionError
|
|
7
|
+
} from "./chunk-CUD5PEXL.js";
|
|
8
|
+
import {
|
|
9
|
+
GOAL_RUNG_VALUES,
|
|
10
|
+
REGISTERS,
|
|
11
|
+
TESTING_RUNGS,
|
|
12
|
+
confidence,
|
|
13
|
+
derivation_exports,
|
|
14
|
+
derivedImpacts,
|
|
15
|
+
readingStrength,
|
|
16
|
+
risk,
|
|
17
|
+
sourceQuality
|
|
18
|
+
} from "./chunk-ZNOCENEV.js";
|
|
19
|
+
import "./chunk-PZ5AY32C.js";
|
|
20
|
+
|
|
21
|
+
// src/recompute.ts
|
|
22
|
+
var STANDING_DECISION = /* @__PURE__ */ new Set(["Active", "Provisional"]);
|
|
23
|
+
function toConfidenceInput(r) {
|
|
24
|
+
return {
|
|
25
|
+
id: r.id,
|
|
26
|
+
source: r.Source,
|
|
27
|
+
rung: r.Rung,
|
|
28
|
+
result: r.Result,
|
|
29
|
+
representativeness: r.Representativeness,
|
|
30
|
+
credibility: r.Credibility,
|
|
31
|
+
date: r.Date,
|
|
32
|
+
magnitudeBand: r.magnitudeBand
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function recomputeDerived(input) {
|
|
36
|
+
const { assumptions, readings, decisions } = input;
|
|
37
|
+
const readingsByAssumption = /* @__PURE__ */ new Map();
|
|
38
|
+
for (const a of assumptions) readingsByAssumption.set(a.id, []);
|
|
39
|
+
for (const r of readings) readingsByAssumption.get(r.assumptionId)?.push(r);
|
|
40
|
+
const confidenceById = /* @__PURE__ */ new Map();
|
|
41
|
+
for (const a of assumptions) {
|
|
42
|
+
const rs = readingsByAssumption.get(a.id) ?? [];
|
|
43
|
+
confidenceById.set(a.id, confidence(rs.map(toConfidenceInput)));
|
|
44
|
+
}
|
|
45
|
+
const basedOnCounts = {};
|
|
46
|
+
for (const d of decisions) {
|
|
47
|
+
if (!STANDING_DECISION.has(d.Status)) continue;
|
|
48
|
+
for (const aid of d.basedOnIds ?? []) {
|
|
49
|
+
basedOnCounts[aid] = (basedOnCounts[aid] ?? 0) + 1;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const impactById = derivedImpacts(
|
|
53
|
+
assumptions.map((a) => ({
|
|
54
|
+
id: a.id,
|
|
55
|
+
impact: a.moot ? 0 : a.Impact,
|
|
56
|
+
moot: a.moot,
|
|
57
|
+
dependsOnIds: a.dependsOnIds
|
|
58
|
+
})),
|
|
59
|
+
basedOnCounts
|
|
60
|
+
);
|
|
61
|
+
const out = /* @__PURE__ */ new Map();
|
|
62
|
+
for (const a of assumptions) {
|
|
63
|
+
const c = confidenceById.get(a.id) ?? 0;
|
|
64
|
+
const di = impactById.get(a.id) ?? 0;
|
|
65
|
+
out.set(a.id, { confidence: c, derivedImpact: di, risk: risk(di, c) });
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
GOAL_RUNG_VALUES,
|
|
71
|
+
NotFoundError,
|
|
72
|
+
REGISTERS,
|
|
73
|
+
RELATIONS,
|
|
74
|
+
StaleVersionError,
|
|
75
|
+
TESTING_RUNGS,
|
|
76
|
+
derivation_exports as derivation,
|
|
77
|
+
isNotFoundError,
|
|
78
|
+
isStaleVersionError,
|
|
79
|
+
recomputeDerived,
|
|
80
|
+
sourceQuality as recomputeSourceQuality,
|
|
81
|
+
readingStrength as recomputeStrength
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/recompute.ts"],"sourcesContent":["/**\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 * This is the one place that maps stored record shapes → the derivation\n * module's typed inputs, so the pure functions stay decoupled from field\n * names.\n */\nimport {\n confidence,\n derivedImpacts,\n risk,\n type ConfidenceReadingInput,\n} from \"./derivation/index.js\";\nimport type {\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\nfunction toConfidenceInput(r: ReadingRecord): ConfidenceReadingInput {\n return {\n id: r.id,\n source: r.Source,\n rung: r.Rung,\n result: r.Result,\n representativeness: r.Representativeness,\n credibility: r.Credibility,\n date: r.Date,\n magnitudeBand: r.magnitudeBand,\n };\n}\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(a.id, confidence(rs.map(toConfidenceInput)));\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, { confidence: c, derivedImpact: di, risk: risk(di, c) });\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":";;;;;;;;;;;;;;;;;;;;;AAyBA,IAAM,oBAAoB,oBAAI,IAAI,CAAC,UAAU,aAAa,CAAC;AAE3D,SAAS,kBAAkB,GAA0C;AACnE,SAAO;AAAA,IACL,IAAI,EAAE;AAAA,IACN,QAAQ,EAAE;AAAA,IACV,MAAM,EAAE;AAAA,IACR,QAAQ,EAAE;AAAA,IACV,oBAAoB,EAAE;AAAA,IACtB,aAAa,EAAE;AAAA,IACf,MAAM,EAAE;AAAA,IACR,eAAe,EAAE;AAAA,EACnB;AACF;AASO,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,IAAI,EAAE,IAAI,WAAW,GAAG,IAAI,iBAAiB,CAAC,CAAC;AAAA,EAChE;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,EAAE,YAAY,GAAG,eAAe,IAAI,MAAM,KAAK,IAAI,CAAC,EAAE,CAAC;AAAA,EACvE;AACA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { C as Collection, c as AnyRecord, R as Relation, m as RecordRef } from './types-BS3hq4-i.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The `DataProvider` — the single integration seam between the dashboard/API
|
|
5
|
+
* and a concrete backend. Hand-rolled (patterns borrowed from
|
|
6
|
+
* Refine/React-Admin's dataProvider, not forked). One adapter per backend;
|
|
7
|
+
* Firestore is the first. Writing a new adapter is a bounded task: implement
|
|
8
|
+
* these five methods.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface DataProvider {
|
|
12
|
+
/** Every row of a register — never a filtered view (a filter drops rows). */
|
|
13
|
+
list(register: Collection): Promise<AnyRecord[]>;
|
|
14
|
+
get(register: Collection, id: string): Promise<AnyRecord>;
|
|
15
|
+
create(register: Collection, data: Partial<AnyRecord>): Promise<AnyRecord>;
|
|
16
|
+
/**
|
|
17
|
+
* Version-guarded update: `version` is the value the caller loaded. If the
|
|
18
|
+
* stored version has moved on, the write is rejected with
|
|
19
|
+
* {@link StaleVersionError} (surfaced to the user as a 409).
|
|
20
|
+
*/
|
|
21
|
+
update(register: Collection, id: string, patch: Partial<AnyRecord>, version: number): Promise<AnyRecord>;
|
|
22
|
+
/** Set a relation on both ends in one logical write. */
|
|
23
|
+
link(relation: Relation, from: RecordRef, to: RecordRef): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Thrown when an update carries a version older than what is stored — a
|
|
27
|
+
* concurrent edit landed first. The API turns this into a 409 with
|
|
28
|
+
* plain-language copy; it is never surfaced as version jargon.
|
|
29
|
+
*/
|
|
30
|
+
declare class StaleVersionError extends Error {
|
|
31
|
+
readonly register: Collection;
|
|
32
|
+
readonly id: string;
|
|
33
|
+
readonly expected: number;
|
|
34
|
+
readonly actual: number;
|
|
35
|
+
readonly name = "StaleVersionError";
|
|
36
|
+
constructor(register: Collection, id: string, expected: number, actual: number);
|
|
37
|
+
}
|
|
38
|
+
declare function isStaleVersionError(e: unknown): e is StaleVersionError;
|
|
39
|
+
/** Thrown when a get/update targets a record that does not exist (→ 404). */
|
|
40
|
+
declare class NotFoundError extends Error {
|
|
41
|
+
readonly register: Collection;
|
|
42
|
+
readonly id: string;
|
|
43
|
+
readonly name = "NotFoundError";
|
|
44
|
+
constructor(register: Collection, id: string);
|
|
45
|
+
}
|
|
46
|
+
declare function isNotFoundError(e: unknown): e is NotFoundError;
|
|
47
|
+
|
|
48
|
+
export { type DataProvider as D, NotFoundError as N, StaleVersionError as S, isStaleVersionError as a, isNotFoundError as i };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { D as DataProvider } from './provider-N0ylgK8f.js';
|
|
2
|
+
import { C as Collection, c as AnyRecord, R as Relation, m as RecordRef } from './types-BS3hq4-i.js';
|
|
3
|
+
|
|
4
|
+
interface InMemoryProviderOptions {
|
|
5
|
+
now?: () => string;
|
|
6
|
+
/** Seed data keyed by register. */
|
|
7
|
+
seed?: Partial<Record<Collection, AnyRecord[]>>;
|
|
8
|
+
}
|
|
9
|
+
declare class InMemoryProvider implements DataProvider {
|
|
10
|
+
private readonly store;
|
|
11
|
+
private readonly now;
|
|
12
|
+
private counter;
|
|
13
|
+
constructor(options?: InMemoryProviderOptions);
|
|
14
|
+
private col;
|
|
15
|
+
list(register: Collection): Promise<AnyRecord[]>;
|
|
16
|
+
get(register: Collection, id: string): Promise<AnyRecord>;
|
|
17
|
+
create(register: Collection, data: Partial<AnyRecord>): Promise<AnyRecord>;
|
|
18
|
+
update(register: Collection, id: string, patch: Partial<AnyRecord>, version: number): Promise<AnyRecord>;
|
|
19
|
+
link(relation: Relation, from: RecordRef, to: RecordRef): Promise<void>;
|
|
20
|
+
private applyEnd;
|
|
21
|
+
}
|
|
22
|
+
declare function createInMemoryProvider(options?: InMemoryProviderOptions): InMemoryProvider;
|
|
23
|
+
|
|
24
|
+
export { InMemoryProvider, type InMemoryProviderOptions, createInMemoryProvider };
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NotFoundError,
|
|
3
|
+
RELATIONS,
|
|
4
|
+
StaleVersionError
|
|
5
|
+
} from "./chunk-CUD5PEXL.js";
|
|
6
|
+
import "./chunk-PZ5AY32C.js";
|
|
7
|
+
|
|
8
|
+
// src/testing.ts
|
|
9
|
+
var InMemoryProvider = class {
|
|
10
|
+
store = /* @__PURE__ */ new Map();
|
|
11
|
+
now;
|
|
12
|
+
counter = 0;
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
15
|
+
for (const [register, rows] of Object.entries(options.seed ?? {})) {
|
|
16
|
+
const col = this.col(register);
|
|
17
|
+
for (const r of rows ?? []) col.set(r.id, structuredClone(r));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
col(register) {
|
|
21
|
+
let c = this.store.get(register);
|
|
22
|
+
if (!c) {
|
|
23
|
+
c = /* @__PURE__ */ new Map();
|
|
24
|
+
this.store.set(register, c);
|
|
25
|
+
}
|
|
26
|
+
return c;
|
|
27
|
+
}
|
|
28
|
+
async list(register) {
|
|
29
|
+
return [...this.col(register).values()].map((r) => structuredClone(r));
|
|
30
|
+
}
|
|
31
|
+
async get(register, id) {
|
|
32
|
+
const r = this.col(register).get(id);
|
|
33
|
+
if (!r) throw new NotFoundError(register, id);
|
|
34
|
+
return structuredClone(r);
|
|
35
|
+
}
|
|
36
|
+
async create(register, data) {
|
|
37
|
+
const id = data.id ? String(data.id) : `${register}-${++this.counter}`;
|
|
38
|
+
if (this.col(register).has(id)) {
|
|
39
|
+
throw new Error(`${register}/${id} already exists`);
|
|
40
|
+
}
|
|
41
|
+
const ts = this.now();
|
|
42
|
+
const { id: _i, ...rest } = data;
|
|
43
|
+
const record = {
|
|
44
|
+
...rest,
|
|
45
|
+
id,
|
|
46
|
+
version: 0,
|
|
47
|
+
createdAt: ts,
|
|
48
|
+
updatedAt: ts
|
|
49
|
+
};
|
|
50
|
+
this.col(register).set(id, record);
|
|
51
|
+
return structuredClone(record);
|
|
52
|
+
}
|
|
53
|
+
async update(register, id, patch, version) {
|
|
54
|
+
const current = this.col(register).get(id);
|
|
55
|
+
if (!current) throw new NotFoundError(register, id);
|
|
56
|
+
if (current.version !== version) {
|
|
57
|
+
throw new StaleVersionError(register, id, version, current.version);
|
|
58
|
+
}
|
|
59
|
+
const { id: _i, version: _v, createdAt: _c, ...safe } = patch;
|
|
60
|
+
const next = {
|
|
61
|
+
...current,
|
|
62
|
+
...safe,
|
|
63
|
+
id,
|
|
64
|
+
version: current.version + 1,
|
|
65
|
+
updatedAt: this.now()
|
|
66
|
+
};
|
|
67
|
+
this.col(register).set(id, next);
|
|
68
|
+
return structuredClone(next);
|
|
69
|
+
}
|
|
70
|
+
async link(relation, from, to) {
|
|
71
|
+
const spec = RELATIONS[relation];
|
|
72
|
+
this.applyEnd(spec.from, from.register, from.id, to.id);
|
|
73
|
+
if (spec.to) this.applyEnd(spec.to, to.register, to.id, from.id);
|
|
74
|
+
}
|
|
75
|
+
applyEnd(end, register, ownerId, otherId) {
|
|
76
|
+
const rec = this.col(register).get(ownerId);
|
|
77
|
+
if (!rec) throw new NotFoundError(register, ownerId);
|
|
78
|
+
if (end.cardinality === "many") {
|
|
79
|
+
const arr = Array.isArray(rec[end.field]) ? rec[end.field] : [];
|
|
80
|
+
if (!arr.includes(otherId)) arr.push(otherId);
|
|
81
|
+
rec[end.field] = arr;
|
|
82
|
+
} else {
|
|
83
|
+
rec[end.field] = otherId;
|
|
84
|
+
}
|
|
85
|
+
rec.version += 1;
|
|
86
|
+
rec.updatedAt = this.now();
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
function createInMemoryProvider(options) {
|
|
90
|
+
return new InMemoryProvider(options);
|
|
91
|
+
}
|
|
92
|
+
export {
|
|
93
|
+
InMemoryProvider,
|
|
94
|
+
createInMemoryProvider
|
|
95
|
+
};
|
|
96
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testing.ts"],"sourcesContent":["/**\n * An in-memory `DataProvider` — the fast, deterministic fake that backs API\n * and dashboard tests (the spec's \"Seam 1\" in-memory adapter). It honours the\n * full contract, including version-guarded writes (409 on stale) and\n * both-ends linking, so tests exercise real behaviour without a database.\n */\nimport { NotFoundError, StaleVersionError } from \"./provider.js\";\nimport type { DataProvider } from \"./provider.js\";\nimport { RELATIONS } from \"./relations.js\";\nimport type { RelationEnd } from \"./relations.js\";\nimport type { AnyRecord, Collection, RecordRef, Relation } from \"./types.js\";\n\nexport interface InMemoryProviderOptions {\n now?: () => string;\n /** Seed data keyed by register. */\n seed?: Partial<Record<Collection, AnyRecord[]>>;\n}\n\nexport class InMemoryProvider implements DataProvider {\n private readonly store = new Map<Collection, Map<string, AnyRecord>>();\n private readonly now: () => string;\n private counter = 0;\n\n constructor(options: InMemoryProviderOptions = {}) {\n this.now = options.now ?? (() => new Date().toISOString());\n for (const [register, rows] of Object.entries(options.seed ?? {})) {\n const col = this.col(register as Collection);\n for (const r of rows ?? []) col.set(r.id, structuredClone(r));\n }\n }\n\n private col(register: Collection): Map<string, AnyRecord> {\n let c = this.store.get(register);\n if (!c) {\n c = new Map();\n this.store.set(register, c);\n }\n return c;\n }\n\n async list(register: Collection): Promise<AnyRecord[]> {\n return [...this.col(register).values()].map((r) => structuredClone(r));\n }\n\n async get(register: Collection, id: string): Promise<AnyRecord> {\n const r = this.col(register).get(id);\n if (!r) throw new NotFoundError(register, id);\n return structuredClone(r);\n }\n\n async create(\n register: Collection,\n data: Partial<AnyRecord>,\n ): Promise<AnyRecord> {\n const id = data.id ? String(data.id) : `${register}-${++this.counter}`;\n if (this.col(register).has(id)) {\n throw new Error(`${register}/${id} already exists`);\n }\n const ts = this.now();\n const { id: _i, ...rest } = data;\n const record = {\n ...rest,\n id,\n version: 0,\n createdAt: ts,\n updatedAt: ts,\n } as AnyRecord;\n this.col(register).set(id, record);\n return structuredClone(record);\n }\n\n async update(\n register: Collection,\n id: string,\n patch: Partial<AnyRecord>,\n version: number,\n ): Promise<AnyRecord> {\n const current = this.col(register).get(id);\n if (!current) throw new NotFoundError(register, id);\n if (current.version !== version) {\n throw new StaleVersionError(register, id, version, current.version);\n }\n const { id: _i, version: _v, createdAt: _c, ...safe } = patch;\n const next = {\n ...current,\n ...safe,\n id,\n version: current.version + 1,\n updatedAt: this.now(),\n } as AnyRecord;\n this.col(register).set(id, next);\n return structuredClone(next);\n }\n\n async link(relation: Relation, from: RecordRef, to: RecordRef): Promise<void> {\n const spec = RELATIONS[relation];\n this.applyEnd(spec.from, from.register, from.id, to.id);\n if (spec.to) this.applyEnd(spec.to, to.register, to.id, from.id);\n }\n\n private applyEnd(\n end: RelationEnd,\n register: Collection,\n ownerId: string,\n otherId: string,\n ): void {\n const rec = this.col(register).get(ownerId);\n if (!rec) throw new NotFoundError(register, ownerId);\n if (end.cardinality === \"many\") {\n const arr = Array.isArray(rec[end.field])\n ? (rec[end.field] as string[])\n : [];\n if (!arr.includes(otherId)) arr.push(otherId);\n rec[end.field] = arr;\n } else {\n rec[end.field] = otherId;\n }\n rec.version += 1;\n rec.updatedAt = this.now();\n }\n}\n\nexport function createInMemoryProvider(\n options?: InMemoryProviderOptions,\n): InMemoryProvider {\n return new InMemoryProvider(options);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared registry types — the field vocabulary the whole system speaks.
|
|
3
|
+
*
|
|
4
|
+
* These mirror `skills/_shared/registry-schema.md` and `ontology.yaml` (the
|
|
5
|
+
* single source of truth). Field *meaning* lives there; this file is the
|
|
6
|
+
* checkable TypeScript shape the packages import.
|
|
7
|
+
*/
|
|
8
|
+
/** The six registers plus the `people` reference collection. */
|
|
9
|
+
declare const REGISTERS: readonly ["assumptions", "experiments", "readings", "goals", "decisions", "glossary"];
|
|
10
|
+
type Register = (typeof REGISTERS)[number];
|
|
11
|
+
/** `people` is a reference collection, not one of the six registers. */
|
|
12
|
+
type Collection = Register | "people";
|
|
13
|
+
/** Every stored record carries an id, a version, and timestamps. */
|
|
14
|
+
interface BaseRecord {
|
|
15
|
+
id: string;
|
|
16
|
+
/** Optimistic-concurrency token; bumped on every write. */
|
|
17
|
+
version: number;
|
|
18
|
+
createdAt: string;
|
|
19
|
+
updatedAt: string;
|
|
20
|
+
}
|
|
21
|
+
type AssumptionStatus = "Draft" | "Live" | "Invalidated";
|
|
22
|
+
type ExperimentStatus = "Running" | "Closed";
|
|
23
|
+
type GoalStatus = "Draft" | "Active" | "Closed";
|
|
24
|
+
type DecisionStatus = "Active" | "Provisional" | "Superseded" | "Reversed";
|
|
25
|
+
type GlossaryStatus = "Active" | "Provisional" | "Superseded";
|
|
26
|
+
type Result = "Validated" | "Invalidated" | "Inconclusive";
|
|
27
|
+
type MagnitudeBand = "Low" | "Typical" | "High";
|
|
28
|
+
type Feasibility = "High" | "Medium" | "Low";
|
|
29
|
+
/** The 8-rung activity-and-strength ladder (order = strength, weakest first). */
|
|
30
|
+
declare const TESTING_RUNGS: readonly ["Opinion", "Pitch-deck reaction", "Anecdotal", "Desk research", "Survey at scale", "Prototype usage"];
|
|
31
|
+
declare const GOAL_RUNG_VALUES: readonly ["Signed intent", "Paying users"];
|
|
32
|
+
type TestingRung = (typeof TESTING_RUNGS)[number];
|
|
33
|
+
type GoalRung = (typeof GOAL_RUNG_VALUES)[number];
|
|
34
|
+
type Rung = TestingRung | GoalRung;
|
|
35
|
+
/** Representativeness and Credibility are each picked from these. */
|
|
36
|
+
type SourceQualityPick = 1.0 | 0.7 | 0.5;
|
|
37
|
+
/** The four derived numbers stored on an assumption (never hand-typed). */
|
|
38
|
+
interface AssumptionDerived {
|
|
39
|
+
derivedImpact: number;
|
|
40
|
+
risk: number;
|
|
41
|
+
confidence: number;
|
|
42
|
+
}
|
|
43
|
+
interface AssumptionRecord extends BaseRecord {
|
|
44
|
+
Title: string;
|
|
45
|
+
Description: string;
|
|
46
|
+
Lens: string | null;
|
|
47
|
+
Theme: string[];
|
|
48
|
+
/** The hand-scored seed (0–100), the only hand-scored number here. */
|
|
49
|
+
Impact: number | null;
|
|
50
|
+
Status: AssumptionStatus;
|
|
51
|
+
Owner: string[];
|
|
52
|
+
Gaps: string[];
|
|
53
|
+
moot: boolean;
|
|
54
|
+
dependsOnIds: string[];
|
|
55
|
+
enablesIds: string[];
|
|
56
|
+
contradictsIds: string[];
|
|
57
|
+
readingIds: string[];
|
|
58
|
+
derived: AssumptionDerived;
|
|
59
|
+
}
|
|
60
|
+
interface ReadingRecord extends BaseRecord {
|
|
61
|
+
Title: string;
|
|
62
|
+
/** First-class link to the artifact — the independence-dedupe key. */
|
|
63
|
+
Source: string | null;
|
|
64
|
+
assumptionId: string;
|
|
65
|
+
experimentId: string | null;
|
|
66
|
+
goalId: string | null;
|
|
67
|
+
Rung: Rung;
|
|
68
|
+
Representativeness: SourceQualityPick;
|
|
69
|
+
Credibility: SourceQualityPick;
|
|
70
|
+
/** For Goal-rung readings: the magnitude band from the absolute outcome. */
|
|
71
|
+
magnitudeBand?: MagnitudeBand;
|
|
72
|
+
Result: Result;
|
|
73
|
+
Date: string | null;
|
|
74
|
+
Owner: string[];
|
|
75
|
+
derived: {
|
|
76
|
+
sourceQuality: number;
|
|
77
|
+
strength: number;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
interface ExperimentRecord extends BaseRecord {
|
|
81
|
+
Title: string;
|
|
82
|
+
Instrument: string | null;
|
|
83
|
+
Feasibility: Feasibility | null;
|
|
84
|
+
Status: ExperimentStatus;
|
|
85
|
+
closureReason: "Completed" | "Early-stop" | "Kill" | null;
|
|
86
|
+
Owner: string[];
|
|
87
|
+
Date: string | null;
|
|
88
|
+
barLineAssumptionIds: string[];
|
|
89
|
+
}
|
|
90
|
+
interface GoalRecord extends BaseRecord {
|
|
91
|
+
Title: string;
|
|
92
|
+
Status: GoalStatus;
|
|
93
|
+
Outcome: "Achieved" | "Missed" | "Dropped" | null;
|
|
94
|
+
Owner: string[];
|
|
95
|
+
Date: string | null;
|
|
96
|
+
basedOnIds: string[];
|
|
97
|
+
}
|
|
98
|
+
interface DecisionRecord extends BaseRecord {
|
|
99
|
+
Title: string;
|
|
100
|
+
Status: DecisionStatus;
|
|
101
|
+
Owner: string[];
|
|
102
|
+
basedOnIds: string[];
|
|
103
|
+
resolvesIds: string[];
|
|
104
|
+
}
|
|
105
|
+
interface GlossaryRecord extends BaseRecord {
|
|
106
|
+
Title: string;
|
|
107
|
+
Status: GlossaryStatus;
|
|
108
|
+
}
|
|
109
|
+
/** A record of any register — the DataProvider's currency. */
|
|
110
|
+
type AnyRecord = BaseRecord & Record<string, unknown>;
|
|
111
|
+
/** A pointer to one record. */
|
|
112
|
+
interface RecordRef {
|
|
113
|
+
register: Collection;
|
|
114
|
+
id: string;
|
|
115
|
+
}
|
|
116
|
+
/** The relations the dashboard can set (each writes both ends). */
|
|
117
|
+
type Relation = "assumption-reading" | "assumption-depends-on" | "assumption-contradicts" | "reading-experiment" | "reading-goal" | "decision-based-on" | "decision-resolves" | "goal-based-on";
|
|
118
|
+
|
|
119
|
+
export { type AssumptionRecord as A, type BaseRecord as B, type Collection as C, type DecisionRecord as D, type ExperimentRecord as E, type Feasibility as F, GOAL_RUNG_VALUES as G, type MagnitudeBand 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 DecisionStatus as e, type ExperimentStatus as f, type GlossaryRecord as g, type GlossaryStatus as h, type GoalRecord as i, type GoalRung as j, type GoalStatus 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
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@validation-os/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The DataProvider seam, the shared derivation module (pure functions), and shared registry types for validation-os.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./derivation": {
|
|
16
|
+
"types": "./dist/derivation/index.d.ts",
|
|
17
|
+
"import": "./dist/derivation/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./testing": {
|
|
20
|
+
"types": "./dist/testing.d.ts",
|
|
21
|
+
"import": "./dist/testing.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"tsup": "^8.3.0",
|
|
33
|
+
"typescript": "^5.6.3"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/bfish1996/validation-os.git",
|
|
38
|
+
"directory": "packages/core"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsup",
|
|
42
|
+
"typecheck": "tsc --noEmit"
|
|
43
|
+
}
|
|
44
|
+
}
|