@validation-os/core 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-ZNOCENEV.js → chunk-OS7CTJ2A.js} +1 -1
- package/dist/chunk-OS7CTJ2A.js.map +1 -0
- package/dist/derivation/index.d.ts +2 -2
- package/dist/derivation/index.js +1 -1
- package/dist/{index-BSd5_vwB.d.ts → index-BWTfuyde.d.ts} +1 -1
- package/dist/index.d.ts +30 -5
- package/dist/index.js +20 -1
- package/dist/index.js.map +1 -1
- package/dist/{provider-N0ylgK8f.d.ts → provider-78NHbYi9.d.ts} +1 -1
- package/dist/testing.d.ts +2 -2
- package/dist/{types-BS3hq4-i.d.ts → types-D4Kn18fv.d.ts} +10 -0
- package/package.json +1 -1
- package/dist/chunk-ZNOCENEV.js.map +0 -1
|
@@ -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 /**\n * Presence-gap fields — long-form prose promoted from body sections to\n * first-class fields (OPS-1273). Their PRESENCE is a structural, blocking\n * check (see `presence.ts`): non-empty is required to move Status to `Live`\n * (the presence half of the Draft→Live gaps invariant, OPS-1251). May be\n * empty (\"\") while `Draft`.\n */\n \"5 Whys\": string;\n \"Metric for truth\": string;\n \"Scoring justification\": string;\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"]}
|
|
@@ -1,2 +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-
|
|
2
|
-
import '../types-
|
|
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-BWTfuyde.js';
|
|
2
|
+
import '../types-D4Kn18fv.js';
|
package/dist/derivation/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { j as GoalRung, M as MagnitudeBand, q as TestingRung, p as Rung, o as Result } from './types-
|
|
1
|
+
import { j as GoalRung, M as MagnitudeBand, q as TestingRung, p as Rung, o as Result } from './types-D4Kn18fv.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Round to 2 decimals, matching the migration's `+(n).toFixed(2)`.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,32 @@
|
|
|
1
|
-
import { R as Relation, C as Collection, A as AssumptionRecord, a as ReadingRecord, D as DecisionRecord, b as AssumptionDerived } from './types-
|
|
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-
|
|
3
|
-
export { D as DataProvider, N as NotFoundError, S as StaleVersionError, i as isNotFoundError, a as isStaleVersionError } from './provider-
|
|
4
|
-
export { i as derivation, s as recomputeSourceQuality, r as recomputeStrength } from './index-
|
|
1
|
+
import { R as Relation, C as Collection, A as AssumptionRecord, a as ReadingRecord, D as DecisionRecord, b as AssumptionDerived } from './types-D4Kn18fv.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-D4Kn18fv.js';
|
|
3
|
+
export { D as DataProvider, N as NotFoundError, S as StaleVersionError, i as isNotFoundError, a as isStaleVersionError } from './provider-78NHbYi9.js';
|
|
4
|
+
export { i as derivation, s as recomputeSourceQuality, r as recomputeStrength } from './index-BWTfuyde.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Presence checks — the structural half of the assumption write guardrail.
|
|
8
|
+
*
|
|
9
|
+
* `5 Whys`, `Metric for truth`, and `Scoring justification` used to live as
|
|
10
|
+
* body prose audited as *semantic* Gaps (the audit had to parse markdown and
|
|
11
|
+
* guess). OPS-1273 promotes them to first-class fields, so their PRESENCE is a
|
|
12
|
+
* cheap structural check: non-empty is required to move an assumption to
|
|
13
|
+
* `Live` — the presence half of the Draft→Live gaps invariant (OPS-1251).
|
|
14
|
+
*
|
|
15
|
+
* These are pure functions with no backend dependency: the primitive the CRUD
|
|
16
|
+
* write model is to block a Draft→Live write on (write-time enforcement lands
|
|
17
|
+
* with the write slice, OPS-1256), and that the audit reports as an error-level
|
|
18
|
+
* finding meanwhile (`presence-field-missing` in `ontology.yaml`).
|
|
19
|
+
*/
|
|
20
|
+
/** The assumption fields whose presence is structurally required to go `Live`. */
|
|
21
|
+
declare const ASSUMPTION_PRESENCE_FIELDS: readonly ["5 Whys", "Metric for truth", "Scoring justification"];
|
|
22
|
+
type AssumptionPresenceField = (typeof ASSUMPTION_PRESENCE_FIELDS)[number];
|
|
23
|
+
/** The presence fields that are absent or blank on a record. */
|
|
24
|
+
declare function missingPresenceFields(record: Partial<Record<AssumptionPresenceField, unknown>>): AssumptionPresenceField[];
|
|
25
|
+
/**
|
|
26
|
+
* True when every presence field is non-blank — the structural precondition
|
|
27
|
+
* for an assumption to be `Live`. A `Draft` may legally fail this.
|
|
28
|
+
*/
|
|
29
|
+
declare function assumptionPresenceComplete(record: Partial<Record<AssumptionPresenceField, unknown>>): boolean;
|
|
5
30
|
|
|
6
31
|
/**
|
|
7
32
|
* Relation config — the single table describing, for each linkable relation,
|
|
@@ -34,4 +59,4 @@ interface RecomputeInput {
|
|
|
34
59
|
/** id → recomputed derived tuple for every assumption in the register. */
|
|
35
60
|
declare function recomputeDerived(input: RecomputeInput): Map<string, AssumptionDerived>;
|
|
36
61
|
|
|
37
|
-
export { AssumptionDerived, AssumptionRecord, Collection, DecisionRecord, RELATIONS, ReadingRecord, type RecomputeInput, Relation, type RelationEnd, type RelationSpec, recomputeDerived };
|
|
62
|
+
export { ASSUMPTION_PRESENCE_FIELDS, AssumptionDerived, type AssumptionPresenceField, AssumptionRecord, Collection, DecisionRecord, RELATIONS, ReadingRecord, type RecomputeInput, Relation, type RelationEnd, type RelationSpec, assumptionPresenceComplete, missingPresenceFields, recomputeDerived };
|
package/dist/index.js
CHANGED
|
@@ -15,9 +15,25 @@ import {
|
|
|
15
15
|
readingStrength,
|
|
16
16
|
risk,
|
|
17
17
|
sourceQuality
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-OS7CTJ2A.js";
|
|
19
19
|
import "./chunk-PZ5AY32C.js";
|
|
20
20
|
|
|
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
|
+
|
|
21
37
|
// src/recompute.ts
|
|
22
38
|
var STANDING_DECISION = /* @__PURE__ */ new Set(["Active", "Provisional"]);
|
|
23
39
|
function toConfidenceInput(r) {
|
|
@@ -67,15 +83,18 @@ function recomputeDerived(input) {
|
|
|
67
83
|
return out;
|
|
68
84
|
}
|
|
69
85
|
export {
|
|
86
|
+
ASSUMPTION_PRESENCE_FIELDS,
|
|
70
87
|
GOAL_RUNG_VALUES,
|
|
71
88
|
NotFoundError,
|
|
72
89
|
REGISTERS,
|
|
73
90
|
RELATIONS,
|
|
74
91
|
StaleVersionError,
|
|
75
92
|
TESTING_RUNGS,
|
|
93
|
+
assumptionPresenceComplete,
|
|
76
94
|
derivation_exports as derivation,
|
|
77
95
|
isNotFoundError,
|
|
78
96
|
isStaleVersionError,
|
|
97
|
+
missingPresenceFields,
|
|
79
98
|
recomputeDerived,
|
|
80
99
|
sourceQuality as recomputeSourceQuality,
|
|
81
100
|
readingStrength as recomputeStrength
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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":";;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["../src/presence.ts","../src/recompute.ts"],"sourcesContent":["/**\n * Presence checks — the structural half of the assumption write guardrail.\n *\n * `5 Whys`, `Metric for truth`, and `Scoring justification` used to live as\n * body prose audited as *semantic* Gaps (the audit had to parse markdown and\n * guess). OPS-1273 promotes them to first-class fields, so their PRESENCE is a\n * cheap structural check: non-empty is required to move an assumption to\n * `Live` — the presence half of the Draft→Live gaps invariant (OPS-1251).\n *\n * These are pure functions with no backend dependency: the primitive the CRUD\n * write model is to block a Draft→Live write on (write-time enforcement lands\n * with the write slice, OPS-1256), and that the audit reports as an error-level\n * finding meanwhile (`presence-field-missing` in `ontology.yaml`).\n */\n\n/** The assumption fields whose presence is structurally required to go `Live`. */\nexport const ASSUMPTION_PRESENCE_FIELDS = [\n \"5 Whys\",\n \"Metric for truth\",\n \"Scoring justification\",\n] as const;\n\nexport type AssumptionPresenceField = (typeof ASSUMPTION_PRESENCE_FIELDS)[number];\n\n/** A value counts as present only when it is a non-blank string. */\nfunction isPresent(value: unknown): boolean {\n return typeof value === \"string\" && value.trim().length > 0;\n}\n\n/** The presence fields that are absent or blank on a record. */\nexport function missingPresenceFields(\n record: Partial<Record<AssumptionPresenceField, unknown>>,\n): AssumptionPresenceField[] {\n return ASSUMPTION_PRESENCE_FIELDS.filter((field) => !isPresent(record[field]));\n}\n\n/**\n * True when every presence field is non-blank — the structural precondition\n * for an assumption to be `Live`. A `Draft` may legally fail this.\n */\nexport function assumptionPresenceComplete(\n record: Partial<Record<AssumptionPresenceField, unknown>>,\n): boolean {\n return missingPresenceFields(record).length === 0;\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 * 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":";;;;;;;;;;;;;;;;;;;;;AAgBO,IAAM,6BAA6B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AACF;AAKA,SAAS,UAAU,OAAyB;AAC1C,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS;AAC5D;AAGO,SAAS,sBACd,QAC2B;AAC3B,SAAO,2BAA2B,OAAO,CAAC,UAAU,CAAC,UAAU,OAAO,KAAK,CAAC,CAAC;AAC/E;AAMO,SAAS,2BACd,QACS;AACT,SAAO,sBAAsB,MAAM,EAAE,WAAW;AAClD;;;ACnBA,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":[]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as Collection, c as AnyRecord, R as Relation, m as RecordRef } from './types-
|
|
1
|
+
import { C as Collection, c as AnyRecord, R as Relation, m as RecordRef } from './types-D4Kn18fv.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, m as RecordRef } from './types-
|
|
1
|
+
import { D as DataProvider } from './provider-78NHbYi9.js';
|
|
2
|
+
import { C as Collection, c as AnyRecord, R as Relation, m as RecordRef } from './types-D4Kn18fv.js';
|
|
3
3
|
|
|
4
4
|
interface InMemoryProviderOptions {
|
|
5
5
|
now?: () => string;
|
|
@@ -51,6 +51,16 @@ interface AssumptionRecord extends BaseRecord {
|
|
|
51
51
|
Owner: string[];
|
|
52
52
|
Gaps: string[];
|
|
53
53
|
moot: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Presence-gap fields — long-form prose promoted from body sections to
|
|
56
|
+
* first-class fields (OPS-1273). Their PRESENCE is a structural, blocking
|
|
57
|
+
* check (see `presence.ts`): non-empty is required to move Status to `Live`
|
|
58
|
+
* (the presence half of the Draft→Live gaps invariant, OPS-1251). May be
|
|
59
|
+
* empty ("") while `Draft`.
|
|
60
|
+
*/
|
|
61
|
+
"5 Whys": string;
|
|
62
|
+
"Metric for truth": string;
|
|
63
|
+
"Scoring justification": string;
|
|
54
64
|
dependsOnIds: string[];
|
|
55
65
|
enablesIds: string[];
|
|
56
66
|
contradictsIds: string[];
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
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":";;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKO,SAAS,OAAO,GAAmB;AACxC,SAAO,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC5B;;;ACEO,IAAM,YAAY;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAgCO,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACO,IAAM,mBAAmB,CAAC,iBAAiB,cAAc;;;AC9CzD,IAAM,cAA2C;AAAA,EACtD,SAAS;AAAA,EACT,uBAAuB;AAAA,EACvB,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,mBAAmB;AACrB;AAEO,IAAM,mBACX;AAAA,EACE,iBAAiB,EAAE,KAAK,IAAI,SAAS,IAAI,MAAM,GAAG;AAAA,EAClD,gBAAgB,EAAE,KAAK,IAAI,SAAS,IAAI,MAAM,GAAG;AACnD;AAEF,IAAM,gBAAgB,IAAI,IAAU,gBAAgB;AAE7C,SAAS,WAAW,MAA8B;AACvD,SAAO,cAAc,IAAI,IAAI;AAC/B;;;ACnBO,SAAS,KAAK,QAA4B;AAC/C,MAAI,WAAW,YAAa,QAAO;AACnC,MAAI,WAAW,cAAe,QAAO;AACrC,SAAO;AACT;AASO,SAAS,gBAAgB,OAA8B;AAC5D,QAAM,IAAI,KAAK,MAAM,MAAM;AAC3B,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,WAAW,MAAM,IAAI,GAAG;AAC1B,UAAM,OAAO,MAAM,iBAAiB;AACpC,WAAO,iBAAiB,MAAM,IAAI,EAAE,IAAI,IAAI;AAAA,EAC9C;AACA,UAAQ,YAAY,MAAM,IAAI,KAAK,KAAK;AAC1C;;;ACrBO,SAAS,cACd,oBACA,aACQ;AACR,SAAO,OAAO,qBAAqB,WAAW;AAChD;;;ACIO,IAAM,KAAK;AAqBX,SAAS,WAAW,UAA4C;AACrE,QAAM,SAAmB,SACtB,OAAO,CAAC,MAAM,EAAE,WAAW,eAAe,EAAE,WAAW,aAAa,EACpE,IAAI,CAAC,OAAO;AAAA,IACX,OAAO;AAAA,IACP,UAAU,gBAAgB,CAAC;AAAA,IAC3B,IAAI,cAAc,EAAE,oBAAoB,EAAE,WAAW;AAAA,EACvD,EAAE,EACD,OAAO,CAAC,MAAM,EAAE,aAAa,CAAC;AAGjC,QAAM,OAAO,oBAAI,IAAoB;AACrC,aAAW,KAAK,QAAQ;AACtB,QAAI,WAAW,EAAE,MAAM,IAAI,GAAG;AAC5B,WAAK,IAAI,EAAE,MAAM,IAAI,CAAC;AACtB;AAAA,IACF;AACA,UAAM,MAAM,EAAE,MAAM,UAAU,EAAE,MAAM;AACtC,UAAM,MAAM,KAAK,IAAI,GAAG;AACxB,UAAM,SACJ,CAAC,OACD,KAAK,IAAI,EAAE,QAAQ,IAAI,KAAK,IAAI,IAAI,QAAQ,KAC3C,KAAK,IAAI,EAAE,QAAQ,MAAM,KAAK,IAAI,IAAI,QAAQ,MAC5C,EAAE,MAAM,QAAQ,OAAO,IAAI,MAAM,QAAQ;AAC9C,QAAI,OAAQ,MAAK,IAAI,KAAK,CAAC;AAAA,EAC7B;AAEA,MAAI,MAAM;AACV,MAAI,MAAM;AACV,aAAW,KAAK,KAAK,OAAO,GAAG;AAC7B,UAAM,IAAI,KAAK,IAAI,EAAE,QAAQ,IAAI,EAAE;AACnC,WAAO,IAAI,EAAE;AACb,WAAO;AAAA,EACT;AACA,SAAO,MAAM,IAAI,OAAO,MAAM,GAAG,IAAI;AACvC;;;AC7CO,SAAS,eACd,aACA,gBAAwC,CAAC,GACpB;AACrB,QAAM,OAAO,IAAI,IAAI,YAAY,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AAGtD,QAAM,aAAa,oBAAI,IAAsB;AAC7C,aAAW,KAAK,YAAa,YAAW,IAAI,EAAE,IAAI,CAAC,CAAC;AACpD,aAAW,KAAK,aAAa;AAC3B,eAAW,OAAO,EAAE,aAAc,YAAW,IAAI,GAAG,GAAG,KAAK,EAAE,EAAE;AAAA,EAClE;AAEA,QAAM,OAAO,oBAAI,IAAoB;AACrC,QAAM,UAAU,CAAC,IAAY,SAA8B;AACzD,UAAM,SAAS,KAAK,IAAI,EAAE;AAC1B,QAAI,WAAW,OAAW,QAAO;AACjC,QAAI,KAAK,IAAI,EAAE,EAAG,QAAO;AACzB,SAAK,IAAI,EAAE;AACX,UAAM,IAAI,KAAK,IAAI,EAAE;AACrB,QAAI,CAAC,EAAG,QAAO;AACf,QAAI,EAAE,MAAM;AACV,WAAK,IAAI,IAAI,CAAC;AACd,aAAO;AAAA,IACT;AACA,UAAM,OAAO,EAAE,UAAU;AACzB,QAAI,IAAI;AACR,eAAW,SAAS,WAAW,IAAI,EAAE,KAAK,CAAC,GAAG;AAC5C,YAAM,IAAI,KAAK,IAAI,KAAK;AACxB,UAAI,KAAK,CAAC,EAAE,KAAM,MAAK,QAAQ,OAAO,IAAI;AAAA,IAC5C;AACA,SAAK,OAAO,cAAc,EAAE,KAAK;AACjC,UAAM,QAAQ,QAAQ,MAAM,SAAS,KAAK,IAAI;AAC9C,UAAM,UAAU,OAAO,KAAK;AAC5B,SAAK,IAAI,IAAI,OAAO;AACpB,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,oBAAI,IAAoB;AACpC,aAAW,KAAK,YAAa,KAAI,IAAI,EAAE,IAAI,QAAQ,EAAE,IAAI,oBAAI,IAAI,CAAC,CAAC;AACnE,SAAO;AACT;;;AC7DO,SAAS,KAAK,eAAuBA,aAA4B;AACtE,SAAO,OAAO,iBAAiB,IAAI,KAAK,IAAI,GAAGA,WAAU,IAAI,IAAI;AACnE;","names":["confidence"]}
|